unicorn_learning3

  1. 目标
  2. 打印调试信息
  3. 创建模拟器+导入so文件+执行
  4. 跟踪报错信息
  5. hook

unicorn start

模拟器框架自动填充got表项内容

目标

hook JNI函数

image-20221231092735710

hook JNI函数的条件:

1

框架:image-20221231093245212

打印调试信息

import logging
import sys

# 配置logging
logging.basicConfig(
    stream=sys.stdout,
    level=logging.debug,
    format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
                    )
# 用于打印调试信息
logger = logging.getLogger(__name__)

创建模拟器+导入so文件+执行

用写好的框架进行模拟执行,不需要考虑手动填充got表项

# 创建模拟器
emulator = Emulator()

# 加载相关so文件
# do_init 参数为false,即不执行初始化函数,因为unicorn不能实现相关初始化操作,
# 这可能会涉及更多库的调用
emulator.load_library('./lib/libc.so',do_init=False)
libnat_mod = emulator.load_library('./lib/libnative-lib.so',do_init=False)

# 开始执行想要执行的jni函数
try:
    # 根据so模块调用指定的函数,后面都是函数的参数
    # 参数1: JNIEnv
    # 参数2: 是java类的对象,这里填0即可
    # 参数3: 是函数输入字符串
    sign = emulator.call_symbol(libnat_mod,'Java_com_sec_udemo_MainActivity_sign_1lv2',
                         emulator.java_vm.jni_env.address_ptr,0,'123')
    print(sign)
except UcError as e:
    print(e)

这里出现libc的基地址

image-20221231100141520

这里出现libnative-lib.so的基地址。3)处有报错,但又不知道哪里有错

image-20221231100259956

跟踪报错信息

打印异常地址和地址调用栈,这里打印最后100条即可,用udbg打印

# 执行报错时打印调试信息,跟踪报错与定位发生报错的相关地址
dbg = udbg.UnicornDebugger(emulator.mu,udbg.UDBG_MODE_ALL)


# 开始执行想要执行的jni函数
try:
    # 根据so模块调用指定的函数,后面都是函数的参数
    # 参数1: JNIEnv
    # 参数2: 是java类的对象,这里填0即可
    # 参数3: 是函数输入字符串
    sign = emulator.call_symbol(libnat_mod,'Java_com_sec_udemo_MainActivity_sign_1lv2',
                         emulator.java_vm.jni_env.address_ptr,0,'123')
    print(sign)
except UcError as e:
    # 打印调用栈
    tracks = dbg.get_tracks()
    # 这里打印调用地址即可,打印后100条
    for addr in tracks[-100:-1]:
        print(hex(addr - 0xcbc66000)) # 注意这里要减去基地址
    print(e)

这里有函数参数提示:

image-20221231100710715

这里有出现负地址,负地址就是函数执行到libc.so文件中,这里由于没有初始化的缘故,可能libc中部分函数执行失败,这里我们手动hook,==本质是hook got表地址==

image-20221231101354303

这里我们定位到ida中出错的地址

image-20221231101510570

发现是这个库函数有问题。那就hook它

image-20221231102149719

hook

添加hook 此处必须在导入so文件之前就hook,在导入so文件之前hook才生效,导入之后没法hook

# 根据执行失败的库函数,进行hook,直接本地写函数实现相关库函数功能,首先导入修饰器
@native_method
def __aeabi_memclr(mu,addr,size):
    print('__aeabi_memclr_ptr (addr:%x,size:%d)' % (addr,size))
    
# 添加hook 此处必须在导入so文件之前就hook,因为此hook的本质就是hook got表的内容
# 在导入so文件之前hook才生效,导入之后没法hook
emulator.modules.add_symbol_hook('__aeabi_memclr', emulator.hooker.write_function(__aeabi_memclr) + 1)

其他函数以此类推

关于sprintf函数,第一个参数,是a2,就是buffer地址

image-20221231103838068

全部函数hook后:

image-20221231104133180

这里输出是空因为,我们hook相关函数还未在unicorn中实现,这里我们做个实现即可

@native_method
def __aeabi_memclr(mu,addr,size):
    mu.mem_write(addr,bytes(size))
    print('__aeabi_memclr_ptr (addr:%x,size:%d)' % (addr,size))

@native_method
def __aeabi_memcpy(mu,dist,source,size):
    data = mu.mem_read(source,size)
    mu.mem_write(dist,bytes(data))
    print('__aeabi_memcpy (dist:%x,source:%x)' % (dist,source))

@native_method
def sprintf(mu,buffer,format,arg1,arg2):
    format1 = memory_helpers.read_utf8(mu,format)
    data1 = memory_helpers.read_utf8(mu,arg1)
    res = format1 % (data1,arg2)
    mu.mem_write(buffer,bytes((res+'\x00').encode('utf-8')))
    print('sprintf (%s)' % (format))

这里说明一下bytes()函数放入int型会输出什么:

image-20221231104346170

最终结果:

image-20221231105346413

全部代码:

from unicorn import *
import logging
import sys
from androidemu.java.helpers.native_method import native_method
from UnicornTraceDebugger import udbg
from androidemu.emulator import Emulator
from androidemu.utils import memory_helpers

# 配置logging
logging.basicConfig(
    stream=sys.stdout,
    level=logging.DEBUG,
    format="%(asctime)s %(levelname)7s %(name)34s | %(message)s"
                    )
# 用于打印调试信息
logger = logging.getLogger(__name__)

# 根据执行失败的库函数,进行hook,直接本地写函数实现相关库函数功能,首先导入修饰器
@native_method
def __aeabi_memclr(mu,addr,size):
    mu.mem_write(addr,bytes(size))
    print('__aeabi_memclr_ptr (addr:%x,size:%d)' % (addr,size))

@native_method
def __aeabi_memcpy(mu,dist,source,size):
    data = mu.mem_read(source,size)
    mu.mem_write(dist,bytes(data))
    print('__aeabi_memcpy (dist:%x,source:%x)' % (dist,source))

@native_method
def sprintf(mu,buffer,format,arg1,arg2):
    format1 = memory_helpers.read_utf8(mu,format)
    data1 = memory_helpers.read_utf8(mu,arg1)
    res = format1 % (data1,arg2)
    mu.mem_write(buffer,bytes((res+'\x00').encode('utf-8')))
    print('sprintf (%s)' % (format))
# 创建模拟器
emulator = Emulator()

# 添加hook 此处必须在导入so文件之前就hook,因为此hook的本质就是hook got表的内容
# 在导入so文件之前hook才生效,导入之后没法hook
emulator.modules.add_symbol_hook('__aeabi_memclr', emulator.hooker.write_function(__aeabi_memclr) + 1)
emulator.modules.add_symbol_hook('__aeabi_memcpy', emulator.hooker.write_function(__aeabi_memcpy) + 1)
emulator.modules.add_symbol_hook('sprintf', emulator.hooker.write_function(sprintf) + 1)


# 加载相关so文件
# do_init 参数为false,即不执行初始化函数,因为unicorn不能实现相关初始化操作,
# 这可能会涉及更多库的调用
emulator.load_library('./lib/libc.so',do_init=False)
libnat_mod = emulator.load_library('./lib/libnative-lib.so',do_init=False)


# 执行报错时打印调试信息,跟踪报错与定位发生报错的相关地址
dbg = udbg.UnicornDebugger(emulator.mu,udbg.UDBG_MODE_ALL)


# 开始执行想要执行的jni函数
try:
    # 根据so模块调用指定的函数,后面都是函数的参数
    # 参数1: JNIEnv
    # 参数2: 是java类的对象,这里填0即可
    # 参数3: 是函数输入字符串
    sign = emulator.call_symbol(libnat_mod,'Java_com_sec_udemo_MainActivity_sign_1lv2',
                         emulator.java_vm.jni_env.address_ptr,0,'123')
    print(sign)
except UcError as e:
    # 打印调用栈
    tracks = dbg.get_tracks()
    # 这里打印调用地址即可,打印后100条
    for addr in tracks[-100:-1]:
        print(hex(addr - 0xcbc66000))
    print(e)



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com

×

喜欢就点赞,疼爱就打赏