안녕하세요. 내일 sca festival 2008 발표준비를 마치고, 오밤중에 귀찮은 몸 이끌고 포스팅을 합니다.

오늘 써보려는 것은, LordPE나 OllyDbg같은 다른 분석툴이, 프로세스에 로드된 DLL 목록을 얻는데,

이를 회피하는 방법에 대해서 입니다. (물론 ring3에서 입니다.)

LordPE는 프로세스에 로드된 dll들을 아래와 같이 보여줍니다.


LordPE가 보여주는 dll list



그리고 Ollydbg는 로드된 모듈 목록을 아래와 같이 보여줍니다.

OllyDbg가 보여주는 dll list



이 툴들은 다른 프로세스에 로드된 dll 목록을 어떻게 구하는 걸까요?

그것부터 알아야 숨기든 말든 하겠죠?

먼저 모든 프로세스는 메모리 어딘가에 PEB를 갖습니다. 프로세스의 정보가 들어가있죠.

자세한건 http://undocumented.ntinternals.net/ 에서 보시거나, 구글링하면 많이 나옵니다.

(PEB : http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Process/PEB.html )

여기에 BeingDebugged나 ImageBase, 프로세스가 생성될 때 command line, 환경변수, 힙정보 등 여러가지가 표시되는데, 그 중 로드된 DLL의 정보도 있습니다.

PPEB_LDR_DATA           LoaderData; 가 바로 그건데요.

NT Internals에서 보면
typedef struct _PEB_LDR_DATA {
  ULONG                   Length;
  BOOLEAN                 Initialized;
  PVOID                   SsHandle;
  LIST_ENTRY              InLoadOrderModuleList;
  LIST_ENTRY              InMemoryOrderModuleList;
  LIST_ENTRY              InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

이렇게 멤버가 있습니다.

위 세가지는 무시해도 되고, 아래 세가지 LIST_ENTRY 보이시죠?

저게 dll의 linked list 시작입니다.

이름보면 아시겠지만, 로드순서대로의 link, 메모리 정렬대로의 link, 초기화 순서대로의 link 세가지 link가 있습니다.

물론 모든 dll이 순서만 바뀐채 각 link에 존재하겠죠^^

LordPE는 저 정보를 따라 들어가서 dll의 목록을 구해냅니다.

비단 LordPE뿐 아니라 dll 목록을 구해내는 모든 프로그램이 저런 방식을 씁니다.

이걸 Wrapping한 API가 Module32First, Module32Next 입니다.





계속, PEB에 대해 얘기해보죠.

그럼 저 LIST_ENTRY linked list에서 내 모듈을 빼면 어떻게 될까요? LordPE에 보이지 않겠죠.

방법은 몇가지 있습니다.

1. LoadLibrary 함수를 쓰는 대신, 직접 dll 파일을 열어서 FileMapping 해주고, 
   Import 세팅해주고 EntryPoint 파싱해서 실행해주면 됩니다.

2. 아니면 LoadLibrary 함수를 쓰는 대신, 직접 linkedlist를 뒤비며 내 모듈의 link를 찾아내 그 link를 깨면 결과적으로 그 link에 내 모듈은 없어집니다.


1번은 어마어마한 작업이고(새로 LoadLibrary를 만들어야하니....)

2번이 만만해보이네요.

하지만, 굳이 링크를 깰 필요도 없습니다.

예전에 행위기반 엔진을 만들며 로드된 dll목록을 구할 일이 있었는데, 어떤 모듈은 link에 없어서 어떻게 된 일인가 싶어서 LoadLibraryA함수를 싸그리 분석한 적이 있습니다.

이 때 알게 되었는데, 특정 flag를 이용하면 됩니다.

Kernel32.LoadLibraryA함수는 내부적으로 LoadLibraryExA를 지나 ntdll.LdrLoadDll 함수로 들어가게 됩니다.

이 때 LoadLibraryExA 부터는 새로운 인자 flag를 받는데요.

LoadLibraryEx( "testloaded.dll", NULL, LOAD_LIBRARY_AS_DATAFILE );

이렇게 쓰면 저 dll은 data 전용 dll로 취급을 받으며, 코드취급을 못받습니다.

DllMain도 호출되지 않고, LIST_ENTRY 링크에도 들어가지 않게 되죠.

(다만 DllMain이 호출 안되면 직접 DllMain을 호출해주던지, DllMain을 안쓰던지, 다른 방법이 필요합니다.)




이렇게 하면 dll 목록에 보이지 않습니다. 심지어 디버거에게 LOAD_DLL_DEBUG_EVENT 조차 가지 않습니다.

실험해보죠.

간단하게 짠 코드입니다. testloaded.dll은 DllMain에서 메시지박스를 출력하는 단순dll입니다.

먼저 그냥 LoadLibrary 할 경우 아래처럼 DllMain도 호출되고, LordPE에서도 보입니다.

그냥 LoadLibrary 하는 code



 
DllMain 호출 됐음

 
LordPE에 모듈 탐지됨




 
이번엔 LOAD_LIBRARY_AS_DATAFILE flag 주고 LoadLibraryEx로 로드


 
DllMain 호출 안되고 바로 main함수내의 메시지 박스 호출


 
LordPE에 탐지 되지 않음



로드 된게 맞긴 맞나 확인해보죠. TSearch로 메모리를 탐색해보겠습니다.


0x10000000에 로드 돼있는 testloaded.dll 확인


자 여기까지입니다.

간단한 dll list 회피 방법이었습니다.

이 방법은 예전에 제가 포스팅한 Anti-Injection 을 뚫진 못합니다.

그걸 뚫으려면 메모리를 패치 후 inject 해야 할 것입니다.

물론 함부로 패치되지 않도록 Anti-Injection에 코드검사루틴을 추가하면 계속 공방이 이뤄지겠죠 ^^



감사합니다. 이제 자야겠네요 크크