[plaid 2014] paris 1
Posted 2014.07.03 20:47, Filed under: 리버싱 문제 풀이이 문제가 가상화 문제라는것만 파악하면 난이도가 확 내려가는 문제이다
하지만 그래도 어려웠다 ㅠ
==
메인함수이다
except 함수를 호출해서 esi값이 0xdeadbeef 이면 정답이다
except 함수에선 예외처리 핸들러를 등록하고 예외를 발생시킨 후, edi를 0x1111 뺀다
여기서 sub edi,0x1111을 기억해두자
예외핸들러선 함수 리스트에서 함수 하나 꺼내와서 호출하는게 전부이다
함수 리스트에는 총 20개의 함수가 있는데 대부분 기본적으로 이런 형태를 가지고 있었다
.text:0040225F AND proc near ; DATA XREF: .text:0040222Eo
.text:0040225F
.text:0040225F context = dword ptr 0Ch
.text:0040225F
.text:0040225F call two_byte_opcode
.text:00402264 add ebx, 9Ch
.text:0040226A add ebx, [esp+context]
.text:0040226E movzx ebx, word ptr [ebx]
.text:00402271 add ecx, 9Ch
.text:00402277 add ecx, [esp+context]
.text:0040227B movzx edx, word ptr [ecx]
.text:0040227E and ebx, edx
.text:00402280 mov [ecx], bx
.text:00402283 sub eax, 9Bh
.text:00402288 setz al
.text:0040228B jmp restore_if
.text:0040228B AND endp
two_byte_opcode 에서는 opcode를 해석하고 backup_all 함수를 불러서 현재 레지스터를 포함한 가상화에서 쓰이는 모든 값을 백업한다
[백업과 복구하는 함수]
여기서 중요한건 context이다
예외 핸들러 함수의 원형은 다음과 같다고 한다
EXCEPTION_DISPOSITION __cdecl _except_handler (
struct _EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
struct _CONTEXT *ContextRecord,
void * DispatcherContext
);
그렇기에 [esp+0xc] 는 ContextRecord 가 되며 이걸 알아보는게 문제풀이에 중요한 포인트가 되겠다
void __usercall AND(int a1<ebx>, int a2, int a3, void *a4)
{
int v4; // eax@1
int v5; // ecx@1
v4 = two_byte_opcode();
*(_WORD *)((char *)a4 + v5 + 156) &= *(_WORD *)((char *)a4 + a1 + 156);
restore_if(v4 == 155, a2, a3, a4);
}
대충 이런식으로 디컴파일이 되는데 여기서 a4가 CONTEXT가 되는데 CONTEXT 구조체를 잠깐 보자면
....
0000009C _Edi dd ?
000000A0 _Esi dd ?
000000A4 _Ebx dd ?
000000A8 _Edx dd ?
000000AC _Ecx dd ?
000000B0 _Eax dd ?
000000B4 _Ebp dd ?
000000B8 _Eip dd ?
....
와 같다
정리하면 다음과 같이 된다
156 이 0x9c, 즉 이 프로그램은 EDI 부터 몇개의 레지스터를 가상화속 레지스터로 활용한다는 뜻이다
1. 현재 opcode를 해석하고 현재 레지스터, 메모리 등 여러 값들을 백업을 해둔다. 참고로 opcode의 길이는 1,2,3 바이트 3종류가 있고 그에따라 처리하는 함수도 다르지만 거의 비슷비슷하다. 그리고 그 함수들 내부엔 backup_all 이란 함수가 있다 그리고 이 함수는 eax에 해석한 opcode의 값, ebx,ecx 에 인자를 넣는다
2. opcode와 관계없이 명령어를 무조건 실행한다
3. 만약 첫번째 함수에서 해석한 opcode값과 와 현재 opcode 의 값이 다르면 맨처음 백업 해두었던 값들을 복구한다(결과적으로 영향 없음)
참고로 여기서 해석한 opcode값과 현재 opcode값이 뭐냐면 이 프로그램의 가상화된 명령어들은 암호화 되어 있는데 그걸 첫번째 해석하는 프로그램에서 복호화를 한다
이게 해석한 opcode이고,
sub eax, 9Bh
이부분의 0x9b가 현재 opcode값이 된다
그럼 이제 옵코드를 해석하는 부분을 보자
옵코드를 해석하는 함수는 3개의 함수가 있는데(1~3byte) 공통적으로 시작부분에 backup_all 을 호출해서 모든 값을 백업한다
그리고 eax,ebx,ecx에 간단한 and 연산과 shift 연산을 한 결과값을 넣는다
1byte
-eax 해석한 opcode 값
-ebx 인자 1, 0~14의 짝수 값을 가짐, 보통 레지스터 r0~r7 을 뜻함
2byte
-eax 해석한 opcode 값
-ebx 인자 1, 0~14의 짝수 값을 가짐, 보통 레지스터 r0~r7 을 뜻함
-ecx 인자 2, 0~14의 짝수 값을 가짐, 보통 레지스터 r0~r7 을 뜻함
3byte
-eax 해석한 opcode 값
-ebx 인자 1, 0~14의 짝수 값을 가짐, 보통 레지스터 r0~r7 을 뜻함
-ecx 인자 2, 2바이트크기의 값을 가짐, 보통 정수를 뜻함
그러니까
.text:00402264 add ebx, 9Ch
.text:0040226A add ebx, [esp+context]
.text:0040226E movzx ebx, word ptr [ebx]
이와 같은 명령어는, 처음 ebx에는 가상화 속 레지스터 오프셋이 들어가 있고 마지막 movzx 를 실행하면 그 레지스터의 값이 ebx로 들어가게 되는 것이다
그리고 이때 레지스터 오프셋이 0~14인데 케스팅이 word 단위인것과 짝수값을 가지는걸 보면 가상화에서 쓰이는 레지스터 크기는 2바이트인걸 알 수 있다
그럼 레지스터 배치가 다음과 같이 된다
_Edi = [r0,r1]
_Esi = [r2,r3]
_Ebx = [r4,r5]
_Ecx = [r6,r7]
이제 함수 리스트 20개가 뭘 하는지만 알아내면 되는데 이는 지금까지 지식을 바탕으로 한다면 별 어려움이 없을것이니 넘어간다
다만 1개 특이한 명령어가 있다
함수리스트중 마지막에서 2번째에 있는 함수 인데 내용은 다음과 같다
xor_table 에서 인자1을 오프셋으로 값 하나를 가져와 어떤 테이블을 xor한다
기억해두자
내가 분석한 함수 리스트의 이름은 다음과 같다
.text:0040220E func_list dd offset NOP ; DATA XREF: jump_func+7o
.text:00402212 dd offset MOV_REG2REG
.text:00402216 dd offset MOV_REG2MEM
.text:0040221A dd offset ???
.text:0040221E dd offset MOV_INT2REG
.text:00402222 dd offset ADD
.text:00402226 dd offset SUB
.text:0040222A dd offset XOR
.text:0040222E dd offset AND
.text:00402232 dd offset SHR8
.text:00402236 dd offset NOT
.text:0040223A dd offset INC
.text:0040223E dd offset CMP
.text:00402242 dd offset JMP_2_1_
.text:00402246 dd offset JMP_IF_FLAG
.text:0040224A dd offset PUSH
.text:0040224E dd offset POP
.text:00402252 dd offset XCHG_1byte_2byte
.text:00402256 dd offset WTF
.text:0040225A dd offset FUNC_END
중간에 ???은 파이썬 코드로 말하면
r?=input[rx:rx+2:-1] 쯤 되겠다 (?와 x에는 0~7이 들어간다. 즉 레지스터이다, input은 우리가 입력한 값이 들어가 있는 곳)
쩃든 그러면 이제 디스어셈블러를 짜서 디스어셈블을 해보자
코드는 여기에 올려놓았다
https://www.dropbox.com/s/mr2yjgnw8a9x02q/paris_vm_disas_new.py
결과를 살짝 보기 좋게 해봤다
0x0 NOP
0x1 NOP
0x2 NOP
0x3 MOV_INT2REG r2,0x3133
0x6 MOV_INT2REG r3,0x0
0x9 MOV_INT2REG r4,0xff00
0xc MOV_INT2REG r5,0xff
0xf r0=input[r3:r3+2][::-1]
//r0 = r0 - 0x3332
0x11 MOV_REG2REG r7,r0
0x13 XCHG_1byte_2byte r7
0x14 NOT r7
0x15 CMP r7,r2
0x17 JMP_IF_FLAG r7,0x37
0x1a MOV_REG2REG r6,r7
0x1c AND r6,r4
0x1e AND r7,r5
0x20 SHR8 r6
0x21 XOR r7,r6
0x23 MOV_INT2REG r6,0x200
0x26 ADD r7,r7
0x28 ADD r6,r7
0x2a r7=input[r6:r6+2][::-1]
0x2c XCHG_1byte_2byte r7
0x2d POP r6
0x2e XOR r7,r6
0x30 PUSH r6
0x31 PUSH r7
0x32 WTF r3
0x33 INC r3
0x34 JMP_2_1_ r7,0xf
0x37 XOR r7,r7
0x39 MOV_INT2REG r2,0x100
0x3c MOV_INT2REG r6,0xaf21
0x3f r5=input[r2:r2+2][::-1]
0x41 XCHG_1byte_2byte r5
0x42 INC r2
0x43 INC r2
0x44 POP r3
0x45 CMP r5,r6
0x47 JMP_IF_FLAG r7,0x56
0x4a CMP r5,r3
0x4c JMP_IF_FLAG r7,0x3f
0x4f MOV_INT2REG r3,0x0
0x52 MOV_INT2REG r2,0x0
0x55 FUNC_END r7
0x56 MOV_INT2REG r5,0x5a4d
0x59 CMP r3,r5
0x5b JMP_IF_FLAG r7,0x65
0x5e MOV_INT2REG r3,0x0
0x61 MOV_INT2REG r2,0x0
0x64 FUNC_END r7
0x65 MOV_INT2REG r3,0xdead
0x68 MOV_INT2REG r2,0xbeef
0x6b FUNC_END r7
중간에 빨간 부분이 눈에 들어올텐데 저부분에 숨겨진 명령이 있어서 그렇다
맨 처음 예외를 일으키는 부분에서 sub edi,0x1111 이 나왔는데 이는 가상화 속 레지스터에도 영향을 끼친다(r0,r1)
그리고 전체 디스어셈블 결과를 보면 알겠지만 저부분이 유일하게 그 영향을 받는 부분이다
하나 명령어를 체크할때마다 0x1111이 줄어들고, r0 크기가 2바이트, edi의 하위 2바이트인걸 감안하면
0xf r0=input[r3:r3+2][::-1]
0x11 MOV_REG2REG r7,r0
사이에서 r0=r0-(18*0x1111)&0xffff 가 있다는걸 알 수 있다
18인 이유는 저 input 어쩌구 명령어부터 MOV_REG2REG 까지 18번의 sub edi,0x1111을 거쳐가서 그렇다
그리고 이걸 파이썬틱한 형태로 옮겨보았다
몇개 값들을 설명하자면
dontknow_table은 [input+0x200] 에, dontknow_table2는 [input+0x100] 에 있던 문자열이다.
위 내용을 해석하면... 은 레포트를 써야하는 관계로 내일 나눠서 올린다
'리버싱 문제 풀이' 카테고리의 다른 글
[plaid 2014] paris 1 (5) | 2014.07.03 |
---|---|
2014 plaidctf hudak write up (0) | 2014.04.14 |
[코드게이트 2012] bin 300 (0) | 2014.02.17 |
[코드게이트 2011] bin 200 (0) | 2014.02.15 |
[코드게이트 2012] Bin 100 (0) | 2014.02.15 |
[YISF Pre Qual 2013] bin300 (0) | 2014.02.12 |
-
비밀댓글입니다
-
아래 댓글로 달았습니다
-
-
여기서 가상화란건 다음에서 쓰인 코드가상화를 뜻해요
제가 설명을 하는것보단 이게 나은것 같아서 링크로 대체합니다
http://ezbeat.tistory.com/361
http://ezbeat.tistory.com/338 -
감사합니다~
-
잘 읽고 가여~