stackplz hook art 记录

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
#include <jni.h>
#include <string>
#include <stdio.h>
#include <string.h>


void get_sign(const char *str);

extern "C" JNIEXPORT jstring JNICALL
Java_com_zj_my_1md5_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */, jstring url) {
std::string hello = env->GetStringUTFChars(url, 0);
hello = hello + "1";
get_sign(hello.c_str());

// return env->NewStringUTF(hello.c_str());
return env->NewStringUTF(hello.c_str());
}


void get_sign(const char *original) {
char buffer[50]; // 假设最大长度为 50
strncpy(buffer, original, sizeof(buffer));
strcat(buffer, " Additional ");
printf("Modified string: %s\n", buffer);

}

上面是我自己写的jni实现.

stackplz hook NewStringUTF 打印堆栈

1
./stackplz -n com.zj.my_md5 -l /apex/com.android.art/lib64/libart.so -w _ZN3art3JNIILb1EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh[str] --stack

结果如下, 可以看到调用堆栈已经打印出来了.

img_1.png

hook 一下GetStringUTFChars 看看入参?

先查看一下导出函数地址

1
./stackplz -n com.zj.my_md5 -l /apex/com.android.art/lib64/libart.so -w GetStringUTFChars --dumpret

执行结果如下

1
2
3
4
5
6
findBTFAssets btf_file=a12-5.10-arm64_min.btf
[*] save maps to maps_6024.txt
FindRet for GetStringUTFChars failed, sym:_ZN3art12_GLOBAL__N_18CheckJNI17GetStringUTFCharsEP7_JNIEnvP8_jstringPh.llvm.14181278422957417913 offse
t:0x44b610
FindRet for GetStringUTFChars -> [0x4b7528] sym:_ZN3art3JNIILb0EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh offset:0x4b6e60
FindRet for GetStringUTFChars -> [0x51acb8] sym:_ZN3art3JNIILb1EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh offset:0x51a5f0

执行结果解析

0x4b7528 是函数最后一条指令的地址. _ZN3art3JNIILb0EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh 是导出函数名, 0x4b6e60 是导出函数在so中的偏移

我们试试hook _ZN3art3JNIILb0EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh

1
./stackplz -n com.zj.my_md5 -l /apex/com.android.art/lib64/libart.so -w _ZN3art3JNIILb0EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh[int,str]

看看结果

1
2
3
4
5
6
idx:0 [/apex/com.android.art/lib64/libart.so] -> sym:_ZN3art3JNIILb0EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh off:0x0
ConfigMap{stackplz_pid=7690,thread_whitelist=0}
uid => whitelist:[10274];blacklist:[]
pid => whitelist:[6024];blacklist:[]
tid => whitelist:[];blacklist:[]
start 2 modules

发现并没有调用,说明很可能是调用的另一个函数, 试试另一个函数 _ZN3art3JNIILb1EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh

1
./stackplz -n com.zj.my_md5 -l /apex/com.android.art/lib64/libart.so -w _ZN3art3JNIILb1EE17GetStringUTFCharsEP7_JNIEnvP8_jstringPh[int,str]

结果如下

img_1.png

乱码 这是为什么呢? 原来 GetStringUTFChars 的入参是jstring, 出参是char * 所以我们要inline hook GetStringUTFChars 的最后一条指令的地址, 然后打印x0的值(arm64调用约定, 返回值一般放在x0寄存器)。

试试看, 代码如下

1
./stackplz -n com.zj.my_md5 -l /apex/com.android.art/lib64/libart.so -w 0x51acb8[str:x0]

执行结果

img_1.png

可以看到 hook成功

同理可以Hook NewStringUTF, NewStringUTF 的入参是 char *, 所以只需要hook 偏移或者导出函数地址就可以了, 不需要hook 函数的最后一条指令的地址

1
./stackplz -n com.zj.my_md5 -l /apex/com.android.art/lib64/libart.so -w _ZN3art3JNIILb1EE12NewStringUTFEP7_JNIEnvPKc[int,str] --regs

亲测可行, 并且不同的so, 可以调用的导出函数并不一样. 例如某音和我自己写的so, 虽然都是调用的 NewStringUTF, 但是对应的导出函数名是不一样的,建议两个都试一下

hook 某音 GetStringUTFChars

1
./stackplz -n com.ss.android.ugc.aweme -l /apex/com.android.art/lib64/libart.so -w 0x4b7528[str:x0]

效果如下

img_1.png

打印的无关信息过多, 试着过滤掉一部分. 只要/data开头的数据. -f 表示filter w:/data 表示 white 白名单. str.f0表示对第一个char* 应用f0过滤策略, 即 w:/data

1
./stackplz -n com.ss.android.ugc.aweme -l /apex/com.android.art/lib64/libart.so -w 0x4b7528[str.f0:x0] -f w:/data

看看结果

img_1.png

hook GetStaticMethodID

1
2
./stackplz -n com.ss.android.ugc.aweme -l /apex/com.android.art/lib64/libart.so -w GetStaticMethodID --dumpret
./stackplz -n com.ss.android.ugc.aweme -l /apex/com.android.art/lib64/libart.so -w 0x49e250[str:x2,str:x3]

结果如下 豁然开朗

img_1.png

硬件断点 hook 0x1e66c 打印调用堆栈

1
./stackplz -p `pidof com.zj.my_md5` --brk 0x715adae66c:x  --stack

0x715adae66c 是绝对地址. 通过so基址加偏移得到的.

结果如下

img_1.png