写点什么

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

作者:Java-fenn
  • 2022 年 9 月 14 日
    湖南
  • 本文字数:7111 字

    阅读完需:约 23 分钟

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


题目出自看雪高研班 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_utf8memory_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 = 0elif s1_utf8 < s2_utf8:    ret = -1else:    ret = 1logger.info("s1:{},s2:{},ret:{}".format(s1_utf8, s2_utf8, ret))return ret
复制代码

Configure logging

logging.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 emulator

emulator = 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.proc

emulator.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_utf8memory_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 logging

logging.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.baseelif 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.baseelse: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 = Nonefor i in cs.disasm(CODE, 0, len(CODE)):    dis_code = i.mnemonic + " " + i.op_strprint(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 emulator

emulator = 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.proc

emulator.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 模拟成功。

用户头像

Java-fenn

关注

需要Java资料或者咨询可加我v : Jimbye 2022.08.16 加入

还未添加个人简介

评论

发布
暂无评论
利用AndroidNativeEmu完成多层jni调用的模拟_Java_Java-fenn_InfoQ写作社区