初探 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 )
这是因为 BaseApplication
的 getAppContext()
方法返回了一个 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 )
DvmObject
的getObjectType()
用于实现这个功能。
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
。
DvmClass
的getClassName
可以获取类名,我们要截取末尾的名字。
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" , "" ); }) }
运行
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); }