문제를 풀어보면서 느낀건데 이거 만든 사람이 참 대단하게 느껴지더군요. 뭐 일단은 문제를 풀어보도록 하죠.
( 혹시 혼자서 먼저 풀어보실 분은 풀어보셔요. )
문제 파일
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]
잘 보니 인자로 "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 |