实战目标
以下信息通过分析所得, 具体分析过程不是本文重点, 这里不赘述;1
2
3
4目标文件: libtest.so
目标函数: a(char* buf, int buf_len)
返回值: return_value > 0, 表示风险环境并且会在buf参数里写入详细风险环境信息;
return_value == 0, 表示正常环境
EMU代码
详情看注释, 写的很详细1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72import 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等系统环境, 猜测可能是通过这些文件来判断的, 这里列出个别几个1
2
3
4
5
6
7
82019-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等等这些开源大神, 是这些人推进了整个圈子的技术迭代;