jz_crackme 풀이
집에와서 jz님이 만드신 크랙미를 살펴보았습니다.
제 언팩미 1탄을 상세하게 풀어주셔서 고마운 마음에 답례(?)를 하기 위해서 열심히 IDA를 돌렸지요. ^^
순전히 저의 추측으로만 분석을 한것이어서 정답이 아닐수도 있어서 염려가 됩니다.
안티 디버깅은 2가지 정도 찾았는데 못찾은것이 더 있을 가능성도 있습니다.
안티디버깅 이후에 비로소 사용자 키값을 처리하는 루틴과 키값을 암호화후 비교하는 루틴이 있습니다.
살펴볼 루틴은 다음과 같습니다.
- Explorer.exe의 프로세스와 jz_crackme.exe프로세스내 ntdll.dll의 코드섹션을 비교하여서 틀린 부분이 있으면 안티디버깅 루틴 수행
- Trap Flag를 설정후 SEH핸들러가 실행되는지 확인하여 안티디버깅 루틴 수행
- 시간측정 루틴(입력시간 초과 방지용)
- 입력된 패스워드를 암호화하는 함수
안티 디버깅 루틴은 프로그램을 종료시키는 단순한 방식이 아닌, 정답이 제대로 안나오게 방해하는 역활을
수행하는것 같습니다. 덕분에 badfood를 밤새 많이 먹었네요 ;;
일단 안티디버깅 루틴부터 살펴보겠습니다.
1. explorer::ntdll.dll과 jz_crackme::ntdll.dll의 코드섹션 비교하는 안티디버깅 분석
explorer.exe PID검색 루틴
.text:004010E7 loc_4010E7: ; CODE XREF: _main+CEj
.text:004010E7 lea edx, [ebp+dwProcessId]
.text:004010ED push edx
.text:004010EE push offset aExplorer_exe ; "explorer.exe"
.text:004010F3 call maybe_GetPidByProcessName
.text:004010F8 add esp, 8
.text:004010FB test eax, eax
.text:004010FD jnz short loc_401104
.text:004010FF jmp loc_4011D1
maybe_GetPidByProcessName 내부를 살펴보면 TlHelp32 함수를 이용하여 전달된 함수명으로 Pid를 구하고 있습니다.
explorer.exe의 ntdll 코드섹션을 읽어 오는 루틴
.text:00401104 loc_401104: ; CODE XREF: _main+FDj
.text:00401104 mov eax, [ebp+Size]
.text:0040110A push eax ; dwSize
.text:0040110B mov ecx, [ebp+lpMem]
.text:00401111 push ecx ; lpBuffer
.text:00401112 mov edx, [ebp+maybe_Ntdll_CodeSectionAddress]
.text:00401118 push edx ; lpAddress
.text:00401119 mov eax, [ebp+dwProcessId]
.text:0040111F push eax ; dwProcessId
.text:00401120 call maybe_ReadMemoryFromPid
.text:00401125 add esp, 10h
.text:00401128 test eax, eax
.text:0040112A jnz short loc_401131
.text:0040112C jmp loc_4011D1
maybe_ReadMemoryFromPid 의 내부는 전달된 Pid를 OpenProcess하여 메모리를 읽어서 리턴하는 함수입니다.
for문을 돌면서 explorer::ntdll의 코드섹션과 jz_crackme.exe::ntdll의 코드섹션과 비교하는 루틴
.text:00401154 loc_401154: ; CODE XREF: _main+150j
.text:00401154 mov [ebp+maybe_for_i_value], 0
.text:0040115E jmp short loc_40116F
.text:00401160 ; ---------------------------------------------------------------------------
.text:00401160
.text:00401160 loc_401160: ; CODE XREF: _main:loc_4011CFj
.text:00401160 mov ecx, [ebp+maybe_for_i_value]
.text:00401166 add ecx, 1
.text:00401169 mov [ebp+maybe_for_i_value], ecx
.text:0040116F
.text:0040116F loc_40116F: ; CODE XREF: _main+15Ej
.text:0040116F mov edx, [ebp+maybe_for_i_value]
.text:00401175 cmp edx, [ebp+Size]
.text:0040117B jge short loc_4011D1
.text:0040117D mov eax, [ebp+lpMem]
.text:00401183 add eax, [ebp+maybe_for_i_value]
.text:00401189 xor ecx, ecx
.text:0040118B mov cl, [eax]
.text:0040118D mov edx, [ebp+var_524]
.text:00401193 add edx, [ebp+maybe_for_i_value]
.text:00401199 xor eax, eax
.text:0040119B mov al, [edx]
.text:0040119D cmp ecx, eax
.text:0040119F jz short loc_4011CF
.text:004011A1 mov ecx, maybe_DebuggerFuckRandomValue
.text:004011A7 add ecx, 900Dh
.text:004011AD mov maybe_DebuggerFuckRandomValue, ecx
.text:004011B3 mov edx, [ebp+var_524]
.text:004011B9 add edx, [ebp+maybe_for_i_value]
.text:004011BF mov eax, [ebp+lpMem]
.text:004011C5 add eax, [ebp+maybe_for_i_value]
.text:004011CB mov cl, [eax]
.text:004011CD mov [edx], cl
.text:004011CF
.text:004011CF loc_4011CF: ; CODE XREF: _main+19Fj
.text:004011CF jmp short loc_401160
maybe_DebuggerFuckRandomValue는 틀린 부분이 발견될때마다 900dh 값을 더해주는 것을 볼 수 있습니다.
maybe_DebuggerFuckRandomValue는 정답비교시 방해하는 역활을 하는 값으로 0 값을 유지해야만 합니다.
Trap Flag를 설정후 SEH핸들러가 실행되는지 확인하여 안티디버깅 루틴 수행
.text:00401203 loc_401203: ; CODE XREF: _main+1F2j
.text:00401203 push offset seh_DebuggerNotFoundContinueExecution ; 디버거 체크
.text:00401208 push large dword ptr fs:0
.text:0040120F mov large fs:0, esp
.text:00401216 pushf
.text:00401217 push edx
.text:00401218 or [esp+584h+var_580], 100h ; eflag pushf후 trap 플래그를 설정하여 디버거 탐지
.text:00401220 push ebx
.text:00401221 mov ebx, [esp+588h+var_580]
.text:00401225 push ebx
.text:00401226 popf
.text:00401227 inc edx ; 예외 발생하고 준비된 핸들러가 실행되어야만 함
.text:00401227 ; 디버거가 캐치하여 실행하면 badfood -_-
.text:00401228 inc edx
.text:00401229 pop ebx
.text:0040122A pop edx
.text:0040122B add esp, 4
.text:0040122E pop large dword ptr fs:0
.text:00401235 add esp, 4
.text:00401238 inc maybe_IsDebuggerFound
.text:0040123E rdtsc
.text:00401240 add maybe_DebuggerFuckRandomValue, eax
.text:00401246 jmp short loc_401268
.text:00401248 ; ---------------------------------------------------------------------------
.text:00401248
.text:00401248 seh_DebuggerNotFoundContinueExecution: ; DATA XREF: _main:loc_401203o
.text:00401248 mov edx, [esp-4+envp]
.text:0040124C mov dword ptr [edx+0B8h], offset seh_RunNextFunction
.text:00401256 xor eax, eax
.text:00401258 retn
.text:00401259 ; ---------------------------------------------------------------------------
.text:00401259
.text:00401259 seh_RunNextFunction: ; DATA XREF: _main+24Co
.text:00401259 pop ebx
.text:0040125A pop edx
.text:0040125B add esp, 4
.text:0040125E pop large dword ptr fs:0
.text:00401265 add esp, 4
seh_DebuggerNotFoundContinueExecution() 예외함수를 SEH Handler로 등록시키고 eflags값의 trap 비트를 설정하여 예외가 발생하게 하고 있습니다.
디버거가 예외를 무시하게 되면 maybe_IsDebuggerFound와 maybe_DebuggerFuckRandomValue값이 설정되고 결국에는 badfood를 맛보게 될 것이지요.
정상적으로 실행이 되면 예외핸들러를 타게 되고 사용자 암호를 물어보는 메인 코드가 실행되게 됩니다
입력된 패스워드를 암호화하는 함수
정답 확인하는 루틴
.text:00401354 loc_401354: ; CODE XREF: _main+31Aj
.text:00401354 mov ecx, maybe_DebuggerFuckRandomValue
.text:0040135A mov maybe_1st_Copied_DebuggerFuckRandomValue, ecx
.text:00401360 mov edx, [ebp+maybe_UserInputedPasswordStrLen]
.text:00401366 push edx
.text:00401367 lea eax, [ebp+maybe_UserInputedPasswordBuffer]
.text:0040136D push eax
.text:0040136E call maybe_Encryption
.text:00401373 add esp, 8
.text:00401376 mov [ebp+maybe_EncryptedResultValue], eax
.text:0040137C mov ecx, maybe_1st_Copied_DebuggerFuckRandomValue
.text:00401382 mov maybe_2nd_Copied_DebuggerFuckRandomValue, ecx
.text:00401388 mov edx, [ebp+maybe_EncryptedResultValue]
.text:0040138E xor edx, maybe_IsDebuggerFound
.text:00401394 mov [ebp+maybe_EncryptedResultValue], edx
.text:0040139A mov eax, maybe_2nd_Copied_DebuggerFuckRandomValue
.text:0040139F mov maybe_3rd_Copied_DebuggerFuckRandomValue, eax
.text:004013A4 mov ecx, [ebp+maybe_EncryptedResultValue]
.text:004013AA xor ecx, maybe_3rd_Copied_DebuggerFuckRandomValue
.text:004013B0 mov [ebp+maybe_EncryptedResultValue], ecx
.text:004013B6 cmp [ebp+maybe_EncryptedResultValue], 0AF006DC3h
.text:004013C0 jz short loc_4013D1
.text:004013C2 push offset aWrong ; "wrong!\n"
.text:004013C7 call maybe_printf
.text:004013CC add esp, 4
.text:004013CF jmp short loc_4013E5
.text:004013D1 ; ---------------------------------------------------------------------------
.text:004013D1
.text:004013D1 loc_4013D1: ; CODE XREF: _main+3C0j
.text:004013D1 lea edx, [ebp+maybe_UserInputedPasswordBuffer]
.text:004013D7 push edx
.text:004013D8 push offset aDFS@gmail_comNF ; "정답! %s@gmail.com으로 메일을 보내서 인"...
.text:004013DD call maybe_printf
.text:004013E2 add esp, 8
.text:004013E5
.text:004013E5 loc_4013E5: ; CODE XREF: _main+3CFj
.text:004013E5 push offset aPause ; "pause"
.text:004013EA call sub_401DB6
maybe_Encryption 함수가 사용자가 입력한 내용과 길이를 가지고 어떤값을 만들고 리턴해주는 함수입니다.
maybe_Encryption함수의 리턴값을 가지고 maybe_DebuggerFuckRandomValue와 xor 연산을 2번 시켜주고 있습니다.
리턴값은 결국 0AF006DC3h 값이 되어야 함을 004013B6 주소에 알 수가 있습니다.
그러면 maybe_Encryption함수의 내부를 살펴보겠습니다.
maybe_Encryption() 함수의 내부
maybe_Encryption을 처음보고 해쉬값의 원래 데이터를 찾는게 아닌가 하고 좌절했습니다만, 곧 차근 차근 따라 가보니 힌트가 나오는것을 알 수 있었습니다.
일단 분석을 방해하는 코드를 빨간색으로 색칠후 따라가보겠습니다.
.text:00401420 maybe_Encryption proc near ; CODE XREF: _main+36Ep
.text:00401420
.text:00401420 var_8 = byte ptr -8
.text:00401420 var_4 = dword ptr -4
.text:00401420 InputBuffer = dword ptr 8
.text:00401420 InputLen = dword ptr 0Ch
.text:00401420
.text:00401420 push ebp
.text:00401421 mov ebp, esp
.text:00401423 sub esp, 8
.text:00401426 push ebx
.text:00401427 push esi
.text:00401428 push edi
.text:00401429 mov [ebp+var_4], 0FEBAF001h
.text:00401430 mov eax, [ebp+InputBuffer]
.text:00401433 movsx ecx, byte ptr [eax]
.text:00401436 cmp ecx, 74h
.text:00401439 jz short loc_40144E
.text:0040143B mov edx, [ebp+InputBuffer]
.text:0040143E movsx eax, byte ptr [edx+2]
.text:00401442 mov ecx, [ebp+var_4]
.text:00401445 xor ecx, eax
.text:00401447 mov [ebp+var_4], ecx
.text:0040144A rol [ebp+var_4], 4
.text:0040144E
.text:0040144E loc_40144E: ; CODE XREF: maybe_Encryption+19j
.text:0040144E rol [ebp+var_4], 8
.text:00401452 cmp [ebp+InputLen], 7
.text:00401456 jz short loc_401467
.text:00401458 mov edx, [ebp+InputBuffer]
.text:0040145B movsx eax, byte ptr [edx+5]
.text:0040145F mov ecx, [ebp+var_4]
.text:00401462 xor ecx, eax
.text:00401464 mov [ebp+var_4], ecx
00401436 를 보시면 InputBuffer는 00000074('t')로 시작되는 것을 알 수 있습니다. 또 00401452에서는 문자열 길이가 7이어야 된다는 것을 알 수 있습니다.
그러면 InputBuffer는 t _ _ _ _ _ _ 이 되겠군요. var_4 값은 FEBAF001 로 설정되고요
.text:00401467 loc_401467: ; CODE XREF: maybe_Encryption+36j
.text:00401467 rol [ebp+var_4], 4
.text:0040146B mov edx, [ebp+InputBuffer]
.text:0040146E movsx eax, byte ptr [edx]
.text:00401471 mov ecx, [ebp+InputBuffer]
.text:00401474 movsx edx, byte ptr [ecx+4]
.text:00401478 sub eax, edx
.text:0040147A cmp eax, 2
.text:0040147D jz short loc_401490
.text:0040147F mov eax, [ebp+InputBuffer]
.text:00401482 movsx ecx, byte ptr [eax+6]
.text:00401486 mov edx, [ebp+var_4]
.text:00401489 xor edx, ecx
.text:0040148B mov [ebp+var_4], edx
.text:0040148E jmp short loc_40149F
.text:00401490 ; ---------------------------------------------------------------------------
.text:00401490
.text:00401490 loc_401490: ; CODE XREF: maybe_Encryption+5Dj
.text:00401490 mov eax, [ebp+InputBuffer]
.text:00401493 movsx ecx, byte ptr [eax+4]
.text:00401497 mov edx, [ebp+var_4]
.text:0040149A xor edx, ecx
.text:0040149C mov [ebp+var_4], edx
.text:0040149F
.text:0040149F loc_40149F: ; CODE XREF: maybe_Encryption+6Ej
.text:0040149F mov eax, [ebp+InputBuffer]
.text:004014A2 movsx ecx, byte ptr [eax+4]
.text:004014A6 mov edx, [ebp+InputBuffer]
.text:004014A9 movsx eax, byte ptr [edx+1]
.text:004014AD sub ecx, eax
.text:004014AF cmp ecx, 0Ah
.text:004014B2 jz short loc_4014BA
.text:004014B4 rol [ebp+var_4], 10h
.text:004014B8 jmp short loc_4014CD
00401467 ~ 0040147D 루틴을 분석해 보면,
var_4 = BAF001FE 가 되고, InputBuffer[0]와 InputBuffer[4]의 차가 2가 된다는 것을 알 수 있습니다.
그러므로 InputBuffer는 t _ _ _ r _ _ 가 됩니다.
00401490 ~ 004014B2 를 해석 하면,
var_4 = AF001F99 가 되고, InputBuffer[4]와 InputBuffer[1]의 차가 0ah가 된다는 것을 알 수 있습니다.
그러므로 InputBuffer t h _ _ r _ _ 가 됩니다.
.text:004014BA loc_4014BA: ; CODE XREF: maybe_Encryption+92j
.text:004014BA mov ecx, [ebp+InputBuffer]
.text:004014BD movsx edx, byte ptr [ecx+2]
.text:004014C1 mov eax, [ebp+var_4]
.text:004014C4 xor eax, edx
.text:004014C6 mov [ebp+var_4], eax
.text:004014C9 rol [ebp+var_4], 8
.text:004014CD
.text:004014CD loc_4014CD: ; CODE XREF: maybe_Encryption+98j
.text:004014CD mov ecx, [ebp+InputBuffer]
.text:004014D0 movsx edx, byte ptr [ecx+1]
.text:004014D4 mov eax, [ebp+InputBuffer]
.text:004014D7 movsx ecx, byte ptr [eax+2]
.text:004014DB sub edx, ecx
.text:004014DD cmp edx, 35h
.text:004014E0 jz short loc_4014F3
.text:004014E2 mov edx, [ebp+InputBuffer]
.text:004014E5 movsx eax, byte ptr [edx+5]
.text:004014E9 mov ecx, [ebp+var_4]
.text:004014EC xor ecx, eax
.text:004014EE mov [ebp+var_4], ecx
.text:004014F1 jmp short loc_4014F7
004014BA ~ 004014E0 을 분석해 보면,
var_4 = AF001F99 ^ arg0[2] 가 되고, InputBuffer[1]과 InputBuffer[2]의 차가 35h 가 되며 이는 곳 숫자 '3' 인것을 알 수 있습니다.
그러므로 InputBuffer 는 t h 3 _ r _ _ 가 됩니다.
이제 마지막 남은 루틴을 분석할 차례입니다. 빨간 부분이 얼마 남아있있지 않군요 ㅎㅎ
.text:004014F3 loc_4014F3: ; CODE XREF: maybe_Encryption+C0j
.text:004014F3 rol [ebp+var_4], 10h
.text:004014F7
.text:004014F7 loc_4014F7: ; CODE XREF: maybe_Encryption+D1j
.text:004014F7 mov edx, [ebp+InputBuffer]
.text:004014FA movsx eax, byte ptr [edx]
.text:004014FD mov ecx, [ebp+InputBuffer]
.text:00401500 movsx edx, byte ptr [ecx+3]
.text:00401504 sub eax, edx
.text:00401506 mov [ebp+var_8], al
.text:00401509 movsx eax, [ebp+var_8]
.text:0040150D mov ecx, [ebp+var_4]
.text:00401510 xor ecx, eax ; var4 = AAAF001F
.text:00401510 ; var8 = 't' - arg0[3]
.text:00401512 mov [ebp+var_4], ecx
.text:00401515 mov edx, [ebp+InputBuffer]
.text:00401518 movsx eax, byte ptr [edx+4]
.text:0040151C mov ecx, [ebp+var_4]
.text:0040151F xor ecx, eax
.text:00401521 mov [ebp+var_4], ecx
.text:00401524 rol [ebp+var_4], 8
.text:00401528 mov edx, [ebp+InputBuffer]
.text:0040152B movsx eax, byte ptr [edx+5]
.text:0040152F cmp eax, 69h
.text:00401532 jz short loc_40153B
.text:00401534 mov [ebp+var_4], 0F001F001h
.text:0040153B
.text:0040153B loc_40153B: ; CODE XREF: maybe_Encryption+112j
.text:0040153B mov ecx, [ebp+InputBuffer]
.text:0040153E movsx edx, byte ptr [ecx+5]
.text:00401542 mov eax, [ebp+var_4]
.text:00401545 xor eax, edx
.text:00401547 mov [ebp+var_4], eax
.text:0040154A mov ecx, [ebp+InputBuffer]
.text:0040154D movsx edx, byte ptr [ecx+6]
.text:00401551 cmp edx, 6Eh
.text:00401554 jz short loc_40155D
.text:00401556 mov [ebp+var_4], 0BADDF00Dh
.text:0040155D
.text:0040155D loc_40155D: ; CODE XREF: maybe_Encryption+134j
.text:0040155D mov eax, [ebp+var_4]
.text:00401560 pop edi
.text:00401561 pop esi
.text:00401562 pop ebx
.text:00401563 mov esp, ebp
.text:00401565 pop ebp
.text:00401566 retn
.text:00401566 maybe_Encryption endp
004014F3 ~ 00401524 까지는 InputBuffer[3]값과 InputBuffer[4]값을 가지고 var_4를 변화시킵니다.
여기서 부터는 var_4값을 따라가는 것을 포기했습니다. InputBuffer[3]값을 알 수 없었기 때문에 머리가 안돌아가 더라구요;;
0040152B ~ 00401532 을 보시면, InputBuffer[5]값이 'i'(69h)와 같은 지 비교하는 것을 알 수 있기 떄문에,
InputBuffer는 t h 3 _ r i _ 가 됩니다.
0040154A ~ 00401554를 해석해 보면, InputBuffer[6]이 'n'(6eh)가 되는 것을 알 수 있습니다.
그러므로 InputBuffer는 t h 3 _ r i n 이 됩니다.
함수의 끝은 현재까지 변화된 var_4를 리턴하게 됩니다. 그러나 저는 var_4를 분석을 중간에 포기했기때문에 th3_rin 값만 알 수 있었습니다.
여기서 선택할 수 있습니다. bruteforce 입력을 하여서 남은 1자리의 문자를 맞출 수 있기도 합니다. 실제로 성질 급한 저는 그렇게도 해보았습니다.
그러나 'wrong'만 나오더라구요. 나중에 알고 보니 안티디버깅 루틴중 ntdll의 code section비교 루틴이 있는데, 제 컴퓨터에서는 디버거가
없어도 코드 섹션 비교가 동일하지 않게 나오는 지 정상적인 상태에서도 답을 알아 낼 수 가 없었습니다. OTL
그래서 엄청나게 많은 badfood를 맛보았습니다.
결국 올리를 붙여서 값을 추적할 수 밖에 없었습니다.
앞서 설명드린 내용중에 다음과 같이 Encryption함수가 리턴하는 값을 비교하는 루틴을 아래와 같이 보여 드렸습니다.
.text:00401360 mov edx, [ebp+maybe_UserInputedPasswordStrLen]
.text:00401366 push edx
.text:00401367 lea eax, [ebp+maybe_UserInputedPasswordBuffer]
.text:0040136D push eax
.text:0040136E call maybe_Encryption
.text:00401373 add esp, 8
.text:00401376 mov [ebp+maybe_EncryptedResultValue], eax
... 생략 ...
.text:004013B6 cmp [ebp+maybe_EncryptedResultValue], 0AF006DC3h
.text:004013C0 jz short loc_4013D1
... 생략 ...
.text:004013D8 push offset aDFS@gmail_comNF ; "정답! %s@gmail.com으로 메일을 보내서 인"...
.text:004013DD call maybe_printf
.text:004013E2 add esp, 8
여기서 00401373에 브레이크포인트를 걸고 'th3_rin'값을 입력하였을때 결과를 확인하여 보았습니다.
AF0078C3 값이 리턴됩니다. 이번에는 'th3arin'값을 입력하여 보았습니다. AF007EC3 값이 나옵니다. '_'문자와 'a'문자를
다르게 입력하여도 AF00??C3h 값이 유지되는것을 볼 수 있었습니다. 여기서 위에서 포기한 var_4 변경 루틴을
조금더 살펴 보겠습니다.
살펴볼 부분은 다음과 같습니다.
.text:004014F7 loc_4014F7: ; CODE XREF: maybe_Encryption+D1j
.text:004014F7 mov edx, [ebp+InputBuffer]
.text:004014FA movsx eax, byte ptr [edx]
.text:004014FD mov ecx, [ebp+InputBuffer]
.text:00401500 movsx edx, byte ptr [ecx+3]
.text:00401504 sub eax, edx
.text:00401506 mov [ebp+var_8], al
.text:00401509 movsx eax, [ebp+var_8]
.text:0040150D mov ecx, [ebp+var_4]
.text:00401510 xor ecx, eax
.text:00401512 mov [ebp+var_4], ecx
.text:00401515 mov edx, [ebp+InputBuffer]
.text:00401518 movsx eax, byte ptr [edx+4]
.text:0040151C mov ecx, [ebp+var_4]
.text:0040151F xor ecx, eax
.text:00401521 mov [ebp+var_4], ecx
.text:00401524 rol [ebp+var_4], 8
.text:00401528 mov edx, [ebp+InputBuffer]
.text:0040152B movsx eax, byte ptr [edx+5]
.text:0040152F cmp eax, 69h
.text:00401532 jz short loc_40153B
입력값을 'th3_rin'을 입력하고 0040151F에 브레이크포인트를 걸고 eax값과 var_4를 확인해 보았습니다.
eax = 00000072 ('r') InputBuffer[4] 문자
ecx = AAAF000A
이 되며, var4에는 eax xor ecx한 값 AAAF0078이 들어가게 됩니다. 그리고 이 값을 8bit 왼쪽으로 로테이트 쉬프트 시키면
AF0078AA값 됩니다. AA값은 maybe_Encryption함수가 리턴할 즈음에 C3값으로 바뀝니다. 그래서 리턴되는 값은
AF0078C3 이 됩니다. AF0078C3 값은 AF00??C3 패턴과 유사합니다. 즉 InputBuffer[3]값에 따라서 ??가 정해지고, ??값은
6d가 되어야 비교문(0AF006DC3h와 비교)을 통과할 수 있는 것입니다.
다시 0040151F 을 살펴보겠습니다. xor ecx, eax('r')=00000072 을 했을 때, 결과값이 6d가 되는 값을 찾으면 되겠습니다.
?? = 0001 1111 -> 1f
xor) 72 = 0111 0010 -> 72
----------------------------
6d = 0110 1101
이렇게 계산 해보니, 1f 가 나옵니다. 그러면 ecx값은 AAAF000A(th3_rin 입력시의 값)이 아닌 AAAF001F(올바른 값입력시)이 되어야만
한다는 것을 알 수 있습니다. 그러면 과연 InputBuffer[3]에 어떤값을 넣어야 var4의 값이 AAAF001F값이 나올까요..?
답을 찾기 위해 이번에는 조금 더 윗부분을 조사해 보겠습니다.
00401510 을 보시면 var_4 값을 가지고 eax값이 xor연산을 하는 부분이 있습니다. 이번에는 여기에 브레이크포인트를 걸고 살펴 보겠습니다.
마찬가지로 입력값은 'th3_rin' 입니다.
eax = 00000015
ecx = AAAF001F
여기서 xor연산을 하게 되면 ecx는 AAAF000A로 바뀌게 됩니다. 그러나 위에서 살펴보았듯이 우리가 원하는 값은 AAAF001F가 되어야만
합니다. AAAF001F = AAAF001F xor eax 의 연산에서 eax가 0이 되면 자기 자신값 AAAF001F이 나올수 있습니다.
그렇다면 이번에는 eax값을 0이 되도록 하는 부분을 살펴보겠습니다.
00401504 을 보시면 eax(InputBuffer[3])에서 edx(InputBuffer[0] = 74('t'))을 빼주는 것을 볼 수 있습니다. 여기서 eax가 0이 되려면
edx와 동일한 값 74('t')이면 되겠습니다. 즉 InputBuffer[3]은 74('t')라는 것을 알 수 있겠습니다.
종합해보면 InputBuffer는 't h 3 t r i n'이 되어야 maybe_Encryption함수가 올바른 리턴값 0AF006DC3h을 리턴하는 것을 알 수 있습니다.
최종적으로 확인해 보겠습니다.
이제 제대로 맞게 풀었는지 여쭤봐야겠습니다.
History
Last edited on 05/01/2009 15:10 by seyool
Comments (0)