题目链接详见:https://github.com/Randark-JMT/CTF_Archive/releases/tag/2022-xhlj
re 部分
dual personality
sub_401120
函数,将返回地址改成 jmp far 33:a2
,跳转到64位
1 | int __cdecl sub_401120(size_t Size, int a2) |
需要在 call sub_401120
的下一跳指令处下一个硬件断点
1 | ba e1 004013E8 |
注意到这一段是ret回来的,很像x64下的代码
1 | .text:0040146E 48 dec eax |
main函数扣掉x64的代码
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
第一段x64代码
1 | __int64 sub_0() |
第二段扣掉的x64的代码
1 | __int64 __fastcall sub_30(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5) |
第三段扣掉的x64代码
1 | __int64 sub_160() |
先用magic运算,再ror,再异或
exp:
1 | from Crypto.Util.number import * |
babyre
所有逻辑在initterm_e
里面
initterm_e
可以看作init_array
有三个函数,同时按顺序注册了退出函数
第一个函数,限制了输入范围在[0-9]
1 | sub_4025A0("Input:", v2); |
第二个函数 取反了magic,即变成了”012345678”
1 | for ( i = 0; i < 8; ++i ) |
第三个函数hook了GetLastError
,执行了
1 | qmemcpy(magic2, "dcbahgfelkjiponm", sizeof(magic2)); |
退出的第一个函数,做了类似base64的编码,base8,每三个字节编码到8个字节,将[16:112]
的加密结果进行比对,即知道了[6:42]
1 | base8_encode(Str, (int)&unk_4081C0); |
退出的第二个函数,将编码后的结果进行魔改的SHA1? 计算,但是跑一下很久,很奇怪?
退出的第三个函数,RC4加密
1 | rc4_init((sbox, (int)&unk_408182, 6u); |
这里的dword_408182
正好是输入的字符串str[42:48]
爆rc4密钥反推前6个字节
1 |
|
得到正确的结果
1 | 807391 |
exp:
1 | magic = [0xCF, 0xCE, 0xCD, 0xCC, 0xCB, 0xCA, 0xC9, 0xC8] |
EasyVT
驱动部分打开了VT虚拟机,在sub_401C90
是#VMExit
的处理函数。美化后如下:
1 | int sub_401C90() |
这个处理函数的上层用于设置和恢复环境,vmresume
会恢复到虚拟机执行的位置:
1 | .text:F89DBC10 mov _guest_eax, eax |
应用程序方面,由于已经在虚拟机内了,执行这些虚拟化指令均会导致#VMExit
,执行上述处理函数。
1 | .text:00401111 loc_401111: ; CODE XREF: _main+97↓j |
所以按执行顺序分析#VMExit
的Handler
即可。
exp:
1 |
|
pwn 部分
MessageBoard
迁移栈到bss段上,执行open + sendfile
1 | # coding=utf-8 |
babycalc
off-by-null
+ 任意写一个字节
1 | for ( i = 0; i <= 15; ++i ) |
z3先上
1 | from z3 import * |
修改返回地址低位到leave; ret;
,同时rbp
低位被清零,在远程环境下,rsp
能刚好落在第一次输入的[0:0x100 - 0x30]
附近
1 | # coding=utf-8 |