• IOLI-crackme0x01-0x05 writeup


    Radare2
    上一篇开了个头, 使用Radare2并用3中方法来解决crackme0x00, 由于是第一篇,
    所以解释得事无巨细, 今天就稍微加快点步伐, 分析一下另外几个crackme.

    如果你忘记了crackme的来源, 那就再告诉你一遍, 它们都是来自IOLI-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
    "
    |           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 0x149a
    5274
    

    所以:

    $ ./crackme0x01 
    IOLI Crackme Level 0x01
    Password: 5274
    Password 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
    "
    |           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 @ 0x0804842b
    0x0804842b      c745f85a0000.  mov dword [a], 0x5a         ; 'Z' ; 90
    0x08048432      c745f4ec0100.  mov dword [b], 0x1ec        ; 492
    0x08048439      8b55f4         mov edx, dword [b]
    0x0804843c      8d45f8         lea eax, [a]
    0x0804843f      0110           add dword [eax], edx
    0x08048441      8b45f8         mov eax, dword [a]
    0x08048444      0faf45f8       imul eax, dword [a]
    0x08048448      8945f4         mov dword [b], eax
    0x0804844b      8b45fc         mov eax, dword [input]
    0x0804844e      3b45f4         cmp eax, dword [b]
    

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

    int a = 0x5a
    int b = 0x1ec
    a = b + a
    a = a * a
    b = a
    if input == b
    

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

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

    抬走, 下一个.

    crackme0x03

    流程和之前一样:

    $ ./crackme0x03 
    IOLI Crackme Level 0x03
    Password: 12345
    Invalid 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
    
    003 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.py
    def shift(src):
        dst = []
        for i in src:
            dst.append(chr(ord(i)-3))
        print(''.join(dst))
    shift('Lqydolg#Sdvvzrug$')
    shift('Sdvvzrug#RN$$$#=,')
    

    运行:

    $ python shift.py
    Invalid Password!
    Password OK!!! :)
    

    OK, 现在回到test函数, 这个函数比较简单, 接受2个参数, 如果第二个参数等于第一个参数,
    则进入我们想要的分支.
    再回到main函数, scanf接受一个整数input, 然后进行数学运算, 如下(重命名了一些变量名称):

    0x080484df      c745f85a0000.  mov dword [a], 0x5a         ; 'Z' ; 90
    0x080484e6      c745f4ec0100.  mov dword [b], 0x1ec        ; 492
    0x080484ed      8b55f4         mov edx, dword [b]
    0x080484f0      8d45f8         lea eax, [a]
    0x080484f3      0110           add dword [eax], edx
    0x080484f5      8b45f8         mov eax, dword [a]
    0x080484f8      0faf45f8       imul eax, dword [a]
    0x080484fc      8945f4         mov dword [b], eax
    0x080484ff      8b45f4         mov eax, dword [b]
    0x08048502      89442404       mov dword [esp + 4], eax
    0x08048506      8b45fc         mov eax, dword [input]
    0x08048509      890424         mov dword [esp], eax
    0x0804850c      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 0x03
    Password: 338724
    Password OK!!! :)
    

    密码和上一题一样, 囧~

    crackme0x04

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

    [0x08048509]> pdf @ main
    ...
    0x08048528      c704245e8604.  mov dword [esp], str.IOLI_Crackme_Level_0x04 ; [0x804865e:4]=0x494c4f49 ; "IOLI Crackme Level 0x04
    "
    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], eax
    0x08048547      c70424828604.  mov dword [esp], 0x8048682  ; [0x8048682:4]=0x7325
    0x0804854e      e821feffff     call sym.imp.scanf          ; int scanf(const char *format)
    0x08048553      8d4588         lea eax, [local_78h]
    0x08048556      890424         mov dword [esp], eax
    0x08048559      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!
    "
    |     ||:   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!
    "
    |           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!
    ");
        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!
    ");
    return;
    

    这里要注意mov byte [local_dh], al的意思是把eax中的最低字节移动到local_dh
    的第一字节. 也就是说, check对输入的字符串的每个字节都进行sscanf扫描, 如果是个整数
    就累加local_8h里, 只要其等于0xf(=15), 则通过, 所以密码可以有多个, 最简单就是15个1:

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

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

    $ ./crackme0x04 
    IOLI Crackme Level 0x04
    Password: 96
    Password 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!
    "                 |    | 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
    ");
        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!
    "
    |           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!
    ");
            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就可以了:

    $ ./crackme0x05
    IOLI Crackme Level 0x05
    Password: 88
    Password OK!
    $ ./crackme0x05
    IOLI Crackme Level 0x05
    Password: 88666
    Password OK!
    

    完美解决!

    后记

    说实话我一开始对汇编还不是很熟悉, 但动手写了几个writeup之后也逐渐有了点感觉.
    对于不熟悉的指令, 比如movzx等可以查看X86的手册, 比如这里:Opcode of programming language,
    而对于不熟悉的语法, 比如Size Directives或者Calling Conventions, 可以参考x86 Assembly Guide
    以及维基百科. 总之, 熟能生巧, 汇编也不是那么可怕嘛!

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

  • 相关阅读:
    Linux systemtap定位系统IO资源使用情况(ok)
    DISK 100% BUSY,谁造成的?(ok)
    容易被误读的IOSTAT
    利用BLKTRACE分析IO性能
    iowait 过高问题的查找及解决linux
    top后台执行显示:top: failed tty get 错误
    Nginx解读内置非默认模块 ngx_http_stub_status_module
    Nginx健康检查模块
    平滑升级你的Nginx
    Practice telephone techniques
  • 原文地址:https://www.cnblogs.com/pannengzhi/p/2018-02-11-ioli-crackme1-5.html
Copyright © 2020-2023  润新知