[ksnctf] Lives out

ksnctfにチャレンジ 第27問目です。

※ksnctfは常駐型のCTFサイトです。 ※問題のページはコチラです。

リバースエンジニアリング問題です。 対象のプログラムがPE64形式つまり Windowsの64bitアプリケーション ということで各種ツールに慣れていなかったり、なんとなく敬遠していたりで 飛ばしていましたが、重い腰を上げて取り組んでみました。

プログラムを起動すると ライフゲームらしきドットがチカチカ し始めます。問題ページのヒントには

Can you switch all the lights off?

とありますが、もちろんプログラムの特性上全部の輝点を 消すのはむりなので当然プログラム中の判定部分を探せ、 ということでしょう。

まずは VisualStudioで livesout.exe のプロセスにアタッチして 一時停止ボタンをポチポチ押しつつ動作を探って行き、 同時に snowman を使ってなんちゃってC++のコードに直しました。

x86コードならIDA Proで片付くはずですがFree版は x86_64が弄れないのでこのあたり面倒です。

VisualStudioの逆アセンブル画面とsnowmanの画面を見比べて Callで呼び出している API名なども確認していきます。

例えば VisualStudioの ↑この部分と

00007FF737E311C0  lea         rcx,[rsp+60h]  
00007FF737E311C5  call        qword ptr [7FF737E331D0h]  
00007FF737E311CB  lea         rcx,[rsp+60h]  
00007FF737E311D0  call        qword ptr [7FF737E33210h]  
00007FF737E311D6  lea         rcx,[rsp+60h]  
00007FF737E311DB  xor         r9d,r9d  
00007FF737E311DE  xor         r8d,r8d  
00007FF737E311E1  xor         edx,edx  
00007FF737E311E3  call        qword ptr [7FF737E331B0h]  
00007FF737E311E9  test        eax,eax  
00007FF737E311EB  jne         00007FF737E311C0 

Snowmanの画面の↑この部分を見比べれば この部分はもともとWinアプリの メッセージループに相当する場所だとわかります。

同じようにして snowmanを軽く参考にしつつVisualStudioでブレークポイントを 打って調べていきます。

7FF7'37E31220 からが主な部分で snowmanの出力コードから

SetTime
PostQuitMessage
BeginPaint
CreateSolidBrush
SelectObject
Rectangle
DeleteObject
InvalidateRect
DefWindowProcW
EndPaint
TextOutW

このようなAPIが呼び出されていることがわかります。 ここで明らかに怪しいのが

TextOutW

当然これですね。

Livesout.exeでは基本的に文字列を表示することは ないはずなのでこれがFLAGを表示している可能性が大です。

TextOutWは

00007FF737E31728  lea         r9,[rbp+160h]  ←なんらかの文字列
00007FF737E3172F  xor         r8d,r8d  ← 表示y座標 0
00007FF737E31732  xor         edx,edx  ← 表示x座標 0
00007FF737E31734  mov         rcx,rbx  ← hdc?
00007FF737E31737  mov         dword ptr [rsp+20h],15h ← 文字数が 0x15 = 21 FLAG_{16文字}とあってる!
00007FF737E3173F  call        qword ptr [7FF737E33010h] ← TextOutW()

この部分で呼ばれていて、 座標 (0,0) に 21文字の文字列を表示するようになってます これは確定っぽいです。

この部分の前のコードをどんどん遡っていくと なにやらmovdqa命令を連発してメモリにデータを作っているようなので ここでFLAG文字列を構成しているのでしょう。

問題はこの部分の直前

00007FF737E3155E  test        r11b,r11b  
00007FF737E31561  je          00007FF737E31745  
00007FF737E31567  movdqa      xmm0,xmmword ptr [7FF737E332B0h]  
 …以下 movdaqaいっぱい
   紆余曲折して
00007FF737E3173F  call        qword ptr [7FF737E33010h] ← TextOutW()

ここです。

test命令の結果常にゼロフラグ=1 になるので直後の je は常に 有効でいずこかへ飛んでいってしまいます。 ここを書き換えれば良さそうです。

というわけでこういうときは うさみみハリケーンの出番です。

うさみみハリケーンのデバッグから [選択範囲を逆アセンブル] を選んで問題の JE命令部分

7FF7'37E31561 -- 7FF7'37E31566

を逆アセンブル このジャンプ命令を NOP(0x90) で埋め尽くして潰してしまいます。

すると TextOutW() に到達するようになって 画面に FLAG文字列が表示されるようになりました。