티스토리 뷰

PE Image Switching 

PE Image Switching 기법은 어떤 프로세스(A.exe)를 SUSPEND 모드로 실행한 후 전혀 다른 PE 파일(B.exe)의 PE Image를 매핑시켜 A.exe 프로세스 메모리 공간에서 실행하는 기법입니다.

ex) A.exe 프로세스 생성 -> A.exe PE Image 제거 -> B.exe PE Image 로딩 -> B.exe PE Image 실행

PE Image를 변경하면 프로세스 이름은 A.exe이지만, 실제 프로세스 메모리에 매핑된 PE Image는 B.exe이기 때문에 원본(A.exe)과는 전혀 다른 동작을 하게 됩니다. A.exe는 겉모습만 남아있는 껍데기 이고, B.exe가 실제로 실행되는 진짜배기 입니다.


DebugMe3.exe 파일은 첫 번째 인자(fake)와 두 번째 인자(real)을 받아서 fake를 실행하는 척 하지만 실제 구동되는 프로세스는 두 번째 입니다. fake로 calc.exe를 두고, 두 번째 인자로 notepad.exe로 두겠습니다.


밑을 보시면 calc.exe를 실행하고 있지만, 실제로 구동되는 프로세스는 notepad.exe입니다.

* WinXp에서는 PE Image Swiching 기법이 잘 동작하지만, Win7에서는 시스템 프로세스에 대한 보안이 강화되어 잘 동작하지 않습니다.


올리디버거로 Open을 통해 Arguments에 인자의 값들을 차례대로 적습니다. 첫 번째 인자는 껍데기이고, 두 번째 인자가 진짜배기인 것을 위에서 설명했습니다.


main() 함수에 들어와서 트레이싱 하다보면 CreateFile() 함수로 notepad.exe를 OPEN합니다.


아래로 내려오면 파일 사이즈를 확인하는 GetFileSize()를 통해 체크하고, ReadFile()을 통해서 notepad.exe 파일을 통째로 메모리에 읽어 들입니다. 이제 메모리에는 notepad.exe 파일의 내용이 저장되어 있습니다.


함수를 빠져나와서 내려가다 보면 CreateProcess로 껍데기 프로세스 calc.exe 프로세스를 생성합니다. calc.exe 프로세스를 SUSPEND 모드로 생성한다는 의미입니다. 

* SUSPEND 모드로 생성하는 이유는 물론 프로세스 실행을 멈춘 상태에서 메모리를 조작하기 위함입니다.

GetThreadContext() 함수를 호출하여 calc.exe 프로세스의 메인 스레드 컨텍스트(CONTEXT)를 구합니다. calc.exe 프로세스의 메인 스레드 컨텍스트를 구하는 이유는 PEB(Process Environment Block)을 구하기 위해서입니다. 그리고 프로세스의 실제 매핑 주소는 PEB.ImageBase 멤버에 저장되어 있습니다.


이제 ReadProcessMemory() 함수를 통해서 calc.exe 프로세스의 매핑 주소를 구합니다.


현재 calc.exe 프로세스는 SUSPEND모드로 생성된 채 정지되어 있습니다. 프로세스가 생성되면 시스템의 PE Loader는 EBX레지스터의 초기 값을 PEB 구조체의 주소로 세팅합니다. 따라서 EBX 레지스터 값을 구하기 위해서는 CONTEXT를 구해야 합니다. 


<프로세스의 실제 매핑 주소를 구하는 방법>



아래 두 줄을 보면 윗 줄은 notepad.exe(real) 파일의 PE 헤더 정보를 읽어서 ImageBase를 구하는 코드입니다.

EDI = notepad.exe = Address of IDH

EDI + 3C = IDH.e_Ifanew

EDI는 notepad.exe 파일의 PE 헤더를 가리키고 있다고 볼 수 있습니다. 따라서 EDI + 3C가 의미하는 것은 IMAGE_DOS_HEADER 구조체의 e_Ifanew 멤버입니다. 이 값을 EAX에 저장하는 것입니다. 이 부분은 PE 구조를 익히고 보면 도움이 많이 됩니다.

두 번째 줄에서 EAX+EDI + 34에서 EAX+EDI = IDH.e_Ifanew + Start of PE = IMAGE_NT_HEADERS 구조체가 시작 주소, EAX+EDI+34는 IMAGE_OPTIONAL_HEADER.ImageBase 멤버를 의미합니다. 결국 ECX 레지스터에 notepad.exe 파일의 ImageBase 값이 저장이 됩니다.


아래 줄에서 CMP 문은 위에서 구한 notepad.exe(real)의 ImageBase 값과 calc.exe(fake) 프로세스의 실제 매핑주소 값을 비교합니다. 같다면 아래로 내려가서 ntdll.dll을 로드하여 ZwUnmapViewOfSection() 함수를 통해서 calc.exe(fake)의 PE Image를 언매핑 해줘야 합니다. calc.exe(fake) 프로세스는 SUSPEND 상태이기 때문에 PE Image를 언매핑해도 프로세스에 오류가 발생하지는 않습니다.


위에서 CMP를 통해 두 개의 프로세스의 ImageBase 값이 다르다면 굳이 calc.exe(fake)의 PE Image를 언매핑 시킬 필요가 없습니다.

 calc.exe 프로세스의 가상 메모리 공간에 notepad.exe(real)의 PE Image를 위해 필요한 공간을 할당하고 noteapd.exe(real)을 매핑시키면 됩니다.

그리고 PE 로더에게 calc.exe(fake) 프로세스의 PE Image는 400000 주소의 notepad.exe(real)라는 것을 알려주어야 합니다. 이 작업 간단히 PEB의 ImageBase 멤버를 변경하면 됩니다. 아래의 WriteProcessMemory() 함수를 이용하여 calc.exe(fake)프로세스의 PEB.ImageBase 값을 notepad.exe(real) 파일의 ImageBase로 변경하고 있씁니다.

위 과정들을 통해서 calc.exe(fake) 프로세스의 PE Image는 언매핑이 되었습니다.


[PE 헤더 매핑]

이제 notepad.exe(real)파일을 calc.exe(fake) 프로세스에 새롭게 매핑시킬 차례입니다. VirtualAllocEx() 함수를 통해서 calc.exe(fake) 프로세스에 notepad.exe(real)의 PE Image를 위한 메모리 공간을 할당받기 위한 것입니다. 

VirtualAllocEx의 두 번째 인자로 lpAddress를 보면 400000 은 할당받고자 하는 메모리 시작 주소입니다. 이 값은 notepad.exe(real)의 ImageBase(400000)로 되어 있습니다. 세 번째 인자는 메모리의 크기를 의미합니다. 이 값은 notepad.exe의 SizeOfImage(A000)으로 되어 있습니다.



[PE Section 매핑]

이제 notepad.exe(real)의 PE Image를 위한 메모리 공간이 생성되었으므로 notepad.exe를 calc.exe 프로세스에 매핑할 차례입니다. WriteProcessMemory() 함수를 이용하여 notepad.exe(real)의 PE 헤더를 좀 전에 할당 받은 메모리 영역에 씁니다. 



[EP 변경]

현재까지 calc.exe 프로세스는 SUSPEND 모드로 생성된 상태입니다. 이렇게 SUSPEND 모드로 생성된 프로세스가 Resume되면, 프로세스의 메인 스레드는 과연 어떤 코드를 실행하는지 알아보기 위해 메인 스레드의 CONTEXT 구조체를 구해서 Eip 멤버를 확인하면 됩니다.

CONTEXT 구조체에서 중요한 2개의 멤버는 EAX와 EIP 멤버입니다. 각각 구조체 시작 주소에서 B0과 B8 옵셋 만큼 떨어진 위치에 존재하게 됩니다.EAX 값은 00401041 으로 calc.exe(fake)의 EP 주소입니다. EIP 값은 7C7E0705 입니다. 이 주소는 ntdll.RtlUserThreadStart() 함수 시작 주소입니다. 


즉, SUSPEND 모드로 생성된 calc.exe 프로세스가 RESUME 되면 가장 먼저 ntdll.RtlUserThreadStart() 함수를 실행하고, 결국 EP 주소(CONTEXT.EAX)로 가게 됩니다. 이 전에 calc.exe 프로세스의 PE Image를 notepad.exe 로 변경했기 때문에 CONTEXT.EAX를 notepad.exe의 EP 주소(401060)로 변경해야 합니다. 

아래의 SetThreadContext() 함수로 변경 사항이 calc.exe 프로세스에 적용되고, 이제 calc.exe 프로세스의 PE Image를 기존의 calc.exe에서 notepad.exe로 변경하는 작업을 완료 했습니다.


마지막으로 ResumeThread()함수를 호출하여 calc.exe 프로세스를 Resume 시킵니다.


아래와 같이 calc.exe가 DebugMe3.exe파일 자식 프로세스로 생성 되었지만, notepad.exe가 실행이 됩니다. 



참고 : 리버싱 핵심원리 

'리버싱 > 정리' 카테고리의 다른 글

VS2015 에서 어셈블리 개발환경 만들기  (0) 2016.01.06
RVA to RAW 계산  (0) 2015.12.29
[Self Creation 디버깅 방법]  (0) 2015.08.13
[서비스 디버깅 방법]  (0) 2015.08.12
서비스 프로세스 동작 원리  (0) 2015.08.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함