Mafia의 진실
Mafia의 진실 2010.02.10

낚시성 제목에 방문하신 분께는 죄송합니다. 이 글은 오래전부터 twitter 사용자들을 대상으로 퍼져나가고 있는 'Mobster world'라는 온라인 MMORPG 게임과 관련된 글입니다. 트위터 사용자 분들 중에는 위와 유..

iPhoto 슬라이드 쇼

트위터에 질문이 하나 올라와 짧게 작성해봤습니다. iPhoto에서 슬라이드 쇼 생성하는 방법입니다. 1. 먼저 사진 메뉴에서 슬라이드 쇼에 추가할 사진을 선택합니다. 사진 선택은 Command 키와 마우스를 이용합니다. 2...

노키아에서 동작하는 Mac OS X 10.3

핀란드에 사는 Toni Nikkanen이라는 분이 자신의 Nokia N900 모델에 Mac OS 10.3 Panther를 설치하고 실행시키는데 성공했다고 합니다. Toni의 블로그에 가보니 Mac OS X 말고도 Windows..

FSC2007 문제 풀이 두번째 글입니다. 첫번째 글은 여기를 참고해 주세요.

(딴지방지 : 빠르게 문제를 풀어야 한다는 목적에 충실한 더 좋은 솔루션이 있음을 저도
 압니다. 이 글은 그냥 많이 배워보자는 목적에서 작성되었습니다. ^^)
 
준비된 파일을 IDA로 열어 엔트리 포인트 부분을 살펴보았습니다. 군데군데 가비지 코드와 코드 퍼뮤테이션 같은 것들이 보입니다.

CODE:00401352 start           proc near
CODE:00401352
CODE:00401352 var_4           = dword ptr -4
CODE:00401352
CODE:00401352 ; FUNCTION CHUNK AT CODE:00401000 SIZE 0000000F BYTES
CODE:00401352
CODE:00401352     xor     ebx, ebx           ;디버거가 탐지된 경우 실행되지 않는
                                             ;부분. 매우 중요한 코드 인듯.
                                             ;일단 스킵
CODE:00401354     push    offset loc_40135A  ;코드 퍼뮤테이션 기법
CODE:00401359     retn                       ;push xxxx/retn => jmp xxxx
CODE:0040135A
CODE:0040135A loc_40135A:                    ; DATA XREF: start+2o

CODE:0040135A     push    eax                ;eax값을 스택에 push
CODE:0040135B     xor     eax, 427108h       ;귀여운 가비지 코드 ^^;
CODE:00401360     sbb     eax, edx           ;귀여운 가비지 코드 ^^;
CODE:00401362     xchg    eax, [esp+0]       ;결국 eax은 원래대로 복원됨
                                             ;따라서 위의 두 명령은 가비지

CODE:00401365     pop     ds:dword_402064    ;너는 뭐냐? 가비지냐?
                                             ;402064번지를 직접 가서 이 번지를
                                             ;참조하는 코드를 보면 여기 말고는
                                             ;없습니다. 일단 가비지인것 같군요.

CODE:0040136B     push    offset loc_401000  ;jmp loc_401000
CODE:00401370     retn
CODE:00401370 start           endp ; sp = -8

결국 엔트리 포인트에서 하는 일은 EBX의 값을 0으로 설정하고 loc_401000으로 점프하는 것입니다.
그럼 401000 번지를 향해 고고.

음 재미있는 코드입니다. 결국 JMP loc_40116D랑 같군요.
CODE:00401000 loc_401000:                       ; CODE XREF: start+1Ej
CODE:00401000                                   ; DATA XREF: start+19o
CODE:00401000     push    esp                   ;단순히 스택내 공간 확보가 목적
CODE:00401001     mov     [esp+4+var_4], eax    ;eax 값을 위에서 마련한 스택
                                                ;내 공간에 백업
CODE:00401004     mov     eax, offset loc_40116D
CODE:00401009     xchg    eax, [esp+4+var_4]    ;스택의 top 위치에 40116D저장
CODE:0040100C     retn    0                     ;결국 40116D로 점프



슬슬 귀찮아지기 시작하는 지점입니다. 어쨌든 40116D로 가보겠습니다.

CODE:0040116D loc_40116D:              ; DATA XREF: start-34Eo
CODE:0040116D     push    esp         
CODE:0040116E     mov     [esp], eax
CODE:00401171     mov     eax, offset sub_401186
CODE:00401176     xchg    eax, [esp]    ;스택의 top에 0x401186 저장
                                        ;뒤에 나오는 GetCommandLineA
                                        ;호출 후 복귀 주소입니다.
CODE:00401179     push    esp           
CODE:0040117A     mov     [esp], eax  
CODE:0040117D     mov     eax, offset loc_40137D
CODE:00401182     xchg    eax, [esp]    ;스택에 top에 loc_401037D 저장
CODE:00401185     retn                  ;결국 0x401037D로 점프.. 아띠..장난하나.

ㅠ.ㅠ;; 401037D로 가봅니다.
CODE:0040137D loc_40137D:          ; DATA XREF: CODE:0040117Do
CODE:0040137D     jmp     ds:GetCommandLineA


눈이 반짝반짝~ 뭔가 의미있는 코드인것 같습니다. GetCommandLineA가 호출됩니다. 이 프로그램호출 시 커맨드 라인을 줘야 하나 봅니다. GetCommandLineA를 이용하여 아규먼트를 읽은 다음에는 401186번지로 복귀합니다. 스택의 모양을 잘 그려보세요.  어쨌든 알아둘만한 패턴입니다.(PUSH return주소/JMP 함수시작주소 = CALL 함수 요넘의 변형입니다.)  

GetCommandLineA 호출 후 리턴주소인 0x401186번지로 이동해 보겠습니다. 하하.. 복잡해보이는군요. 이제는 디버거의 도움을 받아야 겠습니다. Ollydbg로 프로그램을 오픈한 후 401186번지에 브레이크 포인트를 설정하고서 실행시킨 다음 한 스텝씩 진행시켜 가며 코드를 분석했습니다. 먼저 00401187번지에서 EDX에 커맨드라인 아규먼트의 시작 주소를 복사해 넣는 것을 확인할 수 있군요. [그림 1]참고.
 

사용자 삽입 이미지
 [그림 1] 00401187번지에서 EDX에 커맨드라인 아규먼트의 시작 주소를 저장

그 다음 코드는 루프를 반복합니다. 변화되는 내용을 관찰하며 한두번 루프를 반복하다 보면 쉽게 이해할 수 있는 코드입니다. 복잡해 보이기만 했지 확인해보면 커맨드라인 아규먼트의 끝부분을 찾는 단순한 코드입니다. 끝부분 즉 널문자의 위치는 EDX 레지스터에 저장됩니다.
 
사용자 삽입 이미지
   [그림 2] 커맨드라인에서 NULL 문자의 위치를 찾음, 결과값은 EDX에 저장

다음 코드는 커맨드라인 아규먼트에서 실행파일 경로명을 제외한 나머지 부분을 체크하는 코드입니다. 코드를 찬찬이 뜯어보면 실행파일에 전달되는 아규먼트가 모두 4bytes이어야 한다는 사실을 알 수 있습니다. 즉 "FSC2007_Level2.exe XXXX" 형태가 되어야 하는 거죠.
사용자 삽입 이미지
[그림 3] 실행파일 경로를 제외한 아규먼트의 길이가 4bytes인지 검사

아규먼트를 주지 않은 채로 실행시켰기 때문에 정답과는 거리가 먼 곳으로 점프하겠군요. 그래서 004011E1에 브레이크 포인트를 설정하고 프로그램에 전달한 아규먼트로 "XXXX"를 지정한 후 다시 실행시켜 보았습니다.
사용자 삽입 이미지
                           [그림 4] 아규먼트 지정

4bytes 아규먼트가 주어진 경우 의미있는 코드가 실행되는군요. [그림 5]의 주석대로 EAX에 4bytes아규먼트와 0x5528566D 그리고 BH의 값을 XOR 한 결과 값을 저장합니다. 앞서 분석했던 TLS callback 함수에서 엔트리 포인트를 변경한 이유가 여기서 밝혀지는 군요. 디버거가 존재하는 경우 EBX에는 0이 아닌 다른 값이 저장되어 아래 코드의 계산 결과가 달라지겠습니다. 결국 엉뚱한 정답을 얻게 되겠네요.

사용자 삽입 이미지
[그림 5] EAX = 아규먼트(4bytes) XOR 0x5528566D XOR BH

이어지는 다음코드는 아래와 같습니다.
사용자 삽입 이미지
[그림 6]. EDX-6에 위차한 문자가 큰따옴표(")인지 검사.

이어지는 코드는 아래와 같습니다. 이 코드는 프로그램에 전달된 아규먼트 4bytes와 0x5528566D를 XOR한 결과 값이 6578652e인지를 비교합니다. 그렇다면 프로그램에 전달할 아규먼트는 0x5528566D와 0x6578652e를 XOR한 결과 값이 되면 되겠습니다. 휴~ 드디어 문제를 풀었군요.

사용자 삽입 이미지
[그림 7] 프로그램에 전달한 아규먼트 = 0x6578652e ^ 0x5528566d

위 값을 계산해보면 아규먼트는 0x30503343 이므로 리틀 엔디언임을 고려하면 C3P0 가 될 것이다.
ㅠ.ㅠ; 그럼 확인해 보겠습니다.

사용자 삽입 이미지
             [그림 8] 바로가기 등록

두근두근두근~~

사용자 삽입 이미지
               [그림 9] 유레카!!

레벨 2는 TLS callback 함수에 대한 처리만 잘 하면 그다지 어려울 것은 없어 보입니다. 실제 분석 시간도 1시간이 채 안걸렸는데요... FSC 사이트에 가보니 제일 먼저 푸신 분이 1시간 10분 정도 걸리셨다고 하는데... ^^; 그정도는 아닌 듯 싶습니다. 근데 이거 정답인가요... 알길이 없으니.. ㅠ.ㅠ;



이제 레벨 3 문제를 풀어볼까요..

Posted by zesrever

FSC2007 Level 2문제 풀이 첫번재 글 입니다. 풀이 내용이 길어서 2개의 글로 나누어 설명하겠습니다. 문제 파일은 두번째 글에 첨부되어 있습니다.

문제 : Level1과 마찬가지로 이메일 주소를 알아내야 합니다.

먼저 프로그램을 실행시켜 보겠습니다.

사용자 삽입 이미지

                   [그림 1] FSC_Level2.exe 실행 결과

TLS callback 분석
[그림 1]처럼 메시지박스 하나 뜨고는 아무런 이야기도 없군요. -.-;;  음... Level 1보다는 더 문제답게 보입니다. 먼저 PE 헤더 정보를 확인해 보겠습니다. 저는 stud_pe를 이용하였습니다. 뭐 아무거나 상관없습니다. PE 헤더를 살펴보다보니 TLS 섹션이 존재하는 군요. 이러한 경우 TLS callback 함수를 사용하는지 확인해봐야 합니다. TLS callback에 대해서는 여기를 참고하세요.

사용자 삽입 이미지
                           [그림 2] Stud_PE를 통해 TLS 섹션의 존재 확인

TLS callback 함수가 존재하는지 그 주소가 어떻게 되는지를 알아보기 위해서 IDA의 도움을 받아보겠습니다. IDA로 프로그램을 오픈한 후 Ctrl+E를 누르면 엔트리포인트 정보를 확인할 수 있습니다. IDA는 TLS callback을 분석할 줄 알기 때문에 엔트리포인트 윈도우에 그 주소가 나타납니다. 확인해보니 TLS callback 함수가 존재하는 군요!!! ^^; 음.. TLS callback 함수를 확인해 봐야겠죠.  함수의 시작 주소는 [그림 3]에서 확인할 수 있는 것처럼 0x004070A4 번지입니다.
 
사용자 삽입 이미지
                     [그림 3] IDA를 이용한 TLS callback 함수 주소 확인

해당 번지로 이동하여 코드를 살펴보았습니다.

UPX2:004070A4 TlsCallback_0   proc near  ; DATA XREF: UPX2:TlsCallbackso
UPX2:004070A4
UPX2:004070A4 arg_4           = dword ptr  8
UPX2:004070A4
UPX2:004070A4   cmp     [esp+arg_4], 1
UPX2:004070A9   jnz     short locret_4070C9
UPX2:004070AB   push    eax     ;eax값을 백업
UPX2:004070AC   mov     eax, large fs:18h     ;TIB의 주소를 eax에 저장
UPX2:004070B2   mov     eax, [eax+30h]        ;PEB의 주소를 eax에 저장
UPX2:004070B5   movzx   eax, word ptr [eax+2] ;BeingDebugged 값을 eax에 저장
UPX2:004070B9   cmp     eax, 0                ;BegingDebugged 값이 0인지 확인
UPX2:004070BC   setz    al                    ;0이면(디버깅 중이 아니면) a
                                              ;l의 값을 1로 설정
UPX2:004070BF   imul    eax, 8                ;eax = eax*8
UPX2:004070C2   sub     byte ptr ds:loc_4063BC+1, al
                ;loc_4063BC+1 번지에 저장되어 있는 값에서 al에 저장되어 있는
                ;값을 뺀 후 저장
                ;loc_4063BC에는 "jmp  near ptr word_40135A"가 위치함
                ;JMP가 1byte이고 주소값이 4bytes이므로 결국 JMP할 지점을
                ;수정하게 됨
UPX2:004070C8   pop     eax     ;eax값을 복구
UPX2:004070C9
UPX2:004070C9 locret_4070C9:           ; CODE XREF: TlsCallback_0+5j
UPX2:004070C9                 retn
UPX2:004070C9 TlsCallback_0   endp


파란색으로 표시된 코드 부분은 전형적인 안티 리버싱 기법이죠. PEB에 존재하는 BeingDebugged 값을 확인하여 디버거를 탐지하는 기법입니다. 자세한 내용은 여기를 참고하세요.

문제에서 TLS callback으로 등록된 함수는 PEB의 멤버 중 BeingDebugged 값을 확인하는 방법으로 디버거를 탐지하고 디버거가 탐지되는 경우 JMP할 곳의 주소를 변경하는 함수입니다. 디버거가 탐지된다면 loc_40135A로 디버거가 탐지되지 않는다면 loc_401352번지로 점프합니다. 아직 풀리지 않은 문제는 조작 대상이 되는 jmp 코드의 의미입니다. 이 부분은 잠시 후에 다시 보기로 하겠습니다.

분석을 더 진행하기 전에 한가지 더 생각해봐야 할 사실이 있습니다. 섹션 이름에 UPX라는 문자열이 확인된다는 점인데요, 아마도 UPX로 패킹했을 것이라는 추측이 가능합니다. PEiD를 사용할 필요없이 UPX를 이용하여 바로 확인해 보았습니다.

사용자 삽입 이미지
        [그림 4]UPX로 패킹되었는지 확인

예상대로 UPX로 패킹되었군요. 이건 함정을 파놓은 것이라 생각됩니다. 그도 그럴 것이 UPX는 단순히 실행파일 압축을 목적으로 한 패커로 안티리버싱 테크닉 같은 것은 구현되어 있지 않습니다. 게다가 UPX 프로그램 자체에 UPX를 언패킹하는 기능이 포함되어 있죠. TLS callback 함수의 존재 여부를 확인하지않고 PEiD등의 툴로 문제 파일을 조사하였거나 IDA로 열어서 본 경우 약간의 경험을 가지고 있는 리버서라면 누구나 UPX로 패킹되었다는 사실을 알 수 있었을 테고 UPX를 이용하여 간단하게 언패킹 하였을 것입니다. 이런식으로 언패킹을 하게되면 패킹되기 전의 실행 파일을 얻을 수 있을테지만 TLS callback 함수를 날려버리게 됩니다. 만약 TLS callback 함수에 프로그램의 실행과 관련된 중요한 코드가 포함되어 있다면 어떻게 되겠습니까? 그야말로 뻘짓을 하게 됩니다. 아무리 분석해도 정답에 가까이 가기는 힘들겠죠. 

결국 TLS callback에서 하는 일이 무엇인가가 중요한 문제인 것 같습니다. TLS callback 함수가 디버거의 존재 여부에 따라  4063BC번지에 존재하는 jmp 문의 오퍼런드(절대주소)를 변경한다는 것 까지는 앞에서 알아봤습니다. 그럼 조작되는 jmp문의 역할을 알아볼 차례인 것 같습니다. 먼저 해당 코드 부분을 살펴보았습니다.

UPX1:004063BB loc_4063BB:           ; CODE XREF: start+DDj
UPX1:004063BB               popa
UPX1:004063BC
UPX1:004063BC loc_4063BC:           ; DATA XREF: TlsCallback_0+1Ew
UPX1:004063BC               jmp     near ptr word_40135A
UPX1:004063BC start         endp

대충 감이 잡히는 군요. UPX의 경우 별다른 조작을 가하지 않았다면 UPX1이라는 이름의 섹션에 마지막에 존재하는 jmp 명령어로 점프하는 곳이 OEP 입니다. 게다가 위에 POPA 까지보이니 40135A가 OEP라는 것이 거의 맞는 것 같습니다. 하지만 이건 어디까지나 추측일 뿐이고, 실제로 004063BC에 위치한 jmp문이 실행되지 않는다면 이 역시 트릭이겠죠. 디버깅을 통해서 확인해 보겠습니다. 만사불여튼튼... ^^; 매 단계마다 검증 과정을 빼놓는다면 트릭에 빠질 확률이 높습니다.  검증을 위해 004063BC에 브레이크 포인트를 설정한 후 실행시켜 보았습니다. 

사용자 삽입 이미지
       [그림 5]004063BC번지에 브레이크 포인트 설정


두세번 F9(Run)를 누르면 004063BC 번지에 설정한 브레이크 포인트가 히트됨을 알 수 있습니다. 트릭은 아니군요. ^^;

사용자 삽입 이미지
         [그림 6] 004063BC에서 브레이크 포인트 히트

여기까지 정리해보겠습니다. 

* FSC_Level2.exe는 UPX로 패킹한 후 TLS 섹션이 추가되었으며 TLS callback 함수가 존재한다.
* TLS callback 함수는 디버거의 존재 여부에 따라 엔트리 포인트 값을 변경하는 기능을 가지고 있다.
* 디버거가 존재하지 않는 경우의 엔트리 포인트는 00401352번지이다.


UPX로 언패킹 후 엔트리 포인트 수정
음.. 대충 해결 방법이 보이는 군요. 그냥 UPX로 언패킹한 후 엔트리 포인트를 00401352 번지로 변경하면 되겠습니다. 
사용자 삽입 이미지
         [그림 7]UPX를 이용하여 FSC_Level2.exe를 언패킹


Stud_PE를 이용하여 언패킹된 파일을 오픈한 후 엔트리포인트를 0000135A에서 00001352로 번경하였습니다.

사용자 삽입 이미지
                     
   [그림 8]언패킹한 후 엔트리 포인트 변경

음 이제서야 분석할 준비가 끝났습니다. ^^; 사실 설명이 좀 길어서 그렇지 실제 분석에 소요된 시간은 10분 남짓입니다. 언패킹한 후 조작한 파일을 실행시켜보면 [그림 1]과 동일한 결과를 얻을 수 있습니다. 그럼 문제를 계속 분석해 보도록 하겠습니다.

다음글로 이어집니다.

Posted by zesrever

어제 동호회 사람들과 워크샵을 다녀온 뒤라 좀 피곤합니다. 간만에 술을 뽀지게 마셔서.. 뒹글뒹글 하던 차에 좀 뒤 늦은 감이 있지만 아는 분이 시간 때우기(?) 좋다고해서 해봤습니다. F-Secure Challenge 공식 사이트에 공개되어 있습니다. 리버싱을 처음 공부하시는 분들은 솔루션 보고 따라해보시는 것도 공부에 도움이 될 듯 싶습니다.

덧붙임 :  이런 챌린지들에 대한 솔루션은 매우 다양합니다. 이 글에서 언급하고 있는 방법보다 더 좋은 방법들이 있을 수도 있겠습니다. Level1의 경우 사실 문제 푸는데는 초보자라 하더라도 1~2분 정도면 충분할 정도로 쉽습니다. 하지만 문제 푸는 것 자체보다는 과정을 이해하시는 것이 더 좋을 것 같아 좀 풀어서 설명합니다. 어차피 시간 지난 문제인데 빨리 풀어내는 방법에 초점을 맞출 필요는 없을 것 같아서요.

Level 1:  제공되는 바이너리에 이메일 주소가 숨겨져 있습니다. 이 이메일 주소를 찾아서 이메일을 보내면 다음 레벨로 가기 위한 방법을 메일로 보내줬답니다.. ^^; 물론 대회는 이미 종료된 관계로 실제 메일을 보내도 응답 메일이 올리는 만무하죠. 이메일만 찾아보면 되겠습니다.


문제로 출제된 프로그램을 실행시켜 보니 아래와 같이 Key를 입력하라고 나옵니다.

사용자 삽입 이미지
            [그림 1] FSC 2007 Level 1. 문제 화면

아마도 Key를 입력하면 이메일 주소가 나오나 봅니다.  아무 문자열이나 입력해 보았습니다. 물론 맞을리 없겠죠. 하지만 Key값을 비교한 후 정답이 되는 키값과 다르면 출력되는 메시지는 확인할 수 있겠죠. 메시지를 확인하면 메시지를 참조하는 부분을 찾을 수 있을테고 그 근처를 조사하다보면 key값이 올바른지 검사하는 부분을 찾을 수 있을 겁니다. [그림2]는 잘못된 키 값을 입력한 결과화면 입니다.
사용자 삽입 이미지
            [그림 2] 잘못된 키값을 입력한 결과

사랑스러운 그녀, IDA의 도움을 받아보겠습니다. IDA로 문제 파일을 오픈한 후 String 윈도우에서 "Sorry, this key is not valid!"라는 문자열을 찾아보았습니다.
사용자 삽입 이미지
        [그림 3] String 윈도우에서 메시지 확인

찾은 문자열을 더블클릭합니다.  위 문자열은 ".rdata" 섹션의 690020c0 번지에 저장되어 있음을 알 수 있습니다. 우측에 주석처리되어 있는 크로스 레퍼런스를 확인해보면 0x690010F3 번지에 있는 코드가 이 문자열을 참조하는 것을 알 수 있습니다. 필시 [그림2]와 같은 메시지를 출력하는 부분이겠죠.
사용자 삽입 이미지
         [그림 4] .rdata 섹션에서 확인한 문자열

크로스레펀런스를 더블 클릭하여 해당 코드로 이동하여 보겠습니다.
사용자 삽입 이미지
  [그림 5] Sorry 메시지 출력 부분

역시 예상대로 printf를 이용하여 메시지를 출력하고 있습니다. 우측 주석을 보면 _main+D4에서 이 코드로 jump해 온 것을 알 수 있습니다. 문제의 성격 상 키값을 비교하여 그 결과에 따라서 이 코드로 점프를 해 왔을 것이란 추측이 가능합니다. 비교문의 경우 그래프로 보면 좀 더 편하게 코드를 분석할 수 있습니다. IDA에서 텍스트 뷰와 그래프 뷰 모드 간 전환은 [스페이스바]를 이용하면 됩니다. 스페이스바를 눌러 그래프 뷰로 전환하여 코드를 살펴보았습니다.

사용자 삽입 이미지
 [그림 6] 키값 비교 부분을 그래프 뷰로 확인한 결과

한 눈에 잘 들어오네요. stricmp로 문자열을 비교하고 그 결과에 따라(test eax, eax)  적절한 메시지를 출력하는 분기가 이루어짐을 확인할 수 있습니다. 여기부터는 각자의 취향이나 목적에 따라 솔루션이 달라지겠습니다. 쉽게 생각할 수 있는 몇 가지 솔루션으로 나누어 확인해 보겠습니다.

(솔루션 1) 가장 쉽고 정확한 솔루션 : 디버깅을 통해 비교되는 문자열 확인하기
stricmp를 이용하여 문자열을 비교한다는 사실을 위에서 확인했습니다. stricmp(정상적인키값,사용자입력값) 이런 식으로 비교하겠죠. 따라서 stricmp에 브레이크 포인트를 설정하고 파라미터로 넘어오는 값을 확인하면 키 값을 쉽게 알아낼 수 있을 것입니다. 문자열 비교하는 지점을 정확하게 알 수가 없다면 브레이크 포인트를 IAT에 설정해야겠지만 문제의 경우 문자열 비교 지점을 정확하게 알고 있으므로 690010c9 번지에 직접 브레이크 포인트를 설정하면 됩니다.

사용자 삽입 이미지
  [그림 7] 문자열 비교 지점에 브레이크 포인트를 설정한 후 실행시켜 파라미터 확인

Ollydbg의 스택 윈도우를 살펴보면 첫번째 파라미터는 입력한 문자열 "zesrever"를 가르키고 있으며 두번째 파라미터는 키값 "Asm07REC"를 가르키고 있음을 확인할 수 있습니다. 정답인지 확인해 보겠습니다.

사용자 삽입 이미지
            [그림 8] 정답 화면

음.. 이메일 주소가 나왔습니다. 메일을 보내면 다음 문제가 온다고 해서 바보같지만 혹시나 하는 마음에 보내봐도 역시 안오는군요. ^^; 정답인지 확인할 길은 없습니다만... 별다른 코드가 없는 것으로 봐선 확실한 것 같습니다.

(솔루션 2) 문자열 비교 결과를 조작하여 성공했을 경우 출력되는 메시지 확인(코드 패치)
또 다른 방법은 stricmp후 비교하는 부분의 코드를 수정하는 것입니다. 키값으로 아무 내용이나 입력하고 비교문에서 조건을 반대로 주어 성공했을 경우의 메시지가 출력되도록 하는 것이죠. 아래 [그림 9]에서 처럼 690010D4 번지로 이동한 후 [스페이스바]를 눌러 어셈블 창을 띄운 후 JNZ를 JZ로 변경합니다.

사용자 삽입 이미지

              [그림 9] JNZ를 JZ로 변경

[그림 10]은 코드 패치 후 실행 시킨 결과입니다. 역시 동일한 메시지가 출력되는 군요.

사용자 삽입 이미지
             [그림 10] 코드 패치 후 실행 결과


(솔루션 3) 키값을 직접 계산하기
 키 생성 알고리즘이나 키 생성 방법등을 리버싱하는 것도 한 방법입니다. 물론 이 문제의 경우 이 솔루션은 앞의 솔루션들에 비하면 그다지 효과적이라 할 수는 없습니다. 그래도 정답도 검증할 겸해서 살펴보도록 하죠. 먼저 문자열 비교하는 부분을 살펴보겠습니다.

.text:690010BF     push    offset byte_69003310 ; char *
.text:690010C4     push    offset byte_690031A0 ; char *
.text:690010C9     call    ds:_stricmp


stricmp에 파라미터로 전달되는 두 개의 포인터는 모두 전역변수를 가르키는 것을 쉽게 알 수 있습니다. 둘 중 하나는 키 값을 저장하는 변수일테고 다른 하나는 입력받은 키 값을 저장하는 변수일 것입니다. 아래의 코드를 보면 0x690031A0는 입력받은 키 값을 저장하는 변수의 주소임을 알 수 있습니다.

.text:6900105D     push    offset byte_690031A0
.text:69001062     push    offset aS       ; "%s"
.text:69001067     call    ds:scanf

그렇다면 0x69003310번지가 키 값을 저장하는 변수의 주소임을 알 수 있습니다. 그럼 이 번지에 어떠한 값이 저장되는지를 추적하면 키 값을 알아낼 수 있겠군요. 관련 코드는 어렵지 않게 찾을 수 있습니다. 아래 부분이 키값을 생성하는 코드입니다.

.text:6900106D  movsx   ecx, byte ptr aAssembly2007Re+22h
.text:69001074  movsx   edx, byte ptr aAssembly2007Re+16h
.text:6900107B  movsx   eax, byte ptr aAssembly2007Re+0Eh
.text:69001082  mov     edi, ds:sprintf
.text:69001088  push    ecx
.text:69001089  movsx   ecx, byte ptr aAssembly2007Re+0Ch
.text:69001090  push    edx
.text:69001091  movsx   edx, byte ptr aAssembly2007Re+0Bh
.text:69001098  push    eax
.text:69001099  movsx   eax, byte ptr aAssembly2007Re+4
.text:690010A0  push    ecx
.text:690010A1  movsx   ecx, byte ptr aAssembly2007Re+1
.text:690010A8  push    edx
.text:690010A9  movsx   edx, byte ptr aAssembly2007Re ; "Assembly 2007 Reverse-Engineering Chall"...
.text:690010B0  push    eax
.text:690010B1  push    ecx
.text:690010B2  push    edx
.text:690010B3  push    offset aCCCCCCCC ; "%c%c%c%c%c%c%c%c"
.text:690010B8  push    offset byte_69003310 ; char *
.text:690010BD  call    edi ; sprintf

위 코드를 보기 좋게 정리해 보겠습니다.
.text:6900106D  movsx   ecx, byte ptr aAssembly2007Re+22h
.text:69001088  push    ecx
.text:69001074  movsx   edx, byte ptr aAssembly2007Re+16h
.text:69001090  push    edx
.text:6900107B  movsx   eax, byte ptr aAssembly2007Re+0Eh
.text:69001098  push    eax
.text:69001089  movsx   ecx, byte ptr aAssembly2007Re+0Ch
.text:690010A0  push    ecx
.text:69001091  movsx   edx, byte ptr aAssembly2007Re+0Bh
.text:690010A8  push    edx
.text:69001099  movsx   eax, byte ptr aAssembly2007Re+4
.text:690010B0  push    eax
.text:690010A1  movsx   ecx, byte ptr aAssembly2007Re+1
.text:690010B1  push    ecx
.text:690010A9  movsx   edx, byte ptr aAssembly2007Re
.text:690010B2  push    edx
.text:69001082  mov     edi, ds:sprintf
.text:690010B3  push    offset aCCCCCCCC ; "%c%c%c%c%c%c%c%c"
.text:690010B8  push    offset byte_69003310 ; char *
.text:690010BD  call    edi ; sprintf


한결 보기 편하군요. 코드내에서 aAssembly2007Re라고 이름 붙여진 곳에 가보면 아래에서 확인할 수 있는 것처럼 "Assembly 2007 Reverse-Engineering Challenge - Level 1"이라는 문자열이 저장되어 있습니다.
.data:69003020 aAssembly2007Re db 'Assembly 2007 Reverse-Engineering Challenge - Level 1',0Ah

따라서 위의 코드는 위 문자열의 시작 주소 +0, +1, +4, +0xB, + 0xC, +0xE, +0x16, +0x22,에 해당하는 문자들을 스택에 push하고, 문자열 "%c%c%c%c%c%c%c%c"의 주소값과 키값을저장할 변수의 주소를 스택에 push한 후  sprintf를 호출하고 있습니다. 따라서 위의 긴 코드는 아래와 같이 정리할 수 있을 것입니다.

char key[10];
banner = "Assembly 2007 Reverse-Engineering Challenge - Level 1";
sprintf(key,"%c%c%c%c%c%c%c%c",banner[0],banner[1],banner[4],banner[11],banner[12], banner[14],banner[22],banner[34]);

그럼 key값은 문자열 "Assembly 2007 Reverse-Engineering Challenge - Level 1" 중에서 파란 색으로 표시된 문자들을 모아놓은 것이 되겠군요. 앞서 확인한 결과와 일치합니다.



간단한 문제 하나가지고 잘 울궈먹은 것 같습니다. ^^;; 이제 Level 2로 가보실까요..



Posted by zesrever

BLOG main image
Slow but Steady, Broad and Deep ... by zesrever

공지사항

카테고리

분류 전체보기 (44)
Digital Forensics (4)
Reverse Engineering (21)
Vulnerability (2)
Secure Coding (0)
Book Story (1)
Digital Life (7)
My Life (7)
세미나자료 (1)
개인용 (0)
Musics (0)
Total : 261,011
Today : 57 Yesterday : 223