首页
论坛
课程
招聘
[原创] 跨平台模拟执行 - AndroidNativeEmu食用手册
2019-9-29 11:44 14892

[原创] 跨平台模拟执行 - AndroidNativeEmu食用手册

2019-9-29 11:44
14892

安装

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可能会失败(反正我是没装上)

 

解决方案:

    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/"的目录访问路径

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

4.运行例子

    python example.py

5.不出意外的话就可以看到结果了

 

screenShot.png

例子文件阅读

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 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: 绑定的自定义函数

执行结果

5.png

实战一款风控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

执行结果:
QQ图片20190921161209.png

 

可以看见, 函数已经调用成功, 并且已经成功获取返回值和参数, 不过检测出风险环境了(因为我的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.通过反复测试, 修改对应文件中的关键信息, 最终成功躲过该风控模块的环境检测

 

如下:

 

QQ图片20190921162353.png

总结

该项目是通过Unicron来实现的, Unicorn 是一款非常优秀的跨平台模拟执行框架, 通过上帝视角来调试和调用二进制代码, 几乎可以很清晰发现反调试和检测手段, 而Unicorn的应用绝不仅仅只是个虚拟机, 可以实现很多骚操作, 再次感谢QEMU, Unicron, AndroidNativeEmu等等这些开源大神, 是这些人的分享精神推进了整个圈子的技术迭代;


看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~

最后于 2019-9-29 11:44 被StriveMario编辑 ,原因:
收藏
点赞11
打赏
分享
打赏 + 2.00雪花
打赏次数 1 雪花 + 2.00
 
赞赏  Misirluo   +2.00 2019/12/13
最新回复 (34)
雪    币: 1926
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
DeeLMind 活跃值 2019-9-29 11:52
2
0
围观大佬
雪    币: 133
活跃值: 活跃值 (328)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
SevenSir 活跃值 1 2019-9-29 11:54
3
0
大佬,逆向的事情就拜托了。
雪    币: 1694
活跃值: 活跃值 (528)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
richor 活跃值 2019-9-29 11:54
4
0
围观大佬
雪    币: 1333
活跃值: 活跃值 (939)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
gtict 活跃值 2019-9-29 12:01
5
0
楼主帮看看什么原因,我也是刚刚克隆下跑例子的log
/samples/example.py
2019-09-29 12:09:24,012   DEBUG        androidemu.internal.modules | Loading module 'example_binaries/libc.so'.
2019-09-29 12:09:24,014   DEBUG         androidemu.internal.memory | => Mapping memory page 0xcbbcb000 - 0xcbc57000, size 0x0008c000, prot 5
2019-09-29 12:09:24,015   DEBUG         androidemu.internal.memory | => Mapping memory page 0xcbc58000 - 0xcbc66000, size 0x0000e000, prot 3
2019-09-29 12:09:24,020   ERROR        androidemu.internal.modules | => Undefined external symbol: dlerror
2019-09-29 12:09:24,020   ERROR        androidemu.internal.modules | => Undefined external symbol: dlclose
2019-09-29 12:09:24,020   ERROR        androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version
2019-09-29 12:09:24,020   ERROR        androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx
2019-09-29 12:09:24,560   ERROR        androidemu.internal.modules | => Undefined external symbol: dlerror
2019-09-29 12:09:24,560   ERROR        androidemu.internal.modules | => Undefined external symbol: dlclose
2019-09-29 12:09:24,563   ERROR        androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version
2019-09-29 12:09:24,563   ERROR        androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx
Traceback (most recent call last):
  File "/Users/kwing/PycharmProjects/AndroidNativeEmuwu/samples/example.py", line 20, in <module>
    emulator.load_library("example_binaries/libc.so")
  File "/Users/kwing/PycharmProjects/AndroidNativeEmuwu/androidemu/emulator.py", line 120, in load_library
    self.call_native(fun_ptr)
  File "/Users/kwing/PycharmProjects/AndroidNativeEmuwu/androidemu/emulator.py", line 147, in call_native
    self.mu.emu_start(addr, stop_pos - 1)
2019-09-29 12:09:24,834   DEBUG                androidemu.emulator | Calling Init for: example_binaries/libc.so 
  File "/Users/kwing/Library/Python/3.7/lib/python/site-packages/unicorn/unicorn.py", line 288, in emu_start
    raise UcError(status)
雪    币: 8958
活跃值: 活跃值 (1209)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
xmhwws 活跃值 2019-9-29 12:08
6
0
大佬666
雪    币: 16103
活跃值: 活跃值 (2128)
能力值: (RANK:648 )
在线值:
发帖
回帖
粉丝
KevinsBobo 活跃值 8 2019-9-29 12:37
7
0
大佬666!点赞、收藏、留言,一波三连走起
雪    币: 386
活跃值: 活跃值 (913)
能力值: ( LV12,RANK:218 )
在线值:
发帖
回帖
粉丝
奔跑的阿狸 活跃值 1 2019-9-29 12:48
8
0
666
雪    币: 221
活跃值: 活跃值 (2267)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
Bk_Humor 活跃值 2019-9-29 12:59
9
0
雪    币: 6211
活跃值: 活跃值 (3491)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
LowRebSwrd 活跃值 4 2019-9-29 14:13
10
0
漂亮,结合例子展示了unicron的强大
雪    币: 94
活跃值: 活跃值 (534)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
wangzehua 活跃值 2019-9-29 14:35
11
0
学习学习!
雪    币: 993
活跃值: 活跃值 (375)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
上海刘一刀 活跃值 2 2019-9-29 15:16
12
0
雪    币: 28
活跃值: 活跃值 (215)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
endlif 活跃值 1 2019-9-29 15:26
13
0
感谢分享
雪    币: 10684
活跃值: 活跃值 (2360)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tDasm 活跃值 2019-9-29 16:02
14
0
学习
雪    币: 3653
活跃值: 活跃值 (419)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
逻辑错误 活跃值 1 2019-9-29 17:40
15
0
DeeLMind 围观大佬
围观大佬的好基友
雪    币: 3475
活跃值: 活跃值 (516)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
不知世事 活跃值 1 2019-9-29 17:49
16
0
大佬,自动化逆向机器人就拜托了
雪    币: 301
活跃值: 活跃值 (679)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
天边之云 活跃值 2019-9-30 14:11
17
0
gtict 楼主帮看看什么原因,我也是刚刚克隆下跑例子的log /samples/example.py 2019-09-29 12:09:24,012 DEBUG androidemu.in ...
项目中的少了这里
emulator.load_library("example_binaries/libc.so")改为emulator.load_library("example_binaries/libc.so", do_init=False),他的例子有说明的了
雪    币: 390
活跃值: 活跃值 (166)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
王正飞 活跃值 2019-9-30 15:23
18
0
androidemu.emulator这个库pip安装不了, Not Found for url: https://pypi.org/simple/androidemu/,有搞好的同学吗
雪    币: 200
活跃值: 活跃值 (90)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
荧光之夏 活跃值 2019-9-30 17:25
19
0
看不懂系列 
雪    币: 73
活跃值: 活跃值 (676)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hixhi 活跃值 2019-9-30 18:01
20
0
我记得android studio自带的profile就可以啊,楼主这个是基于什么原理啊
雪    币: 1333
活跃值: 活跃值 (939)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
gtict 活跃值 2019-9-30 18:24
21
0
天边之云 项目中的少了这里 emulator.load_library("example_binaries/libc.so")改为emulator.load_library("e ...
可以运行了,,谢啦,,现在还有原因androidemu.internal.modules | => Undefined external symbol: dlerror  怎么这一类的函数找不到
雪    币: 143
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zylyy 活跃值 2019-9-30 23:47
22
0
我卡在回调这里了
雪    币: 620
活跃值: 活跃值 (2073)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
默NJ 活跃值 2019-10-3 09:23
23
1
keystone就是个坑,直接用官方的二进制安装软件比较好
雪    币: 18
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
rawer886 活跃值 2019-10-7 11:02
24
0
学习
雪    币: 358
活跃值: 活跃值 (498)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
陈某人 活跃值 2019-10-8 15:07
25
0
Unable to find symbol 'JNI_OnLoad' in module

上传的附件:
游客
登录 | 注册 方可回帖
返回