QiLing 框架学习WriteUp

QiLing 框架学习

Qiling

Qiling 是一个高级二进制仿真框架,是基于Unicorn(独角兽)具有以下特点:

  • 跨平台:Windows、MacOS、Linux、BSD、UEFI、DOS
  • 跨架构:X86、X86_64、Arm、Arm64、MIPS、8086
  • 多种文件格式:PE、MachO、ELF、COM
  • 通过Demigod支持 Linux Kernel Module(.ko)、Windows Driver(.sys) 和 MacOS Kernel(.kext)
  • 在隔离环境中模拟和沙箱机器代码
  • 支持跨架构和平台调试功能
  • 提供高级 API 来设置和配置沙箱
  • 细粒度检测:允许不同级别的钩子(指令/基本块/内存访问/异常/系统调用/IO/等)
  • 允许动态热补丁运行代码,包括加载的库
  • 真正的 Python 框架,可以轻松地在上面构建自定义的安全分析工具

安装过程

新建一个ubuntu 18.04 的虚拟机。伊万说这个东西会改变其他的环境配置

自动安装 ,这种安装方式失败

image-20220412185946587

手动安装

1
2
3
4
5
6
7
8
9
安装方式  Ubuntu 20.04
sudo apt-get update
sudo apt-get upgrade
sudo apt install python3-pip git cmake
git clone -b dev https://github.com/qilingframework/qiling.git
cd qiling && git submodule update --init --recursive
sudo pip3 install .


基础语法和函数

开发文档

ql.verbose = 0

查看输出,不带寄存器、汇编指令的输出。

内存映射

在写入内存之前进行内存映射

ql.mem.map(addr,size,info = [my_first_map])

addr 要注意内存偏移量和映射地址

size : 应该映射的内存大小,如果使用linux 系统,至少需要考虑4096的倍数进行对齐。

ql.loader.elf_entry 获取程序入口的地址

image-20220426181633377

ql.loader.load_address 获取加载基地址

ql.men.get_lib_base(“httpd”) 和 ql.loader.load_address 获取的值是一样的。

image-20220426182018533

image-20220426183049546

ql.os.set_api(‘sleep’,fake_sleep) hook 某个函数

image-20220426182136100

ql.arch.regs.r0 =1 设置寄存器r0 的值

ql.arch.regs.read(‘r0’) 获取寄存器r0 中的值

ql.mem.show_mapinfo() 显示映射信息

image-20220426182517111

image-20220426182719013

challenge

https://bbs.pediy.com/thread-268989.htm

显示找不到registry 文件夹

image-20220413170526016解决方案: 需要查看 “ Important note on Windows DLLs and registry “ 这一章节window 依赖

在运行的时候会报错

image-20220413170458755

解决方案: python3.8以上

challenge1 writeup

程序尝试读取0x1337 处未映射的内存,因此,为了通过这个检查,我们只需要映射这虚拟内存区域并写入期望值。

image-20220414153151724

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#-*- coding:utf-8 -*-
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import sys
sys.path.append("..")

from qiling import Qiling
from qiling.const import QL_VERBOSE

#
def challenge1(ql:Qiling):
print("1111111"+str(ql.mem.is_mapped(0x1000,0x1000)))
ql.mem.map(0x1000,0x1000,info='[challenge1]')
ql.mem.write(0x1337,ql.pack16(1337)) # pack16(value) == struct.pack('H',value)

if __name__ == "__main__":
#ql = Qiling(["rootfs/x8664_linux/qilinglab-x86_64"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEFAULT)
ql = Qiling(["rootfs/x8664_linux/qilinglab-x86_64"], "rootfs/x8664_linux")
challenge1(ql)
ql.verbose = 1 # 设置输出信息的详细级别
ql.mem.show_mapinfo() # 显示完整的内存映射来查看我们刚刚映射的区域
ql.run()

为啥要ql.mem.map(0x1000,0x1000) ,因为我们要修改的内存地址是0x1337。

image-20220414140014259

主要收获

ql.mem.write(0x1337,ql.pack16(1337)) 修改内存地址上的值。

challenge2 writeup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
nsigned __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;
}

可以看到utsname 结构体

image-20220414160121966

根据前面反汇编出来的代码可以看到,函数调用了系统调用uname函数来获取有关底层操作系统的信息,uname 函数会获取ustname 结构体的指针,为了通过检查,ustname 结构体中的sysname 参数必须是 ”QilingOS“,而version 参数必须是” ChallengeStart”。

我们可以在uname 函数返回之前,使用Qiling API 进行hook 系统调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
def hook_uname_on_exit(ql: Qiling, *args):
'''
.text:0000000000000BD8 lea rax, [rbp+name]
.text:0000000000000BDF mov rdi, rax ; name
.text:0000000000000BE2 call _uname
.text:0000000000000BE7 test eax, eax
'''
rdi = ql.arch.regs.rdi # 控制寄存器
ql.mem.write(rdi, b'QilingOS\x00')
ql.mem.write(rdi + 65 * 3, b'ChallengeStart\x00')

def challenge2(ql:Qiling):
ql.os.set_syscall('uname', hook_uname_on_exit, QL_INTERCEPT.EXIT)

os_set_syscall()

https://docs.qiling.io/en/latest/hijack/

challenge3 writeup

根据函数,我们可以看到两个问题,

一个是从/dev/urandom 得到随机数,然后和gerandom 获取的随机数要一样。

从/dev/urandom 获取的第一个随机数要和其他的不一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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);
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;
}

hook

hook 的思路:使用set_syscall 来劫持系统调用的函数getrandom,让getrandom 函数全部返回00字节。这个和challenge2 劫持不一样,而是整个的覆盖这个函数。

然后使用add_fs_mapper 函数与 QlFsMappedObject 一起自定义文件系统/dev/urandom,让urandom 再请求多个字节时返回00字节,再请求一个字节时返回不同的字节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from qiling.os.mapper import QlFsMappedObject
class Fake_urandom(QlFsMappedObject):
# Fake read fs operation
def read(self, size):
if size == 1:
return b"\x41"
return b"\x00"*size
# Fake close fs operation
def close(self):
return 0

def getrandom_hook(ql:Qiling, buf, buflen, flags, *args):
ql.mem.write(buf,b"\x00"*buflen)
ql.os.set_syscall_return(0)

def challenge3(ql:Qiling):
ql.os.set_syscall("getrandom", getrandom_hook)
ql.add_fs_mapper("/dev/urandom",Fake_urandom())

主要收获:

ql.add_fs_mapper() fs 映射器,可以讲读/写操作重定向到用户自定义的对象

challenge4 writeup

使用IDA 无法显示反汇编的代码,但是通过汇编不难看出这是一个循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
:0000000000000E1D ; Attributes: bp-based frame
.text:0000000000000E1D
.text:0000000000000E1D public challenge4
.text:0000000000000E1D challenge4 proc near ; CODE XREF: start+18F↓p
.text:0000000000000E1D
.text:0000000000000E1D var_18 = qword ptr -18h
.text:0000000000000E1D var_8 = dword ptr -8
.text:0000000000000E1D var_4 = dword ptr -4
.text:0000000000000E1D
.text:0000000000000E1D ; __unwind {
.text:0000000000000E1D push rbp
.text:0000000000000E1E mov rbp, rsp
.text:0000000000000E21 mov [rbp+var_18], rdi
.text:0000000000000E25 mov [rbp+var_8], 0
.text:0000000000000E2C mov [rbp+var_4], 0
.text:0000000000000E33 jmp short loc_E40
.text:0000000000000E35 ; ---------------------------------------------------------------------------
.text:0000000000000E35
.text:0000000000000E35 loc_E35: ; CODE XREF: challenge4+29↓j
.text:0000000000000E35 mov rax, [rbp+var_18]
.text:0000000000000E39 mov byte ptr [rax], 1
.text:0000000000000E3C add [rbp+var_4], 1
.text:0000000000000E40
.text:0000000000000E40 loc_E40: ; CODE XREF: challenge4+16↑j
.text:0000000000000E40 mov eax, [rbp+var_8]
.text:0000000000000E43 cmp [rbp+var_4], eax # hook eax 寄存器内的值为 1
.text:0000000000000E46 jl short loc_E35
.text:0000000000000E48 nop
.text:0000000000000E49 pop rbp
.text:0000000000000E4A retn
.text:0000000000000E4A ; } // starts at E1D
.text:0000000000000E4A challenge4 endp

void challenge4(char *check) {
int i;
i = 0;
while (i < 0) {
*check = 1;
i = i + 1;
}
}

只需要hook eax寄存器里的值为1 ,这样var4 就会小于1.从而改变程序的运行流程。

1
2
3
4
5
6
7
8
9
10
11
12
13
def enter_forbidden_loop_hook(ql:Qiling):
ql.arch.regs.eax =1

def challenge4(ql:Qiling):
print(ql.path)
# get_lib_base need the real filename which is qilinglab-x86_64
# 获取真实的文件的路径
base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
print(base_addr)
# Address we need to patch
test_forbidden_loop_enter =base_addr+ 0xE43
# Place hook
ql.hook_address(enter_forbidden_loop_hook, test_forbidden_loop_enter)

主要收获: 修改某个指令中的寄存器的值。

hook_address(callback,address) hook 特定的地址

get_lib_base(filename) 获取文件的基址

challenge5 writeup

这个函数要求,所有从rand函数出来的值都要相等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned __int64 __fastcall challenge5(_BYTE *a1)
{
unsigned int v1; // eax
int i; // [rsp+18h] [rbp-48h]
int j; // [rsp+1Ch] [rbp-44h]
int v5[14]; // [rsp+20h] [rbp-40h]
unsigned __int64 v6; // [rsp+58h] [rbp-8h]

v6 = __readfsqword(0x28u);
v1 = time(0LL);
srand(v1);
for ( i = 0; i <= 4; ++i )
{
v5[i] = 0;
v5[i + 8] = rand();
}
for ( j = 0; j <= 4; ++j )
{
if ( v5[j] != v5[j + 8] )
{
*a1 = 0;
return __readfsqword(0x28u) ^ v6;
}
}
*a1 = 1;
return __readfsqword(0x28u) ^ v6;
}

只要每次让rand() 每次返回0 就可以。

1
2
3
4
5
6
def hook_rand(ql:Qiling):
ql.arch.regs.rax = 0

def challenge5(ql:Qiling):
ql.os.set_api('rand',hook_rand)

主要收获:

rax 寄存器主要作为函数返回值使用。因此直接hook rand 函数返回值传入的寄存器rax 即可。

os.set_api() 主要用于windows API 函数的hook

challenge6 writeup

死循环

1
2
3
4
5
6
7
8
9
10
11
12
void challenge6(void)

{
do {
} while( true );
}

.text:0000000000000F12 movzx eax, [rbp+var_5]
.text:0000000000000F16 test al, al # 修改rax 寄存器的值
.text:0000000000000F18 jnz short loc_F0B
.text:0000000000000F1A mov rax, [rbp+var_18]
.text:0000000000000F1E mov byte ptr [rax], 1

只要修改死循环的部门

1
2
3
4
5
6
7
8
def hook_while_true(ql:Qiling):

ql.arch.regs.rax = 0

def challenge6(ql:Qiling):
base = ql.mem.get_lib_base("qilinglab-x86_64")
ql.hook_address(hook_while_true,base + 0xF16)

challenge7 writeup

这是一个sleep 的函数,challenge7 函数会返回sleep 函数,一直sleep。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.text:0000000000000F24 challenge7      proc near               ; CODE XREF: start+1FB↓p
.text:0000000000000F24
.text:0000000000000F24 var_8 = qword ptr -8
.text:0000000000000F24
.text:0000000000000F24 ; __unwind {
.text:0000000000000F24 push rbp
.text:0000000000000F25 mov rbp, rsp
.text:0000000000000F28 sub rsp, 10h
.text:0000000000000F2C mov [rbp+var_8], rdi
.text:0000000000000F30 mov rax, [rbp+var_8]
.text:0000000000000F34 mov byte ptr [rax], 1
.text:0000000000000F37 mov edi, 0FFFFFFFFh ; seconds
.text:0000000000000F3C call _sleep # hook 这个地址
.text:0000000000000F41 nop
.text:0000000000000F42 leave
.text:0000000000000F43 retn
.text:0000000000000F43 ; } // starts at F24
.text:0000000000000F43 challenge7 endp

unsigned int __fastcall challenge7(_BYTE *a1)
{
*a1 = 1;
return sleep(0xFFFFFFFF);
}

hook 的办法,可以修改sleep 的参数。

网上有三种办法: 1.直接使用qiling 的api 来hook sleep 函数,直接返回。

  1. 直接hook 这个参数
  2. 直接hook sleep 的系统调用函数nanosleep (这个用的应该不多)
1
2
3
4
5
6
7
8
9
10
11
def modify_arg(ql:Qiling):
ql.arch.regs.edi=0
def hook_sleep(ql:Qiling):
return
def challenge7(ql:Qiling):
base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
# method1 直接hook sleep 传入的参数 的寄存器edi
#ql.os.set_api('sleep',modify_arg,QL_INTERCEPT.ENTER)
ql.hook_address(modify_arg,base_addr+0xF3C)
# method2 直接hook sleep 函数,让它返回0
#ql.os.set_api('sleep',hook_sleep)

challenge8 writeup

challenge9 writeup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
unsigned __int64 __fastcall challenge9(bool *a1)
{
char *i; // [rsp+18h] [rbp-58h]
char dest[32]; // [rsp+20h] [rbp-50h] BYREF
char src[40]; // [rsp+40h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+68h] [rbp-8h]

v5 = __readfsqword(0x28u);
strcpy(src, "aBcdeFghiJKlMnopqRstuVWxYz");
strcpy(dest, src);
for ( i = dest; *i; ++i )
*i = tolower(*i);
*a1 = strcmp(src, dest) == 0;
return __readfsqword(0x28u) ^ v5;

只需要最终的src和dest 一致就够了,但是tolower 会改变dest的大小写。

1
2
3
4
5
6
7
8
9
10
def hook_tolower(ql:Qiling):
return

def hook_strcmp(ql:Qiling):
ql.arch.regs.rax=0
#return 0

def challenge9(ql:Qiling):
ql.os.set_api('strcmp',hook_strcmp)
#ql.os.set_api('tolower',hook_tolower)

因此只需要hook 其中的strcmp() 和tolower 函数。

challenge10 writeup hook FS

hook 文件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
unsigned __int64 __fastcall challenge10(_BYTE *a1)
{
int i; // [rsp+10h] [rbp-60h]
int fd; // [rsp+14h] [rbp-5Ch]
ssize_t v4; // [rsp+18h] [rbp-58h]
char buf[72]; // [rsp+20h] [rbp-50h] BYREF
unsigned __int64 v6; // [rsp+68h] [rbp-8h]

v6 = __readfsqword(0x28u);
fd = open("/proc/self/cmdline", 0);
if ( fd != -1 )
{
v4 = read(fd, buf, 0x3FuLL);
if ( v4 > 0 )
{
close(fd);
for ( i = 0; v4 > i; ++i )
{
if ( !buf[i] )
buf[i] = 32;
}
buf[v4] = 0;
if ( !strcmp(buf, "qilinglab") )
*a1 = 1;
}
}
return __readfsqword(0x28u) ^ v6;

这里的hook思路是hook 文件系统/proc/self/cmdline ,使读取的值是qilinglab

1
2
3
4
5
6
7
8
9
class hook_cmdline(QlFsMappedObject):
def read(self,size):
return b"qilinglab"
def close(self):
return 0

def challenge10(ql:Qiling):
ql.add_fs_mapper("/proc/self/cmdline",hook_cmdline())

还有另一种方法

可以直接替换指定文件成我们的文件,先创建我们需要的文件:

1
echo -n "qilinglab"> fake_cmdline

然后代码就可以这样写了:

1
2
def challenge10(ql:Qiling):
ql.add_fs_mapper("/proc/self/cmdline", "./fake_cmdline")

还一种方法,代码也不用写了,直接往qiling的rootfs里面写:

1
2
mkdir -p ./qiling/examples/rootfs/x8664_linux/proc/self
echo -n "qilinglab" > ./qiling/examples/rootfs/x8664_linux/proc/self/cmdline

challenge11 writeup hook 指令

使用的是hook_code()对单挑指令进行hook,不同于hook库函数和系统调用,这里使用hook_code对单条指令进行hook,hook指令的时候可以限制hook的范围,加速hook的效率,具体的方法是通过map的读写权限和map的文件名限制hook的范围

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def hook_cpuid(ql,address,size):
opcode = ql.mem.read(address,size)
if opcode == b"\x0F\xA2":
# bypass check and jump to next instruction
ql.reg.ebx = 0x696C6951
ql.reg.ecx = 0x614C676E
ql.reg.edx = 0x20202062
ql.reg.rip += 2
# search code segment hook every cpuid
begin, end = 0, 0
for info in ql.mem.map_info:
if ql.path in info[3] and info[2] == 5:
begin,end = info[:2]
ql.hook_code(hook_cpuid,begin=begin,end=end)

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#-*- coding:utf-8 -*-
#!/usr/bin/env python3
#
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import sys
sys.path.append("..")

from qiling import *
#from qiling.const import QL_VERBOSE
from qiling.const import *
from qiling.os.mapper import QlFsMappedObject
import os

#
def challenge1(ql:Qiling):
print("1111111"+str(ql.mem.is_mapped(0x1000,0x1000)))
ql.mem.map(0x1000,0x1000,info='[challenge1]')
ql.mem.write(0x1337,ql.pack16(1337)) # pack16(value) == struct.pack('H',value)


def hook_uname_on_exit(ql: Qiling, *args):
'''
.text:0000000000000BD8 lea rax, [rbp+name]
.text:0000000000000BDF mov rdi, rax ; name
.text:0000000000000BE2 call _uname
.text:0000000000000BE7 test eax, eax
'''
rdi = ql.arch.regs.rdi
ql.mem.write(rdi, b'QilingOS\x00')
ql.mem.write(rdi + 65 * 3, b'ChallengeStart\x00')

def challenge2(ql:Qiling):
ql.os.set_syscall('uname', hook_uname_on_exit, QL_INTERCEPT.EXIT)


class Fake_urandom(QlFsMappedObject):
# Fake read fs operation
def read(self, size):
if size == 1:
return b"\x41"
return b"\x00"*size
# Fake close fs operation
def close(self):
return 0

def getrandom_hook(ql:Qiling, buf, buflen, flags, *args):
ql.mem.write(buf,b"\x00"*buflen)
ql.os.set_syscall_return(0)

def challenge3(ql:Qiling):
ql.os.set_syscall("getrandom", getrandom_hook)
ql.add_fs_mapper("/dev/urandom",Fake_urandom())

def enter_forbidden_loop_hook(ql:Qiling):
ql.arch.regs.eax =1

def challenge4(ql:Qiling):
print("[++++]"+os.path.split(ql.path)[-1])
# get_lib_base need the real filename which is qilinglab-x86_64
base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
print(base_addr)
# Address we need to patch
test_forbidden_loop_enter =base_addr+ 0xE43
# Place hook
ql.hook_address(enter_forbidden_loop_hook, test_forbidden_loop_enter)

def hook_rand(ql:Qiling):
ql.arch.regs.rax = 0

def challenge5(ql:Qiling):
ql.os.set_api('rand',hook_rand)


def hook_while_true(ql:Qiling):

ql.arch.regs.rax = 0

def challenge6(ql:Qiling):
base = ql.mem.get_lib_base("qilinglab-x86_64")
ql.hook_address(hook_while_true,base + 0xF16)

def modify_arg(ql:Qiling):
base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
ql.arch.regs.edi=0


def hook_sleep(ql:Qiling):
return

def challenge7(ql:Qiling):
# 这个是获取文件的名字
base_addr = ql.mem.get_lib_base(os.path.split(ql.path)[-1])
# method1
#ql.os.set_api('sleep',modify_arg,QL_INTERCEPT.ENTER)
ql.hook_address(modify_arg,base_addr+0xF3C)
# method2
#ql.os.set_api('sleep',hook_sleep)

def hook_tolower(ql:Qiling):
return

def hook_strcmp(ql:Qiling):
ql.arch.regs.rax=0
#return 0

def challenge9(ql:Qiling):
ql.os.set_api('strcmp',hook_strcmp)
#ql.os.set_api('tolower',hook_tolower)

class hook_cmdline(QlFsMappedObject):
def read(self,size):
return b"qilinglab"
def close(self):
return 0

def challenge10(ql:Qiling):
ql.add_fs_mapper("/proc/self/cmdline",hook_cmdline())



if __name__ == "__main__":
#ql = Qiling(["rootfs/x8664_linux/qilinglab-x86_64"], "rootfs/x8664_linux", verbose=QL_VERBOSE.DEFAULT)
path=["rootfs/x8664_linux/qilinglab-x86_64"]
rootfs = "rootfs/x8664_linux"
ql = Qiling(path, rootfs)
challenge1(ql)
challenge2(ql)
challenge3(ql)
challenge4(ql)
challenge5(ql)
challenge6(ql)
challenge7(ql)
challenge9(ql)
challenge10(ql)
ql.verbose = 0
ql.mem.show_mapinfo()
ql.run()

参考讲解:

https://badmonkey.site/archives/qilinglab-writeup#challenge-11


QiLing 框架学习WriteUp
https://tig3rhu.github.io/2023/12/18/3__QiLing_框架学习/
Author
Tig3rHu
Posted on
December 18, 2023
Licensed under