unicorn start
模拟器框架自动填充got表项内容
目标
hook JNI函数
hook JNI函数的条件:
框架:
打印调试信息
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的基地址
这里出现libnative-lib.so的基地址。3)处有报错,但又不知道哪里有错
跟踪报错信息
打印异常地址和地址调用栈,这里打印最后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)
这里有函数参数提示:
这里有出现负地址,负地址就是函数执行到libc.so文件中,这里由于没有初始化的缘故,可能libc中部分函数执行失败,这里我们手动hook,==本质是hook got表地址==
这里我们定位到ida中出错的地址
发现是这个库函数有问题。那就hook它
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地址
全部函数hook后:
这里输出是空因为,我们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型会输出什么:
最终结果:
全部代码:
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