Slide 1

Slide 1 text

PE(exe)ファイルの Rev問を解いてみよう <公開版> Ciruela(@__Xcyba17her_)

Slide 2

Slide 2 text

目次  問題解説  Binary 2 (NeverLAN CTF 2019)  Crack me (InnoCTF 2019)  Left 5 Seconds (Victor of Cyberists)  Gacha (Victor of Cyberists)  完全版ではksnctfの3問のWriteupと,問題ファイルやソルバを 用意しています.色々と考慮すべきことがあるので,完全版の 配布はTwitter DMにより個人的に行うことにします.  完全版からksnctfの部分のwriteupを抜いただけなので,ツー ルのある機能の説明が省かれているにもかかわらず,その後の 問題で既知の機能として扱われている可能性があります. 2

Slide 3

Slide 3 text

Binary 2 (NeverLAN CTF 2019) 3

Slide 4

Slide 4 text

どんな問題?  認証システムのユーザーネームとパスワードを忘れてしまった…  Employee_Payroll.exe(32bit)というファイルが渡される  実行すると,UsernameとPasswordが要求される 4

Slide 5

Slide 5 text

ファイルの種類を識別する  WindowsではTrIDNetが便利(シグネチャデータが大きいが我慢)  画像から,exeファイルの可能性が高い 5

Slide 6

Slide 6 text

dnSpyで逆コンパイル  dnSpyは,.NET製かつシンボル情報が多ければバイナリから ソースコードに復元する超優秀なツール  exeファイルはまずこいつに突っ込んでみる!  exeファイルが32bitか64bitかで32bit版と64bit版のdnSpyを使 い分ける必要がある 6

Slide 7

Slide 7 text

怪しいbtnLogin_Click関数  “OK”ボタンを押すと実行されると予想 UsernameとPasswordが正し ければ実行される 文字列を作った後,それを ウィンドウで表示する ログイン試行が3回を超えたら 実行される… UsernameとPasswordのいずれ かが誤っていれば実行される … 7

Slide 8

Slide 8 text

checkUsername関数とcheckPassword関数  直接文字列を比較している…  Usernameは”admin”  Passwordは ”dGhpc19pc19ub3RfdGhlX2ZsYWdfeW91X3NlZWtfa2VlcF9sb29raW5n” 8

Slide 9

Slide 9 text

見つけたUsernameとPasswordを入れる  flag{ST0RING_STAT1C_PA55WORDS_1N_FIL3S_1S_N0T_S3CUR3}  2ページ前のthis.r1~this.r53にはflagが平文で入っていた 9

Slide 10

Slide 10 text

Crack me (InnoCTF 2019) 10

Slide 11

Slide 11 text

 Crack_me.exe(32bit)が渡される  “Enter the key:”と表示され,何か入力するとウィンドウが閉 じる 11 どんな問題?

Slide 12

Slide 12 text

dnSpyを使えない  セクション情報などしか表示されない 12

Slide 13

Slide 13 text

IDA Freewareで眺める  文字入力関数のcinやscanfを探す  Importsタブ → Ctrl+Fで検索機能を表示 → cinで検索  1件だけ表示されるので,ダブルクリックでその場所へ移動 13

Slide 14

Slide 14 text

IDA Freewareで眺める  cinはsub_403630関数で呼ばれている  “sub_403630”の部分をダブルクリック 14

Slide 15

Slide 15 text

IDA Freewareで眺める  “Enter the key:”の表示と入力の処理がある  ここがメインの処理のようだ 15

Slide 16

Slide 16 text

IDA Freewareで眺める  cinの入力処理後,分岐でsub_4032F0関数が呼ばれることが多い  入力後の文字列の検証でダメだった場合の処理と予想 16

Slide 17

Slide 17 text

IDA Freewareで眺める 17 “Invalid key!”と表示 exit(0)のような処理  sub_4032F0関数は検証でダメだった場合の処理でよさそう  “Invalid Key”の表示やexit(0)的な処理がある

Slide 18

Slide 18 text

IDA Freewareで眺める  sub_4032F0関数に名前”wrong_exit”と名付けておく  sub_4032F0をクリック→Nキーを押す→Nameに”wrong_exit”と入力 18

Slide 19

Slide 19 text

IDA Freewareで眺める  一つ目の分岐を見る  sub_403310関数の返り値が0xFFFFFFFF(=-1)以外ならよさそうだが… 19

Slide 20

Slide 20 text

IDA Freewareで変数に名前をつける  [ebp+var_28]は,cinの引数であり,入力文字列へのポインタが 入っていると考えられる  var_28に”input_string”と名前をつけてみる  var_28をクリック → Nキーを押す → 名前を付ける 20

Slide 21

Slide 21 text

IDA Freewareで眺める  読むのが非常に面倒くさい…  さらに関数を呼んでいる  デバッグしてみよう  Strという第一引数に”_”を 指定していることから入力に おける”_”に関する関数と 予想  デバッグ時には入力文字列に “_”を含ませてみる 21

Slide 22

Slide 22 text

(x64dbgに付属する)x32dbgを使う  x64dbgとx32dbgは,x32とx64のPEファイルをデバッグできる  Ollydbgは32bitしかデバッグできないのでこちらのほうが便利  Ollydbgよりカラフルでよさそう(小並感) 22

Slide 23

Slide 23 text

x32/x64dbgで関数を探す  CPUタブ内で左クリック → 検索 → 全モジュールを検索 → 外部 関数呼び出し → 関数名cinで検索 → 結果をダブルクリックし て移動 23

Slide 24

Slide 24 text

x32/x64dbgでグラフ表示  CPUタブ内で左クリック→グラフ  関数全体が表示されない場合は,左クリック→全体図 24

Slide 25

Slide 25 text

x32/x64dbgでブレークポイントを設置  左の灰色の点をクリックするとブレークポイントを設置できる  IDAでsub_403310関数に相当する関数を探し,その直後にブレーク ポイントを置く  IDAでsub_403310関数に相当する関数の返り値をeaxの値から調べ ることができる 25

Slide 26

Slide 26 text

sub_403310の返り値を検証する  “_”の入力の有無や位置を変えて返り値(eax)の変化を調べる  “_”を入れないとeaxは0xffffffff(=-1)に,入れると”_”のイン デックスがeaxに入ることがわかる  関数直前には入力文字列へのポインタがecxに格納されるため,入 力文字列も扱う関数だと予想できる  sub_403310は,指定文字列から指定文字か文字列を検索しイン デックスを返し,見つからなければ-1を返す,strstrのような 関数だと予想できる  分岐前に返り値(eax)が0xffffffffならwrong_exitに移動するこ とになっていた  入力文字列に”_”が含まれていればwrong_exitに移動しない! 26

Slide 27

Slide 27 text

x32/x64dbgで関数に名前を付ける  sub_403310に”sub_strstr”という名前をつけてみる  該当命令で左クリック→ラベル→ラベル00E53310を選択し,名前 を付ける 27

Slide 28

Slide 28 text

2つめの分岐を調べる  またもやsub_strstrを呼んでいる  ebp+var_30には以前のsub_strstrで見つかった”_”のインデッ クス(厳密にはアドレス)が入っており,それに1を加えたものを 引数として渡している  つまり入力文字列にはアンダーバー’_’が2つ以上必要 28

Slide 29

Slide 29 text

3つめの分岐を調べる  sub_4033F0の返り値(eax)が0x0fであればよい  入力にアンダーバー’_’が2つ入るようにしながら,入力文字列 を変えてみる  eaxには入力文字列の長さが入ることが分かる  sub_4033F0の機能はstrlenと同じ  入力すべき文字列は15文字 29

Slide 30

Slide 30 text

4つめの分岐を調べる  sub_402850の返り値(eax)が指すアドレスに入っている1バイト の値が0x72(=‘r’)であればよい  cmp命令の前にブレークポイントを置き,eaxの値を調べる  eaxは入力文字列の先頭のアドレス  入力する文字列の1文字目は’r’ 30

Slide 31

Slide 31 text

5つめの分岐を調べる  エスパーできる  1文字目の’r’に平文で見 れる”everse”が続くので はないか  実際に1文字目から ”reverse”とすると exit_wrongに分岐しない 31

Slide 32

Slide 32 text

6つめの分岐を調べる  sub_402850は4つめの分岐に出てきた  4つめの分岐のsub_402850では,0を引数とし,返り値は入力文字 列の1文字目のアドレスだった  今回は8を引数としていて,今回は返り値が入力文字列の9文字目の アドレスであると予想する.すなわち,9文字目が’i’(=0x69)であ ると予想する 32

Slide 33

Slide 33 text

7つめの分岐を調べる  9文字目のASCIIコードと10文字目のASCIIコードを加えると 0xdcになる  9文字目は’i’(=0x69)だった → 10文字目は’s’(=0x73) 33

Slide 34

Slide 34 text

8つめの分岐を調べる  これまでの情報から,1文字目 から11文字目は確定し, ”reverse_is_????” と考えられる  “fine”が見えるので, “reverse_is_fine”かと 思いきや通らない 34

Slide 35

Slide 35 text

8つめの分岐を調べる  ごめんなさい,これは完全に勘です(m´・ω・`)m  実は,sub_403090を通るとeaxには入力文字列の12~15文字目を前に3 だけシフトした文字列が入る  例えば,入力の12~15文字目が”abcd”だと返り値eaxには”^_`a”が 入る  では,入力の12~15文字目を前に3だけシフトして”fine”だったら いい,すなわち”ilqh”だったらいいのではと考えた  これでだめだったら諦めるか地道に読んでいくつもりだった 35

Slide 36

Slide 36 text

偶然の正解  ”reverse_is_ilqh”が正解だった  しかし,フラグ文字列では’e’が’3’に改変されていた  恐らく最後の分岐以降がその処理 36

Slide 37

Slide 37 text

Left 5 Seconds (Victor of Cyberists) 37

Slide 38

Slide 38 text

どんな問題?  left_five_seconds.exeとGameLogic.dllが渡される  1から30のマスを5秒以内で順にクリックする  マウスで30回すべてのマスをクリックしていくブルートフォースの ようなスクリプトを書いても解けそう 38

Slide 39

Slide 39 text

dnSpyが使える  クリア条件が見つからないが,時間計測とそれに応じexitする Form1.Timer1_Tick関数が見つかる  exitしてしまう条件の500という値は5.00秒を示していそう  この値を大きくすれば1~30までクリックする十分な時間を確保できそう 39

Slide 40

Slide 40 text

dnSpyでプログラムを書き換える  dnSpyでは,逆コンパイルしたソースコードを書き換えて再コン パイルすることができる!  if (this.time_count == 500)をif (this.time_count == 50000)に書き換えたい  ソースコード上で左クリック → Edit Method → 該当部分を書き換 える → 右下のCompileボタン → ソースコード画面のFile → save All… → left_five_seconds_edited.exeなどと保存 40

Slide 41

Slide 41 text

うまく起動しない  left_five_seconds_edited.exeを起動してみる  “Invalid Hash!”と表示されて起動しない  ハッシュ値の検証をして改造の検知をしている? 41

Slide 42

Slide 42 text

どこでハッシュ値の検証を行っているか  left_five_minutes.exeとGameLogic.dllの両方で”Invalid Hash!”という文字列を探す  IDAでView → Open subviews → stringsでバイナリ内の文字列が 表示される 42

Slide 43

Slide 43 text

どこでハッシュ値の検証を行っているか  GameLogic.dllで”Invalid Hash!”が見つかる  それに加え,”77777f298dba696637adfadf59a5630f”という文 字列が見つかる  left_five_seconds.exeのハッシュ値とこれを比較し,合ってい なければ起動しない可能性がある 43

Slide 44

Slide 44 text

Windowsで,ハッシュ値を算出する  HashSumやHashCalcが使える  left_five_minutes.exeのMD5ハッシュ値 が”77777f298dba696637adfadf59a5630f”に一致  GameLogic.dllのMD5ハッシュ値照合用の文字 列”77777f298dba696637adfadf59a5630f”を, left_five_seconds_edited.exeのMD5ハッシュ値に変えてしまえ ばハッシュ値による改造検知を通過できそう 44

Slide 45

Slide 45 text

ハッシュ値の検査を回避する  left_five_seconds_edited.exeのハッシュ値を算出する  f97402713388f91420ad583e679df8b9 45

Slide 46

Slide 46 text

ハッシュ値の検査を回避する  GameLogic.dllのハッシュ値検査用文字列を改ざんする  Stirlingで改ざんする  検索・移動 → 検索 → 検索文字列入力 (データ種別は文字列・検 索範囲はデータ全体) 46

Slide 47

Slide 47 text

ハッシュ値の検査を回避する  右のASCII文字列からハッシュ値検査用文字列を変更する  書き換えたら,ファイル → 上書き保存 47

Slide 48

Slide 48 text

ハッシュ値の検査を回避する  left_five_minutes_edited.exeを起動する  ハッシュ値検査を回避できている  残り時間を示す文字列が0:00になっても終了しない  1~30をクリックし終わるとフラグが表示される 48

Slide 49

Slide 49 text

別解  Form1.Button_Clickといういかにもボタンが押されたら呼ば れそうな関数がある  さて,プログラムが終了してしまう条件は,time_countが500 を超えてしまうことだった  つまり,Form1.Button_Clickにブレークポイントを置いて実 行し,ボタンを押す → プログラムがブレークポイントで一時停 止する → time_countの値を編集して0に戻す → プログラムを 進めるというのを繰り返せば解ける  time_countはunsignedの変数のようであり,負数には設定できな い.つまり,プログラムを動かしてボタンを押すまでを5秒でやら なければいけないので少し不安があり,そして30回もやるのはめん どくさい 49

Slide 50

Slide 50 text

Gacha (Victor of Cyberists) 50

Slide 51

Slide 51 text

どんな問題?  Gacha.exe(64bit)と使用画像のフォルダが渡される  12回のガチャで12種類の誕生石を集めなければならない 51

Slide 52

Slide 52 text

dnSpyで開ける  しかし,難読化処理されておりほぼコードを読めない 52

Slide 53

Slide 53 text

IDAで眺める  難読化データばかりで処理がわからない  難読化手法も不明 53

Slide 54

Slide 54 text

CheatEngineを使ってみる  CheatEngineは,ゲームチーター御用達?のオープンソースソ フトウェアであり,条件を指定してメモリアドレスの検索を 行ったり,メモリの書き換えを行うことができる  Gacha.exeを起動してからCheatEngineを起動し,左上のコン ピュータのアイコンをクリック,プロセスの一覧からGachaを選択 し,Openをクリック 54

Slide 55

Slide 55 text

未知数検索  未知数検索とは,ゲーム内でのアクションごとに値が変化した メモリアドレスを絞っていき,重要な機能を持つアドレスを特 定すること  Gacha.exeにおいては,ガチャを引くごとに残り回数が減るた め,残り回数や資金を格納しているアドレスを特定できる可能 性がある  アドレスを特定したら,あとは内容を好きなように書き換える 55

Slide 56

Slide 56 text

未知数検索  まずScan Typeを”Unknown Initial Value”,Value Type を”4 Bytes”にし,First Scanをクリック  以降は,ガチャを引いた後,Scan Typeを”Changed Value”と してNext Scanをすることを繰り返す  残念ながらこの方法では絞り込めない 56

Slide 57

Slide 57 text

特定の値の配列の検索  Gachaでは,これまでに出た誕生石を1,出ていない誕生石を0と する12要素の配列を作成し,12種類の誕生石が全て排出された か判断している可能性がある  例えば,1月,4月,6月の誕生石が既出であったら,内容が [1,0,0,1,0,1,0,0,0,0,0,0]となっている配列が存在する可能性 がある  配列の検索にあたり,現在2,3,4,5,9,11,12の誕生石が排出されて いる場合を例にやりかたを示す 57

Slide 58

Slide 58 text

特定の値の配列の検索(1byte単位)  配列の各要素が1バイトである場合と4バイトである場合の2通り で検索してみる.まずは1バイト単位で検索  New Scan → Value Type: Array of Byte → Valueのテキス トボックスに”00 01 01 01 01 00 00 00 01 00 01 01”を入 力 → 全てのメモリを調べるためにWritable, Executable, CopyOnWriteのチェックを全て■にする → First Scan  しかし結果は1件もない 58

Slide 59

Slide 59 text

特定の値の配列の検索(4byte単位)  New Scan → Value Type: Grouped → Valueのテキストボッ クスに”4:0 4:1 4:1 4:1 4:1 4:0 4:0 4:0 4:1 4:0 4:1 4:1”を入力(4:0は4バイトの値が0の意) → 全てのメモリを調べ るためにWritable, Executable, CopyOnWriteのチェックを 全て■にする → First Scan  1件だけ結果が出る 59

Slide 60

Slide 60 text

CheatEngineでメモリを書き換える  結果のアドレスをダブルクリック → Valueが0である各アドレ スのValueをダブルクリックして1を入力 60

Slide 61

Slide 61 text

CheatEngineでメモリを書き換える  次にガチャを引くと,フラグが表示される  本来ならサーバ側に処理を隠すし,わかりやすく連続したアドレスに格納し ないが,それだと初心者は絶対に解けないのでこうしたとのこと 61

Slide 62

Slide 62 text

解説終了! お疲れさまでした(o^―^o) 62