웬디의 기묘한 이야기

글 작성자: WENDYS
반응형

DLL Injection

기본적으로 내가 만든 Process에 DLL을 Load하는 방법은 간단합니다. 그냥 일반적으로 사용하면 되죠

하지만 다른 Process에 내가 원하는 기능을 동작하게 하고싶은 경우엔 어떻게 해야할까요??


특별한 방법을 사용하여 내가만든 DLL을 다른 Process에서 Load 하여 DllMain에서 필요한 기능을 실행해버리는겁니다.


정상적으로 DLL을 Load했으니 해당 Process의 메모리에 대한 접근 권한을 갖기때문에 여러가지 동작이 가능합니다.


그 특별한 방법이 어떤건지 code로 보겠습니다.

DLL Injection code


#include <Windows.h>
#include <TlHelp32.h>

#include <string>

bool process_name_to_pid(
    __out DWORD& pid,
    __in const std::wstring& process_name
    );

bool dll_injection(
    __in DWORD pid,
    __in const std::wstring& dll_name
    );

int main()
{
    DWORD pid = 0;
    std::wstring process_name = L"notepad.exe";
    std::wstring dll_name = L"C:\\my_dll.dll";

    if (process_name_to_pid(pid, process_name)) {
        dll_injection(pid, dll_name);
    }

    return 0;
}

bool process_name_to_pid(
    __out DWORD& pid,
    __in const std::wstring& process_name
    ) {
    bool result = false;
    HANDLE snapshot = nullptr;
    PROCESSENTRY32 entry = {};

    entry.dwSize = sizeof(PROCESSENTRY32);
    snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);

    if (snapshot != INVALID_HANDLE_VALUE) {
        Process32First(snapshot, &entry);
        do {
            if (!_tcsicmp(process_name.c_str(), entry.szExeFile)) {
                pid = entry.th32ProcessID;
                result = true;
                break;
            }
        } while (Process32Next(snapshot, &entry));

        CloseHandle(snapshot);
    }

    return result;
}

bool dll_injection(
    __in DWORD pid,
    __in const std::wstring& dll_name
    ) {
    bool result = false;

    HANDLE process_handle = nullptr;
    HANDLE thread_handle = nullptr;
    LPVOID remote_buffer = nullptr;
    HMODULE module = {};

    LPTHREAD_START_ROUTINE thread_start_routine = nullptr;

    do {
        process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
        if (process_handle == nullptr) {
            break;
        }

        remote_buffer = VirtualAllocEx(
                            process_handle, 
                            nullptr, 
                            dll_name.size(), 
                            MEM_COMMIT, 
                            PAGE_READWRITE
                            );
        if (!remote_buffer) {
            break;
        }

        if (!WriteProcessMemory(
                process_handle,
                remote_buffer,
                dll_name.c_str(),
                dll_name.size() * sizeof(wchar_t),
                nullptr
                )) {
            break;
        }

        module = GetModuleHandle(L"kernel32.dll");
        thread_start_routine = (LPTHREAD_START_ROUTINE)GetProcAddress(module, "LoadLibraryW");

        thread_handle = CreateRemoteThread(
                            process_handle, 
                            nullptr, 
                            0, 
                            thread_start_routine, 
                            remote_buffer, 
                            0, 
                            nullptr
                            );

        WaitForSingleObject(thread_handle, INFINITE);

        result = true;

    } while (false);

    CloseHandle(process_handle);
    CloseHandle(thread_handle);

    return result;
}


DllMain code


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
    case DLL_PROCESS_ATTACH: {
        MessageBox(nullptr, L"injection success", L"dll injection", MB_OK);
    }
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}


위 코드의 핵심은 dll_injection() 함수 입니다.


1. OpenProcess를 통하여 Injection 대상 Process의 Handle을 구한다.


2. VirtualAllocEx를 이용하여 해당 Process의 가상 메모리 공간에 메모리를 할당한다.

옵션은 메모리를 직접 써야하므로 Reverse 로 예약하는것이 아니라 Commit을 해야합니다.

return 되는 remote_buffer는 말 그대로 내 Process의 메모리가 아닌 Injection될 대상 Process의 메모리 주소입니다.


3. WriteProcessMemory로 Load될 DLL의 경로를 대상 Process 메모리에 입력한다.


4. CreateRemoteThread 을 이용하여 Thread를 생성하는 척(?) LoadLibrary를 실행합니다.


이게 가능한 이유는 ThreadProc과 LoadLibrary의 함수 원형이 같기 때문입니다. 두 함수 모두 4Byte의 return값과 4Byte의 전달 인자를 받습니다.

그러면 다른 API로 호출이 가능할까요?? 물론 가능합니다. 원형이 같은 함수들은 모두 호출이 가능하다고 보시면 됩니다.

하지만 그렇게 하는것보다 DLL Injection을 하게되면 더 다양한 기능을 사용할 수 있기때문에 해당 방법은 Injection의 정석이 된 것 입니다.


동작

Pid는 Process Excpolorer, 작업관리자 등을 통해서도 확인이 가능하며, 위의 함수를 사용해도 획득할 수 있습니다.




notepad.exe에 Load되어있는 dll의 목록 중 일부 입니다.

process 하나에는 정말 많은 dll이 Load 됩니다.


그렇다면 위의 Injection code를 실행하게 되면 어떻게 될까요??





my_dll.dll 은 notepad.exe에서 정식으로 Load되는 Library가 아니지만 DLL Injection에 의해서 강제로 Load 되었습니다.

그렇다면 실제 notepad에선 무슨일이 일어났을까요??





DllMain의 DLL_PROCESS_ATTACH 에서 작업해놓은 MessageBox가 실행되었습니다!


이 외에도 무엇을 더 할 수 있을까요??

뭐든지 할 수 있습니다.

Thread를 생성해서 IPC 통신을 할 수도 있고 Code Injection을 통한 Hooking이 가능해집니다.


이처럼 다양한 일을 하기위해선 일단 DLL Injection이 되어야합니다.


CreateRemoteThread 를 사용하는 방법은 Injection의 하나의 기법이지만, 거의 정석이라고 해도 무방할정도로 사용되는 방법 입니다.


DLL Injection은 프로그래밍에서 매우 유용한기법이며, 다양한 분야에서 실제로 사용중입니다. 악성코드에서도 많이 사용되며, DLP, 유해사이트, 프로그램, 동영상 차단, API Hooking 등에서 사용됩니다.


위 프로젝트는 첨부를 통하여 다운로드가 가능합니다.


dll_injection_sample.zip

my_dll.zip


반응형