使用Xposed绕过某加固的Xposed堆栈检测
## 概述更新某营业厅app(版本号`7.4.0`)后,发现加载`Xposed`模块会导致app crash。
通过分析`Tombstone`日志找到检测点,编写`Xposed`模块绕过堆栈检测。
## 日志分析
过滤`Error`级别的日志,得到以下信息:
```
A/libc: Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x78c in tid 20120 (c10086.activity), pid 20120 (c10086.activity)
A/DEBUG: pid: 20120, tid: 20120, name: c10086.activity>>> com.greenpoint.android.mc10086.activity <<<
A/DEBUG: uid: 10480
A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x78c
A/DEBUG: Cause: null pointer dereference
A/DEBUG: r000000000r1ffb273e6r20000078cr3b6a287de
A/DEBUG: r4c6049d88r500000000r600000000r7c6049d88
A/DEBUG: r8b6a287der90000078cr10 f72255a0r11 ffb284d0
A/DEBUG: ipf4c7d0a4sp00000000lr00000000pc0000078c
A/DEBUG: backtrace:
A/DEBUG: #00 pc 0000078c<unknown>
A/DEBUG: #01 pc 00000000<unknown>
E/tombstoned: Tombstone written to: /data/tombstones/tombstone_18
```
导出`/data/tombstones/tombstone_18`到电脑,查看内存(方法栈):
```
memory near r1 ():
ffb273c4 00000305 00000017 000002f1 00000581................
ffb273d4 f4c8125c 00000000 00000000 736f7078\...........xpos
ffb273e4 1d006465 00000000 43746567 7373616ced......getClass
ffb273f4 656d614e 0000e400 72727563 54746e65Name....currentT
ffb27404 61657268 00f00064 53746567 6b636174hread...getStack
ffb27414 63617254 00000065 6176616a 6e616c2fTrace...java/lan
ffb27424 68542f67 64616572 0000f200 6a4c2928g/Thread....()Lj
ffb27434 2f617661 676e616c 7268542f 3b646165ava/lang/Thread;
ffb27444 00009d00 6a4c2928 2f617661 676e616c....()Ljava/lang
ffb27454 7274532f 3b676e69 00004100 6176616a/String;.A..java
ffb27464 6e616c2f 74532f67 546b6361 65636172/lang/StackTrace
ffb27474 6d656c45 00746e65 000000ef 4c5b2928Element.....()[L
ffb27484 6176616a 6e616c2f 74532f67 546b6361java/lang/StackT
ffb27494 65636172 6d656c45 3b746e65 00005100raceElement;.Q..
ffb274a4 14765802 00000000 c6049d88 f4c8125c.Xv.........\...
ffb274b4 00000000 f72255a0 c6001aa5 31362f64.....U".....d/61
```
首先通过`java.lang.Thread.currentThread`获得当前线程对象,然后调用`getStackTrace`获得`StackTraceElement`数组,遍历该数组,调用`getClassName`,判断是否包含`xposed`
## 定位
`Xposed`Hook`Thread.getStackTrace`,打印调用栈(函数返回值):
```java
findAndHookMethod(Thread.class, "getStackTrace", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
StackTraceElement[] st = (StackTraceElement[]) param.getResult();
String sts = "";
for (StackTraceElement ste : st) {
sts += ste.toString() + "\n";
}
Log.e("StackTrace", sts);
super.afterHookedMethod(param);
}
});
```
(如果要Hook`StackTraceElement.getClassName`,打印返回值即可)
日志如下:
```
dalvik.system.VMStack.getThreadStackTrace(Native Method)
java.lang.Thread.getStackTrace(Thread.java:1736)
java.lang.reflect.Method.invoke(Native Method)
de.robv.android.xposed.LspHooker.handleHookedMethod(Unknown Source:107)
LspHooker_.getStackTrace(Unknown Source:8)
java.lang.Runtime.nativeLoad(Native Method)
java.lang.Runtime.nativeLoad(Runtime.java:1131)
java.lang.Runtime.loadLibrary0(Runtime.java:1085)
java.lang.Runtime.loadLibrary0(Runtime.java:1008)
java.lang.System.loadLibrary(System.java:1664)
com.secneo.apkwrapper.AW.attachBaseContext(Unknown Source:17)
```
可以看到调用者是`java.lang.Runtime.nativeLoad`,即so加载时调用(在`Native`层通过`JNI`调用`getStackTrace`)
## 分析
使用`JEB`反编译`classes.dex`,定位到`com.secneo.apkwrapper.AW.attachBaseContext`:
```java
@Override// android.content.ContextWrapper
protected void attachBaseContext(Context context) {
// ...
AW.mC = context;
System.loadLibrary("DexHelper");
H.i();
AW.ᵢ = this;
super.attachBaseContext(context);
// ...
}
```
可以看到加载的是`libDexHelper.so`(旧版本是在`<clinit>`加载,现在改到`attachBaseContext`了)
由so的加载流程可知,检测函数的调用要么是在`JNI_OnLoad`,要么是在`.init`或`.init_array`段
使用`IDA`动态调试,发现该加固是在`JNI_OnLoad`中调用检测函数(并非本文重点,就不详细介绍了)
由于最终还是要调用`Java`层的`getClassName`方法,考虑到通用性,决定编写`Xposed`模块,绕过堆栈检测
## 绕过
`Xposed`Hook`StackTraceElement.getClassName`方法,判断是否包含`xposed`,如果包含则替换返回值为`android.os.Handler`
注意:需要在`DexHelper`加载前Hook
```java
findAndHookMethod(StackTraceElement.class, "getClassName", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String className = (String) param.getResult();
if (className != null && className.contains("xposed")) {
param.setResult("android.os.Handler");
}
super.afterHookedMethod(param);
}
});
``` 无闻无问 发表于 2021-10-8 07:04
dalvik.system.VMStack.getThreadStackTrace(Native Method)
java.lang.Thread.getStackTrace(Thread.java ...
这个是堆栈打印,as的logcat能输出,另外xposed、frida都可以打印堆栈
MT的注入能打印指定类指定方法的输出,但是没办法输出堆栈调用
我记得酷安有个logcat的软件,你可以试试 dalvik.system.VMStack.getThreadStackTrace(Native Method)
java.lang.Thread.getStackTrace(Thread.java:1736)
java.lang.reflect.Method.invoke(Native Method)
de.robv.android.xposed.LspHooker.handleHookedMethod(Unknown Source:107)
LspHooker_.getStackTrace(Unknown Source:8)
java.lang.Runtime.nativeLoad(Native Method)
java.lang.Runtime.nativeLoad(Runtime.java:1131)
java.lang.Runtime.loadLibrary0(Runtime.java:1085)
java.lang.Runtime.loadLibrary0(Runtime.java:1008)
java.lang.System.loadLibrary(System.java:1664)
com.secneo.apkwrapper.AW.attachBaseContext(Unknown Source:17)
大神,这些日志是怎么打印的,工具还是什么?求指教,求分享!
我用mt注入,没这种函数执行流程… 厉害了,感谢分享! 电信营业厅也打不开,xp冲突,不知道是不是手机问题 确实是好帖子,不过对于小白来说还有点难啊,目前所知,好些个银行类的装了xp也是进不去 大老牛逼了~~~~~~ 不错学习了 大神,嘀嗒出行也检测了,能不能帮忙出个防嘀嗒检测的工具。 谢谢分享,学习了 大佬能给成品吗