Skip to content

Commit

Permalink
pull request (AeonLucid#7)
Browse files Browse the repository at this point in the history
* 添加抖音X-Gorgen

* delete ida files

* 添加 中文README

* 修改:README

* 修复log

* socket.
  • Loading branch information
P4nda0s authored and AeonLucid committed Aug 20, 2019
1 parent 6af9287 commit 8568b31
Show file tree
Hide file tree
Showing 27 changed files with 1,742 additions and 39 deletions.
Binary file added .DS_Store
Binary file not shown.
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# AndroidNativeEmu
[ 中文README & 教程? ](README_cn.md)

Allows you to partly emulate an Android native library.

Expand All @@ -13,6 +14,15 @@ This is an educational project to learn more about the ELF file format and [Unic
- All JavaVM, JNIEnv and hooked functions are handled by python.
- Enable VFP support.

## My Changes
- Add init_array support depends on Relocation information.
- Add support of modify object value by reference id.
- Implement getcpu() syscall
- Implement set_byte_array_region
- Register Function failed would't raise an error(beacuse most jni functions are not used.)
- samples:添加抖音 X-Gorgen 调用实例
- [ 中文README ](README_cn.md)

## Usage

> In the future this will be possible through pypi.
Expand Down
136 changes: 136 additions & 0 deletions README_cn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# AndroidNativeEmu
AndroidNativeEmu 让你能够跨平台模拟Android Native库函数,比如JNI_OnLoad、Java_XXX_XX等函数。
fork from : https://github.com/AeonLucid/AndroidNativeEmu

## 特性
- 模拟 [JNI Invocation API](https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html) so `JNI_OnLoad` can be called properly.
- 模拟 memory、malloc、memcpy
- 支持拦截系统调用(SVC #0)
- 通过符号Hook
- 所有 JavaVM, JNIEnv 和 hooked functions 都可以用python来处理
- 支持 VFP
- 支持文件系统(也就是说你可以模拟maps、status等文件)

## 本人瞎改
小弟不才,修改了一些代码,使其能够运行libcms的leviathan.
- 添加 自动调用InitArray初始化代码,基于重定位表解析。
- 添加 修改对象引用的值
- 实现 getcpu() 系统调用
- 实现 setByteArrayRegion
- JNI中动态注册Native函数失败将不再报错(libcms中注册了大量不需要的函数)

## 使用方法
运行环境:python 3.7 必须!
1. Clone the repository
2. Run `pip install -r requirements.txt`
3. Run `python example.py`

Windows上可以跑,自行尝试。


## 依赖库
- [Unicorn CPU emulator framework](https://github.com/unicorn-engine/unicorn)
- [Keystone assembler framework](https://github.com/keystone-engine/keystone)


## 初始化模拟器
```python
# Initialize emulator
emulator = Emulator(
vfp_inst_set=True,
vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
)
```

## 如何定义Java类呢?

### Jni 中会调用到的类
注意看看各项参数的定义
```python
class java_lang_System(metaclass=JavaClassDef, jvm_name='java/lang/System'):
def __init__(self):
pass

@java_method_def(name='getProperty', args_list=["jstring"] ,signature='(Ljava/lang/String;)Ljava/lang/String;', native=False)
def getProperty(self, *args, **kwargs):
print (args[0].value)
return "2.1.0"
```
### 我们的目标类
```python
class XGorgen(metaclass=JavaClassDef, jvm_name='com/ss/sys/ces/a'):
def __init__(self):
pass

@java_method_def(name='leviathan', signature='(I[B)[B', native=True)
def leviathan(self, mu):
pass

def test(self):
pass
```

### 模拟stacktrace的类
```python
class java_lang_Thread(metaclass=JavaClassDef, jvm_name='java/lang/Thread'):
def __init__(self):
pass

@java_method_def(name="currentThread", signature='()Ljava/lang/Thread;', native=False)
def currentThread(self, *args, **kwargs):
return java_lang_Thread()

@java_method_def(name="getStackTrace", signature='()[Ljava/lang/StackTraceElement;', native=False)
def getStackTrace(self, *args, **kwargs):
return [java_lang_StackTraceElement("dalvik.system.VMStack"),
java_lang_StackTraceElement("java.lang.Thread"),
java_lang_StackTraceElement("com.ss.sys.ces.a"),
java_lang_StackTraceElement("com.yf.douyintool.MainActivity"),
java_lang_StackTraceElement("java.lang.reflect.Method"),
java_lang_StackTraceElement("java.lang.reflect.Method"),
java_lang_StackTraceElement("android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener"),
java_lang_StackTraceElement("android.view.View"),
java_lang_StackTraceElement("android.os.Handler"),
java_lang_StackTraceElement("android.os.Handler"),
java_lang_StackTraceElement("android.os.Looper"),
java_lang_StackTraceElement("android.app.ActivityThread"),
java_lang_StackTraceElement("java.lang.reflect.Method"),
java_lang_StackTraceElement("java.lang.reflect.Method"),
java_lang_StackTraceElement("com.android.internal.os.ZygoteInit$MethodAndArgsCaller"),
java_lang_StackTraceElement("com.android.internal.os.ZygoteInit"),
java_lang_StackTraceElement("dalvik.system.NativeStart")
]
```
更多的类请见example

## 注册类
```python
emulator.java_classloader.add_class(XGorgen)
emulator.java_classloader.add_class(java_lang_System)
emulator.java_classloader.add_class(java_lang_Thread)
emulator.java_classloader.add_class(java_lang_StackTraceElement)
```

## 调用JNI_OnLoad
init array 已经自动调用了,SO如果有加密也没关系。
```python
# 添加依赖库
emulator.load_library("samples/example_binaries/libdl.so")
emulator.load_library("samples/example_binaries/libc.so")
emulator.load_library("samples/example_binaries/libstdc++.so")
emulator.load_library("samples/example_binaries/libm.so")

lib_module = emulator.load_library("samples/example_binaries/libcms.so")

# JNI_OnLoad will call 'RegisterNatives'.
emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)

```

## 调用native 方法
```python
x = XGorgen()
data = 'acde74a94e6b493a3399fac83c7c08b35D58B21D9582AF77647FC9902E36AE70f9c001e9334e6e94916682224fbe4e5f00000000000000000000000000000000'
data = bytearray(bytes.fromhex(data))
result = x.leviathan(emulator, 1562848170, data)
```
6 changes: 4 additions & 2 deletions androidemu/cpu/syscall_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ def _handle_syscall(self, mu):
if result is not None:
mu.reg_write(UC_ARM_REG_R0, result)
else:
logger.error("Unhandled syscall 0x%x (%u) at 0x%x, stopping emulation" % (idx, idx,
mu.reg_read(UC_ARM_REG_PC)))

error = "Unhandled syscall 0x%x (%u) at 0x%x, stopping emulation" % (idx, idx,
mu.reg_read(UC_ARM_REG_PC))
mu.emu_stop()
raise RuntimeError(error)
25 changes: 24 additions & 1 deletion androidemu/cpu/syscall_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,31 @@ def __init__(self, mu, syscall_handler):
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._clock_start = time.time()
self._clock_offset = randint(1000, 2000)

def _null(self, mu):
return 0

def _gettid(self, mu):
return 0x2211

def _getpid(self, mu):
return 0x1122

def _faccessat(self, mu, filename, pathname, mode, flag):
return 0

def _getcpu(self, mu, _cpu, node, cache):
if _cpu != 0:
mu.mem_write(_cpu, int(1).to_bytes(4, byteorder='little'))
return 0

def _handle_gettimeofday(self, uc, tv, tz):
"""
If either tv or tz is NULL, the corresponding structure is not set or returned.
Expand Down Expand Up @@ -120,7 +142,8 @@ def _handle_clock_gettime(self, mu, clk_id, tp_ptr):
raise NotImplementedError("Unsupported clk_id: %d (%x)" % (clk_id, clk_id))

def _socket(self, mu, family, type_in, protocol):
raise NotImplementedError()
return 0
# raise NotImplementedError()

def _connect(self, mu, fd, addr, addr_len):
print(hexdump.hexdump(mu.mem_read(addr, addr_len)))
Expand Down
17 changes: 13 additions & 4 deletions androidemu/emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@


class Emulator:

"""
:type mu Uc
:type modules Modules
Expand All @@ -41,7 +40,7 @@ def __init__(self, vfs_root=None, vfp_inst_set=False):
self._enable_vfp()

# Android
self.system_properties = {}
self.system_properties = {"libc.debug.malloc.options":""}

# Stack.
self.mu.mem_map(config.STACK_ADDR, config.STACK_SIZE)
Expand Down Expand Up @@ -109,8 +108,17 @@ def _enable_vfp(self):
finally:
self.mu.mem_unmap(address, mem_size)

def load_library(self, filename):
return self.modules.load_module(filename)
def _call_init_array(self):
pass

def load_library(self, filename, do_init=True):
libmod = self.modules.load_module(filename)
if do_init:
logger.debug("Calling Init for: %s " % filename)
for fun_ptr in libmod.init_array:
logger.debug("Calling Init function: %x " % fun_ptr)
self.call_native(fun_ptr)
return libmod

def call_symbol(self, module, symbol_name, *argv):
symbol = module.find_symbol(symbol_name)
Expand All @@ -132,6 +140,7 @@ def call_native(self, addr, *argv):

try:
# Execute native call.

native_write_args(self, *argv)
stop_pos = randint(HOOK_MEMORY_BASE, HOOK_MEMORY_BASE + HOOK_MEMORY_SIZE) | 1
self.mu.reg_write(UC_ARM_REG_LR, stop_pos)
Expand Down
3 changes: 3 additions & 0 deletions androidemu/internal/memory.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,7 @@ def mem_map(self, address, 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)


11 changes: 9 additions & 2 deletions androidemu/internal/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ class Module:
:type base int
:type size int
"""
def __init__(self, filename, address, size, symbols_resolved):
def __init__(self, filename, address, size, symbols_resolved, init_array=[]):
self.filename = filename
self.base = address
self.size = size
self.symbols = symbols_resolved
self.symbol_lookup = dict()
self.init_array = list(init_array)

# Create fast lookup.
for symbol_name, symbol in self.symbols.items():
Expand All @@ -20,5 +21,11 @@ def __init__(self, filename, address, size, symbols_resolved):
def find_symbol(self, name):
if name in self.symbols:
return self.symbols[name]

return None

def is_symbol_addr(self, addr):
if addr in self.symbol_lookup:
return self.symbol_lookup[addr](0)
else:
return None

Loading

0 comments on commit 8568b31

Please sign in to comment.