본문 바로가기

ReverseEngineering

PEB에서 로딩된 DLL 정보 찾기

악성코드에 포함된 Shellcode를 보면 원하는 DLL를 로딩하기 위해 PEB(Process Environment Blcok)에서 DLL의 Base Address를 가져온다. 이 과정을 윈도우 XP와 7으로 나누어 살펴보자.


[윈도우 XP]

먼저 PEB의 구조를 그림으로 나타내면 아래와 같다. PEB 구조체의 LDR 멤버는 _PEB_LDR_DATA 구조체의 포인터이다. 그리고 _PEB_LDR_DATA 구조체에는 양방향 연결 리스트인 _LIST_ENTRY 타입의 InLoadOrderMoudleList, InMemoryOrderModuleList, InInitializationOrderModuleList라는 세 가지 멤버를 가지고 있다. 이 멈버들은 각각 _LDR_DATA_TABLE_ENTRY 구조체의 InLoadOrderLinks, InMemoryOrderLinks, InInitializationOrderLinks 멤버의 포인터이다. 바로 이 _LDR_DATA_TABE_ENTRY 구조체가 프로세스 정보와 프로세스에 로딩되는 DLL의 정보를 가지고 있고, 이 구조체를 이용하여 로딩되는 DLL의 Base Address를 직접 구할 수 있다. 즉, 프로세스에 로딩되는 DLL마다 _LDR_DATA_TABLE_ENTRY 구조체가 하나씩 생성되는데, 연결 방법이 세 가지인 것이다. 첫 번째 _LDR_DATA_TABLE_ENTRY는 해당 process의 정보를, 두 번째는 ntdll.dll의 정보, 세 번째는 kernel32.dll의 정보를 가지고 있다. 프로세스에 로딩되는 순서에 따라 연결 리스크가 형성되는 것 같다. 


주의해야 할 것은 InLoadOrderModuleLinks와 InMemoryOrderLinks는 순차적으로 연결되는 것에 반해, InInitializationOrderLinks는 그렇지 않다. _PEB_LDR_DATA.InInitializationOrderModuleList는  첫 번째 _LDR_DATA_TABLE_ENTRY가 아닌 두 번째 _LDR_DATA_TABLE_ENTRY의 InInitializationOrderLinks를 가리킨다. ntdll.dll은 kernel32.dll과 연결되지만, kernel32.dll은 그 다음 _LDR_DATA_TABLE_ENTRY가 아닌 다른(3번 건너띈) _LDR_DATA_TABLE_ENTRY와 연결된다. (이 부분은 해당 글에서 다루는 범위를 넘어서므로 설명하지 않는다.)




이번에는 실제 shellcode에서 kernel32.dll의 Base Address를 구하는 assembly를 보면 아래와 같다. FS:[30]으로 PEB의 주소를 구하고, _PEB.LDR과 PEB_LDR_DATA.InInitalizationOrderModuleList을 따라가서 ntdll의 _LDR_DATA_TABLE_ENTRY 구조체로 들어간다. 그리고 그 다음 _LDR_DATA_TABLE_ENTRY.DllBase 멤버에서 kernel32.dll의 Base Address를 가져온다.




위 과정을 WinDbg로 분석해 보면 아래와 같다. 첫 번째 _LDR_DATA_TABLE_ENTRY는 현재 process의 정보를 가지고 있고, 두 번째와 세 번째 _LDR_DATA_TABLE_ENTRY는 각각 ntdll.dll과 kernel32.dll의 정보를 가지고 있다. process의 _LDR_DATA_TABLE_ENTRY.InInitializationOrderLinks 멤버의 값은 0으로 다른 LDR과 연결되지 않는다.





[윈도우 7]

윈도우 7부터 kernelbase.dll이라는 새로운 dll이 추가되었다. 윈도우 7 이전에는 kernel32.dll 함수들이 ntdll.dll 함수를 호출하였지만, 윈도우 7부터는 kernel32.dll 함수는 kernelbase.dll 함수를 호출하고 kernelbase.dll 함수는 ntdll.dll의 함수를 호출한다. 따라서 _LDR_DATA_TABLE_ENTRY의 연결 리스트도 약간에 변화가 생겼다. 좀 더 자세하게 설명하기 위해 FLink와 BLink를 나눠서 그렸다.


ntdll.dll의 _LDR_DATA_TABLE_ENTRY.InInitializationOrderLinks가 kernel32.dll을 가리키는 것이 아니라 KERNELBA를 가리킨다. 그리고 KERNELBA는 kernel32.dll과 연결된다. 따라서 윈도우 7에서 위의 shellcode를 실행시키면 kernel32.dll의 Base Address가 아닌 KERNELBA의 Base Address를 가져온다.