-
-
[原创] Qiling框架分析实战:从 QilingLab 详解到 Qiling 源码分析
-
发表于: 2023-5-29 21:42 22985
-
最近在学习 IoT 相关漏洞利用,打算先学习一下 Qiling 框架,为后续的漏洞利用文章做铺垫。 Qiling 框架是基于 unicorn 的多架构平台模拟执行框架,提供的仿真环境很全面,能够在模拟执行的基础上提供统一的分析 API,这个A PI 包括插桩分析、快照、系统调用和API劫持等操作。在2021年JOANSIVION提供了两个QilingLab能够针对Qiling框架各种操作进行学习的程序,并提供了相应的writeup。他提供的 writeup 是 arm 架构的,因此本文以 x86_64 架构为基础,在他的文章上进行相应补充。程序下载地址放在了文末参考链接中。
时隔两年,Qiling 框架进行了很多更新与改动,我在完成 QilingLab 的过程中发现高版本的 Qiling 存在问题,因此从代码审计的角度入手,分析并提出解决方案,如果曾经分析过 QilingLab 的朋友可以直接跳到 Challenge 10 部分。本文将以最为基础的视角来陪着大家从 0 到 1 学习 Qiling 框架,希望大家看完能有所收获。
首先下载Qiling:
使用版本为最新版:
可以看到 QilingLab 提供的11个挑战如下:
运行一下程序,可以看到输出了挑战的所有题目:
先调用Qiling仿真环境运行一下:
发现报错:
接下来进行分析无法运行的原因,按照出题人所说需要解决第一个挑战才能正常进入。首先查看一下文件信息,可以看到程序使用小端顺序,符号表未裁剪:
拖到IDA里看一下函数流程,main函数:
可以看到跳转到start,查看一下start伪代码:
查看调用的checker函数伪代码,可以知道当检查到问题已解决时返回1:
我们梳理一下函数逻辑:循环初始化数组v13所有元素为0,如果完成对应挑战就会改变v13对应数组的值,再用checker进行一次检查v13数组元素是否为0.再将checker返回值传递给变量,再加到下一次的检查结果上。这个过程不断重复,最后将累加起来的值输出到"\nYou solved %d/%d of the challenges\n"
。
拖到IDA里看一下 challenge 1 汇编代码:
可以看出我们需要让内存地址1337h处的值为1337才能让cmp判断相等,rdi传入的是数组地址对应数组首个元素地址,相等后调整rdi地址处的值为1,这也符合后续被checker函数检查数组后进行的一系列操作。
challenge 1 对应的伪代码如下:
在Qiling中编写字节序列常用有以下方式,我选用的是pack16。
根据前面的分析可以得到 challenge 1 writeup:
运行上面的脚本,可以看到 challenge 1 已经解决:
如果我们想使用Qiling进行远程调试的话,应该怎么做?
我们在使用qemu配合IDA进行远程调试时,需要使用下面的qemu-arm-static:
查了一下qiling文档,调试qiling的仿真程序需要在脚本中添加下面的代码:
接着设置一下IDA的gdb debugger,断点在入口函数处,
运行添加dbg后的脚本:
运行后报错:
Qiling对于报错的提示都挺好的,这里需要将qilinglab-x86_64放置到指定的rootfs目录(我这里是使用的rootfs/x8664_linux作为rootfs目录)下运行,复制后更改一下程序运行的路径,更改后的代码如下:
可以看到程序已经可以远程调试了:
Challenge 2: Make the 'uname' syscall return the correct values.
查看challenges 2 汇编代码:
正确的运行逻辑: challenge2 -> loc_555555554BFC -> loc_555555554C6D -> loc_555555554CB8 -> loc_555555554D13 -> loc_555555554D27
对应伪代码:
第一个if判断:
uname(&name)
是一个系统调用函数,它的作用是获取当前系统的名称和版本信息,并将这些信息存储到 struct utsname
类型的结构体变量 name
中,name
使用struct utsname name;
声明 ,如果成功获取的话函数返回值为0,就可以进入else了。else中我们最后需要执行*a1 = 1
,这里面有两个循环进行字符串逐个判断,如果相同就能在最后的if判断中让*a1 = 1
。此处我们需要了解uname的结构体,uname的结构为:
我们需要让name.sysname等于"QilingOS",name.version等于"ChallengeStart"。
出题人提供了源码,可以看到源码的逻辑跟分析的一致:
根据上述分析,我们需要hook系统调用uname。在Qiling中有四种hook方式,这四个方式都是常量:
为了使用上面的hook方式,需要使用 from qiling.const import *
导入Qiling模拟器中的常量。
系统调用返回结构体型数据时,会将结构体的地址存放在寄存器rdi中,因此我们需要使用 ql.arch.regs.rdi
得到rdi中存储的uname地址。
writeup:
用IDA看一下运行脚本后rdi指向的sysname和version,可以看到已经按照我们的脚本进行了修改:
运行脚本,challenge2已解决:
Challenge 3: Make '/dev/urandom' and 'getrandom' "collide".
IDA 看一下 Challenge 3:
Challenge 3 伪代码:
分析程序逻辑,主要关注点在buf,v5与v7三者之间的关系:
read(fd, buf, 0x20uLL)
:从文件描述符fd读取32(0x20)字节的数据,并将其存储在字符数组buf中。原函数:ssize_t read(int fd, void *buf, size_t count);
read(fd, &v5, 1uLL)
:从文件描述符fd读取1字节的数据,并将其存储在字符变量v5中。getrandom(v7, 32LL, 1LL)
:系统调用,从系统提供的随机数源获取随机数据。代码含义是从系统熵池中获取32字节的随机数据,并将其存储在字符数组v7中。
我们需要在i循环中让buf[i]
与v7[i]
相等,且buf[i]
不等于v5
。getrandom是利用系统调用获取随机数,urandom是利用文件读写操作获取随机数,要解决这道题需要让两者一样。关于 /dev/urandom
与 getrandom
的相关知识:
/dev/urandom
是一个 Unix/Linux 系统中的特殊文件,它是一个伪随机数发生器设备文件,用于生成随机数。
在Qiling中使用 ql.add_fs_mapper("/dev/urandom", "/dev/urandom")
将宿主机中的 /dev/urandom (后面的)
设备文件映射到 Qiling 虚拟机中的 /dev/urandom (前面的)
文件上,以便为虚拟机中的程序提供随机数服务。
getrandom使用方法:
我们可以使用QlFsMappedObject自定义文件对象,实现"/dev/urandom":
我们可以通过hook系统调用自定义一个"getrandom",例如:
运行后,可以看到challenge 3已解决:
Challenge 4: Enter inside the "forbidden" loop.
IDA 查看伪代码发现什么都看不到:
根据汇编逻辑,我们最终需要实现 mov byte ptr [rax], 1
,但是在 loc_555555554E40 中 :
实际上就是 0比0,我们需要让[rbp+var_4]
小于[rbp+var_8]
,jl指令才会跳转到loc_555555554E35函数中,执行*a=1
,然后再通过 add [rbp+var_4], 1
跳出循环。
我们根据逻辑patch汇编使得 [rbp+var_8], 1
,就可以得到正确的伪代码:
可以看到我们的分析是正确的,接着就来编写 Qiling challenge 4 writeup:
运行后,可以看到 challenge 4 已经被解决:
Challenge 5: Guess every call to rand().
challenge 5 伪代码如下:
分析代码逻辑,我们想要最后得到*a1 = 1
的话,需要绕过第二个for循环。让rand()得到的值都为0就可以了。hook方法在官方手册 Hijacking OS API (POSIX) 。
运行后没有显示第五个挑战是否被解决,因为被第六题的死循环卡住了。
Challenge 6: Avoid the infinite loop.
查看伪代码,可以看到跟第四题一样:
我们需要执行loc_555555554F0B函数,然后再退出循环。我们需要让ZF=0,jnz才能跳转loc_555555554F0B函数。
主要关注点在test al, al
上,这条指令的含义是将EAX的低8位(al)与自身与运算,主要是用于修改ZF标志位。
因此我们需要将eax设置为0,对mov [rbp+var_5], 1
进行patch,改为 mov [rbp+var_5], 0
,就可以正确生成伪代码了,得到的和我们预期的一致。
运行后可以看到第五个挑战已经解决,但是第六个还没显示,应该是需要解出第七题:
Challenge 7: Don't waste time waiting for 'sleep'.
伪代码如下:
我们可以利用之前学的方法hook sleep函数,还是比较简单的。
运行后,可以看到1-7题已解决:
Challenge 8: Unpack the struct and write at the target address.
伪代码如下:
v2是一个指针,指向malloc分配的内存块,相当于结构体的头指针,结构体整理后的内容如下:
我们需要修改*a1
为1,我使用的是以下方法对程序运行时存在的魔数进行搜索来判断结构体头指针位置,进而根据对应地址进行修改:
运行后可以看到第八题已解出:
Challenge 9: Fix some string operation to make the iMpOsSiBlE come true.
伪代码为:
根据代码,我们需要让 strcmp(src, dest) == 0
,strcmp(str1,str2) 如果str1 == str2
,则返回 0。
tolower函数是将给定的字符转换为小写形式。我们可以通过hook tolower让tolower失效,strcmp自然能够顺利对比一致。
运行后顺利通过:
Challenge 10: Fake the 'cmdline' line file to return the right content.
对应伪代码为:
根据代码,我们需要让 buf 等于 "qilinglab"。
fd:open失败为-1,open成功为0。
/proc/self/cmdline是一个在Linux系统中的特殊文件,它提供了当前进程(即访问/proc/self/cmdline的进程)的命令行参数信息。
假如我们使用命令./my_program arg1 arg2 arg3
启动一个程序时,读取/proc/self/cmdline的过程在 ./my_program
中,/proc/self/cmdline文件的内容将是:
按照第三个挑战的方法,我们可以使用自定义文件对象来更改读取文件时的返回值:
在运行后并没有解题成功,好像是在hook /proc/self/cmdline
这里存在问题,我写了个脚本单独调试以下cmdline读取程序:
c 程序:用来读取cmdline,文件名:test_cmdline
python程序:用来hook test_cmdline 中的文件操作
运行结果如下,可以看到确实在读取操作的过程中存在问题。
切换到 Qilinglab当时使用的版本看一下是否存在这个问题,Qilinglab的文章是21年7月发的,对应Qiling版本应该是1.2.4,使用pip指定版本安装方法:
运行后存在版本不兼容问题,因此我将从这个版本开始直到最新版的每个版本都进行了测试。结果如下:
1.4.3 可以正确进行文件映射:
1.4.4 无法正确进行文件映射:
我们使用的最新版是 1.4.5,也就是说在1.4.3到1.4.4这个版本更新中可能存在问题,因此接下来进行代码审计。文件映射在源码对应的 qiling/os/mapper.py
中,我们对比一下文件改动:
改动如下:
第1点:
在之前,Qiling只支持字符串和对象做为文件系统映射。而QlFsMappedCallable允许用户传入一个类对象real_dest,然后在调用时实例化这个类对象。
用于 add_fs_mapping(self, ql_path: Union[os.PathLike, str], real_dest: Union[str, QlFsMappedObject, QlFsMappedCallable])
。
第2点:
第3点:
第4点:
我们替换1.4.4的mapper.py为1.4.3版本,
更改后依然没变化,报错。接着分析是否是针对 /proc/self/cmdline
有新的处理方式,加载1.4.4的源码到vscode中,发现在 linux.py
文件中新增以下代码:
看一下更新对比:
1.4.4版本的linux.py文件与1.4.3版本的linux.py文件相比,有以下主要改动:
之前在1.4.3版本中模拟/proc目录的方式有两个:
这两种方式存在一定缺点:
所以总的来说1.4.4版本对/proc处理做了封装,使得它更模块化、易用。但是在新增的几个模块中对 '/proc/self/auxv' 等文件做出了额外的限制,我们在映射这几个文件时可能存在问题。因此我怀疑 cmdline 的问题就是在这个地方。
接着看run(self)函数:
这段代码的含义是:
只有在加载ELF文件或可执行程序的时候,才设置/proc文件系统。
因此将此处判断代码注释后,就应该可以成功运行了。注释后运行:
运行成功!
这个问题应该在1.4.4和1.4.5中都存在,我们可以通过新增一个选项 ql = Qiling(path, rootfs, enable_procfs=False)
来控制是否设置/proc文件系统,当enable_procfs=False
时关闭,默认值为True。我们需要修改 core.py
文件:
修改 linux.py
中的代码新增对参数enable_procfs的判断:
这样我们只需要将ql改成下面的代码即可:
然后修改我们的challenge10测试一下,运行成功!
Challenge 11: Bypass CPUID/MIDR_EL1 checks.
伪代码如下:
关键部分汇编代码如下:
我们需要让*a=1
,就要让if的判断顺利通过,if ( __PAIR64__(_RBX, _RCX) == 0x696C6951614C676ELL && (_DWORD)_RDX == 538976354 )
的含义为:
最后可以看到全部题目均已解决:
最后完整的代码为:
跟着上面的文章思路走一遍,我相信大家已经了解了 Qiling 框架的使用方法。通过攥写本文时分析 Qiling 的源码,也加强了我对 Qiling框架的理解。
如果在文中发现问题,欢迎大家在下面留言。
Qiling 官方文档
Qiling lab
Qiling Framework入门,11个挑战快速上手@PJXRocks
浅尝qiling框架-qilinglab writeup@badmonkey
11个小挑战,Qiling Framework 入门上手跟练@Cr0ssx2
git clone
-
-
recurse
-
submodules https:
/
/
github.com
/
qilingframework
/
qiling.git
# 使用 --recurse-submodules 递归下载模块,rootfs是单独更新的项目
git clone
-
-
recurse
-
submodules https:
/
/
github.com
/
qilingframework
/
qiling.git
# 使用 --recurse-submodules 递归下载模块,rootfs是单独更新的项目
> pip show qiling
Name: qiling
Version:
1.4
.
5
Summary: Qiling
is
an advanced binary emulation framework that cross
-
platform
-
architecture
> pip show qiling
Name: qiling
Version:
1.4
.
5
Summary: Qiling
is
an advanced binary emulation framework that cross
-
platform
-
architecture
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.
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.
> .
/
qilinglab
-
x86_64
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.
[
1
]
8036
segmentation fault (core dumped) .
/
qilinglab
-
x86_64
> .
/
qilinglab
-
x86_64
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.
[
1
]
8036
segmentation fault (core dumped) .
/
qilinglab
-
x86_64
from
qiling
import
*
if
__name__
=
=
'__main__'
:
path
=
[
"qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
ql.run()
from
qiling
import
*
if
__name__
=
=
'__main__'
:
path
=
[
"qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
ql.run()
unicorn.unicorn.UcError: Invalid memory read (UC_ERR_READ_UNMAPPED)
unicorn.unicorn.UcError: Invalid memory read (UC_ERR_READ_UNMAPPED)
> readelf
-
h qilinglab
-
x86_64
ELF Header:
Magic:
7f
45
4c
46
02
01
01
00
00
00
00
00
00
00
00
00
Class: ELF64
Data:
2
's complement, little endian
Version:
1
(current)
OS
/
ABI: UNIX
-
System V
ABI Version:
0
Type
: DYN (Shared
object
file
)
Machine: Advanced Micro Devices X86
-
64
Version:
0x1
Entry point address:
0xa80
Start of program headers:
64
(bytes into
file
)
Start of section headers:
15840
(bytes into
file
)
Flags:
0x0
Size of this header:
64
(bytes)
Size of program headers:
56
(bytes)
Number of program headers:
9
Size of section headers:
64
(bytes)
Number of section headers:
29
Section header string table index:
28
> readelf
-
h qilinglab
-
x86_64
ELF Header:
Magic:
7f
45
4c
46
02
01
01
00
00
00
00
00
00
00
00
00
Class: ELF64
Data:
2
's complement, little endian
Version:
1
(current)
OS
/
ABI: UNIX
-
System V
ABI Version:
0
Type
: DYN (Shared
object
file
)
Machine: Advanced Micro Devices X86
-
64
Version:
0x1
Entry point address:
0xa80
Start of program headers:
64
(bytes into
file
)
Start of section headers:
15840
(bytes into
file
)
Flags:
0x0
Size of this header:
64
(bytes)
Size of program headers:
56
(bytes)
Number of program headers:
9
Size of section headers:
64
(bytes)
Number of section headers:
29
Section header string table index:
28
>
file
-
L qilinglab
-
x86_64
qilinglab
-
x86_64: ELF
64
-
bit LSB shared
object
, x86
-
64
, version
1
(SYSV), dynamically linked, interpreter
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
,
for
GNU
/
Linux
3.2
.
0
, BuildID[sha1]
=
76164e6b494c1af9d9f746e2dc7d3663cc23525c
,
not
stripped
>
file
-
L qilinglab
-
x86_64
qilinglab
-
x86_64: ELF
64
-
bit LSB shared
object
, x86
-
64
, version
1
(SYSV), dynamically linked, interpreter
/
lib64
/
ld
-
linux
-
x86
-
64.so
.
2
,
for
GNU
/
Linux
3.2
.
0
, BuildID[sha1]
=
76164e6b494c1af9d9f746e2dc7d3663cc23525c
,
not
stripped
; Attributes: bp
-
based frame
;
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
public main
main proc near
var_10
=
qword ptr
-
10h
var_4
=
dword ptr
-
4
; __unwind {
push rbp
mov rbp, rsp
sub rsp,
10h
mov [rbp
+
var_4], edi
mov [rbp
+
var_10], rsi
mov eax,
0
call start
mov eax,
0
leave
retn
; }
/
/
starts at
15CA
main endp
; Attributes: bp
-
based frame
;
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
public main
main proc near
var_10
=
qword ptr
-
10h
var_4
=
dword ptr
-
4
; __unwind {
push rbp
mov rbp, rsp
sub rsp,
10h
mov [rbp
+
var_4], edi
mov [rbp
+
var_10], rsi
mov eax,
0
call start
mov eax,
0
leave
retn
; }
/
/
starts at
15CA
main endp
unsigned __int64 start()
{
int
v0;
/
/
eax
int
i;
/
/
[rsp
+
0h
] [rbp
-
20h
]
int
v3;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v4;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v5;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v6;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v7;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v8;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v9;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v10;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v11;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v12;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
char v13[
11
];
/
/
[rsp
+
Dh] [rbp
-
13h
] BYREF
char v14[
11
];
/
/
[rsp
+
Eh] [rbp
-
12h
] BYREF
char v15[
11
];
/
/
[rsp
+
10h
] [rbp
-
10h
] BYREF
unsigned __int64 v16;
/
/
[rsp
+
18h
] [rbp
-
8h
]
v16
=
__readfsqword(
0x28u
);
/
/
__readfsqword指令的参数是一个偏移量,
0x28u
是一个无符号整数常量,值为
0x28
,即
40
,表示从FS寄存器中的偏移量为
40
的地址开始读取
64
位数据。
for
( i
=
0
; i <
11
;
+
+
i )
v13[i]
=
0
;
printf(
"Welcome to QilingLab.\nHere is the list of challenges:"
);
printf(
"\nChallenge 1: Store 1337 at pointer 0x1337."
);
printf(
"\nChallenge 2: Make the 'uname' syscall return the correct values."
);
printf(
"\nChallenge 3: Make '/dev/urandom' and 'getrandom' \"collide\"."
);
printf(
"\nChallenge 4: Enter inside the \"forbidden\" loop."
);
printf(
"\nChallenge 5: Guess every call to rand()."
);
printf(
"\nChallenge 6: Avoid the infinite loop."
);
printf(
"\nChallenge 7: Don't waste time waiting for 'sleep'."
);
printf(
"\nChallenge 8: Unpack the struct and write at the target address."
);
printf(
"\nChallenge 9: Fix some string operation to make the iMpOsSiBlE come true."
);
printf(
"\nChallenge 10: Fake the 'cmdline' line file to return the right content."
);
printf(
"\nChallenge 11: Bypass CPUID/MIDR_EL1 checks."
);
puts(
"\n"
"\n"
"Checking which challenge are solved...\n"
"Note: Some challenges will results in segfaults and infinite loops if they aren't solved."
);
challenge1(v13);
v3
=
checker(v13,
0LL
);
challenge2(v14);
v4
=
checker(v13,
1LL
)
+
v3;
challenge3(&v14[
1
]);
v5
=
checker(v13,
2LL
)
+
v4;
challenge4(v15);
v6
=
checker(v13,
3LL
)
+
v5;
challenge5(&v15[
1
]);
v7
=
checker(v13,
4LL
)
+
v6;
challenge6(&v15[
2
]);
v8
=
checker(v13,
5LL
)
+
v7;
challenge7(&v15[
3
]);
v9
=
checker(v13,
6LL
)
+
v8;
challenge8(&v15[
4
]);
v10
=
checker(v13,
7LL
)
+
v9;
challenge9(&v15[
5
]);
v11
=
checker(v13,
8LL
)
+
v10;
challenge10(&v15[
6
]);
v12
=
checker(v13,
9LL
)
+
v11;
challenge11(&v15[
7
]);
v0
=
checker(v13,
10LL
);
printf(
"\nYou solved %d/%d of the challenges\n"
, (unsigned
int
)(v0
+
v12),
11LL
);
return
__readfsqword(
0x28u
) ^ v16;
}
unsigned __int64 start()
{
int
v0;
/
/
eax
int
i;
/
/
[rsp
+
0h
] [rbp
-
20h
]
int
v3;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v4;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v5;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v6;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v7;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v8;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v9;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v10;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v11;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
int
v12;
/
/
[rsp
+
4h
] [rbp
-
1Ch
]
char v13[
11
];
/
/
[rsp
+
Dh] [rbp
-
13h
] BYREF
char v14[
11
];
/
/
[rsp
+
Eh] [rbp
-
12h
] BYREF
char v15[
11
];
/
/
[rsp
+
10h
] [rbp
-
10h
] BYREF
unsigned __int64 v16;
/
/
[rsp
+
18h
] [rbp
-
8h
]
v16
=
__readfsqword(
0x28u
);
/
/
__readfsqword指令的参数是一个偏移量,
0x28u
是一个无符号整数常量,值为
0x28
,即
40
,表示从FS寄存器中的偏移量为
40
的地址开始读取
64
位数据。
for
( i
=
0
; i <
11
;
+
+
i )
v13[i]
=
0
;
printf(
"Welcome to QilingLab.\nHere is the list of challenges:"
);
printf(
"\nChallenge 1: Store 1337 at pointer 0x1337."
);
printf(
"\nChallenge 2: Make the 'uname' syscall return the correct values."
);
printf(
"\nChallenge 3: Make '/dev/urandom' and 'getrandom' \"collide\"."
);
printf(
"\nChallenge 4: Enter inside the \"forbidden\" loop."
);
printf(
"\nChallenge 5: Guess every call to rand()."
);
printf(
"\nChallenge 6: Avoid the infinite loop."
);
printf(
"\nChallenge 7: Don't waste time waiting for 'sleep'."
);
printf(
"\nChallenge 8: Unpack the struct and write at the target address."
);
printf(
"\nChallenge 9: Fix some string operation to make the iMpOsSiBlE come true."
);
printf(
"\nChallenge 10: Fake the 'cmdline' line file to return the right content."
);
printf(
"\nChallenge 11: Bypass CPUID/MIDR_EL1 checks."
);
puts(
"\n"
"\n"
"Checking which challenge are solved...\n"
"Note: Some challenges will results in segfaults and infinite loops if they aren't solved."
);
challenge1(v13);
v3
=
checker(v13,
0LL
);
challenge2(v14);
v4
=
checker(v13,
1LL
)
+
v3;
challenge3(&v14[
1
]);
v5
=
checker(v13,
2LL
)
+
v4;
challenge4(v15);
v6
=
checker(v13,
3LL
)
+
v5;
challenge5(&v15[
1
]);
v7
=
checker(v13,
4LL
)
+
v6;
challenge6(&v15[
2
]);
v8
=
checker(v13,
5LL
)
+
v7;
challenge7(&v15[
3
]);
v9
=
checker(v13,
6LL
)
+
v8;
challenge8(&v15[
4
]);
v10
=
checker(v13,
7LL
)
+
v9;
challenge9(&v15[
5
]);
v11
=
checker(v13,
8LL
)
+
v10;
challenge10(&v15[
6
]);
v12
=
checker(v13,
9LL
)
+
v11;
challenge11(&v15[
7
]);
v0
=
checker(v13,
10LL
);
printf(
"\nYou solved %d/%d of the challenges\n"
, (unsigned
int
)(v0
+
v12),
11LL
);
return
__readfsqword(
0x28u
) ^ v16;
}
__int64 __fastcall checker(__int64 a1,
int
a2)
{
__int64 result;
/
/
rax
if
(
*
(_BYTE
*
)(a2
+
a1) ) ; 检查数组a1中下标为a2的元素是否为
0
,非
0
返回
1
{
printf(
"\nChallenge %d: SOLVED"
, (unsigned
int
)(a2
+
1
));
result
=
1LL
;
}
else
{
printf(
"\nChallenge %d: NOT SOLVED"
, (unsigned
int
)(a2
+
1
));
result
=
0LL
;
}
return
result;
}
__int64 __fastcall checker(__int64 a1,
int
a2)
{
__int64 result;
/
/
rax
if
(
*
(_BYTE
*
)(a2
+
a1) ) ; 检查数组a1中下标为a2的元素是否为
0
,非
0
返回
1
{
printf(
"\nChallenge %d: SOLVED"
, (unsigned
int
)(a2
+
1
));
result
=
1LL
;
}
else
{
printf(
"\nChallenge %d: NOT SOLVED"
, (unsigned
int
)(a2
+
1
));
result
=
0LL
;
}
return
result;
}
.text:
0000000000000B8A
public challenge1
.text:
0000000000000B8A
challenge1 proc near ; CODE XREF: start
+
123
↓p
.text:
0000000000000B8A
.text:
0000000000000B8A
var_18
=
qword ptr
-
18h
; 分配quadword类型的局部变量var_18位于[rbp
-
18h
]的位置
.text:
0000000000000B8A
var_C
=
dword ptr
-
0Ch
; qword
8
字节,dword
4
字节
.text:
0000000000000B8A
var_8
=
qword ptr
-
8
.text:
0000000000000B8A
.text:
0000000000000B8A
; __unwind {
.text:
0000000000000B8A
push rbp
.text:
0000000000000B8B
mov rbp, rsp
.text:
0000000000000B8E
mov [rbp
+
var_18], rdi ; [rbp
-
18h
], rdi 此处rdi值为指向v13的指针
.text:
0000000000000B92
mov [rbp
+
var_8],
1337h
.text:
0000000000000B9A
mov rax, [rbp
+
var_8]
.text:
0000000000000B9E
mov eax, [rax] ; 将内存地址
1337h
的值传递给eax
.text:
0000000000000BA0
mov [rbp
+
var_C], eax
.text:
0000000000000BA3
cmp
[rbp
+
var_C],
539h
;
cmp
jnz: 不相等就跳转loc_BB3进行retn (
539h
=
1337
)
.text:
0000000000000BAA
jnz short loc_BB3
.text:
0000000000000BAC
mov rax, [rbp
+
var_18]
.text:
0000000000000BB0
mov byte ptr [rax],
1
; 将值为
1
的字节写入存储在rax所指向的内存地址中
.text:
0000000000000BB3
.text:
0000000000000BB3
loc_BB3: ; CODE XREF: challenge1
+
20
↑j
.text:
0000000000000BB3
nop
.text:
0000000000000BB4
pop rbp
.text:
0000000000000BB5
retn
.text:
0000000000000BB5
; }
/
/
starts at B8A
.text:
0000000000000B8A
public challenge1
.text:
0000000000000B8A
challenge1 proc near ; CODE XREF: start
+
123
↓p
.text:
0000000000000B8A
.text:
0000000000000B8A
var_18
=
qword ptr
-
18h
; 分配quadword类型的局部变量var_18位于[rbp
-
18h
]的位置
.text:
0000000000000B8A
var_C
=
dword ptr
-
0Ch
; qword
8
字节,dword
4
字节
.text:
0000000000000B8A
var_8
=
qword ptr
-
8
.text:
0000000000000B8A
.text:
0000000000000B8A
; __unwind {
.text:
0000000000000B8A
push rbp
.text:
0000000000000B8B
mov rbp, rsp
.text:
0000000000000B8E
mov [rbp
+
var_18], rdi ; [rbp
-
18h
], rdi 此处rdi值为指向v13的指针
.text:
0000000000000B92
mov [rbp
+
var_8],
1337h
.text:
0000000000000B9A
mov rax, [rbp
+
var_8]
.text:
0000000000000B9E
mov eax, [rax] ; 将内存地址
1337h
的值传递给eax
.text:
0000000000000BA0
mov [rbp
+
var_C], eax
.text:
0000000000000BA3
cmp
[rbp
+
var_C],
539h
;
cmp
jnz: 不相等就跳转loc_BB3进行retn (
539h
=
1337
)
.text:
0000000000000BAA
jnz short loc_BB3
.text:
0000000000000BAC
mov rax, [rbp
+
var_18]
.text:
0000000000000BB0
mov byte ptr [rax],
1
; 将值为
1
的字节写入存储在rax所指向的内存地址中
.text:
0000000000000BB3
.text:
0000000000000BB3
loc_BB3: ; CODE XREF: challenge1
+
20
↑j
.text:
0000000000000BB3
nop
.text:
0000000000000BB4
pop rbp
.text:
0000000000000BB5
retn
.text:
0000000000000BB5
; }
/
/
starts at B8A
.text:
0000000000000B8E
mov [rbp
+
var_18], rdi
...
.text:
0000000000000BAC
mov rax, [rbp
+
var_18]
.text:
0000000000000BB0
mov byte ptr [rax],
1
.text:
0000000000000B8E
mov [rbp
+
var_18], rdi
...
.text:
0000000000000BAC
mov rax, [rbp
+
var_18]
.text:
0000000000000BB0
mov byte ptr [rax],
1
_BYTE
*
__fastcall challenge1(_BYTE
*
a1)
{
_BYTE
*
result;
/
/
rax
result
=
(_BYTE
*
)
*
(unsigned
int
*
)((char
*
)&loc_1335
+
2
);
/
/
标识符loc_xxxx通常用于表示一个内存地址或常量值
if
(
*
(_DWORD
*
)((char
*
)&loc_1335
+
2
)
=
=
1337
)
{
result
=
a1;
*
a1
=
1
;
}
return
result;
}
_BYTE
*
__fastcall challenge1(_BYTE
*
a1)
{
_BYTE
*
result;
/
/
rax
result
=
(_BYTE
*
)
*
(unsigned
int
*
)((char
*
)&loc_1335
+
2
);
/
/
标识符loc_xxxx通常用于表示一个内存地址或常量值
if
(
*
(_DWORD
*
)((char
*
)&loc_1335
+
2
)
=
=
1337
)
{
result
=
a1;
*
a1
=
1
;
}
return
result;
}
ql.mem.write(
0x1000
, b
'\x41\x42\x43'
)
# 手动输入
ql.pack(
">I"
,
0x12345678
)
# 函数调用将返回一个4字节的大端序字节序列,其中包含整数值 0x12345678 的二进制表示。
# ">I"参数指定了这个字节序列的格式,其中">"表示大端序,"<"表示小端序,"I"表示一个32位无符号整数。
ql.pack16(
0x1234
)
# 这个函数专门用于16位整数
ql.mem.write(
0x1000
, b
'\x41\x42\x43'
)
# 手动输入
ql.pack(
">I"
,
0x12345678
)
# 函数调用将返回一个4字节的大端序字节序列,其中包含整数值 0x12345678 的二进制表示。
# ">I"参数指定了这个字节序列的格式,其中">"表示大端序,"<"表示小端序,"I"表示一个32位无符号整数。
ql.pack16(
0x1234
)
# 这个函数专门用于16位整数
from
qiling
import
*
def
challenge1(ql):
ql.mem.
map
(
0x1000
,
0x1000
, info
=
'[challenge1]'
)
# 为地址范围 [0x1000, 0x2000) 映射了一块内存,第一个参数0x1000是映射的起始地址,第二个参数0x1000是映射的大小,映射的大小必须是页大小的倍数,这里的页大小是 Qiling模拟器的默认页大小,4096字节。info是对这部分内存做的一个标记,后续Qiling如果想接着使用的话就可以用这个标记来定位。
ql.mem.write(
0x1337
, ql.pack16(
1337
))
# 将整数值 1337 转换为一个16位字节序列写入内存地址 0x1337 所指定的内存位置,小端输入
if
__name__
=
=
'__main__'
:
path
=
[
"qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
challenge1(ql)
ql.run()
from
qiling
import
*
def
challenge1(ql):
ql.mem.
map
(
0x1000
,
0x1000
, info
=
'[challenge1]'
)
# 为地址范围 [0x1000, 0x2000) 映射了一块内存,第一个参数0x1000是映射的起始地址,第二个参数0x1000是映射的大小,映射的大小必须是页大小的倍数,这里的页大小是 Qiling模拟器的默认页大小,4096字节。info是对这部分内存做的一个标记,后续Qiling如果想接着使用的话就可以用这个标记来定位。
ql.mem.write(
0x1337
, ql.pack16(
1337
))
# 将整数值 1337 转换为一个16位字节序列写入内存地址 0x1337 所指定的内存位置,小端输入
if
__name__
=
=
'__main__'
:
path
=
[
"qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
challenge1(ql)
ql.run()
Challenge
11
: Bypass CPUID
/
MIDR_EL1 checks.
Checking which challenge are solved...
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x54
)
=
0x54
Note: Some challenges will results
in
segfaults
and
infinite loops
if
they aren't solved.
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x5a
)
=
0x5a
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x1
)
=
0x1
[
=
] uname(buf
=
0x80000000db70
)
=
0x0
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
-
0x2
(ENOENT)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dce0
, length
=
0x20
)
=
-
0x9
(EBADF)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dcdf
, length
=
0x1
)
=
-
0x9
(EBADF)
[
=
] close(fd
=
0xffffffff
)
=
-
0x1
(EPERM)
[
=
] getrandom(buf
=
0x80000000dd00
, buflen
=
0x20
, flags
=
0x1
)
=
0x20
Challenge
2
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
Challenge
3
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
[
=
] time()
=
0x64704df9
Challenge
4
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
Challenge
11
: Bypass CPUID
/
MIDR_EL1 checks.
Checking which challenge are solved...
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x54
)
=
0x54
Note: Some challenges will results
in
segfaults
and
infinite loops
if
they aren't solved.
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x5a
)
=
0x5a
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x1
)
=
0x1
[
=
] uname(buf
=
0x80000000db70
)
=
0x0
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
-
0x2
(ENOENT)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dce0
, length
=
0x20
)
=
-
0x9
(EBADF)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dcdf
, length
=
0x1
)
=
-
0x9
(EBADF)
[
=
] close(fd
=
0xffffffff
)
=
-
0x1
(EPERM)
[
=
] getrandom(buf
=
0x80000000dd00
, buflen
=
0x20
, flags
=
0x1
)
=
0x20
Challenge
2
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
Challenge
3
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
[
=
] time()
=
0x64704df9
Challenge
4
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
sudo chroot .
/
.
/
qemu
-
arm
-
static
-
g
12345
.
/
bin
/
httpd
sudo chroot .
/
.
/
qemu
-
arm
-
static
-
g
12345
.
/
bin
/
httpd
ql.debugger
=
'gdb:0.0.0.0:12345'
ql.debugger
=
'gdb:0.0.0.0:12345'
ValueError:
'/home/ubuntu/Desktop/qilinglab/qilinglab-x86_64'
does
not
start with
'/home/ubuntu/Desktop/qilinglab/rootfs/x8664_linux'
ValueError:
'/home/ubuntu/Desktop/qilinglab/qilinglab-x86_64'
does
not
start with
'/home/ubuntu/Desktop/qilinglab/rootfs/x8664_linux'
from
qiling
import
*
def
challenge1(ql):
ql.mem.
map
(
0x1000
,
0x1000
, info
=
'[challenge1]'
)
ql.mem.write(
0x1337
, ql.pack16(
1337
))
if
__name__
=
=
'__main__'
:
path
=
[
"rootfs/x8664_linux/qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
challenge1(ql)
ql.debugger
=
'gdb:0.0.0.0:12345'
ql.run()
from
qiling
import
*
def
challenge1(ql):
ql.mem.
map
(
0x1000
,
0x1000
, info
=
'[challenge1]'
)
ql.mem.write(
0x1337
, ql.pack16(
1337
))
if
__name__
=
=
'__main__'
:
path
=
[
"rootfs/x8664_linux/qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
challenge1(ql)
ql.debugger
=
'gdb:0.0.0.0:12345'
ql.run()
.text:
0000555555554BB6
public challenge2
.text:
0000555555554BB6
challenge2 proc near ; CODE XREF: start
+
147
↓p
.text:
0000555555554BB6
.text:
0000555555554BB6
var_1D8
=
qword ptr
-
1D8h
.text:
0000555555554BB6
var_1D0
=
dword ptr
-
1D0h
.text:
0000555555554BB6
var_1CC
=
dword ptr
-
1CCh
.text:
0000555555554BB6
var_1C8
=
dword ptr
-
1C8h
.text:
0000555555554BB6
var_1C4
=
dword ptr
-
1C4h
.text:
0000555555554BB6
name
=
utsname ptr
-
1C0h
.text:
0000555555554BB6
s
=
byte ptr
-
3Ah
.text:
0000555555554BB6
var_32
=
word ptr
-
32h
.text:
0000555555554BB6
var_30
=
byte ptr
-
30h
.text:
0000555555554BB6
var_18
=
qword ptr
-
18h
.text:
0000555555554BB6
.text:
0000555555554BB6
push rbp
.text:
0000555555554BB7
mov rbp, rsp
.text:
0000555555554BBA
push rbx
.text:
0000555555554BBB
sub rsp,
1D8h
.text:
0000555555554BC2
mov [rbp
+
var_1D8], rdi
.text:
0000555555554BC9
mov rax, fs:
28h
; 从Segment fs(用于TLS)对应的内存段,偏移量
40h
处取一个值,把这个值加载到rax寄存器
.text:
0000555555554BD2
mov [rbp
+
var_18], rax
.text:
0000555555554BD6
xor eax, eax ; 清零eax寄存器,更新零标志位ZF
=
1
(表示eax为
0
)
.text:
0000555555554BD8
lea rax, [rbp
+
name] ; 将[rbp
+
name]的地址传给rax
.text:
0000555555554BDF
mov rdi, rax ; name
.text:
0000555555554BE2
call _uname
.text:
0000555555554BE7
test eax, eax ; 不修改eax的内容,只更新标志位
.text:
0000555555554BE9
jz short loc_555555554BFC ; ZF
=
1
,则转移到目的地址
.text:
0000555555554BEB
lea rdi, s ;
"uname"
.text:
0000555555554BF2
call _perror ; _perror() 用于打印错误信息
.text:
0000555555554BF7
jmp loc_555555554D13
.text:
0000555555554BFC
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554BFC
.text:
0000555555554BFC
loc_555555554BFC: ; CODE XREF: challenge2
+
33
↑j
.text:
0000555555554BFC
mov rax, cs:qword_55555555567E
.text:
0000555555554C03
mov qword ptr [rbp
+
s], rax
.text:
0000555555554C07
movzx eax, cs:word_555555555686
.text:
0000555555554C0E
mov [rbp
+
var_32], ax
.text:
0000555555554C12
mov rax, cs:qword_555555555688
.text:
0000555555554C19
mov rdx, cs:qword_555555555690
.text:
0000555555554C20
mov qword ptr [rbp
+
var_30], rax
.text:
0000555555554C24
mov qword ptr [rbp
+
var_30
+
8
], rdx
.text:
0000555555554C28
mov [rbp
+
var_1D0],
0
.text:
0000555555554C32
mov [rbp
+
var_1CC],
0
.text:
0000555555554C3C
jmp short loc_555555554C6D
.text:
0000555555554C3E
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554C3E
.text:
0000555555554C3E
loc_555555554C3E: ; CODE XREF: challenge2
+
CF↓j
.text:
0000555555554C3E
mov eax, [rbp
+
var_1C8]
.text:
0000555555554C44
cdqe
.text:
0000555555554C46
movzx edx, [rbp
+
rax
+
name.sysname]
.text:
0000555555554C4E
mov eax, [rbp
+
var_1C8]
.text:
0000555555554C54
cdqe
.text:
0000555555554C56
movzx eax, [rbp
+
rax
+
s]
.text:
0000555555554C5B
cmp
dl, al
.text:
0000555555554C5D
jnz short loc_555555554C66
.text:
0000555555554C5F
add [rbp
+
var_1D0],
1
.text:
0000555555554C66
.text:
0000555555554C66
loc_555555554C66: ; CODE XREF: challenge2
+
A7↑j
.text:
0000555555554C66
add [rbp
+
var_1C8],
1
.text:
0000555555554C6D
.text:
0000555555554C6D
loc_555555554C6D: ; CODE XREF: challenge2
+
86
↑j
.text:
0000555555554C6D
mov eax, [rbp
+
var_1C8]
.text:
0000555555554C73
movsxd rbx, eax
.text:
0000555555554C76
lea rax, [rbp
+
s]
.text:
0000555555554C7A
mov rdi, rax ; s
.text:
0000555555554C7D
call _strlen
.text:
0000555555554C82
cmp
rbx, rax ; 相等,则ZF
=
1
, CF
=
0
, OF
=
0
; 小于,则ZF
=
0
, CF
=
1
, OF
=
0
; 大于,则ZF
=
0
, CF
=
0
, OF
=
0
.text:
0000555555554C85
jb short loc_555555554C3E ; 前一条指令执行的结果为小于则跳转
.text:
0000555555554C87
jmp short loc_555555554CB8 ; 此处跳出循环
.text:
0000555555554C89
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554C89
.text:
0000555555554C89
loc_555555554C89: ; CODE XREF: challenge2
+
11A
↓j
.text:
0000555555554C89
mov eax, [rbp
+
var_1C4]
.text:
0000555555554C8F
cdqe
.text:
0000555555554C91
movzx edx, [rbp
+
rax
+
name.version]
.text:
0000555555554C99
mov eax, [rbp
+
var_1C4]
.text:
0000555555554C9F
cdqe
.text:
0000555555554CA1
movzx eax, [rbp
+
rax
+
var_30]
.text:
0000555555554CA6
cmp
dl, al
.text:
0000555555554CA8
jnz short loc_555555554CB1
.text:
0000555555554CAA
add [rbp
+
var_1CC],
1
.text:
0000555555554CB1
.text:
0000555555554CB1
loc_555555554CB1: ; CODE XREF: challenge2
+
F2↑j
.text:
0000555555554CB1
add [rbp
+
var_1C4],
1
.text:
0000555555554CB8
.text:
0000555555554CB8
loc_555555554CB8: ; CODE XREF: challenge2
+
D1↑j
.text:
0000555555554CB8
mov eax, [rbp
+
var_1C4]
.text:
0000555555554CBE
movsxd rbx, eax ; 将eax寄存器中的
32
位符号扩展为
64
位,并将结果存储到rbx寄存器中
.text:
0000555555554CC1
lea rax, [rbp
+
var_30]
.text:
0000555555554CC5
mov rdi, rax ; s
.text:
0000555555554CC8
call _strlen
.text:
0000555555554CCD
cmp
rbx, rax
.text:
0000555555554CD0
jb short loc_555555554C89
.text:
0000555555554CD2
mov ebx, [rbp
+
var_1D0]
.text:
0000555555554CD8
lea rax, [rbp
+
s]
.text:
0000555555554CDC
mov rdi, rax ; s
.text:
0000555555554CDF
call _strlen
.text:
0000555555554CE4
cmp
rbx, rax
.text:
0000555555554CE7
jnz short loc_555555554D13
.text:
0000555555554CE9
mov ebx, [rbp
+
var_1CC]
.text:
0000555555554CEF
lea rax, [rbp
+
var_30]
.text:
0000555555554CF3
mov rdi, rax ; s
.text:
0000555555554CF6
call _strlen
.text:
0000555555554CFB
cmp
rbx, rax
.text:
0000555555554CFE
jnz short loc_555555554D13
.text:
0000555555554D00
cmp
[rbp
+
var_1D0],
5
.text:
0000555555554D07
jbe short loc_555555554D13
.text:
0000555555554D09
mov rax, [rbp
+
var_1D8]
.text:
0000555555554D10
mov byte ptr [rax],
1
.text:
0000555555554D13
.text:
0000555555554D13
loc_555555554D13: ; CODE XREF: challenge2
+
41
↑j
.text:
0000555555554D13
; challenge2
+
131
↑j ...
.text:
0000555555554D13
mov rax, [rbp
+
var_18]
.text:
0000555555554D17
xor rax, fs:
28h
.text:
0000555555554D20
jz short loc_555555554D27
.text:
0000555555554D22
call ___stack_chk_fail
.text:
0000555555554D27
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554D27
.text:
0000555555554D27
loc_555555554D27: ; CODE XREF: challenge2
+
16A
↑j
.text:
0000555555554D27
add rsp,
1D8h
.text:
0000555555554D2E
pop rbx
.text:
0000555555554D2F
pop rbp
.text:
0000555555554D30
retn
.text:
0000555555554D30
challenge2 endp
.text:
0000555555554BB6
public challenge2
.text:
0000555555554BB6
challenge2 proc near ; CODE XREF: start
+
147
↓p
.text:
0000555555554BB6
.text:
0000555555554BB6
var_1D8
=
qword ptr
-
1D8h
.text:
0000555555554BB6
var_1D0
=
dword ptr
-
1D0h
.text:
0000555555554BB6
var_1CC
=
dword ptr
-
1CCh
.text:
0000555555554BB6
var_1C8
=
dword ptr
-
1C8h
.text:
0000555555554BB6
var_1C4
=
dword ptr
-
1C4h
.text:
0000555555554BB6
name
=
utsname ptr
-
1C0h
.text:
0000555555554BB6
s
=
byte ptr
-
3Ah
.text:
0000555555554BB6
var_32
=
word ptr
-
32h
.text:
0000555555554BB6
var_30
=
byte ptr
-
30h
.text:
0000555555554BB6
var_18
=
qword ptr
-
18h
.text:
0000555555554BB6
.text:
0000555555554BB6
push rbp
.text:
0000555555554BB7
mov rbp, rsp
.text:
0000555555554BBA
push rbx
.text:
0000555555554BBB
sub rsp,
1D8h
.text:
0000555555554BC2
mov [rbp
+
var_1D8], rdi
.text:
0000555555554BC9
mov rax, fs:
28h
; 从Segment fs(用于TLS)对应的内存段,偏移量
40h
处取一个值,把这个值加载到rax寄存器
.text:
0000555555554BD2
mov [rbp
+
var_18], rax
.text:
0000555555554BD6
xor eax, eax ; 清零eax寄存器,更新零标志位ZF
=
1
(表示eax为
0
)
.text:
0000555555554BD8
lea rax, [rbp
+
name] ; 将[rbp
+
name]的地址传给rax
.text:
0000555555554BDF
mov rdi, rax ; name
.text:
0000555555554BE2
call _uname
.text:
0000555555554BE7
test eax, eax ; 不修改eax的内容,只更新标志位
.text:
0000555555554BE9
jz short loc_555555554BFC ; ZF
=
1
,则转移到目的地址
.text:
0000555555554BEB
lea rdi, s ;
"uname"
.text:
0000555555554BF2
call _perror ; _perror() 用于打印错误信息
.text:
0000555555554BF7
jmp loc_555555554D13
.text:
0000555555554BFC
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554BFC
.text:
0000555555554BFC
loc_555555554BFC: ; CODE XREF: challenge2
+
33
↑j
.text:
0000555555554BFC
mov rax, cs:qword_55555555567E
.text:
0000555555554C03
mov qword ptr [rbp
+
s], rax
.text:
0000555555554C07
movzx eax, cs:word_555555555686
.text:
0000555555554C0E
mov [rbp
+
var_32], ax
.text:
0000555555554C12
mov rax, cs:qword_555555555688
.text:
0000555555554C19
mov rdx, cs:qword_555555555690
.text:
0000555555554C20
mov qword ptr [rbp
+
var_30], rax
.text:
0000555555554C24
mov qword ptr [rbp
+
var_30
+
8
], rdx
.text:
0000555555554C28
mov [rbp
+
var_1D0],
0
.text:
0000555555554C32
mov [rbp
+
var_1CC],
0
.text:
0000555555554C3C
jmp short loc_555555554C6D
.text:
0000555555554C3E
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554C3E
.text:
0000555555554C3E
loc_555555554C3E: ; CODE XREF: challenge2
+
CF↓j
.text:
0000555555554C3E
mov eax, [rbp
+
var_1C8]
.text:
0000555555554C44
cdqe
.text:
0000555555554C46
movzx edx, [rbp
+
rax
+
name.sysname]
.text:
0000555555554C4E
mov eax, [rbp
+
var_1C8]
.text:
0000555555554C54
cdqe
.text:
0000555555554C56
movzx eax, [rbp
+
rax
+
s]
.text:
0000555555554C5B
cmp
dl, al
.text:
0000555555554C5D
jnz short loc_555555554C66
.text:
0000555555554C5F
add [rbp
+
var_1D0],
1
.text:
0000555555554C66
.text:
0000555555554C66
loc_555555554C66: ; CODE XREF: challenge2
+
A7↑j
.text:
0000555555554C66
add [rbp
+
var_1C8],
1
.text:
0000555555554C6D
.text:
0000555555554C6D
loc_555555554C6D: ; CODE XREF: challenge2
+
86
↑j
.text:
0000555555554C6D
mov eax, [rbp
+
var_1C8]
.text:
0000555555554C73
movsxd rbx, eax
.text:
0000555555554C76
lea rax, [rbp
+
s]
.text:
0000555555554C7A
mov rdi, rax ; s
.text:
0000555555554C7D
call _strlen
.text:
0000555555554C82
cmp
rbx, rax ; 相等,则ZF
=
1
, CF
=
0
, OF
=
0
; 小于,则ZF
=
0
, CF
=
1
, OF
=
0
; 大于,则ZF
=
0
, CF
=
0
, OF
=
0
.text:
0000555555554C85
jb short loc_555555554C3E ; 前一条指令执行的结果为小于则跳转
.text:
0000555555554C87
jmp short loc_555555554CB8 ; 此处跳出循环
.text:
0000555555554C89
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554C89
.text:
0000555555554C89
loc_555555554C89: ; CODE XREF: challenge2
+
11A
↓j
.text:
0000555555554C89
mov eax, [rbp
+
var_1C4]
.text:
0000555555554C8F
cdqe
.text:
0000555555554C91
movzx edx, [rbp
+
rax
+
name.version]
.text:
0000555555554C99
mov eax, [rbp
+
var_1C4]
.text:
0000555555554C9F
cdqe
.text:
0000555555554CA1
movzx eax, [rbp
+
rax
+
var_30]
.text:
0000555555554CA6
cmp
dl, al
.text:
0000555555554CA8
jnz short loc_555555554CB1
.text:
0000555555554CAA
add [rbp
+
var_1CC],
1
.text:
0000555555554CB1
.text:
0000555555554CB1
loc_555555554CB1: ; CODE XREF: challenge2
+
F2↑j
.text:
0000555555554CB1
add [rbp
+
var_1C4],
1
.text:
0000555555554CB8
.text:
0000555555554CB8
loc_555555554CB8: ; CODE XREF: challenge2
+
D1↑j
.text:
0000555555554CB8
mov eax, [rbp
+
var_1C4]
.text:
0000555555554CBE
movsxd rbx, eax ; 将eax寄存器中的
32
位符号扩展为
64
位,并将结果存储到rbx寄存器中
.text:
0000555555554CC1
lea rax, [rbp
+
var_30]
.text:
0000555555554CC5
mov rdi, rax ; s
.text:
0000555555554CC8
call _strlen
.text:
0000555555554CCD
cmp
rbx, rax
.text:
0000555555554CD0
jb short loc_555555554C89
.text:
0000555555554CD2
mov ebx, [rbp
+
var_1D0]
.text:
0000555555554CD8
lea rax, [rbp
+
s]
.text:
0000555555554CDC
mov rdi, rax ; s
.text:
0000555555554CDF
call _strlen
.text:
0000555555554CE4
cmp
rbx, rax
.text:
0000555555554CE7
jnz short loc_555555554D13
.text:
0000555555554CE9
mov ebx, [rbp
+
var_1CC]
.text:
0000555555554CEF
lea rax, [rbp
+
var_30]
.text:
0000555555554CF3
mov rdi, rax ; s
.text:
0000555555554CF6
call _strlen
.text:
0000555555554CFB
cmp
rbx, rax
.text:
0000555555554CFE
jnz short loc_555555554D13
.text:
0000555555554D00
cmp
[rbp
+
var_1D0],
5
.text:
0000555555554D07
jbe short loc_555555554D13
.text:
0000555555554D09
mov rax, [rbp
+
var_1D8]
.text:
0000555555554D10
mov byte ptr [rax],
1
.text:
0000555555554D13
.text:
0000555555554D13
loc_555555554D13: ; CODE XREF: challenge2
+
41
↑j
.text:
0000555555554D13
; challenge2
+
131
↑j ...
.text:
0000555555554D13
mov rax, [rbp
+
var_18]
.text:
0000555555554D17
xor rax, fs:
28h
.text:
0000555555554D20
jz short loc_555555554D27
.text:
0000555555554D22
call ___stack_chk_fail
.text:
0000555555554D27
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554D27
.text:
0000555555554D27
loc_555555554D27: ; CODE XREF: challenge2
+
16A
↑j
.text:
0000555555554D27
add rsp,
1D8h
.text:
0000555555554D2E
pop rbx
.text:
0000555555554D2F
pop rbp
.text:
0000555555554D30
retn
.text:
0000555555554D30
challenge2 endp
unsigned __int64 __fastcall challenge2(_BYTE
*
a1)
{
unsigned
int
v2;
/
/
[rsp
+
10h
] [rbp
-
1D0h
]
int
v3;
/
/
[rsp
+
14h
] [rbp
-
1CCh
]
int
v4;
/
/
[rsp
+
18h
] [rbp
-
1C8h
]
int
v5;
/
/
[rsp
+
1Ch
] [rbp
-
1C4h
]
struct utsname name;
/
/
[rsp
+
20h
] [rbp
-
1C0h
] BYREF
char s[
10
];
/
/
[rsp
+
1A6h
] [rbp
-
3Ah
] BYREF
char v8[
24
];
/
/
[rsp
+
1B0h
] [rbp
-
30h
] BYREF
unsigned __int64 v9;
/
/
[rsp
+
1C8h
] [rbp
-
18h
]
v9
=
__readfsqword(
0x28u
);
if
( uname(&name) )
{
perror(
"uname"
);
}
else
{
strcpy(s,
"QilingOS"
);
strcpy(v8,
"ChallengeStart"
);
v2
=
0
;
v3
=
0
;
while
( v4 < strlen(s) )
{
if
( name.sysname[v4]
=
=
s[v4] )
+
+
v2;
+
+
v4;
}
while
( v5 < strlen(v8) )
{
if
( name.version[v5]
=
=
v8[v5] )
+
+
v3;
+
+
v5;
}
if
( v2
=
=
strlen(s) && v3
=
=
strlen(v8) && v2 >
5
)
*
a1
=
1
;
}
return
__readfsqword(
0x28u
) ^ v9;
}
unsigned __int64 __fastcall challenge2(_BYTE
*
a1)
{
unsigned
int
v2;
/
/
[rsp
+
10h
] [rbp
-
1D0h
]
int
v3;
/
/
[rsp
+
14h
] [rbp
-
1CCh
]
int
v4;
/
/
[rsp
+
18h
] [rbp
-
1C8h
]
int
v5;
/
/
[rsp
+
1Ch
] [rbp
-
1C4h
]
struct utsname name;
/
/
[rsp
+
20h
] [rbp
-
1C0h
] BYREF
char s[
10
];
/
/
[rsp
+
1A6h
] [rbp
-
3Ah
] BYREF
char v8[
24
];
/
/
[rsp
+
1B0h
] [rbp
-
30h
] BYREF
unsigned __int64 v9;
/
/
[rsp
+
1C8h
] [rbp
-
18h
]
v9
=
__readfsqword(
0x28u
);
if
( uname(&name) )
{
perror(
"uname"
);
}
else
{
strcpy(s,
"QilingOS"
);
strcpy(v8,
"ChallengeStart"
);
v2
=
0
;
v3
=
0
;
while
( v4 < strlen(s) )
{
if
( name.sysname[v4]
=
=
s[v4] )
+
+
v2;
+
+
v4;
}
while
( v5 < strlen(v8) )
{
if
( name.version[v5]
=
=
v8[v5] )
+
+
v3;
+
+
v5;
}
if
( v2
=
=
strlen(s) && v3
=
=
strlen(v8) && v2 >
5
)
*
a1
=
1
;
}
return
__readfsqword(
0x28u
) ^ v9;
}
if
( uname(&name) )
{
perror(
"uname"
);
}
if
( uname(&name) )
{
perror(
"uname"
);
}
struct utsname {
char sysname[
65
];
char nodename[
65
];
char release[
65
];
char version[
65
];
char machine[
65
];
char domainname[
65
];
};
struct utsname {
char sysname[
65
];
char nodename[
65
];
char release[
65
];
char version[
65
];
char machine[
65
];
char domainname[
65
];
};
void challenge2(char
*
check) {
unsigned
int
i, j, k, l;
struct utsname name;
char qiling_OS[
10
];
char chall_start[
24
];
if
( uname(&name) ) {
perror(
"uname"
);
}
else
{
strcpy(qiling_OS,
"QilingOS"
);
strcpy(chall_start,
"ChallengeStart"
);
i
=
0
;
j
=
0
;
while
( k < strlen(qiling_OS) ) {
if
( name.sysname[k]
=
=
qiling_OS[k] )
+
+
i;
+
+
k;
}
while
( l < strlen(chall_start) ) {
if
( name.version[l]
=
=
chall_start[l] )
+
+
j;
+
+
l;
}
if
( i
=
=
strlen(qiling_OS) && j
=
=
strlen(chall_start) && i >
5
)
*
check
=
1
;
}
}
void challenge2(char
*
check) {
unsigned
int
i, j, k, l;
struct utsname name;
char qiling_OS[
10
];
char chall_start[
24
];
if
( uname(&name) ) {
perror(
"uname"
);
}
else
{
strcpy(qiling_OS,
"QilingOS"
);
strcpy(chall_start,
"ChallengeStart"
);
i
=
0
;
j
=
0
;
while
( k < strlen(qiling_OS) ) {
if
( name.sysname[k]
=
=
qiling_OS[k] )
+
+
i;
+
+
k;
}
while
( l < strlen(chall_start) ) {
if
( name.version[l]
=
=
chall_start[l] )
+
+
j;
+
+
l;
}
if
( i
=
=
strlen(qiling_OS) && j
=
=
strlen(chall_start) && i >
5
)
*
check
=
1
;
}
}
from
qiling
import
*
from
qiling.const
import
*
# 导入Qiling模拟器中的常量
def
my_uname_on_exit_hook(ql,
*
args):
rdi
=
ql.arch.regs.rdi
print
(f
"utsname address: {hex(rdi)}"
)
# utsname address: 0x80000000db50
ql.mem.write(rdi, b
'QilingOS\x00'
)
ql.mem.write(rdi
+
65
*
3
, b
'ChallengeStart\x00'
)
# 000080000000DC13
def
challenge2(ql):
# 使用QL_INTERCEPT.EXIT在系统调用执行完成之后立即执行hook函数,ql.os.set_syscall 使用方法在官方文档 Hijack 部分
ql.os.set_syscall(
"uname"
, my_uname_on_exit_hook, QL_INTERCEPT.EXIT)
if
__name__
=
=
'__main__'
:
path
=
[
"rootfs/x8664_linux/qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
# 记得加上challenge1
challenge2(ql)
ql.run()
from
qiling
import
*
from
qiling.const
import
*
# 导入Qiling模拟器中的常量
def
my_uname_on_exit_hook(ql,
*
args):
rdi
=
ql.arch.regs.rdi
print
(f
"utsname address: {hex(rdi)}"
)
# utsname address: 0x80000000db50
ql.mem.write(rdi, b
'QilingOS\x00'
)
ql.mem.write(rdi
+
65
*
3
, b
'ChallengeStart\x00'
)
# 000080000000DC13
def
challenge2(ql):
# 使用QL_INTERCEPT.EXIT在系统调用执行完成之后立即执行hook函数,ql.os.set_syscall 使用方法在官方文档 Hijack 部分
ql.os.set_syscall(
"uname"
, my_uname_on_exit_hook, QL_INTERCEPT.EXIT)
if
__name__
=
=
'__main__'
:
path
=
[
"rootfs/x8664_linux/qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
# 记得加上challenge1
challenge2(ql)
ql.run()
Checking which challenge are solved...
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x54
)
=
0x54
Note: Some challenges will results
in
segfaults
and
infinite loops
if
they aren't solved.
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x5a
)
=
0x5a
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x1
)
=
0x1
[
=
] uname(buf
=
0x80000000db50
)
=
0x0
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
-
0x2
(ENOENT)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dcc0
, length
=
0x20
)
=
-
0x9
(EBADF)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dcbf
, length
=
0x1
)
=
-
0x9
(EBADF)
[
=
] close(fd
=
0xffffffff
)
=
-
0x1
(EPERM)
[
=
] getrandom(buf
=
0x80000000dce0
, buflen
=
0x20
, flags
=
0x1
)
=
0x20
Challenge
2
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
Challenge
3
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
[
=
] time()
=
0x6471f7b0
Challenge
4
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
Checking which challenge are solved...
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x54
)
=
0x54
Note: Some challenges will results
in
segfaults
and
infinite loops
if
they aren't solved.
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x5a
)
=
0x5a
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x1
)
=
0x1
[
=
] uname(buf
=
0x80000000db50
)
=
0x0
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
-
0x2
(ENOENT)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dcc0
, length
=
0x20
)
=
-
0x9
(EBADF)
[
=
] read(fd
=
0xffffffff
, buf
=
0x80000000dcbf
, length
=
0x1
)
=
-
0x9
(EBADF)
[
=
] close(fd
=
0xffffffff
)
=
-
0x1
(EPERM)
[
=
] getrandom(buf
=
0x80000000dce0
, buflen
=
0x20
, flags
=
0x1
)
=
0x20
Challenge
2
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
Challenge
3
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
[
=
] time()
=
0x6471f7b0
Challenge
4
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
.text:
0000555555554D31
public challenge3
.text:
0000555555554D31
challenge3 proc near ; CODE XREF: start
+
16B
↓p
.text:
0000555555554D31
.text:
0000555555554D31
var_68
=
qword ptr
-
68h
.text:
0000555555554D31
var_60
=
dword ptr
-
60h
.text:
0000555555554D31
var_5C
=
dword ptr
-
5Ch
.text:
0000555555554D31
fd
=
dword ptr
-
58h
.text:
0000555555554D31
var_51
=
byte ptr
-
51h
.text:
0000555555554D31
buf
=
byte ptr
-
50h
.text:
0000555555554D31
var_30
=
byte ptr
-
30h
.text:
0000555555554D31
var_8
=
qword ptr
-
8
.text:
0000555555554D31
.text:
0000555555554D31
push rbp
.text:
0000555555554D32
mov rbp, rsp
.text:
0000555555554D35
sub rsp,
70h
.text:
0000555555554D39
mov [rbp
+
var_68], rdi
.text:
0000555555554D3D
mov rax, fs:
28h
.text:
0000555555554D46
mov [rbp
+
var_8], rax
.text:
0000555555554D4A
xor eax, eax
.text:
0000555555554D4C
mov esi,
0
; oflag
.text:
0000555555554D51
lea rdi,
file
;
"/dev/urandom"
.text:
0000555555554D58
mov eax,
0
.text:
0000555555554D5D
call _open
.text:
0000555555554D62
mov [rbp
+
fd], eax
.text:
0000555555554D65
lea rcx, [rbp
+
buf]
.text:
0000555555554D69
mov eax, [rbp
+
fd]
.text:
0000555555554D6C
mov edx,
20h
;
' '
; nbytes
.text:
0000555555554D71
mov rsi, rcx ; buf
.text:
0000555555554D74
mov edi, eax ; fd
.text:
0000555555554D76
call _read
.text:
0000555555554D7B
lea rcx, [rbp
+
var_51]
.text:
0000555555554D7F
mov eax, [rbp
+
fd]
.text:
0000555555554D82
mov edx,
1
; nbytes
.text:
0000555555554D87
mov rsi, rcx ; buf
.text:
0000555555554D8A
mov edi, eax ; fd
.text:
0000555555554D8C
call _read
.text:
0000555555554D91
mov eax, [rbp
+
fd]
.text:
0000555555554D94
mov edi, eax ; fd
.text:
0000555555554D96
call _close
.text:
0000555555554D9B
lea rax, [rbp
+
var_30]
.text:
0000555555554D9F
mov edx,
1
.text:
0000555555554DA4
mov esi,
20h
;
.text:
0000555555554DA9
mov rdi, rax
.text:
0000555555554DAC
call _getrandom
.text:
0000555555554DB1
mov [rbp
+
var_60],
0
.text:
0000555555554DB8
mov [rbp
+
var_5C],
0
.text:
0000555555554DBF
jmp short loc_555555554DF3
.text:
0000555555554DC1
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554DC1
.text:
0000555555554DC1
loc_555555554DC1: ; CODE XREF: challenge3
+
C6↓j
.text:
0000555555554DC1
mov eax, [rbp
+
var_5C]
.text:
0000555555554DC4
cdqe
.text:
0000555555554DC6
movzx edx, [rbp
+
rax
+
buf] ; edx清零,读取 rbp
+
rax
+
buf 地址处的一个字节,并将其扩充到
64
位移入edx寄存器低
8
位
.text:
0000555555554DCB
mov eax, [rbp
+
var_5C]
.text:
0000555555554DCE
cdqe ; 将
32
位eax值扩展为
64
位rax值
.text:
0000555555554DD0
movzx eax, [rbp
+
rax
+
var_30]
.text:
0000555555554DD5
cmp
dl, al
.text:
0000555555554DD7
jnz short loc_555555554DEF
.text:
0000555555554DD9
mov eax, [rbp
+
var_5C]
.text:
0000555555554DDC
cdqe
.text:
0000555555554DDE
movzx edx, [rbp
+
rax
+
buf]
.text:
0000555555554DE3
movzx eax, [rbp
+
var_51]
.text:
0000555555554DE7
cmp
dl, al ; di:edx低
8
位,al:eax低
8
位
.text:
0000555555554DE9
jz short loc_555555554DEF
.text:
0000555555554DEB
add [rbp
+
var_60],
1
.text:
0000555555554DEF
.text:
0000555555554DEF
loc_555555554DEF: ; CODE XREF: challenge3
+
A6↑j
.text:
0000555555554DEF
; challenge3
+
B8↑j
.text:
0000555555554DEF
add [rbp
+
var_5C],
1
.text:
0000555555554DF3
.text:
0000555555554DF3
loc_555555554DF3: ; CODE XREF: challenge3
+
8E
↑j
.text:
0000555555554DF3
cmp
[rbp
+
var_5C],
1Fh
.text:
0000555555554DF7
jle short loc_555555554DC1 ; 小于或等于则跳转
.text:
0000555555554DF9
cmp
[rbp
+
var_60],
20h
;
.text:
0000555555554DFD
jnz short loc_555555554E06
.text:
0000555555554DFF
mov rax, [rbp
+
var_68]
.text:
0000555555554E03
mov byte ptr [rax],
1
; byte ptr是指定操作数的大小,这里是一个byte
; 将字节值
1
存储在rax寄存器指向的内存地址中
.text:
0000555555554E06
.text:
0000555555554E06
loc_555555554E06: ; CODE XREF: challenge3
+
CC↑j
.text:
0000555555554E06
nop
.text:
0000555555554E07
mov rax, [rbp
+
var_8]
.text:
0000555555554E0B
xor rax, fs:
28h
.text:
0000555555554E14
jz short locret_555555554E1B
.text:
0000555555554E16
call ___stack_chk_fail
.text:
0000555555554E1B
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554E1B
.text:
0000555555554E1B
locret_555555554E1B: ; CODE XREF: challenge3
+
E3↑j
.text:
0000555555554E1B
leave
.text:
0000555555554E1C
retn
.text:
0000555555554E1C
challenge3 endp
.text:
0000555555554D31
public challenge3
.text:
0000555555554D31
challenge3 proc near ; CODE XREF: start
+
16B
↓p
.text:
0000555555554D31
.text:
0000555555554D31
var_68
=
qword ptr
-
68h
.text:
0000555555554D31
var_60
=
dword ptr
-
60h
.text:
0000555555554D31
var_5C
=
dword ptr
-
5Ch
.text:
0000555555554D31
fd
=
dword ptr
-
58h
.text:
0000555555554D31
var_51
=
byte ptr
-
51h
.text:
0000555555554D31
buf
=
byte ptr
-
50h
.text:
0000555555554D31
var_30
=
byte ptr
-
30h
.text:
0000555555554D31
var_8
=
qword ptr
-
8
.text:
0000555555554D31
.text:
0000555555554D31
push rbp
.text:
0000555555554D32
mov rbp, rsp
.text:
0000555555554D35
sub rsp,
70h
.text:
0000555555554D39
mov [rbp
+
var_68], rdi
.text:
0000555555554D3D
mov rax, fs:
28h
.text:
0000555555554D46
mov [rbp
+
var_8], rax
.text:
0000555555554D4A
xor eax, eax
.text:
0000555555554D4C
mov esi,
0
; oflag
.text:
0000555555554D51
lea rdi,
file
;
"/dev/urandom"
.text:
0000555555554D58
mov eax,
0
.text:
0000555555554D5D
call _open
.text:
0000555555554D62
mov [rbp
+
fd], eax
.text:
0000555555554D65
lea rcx, [rbp
+
buf]
.text:
0000555555554D69
mov eax, [rbp
+
fd]
.text:
0000555555554D6C
mov edx,
20h
;
' '
; nbytes
.text:
0000555555554D71
mov rsi, rcx ; buf
.text:
0000555555554D74
mov edi, eax ; fd
.text:
0000555555554D76
call _read
.text:
0000555555554D7B
lea rcx, [rbp
+
var_51]
.text:
0000555555554D7F
mov eax, [rbp
+
fd]
.text:
0000555555554D82
mov edx,
1
; nbytes
.text:
0000555555554D87
mov rsi, rcx ; buf
.text:
0000555555554D8A
mov edi, eax ; fd
.text:
0000555555554D8C
call _read
.text:
0000555555554D91
mov eax, [rbp
+
fd]
.text:
0000555555554D94
mov edi, eax ; fd
.text:
0000555555554D96
call _close
.text:
0000555555554D9B
lea rax, [rbp
+
var_30]
.text:
0000555555554D9F
mov edx,
1
.text:
0000555555554DA4
mov esi,
20h
;
.text:
0000555555554DA9
mov rdi, rax
.text:
0000555555554DAC
call _getrandom
.text:
0000555555554DB1
mov [rbp
+
var_60],
0
.text:
0000555555554DB8
mov [rbp
+
var_5C],
0
.text:
0000555555554DBF
jmp short loc_555555554DF3
.text:
0000555555554DC1
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554DC1
.text:
0000555555554DC1
loc_555555554DC1: ; CODE XREF: challenge3
+
C6↓j
.text:
0000555555554DC1
mov eax, [rbp
+
var_5C]
.text:
0000555555554DC4
cdqe
.text:
0000555555554DC6
movzx edx, [rbp
+
rax
+
buf] ; edx清零,读取 rbp
+
rax
+
buf 地址处的一个字节,并将其扩充到
64
位移入edx寄存器低
8
位
.text:
0000555555554DCB
mov eax, [rbp
+
var_5C]
.text:
0000555555554DCE
cdqe ; 将
32
位eax值扩展为
64
位rax值
.text:
0000555555554DD0
movzx eax, [rbp
+
rax
+
var_30]
.text:
0000555555554DD5
cmp
dl, al
.text:
0000555555554DD7
jnz short loc_555555554DEF
.text:
0000555555554DD9
mov eax, [rbp
+
var_5C]
.text:
0000555555554DDC
cdqe
.text:
0000555555554DDE
movzx edx, [rbp
+
rax
+
buf]
.text:
0000555555554DE3
movzx eax, [rbp
+
var_51]
.text:
0000555555554DE7
cmp
dl, al ; di:edx低
8
位,al:eax低
8
位
.text:
0000555555554DE9
jz short loc_555555554DEF
.text:
0000555555554DEB
add [rbp
+
var_60],
1
.text:
0000555555554DEF
.text:
0000555555554DEF
loc_555555554DEF: ; CODE XREF: challenge3
+
A6↑j
.text:
0000555555554DEF
; challenge3
+
B8↑j
.text:
0000555555554DEF
add [rbp
+
var_5C],
1
.text:
0000555555554DF3
.text:
0000555555554DF3
loc_555555554DF3: ; CODE XREF: challenge3
+
8E
↑j
.text:
0000555555554DF3
cmp
[rbp
+
var_5C],
1Fh
.text:
0000555555554DF7
jle short loc_555555554DC1 ; 小于或等于则跳转
.text:
0000555555554DF9
cmp
[rbp
+
var_60],
20h
;
.text:
0000555555554DFD
jnz short loc_555555554E06
.text:
0000555555554DFF
mov rax, [rbp
+
var_68]
.text:
0000555555554E03
mov byte ptr [rax],
1
; byte ptr是指定操作数的大小,这里是一个byte
; 将字节值
1
存储在rax寄存器指向的内存地址中
.text:
0000555555554E06
.text:
0000555555554E06
loc_555555554E06: ; CODE XREF: challenge3
+
CC↑j
.text:
0000555555554E06
nop
.text:
0000555555554E07
mov rax, [rbp
+
var_8]
.text:
0000555555554E0B
xor rax, fs:
28h
.text:
0000555555554E14
jz short locret_555555554E1B
.text:
0000555555554E16
call ___stack_chk_fail
.text:
0000555555554E1B
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554E1B
.text:
0000555555554E1B
locret_555555554E1B: ; CODE XREF: challenge3
+
E3↑j
.text:
0000555555554E1B
leave
.text:
0000555555554E1C
retn
.text:
0000555555554E1C
challenge3 endp
unsigned __int64 __fastcall challenge3(_BYTE
*
a1)
{
int
v2;
/
/
[rsp
+
10h
] [rbp
-
60h
]
int
i;
/
/
[rsp
+
14h
] [rbp
-
5Ch
]
int
fd;
/
/
[rsp
+
18h
] [rbp
-
58h
]
char v5;
/
/
[rsp
+
1Fh
] [rbp
-
51h
] BYREF
char buf[
32
];
/
/
[rsp
+
20h
] [rbp
-
50h
] BYREF
char v7[
40
];
/
/
[rsp
+
40h
] [rbp
-
30h
] BYREF
unsigned __int64 v8;
/
/
[rsp
+
68h
] [rbp
-
8h
]
v8
=
__readfsqword(
0x28u
);
/
/
从fs段读取地址
0x28
处的
8
个字节,并将其存储在v8变量中。地址
0x28
处的数据通常被用来存储函数的返回地址
fd
=
open
(
"/dev/urandom"
,
0
);
read(fd, buf,
0x20uLL
);
read(fd, &v5,
1uLL
);
close(fd);
getrandom(v7,
32LL
,
1LL
);
v2
=
0
;
for
( i
=
0
; i <
=
31
;
+
+
i )
{
if
( buf[i]
=
=
v7[i] && buf[i] !
=
v5 )
+
+
v2;
}
if
( v2
=
=
32
)
*
a1
=
1
;
/
/
修改指针所指向的内存中的值
return
__readfsqword(
0x28u
) ^ v8;
}
unsigned __int64 __fastcall challenge3(_BYTE
*
a1)
{
int
v2;
/
/
[rsp
+
10h
] [rbp
-
60h
]
int
i;
/
/
[rsp
+
14h
] [rbp
-
5Ch
]
int
fd;
/
/
[rsp
+
18h
] [rbp
-
58h
]
char v5;
/
/
[rsp
+
1Fh
] [rbp
-
51h
] BYREF
char buf[
32
];
/
/
[rsp
+
20h
] [rbp
-
50h
] BYREF
char v7[
40
];
/
/
[rsp
+
40h
] [rbp
-
30h
] BYREF
unsigned __int64 v8;
/
/
[rsp
+
68h
] [rbp
-
8h
]
v8
=
__readfsqword(
0x28u
);
/
/
从fs段读取地址
0x28
处的
8
个字节,并将其存储在v8变量中。地址
0x28
处的数据通常被用来存储函数的返回地址
fd
=
open
(
"/dev/urandom"
,
0
);
read(fd, buf,
0x20uLL
);
read(fd, &v5,
1uLL
);
close(fd);
getrandom(v7,
32LL
,
1LL
);
v2
=
0
;
for
( i
=
0
; i <
=
31
;
+
+
i )
{
if
( buf[i]
=
=
v7[i] && buf[i] !
=
v5 )
+
+
v2;
}
if
( v2
=
=
32
)
*
a1
=
1
;
/
/
修改指针所指向的内存中的值
return
__readfsqword(
0x28u
) ^ v8;
}
read(fd, buf,
0x20uLL
);
read(fd, &v5,
1uLL
);
···
getrandom(v7,
32LL
,
1LL
);
read(fd, buf,
0x20uLL
);
read(fd, &v5,
1uLL
);
···
getrandom(v7,
32LL
,
1LL
);
getrandom(ql, buf, buflen, flags)
buf:指向缓冲区的指针,用于存储读取到的随机数据。
buflen:要从系统熵池读取的字节数。
flags:指定 getrandom 函数的行为。常见的 flags 值包括:
0
:如果系统熵池中没有足够的熵,getrandom 会阻塞直到有足够的熵可用。
GRND_NONBLOCK(通常为
1
):getrandom 在系统熵池中没有足够的熵时,会立即返回错误而不是阻塞。
GRND_RANDOM(通常为
2
):尝试从
/
dev
/
random 获取随机数据,而不是从
/
dev
/
urandom 获取。这个选项会导致 getrandom 的行为更加谨慎,可能会在熵不足时阻塞。
返回值:
如果成功获取随机数据,getrandom 返回实际读取的字节数。
如果出错,返回
-
1
并设置 errno。常见的错误包括 EAGAIN(系统熵池中没有足够的熵,且 flags 设置为非阻塞模式)和 EFAULT(buf 指针无效或指向不可访问的内存区域)。
getrandom(ql, buf, buflen, flags)
buf:指向缓冲区的指针,用于存储读取到的随机数据。
buflen:要从系统熵池读取的字节数。
flags:指定 getrandom 函数的行为。常见的 flags 值包括:
0
:如果系统熵池中没有足够的熵,getrandom 会阻塞直到有足够的熵可用。
GRND_NONBLOCK(通常为
1
):getrandom 在系统熵池中没有足够的熵时,会立即返回错误而不是阻塞。
GRND_RANDOM(通常为
2
):尝试从
/
dev
/
random 获取随机数据,而不是从
/
dev
/
urandom 获取。这个选项会导致 getrandom 的行为更加谨慎,可能会在熵不足时阻塞。
返回值:
如果成功获取随机数据,getrandom 返回实际读取的字节数。
如果出错,返回
-
1
并设置 errno。常见的错误包括 EAGAIN(系统熵池中没有足够的熵,且 flags 设置为非阻塞模式)和 EFAULT(buf 指针无效或指向不可访问的内存区域)。
from
qiling
import
Qiling
from
qiling.os.mapper
import
QlFsMappedObject
class
FakeUrandom(QlFsMappedObject):
def
read(
self
, size:
int
)
-
> bytes:
if
size
=
=
1
:
return
b
"\x42"
# v5的长度为1
# return a constant value upon reading
else
:
return
b
"\x41"
def
fstat(
self
)
-
>
int
:
# fstat() 是Linux/Unix系统调用之一,用来获取文件的状态信息
# return -1 to let syscall fstat ignore it
return
-
1
def
close(
self
)
-
>
int
:
return
0
if
__name__
=
=
"__main__"
:
ql
=
Qiling([r
'rootfs/x86_linux/bin/x86_fetch_urandom'
], r
'rootfs/x86_linux'
)
ql.add_fs_mapper(r
'/dev/urandom'
, FakeUrandom())
ql.run()
from
qiling
import
Qiling
from
qiling.os.mapper
import
QlFsMappedObject
class
FakeUrandom(QlFsMappedObject):
def
read(
self
, size:
int
)
-
> bytes:
if
size
=
=
1
:
return
b
"\x42"
# v5的长度为1
# return a constant value upon reading
else
:
return
b
"\x41"
def
fstat(
self
)
-
>
int
:
# fstat() 是Linux/Unix系统调用之一,用来获取文件的状态信息
# return -1 to let syscall fstat ignore it
return
-
1
def
close(
self
)
-
>
int
:
return
0
if
__name__
=
=
"__main__"
:
ql
=
Qiling([r
'rootfs/x86_linux/bin/x86_fetch_urandom'
], r
'rootfs/x86_linux'
)
ql.add_fs_mapper(r
'/dev/urandom'
, FakeUrandom())
ql.run()
def
hook_getrandom(ql, buf, buflen, flags):
# 自定义 getrandom 函数实现
if
buflen
=
=
32
:
data
=
b
'\x41'
*
buflen
# b'\x41' = A
ql.mem.write(buf, data)
ql.os.set_syscall_return(buflen)
else
:
ql.os.set_syscall_return(
-
1
)
···
# 调用自定义系统调用
ql.os.set_syscall(
"getrandom"
, hook_getrandom)
def
hook_getrandom(ql, buf, buflen, flags):
# 自定义 getrandom 函数实现
if
buflen
=
=
32
:
data
=
b
'\x41'
*
buflen
# b'\x41' = A
ql.mem.write(buf, data)
ql.os.set_syscall_return(buflen)
else
:
ql.os.set_syscall_return(
-
1
)
···
# 调用自定义系统调用
ql.os.set_syscall(
"getrandom"
, hook_getrandom)
from
qiling
import
*
from
qiling.const
import
*
# 导入Qiling模拟器中的常量
from
qiling.os.mapper
import
QlFsMappedObject
class
FakeUrandom(QlFsMappedObject):
def
read(
self
, size:
int
)
-
> bytes:
if
size
=
=
1
:
return
b
"\x42"
else
:
return
b
"\x41"
*
size
def
close(
self
)
-
>
int
:
return
0
def
hook_getrandom(ql, buf, buflen, flags):
# 自定义 getrandom 函数实现
if
buflen
=
=
32
:
data
=
b
'\x41'
*
buflen
# b'\x41' = A
ql.mem.write(buf, data)
ql.os.set_syscall_return(buflen)
else
:
ql.os.set_syscall_return(
-
1
)
def
challenge3(ql):
ql.add_fs_mapper(r
'/dev/urandom'
, FakeUrandom())
ql.os.set_syscall(
"getrandom"
, hook_getrandom)
if
__name__
=
=
'__main__'
:
path
=
[
"rootfs/x8664_linux/qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
# 记得加上challenge1
challenge2(ql)
ql.run()
from
qiling
import
*
from
qiling.const
import
*
# 导入Qiling模拟器中的常量
from
qiling.os.mapper
import
QlFsMappedObject
class
FakeUrandom(QlFsMappedObject):
def
read(
self
, size:
int
)
-
> bytes:
if
size
=
=
1
:
return
b
"\x42"
else
:
return
b
"\x41"
*
size
def
close(
self
)
-
>
int
:
return
0
def
hook_getrandom(ql, buf, buflen, flags):
# 自定义 getrandom 函数实现
if
buflen
=
=
32
:
data
=
b
'\x41'
*
buflen
# b'\x41' = A
ql.mem.write(buf, data)
ql.os.set_syscall_return(buflen)
else
:
ql.os.set_syscall_return(
-
1
)
def
challenge3(ql):
ql.add_fs_mapper(r
'/dev/urandom'
, FakeUrandom())
ql.os.set_syscall(
"getrandom"
, hook_getrandom)
if
__name__
=
=
'__main__'
:
path
=
[
"rootfs/x8664_linux/qilinglab-x86_64"
]
rootfs
=
"rootfs/x8664_linux"
ql
=
Qiling(path, rootfs)
# 记得加上challenge1
challenge2(ql)
ql.run()
Checking which challenge are solved...
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x54
)
=
0x54
Note: Some challenges will results
in
segfaults
and
infinite loops
if
they aren't solved.
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x5a
)
=
0x5a
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x1
)
=
0x1
utsname address:
0x80000000db50
[
=
] uname(buf
=
0x80000000db50
)
=
0x0
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
0x3
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcc0
, length
=
0x20
)
=
0x20
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcbf
, length
=
0x1
)
=
0x1
[
=
] close(fd
=
0x3
)
=
0x0
[
=
] getrandom(buf
=
0x80000000dce0
, buflen
=
0x20
, flags
=
0x1
)
=
?
Challenge
2
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
Challenge
3
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] time()
=
0x6472521d
Challenge
4
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
Checking which challenge are solved...
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x54
)
=
0x54
Note: Some challenges will results
in
segfaults
and
infinite loops
if
they aren't solved.
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x5a
)
=
0x5a
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x1
)
=
0x1
utsname address:
0x80000000db50
[
=
] uname(buf
=
0x80000000db50
)
=
0x0
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
0x3
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcc0
, length
=
0x20
)
=
0x20
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcbf
, length
=
0x1
)
=
0x1
[
=
] close(fd
=
0x3
)
=
0x0
[
=
] getrandom(buf
=
0x80000000dce0
, buflen
=
0x20
, flags
=
0x1
)
=
?
Challenge
2
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
Challenge
3
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] time()
=
0x6472521d
Challenge
4
: NOT SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x18
)
=
0x18
.text:
0000555555554E1D
public challenge4
.text:
0000555555554E1D
challenge4 proc near ; CODE XREF: start
+
18F
↓p
.text:
0000555555554E1D
.text:
0000555555554E1D
var_18
=
qword ptr
-
18h
.text:
0000555555554E1D
var_8
=
dword ptr
-
8
.text:
0000555555554E1D
var_4
=
dword ptr
-
4
.text:
0000555555554E1D
.text:
0000555555554E1D
push rbp
.text:
0000555555554E1E
mov rbp, rsp
.text:
0000555555554E21
mov [rbp
+
var_18], rdi
.text:
0000555555554E25
mov [rbp
+
var_8],
0
.text:
0000555555554E2C
mov [rbp
+
var_4],
0
.text:
0000555555554E33
jmp short loc_555555554E40
.text:
0000555555554E35
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554E35
.text:
0000555555554E35
loc_555555554E35: ; CODE XREF: challenge4
+
29
↓j
.text:
0000555555554E35
mov rax, [rbp
+
var_18]
.text:
0000555555554E39
mov byte ptr [rax],
1
.text:
0000555555554E3C
add [rbp
+
var_4],
1
.text:
0000555555554E40
.text:
0000555555554E40
loc_555555554E40: ; CODE XREF: challenge4
+
16
↑j
.text:
0000555555554E40
mov eax, [rbp
+
var_8]
.text:
0000555555554E43
cmp
[rbp
+
var_4], eax
.text:
0000555555554E46
jl short loc_555555554E35 ; 小于则跳转
.text:
0000555555554E48
nop
.text:
0000555555554E49
pop rbp
.text:
0000555555554E4A
retn
.text:
0000555555554E4A
challenge4 endp
.text:
0000555555554E1D
public challenge4
.text:
0000555555554E1D
challenge4 proc near ; CODE XREF: start
+
18F
↓p
.text:
0000555555554E1D
.text:
0000555555554E1D
var_18
=
qword ptr
-
18h
.text:
0000555555554E1D
var_8
=
dword ptr
-
8
.text:
0000555555554E1D
var_4
=
dword ptr
-
4
.text:
0000555555554E1D
.text:
0000555555554E1D
push rbp
.text:
0000555555554E1E
mov rbp, rsp
.text:
0000555555554E21
mov [rbp
+
var_18], rdi
.text:
0000555555554E25
mov [rbp
+
var_8],
0
.text:
0000555555554E2C
mov [rbp
+
var_4],
0
.text:
0000555555554E33
jmp short loc_555555554E40
.text:
0000555555554E35
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554E35
.text:
0000555555554E35
loc_555555554E35: ; CODE XREF: challenge4
+
29
↓j
.text:
0000555555554E35
mov rax, [rbp
+
var_18]
.text:
0000555555554E39
mov byte ptr [rax],
1
.text:
0000555555554E3C
add [rbp
+
var_4],
1
.text:
0000555555554E40
.text:
0000555555554E40
loc_555555554E40: ; CODE XREF: challenge4
+
16
↑j
.text:
0000555555554E40
mov eax, [rbp
+
var_8]
.text:
0000555555554E43
cmp
[rbp
+
var_4], eax
.text:
0000555555554E46
jl short loc_555555554E35 ; 小于则跳转
.text:
0000555555554E48
nop
.text:
0000555555554E49
pop rbp
.text:
0000555555554E4A
retn
.text:
0000555555554E4A
challenge4 endp
__int64 challenge4()
{
return
0LL
;
}
__int64 challenge4()
{
return
0LL
;
}
mov [rbp
+
var_8],
0
mov [rbp
+
var_4],
0
···
mov eax, [rbp
+
var_8]
cmp
[rbp
+
var_4], eax
add [rbp
+
var_4],
1
mov [rbp
+
var_8],
0
mov [rbp
+
var_4],
0
···
mov eax, [rbp
+
var_8]
cmp
[rbp
+
var_4], eax
add [rbp
+
var_4],
1
__int64 __fastcall challenge4(_BYTE
*
a1)
{
__int64 result;
/
/
rax
int
i;
/
/
[rsp
+
14h
] [rbp
-
4h
]
for
( i
=
0
; ;
+
+
i )
/
/
for
循环运行一次后让
*
a1
=
1
,接着
break
。
{
result
=
1LL
;
if
( i >
=
1
)
break
;
*
a1
=
1
;
}
return
result;
}
__int64 __fastcall challenge4(_BYTE
*
a1)
{
__int64 result;
/
/
rax
int
i;
/
/
[rsp
+
14h
] [rbp
-
4h
]
for
( i
=
0
; ;
+
+
i )
/
/
for
循环运行一次后让
*
a1
=
1
,接着
break
。
{
result
=
1LL
;
if
( i >
=
1
)
break
;
*
a1
=
1
;
}
return
result;
}
def
enter_forbidden_loop_hook(ql):
ql.arch.regs.eax
=
1
def
challenge4(ql):
"""
.text:0000555555554E40 mov eax, [rbp+var_8]
.text:0000555555554E43 cmp [rbp+var_4], eax <-- 在运行此命令前hook eax,使得eax = 1
.text:0000555555554E46 jl short loc_555555554E35
"""
base
=
ql.mem.get_lib_base(os.path.split(ql.path)[
-
1
])
# 根据文件路径查找已经加载的文件,获取对应文件的基地址
# os.path.split(ql.path):os.path.split 函数将 ql.path 分成两部分:目录名和基本文件名。这个函数返回一个包含这两部分的元组。
# os.path.split(ql.path)[-1]:这将返回元组中的最后一个元素,即基本文件名。在 Python 中,-1 索引表示列表或元组的最后一个元素。
# base = ql.mem.get_lib_base(os.path.split(ql.path)[-1]):ql.mem.get_lib_base 函数使用提取的文件名作为参数,以获取已加载库的基地址。将返回的基地址赋值给变量 base
hook_addr
=
base
+
0xE43
ql.hook_address(enter_forbidden_loop_hook, hook_addr)
# 当执行流程到达hook_addr时,该函数将被调用。此时hook_addr处的代码还未被执行。
def
enter_forbidden_loop_hook(ql):
ql.arch.regs.eax
=
1
def
challenge4(ql):
"""
.text:0000555555554E40 mov eax, [rbp+var_8]
.text:0000555555554E43 cmp [rbp+var_4], eax <-- 在运行此命令前hook eax,使得eax = 1
.text:0000555555554E46 jl short loc_555555554E35
"""
base
=
ql.mem.get_lib_base(os.path.split(ql.path)[
-
1
])
# 根据文件路径查找已经加载的文件,获取对应文件的基地址
# os.path.split(ql.path):os.path.split 函数将 ql.path 分成两部分:目录名和基本文件名。这个函数返回一个包含这两部分的元组。
# os.path.split(ql.path)[-1]:这将返回元组中的最后一个元素,即基本文件名。在 Python 中,-1 索引表示列表或元组的最后一个元素。
# base = ql.mem.get_lib_base(os.path.split(ql.path)[-1]):ql.mem.get_lib_base 函数使用提取的文件名作为参数,以获取已加载库的基地址。将返回的基地址赋值给变量 base
hook_addr
=
base
+
0xE43
ql.hook_address(enter_forbidden_loop_hook, hook_addr)
# 当执行流程到达hook_addr时,该函数将被调用。此时hook_addr处的代码还未被执行。
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
0x3
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcc0
, length
=
0x20
)
=
0x20
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcbf
, length
=
0x1
)
=
0x1
[
=
] close(fd
=
0x3
)
=
0x0
[
=
] getrandom(buf
=
0x80000000dce0
, buflen
=
0x20
, flags
=
0x1
)
=
?
Challenge
2
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
Challenge
3
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] time()
=
0x64730987
Challenge
4
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
Challenge
1
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] openat(fd
=
0xffffff9c
, path
=
0x555555555698
, flags
=
0x0
, mode
=
0x0
)
=
0x3
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcc0
, length
=
0x20
)
=
0x20
[
=
] read(fd
=
0x3
, buf
=
0x80000000dcbf
, length
=
0x1
)
=
0x1
[
=
] close(fd
=
0x3
)
=
0x0
[
=
] getrandom(buf
=
0x80000000dce0
, buflen
=
0x20
, flags
=
0x1
)
=
?
Challenge
2
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
Challenge
3
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
[
=
] time()
=
0x64730987
Challenge
4
: SOLVED
[
=
] write(fd
=
0x1
, buf
=
0x55555575a260
, count
=
0x14
)
=
0x14
.text:
0000555555554E4B
public challenge5
.text:
0000555555554E4B
challenge5 proc near ; CODE XREF: start
+
1B3
↓p
.text:
0000555555554E4B
.text:
0000555555554E4B
var_58
=
qword ptr
-
58h
.text:
0000555555554E4B
var_48
=
dword ptr
-
48h
.text:
0000555555554E4B
var_44
=
dword ptr
-
44h
.text:
0000555555554E4B
var_40
=
dword ptr
-
40h
.text:
0000555555554E4B
var_20
=
dword ptr
-
20h
.text:
0000555555554E4B
var_8
=
qword ptr
-
8
.text:
0000555555554E4B
.text:
0000555555554E4B
push rbp
.text:
0000555555554E4C
mov rbp, rsp
.text:
0000555555554E4F
sub rsp,
60h
.text:
0000555555554E53
mov [rbp
+
var_58], rdi
.text:
0000555555554E57
mov rax, fs:
28h
.text:
0000555555554E60
mov [rbp
+
var_8], rax
.text:
0000555555554E64
xor eax, eax
.text:
0000555555554E66
mov edi,
0
; timer
.text:
0000555555554E6B
call _time
.text:
0000555555554E70
mov edi, eax ; seed
.text:
0000555555554E72
call _srand
.text:
0000555555554E77
mov [rbp
+
var_48],
0
.text:
0000555555554E7E
jmp short loc_555555554EA1
.text:
0000555555554E80
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554E80
.text:
0000555555554E80
loc_555555554E80: ; CODE XREF: challenge5
+
5A
↓j
.text:
0000555555554E80
mov eax, [rbp
+
var_48]
.text:
0000555555554E83
cdqe
.text:
0000555555554E85
mov [rbp
+
rax
*
4
+
var_40],
0
.text:
0000555555554E8D
call _rand
.text:
0000555555554E92
mov edx, eax
.text:
0000555555554E94
mov eax, [rbp
+
var_48]
.text:
0000555555554E97
cdqe
.text:
0000555555554E99
mov [rbp
+
rax
*
4
+
var_20], edx
.text:
0000555555554E9D
add [rbp
+
var_48],
1
.text:
0000555555554EA1
.text:
0000555555554EA1
loc_555555554EA1: ; CODE XREF: challenge5
+
33
↑j
.text:
0000555555554EA1
cmp
[rbp
+
var_48],
4
.text:
0000555555554EA5
jle short loc_555555554E80
.text:
0000555555554EA7
mov [rbp
+
var_44],
0
.text:
0000555555554EAE
jmp short loc_555555554ED3
.text:
0000555555554EB0
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554EB0
.text:
0000555555554EB0
loc_555555554EB0: ; CODE XREF: challenge5
+
8C
↓j
.text:
0000555555554EB0
mov eax, [rbp
+
var_44]
.text:
0000555555554EB3
cdqe
.text:
0000555555554EB5
mov edx, [rbp
+
rax
*
4
+
var_40]
.text:
0000555555554EB9
mov eax, [rbp
+
var_44]
.text:
0000555555554EBC
cdqe
.text:
0000555555554EBE
mov eax, [rbp
+
rax
*
4
+
var_20]
.text:
0000555555554EC2
cmp
edx, eax
.text:
0000555555554EC4
jz short loc_555555554ECF
.text:
0000555555554EC6
mov rax, [rbp
+
var_58]
.text:
0000555555554ECA
mov byte ptr [rax],
0
.text:
0000555555554ECD
jmp short loc_555555554EE0
.text:
0000555555554ECF
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554ECF
.text:
0000555555554ECF
loc_555555554ECF: ; CODE XREF: challenge5
+
79
↑j
.text:
0000555555554ECF
add [rbp
+
var_44],
1
.text:
0000555555554ED3
.text:
0000555555554ED3
loc_555555554ED3: ; CODE XREF: challenge5
+
63
↑j
.text:
0000555555554ED3
cmp
[rbp
+
var_44],
4
.text:
0000555555554ED7
jle short loc_555555554EB0
.text:
0000555555554ED9
mov rax, [rbp
+
var_58]
.text:
0000555555554EDD
mov byte ptr [rax],
1
.text:
0000555555554EE0
.text:
0000555555554EE0
loc_555555554EE0: ; CODE XREF: challenge5
+
82
↑j
.text:
0000555555554EE0
mov rax, [rbp
+
var_8]
.text:
0000555555554EE4
xor rax, fs:
28h
.text:
0000555555554EED
jz short locret_555555554EF4
.text:
0000555555554EEF
call ___stack_chk_fail
.text:
0000555555554EF4
;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
.text:
0000555555554EF4
.text:
0000555555554EF4
locret_555555554EF4: ; CODE XREF: challenge5
+
A2↑j
.text:
0000555555554EF4
leave
.text:
0000555555554EF5
retn
.text:
0000555555554EF5
challenge5 endp
.text:
0000555555554E4B
public challenge5
.text:
0000555555554E4B
challenge5 proc near ; CODE XREF: start
+
1B3
↓p
.text:
0000555555554E4B
.text:
0000555555554E4B
var_58
=
qword ptr
-
58h
.text:
0000555555554E4B
var_48
=
dword ptr
-
48h
.text:
0000555555554E4B
var_44
=
dword ptr
-
44h
.text:
0000555555554E4B
var_40
=
dword ptr
-
40h
.text:
0000555555554E4B
var_20
=
dword ptr
-
20h
.text:
0000555555554E4B
var_8
=
qword ptr
-
8
.text:
0000555555554E4B
.text:
0000555555554E4B
push rbp
.text:
0000555555554E4C
mov rbp, rsp
.text:
0000555555554E4F
sub rsp,
60h
.text:
0000555555554E53
mov [rbp
+
var_58], rdi
.text:
0000555555554E57
mov rax, fs:
28h
.text:
0000555555554E60
mov [rbp
+
var_8], rax
.text:
0000555555554E64
xor eax, eax
.text:
0000555555554E66
mov edi,
0
; timer
.text:
0000555555554E6B
call _time
.text:
0000555555554E70
mov edi, eax ; seed
.text:
0000555555554E72
call _srand
.text:
0000555555554E77
mov [rbp
+
var_48],
0
.text:
0000555555554E7E
jmp short loc_555555554EA1
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
|
|
---|---|
|
有一个图挂了,补下。
|
|
胡一米 有一个图挂了,补下。好的,已检查全文补齐图片 |
|
感谢分享
|
|
写得好,感谢分享
|
|
很有耐心~
|