반응형

stolen bytes 기법

패커가 원래 코드의 일부를 잘라내어 다른 영역에 복사해둔 뒤 원래 잘라낸 코드가 있던 부분은 의미 없는 값으로 채워 넣는다.

 

stolen bytes 기법을 쓰는 이유

이렇게 하는 이유는 unpacking을 완료한 리버서가 메모리를 dump 뜨는 것을 방지하기 위함이다.

 

dump를 뜬다고 하더라도 startup code의 앞부분이 사라진 상태이므로 비정상적으로 동작하기 때문에

잘라낸 strlen bytes들은 언패킹 과정 중 보이는 의미없는 이상한 코드들 사이사이에 몰래 숨겨져 있다.

 


실습 환경

 

Virtual Box에 Windows 7 32bit를 올려 그 위에서 실습을 진행했다.

 

패킹된 파일(stolen_bytes_pespin.exe)은 PESpin v1.32 프로텍터의 "Remove OEP" 옵션으로 보호되어 있다.

 


파일 실행 결과

 

stolen_bytes.exe와 stolen_bytes_pespin.exe 모두 "[Stolen Bytes] test!" 문자열을 출력하고 종료한다.

 


Exeinfo PE로 패킹 정보 확인

 

stolen_bytes.exe 파일의 패킹 정보

 

stolen_bytes_pespin.exe 파일의 패킹 정보


PE View로 정보 확인

 

stolen_bytes.exe 파일의 정보

패킹이 안된 stolen_bytes.exe 파일은 base of code의 값, size of code의 값, address of EntryPoint 값이 각각 1000, 5000, 1041이다.

 

즉 EP 주소(1041)가 code 영역 1000 ~ 5000 사이의 범주에 포함된다.

 

stolen_bytes_pespin.exe 파일의 정보

패킹 된 stolen_bytes_pespin.exe 파일은 base of code의 값, size of code의 값, address of EntryPoint 값이 각각 1000, 5000, B0D4이다.

 

즉 EP 주소(B0D4)가 code 영역 1000 ~ 5000 사이의 범주에 포함되지 않고 초과되어 있다.

 

또한 stolen_bytes.exe 파일에서는 섹션이 3개이고, 각 섹션의 명칭도 기재되어 있었는데, 위의 사진을 보면 stolen_bytes_pespin.exe 파일은 섹션이 4개이고, 각 섹션의 명칭도 기재되어 있지 않다.

 


OllyDbg를 이용해 매뉴얼 Unpacking

 

 

OllyDbg에 stolen_bytes_pespin.exe 파일을 올리면 위와 같이 경고 메시지 창이 뜬다.

 

메시지 창의 내용은 "PE 헤더에 지정된 EP가 PE 헤더에 지정된 Code 영역 범위 밖에 있는 것으로 보아 정상적인 파일이 아니다" 라는 내용이다.

 

이는 패킹된 파일들의 특징이다.

 

 

이어서 압축 된 파일이냐 라는 내용의 메시지 창이 뜨는데 "Yes"를 선택해주면 된다.

 

 

40B0D4 주소에 JMP 문이 있는데 이는 40B0D7 주소로 점프하여 PUSHAD 명령을 수행한다.

 

PUSHAD는 현재 레지스터들을 전부 스택에 백업하는 명령인데, PUSHAD와 한 세트인 POPAD 명령이 있다.

 

POPAD 명령은 백업한 레지스터들을 복구하는 명령이다.

 

즉 패킹된 파일은 PUSHAD 명령으로 레지스터들을 백업하여 원래 프로그램의 흐름에 영향을 끼치지 않게 하고, 패커가 원하는 동작을 하고 나면 POPAD 명령으로 레지스터들을 복구하여 원래 프로그램의 흐름대로 흐르게 한다.

 

PUSHAD 명령이 있으니 ESP Trick 기법으로 언패킹을 진행한다.

 

 

F8(stepover) 키를 눌러 pushad 명령을 실행하고 나서 pushad 명령에 의해 스택에 쌓인 값들 중 1bytes에 BP를 설치한다.

 

그러면 언패킹이 끝난 후 popad 명령을 수행하면서 BP가 설치된 데이터에 접근하려고 할 때 설치된 BP에 멈추게 될 것이다.

 

이게 ESP Trick의 원리이다.

 

위와 같이 pushad 명령을 수행 후 dump 창에서 ESP 레지스터에 있는 값으로 위치를 이동한다.

 

그 후 1byte를 선택하고 마우스를 우클릭하여 Breakpoint -> Hardware, on access -> Byte

하드웨어 BP를 설치할 때 크기는 상관이 없기 때문에 반드시 Byte일 필요는ㅇ 벗다.

memory BP가 아닌 hardware BP를 설치하는 이유는 memory BP는 해당 메모리 영역 전체에 BP를 걸어버리기 때문이다.

software BP가 아닌 hardware BP를 설치하는 이유는 software BP는 코드 실행(Excution)에 대한 감지만 가능하기 때문이다.
(현재 상황에서는 해당 주소의 실행이 아닌, 데이터에 접근했을 때이므로 software BP는 적절하지 않다.)

 

 

OllyDbg 상단 메뉴바 -> Debug -> Hardware breakpoints -> 설치된 하드웨어 BP 주소가 맞는지 확인

 

 

OllyDbg 상단 메뉴바 -> Options -> Debugging options -> 위와 같이 설정

 

예외 처리와 관련된 설정을 해주지 않으면 F9 키를 눌러 디버깅을 할 때 여러 exception들을 마주하게 되고 매번 Shift + F9 키로 디버기에게 예외 처리를 하도록 넘겨줘야 하므로 귀찮기 때문이다.

 

 

예외 처리와 관련된 설정을 완료한 후 F9 키를 눌러 실행하면 위와 같이 40CCB5 주소에서 멈추게 되는데 위와 같이 화면이 보이는 건 OllyDbg에서 분석을 실패했기 때문이다.

 

 

 

OllyDbg Code 창에서 마우스 우클릭 -> Analysis -> Remove analysis from module을 하면 위와 같이 "NOT EDX" 명령어가 보일 것이다.

 

 

40CCB5 "NOT EDX" 명령어에서 멈춘 상태에서 키보드의 화살표 방향키 중 위쪽 방향키를 눌러 명령어 한 줄을 올라가면 위와 같이 POPAD 명령이 보인다.

 

40CCB4 주소의 POPAD 명령을 수행하면서 이전에 설치했던 Hardware BP 주소에 있는 데이터에 접근했기 때문에 멈추게 된 것이다.

 

다른 패커들의 파일을 언패킹할 때의 경우엔 POPAD 후에 바로 OEP(Original Entry Point)로 가는데, PESpin 패커는 POPAD 명령 이후에 뭔가를 더 수행하는 명령어들이 있지만 이는 대부분이 딱히 의미가 없는명령어들이다.

 

 

위는 실제로 UPX 패커로 패킹된 notepad.exe 파일을 매뉴얼 언패킹 하는 과정이다.

 

과정 속에 POPAD 명령 부분을 보면 길지 않은 명령어를 수행 후 OEP로 JMP 한다.

 

 

다시 stolen_bytes_pespin.exe 언패킹으로 돌아와서 F8 키를 눌러 트레이싱 하다 보면 엄청 많은 JMP문들을 볼 수 있다.

 

계속 F8 키로 트레이싱 하다 보면 40CD76 주소의 명령이 "JMP 401088"로 바뀌고, 해당 명령이 실행되면

 

 

위와 같이 401088 주소로 오게 된다.

 

아마 여기가 OEP 부분으로 예측된다.

 

stolen_bytes.exe 파일의 EP

이쯤에서 패킹이 안된 stolen_bytes.exe 파일의 EP 부분을 보면 위와 같다.

 

위의 stolen_bytes_pespin.exe 파일의 OEP로 추정되는 401088 주소의 코드와 stolen_bytes.exe 파일의 EP인 401041 주소에서 401088 주소부터 그 이후 명령들을 보면 완전히 똑같은 것을 확인할 수 있다.

 

즉 패커에 의해 원본 파일의 401041 ~ 401087 주소에 있는 명령어들이 감춰진 것이다.

 

 

다시 stolen_bytes_pespin.exe 디버깅으로 돌아와 401088 주소로 JMP된 그 상태에서 OllyDbg code 창의 스크롤을 올려보면 위와 같이 401041 ~ 401087 주소 영역이 이상한 명령어로 채워져 있는 것을 확인할 수 있다.

 

이는 패커가 Stolen Bytes 기법을 이용해 OEP 부분의 원래 코드 잘라내어 숨기고 위와 같이 의미없는 값으로 채워넣은 것이다.

 

 

참고로 PESpin 패커에 의해 잘라내어진 코드는 PESpin 패커가 추가시킨 섹션에 저장되어 있다.

 

 

그리고 원래 401041 ~ 401087 주소의 명령어들의 수행은 POPAD 명령어 이후 수 많은 JMP문들이 있는 위의 영역의 명령어들이 수행되면서 실시간으로 언패킹되면서(즉 명령어들이 실행되면서 실시간으로 다음으로 수행될 명령들이 바뀌며) 실행될 것이다.

 

 

언패킹이 완료되고 OEP 부분인 401088 주소의 명령부터 F8 키로 트레이싱하다가 4010F0 주소의 401000 함수 호출 명령 내부로 가면

 

 

위와 같이 stolen_bytes.exe와 stolen_bytes_pespin.exe 파일을 실행했을 때의 메시지가 보인다.

 


OEP dump 뜨기 실패

 

 

위에서 매뉴얼 언패킹을 진행하며 POPAD 명령어 이후 주소인 40CCB5 주소부터가 언패킹하는 코드임을 알았고, 언패킹이 끝나면 401088 주소로 JMP 한다는 것을 알았다.

(바로 위의 40CCB5 주소의 언패킹 코드 사진은 F8 키로 트레이싱하는 매 순간을 다 찍은 게 아니다. 실제로 F8 키로 트레이싱할 때는 바로바로 다음 명령이 바뀌어지기 때문에 위의 사진과는 다를 수 있다.)

 

dump를 뜰려면 40CCB5 주소부터 진행되는 언패킹 코드를 F8 키로 트레이싱하며 분석하여 OEP인 401041 ~ 401087 주소의 코드를 수동으로 복구해줘야 한다.

 

0040CCE8 PUSH EBP
0040CCE9 JMP SHORT 0040CCEC

0040CCEC MOV EBP, ESP
0040CCEE JMP SHORT 0040CCF1

0040CCF1 PUSH -1
0040CCF3 JMP SHORT 0040CCF6

0040CCF6 PUSH D021EE22 ; "PUSH 004060B0"
0040CCFB ADD DWORD PTR SS:[ESP], 301E728E ; "PUSH 004060B0"
0040CD02 PUSH 4F6228 ; "PUSH 00402688"
0040CD07 SUB DWORD PTR SS:[ESP], 0F3BA0 ; "PUSH 00402688"

0040CD0E MOV EAX, DWORD PTR FS:[0]
0040CD14 JMP SHORT 0040CD17

0040CD17 PUSH EAX
0040CD18 JMP SHORT 0040CD1B

0040CD1B MOV DWORD PTR FS:[0], ESP
0040CD22 JMP SHORT 0040CD25

0040CD25 SUB ESP, 10
0040CD28 JMP SHORT 0040CD2B

0040CD2B PUSH EBX
0040CD2C JMP SHORT 0040CD2F

0040CD2F PUSH ESI
0040CD30 JMP SHORT 0040CD33

0040CD33 PUSH EDI
0040CD34 JMP SHORT 0040CD37

0040CD37 MOV DWORD PTR SS:[EBP-18], ESP
0040CD3A JMP SHORT 0040CD3D

0040CD3D CALL DWORD PTR DS:[40FE24] ; kernel32.GetVersion
0040CD43 JMP SHORT 0040CD46

0040CD46 XOR EDX, EDX
0040CD48 JMP SHORT 0040CD4B

0040CD4B MOV DL, AH
0040CD4D JMP SHORT 0040CD50

0040CD50 MOV DWORD PTR DS:[409918], EDX
0040CD56 JMP SHORT 0040CD59

0040CD59 MOV ECX, EAX
0040CD5B JMP SHORT 0040CD5E

0040CD5E AND ECX, 0FF
0040CD64 JMP SHORT 0040CD67

0040CD67 MOV DWORD PTR DS:[409914], ECX
0040CD6D JMP SHORT 0040CD70

0040CD70 SHL ECX, 8
0040CD73 JMP SHORT 0040CD76

0040CD76 JMP 00401088

 

위의 코드는 실제로 PESpin 섹션에 저장된 "사라진 OEP 코드 부분" 이다.

 

PUSH EBP
MOV EBP, ESP
PUSH -1
PUSH 004060B0
PUSH 00402688
MOV EAX, DWORD PTR FS:[0]
PUSH EAX
MOV DWORD PTR FS:[0], ESP
SUB ESP, 10
PUSH EBX
PUSH ESI
PUSH EDI
MOV DWORD PTR SS:[EBP-18], ESP
CALL DWORD PTR DS:[40FE24]
XOR EDX, EDX
MOV DL, AH
MOV DWORD PTR DS:[409918], EDX
MOV ECX, EAX
AND ECX, 0FF
MOV DWORD PTR DS:[409914], ECX
SHL ECX, 8
55 8B EC 6A FF 68 B0 60 40 00 68 88 26 40 00 64 A1 00 00 00 00 50 64 89 25 00 00 00 00 83 EC 10
53 56 57 89 65 E8 FF 15 04 60 40 00 33 D2 8A D4 89 15 18 99 40 00 8B C8 81 E1 FF 00 00 00 89 0D
14 99 40 00 C1 E1 08

 

사라진 OEP 코드 부분에서 JMP 문들을 제거하고 정리한 코드는 위와 같다.

 

위의 코드를 stolen_bytes_pespin.exe 파일의 401041~401087 주소에 복사한다.

 

 

Ctrl + F2 키를 눌러 재실행 한 후 Hardware BP와 F8 키를 이용해 401088 까지 다시 온다.

 

55 8B EC 6A FF 68 B0 60 40 00 68 88 26 40 00 64 A1 00 00 00 00 50 64 89 25 00 00 00 00 83 EC 10
53 56 57 89 65 E8 FF 15 04 60 40 00 33 D2 8A D4 89 15 18 99 40 00 8B C8 81 E1 FF 00 00 00 89 0D
14 99 40 00 C1 E1 08

 

위의 바이너리 코드는 사라진 OEP 코드 부분을 정리한 코드의 바이너리 코드이다.

 

 

스크롤을 위로 올려 401041 ~ 401087 주소를 드래그 한 후 마우스 우클릭 -> Binary -> Binary paste 한다.

 

 

그러면 위와 같이 stolen_bytes.exe의 EP와 같이 수정된다.

 

 

이어서 401041 주소를 선택 후 마우스 오른쪽 -> New origin here을 선택하여 EIP를 변경한다.

 

 

OllyDump 플러그인을 이용해 덤프를 뜬다.

 

 

하지만 덤프 뜬 파일을 실행하면 뻗어버린다.

 

 

OllyDump는 실패하는 경우가 많기 때문에 PE Tools와 import REConstructor을 이용해서 해봤지만 IAT 불러오는 것에서 실패했다.

 

아무래도 IAT를 불러오는 것에서 에러가 발생하는 것 같다.

 

매뉴얼 언패킹은 성공했지만, 덤프 뜨는 것에는 실패했다.

 


https://www.hackerschool.org/Sub_Html/HS_University/REVERSING/Unpacking/PESpin_1.32/PESPIN_1.32.html

 

https://sean.tistory.com/376

 

 

반응형

+ Recent posts