├── androidemu ├── __init__.py ├── const │ ├── __init__.py │ ├── android.py │ └── linux.py ├── cpu │ ├── __init__.py │ ├── syscall_handler.py │ ├── interrupt_handler.py │ ├── syscall_handlers.py │ └── syscall_hooks.py ├── java │ ├── __init__.py │ ├── classes │ │ ├── __init__.py │ │ ├── executable.py │ │ ├── constructor.py │ │ └── method.py │ ├── helpers │ │ ├── __init__.py │ │ └── native_method.py │ ├── constant_values.py │ ├── java_field_def.py │ ├── jni_const.py │ ├── java_classloader.py │ ├── java_method_def.py │ ├── reference_table.py │ ├── jni_ref.py │ ├── java_vm.py │ ├── java_class_def.py │ └── jni_env.py ├── utils │ ├── __init__.py │ └── memory_helpers.py ├── vfs │ ├── __init__.py │ ├── file_helpers.py │ └── file_system.py ├── native │ ├── __init__.py │ ├── memory.py │ ├── hooks.py │ └── memory_heap.py ├── emulator_error.py ├── internal │ ├── symbol_resolved.py │ ├── arm.py │ ├── __init__.py │ ├── module.py │ ├── memory.py │ └── modules.py ├── config.py ├── tracer.py ├── hooker.py └── emulator.py ├── samples ├── vfs │ ├── proc │ │ └── sys │ │ │ └── vm │ │ │ └── overcommit_memory │ └── sys │ │ └── devices │ │ └── system │ │ └── cpu │ │ ├── online │ │ └── online.meta_emu ├── .DS_Store ├── app_process32 ├── example_binaries │ ├── libc.so │ ├── libcms.so │ ├── libdl.so │ ├── libm.so │ ├── libstdc++.so │ ├── libnative-lib.so │ └── libnative-lib_jni.so ├── example.py ├── debug_utils.py ├── example_jiagu.py ├── example_jni.py └── example_douyin.py ├── .DS_Store ├── requirements.txt ├── README.md ├── .gitignore ├── README_cn.md └── tools └── gen_jni_env.py /androidemu/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/const/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/cpu/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/java/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/vfs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/native/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/java/classes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /androidemu/java/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /samples/vfs/proc/sys/vm/overcommit_memory: -------------------------------------------------------------------------------- 1 | 0 -------------------------------------------------------------------------------- /samples/vfs/sys/devices/system/cpu/online: -------------------------------------------------------------------------------- 1 | 0-3 -------------------------------------------------------------------------------- /androidemu/const/android.py: -------------------------------------------------------------------------------- 1 | PR_SET_VMA = 0x53564d41 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/.DS_Store -------------------------------------------------------------------------------- /androidemu/emulator_error.py: -------------------------------------------------------------------------------- 1 | class EmulatorError(Exception): 2 | pass 3 | -------------------------------------------------------------------------------- /samples/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/.DS_Store -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | unicorn==1.0.1 2 | pyelftools==0.24 3 | hexdump==3.3 4 | keystone-engine==0.9.1.post3 -------------------------------------------------------------------------------- /samples/app_process32: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/app_process32 -------------------------------------------------------------------------------- /samples/example_binaries/libc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/example_binaries/libc.so -------------------------------------------------------------------------------- /samples/example_binaries/libcms.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/example_binaries/libcms.so -------------------------------------------------------------------------------- /samples/example_binaries/libdl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/example_binaries/libdl.so -------------------------------------------------------------------------------- /samples/example_binaries/libm.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/example_binaries/libm.so -------------------------------------------------------------------------------- /samples/example_binaries/libstdc++.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/example_binaries/libstdc++.so -------------------------------------------------------------------------------- /samples/example_binaries/libnative-lib.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/example_binaries/libnative-lib.so -------------------------------------------------------------------------------- /samples/example_binaries/libnative-lib_jni.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/P4nda0s/AndroidNativeEmu/HEAD/samples/example_binaries/libnative-lib_jni.so -------------------------------------------------------------------------------- /androidemu/internal/symbol_resolved.py: -------------------------------------------------------------------------------- 1 | class SymbolResolved: 2 | 3 | def __init__(self, address, symbol): 4 | self.address = address 5 | self.symbol = symbol 6 | -------------------------------------------------------------------------------- /androidemu/cpu/syscall_handler.py: -------------------------------------------------------------------------------- 1 | class SyscallHandler: 2 | 3 | def __init__(self, idx, name, arg_count, callback): 4 | self.idx = idx 5 | self.name = name 6 | self.arg_count = arg_count 7 | self.callback = callback 8 | -------------------------------------------------------------------------------- /androidemu/internal/arm.py: -------------------------------------------------------------------------------- 1 | # From http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf 2 | 3 | R_ARM_ABS32 = 2 4 | R_ARM_GLOB_DAT = 21 5 | R_ARM_JUMP_SLOT = 22 6 | R_ARM_RELATIVE = 23 7 | 8 | R_AARCH64_GLOB_DAT = 1025 9 | R_AARCH64_JUMP_SLOT = 1026 10 | R_AARCH64_RELATIVE = 1027 11 | -------------------------------------------------------------------------------- /androidemu/config.py: -------------------------------------------------------------------------------- 1 | STACK_ADDR = 0x00000000 2 | STACK_SIZE = 0x00100000 3 | 4 | HOOK_MEMORY_BASE = 0x1000000 5 | HOOK_MEMORY_SIZE = 0x0200000 # 2 * 1024 * 1024 - 2MB 6 | 7 | HEAP_BASE = 0x2000000 8 | HEAP_SIZE = 0x0200000 # 2 * 1024 * 1024 - 2MB 9 | 10 | BASE_ADDR = 0xCBBCB000 11 | 12 | WRITE_FSTAT_TIMES = True 13 | -------------------------------------------------------------------------------- /androidemu/java/classes/executable.py: -------------------------------------------------------------------------------- 1 | from androidemu.java.java_class_def import JavaClassDef 2 | from androidemu.java.java_field_def import JavaFieldDef 3 | 4 | 5 | class Executable(metaclass = JavaClassDef,jvm_name = 'java/lang/reflect/Executable',jvm_fields=[JavaFieldDef('accessFlags', 'I', False)]): 6 | def __init__(self): 7 | pass 8 | -------------------------------------------------------------------------------- /androidemu/java/constant_values.py: -------------------------------------------------------------------------------- 1 | # https://docs.oracle.com/javase/7/docs/api/constant-values.html 2 | 3 | MODIFIER_PUBLIC = 1 4 | MODIFIER_PRIVATE = 2 5 | MODIFIER_PROTECTED = 4 6 | MODIFIER_STATIC = 8 7 | MODIFIER_FINAL = 16 8 | MODIFIER_SYNCHRONIZED = 32 9 | MODIFIER_VOLATILE = 64 10 | MODIFIER_TRANSIENT = 128 11 | MODIFIER_NATIVE = 256 12 | MODIFIER_INTERFACE = 512 13 | MODIFIER_ABSTRACT = 1024 14 | MODIFIER_STRICT = 2048 15 | -------------------------------------------------------------------------------- /samples/vfs/sys/devices/system/cpu/online.meta_emu: -------------------------------------------------------------------------------- 1 | { 2 | "st_dev": 0, 3 | "__st_ino": 0, 4 | "st_mode": 0, 5 | "st_nlink": 0, 6 | "st_uid": 0, 7 | "st_gid": 0, 8 | "st_rdev": 0, 9 | "st_size": 0, 10 | "st_blksize": 0, 11 | "st_blocks": 0, 12 | "st_atime": 0, 13 | "st_atime_ns": 0, 14 | "st_mtime": 0, 15 | "st_mtime_ns": 0, 16 | "st_ctime": 0, 17 | "st_ctime_ns": 0, 18 | "st_ino": 0 19 | } -------------------------------------------------------------------------------- /androidemu/java/java_field_def.py: -------------------------------------------------------------------------------- 1 | class JavaFieldDef: 2 | 3 | def __init__(self, name, signature, is_static, static_value=None, ignore=False): 4 | self.jvm_id = None # Assigned by JavaClassDef. 5 | self.name = name 6 | self.signature = signature 7 | self.is_static = is_static 8 | self.static_value = static_value 9 | self.ignore = ignore 10 | 11 | if self.is_static and self.static_value is None: 12 | raise ValueError('Static value may not be None for a static field.') 13 | -------------------------------------------------------------------------------- /androidemu/java/jni_const.py: -------------------------------------------------------------------------------- 1 | JNI_FALSE = 0 2 | JNI_TRUE = 1 3 | 4 | JNI_VERSION_1_1 = 0x00010001 5 | JNI_VERSION_1_2 = 0x00010002 6 | JNI_VERSION_1_4 = 0x00010004 7 | JNI_VERSION_1_6 = 0x00010006 8 | 9 | JNI_OK = 0 # no error 10 | JNI_ERR = -1 # generic error 11 | JNI_EDETACHED = -2 # thread detached from the VM 12 | JNI_EVERSION = -3 # JNI version error 13 | JNI_ENOMEM = -4 # Out of memory 14 | JNI_EEXIST = -5 # VM already created 15 | JNI_EINVAL = -6 # Invalid argument 16 | 17 | JNI_COMMIT = 1 # copy content, do not free buffer 18 | JNI_ABORT = 2 # free buffer w/o copying back 19 | -------------------------------------------------------------------------------- /androidemu/tracer.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from unicorn import * 4 | 5 | from androidemu.internal.modules import Modules 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | class Tracer: 11 | def __init__(self, uc: Uc, modules: Modules): 12 | self._uc = uc 13 | self._modules = modules 14 | 15 | def enable(self): 16 | self._uc.hook_add(UC_HOOK_BLOCK, self._hook_block) 17 | 18 | def _hook_block(self, uc: Uc, address, size, user_data): 19 | (name, symbol) = self._modules.find_symbol(address | 1) 20 | 21 | if symbol is not None: 22 | print(name) 23 | -------------------------------------------------------------------------------- /androidemu/const/linux.py: -------------------------------------------------------------------------------- 1 | CLOCK_REALTIME = 0 2 | CLOCK_MONOTONIC = 1 3 | CLOCK_PROCESS_CPUTIME_ID = 2 4 | CLOCK_THREAD_CPUTIME_ID = 3 5 | CLOCK_MONOTONIC_RAW = 4 6 | CLOCK_REALTIME_COARSE = 5 7 | CLOCK_MONOTONIC_COARSE = 6 8 | CLOCK_BOOTTIME = 7 9 | CLOCK_REALTIME_ALARM = 8 10 | CLOCK_BOOTTIME_ALARM = 9 11 | 12 | FUTEX_WAIT = 0 13 | FUTEX_WAKE = 1 14 | FUTEX_FD = 2 15 | FUTEX_REQUEUE = 3 16 | FUTEX_CMP_REQUEUE = 4 17 | FUTEX_WAKE_OP = 5 18 | FUTEX_LOCK_PI = 6 19 | FUTEX_UNLOCK_PI = 7 20 | FUTEX_TRYLOCK_PI = 8 21 | FUTEX_WAIT_BITSET = 9 22 | FUTEX_WAKE_BITSET = 10 23 | FUTEX_WAIT_REQUEUE_PI = 11 24 | FUTEX_CMP_REQUEUE_PI = 12 25 | 26 | FUTEX_PRIVATE_FLAG = 128 27 | FUTEX_CLOCK_REALTIME = 256 28 | -------------------------------------------------------------------------------- /androidemu/cpu/interrupt_handler.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from unicorn import * 4 | from unicorn.arm_const import * 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class InterruptHandler: 10 | 11 | """ 12 | :type mu Uc 13 | """ 14 | def __init__(self, mu): 15 | self._mu = mu 16 | self._mu.hook_add(UC_HOOK_INTR, self._hook_interrupt) 17 | self._handlers = dict() 18 | 19 | def _hook_interrupt(self, uc, intno, data): 20 | if intno in self._handlers: 21 | self._handlers[intno](uc) 22 | else: 23 | logger.error("Unhandled interrupt %d at %x, stopping emulation" % (intno, self._mu.reg_read(UC_ARM_REG_PC))) 24 | self._mu.emu_stop() 25 | 26 | def set_handler(self, intno, handler): 27 | self._handlers[intno] = handler 28 | -------------------------------------------------------------------------------- /androidemu/java/classes/constructor.py: -------------------------------------------------------------------------------- 1 | from androidemu.java.classes.executable import Executable 2 | from androidemu.java.java_class_def import JavaClassDef 3 | from androidemu.java.java_field_def import JavaFieldDef 4 | from androidemu.java.java_method_def import JavaMethodDef 5 | 6 | class Constructor(metaclass=JavaClassDef, 7 | jvm_name = 'java/lang/reflect/Constructor', 8 | jvm_fields=[ 9 | JavaFieldDef('slot', 'I', False, ignore=True), 10 | JavaFieldDef('declaringClass', 'Ljava/lang/Class;', False)], 11 | jvm_super=Executable): 12 | 13 | def __init__(self, clazz: JavaClassDef, method: JavaMethodDef): 14 | self._clazz = clazz 15 | self._method = method 16 | self.slot = method.jvm_id 17 | self.declaringClass = self._clazz 18 | self.accessFlags = method.modifier 19 | -------------------------------------------------------------------------------- /androidemu/internal/__init__.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | 3 | UC_MEM_ALIGN = 0x1000 4 | 5 | PF_X = 0x1 # Executable 6 | PF_W = 0x2 # Writable 7 | PF_R = 0x4 # Readable 8 | 9 | 10 | # Thansk to https://github.com/lunixbochs/usercorn/blob/master/go/mem.go 11 | def align(addr, size, growl): 12 | to = ctypes.c_uint64(UC_MEM_ALIGN).value 13 | mask = ctypes.c_uint64(0xFFFFFFFFFFFFFFFF).value ^ ctypes.c_uint64(to - 1).value 14 | right = addr + size 15 | right = (right + to - 1) & mask 16 | addr &= mask 17 | size = right - addr 18 | if growl: 19 | size = (size + to - 1) & mask 20 | return addr, size 21 | 22 | 23 | def get_segment_protection(prot_in): 24 | prot = 0 25 | 26 | if prot_in & PF_R is not 0: 27 | prot |= 1 28 | 29 | if prot_in & PF_W is not 0: 30 | prot |= 2 31 | 32 | if prot_in & PF_X is not 0: 33 | prot |= 4 34 | 35 | return prot 36 | -------------------------------------------------------------------------------- /androidemu/internal/module.py: -------------------------------------------------------------------------------- 1 | class Module: 2 | 3 | """ 4 | :type filename str 5 | :type base int 6 | :type size int 7 | """ 8 | def __init__(self, filename, address, size, symbols_resolved, init_array=[]): 9 | self.filename = filename 10 | self.base = address 11 | self.size = size 12 | self.symbols = symbols_resolved 13 | self.symbol_lookup = dict() 14 | self.init_array = list(init_array) 15 | 16 | # Create fast lookup. 17 | for symbol_name, symbol in self.symbols.items(): 18 | if symbol.address != 0: 19 | self.symbol_lookup[symbol.address] = (symbol_name, symbol) 20 | 21 | def find_symbol(self, name): 22 | if name in self.symbols: 23 | return self.symbols[name] 24 | return None 25 | 26 | def is_symbol_addr(self, addr): 27 | if addr in self.symbol_lookup: 28 | return self.symbol_lookup[addr](0) 29 | else: 30 | return None 31 | 32 | -------------------------------------------------------------------------------- /androidemu/java/java_classloader.py: -------------------------------------------------------------------------------- 1 | from androidemu.java.java_class_def import JavaClassDef 2 | 3 | 4 | class JavaClassLoader: 5 | 6 | """ 7 | :type class_by_id dict[int, JavaClassDef] 8 | :type class_by_name dict[string, JavaClassDef] 9 | """ 10 | def __init__(self): 11 | self.class_by_id = dict() 12 | self.class_by_name = dict() 13 | 14 | def add_class(self, clazz): 15 | if not isinstance(clazz, JavaClassDef): 16 | raise ValueError('Expected a JavaClassDef.') 17 | 18 | if clazz.jvm_name in self.class_by_name: 19 | raise KeyError('The class \'%s\' is already registered.' % clazz.jvm_name) 20 | 21 | self.class_by_id[clazz.jvm_id] = clazz 22 | self.class_by_name[clazz.jvm_name] = clazz 23 | 24 | def find_class_by_id(self, jvm_id): 25 | if jvm_id not in self.class_by_id: 26 | return None 27 | 28 | return self.class_by_id[jvm_id] 29 | 30 | def find_class_by_name(self, name): 31 | if name not in self.class_by_name: 32 | return None 33 | 34 | return self.class_by_name[name] 35 | -------------------------------------------------------------------------------- /androidemu/internal/memory.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from androidemu import config 4 | from androidemu.internal import align 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class Memory: 10 | 11 | """ 12 | :type emu androidemu.emulator.Emulator 13 | """ 14 | def __init__(self, emu): 15 | self.emu = emu 16 | self.counter_memory = config.BASE_ADDR 17 | self.counter_stack = config.STACK_ADDR + config.STACK_SIZE 18 | 19 | def mem_reserve(self, size): 20 | (_, size_aligned) = align(0, size, True) 21 | ret = self.counter_memory 22 | self.counter_memory += size_aligned 23 | return ret 24 | 25 | def mem_map(self, address, size, prot): 26 | (address, size) = align(address, size, True) 27 | 28 | self.emu.mu.mem_map(address, size, prot) 29 | 30 | logger.debug("=> Mapping memory page 0x%08x - 0x%08x, size 0x%08x, prot %s" % (address, address + size, size, 31 | prot)) 32 | 33 | def mem_write(self, address, data): 34 | self.emu.mu.mem_write(address, data) 35 | 36 | def mem_read(self, address, size): 37 | return self.emu.mu.mem_read(address, size) 38 | 39 | 40 | -------------------------------------------------------------------------------- /samples/example.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import sys 3 | 4 | from unicorn import UC_HOOK_CODE 5 | from unicorn.arm_const import * 6 | 7 | from androidemu.emulator import Emulator 8 | 9 | # Configure logging 10 | logging.basicConfig( 11 | stream=sys.stdout, 12 | level=logging.DEBUG, 13 | format="%(asctime)s %(levelname)7s %(name)34s | %(message)s" 14 | ) 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | # Initialize emulator 19 | emulator = Emulator() 20 | emulator.load_library("samples/example_binaries/libc.so") 21 | lib_module = emulator.load_library("samples/example_binaries/libnative-lib.so") 22 | 23 | # Show loaded modules. 24 | logger.info("Loaded modules:") 25 | 26 | for module in emulator.modules: 27 | logger.info("[0x%x] %s" % (module.base, module.filename)) 28 | 29 | 30 | # Add debugging. 31 | def hook_code(mu, address, size, user_data): 32 | instruction = mu.mem_read(address, size) 33 | instruction_str = ''.join('{:02x} '.format(x) for x in instruction) 34 | 35 | print('# Tracing instruction at 0x%x, instruction size = 0x%x, instruction = %s' % (address, size, instruction_str)) 36 | 37 | 38 | emulator.mu.hook_add(UC_HOOK_CODE, hook_code) 39 | 40 | # Runs a method of "libnative-lib.so" that calls an imported function "strlen" from "libc.so". 41 | emulator.call_symbol(lib_module, '_Z4testv') 42 | 43 | print("String length is: %i" % emulator.mu.reg_read(UC_ARM_REG_R0)) 44 | -------------------------------------------------------------------------------- /androidemu/utils/memory_helpers.py: -------------------------------------------------------------------------------- 1 | import hexdump 2 | import struct 3 | 4 | def hex_dump(mu, address, size): 5 | data = mu.mem_read(address, size) 6 | return hexdump.hexdump(data) 7 | 8 | 9 | def read_ptr(mu, address): 10 | return int.from_bytes(mu.mem_read(address, 4), byteorder='little') 11 | 12 | def read_byte_array(mu, address, size): 13 | return mu.mem_read(address, size) 14 | 15 | def read_utf8(mu, address): 16 | buffer_address = address 17 | buffer_read_size = 32 18 | buffer = b"" 19 | null_pos = None 20 | 21 | # Keep reading until we read something that contains a null terminator. 22 | while null_pos is None: 23 | buf_read = mu.mem_read(buffer_address, buffer_read_size) 24 | if b'\x00' in buf_read: 25 | null_pos = len(buffer) + buf_read.index(b'\x00') 26 | buffer += buf_read 27 | buffer_address += buffer_read_size 28 | 29 | return buffer[:null_pos].decode("utf-8") 30 | 31 | def read_uints(mu, address, num = 1): 32 | data = mu.mem_read(address, num * 4) 33 | return struct.unpack("I"*num,data) 34 | 35 | def write_utf8(mu, address, value): 36 | mu.mem_write(address, value.encode(encoding="utf-8") + b"\x00") 37 | 38 | def write_uints(mu, address, num): 39 | l = [] 40 | if not isinstance(num, list): 41 | l = [num] 42 | else: 43 | l = num 44 | 45 | for v in l: 46 | mu.mem_write(address, int(v).to_bytes(4, byteorder='little')) 47 | address += 4 48 | -------------------------------------------------------------------------------- /samples/debug_utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from unicorn.arm_const import * 4 | 5 | logger = logging.getLogger(__name__) 6 | 7 | 8 | def hook_code(mu, address, size, user_data): 9 | instruction = mu.mem_read(address, size) 10 | instruction_str = ''.join('{:02x} '.format(x) for x in instruction) 11 | 12 | logger.debug('# Tracing instruction at 0x%x, instruction size = 0x%x, instruction = %s' % 13 | (address, size, instruction_str)) 14 | 15 | if instruction == b"\x00\x00\x00\x00": 16 | logger.error("Uh oh, we messed up.") 17 | mu.emu_stop() 18 | 19 | 20 | def hook_unmapped(mu, access, address, length, value, context): 21 | pc = mu.reg_read(UC_ARM_REG_PC) 22 | 23 | logger.debug("mem unmapped: pc: %x access: %x address: %x length: %x value: %x" % 24 | (pc, access, address, length, value)) 25 | mu.emu_stop() 26 | return True 27 | 28 | 29 | def hook_mem_write(uc, access, address, size, value, user_data): 30 | pc = uc.reg_read(UC_ARM_REG_PC) 31 | logger.debug(">>> Memory WRITE at 0x%x, data size = %u, data value = 0x%x, pc: %x" % (address, size, value, pc)) 32 | 33 | 34 | def hook_mem_read(uc, access, address, size, value, user_data): 35 | pc = uc.reg_read(UC_ARM_REG_PC) 36 | data = uc.mem_read(address, size) 37 | logger.debug(">>> Memory READ at 0x%x, data size = %u, pc: %x, data value = 0x%s" % (address, size, pc, data.hex())) 38 | 39 | 40 | def hook_interrupt(uc, intno, data): 41 | logger.debug(">>> Triggering interrupt %d" % intno) 42 | return 43 | -------------------------------------------------------------------------------- /androidemu/java/java_method_def.py: -------------------------------------------------------------------------------- 1 | class JavaMethodDef: 2 | 3 | def __init__(self, func_name, func, name, signature, native, args_list=None, modifier=None, ignore=None): 4 | self.jvm_id = None # Assigned by JavaClassDef. 5 | self.func_name = func_name 6 | self.func = func 7 | self.name = name 8 | self.signature = signature 9 | self.native = native 10 | self.native_addr = None 11 | self.args_list = args_list 12 | self.modifier = modifier 13 | self.ignore = ignore 14 | 15 | 16 | def java_method_def(name, signature, native=False, args_list=None, modifier=None, ignore=False): 17 | def java_method_def_real(func): 18 | def native_wrapper(self, emulator, *argv): 19 | return emulator.call_native( 20 | native_wrapper.jvm_method.native_addr, 21 | emulator.java_vm.jni_env.address_ptr, # JNIEnv* 22 | 0xFA, # this, TODO: Implement proper "this", a reference to the Java object inside which this native 23 | # method has been declared in 24 | *argv # Extra args. 25 | ) 26 | 27 | def normal_wrapper(*args, **kwargs): 28 | result = func(*args, **kwargs) 29 | return result 30 | 31 | wrapper = native_wrapper if native else normal_wrapper 32 | wrapper.jvm_method = JavaMethodDef(func.__name__, wrapper, name, signature, native, 33 | args_list=args_list, 34 | modifier=modifier, 35 | ignore=ignore) 36 | return wrapper 37 | 38 | return java_method_def_real 39 | -------------------------------------------------------------------------------- /androidemu/java/classes/method.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from androidemu.emulator_error import EmulatorError 4 | from androidemu.java.classes.executable import Executable 5 | from androidemu.java.java_class_def import JavaClassDef 6 | from androidemu.java.java_field_def import JavaFieldDef 7 | from androidemu.java.java_method_def import java_method_def, JavaMethodDef 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class Method(metaclass=JavaClassDef, 13 | jvm_name='java/lang/reflect/Method', 14 | jvm_fields=[ 15 | JavaFieldDef('slot', 'I', False, ignore=True), 16 | JavaFieldDef('declaringClass', 'Ljava/lang/Class;', False), 17 | ], 18 | jvm_super=Executable): 19 | 20 | def __init__(self, clazz: JavaClassDef, method: JavaMethodDef): 21 | super().__init__() 22 | self._clazz = clazz 23 | self._method = method 24 | self.slot = method.jvm_id 25 | self.declaringClass = self._clazz 26 | self.accessFlags = method.modifier 27 | 28 | @staticmethod 29 | @java_method_def( 30 | name="getMethodModifiers", 31 | signature="(Ljava/lang/Class;I)I", 32 | args_list=['jobject', 'jint'], 33 | ignore=True 34 | ) 35 | def get_method_modifiers(emu, clazz_obj, jvm_method_id): 36 | clazz = clazz_obj.value 37 | method = clazz.find_method_by_id(jvm_method_id) 38 | 39 | logger.debug('get_method_modifiers(%s, %s)' % (clazz.jvm_name, method.name)) 40 | 41 | if method.modifier is None: 42 | raise EmulatorError('No modifier was given to class %s method %s' % (clazz.jvm_name, method.name)) 43 | 44 | return method.modifier 45 | -------------------------------------------------------------------------------- /androidemu/java/reference_table.py: -------------------------------------------------------------------------------- 1 | from androidemu.java.jni_ref import * 2 | 3 | 4 | class ReferenceTable: 5 | 6 | """ 7 | :type _table dict[int, jobject|None] 8 | """ 9 | def __init__(self, start=1, max_entries=1024): 10 | self._table = dict() 11 | self._start = start 12 | self._size = max_entries 13 | 14 | def set(self, idx, newobj): 15 | if not isinstance(newobj, jobject): 16 | raise ValueError('Expected a jobject.') 17 | 18 | if idx not in self._table: 19 | raise ValueError('Expected a index.') 20 | 21 | self._table[idx] = newobj 22 | 23 | 24 | def add(self, obj): 25 | if not isinstance(obj, jobject): 26 | raise ValueError('Expected a jobject.') 27 | 28 | # Search a free index. 29 | index = self._start 30 | while index in self._table: 31 | index += 1 32 | 33 | # Add to table. 34 | self._table[index] = obj 35 | 36 | # Return local reference. 37 | return index 38 | 39 | def remove(self, obj): 40 | # TODO: Test 41 | index = None 42 | for i in range(self._start, self._start + len(self._table)): 43 | if self._table[i] is obj: 44 | index = i 45 | break 46 | 47 | if index is None: 48 | return False 49 | 50 | self._table[index] = None 51 | return True 52 | 53 | def get(self, idx): 54 | if idx not in self._table: 55 | return None 56 | 57 | return self._table[idx] 58 | 59 | def in_range(self, idx): 60 | return self._start <= idx < self._start + self._size 61 | 62 | def clear(self): 63 | self._table.clear() 64 | -------------------------------------------------------------------------------- /androidemu/java/jni_ref.py: -------------------------------------------------------------------------------- 1 | class jvalue: 2 | 3 | def __init__(self, value=None): 4 | self.value = value 5 | 6 | 7 | class jobject: 8 | 9 | def __init__(self, value=None): 10 | self.value = value 11 | 12 | 13 | class jclass(jobject): 14 | 15 | def __init__(self, value=None): 16 | super().__init__(value) 17 | 18 | 19 | class jstring(jobject): 20 | 21 | def __init__(self, value=None): 22 | super().__init__(value) 23 | 24 | 25 | class jarray(jobject): 26 | 27 | def __init__(self, value=None): 28 | super().__init__(value) 29 | 30 | 31 | class jobjectArray(jarray): 32 | 33 | def __init__(self, value=None): 34 | super().__init__(value) 35 | 36 | 37 | class jbooleanArray(jarray): 38 | 39 | def __init__(self, value=None): 40 | super().__init__(value) 41 | 42 | 43 | class jbyteArray(jarray): 44 | 45 | def __init__(self, value=None): 46 | super().__init__(value) 47 | 48 | 49 | class jcharArray(jarray): 50 | 51 | def __init__(self, value=None): 52 | super().__init__(value) 53 | 54 | 55 | class jshortArray(jarray): 56 | 57 | def __init__(self, value=None): 58 | super().__init__(value) 59 | 60 | 61 | class jintArray(jarray): 62 | 63 | def __init__(self, value=None): 64 | super().__init__(value) 65 | 66 | 67 | class jlongArray(jarray): 68 | 69 | def __init__(self, value=None): 70 | super().__init__(value) 71 | 72 | 73 | class jfloatArray(jarray): 74 | 75 | def __init__(self, value=None): 76 | super().__init__(value) 77 | 78 | 79 | class jdoubleArray(jarray): 80 | 81 | def __init__(self, value=None): 82 | super().__init__(value) 83 | 84 | 85 | class jthrowable(jobject): 86 | 87 | def __init__(self, value=None): 88 | super().__init__(value) 89 | 90 | 91 | -------------------------------------------------------------------------------- /androidemu/java/java_vm.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from androidemu.hooker import Hooker 4 | from androidemu.java.helpers.native_method import native_method 5 | from androidemu.java.java_classloader import JavaClassLoader 6 | from androidemu.java.jni_const import * 7 | from androidemu.java.jni_env import JNIEnv 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | # https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html 13 | # This class attempts to mimic the JNIInvokeInterface table. 14 | class JavaVM: 15 | 16 | """ 17 | :type class_loader JavaClassLoader 18 | :type hooker Hooker 19 | """ 20 | def __init__(self, emu, class_loader, hooker): 21 | (self.address_ptr, self.address) = hooker.write_function_table({ 22 | 3: self.destroy_java_vm, 23 | 4: self.attach_current_thread, 24 | 5: self.detach_current_thread, 25 | 6: self.get_env, 26 | 7: self.attach_current_thread 27 | }) 28 | 29 | self.jni_env = JNIEnv(emu, class_loader, hooker) 30 | 31 | @native_method 32 | def destroy_java_vm(self, mu): 33 | raise NotImplementedError() 34 | 35 | @native_method 36 | def attach_current_thread(self, mu): 37 | raise NotImplementedError() 38 | 39 | @native_method 40 | def detach_current_thread(self, mu): 41 | # TODO: NooOO idea. 42 | pass 43 | 44 | @native_method 45 | def get_env(self, mu, java_vm, env, version): 46 | logger.debug("java_vm: 0x%08x" % java_vm) 47 | logger.debug("env: 0x%08x" % env) 48 | logger.debug("version: 0x%08x" % version) 49 | 50 | mu.mem_write(env, self.jni_env.address_ptr.to_bytes(4, byteorder='little')) 51 | 52 | logger.debug("JavaVM->GetENV() was called!") 53 | 54 | return JNI_OK 55 | 56 | @native_method 57 | def attach_current_thread_as_daemon(self, mu): 58 | raise NotImplementedError() 59 | -------------------------------------------------------------------------------- /androidemu/cpu/syscall_handlers.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from unicorn import * 4 | from unicorn.arm_const import * 5 | 6 | from androidemu.cpu.interrupt_handler import InterruptHandler 7 | from androidemu.cpu.syscall_handler import SyscallHandler 8 | from androidemu.utils import memory_helpers 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | class SyscallHandlers: 14 | 15 | """ 16 | :type interrupt_handler InterruptHandler 17 | """ 18 | def __init__(self, interrupt_handler): 19 | self._handlers = dict() 20 | interrupt_handler.set_handler(2, self._handle_syscall) 21 | 22 | def set_handler(self, idx, name, arg_count, callback): 23 | self._handlers[idx] = SyscallHandler(idx, name, arg_count, callback) 24 | 25 | def _handle_syscall(self, mu): 26 | idx = mu.reg_read(UC_ARM_REG_R7) 27 | args = [mu.reg_read(reg_idx) for reg_idx in range(UC_ARM_REG_R0, UC_ARM_REG_R6 + 1)] 28 | 29 | if idx in self._handlers: 30 | handler = self._handlers[idx] 31 | args = args[:handler.arg_count] 32 | args_formatted = ", ".join(["%08x" % arg for arg in args]) 33 | logger.debug("Executing syscall %s(%s) at 0x%x" % (handler.name, args_formatted, 34 | mu.reg_read(UC_ARM_REG_PC))) 35 | 36 | try: 37 | result = handler.callback(mu, *args) 38 | except: 39 | logger.error("An error occured during in %x syscall hander, stopping emulation" % idx) 40 | mu.emu_stop() 41 | raise 42 | 43 | if result is not None: 44 | mu.reg_write(UC_ARM_REG_R0, result) 45 | else: 46 | 47 | error = "Unhandled syscall 0x%x (%u) at 0x%x, stopping emulation" % (idx, idx, 48 | mu.reg_read(UC_ARM_REG_PC)) 49 | mu.emu_stop() 50 | raise RuntimeError(error) 51 | -------------------------------------------------------------------------------- /androidemu/native/memory.py: -------------------------------------------------------------------------------- 1 | from unicorn import Uc, UC_PROT_READ, UC_PROT_WRITE 2 | from androidemu.cpu.syscall_handlers import SyscallHandlers 3 | from androidemu.native.memory_heap import UnicornSimpleHeap 4 | 5 | 6 | class NativeMemory: 7 | 8 | """ 9 | :type mu Uc 10 | :type syscall_handler SyscallHandlers 11 | """ 12 | def __init__(self, mu, memory_base, memory_size, syscall_handler): 13 | self._mu = mu 14 | self._heap = UnicornSimpleHeap(mu, memory_base, memory_base + memory_size) 15 | self._memory_base = memory_base 16 | self._memory_current = memory_base 17 | self._memory_size = memory_size 18 | self._syscall_handler = syscall_handler 19 | self._syscall_handler.set_handler(0x5B, "munmap", 2, self._handle_munmap) 20 | self._syscall_handler.set_handler(0x7D, "mprotect", 3, self._handle_mprotect) 21 | self._syscall_handler.set_handler(0xC0, "mmap2", 6, self._handle_mmap2) 22 | self._syscall_handler.set_handler(0xDC, "madvise", 3, self._handle_madvise) 23 | 24 | def allocate(self, length, prot=UC_PROT_READ | UC_PROT_WRITE): 25 | return self._heap.malloc(length, prot) 26 | 27 | def _handle_munmap(self, uc, addr, len_in): 28 | # TODO: Use len_in 29 | self._heap.free(addr) 30 | 31 | def _handle_mmap2(self, mu, addr, length, prot, flags, fd, offset): 32 | """ 33 | void *mmap2(void *addr, size_t length, int prot, int flags, int fd, off_t pgoffset); 34 | """ 35 | 36 | # MAP_FILE 0 37 | # MAP_SHARED 0x01 38 | # MAP_PRIVATE 0x02 39 | # MAP_FIXED 0x10 40 | # MAP_ANONYMOUS 0x20 41 | 42 | return self._heap.malloc(length, prot) 43 | 44 | def _handle_madvise(self, mu, start, len_in, behavior): 45 | """ 46 | int madvise(void *addr, size_t length, int advice); 47 | The kernel is free to ignore the advice. 48 | On success madvise() returns zero. On error, it returns -1 and errno is set appropriately. 49 | """ 50 | # We don't need your advise. 51 | return 0 52 | 53 | def _handle_mprotect(self, mu, addr, len_in, prot): 54 | """ 55 | int mprotect(void *addr, size_t len, int prot); 56 | 57 | mprotect() changes protection for the calling process's memory page(s) containing any part of the address 58 | range in the interval [addr, addr+len-1]. addr must be aligned to a page boundary. 59 | """ 60 | self._heap.protect(addr, len_in, prot) 61 | return 0 62 | 63 | def free(self, addr): 64 | self._heap.free(addr) 65 | -------------------------------------------------------------------------------- /samples/example_jiagu.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import posixpath 3 | import sys 4 | 5 | from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPED 6 | from unicorn.arm_const import * 7 | 8 | from androidemu.emulator import Emulator 9 | from androidemu.java.java_class_def import JavaClassDef 10 | from androidemu.java.java_method_def import java_method_def 11 | 12 | 13 | # Create java class. 14 | from samples import debug_utils 15 | 16 | 17 | class MainActivity(metaclass=JavaClassDef, jvm_name='local/myapp/testnativeapp/MainActivity'): 18 | 19 | def __init__(self): 20 | pass 21 | 22 | @java_method_def(name='stringFromJNI', signature='()Ljava/lang/String;', native=True) 23 | def string_from_jni(self, mu): 24 | pass 25 | 26 | def test(self): 27 | pass 28 | 29 | 30 | # Configure logging 31 | logging.basicConfig( 32 | stream=sys.stdout, 33 | level=logging.DEBUG, 34 | format="%(asctime)s %(levelname)7s %(name)34s | %(message)s" 35 | ) 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | # Initialize emulator 40 | emulator = Emulator( 41 | vfp_inst_set=True, 42 | vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs") 43 | ) 44 | 45 | # Register Java class. 46 | emulator.java_classloader.add_class(MainActivity) 47 | 48 | # Load all libraries. 49 | emulator.load_library("example_binaries/libdl.so") 50 | emulator.load_library("example_binaries/libc.so") 51 | emulator.load_library("example_binaries/libstdc++.so") 52 | emulator.load_library("example_binaries/libm.so") 53 | lib_module = emulator.load_library("example_binaries/libnative-lib_jni.so") 54 | 55 | # Show loaded modules. 56 | logger.info("Loaded modules:") 57 | 58 | for module in emulator.modules: 59 | logger.info("=> 0x%08x - %s" % (module.base, module.filename)) 60 | 61 | # Debug 62 | # emulator.mu.hook_add(UC_HOOK_CODE, debug_utils.hook_code) 63 | # emulator.mu.hook_add(UC_HOOK_MEM_UNMAPPED, debug_utils.hook_unmapped) 64 | # emulator.mu.hook_add(UC_HOOK_MEM_WRITE, debug_utils.hook_mem_write) 65 | # emulator.mu.hook_add(UC_HOOK_MEM_READ, debug_utils.hook_mem_read) 66 | 67 | try: 68 | # Run JNI_OnLoad. 69 | # JNI_OnLoad will call 'RegisterNatives'. 70 | emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00) 71 | emulator.mu.hook_add(UC_HOOK_MEM_UNMAPPED, debug_utils.hook_unmapped) 72 | 73 | # Do native stuff. 74 | emulator.mu.hook_add(UC_HOOK_CODE, debug_utils.hook_code) 75 | main_activity = MainActivity() 76 | logger.info("Response from JNI call: %s" % main_activity.string_from_jni(emulator)) 77 | 78 | # Dump natives found. 79 | logger.info("Exited EMU.") 80 | logger.info("Native methods registered to MainActivity:") 81 | 82 | for method in MainActivity.jvm_methods.values(): 83 | if method.native: 84 | logger.info("- [0x%08x] %s - %s" % (method.native_addr, method.name, method.signature)) 85 | except UcError as e: 86 | print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC)) 87 | raise -------------------------------------------------------------------------------- /samples/example_jni.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import posixpath 3 | import sys 4 | 5 | from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPED 6 | from unicorn.arm_const import * 7 | 8 | from androidemu.emulator import Emulator 9 | from androidemu.java.java_class_def import JavaClassDef 10 | from androidemu.java.java_method_def import java_method_def 11 | 12 | 13 | # Create java class. 14 | from samples import debug_utils 15 | 16 | 17 | class MainActivity(metaclass=JavaClassDef, jvm_name='local/myapp/testnativeapp/MainActivity'): 18 | 19 | def __init__(self): 20 | pass 21 | 22 | @java_method_def(name='stringFromJNI', signature='()Ljava/lang/String;', native=True) 23 | def string_from_jni(self, mu): 24 | pass 25 | 26 | def test(self): 27 | pass 28 | 29 | 30 | # Configure logging 31 | logging.basicConfig( 32 | stream=sys.stdout, 33 | level=logging.DEBUG, 34 | format="%(asctime)s %(levelname)7s %(name)34s | %(message)s" 35 | ) 36 | 37 | logger = logging.getLogger(__name__) 38 | 39 | # Initialize emulator 40 | emulator = Emulator( 41 | vfp_inst_set=True, 42 | vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs") 43 | ) 44 | 45 | # Register Java class. 46 | emulator.java_classloader.add_class(MainActivity) 47 | 48 | # Load all libraries. 49 | emulator.load_library("example_binaries/libdl.so") 50 | emulator.load_library("example_binaries/libc.so") 51 | emulator.load_library("example_binaries/libstdc++.so") 52 | emulator.load_library("example_binaries/libm.so") 53 | lib_module = emulator.load_library("example_binaries/libnative-lib_jni.so") 54 | 55 | # Show loaded modules. 56 | logger.info("Loaded modules:") 57 | 58 | for module in emulator.modules: 59 | logger.info("=> 0x%08x - %s" % (module.base, module.filename)) 60 | 61 | # Debug 62 | # emulator.mu.hook_add(UC_HOOK_CODE, debug_utils.hook_code) 63 | # emulator.mu.hook_add(UC_HOOK_MEM_UNMAPPED, debug_utils.hook_unmapped) 64 | # emulator.mu.hook_add(UC_HOOK_MEM_WRITE, debug_utils.hook_mem_write) 65 | # emulator.mu.hook_add(UC_HOOK_MEM_READ, debug_utils.hook_mem_read) 66 | 67 | try: 68 | # Run JNI_OnLoad. 69 | # JNI_OnLoad will call 'RegisterNatives'. 70 | emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00) 71 | emulator.mu.hook_add(UC_HOOK_MEM_UNMAPPED, debug_utils.hook_unmapped) 72 | 73 | # Do native stuff. 74 | emulator.mu.hook_add(UC_HOOK_CODE, debug_utils.hook_code) 75 | main_activity = MainActivity() 76 | logger.info("Response from JNI call: %s" % main_activity.string_from_jni(emulator)) 77 | 78 | # Dump natives found. 79 | logger.info("Exited EMU.") 80 | logger.info("Native methods registered to MainActivity:") 81 | 82 | for method in MainActivity.jvm_methods.values(): 83 | if method.native: 84 | logger.info("- [0x%08x] %s - %s" % (method.native_addr, method.name, method.signature)) 85 | except UcError as e: 86 | print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC)) 87 | raise -------------------------------------------------------------------------------- /androidemu/native/hooks.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from androidemu.hooker import Hooker 4 | from androidemu.native.memory import NativeMemory 5 | 6 | from androidemu.java.helpers.native_method import native_method 7 | from androidemu.utils import memory_helpers 8 | 9 | logger = logging.getLogger(__name__) 10 | 11 | 12 | class NativeHooks: 13 | """ 14 | :type memory NativeMemory 15 | :type modules Modules 16 | :type hooker Hooker 17 | """ 18 | 19 | def __init__(self, emu, memory, modules, hooker): 20 | self._module_mgr = modules 21 | self._emu = emu 22 | self._memory = memory 23 | self.atexit = [] 24 | 25 | modules.add_symbol_hook('__system_property_get', hooker.write_function(self.system_property_get) + 1) 26 | modules.add_symbol_hook('dladdr', hooker.write_function(self.nop('dladdr')) + 1) 27 | modules.add_symbol_hook('dlsym', hooker.write_function(self.nop('dlsym')) + 1) 28 | modules.add_symbol_hook('dlopen', hooker.write_function(self.mydlopen) + 1) 29 | modules.add_symbol_hook('pthread_create', hooker.write_function(self.nop('pthread_create')) + 1) 30 | modules.add_symbol_hook('pthread_join', hooker.write_function(self.nop('pthread_join')) + 1) 31 | modules.add_symbol_hook('vfprintf', hooker.write_function(self.nop('vfprintf')) + 1) 32 | modules.add_symbol_hook('fprintf', hooker.write_function(self.nop('fprintf')) + 1) 33 | modules.add_symbol_hook('dladdr', hooker.write_function(self.dladdr) + 1) 34 | 35 | @native_method 36 | def system_property_get(self, uc, name_ptr, buf_ptr): 37 | name = memory_helpers.read_utf8(uc, name_ptr) 38 | logger.debug("Called __system_property_get(%s, 0x%x)" % (name, buf_ptr)) 39 | 40 | if name in self._emu.system_properties: 41 | memory_helpers.write_utf8(uc, buf_ptr, self._emu.system_properties[name]) 42 | else: 43 | raise ValueError('%s was not found in system_properties dictionary.' % name) 44 | 45 | return None 46 | 47 | @native_method 48 | def mydlopen(self, uc, path): 49 | path = memory_helpers.read_utf8(uc, path) 50 | logger.debug("Called dlopen(%s)" % path) 51 | return None 52 | 53 | @native_method 54 | def dladdr(self, uc, addr, info): 55 | infos = memory_helpers.read_uints(uc, info, 4) 56 | Dl_info = {} 57 | 58 | nm = self._emu.native_memory 59 | isfind = False 60 | for mod in self._module_mgr.modules: 61 | if mod.base <= addr < mod.base + mod.size: 62 | dli_fname = nm.allocate(len(mod.filename) + 1) 63 | memory_helpers.write_utf8(uc, dli_fname, mod.filename + '\x00') 64 | memory_helpers.write_uints(uc, addr, [dli_fname, mod.base, 0, 0]) 65 | return 1 66 | 67 | 68 | def nop(self, name): 69 | @native_method 70 | def nop_inside(emu): 71 | raise NotImplementedError('Symbol hook not implemented %s' % name) 72 | return nop_inside 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidNativeEmu 2 | [ 中文README & 教程? ](README_cn.md) 3 | 4 | 5 | Allows you to partly emulate an Android native library. 6 | 7 | This is an educational project to learn more about the ELF file format and [Unicorn](https://github.com/unicorn-engine/unicorn). 8 | 9 | ## Features 10 | 11 | - Emulation of the [JNI Invocation API](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html) so `JNI_OnLoad` can be called properly. 12 | - Emulation of native memory for malloc / memcpy. 13 | - Emulation of syscalls (SVC #0) instruction. 14 | - Hooking through the symbol table. 15 | - All JavaVM, JNIEnv and hooked functions are handled by python. 16 | - Enable VFP support. 17 | 18 | ## My Changes 19 | - Add init_array support depends on Relocation information. 20 | - Add support of modify object value by reference id. 21 | - Implement getcpu() syscall 22 | - Implement set_byte_array_region 23 | - Register Function failed would't raise an error(beacuse most jni functions are not used.) 24 | - samples:添加抖音 X-Gorgen 调用实例 25 | - [ 中文README ](README_cn.md) 26 | 27 | ## Usage 28 | 29 | > In the future this will be possible through pypi. 30 | 31 | Make sure you are using python 3.7. 32 | 33 | 1. Clone the repository 34 | 2. Run `pip install -r requirements.txt` 35 | 3. Run `python example.py` 36 | 37 | > If you have trouble getting the `keystone-engine` dependency on Windows (as I did): 38 | > 1. Clone their [repository](https://github.com/keystone-engine/keystone) 39 | > 2. Open a terminal in `bindings/python` 40 | > 3. Run `python setup.py install` (Make sure you are using python 3.7) 41 | > 4. Download their `Windows - Core engine` package [here](http://www.keystone-engine.org/download/) for your python arch. 42 | > 5. Put the `keystone.dll` in `C:\location_to_python\Lib\site-packages\keystone\`. 43 | 44 | ## TODO 45 | 46 | - Improve file descriptors in `vfs/file_system.py` so they are re-useable. 47 | - Add a way for the VirtualFileSystem to give back dynamic files, such as `/proc/self/status`, `/proc/self/status` but also `/dev/urandom`. 48 | - Library consumers must be able to easily rebuild the needed Java classes for a native library, which are used by the native library through the JNIEnv. 49 | - ~~Classes~~ 50 | - ~~Objects~~ 51 | - ~~Methods~~ 52 | - ~~Native methods~~ 53 | - Fields 54 | - Types 55 | - Reflection 56 | 57 | ## Dependencies 58 | 59 | - [Unicorn CPU emulator framework](https://github.com/unicorn-engine/unicorn) 60 | - [Keystone assembler framework](https://github.com/keystone-engine/keystone) 61 | 62 | ## Resources 63 | 64 | All resources used while developing AndroidNativeEmu. 65 | 66 | ### Text sources 67 | - https://greek0.net/elf.html 68 | - https://stackoverflow.com/questions/13908276/loading-elf-file-in-c-in-user-space 69 | - https://programtalk.com/python-examples/pyelftools.elftools.elf.relocation.Relocation/ 70 | - http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044f/IHI0044F_aaelf.pdf 71 | - https://wiki.osdev.org/ELF_Tutorial 72 | - https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html 73 | - https://android.googlesource.com/platform/dalvik/+/donut-release/vm/Jni.c 74 | 75 | ### Code sources 76 | - https://github.com/lunixbochs/usercorn 77 | - https://github.com/slick1015/pad_unpacker (SVC 0 instruction) 78 | -------------------------------------------------------------------------------- /androidemu/java/java_class_def.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | import itertools 3 | import logging 4 | 5 | logger = logging.getLogger(__name__) 6 | 7 | 8 | class JavaClassDef(type): 9 | next_jvm_id = itertools.count(start=1) 10 | next_jvm_method_id = itertools.count(start=0xd2000000, step=4) 11 | next_jvm_field_id = itertools.count(start=0xe2000000, step=4) 12 | 13 | def __init__(cls, name, base, ns, jvm_name=None, jvm_fields=None, jvm_ignore=False, jvm_super=None): 14 | cls.jvm_id = next(JavaClassDef.next_jvm_id) 15 | cls.jvm_name = jvm_name 16 | cls.jvm_methods = dict() 17 | cls.jvm_fields = dict() 18 | cls.jvm_ignore = jvm_ignore 19 | cls.jvm_super = jvm_super 20 | 21 | # Register all defined Java methods. 22 | for func in inspect.getmembers(cls, predicate=inspect.isfunction): 23 | if hasattr(func[1], 'jvm_method'): 24 | method = func[1].jvm_method 25 | method.jvm_id = next(JavaClassDef.next_jvm_method_id) 26 | cls.jvm_methods[method.jvm_id] = method 27 | 28 | # Register all defined Java fields. 29 | if jvm_fields is not None: 30 | for jvm_field in jvm_fields: 31 | jvm_field.jvm_id = next(JavaClassDef.next_jvm_field_id) 32 | cls.jvm_fields[jvm_field.jvm_id] = jvm_field 33 | 34 | type.__init__(cls, name, base, ns) 35 | 36 | def __new__(mcs, name, base, ns, **kargs): 37 | return type.__new__(mcs, name, base, ns) 38 | 39 | def register_native(self, name, signature, ptr_func): 40 | found = False 41 | found_method = None 42 | 43 | # Search for a defined jvm method. 44 | for method in self.jvm_methods.values(): 45 | if method.name == name and method.signature == signature: 46 | method.native_addr = ptr_func 47 | found = True 48 | found_method = method 49 | break 50 | 51 | if not found: 52 | x = "Register native ('%s', '%s') failed on class %s." % (name, signature, self.__name__) 53 | logger.warning(x) 54 | return 55 | # raise RuntimeError("Register native ('%s', '%s') failed on class %s." % (name, signature, self.__name__)) 56 | logger.debug("Registered native function ('%s', '%s') to %s.%s" % (name, signature, 57 | self.__name__, found_method.func_name)) 58 | 59 | def find_method(cls, name, signature): 60 | for method in cls.jvm_methods.values(): 61 | if method.name == name and method.signature == signature: 62 | return method 63 | 64 | return None 65 | 66 | def find_method_by_id(cls, jvm_id): 67 | return cls.jvm_methods[jvm_id] 68 | 69 | def find_field(cls, name, signature, is_static): 70 | for field in cls.jvm_fields.values(): 71 | if field.name == name and field.signature == signature and field.is_static == is_static: 72 | return field 73 | 74 | return None 75 | 76 | def find_field_by_id(cls, jvm_id): 77 | try: 78 | if cls.jvm_super is not None: 79 | return cls.jvm_super.find_field_by_id(jvm_id) 80 | except KeyError: 81 | pass 82 | 83 | return cls.jvm_fields[jvm_id] 84 | -------------------------------------------------------------------------------- /androidemu/vfs/file_helpers.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | from os import stat_result 4 | 5 | from unicorn import Uc 6 | 7 | from androidemu.config import WRITE_FSTAT_TIMES 8 | 9 | 10 | def stat64(path): 11 | meta_path = path + '.meta_emu' 12 | 13 | if not os.path.exists(meta_path): 14 | meta_path_dir = os.path.dirname(meta_path) 15 | 16 | if not os.path.isdir(meta_path_dir): 17 | os.makedirs(meta_path_dir) 18 | 19 | with open(meta_path, 'w') as f: 20 | json.dump({ 21 | 'st_dev': 0, 22 | '__st_ino': 0, 23 | 'st_mode': 0, 24 | 'st_nlink': 0, 25 | 'st_uid': 0, 26 | 'st_gid': 0, 27 | 'st_rdev': 0, 28 | 'st_size': 0, 29 | 'st_blksize': 0, 30 | 'st_blocks': 0, 31 | 'st_atime': 0, 32 | 'st_atime_ns': 0, 33 | 'st_mtime': 0, 34 | 'st_mtime_ns': 0, 35 | 'st_ctime': 0, 36 | 'st_ctime_ns': 0, 37 | 'st_ino': 0 38 | }, fp=f, indent=4) 39 | 40 | with open(meta_path, 'r') as f: 41 | return json.load(fp=f) 42 | 43 | 44 | def stat_to_memory(uc: Uc, buf_ptr, stat, write_times): 45 | uc.mem_write(buf_ptr, stat['st_dev'].to_bytes(8, byteorder='little')) 46 | uc.mem_write(buf_ptr + 8, int(0).to_bytes(4, byteorder='little')) # PAD 4 47 | uc.mem_write(buf_ptr + 12, stat['__st_ino'].to_bytes(4, byteorder='little')) 48 | uc.mem_write(buf_ptr + 16, stat['st_mode'].to_bytes(4, byteorder='little')) 49 | uc.mem_write(buf_ptr + 20, stat['st_nlink'].to_bytes(4, byteorder='little')) 50 | uc.mem_write(buf_ptr + 24, stat['st_uid'].to_bytes(4, byteorder='little')) 51 | uc.mem_write(buf_ptr + 28, stat['st_gid'].to_bytes(4, byteorder='little')) 52 | uc.mem_write(buf_ptr + 32, stat['st_rdev'].to_bytes(8, byteorder='little')) 53 | uc.mem_write(buf_ptr + 40, int(0).to_bytes(4, byteorder='little')) # PAD 4 54 | uc.mem_write(buf_ptr + 44, int(0).to_bytes(4, byteorder='little')) # PAD 4 55 | uc.mem_write(buf_ptr + 48, stat['st_size'].to_bytes(8, byteorder='little')) 56 | uc.mem_write(buf_ptr + 56, stat['st_blksize'].to_bytes(4, byteorder='little')) 57 | uc.mem_write(buf_ptr + 60, int(0).to_bytes(4, byteorder='little')) # PAD 4 58 | uc.mem_write(buf_ptr + 64, stat['st_blocks'].to_bytes(8, byteorder='little')) 59 | 60 | if write_times: 61 | uc.mem_write(buf_ptr + 72, stat['st_atime'].to_bytes(4, byteorder='little')) 62 | uc.mem_write(buf_ptr + 76, stat['st_atime_ns'].to_bytes(4, byteorder='little')) 63 | uc.mem_write(buf_ptr + 80, stat['st_mtime'].to_bytes(4, byteorder='little')) 64 | uc.mem_write(buf_ptr + 84, stat['st_mtime_ns'].to_bytes(4, byteorder='little')) 65 | uc.mem_write(buf_ptr + 88, stat['st_ctime'].to_bytes(4, byteorder='little')) 66 | uc.mem_write(buf_ptr + 92, stat['st_ctime_ns'].to_bytes(4, byteorder='little')) 67 | else: 68 | uc.mem_write(buf_ptr + 72, int(0).to_bytes(4, byteorder='little')) 69 | uc.mem_write(buf_ptr + 76, int(0).to_bytes(4, byteorder='little')) 70 | uc.mem_write(buf_ptr + 80, int(0).to_bytes(4, byteorder='little')) 71 | uc.mem_write(buf_ptr + 84, int(0).to_bytes(4, byteorder='little')) 72 | uc.mem_write(buf_ptr + 88, int(0).to_bytes(4, byteorder='little')) 73 | uc.mem_write(buf_ptr + 92, int(0).to_bytes(4, byteorder='little')) 74 | 75 | uc.mem_write(buf_ptr + 96, stat['st_ino'].to_bytes(8, byteorder='little')) 76 | -------------------------------------------------------------------------------- /androidemu/hooker.py: -------------------------------------------------------------------------------- 1 | from keystone import Ks, KS_ARCH_ARM, KS_MODE_THUMB 2 | from unicorn import * 3 | from unicorn.arm_const import * 4 | 5 | STACK_OFFSET = 8 6 | 7 | 8 | # Utility class to create a bridge between ARM and Python. 9 | class Hooker: 10 | 11 | """ 12 | :type emu androidemu.emulator.Emulator 13 | """ 14 | def __init__(self, emu, base_addr, size): 15 | self._emu = emu 16 | self._keystone = Ks(KS_ARCH_ARM, KS_MODE_THUMB) 17 | self._size = size 18 | self._current_id = 0xFF00 19 | self._hooks = dict() 20 | self._hook_magic = base_addr 21 | self._hook_start = base_addr + 4 22 | self._hook_current = self._hook_start 23 | self._emu.mu.hook_add(UC_HOOK_CODE, self._hook, None, self._hook_start, self._hook_start + size) 24 | 25 | def _get_next_id(self): 26 | idx = self._current_id 27 | self._current_id += 1 28 | return idx 29 | 30 | def write_function(self, func): 31 | # Get the hook id. 32 | hook_id = self._get_next_id() 33 | hook_addr = self._hook_current 34 | 35 | # Create the ARM assembly code. 36 | # Make sure to update STACK_OFFSET if you change the PUSH/POP. 37 | asm = "PUSH {R4,LR}\n" \ 38 | "MOV R4, #" + hex(hook_id) + "\n" \ 39 | "IT AL\n" \ 40 | "POP {R4,PC}" 41 | 42 | asm_bytes_list, asm_count = self._keystone.asm(bytes(asm, encoding='ascii')) 43 | 44 | if asm_count != 4: 45 | raise ValueError("Expected asm_count to be 4 instead of %u." % asm_count) 46 | 47 | # Write assembly code to the emulator. 48 | self._emu.mu.mem_write(hook_addr, bytes(asm_bytes_list)) 49 | 50 | # Save results. 51 | self._hook_current += len(asm_bytes_list) 52 | self._hooks[hook_id] = func 53 | 54 | return hook_addr 55 | 56 | def write_function_table(self, table): 57 | if not isinstance(table, dict): 58 | raise ValueError("Expected a dictionary for the function table.") 59 | 60 | index_max = int(max(table, key=int)) + 1 61 | 62 | # First, we write every function and store its result address. 63 | hook_map = dict() 64 | 65 | for index, func in table.items(): 66 | hook_map[index] = self.write_function(func) 67 | 68 | # Then we write the function table. 69 | table_bytes = b"" 70 | table_address = self._hook_current 71 | 72 | for index in range(0, index_max): 73 | address = hook_map[index] if index in hook_map else 0 74 | table_bytes += int(address + 1).to_bytes(4, byteorder='little') # + 1 because THUMB. 75 | 76 | self._emu.mu.mem_write(table_address, table_bytes) 77 | self._hook_current += len(table_bytes) 78 | 79 | # Then we write the a pointer to the table. 80 | ptr_address = self._hook_current 81 | self._emu.mu.mem_write(ptr_address, table_address.to_bytes(4, byteorder='little')) 82 | self._hook_current += 4 83 | 84 | return ptr_address, table_address 85 | 86 | def _hook(self, mu, address, size, user_data): 87 | # Check if instruction is "IT AL" 88 | if size != 2 or self._emu.mu.mem_read(address, size) != b"\xE8\xBF": 89 | return 90 | 91 | # Find hook. 92 | hook_id = self._emu.mu.reg_read(UC_ARM_REG_R4) 93 | hook_func = self._hooks[hook_id] 94 | 95 | # Call hook. 96 | try: 97 | hook_func(self._emu) 98 | except: 99 | # Make sure we catch exceptions inside hooks and stop emulation. 100 | mu.emu_stop() 101 | raise 102 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/venv,python,pycharm+all 2 | 3 | ### PyCharm+all ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff 8 | .idea/**/workspace.xml 9 | .idea/**/tasks.xml 10 | .idea/**/usage.statistics.xml 11 | .idea/**/dictionaries 12 | .idea/**/shelf 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # CMake 28 | cmake-build-*/ 29 | 30 | # Mongo Explorer plugin 31 | .idea/**/mongoSettings.xml 32 | 33 | # File-based project format 34 | *.iws 35 | 36 | # IntelliJ 37 | out/ 38 | 39 | # mpeltonen/sbt-idea plugin 40 | .idea_modules/ 41 | 42 | # JIRA plugin 43 | atlassian-ide-plugin.xml 44 | 45 | # Cursive Clojure plugin 46 | .idea/replstate.xml 47 | 48 | # Crashlytics plugin (for Android Studio and IntelliJ) 49 | com_crashlytics_export_strings.xml 50 | crashlytics.properties 51 | crashlytics-build.properties 52 | fabric.properties 53 | 54 | # Editor-based Rest Client 55 | .idea/httpRequests 56 | 57 | ### PyCharm+all Patch ### 58 | # Ignores the whole .idea folder and all .iml files 59 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 60 | 61 | .idea/ 62 | 63 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 64 | 65 | *.iml 66 | modules.xml 67 | .idea/misc.xml 68 | *.ipr 69 | 70 | ### Python ### 71 | # Byte-compiled / optimized / DLL files 72 | __pycache__/ 73 | *.py[cod] 74 | *$py.class 75 | 76 | # C extensions 77 | *.so 78 | 79 | # Distribution / packaging 80 | .Python 81 | build/ 82 | develop-eggs/ 83 | dist/ 84 | downloads/ 85 | eggs/ 86 | .eggs/ 87 | lib/ 88 | lib64/ 89 | parts/ 90 | sdist/ 91 | var/ 92 | wheels/ 93 | *.egg-info/ 94 | .installed.cfg 95 | *.egg 96 | MANIFEST 97 | 98 | # PyInstaller 99 | # Usually these files are written by a python script from a template 100 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 101 | *.manifest 102 | *.spec 103 | 104 | # Installer logs 105 | pip-log.txt 106 | pip-delete-this-directory.txt 107 | 108 | # Unit test / coverage reports 109 | htmlcov/ 110 | .tox/ 111 | .coverage 112 | .coverage.* 113 | .cache 114 | nosetests.xml 115 | coverage.xml 116 | *.cover 117 | .hypothesis/ 118 | .pytest_cache/ 119 | 120 | # Translations 121 | *.mo 122 | *.pot 123 | 124 | # Django stuff: 125 | *.log 126 | local_settings.py 127 | db.sqlite3 128 | 129 | # Flask stuff: 130 | instance/ 131 | .webassets-cache 132 | 133 | # Scrapy stuff: 134 | .scrapy 135 | 136 | # Sphinx documentation 137 | docs/_build/ 138 | 139 | # PyBuilder 140 | target/ 141 | 142 | # Jupyter Notebook 143 | .ipynb_checkpoints 144 | 145 | # pyenv 146 | .python-version 147 | 148 | # celery beat schedule file 149 | celerybeat-schedule 150 | 151 | # SageMath parsed files 152 | *.sage.py 153 | 154 | # Environments 155 | .env 156 | .venv 157 | env/ 158 | venv/ 159 | ENV/ 160 | env.bak/ 161 | venv.bak/ 162 | 163 | # Spyder project settings 164 | .spyderproject 165 | .spyproject 166 | 167 | # Rope project settings 168 | .ropeproject 169 | 170 | # mkdocs documentation 171 | /site 172 | 173 | # mypy 174 | .mypy_cache/ 175 | 176 | ### Python Patch ### 177 | .venv/ 178 | 179 | ### venv ### 180 | # Virtualenv 181 | # http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ 182 | [Bb]in 183 | [Ii]nclude 184 | [Ll]ib 185 | [Ll]ib64 186 | [Ll]ocal 187 | [Ss]cripts 188 | pyvenv.cfg 189 | pip-selfcheck.json 190 | 191 | 192 | # End of https://www.gitignore.io/api/venv,python,pycharm+all 193 | 194 | # Custom 195 | private.py 196 | !samples/example_binaries/*.so -------------------------------------------------------------------------------- /androidemu/java/helpers/native_method.py: -------------------------------------------------------------------------------- 1 | import inspect 2 | 3 | from unicorn import Uc 4 | from unicorn.arm_const import * 5 | 6 | from androidemu.hooker import STACK_OFFSET 7 | from androidemu.java.java_class_def import JavaClassDef 8 | from androidemu.java.jni_const import JNI_ERR 9 | from androidemu.java.jni_ref import jobject, jstring, jobjectArray, jbyteArray 10 | 11 | 12 | def native_write_args(emu, *argv): 13 | amount = len(argv) 14 | 15 | if amount == 0: 16 | return 17 | 18 | if amount >= 1: 19 | native_write_arg_register(emu, UC_ARM_REG_R0, argv[0]) 20 | 21 | if amount >= 2: 22 | native_write_arg_register(emu, UC_ARM_REG_R1, argv[1]) 23 | 24 | if amount >= 3: 25 | native_write_arg_register(emu, UC_ARM_REG_R2, argv[2]) 26 | 27 | if amount >= 4: 28 | native_write_arg_register(emu, UC_ARM_REG_R3, argv[3]) 29 | 30 | if amount >= 5: 31 | sp_start = emu.mu.reg_read(UC_ARM_REG_SP) 32 | sp_current = sp_start - STACK_OFFSET # Need to offset because our hook pushes one register on the stack. 33 | 34 | for arg in argv[4:]: 35 | emu.mu.mem_write(sp_current - STACK_OFFSET, native_translate_arg(emu, arg).to_bytes(4, byteorder='little')) 36 | sp_current = sp_current - 4 37 | 38 | emu.mu.reg_write(UC_ARM_REG_SP, sp_current) 39 | 40 | 41 | def native_translate_arg(emu, val): 42 | if isinstance(val, int): 43 | return val 44 | elif isinstance(val, str): 45 | return emu.java_vm.jni_env.add_local_reference(jstring(val)) 46 | elif isinstance(val, list): 47 | return emu.java_vm.jni_env.add_local_reference(jobjectArray(val)) 48 | elif isinstance(val, bytearray): 49 | return emu.java_vm.jni_env.add_local_reference(jbyteArray(val)) 50 | elif isinstance(type(val), JavaClassDef): 51 | # TODO: Look into this, seems wrong.. 52 | return emu.java_vm.jni_env.add_local_reference(jobject(val)) 53 | elif isinstance(val, JavaClassDef): 54 | return emu.java_vm.jni_env.add_local_reference(jobject(val)) 55 | else: 56 | raise NotImplementedError("Unable to write response '%s' type '%s' to emulator." % (str(val), type(val))) 57 | 58 | 59 | def native_write_arg_register(emu, reg, val): 60 | emu.mu.reg_write(reg, native_translate_arg(emu, val)) 61 | 62 | 63 | def native_method(func): 64 | def native_method_wrapper(*argv): 65 | """ 66 | :type self 67 | :type emu androidemu.emulator.Emulator 68 | :type mu Uc 69 | """ 70 | 71 | emu = argv[1] if len(argv) == 2 else argv[0] 72 | mu = emu.mu 73 | 74 | args = inspect.getfullargspec(func).args 75 | args_count = len(args) - (2 if 'self' in args else 1) 76 | 77 | if args_count < 0: 78 | raise RuntimeError("NativeMethod accept at least (self, mu) or (mu).") 79 | 80 | native_args = [] 81 | 82 | if args_count >= 1: 83 | native_args.append(mu.reg_read(UC_ARM_REG_R0)) 84 | 85 | if args_count >= 2: 86 | native_args.append(mu.reg_read(UC_ARM_REG_R1)) 87 | 88 | if args_count >= 3: 89 | native_args.append(mu.reg_read(UC_ARM_REG_R2)) 90 | 91 | if args_count >= 4: 92 | native_args.append(mu.reg_read(UC_ARM_REG_R3)) 93 | 94 | sp = mu.reg_read(UC_ARM_REG_SP) 95 | sp = sp + STACK_OFFSET # Need to offset by 4 because our hook pushes one register on the stack. 96 | 97 | if args_count >= 5: 98 | for x in range(0, args_count - 4): 99 | native_args.append(int.from_bytes(mu.mem_read(sp + (x * 4), 4), byteorder='little')) 100 | 101 | if len(argv) == 1: 102 | result = func(mu, *native_args) 103 | else: 104 | result = func(argv[0], mu, *native_args) 105 | 106 | if result is not None: 107 | native_write_arg_register(emu, UC_ARM_REG_R0, result) 108 | else: 109 | mu.reg_write(UC_ARM_REG_R0, JNI_ERR) 110 | 111 | return native_method_wrapper 112 | -------------------------------------------------------------------------------- /androidemu/native/memory_heap.py: -------------------------------------------------------------------------------- 1 | from unicorn import * 2 | from unicorn.arm64_const import * 3 | 4 | # Page size required by Unicorn 5 | UNICORN_PAGE_SIZE = 0x1000 6 | 7 | # Max allowable segment size (1G) 8 | MAX_ALLOWABLE_SEG_SIZE = 1024 * 1024 * 1024 9 | 10 | # Alignment functions to align all memory segments to Unicorn page boundaries (4KB pages only) 11 | ALIGN_PAGE_DOWN = lambda x: x & ~(UNICORN_PAGE_SIZE - 1) 12 | ALIGN_PAGE_UP = lambda x: (x + UNICORN_PAGE_SIZE - 1) & ~(UNICORN_PAGE_SIZE-1) 13 | 14 | 15 | # Implementation from 16 | # https://github.com/Battelle/afl-unicorn/blob/44a50c8a9426ffe4ad8714ef8a35dc011e62f739/unicorn_mode/helper_scripts/unicorn_loader.py#L45 17 | class UnicornSimpleHeap: 18 | """ Use this class to provide a simple heap implementation. This should 19 | be used if malloc/free calls break things during emulation. 20 | """ 21 | 22 | # Helper data-container used to track chunks 23 | class HeapChunk(object): 24 | def __init__(self, data_addr, data_size): 25 | self.data_addr = data_addr 26 | self.data_size = data_size 27 | 28 | # Returns true if the specified buffer is completely within the chunk, else false 29 | def is_buffer_in_chunk(self, addr, size): 30 | if addr >= self.data_addr and ((addr + size) <= (self.data_addr + self.data_size)): 31 | return True 32 | else: 33 | return False 34 | 35 | _uc = None # Unicorn engine instance to interact with 36 | _chunks = [] # List of all known chunks 37 | _debug_print = False # True to print debug information 38 | 39 | def __init__(self, uc, heap_min_addr, heap_max_addr, debug_print=False): 40 | self._uc = uc 41 | self._heap_min_addr = heap_min_addr 42 | self._heap_max_addr = heap_max_addr 43 | self._debug_print = debug_print 44 | 45 | # Add the watchpoint hook that will be used to implement psuedo-guard page support 46 | # self._uc.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, self.__check_mem_access) 47 | 48 | def malloc(self, size, prot=UC_PROT_READ | UC_PROT_WRITE): 49 | # Figure out the overall size to be allocated/mapped 50 | # - Allocate at least 1 4k page of memory to make Unicorn happy 51 | data_size = ALIGN_PAGE_UP(size) 52 | # Gross but efficient way to find space for the chunk: 53 | chunk = None 54 | for addr in range(self._heap_min_addr, self._heap_max_addr, UNICORN_PAGE_SIZE): 55 | try: 56 | self._uc.mem_map(addr, data_size, prot) 57 | chunk = self.HeapChunk(addr, data_size) 58 | if self._debug_print: 59 | print("Allocating 0x{0:x}-byte chunk @ 0x{1:016x}".format(chunk.data_size, chunk.data_addr)) 60 | break 61 | except UcError as e: 62 | continue 63 | # Something went very wrong 64 | if chunk is None: 65 | raise Exception("Oh no.") 66 | self._chunks.append(chunk) 67 | return chunk.data_addr 68 | 69 | def calloc(self, size, count): 70 | # Simple wrapper around malloc with calloc() args 71 | return self.malloc(size * count) 72 | 73 | def realloc(self, ptr, new_size): 74 | # Wrapper around malloc(new_size) / memcpy(new, old, old_size) / free(old) 75 | if self._debug_print: 76 | print("Reallocating chunk @ 0x{0:016x} to be 0x{1:x} bytes".format(ptr, new_size)) 77 | old_chunk = None 78 | for chunk in self._chunks: 79 | if chunk.data_addr == ptr: 80 | old_chunk = chunk 81 | new_chunk_addr = self.malloc(new_size) 82 | if old_chunk is not None: 83 | self._uc.mem_write(new_chunk_addr, str(self._uc.mem_read(old_chunk.data_addr, old_chunk.data_size))) 84 | self.free(old_chunk.data_addr) 85 | return new_chunk_addr 86 | 87 | def protect(self, addr, len_in, prot): 88 | for chunk in self._chunks: 89 | if chunk.is_buffer_in_chunk(addr, len_in): 90 | self._uc.mem_protect(chunk.data_addr, chunk.data_size, perms=prot) 91 | return True 92 | return False 93 | 94 | def free(self, addr): 95 | for chunk in self._chunks: 96 | if chunk.is_buffer_in_chunk(addr, 1): 97 | if self._debug_print: 98 | print("Freeing 0x{0:x}-byte chunk @ 0x{0:016x}".format(chunk.data_addr, chunk.data_size)) 99 | self._uc.mem_unmap(chunk.data_addr, chunk.data_size) 100 | self._chunks.remove(chunk) 101 | return True 102 | return False 103 | -------------------------------------------------------------------------------- /README_cn.md: -------------------------------------------------------------------------------- 1 | # AndroidNativeEmu 2 | AndroidNativeEmu 让你能够跨平台模拟Android Native库函数,比如JNI_OnLoad、Java_XXX_XX等函数。 3 | fork from : https://github.com/AeonLucid/AndroidNativeEmu 4 | 5 | ## 特性 6 | - 模拟 [JNI Invocation API](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html) so `JNI_OnLoad` can be called properly. 7 | - 模拟 memory、malloc、memcpy 8 | - 支持拦截系统调用(SVC #0) 9 | - 通过符号Hook 10 | - 所有 JavaVM, JNIEnv 和 hooked functions 都可以用python来处理 11 | - 支持 VFP 12 | - 支持文件系统(也就是说你可以模拟maps、status等文件) 13 | 14 | ## 本人瞎改 15 | 小弟不才,修改了一些代码,使其能够运行libcms的leviathan. 16 | - 添加 自动调用InitArray初始化代码,基于重定位表解析。 17 | - 添加 修改对象引用的值 18 | - 实现 getcpu() 系统调用 19 | - 实现 setByteArrayRegion 20 | - JNI中动态注册Native函数失败将不再报错(libcms中注册了大量不需要的函数) 21 | 22 | ## 使用方法 23 | 运行环境:python 3.7 必须! 24 | 1. Clone the repository 25 | 2. Run `pip install -r requirements.txt` 26 | 3. Run `python example.py` 27 | 28 | Windows上可以跑,自行尝试。 29 | 30 | 31 | ## 依赖库 32 | - [Unicorn CPU emulator framework](https://github.com/unicorn-engine/unicorn) 33 | - [Keystone assembler framework](https://github.com/keystone-engine/keystone) 34 | 35 | 36 | ## 初始化模拟器 37 | ```python 38 | # Initialize emulator 39 | emulator = Emulator( 40 | vfp_inst_set=True, 41 | vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs") 42 | ) 43 | ``` 44 | 45 | ## 如何定义Java类呢? 46 | 47 | ### Jni 中会调用到的类 48 | 注意看看各项参数的定义 49 | ```python 50 | class java_lang_System(metaclass=JavaClassDef, jvm_name='java/lang/System'): 51 | def __init__(self): 52 | pass 53 | 54 | @java_method_def(name='getProperty', args_list=["jstring"] ,signature='(Ljava/lang/String;)Ljava/lang/String;', native=False) 55 | def getProperty(self, *args, **kwargs): 56 | print (args[0].value) 57 | return "2.1.0" 58 | ``` 59 | ### 我们的目标类 60 | ```python 61 | class XGorgen(metaclass=JavaClassDef, jvm_name='com/ss/sys/ces/a'): 62 | def __init__(self): 63 | pass 64 | 65 | @java_method_def(name='leviathan', signature='(I[B)[B', native=True) 66 | def leviathan(self, mu): 67 | pass 68 | 69 | def test(self): 70 | pass 71 | ``` 72 | 73 | ### 模拟stacktrace的类 74 | ```python 75 | class java_lang_Thread(metaclass=JavaClassDef, jvm_name='java/lang/Thread'): 76 | def __init__(self): 77 | pass 78 | 79 | @java_method_def(name="currentThread", signature='()Ljava/lang/Thread;', native=False) 80 | def currentThread(self, *args, **kwargs): 81 | return java_lang_Thread() 82 | 83 | @java_method_def(name="getStackTrace", signature='()[Ljava/lang/StackTraceElement;', native=False) 84 | def getStackTrace(self, *args, **kwargs): 85 | return [java_lang_StackTraceElement("dalvik.system.VMStack"), 86 | java_lang_StackTraceElement("java.lang.Thread"), 87 | java_lang_StackTraceElement("com.ss.sys.ces.a"), 88 | java_lang_StackTraceElement("com.yf.douyintool.MainActivity"), 89 | java_lang_StackTraceElement("java.lang.reflect.Method"), 90 | java_lang_StackTraceElement("java.lang.reflect.Method"), 91 | java_lang_StackTraceElement("android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener"), 92 | java_lang_StackTraceElement("android.view.View"), 93 | java_lang_StackTraceElement("android.os.Handler"), 94 | java_lang_StackTraceElement("android.os.Handler"), 95 | java_lang_StackTraceElement("android.os.Looper"), 96 | java_lang_StackTraceElement("android.app.ActivityThread"), 97 | java_lang_StackTraceElement("java.lang.reflect.Method"), 98 | java_lang_StackTraceElement("java.lang.reflect.Method"), 99 | java_lang_StackTraceElement("com.android.internal.os.ZygoteInit$MethodAndArgsCaller"), 100 | java_lang_StackTraceElement("com.android.internal.os.ZygoteInit"), 101 | java_lang_StackTraceElement("dalvik.system.NativeStart") 102 | ] 103 | ``` 104 | 更多的类请见example 105 | 106 | ## 注册类 107 | ```python 108 | emulator.java_classloader.add_class(XGorgen) 109 | emulator.java_classloader.add_class(java_lang_System) 110 | emulator.java_classloader.add_class(java_lang_Thread) 111 | emulator.java_classloader.add_class(java_lang_StackTraceElement) 112 | ``` 113 | 114 | ## 调用JNI_OnLoad 115 | init array 已经自动调用了,SO如果有加密也没关系。 116 | ```python 117 | # 添加依赖库 118 | emulator.load_library("samples/example_binaries/libdl.so") 119 | emulator.load_library("samples/example_binaries/libc.so") 120 | emulator.load_library("samples/example_binaries/libstdc++.so") 121 | emulator.load_library("samples/example_binaries/libm.so") 122 | 123 | lib_module = emulator.load_library("samples/example_binaries/libcms.so") 124 | 125 | # JNI_OnLoad will call 'RegisterNatives'. 126 | emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00) 127 | 128 | ``` 129 | 130 | ## 调用native 方法 131 | ```python 132 | x = XGorgen() 133 | data = 'acde74a94e6b493a3399fac83c7c08b35D58B21D9582AF77647FC9902E36AE70f9c001e9334e6e94916682224fbe4e5f00000000000000000000000000000000' 134 | data = bytearray(bytes.fromhex(data)) 135 | result = x.leviathan(emulator, 1562848170, data) 136 | ``` -------------------------------------------------------------------------------- /androidemu/cpu/syscall_hooks.py: -------------------------------------------------------------------------------- 1 | import math 2 | import time 3 | from random import randint 4 | 5 | import hexdump 6 | from unicorn import Uc 7 | 8 | from androidemu.const.android import * 9 | from androidemu.const.linux import * 10 | from androidemu.cpu.syscall_handlers import SyscallHandlers 11 | 12 | OVERRIDE_TIMEOFDAY = False 13 | OVERRIDE_TIMEOFDAY_SEC = 0 14 | OVERRIDE_TIMEOFDAY_USEC = 0 15 | 16 | OVERRIDE_CLOCK = False 17 | OVERRIDE_CLOCK_TIME = 0 18 | 19 | 20 | class SyscallHooks: 21 | 22 | """ 23 | :type mu Uc 24 | :type syscall_handler SyscallHandlers 25 | """ 26 | def __init__(self, mu, syscall_handler): 27 | self._mu = mu 28 | self._syscall_handler = syscall_handler 29 | self._syscall_handler.set_handler(0x4E, "gettimeofday", 2, self._handle_gettimeofday) 30 | self._syscall_handler.set_handler(0xAC, "prctl", 5, self._handle_prctl) 31 | self._syscall_handler.set_handler(0xF0, "futex", 6, self._handle_futex) 32 | self._syscall_handler.set_handler(0x107, "clock_gettime", 2, self._handle_clock_gettime) 33 | self._syscall_handler.set_handler(0x119, "socket", 3, self._socket) 34 | self._syscall_handler.set_handler(0x11b, "connect", 3, self._connect) 35 | self._syscall_handler.set_handler(0x159, "getcpu", 3, self._getcpu) 36 | self._syscall_handler.set_handler(0x14e, "faccessat", 4, self._faccessat) 37 | self._syscall_handler.set_handler(0x14, "getpid", 0, self._getpid) 38 | self._syscall_handler.set_handler(0xe0, "gettid", 0, self._gettid) 39 | self._syscall_handler.set_handler(0x180,"null1",0, self._null) 40 | self._clock_start = time.time() 41 | self._clock_offset = randint(1000, 2000) 42 | 43 | def _null(self, mu): 44 | return 0 45 | 46 | def _gettid(self, mu): 47 | return 0x2211 48 | 49 | def _getpid(self, mu): 50 | return 0x1122 51 | 52 | def _faccessat(self, mu, filename, pathname, mode, flag): 53 | return 0 54 | 55 | def _getcpu(self, mu, _cpu, node, cache): 56 | if _cpu != 0: 57 | mu.mem_write(_cpu, int(1).to_bytes(4, byteorder='little')) 58 | return 0 59 | 60 | def _handle_gettimeofday(self, uc, tv, tz): 61 | """ 62 | If either tv or tz is NULL, the corresponding structure is not set or returned. 63 | """ 64 | 65 | if tv != 0: 66 | if OVERRIDE_TIMEOFDAY: 67 | uc.mem_write(tv + 0, int(OVERRIDE_TIMEOFDAY_SEC).to_bytes(4, byteorder='little')) 68 | uc.mem_write(tv + 4, int(OVERRIDE_TIMEOFDAY_USEC).to_bytes(4, byteorder='little')) 69 | else: 70 | timestamp = time.time() 71 | (usec, sec) = math.modf(timestamp) 72 | usec = abs(int(usec * 100000)) 73 | 74 | uc.mem_write(tv + 0, int(sec).to_bytes(4, byteorder='little')) 75 | uc.mem_write(tv + 4, int(usec).to_bytes(4, byteorder='little')) 76 | 77 | if tz != 0: 78 | uc.mem_write(tz + 0, int(-120).to_bytes(4, byteorder='little')) # minuteswest -(+GMT_HOURS) * 60 79 | uc.mem_write(tz + 4, int().to_bytes(4, byteorder='little')) # dsttime 80 | 81 | return 0 82 | 83 | def _handle_prctl(self, mu, option, arg2, arg3, arg4, arg5): 84 | """ 85 | int prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); 86 | See: 87 | - https://linux.die.net/man/2/prctl 88 | - https://github.com/torvalds/linux/blob/master/include/uapi/linux/prctl.h 89 | 90 | For PR_SET_VMA: 91 | - https://android.googlesource.com/platform/bionic/+/263325d/libc/include/sys/prctl.h 92 | - https://sourceforge.net/p/strace/mailman/message/34329772/ 93 | """ 94 | 95 | if option == PR_SET_VMA: 96 | # arg5 contains ptr to a name. 97 | return 0 98 | else: 99 | raise NotImplementedError("Unsupported prctl option %d (0x%x)" % (option, option)) 100 | 101 | def _handle_futex(self, mu, uaddr, op, val, timeout, uaddr2, val3): 102 | """ 103 | See: https://linux.die.net/man/2/futex 104 | """ 105 | 106 | if op & FUTEX_WAIT: 107 | raise NotImplementedError() 108 | elif op & FUTEX_WAKE: 109 | wakes_at_most = val 110 | return 0 111 | elif op & FUTEX_FD: 112 | raise NotImplementedError() 113 | elif op & FUTEX_REQUEUE: 114 | raise NotImplementedError() 115 | elif op & FUTEX_CMP_REQUEUE: 116 | raise NotImplementedError() 117 | 118 | return 0 119 | 120 | def _handle_clock_gettime(self, mu, clk_id, tp_ptr): 121 | """ 122 | The functions clock_gettime() retrieve the time of the specified clock clk_id. 123 | 124 | The clk_id argument is the identifier of the particular clock on which to act. A clock may be system-wide and 125 | hence visible for all processes, or per-process if it measures time only within a single process. 126 | 127 | clock_gettime(), clock_settime() and clock_getres() return 0 for success, or -1 for failure (in which case 128 | errno is set appropriately). 129 | """ 130 | 131 | if clk_id == CLOCK_MONOTONIC_COARSE: 132 | if OVERRIDE_CLOCK: 133 | mu.mem_write(tp_ptr + 0, int(OVERRIDE_CLOCK_TIME).to_bytes(4, byteorder='little')) 134 | mu.mem_write(tp_ptr + 4, int(0).to_bytes(4, byteorder='little')) 135 | else: 136 | clock_add = time.time() - self._clock_start # Seconds passed since clock_start was set. 137 | 138 | mu.mem_write(tp_ptr + 0, int(self._clock_start + clock_add).to_bytes(4, byteorder='little')) 139 | mu.mem_write(tp_ptr + 4, int(0).to_bytes(4, byteorder='little')) 140 | return 0 141 | else: 142 | raise NotImplementedError("Unsupported clk_id: %d (%x)" % (clk_id, clk_id)) 143 | 144 | def _socket(self, mu, family, type_in, protocol): 145 | return 0 146 | # raise NotImplementedError() 147 | 148 | def _connect(self, mu, fd, addr, addr_len): 149 | print(hexdump.hexdump(mu.mem_read(addr, addr_len))) 150 | raise NotImplementedError() 151 | -------------------------------------------------------------------------------- /androidemu/emulator.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import time 4 | from random import randint 5 | 6 | import hexdump 7 | from unicorn import Uc, UC_ARCH_ARM, UC_MODE_ARM 8 | from unicorn.arm_const import UC_ARM_REG_SP, UC_ARM_REG_LR, UC_ARM_REG_R0 9 | 10 | from androidemu import config 11 | from androidemu.config import HOOK_MEMORY_BASE, HOOK_MEMORY_SIZE 12 | from androidemu.cpu.interrupt_handler import InterruptHandler 13 | from androidemu.cpu.syscall_handlers import SyscallHandlers 14 | from androidemu.cpu.syscall_hooks import SyscallHooks 15 | from androidemu.hooker import Hooker 16 | from androidemu.internal.memory import Memory 17 | from androidemu.internal.modules import Modules 18 | from androidemu.java.helpers.native_method import native_write_args 19 | from androidemu.java.java_classloader import JavaClassLoader 20 | from androidemu.java.java_vm import JavaVM 21 | from androidemu.native.hooks import NativeHooks 22 | from androidemu.native.memory import NativeMemory 23 | from androidemu.tracer import Tracer 24 | from androidemu.vfs.file_system import VirtualFileSystem 25 | 26 | logger = logging.getLogger(__name__) 27 | 28 | 29 | class Emulator: 30 | """ 31 | :type mu Uc 32 | :type modules Modules 33 | :type memory Memory 34 | """ 35 | def __init__(self, vfs_root=None, vfp_inst_set=False): 36 | # Unicorn. 37 | self.mu = Uc(UC_ARCH_ARM, UC_MODE_ARM) 38 | 39 | if vfp_inst_set: 40 | self._enable_vfp() 41 | 42 | # Android 43 | self.system_properties = {"libc.debug.malloc.options":""} 44 | 45 | # Stack. 46 | self.mu.mem_map(config.STACK_ADDR, config.STACK_SIZE) 47 | self.mu.reg_write(UC_ARM_REG_SP, config.STACK_ADDR + config.STACK_SIZE) 48 | 49 | # Executable data. 50 | self.modules = Modules(self) 51 | self.memory = Memory(self) 52 | 53 | # CPU 54 | self.interrupt_handler = InterruptHandler(self.mu) 55 | self.syscall_handler = SyscallHandlers(self.interrupt_handler) 56 | self.syscall_hooks = SyscallHooks(self.mu, self.syscall_handler) 57 | 58 | # File System 59 | if vfs_root is not None: 60 | self.vfs = VirtualFileSystem(vfs_root, self.syscall_handler) 61 | else: 62 | self.vfs = None 63 | 64 | # Hooker 65 | self.mu.mem_map(config.HOOK_MEMORY_BASE, config.HOOK_MEMORY_SIZE) 66 | self.hooker = Hooker(self, config.HOOK_MEMORY_BASE, config.HOOK_MEMORY_SIZE) 67 | 68 | # JavaVM 69 | self.java_classloader = JavaClassLoader() 70 | self.java_vm = JavaVM(self, self.java_classloader, self.hooker) 71 | 72 | # Native 73 | self.native_memory = NativeMemory(self.mu, config.HEAP_BASE, config.HEAP_SIZE, self.syscall_handler) 74 | self.native_hooks = NativeHooks(self, self.native_memory, self.modules, self.hooker) 75 | 76 | # Tracer 77 | self.tracer = Tracer(self.mu, self.modules) 78 | 79 | # https://github.com/unicorn-engine/unicorn/blob/8c6cbe3f3cabed57b23b721c29f937dd5baafc90/tests/regress/arm_fp_vfp_disabled.py#L15 80 | def _enable_vfp(self): 81 | # MRC p15, #0, r1, c1, c0, #2 82 | # ORR r1, r1, #(0xf << 20) 83 | # MCR p15, #0, r1, c1, c0, #2 84 | # MOV r1, #0 85 | # MCR p15, #0, r1, c7, c5, #4 86 | # MOV r0,#0x40000000 87 | # FMXR FPEXC, r0 88 | code = '11EE501F' 89 | code += '41F47001' 90 | code += '01EE501F' 91 | code += '4FF00001' 92 | code += '07EE951F' 93 | code += '4FF08040' 94 | code += 'E8EE100A' 95 | # vpush {d8} 96 | code += '2ded028b' 97 | 98 | address = 0x1000 99 | mem_size = 0x1000 100 | code_bytes = bytes.fromhex(code) 101 | 102 | try: 103 | self.mu.mem_map(address, mem_size) 104 | self.mu.mem_write(address, code_bytes) 105 | self.mu.reg_write(UC_ARM_REG_SP, address + mem_size) 106 | 107 | self.mu.emu_start(address | 1, address + len(code_bytes)) 108 | finally: 109 | self.mu.mem_unmap(address, mem_size) 110 | 111 | def _call_init_array(self): 112 | pass 113 | 114 | def load_library(self, filename, do_init=True): 115 | libmod = self.modules.load_module(filename) 116 | if do_init: 117 | logger.debug("Calling Init for: %s " % filename) 118 | for fun_ptr in libmod.init_array: 119 | logger.debug("Calling Init function: %x " % fun_ptr) 120 | self.call_native(fun_ptr) 121 | return libmod 122 | 123 | def call_symbol(self, module, symbol_name, *argv): 124 | symbol = module.find_symbol(symbol_name) 125 | 126 | if symbol is None: 127 | logger.error('Unable to find symbol \'%s\' in module \'%s\'.' % (symbol_name, module.filename)) 128 | return 129 | 130 | self.call_native(symbol.address, *argv) 131 | 132 | def call_native(self, addr, *argv): 133 | # Detect JNI call 134 | is_jni = False 135 | 136 | if len(argv) >= 1: 137 | is_jni = argv[0] == self.java_vm.address_ptr or argv[0] == self.java_vm.jni_env.address_ptr 138 | 139 | # TODO: Write JNI args to local ref table if jni. 140 | 141 | try: 142 | # Execute native call. 143 | 144 | native_write_args(self, *argv) 145 | stop_pos = randint(HOOK_MEMORY_BASE, HOOK_MEMORY_BASE + HOOK_MEMORY_SIZE) | 1 146 | self.mu.reg_write(UC_ARM_REG_LR, stop_pos) 147 | self.mu.emu_start(addr, stop_pos - 1) 148 | 149 | # Read result from locals if jni. 150 | if is_jni: 151 | result_idx = self.mu.reg_read(UC_ARM_REG_R0) 152 | result = self.java_vm.jni_env.get_local_reference(result_idx) 153 | 154 | if result is None: 155 | return result 156 | 157 | return result.value 158 | finally: 159 | # Clear locals if jni. 160 | if is_jni: 161 | self.java_vm.jni_env.clear_locals() 162 | 163 | def dump(self, out_dir): 164 | os.makedirs(out_dir) 165 | 166 | for begin, end, prot in [reg for reg in self.mu.mem_regions()]: 167 | filename = "{:#010x}-{:#010x}.bin".format(begin, end) 168 | pathname = os.path.join(out_dir, filename) 169 | with open(pathname, "w") as f: 170 | f.write(hexdump.hexdump(self.mu.mem_read(begin, end - begin), result='return')) 171 | -------------------------------------------------------------------------------- /samples/example_douyin.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import posixpath 3 | import sys 4 | 5 | from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPED 6 | from unicorn.arm_const import * 7 | 8 | from androidemu.emulator import Emulator 9 | from androidemu.java.java_class_def import JavaClassDef 10 | from androidemu.java.java_method_def import java_method_def 11 | 12 | from samples import debug_utils 13 | 14 | class XGorgen(metaclass=JavaClassDef, jvm_name='com/ss/sys/ces/a'): 15 | def __init__(self): 16 | pass 17 | 18 | @java_method_def(name='leviathan', signature='(I[B)[B', native=True) 19 | def leviathan(self, mu): 20 | pass 21 | 22 | def test(self): 23 | pass 24 | 25 | class secuni_b(metaclass=JavaClassDef, jvm_name='com/ss/sys/secuni/b/c'): 26 | def __init__(self): 27 | pass 28 | 29 | @java_method_def(name='n0', signature='(Landroid/content/Context;)[B', native=True) 30 | def n0(self, mu): 31 | pass 32 | 33 | @java_method_def(name='n1', signature='(Landroid/content/Context;Ljava/lang/String;)I', native=True) 34 | def n1(self, mu): 35 | pass 36 | 37 | 38 | class UserInfo(metaclass=JavaClassDef, jvm_name='com/ss/android/common/applog/UserInfo'): 39 | def __init__(self): 40 | pass 41 | 42 | 43 | class java_lang_System(metaclass=JavaClassDef, jvm_name='java/lang/System'): 44 | def __init__(self): 45 | pass 46 | 47 | @java_method_def(name='getProperty', args_list=["jstring"] ,signature='(Ljava/lang/String;)Ljava/lang/String;', native=False) 48 | def getProperty(self, *args, **kwargs): 49 | print (args[0].value) 50 | return "2.1.0" 51 | 52 | class java_lang_StackTraceElement(metaclass=JavaClassDef, jvm_name='java/lang/StackTraceElement'): 53 | def __init__(self, _name): 54 | self.name = _name 55 | 56 | @java_method_def(native=False, name='getClassName', signature="()Ljava/lang/String;") 57 | def getClassName(self, *args, **kwargs): 58 | return self.name 59 | 60 | 61 | class java_lang_Thread(metaclass=JavaClassDef, jvm_name='java/lang/Thread'): 62 | def __init__(self): 63 | pass 64 | 65 | @java_method_def(name="currentThread", signature='()Ljava/lang/Thread;', native=False) 66 | def currentThread(self, *args, **kwargs): 67 | return java_lang_Thread() 68 | 69 | @java_method_def(name="getStackTrace", signature='()[Ljava/lang/StackTraceElement;', native=False) 70 | def getStackTrace(self, *args, **kwargs): 71 | return [java_lang_StackTraceElement("dalvik.system.VMStack"), 72 | java_lang_StackTraceElement("java.lang.Thread"), 73 | java_lang_StackTraceElement("com.ss.sys.ces.a"), 74 | java_lang_StackTraceElement("com.yf.douyintool.MainActivity"), 75 | java_lang_StackTraceElement("java.lang.reflect.Method"), 76 | java_lang_StackTraceElement("java.lang.reflect.Method"), 77 | java_lang_StackTraceElement("android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener"), 78 | java_lang_StackTraceElement("android.view.View"), 79 | java_lang_StackTraceElement("android.os.Handler"), 80 | java_lang_StackTraceElement("android.os.Handler"), 81 | java_lang_StackTraceElement("android.os.Looper"), 82 | java_lang_StackTraceElement("android.app.ActivityThread"), 83 | java_lang_StackTraceElement("java.lang.reflect.Method"), 84 | java_lang_StackTraceElement("java.lang.reflect.Method"), 85 | java_lang_StackTraceElement("com.android.internal.os.ZygoteInit$MethodAndArgsCaller"), 86 | java_lang_StackTraceElement("com.android.internal.os.ZygoteInit"), 87 | java_lang_StackTraceElement("dalvik.system.NativeStart") 88 | ] 89 | 90 | 91 | 92 | # Configure logging 93 | logging.basicConfig( 94 | stream=sys.stdout, 95 | level=logging.DEBUG, 96 | format="%(asctime)s %(levelname)7s %(name)34s | %(message)s" 97 | ) 98 | 99 | logger = logging.getLogger(__name__) 100 | 101 | # Initialize emulator 102 | emulator = Emulator( 103 | vfp_inst_set=True, 104 | vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs") 105 | ) 106 | 107 | # Register Java class. 108 | # emulator.java_classloader.add_class(MainActivity) 109 | emulator.java_classloader.add_class(XGorgen) 110 | emulator.java_classloader.add_class(secuni_b) 111 | emulator.java_classloader.add_class(UserInfo) 112 | emulator.java_classloader.add_class(java_lang_System) 113 | emulator.java_classloader.add_class(java_lang_Thread) 114 | emulator.java_classloader.add_class(java_lang_StackTraceElement) 115 | 116 | # Load all libraries. 117 | emulator.load_library("samples/example_binaries/libdl.so") 118 | emulator.load_library("samples/example_binaries/libc.so") 119 | emulator.load_library("samples/example_binaries/libstdc++.so") 120 | emulator.load_library("samples/example_binaries/libm.so") 121 | lib_module = emulator.load_library("samples/example_binaries/libcms.so") 122 | 123 | # Show loaded modules. 124 | logger.info("Loaded modules:") 125 | 126 | for module in emulator.modules: 127 | logger.info("=> 0x%08x - %s" % (module.base, module.filename)) 128 | 129 | # Debug 130 | # emulator.mu.hook_add(UC_HOOK_CODE, debug_utils.hook_code) 131 | emulator.mu.hook_add(UC_HOOK_MEM_UNMAPPED, debug_utils.hook_unmapped) 132 | # emulator.mu.hook_add(UC_HOOK_MEM_WRITE, debug_utils.hook_mem_write) 133 | # emulator.mu.hook_add(UC_HOOK_MEM_READ, debug_utils.hook_mem_read) 134 | 135 | try: 136 | # Run JNI_OnLoad. 137 | # JNI_OnLoad will call 'RegisterNatives'. 138 | emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00) 139 | 140 | 141 | # bypass douyin checks 142 | with open("samples/app_process32", 'rb') as ap: 143 | data = ap.read() 144 | len1 = len(data) + 1024 - (len(data) % 1024) 145 | emulator.mu.mem_map(0xab006000, len1) 146 | emulator.mu.mem_write(0xab006000, data) 147 | 148 | 149 | x = XGorgen() 150 | data = 'acde74a94e6b493a3399fac83c7c08b35D58B21D9582AF77647FC9902E36AE70f9c001e9334e6e94916682224fbe4e5f00000000000000000000000000000000' 151 | data = bytearray(bytes.fromhex(data)) 152 | result = x.leviathan(emulator, 1562848170, data) 153 | 154 | 155 | 156 | print(''.join(['%02x' % b for b in result])) 157 | # 037d560d0000903e34fb093f1d21e78f3bdf3fbebe00b124becc 158 | # 036d2a7b000010f4d05395b7df8b0ec2b5ec085b938a473a6a51 159 | # 036d2a7b000010f4d05395b7df8b0ec2b5ec085b938a473a6a51 160 | 161 | # 0300000000002034d288fe8d6b95b778105cc36eade709d2b500 162 | # 0300000000002034d288fe8d6b95b778105cc36eade709d2b500 163 | # 0300000000002034d288fe8d6b95b778105cc36eade709d2b500 164 | # Dump natives found. 165 | 166 | # for method in MainActivity.jvm_methods.values(): 167 | # if method.native: 168 | # logger.info("- [0x%08x] %s - %s" % (method.native_addr, method.name, method.signature)) 169 | except UcError as e: 170 | print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC)) 171 | raise 172 | -------------------------------------------------------------------------------- /tools/gen_jni_env.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | 4 | def convert(name): 5 | s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) 6 | return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() 7 | 8 | 9 | table = """NULL, 10 | NULL, 11 | NULL, 12 | NULL, 13 | GetVersion, 14 | 15 | DefineClass, 16 | FindClass, 17 | 18 | FromReflectedMethod, 19 | FromReflectedField, 20 | ToReflectedMethod, 21 | 22 | GetSuperclass, 23 | IsAssignableFrom, 24 | 25 | ToReflectedField, 26 | 27 | Throw, 28 | ThrowNew, 29 | ExceptionOccurred, 30 | ExceptionDescribe, 31 | ExceptionClear, 32 | FatalError, 33 | 34 | PushLocalFrame, 35 | PopLocalFrame, 36 | 37 | NewGlobalRef, 38 | DeleteGlobalRef, 39 | DeleteLocalRef, 40 | IsSameObject, 41 | NewLocalRef, 42 | EnsureLocalCapacity, 43 | 44 | AllocObject, 45 | NewObject, 46 | NewObjectV, 47 | NewObjectA, 48 | 49 | GetObjectClass, 50 | IsInstanceOf, 51 | 52 | GetMethodID, 53 | 54 | CallObjectMethod, 55 | CallObjectMethodV, 56 | CallObjectMethodA, 57 | CallBooleanMethod, 58 | CallBooleanMethodV, 59 | CallBooleanMethodA, 60 | CallByteMethod, 61 | CallByteMethodV, 62 | CallByteMethodA, 63 | CallCharMethod, 64 | CallCharMethodV, 65 | CallCharMethodA, 66 | CallShortMethod, 67 | CallShortMethodV, 68 | CallShortMethodA, 69 | CallIntMethod, 70 | CallIntMethodV, 71 | CallIntMethodA, 72 | CallLongMethod, 73 | CallLongMethodV, 74 | CallLongMethodA, 75 | CallFloatMethod, 76 | CallFloatMethodV, 77 | CallFloatMethodA, 78 | CallDoubleMethod, 79 | CallDoubleMethodV, 80 | CallDoubleMethodA, 81 | CallVoidMethod, 82 | CallVoidMethodV, 83 | CallVoidMethodA, 84 | 85 | CallNonvirtualObjectMethod, 86 | CallNonvirtualObjectMethodV, 87 | CallNonvirtualObjectMethodA, 88 | CallNonvirtualBooleanMethod, 89 | CallNonvirtualBooleanMethodV, 90 | CallNonvirtualBooleanMethodA, 91 | CallNonvirtualByteMethod, 92 | CallNonvirtualByteMethodV, 93 | CallNonvirtualByteMethodA, 94 | CallNonvirtualCharMethod, 95 | CallNonvirtualCharMethodV, 96 | CallNonvirtualCharMethodA, 97 | CallNonvirtualShortMethod, 98 | CallNonvirtualShortMethodV, 99 | CallNonvirtualShortMethodA, 100 | CallNonvirtualIntMethod, 101 | CallNonvirtualIntMethodV, 102 | CallNonvirtualIntMethodA, 103 | CallNonvirtualLongMethod, 104 | CallNonvirtualLongMethodV, 105 | CallNonvirtualLongMethodA, 106 | CallNonvirtualFloatMethod, 107 | CallNonvirtualFloatMethodV, 108 | CallNonvirtualFloatMethodA, 109 | CallNonvirtualDoubleMethod, 110 | CallNonvirtualDoubleMethodV, 111 | CallNonvirtualDoubleMethodA, 112 | CallNonvirtualVoidMethod, 113 | CallNonvirtualVoidMethodV, 114 | CallNonvirtualVoidMethodA, 115 | 116 | GetFieldID, 117 | 118 | GetObjectField, 119 | GetBooleanField, 120 | GetByteField, 121 | GetCharField, 122 | GetShortField, 123 | GetIntField, 124 | GetLongField, 125 | GetFloatField, 126 | GetDoubleField, 127 | SetObjectField, 128 | SetBooleanField, 129 | SetByteField, 130 | SetCharField, 131 | SetShortField, 132 | SetIntField, 133 | SetLongField, 134 | SetFloatField, 135 | SetDoubleField, 136 | 137 | GetStaticMethodID, 138 | 139 | CallStaticObjectMethod, 140 | CallStaticObjectMethodV, 141 | CallStaticObjectMethodA, 142 | CallStaticBooleanMethod, 143 | CallStaticBooleanMethodV, 144 | CallStaticBooleanMethodA, 145 | CallStaticByteMethod, 146 | CallStaticByteMethodV, 147 | CallStaticByteMethodA, 148 | CallStaticCharMethod, 149 | CallStaticCharMethodV, 150 | CallStaticCharMethodA, 151 | CallStaticShortMethod, 152 | CallStaticShortMethodV, 153 | CallStaticShortMethodA, 154 | CallStaticIntMethod, 155 | CallStaticIntMethodV, 156 | CallStaticIntMethodA, 157 | CallStaticLongMethod, 158 | CallStaticLongMethodV, 159 | CallStaticLongMethodA, 160 | CallStaticFloatMethod, 161 | CallStaticFloatMethodV, 162 | CallStaticFloatMethodA, 163 | CallStaticDoubleMethod, 164 | CallStaticDoubleMethodV, 165 | CallStaticDoubleMethodA, 166 | CallStaticVoidMethod, 167 | CallStaticVoidMethodV, 168 | CallStaticVoidMethodA, 169 | 170 | GetStaticFieldID, 171 | 172 | GetStaticObjectField, 173 | GetStaticBooleanField, 174 | GetStaticByteField, 175 | GetStaticCharField, 176 | GetStaticShortField, 177 | GetStaticIntField, 178 | GetStaticLongField, 179 | GetStaticFloatField, 180 | GetStaticDoubleField, 181 | 182 | SetStaticObjectField, 183 | SetStaticBooleanField, 184 | SetStaticByteField, 185 | SetStaticCharField, 186 | SetStaticShortField, 187 | SetStaticIntField, 188 | SetStaticLongField, 189 | SetStaticFloatField, 190 | SetStaticDoubleField, 191 | 192 | NewString, 193 | 194 | GetStringLength, 195 | GetStringChars, 196 | ReleaseStringChars, 197 | 198 | NewStringUTF, 199 | GetStringUTFLength, 200 | GetStringUTFChars, 201 | ReleaseStringUTFChars, 202 | 203 | GetArrayLength, 204 | 205 | NewObjectArray, 206 | GetObjectArrayElement, 207 | SetObjectArrayElement, 208 | 209 | NewBooleanArray, 210 | NewByteArray, 211 | NewCharArray, 212 | NewShortArray, 213 | NewIntArray, 214 | NewLongArray, 215 | NewFloatArray, 216 | NewDoubleArray, 217 | 218 | GetBooleanArrayElements, 219 | GetByteArrayElements, 220 | GetCharArrayElements, 221 | GetShortArrayElements, 222 | GetIntArrayElements, 223 | GetLongArrayElements, 224 | GetFloatArrayElements, 225 | GetDoubleArrayElements, 226 | 227 | ReleaseBooleanArrayElements, 228 | ReleaseByteArrayElements, 229 | ReleaseCharArrayElements, 230 | ReleaseShortArrayElements, 231 | ReleaseIntArrayElements, 232 | ReleaseLongArrayElements, 233 | ReleaseFloatArrayElements, 234 | ReleaseDoubleArrayElements, 235 | 236 | GetBooleanArrayRegion, 237 | GetByteArrayRegion, 238 | GetCharArrayRegion, 239 | GetShortArrayRegion, 240 | GetIntArrayRegion, 241 | GetLongArrayRegion, 242 | GetFloatArrayRegion, 243 | GetDoubleArrayRegion, 244 | SetBooleanArrayRegion, 245 | SetByteArrayRegion, 246 | SetCharArrayRegion, 247 | SetShortArrayRegion, 248 | SetIntArrayRegion, 249 | SetLongArrayRegion, 250 | SetFloatArrayRegion, 251 | SetDoubleArrayRegion, 252 | 253 | RegisterNatives, 254 | UnregisterNatives, 255 | 256 | MonitorEnter, 257 | MonitorExit, 258 | 259 | GetJavaVM, 260 | 261 | GetStringRegion, 262 | GetStringUTFRegion, 263 | 264 | GetPrimitiveArrayCritical, 265 | ReleasePrimitiveArrayCritical, 266 | 267 | GetStringCritical, 268 | ReleaseStringCritical, 269 | 270 | NewWeakGlobalRef, 271 | DeleteWeakGlobalRef, 272 | 273 | ExceptionCheck, 274 | 275 | NewDirectByteBuffer, 276 | GetDirectBufferAddress, 277 | GetDirectBufferCapacity, 278 | 279 | GetObjectRefType""" 280 | 281 | index = 0 282 | functions = dict() 283 | 284 | for entry in table.split("\n"): 285 | entry = entry.strip() 286 | 287 | if len(entry) == 0: 288 | continue 289 | 290 | if entry.endswith(","): 291 | entry = entry[:-1] 292 | 293 | func_index = index 294 | func_name = convert(entry) 295 | index += 1 296 | 297 | if entry == 'NULL': 298 | continue 299 | 300 | functions[func_index] = func_name 301 | 302 | # write_function_table entries 303 | for (index, name) in functions.items(): 304 | print(" %d: self.%s," % (index, name)) 305 | 306 | # write functions 307 | for (index, name) in functions.items(): 308 | print(""" 309 | @native_method 310 | def %s(self, mu, env): 311 | raise NotImplementedError()""" % name) 312 | -------------------------------------------------------------------------------- /androidemu/internal/modules.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from elftools.elf.elffile import ELFFile 4 | from elftools.elf.relocation import RelocationSection 5 | from elftools.elf.sections import SymbolTableSection 6 | from unicorn import UC_PROT_ALL 7 | 8 | from androidemu.internal import get_segment_protection, arm 9 | from androidemu.internal.module import Module 10 | from androidemu.internal.symbol_resolved import SymbolResolved 11 | 12 | import struct 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | class Modules: 18 | """ 19 | :type emu androidemu.emulator.Emulator 20 | :type modules list[Module] 21 | """ 22 | def __init__(self, emu): 23 | self.emu = emu 24 | self.modules = list() 25 | self.symbol_hooks = dict() 26 | 27 | def add_symbol_hook(self, symbol_name, addr): 28 | self.symbol_hooks[symbol_name] = addr 29 | 30 | def find_symbol(self, addr): 31 | for module in self.modules: 32 | if addr in module.symbol_lookup: 33 | return module.symbol_lookup[addr] 34 | return None, None 35 | 36 | 37 | def load_module(self, filename): 38 | logger.debug("Loading module '%s'." % filename) 39 | 40 | with open(filename, 'rb') as fstream: 41 | elf = ELFFile(fstream) 42 | 43 | dynamic = elf.header.e_type == 'ET_DYN' 44 | 45 | if not dynamic: 46 | raise NotImplementedError("Only ET_DYN is supported at the moment.") 47 | 48 | # Parse program header (Execution view). 49 | 50 | # - LOAD (determinate what parts of the ELF file get mapped into memory) 51 | load_segments = [x for x in elf.iter_segments() if x.header.p_type == 'PT_LOAD'] 52 | 53 | 54 | # Find bounds of the load segments. 55 | bound_low = 0 56 | bound_high = 0 57 | 58 | for segment in load_segments: 59 | if segment.header.p_memsz == 0: 60 | continue 61 | 62 | if bound_low > segment.header.p_vaddr: 63 | bound_low = segment.header.p_vaddr 64 | 65 | high = segment.header.p_vaddr + segment.header.p_memsz 66 | 67 | if bound_high < high: 68 | bound_high = high 69 | 70 | # Retrieve a base address for this module. 71 | load_base = self.emu.memory.mem_reserve(bound_high - bound_low) 72 | 73 | for segment in load_segments: 74 | prot = get_segment_protection(segment.header.p_flags) 75 | prot = prot if prot is not 0 else UC_PROT_ALL 76 | 77 | self.emu.memory.mem_map(load_base + segment.header.p_vaddr, segment.header.p_memsz, prot) 78 | self.emu.memory.mem_write(load_base + segment.header.p_vaddr, segment.data()) 79 | 80 | rel_section = None 81 | for section in elf.iter_sections(): 82 | if not isinstance(section, RelocationSection): 83 | continue 84 | rel_section = section 85 | break 86 | 87 | # Parse section header (Linking view). 88 | dynsym = elf.get_section_by_name(".dynsym") 89 | dynstr = elf.get_section_by_name(".dynstr") 90 | 91 | # Find init array. 92 | init_array_size = 0 93 | init_array_offset = 0 94 | init_array = [] 95 | for x in elf.iter_segments(): 96 | if x.header.p_type == "PT_DYNAMIC": 97 | for tag in x.iter_tags(): 98 | if tag.entry.d_tag == "DT_INIT_ARRAYSZ": 99 | init_array_size = tag.entry.d_val 100 | elif tag.entry.d_tag == "DT_INIT_ARRAY": 101 | init_array_offset = tag.entry.d_val 102 | 103 | for _ in range(int(init_array_size / 4)): 104 | # covert va to file offset 105 | for seg in load_segments: 106 | if seg.header.p_vaddr <= init_array_offset < seg.header.p_vaddr + seg.header.p_memsz: 107 | init_array_foffset = init_array_offset - seg.header.p_vaddr + seg.header.p_offset 108 | fstream.seek(init_array_foffset) 109 | data = fstream.read(4) 110 | fun_ptr = struct.unpack('I', data)[0] 111 | if fun_ptr != 0: 112 | # fun_ptr += load_base 113 | init_array.append(fun_ptr + load_base) 114 | #print ("find init array for :%s %x" % (filename, fun_ptr)) 115 | else: 116 | # search in reloc 117 | for rel in rel_section.iter_relocations(): 118 | rel_info_type = rel['r_info_type'] 119 | rel_addr = rel['r_offset'] 120 | if rel_info_type == arm.R_ARM_ABS32 and rel_addr == init_array_offset: 121 | sym = dynsym.get_symbol(rel['r_info_sym']) 122 | sym_value = sym['st_value'] 123 | init_array.append(load_base + sym_value) 124 | #print ("find init array for :%s %x" % (filename, sym_value)) 125 | break 126 | init_array_offset += 4 127 | 128 | # Resolve all symbols. 129 | symbols_resolved = dict() 130 | 131 | for section in elf.iter_sections(): 132 | if not isinstance(section, SymbolTableSection): 133 | continue 134 | 135 | itersymbols = section.iter_symbols() 136 | next(itersymbols) # Skip first symbol which is always NULL. 137 | for symbol in itersymbols: 138 | symbol_address = self._elf_get_symval(elf, load_base, symbol) 139 | if symbol_address is not None: 140 | symbols_resolved[symbol.name] = SymbolResolved(symbol_address, symbol) 141 | 142 | # Relocate. 143 | for section in elf.iter_sections(): 144 | if not isinstance(section, RelocationSection): 145 | continue 146 | 147 | for rel in section.iter_relocations(): 148 | sym = dynsym.get_symbol(rel['r_info_sym']) 149 | sym_value = sym['st_value'] 150 | 151 | rel_addr = load_base + rel['r_offset'] # Location where relocation should happen 152 | rel_info_type = rel['r_info_type'] 153 | 154 | 155 | # Relocation table for ARM 156 | if rel_info_type == arm.R_ARM_ABS32: 157 | # Create the new value. 158 | value = load_base + sym_value 159 | # Write the new value 160 | self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little')) 161 | 162 | elif rel_info_type == arm.R_ARM_GLOB_DAT or \ 163 | rel_info_type == arm.R_ARM_JUMP_SLOT or \ 164 | rel_info_type == arm.R_AARCH64_GLOB_DAT or \ 165 | rel_info_type == arm.R_AARCH64_JUMP_SLOT: 166 | # Resolve the symbol. 167 | if sym.name in symbols_resolved: 168 | value = symbols_resolved[sym.name].address 169 | 170 | # Write the new value 171 | self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little')) 172 | elif rel_info_type == arm.R_ARM_RELATIVE or \ 173 | rel_info_type == arm.R_AARCH64_RELATIVE: 174 | if sym_value == 0: 175 | # Load address at which it was linked originally. 176 | value_orig_bytes = self.emu.mu.mem_read(rel_addr, 4) 177 | value_orig = int.from_bytes(value_orig_bytes, byteorder='little') 178 | 179 | # Create the new value 180 | value = load_base + value_orig 181 | 182 | # Write the new value 183 | self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little')) 184 | else: 185 | raise NotImplementedError() 186 | else: 187 | logger.error("Unhandled relocation type %i." % rel_info_type) 188 | 189 | # Store information about loaded module. 190 | module = Module(filename, load_base, bound_high - bound_low, symbols_resolved, init_array) 191 | self.modules.append(module) 192 | 193 | #do init 194 | 195 | 196 | return module 197 | 198 | def _elf_get_symval(self, elf, elf_base, symbol): 199 | if symbol.name in self.symbol_hooks: 200 | return self.symbol_hooks[symbol.name] 201 | 202 | if symbol['st_shndx'] == 'SHN_UNDEF': 203 | # External symbol, lookup value. 204 | target = self._elf_lookup_symbol(symbol.name) 205 | if target is None: 206 | # Extern symbol not found 207 | if symbol['st_info']['bind'] == 'STB_WEAK': 208 | # Weak symbol initialized as 0 209 | return 0 210 | else: 211 | logger.error('=> Undefined external symbol: %s' % symbol.name) 212 | return None 213 | else: 214 | return target 215 | elif symbol['st_shndx'] == 'SHN_ABS': 216 | # Absolute symbol. 217 | return elf_base + symbol['st_value'] 218 | else: 219 | # Internally defined symbol. 220 | return elf_base + symbol['st_value'] 221 | 222 | def _elf_lookup_symbol(self, name): 223 | for module in self.modules: 224 | if name in module.symbols: 225 | symbol = module.symbols[name] 226 | 227 | if symbol.address != 0: 228 | return symbol.address 229 | 230 | return None 231 | 232 | def __iter__(self): 233 | for x in self.modules: 234 | yield x 235 | -------------------------------------------------------------------------------- /androidemu/vfs/file_system.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | import posixpath 4 | import sys 5 | 6 | from androidemu.config import WRITE_FSTAT_TIMES 7 | from androidemu.cpu.syscall_handlers import SyscallHandlers 8 | from androidemu.utils import memory_helpers 9 | from androidemu.vfs import file_helpers 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | OVERRIDE_URANDOM = False 14 | OVERRIDE_URANDOM_BYTE = b"\x00" 15 | 16 | 17 | class VirtualFile: 18 | 19 | def __init__(self, name, file_descriptor, name_virt=None): 20 | self.name = name 21 | self.name_virt = name_virt 22 | self.descriptor = file_descriptor 23 | 24 | 25 | class VirtualFileSystem: 26 | 27 | """ 28 | :type syscall_handler SyscallHandlers 29 | """ 30 | 31 | def __init__(self, root_path, syscall_handler): 32 | self._root_path = root_path 33 | 34 | # TODO: Improve fd logic. 35 | self._file_descriptor_counter = 3 36 | self._file_descriptors = dict() 37 | self._file_descriptors[0] = VirtualFile('stdin', 0) 38 | self._file_descriptors[1] = VirtualFile('stdout', 1) 39 | self._file_descriptors[2] = VirtualFile('stderr', 2) 40 | 41 | syscall_handler.set_handler(0x3, "read", 3, self._handle_read) 42 | syscall_handler.set_handler(0x5, "open", 3, self._handle_open) 43 | syscall_handler.set_handler(0x6, "close", 1, self._handle_close) 44 | syscall_handler.set_handler(0x92, "writev", 3, self._handle_writev) 45 | syscall_handler.set_handler(0xC5, "fstat64", 2, self._handle_fstat64) 46 | syscall_handler.set_handler(0x142, "openat", 4, self._handle_openat) 47 | syscall_handler.set_handler(0x147, "fstatat64", 4, self._handle_fstatat64) 48 | 49 | def translate_path(self, filename): 50 | if filename.startswith("/"): 51 | filename = filename[1:] 52 | 53 | if os.name == 'nt': 54 | filename = filename.replace(':', '_') 55 | 56 | file_path = posixpath.join(self._root_path, filename) 57 | file_path = posixpath.normpath(file_path) 58 | 59 | if posixpath.commonpath([file_path, self._root_path]) != self._root_path: 60 | raise RuntimeError("Emulated binary tried to escape vfs jail.") 61 | 62 | return file_path 63 | 64 | def _store_fd(self, name, name_virt, file_descriptor): 65 | next_fd = self._file_descriptor_counter 66 | self._file_descriptor_counter += 1 67 | self._file_descriptors[next_fd] = VirtualFile(name, file_descriptor, name_virt=name_virt) 68 | return next_fd 69 | 70 | def _open_file(self, filename): 71 | # Special cases, such as /dev/urandom. 72 | orig_filename = filename 73 | 74 | if filename == '/dev/urandom': 75 | logger.info("File opened '%s'" % filename) 76 | return self._store_fd('/dev/urandom', None, 'urandom') 77 | 78 | file_path = self.translate_path(filename) 79 | 80 | if os.path.isfile(file_path): 81 | logger.info("File opened '%s'" % orig_filename) 82 | flags = os.O_RDWR 83 | if hasattr(os, "O_BINARY"): 84 | flags |= os.O_BINARY 85 | return self._store_fd(orig_filename, file_path, os.open(file_path, flags=flags)) 86 | else: 87 | logger.warning("File does not exist '%s'" % orig_filename) 88 | return -1 89 | 90 | def _handle_read(self, mu, fd, buf_addr, count): 91 | """ 92 | ssize_t read(int fd, void *buf, size_t count); 93 | 94 | On files that support seeking, the read operation commences at the current file offset, and the file offset 95 | is incremented by the number of bytes read. If the current file offset is at or past the end of file, 96 | no bytes are read, and read() returns zero. 97 | 98 | If count is zero, read() may detect the errors described below. In the absence of any errors, or if read() 99 | does not check for errors, a read() with a count of 0 returns zero and has no other effects. 100 | 101 | If count is greater than SSIZE_MAX, the result is unspecified. 102 | """ 103 | if fd <= 2: 104 | raise NotImplementedError("Unsupported read operation for file descriptor %d." % fd) 105 | 106 | if fd not in self._file_descriptors: 107 | # TODO: Return valid error. 108 | raise NotImplementedError() 109 | 110 | file = self._file_descriptors[fd] 111 | 112 | logger.info("Reading %d bytes from '%s'" % (count, file.name)) 113 | 114 | if file.descriptor == 'urandom': 115 | if OVERRIDE_URANDOM: 116 | buf = OVERRIDE_URANDOM_BYTE * count 117 | else: 118 | buf = os.urandom(count) 119 | else: 120 | buf = os.read(file.descriptor, count) 121 | 122 | result = len(buf) 123 | mu.mem_write(buf_addr, buf) 124 | return result 125 | 126 | def _handle_open(self, mu, filename_ptr, flags, mode): 127 | """ 128 | int open(const char *pathname, int flags, mode_t mode); 129 | 130 | return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately). 131 | """ 132 | filename = memory_helpers.read_utf8(mu, filename_ptr) 133 | 134 | return self._open_file(filename) 135 | 136 | def _handle_close(self, mu, fd): 137 | """ 138 | int close(int fd); 139 | 140 | close() closes a file descriptor, so that it no longer refers to any file and may be reused. Any record locks 141 | (see fcntl(2)) held on the file it was associated with, and owned by the process, are removed (regardless of 142 | the file descriptor that was used to obtain the lock). 143 | 144 | close() returns zero on success. On error, -1 is returned, and errno is set appropriately. 145 | """ 146 | if fd not in self._file_descriptors: 147 | return 0 148 | 149 | file = self._file_descriptors[fd] 150 | 151 | if file.descriptor != 'urandom': 152 | logger.info("File closed '%s'" % file.name) 153 | os.close(file.descriptor) 154 | else: 155 | logger.info("File closed '%s'" % '/dev/urandom') 156 | 157 | return 0 158 | 159 | def _handle_writev(self, mu, fd, vec, vlen): 160 | if fd == 2: 161 | for i in range(0, vlen): 162 | addr = memory_helpers.read_ptr(mu, (i * 8) + vec) 163 | size = memory_helpers.read_ptr(mu, (i * 8) + vec + 4) 164 | sys.stderr.buffer.write(mu.mem_read(addr, size)) 165 | 166 | return 0 167 | 168 | raise NotImplementedError() 169 | 170 | def _handle_fstat64(self, mu, fd, buf_ptr): 171 | """ 172 | These functions return information about a file. No permissions are required on the file itself, but-in the 173 | case of stat() and lstat() - execute (search) permission is required on all of the directories in path that 174 | lead to the file. 175 | 176 | fstat() is identical to stat(), except that the file to be stat-ed is specified by the file descriptor fd. 177 | """ 178 | if fd not in self._file_descriptors: 179 | return -1 180 | 181 | file = self._file_descriptors[fd] 182 | logger.info("File stat64 '%s'" % file.name) 183 | 184 | stat = file_helpers.stat64(file.name_virt) 185 | # stat = os.fstat(file.descriptor) 186 | file_helpers.stat_to_memory(mu, buf_ptr, stat, WRITE_FSTAT_TIMES) 187 | 188 | return 0 189 | 190 | def _handle_openat(self, mu, dfd, filename_ptr, flags, mode): 191 | """ 192 | int openat(int dirfd, const char *pathname, int flags, mode_t mode); 193 | 194 | On success, openat() returns a new file descriptor. 195 | On error, -1 is returned and errno is set to indicate the error. 196 | 197 | EBADF 198 | dirfd is not a valid file descriptor. 199 | ENOTDIR 200 | pathname is relative and dirfd is a file descriptor referring to a file other than a directory. 201 | """ 202 | filename = memory_helpers.read_utf8(mu, filename_ptr) 203 | 204 | if not filename.startswith("/") and dfd != 0: 205 | raise NotImplementedError("Directory file descriptor has not been implemented yet.") 206 | 207 | return self._open_file(filename) 208 | 209 | def _handle_fstatat64(self, mu, dirfd, pathname_ptr, buf, flags): 210 | """ 211 | int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags); 212 | 213 | If the pathname given in pathname is relative, then it is interpreted relative to the directory referred 214 | to by the file descriptor dirfd (rather than relative to the current working directory of the calling process, 215 | as is done by stat(2) for a relative pathname). 216 | 217 | If pathname is relative and dirfd is the special value AT_FDCWD, 218 | then pathname is interpreted relative to the current working directory of the calling process (like stat(2)). 219 | 220 | If pathname is absolute, then dirfd is ignored. 221 | 222 | flags can either be 0, or include one or more of the following flags .. 223 | 224 | On success, fstatat() returns 0. On error, -1 is returned and errno is set to indicate the error. 225 | """ 226 | pathname = memory_helpers.read_utf8(mu, pathname_ptr) 227 | 228 | if not pathname.startswith('/'): 229 | raise NotImplementedError("Directory file descriptor has not been implemented yet.") 230 | 231 | if not flags == 0: 232 | if flags & 0x100: # AT_SYMLINK_NOFOLLOW 233 | pass 234 | if flags & 0x800: # AT_NO_AUTOMOUNT 235 | pass 236 | # raise NotImplementedError("Flags has not been implemented yet.") 237 | 238 | logger.info("File fstatat64 '%s'" % pathname) 239 | pathname = self.translate_path(pathname) 240 | 241 | if not os.path.exists(pathname): 242 | logger.warning('> File was not found.') 243 | return -1 244 | 245 | logger.warning('> File was found.') 246 | 247 | stat = file_helpers.stat64(path=pathname) 248 | # stat = os.stat(path=file_path, dir_fd=None, follow_symlinks=False) 249 | file_helpers.stat_to_memory(mu, buf, stat, WRITE_FSTAT_TIMES) 250 | 251 | return 0 252 | -------------------------------------------------------------------------------- /androidemu/java/jni_env.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from androidemu.hooker import Hooker 4 | from androidemu.java.classes.constructor import Constructor 5 | from androidemu.java.classes.method import Method 6 | from androidemu.java.constant_values import MODIFIER_STATIC 7 | from androidemu.java.helpers.native_method import native_method 8 | from androidemu.java.java_classloader import JavaClassLoader 9 | from androidemu.java.jni_const import * 10 | from androidemu.java.jni_ref import * 11 | from androidemu.java.reference_table import ReferenceTable 12 | from androidemu.utils import memory_helpers 13 | 14 | logger = logging.getLogger(__name__) 15 | 16 | 17 | # This class attempts to mimic the JNINativeInterface table. 18 | class JNIEnv: 19 | """ 20 | :type class_loader JavaClassLoader 21 | :type hooker Hooker 22 | """ 23 | 24 | def __init__(self, emu, class_loader, hooker): 25 | self._emu = emu 26 | self._class_loader = class_loader 27 | self._locals = ReferenceTable(start=1, max_entries=2048) 28 | self._globals = ReferenceTable(start=4096, max_entries=512000) 29 | 30 | (self.address_ptr, self.address) = hooker.write_function_table({ 31 | 4: self.get_version, 32 | 5: self.define_class, 33 | 6: self.find_class, 34 | 7: self.from_reflected_method, 35 | 8: self.from_reflected_field, 36 | 9: self.to_reflected_method, 37 | 10: self.get_superclass, 38 | 11: self.is_assignable_from, 39 | 12: self.to_reflected_field, 40 | 13: self.throw, 41 | 14: self.throw_new, 42 | 15: self.exception_occurred, 43 | 16: self.exception_describe, 44 | 17: self.exception_clear, 45 | 18: self.fatal_error, 46 | 19: self.push_local_frame, 47 | 20: self.pop_local_frame, 48 | 21: self.new_global_ref, 49 | 22: self.delete_global_ref, 50 | 23: self.delete_local_ref, 51 | 24: self.is_same_object, 52 | 25: self.new_local_ref, 53 | 26: self.ensure_local_capacity, 54 | 27: self.alloc_object, 55 | 28: self.new_object, 56 | 29: self.new_object_v, 57 | 30: self.new_object_a, 58 | 31: self.get_object_class, 59 | 32: self.is_instance_of, 60 | 33: self.get_method_id, 61 | 34: self.call_object_method, 62 | 35: self.call_object_method_v, 63 | 36: self.call_object_method_a, 64 | 37: self.call_boolean_method, 65 | 38: self.call_boolean_method_v, 66 | 39: self.call_boolean_method_a, 67 | 40: self.call_byte_method, 68 | 41: self.call_byte_method_v, 69 | 42: self.call_byte_method_a, 70 | 43: self.call_char_method, 71 | 44: self.call_char_method_v, 72 | 45: self.call_char_method_a, 73 | 46: self.call_short_method, 74 | 47: self.call_short_method_v, 75 | 48: self.call_short_method_a, 76 | 49: self.call_int_method, 77 | 50: self.call_int_method_v, 78 | 51: self.call_int_method_a, 79 | 52: self.call_long_method, 80 | 53: self.call_long_method_v, 81 | 54: self.call_long_method_a, 82 | 55: self.call_float_method, 83 | 56: self.call_float_method_v, 84 | 57: self.call_float_method_a, 85 | 58: self.call_double_method, 86 | 59: self.call_double_method_v, 87 | 60: self.call_double_method_a, 88 | 61: self.call_void_method, 89 | 62: self.call_void_method_v, 90 | 63: self.call_void_method_a, 91 | 64: self.call_nonvirtual_object_method, 92 | 65: self.call_nonvirtual_object_method_v, 93 | 66: self.call_nonvirtual_object_method_a, 94 | 67: self.call_nonvirtual_boolean_method, 95 | 68: self.call_nonvirtual_boolean_method_v, 96 | 69: self.call_nonvirtual_boolean_method_a, 97 | 70: self.call_nonvirtual_byte_method, 98 | 71: self.call_nonvirtual_byte_method_v, 99 | 72: self.call_nonvirtual_byte_method_a, 100 | 73: self.call_nonvirtual_char_method, 101 | 74: self.call_nonvirtual_char_method_v, 102 | 75: self.call_nonvirtual_char_method_a, 103 | 76: self.call_nonvirtual_short_method, 104 | 77: self.call_nonvirtual_short_method_v, 105 | 78: self.call_nonvirtual_short_method_a, 106 | 79: self.call_nonvirtual_int_method, 107 | 80: self.call_nonvirtual_int_method_v, 108 | 81: self.call_nonvirtual_int_method_a, 109 | 82: self.call_nonvirtual_long_method, 110 | 83: self.call_nonvirtual_long_method_v, 111 | 84: self.call_nonvirtual_long_method_a, 112 | 85: self.call_nonvirtual_float_method, 113 | 86: self.call_nonvirtual_float_method_v, 114 | 87: self.call_nonvirtual_float_method_a, 115 | 88: self.call_nonvirtual_double_method, 116 | 89: self.call_nonvirtual_double_method_v, 117 | 90: self.call_nonvirtual_double_method_a, 118 | 91: self.call_nonvirtual_void_method, 119 | 92: self.call_nonvirtual_void_method_v, 120 | 93: self.call_nonvirtual_void_method_a, 121 | 94: self.get_field_id, 122 | 95: self.get_object_field, 123 | 96: self.get_boolean_field, 124 | 97: self.get_byte_field, 125 | 98: self.get_char_field, 126 | 99: self.get_short_field, 127 | 100: self.get_int_field, 128 | 101: self.get_long_field, 129 | 102: self.get_float_field, 130 | 103: self.get_double_field, 131 | 104: self.set_object_field, 132 | 105: self.set_boolean_field, 133 | 106: self.set_byte_field, 134 | 107: self.set_char_field, 135 | 108: self.set_short_field, 136 | 109: self.set_int_field, 137 | 110: self.set_long_field, 138 | 111: self.set_float_field, 139 | 112: self.set_double_field, 140 | 113: self.get_static_method_id, 141 | 114: self.call_static_object_method, 142 | 115: self.call_static_object_method_v, 143 | 116: self.call_static_object_method_a, 144 | 117: self.call_static_boolean_method, 145 | 118: self.call_static_boolean_method_v, 146 | 119: self.call_static_boolean_method_a, 147 | 120: self.call_static_byte_method, 148 | 121: self.call_static_byte_method_v, 149 | 122: self.call_static_byte_method_a, 150 | 123: self.call_static_char_method, 151 | 124: self.call_static_char_method_v, 152 | 125: self.call_static_char_method_a, 153 | 126: self.call_static_short_method, 154 | 127: self.call_static_short_method_v, 155 | 128: self.call_static_short_method_a, 156 | 129: self.call_static_int_method, 157 | 130: self.call_static_int_method_v, 158 | 131: self.call_static_int_method_a, 159 | 132: self.call_static_long_method, 160 | 133: self.call_static_long_method_v, 161 | 134: self.call_static_long_method_a, 162 | 135: self.call_static_float_method, 163 | 136: self.call_static_float_method_v, 164 | 137: self.call_static_float_method_a, 165 | 138: self.call_static_double_method, 166 | 139: self.call_static_double_method_v, 167 | 140: self.call_static_double_method_a, 168 | 141: self.call_static_void_method, 169 | 142: self.call_static_void_method_v, 170 | 143: self.call_static_void_method_a, 171 | 144: self.get_static_field_id, 172 | 145: self.get_static_object_field, 173 | 146: self.get_static_boolean_field, 174 | 147: self.get_static_byte_field, 175 | 148: self.get_static_char_field, 176 | 149: self.get_static_short_field, 177 | 150: self.get_static_int_field, 178 | 151: self.get_static_long_field, 179 | 152: self.get_static_float_field, 180 | 153: self.get_static_double_field, 181 | 154: self.set_static_object_field, 182 | 155: self.set_static_boolean_field, 183 | 156: self.set_static_byte_field, 184 | 157: self.set_static_char_field, 185 | 158: self.set_static_short_field, 186 | 159: self.set_static_int_field, 187 | 160: self.set_static_long_field, 188 | 161: self.set_static_float_field, 189 | 162: self.set_static_double_field, 190 | 163: self.new_string, 191 | 164: self.get_string_length, 192 | 165: self.get_string_chars, 193 | 166: self.release_string_chars, 194 | 167: self.new_string_utf, 195 | 168: self.get_string_utf_length, 196 | 169: self.get_string_utf_chars, 197 | 170: self.release_string_utf_chars, 198 | 171: self.get_array_length, 199 | 172: self.new_object_array, 200 | 173: self.get_object_array_element, 201 | 174: self.set_object_array_element, 202 | 175: self.new_boolean_array, 203 | 176: self.new_byte_array, 204 | 177: self.new_char_array, 205 | 178: self.new_short_array, 206 | 179: self.new_int_array, 207 | 180: self.new_long_array, 208 | 181: self.new_float_array, 209 | 182: self.new_double_array, 210 | 183: self.get_boolean_array_elements, 211 | 184: self.get_byte_array_elements, 212 | 185: self.get_char_array_elements, 213 | 186: self.get_short_array_elements, 214 | 187: self.get_int_array_elements, 215 | 188: self.get_long_array_elements, 216 | 189: self.get_float_array_elements, 217 | 190: self.get_double_array_elements, 218 | 191: self.release_boolean_array_elements, 219 | 192: self.release_byte_array_elements, 220 | 193: self.release_char_array_elements, 221 | 194: self.release_short_array_elements, 222 | 195: self.release_int_array_elements, 223 | 196: self.release_long_array_elements, 224 | 197: self.release_float_array_elements, 225 | 198: self.release_double_array_elements, 226 | 199: self.get_boolean_array_region, 227 | 200: self.get_byte_array_region, 228 | 201: self.get_char_array_region, 229 | 202: self.get_short_array_region, 230 | 203: self.get_int_array_region, 231 | 204: self.get_long_array_region, 232 | 205: self.get_float_array_region, 233 | 206: self.get_double_array_region, 234 | 207: self.set_boolean_array_region, 235 | 208: self.set_byte_array_region, 236 | 209: self.set_char_array_region, 237 | 210: self.set_short_array_region, 238 | 211: self.set_int_array_region, 239 | 212: self.set_long_array_region, 240 | 213: self.set_float_array_region, 241 | 214: self.set_double_array_region, 242 | 215: self.register_natives, 243 | 216: self.unregister_natives, 244 | 217: self.monitor_enter, 245 | 218: self.monitor_exit, 246 | 219: self.get_java_vm, 247 | 220: self.get_string_region, 248 | 221: self.get_string_utf_region, 249 | 222: self.get_primitive_array_critical, 250 | 223: self.release_primitive_array_critical, 251 | 224: self.get_string_critical, 252 | 225: self.release_string_critical, 253 | 226: self.new_weak_global_ref, 254 | 227: self.delete_weak_global_ref, 255 | 228: self.exception_check, 256 | 229: self.new_direct_byte_buffer, 257 | 230: self.get_direct_buffer_address, 258 | 231: self.get_direct_buffer_capacity, 259 | 232: self.get_object_ref_type 260 | }) 261 | 262 | def get_reference(self, idx): 263 | if idx == 0: 264 | return None 265 | 266 | if self._locals.in_range(idx): 267 | return self._locals.get(idx) 268 | 269 | if self._globals.in_range(idx): 270 | return self._globals.get(idx) 271 | 272 | raise RuntimeError('Invalid get_reference(%d)' % idx) 273 | 274 | def add_local_reference(self, obj): 275 | if not isinstance(obj, jobject): 276 | raise ValueError('Expected a jobject.') 277 | 278 | return self._locals.add(obj) 279 | 280 | def set_local_reference(self, idx, newobj): 281 | if not isinstance(newobj, jobject): 282 | raise ValueError('Expected a jobject.') 283 | self._locals.set(idx, newobj) 284 | 285 | 286 | def get_local_reference(self, idx): 287 | return self._locals.get(idx) 288 | 289 | def delete_local_reference(self, obj): 290 | if not isinstance(obj, jobject): 291 | raise ValueError('Expected a jobject.') 292 | 293 | self._locals.remove(obj) 294 | 295 | def clear_locals(self): 296 | self._locals.clear() 297 | 298 | def add_global_reference(self, obj): 299 | if not isinstance(obj, jobject): 300 | raise ValueError('Expected a jobject.') 301 | 302 | return self._globals.add(obj) 303 | 304 | def get_global_reference(self, idx): 305 | return self._globals.get(idx) 306 | 307 | def delete_global_reference(self, obj): 308 | if not isinstance(obj, jobject): 309 | raise ValueError('Expected a jobject.') 310 | 311 | return self._globals.remove(obj) 312 | 313 | def read_args_v(self, mu, args_ptr, args_list): 314 | if args_list is None: 315 | return [] 316 | 317 | result = [] 318 | 319 | for arg_name in args_list: 320 | if arg_name == 'jint': 321 | ref = int.from_bytes(mu.mem_read(args_ptr, 4), byteorder='little') 322 | result.append(ref) 323 | args_ptr = args_ptr + 4 324 | elif arg_name == 'jstring': 325 | ref = int.from_bytes(mu.mem_read(args_ptr, 4), byteorder='little') 326 | result.append(self.get_reference(ref)) 327 | args_ptr = args_ptr + 4 328 | elif arg_name == 'jobject': 329 | ref = int.from_bytes(mu.mem_read(args_ptr, 4), byteorder='little') 330 | result.append(self.get_reference(ref)) 331 | args_ptr = args_ptr + 4 332 | else: 333 | raise NotImplementedError('Unknown arg name %s' % arg_name) 334 | 335 | return result 336 | 337 | @native_method 338 | def get_version(self, mu, env): 339 | raise NotImplementedError() 340 | 341 | @native_method 342 | def define_class(self, mu, env): 343 | raise NotImplementedError() 344 | 345 | @native_method 346 | def find_class(self, mu, env, name_ptr): 347 | """ 348 | Returns a class object from a fully-qualified name, or NULL if the class cannot be found. 349 | """ 350 | name = memory_helpers.read_utf8(mu, name_ptr) 351 | logger.debug("JNIEnv->FindClass(%s) was called" % name) 352 | 353 | if name.startswith('['): 354 | raise NotImplementedError('Array type not implemented.') 355 | 356 | clazz = self._class_loader.find_class_by_name(name) 357 | 358 | if clazz is None: 359 | # TODO: Proper Java error? 360 | raise RuntimeError('Could not find class \'%s\' for JNIEnv.' % name) 361 | 362 | if clazz.jvm_ignore: 363 | return 0 364 | 365 | return self.add_local_reference(jclass(clazz)) 366 | 367 | @native_method 368 | def from_reflected_method(self, mu, env): 369 | raise NotImplementedError() 370 | 371 | @native_method 372 | def from_reflected_field(self, mu, env): 373 | raise NotImplementedError() 374 | 375 | @native_method 376 | def to_reflected_method(self, mu, env, class_idx, method_id, is_static): 377 | """ 378 | Converts a method ID derived from cls to a java.lang.reflect.Method or java.lang.reflect.Constructor object. 379 | isStatic must be set to JNI_TRUE if the method ID refers to a static field, and JNI_FALSE otherwise. 380 | 381 | Throws OutOfMemoryError and returns 0 if fails. 382 | """ 383 | clazz = self.get_reference(class_idx) 384 | 385 | if not isinstance(clazz, jclass): 386 | raise ValueError('Expected a jclass.') 387 | 388 | method = clazz.value.find_method_by_id(method_id) 389 | if method is None: 390 | raise RuntimeError("Could not find method ('%u') in class %s." % (method_id, clazz.value.jvm_name)) 391 | 392 | if method.modifier & MODIFIER_STATIC: 393 | mu.mem_write(is_static, int(JNI_TRUE).to_bytes(4, byteorder='little')) 394 | else: 395 | mu.mem_write(is_static, int(JNI_FALSE).to_bytes(4, byteorder='little')) 396 | 397 | logger.debug("JNIEnv->ToReflectedMethod(%s, %s, %u) was called" % (clazz.value.jvm_name, 398 | method.name, 399 | is_static)) 400 | 401 | if method.name == '' and method.signature.endswith('V'): 402 | return Constructor(clazz.value, method) 403 | else: 404 | return Method(clazz.value, method) 405 | 406 | @native_method 407 | def get_superclass(self, mu, env): 408 | raise NotImplementedError() 409 | 410 | @native_method 411 | def is_assignable_from(self, mu, env): 412 | raise NotImplementedError() 413 | 414 | @native_method 415 | def to_reflected_field(self, mu, env): 416 | raise NotImplementedError() 417 | 418 | @native_method 419 | def throw(self, mu, env): 420 | raise NotImplementedError() 421 | 422 | @native_method 423 | def throw_new(self, mu, env): 424 | raise NotImplementedError() 425 | 426 | @native_method 427 | def exception_occurred(self, mu, env): 428 | raise NotImplementedError() 429 | 430 | @native_method 431 | def exception_describe(self, mu, env): 432 | raise NotImplementedError() 433 | 434 | @native_method 435 | def exception_clear(self, mu, env): 436 | """ 437 | Clears any exception that is currently being thrown. 438 | If no exception is currently being thrown, this routine has no effect. 439 | """ 440 | logger.debug("JNIEnv->ExceptionClear() was called") 441 | # TODO: Implement 442 | return None 443 | 444 | @native_method 445 | def fatal_error(self, mu, env): 446 | raise NotImplementedError() 447 | 448 | @native_method 449 | def push_local_frame(self, mu, env): 450 | raise NotImplementedError() 451 | 452 | @native_method 453 | def pop_local_frame(self, mu, env): 454 | raise NotImplementedError() 455 | 456 | @native_method 457 | def new_global_ref(self, mu, env, obj): 458 | """ 459 | Creates a new global reference to the object referred to by the obj argument. The obj argument may be a 460 | global or local reference. Global references must be explicitly disposed of by calling DeleteGlobalRef(). 461 | """ 462 | logger.debug("JNIEnv->NewGlobalRef(%d) was called" % obj) 463 | 464 | if obj == 0: 465 | return 0 466 | 467 | obj = self.get_local_reference(obj) 468 | 469 | if obj is None: 470 | # TODO: Implement global > global support (?) 471 | raise NotImplementedError('Invalid local reference obj.') 472 | 473 | return self.add_global_reference(obj) 474 | 475 | @native_method 476 | def delete_global_ref(self, mu, env, idx): 477 | """ 478 | Deletes the global reference pointed to by globalRef. 479 | """ 480 | logger.debug("JNIEnv->DeleteGlobalRef(%d) was called" % idx) 481 | 482 | if idx == 0: 483 | return None 484 | 485 | obj = self.get_global_reference(idx) 486 | self.delete_global_reference(obj) 487 | 488 | @native_method 489 | def delete_local_ref(self, mu, env, idx): 490 | """ 491 | Deletes the local reference pointed to by localRef. 492 | """ 493 | logger.debug("JNIEnv->DeleteLocalRef(%d) was called" % idx) 494 | 495 | if idx == 0: 496 | return None 497 | 498 | obj = self.get_local_reference(idx) 499 | self.delete_local_reference(obj) 500 | 501 | @native_method 502 | def is_same_object(self, mu, env, ref1, ref2): 503 | """ 504 | Returns JNI_TRUE if ref1 and ref2 refer to the same Java object, or are both NULL; otherwise, returns JNI_FALSE. 505 | """ 506 | logger.debug("JNIEnv->IsSameObject(%d, %d) was called" % (ref1, ref2)) 507 | 508 | if ref1 == 0 and ref2 == 0: 509 | return JNI_TRUE 510 | 511 | obj1 = self.get_reference(ref1) 512 | obj2 = self.get_reference(ref2) 513 | 514 | if obj1 is obj2: 515 | return JNI_TRUE 516 | 517 | return JNI_FALSE 518 | 519 | @native_method 520 | def new_local_ref(self, mu, env, ref): 521 | """ 522 | Creates a new local reference that refers to the same object as ref. 523 | The given ref may be a global or local reference. Returns NULL if ref refers to null. 524 | """ 525 | logger.debug("JNIEnv->NewLocalRef(%d) was called" % ref) 526 | 527 | obj = self.get_reference(ref) 528 | 529 | if obj is None: 530 | return 0 531 | 532 | return self.add_local_reference(obj) 533 | 534 | @native_method 535 | def ensure_local_capacity(self, mu, env): 536 | raise NotImplementedError() 537 | 538 | @native_method 539 | def alloc_object(self, mu, env): 540 | raise NotImplementedError() 541 | 542 | @native_method 543 | def new_object(self, mu, env): 544 | raise NotImplementedError() 545 | 546 | @native_method 547 | def new_object_v(self, mu, env, clazz_idx, method_id, args): 548 | # Get class reference. 549 | clazz = self.get_reference(clazz_idx) 550 | if not isinstance(clazz, jclass): 551 | raise ValueError('Expected a jclass.') 552 | 553 | # Create class instance. 554 | obj = clazz.value() 555 | 556 | # Get constructor method. 557 | method = obj.__class__.find_method_by_id(method_id) 558 | if method.name != '' or not method.signature.endswith('V'): 559 | raise ValueError('Class constructor has the wrong name or does not return void.') 560 | 561 | logger.debug("JNIEnv->NewObjectV(%s, %s, 0x%x) was called" % (clazz.value.jvm_name, method.name, args)) 562 | 563 | # Parse arguments. 564 | constructor_args = self.read_args_v(mu, args, method.args_list) 565 | 566 | # Execute function. 567 | method.func(obj, self._emu, *constructor_args) 568 | 569 | return self.add_local_reference(jobject(obj)) 570 | 571 | @native_method 572 | def new_object_a(self, mu, env): 573 | raise NotImplementedError() 574 | 575 | @native_method 576 | def get_object_class(self, mu, env): 577 | raise NotImplementedError() 578 | 579 | @native_method 580 | def is_instance_of(self, mu, env, obj_idx, class_idx): 581 | """ 582 | Tests whether an object is an instance of a class. 583 | Returns JNI_TRUE if obj can be cast to clazz; otherwise, returns JNI_FALSE. A NULL object can be cast to any class. 584 | """ 585 | obj = self.get_reference(obj_idx) 586 | if not isinstance(obj, jobject): 587 | raise ValueError('Expected a jobject.') 588 | 589 | clazz = self.get_reference(class_idx) 590 | if not isinstance(clazz, jclass): 591 | raise ValueError('Expected a jclass.') 592 | 593 | # TODO: Casting check (?) 594 | 595 | return JNI_TRUE if obj.value.jvm_id == clazz.value.jvm_id else JNI_FALSE 596 | 597 | @native_method 598 | def get_method_id(self, mu, env, clazz_idx, name_ptr, sig_ptr): 599 | name = memory_helpers.read_utf8(mu, name_ptr) 600 | sig = memory_helpers.read_utf8(mu, sig_ptr) 601 | clazz = self.get_reference(clazz_idx) 602 | logger.debug("JNIEnv->GetMethodId(%d, %s, %s) was called" % (clazz_idx, name, sig)) 603 | 604 | if not isinstance(clazz, jclass): 605 | raise ValueError('Expected a jclass.') 606 | 607 | method = clazz.value.find_method(name, sig) 608 | 609 | if method is None: 610 | # TODO: Proper Java error? 611 | raise RuntimeError("Could not find method ('%s', '%s') in class %s." % (name, sig, clazz.value.jvm_name)) 612 | 613 | return method.jvm_id 614 | 615 | @native_method 616 | def call_object_method(self, mu, env): 617 | raise NotImplementedError() 618 | 619 | @native_method 620 | def call_object_method_v(self, mu, env, obj_idx, method_id, args): 621 | obj = self.get_reference(obj_idx) 622 | 623 | if not isinstance(obj, jobject): 624 | raise ValueError('Expected a jobject.') 625 | 626 | method = obj.value.__class__.find_method_by_id(method_id) 627 | if method is None: 628 | # TODO: Proper Java error? 629 | raise RuntimeError("Could not find method %d in object %s by id." % (method_id, obj.value.jvm_name)) 630 | 631 | logger.debug("JNIEnv->CallObjectMethodV(%s, %s <%s>, 0x%x) was called" % ( 632 | obj.value.jvm_name, 633 | method.name, 634 | method.signature, args)) 635 | 636 | # Parse arguments. 637 | constructor_args = self.read_args_v(mu, args, method.args_list) 638 | 639 | return method.func(obj.value, self._emu, *constructor_args) 640 | 641 | @native_method 642 | def call_object_method_a(self, mu, env): 643 | raise NotImplementedError() 644 | 645 | @native_method 646 | def call_boolean_method(self, mu, env): 647 | raise NotImplementedError() 648 | 649 | @native_method 650 | def call_boolean_method_v(self, mu, env): 651 | raise NotImplementedError() 652 | 653 | @native_method 654 | def call_boolean_method_a(self, mu, env): 655 | raise NotImplementedError() 656 | 657 | @native_method 658 | def call_byte_method(self, mu, env): 659 | raise NotImplementedError() 660 | 661 | @native_method 662 | def call_byte_method_v(self, mu, env): 663 | raise NotImplementedError() 664 | 665 | @native_method 666 | def call_byte_method_a(self, mu, env): 667 | raise NotImplementedError() 668 | 669 | @native_method 670 | def call_char_method(self, mu, env): 671 | raise NotImplementedError() 672 | 673 | @native_method 674 | def call_char_method_v(self, mu, env): 675 | raise NotImplementedError() 676 | 677 | @native_method 678 | def call_char_method_a(self, mu, env): 679 | raise NotImplementedError() 680 | 681 | @native_method 682 | def call_short_method(self, mu, env): 683 | raise NotImplementedError() 684 | 685 | @native_method 686 | def call_short_method_v(self, mu, env): 687 | raise NotImplementedError() 688 | 689 | @native_method 690 | def call_short_method_a(self, mu, env): 691 | raise NotImplementedError() 692 | 693 | @native_method 694 | def call_int_method(self, mu, env): 695 | raise NotImplementedError() 696 | 697 | @native_method 698 | def call_int_method_v(self, mu, env): 699 | raise NotImplementedError() 700 | 701 | @native_method 702 | def call_int_method_a(self, mu, env): 703 | raise NotImplementedError() 704 | 705 | @native_method 706 | def call_long_method(self, mu, env): 707 | raise NotImplementedError() 708 | 709 | @native_method 710 | def call_long_method_v(self, mu, env, obj_idx, method_id, args): 711 | obj = self.get_reference(obj_idx) 712 | 713 | if not isinstance(obj, jobject): 714 | raise ValueError('Expected a jobject.') 715 | 716 | method = obj.value.__class__.find_method_by_id(method_id) 717 | 718 | if method is None: 719 | # TODO: Proper Java error? 720 | raise RuntimeError("Could not find method %d in object %s by id." % (method_id, obj.value.jvm_name)) 721 | 722 | logger.debug("JNIEnv->CallLongMethodV(%s, %s <%s>, 0x%x) was called" % ( 723 | obj.value.jvm_name, 724 | method.name, 725 | method.signature, args)) 726 | 727 | # Parse arguments. 728 | constructor_args = self.read_args_v(mu, args, method.args_list) 729 | 730 | return method.func(obj.value, self._emu, *constructor_args) 731 | 732 | @native_method 733 | def call_long_method_a(self, mu, env): 734 | raise NotImplementedError() 735 | 736 | @native_method 737 | def call_float_method(self, mu, env): 738 | raise NotImplementedError() 739 | 740 | @native_method 741 | def call_float_method_v(self, mu, env): 742 | raise NotImplementedError() 743 | 744 | @native_method 745 | def call_float_method_a(self, mu, env): 746 | raise NotImplementedError() 747 | 748 | @native_method 749 | def call_double_method(self, mu, env): 750 | raise NotImplementedError() 751 | 752 | @native_method 753 | def call_double_method_v(self, mu, env): 754 | raise NotImplementedError() 755 | 756 | @native_method 757 | def call_double_method_a(self, mu, env): 758 | raise NotImplementedError() 759 | 760 | @native_method 761 | def call_void_method(self, mu, env): 762 | raise NotImplementedError() 763 | 764 | @native_method 765 | def call_void_method_v(self, mu, env, obj_idx, method_id, args): 766 | obj = self.get_reference(obj_idx) 767 | 768 | if not isinstance(obj, jobject): 769 | raise ValueError('Expected a jobject.') 770 | 771 | method = obj.value.__class__.find_method_by_id(method_id) 772 | 773 | if method is None: 774 | # TODO: Proper Java error? 775 | raise RuntimeError("Could not find method %d in object %s by id." % (method_id, obj.value.jvm_name)) 776 | 777 | logger.debug("JNIEnv->CallVoidMethodV(%s, %s <%s>, 0x%x) was called" % ( 778 | obj.value.jvm_name, 779 | method.name, 780 | method.signature, args)) 781 | 782 | method.func(obj.value, self._emu) 783 | 784 | return None 785 | 786 | @native_method 787 | def call_void_method_a(self, mu, env): 788 | raise NotImplementedError() 789 | 790 | @native_method 791 | def call_nonvirtual_object_method(self, mu, env): 792 | raise NotImplementedError() 793 | 794 | @native_method 795 | def call_nonvirtual_object_method_v(self, mu, env): 796 | raise NotImplementedError() 797 | 798 | @native_method 799 | def call_nonvirtual_object_method_a(self, mu, env): 800 | raise NotImplementedError() 801 | 802 | @native_method 803 | def call_nonvirtual_boolean_method(self, mu, env): 804 | raise NotImplementedError() 805 | 806 | @native_method 807 | def call_nonvirtual_boolean_method_v(self, mu, env): 808 | raise NotImplementedError() 809 | 810 | @native_method 811 | def call_nonvirtual_boolean_method_a(self, mu, env): 812 | raise NotImplementedError() 813 | 814 | @native_method 815 | def call_nonvirtual_byte_method(self, mu, env): 816 | raise NotImplementedError() 817 | 818 | @native_method 819 | def call_nonvirtual_byte_method_v(self, mu, env): 820 | raise NotImplementedError() 821 | 822 | @native_method 823 | def call_nonvirtual_byte_method_a(self, mu, env): 824 | raise NotImplementedError() 825 | 826 | @native_method 827 | def call_nonvirtual_char_method(self, mu, env): 828 | raise NotImplementedError() 829 | 830 | @native_method 831 | def call_nonvirtual_char_method_v(self, mu, env): 832 | raise NotImplementedError() 833 | 834 | @native_method 835 | def call_nonvirtual_char_method_a(self, mu, env): 836 | raise NotImplementedError() 837 | 838 | @native_method 839 | def call_nonvirtual_short_method(self, mu, env): 840 | raise NotImplementedError() 841 | 842 | @native_method 843 | def call_nonvirtual_short_method_v(self, mu, env): 844 | raise NotImplementedError() 845 | 846 | @native_method 847 | def call_nonvirtual_short_method_a(self, mu, env): 848 | raise NotImplementedError() 849 | 850 | @native_method 851 | def call_nonvirtual_int_method(self, mu, env): 852 | raise NotImplementedError() 853 | 854 | @native_method 855 | def call_nonvirtual_int_method_v(self, mu, env): 856 | raise NotImplementedError() 857 | 858 | @native_method 859 | def call_nonvirtual_int_method_a(self, mu, env): 860 | raise NotImplementedError() 861 | 862 | @native_method 863 | def call_nonvirtual_long_method(self, mu, env): 864 | raise NotImplementedError() 865 | 866 | @native_method 867 | def call_nonvirtual_long_method_v(self, mu, env): 868 | raise NotImplementedError() 869 | 870 | @native_method 871 | def call_nonvirtual_long_method_a(self, mu, env): 872 | raise NotImplementedError() 873 | 874 | @native_method 875 | def call_nonvirtual_float_method(self, mu, env): 876 | raise NotImplementedError() 877 | 878 | @native_method 879 | def call_nonvirtual_float_method_v(self, mu, env): 880 | raise NotImplementedError() 881 | 882 | @native_method 883 | def call_nonvirtual_float_method_a(self, mu, env): 884 | raise NotImplementedError() 885 | 886 | @native_method 887 | def call_nonvirtual_double_method(self, mu, env): 888 | raise NotImplementedError() 889 | 890 | @native_method 891 | def call_nonvirtual_double_method_v(self, mu, env): 892 | raise NotImplementedError() 893 | 894 | @native_method 895 | def call_nonvirtual_double_method_a(self, mu, env): 896 | raise NotImplementedError() 897 | 898 | @native_method 899 | def call_nonvirtual_void_method(self, mu, env): 900 | raise NotImplementedError() 901 | 902 | @native_method 903 | def call_nonvirtual_void_method_v(self, mu, env): 904 | raise NotImplementedError() 905 | 906 | @native_method 907 | def call_nonvirtual_void_method_a(self, mu, env): 908 | raise NotImplementedError() 909 | 910 | @native_method 911 | def get_field_id(self, mu, env, clazz_idx, name_ptr, sig_ptr): 912 | """ 913 | Returns the field ID for an instance (nonstatic) field of a class. The field is specified by its name and 914 | signature. The GetField and SetField families of accessor functions use field IDs to retrieve 915 | object fields. 916 | """ 917 | name = memory_helpers.read_utf8(mu, name_ptr) 918 | sig = memory_helpers.read_utf8(mu, sig_ptr) 919 | clazz = self.get_reference(clazz_idx) 920 | 921 | logger.debug("JNIEnv->GetFieldId(%d, %s, %s) was called" % (clazz_idx, name, sig)) 922 | 923 | field = clazz.value.find_field(name, sig, False) 924 | 925 | if field is None: 926 | # TODO: Proper Java error? 927 | raise RuntimeError("Could not find field ('%s', '%s') in class %s." % (name, sig, clazz.value.jvm_name)) 928 | 929 | if field.ignore: 930 | return 0 931 | 932 | return field.jvm_id 933 | 934 | @native_method 935 | def get_object_field(self, mu, env, obj_idx, field_id): 936 | obj = self.get_reference(obj_idx) 937 | 938 | if not isinstance(obj, jobject): 939 | raise ValueError('Expected a jobject.') 940 | 941 | field = obj.value.__class__.find_field_by_id(field_id) 942 | 943 | if field is None: 944 | # TODO: Proper Java error? 945 | raise RuntimeError("Could not find field %d in object %s by id." % (field_id, obj.value.jvm_name)) 946 | 947 | logger.debug("JNIEnv->GetObjectField(%s, %s <%s>) was called" % (obj.value.jvm_name, 948 | field.name, 949 | field.signature)) 950 | 951 | return getattr(obj.value, field.name) 952 | 953 | @native_method 954 | def get_boolean_field(self, mu, env): 955 | raise NotImplementedError() 956 | 957 | @native_method 958 | def get_byte_field(self, mu, env): 959 | raise NotImplementedError() 960 | 961 | @native_method 962 | def get_char_field(self, mu, env): 963 | raise NotImplementedError() 964 | 965 | @native_method 966 | def get_short_field(self, mu, env): 967 | raise NotImplementedError() 968 | 969 | @native_method 970 | def get_int_field(self, mu, env, obj_idx, field_id): 971 | obj = self.get_reference(obj_idx) 972 | 973 | if not isinstance(obj, jobject): 974 | raise ValueError('Expected a jobject.') 975 | 976 | field = obj.value.__class__.find_field_by_id(field_id) 977 | 978 | if field is None: 979 | # TODO: Proper Java error? 980 | raise RuntimeError("Could not find field %d in object %s by id." % (field_id, obj.value.jvm_name)) 981 | 982 | logger.debug("JNIEnv->GetIntField(%s, %s <%s>) was called" % (obj.value.jvm_name, 983 | field.name, 984 | field.signature)) 985 | 986 | return getattr(obj.value, field.name) 987 | 988 | @native_method 989 | def get_long_field(self, mu, env): 990 | raise NotImplementedError() 991 | 992 | @native_method 993 | def get_float_field(self, mu, env): 994 | raise NotImplementedError() 995 | 996 | @native_method 997 | def get_double_field(self, mu, env): 998 | raise NotImplementedError() 999 | 1000 | @native_method 1001 | def set_object_field(self, mu, env): 1002 | raise NotImplementedError() 1003 | 1004 | @native_method 1005 | def set_boolean_field(self, mu, env): 1006 | raise NotImplementedError() 1007 | 1008 | @native_method 1009 | def set_byte_field(self, mu, env): 1010 | raise NotImplementedError() 1011 | 1012 | @native_method 1013 | def set_char_field(self, mu, env): 1014 | raise NotImplementedError() 1015 | 1016 | @native_method 1017 | def set_short_field(self, mu, env): 1018 | raise NotImplementedError() 1019 | 1020 | @native_method 1021 | def set_int_field(self, mu, env): 1022 | raise NotImplementedError() 1023 | 1024 | @native_method 1025 | def set_long_field(self, mu, env): 1026 | raise NotImplementedError() 1027 | 1028 | @native_method 1029 | def set_float_field(self, mu, env): 1030 | raise NotImplementedError() 1031 | 1032 | @native_method 1033 | def set_double_field(self, mu, env): 1034 | raise NotImplementedError() 1035 | 1036 | @native_method 1037 | def get_static_method_id(self, mu, env, clazz_idx, name_ptr, sig_ptr): 1038 | """ 1039 | Returns the method ID for a static method of a class. The method is specified by its name and signature. 1040 | """ 1041 | name = memory_helpers.read_utf8(mu, name_ptr) 1042 | sig = memory_helpers.read_utf8(mu, sig_ptr) 1043 | clazz = self.get_reference(clazz_idx) 1044 | 1045 | logger.debug("JNIEnv->GetStaticMethodId(%d, %s, %s) was called" % (clazz_idx, name, sig)) 1046 | 1047 | if not isinstance(clazz, jclass): 1048 | raise ValueError('Expected a jclass.') 1049 | 1050 | method = clazz.value.find_method(name, sig) 1051 | 1052 | if method is None: 1053 | # TODO: Proper Java error? 1054 | raise RuntimeError( 1055 | "Could not find static method ('%s', '%s') in class %s." % (name, sig, clazz.value.jvm_name)) 1056 | 1057 | if method.ignore: 1058 | return 0 1059 | 1060 | return method.jvm_id 1061 | 1062 | @native_method 1063 | def call_static_object_method(self, mu, env): 1064 | raise NotImplementedError() 1065 | 1066 | @native_method 1067 | def call_static_object_method_v(self, mu, env, clazz_idx, method_id, args): 1068 | clazz = self.get_reference(clazz_idx) 1069 | 1070 | if not isinstance(clazz, jclass): 1071 | raise ValueError('Expected a jclass.') 1072 | 1073 | method = clazz.value.find_method_by_id(method_id) 1074 | 1075 | if method is None: 1076 | # TODO: Proper Java error? 1077 | raise RuntimeError("Could not find method %d in class %s by id." % (method_id, clazz.value.jvm_name)) 1078 | 1079 | logger.debug("JNIEnv->CallStaticObjectMethodV(%s, %s <%s>, 0x%x) was called" % ( 1080 | clazz.value.jvm_name, 1081 | method.name, 1082 | method.signature, args)) 1083 | 1084 | # Parse arguments. 1085 | constructor_args = self.read_args_v(mu, args, method.args_list) 1086 | 1087 | return method.func(self._emu, *constructor_args) 1088 | 1089 | @native_method 1090 | def call_static_object_method_a(self, mu, env): 1091 | raise NotImplementedError() 1092 | 1093 | @native_method 1094 | def call_static_boolean_method(self, mu, env): 1095 | raise NotImplementedError() 1096 | 1097 | @native_method 1098 | def call_static_boolean_method_v(self, mu, env): 1099 | raise NotImplementedError() 1100 | 1101 | @native_method 1102 | def call_static_boolean_method_a(self, mu, env): 1103 | raise NotImplementedError() 1104 | 1105 | @native_method 1106 | def call_static_byte_method(self, mu, env): 1107 | raise NotImplementedError() 1108 | 1109 | @native_method 1110 | def call_static_byte_method_v(self, mu, env): 1111 | raise NotImplementedError() 1112 | 1113 | @native_method 1114 | def call_static_byte_method_a(self, mu, env): 1115 | raise NotImplementedError() 1116 | 1117 | @native_method 1118 | def call_static_char_method(self, mu, env): 1119 | raise NotImplementedError() 1120 | 1121 | @native_method 1122 | def call_static_char_method_v(self, mu, env): 1123 | raise NotImplementedError() 1124 | 1125 | @native_method 1126 | def call_static_char_method_a(self, mu, env): 1127 | raise NotImplementedError() 1128 | 1129 | @native_method 1130 | def call_static_short_method(self, mu, env): 1131 | raise NotImplementedError() 1132 | 1133 | @native_method 1134 | def call_static_short_method_v(self, mu, env): 1135 | raise NotImplementedError() 1136 | 1137 | @native_method 1138 | def call_static_short_method_a(self, mu, env): 1139 | raise NotImplementedError() 1140 | 1141 | @native_method 1142 | def call_static_int_method(self, mu, env): 1143 | raise NotImplementedError() 1144 | 1145 | @native_method 1146 | def call_static_int_method_v(self, mu, env, clazz_idx, method_id, args): 1147 | clazz = self.get_reference(clazz_idx) 1148 | 1149 | if not isinstance(clazz, jclass): 1150 | raise ValueError('Expected a jclass.') 1151 | 1152 | method = clazz.value.find_method_by_id(method_id) 1153 | 1154 | if method is None: 1155 | # TODO: Proper Java error? 1156 | raise RuntimeError("Could not find method %d in class %s by id." % (method_id, clazz.value.jvm_name)) 1157 | 1158 | logger.debug("JNIEnv->CallStaticIntMethodV(%s, %s <%s>, 0x%x) was called" % ( 1159 | clazz.value.jvm_name, 1160 | method.name, 1161 | method.signature, args)) 1162 | 1163 | # Parse arguments. 1164 | constructor_args = self.read_args_v(mu, args, method.args_list) 1165 | 1166 | return method.func(self._emu, *constructor_args) 1167 | 1168 | @native_method 1169 | def call_static_int_method_a(self, mu, env): 1170 | raise NotImplementedError() 1171 | 1172 | @native_method 1173 | def call_static_long_method(self, mu, env): 1174 | raise NotImplementedError() 1175 | 1176 | @native_method 1177 | def call_static_long_method_v(self, mu, env): 1178 | raise NotImplementedError() 1179 | 1180 | @native_method 1181 | def call_static_long_method_a(self, mu, env): 1182 | raise NotImplementedError() 1183 | 1184 | @native_method 1185 | def call_static_float_method(self, mu, env): 1186 | raise NotImplementedError() 1187 | 1188 | @native_method 1189 | def call_static_float_method_v(self, mu, env): 1190 | raise NotImplementedError() 1191 | 1192 | @native_method 1193 | def call_static_float_method_a(self, mu, env): 1194 | raise NotImplementedError() 1195 | 1196 | @native_method 1197 | def call_static_double_method(self, mu, env): 1198 | raise NotImplementedError() 1199 | 1200 | @native_method 1201 | def call_static_double_method_v(self, mu, env): 1202 | raise NotImplementedError() 1203 | 1204 | @native_method 1205 | def call_static_double_method_a(self, mu, env): 1206 | raise NotImplementedError() 1207 | 1208 | @native_method 1209 | def call_static_void_method(self, mu, env): 1210 | raise NotImplementedError() 1211 | 1212 | @native_method 1213 | def call_static_void_method_v(self, mu, env): 1214 | raise NotImplementedError() 1215 | 1216 | @native_method 1217 | def call_static_void_method_a(self, mu, env): 1218 | raise NotImplementedError() 1219 | 1220 | @native_method 1221 | def get_static_field_id(self, mu, env, clazz_idx, name_ptr, sig_ptr): 1222 | """ 1223 | Returns the field ID for a static field of a class. The field is specified by its name and signature. The 1224 | GetStaticField and SetStaticField families of accessor functions use field IDs to retrieve static 1225 | fields. 1226 | """ 1227 | name = memory_helpers.read_utf8(mu, name_ptr) 1228 | sig = memory_helpers.read_utf8(mu, sig_ptr) 1229 | clazz = self.get_reference(clazz_idx) 1230 | 1231 | logger.debug("JNIEnv->GetStaticFieldId(%d, %s, %s) was called" % (clazz_idx, name, sig)) 1232 | 1233 | field = clazz.value.find_field(name, sig, True) 1234 | 1235 | if field is None: 1236 | # TODO: Proper Java error? 1237 | raise RuntimeError( 1238 | "Could not find static field ('%s', '%s') in class %s." % (name, sig, clazz.value.jvm_name)) 1239 | 1240 | return field.jvm_id 1241 | 1242 | @native_method 1243 | def get_static_object_field(self, mu, env, clazz_idx, field_id): 1244 | logger.debug("JNIEnv->GetStaticObjectField(%d, %d) was called" % (clazz_idx, field_id)) 1245 | 1246 | clazz = self.get_reference(clazz_idx) 1247 | field = clazz.value.find_field_by_id(field_id) 1248 | 1249 | return field.static_value 1250 | 1251 | @native_method 1252 | def get_static_boolean_field(self, mu, env): 1253 | raise NotImplementedError() 1254 | 1255 | @native_method 1256 | def get_static_byte_field(self, mu, env): 1257 | raise NotImplementedError() 1258 | 1259 | @native_method 1260 | def get_static_char_field(self, mu, env): 1261 | raise NotImplementedError() 1262 | 1263 | @native_method 1264 | def get_static_short_field(self, mu, env): 1265 | raise NotImplementedError() 1266 | 1267 | @native_method 1268 | def get_static_int_field(self, mu, env, clazz_idx, field_id): 1269 | logger.debug("JNIEnv->GetStaticIntField(%d, %d) was called" % (clazz_idx, field_id)) 1270 | 1271 | clazz = self.get_reference(clazz_idx) 1272 | field = clazz.value.find_field_by_id(field_id) 1273 | 1274 | return field.static_value 1275 | 1276 | @native_method 1277 | def get_static_long_field(self, mu, env): 1278 | raise NotImplementedError() 1279 | 1280 | @native_method 1281 | def get_static_float_field(self, mu, env): 1282 | raise NotImplementedError() 1283 | 1284 | @native_method 1285 | def get_static_double_field(self, mu, env): 1286 | raise NotImplementedError() 1287 | 1288 | @native_method 1289 | def set_static_object_field(self, mu, env): 1290 | raise NotImplementedError() 1291 | 1292 | @native_method 1293 | def set_static_boolean_field(self, mu, env): 1294 | raise NotImplementedError() 1295 | 1296 | @native_method 1297 | def set_static_byte_field(self, mu, env): 1298 | raise NotImplementedError() 1299 | 1300 | @native_method 1301 | def set_static_char_field(self, mu, env): 1302 | raise NotImplementedError() 1303 | 1304 | @native_method 1305 | def set_static_short_field(self, mu, env): 1306 | raise NotImplementedError() 1307 | 1308 | @native_method 1309 | def set_static_int_field(self, mu, env): 1310 | raise NotImplementedError() 1311 | 1312 | @native_method 1313 | def set_static_long_field(self, mu, env): 1314 | raise NotImplementedError() 1315 | 1316 | @native_method 1317 | def set_static_float_field(self, mu, env): 1318 | raise NotImplementedError() 1319 | 1320 | @native_method 1321 | def set_static_double_field(self, mu, env): 1322 | raise NotImplementedError() 1323 | 1324 | @native_method 1325 | def new_string(self, mu, env): 1326 | raise NotImplementedError() 1327 | 1328 | @native_method 1329 | def get_string_length(self, mu, env): 1330 | raise NotImplementedError() 1331 | 1332 | @native_method 1333 | def get_string_chars(self, mu, env): 1334 | raise NotImplementedError() 1335 | 1336 | @native_method 1337 | def release_string_chars(self, mu, env): 1338 | raise NotImplementedError() 1339 | 1340 | @native_method 1341 | def new_string_utf(self, mu, env, bytes_ptr): 1342 | string = memory_helpers.read_utf8(mu, bytes_ptr) 1343 | logger.debug("JNIEnv->NewStringUtf(%s) was called" % string) 1344 | 1345 | return self.add_local_reference(jstring(string)) 1346 | 1347 | @native_method 1348 | def get_string_utf_length(self, mu, env): 1349 | raise NotImplementedError() 1350 | 1351 | @native_method 1352 | def get_string_utf_chars(self, mu, env, string, is_copy_ptr): 1353 | logger.debug("JNIEnv->GetStringUtfChars(%u, %x) was called" % (string, is_copy_ptr)) 1354 | 1355 | if is_copy_ptr != 0: 1356 | raise NotImplementedError() 1357 | 1358 | str_ref = self.get_reference(string) 1359 | str_val = str_ref.value 1360 | str_ptr = self._emu.native_memory.allocate(len(str_val) + 1) 1361 | 1362 | logger.debug("=> %s" % str_val) 1363 | 1364 | memory_helpers.write_utf8(mu, str_ptr, str_val) 1365 | 1366 | return str_ptr 1367 | 1368 | @native_method 1369 | def release_string_utf_chars(self, mu, env, string, utf_ptr): 1370 | pass 1371 | 1372 | @native_method 1373 | def get_array_length(self, mu, env, array): 1374 | logger.debug("JNIEnv->GetArrayLength(%u) was called" % array) 1375 | 1376 | obj = self.get_reference(array) 1377 | 1378 | if not isinstance(obj, jarray): 1379 | raise ValueError('Expected a jarray.') 1380 | 1381 | return len(obj.value) 1382 | 1383 | @native_method 1384 | def new_object_array(self, mu, env): 1385 | raise NotImplementedError() 1386 | 1387 | @native_method 1388 | def get_object_array_element(self, mu, env, array_idx, item_idx): 1389 | logger.debug("JNIEnv->GetObjectArrayElement(%u, %u) was called" % (array_idx, item_idx)) 1390 | 1391 | obj = self.get_reference(array_idx) 1392 | 1393 | if not isinstance(obj, jarray): 1394 | raise ValueError('Expected a jarray.') 1395 | 1396 | return obj.value[item_idx] 1397 | 1398 | @native_method 1399 | def set_object_array_element(self, mu, env): 1400 | raise NotImplementedError() 1401 | 1402 | @native_method 1403 | def new_boolean_array(self, mu, env): 1404 | raise NotImplementedError() 1405 | 1406 | @native_method 1407 | def new_byte_array(self, mu, env, bytelen): 1408 | logger.debug("JNIEnv->NewByteArray(%u) was called" % bytelen) 1409 | return self.add_local_reference(jbyteArray(bytelen)) 1410 | #raise NotImplementedError() 1411 | 1412 | @native_method 1413 | def new_char_array(self, mu, env): 1414 | raise NotImplementedError() 1415 | 1416 | @native_method 1417 | def new_short_array(self, mu, env): 1418 | raise NotImplementedError() 1419 | 1420 | @native_method 1421 | def new_int_array(self, mu, env): 1422 | raise NotImplementedError() 1423 | 1424 | @native_method 1425 | def new_long_array(self, mu, env): 1426 | raise NotImplementedError() 1427 | 1428 | @native_method 1429 | def new_float_array(self, mu, env): 1430 | raise NotImplementedError() 1431 | 1432 | @native_method 1433 | def new_double_array(self, mu, env): 1434 | raise NotImplementedError() 1435 | 1436 | @native_method 1437 | def get_boolean_array_elements(self, mu, env): 1438 | raise NotImplementedError() 1439 | 1440 | @native_method 1441 | def get_byte_array_elements(self, mu, env): 1442 | raise NotImplementedError() 1443 | 1444 | @native_method 1445 | def get_char_array_elements(self, mu, env): 1446 | raise NotImplementedError() 1447 | 1448 | @native_method 1449 | def get_short_array_elements(self, mu, env): 1450 | raise NotImplementedError() 1451 | 1452 | @native_method 1453 | def get_int_array_elements(self, mu, env): 1454 | raise NotImplementedError() 1455 | 1456 | @native_method 1457 | def get_long_array_elements(self, mu, env): 1458 | raise NotImplementedError() 1459 | 1460 | @native_method 1461 | def get_float_array_elements(self, mu, env): 1462 | raise NotImplementedError() 1463 | 1464 | @native_method 1465 | def get_double_array_elements(self, mu, env): 1466 | raise NotImplementedError() 1467 | 1468 | @native_method 1469 | def release_boolean_array_elements(self, mu, env): 1470 | raise NotImplementedError() 1471 | 1472 | @native_method 1473 | def release_byte_array_elements(self, mu, env): 1474 | raise NotImplementedError() 1475 | 1476 | @native_method 1477 | def release_char_array_elements(self, mu, env): 1478 | raise NotImplementedError() 1479 | 1480 | @native_method 1481 | def release_short_array_elements(self, mu, env): 1482 | raise NotImplementedError() 1483 | 1484 | @native_method 1485 | def release_int_array_elements(self, mu, env): 1486 | raise NotImplementedError() 1487 | 1488 | @native_method 1489 | def release_long_array_elements(self, mu, env): 1490 | raise NotImplementedError() 1491 | 1492 | @native_method 1493 | def release_float_array_elements(self, mu, env): 1494 | raise NotImplementedError() 1495 | 1496 | @native_method 1497 | def release_double_array_elements(self, mu, env): 1498 | raise NotImplementedError() 1499 | 1500 | @native_method 1501 | def get_boolean_array_region(self, mu, env): 1502 | raise NotImplementedError() 1503 | 1504 | @native_method 1505 | def get_byte_array_region(self, mu, env, array_idx, start, len_in, buf_ptr): 1506 | logger.debug("JNIEnv->GetByteArrayRegion(%u, %u, %u, 0x%x) was called" % (array_idx, start, len_in, buf_ptr)) 1507 | 1508 | obj = self.get_reference(array_idx) 1509 | 1510 | if not isinstance(obj, jbyteArray): 1511 | raise ValueError('Expected a jbyteArray.') 1512 | 1513 | mu.mem_write(buf_ptr, bytes(obj.value[start:start + len_in])) 1514 | 1515 | return None 1516 | 1517 | @native_method 1518 | def get_char_array_region(self, mu, env): 1519 | raise NotImplementedError() 1520 | 1521 | @native_method 1522 | def get_short_array_region(self, mu, env): 1523 | raise NotImplementedError() 1524 | 1525 | @native_method 1526 | def get_int_array_region(self, mu, env): 1527 | raise NotImplementedError() 1528 | 1529 | @native_method 1530 | def get_long_array_region(self, mu, env): 1531 | raise NotImplementedError() 1532 | 1533 | @native_method 1534 | def get_float_array_region(self, mu, env): 1535 | raise NotImplementedError() 1536 | 1537 | @native_method 1538 | def get_double_array_region(self, mu, env): 1539 | raise NotImplementedError() 1540 | 1541 | @native_method 1542 | def set_boolean_array_region(self, mu, env): 1543 | raise NotImplementedError() 1544 | 1545 | @native_method 1546 | def set_byte_array_region(self, mu, env, arrayJREF, startIndex, length, bufAddress): 1547 | string = memory_helpers.read_byte_array(mu, bufAddress, length) 1548 | logger.debug("JNIEnv->SetByteArrayRegion was called") 1549 | self.set_local_reference(arrayJREF,jbyteArray(string)) 1550 | 1551 | 1552 | @native_method 1553 | def set_char_array_region(self, mu, env): 1554 | raise NotImplementedError() 1555 | 1556 | @native_method 1557 | def set_short_array_region(self, mu, env): 1558 | raise NotImplementedError() 1559 | 1560 | @native_method 1561 | def set_int_array_region(self, mu, env): 1562 | raise NotImplementedError() 1563 | 1564 | @native_method 1565 | def set_long_array_region(self, mu, env): 1566 | raise NotImplementedError() 1567 | 1568 | @native_method 1569 | def set_float_array_region(self, mu, env): 1570 | raise NotImplementedError() 1571 | 1572 | @native_method 1573 | def set_double_array_region(self, mu, env): 1574 | raise NotImplementedError() 1575 | 1576 | @native_method 1577 | def register_natives(self, mu, env, clazz_id, methods, methods_count): 1578 | logger.debug("JNIEnv->RegisterNatives(%d, 0x%08x, %d) was called" % (clazz_id, methods, methods_count)) 1579 | 1580 | clazz = self.get_local_reference(clazz_id) 1581 | 1582 | if not isinstance(clazz, jclass): 1583 | raise ValueError('Expected a jclass.') 1584 | 1585 | clazz = clazz.value 1586 | 1587 | for i in range(0, methods_count): 1588 | ptr_name = memory_helpers.read_ptr(mu, (i * 12) + methods) 1589 | ptr_sign = memory_helpers.read_ptr(mu, (i * 12) + methods + 4) 1590 | ptr_func = memory_helpers.read_ptr(mu, (i * 12) + methods + 8) 1591 | 1592 | name = memory_helpers.read_utf8(mu, ptr_name) 1593 | signature = memory_helpers.read_utf8(mu, ptr_sign) 1594 | 1595 | clazz.register_native(name, signature, ptr_func) 1596 | 1597 | return JNI_OK 1598 | 1599 | @native_method 1600 | def unregister_natives(self, mu, env): 1601 | raise NotImplementedError() 1602 | 1603 | @native_method 1604 | def monitor_enter(self, mu, env): 1605 | raise NotImplementedError() 1606 | 1607 | @native_method 1608 | def monitor_exit(self, mu, env): 1609 | raise NotImplementedError() 1610 | 1611 | @native_method 1612 | def get_java_vm(self, mu, env): 1613 | raise NotImplementedError() 1614 | 1615 | @native_method 1616 | def get_string_region(self, mu, env): 1617 | raise NotImplementedError() 1618 | 1619 | @native_method 1620 | def get_string_utf_region(self, mu, env): 1621 | raise NotImplementedError() 1622 | 1623 | @native_method 1624 | def get_primitive_array_critical(self, mu, env): 1625 | raise NotImplementedError() 1626 | 1627 | @native_method 1628 | def release_primitive_array_critical(self, mu, env): 1629 | raise NotImplementedError() 1630 | 1631 | @native_method 1632 | def get_string_critical(self, mu, env): 1633 | raise NotImplementedError() 1634 | 1635 | @native_method 1636 | def release_string_critical(self, mu, env): 1637 | raise NotImplementedError() 1638 | 1639 | @native_method 1640 | def new_weak_global_ref(self, mu, env): 1641 | raise NotImplementedError() 1642 | 1643 | @native_method 1644 | def delete_weak_global_ref(self, mu, env): 1645 | raise NotImplementedError() 1646 | 1647 | @native_method 1648 | def exception_check(self, mu, env): 1649 | """ 1650 | Returns JNI_TRUE when there is a pending exception; otherwise, returns JNI_FALSE. 1651 | """ 1652 | logger.debug("JNIEnv->ExceptionCheck() was called") 1653 | # TODO: Implement 1654 | return JNI_FALSE 1655 | 1656 | @native_method 1657 | def new_direct_byte_buffer(self, mu, env): 1658 | raise NotImplementedError() 1659 | 1660 | @native_method 1661 | def get_direct_buffer_address(self, mu, env): 1662 | raise NotImplementedError() 1663 | 1664 | @native_method 1665 | def get_direct_buffer_capacity(self, mu, env): 1666 | raise NotImplementedError() 1667 | 1668 | @native_method 1669 | def get_object_ref_type(self, mu, env): 1670 | raise NotImplementedError() 1671 | --------------------------------------------------------------------------------