본문 바로가기

[Study & Job]/[기타]

OEP를 찾아내자

보통 OEP를 찾는 방법은 여러가지가 있습니다. 단순히 코드를 하나하나 분석해가며 진행하는 방법에서부터 Stack을 이용한 방법, VirtualAlloc()를 이용하는 방법, LoadLibrary()를 이용하는 방법, Exception Handler를 이용하는 방법 등 요령껏 Packer마다의 다양한 방법이 이용될 수 있습니다.


1. Stack Based

보통 Anti-Reversing 기법이 없는 일반적인 Packer들은 언팩루틴이 끝나고 OEP로 진입하기 전에 반드시 어떠한 현태로든지 스택에 저장된 레지스터를 복구하게 됩니다. 즉, Unpacking이 끝나고 나면 스택에 저장되어 있던 레지스터들을 복구하여 본래 프로그램에 재사용한다는 것입니다.


그럼 스택에서 레지스터들이 POP되는 순간을 잡아내면, OEP로 진입할 수 있는 힌트를 찾아낼 수 있지 않을까 라는 생각을 해볼 수 있습니다.


(1) OEP Find-1

AsPack으로 Packing되어 있는 프로그램을 올리디버거로 까보면 PEP가 나오는데 자세히 들여다보면 처음부터 PUSHAD로 시작하는 첫번째 Instruction이 보일 것입니다. 일단 PUSHAD 명령어는 EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI 모든 레지스터들을 스택에 PUSH하는 역활을 합니다. 물론 DWORD형태로 저장을 합니다. 아 물론 PUSHA는 WORD형태로 저장합니다. 


보통 일반적인 Packer는 이와같이 특정 위치의 레지스터들의 백업을 만들어 놓기 위하여 처음에 PUSH 명령을 사용합니다. 스택에 레지스터 백업들을 저장하고 Unpack 작업을 위한 루틴이 시작 된 후, Unpack 루틴이 끝나고 본래 프로그램의 Entry Point가 시작이 되기 전에 POP Instruction으로 처음에 스택에 저장되어 있던 레지스터들을 POP합니다. 이렇게 모든 레지스터들을 복귀시켜놓고 프로그램 동작을 위한 루틴이 시작하는 것입니다. 


구럼 여기서 어떻게 OEP를 잡느냐면!!

PUSHAD이후 스택에 EDI 레지스터 값이 있눈데 TOP에 위치한 EDI 레지스터에 BreakPoint를 걸어주면 Unpack 루프가 끝나고 프로그램의 시작을 위하여 스택내의 레지스터들을 복원해주는 시점에서 브레이크가 걸린다. 한데 잠깐 여기서 넘어갈 것이 PUSH XXXXXX / RETN이 CALL XXXXXX 과 같은 명령이라는 점이다. 코드에서 그 이후 RETN명령에서 Step Over시키면 EP로 들어와 있는 것을 볼수 있다.


(2) OEP Find -2

이번에 PE Compact의 OEP를 찾아보면 아래와 같습니다.

ASPack과 다르게 PUSH명령이 없구 41B0F4주소를 EAX에 넣고 저장한 다움 스택에 PUSH합니다.

그리고 FS:[0]을 스택에 PUSH하는데 이것은 예외를 불러 일으키는 것입니다. 잘못된 주소에 접근하거나 0으로 나누거나 하는 등의 행위를 하게 되면 예외 상황이 발생되게 됩니다. 예를 들어 ECX 레지스터 값을 DS:[0]에 넣는다거나 하는 등의 행동이다!


반응형