逆向入坑的试炼,跌跌撞撞地做完,也算是快速得积累了一些逆向的经验,很多地方不甚了了,望见谅
看雪的CTF都是一些逆向题,对于最近想要入门逆向的我很具有吸引力,正在自学win32汇编的我想来练手,看到第一题太简单了,故第二题也想一起做了,然后发现事情并没有那么简单(后来事实证明这个比赛不适合入门,2333) 进入正题
分析
PE分析软件告诉我这是一个未加壳的win32 console程序 (巧了,我正在学win32 汇编)
随直接丢IDA静态分析一下
主函数很简单,显示打印欢迎界面,然后初始化dword_41B034为2,接着调用三个函数,当三个函数进行完之后,若dword_41B034为0的话,就打印
"You get it!\n"
那么先查看调用的第一个函数sub_401050
看似很简单,一开始没注意,先理解为scanf一个字符串进来并返回了该字符串的地址
在看接下来的两个函数
sub_401090
和sub_4010E0
看样子只要v1和v0满足这两个判断的要求就能将
dword_41B034--
两次,就能打印出"You get it!\n"
了
随后我就没啥好说的了,可以直接写脚本跑出来v0和v1就好了
陷入僵局
然后我看了一下别人的WP,发现事情果然没有那么简单
还是太naive啦
原来这两个条件是无解的
5 * (v1 - v0) + v1 == 0x8F503A42
17 * (v1 - v0) + v1 == 0xF3A94883
#两式相减
12*(v1-v0)=1683557953(奇数)
这就非常的尴尬了
还是太naive
再看第一个函数sub_401050
的汇编代码
IDA反出来的代码不太利于理解,直接放Ollydbg的反汇编代码,等会反正会用到它
sub esp,0xC
push ctf2017_.0041B0AC ; Coded by Fpc.\n\n
call ctf2017_.00413D42
add esp,0x4
push ctf2017_.0041B090 ; Please input your code:
call ctf2017_.00413D42
add esp,0x4
lea eax,dword ptr ss:[esp]
push eax
push ctf2017_.0041B08C ; %s
call ctf2017_.00413D73
lea eax,dword ptr ss:[esp+0x8]
add esp,0x14
retn
首先申请的0xc的栈空间push字符串地址,然后print出来,再将esp+4将栈顶指回原处,然后在地址0040106D
处将栈顶push进栈然后将scanf的格式化字符%s
的地址也push进栈,留着scanf备用
然后call scanf,执行完之后esp依然指向字符串%s
地址处
然后esp+0x14即esp+20=esp+12(字符串长)+4(之前push的栈顶地址长度)+4(之前push的格式化字符地址长度)
指回到(0x0018FF40)之前调用函数
sub_401050
的地址,然后return回去
其实说了那么多就是想告诉读者 可以利用缓冲区溢出构造输入的字符串使得return 的地址是可控的
怎么利用?
当我们正正经经地输入12个字符如112233445566
的时候,我们会刚好把申请的12字节缓冲区占满,但是我们如果输入112233445566aaa
,此时aaa
就会覆盖到return的地址上去,然后return
一下就可以执行对用地址的代码了
如果我们想直接跳到打印"You get it!\n"
函数的地址
可以将字符串后三个字符构造为对应ascl码为0x2f 0x10
0x40的字符串就可以跳转到
YouGotit
了
再次陷入僵局
然而事情还是没有那么简单 比赛规定了咱们只能输入数字和字母,而对应ASCLL码为0x2f 0x10 0x40的字符串并不是都是字母和数字 emmmmmmmm...... 果然不适合入门
好戏才刚刚开始
通过观(kan)察(WP)我们注意到有一段很奇怪的数据段
然后还是通过观(kan)察(WP)发现这些数据居然都是代码,而且还是加了花的代码
(在IDA中按C键将数据转换成代码)
emmmmmm....... 这个花加得 果然不适合入门
该数据段的地址为
0x00413131
对应的ASCLL码字符串为11A
到此,我们可以看出这到题的思路就是构造缓冲区溢出,跳转到这个加了花的数据段上执行,然后分析得出flag
看到了大佬的WP中的一句话
可以看到很多跳转指令。熟悉的人一眼就明白。这里其实是花指令。而且是最简单的花指令。就是来回跳一跳,吓唬吓唬你而已。相信我。所有的花指令都是纸老虎。复杂的花指令可以用插件记录程序流程分析一下脱花。简单的花指令(就像本题中遇到的这种)根本不用鸟它。完全是战五渣。直接gang正面……
于是我充满信心,开始了我第一次去花之旅 打开Ollydbg开始动态调试
(恩,这是我第一次动态调试)
构造字符串11223344556611A
使得咱们能给够跳转到0x00413131处执行
首先右键选择分析->从模块移除分析
然后代码就出来了
emmmmmm..... 这么多的花指令跳转,让我瑟瑟发抖
借助上面大佬的"教诲"壮胆,硬着头皮执行了下去 去花的原则是(当然我也不知道这样对不对,是不是一个好方法)
- 上一步指令和跳转所依赖的标志位无关的直接跳过
- 若是遇到依赖的标志位的跳转分支则下断点,选择其中一个跟进,不行的话就重新来,进入另一个分支
历经两天课下时间的动态调试,终于把花给去掉了 整理如下(可能与各位大佬整理的有出入):
;将输入的前12个字符分割为3个长度为4字节的子字符串S1,S2,S3
00413131 add esp,-0x10 ;栈顶指向输入的字符串“11223344556611A”
00413150 xor eax,eax ;将zf置零
00413184 mov dword ptr ds:[0x41B034],eax ;将关键判断字符赋值为0
004131BA pop eax ;eax=S1
004131EB mov ecx,eax ;ecx=S1
0041321F pop eax ;eax=S2
00413254 mov ebx,eax ;ebx=S2
00413289 pop eax ;eax=S3
004132B5 mov edx,eax ;edx=S3
004132E2 mov eax,ecx ;eax=S1
00413316 sub eax,ebx ;eax=S1-S2
if(zf==1){
;即S1==S2
00413306 lea edi,dword ptr ds:[ecx+0x6]
00413301 mov eax,0x28741C79
00413306 lea edi,dword ptr ds:[ecx+0x6]
;死循环
}
else{
;zf==0,即S1!=S2
00413349 shl eax,0x2 ;eax=(S1-S2)*4
00413380 add eax,ecx ;eax=(S1-S2)*4+S1
004133B5 add eax,edx ;eax=(S1-S2)*4+S1+S3
004133E9 sub eax,0xEAF917E2 ;BreakPoint,eax=(S1-S2)*4+S1+S3-0xEAF917E2
if(zf==0)
{
;即(S1-S2)*4+S1+S3!=0xEAF917E2
00413B1E pop eax ; ctf2017_.00413131
00413B4E xor eax,0x1210E
00413B1E pop eax ; ctf2017_.00413131
00413B4E xor eax,0x1210E
00413B83 xor eax,dword ptr ds:[0x41B034]
0040103F push ctf2017_.0041B038 ; ASCII "Bad register-code, keep trying.\n"
;打印上述字符并退出
}
else
{
;zf==1,eax=0
00413455 add eax,ecx ;eax=S1
00413489 sub eax,ebx ;eax=S1-S2
004134BF mov ebx,eax ;ebx=S1-S2
004134F3 shl eax,1 ;BreakPoint,eax=(S1-S2)*2
00413525 add eax,ebx ;eax=(S1-S2)*2+S1-S2
00413559 add eax,ecx ;eax=(S1-S2)*2+S1-S2+S1
0041358F mov ecx,eax ;ecx=(S1-S2)*2+S1-S2+S1
004135C3 add eax,edx ;eax=(S1-S2)*2+S1-S2+S1+S3
004135F7 sub eax,0xE8F508C8 ;BreakPoint,eax=(S1-S2)*2+S1-S2+S1+S3-0xE8F508C8
;jl{
00413B1E pop eax ; ctf2017_.00413131
00413B4E xor eax,0x1210E
00413B83 xor eax,dword ptr ds:[0x41B034]
0040103F push ctf2017_.0041B038 ; ASCII "Bad register-code, keep trying.\n"
打印上述字符并退出
}
;jge 且当zf=0
{
00413B1E pop eax ; ctf2017_.00413131
00413B4E xor eax,0x1210E
00413B83 xor eax,dword ptr ds:[0x41B034]
0040103F push ctf2017_.0041B038 ; ASCII "Bad register-code, keep trying.\n"
;打印上述字符并退出
}
;jge 且当zf=1
{
;即(S1-S2)*2+S1-S2+S1+S3=0xE8F508C8
0041365D mov eax,ecx ;eax=(S1-S2)*2+S1-S2+S1
004136A7 sub eax,edx ;BreakPoint,eax=(S1-S2)*2+S1-S2+S1-S3
004136D8 sub eax,0xC0A3C68 ;eax=(S1-S2)*2+S1-S2+S1-S3-0xC0A3C68
;je
;即(S1-S2)*2+S1-S2+S1-S3=0xC0A3C68
00413777 xor eax,0x8101
004137A9 mov edi,eax
004137E2 xor eax,eax
00413817 stos dword ptr es:[edi]
}
}
跳了无数次坑 这可真是个细心活
最后根据代码可以整理得三个方程
-S2)*4+S1+S3=0xEAF917E2
(S1-S2)*2+S1-S2+S1+S3=0xE8F508C8
(S1-S2)*2+S1-S2+S1-S3=0xC0A3C68
(S1#整理得
5*S1-4*S2+S3=0xEAF917E2
4*S1-3*S2+S3=0xE8F508C8
4*S1-3*S2-S3=0xC0A3C68
三条方程三个未知数,解得
="tsuJ"
S1="rof0"
S2="nuf0" S3
由于是小端存储,所以这12个字符应该是Just0for0fun
最后输入Just0for0fun11A
即可
历经磨难,终于写了出来
这题可以说是非常的有意思了
给出题者点个赞
最后说一句,真的不适合入门!
感谢阅读