逆向入坑的试炼,跌跌撞撞地做完,也算是快速得积累了一些逆向的经验,很多地方不甚了了,望见谅

看雪的CTF都是一些逆向题,对于最近想要入门逆向的我很具有吸引力,正在自学win32汇编的我想来练手,看到第一题太简单了,故第二题也想一起做了,然后发现事情并没有那么简单(后来事实证明这个比赛不适合入门,2333) 进入正题

分析

PE分析软件告诉我这是一个未加壳的win32 console程序 (巧了,我正在学win32 汇编)

随直接丢IDA静态分析一下

主函数很简单,显示打印欢迎界面,然后初始化dword_41B034为2,接着调用三个函数,当三个函数进行完之后,若dword_41B034为0的话,就打印"You get it!\n" 那么先查看调用的第一个函数sub_401050

看似很简单,一开始没注意,先理解为scanf一个字符串进来并返回了该字符串的地址 在看接下来的两个函数sub_401090sub_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]
    }

}

跳了无数次坑 这可真是个细心活

最后根据代码可以整理得三个方程


(S1-S2)*4+S1+S3=0xEAF917E2
(S1-S2)*2+S1-S2+S1+S3=0xE8F508C8
(S1-S2)*2+S1-S2+S1-S3=0xC0A3C68
#整理得
5*S1-4*S2+S3=0xEAF917E2
4*S1-3*S2+S3=0xE8F508C8
4*S1-3*S2-S3=0xC0A3C68

三条方程三个未知数,解得

S1="tsuJ"
S2="rof0"
S3="nuf0"

由于是小端存储,所以这12个字符应该是Just0for0fun

最后输入Just0for0fun11A即可

历经磨难,终于写了出来

这题可以说是非常的有意思了

给出题者点个赞

最后说一句,真的不适合入门!

感谢阅读