偃师申请400电话费用【偃师企业全国热线电话办理】偃师开通400电话电信价格、偃师微信公众号代运营外包托管、偃师网店编辑装修美工、偃师网站推广优化大概需要多少钱
偃师区,隶属于河南省洛阳市,位于河南省西部,总面积668.58平方千米 [1] 。截至2019年末,偃师区总人口63.2万人 [3] ,截至2020年6月,偃师区下辖4个街道、9个镇 [2] ,偃师区人民政府驻槐新街道民主路27号。 [24]
周,设偃师县。1993年,偃师撤县设市。 [4] 2021年3月,撤销县级偃师市,设立洛阳市偃师区 [25] 。偃师区是河南省制造业高质量发展综合评价试点市 [5] 、革命文物保护利用片区分县 [6] 、国家卫生城市(区)。 [18]
2019年,偃师区实现地区生产总值443.6亿元,固定资产投资193.8亿元,一般公共预算收入24.6亿元,社会消费品零售总额222.6亿元。 [7]
目录
Xposed ART原理分析笔记
0. 写在前面
1. Xposed结构
2. `Xposed` 注入进程
3. `Xposed` `hook`函数
4. 被`hook`的函数执行流程分析
5. 参考资料
Xposed ART原理分析笔记
0. 写在前面
这篇文章是我在看Xposed源码时所做的一些笔记内容,中间也参考了一些前人的分析文章这里都在最后列出了,除此之外还有一些内容纯属个人理解,如果有错误欢迎各位大佬斧正,感谢!
1. Xposed结构
Xposed 相关源码: https://github.com/rovo89
XposedBridge: Xposed 提供的 jar 文件,app_process 启动过程中会加载该 jar 包,其 他的 Modules 的开发都是基于该 jar 包;
Xposed: Xposed 的 C++ 部 分 , 主 要 是 用 来 替 换 /system/bin/app_process , 并 为 XposedBridge 提供 JNI 方法;
XposedInstaller: Xposed 的安装包,提供对基于 Xposed 框架的 Modules 的管理;
android_art: Xposed修改的art部分源码
2. Xposed 注入进程
Xposed注入进程的方式是通过zygote进程在fork出APP进程时使之加载XposedBridge.jar;
根据Xposed的Android.mk文件可知在5.0以上的手机上编译的是app_main2.cpp
1 2 3 4 5 6 7 8 9 | ifeq (1,$(strip $(shell expr $(PLATFORM_SDK_VERSION) \>= 21)))
LOCAL_SRC_FILES := app_main2.cpp
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32_xposed
LOCAL_MODULE_STEM_64 := app_process64_xposed
else
LOCAL_SRC_FILES := app_main.cpp
LOCAL_MODULE_STEM := app_process_xposed
endif
|
这里选取Android 7.1.1_r6的app_main.cpp文件与app_main2.cpp做对比
从main函数看起

从文件的对比可以发现,app_main2.cpp中多出了对于 Xposed::handleOptions的调用,而这个函数主要是对--xposedversion的处理以及是否测试模式的启用,真正运行时无需理会
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | bool handleOptions(int argc, char* const argv[]) {
// version 信息
parseXposedProp();
if (argc == 2 && strcmp(argv[1], "--xposedversion") == 0) {
printf("Xposed version: %s\n", xposedVersion);
return true;
}
if (argc == 2 && strcmp(argv[1], "--xposedtestsafemode") == 0) {
printf("Testing Xposed safemode trigger\n");
if (detectSafemodeTrigger(shouldSkipSafemodeDelay())) {
printf("Safemode triggered\n");
} else {
printf("Safemode not triggered\n");
}
return true;
}
// From Lollipop coding, used to override the process name
argBlockStart = argv[0];
uintptr_t start = reinterpret_cast<uintptr_t>(argv[0]);
uintptr_t end = reinterpret_cast<uintptr_t>(argv[argc - 1]);
end += strlen(argv[argc - 1]) + 1;
argBlockLength = end - start;
return false;
}
|
在main函数的最后,增加了对Xposed的加载与runtimeStart函数的调用。

在initialize函数最终返回XposedBridge.jar是否成功加入环境变量的标志和XposedInstaller是否成功运行的标志
首先对xposed这个结构体变量进行赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | struct XposedShared {
// Global variables
bool zygote;
bool startSystemServer;
const char* startClassName;
uint32_t xposedVersionInt;
bool isSELinuxEnabled;
bool isSELinuxEnforcing;
uid_t installer_uid;
gid_t installer_gid;
// Provided by runtime-specific library, used by executable
void (*onVmCreated)(JNIEnv* env);
// Provided by the executable, used by runtime-specific library
int (*zygoteservice_accessFile)(const char* path, int mode);
int (*zygoteservice_statFile)(const char* path, struct stat* st);
char* (*zygoteservice_readFile)(const char* path, int* bytesRead);
};
XposedShared* xposed = new XposedShared;
xposed->zygote = zygote;
xposed->startSystemServer = startSystemServer;
xposed->startClassName = className;
xposed->xposedVersionInt = xposedVersionInt;
xposed->isSELinuxEnabled = is_selinux_enabled() == 1;
xposed->isSELinuxEnforcing = xposed->isSELinuxEnabled && security_getenforce() == 1;
xposed->isSELinuxEnabled = false;
xposed->isSELinuxEnforcing = false;
|
然后在logcat中通过printStartupMarker函数和printRomInfo函数打印一些信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | void printStartupMarker() {
sprintf(marker, "Current time: %d, PID: %d", (int) time(NULL), getpid());
ALOG(LOG_DEBUG, "XposedStartupMarker", marker, NULL);
}
void printRomInfo() {
char release[PROPERTY_VALUE_MAX];
char sdk[PROPERTY_VALUE_MAX];
char manufacturer[PROPERTY_VALUE_MAX];
char model[PROPERTY_VALUE_MAX];
char rom[PROPERTY_VALUE_MAX];
char fingerprint[PROPERTY_VALUE_MAX];
char platform[PROPERTY_VALUE_MAX];
const int bit = 64;
const int bit = 32;
property_get("ro.build.version.release", release, "n/a");
property_get("ro.build.version.sdk", sdk, "n/a");
property_get("ro.product.manufacturer", manufacturer, "n/a");
property_get("ro.product.model", model, "n/a");
property_get("ro.build.display.id", rom, "n/a");
property_get("ro.build.fingerprint", fingerprint, "n/a");
property_get("ro.product.cpu.abi", platform, "n/a");
ALOGI("-----------------");
ALOGI("Starting Xposed version %s, compiled for SDK %d", xposedVersion, PLATFORM_SDK_VERSION);
ALOGI("Device: %s (%s), Android version %s (SDK %s)", model, manufacturer, release, sdk);
ALOGI("ROM: %s", rom);
ALOGI("Build fingerprint: %s", fingerprint);
ALOGI("Platform: %s, %d-bit binary, system server: %s", platform, bit, xposed->startSystemServer ? "yes" : "no");
if (!xposed->zygote) {
ALOGI("Class name: %s", xposed->startClassName);
}
ALOGI("SELinux enabled: %s, enforcing: %s",
xposed->isSELinuxEnabled ? "yes" : "no",
xposed->isSELinuxEnforcing ? "yes" : "no");
}
|
然后以下函数通过启动XposedInstaller和相应Xposed服务
1 2 3 | if (!determineXposedInstallerUidGid() || !xposed::service::startAll()) {
return false;
}
|
最后如果XPosed能够正常加载,那么就通过函数addJarToClasspath 将XposedBridge.jar文件加入环境变量
这里如果想要禁用Xposed只需要在XposedInstaller的私有目录下创建一个conf/disabled文件即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /** Create a flag file to disable Xposed. */
void disableXposed() {
int fd;
// FIXME add a "touch" operation to xposed::service::membased
fd = open(XPOSED_LOAD_BLOCKER, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd >= 0)
close(fd);
}
|
如果成功加载则调用runtimeStart函数对调用AndroidRuntime::start()函数对de.robv.android.xposed.XposedBridge类进行启动
1 2 | // XPOSED_CLASS_DOTS_ZYGOTE = de.robv.android.xposed.XposedBridge
runtimeStart(runtime, isXposedLoaded ? "de.robv.android.xposed.XposedBridge" : "com.android.internal.os.ZygoteInit", args, zygote);
|
而AndroidRuntime::start()函数经过观察发现实际上完成了几个工作
1.startVm函数,启动虚拟机
1 2 3 4 5 6 7 8 | /* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
|
2.反射调用传入的类的main函数作为虚拟机的入口点

而Xposed就是通过替换这个className来达到加载自己的art虚拟机的效果。这样就成功注入了XposedBridge.jar文件,达到任意APP在加载时内存空间中总会有XposedBridge.jar。
而XposedBridge.jar文件的main函数如下,其实就是一个对SELinux的处理以及对Xposed模块的加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | protected static void main(String[] args) {
// Initialize the Xposed framework and modules
try {
if (!hadInitErrors()) {
initXResources();
SELinuxHelper.initOnce();
SELinuxHelper.initForProcess(null);
runtime = getRuntime();
XPOSED_BRIDGE_VERSION = getXposedVersion();
if (isZygote) {
// 暂且忽略
XposedInit.hookResources();
XposedInit.initForZygote();
}
// 模块的加载
XposedInit.loadModules();
} else {
Log.e(TAG, "Not initializing Xposed because of previous errors");
}
} catch (Throwable t) {
Log.e(TAG, "Errors during Xposed initialization", t);
disableHooks = true;
}
// Call the original startup code
if (isZygote) {
ZygoteInit.main(args);
} else {
RuntimeInit.main(args);
}
}
|
对模块的加载是通过对XposedInstaller私有目录下的conf/modules.list文件进行读取,并分别是通过BOOTCLASSLOADER对模块进行加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | /*package*/ static void loadModules() throws IOException {
final String filename = BASE_DIR + "conf/modules.list";
BaseService service = SELinuxHelper.getAppDataFileService();
if (!service.checkFileExists(filename)) {
Log.e(TAG, "Cannot load any modules because " + filename + " was not found");
return;
}
ClassLoader topClassLoader = XposedBridge.BOOTCLASSLOADER;
ClassLoader parent;
while ((parent = topClassLoader.getParent()) != null) {
topClassLoader = parent;
}
InputStream stream = service.getFileInputStream(filename);
BufferedReader apks = new BufferedReader(new InputStreamReader(stream));
String apk;
while ((apk = apks.readLine()) != null) {
// 按行加载模块
loadModule(apk, topClassLoader);
}
apks.close();
}
|
在最终的loadModule函数中实际上是通过DexFile的构造函数将模块加载并通过dexFile.loadClass函数的方式对入口类进行加载。最终达到模块注入的效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | private static void loadModule(String apk, ClassLoader topClassLoader) {
...
DexFile dexFile;
try {
dexFile = new DexFile(apk);
} catch (IOException e) {
Log.e(TAG, " Cannot load module", e);
return;
}
// Instant Run的处理
if (dexFile.loadClass(INSTANT_RUN_CLASS, topClassLoader) != null) {
Log.e(TAG, " Cannot load module, please disable \"Instant Run\" in Android Studio.");
closeSilently(dexFile);
return;
}
if (dexFile.loadClass(XposedBridge.class.getName(), topClassLoader) != null) {
Log.e(TAG, " Cannot load module:");
Log.e(TAG, " The Xposed API classes are compiled into the module's APK.");
Log.e(TAG, " This may cause strange issues and must be fixed by the module developer.");
Log.e(TAG, " For details, see: http://api.xposed.info/using.html");
closeSilently(dexFile);
return;
}
...
}
|
3. Xposed hook函数
我们知道Xposed hook函数的方式是大致是通过以下模版完成的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class XposedHook implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
Class clasz = loadPackageParam.classLoader.loadClass("xxxx"); //要hook的方法所在的类名
XposedHelpers.findAndHookMethod(clazz, "xxx",String.class,String.class,String.class, new XC_MethodHook() { //要hook的方法名和参数类型,此处为三个String类型
@Override //重写XC_MethodHook()的回调方法
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Log.i("hook after result:",param.getResult().toString()); //打印返回值(String类型)
}
});
}
}
}
|
其中最关键的执行hook逻辑的函数实际上是XposedHelpers.findAndHookMethod()函数
我们从这个函数跟踪起

观察这个函数分为两个部分
找到回调CallBack和hook的函数
调用XposedBridge.hookMethod()函数完成hook。
第一部分,用于获取回调的部分实际上就是获取参数的最后一个值,而用于寻找hook对应函数的findMethodExact函数,如下所示实际上就是通过反射获取,具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static Method findMethodExact(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
String fullMethodName = clazz.getName() + '#' + methodName + getParametersString(parameterTypes) + "#exact";
// 缓存机制
if (methodCache.containsKey(fullMethodName)) {
Method method = methodCache.get(fullMethodName);
if (method == null)
throw new NoSuchMethodError(fullMethodName);
return method;
}
try {
// 真实逻辑
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
method.setAccessible(true);
methodCache.put(fullMethodName, method);
return method;
} catch (NoSuchMethodException e) {
methodCache.put(fullMethodName, null);
throw new NoSuchMethodError(fullMethodName);
}
}
|
第二部分,执行hook逻辑的函数其内容主要分为三个部分
1.检查要hook的函数是否合法,必须同时满足三个条件:第一,是普通函数或者构造函数;第二,所在类不是接口Interface;第三,函数不是abstract抽象函数。

2.从缓存中确认函数未被hook并将新函数加入缓存。

3.执行真实hook逻辑,具体代码如下。可以发现真实执行hook逻辑的函数交给了hookMethodNative函数,而这个函数实际上是一个native属性的函数。

而这个函数的C++实现在libxposed_art.cpp文件中。观察其函数发现,实际上就是将Java层的Method转换成了ArtMethod,然后通过ArtMethod类的EnableXposedHook函数执行hook。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | void XposedBridge_hookMethodNative(JNIEnv* env, jclass, jobject javaReflectedMethod,
jobject, jint, jobject javaAdditionalInfo) {
// Detect usage errors.
// ScopedObjectAccess:访问管理对象时候调用,一是将jobject加入LocalReference二是线程切换为kRunnable状态,即离开安全区。
ScopedObjectAccess soa(env);
if (javaReflectedMethod == nullptr) {
ThrowIllegalArgumentException("method must not be null");
ThrowIllegalArgumentException(nullptr, "method must not be null");
return;
}
// Get the ArtMethod of the method to be hooked.
ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaReflectedMethod);
// Hook the method
artMethod->EnableXposedHook(soa, javaAdditionalInfo);
}
|
而EnableXposedHook函数的实现就在Xposed修改的art代码中了,打开android_art工程,找到对应实现runtime/art_method.cc文件。
具体实现分为几步
1.备份原先的Method并添加标记kAccXposedOriginalMethod
1 2 3 4 5 6 | // Create a backup of the ArtMethod object
auto* cl = Runtime::Current()->GetClassLinker();
auto* linear_alloc = cl->GetAllocatorForClassLoader(GetClassLoader());
ArtMethod* backup_method = cl->CreateRuntimeMethod(linear_alloc);
backup_method->CopyFrom(this, cl->GetImagePointerSize());
backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod);
|
2.创建一个备份方法对应的反射对象
1 2 3 4 5 6 7 8 | // Create a Method/Constructor object for the backup ArtMethod object
mirror::AbstractMethod* reflected_method;
if (IsConstructor()) {
reflected_method = mirror::Constructor::CreateFromArtMethod(soa.Self(), backup_method);
} else {
reflected_method = mirror::Method::CreateFromArtMethod(soa.Self(), backup_method);
}
reflected_method->SetAccessible<false>(true);
|
3.将所有相关内容保存为一个结构体存储
1 2 3 4 | XposedHookInfo* hook_info = reinterpret_cast<XposedHookInfo*>(linear_alloc->Alloc(soa.Self(), sizeof(XposedHookInfo)));
hook_info->reflected_method = soa.Vm()->AddGlobalRef(soa.Self(), reflected_method);
hook_info->additional_info = soa.Env()->NewGlobalRef(additional_info);
hook_info->original_method = backup_method;
|
4.准备工作,处理函数的JIT即时编译以及其他
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // suspend线程
ScopedThreadSuspension sts(soa.Self(), kSuspended);
// 停止JIT即时编译
jit::ScopedJitSuspend sjs;
// 防止死锁
gc::ScopedGCCriticalSection gcs(soa.Self(),
gc::kGcCauseXposed,
gc::kCollectorTypeXposed);
// 用来暂停占用函数的所有线程
ScopedSuspendAll ssa(__FUNCTION__);
// 取消所有调用
cl->InvalidateCallersForMethod(soa.Self(), this);
// 去除JIT编译的结果
jit::Jit* jit = art::Runtime::Current()->GetJit();
if (jit != nullptr) {
jit->GetCodeCache()->MoveObsoleteMethod(this, backup_method);
}
|
5.设置被hook函数的入口点(关键的hook逻辑)
1 2 3 4 5 6 7 8 9 10 11 | // 将hook信息存到entry_point_from_jni这个指针
SetEntryPointFromJniPtrSize(reinterpret_cast<uint8_t*>(hook_info), sizeof(void*));
// 设替换函数入口点entry_point_from_quick_compiled_code_为自己的art_quick_proxy_invoke_handler
SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler());
// 设置函数在CodeItem偏移
SetCodeItemOffset(0);
// Adjust access flags.
// 更改属性并添加kAccXposedHookedMethod标记
const uint32_t kRemoveFlags = kAccNative | kAccSynchronized | kAccAbstract | kAccDefault | kAccDefaultConflict;
SetAccessFlags((GetAccessFlags() & ~kRemoveFlags) | kAccXposedHookedMethod);
|
6.恢复环境
1 2 3 | // 恢复线程
MutexLock mu(soa.Self(), *Locks::thread_list_lock_);
Runtime::Current()->GetThreadList()->ForEach(StackReplaceMethodAndInstallInstrumentation, this);
|
这样就执行完成了函数的hook
4. 被hook的函数执行流程分析
当被hook的函数执行时,我们直接从ArtMethod->Invoke函数入手。
由于被hook的函数属性是一个native属性,观察函数中重要逻辑。由于在设置函数时已经设置过函数入口点为entry_point_from_quick_compiled_code_不为false则会进入art_quick_invoke_stub或者art_quick_invoke_static_stub跳板函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result,
const char* shorty) {
...
bool have_quick_code = GetEntryPointFromQuickCompiledCode() != nullptr;
if (LIKELY(have_quick_code)) {
if (kLogInvocationStartAndReturn) {
LOG(INFO) << StringPrintf(
"Invoking '%s' quick code=%p static=%d", PrettyMethod(this).c_str(),
GetEntryPointFromQuickCompiledCode(), static_cast<int>(IsStatic() ? 1 : 0));
}
// Ensure that we won't be accidentally calling quick compiled code when -Xint.
if (kIsDebugBuild && runtime->GetInstrumentation()->IsForcedInterpretOnly()) {
CHECK(!runtime->UseJitCompilation());
const void* oat_quick_code = runtime->GetClassLinker()->GetOatMethodQuickCodeFor(this);
CHECK(oat_quick_code == nullptr || oat_quick_code != GetEntryPointFromQuickCompiledCode())
<< "Don't call compiled code when -Xint " << PrettyMethod(this);
}
if (!IsStatic()) {
(*art_quick_invoke_stub)(this, args, args_size, self, result, shorty);
} else {
(*art_quick_invoke_static_stub)(this, args, args_size, self, result, shorty);
}
....
}
...
}
|
这里以art_quick_invoke_stub非静态函数为例,最终不管是arm还是arm64都是以汇编实现的这个函数,只是arm在真实执行函数时有一些中间的跳板,最终实现函数为art_quick_invoke_stub_internal。
以arm为例,其实现函数所在文件为android_art/runtime/arch/arm/quick_entrypoints_arm.S,汇编中存在着一个ART_METHOD_QUICK_CODE_OFFSET_32这个函数的调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | ENTRY art_quick_invoke_stub_internal
....
mov r4,
ldr ip, [r0,
blx ip @ call the method
mov sp, r11 @ restore the stack pointer
.cfi_def_cfa_register sp
ldr r4, [sp,
ldr r9, [sp,
cmp r4,
ite eq
strdeq r0, [r9] @ store r0/r1 into result pointer
vstrne d0, [r9] @ store s0-s1/d0 into result pointer
pop {r4, r5, r6, r7, r8, r9, r10, r11, pc} @ restore spill regs
END art_quick_invoke_stub_internal
|
而ART_METHOD_QUICK_CODE_OFFSET_32函数定义在runtime/asm_support.h文件中实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | struct PACKED(4) PtrSizedFields {
// Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
ArtMethod** dex_cache_resolved_methods_;
// Short cuts to declaring_class_->dex_cache_ member for fast compiled code access.
GcRoot<mirror::Class>* dex_cache_resolved_types_;
// Pointer to JNI function registered to this method, or a function to resolve the JNI function,
// or the profiling data for non-native methods, or an ImtConflictTable.
void* entry_point_from_jni_;
// Method dispatch from quick compiled code invokes this pointer which may cause bridging into
// the interpreter.
void* entry_point_from_quick_compiled_code_;
} ptr_sized_fields_;
static MemberOffset EntryPointFromQuickCompiledCodeOffset(size_t pointer_size) {
return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*) * pointer_size);
}
ADD_TEST_EQ(ART_METHOD_QUICK_CODE_OFFSET_32,
art::ArtMethod::EntryPointFromQuickCompiledCodeOffset(4).Int32Value())
|
EntryPointFromQuickCompiledCodeOffset函数是一个获取PtrSizedFields结构体固定偏移的函数,这里就是获取的entry_point_from_quick_compiled_code_变量的值。
这里由于Xposed对这个函数进行了hook,实际上就是获取的art_quick_proxy_invoke_handler函数的地址。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ENTRY art_quick_proxy_invoke_handler
SETUP_REFS_AND_ARGS_CALLEE_SAVE_FRAME_WITH_METHOD_IN_R0
mov r2, r9 @ pass Thread::Current
mov r3, sp @ pass SP
@ 调用函数
blx artQuickProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, SP)
ldr r2, [r9,
// Tear down the callee-save frame. Skip arg registers.
add sp,
.cfi_adjust_cfa_offset -(FRAME_SIZE_REFS_AND_ARGS_CALLEE_SAVE - FRAME_SIZE_REFS_ONLY_CALLEE_SAVE)
RESTORE_REFS_ONLY_CALLEE_SAVE_FRAME
cbnz r2, 1f @ success if no exception is pending
vmov d0, r0, r1 @ store into fpr, for when it's a fpr return...
bx lr @ return on success
1:
DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler
|
故最终在art_quick_invoke_stub_internal也就是调用的art_quick_proxy_invoke_handler函数。而在这个art_quick_proxy_invoke_handler函数中,又再次调用了artQuickProxyInvokeHandler函数。
在artQuickProxyInvokeHandler函数中又看到了一堆xposed相关的信息,其具体实现在android_art/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc。
这里我梳理了一下Xposed相关的重要代码列出来,具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | extern "C" uint64_t artQuickProxyInvokeHandler(
ArtMethod* proxy_method, mirror::Object* receiver, Thread* self, ArtMethod** sp)
SHARED_REQUIRES(Locks::mutator_lock_) {
const bool is_xposed = proxy_method->IsXposedHookedMethod();
...
if (is_xposed) {
jmethodID proxy_methodid = soa.EncodeMethod(proxy_method);
self->EndAssertNoThreadSuspension(old_cause);
JValue result = InvokeXposedHandleHookedMethod(soa, shorty, rcvr_jobj, proxy_methodid, args);
local_ref_visitor.FixupReferences();
return result.GetJ();
}
}
|
观察发现实际上最重要的就是InvokeXposedHandleHookedMethod函数的调用,其具体内容主要分成三部分。
1.处理参数信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | if (args.size() > 0 || (target_sdk_version > 0 && target_sdk_version <= 21)) {
args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, nullptr);
if (args_jobj == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
return zero;
}
for (size_t i = 0; i < args.size(); ++i) {
if (shorty[i + 1] == 'L') {
jobject val = args.at(i).l;
soa.Env()->SetObjectArrayElement(args_jobj, i, val);
} else {
JValue jv;
jv.SetJ(args.at(i).j);
mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv);
if (val == nullptr) {
CHECK(soa.Self()->IsExceptionPending());
return zero;
}
soa.Decode<mirror::ObjectArray<mirror::Object>* >(args_jobj)->Set<false>(i, val);
}
}
}
|
2.调用XposedBridge.handleHookedMethod()函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | const XposedHookInfo* hook_info = soa.DecodeMethod(method)->GetXposedHookInfo();
// Call XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,
// Object thisObject, Object[] args)
// 利用jvalue传递参数
jvalue invocation_args[5];
invocation_args[0].l = hook_info->reflected_method;
invocation_args[1].i = 1;
invocation_args[2].l = hook_info->additional_info;
invocation_args[3].l = rcvr_jobj;
invocation_args[4].l = args_jobj;
/*
classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);
ArtMethod::xposed_callback_class = classXposedBridge;
methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod",
"(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
ArtMethod::xposed_callback_method = methodXposedBridgeHandleHookedMethod;
*/
jobject result =
soa.Env()->CallStaticObjectMethodA(ArtMethod::xposed_callback_class,
ArtMethod::xposed_callback_method,
invocation_args);
|
3.处理结果并返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // Unbox the result if necessary and return it.
if (UNLIKELY(soa.Self()->IsExceptionPending())) {
return zero;
} else {
if (shorty[0] == 'V' || (shorty[0] == 'L' && result == nullptr)) {
return zero;
}
// This can cause thread suspension.
size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
mirror::Class* result_type = soa.DecodeMethod(method)->GetReturnType(true /* resolve */, pointer_size);
mirror::Object* result_ref = soa.Decode<mirror::Object*>(result);
JValue result_unboxed;
if (!UnboxPrimitiveForResult(result_ref, result_type, &result_unboxed)) {
DCHECK(soa.Self()->IsExceptionPending());
return zero;
}
return result_unboxed;
}
|
在这三个部分中最最重要的实际上是第二部分。
其中首先通过GetXposedHookInfo()函数调用GetEntryPointFromJniPtrSize()函数获取Xposed在设置函数hook时保存在entry_point_from_jni_中的XposedHookInfo对象信息。
1 2 3 4 5 | const XposedHookInfo* GetXposedHookInfo() {
DCHECK(IsXposedHookedMethod());
return reinterpret_cast<const XposedHookInfo*>(GetEntryPointFromJniPtrSize(sizeof(void*)));
}
const XposedHookInfo* hook_info = soa.DecodeMethod(method)->GetXposedHookInfo();
|
然后拼接参数并通过CallStaticObjectMethodA()函数JNI调用XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,Object thisObject, Object[] args)函数这样就再次回到Java层中。
此时再次回到XposedBridge的源码,找到对应handleHookedMethod函数的实现会发现
先调用了CallBack中的beforeHookedMethod函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // call "before method" callbacks
int beforeIdx = 0;
do {
try {
((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);
} catch (Throwable t) {
XposedBridge.log(t);
// reset result (ignoring what the unexpectedly exiting callback did)
param.setResult(null);
param.returnEarly = false;
continue;
}
if (param.returnEarly) {
// skip remaining "before" callbacks and corresponding "after" callbacks
beforeIdx++;
break;
}
} while (++beforeIdx < callbacksLength);
|
2.然后通过invokeOriginalMethodNative调用原函数
1 2 3 4 5 6 7 8 9 | // call original method if not requested otherwise
if (!param.returnEarly) {
try {
param.setResult(invokeOriginalMethodNative(method, originalMethodId,
additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));
} catch (InvocationTargetException e) {
param.setThrowable(e.getCause());
}
}
|
3.最后执行所有的afterHookedMethod回调。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // call "after method" callbacks
int afterIdx = beforeIdx - 1;
do {
Object lastResult = param.getResult();
Throwable lastThrowable = param.getThrowable();
try {
((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);
} catch (Throwable t) {
XposedBridge.log(t);
// reset to last result (ignoring what the unexpectedly exiting callback did)
if (lastThrowable == null)
param.setResult(lastResult);
else
param.setThrowable(lastThrowable);
}
} while (--afterIdx >= 0);
|
其中invokeOriginalMethodNative函数则是通过保存的reflected_method反射对象对原函数进行反射调用,也就是通过原art函数InvokeMethod()函数进行调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | jobject XposedBridge_invokeOriginalMethodNative(JNIEnv* env, jclass, jobject javaMethod,
jint isResolved, jobjectArray, jclass, jobject javaReceiver, jobjectArray javaArgs) {
ScopedFastNativeObjectAccess soa(env);
if (UNLIKELY(!isResolved)) {
ArtMethod* artMethod = ArtMethod::FromReflectedMethod(soa, javaMethod);
if (LIKELY(artMethod->IsXposedHookedMethod())) {
javaMethod = artMethod->GetXposedHookInfo()->reflected_method;
}
}
return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, true);
}
|

偃师申请400电话费用【偃师企业全国热线电话办理】偃师开通400电话电信价格、偃师微信公众号代运营外包托管、偃师网店编辑装修美工、偃师网站推广优化大概需要多少钱