一
本文目的
本文尽可能从初学者的视角来阐述,初学者可以自行实践。
二
分析目标
三
选择模拟执行的原因
静态分析成本过高
用调试器调试
现在想来,可能用Android Studio写个app,然后用lldb来调试可能是最稳定的调试方式。大家有什么其他好的意见呢?
模拟执行
从官方文档了解qiling的大致使用方法后,发现qiling有自带的qdb调试器,不过不支持多线程,而Bet4开源的udbserver支持多线程,配合pwndbg效果图如下(图来自Bet4的帖子):
四
ARM64 demo运行
初始化qiling环境
在我的电脑上软连接无法生效,可能qiling只测试了Linux,所以这里直接用IDA加载脚本的方式来加载qilingida.py。
Qiling->
),加载IDA插件,这里使用qiling默认的辅助脚本(custom_script.py)。在IDA中的一些自动化逻辑都可以放到该脚本中,比如添加一些syscall或者针对某地址的hook到该辅助脚本,之后会举例脚本的用法。File "C:Program FilesPython39libsite-packagesqilingoslinuxlinux.py", line 30, in __init__
super(QlOsLinux, self).__init__(ql)
File "C:Program FilesPython39libsite-packagesqilingosposixposix.py", line 190, in __init__
super().__init__(ql)
File "C:Program FilesPython39libsite-packagesqilingosos.py", line 63, in __init__
sys.stdin.fileno()
AttributeError: 'NoneType' object has no attribute 'fileno'
try:
# Qiling may be used on interactive shells (ex: IDLE) or embedded python
# interpreters (ex: IDA Python). such environments use their own version
# for the standard streams which usually do not support certain operations,
# such as fileno(). here we use this to determine how we are going to use
# the environment standard streams
sys.stdin.fileno()
except UnsupportedOperation:
# Qiling is used on an interactive shell or embedded python interpreter.
# if the internal stream buffer is accessible, we should use it
self._stdin = getattr(sys.stdin, 'buffer', sys.stdin)
self._stdout = getattr(sys.stdout, 'buffer', sys.stdout)
self._stderr = getattr(sys.stderr, 'buffer', sys.stderr)
except (UnsupportedOperation, AttributeError):
这里修复的os.py文件是python库下的qiling,也就是C:Program FilesPython39libsite-packagesqilingosos.py。
File "E:gitRepoqilingexamplesextensionsidaplugincustom_script.py", line 1, in <module>
from future import __annotations__
ImportError: cannot import name '__annotations__' from 'future' (C:Program FilesPython39libsite-packagesfuture__init__.py)
[INFO][(unknown file):0] Custom user script not found.
from __future__ import annotations
开始模拟执行
阅读qilingoslinux.py的QlOsLinux::run方法,由于当前是单线程环境,且没有显式指定运行的起始地址,因此程序的起始地址是动态链接器的入口点,待动态链接器初始化后,将跳转到程序的真正入口点: try:
# 如果是一段二进制代码
if self.ql.code:
self.ql.emu_start(self.entry_point, (self.entry_point + len(self.ql.code)), self.ql.timeout, self.ql.count)
else:
# 如果是多线程环境
if self.ql.multithread:
# start multithreading
thread_management = thread.QlLinuxThreadManagement(self.ql)
self.ql.os.thread_management = thread_management
thread_management.run()
else:
# 不是多线程环境
# 程序入口点是否有显式指定
if self.ql.entry_point is not None:
self.ql.loader.elf_entry = self.ql.entry_point
# do we have an interp?
elif self.ql.loader.elf_entry != self.ql.loader.entry_point:
entry_address = self.ql.loader.elf_entry
if self.ql.arch.type == QL_ARCH.ARM:
entry_address &= ~1
# start running interp, but stop when elf entry point is reached
self.ql.emu_start(self.ql.loader.entry_point, entry_address, self.ql.timeout)
self.ql.do_lib_patch()
self.run_function_after_load()
self.ql.loader.skip_exit_check = False
self.ql.write_exit_trap()
self.ql.emu_start(self.ql.loader.elf_entry, self.exit_point, self.ql.timeout, self.ql.count)
[+] brk: increasing program break from 0x555555568000 to 0x555555589000
[+] 0x00007fffb7e9e93c: brk(inp = 0x555555589000) = 0x555555589000
[+] Received interrupt: 0x2
[+] write() CONTENT: b'Hello, World!n'
[x] Syscall ERROR: ql_syscall_write DEBUG: A string expected
Traceback (most recent call last):
...
File "C:Program FilesPython39libsite-packagesqilingosposixsyscallunistd.py", line 410, in ql_syscall_write
f.write(data)
File "D:ToolsIDA 7.5python3init.py", line 63, in write
ida_kernwin.msg(text)
File "D:ToolsIDA 7.5python3ida_kernwin.py", line 236, in msg
return _ida_kernwin.msg(*args)
TypeError: A string expected
def my_syscall_write(ql: Qiling, fd: int, buf: int, count: int):
ql.log.info('my_syscall_write called')
try:
# read data from emulated memory
data = ql.mem.read(buf, count)
# select the emulated file object that corresponds to the requested
# file descriptor
fobj = ql.os.fd[fd]
if fobj == None:
ql.log.ingo('none file descriptor')
# write the data into the file object, if it supports write operations
elif hasattr(fobj, 'write'):
fobj.write(data.decode('utf-8'))
except:
ret = -1
else:
ret = count
ql.log.info(f'my_syscall_write({fd}, {buf:#x}, {count}) = {ret}')
return ret
class QILING_IDA:
def _show_context(self, ql: Qiling):
registers = tuple(ql.arch.regs.register_mapping.keys())
grouping = 4
for idx in range(0, len(registers), grouping):
ql.log.info('t'.join(f'{r:5s}: {ql.arch.regs.read(r):016x}' for r in registers[idx:idx + grouping]))
def custom_prepare(self, ql: Qiling) -> None:
ql.log.info('Context before starting emulation:')
ql.os.set_syscall('write', my_syscall_write)
ql.log.info('my_syscall_write registered')
self._show_context(ql)
Restart
,重新开始运行,结果正常,打印出了“Hello, World!”。[+] Received interrupt: 0x2
[=] my_syscall_write called
Hello, World!
[=] my_syscall_write(1, 0x555555568260, 14) = 14
[+] 0x00007fffb7e98afc: my_syscall_write(fd = 0x1, buf = 0x555555568260, count = 0xe) = 0xe
[+] Received interrupt: 0x2
[+] 0x00007fffb7e78a2c: exit_group(code = 0x0) = ?
通过显式设置PC寄存器指定程序执行入口
[INFO][(unknown file):0] QIling PC set to 0x724
[x] CPU Context:
[x] x0 : 0x555555554724
[x] x1 : 0x1
[x] x2 : 0x80000000de18
...
[x] PC = 0x0000000000000724 (unreachable)
...
File "C:Program FilesPython39libsite-packagesqilingcore.py", line 771, in emu_start
self.uc.emu_start(begin, end, timeout, count)
File "C:Program FilesPython39libsite-packagesunicornunicorn.py", line 547, in emu_start
raise UcError(status)
unicorn.unicorn.UcError: Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)
def ql_set_pc(self):
if self.qlinit:
# ea = IDA.get_current_address()
ea = self.qlemu.ql_addr_from_ida(IDA.get_current_address())
self.qlemu.ql.arch.regs.arch_pc = ea
logging.info(f"QIling PC set to {hex(ea)}")
self.qlemu.status = self.qlemu.ql.save()
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
else:
logging.error('Qiling should be setup firstly.')
[INFO][(unknown file):0] QIling PC set to 0x555555554724
小节
五
libxx.so的模拟执行(排错)
加载libxx.so、初始化qiling后,不能直接将PC寄存器指向tps_init函数,因为这时运行环境还没有准备好,比如tpidr_el0寄存器,这个寄存器类似与windows的fs段寄存器(TEB),指向当前线程的运行环境。另外,此时还没有做so的重定位,很多so里的全局引用是需要重定位的,比如调用一个memset函数。
[x] Syscall ERROR: ql_syscall_futex DEBUG: 'NoneType' object has no attribute 'cur_thread'
Traceback (most recent call last):
File "C:Program FilesPython39libsite-packagesqilingosposixposix.py", line 374, in load_syscall
retval = syscall_hook(self.ql, *params)
File "C:Program FilesPython39libsite-packagesqilingosposixsyscallfutex.py", line 43, in ql_syscall_futex
regreturn = ql.os.futexm.futex_wake(ql, uaddr,ql.os.thread_management.cur_thread, val)
AttributeError: 'NoneType' object has no attribute 'cur_thread'
elif op & (FUTEX_PRIVATE_FLAG - 1) == FUTEX_WAKE:
regreturn = ql.os.futexm.futex_wake(ql, uaddr,ql.os.thread_management.cur_thread, val)
class QlEmuQiling:
def __init__(self):
self.path = None
self.rootfs = None
self.ql: Qiling = None
self.status = None
self.exit_addr = None
self.baseaddr = None
self.env = {}
def start(self, *args, **kwargs):
self.ql = Qiling(argv=self.path, rootfs=self.rootfs, verbose=QL_VERBOSE.DEFAULT, multithread=True, env=self.env, log_plain=True, *args, **kwargs)
# ...
[x] [Thread 2000] Syscall ERROR: ql_syscall_writev DEBUG: A string expected
Traceback (most recent call last):
File "C:Program FilesPython39libsite-packagesqilingosposixposix.py", line 374, in load_syscall
retval = syscall_hook(self.ql, *params)
File "C:Program FilesPython39libsite-packagesqilingosposixsyscalluio.py", line 23, in ql_syscall_writev
ql.os.fd[fd].write(buf)
File "D:ToolsIDA 7.5python3init.py", line 63, in write
ida_kernwin.msg(text)
File "D:ToolsIDA 7.5python3ida_kernwin.py", line 236, in msg
return _ida_kernwin.msg(*args)
TypeError: A string expected
[x] [Thread 2000] PC = 0x000000000009aec0 (unreachable)
[x] [Thread 2000] Memory map:
[x] [Thread 2000] Start End Perm Label Image
[x] [Thread 2000] 00555555554000 - 00555555847000 r-x libxx.so E:gitRepoqilingexamplesrootfsarm64_linuxbinlibxx.so
[x] [Thread 2000] 00555555856000 - 0055555588f000 rw- libxx.so E:gitRepoqilingexamplesrootfsarm64_linuxbinlibxx.so
[x] [Thread 2000] 0055555588f000 - 00555555891000 rwx [hook_mem]
[x] [Thread 2000] 007ffffffde000 - 0080000000e000 rwx [stack]
Traceback (most recent call last):
...
File "C:Program FilesPython39libsite-packagesunicornunicorn.py", line 547, in emu_start
raise UcError(status)
unicorn.unicorn.UcError: Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)
$ readelf -S libxx.so | grep interp
---
$ readelf -S arm64_hello | grep interp
[ 1] .interp PROGBITS 0000000000000200 00000200
def start(self, *args, **kwargs):
self.ql = Qiling(argv=self.path, rootfs=self.rootfs, verbose=QL_VERBOSE.DEBUG, multithread=True, env=self.env, log_plain=True, *args, **kwargs)
[INFO][qilingida:1034] Custom env: {}
[+] Profile: default
[+] Mapped 0x555555554000-0x555555555000
[+] Mapped 0x555555564000-0x555555566000
[+] mem_start : 0x555555554000
[+] mem_end : 0x555555566000
[+] Interpreter path: /lib/ld-linux-aarch64.so.1
[+] Interpreter addr: 0x7ffff7dd5000
[+] Mapped 0x7ffff7dd5000-0x7ffff7df2000
[+] Mapped 0x7ffff7e01000-0x7ffff7e04000
[+] mmap_address is : 0x7fffb7dd6000
def load_with_ld()
def load_elf_segments()
# ...
# determine interpreter path
interp_seg = next(elffile.iter_segments(type='PT_INTERP'), None)
interp_path = str(interp_seg.get_interp_name()) if interp_seg else ''
if len(interp_path) == 0:
interp_path = "/lib/ld-linux-aarch64.so.1"
[INFO][qilingida:1034] Custom env: {}
[+] Profile: default
[+] Mapped 0x555555554000-0x555555847000
[+] Mapped 0x555555856000-0x55555588f000
[+] mem_start : 0x555555554000
[+] mem_end : 0x55555588f000
[+] Interpreter path: /lib/ld-linux-aarch64.so.1
[+] Interpreter addr: 0x7ffff7dd5000
[+] Mapped 0x7ffff7dd5000-0x7ffff7df2000
[+] Mapped 0x7ffff7e01000-0x7ffff7e04000
[+] [Thread 2000] b'Inconsistency detected by ld.so: '
Inconsistency detected by ld.so: [+] [Thread 2000] b'rtld.c'
rtld.c[+] [Thread 2000] b': '
: [+] [Thread 2000] b'1266'
1266[+] [Thread 2000] b': '
: [+] [Thread 2000] b'dl_main'
dl_main[+] [Thread 2000] b': '
: [+] [Thread 2000] b'Assertion `'
Assertion `[+] [Thread 2000] b'GL(dl_rtld_map).l_libname'
GL(dl_rtld_map).l_libname[+] [Thread 2000] b"' failed!n"
' failed!
...
File "C:Program FilesPython39libsite-packagesqilingoslinuxlinux.py", line 167, in run
thread_management.run()
File "C:Program FilesPython39libsite-packagesqilingoslinuxthread.py", line 613, in run
previous_thread = self._prepare_lib_patch()
File "C:Program FilesPython39libsite-packagesqilingoslinuxthread.py", line 593, in _prepare_lib_patch
raise QlErrorExecutionStop('Dynamic library .init() failed!')
qiling.exception.QlErrorExecutionStop: Dynamic library .init() failed!
$ readelf -d libxx.so
Dynamic section at offset 0x311f88 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [liblog.so]
0x0000000000000001 (NEEDED) Shared library: [libz.so]
0x0000000000000001 (NEEDED) Shared library: [libm.so]
0x0000000000000001 (NEEDED) Shared library: [libdl.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so]
0x000000000000000e (SONAME) Library soname: [libtps.so]
0x0000000000000019 (INIT_ARRAY) 0x302dc0
0x000000000000001b (INIT_ARRAYSZ) 8 (bytes)
0x000000000000001a (FINI_ARRAY) 0x302dc8
...
E:gitRepoqilingexamplesrootfsarm64_linuxsystemlib64:保存libm.so等库
E:gitRepoqilingexamplesrootfsarm64_linuxlib:保存linker64
[x] [Thread 2000] Disassembly:
[=] [Thread 2000] 00007ffff7ddfbd0 [linker64 + 0x00abd0] 9c 20 40 79 ldrh w28, [x4, #0x10]
[=] [Thread 2000] 00007ffff7ddfbd4 [linker64 + 0x00abd4] 9f 0f 00 71 cmp w28, #3
[=] [Thread 2000] 00007ffff7ddfbd8 [linker64 + 0x00abd8] 81 34 00 54 b.ne #0x7ffff7de0268
...
File "C:Program FilesPython39libsite-packagesqilingcore.py", line 771, in emu_start
self.uc.emu_start(begin, end, timeout, count)
File "C:Program FilesPython39libsite-packagesunicornunicorn.py", line 547, in emu_start
raise UcError(status)
unicorn.unicorn.UcError: Invalid memory read (UC_ERR_READ_UNMAPPED)
[+] [Thread 2001] b'libc'
libc[+] [Thread 2001] b': '
: [+] [Thread 2001] b'unable to stat "/proc/self/exe": Operation not permitted'
unable to stat "/proc/self/exe": Operation not permitted[+] [Thread 2001] b'n'
def custom_continue(self, ql: Qiling) -> List[HookRet]:
# ...
def addr_27DA8_hook(ql: Qiling) -> None:
ql.arch.regs.W0 = 0
ql.arch.regs.PC += 4
# ...
return [ql.hook_address(addr_27DA8_hook, 0x27DA8+linker_baseaddr)]
[+] [Thread 2000] b'libc'
libc[+] [Thread 2000] b': '
: [+] [Thread 2000] b'Could not find a PHDR: broken executable?'
Could not find a PHDR: broken executable?[+] [Thread 2000] b'n'
$ readelf -l libxx.so
Elf file type is DYN (Shared object file)
Entry point 0xa5370
There are 8 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000002f25b4 0x00000000002f25b4 R E 0x10000
LOAD 0x00000000002f2dc0 0x0000000000302dc0 0x0000000000302dc0
0x0000000000034478 0x0000000000037d58 RW 0x10000
DYNAMIC 0x0000000000311f88 0x0000000000321f88 0x0000000000321f88
0x0000000000000210 0x0000000000000210 RW 0x8
NOTE 0x0000000000000200 0x0000000000000200 0x0000000000000200
0x0000000000000024 0x0000000000000024 R 0x4
NOTE 0x00000000002f251c 0x00000000002f251c 0x00000000002f251c
0x0000000000000098 0x0000000000000098 R 0x4
GNU_EH_FRAME 0x00000000002da5dc 0x00000000002da5dc 0x00000000002da5dc
0x000000000000355c 0x000000000000355c R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x00000000002f2dc0 0x0000000000302dc0 0x0000000000302dc0
0x0000000000025240 0x0000000000025240 R 0x1
图的注释是分析后的结果。
def ql_continue(self):
logging.info("before continue...")
if self.qlinit:
userhook = None
# 调用hook_code,每条指令执行前调用ql_path_hook
pathhook = self.qlemu.ql.hook_code(self.ql_path_hook)
def ql_path_hook(self, ql, addr, size):
addr = addr - self.qlemu.baseaddr + get_imagebase()
set_color(addr, CIC_ITEM, 0x007FFFAA)
# 获取断点数量
bp_count = get_bpt_qty()
bp_list = []
if bp_count > 0:
for num in range(0, bp_count):
bp_list.append(get_bpt_ea(num))
# 如果当前准备执行的指令是断点处的指令,调用ql.save和ql.os.stop()保存当前模拟执行环境
if addr in bp_list and (addr != self.lastaddr or self.is_change_addr>1):
self.qlemu.status = ql.save()
ql.os.stop()
self.lastaddr = addr
self.is_change_addr = -1
jumpto(addr)
self.is_change_addr += 1
def ql_path_hook(self, ql, addr, size):
addr = addr - self.qlemu.baseaddr + get_imagebase()
set_color(addr, CIC_ITEM, 0x007FFFAA)
bp_count = get_bpt_qty() + 1
bp_list = []
bp_list.append(0x27f2c+0x007ffff7dd5000-0x555555554000)
if bp_count > 0:
for num in range(0, bp_count):
bp_list.append(get_bpt_ea(num))
◆因为第二行addr变量重定位时减去的libxx模块的基址,所以bp_list添加的元素要减去libxx模块的基址,而不是0x27f2c+0x007ffff7dd5000-0x007ffff7dd5000=0x27f2c。 ◆因为中断后,PC寄存器指向linker64模块,所以会报以下错误,不过该错误不影响继续单步调试:
i = 0;
while (program_table_element[i].p_type!=PT_PHDR) {
i++;
if (i >= elf_header.e_phnum)
break;
}
if (i == elf_header.e_phnum) {
print("no phdr");
}
else {
// 获取文件基址在内存的地址
a = phdr_addr - program_table_element[i].p_paddr;
// 获取内存基址
b = phdr_addr - program_table_elemet[i].p_offset;
}
def custom_continue(self, ql: Qiling) -> List[HookRet]:
#...
def addr_27FB4_hook(ql: Qiling) -> None:
# 如果是检测第一个程序表元素,且不是程序头表
if ql.arch.regs.X11 == 0 and ql.arch.regs.W12 == 1:
phdr_vir_addr = ql.arch.regs.X8
poffset_addr = ql.arch.regs.x10
write_base = ql.arch.regs.X19
ql.log.info(f'phdr_vir_addr: {phdr_vir_addr:#x}')
ql.log.info(f'poffset_addr: {poffset_addr:#x}')
sub1 = to_bstring(phdr_vir_addr-0x40)
# *(QWORD*)((__int64)v50+0x100) = image_vir_addr
ql.mem.write(write_base+0x100, sub1)
ql.log.info(b'sub1: ' + sub1)
image_vir_addr = phdr_vir_addr-0x40
# *(QWORD*)((__int64)v50+0x10) = image_vir_addr
ql.mem.write(write_base+0x10, to_bstring(0x40))
ql.log.info(f'sub2: {image_vir_addr:#x}')
ql.arch.regs.X8 = image_vir_addr
ql.arch.regs.PC = 0x28070 + 0x7ffff7dd5000
ql.hook_address(addr_27FB4_hook, 0x27FB4+linker_baseaddr)
记得把之前在qilingida.py添加的断点去掉。
[x] [Thread 2000] v28 : 0x0
[x] [Thread 2000] v29 : 0x0
[x] [Thread 2000] v30 : 0x0
[x] [Thread 2000] v31 : 0x0
[x] [Thread 2000] PC = 0x0000000000000000 (unreachable)
[x] [Thread 2000] Memory map:
[x] [Thread 2000] Start End Perm Label Image
[x] [Thread 2000] 00555555554000 - 00555555847000 r-x libxx.so E:gitRepoqilingexamplesrootfsarm64_linuxbinlibxx.so
[x] [Thread 2000] 00555555856000 - 0055555587c000 r-- libxx.so E:gitRepoqilingexamplesrootfsarm64_linuxbinlibxx.so
[x] [Thread 2000] 0055555587c000 - 0055555588f000 rw- libxx.so E:gitRepoqilingexamplesrootfsarm64_linuxbinlibxx.so
[x] [Thread 2000] 0055555588f000 - 00555555891000 rwx [hook_mem]
[x] [Thread 2000] 007fffb7dd6000 - 007fffb7dd7000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb7dd7000 - 007fffb7dda000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7dda000 - 007fffb7ddb000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb7ddb000 - 007fffb7ddc000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb7ddc000 - 007fffb7de0000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7de0000 - 007fffb7de1000 r-- [mmap anonymous]
[x] [Thread 2000] 007fffb7de1000 - 007fffb7de2000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7de2000 - 007fffb7de3000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7de3000 - 007fffb7de4000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7de5000 - 007fffb7de6000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7de6000 - 007fffb7de7000 r-- [mmap anonymous]
[x] [Thread 2000] 007fffb7de7000 - 007fffb7de8000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb7de8000 - 007fffb7de9000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7de9000 - 007fffb7dea000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb7dea000 - 007fffb7deb000 r-- [mmap anonymous]
[x] [Thread 2000] 007fffb7deb000 - 007fffb7dec000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7dec000 - 007fffb7ded000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7ded000 - 007fffb7dee000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7dee000 - 007fffb7def000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7def000 - 007fffb7df0000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7df0000 - 007fffb7df1000 r-- [mmap anonymous]
[x] [Thread 2000] 007fffb7df1000 - 007fffb7df2000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7e31000 - 007fffb7e32000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7e32000 - 007fffb7e33000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7e4b000 - 007fffb7f0e000 r-x [mmap] libc.so
[x] [Thread 2000] 007fffb7f0e000 - 007fffb7f1d000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb7f1d000 - 007fffb7f23000 r-- [mmap] libc.so
[x] [Thread 2000] 007fffb7f23000 - 007fffb7f26000 rw- [mmap] libc.so
[x] [Thread 2000] 007fffb7f26000 - 007fffb7f34000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb7f5d000 - 007fffb7f5e000 r-x [mmap] libdl.so
[x] [Thread 2000] 007fffb7f5e000 - 007fffb7f6d000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb7f6d000 - 007fffb7f6e000 r-- [mmap] libdl.so
[x] [Thread 2000] 007fffb7f6e000 - 007fffb7f6f000 rw- [mmap] libdl.so
[x] [Thread 2000] 007fffb7f81000 - 007fffb8058000 r-x [mmap] libc++.so
[x] [Thread 2000] 007fffb8058000 - 007fffb8067000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb8067000 - 007fffb806e000 r-- [mmap] libc++.so
[x] [Thread 2000] 007fffb806e000 - 007fffb806f000 rw- [mmap] libc++.so
[x] [Thread 2000] 007fffb806f000 - 007fffb8072000 rw- [mmap anonymous]
[x] [Thread 2000] 007fffb80b5000 - 007fffb80ed000 r-x [mmap] libm.so
[x] [Thread 2000] 007fffb80ed000 - 007fffb80fd000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb80fd000 - 007fffb80fe000 r-- [mmap] libm.so
[x] [Thread 2000] 007fffb80fe000 - 007fffb80ff000 rw- [mmap] libm.so
[x] [Thread 2000] 007fffb8106000 - 007fffb8122000 r-x [mmap] libz.so
[x] [Thread 2000] 007fffb8122000 - 007fffb8131000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb8131000 - 007fffb8132000 r-- [mmap] libz.so
[x] [Thread 2000] 007fffb8132000 - 007fffb8133000 rw- [mmap] libz.so
[x] [Thread 2000] 007fffb8160000 - 007fffb8165000 r-x [mmap] liblog.so
[x] [Thread 2000] 007fffb8165000 - 007fffb8174000 --- [mmap anonymous]
[x] [Thread 2000] 007fffb8174000 - 007fffb8175000 r-- [mmap] liblog.so
[x] [Thread 2000] 007fffb8175000 - 007fffb8176000 rw- [mmap] liblog.so
...
File "C:Program FilesPython39libsite-packagesqilingcore.py", line 771, in emu_start
self.uc.emu_start(begin, end, timeout, count)
File "C:Program FilesPython39libsite-packagesunicornunicorn.py", line 547, in emu_start
raise UcError(status)
unicorn.unicorn.UcError: Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)
def ql_run_to_here(self):
if self.qlinit:
curr_addr = get_screen_ea()
# 每执行一条命令前,调用ql_until_hook
untillhook = self.qlemu.ql.hook_code(self.ql_untill_hook)
if self.qlemu.status is not None:
self.qlemu.ql.restore(self.qlemu.status)
show_wait_box("Qiling is processing ...")
try:
self.qlemu.run(begin=self.qlemu.ql.arch.regs.arch_pc, end=curr_addr+self.qlemu.baseaddr-get_imagebase())
finally:
hide_wait_box()
else:
show_wait_box("Qiling is processing ...")
try:
self.qlemu.run(end=curr_addr+self.qlemu.baseaddr-get_imagebase())
finally:
hide_wait_box()
def ql_untill_hook(self, ql, addr, size):
addr = addr - self.qlemu.baseaddr + get_imagebase()
set_color(addr, CIC_ITEM, 0x00B3CBFF)
def ql_run_to_here(self):
if self.qlinit:
curr_addr = get_screen_ea()
userhook = None
# 注册新创建的custom_run_to_here方法
if self.userobj is not None:
userhook = self.userobj.custom_run_to_here(self.qlemu.ql)
# 去掉ql_until_hook的回调
# untillhook = self.qlemu.ql.hook_code(self.ql_untill_hook)
if self.qlemu.status is not None:
self.qlemu.ql.restore(self.qlemu.status)
show_wait_box("Qiling is processing ...")
try:
self.qlemu.run(begin=self.qlemu.ql.arch.regs.arch_pc, end=curr_addr+self.qlemu.baseaddr-get_imagebase())
finally:
hide_wait_box()
else:
show_wait_box("Qiling is processing ...")
try:
self.qlemu.run(end=curr_addr+self.qlemu.baseaddr-get_imagebase())
finally:
hide_wait_box()
set_color(curr_addr, CIC_ITEM, 0x00B3CBFF)
# 回调删除部分也注视掉
# self.qlemu.ql.hook_del(untillhook)
if userhook and userhook is not None:
for hook in userhook:
self.qlemu.ql.hook_del(hook)
self.qlemu.status = self.qlemu.ql.save()
self.ql_update_views(self.qlemu.ql.arch.regs.arch_pc, self.qlemu.ql)
else:
logging.error('Qiling should be setup firstly.')
def custom_run_to_here(self, ql: Qiling) -> List[HookRet]:
linker_baseaddr = 0x007ffff7dd5000
def addr_27DA8_hook(ql: Qiling) -> None:
# content from custom_continue
def addr_27FB4_hook(ql: Qiling) -> None:
# content from custom_continue
return [ql.hook_address(addr_27DA8_hook, 0x27DA8+linker_baseaddr),
ql.hook_address(addr_27FB4_hook, 0x27FB4+linker_baseaddr)]
[=] [Thread 2000] custom_step hook
[=] [Thread 2001] Executing: 0x7ffff7e0373c
[x] [Thread 2001] [Thread 2001] Expect 0x5555555f9370 but get 0x7ffff7e03740 when running loader.
File "C:Program FilesPython39libsite-packagesqilingoslinuxthread.py", line 611, in run
previous_thread = self._prepare_lib_patch()
File "C:Program FilesPython39libsite-packagesqilingoslinuxthread.py", line 590, in _prepare_lib_patch
raise QlErrorExecutionStop('Dynamic library .init() failed!')
qiling.exception.QlErrorExecutionStop: Dynamic library .init() failed!
# qilingida.py
# 单步执行的入口是qlemu.run
def ql_step(self):
self.qlemu.run(begin=self.qlemu.ql.arch.regs.arch_pc, end=self.qlemu.exit_addr)
# qiling/os/linux/linux.py
class QlOsLinux(QlOsPosix):
def run(self):
if self.ql.multithread:
# start multithreading
# 多线程环境下调用thread_management.run()
thread_management = thread.QlLinuxThreadManagement(self.ql)
self.ql.os.thread_management = thread_management
thread_management.run()
else:
# 单线程环境
if self.ql.entry_point is not None:
self.ql.loader.elf_entry = self.ql.entry_point
# do we have an interp?
# 如果有动态链接器,就用动态链接器初始化目标so
elif self.ql.loader.elf_entry != self.ql.loader.entry_point:
entry_address = self.ql.loader.elf_entry
if self.ql.arch.type == QL_ARCH.ARM:
entry_address &= ~1
# start running interp, but stop when elf entry point is reached
self.ql.emu_start(self.ql.loader.entry_point, entry_address, self.ql.timeout)
# 开始模拟执行目标so
self.ql.emu_start(self.ql.loader.elf_entry, self.exit_point, self.ql.timeout, self.ql.count)
# qiling/os/linux/thread.py
class QlLinuxThreadManagement:
def run(self):
# 调用_prepare_lib_patch,让动态链接器加载目标libxx.so
previous_thread = self._prepare_lib_patch()
def _prepare_lib_patch(self):
# 如果有动态链接器,就用动态链接器初始化目标libxx.so
if self.ql.loader.elf_entry != self.ql.loader.entry_point:
entry_address = self.ql.loader.elf_entry
if self.ql.arch.type == QL_ARCH.ARM:
entry_address &= ~1
self.main_thread = self.ql.os.thread_class.spawn(self.ql, self.ql.loader.entry_point, entry_address)
self.cur_thread = self.main_thread
self._clear_queued_msg()
gevent.joinall([self.main_thread], raise_error=True)
if self.ql.arch.regs.arch_pc != entry_address:
self.ql.log.error(f"{self.cur_thread} Expect {hex(self.ql.loader.elf_entry)} but get {hex(self.ql.arch.regs.arch_pc)} when running loader.")
raise QlErrorExecutionStop('Dynamic library .init() failed!')
self.ql.do_lib_patch()
self.ql.os.run_function_after_load()
self.ql.loader.skip_exit_check = False
self.ql.write_exit_trap()
return self.main_thread
return None
# qiling/os/linux/thread.py
def _prepare_lib_patch(self):
if self.ql.entry_point is not None:
self.ql.loader.elf_entry = self.ql.entry_point
return None
elif self.ql.loader.elf_entry != self.ql.loader.entry_point:
entry_address = self.ql.loader.elf_entry
[x] [Thread 2000] 007fffb8085000 - 007fffb8148000 r-x [mmap] libc.so
六
虚拟机框架解析
虚拟机的简要流程
这里IDA反编译出现了一点问题,传给switch的不是v39
-
改变控制流,做指令跳转
-
一种是a1数组自增到下一个元素
-
一种是直接跳转到a1数组的某个元素
-
调用回调函数
虚拟机结构剖析
handler index:0x2B
subhandler index:0xC2B
handler:[vm_stack-off3] = [vm_stack-off1] + [vm_stack-off2]
example:
[=] [Thread 2008] index: 0xc2b -> [func_data-0x110] = [func_data-0x80] + [func_data-0x128]; 0xffffffffff971488, 0x555555cd9eb0, rs: 0x55555564b338
------------------------------------------------
handler index:0x2B
subhandler index:0xA6B
handler:[vm_stack-off3] = [vm_stack-off1] >> operand2
example:
[=] [Thread 2008] index: 0xa6b -> [func_data-0x128] = d[func_data-0x88] >> 0x18; 0x555f9370, rs: 0x55
------------------------------------------------
handler index:0x9
no subhandler index
handler:[vm_stack-off1] = [vm_stack-off2] + operand1
example:
[=] [Thread 2008] index: 0x9 -> [func_data-0x120] = [func_data-0x130] + 0x4; 0x0, rs: 0x4
看第一个例子,index为0x2B,则进一步判断subindex,执行subindex为0xC2B的操作,这里有3个变量,off1、off2、off3,分别从handler中做位运算提取。
◆控制流的变化
加解密的执行就是遍历handler数组实现的,一个handler一个handler的执行。对于for循环,虚拟机需要改变控制流来实现,以下给出控制流改变的一个例子:
[=] [Thread 2008] index: 0x34 -> [func_data-0x130]; 0x0
[func_data-0x128]; 0x1
[func_data-0x18] = index_table_pointer+4+((int)(0xfffc << 0x10) >> 0xE); 0x5555557e5bd0
[func_data-0x20] = 2
[=] [Thread 2008] index: 0x9 -> [func_data-0x120] = [func_data-0x120] + 0x1; func_data-0x234, rs: func_data-0x233
index_table_pointer = [func_data-0x18]; 0x5555557e5bc4
[func_data-0x20] = 0
[beg] = index_table_pointer
执行0x34 handler,如果*(DWORD*)(func_data-0x130)!= *(DWORD*)(func_data-0x128),handler数组指针就减0xC,并且func_data-0x20地址处写入2,表示下一个handler执行后即将跳转:
index_table_pointer+4+((int)(0xfffc << 0x10) >> 0xE) = index_table_pointer-0xC = 0x5555557e5bd0-0xC= 0x5555557e5bc4
执行下一条0x9 handler时,因为func_data-0x20地址处的值为2,所以让handler跳转到0x5555557e5bc4。
如果0x34 handler执行时*(DWORD*)(func_data-0x130)== *(DWORD*)(func_data-0x128),那么下一个handler执行后仍顺序执行。
虚拟机执行流程解析
def custom_run_to_here(self, ql: Qiling) -> List[HookRet]:
def addr_tps_F7E20_hook(ql: Qiling) -> None:
# ql.log.info(f'index_base: {ql.arch.regs.X19:#x}')
index = ql.arch.regs.X12 & 0x3F
# ql.log.info(f'index_value: {index:#x}')
# ql.log.info(f'off1: {ql.arch.regs.W8:#x}')
# ql.log.info(f'off2: {ql.arch.regs.W9:#x}')
indent = f'ntttttt'
off = calc_off(ql)
W11 = calc_W11(ql)
rs = f'index: {index:#x} -> '
base_W8 = f'[func_data-{0x130-ql.arch.regs.W8*8:#x}]'
base_W9 = f'[func_data-{0x130-ql.arch.regs.W9*8:#x}]'
base_W10 = f'[func_data-{0x130-ql.arch.regs.W10*8:#x}]'
global update_index_table_flag
if update_index_table_flag == 2:
update_index_table_flag += 1
if index == 0x9 or index == 0x1C:
base_W9_value = get_base_W9(ql)
rs += f'{base_W8} = {base_W9} + {off:#x}; {get_base(ql, base_W9_value)}, rs: {get_base(ql, base_W9_value+off)}'
elif index in [0, 0x34]:
flag_20 = ql.arch.regs.X0
if flag_20 != 0:
rs += "ignore!!"
v78 = f'(int)({off:#x} << 0x10) >> 0xE'
v77 = get_base_W8(ql)
rs += f'{base_W8}; {v77:#x}'
v79 = get_base_W9(ql)
rs += f'{indent}{base_W9}; {v79:#x}'
str1 = f'{indent}[func_data-0x18] = index_table_pointer+8(2 elements); {ql.arch.regs.X19:#x}, rs: {ql.arch.regs.X19+8:#x}'
str2 = f'{indent}[func_data-0x18] = index_table_pointer+4+({v78}); {ql.arch.regs.X19:#x}'
if index == 0:
if v77 != v79:
rs += str1
else:
rs += str2
if index == 0x34:
if v77 == v79:
rs += str1
else:
rs += str2
# elif...
if log_flag:
ql.log.info(rs)
return [ql.hook_address(addr_27DA8_hook, 0x7ffff7dfcda8),
ql.hook_address(addr_27FB4_hook, 0x7ffff7dfcfb4),
ql.hook_address(addr_tps_ADAB8_hook, 0x555555601ab8),
ql.hook_address(addr_tps_F7E20_hook, 0x55555564be20)]
◆完整的custom_script.py已上传至附件。 ◆因为switch-case有些路径是没有走到的,也就是说加解密的handler不是全集,所以我们在写handler的时候,只需要一个一个写,遇到一个新的待执行的handler,就增加一个elif,不需要把switch-case的所有路径都挨着写完。 ◆当我们新增handler时,就需要重启qiling环境,这样会很耗时。因此在遇到没有记录的handler时,我们可以手动调用ql.os.stop(),这样ql_run_to_here可以为我们保存当前执行环境,之后我们仍然可以单步模拟执行:
[=] [Thread 2008] index: 0x9 -> [func_data-0x48] = [func_data-0x48] + 0xfea0; func_data-0x150, rs: func_data--0xfd50
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x158] = [func_data-0x38]; func_data-0x158, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x150] = [func_data-0x80]; func_data-0x160, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x148] = [func_data-0x88]; func_data-0x168, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x140] = [func_data-0x90]; func_data-0x170, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x138] = [func_data-0x98]; func_data-0x178, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x130] = [func_data-0xa0]; func_data-0x180, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x128] = [func_data-0xa8]; func_data-0x188, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x120] = [func_data-0xb0]; func_data-0x190, 0x0
[=] [Thread 2008] index: 0x3c -> [func_data-0xb0] = [[func_data-0x110] + 0x10]; func_data-0x450 -> func_data-0x440, 0x20
[=] [Thread 2008] index: 0x3c -> [func_data-0xa8] = [[func_data-0x110] + 0x8]; func_data-0x450 -> func_data-0x448, 0x100000
[=] [Thread 2008] index: 0x3c -> [func_data-0x90] = [[func_data-0x108] + 0x0]; 0x555555857110 -> 0x555555857110, 0x555555f0abb0
[=] [Thread 2008] index: 0x35 -> [func_data-0x88] = (signed )d[[func_data-0x110] + 0x0]; func_data-0x450, 0x555f9370
[=] [Thread 2008] index: 0x3c -> [func_data-0x128] = [[func_data-0x100] + 0x0]; 0x555555857118 -> 0x555555857118, 0x555555cd9eb0
[=] [Thread 2008] index: 0x2d -> [func_data-0x98] = d[func_data-0x130] + (int64)(int)0x0; 0x0, rs: 0x0
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x48] + 0x110] = b[func_data-0x130]; func_data-0x1a0, 0x0
[=] [Thread 2008] index: 0x9 -> [func_data-0xa0] = [func_data-0x48] + 0x4; func_data-0x2b0, rs: func_data-0x2ac
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x108] = [func_data-0xa0]; func_data-0x1a8, func_data-0x2ac
[=] [Thread 2008] index: 0x9 -> [func_data-0x120] = [func_data-0x130] + 0x4; 0x0, rs: 0x4
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x118] = [func_data-0x120]; func_data-0x198, 0x4
[=] [Thread 2008] index: 0x1f -> [func_data-0x120] = int(0xff97 << 0x10)
[=] [Thread 2008] index: 0x1d -> [func_data-0x80] = [func_data-0x120] | 0x1488; 0xffffffffff970000, rs:0xffffffffff971488
[=] [Thread 2008] index: 0xc2b -> [func_data-0x110] = [func_data-0x80] + [func_data-0x128]; 0xffffffffff971488, 0x555555cd9eb0, rs: 0x55555564b338
[=] [Thread 2008] index: 0x92b -> [func_data-0x68] = [func_data-0x130] | [func_data-0xf8]; 0x0, 0x55555564b34c, rs: 0x55555564b34c
[=] [Thread 2008] index: 0xdeb -> [func_data-0x18] = [func_data-0x68]; 0x55555564b34c
[func_data-0x20] = 2
[func_data-0x38] = index_table_pointer+8; 0x5555557e5b90, rs: 0x5555557e5b98
[func_data-0x10] = index_table_pointer+8; 0x5555557e5b90, rs: 0x5555557e5b98
[=] [Thread 2008] index: 0x9 -> [func_data-0x108] = [func_data-0x48] + 0x108; func_data-0x2b0, rs: func_data-0x1a8
index_table_pointer = [func_data-0x18]; 0x55555564b34c
[func_data-0x20] = 0
[beg] = index_table_pointer
call func:(0x55555564b34c)([func_data-0x110], [func_data-0x108]); memset
index_table_pointer = [func_data-0x10]; 0x5555557e5b98
[beg] = index_table_pointer
[=] [Thread 2008] index: 0xa6b -> [func_data-0x128] = d[func_data-0x88] >> 0x18; 0x555f9370, rs: 0x55
[=] [Thread 2008] index: 0xa6b -> [func_data-0x120] = d[func_data-0x88] >> 0x10; 0x555f9370, rs: 0x555f
[=] [Thread 2008] index: 0xa6b -> [func_data-0x118] = d[func_data-0x88] >> 0x8; 0x555f9370, rs: 0x555f93
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x48] + 0x5] = b[func_data-0x118]; func_data-0x2ab, 0x93
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x48] + 0x4] = b[func_data-0x88]; func_data-0x2ac, 0x70
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x48] + 0x6] = b[func_data-0x120]; func_data-0x2aa, 0x5f
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x48] + 0x7] = b[func_data-0x128]; func_data-0x2a9, 0x55
[=] [Thread 2008] index: 0xc2b -> [func_data-0x118] = [func_data-0x80] + [func_data-0x90]; 0xffffffffff971488, 0x555555f0abb0, rs: 0x55555587c038
[=] [Thread 2008] index: 0x38 -> [func_data-0x128] = [func_data-0x98] < 0x100; 0x0, rs: 0x1
七
加解密算法分析
handler执行过程.txt已上传至附件,里面记录了整个执行流程。
环境初始化
[=] [Thread 2008] index: 0x9 -> [func_data-0x48] = [func_data-0x48] + 0xfea0; func_data-0x150, rs: func_data--0xfd50
//这里分号后面为自动生成的注释,表示从左到右,其内存的值。比如下面这一行,[func_data-0x48]的值是func_data-0x158;[func_data-0x38]的值是0。因为这是个写操作,就没有打印[[func_data-0x48]+0x158]的值了。
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x158] = [func_data-0x38]; func_data-0x158, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x150] = [func_data-0x80]; func_data-0x160, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x148] = [func_data-0x88]; func_data-0x168, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x140] = [func_data-0x90]; func_data-0x170, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x138] = [func_data-0x98]; func_data-0x178, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x130] = [func_data-0xa0]; func_data-0x180, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x128] = [func_data-0xa8]; func_data-0x188, 0x0
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x120] = [func_data-0xb0]; func_data-0x190, 0x0
[=] [Thread 2008] index: 0x3c -> [func_data-0xb0] = [[func_data-0x110] + 0x10]; func_data-0x450 -> func_data-0x440, 0x20
[=] [Thread 2008] index: 0x3c -> [func_data-0xa8] = [[func_data-0x110] + 0x8]; func_data-0x450 -> func_data-0x448, 0x100000
[=] [Thread 2008] index: 0x3c -> [func_data-0x90] = [[func_data-0x108] + 0x0]; 0x555555857110 -> 0x555555857110, 0x555555f0abb0
[=] [Thread 2008] index: 0x35 -> [func_data-0x88] = (signed )d[[func_data-0x110] + 0x0]; func_data-0x450, 0x555f9370
[=] [Thread 2008] index: 0x3c -> [func_data-0x128] = [[func_data-0x100] + 0x0]; 0x555555857118 -> 0x555555857118, 0x555555cd9eb0
[=] [Thread 2008] index: 0x2d -> [func_data-0x98] = d[func_data-0x130] + (int64)(int)0x0; 0x0, rs: 0x0
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x48] + 0x110] = b[func_data-0x130]; func_data-0x1a0, 0x0
[=] [Thread 2008] index: 0x9 -> [func_data-0xa0] = [func_data-0x48] + 0x4; func_data-0x2b0, rs: func_data-0x2ac
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x108] = [func_data-0xa0]; func_data-0x1a8, func_data-0x2ac
[=] [Thread 2008] index: 0x9 -> [func_data-0x120] = [func_data-0x130] + 0x4; 0x0, rs: 0x4
[=] [Thread 2008] index: 0x29 -> [[func_data-0x48]+0x118] = [func_data-0x120]; func_data-0x198, 0x4
[=] [Thread 2008] index: 0x1f -> [func_data-0x120] = int(0xff97 << 0x10)
[=] [Thread 2008] index: 0x1d -> [func_data-0x80] = [func_data-0x120] | 0x1488; 0xffffffffff970000, rs:0xffffffffff971488
[=] [Thread 2008] index: 0xc2b -> [func_data-0x110] = [func_data-0x80] + [func_data-0x128]; 0xffffffffff971488, 0x555555cd9eb0, rs: 0x55555564b338
[=] [Thread 2008] index: 0x92b -> [func_data-0x68] = [func_data-0x130] | [func_data-0xf8]; 0x0, 0x55555564b34c, rs: 0x55555564b34c
[=] [Thread 2008] index: 0xdeb -> [func_data-0x18] = [func_data-0x68]; 0x55555564b34c
[func_data-0x20] = 2
[func_data-0x38] = index_table_pointer+8; 0x5555557e5b90, rs: 0x5555557e5b98
[func_data-0x10] = index_table_pointer+8; 0x5555557e5b90, rs: 0x5555557e5b98
[=] [Thread 2008] index: 0x9 -> [func_data-0x108] = [func_data-0x48] + 0x108; func_data-0x2b0, rs: func_data-0x1a8
index_table_pointer = [func_data-0x18]; 0x55555564b34c
[func_data-0x20] = 0
[beg] = index_table_pointer
call func:(0x55555564b34c)([func_data-0x110], [func_data-0x108]); memset
index_table_pointer = [func_data-0x10]; 0x5555557e5b98
[beg] = index_table_pointer
准备一个256字节大小的S-box表
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x120] + 0x0] = b[func_data-0x98]; func_data-0x2a8, 0x0
[=] [Thread 2008] index: 0x2d -> [func_data-0x98] = d[func_data-0x98] + (int64)(int)0x1; 0x0, rs: 0x1
[=] [Thread 2008] index: 0x38 -> [func_data-0x128] = [func_data-0x98] < 0x100; 0x1, rs: 0x1
[=] [Thread 2008] index: 0x34 -> [func_data-0x130]; 0x0
[func_data-0x128]; 0x1
[func_data-0x18] = index_table_pointer+4+((int)(0xfffc << 0x10) >> 0xE); 0x5555557e5bd0
[func_data-0x20] = 2
[=] [Thread 2008] index: 0x9 -> [func_data-0x120] = [func_data-0x120] + 0x1; func_data-0x2a8, rs: func_data-0x2a7
index_table_pointer = [func_data-0x18]; 0x5555557e5bc4
[func_data-0x20] = 0
[beg] = index_table_pointer
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x120] + 0x0] = b[func_data-0x98]; func_data-0x2a7, 0x1
[=] [Thread 2008] index: 0x2d -> [func_data-0x98] = d[func_data-0x98] + (int64)(int)0x1; 0x1, rs: 0x2
[=] [Thread 2008] index: 0x38 -> [func_data-0x128] = [func_data-0x98] < 0x100; 0x2, rs: 0x1
[=] [Thread 2008] index: 0x34 -> [func_data-0x130]; 0x0
[func_data-0x128]; 0x1
[func_data-0x18] = index_table_pointer+4+((int)(0xfffc << 0x10) >> 0xE); 0x5555557e5bd0
[func_data-0x20] = 2
[=] [Thread 2008] index: 0x9 -> [func_data-0x120] = [func_data-0x120] + 0x1; func_data-0x2a7, rs: func_data-0x2a6
index_table_pointer = [func_data-0x18]; 0x5555557e5bc4
[func_data-0x20] = 0
[beg] = index_table_pointer
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x120] + 0x0] = b[func_data-0x98]; func_data-0x2a6, 0x2
[=] [Thread 2008] index: 0x2d -> [func_data-0x98] = d[func_data-0x98] + (int64)(int)0x1; 0x2, rs: 0x3
[=] [Thread 2008] index: 0x38 -> [func_data-0x128] = [func_data-0x98] < 0x100; 0x3, rs: 0x1
[=] [Thread 2008] index: 0x34 -> [func_data-0x130]; 0x0
[func_data-0x128]; 0x1
[func_data-0x18] = index_table_pointer+4+((int)(0xfffc << 0x10) >> 0xE); 0x5555557e5bd0
[func_data-0x20] = 2
[=] [Thread 2008] index: 0x9 -> [func_data-0x120] = [func_data-0x120] + 0x1; func_data-0x2a6, rs: func_data-0x2a5
index_table_pointer = [func_data-0x18]; 0x5555557e5bc4
[func_data-0x20] = 0
[beg] = index_table_pointer
#define SHUFFLE_TABLE_LEN 256
unsigned char shuffle_table[SHUFFLE_TABLE_LEN] = { 0 };
for (int i = 0; i < SHUFFLE_TABLE_LEN; ++i)
shuffle_table[i] = i;
S-box表置换
//读取S-box表的第i个元素 a
[=] [Thread 2008] index: 0x72b -> [func_data-0x128] = (int)d[func_data-0x120] >> 0x1f; 0x0
[=] [Thread 2008] index: 0xa6b -> [func_data-0xe0] = d[func_data-0x128] >> 0x1b; 0x0, rs: 0x0
[=] [Thread 2008] index: 0xcab -> [func_data-0xe0] = d[func_data-0xe0] + d[func_data-0x120]; 0x0, 0x0, rs: 0x0
[=] [Thread 2008] index: 0x9eb -> [func_data-0xe0] = [func_data-0x108] & [func_data-0xe0]; 0xffffffffffffffe0, 0x0, rs: 0x0
[=] [Thread 2008] index: 0xb2b -> [func_data-0xe0] = d[func_data-0x120] - d[func_data-0xe0]; 0x0, 0x0, rs: 0x0
[=] [Thread 2008] index: 0x4ab -> [func_data-0xe0] = d[func_data-0xe0] << 0x0; 0x0
[=] [Thread 2008] index: 0x9 -> [func_data-0xd8] = [func_data-0xf0] + 0x1; func_data-0x2a8, rs: func_data-0x2a7
[=] [Thread 2008] index: 0x30 -> [func_data-0xd0] = b[[func_data-0xf0] + 0x0]; func_data-0x2a8, 0x0
// S-box表的第i个元素和上一轮结果相加:a+last_result
// last_result初始化为0
[=] [Thread 2008] index: 0xcab -> [func_data-0xe8] = d[func_data-0xd0] + d[func_data-0xe8]; 0x0, 0x0, rs: 0x0
// [func_data-0x118]是sub_F736C函数的第3个参数,是一个0x20字节的数组,元素长度为1,这里命名为shuffle_factor_table。
// 从shuffle_factor_table取(i%0x20),然后与上一步相加:
// 得 a+last_result+shuffle_factor_table[i%0x20]
[=] [Thread 2008] index: 0x2d -> [func_data-0xc8] = d[func_data-0x120] + (int64)(int)0x1; 0x0, rs: 0x1
[=] [Thread 2008] index: 0xa6b -> [func_data-0x128] = d[func_data-0x128] >> 0x1e; 0x0, rs: 0x0
[=] [Thread 2008] index: 0xc2b -> [func_data-0xe0] = [func_data-0xe0] + [func_data-0x118]; 0x0, 0x55555587c038, rs: 0x55555587c038
[=] [Thread 2008] index: 0x30 -> [func_data-0xe0] = b[[func_data-0xe0] + 0x0]; 0x55555587c038, 0x48
[=] [Thread 2008] index: 0xcab -> [func_data-0xe8] = d[func_data-0xe0] + d[func_data-0xe8]; 0x48, 0x0, rs: 0x48
// tps_encrypt和tps_decrypt的第一个参数serino,小端序保存在内存中,然后取出一字节与上一步相加:
// 地 a+last_result+shuffle_factor_table[i%0x20]+serino[i%4]
[=] [Thread 2008] index: 0xcab -> [func_data-0x128] = d[func_data-0x128] + d[func_data-0x120]; 0x0, 0x0, rs: 0x0
[=] [Thread 2008] index: 0x9eb -> [func_data-0x128] = [func_data-0xf8] & [func_data-0x128]; 0xfffffffffffffffc, 0x0, rs: 0x0
[=] [Thread 2008] index: 0xb2b -> [func_data-0x128] = d[func_data-0x120] - d[func_data-0x128]; 0x0, 0x0, rs: 0x0
[=] [Thread 2008] index: 0x4ab -> [func_data-0x128] = d[func_data-0x128] << 0x0; 0x0
[=] [Thread 2008] index: 0xc2b -> [func_data-0x128] = [func_data-0x128] + [func_data-0xa0]; 0x0, func_data-0x2ac, rs: func_data-0x2ac
[=] [Thread 2008] index: 0x30 -> [func_data-0x128] = b[[func_data-0x128] + 0x0]; func_data-0x2ac, 0x70
[=] [Thread 2008] index: 0xcab -> [func_data-0x128] = d[func_data-0x128] + d[func_data-0xe8]; 0x70, 0x48, rs: 0xb8
// 上一步结果与0xFF求余
// 得 last_result = (a+last_result+shuffle_factor_table[i%0x20]+serino[i%4]) & 0xFF
[=] [Thread 2008] index: 0x72b -> [func_data-0x120] = (int)d[func_data-0x128] >> 0x1f; 0xb8
[=] [Thread 2008] index: 0xa6b -> [func_data-0x120] = d[func_data-0x120] >> 0x18; 0x0, rs: 0x0
[=] [Thread 2008] index: 0xcab -> [func_data-0x120] = d[func_data-0x120] + d[func_data-0x128]; 0x0, 0xb8, rs: 0xb8
[=] [Thread 2008] index: 0x9eb -> [func_data-0x120] = [func_data-0x100] & [func_data-0x120]; 0xffffffffffffff00, 0xb8, rs: 0x0
[=] [Thread 2008] index: 0xb2b -> [func_data-0xe8] = d[func_data-0x128] - d[func_data-0x120]; 0xb8, 0x0, rs: 0xb8
[=] [Thread 2008] index: 0x4ab -> [func_data-0x128] = d[func_data-0xe8] << 0x0; 0xb8
// S-box表置换
// tmp = S-box[i]
// S-box[i] = S-box[last_result]
// S-box[last_result] = tmp
[=] [Thread 2008] index: 0xc2b -> [func_data-0x128] = [func_data-0x128] + [func_data-0x110]; 0xb8, func_data-0x2a8, rs: func_data-0x1f0
[=] [Thread 2008] index: 0x30 -> [func_data-0x120] = b[[func_data-0x128] + 0x0]; func_data-0x1f0, 0xb8
[=] [Thread 2008] index: 0x2a -> b[[func_data-0xf0] + 0x0] = b[func_data-0x120]; func_data-0x2a8, 0xb8
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x128] + 0x0] = b[func_data-0xd0]; func_data-0x1f0, 0x0
[=] [Thread 2008] index: 0x92b -> [func_data-0x120] = [func_data-0x130] | [func_data-0xc8]; 0x0, 0x1, rs: 0x1
// 没有遍历到0x256,回调到S-box置换的第一条指令,继续置换
[=] [Thread 2008] index: 0x38 -> [func_data-0x128] = [func_data-0x120] < 0x100; 0x1, rs: 0x1
[=] [Thread 2008] index: 0x34 -> [func_data-0x130]; 0x0
[func_data-0x128]; 0x1
[func_data-0x18] = index_table_pointer+4+((int)(0xffde << 0x10) >> 0xE); 0x5555557e5c80
[func_data-0x20] = 2
[=] [Thread 2008] index: 0x92b -> [func_data-0xf0] = [func_data-0x130] | [func_data-0xd8]; 0x0, 0x80000000db39, rs: func_data-0x2a7
index_table_pointer = [func_data-0x18]; 0x5555557e5bfc
[func_data-0x20] = 0
[beg] = index_table_pointer
void Fisher_Yates(unsigned char shuffle_table[SHUFFLE_TABLE_LEN], unsigned int serino) {
unsigned char shuffle_factor_table[SHUFFLE_FACTOR_TABLE_LEN] = {
0x48, 0xA9, 0xC8, 0x12, 0xCA, 0xFC, 0xD3, 0x5E, 0xB7, 0x61, 0x50, 0x17, 0x68, 0xBA, 0x7E, 0x2E,
0xB9, 0xA2, 0x38, 0x85, 0x35, 0x48, 0x55, 0x6C, 0x2C, 0x38, 0x43, 0x1F, 0x51, 0xD6, 0x30, 0x30 };
unsigned char last_result = 0;
unsigned char serino_table[] = { serino & 0xFF, (serino >> 8) & 0xFF, (serino >> 16) & 0xFF, (serino >> 24) & 0xFF };
for (int i = 0; i < SHUFFLE_TABLE_LEN; ++i) {
last_result = (shuffle_factor_table[i % SHUFFLE_FACTOR_TABLE_LEN] + shuffle_table[i] + last_result + serino_table[i % 4]) & 0xFF;
unsigned char tmp = shuffle_table[i];
shuffle_table[i] = shuffle_table[last_result];
shuffle_table[last_result] = tmp;
//printf("exchange: %x -> %x, factor: %xn", i, last_result, shuffle_factor_table[i%SHUFFLE_FACTOR_TABLE_LEN]);
}
}
数据加解密
// a = (i+1) & 0xFF
[=] [Thread 2008] index: 0x2d -> [func_data-0x128] = d[func_data-0x120] + (int64)(int)0x1; 0x100, rs: 0x101
[=] [Thread 2008] index: 0x72b -> [func_data-0x120] = (int)d[func_data-0x128] >> 0x1f; 0x101
[=] [Thread 2008] index: 0xa6b -> [func_data-0x120] = d[func_data-0x120] >> 0x18; 0x0, rs: 0x0
[=] [Thread 2008] index: 0xcab -> [func_data-0x120] = d[func_data-0x120] + d[func_data-0x128]; 0x0, 0x101, rs: 0x101
[=] [Thread 2008] index: 0x9eb -> [func_data-0x120] = [func_data-0x118] & [func_data-0x120]; 0xffffffffffffff00, 0x101, rs: 0x100
[=] [Thread 2008] index: 0xb2b -> [func_data-0x120] = d[func_data-0x128] - d[func_data-0x120]; 0x101, 0x100, rs: 0x1
[=] [Thread 2008] index: 0x4ab -> [func_data-0x128] = d[func_data-0x120] << 0x0; 0x1
// func_data-0xa8为待加解密的数据,这里命名为x
// 保存当前的字节x[i]到[func_data-0x100]
[=] [Thread 2008] index: 0x9 -> [func_data-0xf8] = [func_data-0x100] + 0x1; 0x0, rs: 0x1
[=] [Thread 2008] index: 0xc2b -> [func_data-0x100] = [func_data-0x100] + [func_data-0xa8]; 0x0, 0x100000, rs: 0x100000
// func_data-0x110保存着S-box的地址
// 从置换后的S-box取出一字节,index为a,并与上一轮结果相加
// 得b = S-box[a]+last_result
[=] [Thread 2008] index: 0xc2b -> [func_data-0x128] = [func_data-0x128] + [func_data-0x110]; 0x1, func_data-0x2a8, rs: func_data-0x2a7
[=] [Thread 2008] index: 0x30 -> [func_data-0xf0] = b[[func_data-0x128] + 0x0]; func_data-0x2a7, 0xa9
[=] [Thread 2008] index: 0xcab -> [func_data-0x108] = d[func_data-0xf0] + d[func_data-0x108]; 0xa9, 0x0, rs: 0xa9
[=] [Thread 2008] index: 0x72b -> [func_data-0xe8] = (int)d[func_data-0x108] >> 0x1f; 0xa9
[=] [Thread 2008] index: 0xa6b -> [func_data-0xe8] = d[func_data-0xe8] >> 0x18; 0x0, rs: 0x0
[=] [Thread 2008] index: 0xcab -> [func_data-0xe8] = d[func_data-0xe8] + d[func_data-0x108]; 0x0, 0xa9, rs: 0xa9
[=] [Thread 2008] index: 0x9eb -> [func_data-0xe8] = [func_data-0x118] & [func_data-0xe8]; 0xffffffffffffff00, 0xa9, rs: 0x0
[=] [Thread 2008] index: 0xb2b -> [func_data-0x108] = d[func_data-0x108] - d[func_data-0xe8]; 0xa9, 0x0, rs: 0xa9
[=] [Thread 2008] index: 0x4ab -> [func_data-0xe8] = d[func_data-0x108] << 0x0; 0xa9
// S-box表置换
// tmp = S-box[a]
// S-box[a] = S-box[b]
// S-box[b] = S-box[a]
[=] [Thread 2008] index: 0xc2b -> [func_data-0xe8] = [func_data-0xe8] + [func_data-0x110]; 0xa9, func_data-0x2a8, rs: func_data-0x1ff
[=] [Thread 2008] index: 0x30 -> [func_data-0xe0] = b[[func_data-0xe8] + 0x0]; func_data-0x1ff, 0xc1
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x128] + 0x0] = b[func_data-0xe0]; func_data-0x2a7, 0xc1
[=] [Thread 2008] index: 0x2a -> b[[func_data-0xe8] + 0x0] = b[func_data-0xf0]; func_data-0x1ff, 0xa9
// x[i] = x[i] ^ ((S-box[a] + tmp) & 0xFF)
[=] [Thread 2008] index: 0x30 -> [func_data-0x128] = b[[func_data-0x128] + 0x0]; func_data-0x2a7, 0xc1
[=] [Thread 2008] index: 0xcab -> [func_data-0x128] = d[func_data-0xf0] + d[func_data-0x128]; 0xa9, 0xc1, rs: 0x16a
[=] [Thread 2008] index: 0x30 -> [func_data-0xf0] = b[[func_data-0x100] + 0x0]; 0x100000, 0x12
[=] [Thread 2008] index: 0x37 -> [func_data-0x128] = [func_data-0x128] & 0xff; 0x16a, rs:0x6a
[=] [Thread 2008] index: 0x7 -> [func_data-0x128] = ((1(LL) << (W10+1)) - 1) & ([func_data-0x128] >> W11); 0x1f, 0x6a, 0x0, rs: 0x6a
[=] [Thread 2008] index: 0xc2b -> [func_data-0x128] = [func_data-0x128] + [func_data-0x110]; 0x6a, func_data-0x2a8, rs: func_data-0x23e
[=] [Thread 2008] index: 0x30 -> [func_data-0x128] = b[[func_data-0x128] + 0x0]; func_data-0x23e, 0xef
[=] [Thread 2008] index: 0x36b -> [func_data-0x128] = [func_data-0x128] ^ [func_data-0xf0]; 0xef, 0x12, rs: 0xfd
[=] [Thread 2008] index: 0x2a -> b[[func_data-0x100] + 0x0] = b[func_data-0x128]; 0x100000, 0xfd
// 没有遍历到x的长度,继续遍历
[=] [Thread 2008] index: 0x92b -> [func_data-0x100] = [func_data-0x130] | [func_data-0xf8]; 0x0, 0x1, rs: 0x1
[=] [Thread 2008] index: 0x1eb -> base_W9_value < base_W8_value: [func_data-0x128] = 1; w8=[func_data-0xb0]=32, w9=[func_data-0x100]=1
[=] [Thread 2008] index: 0x34 -> [func_data-0x130]; 0x0
[func_data-0x128]; 0x1
[func_data-0x18] = index_table_pointer+4+((int)(0xffde << 0x10) >> 0xE); 0x5555557e5d24
[func_data-0x20] = 2
[=] [Thread 2008] index: 0x4ab -> [func_data-0x130] = d[func_data-0x130] << 0x0; 0x0
index_table_pointer = [func_data-0x18]; 0x5555557e5ca0
[func_data-0x20] = 0
[beg] = index_table_pointer
void encrypt_and_decrypt_data(unsigned char shuffle_table[SHUFFLE_TABLE_LEN], unsigned char* x, int len) {
unsigned char last_result = 0;
for (int i = 0; i < len; ++i) {
int s_i = (i + 1) & 0xFF;
unsigned char cur = shuffle_table[s_i];
// last_result = (last_result + cur) & 0xFF;
last_result += cur; // no need to 'and 0xFF', because the type of last_result is unsigned char
shuffle_table[s_i] = shuffle_table[last_result];
shuffle_table[last_result] = cur;
unsigned char tmp = (cur + shuffle_table[s_i]) & 0xFF;
x[i] = x[i] ^ shuffle_table[tmp];
}
}
八
总结
看雪ID:coneco
https://bbs.kanxue.com/user-home-744796.htm
# 往期推荐
2、恶意木马历险记
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):aarch64架构的某so模拟执行和加密算法分析