抖音數據採集教程,跨平臺模擬執行AndroidNativeEmu手冊

安裝

AndroidNativeEmu有什麼用?

AndroidNativeEmu是基於Unicron實現的一個指令解析器, 讓您可以跨平臺模擬Android Native庫函數,例如JNI_OnLoad,Java_XXX_XX等函數html

特性

  • 模擬 JNI Invocation API so JNI_OnLoad can be called properly.
  • 模擬 memory、malloc、memcpy
  • 支持攔截系統調用(SVC #0)
  • 經過符號Hook
  • 全部 JavaVM, JNIEnv 和 hooked functions 均可以用python來處理
  • 支持 VFP
  • 支持文件系統(也就是說你能夠模擬maps、status等文件)

項目地址java

安裝過程

環境要求: python 3.7 (注意必須是3.7版本, 我使用3.6裝keystone的時候踩了坑)
 
自測系統環境: win7
 
1.Clone 該項目python

git clone https://github.com/AeonLucid/AndroidNativeEmu.git

2.安裝須要的支持模塊android

pip install -r requirements.txt

安裝keystone-engine可能會失敗(反正我是沒裝上)c++


解決方案:git

  1. 克隆keystone倉庫: git clone https://github.com/keystone-engine/keystone.git
  2. 打開keystone\bindings文件夾安裝: python setup.py install
  3. 下載對應系統和版本dll(由於我是win), 下載連接: http://www.keystone-engine.org/download/
  4. 把dll複製到python的keystone目錄下: [python_path]\Lib\site-packages\keystone\

3.把androidemu文件夾複製至sample文件夾下,並刪除example.py文件下的關於"samples/"的目錄訪問路徑github

如
"samples/example_binaries/libc.so"
改成
"example_binaries/libc.so"

4.運行例子oracle

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的代碼很簡單, 就是一個加法socket

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 onload 這裏沒有, 但不影響執行
    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 == 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 onload 這裏沒有, 但不影響執行
    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

免責聲明:本文檔僅供學習與參考,請勿用於非法用途!不然一切後果自負。

相關文章
相關標籤/搜索