안녕하세요. 내일 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 입니다.

그럼 olly의 경우는 어떻게 목록을 얻을까요?(클릭)





계속, 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에 코드검사루틴을 추가하면 계속 공방이 이뤄지겠죠 ^^



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






  1. Commented by 한자돌이 at 2008.11.15 17:14 신고

    헛... jumpzero님이 SCA 2008 세미나에 나오신다니 몰랐었네요 ^^;;;

    못가서 아쉽지만, 강병탁(window31)님께서 강의하시는 내용 중,

    'PEB 조작 없이 DLL을 숨기는 내용' 이 살짝 유추가 되는 내용이네요..^^;

    좋은 글 잘 읽었습니다.^^; PEB 링크를 끊어서만 숨길 수 있었는데,

    LoadLibrary를 직접 구현할 생각은 못해봤네요.

    (물론, 구현할 생각은 해봤지만 DLL 숨길 용도로는 생각을 못해본…)

  2. Commented by at 2008.11.15 22:12

    비밀댓글입니다

    • Commented by jz- at 2008.11.17 10:14 신고

      ㅋㅋ 저도 좋습니다, 물론 수락도 했구요^^

      근데 제가 메신저를 잘 안들어가서, ㅋㅋ

      수락 당시에는 접속안해계시더군요

  3. Commented by HS at 2008.11.18 16:22 신고

    오호;; 이런 방법도 있었군요~~ㅋ 잘보고 갑니다 ^^

  4. Commented by Externalist at 2008.11.19 00:23 신고

    [QUOTE]LoadLibraryA함수를 싸그리 분석한 적이 있습니다.[/QUOTE]
    =_=乃 U rock dude.
    잘 봤습니다...^^

  5. Commented by vbdream at 2008.12.14 12:42 신고

    Olly가 DLL 정보를 Debugger Event에서 얻는다고 하지만,

    PEB Unlinking 수단으로 숨겼을 때에도 나오진 않더군요 ...

    뭔가 복잡한 메커니즘이(?)

    • Commented by jz- at 2008.12.14 23:59 신고

      아 그런가요??

      '디버거니까 당연히 저걸로 했겠지'라 생각했는데 아닌가보군요.

      생각해보면 돌고있는 프로세스에 attach하는 경우도 있으니,

      (그 경우에는 이미 DLL들이 로드돼있으니 DEBUG_EVENT가 오지 않으므로)

      DLL정보는 항상 공통으로 쓸 수 있는 LDR_DATA에서 얻어오나 봅니다

      ^^ 제보(?) 감사!!

  6. Commented by at 2010.05.18 11:30 신고

    포스트된 글을 보고 몇자 적어 봅니다.

    모듈을 숨기는 것에 대한 글을 쓰셨는데, LoadLibraryEx를 통한 모듈로드는 모듈을 메모리에 파일 매핑하는 것으로 실제 DLL 모듈로써의 역할을 전혀 하지 못합니다. Relocation 되어야 하는 것들도 처리 되지 않은 그냥 데이터 덩어리죠.

    모듈을 숨기기 위해 UnLink 기법을 쓰는 이유는 DLL 모듈로서 사용가능(??)하면서 DLL 리스트에서 숨기기 위해서죠. 결론적으로 UnLink 기법을 말씀하신 LoadLibraryEx로 대체할 수 없습니다. ^^

    • Commented by jz- at 2010.05.18 23:06 신고

      네 맞는 말씀입니다. ^^

      저 옵션을 쓰고 DLL내에 실행코드까지 쓰려면 쉘코드 짜듯 절대주소를 사용하지 않는(Location free) 코드로 작성하면 되긴 하는데, 그럼 dll로 짜서 올리는 장점이 사라지죠. ㅎㅎ 요샌 그냥 간단하게 섹션헤더 읽어서 메모리에 올리고 직접 임포트 하는 일도 흔하더군요.

      댓글 감사합니다.

  7. Commented by 3 at 2014.04.26 18:17 신고

    님 프로세스 선택해서 해당 프로세스에서 특정 모듈 리스트에서 제거하는(이젝션x) 소스좀 올려주세요

  8. Commented by 3 at 2014.04.26 18:17 신고

    님 프로세스 선택해서 해당 프로세스에서 특정 모듈 리스트에서 제거하는(이젝션x) 소스좀 올려주세요

  9. Commented by 3 at 2014.04.26 18:17 신고

    님 프로세스 선택해서 해당 프로세스에서 특정 모듈 리스트에서 제거하는(이젝션x) 소스좀 올려주세요