查看原文
其他

利用AndroidNativeEmu完成多层jni调用的模拟

飞翔的猫咪 看雪学苑 2022-09-22


本文为看雪论坛优秀文章

看雪论坛作者ID:飞翔的猫咪


题目出自看雪高研班2021年12月份作业第二题:已知该apk中有jni函数jnicheck,当接收参数为“XUe”时,返回true,请使用AndroidNativeEmu实现对该jni函数的完全模拟调用(不需要对crypt2函数进行逆向分析)




分析


这道题主要考察的是对多层jni调用的模拟,所谓多层jni调用指native java函数的实现c/c++函数中又反过来调用java函数,而这个被调用的java函数仍然是一个native函数。而对于AndroidNativeEmu和UniDbg目前(做题的时候)都不支持这种多层调用,UniDbg更新应该算是比较频繁的,有可能后边会支持。

不能直接支持,只能用取巧的方式来实现。我这里用了两个程序来实现,一个程序通过subprocess来调用另外一个程序,获取这个程序的结果进一步利用。这种实现方式是有缺陷的,如果模拟的程序函数对全局的变量产生了副作用,或者有共同的依赖,那么模拟可能会失败,在这种情况下需要做特殊的处理,单独处理同步共享的全局变量。

通过这个题目的练习可以学得AndroidNativeEmu和UniDbg不支持的地方,或者说有待改进的点。工具不支持,必要时必须自己写代码扩展功能。




解答



单独为crypt2函数写一个源文件,crypt2_impl.py文件内容为:

import loggingimport sysfrom androidemu.emulator import Emulatorfrom androidemu.java.helpers.native_method import native_methodfrom androidemu.utils import memory_helpers @native_methoddef sprintf(uc, str, format, args): str_utf8 = memory_helpers.read_utf8(uc, str) format_utf8 = memory_helpers.read_utf8(uc, format) args_utf8 = memory_helpers.read_utf8(uc, args) logger.info("str_utf8:{}, format_utf8:{}, args_utf8:{}".format(str_utf8, format_utf8, args_utf8)) result_string = format_utf8 % args_utf8 memory_helpers.write_utf8(uc, str, result_string) return len(result_string) @native_methoddef malloc(uc, size): return emulator.memory_manager.allocate(size) @native_methoddef strcmp(uc, s1, s2): s1_utf8 = memory_helpers.read_utf8(uc, s1) s2_utf8 = memory_helpers.read_utf8(uc, s2) if s1_utf8 == s2_utf8: ret = 0 elif s1_utf8 < s2_utf8: ret = -1 else: ret = 1 logger.info("s1:{},s2:{},ret:{}".format(s1_utf8, s2_utf8, ret)) return ret # Configure logginglogging.basicConfig( stream=sys.stderr, level=logging.DEBUG, format="%(asctime)s %(levelname)7s %(name)34s | %(message)s") logger = logging.getLogger(__name__) so_file = "my_binaries/libnative-lib.so"libc_file = "example_binaries/32/libc.so" # Initialize emulatoremulator = Emulator(vfp_inst_set=True) emulator.modules.add_symbol_hook('sprintf', emulator.hooker.write_function(sprintf) + 1)emulator.modules.add_symbol_hook('malloc', emulator.hooker.write_function(malloc) + 1)emulator.modules.add_symbol_hook('strcmp', emulator.hooker.write_function(strcmp) + 1) libc_module = emulator.load_library(libc_file, do_init=False)lib_module = emulator.load_library(so_file, do_init=False) # call .init.procemulator.call_native(lib_module.base + 0x320B8 + 1) for funcptr in lib_module.init_array: logger.info("init_array -->" + str(hex(funcptr))) emulator.call_native(funcptr) result = emulator.call_symbol(lib_module, "Java_com_kanxue_crackme_MainActivity_crypt2", emulator.java_vm.jni_env.address_ptr, 0, sys.argv[1], is_return_jobject=False)print(result)

main.py文件内容:

import loggingimport sysimport unicornimport capstonefrom androidemu.emulator import Emulatorfrom androidemu.utils import memory_helpersfrom androidemu.java.java_class_def import JavaClassDeffrom androidemu.java.java_method_def import java_method_deffrom androidemu.java.helpers.native_method import native_methodfrom subprocess import check_output cs = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB)cs.detail = True class MainActivity(metaclass=JavaClassDef, jvm_name='com/kanxue/crackme/MainActivity'): def __init__(self): pass @java_method_def(name='crypt2', args_list=['jstring'], signature='(Ljava/lang/String;)Z', native=False) def crypt2(self, *args, **kwargs): arg = args[0].value print("crypt2 args : {}".format(arg)) ret = check_output(["python","crypt2_impl.py",arg]) ret = int(ret.decode('utf-8').strip()) print("crypt2 result : {}".format(ret)) return ret @native_methoddef sprintf(uc, str, format, args): str_utf8 = memory_helpers.read_utf8(uc, str) format_utf8 = memory_helpers.read_utf8(uc, format) args_utf8 = memory_helpers.read_utf8(uc, args) print("str_utf8:{}, format_utf8:{}, args_utf8:{}".format(str_utf8, format_utf8, args_utf8)) result_string = format_utf8 % args_utf8 memory_helpers.write_utf8(uc, str, result_string) return len(result_string) @native_methoddef malloc(uc, size): return emulator.memory_manager.allocate(size) @native_methoddef strcmp(uc, s1, s2): s1_utf8 = memory_helpers.read_utf8(uc, s1) s2_utf8 = memory_helpers.read_utf8(uc, s2) if s1_utf8 == s2_utf8: return 0 print("s1:{},s2:{}".format(s1_utf8, s2_utf8)) return len(s1_utf8) - len(s2_utf8) # Configure logginglogging.basicConfig( stream=sys.stdout, level=logging.DEBUG, format="%(asctime)s %(levelname)7s %(name)34s | %(message)s") logger = logging.getLogger(__name__) def mem_read_unmapped(uc, type, address, size, value, user_data): print("mem_read_unmapped type:{},address:{},size:{},value:{},pc:{}".format(type, address, size, value, hex(uc.reg_read( unicorn.arm_const.UC_ARM_REG_PC) - libc_module.base))) def hook_code(uc, address, size, user_data): if lib_module.base < address < (lib_module.base + lib_module.size): lib_name = "libnative-lib.so" pc = uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC) - lib_module.base elif libc_module.base < address < (libc_module.base + libc_module.size): lib_name = "libc.so" pc = uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC) - libc_module.base else: lib_name = "lib_Unknown" pc = uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC) CODE = uc.mem_read(uc.reg_read(unicorn.arm_const.UC_ARM_REG_PC), size) dis_code = None for i in cs.disasm(CODE, 0, len(CODE)): dis_code = i.mnemonic + " " + i.op_str print(lib_name + " >>> Tracing instruction at {}, instruction size = {} , pc:{}, bytes:{}".format(pc, size, hex(pc), dis_code)) def UC_HOOK_MEM_WRITE(uc, type, address, size, value, userdata): print("address:" + str(hex(address)) + ",value:" + str(value)) so_file = "my_binaries/libnative-lib.so"libc_file = "example_binaries/32/libc.so" # Initialize emulatoremulator = Emulator(vfp_inst_set=True) emulator.modules.add_symbol_hook('sprintf', emulator.hooker.write_function(sprintf) + 1)emulator.modules.add_symbol_hook('malloc', emulator.hooker.write_function(malloc) + 1)emulator.modules.add_symbol_hook('strcmp', emulator.hooker.write_function(strcmp) + 1) libc_module = emulator.load_library(libc_file, do_init=False)lib_module = emulator.load_library(so_file, do_init=False) # emulator.mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) # call .init.procemulator.call_native(lib_module.base + 0x320B8 + 1) for funcptr in lib_module.init_array: logger.info("init_array -->" + str(hex(funcptr))) emulator.call_native(funcptr) emulator.java_classloader.add_class(MainActivity) result = emulator.call_symbol(lib_module, "Java_com_kanxue_crackme_MainActivity_jnicheck", emulator.java_vm.jni_env.address_ptr, 0, "XUe", is_return_jobject=False)print("jnicheck result : {}".format(result))

当输入值为'XUe'时打印出来的结果为:

2022-01-14 14:48:41,303 DEBUG androidemu.internal.modules | Loading module 'example_binaries/32/libc.so'.2022-01-14 14:48:41,311 DEBUG androidemu.internal.modules | => Base address: 0xa00000002022-01-14 14:48:41,330 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:48:41,330 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:48:42,125 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:48:42,125 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:48:42,529 DEBUG androidemu.internal.modules | Loading module 'my_binaries/libnative-lib.so'.2022-01-14 14:48:42,532 DEBUG androidemu.internal.modules | => Base address: 0xa009b0002022-01-14 14:48:42,566 INFO __main__ | init_array -->0xa00cd1e12022-01-14 14:48:42,568 DEBUG androidemu.java.jni_env | JNIEnv->GetStringUtfChars(1, 0) was called2022-01-14 14:48:42,568 DEBUG androidemu.java.jni_env | => XUestr_utf8:, format_utf8:%s666, args_utf8:XUe2022-01-14 14:48:42,572 DEBUG androidemu.java.jni_env | JNIEnv->NewStringUtf(XUe666) was called2022-01-14 14:48:42,573 DEBUG androidemu.java.jni_env | JNIEnv->GetStringUtfChars(2, 0) was called2022-01-14 14:48:42,573 DEBUG androidemu.java.jni_env | => XUe6662022-01-14 14:48:42,575 DEBUG androidemu.java.jni_env | JNIEnv->FindClass(com/kanxue/crackme/MainActivity) was called2022-01-14 14:48:42,575 DEBUG androidemu.java.jni_env | JNIEnv->GetStaticMethodId(3, crypt2, (Ljava/lang/String;)Z) was called2022-01-14 14:48:42,576 DEBUG androidemu.java.jni_env | JNIEnv->CallStaticBooleanMethodV(com/kanxue/crackme/MainActivity, crypt2 <(Ljava/lang/String;)Z>, 0x100ffea4) was calledcrypt2 args : XUe6662022-01-14 14:48:42,856 DEBUG androidemu.internal.modules | Loading module 'example_binaries/32/libc.so'.2022-01-14 14:48:42,864 DEBUG androidemu.internal.modules | => Base address: 0xa00000002022-01-14 14:48:42,883 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:48:42,883 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:48:43,678 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:48:43,679 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:48:44,093 DEBUG androidemu.internal.modules | Loading module 'my_binaries/libnative-lib.so'.2022-01-14 14:48:44,096 DEBUG androidemu.internal.modules | => Base address: 0xa009b0002022-01-14 14:48:44,131 INFO __main__ | init_array -->0xa00cd1e12022-01-14 14:48:44,133 DEBUG androidemu.java.jni_env | JNIEnv->GetStringUtfChars(1, 0) was called2022-01-14 14:48:44,133 DEBUG androidemu.java.jni_env | => XUe6662022-01-14 14:48:44,135 INFO __main__ | str_utf8:, format_utf8:%s, args_utf8:XUe6662022-01-14 14:48:44,143 INFO __main__ | s1:WFVlNjY2,s2:WFVlNjY2,ret:0crypt2 result : 1jnicheck result : 1 Process finished with exit code 0

当输入值不为'XUe'时打印出来的结果为:

2022-01-14 14:49:34,282 DEBUG androidemu.internal.modules | Loading module 'example_binaries/32/libc.so'.2022-01-14 14:49:34,289 DEBUG androidemu.internal.modules | => Base address: 0xa00000002022-01-14 14:49:34,308 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:49:34,308 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:49:35,089 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:49:35,090 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:49:35,493 DEBUG androidemu.internal.modules | Loading module 'my_binaries/libnative-lib.so'.2022-01-14 14:49:35,496 DEBUG androidemu.internal.modules | => Base address: 0xa009b0002022-01-14 14:49:35,530 INFO __main__ | init_array -->0xa00cd1e12022-01-14 14:49:35,531 DEBUG androidemu.java.jni_env | JNIEnv->GetStringUtfChars(1, 0) was called2022-01-14 14:49:35,532 DEBUG androidemu.java.jni_env | => AAAstr_utf8:, format_utf8:%s666, args_utf8:AAA2022-01-14 14:49:35,536 DEBUG androidemu.java.jni_env | JNIEnv->NewStringUtf(AAA666) was called2022-01-14 14:49:35,537 DEBUG androidemu.java.jni_env | JNIEnv->GetStringUtfChars(2, 0) was called2022-01-14 14:49:35,537 DEBUG androidemu.java.jni_env | => AAA6662022-01-14 14:49:35,538 DEBUG androidemu.java.jni_env | JNIEnv->FindClass(com/kanxue/crackme/MainActivity) was called2022-01-14 14:49:35,539 DEBUG androidemu.java.jni_env | JNIEnv->GetStaticMethodId(3, crypt2, (Ljava/lang/String;)Z) was called2022-01-14 14:49:35,540 DEBUG androidemu.java.jni_env | JNIEnv->CallStaticBooleanMethodV(com/kanxue/crackme/MainActivity, crypt2 <(Ljava/lang/String;)Z>, 0x100ffea4) was calledcrypt2 args : AAA6662022-01-14 14:49:35,822 DEBUG androidemu.internal.modules | Loading module 'example_binaries/32/libc.so'.2022-01-14 14:49:35,829 DEBUG androidemu.internal.modules | => Base address: 0xa00000002022-01-14 14:49:35,849 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:49:35,849 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:49:36,647 ERROR androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version2022-01-14 14:49:36,648 ERROR androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx2022-01-14 14:49:37,059 DEBUG androidemu.internal.modules | Loading module 'my_binaries/libnative-lib.so'.2022-01-14 14:49:37,062 DEBUG androidemu.internal.modules | => Base address: 0xa009b0002022-01-14 14:49:37,096 INFO __main__ | init_array -->0xa00cd1e12022-01-14 14:49:37,098 DEBUG androidemu.java.jni_env | JNIEnv->GetStringUtfChars(1, 0) was called2022-01-14 14:49:37,098 DEBUG androidemu.java.jni_env | => AAA6662022-01-14 14:49:37,100 INFO __main__ | str_utf8:, format_utf8:%s, args_utf8:AAA6662022-01-14 14:49:37,108 INFO __main__ | s1:WFVlNjY2,s2:QUFBNjY2,ret:1crypt2 result : 0jnicheck result : 0 Process finished with exit code 0

模拟成功。



看雪ID:飞翔的猫咪

https://bbs.pediy.com/user-home-607812.htm

*本文由看雪论坛 飞翔的猫咪 原创,转载请注明来自看雪社区



# 往期推荐

1.因优化而导致的溢出与CVE-2020-16040

2.LLVM PASS PWN 总结

3.win10 1909逆向之APIC中断和实验

4.EMET下EAF机制分析以及模拟实现

5.sql注入学习分享

6.V8 Array.prototype.concat函数出现过的issues和他们的POC们






球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存