ZCTF - 2016 - Reverse100

题目链接

题目只给了一个程序,没有其他信息。打开程序,发现是控制台,接受用户输入。输入完回车后就退出了。打开OD载入,发现没有加壳。一路F8直到程序处于“运行”状态(获取用户输入)。在call 004011F0处停住。如果在这一句的下一句设断,输入完后程序就退了,说明主程序就在004011F0里面。

00407B80  |.  50            push    eax
00407B81  |.  FF35 44754100 push    dword ptr [417544]
00407B87  |.  FF35 40754100 push    dword ptr [417540]
00407B8D  |.  E8 5E96FFFF   call    004011F0
00407B92  |.  83C4 0C       add     esp, 0C
00407B95  |.  8945 E4       mov     dword ptr [ebp-1C], eax
00407B98  |.  50            push    eax
00407B99  |.  E8 A1F3FFFF   call    00406F3F

于是重开程序,在call 004011F0这里F7跟进,同样发现程序在call 00401C00这一句运行起来。在后一句设断,发现输入完后程序就停住了。猜想00401C00是获取输入的,我把他注释为scanf。下一步的lea就是把输入字符串的地址存到edi里面。

004011FF   .  68 C0714100   push    004171C0
00401204   .  E8 F7090000   call    00401C00
00401209   .  8D7C24 58     lea     edi, dword ptr [esp+58]
0040120D   .  83C9 FF       or      ecx, FFFFFFFF
00401210   .  33C0          xor     eax, eax

下面会有3步操作,经过分析发现是将字符串长度存到ecx里。

00401215   .  F2:AE         repne   scas byte ptr es:[edi]
00401217   .  F7D1          not     ecx
00401219   .  49            dec     ecx

然后程序判断ecx是不是10(h),如果不是则不跳。可以分析出不跳的话进入的函数为退出程序,及时做好Mark。所以我们输入的字符串应该是16长度的,很可能就是最后的Flag。到达je的那一句时,我们先双击修改ZF的值让他跳过去。

0040121A   .  83F9 10       cmp     ecx, 10
0040121D   .  74 06         je      short 00401225
0040121F   .  50            push    eax
00401220   .  E8 1A5D0000   call    <exit>

跳过来后,接下来这一段是把一串字符复制到一个位置并补0,累计占了4*10(h)+1+2+1+8*4=100长度。复制的字符百度一下,为茶花女片段。

00401225   > \B9 10000000   mov     ecx, 10
0040122A   .  BE 9C544100   mov     esi, 0041549C                              ;  ASCII "My dear,for the hurt you sought to do me was is your good opinion."
0040122F   .  8D7C24 60     lea     edi, dword ptr [esp+60]
00401233   .  33C0          xor     eax, eax
00401235   .  F3:A5         rep     movs dword ptr es:[edi], dword ptr [esi]
00401237   .  66:A5         movs    word ptr es:[edi], word ptr [esi]
00401239   .  A4            movs    byte ptr es:[edi], byte ptr [esi]
0040123A   .  B9 08000000   mov     ecx, 8
0040123F   .  8DBC24 A30000>lea     edi, dword ptr [esp+A3]
00401246   .  F3:AB         rep     stos dword ptr es:[edi]
00401248   .  AA            stos    byte ptr es:[edi]

后面是进行类似的操作,总计16句话,在内存中依次存放,长度均补成100。最后我们来到一串赋值处,将16个dword值写在上面那些句子之前。

004014B5   .  C74424 10 86B>mov     dword ptr [esp+10], 2BC86
004014BD   .  C74424 14 B8A>mov     dword ptr [esp+14], 2ADB8
004014C5   .  C74424 18 18C>mov     dword ptr [esp+18], 2C218
004014CD   .  C74424 1C 5EC>mov     dword ptr [esp+1C], 2C85E                  
004014D5   .  C74424 20 42C>mov     dword ptr [esp+20], 2CA42                  
004014DD   .  C74424 24 58C>mov     dword ptr [esp+24], 2C158                  
004014E5   .  C74424 28 C0C>mov     dword ptr [esp+28], 2C4C0                  
004014ED   .  C74424 2C ABB>mov     dword ptr [esp+2C], 2B8AB
004014F5   .  C74424 30 49C>mov     dword ptr [esp+30], 2C549
004014FD   .  C74424 34 5BB>mov     dword ptr [esp+34], 2B15B
00401505   .  C74424 38 DBA>mov     dword ptr [esp+38], 2A7DB
0040150D   .  C74424 3C AAB>mov     dword ptr [esp+3C], 2BEAA                  
00401515   .  C74424 40 81B>mov     dword ptr [esp+40], 2BA81
0040151D   .  C74424 44 F1A>mov     dword ptr [esp+44], 2ACF1
00401525   .  C74424 48 08B>mov     dword ptr [esp+48], 2BA08                  
0040152D   .  C74424 4C D8A>mov     dword ptr [esp+4C], 2ACD8

接下来一段将第一句英文的地址压栈,(观察数据窗)路过call之后发现英文的逗号空格都没了,但是占位还是100。不妨先做好注释。

00401535   .  33DB          xor     ebx, ebx
00401537   .  8D6C24 10     lea     ebp, dword ptr [esp+10]
0040153B   .  8D7424 60     lea     esi, dword ptr [esp+60]
0040153F   >  56            push    esi
00401540   .  33FF          xor     edi, edi
00401542   .  E8 B9FAFFFF   call    <去掉字符串的空格逗号>

下面的操作就很重要了。程序读取第一段英文的第一个字符,读取用户输入的第一个字符,乘起来并加到edi里面。接着循环16次,即读取16个字符分别相乘相加存入edi,再判断值是否与ebp指向位置的值相同。在数据窗中来到ebp指向的位置,发现这个位置就是前面复制的16个dword值中的第一个。如果不相等程序就退出了,我们在jnz那里修改ZF的值,进行之后的循环,很快就找到了规律。程序让用户输入的16个字符与茶花女给出的16句话的前16个字符(去掉空格逗号等)相乘相加,再判断是否与给定的16个值相同。

00401547   .  83C4 04       add     esp, 4
0040154A   .  33C0          xor     eax, eax
0040154C   >  0FBE0C06      movsx   ecx, byte ptr [esi+eax]
00401550   .  0FBE5404 50   movsx   edx, byte ptr [esp+eax+50]
00401555   .  0FAFCA        imul    ecx, edx
00401558   .  03F9          add     edi, ecx
0040155A   .  40            inc     eax
0040155B   .  83F8 10       cmp     eax, 10
0040155E   .^ 7C EC         jl      short 0040154C
00401560   .  397D 00       cmp     dword ptr [ebp], edi
00401563   .  0F85 89000000 jnz     <exit>
00401569   .  43            inc     ebx
0040156A   .  83C6 64       add     esi, 64
0040156D   .  83C5 04       add     ebp, 4
00401570   .  83FB 10       cmp     ebx, 10
00401573   .^ 7C CA         jl      short 0040153F

为了解这个16元线性方程组,写个程序将这些数据以十进制输出,然后用matlab解,再以字符格式输出(我不知道有没有办法在matlab里面直接输入16进制数),发现用户输入的应该是“zctf{Wrong_Flag}”。

这什么鬼。我一下子崩溃了。如果确信前面的步骤没有分析错,就知道后面还有文章。接着程序输出”zctf@You are almost so clever!”,然后来到call 00401050。如果之前查看程序字符串,就知道还有一句”zctf@You are so clever!”,这应该就是最终的结果了。这一段字符串在00401050被引用,可见这里面大有玄机。选择跟进(不然就return了),并注意到用户输入的字符串地址被压栈。

004015DA   > \8D4C24 50     lea     ecx, dword ptr [esp+50]
004015DE   .  51            push    ecx
004015DF   .  E8 6CFAFFFF   call    00401050
004015E4   .  83C4 04       add     esp, 4
004015E7   .  5F            pop     edi
004015E8   .  5E            pop     esi
004015E9   .  5D            pop     ebp
004015EA   .  5B            pop     ebx
004015EB   .  81C4 90060000 add     esp, 690
004015F1   .  C3            retn

进入后发现又是一个scanf,接着把字符串长度存入ecx并压栈调用00401600输出字符串长度,但是不知道有什么用。然后还是获取字符串长度,与14(h)比较,不相等就退出。说明第二次输入的字符串长度为20。在je处把ZF改掉,跳转后继续分析。

0040106A  |.  E8 910B0000   call    <scanf>
0040106F  |.  8D7C24 14     lea     edi, dword ptr [esp+14]
00401073  |.  83C9 FF       or      ecx, FFFFFFFF
00401076  |.  33C0          xor     eax, eax
00401078  |.  83C4 08       add     esp, 8
0040107B  |.  F2:AE         repne   scas byte ptr es:[edi]
0040107D  |.  F7D1          not     ecx
0040107F  |.  49            dec     ecx
00401080  |.  51            push    ecx
00401081  |.  B9 30714100   mov     ecx, 00417130
00401086  |.  E8 75050000   call    00401600
0040108B  |.  8D7C24 0C     lea     edi, dword ptr [esp+C]
0040108F  |.  83C9 FF       or      ecx, FFFFFFFF
00401092  |.  33C0          xor     eax, eax
00401094  |.  F2:AE         repne   scas byte ptr es:[edi]
00401096  |.  F7D1          not     ecx
00401098  |.  49            dec     ecx
00401099  |.  83F9 14       cmp     ecx, 14
0040109C  |.  74 06         je      short 004010A4
0040109E  |.  50            push    eax
0040109F  |.  E8 9B5E0000   call    <exit>

又是一连串赋值。因为ebx已经被xor置零了,所以操作里面的ebx就是0。

004010A4  |> \33DB          xor     ebx, ebx
004010A6  |.  C74424 30 320>mov     dword ptr [esp+30], 232
004010AE  |.  895C24 20     mov     dword ptr [esp+20], ebx
004010B2  |.  895C24 24     mov     dword ptr [esp+24], ebx
004010B6  |.  895C24 28     mov     dword ptr [esp+28], ebx
004010BA  |.  895C24 2C     mov     dword ptr [esp+2C], ebx
004010BE  |.  C74424 34 850>mov     dword ptr [esp+34], 285
004010C6  |.  C74424 38 F40>mov     dword ptr [esp+38], 2F4
004010CE  |.  C74424 3C 530>mov     dword ptr [esp+3C], 353
004010D6  |.  C74424 40 980>mov     dword ptr [esp+40], 398
004010DE  |.  C74424 44 F90>mov     dword ptr [esp+44], 3F9
004010E6  |.  C74424 48 6C0>mov     dword ptr [esp+48], 46C
004010EE  |.  C74424 4C E50>mov     dword ptr [esp+4C], 4E5
004010F6  |.  C74424 50 440>mov     dword ptr [esp+50], 544
004010FE  |.  C74424 54 930>mov     dword ptr [esp+54], 593
00401106  |.  C74424 58 FB0>mov     dword ptr [esp+58], 5FB
0040110E  |.  C74424 5C 5A0>mov     dword ptr [esp+5C], 65A
00401116  |.  C74424 60 A10>mov     dword ptr [esp+60], 6A1
0040111E  |.  C74424 64 100>mov     dword ptr [esp+64], 710
00401126  |.  C74424 68 740>mov     dword ptr [esp+68], 774
0040112E  |.  C74424 6C F10>mov     dword ptr [esp+6C], 7F1

下面是一个小循环,观察数据窗知,程序判断用户第二次输入的前4个字符与第一个输入的前4个字符是否相等,有不相等就退出。因为前面我们已经推出来了,所以第二次输入的前面4个字符就是zctf。

00401136  |.  33C0          xor     eax, eax
00401138  |>  8A4C04 0C     /mov     cl, byte ptr [esp+eax+C]
0040113C  |.  8A5404 74     |mov     dl, byte ptr [esp+eax+74]
00401140  |.  3ACA          |cmp     cl, dl
00401142  |.  75 35         |jnz     short <exit>
00401144  |.  40            |inc     eax
00401145  |.  83F8 04       |cmp     eax, 4
00401148  |.^ 7C EE         \jl      short 00401138

下面可以通过跟踪感觉得出结论。程序让ecx从5开始每次加1,取用户输入字符串的前ecx个求和,与上面写入的16个整数对比,如果有不相同的就会退出。写个程序把上面那16个整数错位相减一下,输出字符(第一个字符要用第一个整数减去zctf的字符和来得到),就得到最终结果:zctf{So_Easy_Oh_God}。(Easy个毛线!)

0040114A  |.  B9 05000000   mov     ecx, 5
0040114F  |.  8D7424 30     lea     esi, dword ptr [esp+30]
00401153  |>  33D2          /xor     edx, edx
00401155  |.  33C0          |xor     eax, eax
00401157  |.  3BCB          |cmp     ecx, ebx
00401159  |.  7E 0C         |jle     short 00401167
0040115B  |>  0FBE7C04 0C   |/movsx   edi, byte ptr [esp+eax+C]
00401160  |.  03D7          ||add     edx, edi
00401162  |.  40            ||inc     eax
00401163  |.  3BC1          ||cmp     eax, ecx
00401165  |.^ 7C F4         |\jl      short 0040115B
00401167  |>  3916          |cmp     dword ptr [esi], edx
00401169  |.  75 14         |jnz     short <exit>
0040116B  |.  41            |inc     ecx
0040116C  |.  83C6 04       |add     esi, 4
0040116F  |.  8D51 FF       |lea     edx, dword ptr [ecx-1]
00401172  |.  83FA 14       |cmp     edx, 14
00401175  |.^ 7C DC         \jl      short 00401153
00401177  |.  EB 0C         jmp     short 00401185

接下来可以重新载入程序,输入输出如下:

zctf{Wrong_Flag}

zctf@You are almost so clever!

zctf{So_Easy_Oh_God}

20zctf@You are so clever!

要稳妥一点还可以继续跟踪,已经没内容了。

注:writeup写于2016年1月25日,第一次接触CTF比赛,留作纪念

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值