抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

跟着unidbg的基本使用(九)

初探 Context

问题

在使用 Unidbg 模拟 JNI 环境时,Context 是一个关键的概念。许多 Android 应用中会调用 getAppContext() 来获取全局上下文。如果我们未正确处理 Context,可能会引发一系列问题。

1
2
3
4
[08:21:02 080]  WARN [com.github.unidbg.linux.ARM32SyscallHandler] (ARM32SyscallHandler:532) - handleInterrupt intno=2, NR=-1073744548, svcNumber=0x170, PC=unidbg@0xfffe0794, LR=RX@0x4004db2f[libnet_crypto.so]0x4db2f, syscall=null
java.lang.UnsupportedOperationException: com/izuiyou/common/base/BaseApplication->getAppContext()Landroid/content/Context;
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:503)
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticObjectMethodV(AbstractJni.java:437)

这是因为 BaseApplicationgetAppContext() 方法返回了一个 Context 类型的对象,我们未正确占位处理。

按照前面讨论的各类规范,应该占位为 Context 实例。

1
DvmObject<?> context = vm.resolveClass("android/content/Context").newObject(null);

但我们却占位为 AppController 实例。

1
DvmObject<?> context = vm.resolveClass("cn/xiaochuankeji/tieba/AppController").newObject(null);

但是这导致了进一步的问题,后续调用 Context 的方法时又报错:

1
2
3
4
java.lang.UnsupportedOperationException: android/content/Context->getClass()Ljava/lang/Class;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:262)
at com.github.unidbg.linux.android.dvm.DvmMethod.callObjectMethodV(DvmMethod.java:89)

DvmObjectgetObjectType()用于实现这个功能。

1
2
3
java.lang.UnsupportedOperationException: java/lang/Class->getSimpleName()Ljava/lang/String;
at com.github.unidbg.linux.android.dvm.AbstractJni.callObjectMethodV(AbstractJni.java:416)
at com.izuiyou.NetWorkNew.callObjectMethodV(NetWorkNew.java:62)

这是在获取类的简写名,比如android.content.Context简写名就是Context

DvmClassgetClassName可以获取类名,我们要截取末尾的名字。

1
2
3
java.lang.UnsupportedOperationException: cn/xiaochuankeji/tieba/common/debug/AppLogReporter->reportAppRuntime(Ljava/lang/String;Ljava/lang/String;)V
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticVoidMethodV(AbstractJni.java:697)
at com.github.unidbg.linux.android.dvm.AbstractJni.callStaticVoidMethodV(AbstractJni.java:692)

样本在调用reportAppRuntime函数,看这个名字是在上报运行情况,打印它的参数看看。

调用处理

在调用返回对象的方法时,例如 getClass()getSimpleName(),需要进一步扩展逻辑:

获取类名

样本可能会调用 Context 对象的 getClass() 方法,进而调用 getSimpleName() 获取类名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
switch (signature) {
case "android/content/Context->getClass()Ljava/lang/Class;": {
return dvmObject.getObjectType(); // 获取类类型
}
case "java/lang/Class->getSimpleName()Ljava/lang/String;": {
String className = ((DvmClass) dvmObject).getClassName();
String[] nameParts = className.split("/");
return new StringObject(vm, nameParts[nameParts.length - 1]); // 截取简写类名
}
}
return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}

打印上报信息

捕获并打印参数,便于调试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void callStaticVoidMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "cn/xiaochuankeji/tieba/common/debug/AppLogReporter->reportAppRuntime(Ljava/lang/String;Ljava/lang/String;)V": {
String arg1 = vaList.getObjectArg(0).getValue().toString();
String arg2 = vaList.getObjectArg(1).getValue().toString();
System.out.println("arg1: " + arg1);
System.out.println("arg2: " + arg2);
break;
}
}
super.callStaticVoidMethodV(vm, dvmClass, signature, vaList);
}

运行结果显示:

1
2
arg1: runtime
arg2: invalid application name: Context

参数 1 看起来是 runtime 运行时的错误拼写,参数 2 则是具体信息—— Application 名字不合规。

分析

context 是一个抽象类,实际返回的应该是其子类对象。通过工具(使用 r0tracer 做确认)对 getAppContext() 的返回值进行了动态追踪:

1
2
3
4
5
6
7
function main() {
Java.perform(function () {
console.Purple("r0tracer begin ... !")
hook("com.izuiyou.common.base.BaseApplication", "");
})
}
// frida -U -f cn.xiaochuankeji.tieba -l r0tracer.js --no-pause

运行

1
2
3
*** entered com.izuiyou.common.base.BaseApplication.getAppContext
retval: cn.xiaochuankeji.tieba.AppController@7ff0e => "<instance: android.content.Context, $className: cn.xiaochuankeji.tieba.AppController>"
*** exiting com.izuiyou.common.base.BaseApplication.getAppContext

确认返回值是 cn.xiaochuankeji.tieba.AppController 的实例,而不是抽象的 Context

这说明在模拟 getAppContext() 时,需要返回具体的子类实例。

因此修改前面的代码为:

1
2
3
4
5
6
7
8
9
@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
switch (signature) {
case "com/izuiyou/common/base/BaseApplication->getAppContext()Landroid/content/Context;": {
return vm.resolveClass("cn/xiaochuankeji/tieba/AppController").newObject(null);
}
}
return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
}

评论