其他
Unicorn 调用SO之加载模块
Unicorn 调用SO之加载模块
load_segments = [x for x in elf.iter_segments() if x.header.p_type == 'PT_LOAD']
for segment in load_segments:
prot = UC_PROT_ALL
self.emu.memory.mem_map(load_base + segment.header.p_vaddr, segment.header.p_memsz, prot)
self.emu.memory.mem_write(load_base + segment.header.p_vaddr, segment.data())
# covert va to file offset
for seg in load_segments:
if seg.header.p_vaddr <= init_array_offset < seg.header.p_vaddr + seg.header.p_memsz:
init_array_foffset = init_array_offset - seg.header.p_vaddr + seg.header.p_offset
fstream.seek(init_array_foffset)
data = fstream.read(4)
fun_ptr = struct.unpack('I', data)[0]
if fun_ptr != 0:
# fun_ptr += load_base
init_array.append(fun_ptr + load_base)
print ("find init array for :%s %x" % (filename, fun_ptr))
else:
# search in reloc
for rel in rel_section.iter_relocations():
rel_info_type = rel['r_info_type']
rel_addr = rel['r_offset']
if rel_info_type == arm.R_ARM_ABS32 and rel_addr == init_array_offset:
sym = dynsym.get_symbol(rel['r_info_sym'])
sym_value = sym['st_value']
init_array.append(load_base + sym_value)
print ("find init array for :%s %x" % (filename, sym_value))
break
init_array_offset += 4
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;
symbols_resolved = dict()
for section in elf.iter_sections():
if not isinstance(section, SymbolTableSection):
continue
itersymbols = section.iter_symbols()
next(itersymbols) # Skip first symbol which is always NULL.
for symbol in itersymbols:
symbol_address = self._elf_get_symval(elf, load_base, symbol)
if symbol_address is not None:
symbols_resolved[symbol.name] = SymbolResolved(symbol_address, symbol)
def _elf_get_symval(self, elf, elf_base, symbol):
if symbol.name in self.symbol_hooks:
return self.symbol_hooks[symbol.name]
if symbol['st_shndx'] == 'SHN_UNDEF': # 外部符号
# External symbol, lookup value.
target = self._elf_lookup_symbol(symbol.name)
if target is None:
# Extern symbol not found
if symbol['st_info']['bind'] == 'STB_WEAK':
# Weak symbol initialized as 0
return 0
else:
logger.error('=> Undefined external symbol: %s' % symbol.name)
return None
else:
return target
elif symbol['st_shndx'] == 'SHN_ABS':
# Absolute symbol.
return elf_base + symbol['st_value']
else:
# Internally defined symbol.
return elf_base + symbol['st_value']
for section in elf.iter_sections():
if not isinstance(section, RelocationSection):
continue
#for relsection in elf.get_dynmic_rel():
for rel in section.iter_relocations():
sym = dynsym.get_symbol(rel['r_info_sym'])
sym_value = sym['st_value']
rel_addr = load_base + rel['r_offset'] # Location where relocation should happen
rel_info_type = rel['r_info_type']
# Relocation table for ARM
if rel_info_type == arm.R_ARM_ABS32:
# Create the new value.
value = load_base + sym_value
# Write the new value
self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
elif rel_info_type == arm.R_ARM_GLOB_DAT or \
rel_info_type == arm.R_ARM_JUMP_SLOT or \
rel_info_type == arm.R_AARCH64_GLOB_DAT or \
rel_info_type == arm.R_AARCH64_JUMP_SLOT:
# Resolve the symbol.
if sym.name in symbols_resolved:
value = symbols_resolved[sym.name].address
# Write the new value
self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
elif rel_info_type == arm.R_ARM_RELATIVE or \
rel_info_type == arm.R_AARCH64_RELATIVE:
if sym_value == 0:
# Load address at which it was linked originally.
value_orig_bytes = self.emu.mu.mem_read(rel_addr, 4)
value_orig = int.from_bytes(value_orig_bytes, byteorder='little')
# Create the new value
value = load_base + value_orig
# Write the new value
self.emu.mu.mem_write(rel_addr, value.to_bytes(4, byteorder='little'))
else:
raise NotImplementedError()
else:
logger.error("Unhandled relocation type %i." % rel_info_type)
Unicorn 调用SO之内存管理
uc_mem_map
uc_mem_read
uc_mem_write
内存对齐
Unicorn map的内存基地址和长度都需要对齐到0x1000,对齐函数如下:
def align(addr, size, growl):
to = ctypes.c_uint64(UC_MEM_ALIGN).value
mask = ctypes.c_uint64(0xFFFFFFFFFFFFFFFF).value ^ ctypes.c_uint64(to - 1).value
right = addr + size
right = (right + to - 1) & mask
addr &= mask
size = right - addr
if growl:
size = (size + to - 1) & mask
return addr, size
在 Unicorn 虚拟机中分配内存(用于加载模块)。
"""
:type emu androidemu.emulator.Emulator
"""
def __init__(self, emu):
self.emu = emu
self.counter_memory = config.BASE_ADDR
self.counter_stack = config.STACK_ADDR + config.STACK_SIZE
def mem_reserve(self, size):
(_, size_aligned) = align(0, size, True)
ret = self.counter_memory
self.counter_memory += size_aligned
return ret
def mem_map(self, address, size, prot):
(address, size) = align(address, size, True)
self.emu.mu.mem_map(address, size, prot)
logger.debug("=> Mapping memory page 0x%08x - 0x%08x, size 0x%08x, prot %s" % (address, address + size, size,
prot))
def mem_write(self, address, data):
self.emu.mu.mem_write(address, data)
def mem_read(self, address, size):
return self.emu.mu.mem_read(address, size)
heap 实现代码
from unicorn.arm64_const import *
# Page size required by Unicorn
UNICORN_PAGE_SIZE = 0x1000
# Max allowable segment size (1G)
MAX_ALLOWABLE_SEG_SIZE = 1024 * 1024 * 1024
# Alignment functions to align all memory segments to Unicorn page boundaries (4KB pages only)
ALIGN_PAGE_DOWN = lambda x: x & ~(UNICORN_PAGE_SIZE - 1)
ALIGN_PAGE_UP = lambda x: (x + UNICORN_PAGE_SIZE - 1) & ~(UNICORN_PAGE_SIZE-1)
# Implementation from
# https://github.com/Battelle/afl-unicorn/blob/44a50c8a9426ffe4ad8714ef8a35dc011e62f739/unicorn_mode/helper_scripts/unicorn_loader.py#L45
class UnicornSimpleHeap:
""" Use this class to provide a simple heap implementation. This should
be used if malloc/free calls break things during emulation.
"""
# Helper data-container used to track chunks
class HeapChunk(object):
def __init__(self, data_addr, data_size):
self.data_addr = data_addr
self.data_size = data_size
# Returns true if the specified buffer is completely within the chunk, else false
def is_buffer_in_chunk(self, addr, size):
if addr >= self.data_addr and ((addr + size) <= (self.data_addr + self.data_size)):
return True
else:
return False
_uc = None # Unicorn engine instance to interact with
_chunks = [] # List of all known chunks
_debug_print = False # True to print debug information
def __init__(self, uc, heap_min_addr, heap_max_addr, debug_print=False):
self._uc = uc
self._heap_min_addr = heap_min_addr
self._heap_max_addr = heap_max_addr
self._debug_print = debug_print
# Add the watchpoint hook that will be used to implement psuedo-guard page support
# self._uc.hook_add(UC_HOOK_MEM_WRITE | UC_HOOK_MEM_READ, self.__check_mem_access)
def malloc(self, size, prot=UC_PROT_READ | UC_PROT_WRITE):
# Figure out the overall size to be allocated/mapped
# - Allocate at least 1 4k page of memory to make Unicorn happy
data_size = ALIGN_PAGE_UP(size)
# Gross but efficient way to find space for the chunk:
chunk = None
for addr in range(self._heap_min_addr, self._heap_max_addr, UNICORN_PAGE_SIZE):
try:
self._uc.mem_map(addr, data_size, prot)
chunk = self.HeapChunk(addr, data_size)
if self._debug_print:
print("Allocating 0x{0:x}-byte chunk @ 0x{1:016x}".format(chunk.data_size, chunk.data_addr))
break
except UcError as e:
continue
# Something went very wrong
if chunk is None:
raise Exception("Oh no.")
self._chunks.append(chunk)
return chunk.data_addr
def calloc(self, size, count):
# Simple wrapper around malloc with calloc() args
return self.malloc(size * count)
def realloc(self, ptr, new_size):
# Wrapper around malloc(new_size) / memcpy(new, old, old_size) / free(old)
if self._debug_print:
print("Reallocating chunk @ 0x{0:016x} to be 0x{1:x} bytes".format(ptr, new_size))
old_chunk = None
for chunk in self._chunks:
if chunk.data_addr == ptr:
old_chunk = chunk
new_chunk_addr = self.malloc(new_size)
if old_chunk is not None:
self._uc.mem_write(new_chunk_addr, str(self._uc.mem_read(old_chunk.data_addr, old_chunk.data_size)))
self.free(old_chunk.data_addr)
return new_chunk_addr
def protect(self, addr, len_in, prot):
for chunk in self._chunks:
if chunk.is_buffer_in_chunk(addr, len_in):
# self._uc.mem_protect(chunk.data_addr, chunk.data_size, perms=prot)
return True
return False
def free(self, addr):
for chunk in self._chunks:
if chunk.is_buffer_in_chunk(addr, 1):
if self._debug_print:
print("Freeing 0x{0:x}-byte chunk @ 0x{0:016x}".format(chunk.data_addr, chunk.data_size))
self._uc.mem_unmap(chunk.data_addr, chunk.data_size)
self._chunks.remove(chunk)
return True
return False
处理内存管理方面的syscall
self._syscall_handler.set_handler(0x7D, "mprotect", 3, self._handle_mprotect)
self._syscall_handler.set_handler(0xC0, "mmap2", 6, self._handle_mmap2)
self._syscall_handler.set_handler(0xDC, "madvise", 3, self._handle_madvise)
"""
void *mmap2(void *addr, size_t length, int prot, int flags, int fd, off_t pgoffset);
"""
# MAP_FILE 0
# MAP_SHARED 0x01
# MAP_PRIVATE 0x02
# MAP_FIXED 0x10
# MAP_ANONYMOUS 0x20
prot = UC_PROT_ALL
addr = self._heap.malloc(length, prot)
if fd != 0xffffffff: # 如果有fd
if fd <= 2:
raise NotImplementedError("Unsupported read operation for file descriptor %d." % fd)
if fd not in self._file_system._file_descriptors:
# TODO: Return valid error.
raise NotImplementedError()
file = self._file_system._file_descriptors[fd]
data = open(file.name_virt, 'rb').read(length)
self._mu.mem_write(addr, data)
return addr
modules.add_symbol_hook('malloc', hooker.write_function(self.malloc) + 1)
modules.add_symbol_hook('free', hooker.write_function(self.free) + 1)
modules.add_symbol_hook('calloc', hooker.write_function(self.calloc) + 1)
Unicorn 调用SO之函数级Hook
用 Python 实现 Unicorn 虚拟机内部的函数
MOV R4, Number
IT AL
POP {R4,PC}
# Check if instruction is "IT AL"
if size != 2 or self._emu.mu.mem_read(address, size) != b"\xE8\xBF":
return
# Find hook.
hook_id = self._emu.mu.reg_read(UC_ARM_REG_R4)
hook_func = self._hooks[hook_id]
# Call hook.
try:
hook_func(self._emu)
except:
# Make sure we catch exceptions inside hooks and stop emulation.
mu.emu_stop()
raise
使用 keystone 动态编译 stub
# Get the hook id.
hook_id = self._get_next_id()
hook_addr = self._hook_current
# Create the ARM assembly code.
# Make sure to update STACK_OFFSET if you change the PUSH/POP.
asm = "PUSH {R4,LR}\n" \
"MOV R4, #" + hex(hook_id) + "\n" \
"IT AL\n" \
"POP {R4,PC}"
asm_bytes_list, asm_count = self._keystone.asm(bytes(asm, encoding='ascii'))
if asm_count != 4:
raise ValueError("Expected asm_count to be 4 instead of %u." % asm_count)
# Write assembly code to the emulator.
self._emu.mu.mem_write(hook_addr, bytes(asm_bytes_list))
# Save results.
self._hook_current += len(asm_bytes_list)
self._hooks[hook_id] = func
return hook_addr
symbol hook
例子:使用Hook 实现 AAssetManager_open
@native_method
def AAssetManager_open(self, mu, mgr, filename_ptr, mode):
filename = memory_helpers.read_utf8(mu, filename_ptr)
filename = "assert/" + filename
logger.info("AAssetManager_open(%s,%d)" % (filename, mode))
fd = self._open_file(filename, 0, mode)
if fd == -1:
fd = 0
return fd
python 实现 Native 函数修饰 @native_method
def native_method_wrapper(*argv):
"""
:type self
:type emu androidemu.emulator.Emulator
:type mu Uc
"""
emu = argv[1] if len(argv) == 2 else argv[0]
mu = emu.mu
args = inspect.getfullargspec(func).args # 判断参数个数
args_count = len(args) - (2 if 'self' in args else 1)
if args_count < 0:
raise RuntimeError("NativeMethod accept at least (self, mu) or (mu).")
native_args = []
if args_count >= 1: # 从寄存器取参数
native_args.append(mu.reg_read(UC_ARM_REG_R0))
if args_count >= 2:
native_args.append(mu.reg_read(UC_ARM_REG_R1))
if args_count >= 3:
native_args.append(mu.reg_read(UC_ARM_REG_R2))
if args_count >= 4:
native_args.append(mu.reg_read(UC_ARM_REG_R3))
sp = mu.reg_read(UC_ARM_REG_SP) # 参数大于4个,从栈中取参数
sp = sp + STACK_OFFSET # Need to offset by 4 because our hook pushes one register on the stack.
if args_count >= 5:
for x in range(0, args_count - 4):
native_args.append(int.from_bytes(mu.mem_read(sp + (x * 4), 4), byteorder='little'))
if len(argv) == 1:
result = func(mu, *native_args)
else:
result = func(argv[0], mu, *native_args)
if result is not None:
native_write_arg_register(emu, UC_ARM_REG_R0, result)
else:
mu.reg_write(UC_ARM_REG_R0, JNI_ERR)
return native_method_wrapper
小结
Unicorn 调用SO之系统调用
intno: 中断号
user_data: user data
实现getpid
def _getpid(self, mu):
return 0x1122
实现文件系统需要拦截的syscall
syscall_handler.set_handler(0x4, "write", 3, self._handle_write)
syscall_handler.set_handler(0x5, "open", 3, self._handle_open)
syscall_handler.set_handler(0x6, "close", 1, self._handle_close)
syscall_handler.set_handler(0x92, "writev", 3, self._handle_writev)
syscall_handler.set_handler(0xC5, "fstat64", 2, self._handle_fstat64)
syscall_handler.set_handler(0x142, "openat", 4, self._handle_openat)
syscall_handler.set_handler(0x147, "fstatat64", 4, self._handle_fstatat64)
syscall_handler.set_handler(0x14e, "faccessat", 4, self._faccessat)
syscall_handler.set_handler(0x14d, "fchmodat", 4, self._fchmodat)
syscall_handler.set_handler(0x8c, "_llseek", 5, self._llseek)
syscall_handler.set_handler(0x13, "lseek", 3, self._lseek)
实现内存管理的syscall
self._syscall_handler.set_handler(0x7D, "mprotect", 3, self._handle_mprotect)
self._syscall_handler.set_handler(0xC0, "mmap2", 6, self._handle_mmap2)
self._syscall_handler.set_handler(0xDC, "madvise", 3, self._handle_madvise)
其它杂项syscall
self._syscall_handler.set_handler(0xAC, "prctl", 5, self._handle_prctl)
self._syscall_handler.set_handler(0xF0, "futex", 6, self._handle_futex)
self._syscall_handler.set_handler(0x107, "clock_gettime", 2, self._handle_clock_gettime)
self._syscall_handler.set_handler(0x119, "socket", 3, self._socket)
self._syscall_handler.set_handler(0x11b, "connect", 3, self._connect)
self._syscall_handler.set_handler(0x159, "getcpu", 3, self._getcpu)
self._syscall_handler.set_handler(0x14e, "faccessat", 4, self._faccessat)
self._syscall_handler.set_handler(0x14, "getpid", 0, self._getpid)
self._syscall_handler.set_handler(0xe0, "gettid", 0, self._gettid)
self._syscall_handler.set_handler(0x180, "null1", 0, self._null)
self._syscall_handler.set_handler(0x7e, "sigprocmask", 0, self._null)
self._syscall_handler.set_handler(0xaf, "rt_sigprocmask", 0, self._null)
self._syscall_handler.set_handler(0x10c, "sigaction", 0, self._tgkill)
self._syscall_handler.set_handler(0x43, "sigaction", 0, self._sigaction)
self._syscall_handler.set_handler(0xf8, "exit", 0, self._null)
self._syscall_handler.set_handler(0x16e, "accept4", 0, self.accept4)
Unicorn 调用SO之文件系统
思路
实现
syscall_handler.set_handler(0x4, "write", 3, self._handle_write)
syscall_handler.set_handler(0x5, "open", 3, self._handle_open)
syscall_handler.set_handler(0x6, "close", 1, self._handle_close)
syscall_handler.set_handler(0x92, "writev", 3, self._handle_writev)
syscall_handler.set_handler(0xC5, "fstat64", 2, self._handle_fstat64)
syscall_handler.set_handler(0x142, "openat", 4, self._handle_openat)
syscall_handler.set_handler(0x147, "fstatat64", 4, self._handle_fstatat64)
syscall_handler.set_handler(0x14e, "faccessat", 4, self._faccessat)
syscall_handler.set_handler(0x14d, "fchmodat", 4, self._fchmodat)
syscall_handler.set_handler(0x8c, "_llseek", 5, self._llseek)
syscall_handler.set_handler(0x13, "lseek", 3, self._lseek)
_handle_open 处理 open syscall
"""
int open(const char *pathname, int flags, mode_t mode);
return the new file descriptor, or -1 if an error occurred (in which case, errno is set appropriately).
"""
filename = memory_helpers.read_utf8(mu, filename_ptr)
return self._open_file(filename, flags, mode)
# Special cases, such as /dev/urandom.
orig_filename = filename
if filename == '/dev/urandom':
logger.info("File opened '%s'" % filename)
return self._store_fd('/dev/urandom', None, 'urandom')
file_path = self.translate_path(filename)
if os.path.isfile(file_path):
logger.info("File opened '%s'" % orig_filename)
flags = os.O_RDWR
if hasattr(os, "O_BINARY"):
flags = os.O_BINARY
return self._store_fd(orig_filename, file_path, os.open(file_path, flags=flags))
else:
logger.warning("File does not exist '%s'" % orig_filename)
return -1
读文件分析
"""
ssize_t read(int fd, void *buf, size_t count);
On files that support seeking, the read operation commences at the current file offset, and the file offset
is incremented by the number of bytes read. If the current file offset is at or past the end of file,
no bytes are read, and read() returns zero.
If count is zero, read() may detect the errors described below. In the absence of any errors, or if read()
does not check for errors, a read() with a count of 0 returns zero and has no other effects.
If count is greater than SSIZE_MAX, the result is unspecified.
"""
if fd <= 2:
raise NotImplementedError("Unsupported read operation for file descriptor %d." % fd)
if fd not in self._file_descriptors:
# TODO: Return valid error.
raise NotImplementedError()
file = self._file_descriptors[fd]
if file.descriptor == 'urandom':
if OVERRIDE_URANDOM:
buf = OVERRIDE_URANDOM_BYTE * count
else:
buf = os.urandom(count)
else:
buf = os.read(file.descriptor, count)
result = len(buf)
mu.mem_write(buf_addr, buf)
return result
Unicorn 调用SO之实现 JNI Functions
模拟实现 Jni Function Table
4: self.get_version,
5: self.define_class,
6: self.find_class,
7: self.from_reflected_method,
8: self.from_reflected_field,
9: self.to_reflected_method,
10: self.get_superclass,
11: self.is_assignable_from,
12: self.to_reflected_field,
13: self.throw,
14: self.throw_new,
15: self.exception_occurred,
16: self.exception_describe,
17: self.exception_clear,
18: self.fatal_error,
19: self.push_local_frame,
20: self.pop_local_frame,
21: self.new_global_ref,
22: self.delete_global_ref,
.......................
232: self.get_object_ref_type
})
if not isinstance(table, dict):
raise ValueError("Expected a dictionary for the function table.")
index_max = int(max(table, key=int)) + 1
# First, we write every function and store its result address.
hook_map = dict()
for index, func in table.items():
hook_map[index] = self.write_function(func)
# Then we write the function table.
table_bytes = b""
table_address = self._hook_current
for index in range(0, index_max):
address = hook_map[index] if index in hook_map else 0
table_bytes += int(address + 1).to_bytes(4, byteorder='little') # + 1 because THUMB.
self._emu.mu.mem_write(table_address, table_bytes)
self._hook_current += len(table_bytes)
# Then we write the a pointer to the table.
ptr_address = self._hook_current
self._emu.mu.mem_write(ptr_address, table_address.to_bytes(4, byteorder='little'))
self._hook_current += 4
return ptr_address, table_address
def call_boolean_method_a(self, mu, env):
raise NotImplementedError()
Classloader
jvm_fields=[JavaFieldDef("CONNECTIVITY_SERVICE", "Ljava/lang/String;", True, "")]):
def __init__(self):
pass
def getPackageName(self, *args, **kwargs):
return "com.test"
emulator.java_classloader.add_class(android_content_context) # load class
如果不是static字段,则还需要在init 中创建这个这个私有的成员变量。
元类可真的是魔法!它可以动态修改类的定义,比如给类增加成员变量,增加方法等。
for func in inspect.getmembers(cls, predicate=inspect.isfunction):
if hasattr(func[1], 'jvm_method'):
method = func[1].jvm_method
method.jvm_id = next(JavaClassDef.next_jvm_method_id)
cls.jvm_methods[method.jvm_id] = method
if jvm_fields is not None:
for jvm_field in jvm_fields:
jvm_field.jvm_id = next(JavaClassDef.next_jvm_field_id)
cls.jvm_fields[jvm_field.jvm_id] = jvm_field
try:
if cls.jvm_super is not None:
find = cls.jvm_super.find_field_by_id(jvm_id)
if find is not None:
return find
except KeyError:
pass
return cls.jvm_fields[jvm_id]
if not isinstance(clazz, JavaClassDef):
raise ValueError('Expected a JavaClassDef.')
if clazz.jvm_name in self.class_by_name:
raise KeyError('The class \'%s\' is already registered.' % clazz.jvm_name)
self.class_by_id[clazz.jvm_id] = clazz
self.class_by_name[clazz.jvm_name] = clazz
引用管理
def get_string_utf_chars(self, mu, env, string, is_copy_ptr):
logger.debug("JNIEnv->GetStringUtfChars(%u, %x) was called" % (string, is_copy_ptr))
if is_copy_ptr != 0:
raise NotImplementedError()
str_ref = self.get_reference(string) #通过引用ID获取引用对象
str_val = str_ref.value # 获取该引用的值
str_ptr = self._emu.native_memory.allocate(len(str_val) + 1)
logger.debug("=> %s" % str_val)
memory_helpers.write_utf8(mu, str_ptr, str_val)
return str_ptr
self._globals = ReferenceTable(start=4096, max_entries=512000)
实现 GetEnv
"""
:type class_loader JavaClassLoader
:type hooker Hooker
"""
def __init__(self, emu, class_loader, hooker):
(self.address_ptr, self.address) = hooker.write_function_table({
3: self.destroy_java_vm,
4: self.attach_current_thread,
5: self.detach_current_thread,
6: self.get_env,
7: self.attach_current_thread
})
self.jni_env = JNIEnv(emu, class_loader, hooker)
@native_method
def get_env(self, mu, java_vm, env, version):
logger.debug("java_vm: 0x%08x" % java_vm)
logger.debug("env: 0x%08x" % env)
logger.debug("version: 0x%08x" % version)
mu.mem_write(env, self.jni_env.address_ptr.to_bytes(4, byteorder='little'))
logger.debug("JavaVM->GetENV() was called!")
return JNI_OK
Native / Java 函数调用
def java_method_def_real(func):
def native_wrapper(self, emulator, *argv): # native 方法,进入虚拟机
return emulator.call_native(
native_wrapper.jvm_method.native_addr,
emulator.java_vm.jni_env.address_ptr, # JNIEnv*
self, # this
# method has been declared in
*argv # Extra args.
)
def normal_wrapper(*args, **kwargs): # 普通方法,直接调用
result = func(*args, **kwargs)
return result
wrapper = native_wrapper if native else normal_wrapper # 判断是否没native函数,分别进入不同的wrapper
wrapper.jvm_method = JavaMethodDef(func.__name__, wrapper, name, signature, native,
args_list=args_list,
modifier=modifier,
ignore=ignore)
return wrapper
return java_method_def_real
参数转换
amount = len(argv)
if amount == 0:
return
if amount >= 1:
native_write_arg_register(emu, UC_ARM_REG_R0, argv[0])
if amount >= 2:
native_write_arg_register(emu, UC_ARM_REG_R1, argv[1])
if amount >= 3:
native_write_arg_register(emu, UC_ARM_REG_R2, argv[2])
if amount >= 4:
native_write_arg_register(emu, UC_ARM_REG_R3, argv[3])
if amount >= 5:
sp_start = emu.mu.reg_read(UC_ARM_REG_SP)
sp_current = sp_start - STACK_OFFSET # Need to offset because our hook pushes one register on the stack.
for arg in argv[4:]:
emu.mu.mem_write(sp_current - STACK_OFFSET, native_translate_arg(emu, arg).to_bytes(4, byteorder='little'))
sp_current = sp_current - 4
emu.mu.reg_write(UC_ARM_REG_SP, sp_current)
def native_translate_arg(emu, val):
if isinstance(val, int):
return val
elif isinstance(val, str):
return emu.java_vm.jni_env.add_local_reference(jstring(val))
elif isinstance(val, list):
return emu.java_vm.jni_env.add_local_reference(jobjectArray(val))
elif isinstance(val, bytearray):
return emu.java_vm.jni_env.add_local_reference(jbyteArray(val))
elif isinstance(type(val), JavaClassDef):
# TODO: Look into this, seems wrong..
return emu.java_vm.jni_env.add_local_reference(jobject(val))
elif isinstance(val, JavaClassDef):
return emu.java_vm.jni_env.add_local_reference(jobject(val))
else:
raise NotImplementedError("Unable to write response '%s' type '%s' to emulator." % (str(val), type(val)))
def native_write_arg_register(emu, reg, val):
emu.mu.reg_write(reg, native_translate_arg(emu, val))
## 返回值转换
result_idx = self.mu.reg_read(UC_ARM_REG_R0)
result = self.java_vm.jni_env.get_local_reference(result_idx)
附录
- End -
看雪ID:无名侠
https://bbs.pediy.com/user-617255.htm
*本文由看雪论坛 无名侠原创,转载请注明来自看雪社区
推荐文章++++
* Unicorn 在 Android 的应用之Hello World
进阶安全圈,不得不读的一本书
﹀
﹀
﹀