官网:https://www.unicorn-engine.org/

无名侠大佬代码

[原创] Unicorn 在 Android 的应用 https://bbs.pediy.com/thread-253868.htm

ida快捷键

g jump address

x jump xref

tab 跳转界面

空格 回到流程界面

安装

1
pip install unicorn
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
~ » ipython                                                  127 ↵ tea@teadeMBP
Python 3.8.0 (default, Sep 22 2020, 15:19:24)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.19.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import unicorn

In [2]: from unicorn import *

In [3]: from unicorn.arm_const import *
模拟执行的代码
# mov r0, #0x37;
# sub r1, r2, r3
In [4]: ARM_CODE = b"\x37\x00\xa0\xe3\x03\x10\x42\xe0"
...:
架构的类型和模式
In [5]: mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)
In [6]: mu
Out[6]: <unicorn.unicorn.Uc at 0x10e682a90>

In [7]: ADDRESS = 0x10000
...:
取模运算
In [8]: ADDRESS % 0x1000
Out[8]: 0
传入地址和大小
In [9]: mu.mem_map(ADDRESS, 0x1000)
写入代码
In [12]: mu.mem_write(ADDRESS, ARM_CODE)
给寄存器赋值
In [13]: mu.reg_write(UC_ARM_REG_R0, 0x1234)
...: mu.reg_write(UC_ARM_REG_R2, 0x6789)
...: mu.reg_write(UC_ARM_REG_R3, 0x3333)
添加hook代码
In [14]: def hook_code(uc, address, size, user_data):
...: print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" %(
...: address, size))
...:
添加hook 返回值是hook id
In [15]: mu.hook_add(UC_HOOK_CODE,hook_code,None,ADDRESS,ADDRESS+0x100)
Out[15]: 140477808707440
开机 trac被执行 2条指令被执行
In [16]: mu.emu_start(ADDRESS,ADDRESS+len(ARM_CODE))
>>> Tracing instruction at 0x10000, instruction size = 0x4
>>> Tracing instruction at 0x10004, instruction size = 0x4
查看执行
In [17]: mu.reg_read(UC_ARM_REG_R0)
Out[17]: 55
In [18]: 0x37
Out[18]: 55
In [19]: mu.reg_read(UC_ARM_REG_R1)
Out[19]: 13398

In [20]: hex(13398)
Out[20]: '0x3456'
r1=r2-r3
In [21]: hex(0x6789-0x3333)
Out[21]: '0x3456'

第二课

libnative-lib.so

链接: https://pan.baidu.com/s/1rDkZA1Ma5WdLPpiyVPfXNg 密码: 62cl

开始位置为加密方法开始的位置

结束位置为

查找0x91ac 有个常量R12

修复表找地址

0x1E1AC + 0xc04

完整代码

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
72
73
from unicorn import * #导入包
from unicorn.arm_const import * #导入常量
import binascii

#设置hook
def hook_code(uc,address,size,userdata):
print(">>> Tracing instruction at 0x%x, instruction size = 0x%x" % (address,size))
#内存异常的
def hook_memory(uc, access, address, size,value,userdata):
# 什么地方异常的 访问地址
pc = uc.reg_read(UC_ARM_REG_PC)
print("memory error:pc:%x address:%x size:%x"%(pc,address,size))

a1 = b'123'

#定义一个虚拟机对象 架构 模式 +2就是THUMB指令集
mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
#把so加到进程里
#image
image_base = 0x0
# 大小8m的镜像
image_size = 0x10000 * 8
#分配
mu.mem_map(image_base,image_size)
#载入文件 rb模式载入
binary = open('libnative-lib.so','rb').read()
#镜像写到虚拟机内存里面
mu.mem_write(image_base,binary)

# 运行函数需要个栈 stack
stack_base = 0xa0000
#3m
stack_size = 0x10000 * 3
# 栈的指针是向下增长的 减4个字节 保留添加的位置空余
stack_top = stack_base + stack_size - 0x4
#分配下stackbase
mu.mem_map(stack_base,stack_size)
#设置下寄存器 reg
mu.reg_write(UC_ARM_REG_SP,stack_top)

# data segment
#传参数需要数据段
data_base = 0xf0000
data_size = 0x10000 * 3
mu.mem_map(data_base, data_size)
# data_base是a1的地址
mu.mem_write(data_base, a1)
mu.reg_write(UC_ARM_REG_R0, data_base)

#fix got 修复
mu.mem_write(0x1EDB0, b'\xD9\x98\x00\x00')

# set hook
mu.hook_add(UC_HOOK_CODE,hook_code,0)
mu.hook_add(UC_HOOK_MEM_UNMAPPED,hook_code,0)
#函数开始地址
target = image_base + 0x9B68
#函数while循环 v16地址 循环了16次 就16
target_end = image_base + 0x9C2C

#start
try:
mu.emu_start(target + 1,target_end)
r2 = mu.reg_read(UC_ARM_REG_R2)
result = mu.mem_read(r2,16)

print(binascii.b2a_hex(result))

except UcError as e:
print("tea111")
print(e)
python lesson1.py tea@teadeMacBook-Pro
b'304fc35f0a785d7b2ec27b1745725c38'

第三课

记录签名的结果

载入完整方法

androidemu

https://github.com/Chenyuxin/AndroidNativeEmu

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
#导入头文件
from unicorn import *
from androidemu.emulator import Emulator
from UnicornTraceDebugger import udbg
from androidemu.java.helpers.native_method import native_method
from androidemu.utils import memory_helpers

import logging
import sys
#日志 输出调试信息
logging.basicConfig(stream=sys.stdout,
level=logging.DEBUG,
format="%(asctime)s %(levelname)7s %(name)34s | %(message)s")
logger = logging.getLogger(__name__)
#创建模拟器对象
emulator = Emulator()
#got 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 so中自加密中使用自解密 字符串加密
emulator.load_library('lib/libc.so',do_init=False)
libmod = emulator.load_library('lib/libnative-lib.so',do_init=False)

try:
#调试工具
dbg = udbg.UnicornDebugger(emulator.mu)
#导入函数123会自动转换
s = emulator.call_symbol(libmod, 'Java_com_sec_udemo_MainActivity_sign_1lv2',
emulator.java_vm.jni_env.address_ptr, 0, "123")
print(s)

except UcError as e:
#打印执行流
list_tracks = dbg.get_tracks()
#打印最后的100项
for addr in list_tracks[-100:-1]:
#实际地址减去模块的加载地址
print(hex(addr - 0xcbc66000))
print (e)

找到lib/libnative-lib.so

0x9260

最后一个正数

emulator.modules.add_symbol_hook(‘__aeabi_memclr’, emulator.hooker.write_function(__aeabi_memclr) + 1)

加上这句代码

def __aeabi_memclr(mu,addr,size):

print(‘__aeabi_memclr(%x,%d)’ % (addr, size))

mu.mem_write(addr, bytes(size))

导入库

from androidemu.java.helpers.native_method import native_method

函数加载有问题 就要hook库函数自己实现

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)

使用课程带的androidemu后就运行可以了

完整代码

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
#导入头文件
from unicorn import *
from androidemu.emulator import Emulator
from UnicornTraceDebugger import udbg
from androidemu.java.helpers.native_method import native_method
from androidemu.utils import memory_helpers

import logging
import sys

#日志 输出调试信息
logging.basicConfig(stream=sys.stdout,
level=logging.DEBUG,
format="%(asctime)s %(levelname)7s %(name)34s | %(message)s")
logger = logging.getLogger(__name__)

#hook3个库函数 hook需要载入so之前hook 载入之后就不行了就没有效果了 已经填充过了
@native_method
def __aeabi_memclr(mu,addr,size):
print('__aeabi_memclr(%x,%d)' % (addr, size))
mu.mem_write(addr, bytes(size))


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

#format1是指针不能读 要读出来
@native_method
def sprintf(mu, buffer, format1, a1, a2):
format1 = memory_helpers.read_utf8(mu, format1)
result = format1 % (memory_helpers.read_utf8(mu, a1), a2)
#手动转换string
mu.mem_write(buffer, bytes((result + '\x00').encode('utf-8')))


#创建模拟器对象
emulator = Emulator()
#got 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 so中自加密中使用自解密 字符串加密
emulator.load_library('lib/libc.so',do_init=False)
libmod = emulator.load_library('lib/libnative-lib.so',do_init=False)

try:
#调试工具 附加了调试器
dbg = udbg.UnicornDebugger(emulator.mu)
#导入函数123会自动转换
s = emulator.call_symbol(libmod, 'Java_com_sec_udemo_MainActivity_sign_1lv2',
emulator.java_vm.jni_env.address_ptr, 0, "123")
print(s)

except UcError as e:
#打印执行流
list_tracks = dbg.get_tracks()
#打印最后的100项
for addr in list_tracks[-100:-1]:
#实际地址减去模块的加载地址
print(hex(addr - 0xcbc66000))
print (e)