The method of Unicorn simulating CPU to execute dynamic Registration of JNI_Onload
This article mainly introduces the relevant knowledge of "Unicorn simulates CPU to perform JNI_Onload dynamic registration". The editor shows you the operation process through an actual case. The operation method is simple, fast and practical. I hope this "Unicorn simulation CPU implementation of JNI_Onload dynamic registration method" article can help you solve the problem.
Unicorn simulates CPU to execute JNI_Onload dynamic registration JNI_OnLoad as shown in the figure
Steps:
First implement GetEnv in javavm (similar to simulating jni process) with a total of eight functions
Initialization
# 1. Start mapping mu.mem_map (0, 0x1000) # initialization mapping parameter 1: address parameter 2: space size default after initialization: 0 # 1.1 initialize each function in java vm java_vm_base = 700room4 # start for i in range (0,10) 1): # A total of 8 functions (5 + 3 reserved) here I have reserved 10 more to write several preparations That is, 10'4 mu.mem_write (i*4+java_vm_base, b'\ X00\ xb5\ X00\ xbd') # fill in casually first Maintain stack balance push {lr} pop {pc} # 1.2 initialize and populate the JNIInvokeInterface structure for i in range (0,10,1): mu.mem_write (i*4+java_vm_base+40, struct.pack ("I", i*4+java_vm_base+1)) # Note the second parameter, pack as bytes And the thmob instruction set should be + 1 # 1.3 initialize the Java vm pointer javavm_pointer=700*4+80 mu.mem_write (javavm_pointer,struct.pack ("I", java_vm_base+40)) # content pointer, the page is the first position of the JNIInvokeInterface, so add 40
**
Then add Hook code to simulate cpu execution
Note: it is problematic to read the function information directly through the address of R2.
Because: linker does not map directly when loading, but loads segments that can not be separated! So the position is impassable.
Solution: to simulate loading and relocation
It involves: dependent library loading, symbol parsing and so on.
Convenient solution: AndroidNativeEmu has encapsulated linker and can simulate the execution of syscall. It also provides hook function for functions.
The code is as follows
Tool
Import unicornimport capstoneimport structclass Tool: "def _ _ init__ (self): self.CP = capstone.Cs (capstone.CS_ARCH_ARM, capstone.CS_MODE_THUMB) def capstone_print (self, code, offset) Total=20): "" code: code offset: offset position total: maximum print line "" for i in self.CP.disasm (code [offset:], 0, total): print ('\ 033 [1 32m address: 0x%x | op code:% s |% s\ 033 [0mm% (offset + i.address, i.mnemonic, i.op_str) def readstring (self, mu,address): "read string" result='' tmp=mu.mem_read (address) " 1) while (tmp [0]! = 0): result=result+chr (tmp [0]) address=address+1 tmp = mu.mem_read (address, 1) return result def printArm32Regs (self, mu, end=78): "" print register "" for i in range (66, end): print ("\ 033 [1") 30m [R% d], value:%x\ 033 [0m "% (iMel 66 mam mu.regained read (I)) print ("\ 033 [1bet30mSP-> value:%x\ 033 [0m "% (mu.reg_read (unicorn.arm_const.UC_ARM_REG_SP) print ("\ 033 [1) 30mPC-> value:%x\ 033 [0m "% (mu.reg_read (unicorn.arm_const.UC_ARM_REG_PC) tl = Tool () if _ _ name__ =" _ _ main__ ": with open (" so/testcalljni.so ", 'rb') as f: CODE=f.read () # tl.capstone_print (CODE, 0x0B58, 10)
**
_ _
Core
Import unicornimport structimport capstonefrom arm_tool import tldef init_java_vm (mu): "" initialize 3 functions of java vm java vm 5 "" # 1. Start mapping mu.mem_map (0, 0x1000) # initialization mapping parameter 1: address parameter 2: space size default after initialization: 0 "Note: to simulate JNI_OnLoad, you also need to initialize JNI" # 0.1 initialize and populate the jni function JniFuntionListbase=0x0 for i in range (0 300): # nearly 300 jni functions (pointer is 4 bytes) mu.mem_write (i*4+JniFuntionListbase, b'\ X00\ xb5\ X00\ xbd') # fill in casually first Maintain stack balance push {lr} pop {pc} # 0.2 initialize populate the JNINaviteInterface structure, each item is, the address # JniNativeInterFace=301 # of the jni function is used for pointers, starting with 301, for i in range (300,600): # 4 bytes are addresses mu.mem_write (iTun4, struct.pack ("I", (iMue 300) * 4q1) # pay attention to the second parameter To pack to bytes, and the thmob instruction set should be + 1 # 0.3 initialize the jnienv pointer jnienv_pointer = 601 / 4 mu.mem_write (jnienv_pointer, struct.pack ("I", 300 / 4)) # content pointer The page is the first 300 "initialize java vm" # 1.1 initialize each function in the java vm java_vm_base = 700room4 # for i in range (0,10,1): # A total of 8 functions (5 + 3 reservations) here I have reserved 10 to write a few more preparations That is, 10'4 mu.mem_write (i*4+java_vm_base, b'\ X00\ xb5\ X00\ xbd') # fill in casually first Maintain stack balance push {lr} pop {pc} # 1.2 initialize and populate the JNIInvokeInterface structure for i in range (0,10,1): mu.mem_write (i*4+java_vm_base+40, struct.pack ("I", i*4+java_vm_base+1)) # Note the second parameter, pack as bytes And the thmob instruction set should be + 1 # 1.3 initialize the Java vm pointer javavm_pointer=700*4+80 mu.mem_write (javavm_pointer,struct.pack ("I", java_vm_base+40)) # content pointer, the page is the first position of the JNIInvokeInterface, so add 40 # 2. Map the code snippet to the simulator's virtual address ADDRESS = 0x1000 # Mapping start address SIZE = 1024 "1024" 10 # assign the mapping size (a little more) # 3. Start mapping mu.mem_map (ADDRESS, SIZE) # initialization mapping parameter 1: address parameter 2: space size default after initialization: 0 mu.mem_write (ADDRESS, CODE) # write instruction parameter 1: write position parameter 2: write content # 4. Register initialization function 2 parameters (JNI_OnLoad has two parameters) mu.reg_write (unicorn.arm_const.UC_ARM_REG_R0, javavm_pointer) # parameter javavm pointer mu.reg_write (unicorn.arm_const.UC_ARM_REG_R1, 0x0) # 0 # 5. Initialize the stack, because you want to set SP SP = ADDRESS+SIZE-16 # multi-subtraction points on memory, reserve the location of the remaining two parameters of sp mu.reg_write (unicorn.arm_const.UC_ARM_REG_SP,SP) # 6. Add hook code "Note: adding an interval when hook can greatly improve the efficiency of hook!" Mu.hook_add (unicorn.UC_HOOK_CODE, hook_code) # mu.hook_add (unicorn.UC_HOOK_MEM_WRITE, hook_mem) # tracking cpu to perform memory operations requires a self-written callback function # mu.hook_add (unicorn.UC_HOOK_INTR,hook_syscall) # hook system call function # mu.hook_add (unicorn.UC_HOOK_BLOCK,hook_block) # hook basic block # 7. Start running add_satrt = ADDRESS+0xc00+1 # offset position ida to view the odd number of THUMB instruction sets, so ADDRESS+ 1, add_end = ADDRESS+0xC66 # return after calling registnative: try: mu.emu_start (add_satrt, add_end) # Parameter 1: start position Parameter 2: end position print ('- unicorn after execution -') r0value = mu.reg_read (unicorn.arm_const.UC_ARM_REG_R0) print ('execution result:', tl.readstring (mu, r0value)) except unicorn.UcError as e: print ('033 [1 31mError:% s\ 033 [0m'% e) def hook_code (mu,address,size,user_data): "defines the callback function Before entering the assembly instruction, you will run here mu: simulator address: execution address size: Assembly instruction size user_data: parameter added by hook_add "" code=mu.mem_read (address,size) # read if address > = 700004 and address=0 and address