利用AndroidNativeEmu完成多层jni调用的模拟
2022-9-13 18:7:41 Author: 看雪学苑(查看原文) 阅读量:9 收藏


本文为看雪论坛优秀文章

看雪论坛作者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们

球分享

球点赞

球在看

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


文章来源: http://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458469186&idx=1&sn=638f144efc64341100fcda3989241b2c&chksm=b18e73c886f9fade876f212d26b818af1a054da7b582477595eea5a1a73ccee4144acdd9697e#rd
如有侵权请联系:admin#unsafe.sh