<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/binary/15660

            0x00 前言


            三個白帽在線挑戰平臺,集WEB和二進制挑戰于一身,以在線挑戰的形式把網絡安全相關領域白帽子的技術和智慧淋漓盡致地展現出來,為白帽子們搭建了一個非常完美、和諧的在線交流平臺,使得白帽子之間可以融洽地施展絕技、交流創意和想法。一次一次的挑戰讓白帽子們大飽眼福、樂在其中,讓大家腦洞大開,并且收獲滿滿,同時回味無窮!

            由于對windows平臺下二進制有過研究,本著和大家交流學習的目的,在和三個白帽平臺管理溝通后,為《條條大路通羅馬系列》設計了一個二進制算法題目。本文將首先從破解者角度對這個二進制題進行分析,單純以破解者身份來逆向分析算法,解密數據,從而最終獲得flag。另外將從該題設計者角度進行分析,一步步描述每步算法相對應的數據如何加密、解密的,試圖達到什么樣的效果,試圖在哪里困住破解者。

            0x01 正向破解加密算法


            首先我們按照程序的流程,一步步分析算法、破解算法。

            1、和第一次二進制題一樣,首先通過字符串查找,找到字符串where is flag?flag is in the beautifull spring!

            2、雙擊字符串來到算法關鍵位置,相關匯編代碼分析如下:

            004026E0  |.  57            push    edi                            ;  這里開始了一個非常初級的數獨游戲
            004026E1  |.  8D4C24 10     lea     ecx, dword ptr [esp+10]
            004026E5  |.  897424 18     mov     dword ptr [esp+18], esi
            004026E9  |.  E8 68080000   call    <jmp.&MFC42.#CString::CString_>
            004026EE  |.  33DB          xor     ebx, ebx
            004026F0  |.  68 60564000   push    00405660                       ;  where is flag?flag is in the beautifull spring!
            004026F5  |.  8D4C24 14     lea     ecx, dword ptr [esp+14]
            004026F9  |.  C78424 D00000>mov     dword ptr [esp+D0], 1
            00402704  |.  895C24 18     mov     dword ptr [esp+18], ebx
            00402708  |.  E8 43080000   call    <jmp.&MFC42.#CString::operator>
            0040270D  |.  8BAC24 D80000>mov     ebp, dword ptr [esp+D8]
            00402714  |.  83C9 FF       or      ecx, FFFFFFFF
            00402717  |.  8BFD          mov     edi, ebp
            00402719  |.  33C0          xor     eax, eax
            0040271B  |.  F2:AE         repne   scas byte ptr es:[edi]
            0040271D  |.  F7D1          not     ecx
            0040271F  |.  49            dec     ecx
            00402720  |.  83E9 0C       sub     ecx, 0C
            00402723  |.  74 5A         je      short 0040277F
            00402725  |>  BF 54564000   /mov     edi, 00405654                 ;  adf438ghi
            0040272A  |.  83C9 FF       |or      ecx, FFFFFFFF
            0040272D  |.  33C0          |xor     eax, eax
            0040272F  |.  33D2          |xor     edx, edx
            00402731  |.  F2:AE         |repne   scas byte ptr es:[edi]
            00402733  |.  F7D1          |not     ecx
            00402735  |.  49            |dec     ecx
            00402736  |.  74 23         |je      short 0040275B
            00402738  |.  8A1C2E        |mov     bl, byte ptr [esi+ebp]        ;  單個取輸入的字符
            0040273B  |>  3A9A 54564000 |/cmp     bl, byte ptr [edx+405654]    ;  判斷取出來的字符是否在adf438ghi中
            00402741  |.  74 14         ||je      short 00402757               ;  如果是就跳出循環
            00402743  |.  BF 54564000   ||mov     edi, 00405654                ;  adf438ghi
            00402748  |.  83C9 FF       ||or      ecx, FFFFFFFF
            0040274B  |.  33C0          ||xor     eax, eax
            0040274D  |.  42            ||inc     edx
            0040274E  |.  F2:AE         ||repne   scas byte ptr es:[edi]
            00402750  |.  F7D1          ||not     ecx                          ;  取字符串adf438ghi的長度
            00402752  |.  49            ||dec     ecx
            00402753  |.  3BD1          ||cmp     edx, ecx
            00402755  |.^ 72 E4         |\jb      short 0040273B
            00402757  |>  8B5C24 14     |mov     ebx, dword ptr [esp+14]
            0040275B  |>  BF 54564000   |mov     edi, 00405654                 ;  adf438ghi
            00402760  |.  83C9 FF       |or      ecx, FFFFFFFF
            00402763  |.  33C0          |xor     eax, eax
            00402765  |.  F2:AE         |repne   scas byte ptr es:[edi]
            00402767  |.  F7D1          |not     ecx
            00402769  |.  49            |dec     ecx
            0040276A  |.  3BD1          |cmp     edx, ecx
            0040276C  |.  74 3F         |je      short 004027AD                ;  如果上面循環次數等于adf438ghi的長度說明判斷失敗,跳到結束
            0040276E  |.  8BFD          |mov     edi, ebp
            00402770  |.  83C9 FF       |or      ecx, FFFFFFFF
            00402773  |.  46            |inc     esi
            00402774  |.  F2:AE         |repne   scas byte ptr es:[edi]
            00402776  |.  F7D1          |not     ecx
            00402778  |.  83C1 F3       |add     ecx, -0D                      ;  這個是循環次數 取輸入的字符串的長度減去13
            0040277B  |.  3BF1          |cmp     esi, ecx
            0040277D  |.^ 72 A6         \jb      short 00402725                ;  跳回去,循環判斷下一個輸入的字符
            0040277F  |>  B9 14000000   mov     ecx, 14
            00402784  |.  BE 00564000   mov     esi, 00405600                  ;  g8azfzzd3f3zzzhazgzdzzzgzf4zzizz8fghzzfdgi3zz3gzfzzizdai3g8zzzzhfd4zzg8z84gzazd3z
            00402789  |.  8D7C24 70     lea     edi, dword ptr [esp+70]
            0040278D  |.  33D2          xor     edx, edx
            0040278F  |.  F3:A5         rep     movs dword ptr es:[edi], dword>
            00402791  |.  A4            movs    byte ptr es:[edi], byte ptr [e>
            00402792  |>  33C0          xor     eax, eax
            00402794  |.  8D7414 70     lea     esi, dword ptr [esp+edx+70]
            00402798  |>  8A0C06        mov     cl, byte ptr [esi+eax]
            0040279B  |.  8D3C02        lea     edi, dword ptr [edx+eax]
            0040279E  |.  80F9 7A       cmp     cl, 7A                         ;  循環判斷405600地址的字符串的字符是否為z
            004027A1  |.  75 2A         jnz     short 004027CD
            004027A3  |.  8A0C2B        mov     cl, byte ptr [ebx+ebp]
            004027A6  |.  43            inc     ebx
            004027A7  |.  884C3C 1C     mov     byte ptr [esp+edi+1C], cl      ;  如果是z就把輸入的字符依次替換掉z
            004027AB  |.  EB 24         jmp     short 004027D1
            004027AD  |>  8BB424 D40000>mov     esi, dword ptr [esp+D4]
            004027B4  |.  8D4424 10     lea     eax, dword ptr [esp+10]
            004027B8  |.  50            push    eax
            004027B9  |.  8BCE          mov     ecx, esi
            004027BB  |.  E8 EA070000   call    <jmp.&MFC42.#CString::CString_>
            004027C0  |.  C74424 18 010>mov     dword ptr [esp+18], 1
            004027C8  |.  E9 A4000000   jmp     00402871
            004027CD  |>  884C3C 1C     mov     byte ptr [esp+edi+1C], cl
            004027D1  |>  40            inc     eax
            004027D2  |.  83F8 09       cmp     eax, 9
            004027D5  |.^ 7C C1         jl      short 00402798                 ;  跳回去判斷下一個字符
            004027D7  |.  83C2 09       add     edx, 9
            004027DA  |.  83FA 51       cmp     edx, 51                        ;  要分9組每組9次循環81次
            004027DD  |.^ 7C B3         jl      short 00402792                 ;  跳回去繼續判斷下一組
            004027DF  |.  C74424 14 000>mov     dword ptr [esp+14], 0
            004027E7  |.  8D5C24 1C     lea     ebx, dword ptr [esp+1C]
            004027EB  |.  8D4C24 1C     lea     ecx, dword ptr [esp+1C]
            004027EF  |>  33C0          /xor     eax, eax                      ;  這里有3層循環是用來檢查數獨結果是否正確
            004027F1  |.  8BEB          |mov     ebp, ebx
            004027F3  |>  8A1401        |/mov     dl, byte ptr [ecx+eax]       ;  最里面一層的第一個循環以行為單位進行檢查
            004027F6  |.  C60401 77     ||mov     byte ptr [ecx+eax], 77       ;  具體方法就是取出當前要檢查的字符,替換為0x77即w
            004027FA  |.  33F6          ||xor     esi, esi
            004027FC  |>  3A1431        ||/cmp     dl, byte ptr [ecx+esi]      ;  然后對該行所有的字符進行判斷是否和取出的那個字符重復
            004027FF  |.  0F84 9A000000 |||je      0040289F                    ;  如果存在重復就跳沒了
            00402805  |.  46            |||inc     esi
            00402806  |.  83FE 09       |||cmp     esi, 9
            00402809  |.^ 7C F1         ||\jl      short 004027FC
            0040280B  |.  881401        ||mov     byte ptr [ecx+eax], dl
            0040280E  |.  8A55 00       ||mov     dl, byte ptr [ebp]
            00402811  |.  C645 00 79    ||mov     byte ptr [ebp], 79           ;  這里以列為單位 取出當前字符替換為字符y,然后判斷該列所有字符是否與取出的字符有重復
            00402815  |.  33F6          ||xor     esi, esi
            00402817  |.  8BFB          ||mov     edi, ebx
            00402819  |>  3A17          ||/cmp     dl, byte ptr [edi]
            0040281B  |.  0F84 85000000 |||je      004028A6                    ;  如果存在 就跳沒了
            00402821  |.  46            |||inc     esi
            00402822  |.  83C7 09       |||add     edi, 9
            00402825  |.  83FE 09       |||cmp     esi, 9
            00402828  |.^ 7C EF         ||\jl      short 00402819
            0040282A  |.  8855 00       ||mov     byte ptr [ebp], dl
            0040282D  |.  40            ||inc     eax
            0040282E  |.  83C5 09       ||add     ebp, 9
            00402831  |.  83F8 09       ||cmp     eax, 9
            00402834  |.^ 7C BD         |\jl      short 004027F3               ;  外邊兩層循環,行判斷時分別控制行標和列標;列判斷時分別控制列標和行標
            00402836  |.  8B4424 14     |mov     eax, dword ptr [esp+14]
            0040283A  |.  83C1 09       |add     ecx, 9
            0040283D  |.  40            |inc     eax
            0040283E  |.  43            |inc     ebx
            0040283F  |.  83F8 09       |cmp     eax, 9
            00402842  |.  894424 14     |mov     dword ptr [esp+14], eax
            00402846  |.^ 7C A7         \jl      short 004027EF
            00402848  |.  68 E0554000   push    004055E0                       ;  good !flag is waving to you!
            

            3、根據上面分析的結果,整理如下:

            首先撇開后12個字符不管,判斷前面的字符串(以s命名)中的字符是否都是字符串adf438ghi中的字符,內存地址405600處字符串(以f命名)為g8azfzzd3f3zzzhazgzdzzzgzf4zzizz8fghzzfdgi3zz3gzfzzizdai3g8zzzzhfd4zzg8z84gzazd3z,把s依次替換f中的z字符,替換后依次9個字符為一行檢查是否有重復字符,同時9個字符為一列檢查是否有重復字符。把f轉為列表形式如下:

            g8azfzzd3
            f3zzzhazg
            zdzzzgzf4
            zzizz8fgh
            zzfdgi3zz
            3gzfzzizd
            ai3g8zzzz
            hfd4zzg8z
            84gzazd3z
            

            那么很明顯這是一個9X9的數獨算法,輸入的字符就是數獨算法的解。為了使數獨更加直觀,我們來用數字代替字母。把字符adf438ghi作以下對應:

            a d f 4 3 8 g h i
            1 2 3 4 5 6 7 8 9
            

            同時根據算法z其實就是數獨算法里的空字符,這個時候列表變為:

            這是個很簡單的難度系數為1的數獨游戲,大家有興趣可以手動完成這個數獨游戲,數獨游戲結果如下:

            761934825
            354628197
            928157634
            219546378
            483279516
            576381942
            195762483
            832495761
            647813259
            

            抽出我們填充的數字:9484629981562154481668142483951839,根據前面設定的對應關系,其對應的字符串為:i4h48diiha38da344ha88ha4d4hfi3ahfi。這里就得到了算法的第一部分的字符串。把斷點設置在該算法函數尾部,單步走,等到該函數返回后我們就來到了下面的算法部分。

            0x02 逆向追蹤算法


            跟蹤到后面我們發現其實第二部分是對我們輸入的后12個字符進行解密運算,然后把運算結果作為key來加密第一部分得到的結果,那么我們需要根據解密流程,逆向加密KEY來獲得正確的輸入字符串。具體分析如下:

            1、相關算法匯編代碼分析如下:

            004029B6   .  8BF0          mov     esi, eax
            004029B8   .  8B06          mov     eax, dword ptr [esi]
            004029BA   .  894424 30     mov     dword ptr [esp+30], eax
            004029BE   .  8B4E 04       mov     ecx, dword ptr [esi+4]
            004029C1   .  894C24 34     mov     dword ptr [esp+34], ecx
            004029C5   .  8B56 14       mov     edx, dword ptr [esi+14]
            004029C8   .  8B0B          mov     ecx, dword ptr [ebx]
            004029CA   .  895424 24     mov     dword ptr [esp+24], edx
            004029CE   .  8B46 18       mov     eax, dword ptr [esi+18]
            004029D1   .  894424 28     mov     dword ptr [esp+28], eax
            004029D5   .  8B69 F8       mov     ebp, dword ptr [ecx-8]
            004029D8   .  83C5 DE       add     ebp, -22                          移動長度34
            004029DB   .  55            push    ebp                       ; /size
            004029DC   .  FF15 34424000 call    dword ptr [<&MSVCRT.mallo>; \malloc
            004029E2   .  8BCD          mov     ecx, ebp
            004029E4   .  8BF8          mov     edi, eax
            004029E6   .  8BD1          mov     edx, ecx
            004029E8   .  33C0          xor     eax, eax
            004029EA   .  C1E9 02       shr     ecx, 2
            004029ED   .  897C24 1C     mov     dword ptr [esp+1C], edi
            004029F1   .  F3:AB         rep     stos dword ptr es:[edi]
            004029F3   .  8BCA          mov     ecx, edx
            004029F5   .  83E1 03       and     ecx, 3
            004029F8   .  F3:AA         rep     stos byte ptr es:[edi]
            004029FA   .  8B4C24 1C     mov     ecx, dword ptr [esp+1C]
            004029FE   .  8D46 22       lea     eax, dword ptr [esi+22]
            00402A01   .  8B56 22       mov     edx, dword ptr [esi+22]
            00402A04   .  8911          mov     dword ptr [ecx], edx
            00402A06   .  8B50 04       mov     edx, dword ptr [eax+4]
            00402A09   .  8951 04       mov     dword ptr [ecx+4], edx
            00402A0C   .  8B40 08       mov     eax, dword ptr [eax+8]
            00402A0F   .  8941 08       mov     dword ptr [ecx+8], eax
            00402A12   .  8BC5          mov     eax, ebp
            00402A14   .  99            cdq
            00402A15   .  83E2 03       and     edx, 3
            00402A18   .  03C2          add     eax, edx
            00402A1A   .  C1F8 02       sar     eax, 2
            00402A1D   .  8D4440 0A     lea     eax, dword ptr [eax+eax*2>
            00402A21   .  50            push    eax                       ; /size
            00402A22   .  FF15 34424000 call    dword ptr [<&MSVCRT.mallo>; \malloc
            00402A28   .  8B4C24 20     mov     ecx, dword ptr [esp+20]
            00402A2C   .  8BF8          mov     edi, eax
            00402A2E   .  57            push    edi
            00402A2F   .  55            push    ebp
            00402A30   .  51            push    ecx
            00402A31   .  E8 CAE5FFFF   call    00401000                  ;  從輸入串第35個字符開始取剩下的字符串進行BASE64解密
            00402A36   .  C60438 00     mov     byte ptr [eax+edi], 0     ;  解密結果最后加上0結束符
            00402A3A   .  8D5424 38     lea     edx, dword ptr [esp+38]   ;  取輸入串第21個開始的8個字符
            00402A3E   .  6A 01         push    1
            00402A40   .  8D4424 48     lea     eax, dword ptr [esp+48]   ;  取輸入串前8個字符
            00402A44   .  52            push    edx
            00402A45   .  50            push    eax
            00402A46   .  57            push    edi
            00402A47   .  E8 94ECFFFF   call    004016E0                  ;  把上面取出的兩組字符作為密鑰進行3DES解密
            00402A4C   .  8A0E          mov     cl, byte ptr [esi]
            00402A4E   .  83C4 24       add     esp, 24
            00402A51   .  33C0          xor     eax, eax
            00402A53   .  84C9          test    cl, cl
            00402A55   .  74 49         je      short 00402AA0
            00402A57   >  8B0B          mov     ecx, dword ptr [ebx]
            00402A59   .  8B49 F8       mov     ecx, dword ptr [ecx-8]
            00402A5C   .  83C1 F4       add     ecx, -0C                  ;  取輸入字符串長度減去12
            00402A5F   .  3BC1          cmp     eax, ecx                  ;  大于這個長度就跳出循環
            00402A61   .  7D 3D         jge     short 00402AA0
            00402A63   .  8BD0          mov     edx, eax                  ;  下面實際上是除以8取余操作,3DES解密后數據長度為8,每8次加法后,又從第一個字節開始取加數
            00402A65   .  81E2 07000080 and     edx, 80000007
            00402A6B   .  79 05         jns     short 00402A72
            00402A6D   .  4A            dec     edx
            00402A6E   .  83CA F8       or      edx, FFFFFFF8
            00402A71   .  42            inc     edx
            00402A72   >  8A0C3A        mov     cl, byte ptr [edx+edi]    ;  依次取上面3DES解密后字節,長度超過8時 從第一個開始取
            00402A75   .  8D2C3A        lea     ebp, dword ptr [edx+edi]
            00402A78   .  8A1430        mov     dl, byte ptr [eax+esi]    ;  取輸入的字符
            00402A7B   .  02D1          add     dl, cl                    ;  兩者相加
            00402A7D   .  8ACA          mov     cl, dl
            00402A7F   .  881430        mov     byte ptr [eax+esi], dl    ;  保存相加后的結果
            00402A82   .  80F9 3E       cmp     cl, 3E                    ;  這里依次判斷相加后的結果是否是字符> ? ;中的一個
            00402A85   .  74 0A         je      short 00402A91
            00402A87   .  80F9 3F       cmp     cl, 3F
            00402A8A   .  74 05         je      short 00402A91
            00402A8C   .  80F9 3B       cmp     cl, 3B
            00402A8F   .  75 06         jnz     short 00402A97
            00402A91   >  024D 00       add     cl, byte ptr [ebp]        ;  如果是的話 就再加一次cl
            00402A94   .  880C30        mov     byte ptr [eax+esi], cl
            00402A97   >  8A4C30 01     mov     cl, byte ptr [eax+esi+1]
            00402A9B   .  40            inc     eax
            00402A9C   .  84C9          test    cl, cl
            00402A9E   .^ 75 B7         jnz     short 00402A57            ;  跳回去依次對每個字符進行加法
            00402AA0   >  68 A0554000   push    004055A0                  ;  \n\n
            00402AA5   .  8D4C24 14     lea     ecx, dword ptr [esp+14]
            00402AA9   .  C60430 00     mov     byte ptr [eax+esi], 0
            00402AAD   .  E8 E6040000   call    <jmp.&MFC42.#CString::ope>
            00402AB2   .  BF F8564000   mov     edi, 004056F8             ;  k8kbdlnjje6fji856ldfdpf5f8kmocfihm
            00402AB7   >  8A16          mov     dl, byte ptr [esi]
            00402AB9   .  8A0F          mov     cl, byte ptr [edi]
            00402ABB   .  8AC2          mov     al, dl
            00402ABD   .  3AD1          cmp     dl, cl
            00402ABF   .  75 1E         jnz     short 00402ADF
            00402AC1   .  84C0          test    al, al
            00402AC3   .  74 16         je      short 00402ADB
            00402AC5   .  8A4E 01       mov     cl, byte ptr [esi+1]
            00402AC8   .  8A57 01       mov     dl, byte ptr [edi+1]
            00402ACB   .  8AC1          mov     al, cl
            00402ACD   .  3ACA          cmp     cl, dl
            00402ACF   .  75 0E         jnz     short 00402ADF
            00402AD1   .  83C6 02       add     esi, 2
            00402AD4   .  83C7 02       add     edi, 2
            00402AD7   .  84C0          test    al, al
            00402AD9   .^ 75 DC         jnz     short 00402AB7            ;  判斷上面加法運算后結果是否與k8kBDlnjje6Fji856ldFDpf5f8kmoCfihm相等
            00402ADB   >  33C0          xor     eax, eax
            00402ADD   .  EB 05         jmp     short 00402AE4
            00402ADF   >  1BC0          sbb     eax, eax
            00402AE1   .  83D8 FF       sbb     eax, -1
            00402AE4   >  85C0          test    eax, eax
            00402AE6   .  0F85 E3000000 jnz     00402BCF                  ;  如果相等 就返回FLAG
            00402AEC   .  8B7424 1C     mov     esi, dword ptr [esp+1C]
            00402AF0   .  6A 01         push    1
            00402AF2   .  8BCE          mov     ecx, esi
            00402AF4   .  E8 ED040000   call    <jmp.&MFC42.#CWnd::Update>
            00402AF9   .  68 E4564000   push    004056E4                  ;  the flag file is:
            

            2、上面的算法實際上就是先對后12個字符進行base64解密,再進行3DES解密,然后用解密結果加密數獨運算得到的字符串,如果最終結果和k8kbdlnjje6fji856ldfdpf5f8kmocfihm相等,就成功了!,首先我們得熟悉一下base64解密算法的匯編代碼,只有了解這個常見的算法,在跟蹤調試的時候才能節約時間,快速出結果,否則就會身陷其中,不可自拔。

            對于base64解密算法,部分c代碼如下:

            #!c
            if( j == 4 )
            {
              *outputBuffer++ = (b[0] << 2) | (b[1] >> 4);
              *outputBuffer++ = (b[1] << 4) | (b[2] >> 2 );
              *outputBuffer++ = (b[2] << 6) | b[3];
            }
            else if( j == 3 )
            {  
              *outputBuffer++ = (b[0] << 2) | (b[1] >> 4);
              *outputBuffer++ = (b[1] << 4) | (b[2] >> 2 );
              return (i >> 2) * 3 + 2;
            }
            else if( j == 2 )
            {  
              *outputBuffer++ = (b[0] << 2) | (b[1] >> 4);
              return (i >> 2) * 3 + 1;
            }
            else
            {
              return -2;   
            }      
            

            } // End for i }

            我們在 函數401000里看到如下匯編代碼cmp edi,4 ;cmp edi,3;cmp edi,2

            這些特征代碼和C代碼if( j == 4 ); j==3 ;j == 2一致,基本可以大致判斷為是BASE64解密代碼。

            3、函數4016e0進去后,同一個函數4011a0執行了3遍:

            再看4011a0函數有以下匯編代碼:

            這里push操作的常數比較多分別為十進制的64 8 56 48

            我們再看看DES函數的C部分代碼:

            這個與匯編代碼push 的幾個常數一致,這時再看看3DES C代碼:

            #!c
            void tri_des(BYTE *dat, BYTE *key1, BYTE *key2, BYTE mode)
            {
                des(dat, key1, mode);
                des(dat, key2, 1 - mode);
                des(dat, key1, mode);
            }
            

            只是改變了傳入參數,跟函數4016e0一致。因此在逆向調試的時候如果遇到比較大型的特別復雜的算法 首先就要去考慮是否為常見的比較著名的加密算法,然后查找記住這些算法的特征,然后快速判斷,就不用再費力的分析這些算法了。

            4、根據之前算法分析的結果把字符串i4h48diiha38da344ha88ha4d4hfi3ahfi八個一組輪流加KEY (8字節)得到k8kbdlnjje6fji856ldfdpf5f8kmocfihm,首先我們做減法運算反推出KEY,因為加法時遇到 > ? ;的結果時又繼續加了一遍KEY 因此,做減法的時候首先進行一次減法運算得到key1,把得到的key1除以2 取整得到key2 ,這時重新進行加法計算來驗證,如果加key2得到了> 或 ?或;那么KEY取key2,否則取key1,這樣就得到了加法運算的key addKey="\x02\x04\x03\x07\x06\x08\x05\x01\x0",當你反推加密結果不正確的時候就得考慮一下這個KEY是否有一個0結束符號了。這里要加一個0結束符。

            5、根據匯編算法得到KEY后,我們首先對其進行3DES加密。從匯編算法里也分析到了加密密鑰是從i4h48diiha38da344ha88ha4d4hfi3ahfi里取的兩組8字節長度的字符串。因此密鑰是已知的。

            題目中對數據進行3DES解密算法如下:

            00402A3E . 6A 01 push 1 00402A40 . 8D4424 48 lea eax, dword ptr [esp+48] ; 取輸入串前8個字符 00402A44 . 52 push edx 00402A45 . 50 push eax 00402A46 . 57 push edi 00402A47 . E8 94ECFFFF call 004016E0 ; 把上面取出的兩組字符作為密鑰進行3DES解密 通過跟蹤調試我們可以分析出:一共傳入了4個參數 其中1代表解密,edx eax是密鑰,這里可以保持不變,edi是要解密的數據,那么我們直接可以利用這個函數來對KEY進行加密。具體操作就是,首先選中push 1這一行,右鍵選擇此處為新EIP,然后把push 1改為push 0來對數據加密,修改edi內存地址對應的數據為KEY,執行一遍這個函數就得到了KEY進行3DES加密后的結果,保存在EDI指向的內存里。把這個結果進行BASE64加密就得到了后12個字符Jji29cqJ+8kA,把它和之前數據運算的結果拼接起來i4h48diiha38da344ha88ha4d4hfi3ahfiJji29cqJ+8kA就得到了最終結果。

            0x03 程序算法設計分析


            1、首先無意中看到一個數獨游戲,難度系數只有1,于是乎就想根據這個設計一個算法。由于二進制題目才開始興起,所以也不打算采用太復雜的算法,設計的時候適當的控制了一下難度。這個算法設計起來比較簡單,首先自己完成了一遍這個數獨游戲,然后用代碼來檢驗數獨結果,代碼如下:。

            #!c
            CString CSgbmz1Dlg::check(char *in)
            {
                CString result;
            //初始化數獨矩陣
                Char *key="adf438ghi",*init_table="g8azfzzd3f3zzzhazgzdzzzgzf4zzizz8fghzzfdgi3zz3gzfzzizdai3g8zzzzhfd4zzg8z84gzazd3z";
                char table[9][9]={0};
                char newtable[9][9];
                int i=0,j=0,n=0;
                result="where is flag?flag is in the beautifull spring!";
            //檢查輸入內容是否在規定范圍 adf438ghi
                for (i=0;i<strlen(in)-12;i++)
                {
                    for (j=0;j<strlen(key);j++)
                    {
                        if (in[i]==key[j])
                        {
                            break;
                        }
                    }
                    if (j==strlen(key))  //根據循環次數判斷,如果小于adf438ghi的長度 表示沒在里面找到 就掛了
                    {
                        //  syslog->SetWindowText("where is flag?flag is in the beautifull spring!");
                        return result;
                    }
                }
                memcpy(table,init_table,81);
            //下面被注釋起來了,這是制作生成數獨初始矩陣和數獨結果的過程
                /*  {"761030025"}//g8azfzzd3=>i4h  //首先初始化一個數字數獨矩陣,再把它轉化為字符對應的形式
                ,{"350008107"}//f34
                ,{"020007034"}
                ,{"009006378"}
                ,{"003279500"}
                ,{"570300902"}
                ,{"195760000"}
                ,{"832400760"}
                ,{"647010250"}
                };
                char *table1[9]={                //初始化一個數獨結果矩陣,再把它轉化為字符對應的形式
                {"761934825"}//g8azfzzd3=>i4h
                ,{"354628197"}//f34
                ,{"928157634"}
                ,{"219546378"}
                ,{"483279516"}
                ,{"576381942"}
                ,{"195762483"}
                ,{"832495761"}
                ,{"647813259"}
                };
                */
                for(i=0;i<9;i++)  //把輸入的字符串填充到初始數獨矩陣中
                    for(j=0;j<9;j++)
                    {
                        if(table[i][j]=='z')newtable[i][j]=in[n++];
                        else newtable[i][j]=table[i][j];
                    }
            
            
                    for (i=0;i<9;i++)
                        for (j=0;j<9;j++)
                        {
                            char test=newtable[i][j];
                            newtable[i][j]='w';
                            for (n=0;n<9;n++)  //進行行檢查
                            {
                                if (test==newtable[i][n])
                                {
                                    //  syslog->SetWindowText("where is flag?flag is in the beautifull spring!");
                                    return result;
                                }
            
                            }
                            newtable[i][j]=test;
            
                            test=newtable[j][i];
                            newtable[j][i]='y';
                            for (n=0;n<9;n++)  ////進行列檢查
                            {
                                if (test==newtable[n][i]) 
                                {
                                    //  syslog->SetWindowText("where is flag?flag is in the beautifull spring!");
                                    return result;
                                }
            
                            }
                            newtable[j][i]=test;
                        }
                        result="good !flag is Waving to you!";
                        return result;
            }
            

            這個基本是算法的全部內容了,但是感覺實在是難度不大,又怕被秒殺,因此決定給這個題目再添點油,加點醋。于是就有了后面12個字符的算法。

            2、后面12字符算法部分:

            #!c
            if(m_sf.IsEmpty())return;   
            
                info=check(m_sf.GetBuffer(m_sf.GetLength())); //數獨游戲
            
                char *temp=m_sf.GetBuffer(m_sf.GetLength());
                memcpy(g_key1,temp,8);  //取3DES密鑰
                memcpy(g_key2,temp+20,8);
                int slen1,slen; 
                TCHAR * tc;
                 //注釋的這部分 是對加密KEY 進行加密 得到最終結果的過程
                /*  tri_des(addKey,g_key1,g_key2,0);//3DES    0加密
            
            
                    int slen1,len=9;
                    int slen = (len / 3) * 4;
                    slen += 10;
            
                      tc = (TCHAR *)malloc(slen);
                      slen = BASE64_Encode((BYTE *)addKey, len, tc);
                      tc[slen]='\0';
                      //syslog->SetWindowText(tc);
                */    
                slen=m_sf.GetLength()-34;
            
                char * addKey;
                addKey= (char *)malloc(slen);
            
                memset(addKey,0,slen);
                memcpy(addKey,temp+34,(9/3)*4);
            
                slen1 = (slen / 4) * 3;
                slen1 += 10;
                BYTE * ct;
                ct = (BYTE *)malloc(slen1);
                int deLen=BASE64_Decode(addKey, slen, ct); //對輸入進行BASE64解密
                ct[deLen]='\0';
            
                tri_des(ct,g_key1,g_key2,1);//3DES  1  解密 
                int i=0,j;
                while(temp[i]&&i<m_sf.GetLength()-12)  //加密數獨運算的結果
                {
                    temp[i]+=ct[i%8];
                    if (temp[i]=='>'||temp[i]=='?'||temp[i]==';')temp[i]+=ct[i%8];
                    i++;
                }
                temp[i]='\0';
                info+="\r\n";   
            
                if (!strcmp(temp,"k8kBDlnjje6Fji856ldFDpf5f8kmoCfihm")) //比較是否相等
                {
                    UpdateData(TRUE);
                    info+="the flag file is:";
                    info+=m_sf.GetBuffer(m_sf.GetLength());
                    info+=".php\r\n";
                    info+="trying to get the flag....\r\n";
            
                    httpdata.Replace("ip_addr",m_ip);
                    httpdata.Replace("main",m_sf.GetBuffer(m_sf.GetLength()));
                    CString rep=SentHttpData(httpdata);
                    if (rep.Find("miao",0)>-1)
                    {
                        info+=rep;
                    }
                    else
                        info+="so sorry!no flag !";
                }
            

            上面的算法其實就是一個加法運算,所以難度也不大。最主要的是如果沒有能識別出3DES加密算法,那么就會被卡住,所以對算法特征的了解在破解過程中往往會有很大幫助,也許這里難住了一部分人。

            3、最終結果如果和k8kBDlnjje6Fji856ldFDpf5f8kmoCfihm相等,這個時候就會以輸入字符串作為文件名,以php為后綴 訪問服務器里的這個文件,這時候會返回flag

            文件里的PHP 代碼為:

            #!php
            <?php
            include 'config.php';
            $sql = "select flag from flag";
            $result = @mysql_query($sql);
            while($row = mysql_fetch_array($result))
            {
                if($row['flag'])
                {
                    echo $row['flag'];
                    break;
                }
                else
                {
                    die('no!');
                }
            }
            ?>
            

            0x04 總結


            本文對算法進行了破解分析以及設計分析。題目主要算法蘊含在一個數獨游戲中。通過數獨游戲的結果以及內存字符串反推得到一個加密KEY 通過把KEY進行3DES加密,再進行BASE64加密得到一個12字節長度的字符串,最后把數獨結果和12字節長度字符串拼接起來得到了可以返回FLAG的文件,通過訪問該文件最終獲得FLAG。

            本題難點在于是否能判斷出來算法的表現是在做一個數獨游戲,是否能根據匯編代碼判斷出題目中涉及的3DES算法和BASE64解密算法。感謝大家的參與,如果文章中有不正確或者表達不完整的地方,感謝批評指正!

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线