ichirin2501's diary

いっちりーん。

Codegate CTF 2011 Binary 200

久しぶりの解析。
解析時には、IDA Pro 6.0 demo, OllyDbg 2.01 alpha3
を使って解析作業を行いました。プラグインは使用してません。
Freeで良いのあったら是非教えてください。




(実行ファイルのリンクとか、あったほうがいいのかな?)
とにかく動的解析する><
プラグインもなければ、何も設定されてないOllyDbgやImmunity Debuggerで
動的解析しようとすると、プログラムが既に終了していると思います。
これはアンチデバッグ処理のために、TLS-Callbacks が使用されているからです。

TLS Callbacks とは、
TLS(Thread Local Storage)と呼ばれるスレッド毎に固有な記憶領域を利用した場合に、
スレッド起動時とスレッド終了時に呼び出されるコールバック関数

http://www.netagent-blog.jp/archives/51735398.html

らしいです。
それでは、解析するために設定を変更します。
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);
}


協定世界時UTCを元に計算するコード sub_40149E

#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;
}