安裝
AndroidNativeEmu有什么用?
AndroidNativeEmu是基于Unicron實作的一個指令決議器, 讓您能夠跨平臺模擬Android Native庫函式,例如JNI_OnLoad,Java_XXX_XX等函式
特性
- 模擬 JNI Invocation API so
JNI_OnLoadcan be called properly. - 模擬 memory、malloc、memcpy
- 支持攔截系統呼叫(SVC #0)
- 通過符號Hook
- 所有 JavaVM, JNIEnv 和 hooked functions 都可以用python來處理
- 支持 VFP
- 支持檔案系統(也就是說你可以模擬maps、status等檔案)
專案地址
安裝程序
環境要求: python 3.7 (注意必須是3.7版本, 我使用3.6裝keystone的時候踩了坑)
自測系統環境: win7
1.Clone 該專案
git clone https://github.com/AeonLucid/AndroidNativeEmu.git
2.安裝需要的支持模塊
pip install -r requirements.txt
安裝keystone-engine可能會失敗(反正我是沒裝上)
解決方案:
- 克隆keystone倉庫: git clone https://github.com/keystone-engine/keystone.git
- 打開keystone\bindings檔案夾安裝: python setup.py install
- 下載對應系統和版本dll(因為我是win), 下載鏈接: http://www.keystone-engine.org/download/
- 把dll復制到python的keystone目錄下: [python_path]\Lib\site-packages\keystone\
3.把androidemu檔案夾復制至sample檔案夾下,并洗掉example.py檔案下的關于"samples/"的目錄訪問路徑
如
"samples/example_binaries/libc.so"
改為
"example_binaries/libc.so"
4.運行例子
python example.py
5.不出意外的話就可以看到結果了

例子檔案閱讀
example_binaries/ : 里面是需要加載的so
vfs/ : 里面是虛擬的檔案系統, 有需要可以自己添加檔案
androidemu/ : android虛擬機
import logging
import sys
from unicorn import UC_HOOK_CODE
from unicorn.arm_const import *
from androidemu.emulator import Emulator
# 配置日志相關設定
logging.basicConfig(
stream=sys.stdout, #標準輸出流
level=logging.DEBUG, #輸出等級
format="%(asctime)s %(levelname)7s %(name)34s | %(message)s" #輸出格式
)
logger = logging.getLogger(__name__) #實體化物件
# 實體化虛擬機
emulator = Emulator()
#加載Libc庫
emulator.load_library("example_binaries/libc.so", do_init=False)
#加載要模擬器的庫
lib_module = emulator.load_library("example_binaries/libnative-lib.so")
#列印已經加載的模塊
logger.info("Loaded modules:")
for module in emulator.modules:
logger.info("[0x%x] %s" % (module.base, module.filename))
#trace 每步執行的指令, 方便除錯, 其實也可以取消
def hook_code(mu, address, size, user_data):
instruction = mu.mem_read(address, size)
instruction_str = ''.join('{:02x} '.format(x) for x in instruction)
print('# Tracing instruction at 0x%x, instruction size = 0x%x, instruction = %s' % (address, size, instruction_str))
emulator.mu.hook_add(UC_HOOK_CODE, hook_code)
#通過匯出符號來呼叫函式
emulator.call_symbol(lib_module, '_Z4testv')
#通過R0來獲取呼叫結構
print("String length is: %i" % emulator.mu.reg_read(UC_ARM_REG_R0))
自己寫個小Demo測驗
Demo代碼
新建一個jni工程, demo的代碼很簡單, 就是一個加法
JNIEXPORT int nativeAdd(int a, int b)
{
return a + b;
}
extern "C" JNIEXPORT jint JNICALL
Java_com_mario_testunicorn_MainActivity_myAdd(
JNIEnv* env,
jobject /*this*/,
int a,
int b){
return nativeAdd(a,b);
}
emu代碼
注釋寫的很詳細, 具體看代碼吧
import logging
import posixpath
import sys
from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPED
from unicorn.arm_const import *
from androidemu.emulator import Emulator
import debug_utils
# 配置日志
logging.basicConfig(
stream=sys.stdout,
level=logging.DEBUG,
format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
)
logger = logging.getLogger(__name__)
# 初始化模擬器
emulator = Emulator(
vfp_inst_set=True,
vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
)
# 加載依賴的動態庫
emulator.load_library("example_binaries/libdl.so")
emulator.load_library("example_binaries/libc.so", do_init=False)
emulator.load_library("example_binaries/libstdc++.so")
emulator.load_library("example_binaries/libm.so")
lib_module = emulator.load_library("example_binaries/libmytest.so")
# 當前已經load的so
logger.info("Loaded modules:")
for module in emulator.modules:
logger.info("=> 0x%08x - %s" % (module.base, module.filename))
try:
# 運行jni onl oad 這里沒有, 但不影響執行
emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)
#直接呼叫符號1, 計算1+2
emulator.call_symbol(lib_module, '_Z9nativeAddii', 1, 2)
print("_Z9nativeAddii result call: %i" % emulator.mu.reg_read(UC_ARM_REG_R0))
#直接呼叫符號2, 計算1000 + 1000
emulator.call_symbol(lib_module, 'Java_com_mario_testunicorn_MainActivity_myAdd', 0, 0, 1000, 1000)
print("myAdd result call: %i" % emulator.mu.reg_read(UC_ARM_REG_R0))
#執行完成, 退出虛擬機
logger.info("Exited EMU.")
logger.info("Native methods registered to MainActivity:")
except UcError as e:
print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC))
raise
RuntimeError: Unhandled syscall x (x) at 解決
這個錯誤是因為沒有實作對應syscall導致的, 缺少什么函式, 自己寫一個函式系結一下, 回傳給他需要的值就可以了, 比如getpid, 那么自己寫的函式隨便回傳一個整形就可以了
在syscall_hooks.py檔案里, 可以看到作者已經實作的函式
self._syscall_handler.set_handler(0x4E, "gettimeofday", 2, self._handle_gettimeofday)
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)
set_handler函式引數:
arg1: 中斷號(intno),中斷號可以在ndk中的unistd.h中找到
arg2: 函式名
arg3: 引數數量
arg4: 系結的自定義函式
執行結果

實戰一款風控SO
實戰目標
以下資訊通過分析所得, 具體分析程序不是本文重點, 這里不贅述;
目標檔案: libtest.so
目標函式: a(char* buf, int buf_len)
回傳值: return_value > 0, 表示風隙訓境并且會在buf引數里寫入詳細風隙訓境資訊;
return_value =https://www.cnblogs.com/titodata/archive/2020/12/31/= 0, 表示正常環境
EMU代碼
詳情看注釋, 寫的很詳細
import logging
import posixpath
import sys
from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPED
from unicorn.arm_const import *
from androidemu.emulator import Emulator
from androidemu.java.java_class_def import JavaClassDef
from androidemu.java.java_method_def import java_method_def
# Create java class.
import debug_utils
# 配置日志
logging.basicConfig(
stream=sys.stdout,
level=logging.DEBUG,
format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
)
logger = logging.getLogger(__name__)
# 初始化模擬器
emulator = Emulator(
vfp_inst_set=True,
vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
)
# 加載依賴的動態庫
emulator.load_library("example_binaries/libdl.so")
emulator.load_library("example_binaries/libc.so", do_init=False)
emulator.load_library("example_binaries/libstdc++.so")
emulator.load_library("example_binaries/liblog.so")
emulator.load_library("example_binaries/libm.so")
#目標so
lib_module = emulator.load_library("example_binaries/libtest.so")
# 當前已經load的so
logger.info("Loaded modules:")
for module in emulator.modules:
logger.info("=> 0x%08x - %s" % (module.base, module.filename))
try:
# 運行jni onl oad 這里沒有, 但不影響執行
emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)
# 增加properties, 該so或通過獲取一些properties來判斷環境
emulator.system_properties['ro.build.fingerprint'] = 'google/passion/passion:2.3.3/GRI40/102588:user/release-keys'
emulator.system_properties['ro.product.cpu.abi'] = 'arm'
emulator.system_properties['microvirt.vbox_dpi'] = ''
#申請一塊buff, 用作引數
emulator.call_symbol(lib_module, 'malloc', 0x1000)
address = emulator.mu.reg_read(UC_ARM_REG_R0)
#在之前申請的buff讀取記憶體
detect_str = memory_helpers.read_utf8(emulator.mu, address)
print("detect_str: " + detect_str)
#執行完成, 退出虛擬機
logger.info("Exited EMU.")
logger.info("Native methods registered to MainActivity:")
except UcError as e:
print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC))
raise
執行結果:
可以看見, 函式已經呼叫成功, 并且已經成功獲取回傳值和引數, 不過檢測出風隙訓境了(因為我的vfs檔案都是從虛擬機里拷貝出來的), 接下來就可以分析檢測點了!~~
過檢測
1.通過執行日志分析, 發現頻繁訪問了build.prop, maps等系統環境, 猜測可能是通過這些檔案來判斷的, 這里列出個別幾個
2019-09-21 16:08:27,677 INFO androidemu.vfs.file_system | Reading 1024 bytes from '/proc/cpuinfo'
2019-09-21 16:08:27,680 DEBUG androidemu.cpu.syscall_handlers | Executing syscall read(00000005, 02089000, 00000400) at 0xcbc1ba7c
2019-09-21 16:08:27,783 INFO androidemu.vfs.file_system | Reading 1024 bytes from '/proc/self/maps'
2019-09-21 16:08:27,784 DEBUG androidemu.cpu.syscall_handlers | Executing syscall close(00000008) at 0xcbc1a854
2019-09-21 16:08:27,886 INFO androidemu.vfs.file_system | File opened '/proc/self/status'
2019-09-21 16:08:27,887 DEBUG androidemu.cpu.syscall_handlers | Executing syscall fstat64(0000000a, 000ff3e8) at 0xcbc1b314
2.通過反復測驗, 修改對應檔案中的關鍵資訊, 最終成功躲過該風控模塊的環境檢測
如下:

總結
該專案是通過Unicron來實作的, Unicorn 是一款非常優秀的跨平臺模擬執行框架, 通過上帝視角來除錯和呼叫二進制代碼, 幾乎可以很清晰發現反除錯和檢測手段, 而Unicorn的應用絕不僅僅只是個虛擬機, 可以實作很多騷操作, 再次感謝QEMU, Unicron, AndroidNativeEmu等等這些開源大神, 是這些人的分享精神推進了整個圈子的技術迭代,
更多短視頻資料實時采集介面,請查看檔案: TiToData
免責宣告:本檔案僅供學習與參考,請勿用于非法用途!否則一切后果自負,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/243222.html
標籤:其他
下一篇:SQL Server死鎖報錯分析
