装置
AndroidNativeEmu有什么用?
AndroidNativeEmu是基于Unicron实现的一个指令解析器, 让您可能跨平台模仿Android Native库函数,例如JNI_OnLoad,Java_XXX_XX等函数
个性
- 模仿 JNI Invocation API so
JNI_OnLoad
can 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/ : 外面是须要加载的sovfs/ : 外面是虚构的文件系统, 有须要能够本人增加文件androidemu/ : android虚拟机
import loggingimport sys from unicorn import UC_HOOK_CODEfrom 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 JNICALLJava_com_mario_testunicorn_MainActivity_myAdd( JNIEnv* env, jobject /*this*/, int a, int b){ return nativeAdd(a,b);}
emu代码
正文写的很具体, 具体看代码吧
import loggingimport posixpathimport sys from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPEDfrom 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的sologger.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 loggingimport posixpathimport sys from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPEDfrom unicorn.arm_const import * from androidemu.emulator import Emulatorfrom androidemu.java.java_class_def import JavaClassDeffrom 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")#指标solib_module = emulator.load_library("example_binaries/libtest.so") # 以后曾经load的sologger.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
免责申明:本文档仅供学习与参考,请勿用于非法用处!否则所有后果自负。