본문 바로가기

My Study/Crack Me

Codegate 2011 B500 writeup

대회가 끝난 .. 오래된 이 시점.. 가상화 프로젝트를 진행도중 예전에 가상화 관련해서 리버싱 문제가 하나 있었던거 같은데 그 당시 대회때는 건들어보지도 못했고; 나중에 시간 있을 때 한번 풀어봐야지 하고 문제만 가지고 있었는데 이제서야 풀어보게 되네요~ b100,200,300,400 다 풀었는데 시간상..ㅠㅠ 보지도 못한..;

문제를 풀어보면서 느낀건데 이거 만든 사람이 참 대단하게 느껴지더군요. 뭐 일단은 문제를 풀어보도록 하죠.
( 혹시 혼자서 먼저 풀어보실 분은 풀어보셔요. )
문제 파일


Q : Find a key.

일단 문제를 실행해보겠습니다.


출제가의 오타가 보이는 군요. 아무튼 키 파일을 체크를 하는데 해당 파일이 없다고 뜹니다.
뭐 리버싱을 해서 원하는 키 파일이 뭔지 알아내야겠지요. 일단 리버싱을 들어가기 전에

문제의 정보를 봐보았습니다.




해당 바이너리의 ImageBase와 EntryPoint를 살펴보니 시작하는 섹션이 .text 섹션이 아닌 .VM 섹션이라는 것을 알 수 있습니다. 0x409000 부터 .VM 섹션이기 때문이죠. 제가 공부하고 구현해보았던 코드 가상화 기법과는 또 다른 것 같습니다.

아무튼 부푼 기대를 가지고 한번 리버싱을 해보죠. 
 


올디로 처음 열어보았을 때 화면입니다. 

뭔가 시작부터 복잡하다고 느껴집니다. 전 미친척 하고 일일이 분석을 해보았지요.. =_=;; ㅎㅎ 사실 여러 가상화 기법들을 봐보아야 했기 때문에 겸사겸사 분석해본 것입니다. 

여기에 모든 분석한 위치를 담기엔 뭐해서 대충 설명을 적겠습니다.

일단 두 번의 가상 메모리를 할당합니다.
VirtualAlloc(NULL,0x1000,0x1000,PAGE_EXECUTE_READWRITE);
첫번째 VirtualAlloc된 메모리 : V100
두번째 VirtualAlloc된 메모리 : V200 
으로 일단 가정..
그림이 있어서 말해보면 위 그림에서 
call dword ptr ds:[eax]
이 위치에서 한번 VirtualAlloc을 합니다.

아무튼 위 과정이 끝나고 쭉쭉 트레이스 하다보면


위와 같은 루틴으로 점프해서 들어오게 되는데 이 루틴이 핵심적인 부분 중 하나입니다.
이 루틴은 분석할만한 가치가 100% +_+ ㅋㅋ   ㅠ_ㅠ @==^^ 설명이나 얼른..

이 루틴에서는 스택 재구성은 물론 별의별 짓을 합니다.
트레이스 하다보면
0x0040B1D4 에 있는 값과 0x0040B1D4 + 1 위치에 있는 값을 가져와 xor를 시킵니다.
그리고 0x0040B1D4 + 1 위치에 있는 값에서 xor된 크기 Byte만큼 데이터를 가져옵니다.
가져와서 또 지지고 볶고.. 쿵짝쿵짝

그리고 나면 V100+0xFD4 위치에 특정 명령어가 생성이 됩니다.
위 루틴의 끝 부분을 보시겠습니다.


이번 실행때 V100이 0x250000 였나보군요. 아무튼 push하고 retn하므로 저 주소로 뛰겠죠. 가보겠습니다.


push ebx를 하고 난 후 0x40ABBD로 점프를 하게 됩니다. 

저 push ebx가 뭘까.... 메모리에 이미 있는 값을 지지고 볶고 한 후 생성된 값인데..

아무튼 계속 트레이스 해본 결과 위 과정이 반복 된다는 것을 확인할 수 있었습니다.

그래서~ 0x0040ABBC 위치에 BP를 잡고 0x00250FD4 위치의 명령어를 계속 확인하면서 적어본 결과입니다. 

00250FD4    6A 00           PUSH 0

00250FD4    6A 00           PUSH 0

00250FD4    60              PUSHAD

00250FD4    9C              PUSHFD

00250FD4    9C              PUSHFD

00250FD4    58              POP EAX

00250FD4    50              PUSH EAX

00250FD4    68 03954000     PUSH 409503

00250FD4    58              POP EAX                                         ; 7CAC6AF2.00409503

00250FD4    8B05 F0914000   MOV EAX,DWORD PTR DS:[4091F0]

00250FD4    8D05 F80F3600   LEA EAX,DWORD PTR DS:[360FF8]

00250FD4    8925 F80F3600   MOV DWORD PTR DS:[360FF8],ESP

00250FD4    8300 04         ADD DWORD PTR DS:[EAX],4

00250FD4    58              POP EAX

00250FD4    8BF2            MOV ESI,EDX                                     ; 7CAC6AF2.00409987

00250FD4    46              INC ESI                                         ; 7CAC6AF2.00409987

00250FD4    8A02            MOV AL,BYTE PTR DS:[EDX]

00250FD4    3242 01         XOR AL,BYTE PTR DS:[EDX+1]

00250FD4    0FB6C0          MOVZX EAX,AL

00250FD4    50              PUSH EAX

00250FD4    56              PUSH ESI                                        ; 7CAC6AF2.00409988

00250FD4    57              PUSH EDI


00250F7C    60              PUSHAD

00250F7C    8B0D CC0F3600   MOV ECX,DWORD PTR DS:[360FCC]

00250F7C    8B35 C80F3600   MOV ESI,DWORD PTR DS:[360FC8]                   ; 7CAC6AF2.00409988

00250F7C    8B3D C40F3600   MOV EDI,DWORD PTR DS:[360FC4]

00250F7C    F3:A4           REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]

00250F7C    61              POPAD


00250FD4    51              PUSH ECX

00250FD4    50              PUSH EAX

00250FD4    57              PUSH EDI

00250FD4    51              PUSH ECX

00250FD4    68 4B934000     PUSH 40934B

00250FD4    59              POP ECX                                         ; 7CAC6AF2.0040934B

00250FD4    8B0D F4914000   MOV ECX,DWORD PTR DS:[4091F4]

00250FD4    66:390F         CMP WORD PTR DS:[EDI],CX

00250FD4    59              POP ECX

00250FD4    51              PUSH ECX

00250FD4    C60438 68       MOV BYTE PTR DS:[EAX+EDI],68

00250FD4    68 66934000     PUSH 409366

00250FD4    59              POP ECX                                         ; 7CAC6AF2.00409366

00250FD4    81C1 47000000   ADD ECX,47

00250FD4    890D DA0F3600   MOV DWORD PTR DS:[360FDA],ECX                   ; 7CAC6AF2.004093AD

00250FD4    C64438 05 C3    MOV BYTE PTR DS:[EAX+EDI+5],0C3

00250FD4    59              POP ECX                                         ; 7CAC6AF2.004093AD

00250FD4    8BF2            MOV ESI,EDX                                     ; 7CAC6AF2.0040998D

00250FD4    46              INC ESI                                         ; 7CAC6AF2.0040998D

00250FD4    8A02            MOV AL,BYTE PTR DS:[EDX]

00250FD4    3242 01         XOR AL,BYTE PTR DS:[EDX+1]

00250FD4    0FB6C0          MOVZX EAX,AL

00250FD4    50              PUSH EAX

00250FD4    56              PUSH ESI                                        ; 7CAC6AF2.0040998E

00250FD4    57              PUSH EDI


00250F7C    60              PUSHAD

00250F7C    8B0D CC0F3600   MOV ECX,DWORD PTR DS:[360FCC]

00250F7C    8B35 C80F3600   MOV ESI,DWORD PTR DS:[360FC8]                   ; 7CAC6AF2.0040998E

00250F7C    8B3D C40F3600   MOV EDI,DWORD PTR DS:[360FC4]

00250F7C    F3:A4           REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]

00250F7C    61              POPAD


00250FD4    51              PUSH ECX

계속 반복............ 


이러한 값이 생성이 되면서 실행을 하게 됩니다.!!
일단 여기서 이 가상화에 대한 결론을 내보면 원본 실행 루틴을 문제 만든이의 방법으로 인코딩을 시켜서 메모리에 저장해두고 실행 시 한줄 한줄 디코딩 하면서 실행하는 과정입니다.

너무 값이 많아 살펴보니 비슷비슷한 루틴이 계속 반복되고 있는 것을 알 수 있었습니다. 그리고 반복되는 루틴을 보니..!!
원본 실행 루틴을 디코딩 시켜서 메모리(V200 +0xFD4)에 저장하는 코드와 동일(너무 비슷?) 함을 알 수 있었습니다. 코드를 전부 분석해보니 눈에 보이더군요. 무튼 디코딩 되서 실행되는 루틴 또한 무언가 값을 디코딩해서 메모리에 저장합니다. 이거 이중 가상화 같군요. 가상화라 해야하나.. 아무튼 그러면 분명히 그 코드를 실행시키는  
 
이와 비슷한 루틴이 분명히 존재할 것입니다.

PUSH V200+0xFD4
RETN
이 루틴이 어딘가 존재한다는 말이죠. 없으면.. 사실상 문제 삭제해버릴 생각이었습니다. ㅎㅎㅎㅎ
있으니 이렇게 풀이를 작성하고 있겠죠? 

 
이 위치 입니다. V200이 0x260000 였나보군요.
RETN 위치로 가보겠습니다. 

 
뭐 여기서도 역시나 한줄 실행하고 0x4093AD 위치로 가는 것을 알 수 있습니다.
이제 0x00260FD4 위치에 있는 값을 또 한줄한줄 잡아볼까요?

아!!~ 그 전에 .text 영역을 잠깐 봐보겠습니다. 
보통 가상화를 시키면 가상화된 코드는 원본 코드보다 크기가 훨씬 크기 때문에 새로운 섹션을 만들어서 거기에 넣어두게 됩니다. 이 문제에서는 .VM 섹션에 있는 거구요. 그러면 원본 코드가 있는 .text 섹션엔?? 뭐 쓰레기 값으로 꽉꽉 넣어놨겠죠. 필요 없는 코드이기 때문에요. 

하지만 문제 만든이가 모든 코드를 가상화 시키지 않고 부분부분만 가상화를 시켜놨습니다. 그래서  


 

이와 같이 듬성듬성 있는 코드 이지만.. (nop 된 부분은 전부 가상화된 코드일 것.. ㅠㅠ)
전부다 BP를 걸어놨습니다.

다시 가상화 코드 부분으로 돌아와 0x00260FD4 위치에 있는 값을 잡아보겠습니다.

00260FD4    8D0D 40FB1200   LEA ECX,DWORD PTR DS:[12FB40]


이거 한줄 실행하고 .text섹션으로 들어오네요.


잘 보니 인자로 "rb", "codegate.key" 가 들어가는 걸로 봐서 codegate.key 파일명을 가지고 read/binary ? 로 여는 것을 알 수 있었습니다. 그래서 문제 파일과 동일한 폴더에 그냥 codegate.key 라는 파일을 하나 만들고 그안에 아무내용이나 막 채워넣었습니다. 그 다음 RETN을 하면 다시 .VM섹션으로 돌아오게 됩니다. 

계속 0x00260FD4 위치에 있는 값을 잡아보겠습니다.


이렇게 잡고나면 running 상태로 빠지게 되며 



이 창에서 멈추게 됩니다. 아무키나 누르면 계속 진행되겠지요. 다시 진행하겠습니다. 

 

이 가상화된 루틴을 실행 후  


.text섹션인 이 부분으로 오게됩니다. 이 부분이 codegate.key 파일이 존재하면 JE문에서 점프하지 않고 없으면 점프하게 됩니다. 즉, 없다면 EAX 값이 0으로. 있다면 0x0B6863BD와 같은 값으로...

다시 0x00260FD4로.. 

 

이 부분 실행 후 다시 .text 섹션..;; 

 

이 부분 실행 후 다시 .VM 섹션.. 뭐 무한반복?? [ECX+4] 에는 0x0B6863BD 값이..

 

위에서 얻어온 값을 가지고 이렇게 비교를 하는 군요. 파일이 없으면 cmp문을 수행 후 I can't find the file 이라는 문장이 출력됩니다. 위 두 문장을 수행 후 다시 .text 섹션으로 오는데 이 부분이 중요합니다.

 

 CALL 문을 호출하는데 들어가는 인자는
Func(0x12FB48,0x200,0x1..) 이렇게 들어갑니다.
실제로 수행을 해보면 0x12FB48 메모리 주소에 0x200 바이트 만큼 codegate.key에서 가져오게 됩니다.
아마 저 함수는 fread 함수 같군요 ㅋㅋ 키 길이는 0x200 Byte인것 같습니다. 계속 계속 gogo 

 

한 문장 수행 후 다시 .text.. 지치네요.. 


EAX에 0x0B70139B라는 값 넣고 있습니다. 

뭐 또 검증 하겠죠. 아무튼 이제 중요한 부분으로 바로 넘어가겠습니다.
계속 0x00260FD4 에 있는 문을 실행하면


이와 같은 루틴을 실행하게 되는데 주석에 달아놨듯이 [ECX+8]은 codegate.key에서 읽어온 0x200 Byte의 첫번째 데이터 값입니다. 그리고 그 값이 0x64와 xor 한 후 0x90과 비교를 하는데.. 당연히 다르겠죠. 그래서 아래와 같이 틀렸다는 문장을 실행하게 됩니다.

이제 cmp al,0x90 위치에 올 때마다 al 값을 옆 값과 맞는 값으로 바꿔주면서 진행해보겠습니다.
혹은 그냥 cmp 실행 후 zero flag를 바꿔줘도 됩니다. ㅋ


이런식으로 진행되게 됩니다. 1Byte씩 가져와 특정 값과 xor 한후 특정 값과 비교를 하죠..
이걸 일일이... 해야 하는거겠죠??.. ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

잠시 글 쓰는걸 중단하고 노가다 작업좀 하겠습니다. 

------------ 잠 시 후 ------------
워...... fread로  0x200 Byte나 읽었길래 정말 512번의 저 거지같은 노가다를 해야되는줄 알았습니다.
다행이.. 0x64..즉 100Byte만 저짓거리 한 후  ...!


위와 같은 루틴을 시행하더군요.

codegate.key의 값을 읽어온 메모리 값을 다른 메모리로 100byte만큼 옮깁니다.
그리고 나서 pop ecx를 마지막으로 .VM섹션에서 한 후.. .text 섹션으로 넘어오게 됩니다. 
이 때 단순히 zf만 1로 바꿔주면서 넘어오신 분들은 여기까지 하고 실제 key file 값을 구해주셔야합니다.
xor되는 값과 cmp되는 값을 xor 시켜주면서 복원.. 100Byte를 말이죠.


이 루틴인데 무언가 printf 할 느낌이 팍팍드는 이 좋은 기분은 뭘까요?? ^^ 
JL점프문에 의해 루프를 돈 다음 나온 화면을 보겠습니다.
 

올바르게 찾은 것 같군요. 아..... 바로 답을 주진 않고 또 뭔가 이상한 값을 주네요.. :-(

하! 지! 만! ㅋㅋ 저도 이렇게 비슷한 유형의 문제를 낸적이 있습니다. 추측해봤지만 맞더군요. 
일단 각각의 숫자를 2진수로 바꿉니다. 그리고 1에 색을 넣어서 한번 봐보겠습니다.


보이십니까?? QR 코드입니다. 하지만 QR코드가 180도 돌아가 있는 것 같군요.
QR코드를 읽을 수 있게끔 변환을 하겠습니다. ( 엑셀 사용.. -.- ) 위 결과를 단 20초면 아래와 같이 변환 가능!



위 값을 나의 패드로 찰칵 찍으면?!

답 : I_am_in_C0d3gat3_2011!!

뿌듯하네요. ㅋㅋ 이거 푸는데.. 가장 많이 시간이 걸린 곳은.. 바로 키파일 100Byte 저거 찾는 부분입니다. ..ㅠ_ㅠ 아흑..

PS. -_-;;;; 이거 조금 찾아보니까.. x86 Virtualizer 툴로 두번 가상화 시킨 것 뿐이네요..;;; 덕분에 x86 가상화 툴 싹다 분석해봤네요. 오픈소스 툴이므로 찾아보면 소스코드도 있습니다. 전 아바스트 백신 쓰는데 백신에서 잡네요. 

 그 전까지 이 문제 만든이가 직접 가상화 코드를 구현한줄 알았습니다.;;; 

'My Study > Crack Me' 카테고리의 다른 글

Codegate 2014 Clone  (0) 2014.03.15
2012 PCTF simple  (2) 2012.05.24
Defcon 2010 예선 바이너리 3번  (0) 2010.06.02
Defcon 2010 예선 바이너리 2번  (0) 2010.06.02
Defcon 2010 예선 바이너리 1번  (8) 2010.05.23