Codegate CTF 2011 Binary 200
久しぶりの解析。
解析時には、IDA Pro 6.0 demo, OllyDbg 2.01 alpha3
を使って解析作業を行いました。プラグインは使用してません。
Freeで良いのあったら是非教えてください。
(実行ファイルのリンクとか、あったほうがいいのかな?)
とにかく動的解析する><
プラグインもなければ、何も設定されてないOllyDbgやImmunity Debuggerで
動的解析しようとすると、プログラムが既に終了していると思います。
これはアンチデバッグ処理のために、TLS-Callbacks が使用されているからです。
TLS Callbacks とは、
http://www.netagent-blog.jp/archives/51735398.html
TLS(Thread Local Storage)と呼ばれるスレッド毎に固有な記憶領域を利用した場合に、
スレッド起動時とスレッド終了時に呼び出されるコールバック関数
らしいです。
それでは、解析するために設定を変更します。
OllyDbg2.01 alpha3だと、
Options->Debugging->Events->Pause on new thread にチェックを入れる
Immunity Debuggerだと、
Options->Debugging options->Events->System breakpoint に変更する
Break on 〜のオプションにチェックを入れてもいいと思います。
てきとーにTLSCallBacksのアドレスを調べて内部を見ていきます。
.text:00401450 public TlsCallback_0 .text:00401450 TlsCallback_0 proc near ; DATA XREF: .data:TlsCallbackso .text:00401450 .text:00401450 arg_4 = dword ptr 0Ch .text:00401450 .text:00401450 push ebp .text:00401451 mov ebp, esp .text:00401453 cmp [ebp+arg_4], 1 .text:00401457 jz short loc_40145F .text:00401459 cmp [ebp+arg_4], 2 .text:0040145D jnz short loc_40149A .text:0040145F .text:0040145F loc_40145F: ; CODE XREF: TlsCallback_0+7j .text:0040145F call sub_401320 ; IsDebuggerPresent .text:00401464 and eax, 0FFh .text:00401469 test eax, eax .text:0040146B jnz short loc_401492 .text:0040146D call sub_401340 ; NtQueryInformationProcess .text:00401472 and eax, 0FFh .text:00401477 test eax, eax .text:00401479 jnz short loc_401492 .text:0040147B call sub_4013C0 ; PEB!NtGlobalFlags .text:00401480 and eax, 0FFh .text:00401485 test eax, eax .text:00401487 jnz short loc_401492 .text:00401489 call sub_401400 ; Interrupt 2Dh .text:0040148E test eax, eax .text:00401490 jz short loc_40149A .text:00401492 .text:00401492 loc_401492: ; CODE XREF: TlsCallback_0+1Bj .text:00401492 ; TlsCallback_0+29j ... .text:00401492 push 0 ; uExitCode .text:00401494 call ds:ExitProcess .text:0040149A ; --------------------------------------------------------------------------- .text:0040149A .text:0040149A loc_40149A: ; CODE XREF: TlsCallback_0+Dj .text:0040149A ; TlsCallback_0+40j .text:0040149A pop ebp .text:0040149B retn 0Ch .text:0040149B TlsCallback_0 endp
TLS-Callbacks処理の先頭から見ていきます。
引数の値が1or2なら処理の続行、それ以外なら何もせず終了しています。
数値は恐らく、
1 = DLL_PROCESS_ATTACH
2 = DLL_THREAD_ATTACH
を指しているものだと思われます。これらはwinnt.hに記述されており、以下のように定義されています。
#define DLL_PROCESS_ATTACH 1 #define DLL_THREAD_ATTACH 2 #define DLL_THREAD_DETACH 3 #define DLL_PROCESS_DETACH 0
1と2以外はデタッチを意味するので、何もしない処理をしてるのでしょう。
続く処理には以下のアンチデバッグ処理が施されてます。
・IsDebuggerPresent
・NtQueryInformationProcess
・PEB!NtGlobalFlags
・Interrupt 2Dh
どれもが、デバッグされてたら1、デバッグされてなければ0を返す関数になっています。
他への影響もないので、動的解析するときは、てきとーに回避すれば大丈夫です。
そのまま解析を進めると、無事にエントリーポイントにやってきます。
.text:00401000 start: .text:00401000 pusha .text:00401001 mov esi, (offset loc_404FFC+4) .text:00401006 lea edi, [esi-4000h] .text:0040100C push edi .text:0040100D or ebp, 0FFFFFFFFh .text:00401010 jmp short loc_401022 .text:00401010 ; --------------------------------------------------------------------------- .text:00401012 align 8 .text:00401018 .text:00401018 loc_401018: ; CODE XREF: .text:loc_401029j .text:00401018 mov al, [esi] .text:0040101A inc esi .text:0040101B mov [edi], al .text:0040101D inc edi .text:0040101E add ebx, ebx .text:00401020 jnz short loc_401029 .text:00401022 .text:00401022 loc_401022: ; CODE XREF: .text:00401010j .text:00401022 mov ebx, [esi] .text:00401024 sub esi, 0FFFFFFFCh .text:00401027 adc ebx, ebx .text:00401029 .text:00401029 loc_401029: ; CODE XREF: .text:00401020j .text:00401029 jb short loc_401018 .text:0040102B mov eax, 1 .text:00401030 .text:00401030 loc_401030: ; CODE XREF: .text:0040103Fj .text:00401030 ; .text:0040104Aj .text:00401030 add ebx, ebx .text:00401032 jnz short loc_40103B .text:00401034 mov ebx, [esi] .text:00401036 sub esi, 0FFFFFFFCh .text:00401039 adc ebx, ebx .text:0040103B .text:0040103B loc_40103B: ; CODE XREF: .text:00401032j .text:0040103B adc eax, eax .text:0040103D add ebx, ebx .text:0040103F jnb short loc_401030 .text:00401041 jnz short loc_40104C .text:00401043 mov ebx, [esi] .text:00401045 sub esi, 0FFFFFFFCh .text:00401048 adc ebx, ebx .text:0040104A jnb short loc_401030 .text:0040104C .text:0040104C loc_40104C: ; CODE XREF: .text:00401041j .text:0040104C xor ecx, ecx .text:0040104E sub eax, 3 .text:00401051 jb short near ptr loc_40105E+2 .text:00401053 shl eax, 8 .text:00401056 pop eax .text:00401057 popa .text:00401058 call sub_401130 .text:0040105D push eax .text:0040105E .text:0040105E loc_40105E: ; CODE XREF: .text:00401051j .text:0040105E call ds:ExitThread .text:00401064 ; --------------------------------------------------------------------------- .text:00401064 retn
いきなりpushaの処理が見えますが、この辺りは無意味な処理が施されています。
それらを無視して、call sub_401130 が本体っぽいので、内部を解析します。
この先も難読化のために無意味な処理が含まれてるので、それらを削除して見ていきます。
.text:00401130 push ebp .text:00401131 mov ebp, esp .text:00401133 sub esp, 44h .***delete*** .text:00401158 lea eax, [ebp+var_C] .text:0040115B push eax .text:0040115C call sub_40149E .text:00401161 add esp, 4 .text:00401164 xor ecx, ecx .text:00401166 cmp [ebp+var_C], 421FBC00h .text:0040116D setnle cl .text:00401170 mov [ebp+var_40], ecx .text:00401173 xor edx, edx .text:00401175 cmp [ebp+var_C], 42210D7Fh .text:0040117C setl dl .text:0040117F mov [ebp+var_4], edx
ローカル変数varCのアドレスを引数に、call sub_40149E が呼ばれています。その後、
varC > 0x421FBC00 のとき ecx に1がセット、結果をローカル変数var40に格納
varC < 0x42210D7F のとき edx に1がセット、結果をローカル変数var4に格納
という処理になっています。それらの変数がどのような使われ方をしてるかを調べると、
0x421FBC00 < varC < 0x42210D7F を満たさなければならないことがわかりました。
なので、動的解析ならてきとーに(ry
call sub_40149E の内部処理も簡単に解析したところ協定世界時UTCを元に計算した値でした。
調べてみると条件を満たす日時は、
2005/2/26 9:00:01 - 2005/2/27 8:59:58 までです。
サマータイムのときは時差修正処理も入ってました。
マルウェア的解釈なら、この時刻にしか発症しないということですね><
解析を続けたいところですが、
実はvarCの条件を満たせば、あとは普通に動かすだけでこの問題の解答が得られます。
が、内部処理を追っていきます。
.text:00401182 mov [ebp+var_12], 0 .text:00401186 mov [ebp+var_13], 14h .text:0040118A mov [ebp+var_18], 43h .text:0040118E mov [ebp+var_15], 13h .text:00401192 mov [ebp+var_16], 9 .text:00401196 mov [ebp+var_14], 13h .text:0040119A mov [ebp+var_17], 55h .***delete*** .text:004011BD mov [ebp+var_2D], 48h .text:004011C1 mov [ebp+var_35], 56h .text:004011C5 mov [ebp+var_37], 52h .text:004011C9 mov [ebp+var_29], 0Bh .text:004011CD mov [ebp+var_34], 1Ch .text:004011D1 mov [ebp+var_2B], 4Fh .text:004011D5 mov [ebp+var_2C], 55h .text:004011D9 mov [ebp+var_33], 9 .text:004011DD mov [ebp+var_32], 9 .text:004011E1 mov [ebp+var_38], 4Eh .text:004011E5 mov [ebp+var_31], 40h .text:004011E9 mov [ebp+var_30], 49h .text:004011ED mov [ebp+var_2F], 54h .text:004011F1 mov [ebp+var_2E], 43h .text:004011F5 mov [ebp+var_36], 52h .text:004011F9 mov [ebp+var_2A], 45h .text:004011FD mov [ebp+var_3C], offset sub_401130 .***delete*** .text:00401233 cmp [ebp+var_40], 0 .text:00401237 jz loc_401314 .***delete*** .text:00401247 mov [ebp+var_25], 49h .text:0040124B mov [ebp+var_28], 56h .text:0040124F mov [ebp+var_1D], 54h .text:00401253 mov [ebp+var_1C], 45h .text:00401257 mov [ebp+var_23], 8 .text:0040125B mov [ebp+var_26], 49h .text:0040125F mov [ebp+var_24], 40h .text:00401263 mov [ebp+var_21], 49h .text:00401267 mov [ebp+var_22], 45h .text:0040126B mov [ebp+var_20], 4Bh .text:0040126F mov [ebp+var_1F], 9 .text:00401273 mov [ebp+var_1E], 47h .text:00401277 mov [ebp+var_19], 50h .text:0040127B mov [ebp+var_1B], 4Eh .text:0040127F mov [ebp+var_1A], 4Fh .text:00401283 mov [ebp+var_27], 54h .text:00401287 lea ecx, [ebp+var_38] .text:0040128A mov [ebp+var_44], ecx .text:0040128D .text:0040128D loc_40128D: ; CODE XREF: sub_401130+182j .text:0040128D mov edx, [ebp+var_44] ; データ列の長さを調べるループ .text:00401290 movsx eax, byte ptr [edx] .text:00401293 mov ecx, [ebp+var_44] .text:00401296 add ecx, 1 .text:00401299 mov [ebp+var_44], ecx .text:0040129C test eax, eax .text:0040129E jz short loc_4012B4 .text:004012A0 mov dx, word_409714 .text:004012A7 add dx, 1 .text:004012AB mov word_409714, dx ; 結果的に、ここには文字数が格納される .text:004012B2 jmp short loc_40128D .text:004012B4 ; --------------------------------------------------------------------------- .text:004012B4 .text:004012B4 loc_4012B4: ; CODE XREF: sub_401130+16Ej .text:004012B4 cmp [ebp+var_4], 0 .text:004012B8 jz short loc_401314 .***delete*** .text:004012D7 lea ecx, [ebp+var_38] .text:004012DA push ecx ; エンコードされたデータの先頭アドレス .text:004012DB call sub_401070 ; xor_decode .text:004012E0 add esp, 4 .***delete*** .text:00401302 push offset unk_409610 ; char .text:00401307 push offset aS ; "%s" .text:0040130C call sub_4010C0 ; wvsprintfA .text:00401311 add esp, 8 .text:00401314 .text:00401314 loc_401314: ; CODE XREF: sub_401130+107j .text:00401314 ; sub_401130+111j ... .text:00401314 xor eax, eax .***delete*** .text:00401319 mov esp, ebp .text:0040131B pop ebp .text:0040131C retn
.text:004012B4 , .text:00401233 の部分で、
先ほど計算した日時が条件を満たしてるか判定をしています。
それと配列に大量のデータが格納されてるのがわかりますが、順序がばらばらです。
整列させると以下のようなデータ列になります。
4e 52 52 56 1c 09 09 40 49 54 43 48 55 4f 45 0b |NRRV...@ITCHUOE.| 56 54 49 49 40 08 45 49 4b 09 47 54 45 4e 4f 50 |VTII@.EIK.GTENOP| 43 55 09 13 13 14 00 |CU....|
call sub_401070 では、これらのデータ列に対して、文字数(38)でXOR処理を行っています。
XOR処理された文字列のデータがこの問題の解答になります。
answer : http://forensic-proof.com/archives/552
参考URL:
NetAgent Official Blog : TLS Callbacks
An Anti-Reverse Engineering Guide - CodeProject
GetSystemTimeが返す値が適切なら良いので、APIフックでもおもしろいはず。
後でやる予定
参考(予定)URL:
初めてのAPIフック - yasulib memo
簡単に逆コンパイルした協定世界時UTCを元に計算するコードと、
XORデコードのコードを以下に置いておきます。
XOR decode
void xor_decode(void){ char code[] = { 0x4E, 0x52, 0x52, 0x56, 0x1C, 0x09, 0x09, 0x40, 0x49, 0x54, 0x43, 0x48, 0x55, 0x4F, 0x45, 0x0B, 0x56, 0x54, 0x49, 0x49, 0x40, 0x08, 0x45, 0x49, 0x4B, 0x09, 0x47, 0x54, 0x45, 0x4E, 0x4F, 0x50, 0x43, 0x55, 0x09, 0x13, 0x13, 0x14, 0x00 }; char buf[128]; char *cpin = code; char *cpout = buf; int len = strlen(code); while( *cpin!='\0' ){ *cpout++ = *cpin++ ^ (char)len; } *cpout = '\0'; puts(buf); }
#include <cstdio> #include <iostream> #include <Windows.h> using namespace std; int sub_401AD9(int,int,int,int,int,int,int); void sub_40157A(); const int month_num[] = {0xFFFFFFFF, 0x1E, 0x3A, 0x59, 0x77, 0x96, 0xB4, 0xD3, 0xF2, 0x10, 0x2F, 0x4D}; int gl_tz_state; int gl_st[1000]; int utc = 0x7080; int summer_time = 0xFFFFF1F0; int gl_tzi[1000]; TIME_ZONE_INFORMATION *lptzi = (TIME_ZONE_INFORMATION*)gl_tzi; int sub_401AD9(int year, int month, int day, int hour, int minutes, int second, int state){ int eax,ecx,edx,esi; int t_year = year - 0x76C; //ebx if( 0x46 > t_year || 0x8A < t_year ){ return -1; } esi = month_num[month-1] + day; if( !(t_year&0x03) && month>2 ){ //だめなうるう年処理? esi++; } sub_40157A(); eax = t_year * 0x16D; //0x16D = 365 ecx = (t_year-1)>>2; //sar ecx,2 だけど、上位ビットは0だから単純な右シフトと同じ edx = esi; edx += ecx; eax += edx; ecx = 3*eax; ecx = hour+ecx*8; ecx *= 0x3C; ecx += minutes; ecx *= 0x3C; ecx += utc + second + 0x7C558180; if( state==1 ){ ecx += summer_time; } eax = ecx; return eax; } void sub_40157A(){ int tmp; int eax; tmp = GetTimeZoneInformation(lptzi); if( tmp==-1 ){ return; } eax = lptzi->Bias * 0x3C; utc = eax; if( lptzi->StandardDate.wMonth==0 ){ goto loc_401606; } utc = lptzi->StandardBias * 0x3C + eax; loc_401606: if( lptzi->DaylightDate.wMonth==0 ){ summer_time = 0; return; } eax = lptzi->DaylightBias; if( eax==0 ){ summer_time = 0; return; } eax -= lptzi->StandardBias; summer_time = eax * 0x3C; return ; } // sub_40149E int main(){ int tmp, tz_state; SYSTEMTIME st; TIME_ZONE_INFORMATION tzi; GetSystemTime(&st); tmp = GetTimeZoneInformation(&tzi); if( tmp==-1 ){ return 0; } else if( tmp==2 ){ tz_state = (int)(tzi.DaylightBias!=0 && tzi.DaylightDate.wMonth!=0); } else{ tz_state = 0; } memcpy((void*)gl_st, (void*)&st, sizeof(SYSTEMTIME)); gl_tz_state = tz_state; int ret = sub_401AD9(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, tz_state); printf("%x(%d)\n",ret,ret); return 0; }