본문 바로가기

High Level Technique/Reversing

PE Image Switching

PE Image Switching


PE Image Switching은 다른 프로세스를 실행한 후에 가상 메모리의 PE Image를 자신의 것과 바꾸는 것을 말합니다.


PE 구조를 공부했을때 아래와 같은 그림을 봤었습니다.



일반적으로 PE 파일은 file Alignment와 Section Alignment가 다르고 각 섹션의 Raw Data Size 와 Virtual Size가 다르기 때문에 PE Image 사에 형태가 달라집니다.





PE Image Switching


A.exe 를 SUSPEND 모드로 실행한 후 B.exe의 PE Image를 Mapping 시켜 A.exe 프로세스 메모리 공간에서 실행하는 기법입니다.


PE Image를 변경하면 프로세스는 A.exe 이지만 메모리 공간에 매핑된 PE Image는 B.exe 이기 때문에 실행이 다릅니다.


간단히 요약하자면 A는 그냥 겉으로만 보여지는 것이고 속으론 B가 실행이 되는 것이죠.



실습예제인 DebugMe3.exe를 분석해 보도록 하겠습니다.



Process Explorer를 통해서 확인해 본 결과 fake.exe로 실행이 되고 있지만 real.exe가 실행된 것을 볼 수 있습니다.



이제 분석을하기 위해서 OllyDBG를 실행합니다. 이때 Arguments로 fake.exe real.exe를 입력해 줍니다.





Func1() - 0x401063



트레이싱을 하다보면 CALL 401150을 볼 수 있습니다.



이 부분에서는 Win32 API를 호출하고 있습니다. 여기서는 real.exe 내용이 메모리에 저장됩니다.



CreateProcessW - 0x40108E



그 다음으로는 CreateProcess API가 나타납니다. 여기서는 fake.exe를 SUSNPEND 모드로 생성하는 것입니다. 프로세스가 멈춘 상태에서 메모리를 조작하기 위해서 SUSNPEND 모드로 생성합니다.




Func2() - 0x1010B2



해당 부분을 따라 들어가면 아래와 같은 루틴이 나타나게 됩니다.








fake.exe 실제 매핑주소 구하기


401218 주소에서 GetThreadContext를 호출합니다. fake.exe 프로세스의 메인 쓰레드 Context를 구합니다.



fake.exe 프로세스의 메인 쓰레드 Context를 구하는 이유는 PEB를 구하기 위해서인데 실제 매핑 주소가 PEB.ImageBase에 저장되어 있기 때문입니다.


40125E 주소에서 ReadProcessMemory를 호출해서  fake.exe 프로세스의 매핑 주소를 구합니다.



GetThreadContext - struct CONTEXT.Ebx - PEB.ImageBase - 실제 로딩된 프로세스 이미지






real.exe ImageBase 구하기


40128C, 40128F 주소의 명령은 real.exe 파일의 PE 헤더 정보를 읽어서 ImageBase를 구합니다.



EDI는 MEM_FILE_REAL_EXE의 주소입니다. Func1()에서 받은 메모리 시작주소입니다. real.exe 파일의 PE Header를 가리키고 있다고 볼 수 있습니다.


EDI+3C가 가리키는 것은 IMAGE_DOS_HEADER의 e_lfanew 값입니다. 이 값을 EAX에 저장합니다.


40128F의 명령어는 EAX+EDI+34를 하고 있는데, EAX+EDI는 e_lfanew + Start of PE 입니다. 즉 IMAGE_NT_HEADERS 구조체 시작주소이고 EAX+EDI+34는 IMAGE_OPTIONAL_HEADER.ImageBase 값입니다. 이 값을 ECX에 저장합니다.





fake.exe의 ImageBase와 real.exe의 ImageBase 비교


401297주소에서 fake.exe의 실제 매핑주소와 real.exe의 ImageBase 값을 비교합니다.



여기서 두 값이 같으면 40129F로 이동하고 다르면 4012EA로 이동합니다.


ImageBase가 같은경우 fake.exe가 이미 매핑이 되어있기 때문에 real.exe를 매핑시키면 충돌이 일어나기 때문에 먼저 fake.exe를 언매핑해야 합니다. SUSPEND 모드이기 때문에 언매핑해도 오류가 발생하지 않습니다.




언매핑 할 때는 ZwUnmapViewOfSection을 이용합니다.



ImageBase가 다른경우 fake.exe를 언매핑할 필요가 없습니다. fake.exe 프로세스 공간에 real.exe를 매핑시키면 됩니다. 

이때 PEB의 ImageBase를 변경해서 PE Image가 real.exe라는 것을 알려줘야 합니다. 4012FC에서 이와 같은 작업을 합니다.




Func3() - 0x4010D1


real.exe를 fake.exe 프로세스에 매핑 시켜야 합니다. 해당 주소를 들어가면 아래와 같은 루틴이 나타납니다.




트레이싱을 하다보면 401383 주소에서 VirtualAllocEx를 호출하는 것을 볼 수 있습니다.




Arg2는 할당받고자 하는 메모리 시작 주소입니다. real.exe의 ImageBase 400000으로 되어있습니다. Arg3는 할당받을 메모리 크기를 의미합니다. real.exe의 SizeOfImage A000 으로 되어있습니다.





PE Header Mapping


real.exe의 공간을 확보 했으므로 rea.lexe를 fake.exe에 매핑해야 합니다.



WriteProcessMemory를 이용해서 real.exe의 PE Header를 할당받았던 메모리 영역에 씁니다.





PE Section Mapping


섹션의 개수만큼 돌면서 WriteProcessMemory를 호출합니다. 여기까지 하면 모두 매핑이 완료된 상태이지만 정상적으로 실행되지 않습니다. EP를 변경해야 실행이 됩니다.




EP 변경


CONTEXT 구조체를 구해서 Eip를 확인합니다.



CONTEXT에서 중요한 멤버는 EAX와 EIP 입니다. EAX의 값은 401041로 fake.exe 프로세스 내의 주소이고 Eip는 770F8F00으로 RtlUserThreadStart 주소입니다.

EIP는 실핼될 주소를 나타내고 EAX는 리턴값을 나타내는데 SUSPEND 모드로 생성된 fake.exe 프로세스가 RESUME 되면 RtlUserThreadStart를 실행하고 EP 주소(CONTEXT.Eax)로 이동하게 됩니다.


여기서 PE Image를 fake.exe에서 real.exe로 변경했기 때문에 CONTEXT.Eax의 값을  real.exe의 EP 주소인 401060으로 변경해 주어야 합니다.



4014A2, 4014A5 주소는 EDX에 rea.exe의 EP 주소를 넣습니다.

4014B3 주소는 EBP-2D0에 CONTEXT.Eax의 값을 넣습니다.


위 과정을 통해서 fake.exe의 PE Image를 real.exe로 변경한 것입니다.




ResumeThread()


4010F0에서 real.exe PE Image를 가진 fake.exe가 실행됩니다.










디버깅


위 과정들을 통해서 바뀐 PE Image 를 디버깅 하는 방법을 알아보도록 하겠습니다.


fake.exe를 디버거에 붙인 후에 real.exe의 EP 주소를 알고 해당 주소에 무한루프 코드를 넣어줍니다.


그리고 fake.exe를 run으로 실행 시키면 변경된 fake.exe는 무한루프에 빠지게 되고 새로 실행한 디버거를 이용하여 attach로 fake.exe를 붙여서 확인하면 무한루프에 빠진 곳에 멈춰있게 됩니다.


다시 이 부분을 원래 코드로 바꾸고 디버깅을 진행하면 됩니다.









이 부분은 나중에 여러번 공부해야봐야 이해가 될 듯.

'High Level Technique > Reversing' 카테고리의 다른 글

C++ 분석  (0) 2016.08.13
Debug Blocker  (0) 2016.08.10
Self Creation Debugging  (0) 2016.08.09
Service Debugging  (0) 2016.08.09
Advanced Anti Debugging  (0) 2016.08.09