XCTF_Re
XCTF 3rd-GCTF-2017
debug
PEID显示为.NET程序
丢进dnspy中查看,得到4个主要函数,分别为
1 | // ᜅ |
1 | // ᜅ |
1 | // ᜅ |
1 | // ᜅ |
在ᜅ.encodeᜀ(a_, Convert.ToInt32(value), ref b);
处下断点进行调试,可以直接得到flag
1 | import hashlib |
hackme
1 | __int64 __fastcall maybe_main(__int64 a1, __int64 a2) |
根据
1 | for ( i = 0; password[i]; ++i ) |
推测password
的长度为22
在do...while
循环中,sub_406D90
是一个较为复杂的函数,猜测是用来生成随机数
循环中的关键代码为
1 | v13 = byte_6B4270[rand % 22]; |
rand%22
说明处理范围在[0,21]
之间,而
1 | v13 = byte_6B4270[rand % 22]; |
说明两者存在一一对应的关系,由此可以得到exp
1 |
|
flag{d826e6926098ef46}
XCTF 3rd-NJCTF-2017
echo-server
错误如图所示,将整段代码Undefine,重新生成,将部分jmp
修改成nop
1 | int __cdecl main() |
1 | unsigned int sub_80487C4() |
1 | .text:08048817 ; char unk_8048817[] |
在Linux下无法直接运行
sudo dpkg --add-architecture i386
启用i386架构
sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y
进行刷新
sudo apt-get install libssl1.0.0:i386
添加运行库
1 | if ( dword_804A088 ) |
在main函数中,dword_804A088
被设置成1
,手动将其patch成0
,或者将jz short loc_8048866
patch成jnz short loc_8048866
运行,并输入F1@gA
,得到flag
XCTF 4th-QCTF-2018
asong
1 | __int64 __fastcall main(__int64 a1, char **a2, char **a3) |
1 | void __fastcall check_and_split(__int64 a1) |
1 | int __fastcall sub_400AAA(const char *a1, __int64 a2) |
1 | __int64 __fastcall get_character_new_index(char a1) |
1 | unsigned __int64 __fastcall sub_400E54(const char *flag, __int64 a2) |
1 | __int64 __fastcall convert(unsigned __int8 *a1) |
1 | _BYTE *__fastcall encrypt(_BYTE *a1, int a2) |
encrypt函数得到的结果被保存在out
文件中
可以根据encrypt函数进行反推
1 | EC29E341E1F7AA1D29ED299939F3B7A9E7AC2BB7AB409FA931352C29EFA83D4BB0E9E1687B41 |
解密
1 | a=[0xEC,0x29,0xE3,0x41,0xE1,0xF7,0xAA,0x1D,0x29,0xED,0x29,0x99,0x39,0xF3,0xB7,0xA9,0xE7,0xAC,0x2B,0xB7,0xAB,0x40,0x9F,0xA9,0x31,0x35,0x2C,0x29,0xEF,0xA8,0x3D,0x4B,0xB0,0xE9,0xE1,0x68,0x7B,0x41] |
验证
1 |
|
对convert函数进行逆向
1 | dword_6020A0=[22, 0, 6, 2, 30, 24, 9, 1, 21, 7, 18, 10, 8, 12, 17, 23, 13, 4, 3, 14, 19, 11, 20, 16, 15, 5, 25, 36, 27, 28, 29, 37, 31, 32, 33, 26, 34, 35] |
根据反汇编对sub_400AAA
进行模拟,从而得到字符频数数组
1 |
|
exp
1 | a=[0xEC,0x29,0xE3,0x41,0xE1,0xF7,0xAA,0x1D,0x29,0xED,0x29,0x99,0x39,0xF3,0xB7,0xA9,0xE7,0xAC,0x2B,0xB7,0xAB,0x40,0x9F,0xA9,0x31,0x35,0x2C,0x29,0xEF,0xA8,0x3D,0x4B,0xB0,0xE9,0xE1,0x68,0x7B,0x41] |
flag:QCTF{that_girl_saying_no_for_your_vindicate}
babymips
利用retdec进行反编译(并不准确,需要结合汇编来看)
python .\retdec-decompiler.py C:\Users\misaka\Downloads\babymips
得到c
文件,内容为反编译代码
关键函数为
1 | int32_t function_4009a8(void) { |
1 | int32_t function_4007f0(int32_t * str) { |
可知信息:
输入的flag的长度为32
对输入的flag进行某种异或操作后,前五个字符要与
Q|j{g
相同剩下的字符进行位移操作和与操作后,得到的字符数组要与
0x52, 0xFD, 0x16, 0xA4, 0x89, 0xBD, 0x92, 0x80, 0x13, 0x41, 0x54, 0xA0, 0x8D, 0x45, 0x18, 0x81, 0xDE, 0xFC, 0x95, 0xF0, 0x16, 0x79, 0x1A, 0x15, 0x5B, 0x75, 0x1F
相同
然后从汇编得到更详细的信息
1 | .text:00400A1C loc_400A1C: # CODE XREF: sub_4009A8+DC↓j |
1 | .text:004007F0 sub_4007F0: # CODE XREF: sub_4009A8+114↓p |
可得exp
1 | data=[81, 124, 106, 123, 103, 82, 253, 22, 164, 137, 189, 146, 128, 19, 65, 84, 160, 141, 69, 24, 129, 222, 252, 149, 240, 22, 121, 26, 21, 91, 117, 31] |
1 | [81, 124, 106, 123, 103, 73, 127, 88, 41, 38, 111, 74, 32, 76, 80, 81, 40, 54, 81, 96, 96, 123, 63, 86, 60, 88, 94, 104, 69, 109, 93, 124] |
flagqctf{ReA11y_4_B@89_mlp5_4_XmAn_}
XCTF 4th-WHCTF-2017
BABYRE
动态分析
- 通过汇编分析
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
尝试进入judge
函数
1 | Decompilation failure: |
judge
汇编
1 | .data:0000000000600B00 public judge |
在if ( v5 == 14 && (unsigned int)judge(&s) )
处下断点进行动态调试
1 | .data:0000000000600B08 mov byte ptr [rbp-20h], 66h |
向栈中填入数值
1 | .data:0000000000600B40 mov dword ptr [rbp-4], 0 ; 初始化某个值为0 |
1 | fmcd=[102, 109, 99, 100, 127, 107, 55, 100, 59, 86, 96, 59, 110, 112] |
flag{n1c3_j0b}
- 动态调试是重新生成伪代码
在第12行,即自解密完成后的地方下断点,进行动态调试,使用程序自身的解密程序进行解密
将有红色报错的代码按U
(取消原来定义),再按C
(重新生成汇编代码),选中600B00-600BB5
(judge的起止位置)按P
(重新生成function),这时就可以
按F5
生成judge的伪代码了
1 | signed __int64 __fastcall judge(__int64 a1) |
同样得到exp
静态分析
judge方法是判断的主要逻辑,在第15行时调用判断,但无法直接进行静态分析,因为judge模块已经被进行了加密,在第7-11行先进行smc自解密,后面才能正常运行,所以按照7-11行的逻辑对judge模块进行解密,使用IDApython手动解密
1 | posi=0x600B00 |
Patch后,将有红色报错的代码按U
(取消原来定义),再按C
(重新生成汇编代码),选中600B00-600BB5
(judge的起止位置)按P
(重新生成function),这时就可以
按F5
生成judge的伪代码了
1 | signed __int64 __fastcall judge(__int64 a1) |
1 | for ( i = 0; i <= 13; ++i ) |
得到与前面相同的exp
EASYHOOK
进行动态调试
1 | int __cdecl main(int argc, const char **argv, const char **envp) |
将断点下在sub_401220();
,进入函数,步过直到return sub_4010D0();
1 | int sub_401220() |
sub_401220();
对WriteFile
进行hook,进入sub_4010D0()
1 | BOOL sub_4010D0() |
当WriteProcessMemory(hProcess, writefile_address, &byte_40C9BC, 5u, 0);
执行完成时,writefile_address
已经变成763E35B0h
,被修改为jmp sub_401080
,说明已经hook完成
此时,回到main函数中,步过到WriteFile(v4, &Buffer, 0x13u, &NumberOfBytesWritten, 0);
,进入WriteFile
1 | kernel32.dll:763E35B0 |
1 | int __stdcall kernel32_WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) |
进入sub_401080
1 | int __stdcall hook_sub(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped) |
encode
函数即为对输入进行处理的函数
1 | signed int __cdecl encode(int input, signed int len) |
根据逻辑进行恢复
1 | byte_40A030=[97, 106, 121, 103, 107, 70, 109, 46, 127, 95, 126, 45, 83, 86, 123, 56, 109, 76, 110, 0] |
得到lag{Ho0k_w1th_Fun}
,补个f
得到flag{Ho0k_w1th_Fun}