第4届四川省网络空间安全大赛-Re-初赛
链接:https://pan.baidu.com/s/1EloxPLfkjCthhkMtrf1McQ
提取码:D0g3
1). 直接运行, 一波乱点击
每次点击一个flag按钮,都会弹窗让你输入,输入为空也好,不为空也罢,都会原封不动的显示你的输入
仔细发现
你点击倒数第2个按钮,他的显示不一样,别人输错显示Oops,Nothing
而倒数第二个显示Oops,有点不同
这说明了啥? CTFer要观察仔细呀~我只想说TMD
你每点击一个按钮,她都会显示一个字符串,看上去像Base64,加密算法千千万,试几下就可以了,不是BAse64也别伤心
不过你无法直接copy字符串,然后解密,得去IDA搜索字符串,然后copy
如果你去Base64解密了,然后解密到了倒数第二个,发现解密的结果是F14g here here
,所以flag的获取和倒数第二个有关
问题来了?怎么找到倒数第二个的窗口处理函数?
2). 拖进Die,看看有什么
发现没什么坑,是一个PE32的文件不过,有TLS
涉及了3个TLS函数的地址,去IDA查看了一下,
1 2 3 | 0x005512C4 0x0055D241 0x00552A22 |
也没过多的TLS分析,没看出啥坑,可能对解题有所影响,但是最后没啥影响
可能TLS是自带的,用于清理或者初始化
3).然后就IDA调试
F9就退出,说明有反调试
一般的话,我可以想到的退出方式
1 2 3 | exit( - 1 ) TerminalProcess() ExitProcess() |
于是就去xdbg看一下AmazingMFC.exe调用了哪些API,其实就去看一下有没有退出函数
下面是对应API调用的位置
1 2 3 4 5 6 7 8 9 10 | TerminalProcess() 0x00540D34 0x005474B2 0x0055044B ExitProcess() 0x0040328F 0x00403E10 0x004045F5 0x0055045D |
于是就去对应地址和对应地方看这些调用,然后在可能退出的地方下断点,也没多少个退出函数,就在调用之前都下一个断点
于是下断电,F9,然后对应地方F9,看看会不会执行退出函数,如果执行了,那就修改流程,如果没有执行退出函数就不管
当然会执行退出函数
于是你可以在修改 标志寄存器,IP地址,修改汇编指令 来不退出
我退出调试,就修改jz为jnz,然乎保存到文件里面去(注意备份一下源文件)
然后F9调试,就再也不会退出了
问题:怎么定位函数?
1). 函数的交叉引用,比如哪些函数都调用了相同的API
2). 利用字符串的调用,我不过多的赘述(不是很行得通)
疑惑
字符串Oops, nothing!
被9次调用,能理解,但是字符串Oops
在IDA里面看不到谁调用了它
正在的flag函数
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 | DlgItem = CWnd::GetDlgItem(this, - 1 ); CWnd::SetWindowTextA(DlgItem, "RjE0ZyBoZXJlIGhlcmU=" ); / / F14g here here strcpy(Source, "CFTDSA|6470*\"c*a6eaa>2fz" ); sub_401C40( 0xD8u ); sub_4045A0(( int )v10, 0 ); v15 = 0 ; CDialog::DoModal((CDialog * )v10); / / 弹窗喊你输入的函数 sub_404300(v11); v8 = std::_Ptr_base<_EXCEPTION_RECORD const>::get((char * )this + 208 ); v7 = sub_403D20(v8); / / v8是指针,指向了输入的字符串,这里是第一个flag校验函数,内含有花指令 BeingDebugged = 0 ; BeingDebugged = NtCurrentPeb() - >BeingDebugged; if ( v7 ! = 0xF1BFF95A || BeingDebugged ) / / 如果你在调试或者你的flag校验值不为 0xF1BFF95A ,就死翘翘 { v5 = (const CHAR * )std::_Ptr_base<_EXCEPTION_RECORD const>::get((char * )this + 208 ); CWnd::MessageBoxA(this, v5, "Oops" , 0 ); } else / / 正常情况下,输出的函数 { v2 = std::_Ptr_base<_EXCEPTION_RECORD const>::get((char * )this + 208 ); * ((_DWORD * )this + 53 ) = sub_548134(v3, v2); sub_401C40( 4u ); sub_401FF0(&v12); LOBYTE(v15) = 1 ; sub_403E30(Source); / / 解密字符串 6470 * \"c * a6eaa> 2fz 的函数,也有花指令,去掉花之后,还要去掉一个异常 sub_402E40(( int )&v12, Source, * ((_DWORD * )this + 53 )); v4 = (const CHAR * )std::_Ptr_base<_EXCEPTION_RECORD const>::get(&v12); CWnd::MessageBoxA(this, v4, "Oops" , 0 ); LOBYTE(v15) = 0 ; sub_4030C0(&v12); } v15 = - 1 ; return sub_404660(); } |
另外一个去花是同理的
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
涉及的花指令类型是一致的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | .text: 00403D29 E8 01 00 00 00 call loc_403D2F .text: 00403D29 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00403D2E 84 db 84h .text: 00403D2F ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00403D2F .text: 00403D2F loc_403D2F: ; CODE XREF: sub_403D20 + 9 ↑j .text: 00403D2F db 36h .text: 00403D2F 36 83 04 24 08 add [esp + 18h + var_18], 8 .text: 00403D34 C3 retn .text: 00403D34 sub_403D20 endp ; sp - analysis failed .text: 00403D34 .text: 00403D34 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00403D35 F2 db 0F2h .text: 00403D36 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00403D36 C7 45 FC 37 25 00 00 mov dword ptr [ebp - 4 ], 2537h .text: 00403D3D |
执行 call loc_403D2F 等价于 push 0x00403D2E
然后 add [esp+18h+var_18], 8 等价于 0x00403D2E+8
所以栈里面的IP就变为了0x00403D2E+8=0x00403D36
然后ret 等价于 ip=0x00403D36
所以你需要干的是
把下面的nop掉
1 2 3 4 5 6 7 8 9 10 11 12 13 | E8 01 00 00 00 call loc_403D2F .text: 00403D29 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00403D2E 84 db 84h .text: 00403D2F ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00403D2F .text: 00403D2F loc_403D2F: ; CODE XREF: sub_403D20 + 9 ↑j .text: 00403D2F db 36h .text: 00403D2F 36 83 04 24 08 add [esp + 18h + var_18], 8 .text: 00403D34 C3 retn .text: 00403D34 sub_403D20 endp ; sp - analysis failed .text: 00403D34 .text: 00403D34 ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - .text: 00403D35 F2 db 0F2h |
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
IDC脚本
其中一个去掉花的IDC脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #include <idc.idc> static main() { auto x,FBin,ProcRange; FBin = "E8 01 00 00 00 84 36 83 04 24 08 C3 F2" ; / / 目标操作码 for (x = FindBinary(MinEA(), 0x03 ,FBin);x ! = BADADDR;x = FindBinary(x, 0x03 ,FBin)) { / / 起点 PatchDword(x, 0x90909090 ); / / 90 90 90 90 00 84 36 83 04 24 08 C3 F2 x = x + 4 ; PatchDword(x, 0x90909090 ); / / 90 90 90 90 90 90 90 90 04 24 08 C3 F2 x = x + 4 ; PatchDword(x, 0x90909090 ); / / 90 90 90 90 90 90 90 90 90 90 90 90 F2 x = x + 4 ; PatchByte (x, 0x90 ); / / 90 90 90 90 90 90 90 90 90 90 90 90 90 } } |
然后鼠标框住push ebp 一直到ret,然后U一下,也就是取消之前IDA的分析
然后框住0x55~0xC3,然后C一下,点击强制分析,也就睡重新生成汇编代码
然后框住push ebp,一直到ret,然后P一下,也就是重新形成函数
就得到
1 2 3 4 5 6 7 8 9 10 11 12 13 | int __cdecl sub_403D20(unsigned __int8 * a1) { int v2; / / [esp + 10h ] [ebp - 8h ] int i; / / [esp + 14h ] [ebp - 4h ] for ( i = 9527 ; ; i = v2 + 33 * i ) { v2 = * a1 + + ; if ( !v2 ) break ; } return i; } |
解密的Python如下
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 | flag1 = '' flag2 = '' enc = b 'CFTDSA|6470*\"c*a6eaa>2fz' for i in range ( 24 ): flag1 + = chr (enc[i]^ 7 ) bignum = [ 0 ] * 8 for bignum[ 0 ] in range ( 0x30 , 0x39 ): for bignum[ 1 ] in range ( 0x30 , 0x39 ): for bignum[ 2 ] in range ( 0x30 , 0x39 ): for bignum[ 3 ] in range ( 0x30 , 0x39 ): for bignum[ 4 ] in range ( 0x30 , 0x39 ): for bignum[ 5 ] in range ( 0x30 , 0x39 ): for bignum[ 6 ] in range ( 0x30 , 0x39 ): for bignum[ 7 ] in range ( 0x30 , 0x39 ): num = 9527 for i in range ( 8 ): tmp = bignum[i] num = tmp + 33 * num if (num& 0xffffffff = = 0xF1BFF95A ): for i in range ( 8 ): flag2 + = chr (bignum[i]) flag = flag1.replace( "%d" ,flag2) print (flag) exit( 0 ) #DASCTF{1307-23075657-f1bff95a} |