使用Xposed绕过某加固的Xposed堆栈检测

1,902 阅读3分钟

概述

更新某营业厅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:     r0  00000000  r1  ffb273e6  r2  0000078c  r3  b6a287de
A/DEBUG:     r4  c6049d88  r5  00000000  r6  00000000  r7  c6049d88
A/DEBUG:     r8  b6a287de  r9  0000078c  r10 f72255a0  r11 ffb284d0
A/DEBUG:     ip  f4c7d0a4  sp  00000000  lr  00000000  pc  0000078c
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 ([stack]):
    ffb273c4 00000305 00000017 000002f1 00000581  ................
    ffb273d4 f4c8125c 00000000 00000000 736f7078  \...........xpos
    ffb273e4 1d006465 00000000 43746567 7373616c  ed......getClass
    ffb273f4 656d614e 0000e400 72727563 54746e65  Name....currentT
    ffb27404 61657268 00f00064 53746567 6b636174  hread...getStack
    ffb27414 63617254 00000065 6176616a 6e616c2f  Trace...java/lan
    ffb27424 68542f67 64616572 0000f200 6a4c2928  g/Thread....()Lj
    ffb27434 2f617661 676e616c 7268542f 3b646165  ava/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 4c5b2928  Element.....()[L
    ffb27484 6176616a 6e616c2f 74532f67 546b6361  java/lang/StackT
    ffb27494 65636172 6d656c45 3b746e65 00005100  raceElement;.Q..
    ffb274a4 14765802 00000000 c6049d88 f4c8125c  .Xv.........\...
    ffb274b4 00000000 f72255a0 c6001aa5 31362f64  .....U".....d/61

首先通过java.lang.Thread.currentThread获得当前线程对象,然后调用getStackTrace获得StackTraceElement数组,遍历该数组,调用getClassName,判断是否包含xposed

定位

XposedHookThread.getStackTrace,打印调用栈(函数返回值):

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);
    }
});

(如果要HookStackTraceElement.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

@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>加载so,现在改到attachBaseContext了)

由so的加载流程可知,检测函数的调用要么是在JNI_OnLoad,要么是在.init.init_array

使用IDA调试,发现该加固是在JNI_OnLoad中调用检测函数,可通过PatchHook绕过(并非本文重点,就不详细介绍了)

由于最终还是要调用Java层的getClassName方法,考虑到通用性,决定编写Xposed模块,绕过堆栈检测

绕过

XposedHookStackTraceElement.getClassName方法,判断是否包含xposed,如果包含则替换返回值为android.os.Handler

注意:需要在DexHelper加载前Hook

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);
    }
});