这篇文章是我在看Xposed
源码时所做的一些笔记内容,中间也参考了一些前人的分析文章这里都在最后列出了,除此之外还有一些内容纯属个人理解,如果有错误欢迎各位大佬斧正,感谢!
Xposed 相关源码: https://github.com/rovo89
Xposed
注入进程的方式是通过zygote
进程在fork
出APP
进程时使之加载XposedBridge.jar
;
根据Xposed
的Android.mk
文件可知在5.0以上的手机上编译的是app_main2.cpp
这里选取Android 7.1.1_r6
的app_main.cpp
文件与app_main2.cpp
做对比
从main
函数看起
从文件的对比可以发现,app_main2.cpp
中多出了对于 Xposed::handleOptions
的调用,而这个函数主要是对--xposedversion
的处理以及是否测试模式的启用,真正运行时无需理会
在main
函数的最后,增加了对Xposed
的加载与runtimeStart
函数的调用。
在initialize
函数最终返回XposedBridge.jar
是否成功加入环境变量的标志和XposedInstaller
是否成功运行的标志
首先对xposed
这个结构体变量进行赋值
然后在logcat
中通过printStartupMarker
函数和printRomInfo
函数打印一些信息
然后以下函数通过启动XposedInstaller
和相应Xposed
服务
最后如果XPosed
能够正常加载,那么就通过函数addJarToClasspath
将XposedBridge.jar
文件加入环境变量
这里如果想要禁用Xposed
只需要在XposedInstaller
的私有目录下创建一个conf/disabled
文件即可。
如果成功加载则调用runtimeStart
函数对调用AndroidRuntime::start()
函数对de.robv.android.xposed.XposedBridge
类进行启动
而AndroidRuntime::start()
函数经过观察发现实际上完成了几个工作
1.startVm
函数,启动虚拟机
2.反射调用传入的类的main
函数作为虚拟机的入口点
而Xposed
就是通过替换这个className
来达到加载自己的art
虚拟机的效果。这样就成功注入了XposedBridge.jar
文件,达到任意APP
在加载时内存空间中总会有XposedBridge.jar
。
而XposedBridge.jar
文件的main
函数如下,其实就是一个对SELinux
的处理以及对Xposed
模块的加载。
对模块的加载是通过对XposedInstaller
私有目录下的conf/modules.list
文件进行读取,并分别是通过BOOTCLASSLOADER
对模块进行加载。
在最终的loadModule
函数中实际上是通过DexFile
的构造函数将模块加载并通过dexFile.loadClass
函数的方式对入口类进行加载。最终达到模块注入的效果
我们知道Xposed
hook
函数的方式是大致是通过以下模版完成的
其中最关键的执行hook
逻辑的函数实际上是XposedHelpers.findAndHookMethod()
函数
我们从这个函数跟踪起
观察这个函数分为两个部分
第一部分,用于获取回调的部分实际上就是获取参数的最后一个值,而用于寻找hook
对应函数的findMethodExact
函数,如下所示实际上就是通过反射获取,具体代码如下:
第二部分,执行hook
逻辑的函数其内容主要分为三个部分
1.检查要hook
的函数是否合法,必须同时满足三个条件:第一,是普通函数或者构造函数;第二,所在类不是接口Interface
;第三,函数不是abstract
抽象函数。
2.从缓存中确认函数未被hook
并将新函数加入缓存。
3.执行真实hook
逻辑,具体代码如下。可以发现真实执行hook
逻辑的函数交给了hookMethodNative
函数,而这个函数实际上是一个native
属性的函数。
而这个函数的C++
实现在libxposed_art.cpp
文件中。观察其函数发现,实际上就是将Java
层的Method
转换成了ArtMethod
,然后通过ArtMethod
类的EnableXposedHook
函数执行hook
。
而EnableXposedHook
函数的实现就在Xposed
修改的art
代码中了,打开android_art
工程,找到对应实现runtime/art_method.cc
文件。
具体实现分为几步
1.备份原先的Method
并添加标记kAccXposedOriginalMethod
2.创建一个备份方法对应的反射对象
3.将所有相关内容保存为一个结构体存储
4.准备工作,处理函数的JIT即时编译以及其他
5.设置被hook
函数的入口点(关键的hook逻辑)
6.恢复环境
这样就执行完成了函数的hook
当被hook
的函数执行时,我们直接从ArtMethod->Invoke
函数入手。
由于被hook
的函数属性是一个native
属性,观察函数中重要逻辑。由于在设置函数时已经设置过函数入口点为entry_point_from_quick_compiled_code_
不为false
则会进入art_quick_invoke_stub
或者art_quick_invoke_static_stub
跳板函数。
这里以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
这个函数的调用。
而ART_METHOD_QUICK_CODE_OFFSET_32
函数定义在runtime/asm_support.h
文件中实现如下
EntryPointFromQuickCompiledCodeOffset
函数是一个获取PtrSizedFields
结构体固定偏移的函数,这里就是获取的entry_point_from_quick_compiled_code_
变量的值。
这里由于Xposed
对这个函数进行了hook
,实际上就是获取的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
相关的重要代码列出来,具体如下:
观察发现实际上最重要的就是InvokeXposedHandleHookedMethod
函数的调用,其具体内容主要分成三部分。
1.处理参数信息
2.调用XposedBridge.handleHookedMethod()
函数
3.处理结果并返回
在这三个部分中最最重要的实际上是第二部分。
其中首先通过GetXposedHookInfo()
函数调用GetEntryPointFromJniPtrSize()
函数获取Xposed
在设置函数hook
时保存在entry_point_from_jni_
中的XposedHookInfo
对象信息。
然后拼接参数并通过CallStaticObjectMethodA()
函数JNI
调用XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,Object thisObject, Object[] args)
函数这样就再次回到Java
层中。
此时再次回到XposedBridge
的源码,找到对应handleHookedMethod
函数的实现会发现
2.然后通过invokeOriginalMethodNative
调用原函数
3.最后执行所有的afterHookedMethod
回调。
其中invokeOriginalMethodNative
函数则是通过保存的reflected_method
反射对象对原函数进行反射调用,也就是通过原art
函数InvokeMethod()
函数进行调用。
https://blog.csdn.net/weixin_47883636/article/details/109018440
https://egguncle.github.io/2018/02/04/xposed-art-hook-%E6%B5%85%E6%9E%90/
https://bbs.pediy.com/thread-257844.htm
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
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
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;
}
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;
}
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;
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;
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"
);
}
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"
);
}
if
(!determineXposedInstallerUidGid() || !xposed::service::startAll()) {
return
false;
}
if
(!determineXposedInstallerUidGid() || !xposed::service::startAll()) {
return
false;
}
/
*
*
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);
}
/
*
*
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);
}
/
/
XPOSED_CLASS_DOTS_ZYGOTE
=
de.robv.android.xposed.XposedBridge
runtimeStart(runtime, isXposedLoaded ?
"de.robv.android.xposed.XposedBridge"
:
"com.android.internal.os.ZygoteInit"
, args, zygote);
/
/
XPOSED_CLASS_DOTS_ZYGOTE
=
de.robv.android.xposed.XposedBridge
runtimeStart(runtime, isXposedLoaded ?
"de.robv.android.xposed.XposedBridge"
:
"com.android.internal.os.ZygoteInit"
, args, zygote);
/
*
start the virtual machine
*
/
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv
*
env;
if
(startVm(&mJavaVM, &env, zygote) !
=
0
) {
return
;
}
onVmCreated(env);
/
*
start the virtual machine
*
/
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv
*
env;
if
(startVm(&mJavaVM, &env, zygote) !
=
0
) {
return
;
}
onVmCreated(env);
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);
}
}
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);
}
}
/
*
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();
}
/
*
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();
}
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
;
}
...
}
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
;
}
...
}
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类型)
}
});
}
}
}
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类型)
}
});
}
}
}
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);
}
}
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);
}
}
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);
}
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) {
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-4-15 18:08
被Simp1er编辑
,原因: