美文网首页
Basis of AndroidNativeEmu

Basis of AndroidNativeEmu

作者: HAPPYers | 来源:发表于2019-10-01 16:12 被阅读0次

    安装使用

    环境要求: python 3.7 (注意必须是3.7版本, 使用3.6装keystone的时候踩了坑)

    git clone https://github.com/AeonLucid/AndroidNativeEmu.git
    pip install -r requirements.txt
    

    安装keystone-engine可能会失败

    解决方案

    1. 克隆keystone仓库:
    git clone https://github.com/keystone-engine/keystone.git
    
    1. 打开keystone\bindings文件夹安装:
    python setup.py install
    
    1. 下载对应系统和版本dll(因为我是win), 下载链接: http://www.keystone-engine.org/download/
    2. 把dll复制到python的keystone目录下: [python_path]\Lib\site-packages\keystone\

    然后把sample文件夹里面的example.py拷贝到上一层目录

    python example.py
    

    运行结果

    λ python example.py
    2019-10-01 13:26:19,404   DEBUG        androidemu.internal.modules | Loading module 'samples/example_binaries/libc.so'.
    2019-10-01 13:26:19,409   DEBUG         androidemu.internal.memory | => Mapping memory page 0xcbbcb000 - 0xcbc57000, size 0x0008c000, prot 5
    2019-10-01 13:26:19,410   DEBUG         androidemu.internal.memory | => Mapping memory page 0xcbc58000 - 0xcbc66000, size 0x0000e000, prot 3
    2019-10-01 13:26:19,417   ERROR        androidemu.internal.modules | => Undefined external symbol: dlerror
    2019-10-01 13:26:19,417   ERROR        androidemu.internal.modules | => Undefined external symbol: dlclose
    2019-10-01 13:26:19,418   ERROR        androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version
    2019-10-01 13:26:19,418   ERROR        androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx
    2019-10-01 13:26:20,023   ERROR        androidemu.internal.modules | => Undefined external symbol: dlerror
    2019-10-01 13:26:20,023   ERROR        androidemu.internal.modules | => Undefined external symbol: dlclose
    2019-10-01 13:26:20,026   ERROR        androidemu.internal.modules | => Undefined external symbol: android_get_application_target_sdk_version
    2019-10-01 13:26:20,027   ERROR        androidemu.internal.modules | => Undefined external symbol: dl_unwind_find_exidx
    2019-10-01 13:26:20,326   DEBUG        androidemu.internal.modules | Loading module 'samples/example_binaries/libnative-lib.so'.
    2019-10-01 13:26:20,330   DEBUG         androidemu.internal.memory | => Mapping memory page 0xcbc66000 - 0xcbc69000, size 0x00003000, prot 5
    2019-10-01 13:26:20,330   DEBUG         androidemu.internal.memory | => Mapping memory page 0xcbc69000 - 0xcbc6b000, size 0x00002000, prot 3
    2019-10-01 13:26:20,340    INFO                           __main__ | Loaded modules:
    2019-10-01 13:26:20,340    INFO                           __main__ | [0xcbbcb000] samples/example_binaries/libc.so
    2019-10-01 13:26:20,341    INFO                           __main__ | [0xcbc66000] samples/example_binaries/libnative-lib.so
    # Tracing instruction at 0xcbc667c4, instruction size = 0x2, instruction = 80 b5
    # Tracing instruction at 0xcbc667c6, instruction size = 0x2, instruction = 6f 46
    # Tracing instruction at 0xcbc667c8, instruction size = 0x2, instruction = 82 b0
    # Tracing instruction at 0xcbc667ca, instruction size = 0x2, instruction = 04 48
    # Tracing instruction at 0xcbc667cc, instruction size = 0x2, instruction = 78 44
    ...
    # Tracing instruction at 0xcbbe567a, instruction size = 0x2, instruction = 80 1e
    # Tracing instruction at 0xcbbe567c, instruction size = 0x2, instruction = 70 47
    # Tracing instruction at 0xcbc667d6, instruction size = 0x2, instruction = 02 b0
    # Tracing instruction at 0xcbc667d8, instruction size = 0x2, instruction = 80 bd
    String length is: 26
    

    示例文件分析

    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)).
    

    目录及用法介绍

    androidemu目录下

    • utils/memory_helpers.py为内存读写的工具,包括read_ptr,read_utf8,write_utf8,write_uints,hex_dump等函数
    • pu/syscall_hooks.py包括了各种系统调用的实现(HOOK)
     def __init__(self, mu, syscall_handler):
            self._mu = mu
            self._syscall_handler = syscall_handler
            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)
            self._clock_start = time.time()
            self._clock_offset = randint(1000, 2000)
    

    我们也可以添加自己的系统调用进去,系统调用号可以在unistd.h查到。

    自写Demo测试

    demo工程

    JNIEXPORT int nativeAdd(int a, int b) {
        return a + b;
    }
    
    extern "C"
    JNIEXPORT int wrap_getpid() {
        return getpid();
    }
    
    extern "C" JNIEXPORT jstring JNICALL
    Java_com_happy_emusotest_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        int c = nativeAdd(2, 3) + wrap_getpid();
        c++;
        return env->NewStringUTF(hello.c_str());
    }
    

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

    运行结果

    _binaries/libdl.so
    2019-10-01 14:48:58,798    INFO                           __main__ | => 0xcbbd1000 - example_binaries/libc.so
    2019-10-01 14:48:58,799    INFO                           __main__ | => 0xcbc6c000 - example_binaries/libstdc++.so
    2019-10-01 14:48:58,800    INFO                           __main__ | => 0xcbc72000 - example_binaries/libm.so
    2019-10-01 14:48:58,800    INFO                           __main__ | => 0xcbc95000 - example_binaries/test/libnative-lib.so
    2019-10-01 14:48:58,800   ERROR                androidemu.emulator | Unable to find symbol 'JNI_OnLoad' in module 'example_binaries/test/libnative-lib.so'.
    _Z9nativeAddii result call: 3
    2019-10-01 14:48:58,801   DEBUG    androidemu.cpu.syscall_handlers | Executing syscall getpid() at 0xcbc1ab14
    my wrap_getpid: 4386
    2019-10-01 14:48:58,802    INFO                           __main__ | Exited EMU.
    2019-10-01 14:48:58,802    INFO                           __main__ | Native methods registered to MainActivity:
    

    测试分控so

    这里的emu处理了字符串相关的函数hook
    目标:

    目标文件:  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
    

    高级用法

    hook 类中函数

    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.
    from samples import debug_utils
    
    
    class MainActivity(metaclass=JavaClassDef, jvm_name='local/myapp/testnativeapp/MainActivity'):
    
        def __init__(self):
            pass
    
        @java_method_def(name='stringFromJNI', signature='()Ljava/lang/String;', native=True)
        def string_from_jni(self, mu):
            pass
    
        def test(self):
            pass
    
    
    # Configure logging
    logging.basicConfig(
        stream=sys.stdout,
        level=logging.DEBUG,
        format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
    )
    
    logger = logging.getLogger(__name__)
    
    # Initialize emulator
    emulator = Emulator(
        vfp_inst_set=True,
        vfs_root=posixpath.join(posixpath.dirname(__file__), "vfs")
    )
    
    # Register Java class.
    emulator.java_classloader.add_class(MainActivity)
    
    # Load all libraries.
    emulator.load_library("example_binaries/libdl.so")
    emulator.load_library("example_binaries/libc.so")
    emulator.load_library("example_binaries/libstdc++.so")
    emulator.load_library("example_binaries/libm.so")
    lib_module = emulator.load_library("example_binaries/libnative-lib_jni.so")
    
    # Show loaded modules.
    logger.info("Loaded modules:")
    
    for module in emulator.modules:
        logger.info("=> 0x%08x - %s" % (module.base, module.filename))
    
    # Debug
    # emulator.mu.hook_add(UC_HOOK_CODE, debug_utils.hook_code)
    # emulator.mu.hook_add(UC_HOOK_MEM_UNMAPPED, debug_utils.hook_unmapped)
    # emulator.mu.hook_add(UC_HOOK_MEM_WRITE, debug_utils.hook_mem_write)
    # emulator.mu.hook_add(UC_HOOK_MEM_READ, debug_utils.hook_mem_read)
    
    try:
        # Run JNI_OnLoad.
        #   JNI_OnLoad will call 'RegisterNatives'.
        emulator.call_symbol(lib_module, 'JNI_OnLoad', emulator.java_vm.address_ptr, 0x00)
        emulator.mu.hook_add(UC_HOOK_MEM_UNMAPPED, debug_utils.hook_unmapped)
    
        # Do native stuff.
        emulator.mu.hook_add(UC_HOOK_CODE, debug_utils.hook_code)
        main_activity = MainActivity()
        logger.info("Response from JNI call: %s" % main_activity.string_from_jni(emulator))
    
        # Dump natives found.
        logger.info("Exited EMU.")
        logger.info("Native methods registered to MainActivity:")
    
        for method in MainActivity.jvm_methods.values():
            if method.native:
                logger.info("- [0x%08x] %s - %s" % (method.native_addr, method.name, method.signature))
    except UcError as e:
        print("Exit at %x" % emulator.mu.reg_read(UC_ARM_REG_PC))
        raise
    

    参考

    相关文章

      网友评论

          本文标题:Basis of AndroidNativeEmu

          本文链接:https://www.haomeiwen.com/subject/wdykpctx.html