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]참고.
그 다음 코드는 루프를 반복합니다. 변화되는 내용을 관찰하며 한두번 루프를 반복하다 보면 쉽게 이해할 수 있는 코드입니다. 복잡해 보이기만 했지 확인해보면 커맨드라인 아규먼트의 끝부분을 찾는 단순한 코드입니다. 끝부분 즉 널문자의 위치는 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 문제를 풀어볼까요..
FSC2007_Level2.zip
이올린에 북마크하기