经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
抖音数据采集教程,跨平台模拟执行AndroidNativeEmu手册
来源:cnblogs  作者:数据工具箱  时间:2021/1/4 9:02:00  对本文有异议

安装

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 该项目

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

2.安装需要的支持模块

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

  1. "samples/example_binaries/libc.so"
  2. 改为
  3. "example_binaries/libc.so"

4.运行例子

  1. python example.py

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

例子文件阅读

  1. example_binaries/ : 里面是需要加载的so
  2. vfs/ : 里面是虚拟的文件系统, 有需要可以自己添加文件
  3. androidemu/ : android虚拟机
  1. import logging
  2. import sys
  3. from unicorn import UC_HOOK_CODE
  4. from unicorn.arm_const import *
  5. from androidemu.emulator import Emulator
  6. # 配置日志相关设置
  7. logging.basicConfig(
  8. stream=sys.stdout, #标准输出流
  9. level=logging.DEBUG, #输出等级
  10. format="%(asctime)s %(levelname)7s %(name)34s | %(message)s" #输出格式
  11. )
  12. logger = logging.getLogger(__name__) #实例化对象
  13. # 实例化虚拟机
  14. emulator = Emulator()
  15. #加载Libc库
  16. emulator.load_library("example_binaries/libc.so", do_init=False)
  17. #加载要模拟器的库
  18. lib_module = emulator.load_library("example_binaries/libnative-lib.so")
  19. #打印已经加载的模块
  20. logger.info("Loaded modules:")
  21. for module in emulator.modules:
  22. logger.info("[0x%x] %s" % (module.base, module.filename))
  23. #trace 每步执行的指令, 方便调试, 其实也可以取消
  24. def hook_code(mu, address, size, user_data):
  25. instruction = mu.mem_read(address, size)
  26. instruction_str = ''.join('{:02x} '.format(x) for x in instruction)
  27. print('# Tracing instruction at 0x%x, instruction size = 0x%x, instruction = %s' % (address, size, instruction_str))
  28. emulator.mu.hook_add(UC_HOOK_CODE, hook_code)
  29. #通过导出符号来调用函数
  30. emulator.call_symbol(lib_module, '_Z4testv')
  31. #通过R0来获取调用结构
  32. print("String length is: %i" % emulator.mu.reg_read(UC_ARM_REG_R0))

自己写个小Demo测试

Demo代码

新建一个jni工程, demo的代码很简单, 就是一个加法

  1. JNIEXPORT int nativeAdd(int a, int b)
  2. {
  3. return a + b;
  4. }
  5. extern "C" JNIEXPORT jint JNICALL
  6. Java_com_mario_testunicorn_MainActivity_myAdd(
  7. JNIEnv* env,
  8. jobject /*this*/,
  9. int a,
  10. int b){
  11. return nativeAdd(a,b);
  12. }

emu代码

注释写的很详细, 具体看代码吧

  1. import logging
  2. import posixpath
  3. import sys
  4. from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPED
  5. from unicorn.arm_const import *
  6. from androidemu.emulator import Emulator
  7. import debug_utils
  8. # 配置日志
  9. logging.basicConfig(
  10. stream=sys.stdout,
  11. level=logging.DEBUG,
  12. format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
  13. )
  14. logger = logging.getLogger(__name__)
  15. # 初始化模拟器
  16. emulator = Emulator(
  17. vfp_inst_set=True,
  18. vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
  19. )
  20. # 加载依赖的动态库
  21. emulator.load_library("example_binaries/libdl.so")
  22. emulator.load_library("example_binaries/libc.so", do_init=False)
  23. emulator.load_library("example_binaries/libstdc++.so")
  24. emulator.load_library("example_binaries/libm.so")
  25. lib_module = emulator.load_library("example_binaries/libmytest.so")
  26. # 当前已经load的so
  27. logger.info("Loaded modules:")
  28. for module in emulator.modules:
  29. logger.info("=> 0x%08x - %s" % (module.base, module.filename))
  30. try:
  31. # 运行jni onload 这里没有, 但不影响执行
  32. emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)
  33. #直接调用符号1, 计算1+2
  34. emulator.call_symbol(lib_module, '_Z9nativeAddii', 1, 2)
  35. print("_Z9nativeAddii result call: %i" % emulator.mu.reg_read(UC_ARM_REG_R0))
  36. #直接调用符号2, 计算1000 + 1000
  37. emulator.call_symbol(lib_module, 'Java_com_mario_testunicorn_MainActivity_myAdd', 0, 0, 1000, 1000)
  38. print("myAdd result call: %i" % emulator.mu.reg_read(UC_ARM_REG_R0))
  39. #执行完成, 退出虚拟机
  40. logger.info("Exited EMU.")
  41. logger.info("Native methods registered to MainActivity:")
  42. except UcError as e:
  43. print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC))
  44. raise

RuntimeError: Unhandled syscall x (x) at 解决

这个错误是因为没有实现对应syscall导致的, 缺少什么函数, 自己写一个函数绑定一下, 返回给他需要的值就可以了, 比如getpid, 那么自己写的函数随便返回一个整形就可以了
 
在syscall_hooks.py文件里, 可以看到作者已经实现的函数

  1. self._syscall_handler.set_handler(0x4E, "gettimeofday", 2, self._handle_gettimeofday)
  2. self._syscall_handler.set_handler(0xAC, "prctl", 5, self._handle_prctl)
  3. self._syscall_handler.set_handler(0xF0, "futex", 6, self._handle_futex)
  4. self._syscall_handler.set_handler(0x107, "clock_gettime", 2, self._handle_clock_gettime)
  5. self._syscall_handler.set_handler(0x119, "socket", 3, self._socket)
  6. self._syscall_handler.set_handler(0x11b, "connect", 3, self._connect)
  7. self._syscall_handler.set_handler(0x159, "getcpu", 3, self._getcpu)
  8. self._syscall_handler.set_handler(0x14e, "faccessat", 4, self._faccessat)
  9. self._syscall_handler.set_handler(0x14, "getpid", 0, self._getpid)
  10. self._syscall_handler.set_handler(0xe0, "gettid", 0, self._gettid)
  11. self._syscall_handler.set_handler(0x180,"null1",0, self._null)
  1. set_handler函数参数:
  2. arg1: 中断号(intno),中断号可以在ndk中的unistd.h中找到
  3. arg2: 函数名
  4. arg3: 参数数量
  5. arg4: 绑定的自定义函数

执行结果

实战一款风控SO

实战目标

以下信息通过分析所得, 具体分析过程不是本文重点, 这里不赘述;

  1. 目标文件: libtest.so
  2. 目标函数: a(char* buf, int buf_len)
  3. 返回值: return_value > 0, 表示风险环境并且会在buf参数里写入详细风险环境信息;
  4. return_value == 0, 表示正常环境

EMU代码

详情看注释, 写的很详细

  1. import logging
  2. import posixpath
  3. import sys
  4. from unicorn import UcError, UC_HOOK_CODE, UC_HOOK_MEM_UNMAPPED
  5. from unicorn.arm_const import *
  6. from androidemu.emulator import Emulator
  7. from androidemu.java.java_class_def import JavaClassDef
  8. from androidemu.java.java_method_def import java_method_def
  9. # Create java class.
  10. import debug_utils
  11. # 配置日志
  12. logging.basicConfig(
  13. stream=sys.stdout,
  14. level=logging.DEBUG,
  15. format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
  16. )
  17. logger = logging.getLogger(__name__)
  18. # 初始化模拟器
  19. emulator = Emulator(
  20. vfp_inst_set=True,
  21. vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
  22. )
  23. # 加载依赖的动态库
  24. emulator.load_library("example_binaries/libdl.so")
  25. emulator.load_library("example_binaries/libc.so", do_init=False)
  26. emulator.load_library("example_binaries/libstdc++.so")
  27. emulator.load_library("example_binaries/liblog.so")
  28. emulator.load_library("example_binaries/libm.so")
  29. #目标so
  30. lib_module = emulator.load_library("example_binaries/libtest.so")
  31. # 当前已经load的so
  32. logger.info("Loaded modules:")
  33. for module in emulator.modules:
  34. logger.info("=> 0x%08x - %s" % (module.base, module.filename))
  35. try:
  36. # 运行jni onload 这里没有, 但不影响执行
  37. emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)
  38. # 增加properties, 该so或通过获取一些properties来判断环境
  39. emulator.system_properties['ro.build.fingerprint'] = 'google/passion/passion:2.3.3/GRI40/102588:user/release-keys'
  40. emulator.system_properties['ro.product.cpu.abi'] = 'arm'
  41. emulator.system_properties['microvirt.vbox_dpi'] = ''
  42. #申请一块buff, 用作参数
  43. emulator.call_symbol(lib_module, 'malloc', 0x1000)
  44. address = emulator.mu.reg_read(UC_ARM_REG_R0)
  45. #在之前申请的buff读取内存
  46. detect_str = memory_helpers.read_utf8(emulator.mu, address)
  47. print("detect_str: " + detect_str)
  48. #执行完成, 退出虚拟机
  49. logger.info("Exited EMU.")
  50. logger.info("Native methods registered to MainActivity:")
  51. except UcError as e:
  52. print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC))
  53. raise

执行结果:

 
可以看见, 函数已经调用成功, 并且已经成功获取返回值和参数, 不过检测出风险环境了(因为我的vfs文件都是从虚拟机里拷贝出来的), 接下来就可以分析检测点了!~~

过检测

1.通过执行日志分析, 发现频繁访问了build.prop, maps等系统环境, 猜测可能是通过这些文件来判断的, 这里列出个别几个

  1. 2019-09-21 16:08:27,677 INFO androidemu.vfs.file_system | Reading 1024 bytes from '/proc/cpuinfo'
  2. 2019-09-21 16:08:27,680 DEBUG androidemu.cpu.syscall_handlers | Executing syscall read(00000005, 02089000, 00000400) at 0xcbc1ba7c
  3. 2019-09-21 16:08:27,783 INFO androidemu.vfs.file_system | Reading 1024 bytes from '/proc/self/maps'
  4. 2019-09-21 16:08:27,784 DEBUG androidemu.cpu.syscall_handlers | Executing syscall close(00000008) at 0xcbc1a854
  5. 2019-09-21 16:08:27,886 INFO androidemu.vfs.file_system | File opened '/proc/self/status'
  6. 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


免责声明:本文档仅供学习与参考,请勿用于非法用途!否则一切后果自负。

原文链接:http://www.cnblogs.com/titodata/p/14218557.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号