好友
阅读权限10
听众
最后登录1970-1-1
|
幼儿园小班
发表于 2023-8-28 03:27
本帖最后由 幼儿园小班 于 2023-9-8 11:33 编辑
qiling framework + qilingLab x86_64 题解
环境
- mac m1
- Qiling framework 1.47dev0
- ida 7.5
- qilingLab
先模拟运行
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题
-
比较简单,就是要地址0x1337的值等于1337即可.但是我们之前一运行就报错.是因为这里的0x1337不存在,所以报错.
-
接下来我们直接写代码
-
-
def challenge1():
ql.mem.map(0x1000, 0x1000)
ql.mem.write(0x1337, ql.pack16(1337))
第2题
-
逻辑解释
-
调用uname函数赋值给name变量
-
name.sysname 要等于 "QilingOS"
-
name.version 要等于 "ChallengeStart"
-
所以我们只需要直接修改 uname函数即可
-
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,大家看张图即可明白了
-
第3题
-
逻辑解释
-
打开/dev/urandom
-
取0x20个字符放到buf
-
取1个字符放到v5
-
生成一个32位的字符放到v7
-
对比buf的每个字符等于v7的每个字符,且buf的字符不等于v5
-
所以解题方式就是做一个假的 /dev/urandom 和 一个我们自己的getrandom
-
-
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题
第5题
-
-
逻辑解释
-
v5[0-4] = 0
-
V5[8-12]=rand() 随机数
-
所以解题方式就是直接改rand 让其返回0即可
-
-
def challenge5():
def my_rand(q: Qiling, *args):
q.arch.regs.eax = 0
ql.os.set_api("rand", my_rand, QL_INTERCEPT.EXIT)
第6题
-
-
逻辑解释,直接一个死循环....参考第4题解题思路
-
-
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题
-
逻辑解释,直接睡死.....
-
解题思路,修改sleep即可
-
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题
-
逻辑解释
-
v2 申请0x18大小的字节, 就是24个字节
-
而dword=4字节,所以v2是6个dowrd
-
qword = 8个字节.
-
为v2起始位置指向的地址 申请0x1e的空间,这里相当于v2[0] + v2[1] = malloc(0x1euLL)
-
v2[2] 和 v2[3] 分别等于 1337 和 1039980266
-
所以v2的第二个指向地址的值是 1039980266 << 32 + 1337,为什么1039980266在前,因为要注意大小端问题,这里为什么要这么算,因为我们要取一个内存魔数,然后通过内存搜索来定位该结构.
-
为v2的起始位置指向的地址赋值成 "Random data"
-
v2+2相当于v2的第三个指向的地址的值=a1,这个就是check值.
-
所以解题方式就是找到这个结构,然后修改第三个值为1即可.
-
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题
第10题
-
逻辑解释
-
打开文件 /proc/self/cmdline
-
读取 0x3f 字节,赋值给buf
-
对buf进行处理,0值赋值成32
-
判断buf是否等于 "qilinglab"
-
所以解题思路就是,直接模拟该文件,让read函数直接读到"qilinglab" 即可
-
-
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题
-
逻辑解释
-
执行汇编指令 cpuid
-
然后判断 rbx+rcx组合后的值要等于 0x696C6951614C676ELL
-
同时 rdx的值要等于 538976354
-
这题比较关键的是 cpuid,不清楚的小伙伴可以自行查询资料.
-
所以我们的解题思路就是在执行cpuid的时候直接不执行,同时对rbc rcx rdx进行赋值即可
-
-
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题确实学到了很多的东西......
|
免费评分
-
查看全部评分
|