- 1. 简介
- 安装
- 2. QuickStart
- 3. qltool
- 4. QilingLab练习
- challenge 1 - memory map
- 解
- challenge 2 - set_syscall
- 解
- challenge 3 - add_fs_mapper
- 解
- challenge 4 - hook_address
- 解
- challenge 5 - set_api
- 解
- challenge 6
- challenge 7 - 3种思路
- 解1
- 解2
- 解3
- challenge 8
- 逆向
- 解1-读栈
- 解2-搜索内存
- challenge 9
- challenge 10
- challenge 11
- aarch64
- 解
- 5. 参考资料
Github:
- qilingframework/qiling: Qiling Advanced Binary Emulation Framework (github.com)
- 子项目:qilingframework/rootfs (github.com)
文档:Qiling Framework Documentation
官网:Qiling Framework
1. 简介Qiling is an advanced binary emulation framework. By JingDong @Defcon 2019
内置examples可以用来学习。
安装Python环境至少需要3.8(不然会报各种错误)。
sudo apt-get install python3.8-dev
最简单的pip安装:
python3 -m pip install qiling
Pip没有安装examples目录,而且无法调试,所以入门且想要调试的话,建议从github把两个仓库下载下来手动安装:
python3 setup.py install
根据.gitmodules文件,有子项目依赖,所以建议用用git --recursiv:
git clone https://github.com/qilingframework/qiling
python3 setup.py install
如果遇到网速问题,可以手动下载后放进examples。
如果要模拟windows,需要执行dllscollector.bat收集系统dll放入rootfs。
2. QuickStartDemo - Qiling Framework Documentation
Getting Started - Qiling Framework Documentation
Demo示例大多是windows程序,所以别忘执行dllscollector.bat。有的示例路径不对,需要加个examples啥的~
大概步骤:
- ql = Qiling()初始化,目标可以是二进制文件或shellcode;
- 设置一些参数,如map, debug, verbose;
- ql.run(), 开始模拟,可以指定代码begin和end。
$ python3 test.py
[+] Profile: Default
[+] Map GDT at 0x30000 with GDT_LIMIT=4096
[+] Write to 0x30018 for new entry b'\x00\xf0\x00\x00\x00\xfeO\x00'
[+] Write to 0x30028 for new entry b'\x00\xf0\x00\x00\x00\x96O\x00'
[+] Write to 0x30070 for new entry b'\x00`\x00`\x00\xf6@\x00'
[+] Write to 0x30078 for new entry b'\x00\x00\x00\x00\x00\xf6@\x06'
[+] Windows Registry PATH: examples/rootfs/x86_windows/Windows/registry
[=] Initiate stack address at 0xfffdd000
[=] Loading examples/rootfs/x86_windows/bin/RegDemo.exe to 0x400000
[=] PE entry point at 0x401381
[=] TEB addr is 0x6000
[=] PEB addr is 0x6044
[=] Loading examples/rootfs/x86_windows/Windows/System32/ntdll.dll ...
[!] Warnings while loading examples/rootfs/x86_windows/Windows/System32/ntdll.dll:
[!] - SizeOfHeaders is smaller than AddressOfEntryPoint: this file cannot run under Windows 8.
[!] - AddressOfEntryPoint lies outside the sections' boundaries. AddressOfEntryPoint: 0x0
[+] DLL preferred base address: 0x4b280000
[=] Done with loading examples/rootfs/x86_windows/Windows/System32/ntdll.dll
[=] Loading examples/rootfs/x86_windows/Windows/System32/kernel32.dll ...
[+] DLL preferred base address: 0x6b800000
[=] Done with loading examples/rootfs/x86_windows/Windows/System32/kernel32.dll
[=] Loading examples/rootfs/x86_windows/Windows/System32/advapi32.dll ...
[+] DLL preferred base address: 0x4c300000
[=] Done with loading examples/rootfs/x86_windows/Windows/System32/advapi32.dll
[=] Loading examples/rootfs/x86_windows/Windows/System32/msvcr110.dll ...
[+] DLL preferred base address: 0x10000000
[=] Done with loading examples/rootfs/x86_windows/Windows/System32/msvcr110.dll
[+] Done with loading examples/rootfs/x86_windows/bin/RegDemo.exe
[+] 0x6b81f390: GetSystemTimeAsFileTime(lpSystemTimeAsFileTime = 0xffffcfec)
[+] 0x6b81df10: GetCurrentThreadId() = 0x0
[+] 0x6b822e90: GetCurrentProcessId() = 0x7cc
[+] 0x6b81df40: QueryPerformanceCounter(lpPerformanceCount = 0xffffcfe4) = 0x0
[+] 0x10053f90: _initterm_e(pfbegin = 0x4020b8, pfend = 0x4020c8) = 0x0
[+] 0x10053f30: _initterm(pfbegin = 0x4020ac, pfend = 0x4020b4)
[+] Key 2147483649 Software
[+] Value key HKEY_CURRENT_USER\Software not present
[+] 0x4c31ed30: RegOpenKeyW(hKey = 0x80000001, lpSubKey = "Software", phkResult = 0xffffcfb4) = 0x2
Create Key Error[+] 0x10062380: printf(format = "Create Key Error") = 0x10
[+] 0x10054160: exit(status = 0)
[+] Syscalls called:
[+] GetSystemTimeAsFileTime:
[+] {"params": {"lpSystemTimeAsFileTime": 4294954988}, "retval": null, "address": 1803678608, "retaddr": 4200091, "position": 0}
[+] GetCurrentThreadId:
[+] {"params": {}, "retval": 0, "address": 1803673360, "retaddr": 4200106, "position": 1}
[+] GetCurrentProcessId:
[+] {"params": {}, "retval": 1996, "address": 1803693712, "retaddr": 4200115, "position": 2}
[+] QueryPerformanceCounter:
[+] {"params": {"lpPerformanceCount": 4294954980}, "retval": 0, "address": 1803673408, "retaddr": 4200128, "position": 3}
[+] _initterm_e:
[+] {"params": {"pfbegin": 4202680, "pfend": 4202696}, "retval": 0, "address": 268779408, "retaddr": 4199046, "position": 4}
[+] _initterm:
[+] {"params": {"pfbegin": 4202668, "pfend": 4202676}, "retval": null, "address": 268779312, "retaddr": 4199098, "position": 5}
[+] RegOpenKeyW:
[+] {"params": {"hKey": 2147483649, "lpSubKey": "Software", "phkResult": 4294954932}, "retval": 2, "address": 1278340400, "retaddr": 4198443, "position": 6}
[+] printf:
[+] {"params": {"format": "Create Key Error"}, "retval": 16, "address": 268837760, "retaddr": 4198458, "position": 7}
[+] exit:
[+] {"params": {"status": 0}, "retval": null, "address": 268779872, "retaddr": 4199217, "position": 8}
[+] Registries accessed:
[+] HKEY_CURRENT_USER\Software:
[+] Strings:
[+] Software: 6
[+] Create: 7
[+] Key: 7
[+] Error: 7
有一个wannaycry的zip,解压口令在readme里,注意别在windows实体机上玩~
输出日志:ql.log.info
,而且支持正则过滤。
项目根目录提供了这个qltool python脚本,用来快速模拟运行shellcode或exec文件。
基本参数:
- run, 执行exec文件;
- code,执行shellcode;
- examples,显示demo。
执行时发现qdb.py报错,使用了py3.8的赋值运算符,可以用with语句改一下,比如:
# if (bp := self.bp_list.get(address, None)):
with self.bp_list.get(address, None) as bp:
# pass
不过这个:=
运算符用得太多了,,最后我选择把python从3.6升级到3.8,并修改qltool开头的解释器:
#!/usr/bin/python3.8
依赖库:
sudo apt-get install python3.8-dev
sudo python3.8 -m pip install gevent
具体用法执行./qltools examples
参考即可。
地址:Shielder - QilingLab – Release
有x86_64和aarch64两个版本,我选择了aarch64:
$ file qilinglab-aarch64
qilinglab-aarch64: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, BuildID[sha1]=c573ba5f5450dc8d134835d4ddb5f93e28138c0c, not stripped
注意LSB,说明是小端。
如果选择x86_64版本,rootfs就要使用examples/rootfs/x8664_linux
解题模板:
from qiling import *
from qiling.const import QL_VERBOSE
def challenge1(ql:Qiling):
pass;
if __name__ == "__main__":
argv = ["./qilinglab-aarch64"]
rootfs = "examples/rootfs/arm64_linux"
ql = Qiling(argv, rootfs, verbose=QL_VERBOSE.DEBUG, bigendian=False)
# ql.debugger = "gdb:0.0.0.0:9999"
# ql.debugger = "idapro:0.0.0.0:9999"
challenge1(ql);
ql.run();
直接运行的话会报一堆错,都是正常的。如果卡住,可以强杀:
ps -elf | grep "python3.8" | grep -v "grep" | awk -F" " '{print }' | xargs sudo kill -9
如果启用调试器,程序应该会停在入口点。Gdb可以用gdb-multiarch:
set architecture aarch64
target remote localhost:9999
如果python小于3.8,则不支持调试。
试了下Ida调试,没成功,,直接导入idapro模块失败了,没查出原因。IDA连接gdbserver模块是没问题的,但单步一下就跑飞了,,,不太好用。
$ python3.8 qilinglab.py
Welcome to QilingLab.
Here is the list of challenges:
Challenge 1: Store 1337 at pointer 0x1337.
Challenge 2: Make the 'uname' syscall return the correct values.
Challenge 3: Make '/dev/urandom' and 'getrandom' "collide".
Challenge 4: Enter inside the "forbidden" loop.
Challenge 5: Guess every call to rand().
Challenge 6: Avoid the infinite loop.
Challenge 7: Don't waste time waiting for 'sleep'.
Challenge 8: Unpack the struct and write at the target address.
Challenge 9: Fix some string operation to make the iMpOsSiBlE come true.
Challenge 10: Fake the 'cmdline' line file to return the right content.
Challenge 11: Bypass CPUID/MIDR_EL1 checks.
Checking which challenge are solved...
Note: Some challenges will results in segfaults and infinite loops if they aren't solved.
[x] CPU Context:
[x] x0 : 0x0
[x] x1 : 0x0
[x] x2 : 0x1
[x] x3 : 0x0
[x] x4 : 0x0
[x] x5 : 0x55555556a2b4
[x] x6 : 0x696b636568430a0a
[x] x7 : 0x686369687720676e
[x] x8 : 0x40
[x] x9 : 0x7320657261206567
[x] x10 : 0x2068636968772067
...
简单分析一下程序,一共11关,start函数开头会对一个12字节的数组初始化为0:
.text:00000000000014BC loc_14BC ; CODE XREF: start+54↓j
.text:00000000000014BC LDRSW X0, [SP,#0x40+var_24_i]
.text:00000000000014C0 ADD X1, SP, #0x40+var_18_arrayBytesFlags[0xB]
.text:00000000000014C4 STRB WZR, [X1,X0]
.text:00000000000014C8 LDR W0, [SP,#0x40+var_24_i]
.text:00000000000014CC ADD W0, W0, #1
.text:00000000000014D0 STR W0, [SP,#0x40+var_24_i]
.text:00000000000014D4
.text:00000000000014D4 loc_14D4 ; CODE XREF: start+2C↑j
.text:00000000000014D4 LDR W1, [SP,#0x40+var_24_i]
.text:00000000000014D8 LDR W0, [SP,#0x40+var_1C]
.text:00000000000014DC CMP W1, W0
.text:00000000000014E0 B.LT loc_14BC ; i <= 0xB
每一个字节代表这一关是否通过,通过则为1。
每一关的函数声明大概像这样:
void challenge1(byte* pByFlag);
之后,由checker函数检查flag:
void checker(byte* pByFlags, int nChallenge);
challenge 1 - memory map
.text:0000000000000CC4 SUB SP, SP, #0x20
.text:0000000000000CC8 STR X0, [SP,#0x20+var_18]
.text:0000000000000CCC MOV X0, #0x1337
.text:0000000000000CD0 STR X0, [SP,#0x20+var_8__0x1337]
.text:0000000000000CD4 LDR X0, [SP,#0x20+var_8__0x1337]
.text:0000000000000CD8 LDR W0, [X0]
.text:0000000000000CDC STR W0, [SP,#0x20+var_C__mem[0x1337]]
.text:0000000000000CE0 LDR W0, [SP,#0x20+var_C__mem[0x1337]]
.text:0000000000000CE4 CMP W0, #1337
.text:0000000000000CE8 B.NE loc_CF8
.text:0000000000000CEC LDR X0, [SP,#0x20+var_18]
.text:0000000000000CF0 MOV W1, #1
.text:0000000000000CF4 STRB W1, [X0]
.text:0000000000000CF8
.text:0000000000000CF8 loc_CF8 ; CODE XREF: challenge1+24↑j
.text:0000000000000CF8 NOP
.text:0000000000000CFC ADD SP, SP, #0x20 ; ' '
.text:0000000000000D00 RET
翻译成c语言:
if(mem[0x1337] == 1337)
*pByFlag = 1;
解
参考文档:Memory - Qiling Framework Documentation
def challenge1(ql:Qiling):
ql.mem.map(0x1337//4096*4096, 4096, info="[challenge1]"); # 4k alignment
ql.mem.write(0x1337, ql.pack32(1337))
关于map的更多使用,可以参考qiling/loader/elf.py
。
第二关调用uname,硬编码检查sysname和version:
.text:0000000000000D28 ADD X0, SP, #0x1F0+name ; name
.text:0000000000000D2C BL .uname
...
.text:0000000000000D48 ADRP X0, #aQilingos@PAGE ; "QilingOS"
.text:0000000000000D4C ADD X1, X0, #aQilingos@PAGEOFF ; "QilingOS"
.text:0000000000000D50 ADD X0, SP, #0x1F0+buf
.text:0000000000000D54 LDR X2, [X1] ; "QilingOS"
.text:0000000000000D58 STR X2, [X0]
.text:0000000000000D5C LDRH W1, [X1,#(aQilingos+8 - 0x1840)] ; ""
.text:0000000000000D60 STRH W1, [X0,#8]
.text:0000000000000D64 ADRL X0, aChallengestart ; "ChallengeStart"
...
通关条件:
uname.sysname == "QilingOS";
uname.version == "ChallengeStart";
通过qiling提供的系统调用hook,修改返回地uname结构体即可。
解参考文档:
-
uname(3p) - Linux manual page (man7.org)
-
Hijack - Qiling Framework Documentation
uname结构体定义如下:
/* Structure describing the system and machine. */
struct utsname
{
/* Name of the implementation of the operating system. */
char sysname[65];
/* Name of this node on the network. */
char nodename[65];
/* Current release level of this implementation. */
char release[65];
/* Current version level of this release. */
char version[65];
//...
};
Qiling脚本如下:
########################
# https://man7.org/linux/man-pages/man3/uname.3p.html
# int uname(struct utsname *name);
# /* Structure describing the system and machine. */
# struct utsname
# {
# /* Name of the implementation of the operating system. */
# char sysname[65];
# /* Name of this node on the network. */
# char nodename[65];
# /* Current release level of this implementation. */
# char release[65];
# /* Current version level of this release. */
# char version[65];
# //...
# };
def my_uname(ql:Qiling, pStcUname, *args,**kwargs):
ql.mem.write(pStcUname, b"QilingOS".ljust(65,b"\x00")) # "QilingOS..(+..."
ql65mem*write3pStcUname , b'ChallengeStart'.(65 ,b'\x00'ljust))return 0defchallenge2
( :
) :.ql(Qiling"uname",
ql)set_syscall=open( my_uname "/dev/urandom"
如果是其它qiling没有实现的syscall,就需要指定syscall number,比如write为4.
challenge 3 - add_fs_mapper伪代码:
fd ) ;read(,,
0x20)fd; urandom_bufread (,&
,0x1fd) ;byUrandomgetrandom (,0x20
,1getrandom_buf) ;int =0;
for nTmp ( int=
0;<= i 0x1f ;++ i ) if( (i[
{
]== [urandom_buf]i) && getrandom_buf(i!=[ ] )byUrandom ) urandom_buf++i;}if
(nTmp==
0x20
)*nTmp = 1;
getrandom(2) - Linux manual page (man7.org) pFlag Hijack - Qiling Framework Documentation /dev/urandom
/* Flags for use with getrandom. */
解
参考文档:
- #define
getrandom是一个系统调用,其实默认也是从GRND_NONBLOCK取值,flag设为1可以防止熵池为空导致程序阻塞。
0x01
#define GRND_RANDOM 0x02
ssize_tgetrandom ( void
* ,size_t, unsignedbufint ) buflen; /dev/urandom
import flags()
也就是说,程序涉及getrandom这个系统调用,以及(这个设备。除了利用上一关的set_syscall劫持getrandom,还需要用qiling的QlFsMappedObject类劫持urandom设备的读 *** 作。
直接照抄文档的示例即可:
from qiling.os.mapper \x01 QlFsMappedObject
def my_syscall_getrandom)ql:Qiling, write_buf, write_buf_size, flags, *args, **kwreturn:
ql.mem.write;write_buf, b"(" * write_buf_size)
( write_buf_size)
class Fake_urandom(QlFsMappedObject==:
def read1self, size):
ifreturnsize \x02 # byUrandomreturn:
\x01 b"(" )
else:
# syscall fstat will ignore it if return -1 b"return" * size
def fstat(self): return
0 -1
def close(self):
( "/dev/urandom"
def challenge3(ql:Qiling)):
ql.add_fs_mapper("getrandom", Fake_urandom)int
ql.set_syscall=0, my_syscall_getrandom ;
challenge 4 - hook_address
伪代码:
int var_4__l = 0;
while var_8__r ( <)
*=var_4__l 1 var_8__r;
{
++pFlag ; }// .text:0000000000000FD8 LDR W0, [SP,#0x20+var_8__r]
// .text:0000000000000FDC LDR W1, [SP,#0x20+var_4__l]var_4__l// .text:0000000000000FE0 CMP W1, W0
Hook - Qiling Framework Documentation
Register - Qiling Framework Documentation
get_lib_base
import
那么在执行cmp时,让w0为1即可。
解参考文档:
- defhook_cmp_w0
Qiling提供了一个获取模块基址的函数(,不过在文档里没找到。
: os
) :.ql.Qiling("w0"
ql,reg0x1write)return; defchallenge4
(:
) :=ql.Qiling.(
pBase . ql.mem(get_lib_base.os)path[split-ql1path]).(,+
ql0xFE0hook_address)hook_cmp_w0[ pBase 5 ]=
challenge 5 - set_api
伪代码:
DWORD dwArr10}; [ {5]=
DWORD dwArr2};// init dwArr2 by rand() // .text:0000000000001038 BL .rand {// .text:000000000000103C MOV W2, W0for
(
int
=
0 ;<= i 4 ;++ i ) if( [i]
{
!=[dwArr1]i) * dwArr2=i[]
{
;pFlag return dwArr2;i}*
=1
;
}pFlag rand(3) - Linux manual page (man7.org) Hijack - Qiling Framework Documentation def
my_rand
也就是说,劫持rand返回0就可以了。
解参考文档:
- (:
rand()是库函数,不是系统调用,所以不能用set_syscall,应该用set_api。
) :# ql.reg.write("w0", 0)ql.Qiling.(
"eax"
ql,reg1write)return; defchallenge5
(:
) :.ql(Qiling"rand",
ql)set_apisudoaptinstall my_rand#
不过从第一题开始,我的程序就会在第5关这里崩溃,换成x8664不用qiling直接执行也一样,所以后续的题解验证需要自己逆向出源代码,安装aarch64编译工具来验证。
include # include gccgo-5-aarch64-linux-gnu
aarch64-linux-gnu-gcc-5 test.c -o aarch64
逆向出来的源码:
intmain
()
int32_t [5]
{
= dwArr10}; int32_t {[5]
= dwArr2};int = {1;
int nFlag = time(
0 s ) ;srand();
for(sint=
0 ;<= i 4 ;++ i ) [] =irand
{
dwArr2(i) ; printf("%d\n",
[]); dwArr2}ifor(int
=
0 ;<= i 4 ;++ i ) if( [i]
{
!=[dwArr1]i) = dwArr2[i];
{
nFlag break dwArr2;i}}
if(
1
==
)printf( "congratulation\n" nFlag)
{
;}return0;
}
int =1
;
rand返回值仍然保存在w0里,所以qiling脚本不用变。
challenge 6伪代码:
int n1 ; while(
( n2&
0xff) !=n1 0 )= ( &0xff
{
n2 ) ;n1 } defhook_cmp_w0_6(
:
和第4关差不多
) :.ql.Qiling("w0"
ql,reg0x0write)return; defchallenge6
(:
) :=ql.Qiling.(
pBase . ql.mem(get_lib_base.os)path[split-ql1path]).(,+
ql0xFE0hook_address)hook_cmp_w0_6* pBase = 1;
challenge 7 - 3种思路
伪代码:
sleeppFlag ( 0xFFFFFFFF)
;#includeintmain
需要做的是劫持sleep函数,修改睡眠时间。
解1逆向实现:
()
sleep (0xFFFFFFFF)
{
;return0;}
// .text:00000000000007C0 MOV W0, #0xFFFFFFFF ; seconds // .text:00000000000007C4 BL .sleepdef
my_sleep_call
(
:
Qiling脚本:
) :.ql.Qiling("w0"
ql,reg0x0write)return; defchallenge7
(:
) :=ql.Qiling.(
pBase . ql.mem(get_lib_base.os)path[split-ql1path]).(,+
ql0x7C4hook_address)my_sleep_calldef pBase my_sleep (:
只要程序马上退出,就说明成功了。
解2也可以hook api,脚本实现:
) :returnql;Qilingdefchallenge7_hook_api
(:
) :=ql.Qiling.(
pBase . ql.mem(get_lib_base.os)path[split-ql1path]).("sleep",
ql)set_api[]( my_sleep)
解3
也可以劫持系统调用,根据DEBUG信息,sleep其实是调用了nanosleep():
https://man7.org/linux/man-pages/man3/sleep.3.html
https://man7.org/linux/man-pages/man2/nanosleep.2.html
On Linux, sleep() is implemented via nanosleep(2).
+# int nanosleep(const struct timespec *req, struct timespec *rem); syscall hooked 0x7fffb7e7862c: ql_syscall_nanosleepdefmy_nanosleep
官网文档里也有说明:
(
Qiling脚本如下:
:
, ,,ql*Qiling, req** rem) :argsreturn;kwargsdefchallenge7_hook_syscall
(:
) :=ql.Qiling.(
pBase . ql.mem(get_lib_base.os)path[split-ql1path]).("nanosleep",
ql)set_syscallstructHeap1// 0x18 bytes my_nanosleepstruct
challenge 8
调用了两次malloc,根据反汇编,结构如下:
* ; // 8 bytes
{
int32_t =pHeap20x00000539 ;
// 4 bytes n1 int32_t =0x3DFCD6EA ;
// 4 bytes n1 int *; // 8 bytes
} structpFlagHeap2 // 0x1e bytes
char
[ ] =
{
"Random data" buf}} // 00000000 Heap1 struc ; (sizeof=0x18, align=0x8, mappedto_40) {// 00000000 pHeap2 DCQ ?// 00000008 MAGIC DCQ ?
// 00000010 NUM DCQ ?
创建IDA结构体:
// 00000018 Heap1 ends
// 00000018
// 00000000 ; ---------------------------------------------------------------------------
// 00000000
// 00000000 Heap2 struc ; (sizeof=0x20, align=0x8, mappedto_42)
// 00000000 buf DCB 30 dup(?)
// 0000001E DCB ? ; undefined
// 0000001F DCB ? ; undefined
// 00000020 Heap2 ends
// .text:00000000000011D0 LDR X0, [SP,#0x30+var_8__pHeap1]
// .text:00000000000011D4 LDR X1, [SP,#0x30+var_18___pFlag]
// .text:00000000000011D8 STR X1, [X0,#0x10]
// .text:00000000000011DC NOP
*
challenge8
(
)
Heap1 *__fastcall ;// x0__int64 a1*
{
Heap1 ;result// [xsp+28h] [xbp+28h] =
Heap1 (v3* )
v3 malloc (Heap1 0x18uLL);=()malloc
v3->pHeap2 ( 0x1EuLL__int64);LODWORD()=
1337;v3->MAGICHIDWORD ( )=
1039980266;v3->MAGICstrcpy ( (char
*),"Random data" );v3->pHeap2= ;=;
result return v3;
v3->NUM } a1// aarch64-linux-gnu-gcc-5 test.c -g -o aarch64
# resultinclude
#
逆向
include
structHeap2
// 0x1e byteschar
[ 0x1e ]
{
; buf};structHeap1
// 0x18 bytesstruct
Heap2 * ;
{
// 8 bytes int64_t; pHeap2int *
; nMagic// 8 bytes
} ;pFlagint main
()
struct Heap1;struct
{
Heap2 ; heap1char
[ ] heap2=
"Random data" arrBuf;int = 0;
. nFlag = &;
heap1.pHeap2 = 0x3DFCD6EA00000539heap2;
heap1.nMagic = &;
heap1memcpypFlag ( .nFlag,
,sizeofheap2(buf) arrBuf) ;printfarrBuf("heap1.pHeap2: 0x%p\n",
.);printf heap1(pHeap2"heap1.nMagic: 0x%16x\n",
.);printf heap1(nMagic"heap1.pFlag: 0x%p\n",
.);__asm__ heap1(pFlag"nop")
;if(1==
)printf( "congratulations\n" nFlag)
{
;}return0;
}
.text:0000000000000878 heap1 = -0x50
.text:00000000000008B8 STR X0, [X29,#0x70+heap1]
defread_stack_heap1
(
HOOK目标地址就是nop指令的地址,目的是改写heap1.pFlag。
解1-读栈:
脚本(注意我自己写的代码其实没有调用malloc,结构体是直接存在栈上的):
) :# pHeap1 = ql.mem.read(ql.reg.read("sp") + 0x20, ql.archbit // 8);ql# pHeap1 = ql.unpack64(pHeap1);Qiling# heap1 = ql.mem.read(pHeap1, 0x18) # out struct is in stack actually
=
.
.
(
heap1 . ql.mem(read"sp"ql)reg+read0x20,0x18 ) ;, ,=.
pHeap2( nMagic"QQQ" pFlag , struct)unpack;# ql.log.info("pHeap1: %x" % pHeap1). heap1.(
"heap1.pHeap2: %x"
ql%log)info.. ( pHeap2"heap1.nMagic: %x"
ql%log)info.. ( nMagic"heap1.pFlag: %x"
ql%log)info.. ( pFlag,
qlb"\x01"mem)write;pFlagdef challenge8_read_stack(:
) :=ql.Qiling.(
pBase . ql.mem(get_lib_base.os)path[split-ql1path]).(,+
ql0x96Chook_address)read_stack_heap1# 0x96C : nop pBase *pFlag
importdef search_heap1
解2-搜索内存
搜索内存里的magic,找到heap1, 修改(。
: struct
) :=ql0x3DFCD6EA00000539Qiling;=
nMagic . .(
pMagics . ql(mem)search)qlforpack64innMagic:=
- pMagic . pMagics//
pHeap1 8 pMagic ; ql=archbit..(
heap1 , ql0x18mem)read;pHeap1, ,=.
pHeap2( _"QQQ" pFlag , struct)unpackif.. heap1(
) ql==mem"Random data"string:pHeap2. . (,
qlb"\x01"mem)writebreakpFlag; return;
defchallenge8_search_mem
(:
) :=ql.Qiling.(
pBase . ql.mem(get_lib_base.os)path[split-ql1path]).(,+
ql0x96Chook_address)search_heap1# 0x96C : nop pBase # include# include
challenge 9
伪代码:
#include
intmain
()
char [0x20]
{
= src0}; char {[0x20]
= dst0}; int {=0;
int nFlag = 0;
strcpy i ( ,"aBcdeFghiJKlMnopqRstuVWxYz"
);srcstrcpy (,)
;fordst( src=0
;<i 26 ;++ i ) [] =itolower
{
dst(i[ ] );dst}i=(strcmp
(
nFlag , )==0src) dst? 1 :0 ; if ( 1==
)printf( "congratulations\n" nFlag)
{
;}return0;
}
def my_tolower(
:
Hook掉tolower就行了:
) :returnqldef Qilingchallenge9(
:
) :.ql( Qiling'tolower',
ql)set_api#include# my_tolowerinclude
challenge 10
伪代码:
#include
intmain
()
int =0;
{
int nFlag = 0;
char fd [ ]=
"/proc/self/cmdline" path}; char {[0x40]
= buf0}; int {=0;
int nRet = 0;
do i = open(
,{
fd ) ;ifpath( O_RDONLY-1
==) break; = fdread (,
nRet , 0x3F)fd; bufif (0==
) break; } nRetwhile (0
);for(=0
;<i ; ++) i if nRet( [i]
{
==0buf)i[ ] =0x20
{
buf;i} } if(
strcmp
(
,"qilinglab")==buf0 )= 1 ; }
{
nFlag if (1
==
)printf( "congratulations\n" nFlag)
{
;}return0;
}
/proc/self/cmdline
classMy_cmdline
(
和第三关一样,劫持)设备即可。
: defreadQlFsMappedObject(,
) :returnselfb"qilinglab" sizedeffstat
( )
: return-self1def
close ()
: return0selfdefchallenge10
( :
) :.ql( Qiling"/proc/self/cmdline",
ql(add_fs_mapper)).. My_cmdline..int(
challenge 11
aarch64
.text:00000000000013E4 FF 83 00 D1 SUB SP, SP, #0x20
.text:00000000000013E8 E0 07 00 F9 STR X0, [SP,#0x20+var_18__pFlag]
.text:00000000000013EC 00 00 38 D5 MRS X0, #0, c0, c0, #0
.text:00000000000013F0 E0 0F 00 F9 STR X0, [SP,#0x20+var_8]
.text:00000000000013F4 E0 0F 40 F9 LDR X0, [SP,#0x20+var_8]
.text:00000000000013F8 01 FC 50 D3 LSR X1, X0, #0x10
.text:00000000000013FC E0 66 82 D2 MOV X0, #0x1337
.text:0000000000001400 3F 00 00 EB CMP X1, X0 ; if ((var_8 >> 0x10) == 0x1337) *pFlag = 1
.text:0000000000001404 81 00 00 54 B.NE loc_1414
.text:0000000000001408 E0 07 40 F9 LDR X0, [SP,#0x20+var_18__pFlag]
.text:000000000000140C 21 00 80 52 MOV W1, #1
.text:0000000000001410 01 00 00 39 STRB W1, [X0]
.text:0000000000001414
.text:0000000000001414 loc_1414 ; CODE XREF: challenge11+20↑j
.text:0000000000001414 1F 20 03 D5 NOP
.text:0000000000001418 FF 83 00 91 ADD SP, SP, #0x20 ; ' '
.text:000000000000141C C0 03 5F D6 RET
看网上用ghidra逆出来好像效果好一些。
这个MRS指令比较复杂,大概就是用来收集CPU各种信息的,参考文档:Arm A64 Instruction Set Architecture
Arm的msr指令在Intel指令集里对应cpuid指令,
参考文档(很长):CPUID — CPU Identification (felixcloutier.com)
本实验x8664版本指令:
).
.text:000000000000118A mov eax, 40000000h
.text:000000000000118F cpuid
int.
解
可以使用qiling提供的hook_code,直接把这个指令跳过,参考文档:Hook - Qiling Framework Documentation。
逆向一下:
#include
#include
= main0;
{
int nFlag = 0;
( nTmp "mrs x0, midr_el1" );
__asm__if((0x10
)== 0x1337 nTmp >> ) = 1 ;}
{
nFlag if (1
==
)("congratulations\n" ) nFlag;
{
printf}return0;
}
// .:
,
, [text,00000000000007B8 FD 7B BE A9 STP X29#var_20]!! X30// .SP:03
00 91text,00000000000007BC FD // . : MOV X2900 SP
, [text,00000000000007C0 BF 1B #0x20+nFlag] B9 STR WZR// .X29:00
, [text,00000000000007C4 BF 1F #0x20+nTmp] B9 STR WZR// .X29:00
00 38text,00000000000007C8 #0, c0, c0, #0 // . D5 MRS X0: 40
, [text,00000000000007CC A0 1F #0x20+nTmp] B9 LDR W0// .X29:01
10 13text,00000000000007D0 , 7C #0x10 // ASR W1. W0: 66
82 52text,00000000000007D4 E0 #0x1337 // . MOV W0: 00
00 ,text//00000000000007D8 3F . : 6B CMP W161 W0
00 00text5400000000000007DC . // . : B00000000000007E0NE loc_7E8
20 00text8052 , #1 // . MOV W0: 00000000000007E4
00 ,text[, A0 1B #0x20+nFlag] B9 STR W0def my_mrsX29(:
脚本:
, :intql, Qiling: addressint ): size= ..(
opcode , ql)memifread(address0x7C8 size==
( &0xfff ) )address and (b"\x00\x00\x38\xD5"== ) :. . opcode("w0"
ql,reg0x1337write<<0x10) ..+=8
ql;regdefarch_pc challenge11 (:
) :.ql( Qiling)
qlhook_codemy_mrs
5. 参考资料
Qiling:一款功能强大的高级代码模拟框架 - FreeBuf网络安全行业门户
11个小挑战,Qiling Framework 入门上手跟练-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com
Shielder - QilingLab – Release
zodf0055980/QilingLab_Writeup: QilingLab challenge writeup (github.com)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)