qiling 是一个基于 Unicorn 开发的二进制文件仿真工具,与 Unicorn 不同,qiling 的功能更加完善更加易用。下面将对该 Python 库的主要 API 进行总结与学习。
A. 类 Qiling
这是 qiling 模拟器主类,将完成仿真的大部分主要操作。
A.1 构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 def __init__ ( self, argv: Sequence [str ] = [], rootfs: str = r'.' , env: MutableMapping[AnyStr, AnyStr] = {}, code: Optional [bytes ] = None , ostype: Optional [QL_OS] = None , archtype: Optional [QL_ARCH] = None , verbose: QL_VERBOSE = QL_VERBOSE.DEFAULT, profile: Optional [Union [str , Mapping]] = None , console: bool = True , log_file: Optional [str ] = None , log_override: Optional ['Logger' ] = None , log_plain: bool = False , multithread: bool = False , filter : Optional [str ] = None , stop: QL_STOP = QL_STOP.NONE, *, endian: Optional [QL_ENDIAN] = None , thumb: bool = False , libcache: bool = False ): ...
一些常用参数的含义:
argv
:命令的具体内容,对于控制台的命令,需要以空格分开并保存在列表中。如要执行/bin/cat flag.txt
则需要传入['/bin/cat', 'flag.txt']
。
rootfs
:本次模拟的根目录,默认为当前目录。
env
:环境变量,传入字典即可。
code
:自定义代码,不能和argv
同时指定,这个参数主要是用于执行机器码字节序列。
ostype
:操作系统,传入QL_OS
类型。
archtype
:处理器架构,传入QL_ARCH
类型。
verbose
:输出信息的详细程度,如调试级别会比默认级别输出更多信息。传入QL_VERBOSE
类型。
profile
:配置项,可以保存在文件中传入文件名,也可以传入字典。
console
:是否运行在终端,将输出内容显示在终端。
log_file
:日志的输出文件。
log_plain
:是否输出去除颜色的日志内容,当日志被保存到文件中时常用。
multithread
:是否使用多线程。
filter
:根据正则表达式筛选指定日志输出。
endian
:端序。
thumb
:模拟 ARM 架构时是否为 thumb 模式。
这里与程序运行过程中关系最大的参数之一就是profile
,我们可以在这里定义堆栈的地址、mmap的输出地址、网络配置等,profile
参数如果为文件名,后缀应为.ql
,文件实际格式为yaml
。下面是默认的 Linux 配置文件:
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 [CODE ] ram_size = 0xa00000 entry_point = 0x1000000 [OS64 ] stack_address = 0x7ffffffd0000 stack_size = 0x30000 load_address = 0x555555554000 interp_address = 0x7ffff7dd5000 mmap_address = 0x7fffb7dd6000 vsyscall_address = 0xffffffffff600000 [OS32 ] stack_address = 0x7ff0d000 stack_size = 0x30000 load_address = 0x56555000 interp_address = 0x047ba000 mmap_address = 0x90000000 [KERNEL ] uid = 1000 gid = 1000 pid = 1996 [MISC ] current_path = / [NETWORK ] ifrname_override = eth0 bindtolocalhost = True ipv6 = False
非常令人不解的是,官方文档并没有详细给出 profile 配置文件中包含哪些有效的项。翻遍了整个互联网,居然没有人对 qiling 配置文件的所有选项进行总结,要想知道某个配置的名字,只能参考现有的 .ql 文件,外加直接搜索 qiling 源代码中的相关用法。加载中需要使用的项在仓库的loader
目录下不同格式的二进制文件加载过程代码中可以找到,下面简单总结一下查到的所有配置文件项列表(可能不全):
A.2. profile 配置文件项
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 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 所有格式通用:(os/os.py、os/posix/posix.py) [CODE](Qiling构造函数需传入code参数) entry_point: 程序入口点 ram_size: RAM的大小 [MISC] current_path: 当前路径(OS类型需要为支持POSIX的,包括Linux、FreeBSD、QNX、macOS、Windows、DOS) [KERNEL] uid: uid号和euid号 gid: gid号和egid号 pid: pid号 [NETWORK] ipv6: 布尔值,是否支持ipv6 bindtolocalhost: 布尔值,是否绑定到本机IP ifrname_override: 主机网络设备映射 BLOB:(无操作系统的Bare Metal二进制文件) [CODE] heap_size: 堆空间大小 DOS: [COM] start_cs: 初始CS值(要求后缀名为.DOS_COM) start_ip: 初始IP值(要求后缀名为.DOS_COM) start_sp: 初始SP值(要求后缀名为.DOS_COM) stack_size: 栈大小 [KERNEL] ticks_per_second: 时钟频率 ELF: [OS{字长bit数}] stack_address: 栈地址 stack_size: 栈大小 load_address: 加载地址(需要为重定向文件) interp_address: Linux加载器地址 mmap_address: mmap返回的地址 vsyscall_address: vsyscall地址(需要为OS64) MacOS: [OS64] stack_address: 栈地址 stack_size: 栈大小 vmmap_trap_address: vmmap内存映射地址 heap_address: 堆地址 heap_size: 堆大小 mmap_address: mmap返回的地址 [LOADER] slide dyld_slide MCU: 无 PE: [HARDWARE] number_processors: 处理器数量 [SYSTEM] productType: 产品类型 majorVersion: 大版本号 minorVersion: 小版本号 VER_SERVICEPACKMAJOR: 系统包大版本 language: 系统语言(调用Windows SDK API) permission: 权限 [OS{字长bit数}] KI_USER_SHARED_DATA:内核用户共享数据地址 stack_address: 栈地址 stack_size: 栈大小 image_address: 程序的加载地址 dll_address: 系统等DLL文件加载地址 entry_point: 程序入口 [VOLUME] name: 卷名 serial_number: 卷序列号 type: 卷类型 sectors_per_cluster: 每个簇的分区数量 bytes_per_sector: 每个分区包含的字节数量 number_of_free_clusters: 可用簇数量 number_of_clusters: 簇总数 [PATH] systemdrive: 系统驱动 ???: 其他任意Windows环境变量 [USER] language: 用户语言(Windows SDK API) [NETWORK] dns_response_ip: DNS服务器IP [PROCESSES] csrss.exe: csrss.exe的pid [KERNEL] parent_pid: 父进程pid [REGISTRY] ???: 任意注册表项 PE_UEFI: [DXE] heap_address: 堆地址 heap_size: 堆大小 stack_address: 栈地址 stack_size: 栈大小 image_address: EFI文件的加载地址 [SMM] smram_size: SMM执行模式下的SMRAM大小 smram_base: SMM执行模式下的SMRAM基址 heap_address: SMM堆地址 heap_size: SMM堆大小 image_address: SMM加载基地址 [LOADED_IMAGE_PROTOCOL] Guid: 加载的image的guid [HOB_LIST] Guid: Hand-Off Block,用于数据交接块的guid
下面,我们尝试模拟一下 Linux 系统的 ASLR 保护机制加载一个 ELF 文件,并在 main 函数开头暂停,输出该 ELF 文件的内存布局情况。
A.3 第一次试运行
在第一次运行的过程中,发现了一些问题,下面逐一进行解决。
测试代码:
1 2 3 4 5 6 #include <stdio.h> int main () { puts ("Hello, Qiling !!!" ); puts ("Greetings from CoLin" ); }
为了模拟 ASLR 内存保护,我们需要对 libc 的加载地址、ELF 的加载地址以及堆栈的地址进行一定程度的随机化。由于 ASLR 要求每一次执行的内存地址都不同,因此我们不能将配置文件写死在 ql 文件之中,只能动态生成到字典中。
而对于 Qiling 类构造函数的参数,需要注意rootfs
选项。这里一定要将根目录设置为 ‘/’,如果设置为默认值,那么 qiling 将无法找到加载 ELF 的 ld.so 文件以及 libc.so 文件。
随后,开启执行发现,Qiling 没有完成一些较新的 Linux 系统调用实现。笔者的 libc 版本为 2.35,在加载器加载时会调用 334 号系统调用,而 Qiling 没有实现这个系统调用的替代,因此导致该系统调用无法正常运行,加载器判定 libc 版本不够。因此下面改为静态编译后运行。
脚本:
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 import qiling.constimport qilingfrom pwn import *def callback (ql: qiling.Qiling ): print ('\n' .join(ql.mem.get_formatted_mapinfo())) if __name__ == '__main__' : profile = { "OS64" : {} } profile['OS64' ]['stack_address' ] = 0x8000_0000_0000 - 0x30000 - (randint(0 , 0x8000_0000 ) << 12 ) profile['OS64' ]['stack_size' ] = 0x30000 profile['OS64' ]['mmap_address' ] = profile['OS64' ]['stack_address' ] - 0x1000_0000 print ("profile: " ) for key, value in profile['OS64' ].items(): print ("{}: {}" .format (key, hex (value))) elf = ELF('./hello_qiling' ) emu = qiling.Qiling( argv=['./hello_qiling' ], rootfs='/' , ostype=qiling.const.QL_OS.LINUX, archtype=qiling.const.QL_ARCH.X8664, profile=profile, ) print (hex (elf.symbols['main' ])) emu.hook_address(callback, elf.symbols['main' ]) emu.run()
这里hook_address
即在main
函数首地址添加 hook。回调函数的第一个参数一定要为 Qiling 对象。ql.mem.get_formatted_mapinfo
用于获取工整的内存映射信息字符串。
输出结果:
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 profile: stack_address: 0x7c76907a4000 stack_size: 0x30000 mmap_address: 0x7c76807a4000 [*] '/home/colin/Desktop/misc/CTF-playground/qiling/qiling_learn/hello_qiling' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) 0x401775 Start End Perm Label Image 000000000000030000 - 000000000000031000 rwx [GDT] 000000000000400000 - 000000000000401000 r-- hello_qiling /home/colin/Desktop/misc/CTF-playground/qiling/qiling_learn/hello_qiling 000000000000401000 - 000000000000498000 r-x hello_qiling /home/colin/Desktop/misc/CTF-playground/qiling/qiling_learn/hello_qiling 000000000000498000 - 0000000000004c1000 r-- hello_qiling /home/colin/Desktop/misc/CTF-playground/qiling/qiling_learn/hello_qiling 0000000000004c1000 - 0000000000004c5000 r-- hello_qiling /home/colin/Desktop/misc/CTF-playground/qiling/qiling_learn/hello_qiling 0000000000004c5000 - 0000000000004cd000 rw- hello_qiling /home/colin/Desktop/misc/CTF-playground/qiling/qiling_learn/hello_qiling 0000000000004cd000 - 0000000000004cf000 rwx [hook_mem] 0000000000004cf000 - 0000000000004d0000 rwx [brk] 0000000000004d0000 - 0000000000004f1000 rwx [brk] 0000007c76907a4000 - 0000007c76907d4000 rwx [stack] 00ffffffffff600000 - 00ffffffffff601000 rwx [vsyscall] Hello, Qiling !!! Greetings from CoLin [=] arch_prctl(code = 0x3001, addr = 0x7c76907d3e00) = -0x1 (EPERM) [=] brk(inp = 0x0) = 0x4cf000 [=] brk(inp = 0x4cfdc0) = 0x4d0000 [=] arch_prctl(code = 0x1002, addr = 0x4cf3c0) = 0x0 [=] set_tid_address(tidptr = 0x4cf690) = 0x2419 [=] set_robust_list(head_ptr = 0x4cf6a0, head_len = 0x18) = 0x0 [!] 0x44ac4f: syscall ql_syscall_rseq number = 0x14e(334) not implemented [=] uname(buf = 0x7c76907d3b90) = 0x0 [=] prlimit64(pid = 0x0, res = 0x3, new_limit = 0x0, old_limit = 0x7c76907d3d10) = 0x0 [=] readlink(pathname = 0x4aea98, buf = 0x7c76907d2c50, bufsize = 0x1000) = 0x48 [=] getrandom(buf = 0x4cc1f0, buflen = 0x8, flags = 0x1) = 0x8 [=] brk(inp = 0x4f1000) = 0x4f1000 [=] mprotect(start = 0x4c1000, mlen = 0x4000, prot = 0x1) = 0x0 [=] newfstatat(dirfd = 0x1, path = 0x4af37d, buf_ptr = 0x7c76907d3b00, flags = 0x1000) = -0x1 (EPERM) [=] write(fd = 0x1, buf = 0x4d0500, count = 0x27) = 0x27 [=] exit_group(code = 0x0) = ?
对于静态编译的程序,其 ELF 加载地址是固定的,但我们对栈地址完成了随机化处理。
A.4 Hooks
Qiling 支持多种程序 Hook,包括内存读写访问、基本块、代码片段、特定指令(只支持 syscall、in、out 等少数几种系统相关指令,Qiling 的一大缺点)。下面使用上面的静态编译程序进行测试,脚本如下:
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 import qiling.constimport qiling.core_hooks_typesfrom pwn import *def callback_address (ql: qiling.Qiling ): print ("*** Address Hook Callback Function ***" ) def callback_code (ql: qiling.Qiling, address: int , inst_size: int ): print (f"*** Code Hook Callback Function: at {address:#x} , instruction size = {inst_size} ***" ) def callback_basicblock (ql: qiling.Qiling, address: int , inst_size: int ): print (f"*** Basic Block Hook Callback Function: at {address:#x} , instruction size = {inst_size} ***" ) def callback_memoryFetch (ql: qiling.Qiling, access_type: int , address: int , memory_size: int , value: int ): print (f"*** Memory Fetch Hook Callback Function: at {address:#x} , memory size = {memory_size} ***" ) if __name__ == '__main__' : profile = { "OS64" : {} } profile['OS64' ]['stack_address' ] = 0x8000_0000_0000 - 0x30000 - (randint(0 , 0x8000_0000 ) << 12 ) profile['OS64' ]['stack_size' ] = 0x30000 profile['OS64' ]['mmap_address' ] = profile['OS64' ]['stack_address' ] - 0x1000_0000 print ("profile: " ) for key, value in profile['OS64' ].items(): print ("{}: {}" .format (key, hex (value))) elf = ELF('./hello_qiling' ) emu = qiling.Qiling( argv=['./hello_qiling' ], rootfs='/' , ostype=qiling.const.QL_OS.LINUX, archtype=qiling.const.QL_ARCH.X8664, profile=profile, ) hooks: list [qiling.core_hooks_types.HookRet] = [ emu.hook_address(callback_address, elf.symbols['main' ]), emu.hook_code(callback_code, begin=elf.symbols['main' ] + 4 , end=elf.symbols['main' ] + 8 ), emu.hook_block(callback_basicblock, begin=elf.symbols['main' ] + 0x17 , end=elf.symbols['main' ] + 0x26 ), emu.hook_mem_read(callback_memoryFetch, begin=0x498016 , end=0x49802b ) ] emu.run()
这里的hook_code
相当于给某个范围内的所有指令前添加 Hook,在不传入begin
和end
参数的情况下,默认是在整个内存空间操作,即对所有指令添加 Hook。hook_block
则是在某个范围内对新找到的基本块添加 Hook。不同的 Hook 的回调函数的参数不太一样,对于内存访问 Hook,其参数包含访问类型、访问地址、访问大小、写入(如果访问为写入操作)的值。当然在创建 Hook 时也可以为回调函数添加其他的参数。
输出:
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 profile: stack_address: 0x784745fb3000 stack_size: 0x30000 mmap_address: 0x784735fb3000 [*] '/home/colin/Desktop/misc/CTF-playground/qiling/qiling_learn/hello_qiling' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) [=] arch_prctl(code = 0x3001, addr = 0x784745fe2e00) = -0x1 (EPERM) [=] brk(inp = 0x0) = 0x4cf000 [=] brk(inp = 0x4cfdc0) = 0x4d0000 [=] arch_prctl(code = 0x1002, addr = 0x4cf3c0) = 0x0 [=] set_tid_address(tidptr = 0x4cf690) = 0x4b1c [=] set_robust_list(head_ptr = 0x4cf6a0, head_len = 0x18) = 0x0 [!] 0x44ac4f: syscall ql_syscall_rseq number = 0x14e(334) not implemented [=] uname(buf = 0x784745fe2b90) = 0x0 [=] prlimit64(pid = 0x0, res = 0x3, new_limit = 0x0, old_limit = 0x784745fe2d10) = 0x0 [=] readlink(pathname = 0x4aea98, buf = 0x784745fe1c50, bufsize = 0x1000) = 0x48 [=] getrandom(buf = 0x4cc1f0, buflen = 0x8, flags = 0x1) = 0x8 [=] brk(inp = 0x4f1000) = 0x4f1000 [=] mprotect(start = 0x4c1000, mlen = 0x4000, prot = 0x1) = 0x0 [=] newfstatat(dirfd = 0x1, path = 0x4af37d, buf_ptr = 0x784745fe2b00, flags = 0x1000) = -0x1 (EPERM) [=] write(fd = 0x1, buf = 0x4d0500, count = 0x27) = 0x27 [=] exit_group(code = 0x0) = ? *** Address Hook Callback Function *** *** Code Hook Callback Function: at 0x401779, instruction size = 1 *** *** Code Hook Callback Function: at 0x40177a, instruction size = 3 *** *** Code Hook Callback Function: at 0x40177d, instruction size = 7 *** *** Memory Fetch Hook Callback Function: at 0x498010, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x498018, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x498020, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x498028, memory size = 8 *** *** Basic Block Hook Callback Function: at 0x40178c, instruction size = 15 *** *** Memory Fetch Hook Callback Function: at 0x498016, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x49801e, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x498020, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x498028, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x498016, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x49801e, memory size = 8 *** *** Memory Fetch Hook Callback Function: at 0x498026, memory size = 4 *** *** Basic Block Hook Callback Function: at 0x40179b, instruction size = 7 *** Hello, Qiling !!! Greetings from CoLin
A.5 runtime 关键数据
runtime 数据包括寄存器值、内存值等。在 pwndbg 中,每一次程序执行中断后都会显示当前 RIP 前后的汇编代码、寄存器值以及堆栈内容。在 qiling 中,这些同样可以实现。
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 import qilingimport qiling.core_hooks_typesfrom random import randintfrom pwn import *context.arch = "amd64" watch_regs = ['rax' , 'rbx' , 'rcx' , 'rdx' , 'rsi' , 'rdi' , 'rsp' , 'rbp' , 'r8' , 'r9' , 'r10' , 'r11' , 'r12' , 'r13' , 'r14' , 'r15' ] def callback_code (ql: qiling.Qiling, address: int , inst_size: int ): current_instruction = ql.mem.read(address, inst_size) assembly = repr (next (ql.arch.disassembler.disasm(current_instruction, 0 ))) print (f"{address:#x} : {assembly} " ) print ("*** REGISTERS ***" ) for reg in watch_regs: print (f"{reg} \t{ql.arch.regs.read(reg):16x} " ) print ("*** STACK ***" ) for i in range (10 ): print (f"{ql.arch.regs.read('rsp' ) + i * 8 :x} \t{ql.arch.stack_read(i * 8 ):16x} " ) input () if __name__ == '__main__' : profile = { "OS64" : {} } profile['OS64' ]['stack_address' ] = 0x8000_0000_0000 - 0x30000 - (randint(0 , 0x8000_0000 ) << 12 ) profile['OS64' ]['stack_size' ] = 0x30000 profile['OS64' ]['mmap_address' ] = profile['OS64' ]['stack_address' ] - 0x1000_0000 emu = qiling.Qiling( argv=['./hello_qiling' ], profile=profile ) hook: qiling.core_hooks_types.HookRet = emu.hook_code(callback_code) emu.run()
如上脚本所示,这里的hook_code
仅传入了回调函数参数,这使得程序在每执行一条汇编指令后都会调用一次回调函数。在回调函数中,可以通过ql.mem_read
方法读取内存中的任意地址处的任意长度值(write 即为写入),返回值为字节数组(bytearray)。在 qiling 中提供了调用 capstone 库进行反汇编的 API:ql.arch.disassembler.disasm
,第一个参数为字节数组,第二个参数为偏移量。
对于寄存器值,可以直接通过ql.arch.regs.read
传入寄存器名获取寄存器的值(write 即为写入)。如果需要查询当前架构的所有寄存器,可以通过ql.arch.regs.register_mapping
获取。
对于堆栈,qiling 也特别设计了 API 便于操作。ql.arch.stack_read
用于读取以rsp
为基地址的任意偏移量的堆栈值(读取 4/8 字节,取决于字长)。stack_write
为写入指定偏移处,stack_push
和stack_pop
即为直接修改rsp
的值,在程序执行外完成 push 和 pop 操作。
部分输出:
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 0x40166f: <CsInsn 0x0 [67e8db130000]: call 0x13e1> *** REGISTERS *** rax 0 rbx 0 rcx 0 rdx 7f69841ede78 rsi 1 rdi 401775 rsp 7f69841ede60 rbp 0 r8 0 r9 0 r10 0 r11 0 r12 0 r13 0 r14 0 r15 0 *** STACK *** 7f69841ede60 7f69841ede68 7f69841ede68 0 7f69841ede70 1 7f69841ede78 7f69841edff0 7f69841ede80 0 7f69841ede88 0 7f69841ede90 10 7f69841ede98 78bfbfd 7f69841edea0 6 7f69841edea8 1000
A.6 内存
1 2 3 4 5 6 7 8 9 def callback_address (ql: qiling.Qiling ): address_to_map = 0x123456780000 if ql.mem.is_mapped(address_to_map, 0x2000 ): return ql.mem.map (address_to_map, 0x2000 , UC_PROT_READ | UC_PROT_WRITE) ql.mem.protect(address_to_map, 0x1000 , UC_PROT_READ) addresses: list [int ] = ql.mem.search(re.compile (b"[a-zA-Z0-9]{4,}\0" ), 0x400000 , 0x500000 ) print ('\n' .join(f"{hex (x)} {ql.mem.string(x)} " for x in addresses)) ql.mem.unmap(address_to_map, 0x2000 )
qiling 支持对内存进行映射、解除映射、搜索等操作。如上,ql.mem.is_mapped
可查询某地址开始往后一段空间是否已经被映射,ql.mem.protect
用于修改某段内存的权限,ql.mem.search
可查询某段内存中的值并返回所有查询结果,查询可使用字节数组或正则表达式。ql.mem.string
可便捷地提取内存中某个地址开始的字符串,但字符串中不可包含不可打印字符。
部分输出结果:
1 2 3 4 5 6 7 8 9 10 11 12 0x4ad2c7 ENOCSI 0x4ad2ce EL2HLT 0x4ad2d5 EBADE 0x4ad2db EBADR 0x4ad2e1 EXFULL 0x4ad2e8 ENOANO 0x4ad2ef EBADRQC 0x4ad2f7 EBADSLT 0x4ad2ff EBFONT 0x4ad306 ENONET 0x4ad30d ENOPKG 0x4ad314 EADV
A.7 pack
qiling 支持整数类型与字符数组的相互转化,甚至还能够将字符数组转化为 C 语言的结构体。字符数组与结构体的转化是通过 Python 自带模块实现的,规则参考 Python 文档 。
1 2 3 4 5 6 7 8 print (emu.pack8(0x12 )) print (emu.pack16(0x1234 ))print (emu.pack32(0x12345678 ))print (emu.pack64(0x1234567890abcdef ))print (emu.pack8s(-0x12 )) print (emu.pack16s(-0x1234 ))print (emu.pack32s(-0x12345678 ))print (emu.pack64s(-0x1234567890abcdef ))
输出:
1 2 3 4 5 6 7 8 b'\x12' b'4\x12' b'xV4\x12' b'\xef\xcd\xab\x90xV4\x12' b'\xee' b'\xcc\xed' b'\x88\xa9\xcb\xed' b'\x112To\x87\xa9\xcb\xed'