💨

WriteUp runme.exe(Reversing)

2023/05/12に公開

前回(https://zenn.dev/kbaur/articles/daff26eac71d31)の記事で
次はCrypto書こうと言っときながらまたReversingですが・・・
SECCON2018のrunme(reversing)を解いてみました。
https://github.com/SECCON/SECCON2018_online_CTF/tree/master/Reversing/Runme

まずは runme.exe を実行してみますがエラーダイアログが出て終わり。

解析(Ghidraで逆アセンブル)

ということで、Ghidraで解析してみました。
でメインウインドウにて真ん中の逆アセンブリ結果のところから、
まずエントリポイントを探します。
エントリポイントの探し方は以下記事が参考になりました。
https://scrapbox.io/ishikwx/Ghidra
runme.exeの場合は 左ペインにある .text をダブルクリックした一番上に出てくるので
わかりやすいですね。
undefined __stdcall entry(void) とあり entry と書かれているので、
そこからもわかりやすいです。

さて、エントリポイントの逆アセンブリ結果ですが、コピペしたものが以下です。

                         //
                         // .text 
                         // ram:00401000-ram:004019ff
                         //
                         **************************************************************
                         *                          FUNCTION                          *
                         **************************************************************
                         undefined __stdcall entry(void)
         undefined         AL:1           <RETURN>
         undefined4        Stack[-0x8]:4  local_8                                 XREF[1]:     0040100a(W)  
                         entry                                           XREF[4]:     Entry Point(*), 004000a8(*), 
                                                                                      004000ac(*), 00400184(*)  
    00401000 55              PUSH       EBP
    00401001 89 e5           MOV        EBP,ESP
    00401003 56              PUSH       ESI
    00401004 ff 15 68        CALL       dword ptr [->KERNEL32.DLL::GetCommandLineA]      = 0000307e
             30 40 00
    0040100a 89 45 fc        MOV        dword ptr [EBP + local_8],EAX
    0040100d 50              PUSH       EAX
    0040100e 6a 22           PUSH       0x22
    00401010 e8 1f 00        CALL       FUN_00401034                                     undefined FUN_00401034(char para
             00 00
    00401015 6a 40           PUSH       0x40
    00401017 68 27 20        PUSH       s_Congratz_00402027                              = "Congratz"
             40 00
    0040101c 68 30 20        PUSH       s_You_know_the_flag!_00402030                    = "You know the flag!"
             40 00
    00401021 6a 00           PUSH       0x0
    00401023 ff 15 98        CALL       dword ptr [->USER32.DLL::MessageBoxA]            = 000030a0
             30 40 00
    00401029 ff 15 64        CALL       dword ptr [->KERNEL32.DLL::ExitProcess]          = 00003070
             30 40 00

機械語なのでちんぷんかんぶんですね。
右ペインのC言語相当の部分を追うだけでもこの問題は十分解ける
(さらにいえば Linux で runme.exe をstrings コマンドで読み込むだけでも十分だったり・・・)
のですが、勉強のために解釈してみました。

まず、PUSHですがこれはスタックに情報を置くものです。
EBPとESPはレジスタ(CPU内にあるメインメモリよりも高速アクセス可能な領域)に関するもので
EBPはスタックの底のアドレス、ESPはスタックの最初のアドレスが入っているとのこと。
で、4行目のCALLは関数を呼ぶもので、
Windows API の GetCommandLineA を呼び出しています。
次のMOVは情報を代入するもので、EAX(レジスタ内の領域の一つ)に
GetCommandLineAの結果を格納しています。
そして、8行目のCALLで FUN_00401034 で
6,7行目のPUSHでスタックに積んでおいた 0x22 と GetCommandLineAの結果 の情報を
引数として実行しています。
スタックは後入れ先出し、LIFO(Last In First Out)なので
0x22 が第一引数、GetCommandLineAの結果が第二引数ですね。
参考情報
https://mathwords.net/stack

右ペイン、C言語相当のコード部分をコピペしたものが以下です。

void entry(void)

{
  LPSTR pCVar1;
  UINT uExitCode;
  
  pCVar1 = GetCommandLineA();
  uExitCode = 0x22;
  FUN_00401034('\"',pCVar1);
  MessageBoxA((HWND)0x0,"You know the flag!","Congratz",0x40);
                    /* WARNING: Subroutine does not return */
  ExitProcess(uExitCode);
}

機械語で書かれている通り、4行目のCALLと5行目のMOV部分として
pCVar1という変数に GetCommandLineA() の結果を代入という処理で書かれており、
6~8行目のPUSH,CALLの処理が
FUN_00401034という関数を '"',pCVar1 の引数とともに呼び出しという形で書かれてますね。

それでは FUN_00401034 部分です。
まず逆アセンブル結果のコピペから。

                         **************************************************************
                         *                          FUNCTION                          *
                         **************************************************************
                         undefined __cdecl FUN_00401034(char param_1, char * para
         undefined         AL:1           <RETURN>
         char              Stack[0x4]:1   param_1                                 XREF[1]:     00401038(R)  
         char *            Stack[0x8]:4   param_2                                 XREF[2]:     0040103c(R), 
                                                                                               0040104f(R)  
                         FUN_00401034                                    XREF[1]:     entry:00401010(c)  
    00401034 55              PUSH       EBP
    00401035 89 e5           MOV        EBP,ESP
    00401037 56              PUSH       ESI
    00401038 0f b6 4d 08     MOVZX      ECX,byte ptr [EBP + param_1]
    0040103c 8b 55 0c        MOV        EDX,dword ptr [EBP + param_2]
    0040103f 0f b6 12        MOVZX      EDX,byte ptr [EDX]
    00401042 39 d1           CMP        ECX,EDX
    00401044 0f 85 71        JNZ        LAB_004018bb
             08 00 00
    0040104a b9 01 00        MOV        ECX,0x1
             00 00
    0040104f 8b 55 0c        MOV        EDX,dword ptr [EBP + param_2]
    00401052 42              INC        EDX
    00401053 52              PUSH       EDX
    00401054 6a 43           PUSH       0x43
    00401056 e8 05 00        CALL       FUN_00401060                                     undefined FUN_00401060(char para
             00 00
    0040105b 5e              POP        ESI
    0040105c 89 ec           MOV        ESP,EBP
    0040105e 5d              POP        EBP
    0040105f c3              RET

                         LAB_004018bb                                    XREF[50]:    00401044(j), 
                                                                                      FUN_00401060:00401070(j), 
                                                                                      FUN_0040108c:0040109c(j), 
                                                                                      FUN_004010b8:004010c8(j), 
                                                                                      FUN_004010e4:004010f4(j), 
                                                                                      FUN_00401110:00401120(j), 
                                                                                      FUN_0040113c:0040114c(j), 
                                                                                      FUN_00401168:00401178(j), 
                                                                                      FUN_00401194:004011a4(j), 
                                                                                      FUN_004011c0:004011d0(j), 
                                                                                      FUN_004011ec:004011fc(j), 
                                                                                      FUN_00401218:00401228(j), 
                                                                                      FUN_00401244:00401254(j), 
                                                                                      FUN_00401270:00401280(j), 
                                                                                      FUN_0040129c:004012ac(j), 
                                                                                      FUN_004012c8:004012d8(j), 
                                                                                      FUN_004012f4:00401304(j), 
                                                                                      FUN_00401320:00401330(j), 
                                                                                      FUN_0040134c:0040135c(j), 
                                                                                      FUN_00401378:00401388(j), [more]
    004018bb 6a 40           PUSH       0x40
    004018bd 68 00 20        PUSH       s_Failed_00402000                                = "Failed"
             40 00
    004018c2 68 07 20        PUSH       s_The_environment_is_not_correct._00402007       = "The environment is not correc
             40 00
    004018c7 6a 00           PUSH       0x0
    004018c9 ff 15 98        CALL       dword ptr [->USER32.DLL::MessageBoxA]            = 000030a0
             30 40 00
    004018cf ff 15 64        CALL       dword ptr [->KERNEL32.DLL::ExitProcess]          = 00003070
             30 40 00

4~7行目ですが、
MOVとMOVZXは値代入で、param1とparam2をそれぞれ ECX,EDX に入れています。
MOVZXでは byte ptr とあるので1byteだけ代入、つまり一文字だけ入れています。
つまり、まずは '"' と GetCommandLineA の一文字目を比べているということですね。
7行目の CMP ECX,EDX で ECX,EDX を比較して、NGなら
次の JNZ LAB_004018bb を呼び出すとなっています。
もしOKなら JNZ は飛ばして次の MOV ECX,0x1 へとなります。
その後 MOV EDX,dword ptr [EBP + param_2],INC EDX,PUSH EDX で
param_2(GetCommandLineA結果)の先頭アドレスを EDX に入れ、
EDX のアドレスを一つインクリメント、
つまり二文字目のアドレスをPUSHでスタックに格納しています。
次に 0x43 ASCIIコードで'C'をスタックに格納し CALL FUN_00401060 を
'C',GetCommandLineAの結果の二文字目のアドレスを引数として関数呼び出し、
ということをずっと続けているということがわかりました。

なおNGの時の LAB_004018bb では MessageBoxA を
0x0,"The environment is not correct.","Failed",0x40 の引数とともに実施して
ExitProcessで終了しています。

ここまでの処理のC言語解釈を見てみると

void __cdecl FUN_00401034(char param_1,char *param_2)

{
  UINT unaff_ESI;
  
  if (param_1 == *param_2) {
    FUN_00401060('C',param_2 + 1);
    return;
  }
  MessageBoxA((HWND)0x0,"The environment is not correct.","Failed",0x40);
                    /* WARNING: Subroutine does not return */
  ExitProcess(unaff_ESI);
}

というように機械語の内容がC言語相当に解釈されていました。

結果

関数を追い続けて、GetCommandLineAの結果と何を比較しているかを確認した結果は
以下となりました。
"C:\Temp\SECCON2018Online.exe" SECCON{Runn1n6_P47h}

それではこれを実行してみます・・・


congratzがでました!

感想

今回Ghidraでの2回目の解析で少しインターフェースがわかりました。
機械語も少しだけわかりました・・・すこしだけ・・・
次は機械語が少しわかったところでgdbかwindbgでデバッグやってみたいところです。

Discussion