qiling framework + qilingLab x86_64 题解
本帖最后由 幼儿园小班 于 2023-9-8 11:33 编辑# qiling framework + qilingLab x86_64 题解
## 环境
- mac m1
- Qiling framework 1.47dev0
- git地址: https://github.com/qilingframework/qiling
- 文档地址: https://docs.qiling.io
- ida 7.5
- qilingLab
- x86_64版本: https://www.shielder.com/attachments/qilinglab-x86_64
- aarch64版本: https://www.shielder.com/attachments/qilinglab-aarch64
### 先模拟运行
```python
ql = Qiling(["qilinglab-x86_64"], "rootfs/x8664_linux",
verbose=QL_VERBOSE.OFF, multithread=True)
ql.run()
```
- 运行后发现报错.
- unicorn.unicorn.UcError: Invalid memory read (UC_ERR_READ_UNMAPPED)
- 说明内存读取不到.不用慌,这是因为第一题我们还没解出来.解出来就解决了.原因下面会讲
### 第1题
- 先看下ida反编译的伪c代码.
!(https://cdn.db-kj.com/markdown/image-20230828014725142.png)
- 比较简单,就是要地址0x1337的值等于1337即可.但是我们之前一运行就报错.是因为这里的0x1337不存在,所以报错.
- 接下来我们直接写代码
-
- ```python
def challenge1():
ql.mem.map(0x1000, 0x1000)
ql.mem.write(0x1337, ql.pack16(1337))
```
### 第2题
!(https://cdn.db-kj.com/markdown/image-20230828020206027.png)
- 逻辑解释
- 调用uname函数赋值给name变量
- name.sysname 要等于 "QilingOS"
- name.version 要等于 "ChallengeStart"
- 所以我们只需要直接修改 uname函数即可
- ```python
def challenge2():
def my_syscall_uname(q: Qiling, *args):
rdi = ql.arch.regs.rdi
ql.mem.write(rdi, b'QilingOS\x00')
ql.mem.write(rdi + 65 * 3, b'ChallengeStart\x00')
ql.os.set_api("uname", my_syscall_uname, QL_INTERCEPT.EXIT)
```
- 可能各位会疑问,为什么写rdi和rdi+65*3,大家看张图即可明白了
- !(https://cdn.db-kj.com/markdown/image-20230828021355516.png)
### 第3题
!(https://cdn.db-kj.com/markdown/image-20230828021512165.png)
- 逻辑解释
- 打开/dev/urandom
- 取0x20个字符放到buf
- 取1个字符放到v5
- 生成一个32位的字符放到v7
- 对比buf的每个字符等于v7的每个字符,且buf的字符不等于v5
- 所以解题方式就是做一个假的 /dev/urandom 和 一个我们自己的getrandom
-
- ```python
def challenge3():
class FKUrandom(QlFsMappedObject):
def read(self, expected_len):
if expected_len > 1:
return b"\x65" * expected_len
else:
return b"\x00"
def fstat(self):
return -1
def close(self):
return 0
def my_get_random(q: Qiling, *args):
p = q.os.resolve_fcall_params({"buf": POINTER, "count": SIZE_T, "flags": INT})
if p["count"] == 32:
q.mem.write(p["buf"], b"\x65" * 32)
return 0
ql.add_fs_mapper("/dev/urandom", FKUrandom())
ql.os.set_api("getrandom", my_get_random, QL_INTERCEPT.CALL)
```
### 第4题
- 该题反编译看不到伪c代码,所以我们直接看汇编码
- !(https://cdn.db-kj.com/markdown/image-20230828022258784.png)
- !(https://cdn.db-kj.com/markdown/image-20230828022514993.png)
- 逻辑解释
- 初始化 = 0 和 = 0
- 0xe43处进行cmp判断,如果相等则死循环
- 所以我们只需要让他们两的值不同即可
- 两种解法:
- 第一种:
- 直接在0xe43处修改eax的值
- ```python
def challenge4():
base_ptr = ql.mem.get_lib_base("qilinglab-x86_64")
hook_addr = base_ptr + 0xe43
def update_eax(q: Qiling, *args):
q.arch.regs.eax = 1
ql.hook_address(update_eax, hook_addr)
```
- 第二种:
- 直接在赋值后进行修改,让其不同
-
- ```python
def challenge4_1():
base_ptr = ql.mem.get_lib_base("qilinglab-x86_64")
hook_addr = base_ptr + 0xe33
def update_var8(q: Qiling, *args):
rbp = q.arch.regs.rbp
q.mem.write(rbp - 0x8, b"\x01")
ql.hook_address(update_var8, hook_addr)
```
### 第5题
- !(https://cdn.db-kj.com/markdown/image-20230828023259185.png)
- 逻辑解释
- v5 = 0
- V5=rand() 随机数
- 所以解题方式就是直接改rand 让其返回0即可
-
- ```python
def challenge5():
def my_rand(q: Qiling, *args):
q.arch.regs.eax = 0
ql.os.set_api("rand", my_rand, QL_INTERCEPT.EXIT)
```
### 第6题
- !(https://cdn.db-kj.com/markdown/image-20230828024829697.png)
- 逻辑解释,直接一个死循环....参考第4题解题思路
-
- ```python
def challenge6():
base_ptr = ql.mem.get_lib_base("qilinglab-x86_64")
hook_addr = base_ptr + 0xf16
def update_rax(q: Qiling, *args):
q.arch.regs.rax = 0
ql.hook_address(update_rax, hook_addr)
```
### 第7题
!(https://cdn.db-kj.com/markdown/image-20230828025012040.png)
- 逻辑解释,直接睡死.....
- 解题思路,修改sleep即可
- ```python
def challenge7():
def my_sleep(q: Qiling, *args):
q.arch.regs.edi = 0
return 0
ql.os.set_api("sleep", my_sleep, QL_INTERCEPT.ENTER)
```
### 第8题
!(https://cdn.db-kj.com/markdown/image-20230828025151930.png)
- 逻辑解释
- v2 申请0x18大小的字节, 就是24个字节
- 而dword=4字节,所以v2是6个dowrd
- qword = 8个字节.
- 为v2起始位置指向的地址 申请0x1e的空间,这里相当于v2 + v2 = malloc(0x1euLL)
- v2 和 v2 分别等于 1337 和 1039980266
- 所以v2的第二个指向地址的值是 1039980266 << 32 + 1337,为什么1039980266在前,因为要注意大小端问题,这里为什么要这么算,因为我们要取一个内存魔数,然后通过内存搜索来定位该结构.
- 为v2的起始位置指向的地址赋值成 "Random data"
- v2+2相当于v2的第三个指向的地址的值=a1,这个就是check值.
- 所以解题方式就是找到这个结构,然后修改第三个值为1即可.
- ```python
def challenge8():
def hook(q: Qiling, *args):
magic = (1039980266 << 32) + 1337
addr_list = q.mem.search(q.pack64(magic))
for addr in addr_list:
st_addr = addr - 8
tmp_struct = q.mem.read(st_addr, 24)
str_addr, _, check_addr = struct.unpack("QQQ", tmp_struct)
# print(q.mem.string(str_addr))
if q.mem.string(str_addr) == "Random data":
# print("Check", q.mem.read(q.arch.regs.rdx, 8))
q.mem.write(check_addr, q.pack8(1))
base_ptr = ql.mem.get_lib_base("qilinglab-x86_64")
ql.hook_address(hook, base_ptr + 0xfb5)
```
### 第9题
!(https://cdn.db-kj.com/markdown/image-20230828031401868.png)
- 逻辑解释
- 对src赋值"aBcdeFghiJKlMnopqRstuVWxYz"
- dest = src 即 dest = "aBcdeFghiJKlMnopqRstuVWxYz"
- 循环对dest做小写处理
- 判断src要等于dest
- 解题思路
- 所以我们只需要处理strcmp或者tolower函数即可.
- 建议不要处理strcmp 因为第10题也用到了这个不利于我们学习.会直接把第10题也过了.
- 所以我们处理tolower
- ```python
def challenge9():
def hook(q: Qiling, *args):
p = q.os.resolve_fcall_params({"s": BYTE})
# print(p["s"])
q.arch.regs.eax = p["s"]
ql.os.set_api("tolower", hook, QL_INTERCEPT.EXIT)
```
### 第10题
!(https://cdn.db-kj.com/markdown/image-20230828031752756.png)
- 逻辑解释
- 打开文件 /proc/self/cmdline
- 读取 0x3f 字节,赋值给buf
- 对buf进行处理,0值赋值成32
- 判断buf是否等于 "qilinglab"
- 所以解题思路就是,直接模拟该文件,让read函数直接读到"qilinglab" 即可
-
- ```python
def challenge10():
class FSHook(QlFsMappedObject):
def read(self, expected_len):
return "qilinglab".encode()
def close(self):
return 0
ql.add_fs_mapper("/proc/self/cmdline", FSHook())
```
### 第11题
!(https://cdn.db-kj.com/markdown/image-20230828032049842.png)
- 逻辑解释
- 执行汇编指令 cpuid
- 然后判断 rbx+rcx组合后的值要等于0x696C6951614C676ELL
- 同时 rdx的值要等于538976354
- 这题比较关键的是 cpuid,不清楚的小伙伴可以自行查询资料.
- 所以我们的解题思路就是在执行cpuid的时候直接不执行,同时对rbc rcx rdx进行赋值即可
-
- ```python
def challenge11():
def hook1(q: Qiling, address, size):
if ql.mem.read(address, size) == b'\x0f\xa2':
regs = ql.arch.regs
regs.ebx = 0x696C6951
regs.ecx = 0x614C676E
regs.edx = 0x20202062
regs.rip += 2
ql.hook_code(hook1)
```
## 到此为止,11题全部拿下.受益匪浅,今后准备在有空的情况下,尝试用qiling框架刷一下ctf的逆向题,然后分享给大家一起学习qiling这个框架.该框架真的非常好.
## 其中第8题和第11题确实学到了很多的东西...... 解析的不错。感谢分享 对于,小白,,,长见识了{:1_921:} 分析详细,非常感谢 学习中,感谢大神分享 @幼儿园小班 有个图好像地址好像不对:image-20230828021355516.png Hmily 发表于 2023-9-6 17:09
@幼儿园小班 有个图好像地址好像不对:image-20230828021355516.png
感谢,之前都没发现,已经修复了 牛掰啊大佬学到了
页:
[1]