DLL Ejection 은 프로세스에 강제로 삽입한 DLL 을 빼내는 기법입니다.

기본 동작 원리는 CreateRemoteThread API 를 이용한 DLL Injection 의 동작 원리와 같습니다.



DLL Ejection 동작 원리


먼저 지난번 강좌에서 소개되었던 CreateRemoteThread() API 를 이용한 DLL Injection 의 동작 원리를 생각해 보겠습니다. 대상 프로세스로 하여금 LoadLibraryA() API 를 호출하도록 만드는 것이었지요.

- 참고 : DLL Injection - 다른 프로세스에 침투하기 (2)

마찬가지로 DLL Ejection 의 동작 원리는 대상 프로세스로 하여금 FreeLibrary() API 를 호출하도록 만드는 것입니다.
즉, CreateRemoteThread() 의 lpStartAddress 파라미터에 FreeLibrary() API 주소를 넘겨주고, lpParameter 파라미터에 ejection 시킬 DLL 의 HANDLE 을 넘겨주면 됩니다.

* 주의!
Windows Kernel Object 에게는 Reference Count 라는 것이 있습니다. (우리말로 '참조 카운트')
LoadLibrary("a.dll") 를 10 번 호출하면 a.dll 에 대한 참조 카운트도 10 이 되어서, 나중에 a.dll 을 해제할때 역시 Freelibrary() 를 10 번 호출해줘야 합니다. (LoadLibrary() 는 참조 카운트 +1 시키고, FreeLibrary() 는 참조 카운트 -1 시킴)
따라서 DLL Ejection 을 할 때는 이 참조 카운트를 잘 고려해야 합니다.



DLL Ejection 구현


Injection 된 myhack.dll 을 Ejection 시킬 프로그램(EjectDll.exe) 을 설명드리겠습니다.
아래 소스 코드를 보시죠.

// EjectDll.cpp

#include "stdio.h"
#include "windows.h"
#include "tlhelp32.h"

#define DEF_PROC_NAME ("notepad.exe")
#define DEF_DLL_NAME ("myhack.dll")

DWORD FindProcessID(LPCTSTR szProcessName);
BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName);

int main(int argc, char* argv[])
{
    DWORD dwPID = 0xFFFFFFFF;
 
    // find process
    dwPID = FindProcessID(DEF_PROC_NAME);
    if( dwPID == 0xFFFFFFFF )
    {
        printf("There is no <%s> process!\n", "notepad.exe");
        return 1;
    }

    // eject dll
    EjectDll(dwPID, DEF_DLL_NAME); 

    return 0;
}

DWORD FindProcessID(LPCTSTR szProcessName)
{
    DWORD dwPID = 0xFFFFFFFF;
    HANDLE hSnapShot = INVALID_HANDLE_VALUE;
    PROCESSENTRY32 pe;

    // Get the snapshot of the system
    pe.dwSize = sizeof( PROCESSENTRY32 );
    hSnapShot = CreateToolhelp32Snapshot( TH32CS_SNAPALL, NULL );

    // find process
    Process32First(hSnapShot, &pe);
    do
    {
        if(!_stricmp(szProcessName, (LPCTSTR)pe.szExeFile))
        {
            dwPID = pe.th32ProcessID;
            break;
        }
    }
    while(Process32Next(hSnapShot, &pe));

    CloseHandle(hSnapShot);

    return dwPID;
}

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
    BOOL bMore = FALSE, bFound = FALSE;
    HANDLE hSnapshot, hProcess, hThread;
    HMODULE hModule = NULL;
    MODULEENTRY32 me = { sizeof(me) };
    LPTHREAD_START_ROUTINE pThreadProc;

    // dwPID = notepad 프로세스 ID
    // TH32CS_SNAPMODULE 파라미터를 이용해서 notepad 프로세스에 로딩된 DLL 이름을 얻음
    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);


    bMore = Module32First(hSnapshot, &me);
    for( ; bMore ; bMore = Module32Next(hSnapshot, &me) )
    {
        if( !_stricmp((LPCTSTR)me.szModule, szDllName) )
        {
            bFound = TRUE;
            break;
        }
    }

    if( !bFound )
    {
        CloseHandle(hSnapshot);
        return FALSE;
    }

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
    hModule = GetModuleHandle("kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
    hThread = CreateRemoteThread(hProcess, NULL, 0,
                                 pThreadProc, me.modBaseAddr,
                                 0, NULL);
    WaitForSingleObject(hThread, INFINITE); 

    CloseHandle(hThread);
    CloseHandle(hProcess);
    CloseHandle(hSnapshot);

    return TRUE;
}

앞에서 DLL Ejection 의 원리가 대상 프로세스로 하여금 스스로 FreeLibrary() API 를 호출하도록 만드는 것이라고 말씀드렸습니다. 위 코드에서 EjectDll() 함수가 바로 그 역할을 수행합니다.

EjectDll() 함수를 자세히 살펴보겠습니다.

#1. 프로세스에 로딩된 DLL 정보 구하기

hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);

CreateToolhelp32Snapshot() API 를 이용하면 프로세스 리스트 뿐만 아니라 프로세스에 로딩된 모듈(DLL)의 정보를 얻을 수 있습니다.

이렇게 구한 hSnaphot 핸들을 Module32First()/Module32Next() 함수에 넘겨주면 MODULEENTRY32 구조체에 해당 모듈의 정보가 세팅됩니다. 아래는 MODULEENTRY32 구조체 정의입니다. (tlhelp32.h 참고)

typedef struct tagMODULEENTRY32
{
    DWORD   dwSize;
    DWORD   th32ModuleID;       // This module
    DWORD   th32ProcessID;      // owning process
    DWORD   GlblcntUsage;       // Global usage count on the module
    DWORD   ProccntUsage;       // Module usage count in th32ProcessID's context
    BYTE  * modBaseAddr;        // Base address of module in th32ProcessID's context
    DWORD   modBaseSize;        // Size in bytes of module starting at modBaseAddr
    HMODULE hModule;            // The hModule of this module in th32ProcessID's context
    char    szModule[MAX_MODULE_NAME32 + 1];
    char    szExePath[MAX_PATH];
} MODULEENTRY32;


szModule 멤버가 DLL 이름이고, modBaseAddr 멤버가 해당 DLL 이 로딩된 주소(프로세스 가상 메모리)입니다.

for 루프에서 szModule 와 Ejection 을 원하는 DLL 파일 이름을 비교하면 정확한 모듈 정보를 찾을 수 있습니다.

#2. 대상 프로세스 핸들 구하기

hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);

프로세스 ID 를 이용해서 대상 프로세스(notepad)의 프로세스 핸들을 구합니다.
(밑에서 이 프로세스 핸들을 이용하여 CreateRemoteThread() API 를 호출하게 됩니다.)

#3. FreeLibrary() API 주소 구하기

hModule = GetModuleHandle("kernel32.dll");
pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");

notepad 프로세스로 하여금 스스로 FreeLibrary() API 를 호출하도록 하려면 FreeLibrary() 의 주소를 알아야 합니다.

하지만 위 코드는 notepad.exe 프로세스에 로딩된 Kernel32!FreeLibrary 주소가 아니라 EjectDll.exe 프로세스에 로딩된 Kernel32!FreeLibrary 주소를 얻어오고 있습니다.

DLL Injection 을 이해하신 분들께서는 이유를 아시겠지요? FreeLibrary 주소는 모든 프로세스에 대해 동일합니다.
자세한 설명은 위에서 소개한 DLL Injection 관련 글을 참고하시기 바랍니다.

#4. 대상 프로세스에 스레드를 실행시킴

hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);

pThreadProc 파라미터는 FreeLibrary() API 의 주소이며, me.modBaseAddr 파라미터는 Ejection 되길 원하는 DLL 의 로딩 주소입니다.

즉, 스레드 함수로 FreeLibrary 함수를 지정하고 스레드 파라미터에 DLL 로딩 주소를 넘겨주면, 결국 대상 프로세스에서는 FreeLibrary() API 가 성공적으로 호출되게 됩니다. (CreateRemoteThread() API 의 원래 의도는 외부 프로세스에 스레드 함수를 실행시키는 것인데, 이 경우에 스레드 함수는 FreeLibrary() 가 되는 것입니다.)

BOOL WINAPI FreeLibrary(
    HMODULE hLibModule
);

ThreadProc 함수와 FreeLibrary 함수의 파라미터가 하나뿐이라는 점에 착안해서 나온 아이디어입니다.
(자세한 설명은 DLL Injection - 다른 프로세스에 침투하기 (2) 를 참고하세요.)



DLL Ejection 간단 실습


notepad 에 Injection 된 myhack.dll 을 Ejection 시켜보도록 하겠습니다.


먼저 첨부된 myhack.dll 을 C:\ 에 복사합니다.
notepad.exe 를 실행시킨 후 InjectDll.exe 를 실행시켜서 myhack.dll 을 notepad 프로세스에 Injection 시킵니다.

myhack.dll 과 InjectDll.exe 에 대한 자세한 설명은 아래 링크를 참고해주세요.

- 참고 : DLL Injection - 다른 프로세스에 침투하기 (1)

Process Explorer 로 확인해 보겠습니다.


<Fig. 1>

notepad 프로세스에 myhack.dll 이 Injection 된것을 확인 할 수 있습니다.

그럼 이제 EjectDll.exe 를 실행 시켜서 myhack.dll 이 Ejection 되는지 여러분 각자 해보시기 바랍니다. ^^


+---+

DLL Ejection 의 기본 동작 원리는 DLL Injection 과 동일하기 때문에 쉽게 이해할 수 있습니다.

처음 접하시는 분께서는 설명을 차근차근 읽어보시고 직접 실습해 보시기 바랍니다.


ReverseCore

Trackback Address :: http://www.reversecore.com/trackback/44 관련글 쓰기

  1. Tracked from www.reversecore.com 2009/08/18 21:42 삭제

    Subject: DLL Injection - 다른 프로세스에 침투하기 (1)

    DLL Injection 에 대해서 자세히 알아보겠습니다. DLL Injection 기법은 다른 프로세스에 침투하는 가장 쉽고 강력한 방법입니다. DLL Injection 이란? 다른 프로세스에 특정 DLL 파일을 강제로 삽입시키는 것입니다. 더 정확히 표현하면 다른 프로세스에게 LoadLibrary() API 를 호출하도록 명령하여 내가 원하는 DLL 을 loading 시키는 것입니다. 따라서 DLL Injection 이 일반적인 DLL loadi..
  2. Tracked from www.reversecore.com 2009/08/18 21:42 삭제

    Subject: DLL Injection - 다른 프로세스에 침투하기 (2)

    DLL Injection 구현 방법 몇 가지 구현 방법이 있습니다. 그 중에서 가장 유명한 방법이 CreateRemoteThread() API 를 이용하는 방법입니다. 이 방법은 윈도우즈 프로그래밍 서적의 바이블인 Jeffrey Richter 의 Programming Applications for Microsoft Windows 에 소개된 내용입니다. 일단 소스 코드를 보겠습니다. (엔지니어에게는 백 마디 설명 보다는 역시 소스 코드를 한번 보는게..
  1. 김정훈 2009/08/21 22:37 댓글주소 | 수정 | 삭제 | 댓글

    휴 정말 대단하군요~!
    솔직히 게임을 하다가 불법프로그램에 관심을 가지게 되서
    dll injector 를 검색하면서 다니다가 재미있어서 여기글 다 읽고 실습까지 해봤네요~
    당연한 질문 한가지 드려도 될까요? ㅎㅎ
    만약 injection된 dll을 ejection으로 뺀다면 빼버린 dll관련 함수들은 해당 프로그램에 동작하지않겠죠?

    • reversecore 2009/08/22 00:29 댓글주소 | 수정 | 삭제

      김정훈님, 안녕하세요.
      흥미를 가지고 실습을 해보셨다니 글을 쓴 보람이 생겼습니다. 감사합니다. ^^
      DLL 을 ejection 시키면 더이상 DLL 내의 함수는 프로그램에 효력을 발휘하지 못합니다. 또 방문해 주세요~

  2. 이진호 2009/08/22 00:35 댓글주소 | 수정 | 삭제 | 댓글

    근데 Ejection 함수를 복사해서 메모장 dll 이아닌 다른
    dll를 Ejection 하고싶은데요 어떻게안돼나요?

    • reversecore 2009/08/22 20:03 댓글주소 | 수정 | 삭제

      이진호님, 위 예제 프로그램을 조금 수정하면 다른 프로세스에 인젝션된 DLL 을 Ejection 시킬 수 있습니다. 물론 Ejection 안당하는 여러가지 기법도 존재하지요.

  3. CherryLove™ 2009/08/29 19:25 댓글주소 | 수정 | 삭제 | 댓글

    오호 ^^ 마침 프로그래밍 중에 DLL Ejection 해야할 일이 있어서 찾아보는 중에 여기에 들르게 됬네요.. ^^;;
    저는 비베 / VC++ 프로그래머입니다. 앞으로 자주 찾아오겠습니다. ㅋㅋ

  4. 늅늅 2009/10/23 13:06 댓글주소 | 수정 | 삭제 | 댓글

    잘 보고 갑니다. DLL Injection 기법에 대하여 포스팅을 해보고자 했는데, 그림과 함께 너무 친절한 설명이 되어 있어서 오전 내내 이거보다 시간 때웠네요 ㅎㅎ;
    제 블로그에 퍼가되 될까요? 물론, 출처는 남겨야죠 ^^

    • reversecore 2009/10/26 11:37 댓글주소 | 수정 | 삭제

      늅늅님, 안녕하세요.
      재밌게 보셨다니 다행이네요. ^^

      제 블로그 글들은 퍼가실 수 없구요, 링크만 허용해 드립니다.

      양해해 주시기 바랍니다.

  5. Ezbeat 2010/04/15 11:08 댓글주소 | 수정 | 삭제 | 댓글

    흐음...... 제대로 한거 같은데 잘 안되서 질문드려봅니다;;
    소스코드 상에는 문제가 없는거 같구요.
    그런데 자꾸 오류가 나더라구요. 처음에는 제가 만든 프로그램에서 오류가 나는줄 알았는데
    오류난 프로세스 PID를 봐보니 Ejection을 당하는 프로세스 였더군요.
    그래서 디버깅을 해보니
    FreeLibrary함수를 실행시키면 1~2초후 오류가 납니다. notepad에서 말이죠.
    대충 오류를 WinDbg에서 분석결과를 보면

    ExceptionAddress: 00c210db (<Unloaded_Open_cmd.dll>+0x000010db)
    ExceptionCode: c0000005 (Access violation)
    ExceptionFlags: 00000008
    NumberParameters: 2
    Parameter[0]: 00000008
    Parameter[1]: 00c210db
    Attempt to execute non-executable address 00c210db

    notepad에서 해당 DLL을 언로드 하면서 문제가 발생한거 같습니다.
    비 실행 주소를 실행하려고 시도 했다고도 나오네요.

    왜 이런 오류가 뜨는건지.. 알수있을까요???;;

    아 그리고 다른 사이트에서 본 Ejection코드에는
    VirtualFreeEx함수를 CloseHandle(hProcess)전에 사용하면서 해당 dll이 있던 메모리를
    RELEASE로 바꿔주는 과정도 넣더라구요. VirtualFreeEx함수를 쓰는게 더욱 안정적이겠죠??

    • reversecore 2010/04/15 23:50 댓글주소 | 수정 | 삭제

      Ezbeat님, 안녕하세요.

      FreeLibrary() 에러라면 아마 파라미터값이 유효하지 않을꺼라 생각되네요. 한번 GetLastError() 를 찍어서 정확한 에러 코드를 확인해 보시기 바랍니다.

      VirtualFreeEx() 를 호출한다고요? 직접 할당받은 메모리도 아닌데 말이죠.
      전 FreeLibrary() 만으로도 충분할꺼라고 생각하는데요...
      나중에 테스트를 해봐야겠군요. ^^
      어떤 효과가 있는지 궁금해지네요.

      감사합니다.

  6. yjy9767 2010/06/06 21:30 댓글주소 | 수정 | 삭제 | 댓글

    가능하시면 주석처리좀 부탁드려용!

    • reversecore 2010/06/08 15:42 댓글주소 | 수정 | 삭제

      안녕하세요.

      코드에 주석을 좀 달아달라는 말씀이시죠? ㅋ

      코드에 주석을 많이 달지 않고 밑의 글에 풀어서 적었습니다.

      혹시 어느 부분이 잘 이해가 안가시는지요?

      핵심은 #1 ~ #4 의 내용이구요, 만약 다른 부분의 코드에서 잘 이해되지 않는 부분이 있다면 다시 질문 올려주시기 바랍니다.

  7. Crash 2010/06/27 13:22 댓글주소 | 수정 | 삭제 | 댓글

    eject가 제대로 안되서 디버깅을 해봤더니
    같은이름의 dll을 찾는거는 제대로 되는데
    해당 dll의 modBaseAddr의 주소값이 잘못되었다고 나옵니다
    vs2008 디버거에서 보면 "0x~~~~ <ptr>이 잘못되었습니다" 이렇게 나오네요 ㅠㅠ
    혹시나 제가만든 dll만 이런가싶어서 검색하는과정을 하나씩 따라가봤는데
    에러가없는 dll도 있고 에러가 있는 dll도 있고 그러네요 ㅠ
    뭐가 문제일까요?

    • reversecore 2010/06/29 23:04 댓글주소 | 수정 | 삭제

      직접 만드신 EXE/DLL 에서 그런 현상이 나타나는 것인가요?
      저 위에 첨부된 EXE/DLL 도 그런것인가요?

      만약 다른 파일이라면 사용하신 EXE, DLL 등을 저에게 보내주시기 바랍니다.

      reversecore@gmail.com 으로 실행파일을 ZIP 압축하신 후 확장자를 ZIPX 로 변경하셔서 보내주세요~ (gmail 에서 실행파일 첨부가 안되거든요...)

  8. crazyj 2011/05/12 10:32 댓글주소 | 수정 | 삭제 | 댓글

    감사히 읽고있습니다. ^^
    근데, 위 FreeLibrary로 dll 제거하는 방식은 dll injection을 createremotethread 등으로 했을 경우에는 잘 되는거 같습니다만, dll injection 포스팅#4번의 방법에는 적용되지 않는 거 같습니다. 포스팅 내용처럼 exe 파일 자체의 IDT를 수정하는게 아닌, 동적으로 createprocess 시점에서 Memory baisic infomation을 통해 PE 포맷을 읽어 IDT를 수정하여 dll을 injection한 경우, 나중에 동적으로 제거 방법이 없는지 궁금합니다...

    • reversecore 2011/05/13 20:14 댓글주소 | 수정 | 삭제

      안녕하세요.

      네, 말씀하신대로 이 방법은 "내가 인젝션 시킨 DLL 에 대해서만" 가능한 방법입니다.

      PE 파일의 Import 섹션에 존재하여 로딩 시에 같이 올라간 DLL 들은 내릴 수 없습니다.

      그리고 CreateProcess() 시점에 IDT 를 수정하는 아이디어는 매우 참신하네요. 재미있을 것 같습니다. 일단 crazyj 님께서 먼저 시도 해 보시고요. 저도 향후에 시도해 본 후 블로그에 올리도록 하겠습니다.

      감사합니다.

  9. KEENBYTE 2011/08/18 15:16 댓글주소 | 수정 | 삭제 | 댓글

    델파이 유저지만

    이걸참고하여

    소스를 델파이화 해봐야겠네요.

    좋은강의 감사합니다 ^ ^

    • reversecore 2012/01/11 07:50 댓글주소 | 수정 | 삭제

      안녕하세요.

      좋은 시도 같습니다. 좀 더 많은 언어로 널리 사용된다면 저야 좋습니다.

      감사합니다.

  10. nagi 2012/01/03 01:12 댓글주소 | 수정 | 삭제 | 댓글

    안녕하세요. 글 잘 보고 있습니다.
    Toolhelp32 함수를 실행시키면
    권한이 없다면서 볼 수가 없는 프로세스들은 어떻게 해야하나요?
    검색 결과 token을 부여하라던 글을 보았는데
    도무지 방법을 모르겠네요.

    • reversecore 2012/01/11 07:49 댓글주소 | 수정 | 삭제

      안녕하세요.

      권한을 따로 얻으셔야 하는데요.
      제가 일부 예제에는 생략하였습니다.

      http://www.reversecore.com/76 이 글의 댓글중에 거의 아래쪽에 권한을 획득하는 함수를 추가하였습니다.

      참고하시기 바랍니다.

      감사합니다.


◀ PREV : [1] : ... [47] : [48] : [49] : [50] : [51] : [52] : [53] : [54] : [55] : ... [91] : NEXT ▶