XhyEax 发表于 2021-10-7 18:24

使用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 13:00

无闻无问 发表于 2021-10-8 07:04
dalvik.system.VMStack.getThreadStackTrace(Native Method)
java.lang.Thread.getStackTrace(Thread.java ...

这个是堆栈打印,as的logcat能输出,另外xposed、frida都可以打印堆栈
MT的注入能打印指定类指定方法的输出,但是没办法输出堆栈调用
我记得酷安有个logcat的软件,你可以试试

无闻无问 发表于 2021-10-8 07:04

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注入,没这种函数执行流程…

vipiu 发表于 2021-10-7 18:29

厉害了,感谢分享!

delta314 发表于 2021-10-7 18:54

电信营业厅也打不开,xp冲突,不知道是不是手机问题

delta314 发表于 2021-10-7 18:59

确实是好帖子,不过对于小白来说还有点难啊,目前所知,好些个银行类的装了xp也是进不去

木子小每文 发表于 2021-10-7 20:16

大老牛逼了~~~~~~

咔c君 发表于 2021-10-7 22:01

不错学习了

asdfliuchen 发表于 2021-10-7 22:29

大神,嘀嗒出行也检测了,能不能帮忙出个防嘀嗒检测的工具。

goda 发表于 2021-10-7 23:32

谢谢分享,学习了

凛冬已至 发表于 2021-10-8 00:41

大佬能给成品吗
页: [1] 2 3 4
查看完整版本: 使用Xposed绕过某加固的Xposed堆栈检测