Last updated on May 11, 2024 pm
记录Unicorn学习笔记
安装 Capstone 我的系统是Mac M1
brew install capstone
brew install unicorn
pip install capstone
然后还需要将brew安装的capstone里面的一个库加到环境变量,即添加如下语句至~/.zhsrc
或者~/bash_profile
export DYLD_LIBRARY_PATH=/opt/homebrew/Cellar/capstone/5.0.1/lib:$DYLD_LIBRARY_PATH
随后就能在Python中使用capstone了
Keystone keystone安装稍微麻烦一点
然后需要从源码构建得到一个共享库
1 2 3 4 5 git clone https://github.com/keystone-engine/keystone.gitmkdir buildcd build cmake -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release -G Ninja .. ninja
然后会在/build/llvm/lib
目录下得到一个libkeystone.dylib
将这个复制去/usr/local/lib/
目录,因为keystone.py
会尝试去这个目录找
1 sudo cp libkeystone.dylib /usr/local/lib
然后就可以在python中使用keystone
了
JavaVM、JNIEnv JavaVM
JavaVM是虚拟机在JNI层的代表,一个进程只有一个JavaVM,所有的线程共用一个JavaVM。
JNIInvokeInterface_ 结构封装和JVM相关的功能函数,如销毁JVM,获得当前线程的Java执行环境
在C和C++中JavaVM的定义有所不同,在C中JavaVM是JNIInvokeInterface_类型指针,而在C++中又对JNIInvokeInterface_进行了一次封装
有两种方式可以获得JavavM
在JNI_OnLoad中作为参数获得,如下
1 JNIEXPORT jint JNI_OnLOad (JavaVM *vm,void * reserved)
该函数由ART负责自动化查找和传入参数进行调用
通过JNIEnv的GetJavaVM函数获取
1 2 JavaVM* thisjvm = nullptr; env.GetJavaVM(&thisjvm)
JNIEnv
JNIEnv表示Java调用native语言的环境,是一个封装了大量JNI方法的指针
JNIEnv只在创建它的线程生效,不能跨线程传递,不同线程的JNIEnv彼此独立
native环境中创建的线程,如果需要访问JNI,必须要调用AttachCurrentThread关联,并使用DetachCurrentThread解除链接
JavaVM和JNIEnv在C语言环境和C++环境下调用是有所区别的,主要表现在
C: (*env)->NewStringITF(env,”HELLO WORLD”);
C++:env->NewStringUTF(“HELLO WORLD”);
静态注册与动态注册 对于任意一个JNI函数来说,在该函数被调用前,必须要完成java函数与so中地址的绑定。这个绑定过程可以是被动的,即由Dalvik/ART虚拟机在调用前查找并完成地址的绑定;也可以是主动的,即由app自己完成地址的绑定。
静态注册
对应的函数名:Java_包名_类名_方法名
其中使用下划线将每部分分开,如果名称中本来就包含下划线,将使用下划线加数字替换
优点:简单明了,语义清晰
缺点:不够安全
必须遵循注册规则从而导致名字过长;由于要保留符号,很容易使用IDA等直接定位地址
动态注册
定义:通过RegisterNatives方法手动完成native方法和so中的方法绑定,这样虚拟机就可以通过这个函数映射关系直接找到相应的方法。
示例
假设有两个native方法如下
1 2 public native int add (int a,int b) ;public native String myStr (String a) ;
在cpp中这样定义
1 2 3 4 5 6 7 8 9 int add (JNIEnv *env, jobject thiz,jint a, jint b) { int sum = a+b; return sum; }jstring myStr (JNIEnv *env,jobject thiz,jstring str) { std::string my_str_ = "Hello from my_str" ; return env->NewStringUTF (my_str_.c_str ()); }
在JNI_OnLoad
中这样注册
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM *vm,void * reserved) { JNIEnv *env = nullptr ; if (vm->GetEnv ((void **)&env, JNI_VERSION_1_6) != JNI_OK) { return -1 ; } JNINativeMethod methods[] = { {"add" , "(II)I" , (void *)add}, {"myStr" , "(Ljava/lang/String;)Ljava/lang/String;" ,(void *)myStr} }; jclass clazz = env->FindClass ("com/yring/unicorn/MainActivity" ); if (clazz == NULL ) { return -1 ; } if (env->RegisterNatives (clazz, methods, sizeof (methods) / sizeof (methods[0 ])) < 0 ) { return -1 ; } return JNI_VERSION_1_6; }
类型签名可以空着,让AS自动提示更正
Unicorn 放一个学习脚本
基本流程就是 地址映射 -> 写入汇编指令 -> 添加回调 -> 模拟执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 import unicornimport capstoneimport binasciidef printArm32Regs (mu ): for i in range (66 , 78 ): print ("R%d,value:%x" % (i - 66 , mu.reg_read(i)))def hook_code (mu: unicorn.Uc, address, size, user_data ): code = mu.mem_read(address, size) CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(code, 0 , len (code)): print ("[addr:%x]:%s %s\n" % (address, i.mnemonic, i.op_str)) return def hook_mem (mu: unicorn.Uc, type , address, size, value, user_data ): if type == unicorn.UC_MEM_WRITE: print (f"write addr:{hex (address)} ,size:{size} ,value:{hex (value)} " ) if type == unicorn.UC_MEM_READ: print (f"read addr:{hex (address)} ,{size:} ,value:{hex (value)} " ) print (f"hook_mem type:{type } ,addr:{address} ,size:{size} ,value:{hex (value)} " ) return def hook_mem_write_unmapped (mu: unicorn.Uc, type , address, size, value, user_data ): if type == unicorn.UC_MEM_WRITE_UNMAPPED: print ( f"UC_MEM_WRITE_UNMAPPED addr:{hex (address)} ,size:{size} ,value:{hex (value)} " ) mu.mem_map(0x0 , 0x1000 ) print (f"hook_mem type:{type } ,addr:{address} ,size:{size} ,value:{hex (value)} " ) return True def hook_syscall (mu: unicorn.Uc, intno, user_data ): print ("------syscall------" ) printArm32Regs(mu) print ("------syscall------" ) return def hook_block (mu: unicorn.Uc, address, size, user_data ): code = mu.mem_read(address, size) CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(code, 0 , len (code)): print ("[addr:%x]:%s %s\n" % (address, i.mnemonic, i.op_str)) print ("------block------" ) printArm32Regs(mu) print ("------block------" ) return def testthumb (): CODE = b"\x0a\x46\x03\x46\x04\x92\x00\xdf" CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) ADDRESS = 0x1000 SIZE = 1024 mu.mem_map(ADDRESS, SIZE) mu.mem_write(ADDRESS, CODE) bytes = mu.mem_read(ADDRESS, 10 ) print ("ADDRESS:%x,content:%s" % (ADDRESS, binascii.b2a_hex(bytes ))) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0x100 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x200 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2, 0x300 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3, 0x400 ) mu.hook_add(unicorn.UC_HOOK_MEM_WRITE, hook_mem) mu.hook_add(unicorn.UC_HOOK_MEM_WRITE_UNMAPPED, hook_mem_write_unmapped) mu.hook_add(unicorn.UC_HOOK_INTR, hook_syscall) mu.hook_add(unicorn.UC_HOOK_BLOCK, hook_block) try : mu.emu_start(ADDRESS + 1 , ADDRESS + len (CODE)) except unicorn.UcError as e: print (e) print ("over" ) printArm32Regs(mu) return if __name__ == "__main__" : testthumb()
调用so中函数 参数传递
ARM32中参数传递:当参数少于等于4个时,直接使用R0-R3寄存器,多于4个时使用栈来传递,从右到左依次入栈
ARM64中参数传递:当参数少于等于8个时,直接使用X0-X7寄存器,多于8个时使用栈来传递,从右到左依次入栈
原函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int add (int a,int b) { int sum = a+b; return sum; }int add_six (int a,int b,int c,int d,int e,int f) { int sum = 0 ; sum = add(a,b); sum = add(sum,c); sum = add(sum,d); sum = add(sum,e); sum = add(sum,f); return sum; }
相关汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 000113 ac movs r0 , #3 000113 ae str r0 , [sp , #8 ] {var_40} {0x3 }000113 b0 movs r1 , #4 000113 b2 str r1 , [sp , #0xc ] {var_3c} {0x4 }000113 b4 bl #add 000113 b8 ldr r2 , [sp , #8 ] {var_40} {0x3 }000113 ba ldr r3 , [sp , #0xc ] {var_3c} {0x4 }000113 bc str r0 , [sp , #0x24 ] {var_24}000113 be mov r1 , sp {var_48}000113 c0 movs r0 , #6 000113 c2 str r0 , [r1 , #4 ] {var_44} {0x6 }000113 c4 movs r0 , #5 000113 c6 str r0 , [r1 ] {var_48} {0x5 }000113 c8 movs r0 , #1 000113 ca movs r1 , #2 000113 cc bl #add_six
调用add(a,b)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 def add (): CODE = None with open ("so/libunicorn.so" , "rb" ) as f: CODE = f.read() CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(CODE[0x11324 :], 0 , 20 ): print ("[addr:%x]:%s %s\n" % (0x11324 + i.address, i.mnemonic, i.op_str)) mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) ADDRESS = 0x1000 SIZE = 10 * 1024 * 1024 mu.mem_map(ADDRESS, SIZE) mu.mem_write(ADDRESS, CODE) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0x1 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x2 ) SP = ADDRESS + SIZE - 1 mu.reg_write(unicorn.arm_const.UC_ARM_REG_SP, SP) mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) add = ADDRESS + 0x00011324 add_end = ADDRESS + 0x00011336 try : mu.emu_start(add_six + 1 , add_end) print ("over" ) printArm32Regs(mu) except unicorn.UcError as e: print (e) return
调用add(a,b,c,d,e,f)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 def add_six (): CODE = None with open ("so/libunicorn.so" , "rb" ) as f: CODE = f.read() CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(CODE[0x00011338 :], 0 , 20 ): print ("[addr:%x]:%s %s\n" % (0x00011338 + i.address, i.mnemonic, i.op_str)) mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) ADDRESS = 0x1000 SIZE = 10 * 1024 * 1024 mu.mem_map(ADDRESS, SIZE) mu.mem_write(ADDRESS, CODE) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0x1 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x2 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2, 0x3 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3, 0x4 ) SP = ADDRESS + SIZE - 0x100 mu.reg_write(unicorn.arm_const.UC_ARM_REG_SP, SP) mu.reg_write(unicorn.arm_const.UC_ARM_REG_LR, ADDRESS + 0x456 ) mu.mem_write(SP, struct.pack("I" , 5 )) mu.mem_write(SP + 4 , struct.pack("I" , 6 )) mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) add_six = ADDRESS + 0x00011338 add_six_end = ADDRESS + 0x00011388 try : mu.emu_start(add_six + 1 , add_six_end + 1 ) print ("over" ) printArm32Regs(mu) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) except unicorn.UcError as e: print (e) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) return
当涉及到其他库函数调用时,需要做对应的patch
如修改add_six
为
1 2 3 4 5 6 7 8 9 10 11 12 int add_six (char * flag,int b,int c,int d,int e,int f) { int sum = 0 ; if (strstr (flag,"add" )){ sum = add(sum,c); sum = add(sum,d); }else { sum = add(sum,d); sum = add(sum,e); } return sum; }
则需要patch对应call strstr
地址,然后转为自己的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 def add_patch (): CODE = None with open ("so/unicorn_patch.so" , "rb" ) as f: CODE = f.read() CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(CODE[0x00011360 :], 0 , 20 ): print ("[addr:%x]:%s %s\n" % (0x00011360 + i.address, i.mnemonic, i.op_str)) mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) ADDRESS = 0x10000 SIZE = 10 * 1024 * 1024 mu.mem_map(ADDRESS, SIZE) mu.mem_write(ADDRESS, CODE) mu.mem_write(ADDRESS + 0x00011390 , b"\x00\xbf\x00\xbf" ) mu.mem_map(ADDRESS + SIZE + 0x10000 , 1024 ) mu.mem_write(ADDRESS + SIZE + 0x10000 , b"1add" ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, ADDRESS + SIZE + 0x10000 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0x2 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2, 0x3 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R3, 0x4 ) SP = ADDRESS + SIZE - 0x100 mu.reg_write(unicorn.arm_const.UC_ARM_REG_SP, SP) mu.reg_write(unicorn.arm_const.UC_ARM_REG_LR, ADDRESS + 0x456 ) mu.mem_write(SP, struct.pack("I" , 5 )) mu.mem_write(SP + 4 , struct.pack("I" , 6 )) mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) add_six = ADDRESS + 0x00011360 add_six_end = ADDRESS + 0x000113C8 try : mu.emu_start(add_six + 1 , add_six_end) print ("over" ) printArm32Regs(mu) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) except unicorn.UcError as e: print (e) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) printArm32Regs(mu) return
模拟调用jni接口函数 对于一般的jni函数来说,必然会使用到jni提供的一系列的api,如GetStringChars,NewStringUTF,FindClass,CallObjectMethod等,那么如何设计和模拟实现jni函数对这些jni中的接口函数调用呢?
在native中编写如下C函数
1 2 3 4 5 6 7 8 9 10 11 12 JNICALLJava_com_yring_unicorn_MainActivity_stringFromJNI ( JNIEnv *env, jobject , jstring content) { const char * content_ptr = env->GetStringUTFChars(content,0 ); __android_log_print(4 ,"jni" ,"%s" ,content_ptr); std ::string hello = "Hello from C++" ; return env->NewStringUTF(hello.c_str()); }
这里JNIEnv是一个结构体,里面保存了大量Jni函数接口,跟进查看
其实就是一个JNINativeInterface*
的指针,继续跟进
就会发现定义了大量的接口函数
将如上Native代码编译成apk后,解包,将so丢进IDA中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 int __fastcall Java_com_yring_unicorn_MainActivity_stringFromJNI (int a1, int a2, int a3) { int v3; int v4; int v5; int result; int v7; char v8; int v9; v3 = a1; v4 = sub_113E4(a1, a3, 0 ); _android_log_print(4 , "jni" , "%s" , v4); sub_11404(); v5 = sub_11472(&v8); v7 = sub_11458(v3, v5); std ::__ndk1::basic_string<char ,std ::__ndk1::char_traits<char >,std ::__ndk1::allocator<char >>::~basic_string(&v8); result = _stack_chk_guard; if ( _stack_chk_guard == v9 ) result = v7; return result; }
这里三个参数就与在Native中三个参数对应,将第一个参数改为JNIEnv*
类型,IDA就会自动识别函数
函数sub_113E4
1 2 3 4 int __fastcall sub_113E4 (void *a1) { return (*(int (**)(void ))(*(_DWORD *)a1 + 676 ))(); }
改为JniEnv*
后
1 2 3 4 int __fastcall sub_113E4 (JNIEnv *a1) { return ((int (*)(void ))(*a1)->GetStringUTFChars)(); }
那么使用Unicorn模拟执行就需要把这些接口函数给补回去,在这里就是需要手动实现GetStringUTFChars
和NewStringUTF
这里直接给出全部代码
核心思路就是找一片内存,初始化JNIEnv
结构体即可,然后可以简单实现里面的一些接口函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 import unicornimport capstoneimport struct ADDRESS = 0x10000 SIZE = 10 * 1024 * 1024 def printArm32Regs (mu: unicorn.Uc ): print ("-----REG-----" ) for i in range (66 , 78 ): print ("R%d,value:%x" % (i - 66 , mu.reg_read(i))) print ("SP-Value:%x" % (mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP))) print ("PC-Value:%x" % (mu.reg_read(unicorn.arm_const.UC_ARM_REG_PC))) print ("-----REG-----" )def readstring (mu: unicorn.Uc, address ): result = b"" tmp = mu.mem_read(address, 1 ) while tmp[0 ] != 0 : result += tmp address += 1 tmp = mu.mem_read(address, 1 ) return str (result)def hook_code (mu: unicorn.Uc, address, size, user_data ): code = mu.mem_read(address, size) CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(code, 0 , len (code) - 1 ): print () print ("[addr:%x]:%s %s" % (address, i.mnemonic, i.op_str)) if address >= 0 and address <= 300 * 4 : print ("-----------in jni interface-----------" ) index = address / 4 if index == 169 : print ("-----call GetStringUTFChars-----\n" ) r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) r2value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R2) content = readstring(mu, r1value) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, r1value) print (str (r0value) + "---" + content + "---" + str (r2value)) print ("\n-----call GetStringUTFChars-----" ) if index == 167 : print ("-----call NewStringUTF-----" ) r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) content = readstring(mu, r1value) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, r1value) print (str (r0value) + "---" + content) print ("-----call NewStringUTF-----" ) print ("-----------in jni interface-----------" ) if address == 0x10000 + 0x11386 : print ("-----call init-----" ) mu.mem_map(ADDRESS + SIZE + 0x11000 , 0x1000 ) mu.mem_write(ADDRESS + SIZE + 0x11000 , b"hello from C++" ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, ADDRESS + SIZE + 0x11000 ) print ("-----call init-----" ) if address == 0x10000 + 0x11390 : print ("-----call 1390-----" ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, ADDRESS + SIZE + 0x11000 ) print ("-----call 1390-----" ) return def testthumb_calljni (): CODE = None with open ("so/libunicorn_jni.so" , "rb" ) as f: CODE = f.read() CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(CODE[0x1134C :], 0 , 20 ): print (f"[addr:{hex (i.address)} ]:{i.mnemonic} {i.op_str} " ) mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) JNIFUNCTIONLISTBASE = 0x0 JNIFUNCTIONLISTSIZE = 0x1000 JNINATIVEINTERFACE = 301 mu.mem_map(0 , 0x1000 ) for i in range (0 , 300 ): mu.mem_write(i * 4 + JNIFUNCTIONLISTBASE, b"\x00\xB5\x00\xBD" ) for i in range (300 , 600 ): mu.mem_write(i * 4 , struct.pack("I" , ((i - 300 ) * 4 + 1 ))) JNIEnv_pointer = 601 * 4 mu.mem_write(JNIEnv_pointer, struct.pack("I" , (300 * 4 ))) mu.mem_map(ADDRESS, SIZE) mu.mem_write(ADDRESS, CODE) mu.mem_write( ADDRESS + 0x11352 , b"\x00\xBF\x00\xBF\x00\xBF\x00\xBF" ) mu.mem_write(ADDRESS + 0x1137A , b"\x00\xBF\x00\xBF" ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, JNIEnv_pointer) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0 ) mu.mem_map(ADDRESS + SIZE + 0x10000 , 1024 ) mu.mem_write(ADDRESS + SIZE + 0x10000 , b"i am from jni" ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R2, ADDRESS + SIZE + 0x10000 ) SP = ADDRESS + SIZE - 0x100 mu.reg_write(unicorn.arm_const.UC_ARM_REG_SP, SP) mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) start = ADDRESS + 0x0001134C end = ADDRESS + 0x000113BA try : mu.emu_start(start + 1 , end, count=80 ) print ("over" ) printArm32Regs(mu) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) content = readstring(mu, r0value) print ("return value-> " , content) except unicorn.UcError as e: print ("\n-----Something went wrong-----" ) print (e) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) printArm32Regs(mu) return if __name__ == "__main__" : testthumb_calljni()
这里可以注意一点android_log
是外部函数,如果很多地方都调用这个函数,可以在plt
表直接hook,相当于hook了全部函数。
模拟调用JNI_OnLoad 一个JNI_OnLoad
如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM *vm,void * reserved) { JNIEnv *env = nullptr ; if (vm->GetEnv ((void **)&env, JNI_VERSION_1_6) != JNI_OK) { return -1 ; } JNINativeMethod methods[] = { {"add" , "(II)I" , (void *)add}, {"myStr" , "(Ljava/lang/String;)Ljava/lang/String;" ,(void *)myStr} }; jclass clazz = env->FindClass ("com/yring/unicorn/MainActivity" ); if (clazz == NULL ) { return -1 ; } if (env->RegisterNatives (clazz, methods, sizeof (methods) / sizeof (methods[0 ])) < 0 ) { return -1 ; } return JNI_VERSION_1_6; }
同样这里,就需要实现vm->GetEnv
,env->FindClass
等操作
JavaVM结构体,比JNIEnv要少很多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 struct _JavaVM { const struct JNIInvokeInterface* functions; #if defined (__cplusplus) jint DestroyJavaVM () { return functions->DestroyJavaVM(this ); } jint AttachCurrentThread (JNIEnv** p_env, void * thr_args) { return functions->AttachCurrentThread(this , p_env, thr_args); } jint DetachCurrentThread () { return functions->DetachCurrentThread(this ); } jint GetEnv (void ** env, jint version) { return functions->GetEnv(this , env, version); } jint AttachCurrentThreadAsDaemon (JNIEnv** p_env, void * thr_args) { return functions->AttachCurrentThreadAsDaemon(this , p_env, thr_args); } #endif }; struct JNIInvokeInterface { void * reserved0; void * reserved1; void * reserved2; jint (*DestroyJavaVM)(JavaVM*); jint (*AttachCurrentThread)(JavaVM*, JNIEnv**, void *); jint (*DetachCurrentThread)(JavaVM*); jint (*GetEnv)(JavaVM*, void **, jint); jint (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void *); };
同样,需要hook掉GetEnv
和RegisterNatives
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 import unicornimport capstoneimport struct ADDRESS = 0x10000 SIZE = 10 * 1024 * 1024 def printArm32Regs (mu: unicorn.Uc ): print ("-----REG-----" ) for i in range (66 , 78 ): print ("R%d,value:%x" % (i - 66 , mu.reg_read(i))) print ("SP-Value:%x" % (mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP))) print ("PC-Value:%x" % (mu.reg_read(unicorn.arm_const.UC_ARM_REG_PC))) print ("-----REG-----" )def readstring (mu: unicorn.Uc, address ): result = b"" tmp = mu.mem_read(address, 1 ) while tmp[0 ] != 0 : result += tmp address += 1 tmp = mu.mem_read(address, 1 ) return str (result)def hook_code (mu: unicorn.Uc, address, size, user_data ): code = mu.mem_read(address, size) CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(code, 0 , len (code) - 1 ): print () print ("[addr:%x]:%s %s" % (address, i.mnemonic, i.op_str)) if address >= 700 * 4 and address <= 710 * 4 : print ("-----------in javavm interface-----------" ) index = (address - 700 * 4 ) / 4 if index == 6 : print ("-----call GetEnv-----\n" ) r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) mu.mem_write(r1value, struct.pack("I" , 601 * 4 )) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 0 ) if address >= 0 and address <= 300 * 4 : print ("-----------in jni interface-----------" ) index = address / 4 if index == 6 : print ("-----call FindClass-----\n" ) r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) classname = readstring(mu, r1value) print (f"FindClass {classname} " ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, 666 ) print ("\n-----call FindClass-----" ) if index == 215 : print ("-----call RegisterNatives-----\n" ) r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) r2value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R2) r3value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R3) function_addr = struct.unpack("I" , mu.mem_read(r2value, 4 ))[0 ] function_name = readstring(mu, function_addr) print (f"-----function name:{function_name} -----" ) print (f"----JNIEnv:{r0value} ----" ) print (f"----jcalss:{r1value} ----" ) print (f"----JNINativeMethod:{r2value} ----" ) print (f"----jint:{r3value} ----" ) print ("\n-----call RegisterNatives-----" ) print ("-----------in jni interface-----------" ) if address == 0x10000 + 0x1162E : print ("-----patch VLD1.32 {D16-D17}, [R1]-----" ) r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) d16 = mu.mem_read(r1value, 4 ) d17 = mu.mem_read(r1value + 4 , 4 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_D16, struct.unpack("I" , d16)[0 ]) mu.reg_write(unicorn.arm_const.UC_ARM_REG_D17, struct.unpack("I" , d17)[0 ]) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, r1value + 2 ) if address == 0x10000 + 0x11636 : print ("-----patch VST1.64 {D16-D17}, [R0]!-----" ) r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) d16 = mu.reg_read(unicorn.arm_const.UC_ARM_REG_D16) d17 = mu.reg_read(unicorn.arm_const.UC_ARM_REG_D17) mu.mem_write(r0value, struct.pack("I" , d16)) mu.mem_write(r0value + 4 , struct.pack("I" , d17)) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, r0value + 2 ) if address == 0x10000 + 0x1163A : print ("-----patch VLDR D16, [R1]-----" ) r1value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R1) r1value = mu.mem_read(r1value, 4 ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_D16, struct.unpack("I" , r1value)[0 ]) if address == 0x10000 + 0x1163E : print ("-----patch VSTR D16, [R0]-----" ) r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) d16 = mu.reg_read(unicorn.arm_const.UC_ARM_REG_D16) mu.mem_write(r0value, struct.pack("I" , d16)) return def testthumb_calljni (): CODE = None with open ("so/libunicorn_jnionload.so" , "rb" ) as f: CODE = f.read() CP = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) for i in CP.disasm(CODE[0x11540 :], 0 , 20 ): print (f"[addr:{hex (i.address)} ]:{i.mnemonic} {i.op_str} " ) mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_THUMB) JNIFUNCTIONLISTBASE = 0x0 JNIFUNCTIONLISTSIZE = 0x1000 JNINATIVEINTERFACE = 301 mu.mem_map(0 , 0x1000 ) for i in range (0 , 300 ): mu.mem_write(i * 4 + JNIFUNCTIONLISTBASE, b"\x00\xB5\x00\xBD" ) for i in range (300 , 600 ): mu.mem_write(i * 4 , struct.pack("I" , ((i - 300 ) * 4 + 1 ))) JNIEnv_pointer = 601 * 4 mu.mem_write(JNIEnv_pointer, struct.pack("I" , (300 * 4 ))) JAVAVMFUNCTIONLISTBASE = 700 * 4 for i in range (0 , 10 ): mu.mem_write(i * 4 + JAVAVMFUNCTIONLISTBASE, b"\x00\xB5\x00\xBD" ) for i in range (0 , 10 ): mu.mem_write( i * 4 + JAVAVMFUNCTIONLISTBASE + 40 , struct.pack("I" , i * 4 + JAVAVMFUNCTIONLISTBASE + 1 ), ) JavaVM_pointer = 700 * 4 + 80 mu.mem_write(JavaVM_pointer, struct.pack("I" , JAVAVMFUNCTIONLISTBASE + 40 )) mu.mem_map(ADDRESS, SIZE) mu.mem_write(ADDRESS, CODE) mu.mem_write( ADDRESS + 0x115FE , b"\x00\xBF\x00\xBF\x00\xBF\x00\xBF" ) mu.mem_write(ADDRESS + 0x1162E , b"\x00\xBF\x00\xBF" ) mu.mem_write(ADDRESS + 0x11636 , b"\x00\xBF\x00\xBF" ) mu.mem_write(ADDRESS + 0x1163A , b"\x00\xBF\x00\xBF" ) mu.mem_write(ADDRESS + 0x1163E , b"\x00\xBF\x00\xBF" ) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R0, JavaVM_pointer) mu.reg_write(unicorn.arm_const.UC_ARM_REG_R1, 0 ) SP = ADDRESS + SIZE - 0x100 mu.reg_write(unicorn.arm_const.UC_ARM_REG_SP, SP) mu.hook_add(unicorn.UC_HOOK_CODE, hook_code) start = ADDRESS + 0x115F8 end = ADDRESS + 0x11666 try : mu.emu_start(start + 1 , end) print ("over" ) printArm32Regs(mu) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) r0value = mu.reg_read(unicorn.arm_const.UC_ARM_REG_R0) content = readstring(mu, r0value) print ("return value-> " , content) except unicorn.UcError as e: print ("\n-----Something went wrong-----" ) print (e) print (f"INIT SP:{SP} NOW SP:{mu.reg_read(unicorn.arm_const.UC_ARM_REG_SP)} " ) printArm32Regs(mu) return if __name__ == "__main__" : testthumb_calljni()
AndroidNativeEmu 安装 直接通过pip install
安装会有问题,似乎是pip那边的源没有更新,构建的unicorn版本也是1.0.6,比较古早。可以直接下载源码
1 git clone https://github.com/AeonLucid/AndroidNativeEmu.git
然后把src/androidemu
文件夹整个copy到python的包目录
1 cp -r src/androidemu /Users/yongrin/opt/anaconda3/envs/frida14/lib/python3.8/site-packages/
然后就能成功运行自带的一些examples
了,不过前提是还需要正确安装keystone
普通函数hook 直接看给的example.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 import loggingimport sysfrom unicorn import UC_HOOK_CODEfrom unicorn.arm_const import *from androidemu.emulator import Emulator logging.basicConfig( stream=sys.stdout, level=logging.DEBUG, format ="%(asctime)s %(levelname)7s %(name)34s | %(message)s" ) logger = logging.getLogger(__name__) emulator = Emulator(vfp_inst_set=True ) emulator.load_library("example_binaries/32/libc.so" , do_init=False ) lib_module = emulator.load_library("example_binaries/32/libnative-lib.so" , do_init=False ) logger.info("Loaded modules:" )for module in emulator.modules: logger.info("[0x%x] %s" % (module.base, module.filename))def hook_code (uc, address, size, user_data ): instruction = uc.mem_read(address, size) instruction_str = '' .join('{:02x} ' .format (x) for x in instruction) print ('# Tracing instruction at 0x%x, instruction size = 0x%x, instruction = %s' % (address, size, instruction_str)) emulator.uc.hook_add(UC_HOOK_CODE, hook_code) emulator.call_symbol(lib_module, '_Z4testv' )print ("String length is: %i" % emulator.uc.reg_read(UC_ARM_REG_R0))
如果将libc.so
注释,那么模拟器就不会加载这个库,而Z4testv
使用了strlen
函数,此函数在libc.so
中,那么此时就会报错,相关日志也会提醒
一般而言对于找不到的外部函数,要么就将外部函数所在库给加载进来,要么就是打patch然后手动实现。
这里给出手动实现方式
定义要hook函数的python实现
1 2 3 4 5 6 7 8 9 from androidemu.utils import memory_helpersfrom androidemu.java.helpers.native_method import native_method@native_method def strlen (uc, buffer ): content = memory_helpers.read_utf8(uc, buffer) length = len (content) return length
将函数hook进so中
1 emulator.modules.add_symbol_hook('strlen' , emulator.hooker.write_function(strlen) + 1 )
除此外,也可以直接hook已有的函数。
JNI函数hook 基本一样
1 2 3 4 5 emulator.call_symbol(lib_module, "Java_com_yring_unicorn_MainActivity_stringFromJNI" , emulator.java_vm.jni_env.address_ptr, 0 , "hello emu" )
模拟实现jni与Java交互 直接调用JNI_Onload
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import loggingimport sysfrom unicorn import UC_HOOK_CODEfrom unicorn.arm_const import *from androidemu.emulator import Emulatorimport debug_utilsfrom androidemu.utils import memory_helpersfrom androidemu.java.helpers.native_method import native_method, native_read_args@native_method def __stack_chk_fail (uc, buffer ): pass @native_method def __stack_chk_guard (uc, buffer ): pass @native_method def __android_log_print (uc, arg1, arg2, arg3,arg4 ): t = memory_helpers.read_utf8(uc, arg2) s = memory_helpers.read_utf8(uc, arg4) print (f"prio-{arg1} tag-{t} string-{s} " ) logging.basicConfig( stream=sys.stdout, level=logging.DEBUG, format ="%(asctime)s %(levelname)7s %(name)34s | %(message)s" , ) logger = logging.getLogger(__name__) emulator = Emulator(vfp_inst_set=True ) emulator.modules.add_symbol_hook('__stack_chk_fail' , emulator.hooker.write_function(__stack_chk_fail) + 1 ) emulator.modules.add_symbol_hook('__stack_chk_guard' , emulator.hooker.write_function(__stack_chk_guard) + 1 ) lib_module = emulator.load_library("so/libunicorn_jnionload.so" , do_init=False ) logger.info("Loaded modules:" )for module in emulator.modules: logger.info("[0x%x] %s" % (module.base, module.filename)) emulator.uc.hook_add(UC_HOOK_CODE, debug_utils.hook_code) function_name = "Java_com_yring_unicorn_MainActivity_stringFromJNI" emulator.call_symbol(lib_module, "JNI_OnLoad" , emulator.java_vm.address_ptr, 0 )
当执行到JNIEnv->FindClass(com/yring/unicorn/MainActivity)
时,会报错,需要使用python实现这个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class MainActivity (metaclass=JavaClassDef, jvm_name='com/yring/unicorn/MainActivity' ): def __init__ (self ): pass @java_method_def(name='myStr' , signature='(Ljava/lang/String;)Ljava/lang/String;' , native=True ) def myStr (self, mu, arg0 ): pass @java_method_def(name='add' , signature='(II)I' , native=True ) def add (self, mu, arg0, arg1 ): pass def test (self ): pass
然后再注册进模拟器
1 emulator.java_classloader.add_class(MainActivity)
随后就能以python方式使用
1 2 main_activity = MainActivity() main_activity.myStr(emulator, "test emu" )
不过当返回值是基本类型比如int
的时候,会出现问题,因为AndroidNativeEmu
不知道返回值是否是基本类型,所以还是会去找它自己维护的一个引用表,这样就会出问题。解决办法就是在返回地方,新增一个print
然后自己判断返回值是啥。
Unidbg Unidbg也是基于Unicorn的,但是用Java所写,比Python的要方便许多。
大体流程如下
创建32位模拟器实例,emulator = AndroidEmulatorBuilder.for32Bit().build();
创建模拟器内存接口final Memory memory = emulator.getMemory();
设置系统类库解析memory.setLibraryResolver(new AndroidResolver(23));
创建 Android 虚拟机vm = emulator.createDalvikVM();
加载 so 到虚拟内存,第二个参数的意思表示是否执行动态库的初始化代码DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/xxx/xxx.so"),true);
获取 so 模块的句柄module = dm.getModule();
设置 JNI 需要继承AbstractJni
vm.setJni(this);
打印日志vm.setVerbose(true);
调用 JNI_Onloaddm.callJNI_OnLoad(emulator);
创建 jobject, 如果没用到的话可以不写 ,要用需要调用函数所在的Java类完整路径,比如a/b/c/d等等,注意.需要用/代替cNative = vm.resolveClass("com/xxx/xxx")
参数构建 基本方式
1 List<Object> args = new ArrayList <>(10 );
*JNIEnv env args.add(vm.getJNIEnv());
jobject 或 jclass
DvmObject<?> cnative = cNative.newObject(null);
args.add(cnative.hashCode());
如果用不到直接填0即可
args.add(0);
字符串对象
String input = "abcdef";
args.add(vm.addLocalObject(new StringObject(vm, input)));
bytes 数组
String str= "abcdef";
byte[] str_bytes = str.getBytes(StandardCharsets.UTF_8);
ByteArray str_bytes _array = new ByteArray(vm,str_bytes );
args.add(vm.addLocalObject(str_bytes _array));
bool
// false 填 0,true 填 1args.add(1);
AndroidEmulator 实例 使用 AndroidEmulatorBuilder 可以来帮助快速创建一个 AndroidEmulator 的实例。
AndroidEmulator emulator = AndroidEmulatorBuilder //指定32位CPU .for32Bit() //添加后端,推荐使用Dynarmic,运行速度快,但并不支持某些新特性 .addBackendFactory(new DynarmicFactory(true)) //指定进程名,推荐以安卓包名做进程名 .setProcessName(“com.github.unidbg”) //设置根路径 .setRootDir(new File(“target/rootfs/default”)) //生成AndroidEmulator实例 .build();
常用API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 Memory memory = emulator.getMemory();int pid = emulator.getPid();VM dalvikVM = emulator.createDalvikVM();VM dalvikVM = emulator.createDalvikVM(new File ("apk file path" ));VM dalvikVM = emulator.getDalvikVM(); emulator.showRegs();Backend backend = emulator.getBackend();String processName = emulator.getProcessName();RegisterContext context = emulator.getContext(); emulator.traceRead(1 ,0 ); emulator.traceWrite(1 ,0 ); emulator.traceCode(1 ,0 );boolean running = emulator.isRunning();
Memory实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Memory memory = emulator.getMemory(); memory.setLibraryResolver(new AndroidResolver (23 ));UnidbgPointer pointer = memory.pointer(address); Collection<MemoryMap> memoryMap = memory.getMemoryMap();Module module = memory.findModule("module name" );Module module = memory.findModuleByAddress(address);
VM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 VM vm = emulator.createDalvikVM(); vm.setVerbose(true );DalvikModule dalvikModule = vm.loadLibrary(new File ("so 文件路径" ), true ); vm.setJni(this );Pointer jniEnv = vm.getJNIEnv();Pointer javaVM = vm.getJavaVM(); vm.callJNI_OnLoad(emulator,dalvikModule.getModule());int hash = vm.addGlobalObject(dvmObj); DvmObject<?> object = vm.getObject(hash);
四种函数调用
加载so,并调用init以及init_array中函数
调用so中普通函数
调用jni函数
调用JNI_OnLoad函数
测试的native-lib.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 #include <jni.h> #include <string> #include <android/log.h> extern "C" void _init(void ){ __android_log_print(4 ,"jin" ,"go into _init" ); }void __attribute__ ((constructor)) myConstructor1 (void ){ __android_log_print(4 ,"jin" ,"go into myConstructor1" ); }void __attribute__ ((constructor)) myConstructor2 (void ){ __android_log_print(4 ,"jin" ,"go into myConstructor2" ); }int add (JNIEnv *env, jobject thiz,jint a, jint b) { int sum = a+b; return sum; }int add_ (int a,int b) { return a+b; }int add_4 (jint a, jint b,jint c,jint d) { int sum = add_ (a,b) + add_ (c ,d); return sum; }jstring myStr (JNIEnv *env, jobject thiz, jstring str) { std::string my_str_ = "Hello from " ; const char * nativeStr = env->GetStringUTFChars (str, nullptr ); if (nativeStr == nullptr ) { return nullptr ; } my_str_ += nativeStr; env->ReleaseStringUTFChars (str, nativeStr); return env->NewStringUTF (my_str_.c_str ()); }extern "C" JNIEXPORT jstring JNICALL Java_com_yring_unicorn_MainActivity_stringFromJNI ( JNIEnv *env, jobject , jstring content) { const char * content_ptr = env->GetStringUTFChars (content,0 ); __android_log_print(4 ,"jni" ,"%s" ,content_ptr); std::string hello = "Hello from C++" ; return env->NewStringUTF (hello.c_str ()); }JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM *vm,void * reserved) { JNIEnv *env = nullptr ; if (vm->GetEnv ((void **)&env, JNI_VERSION_1_6) != JNI_OK) { return -1 ; } JNINativeMethod methods[] = { {"add" , "(II)I" , (void *)add}, {"myStr" , "(Ljava/lang/String;)Ljava/lang/String;" ,(void *)myStr} }; jclass clazz = env->FindClass ("com/yring/unicorn/MainActivity" ); env->RegisterNatives (clazz, methods, 2 ); int sum = add_4 (1 ,2 ,3 ,4 ); __android_log_print(4 ,"add_4" ,"%d" ,sum); return JNI_VERSION_1_6; }
加载so 在加载so的时候可以自行选择是否执行init等函数
1 2 DalvikModule dm = vm.loadLibrary(new File ("unidbg-android/src/test/native/so/libunicorn.so" ), true );
调用普通函数 一般来说,普通函数都没有导出符号,需要使用地址调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private void call_add2 () { DvmObject<?> obj = ProxyDvmObject.createObject(vm, this ); Number numbers = module .callFunction(emulator, 0x11510 | 1 , 1 ,2 ); System.out.println("add2 ====> result: " + numbers.intValue()); }private void call_add4 () { DvmObject<?> obj = ProxyDvmObject.createObject(vm, this ); Number numbers = module .callFunction(emulator, 0x11520 | 1 , 1 ,2 ,3 ,4 ); System.out.println("add4 ====> result: " + numbers.intValue()); }
调用JNI函数 1 2 3 4 5 6 7 8 9 10 11 12 13 public void jni () { DvmObject<?> obj = ProxyDvmObject.createObject(vm, this ); String data = "dta" ; DvmObject<?> result = obj.callJniMethodObject(emulator,"stringFromJNI(Ljava/lang/String;)Ljava/lang/String;" , data); String value = (String) result.getValue(); System.out.println("jni ====> result: " + value); }
调用JNI_OnLoad 需要先调用callJNI_OnLoad方法,才可以正常调用其他方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 private MainActivity () { emulator = AndroidEmulatorBuilder .for32Bit() .addBackendFactory(new DynarmicFactory (true )) .build(); Memory memory = emulator.getMemory(); LibraryResolver resolver = new AndroidResolver (23 ); memory.setLibraryResolver(resolver); vm = emulator.createDalvikVM(); vm.setVerbose(false ); DalvikModule dm = vm.loadLibrary(new File ("unidbg-android/src/test/native/so/libunicorn.so" ), true ); module = dm.getModule(); dm.callJNI_OnLoad(emulator); DvmObject<?> obj = ProxyDvmObject.createObject(vm, this ); DvmObject<?> result = obj.callJniMethodObject(emulator,"myStr(Ljava/lang/String;)Ljava/lang/String;" , "" ); String value = (String) result.getValue(); System.out.println("myStr:" + value); int res = obj.callJniMethodInt(emulator,"add(II)I" , 3 ,4 ); System.out.println("add:" + res); }