博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
IOLI-crackme0x01-0x05 writeup
阅读量:6234 次
发布时间:2019-06-22

本文共 33134 字,大约阅读时间需要 110 分钟。

Radare2

上一篇开了个头, , 由于是第一篇,
所以解释得事无巨细, 今天就稍微加快点步伐, 分析一下另外几个crackme.

如果你忘记了crackme的来源, 那就再告诉你一遍, 它们都是来自.

crackme0x01

直接用radare2打开分析:

[0x080483e4]> pdf @ main            ;-- main:/ (fcn) main 113|   main ();|           ; var int pInput @ ebp-0x4|              ; DATA XREF from 0x08048347 (entry0)|           0x080483e4      55             push ebp|           0x080483e5      89e5           mov ebp, esp|           0x080483e7      83ec18         sub esp, 0x18|           0x080483ea      83e4f0         and esp, 0xfffffff0|           0x080483ed      b800000000     mov eax, 0|           0x080483f2      83c00f         add eax, 0xf|           0x080483f5      83c00f         add eax, 0xf|           0x080483f8      c1e804         shr eax, 4|           0x080483fb      c1e004         shl eax, 4|           0x080483fe      29c4           sub esp, eax|           0x08048400      c70424288504.  mov dword [esp], str.IOLI_Crackme_Level_0x01 ; [0x8048528:4]=0x494c4f49 ; "IOLI Crackme Level 0x01\n"|           0x08048407      e810ffffff     call sym.imp.printf         ; int printf(const char *format)|           0x0804840c      c70424418504.  mov dword [esp], str.Password: ; [0x8048541:4]=0x73736150 ; "Password: "|           0x08048413      e804ffffff     call sym.imp.printf         ; int printf(const char *format)|           0x08048418      8d45fc         lea eax, [pInput]|           0x0804841b      89442404       mov dword [esp + 4], eax|           0x0804841f      c704244c8504.  mov dword [esp], 0x804854c  ; [0x804854c:4]=0x49006425|           0x08048426      e8e1feffff     call sym.imp.scanf          ; int scanf(const char *format)|           0x0804842b      817dfc9a1400.  cmp dword [pInput], 0x149a  ; [0x149a:4]=-1|       ,=< 0x08048432      740e           je 0x8048442[0x080483e4]> ps @ 0x804854c%d

还是scanf获取用户输入, 不过这次是%d即用户输入一个整数, 然后和0x149a比较, 使用rax2转换数据格式:

$ rax2 0x149a5274

所以:

$ ./crackme0x01 IOLI Crackme Level 0x01Password: 5274Password OK :)

密码正确! 和crackme0x00差不多, 逻辑比较简单.

crackme0x02

先运行一下, 发现和之前一样还是要输入密码. radare2打开:

[0x080483e4]> pdf @ main            ;-- main:/ (fcn) main 144|   main ();|           ; var int local_ch @ ebp-0xc|           ; var int local_8h @ ebp-0x8|           ; var int local_4h @ ebp-0x4|           ; var int local_4h_2 @ esp+0x4|              ; DATA XREF from 0x08048347 (entry0)|           0x080483e4      55             push ebp|           0x080483e5      89e5           mov ebp, esp|           0x080483e7      83ec18         sub esp, 0x18|           0x080483ea      83e4f0         and esp, 0xfffffff0|           0x080483ed      b800000000     mov eax, 0|           0x080483f2      83c00f         add eax, 0xf|           0x080483f5      83c00f         add eax, 0xf|           0x080483f8      c1e804         shr eax, 4|           0x080483fb      c1e004         shl eax, 4|           0x080483fe      29c4           sub esp, eax|           0x08048400      c70424488504.  mov dword [esp], str.IOLI_Crackme_Level_0x02 ; [0x8048548:4]=0x494c4f49 ; "IOLI Crackme Level 0x02\n"|           0x08048407      e810ffffff     call sym.imp.printf         ; int printf(const char *format)|           0x0804840c      c70424618504.  mov dword [esp], str.Password: ; [0x8048561:4]=0x73736150 ; "Password: "|           0x08048413      e804ffffff     call sym.imp.printf         ; int printf(const char *format)|           0x08048418      8d45fc         lea eax, [local_4h]|           0x0804841b      89442404       mov dword [local_4h_2], eax|           0x0804841f      c704246c8504.  mov dword [esp], 0x804856c  ; [0x804856c:4]=0x50006425|           0x08048426      e8e1feffff     call sym.imp.scanf          ; int scanf(const char *format)|           0x0804842b      c745f85a0000.  mov dword [local_8h], 0x5a  ; 'Z' ; 90|           0x08048432      c745f4ec0100.  mov dword [local_ch], 0x1ec ; 492|           0x08048439      8b55f4         mov edx, dword [local_ch]|           0x0804843c      8d45f8         lea eax, [local_8h]|           0x0804843f      0110           add dword [eax], edx|           0x08048441      8b45f8         mov eax, dword [local_8h]|           0x08048444      0faf45f8       imul eax, dword [local_8h]|           0x08048448      8945f4         mov dword [local_ch], eax|           0x0804844b      8b45fc         mov eax, dword [local_4h]|           0x0804844e      3b45f4         cmp eax, dword [local_ch]|       ,=< 0x08048451      750e           jne 0x8048461[0x080483e4]> ps @ 0x804856c%d

这个就比之前复杂一点, main函数有三个本地变量, local_ch, local_8hlocal_4h,

但似乎没有初始值. 由0x08048418~0x08048426这几句可以发现local_4h是用户的输入,
且类型为整数.
分析一下用户输入后的逻辑, 先给两个本地变量分别赋值为0x5a和0x1ec, 然后进行数学运算,
先改几个名字方便阅读:

[0x080483e4]> afv-local_4h_2[0x080483e4]> afvn local_4h input[0x080483e4]> afvn local_8h a[0x080483e4]> afvn local_ch b[0x080483e4]> pd 10 @ 0x0804842b0x0804842b      c745f85a0000.  mov dword [a], 0x5a         ; 'Z' ; 900x08048432      c745f4ec0100.  mov dword [b], 0x1ec        ; 4920x08048439      8b55f4         mov edx, dword [b]0x0804843c      8d45f8         lea eax, [a]0x0804843f      0110           add dword [eax], edx0x08048441      8b45f8         mov eax, dword [a]0x08048444      0faf45f8       imul eax, dword [a]0x08048448      8945f4         mov dword [b], eax0x0804844b      8b45fc         mov eax, dword [input]0x0804844e      3b45f4         cmp eax, dword [b]

重新打印scanf之后的10条汇编, 转换成伪代码大意是:

int a = 0x5aint b = 0x1eca = b + aa = a * ab = aif input == b

所以最后和input比较的是(a+b)*(a+b)=582*582=338724, 验证一下:

$ ./crackme0x02 IOLI Crackme Level 0x02Password: 338724Password OK :)

抬走, 下一个.

crackme0x03

流程和之前一样:

$ ./crackme0x03 IOLI Crackme Level 0x03Password: 12345Invalid Password!

不过这次似乎里面的字符串被混淆了, 没有找到Invalid Password出现的地方:

$ rabin2 -z ./crackme0x03 000 0x000005ec 0x080485ec  17  18 (.rodata) ascii Lqydolg#Sdvvzrug$001 0x000005fe 0x080485fe  17  18 (.rodata) ascii Sdvvzrug#RN$$$#=,002 0x00000610 0x08048610  24  25 (.rodata) ascii IOLI Crackme Level 0x03\n003 0x00000629 0x08048629  10  11 (.rodata) ascii Password:

radare2打开并分析main函数, 发现用户输入后调用了test函数, 如下:

...忽略|           0x080484c0      c70424298604.  mov dword [esp], str.Password: ; [0x8048629:4]=0x73736150 ; "Password: "|           0x080484c7      e884feffff     call sym.imp.printf         ; int printf(const char *format)|           0x080484cc      8d45fc         lea eax, [local_4h]|           0x080484cf      89442404       mov dword [local_4h_2], eax|           0x080484d3      c70424348604.  mov dword [esp], 0x8048634  ; [0x8048634:4]=0x6425|           0x080484da      e851feffff     call sym.imp.scanf          ; int scanf(const char *format)|           0x080484df      c745f85a0000.  mov dword [local_8h], 0x5a  ; 'Z' ; 90|           0x080484e6      c745f4ec0100.  mov dword [local_ch], 0x1ec ; 492|           0x080484ed      8b55f4         mov edx, dword [local_ch]|           0x080484f0      8d45f8         lea eax, [local_8h]|           0x080484f3      0110           add dword [eax], edx|           0x080484f5      8b45f8         mov eax, dword [local_8h]|           0x080484f8      0faf45f8       imul eax, dword [local_8h]|           0x080484fc      8945f4         mov dword [local_ch], eax|           0x080484ff      8b45f4         mov eax, dword [local_ch]|           0x08048502      89442404       mov dword [local_4h_2], eax|           0x08048506      8b45fc         mov eax, dword [local_4h]|           0x08048509      890424         mov dword [esp], eax|           0x0804850c      e85dffffff     call sym.test|           0x08048511      b800000000     mov eax, 0|           0x08048516      c9             leave\           0x08048517      c3             ret

main函数内同样有三个本地变量, 面对这种多层调用的目标时候, 可以选择深度优先或者广度优先分析,

这里选择深度优先, 即先分析sym.test函数:

[0x08048498]> pdf @ sym.test/ (fcn) sym.test 42|   sym.test (int arg_8h, int arg_ch);|           ; arg int arg_8h @ ebp+0x8|           ; arg int arg_ch @ ebp+0xc|              ; CALL XREF from 0x0804850c (sym.main)|           0x0804846e      55             push ebp|           0x0804846f      89e5           mov ebp, esp|           0x08048471      83ec08         sub esp, 8|           0x08048474      8b4508         mov eax, dword [arg_8h]     ; [0x8:4]=-1 ; 8|           0x08048477      3b450c         cmp eax, dword [arg_ch]     ; [0xc:4]=-1 ; 12|       ,=< 0x0804847a      740e           je 0x804848a|       |   0x0804847c      c70424ec8504.  mov dword [esp], str.Lqydolg_Sdvvzrug ; [0x80485ec:4]=0x6479714c ; "Lqydolg#Sdvvzrug$"|       |   0x08048483      e88cffffff     call sym.shift|      ,==< 0x08048488      eb0c           jmp 0x8048496|      |`-> 0x0804848a      c70424fe8504.  mov dword [esp], str.Sdvvzrug_RN ; [0x80485fe:4]=0x76766453 ; "Sdvvzrug#RN$$$#=,"|      |    0x08048491      e87effffff     call sym.shift|      |       ; JMP XREF from 0x08048488 (sym.test)|      `--> 0x08048496      c9             leave\           0x08048497      c3             ret

可以看到该函数接受2个参数, 值得一提的是根据(x86)cdecl调用约定, 函数参数通过栈传递,

并且顺序为从右到左. 可以看到test函数中调用了shift函数, 接受1个字符串参数,
估计是解密字符串相关的函数, 先看看它:

[0x08048498]> pdf @ sym.shift/ (fcn) sym.shift 90|   sym.shift (int arg_8h);|           ; var int local_7ch @ ebp-0x7c|           ; var int local_78h @ ebp-0x78|           ; arg int arg_8h @ ebp+0x8|           ; var int local_4h @ esp+0x4|              ; CALL XREF from 0x08048491 (sym.test)|              ; CALL XREF from 0x08048483 (sym.test)|           0x08048414      55             push ebp|           0x08048415      89e5           mov ebp, esp|           0x08048417      81ec98000000   sub esp, 0x98|           0x0804841d      c74584000000.  mov dword [local_7ch], 0|              ; JMP XREF from 0x0804844e (sym.shift)|       .-> 0x08048424      8b4508         mov eax, dword [arg_8h]     ; [0x8:4]=-1 ; 8|       :   0x08048427      890424         mov dword [esp], eax|       :   0x0804842a      e811ffffff     call sym.imp.strlen         ; size_t strlen(const char *s)|       :   0x0804842f      394584         cmp dword [local_7ch], eax  ; [0x13:4]=-1 ; 19|      ,==< 0x08048432      731c           jae 0x8048450|      |:   0x08048434      8d4588         lea eax, [local_78h]|      |:   0x08048437      89c2           mov edx, eax|      |:   0x08048439      035584         add edx, dword [local_7ch]|      |:   0x0804843c      8b4584         mov eax, dword [local_7ch]|      |:   0x0804843f      034508         add eax, dword [arg_8h]|      |:   0x08048442      0fb600         movzx eax, byte [eax]|      |:   0x08048445      2c03           sub al, 3|      |:   0x08048447      8802           mov byte [edx], al|      |:   0x08048449      8d4584         lea eax, [local_7ch]|      |:   0x0804844c      ff00           inc dword [eax]|      |`=< 0x0804844e      ebd4           jmp 0x8048424|      `--> 0x08048450      8d4588         lea eax, [local_78h]|           0x08048453      034584         add eax, dword [local_7ch]|           0x08048456      c60000         mov byte [eax], 0|           0x08048459      8d4588         lea eax, [local_78h]|           0x0804845c      89442404       mov dword [local_4h], eax|           0x08048460      c70424e88504.  mov dword [esp], 0x80485e8  ; [0x80485e8:4]=0xa7325|           0x08048467      e8e4feffff     call sym.imp.printf         ; int printf(const char *format)|           0x0804846c      c9             leave\           0x0804846d      c3             ret

可以看到shift的作用是接受一个字符串->处理->printf, 其实我们可以不用分析shift函数的逻辑,

因为开启gdb一调就知道在test函数中哪个分支是"Password OK"了, 甚至都不用调试, 因为一共就2个分支,
非此即彼, 但秉承着知易行难的原则, 还是分析了一遍, shift函数伪代码如下:

void shift(char *src) {    int i;    char dst[N];    for (i = 0; i < strlen(src); i++) {        dst[i] = src[i] - 3;    }    dst[i] = 0;    printf("%s", dst);}

写个python脚本验证下之前rabin2发现.rodata段的两个字符串解密:

# /usr/bin/env python2# shift.pydef shift(src):    dst = []    for i in src:        dst.append(chr(ord(i)-3))    print(''.join(dst))shift('Lqydolg#Sdvvzrug$')shift('Sdvvzrug#RN$$$#=,')

运行:

$ python shift.pyInvalid Password!Password OK!!! :)

OK, 现在回到test函数, 这个函数比较简单, 接受2个参数, 如果第二个参数等于第一个参数,

则进入我们想要的分支.
再回到main函数, scanf接受一个整数input, 然后进行数学运算, 如下(重命名了一些变量名称):

0x080484df      c745f85a0000.  mov dword [a], 0x5a         ; 'Z' ; 900x080484e6      c745f4ec0100.  mov dword [b], 0x1ec        ; 4920x080484ed      8b55f4         mov edx, dword [b]0x080484f0      8d45f8         lea eax, [a]0x080484f3      0110           add dword [eax], edx0x080484f5      8b45f8         mov eax, dword [a]0x080484f8      0faf45f8       imul eax, dword [a]0x080484fc      8945f4         mov dword [b], eax0x080484ff      8b45f4         mov eax, dword [b]0x08048502      89442404       mov dword [esp + 4], eax0x08048506      8b45fc         mov eax, dword [input]0x08048509      890424         mov dword [esp], eax0x0804850c      e85dffffff     call sym.test

转化为人类语言就是:

int a = 0x5a, b = 0x1ec;a = a + b;b = a * a;test(input, b)

好吧, 结果还是要用输入和(0x5a*0x1ec)^2=338724比较, 若相等则通过, 验证下:

$ ./crackme0x03 IOLI Crackme Level 0x03Password: 338724Password OK!!! :)

密码和上一题一样, 囧~

crackme0x04

老样子, 直接跳转到main函数然后查看汇编:

[0x08048509]> pdf @ main...0x08048528      c704245e8604.  mov dword [esp], str.IOLI_Crackme_Level_0x04 ; [0x804865e:4]=0x494c4f49 ; "IOLI Crackme Level 0x04\n"0x0804852f      e860feffff     call sym.imp.printf         ; int printf(const char *format)0x08048534      c70424778604.  mov dword [esp], str.Password: ; [0x8048677:4]=0x73736150 ; "Password: "0x0804853b      e854feffff     call sym.imp.printf         ; int printf(const char *format)0x08048540      8d4588         lea eax, [local_78h]0x08048543      89442404       mov dword [local_4h], eax0x08048547      c70424828604.  mov dword [esp], 0x8048682  ; [0x8048682:4]=0x73250x0804854e      e821feffff     call sym.imp.scanf          ; int scanf(const char *format)0x08048553      8d4588         lea eax, [local_78h]0x08048556      890424         mov dword [esp], eax0x08048559      e826ffffff     call sym.check...[0x08048509]> ps @ 0x8048682%s

这回main函数挺简单, 主要是scanf输入一个字符串, 然后调用check函数, 汇编如下:

[0x080484fb]> pdf @ sym.check/ (fcn) sym.check 133|   sym.check (char *input);|           ; var int local_dh @ ebp-0xd|           ; var int local_ch @ ebp-0xc|           ; var int local_8h @ ebp-0x8|           ; var int local_4h @ ebp-0x4|           ; arg char * input @ ebp+0x8|              ; CALL XREF from 0x08048559 (sym.main)|           0x08048484      55             push ebp|           0x08048485      89e5           mov ebp, esp|           0x08048487      83ec28         sub esp, 0x28               ; '('|           0x0804848a      c745f8000000.  mov dword [local_8h], 0|           0x08048491      c745f4000000.  mov dword [local_ch], 0|              ; JMP XREF from 0x080484f9 (sym.check)|       .-> 0x08048498      8b4508         mov eax, dword [input]      ; [0x8:4]=-1 ; 8|       :   0x0804849b      890424         mov dword [esp], eax|       :   0x0804849e      e8e1feffff     call sym.imp.strlen         ; size_t strlen(const char *s)|       :   0x080484a3      3945f4         cmp dword [local_ch], eax   ; [0x13:4]=-1 ; 19|      ,==< 0x080484a6      7353           jae 0x80484fb|      |:   0x080484a8      8b45f4         mov eax, dword [local_ch]|      |:   0x080484ab      034508         add eax, dword [input]|      |:   0x080484ae      0fb600         movzx eax, byte [eax]|      |:   0x080484b1      8845f3         mov byte [local_dh], al|      |:   0x080484b4      8d45fc         lea eax, [local_4h]|      |:   0x080484b7      89442408       mov dword [esp + 8], eax|      |:   0x080484bb      c74424043886.  mov dword [esp + 4], 0x8048638 ; [0x8048638:4]=0x50006425|      |:   0x080484c3      8d45f3         lea eax, [local_dh]|      |:   0x080484c6      890424         mov dword [esp], eax|      |:   0x080484c9      e8d6feffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)|      |:   0x080484ce      8b55fc         mov edx, dword [local_4h]|      |:   0x080484d1      8d45f8         lea eax, [local_8h]|      |:   0x080484d4      0110           add dword [eax], edx|      |:   0x080484d6      837df80f       cmp dword [local_8h], 0xf   ; [0xf:4]=-1 ; 15|     ,===< 0x080484da      7518           jne 0x80484f4|     ||:   0x080484dc      c704243b8604.  mov dword [esp], str.Password_OK ; [0x804863b:4]=0x73736150 ; "Password OK!\n"|     ||:   0x080484e3      e8acfeffff     call sym.imp.printf         ; int printf(const char *format)|     ||:   0x080484e8      c70424000000.  mov dword [esp], 0|     ||:   0x080484ef      e8c0feffff     call sym.imp.exit           ; void exit(int status)|     ||:      ; JMP XREF from 0x080484da (sym.check)|     `---> 0x080484f4      8d45f4         lea eax, [local_ch]|      |:   0x080484f7      ff00           inc dword [eax]|      |`=< 0x080484f9      eb9d           jmp 0x8048498|      |       ; JMP XREF from 0x080484a6 (sym.check)|      `--> 0x080484fb      c70424498604.  mov dword [esp], str.Password_Incorrect ; [0x8048649:4]=0x73736150 ; "Password Incorrect!\n"|           0x08048502      e88dfeffff     call sym.imp.printf         ; int printf(const char *format)|           0x08048507      c9             leave\           0x08048508      c3             ret

这个函数比之前的复杂一点, 所以我们用视图模式先有个大局观:

[0x08048484]> VV @ sym.check[0x08048484]> VV @ sym.check (nodes 6 edges 6 zoom 100%) BB-SUMM mouse:canvas-y mov-speed:5                                                        .--------------------.                                                                     |  0x8048484 ;[ga]   |                                                                     `--------------------'                                                                         |                                                                                       .--'                                                .--------------------------------------.                                                   |                                      |                                                   |                                      |                                                   |                                  .---------------------------.                           |                                  |  0x8048498 ;[gd]          |                           |                                  | 0x0804849e sym.imp.strlen |                           |                                  `---------------------------'                           |                                          | |                                             |                                          | '---------.                                   |                        .-----------------'           |                                   |                        |                             |                                   |                        |                             |                                   |                .---------------------------.   .-----------------------------------.     |                |  0x80484a8 ;[gg]          |   | [0x80484fb] ;[gc]                 |     |                | 0x080484c9 sym.imp.sscanf |   | 0x080484fb str.Password_Incorrect |     |                `---------------------------'   | 0x08048502 sym.imp.printf         |     |                        | |                     `-----------------------------------'     |                        | |                                                               |                        | '-------------.                                                 |        .---------------'               |                                                 |        |                               |                                                 |        |                               |                                                 |.----------------------------.    .--------------------.                                  ||  0x80484dc ;[gj]           |    |  0x80484f4 ;[gf]   |                                  || 0x080484dc str.Password_OK |    `--------------------'                                  || 0x080484e3 sym.imp.printf  |        |                                                   || 0x080484ef sym.imp.exit    |        |                                                   |`----------------------------'        |                                                   |                                      |                                                   `--------------------------------------'

radare2在视图模式下可以通过p/P切换视图, 通过O切换asm的类型.

直接按?键可以查看快捷键的帮助.

让我们F5一下, 噢忘了没有F5, 那就人肉反编译一下, check函数有4个本地变量,

但还不知道他们的作用, 有一个参数我已经改成了char *input, 先来个伪代码:

local_8h = 0, local_ch = 0;BEGIN:if (local_ch >= strlen(input)) {    printf("Password Incorrect!\n");    return;}eax = input + local_ch;eax = (int)*eax;(char*)&local_dh[0] = eax;sscanf(local_dh, "%d", local_4h);local_8h = local_4h + local_8h;if (local_8h != 0xf) {    local_ch ++;    goto BEGIN;}printf("Password OK!\n");return;

这里要注意mov byte [local_dh], al的意思是把eax中的最低字节移动到local_dh

的第一字节. 也就是说, check对输入的字符串的每个字节都进行sscanf扫描, 如果是个整数
就累加local_8h里, 只要其等于0xf(=15), 则通过, 所以密码可以有多个, 最简单就是15个1:

$ ./crackme0x04 IOLI Crackme Level 0x04Password: 111111111111111Password OK!

只要满足条件都可以, 比如最短的9+6=15:

$ ./crackme0x04 IOLI Crackme Level 0x04Password: 96Password OK!

crackme0x05

这题和0x04一样, 都是用户输入一个字符串, 然后调用check, 但是check函数有所不同:

[0x080484c8]> VV @ sym.check (nodes 7 edges 8 zoom 100%) BB-SUMM mouse:canvas-y mov-speed:5                                        .--------------------.                                                                                   |  0x80484c8 ;[ga]   |                                                                                   `--------------------'                                                                                       |                                                                                                     .--'                                                                              .----------------------.                                                                                 |                      |                                                                                 |                      |                                                                                 |                  .---------------------------.                                                         |                  |  0x80484dc ;[gd]          |                                                         |                  | 0x080484e2 sym.imp.strlen |                                                         |                  `---------------------------'                                                         |                          | |                                                                           |                          | '---------.                                                                 |        .-----------------'           |                                                                 |        |                             |                                                                 |        |                             |                                                                 |.---------------------------.   .-----------------------------------.                                   ||  0x80484ec ;[gg]          |   | [0x8048532] ;[gc]                 |                                   || 0x0804850d sym.imp.sscanf |   | 0x08048532 str.Password_Incorrect |                                   |`---------------------------'   | 0x08048539 sym.imp.printf         |                                   |        | |                     `-----------------------------------'                                   |        | |                                                                                             |        | '---------------------------------------------------------.                                   |        '-.                                                         |                                   |          |                                                         |                                   |          |                                                         |                                   |  .-----------------------.                                         |                                   |  |  0x8048520 ;[gi]      |                                         |                                   |  | 0x08048526 sym.parell |                                         |                                   |  `-----------------------'                                         |                                   |      |                                                             |                                   |      '---------------------------.                                 |                                   |                                  | .-------------------------------'                                   |                                  | |                                                                   |                                  | | |                              .--------------------.                                                    |                              |  0x804852b ;[gf]   |                                                    |                              `--------------------'                                                    |                                  |                                                                     `----------------------------------'

我们待会再来看它, check函数里还调用了parell函数, 其流程图如下:

[0x08048484]> VV @ sym.parell (nodes 3 edges 2 zoom 100%) BB-NORM mouse:canvas-y mov-speed:5                                .---------------------------------------------.                                                             | [0x8048484] ;[gc]                           |                                                             | (fcn) sym.parell 68                         |                                                             |   sym.parell (int arg_8h);                  |                | ; var int local_4h @ ebp-0x4                |                                                             | ; arg int arg_8h @ ebp+0x8                  |                                                             |    ; CALL XREF from 0x08048526 (sym.check)  |                                                             | push ebp                                    |                                                             | mov ebp, esp                                |                                                             | sub esp, 0x18                               |                                                             | lea eax, [local_4h]                         |                                                             | mov dword [esp + 8], eax                    |                                                             | mov dword [esp + 4], 0x8048668              |                                                             | mov eax, dword [arg_8h]                     |                                                             | mov dword [esp], eax                        |                                                             | call sym.imp.sscanf;[ga]                    |                                                             | mov eax, dword [local_4h]                   |                                                             | and eax, 1                                  |                                                             | test eax, eax                               |                                                             | jne 0x80484c6;[gb]                          |                                                             `---------------------------------------------'                                                                     | |                                                                                                         | '-------------------------.                                                                 .-------------'                           |                                                                 |                                         |                                                         .--------------------------------------.    .--------------------.                                          |  0x80484ae ;[gf]                     |    |  0x80484c6 ;[gb]   |                                          |   ; [0x804866b:4]=0x73736150         |    | leave              |                                          |   ; "Password OK!\n"                 |    | ret                |                                          | mov dword [esp], str.Password_OK     |    `--------------------'                                          | call sym.imp.printf;[gd]             |                                                                    | mov dword [esp], 0                   |                                                                    | call sym.imp.exit;[ge]               |                                                                    `--------------------------------------'

其接受一个参数, 并且经过一顿操作后选择静默返回或者进入正确分支并退出程序.

试着写下伪代码:

void parrel(arg) {    int local_4h;    sscanf(arg, "%d", &local_4h);    local_4h &= 1; // 除了最后一位全部清0    if (local_4h != 0) {        return;    }    printf("Password_OK\n");    exit(0);}

可以猜测arg应该是char *类型, 该函数意思是将输入转化为整数, 如果结果的最低有效位为1则通过.

现在可以回到check函数了. 该函数有4个本地变量, 姑且先将其命名为a,b,c,d:

/ (fcn) sym.check 120|   sym.check (int input);|           ; var int a @ ebp-0xd|           ; var int b @ ebp-0xc|           ; var int c @ ebp-0x8|           ; var int d @ ebp-0x4|           ; arg int input @ ebp+0x8|              ; CALL XREF from 0x08048590 (sym.main)|           0x080484c8      55             push ebp|           0x080484c9      89e5           mov ebp, esp|           0x080484cb      83ec28         sub esp, 0x28               ; '('|           0x080484ce      c745f8000000.  mov dword [c], 0|           0x080484d5      c745f4000000.  mov dword [b], 0|              ; JMP XREF from 0x08048530 (sym.check)|       .-> 0x080484dc      8b4508         mov eax, dword [input]      ; [0x8:4]=-1 ; 8|       :   0x080484df      890424         mov dword [esp], eax|       :   0x080484e2      e89dfeffff     call sym.imp.strlen         ; size_t strlen(const char *s)|       :   0x080484e7      3945f4         cmp dword [b], eax          ; [0x13:4]=-1 ; 19|      ,==< 0x080484ea      7346           jae 0x8048532|      |:   0x080484ec      8b45f4         mov eax, dword [b]|      |:   0x080484ef      034508         add eax, dword [input]|      |:   0x080484f2      0fb600         movzx eax, byte [eax]|      |:   0x080484f5      8845f3         mov byte [a], al|      |:   0x080484f8      8d45fc         lea eax, [d]|      |:   0x080484fb      89442408       mov dword [esp + 8], eax|      |:   0x080484ff      c74424046886.  mov dword [esp + 4], 0x8048668 ; [0x8048668:4]=0x50006425|      |:   0x08048507      8d45f3         lea eax, [a]|      |:   0x0804850a      890424         mov dword [esp], eax|      |:   0x0804850d      e892feffff     call sym.imp.sscanf         ; int sscanf(const char *s, const char *format,   ...)|      |:   0x08048512      8b55fc         mov edx, dword [d]|      |:   0x08048515      8d45f8         lea eax, [c]|      |:   0x08048518      0110           add dword [eax], edx|      |:   0x0804851a      837df810       cmp dword [c], 0x10         ; [0x10:4]=-1 ; 16|     ,===< 0x0804851e      750b           jne 0x804852b|     ||:   0x08048520      8b4508         mov eax, dword [input]      ; [0x8:4]=-1 ; 8|     ||:   0x08048523      890424         mov dword [esp], eax|     ||:   0x08048526      e859ffffff     call sym.parell|     `---> 0x0804852b      8d45f4         lea eax, [b]|      |:   0x0804852e      ff00           inc dword [eax]|      |`=< 0x08048530      ebaa           jmp 0x80484dc|      `--> 0x08048532      c70424798604.  mov dword [esp], str.Password_Incorrect ; [0x8048679:4]=0x73736150 ; "Password Incorrect!\n"|           0x08048539      e856feffff     call sym.imp.printf         ; int printf(const char *format)|           0x0804853e      c9             leave\           0x0804853f      c3             ret

看到有个反向的跳转, 所以b应该是个循环变量, 重命名为i, 写下伪代码:

int c = 0;int i = 0;int d;char a[2];while(1) {    if (i >= strlen(input)) {        printf("Password Incorrect!\n");        return;    }    (char*)a[0] = input[i];    (char*)a[1] = 0;    sscanf(a,"%d",&d);    c += d;    if (c==0x10) {        parell(input)    }    i++;    continue;}

呃...写得有点渣, 不过能看明白逻辑就行了, 意思就是将输入的每个字符转为整数并累加,

如果累加的结果等于16(0x10)则调用parell函数, 前面分析了parrel的作用是将整个字符串
转换为整数, 并判断其最低有效位是否是0(即该数字是否为偶数), 是偶数则通过.
所以我们要输入的密码应该是个偶数, 而且前X位加起来是16就可以了:

$ ./crackme0x05IOLI Crackme Level 0x05Password: 88Password OK!$ ./crackme0x05IOLI Crackme Level 0x05Password: 88666Password OK!

完美解决!

后记

说实话我一开始对汇编还不是很熟悉, 但动手写了几个writeup之后也逐渐有了点感觉.

对于不熟悉的指令, 比如movzx等可以查看X86的手册, 比如这里:,
而对于不熟悉的语法, 比如Size Directives或者Calling Conventions, 可以参考
以及维基百科. 总之, 熟能生巧, 汇编也不是那么可怕嘛!

欢迎交流分享, 转载请注明出处

转载于:https://www.cnblogs.com/pannengzhi/p/2018-02-11-ioli-crackme1-5.html

你可能感兴趣的文章
Elastic Search 新手入门笔记(一)
查看>>
把 Excel 透视表搬到 WEB 上
查看>>
Jenkins配合github实现前端项目自动化构建部署
查看>>
荐书:「时差党」——出国留学不是一件容易的事
查看>>
用户自定义结构数据与VARIANT转换
查看>>
java8-lambda
查看>>
Windows平台JxCore打包
查看>>
[译]ASP.NET Core依赖注入深入讨论
查看>>
设计模式(九)_代理模式
查看>>
02 回归算法 - 线性回归求解 θ(最大似然估计求解)
查看>>
【算法学习】算法的复杂度
查看>>
14、python常用模块
查看>>
swift4.0 CAKeyframeAnimation,抖动动画
查看>>
比特币解锁脚本中的ScriptSignature都包含了什么东西
查看>>
从零开始学java(猜数字游戏)
查看>>
xcode10 编译报错Multiple commands produce
查看>>
使用ab进行页面的压力测试
查看>>
开源大数据周刊-2018年08月03日 第95期
查看>>
React, a gentle introduction.
查看>>
值得一试的Android开发工具
查看>>