三個白帽在線挑戰平臺,集WEB和二進制挑戰于一身,以在線挑戰的形式把網絡安全相關領域白帽子的技術和智慧淋漓盡致地展現出來,為白帽子們搭建了一個非常完美、和諧的在線交流平臺,使得白帽子之間可以融洽地施展絕技、交流創意和想法。一次一次的挑戰讓白帽子們大飽眼福、樂在其中,讓大家腦洞大開,并且收獲滿滿,同時回味無窮!
由于對windows平臺下二進制有過研究,本著和大家交流學習的目的,在和三個白帽平臺管理溝通后,為《條條大路通羅馬系列》設計了一個二進制算法題目。本文將首先從破解者角度對這個二進制題進行分析,單純以破解者身份來逆向分析算法,解密數據,從而最終獲得flag。另外將從該題設計者角度進行分析,一步步描述每步算法相對應的數據如何加密、解密的,試圖達到什么樣的效果,試圖在哪里困住破解者。
首先我們按照程序的流程,一步步分析算法、破解算法。
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。這里就得到了算法的第一部分的字符串。把斷點設置在該算法函數尾部,單步走,等到該函數返回后我們就來到了下面的算法部分。
跟蹤到后面我們發現其實第二部分是對我們輸入的后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就得到了最終結果。
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!');
}
}
?>
本文對算法進行了破解分析以及設計分析。題目主要算法蘊含在一個數獨游戲中。通過數獨游戲的結果以及內存字符串反推得到一個加密KEY 通過把KEY進行3DES加密,再進行BASE64加密得到一個12字節長度的字符串,最后把數獨結果和12字節長度字符串拼接起來得到了可以返回FLAG的文件,通過訪問該文件最終獲得FLAG。
本題難點在于是否能判斷出來算法的表現是在做一個數獨游戲,是否能根據匯編代碼判斷出題目中涉及的3DES算法和BASE64解密算法。感謝大家的參與,如果文章中有不正確或者表達不完整的地方,感謝批評指正!