DLL Injection 을 위한 DLL Injector 간단하게 만들기
Security/Reverse Engineering

DLL Injection 을 위한 DLL Injector 간단하게 만들기

간단하게 DLL 인젝션이 무엇인지 알아봅시다! DLL 인젝션이란 말그대로 현재 동작중인 프로세스에 DLL 을 주입하는 것인데요. 보통은 기능추가의 목적으로 많이 사용됩니다. 플러그인이라는 것이 대표적인 인젝션 기법에 속하죠. 그럼 바로 DLL을 인젝션해주는 인젝터를 만들어 볼까요? 

 

먼저 인젝터를 만들기 위한 전체적인 과정과 설명 후에 인젝터를 본격적으로 만들어 보겠습니다.

 

프로세스에 DLL 을 인젝션 하기 위해서는 해당 프로세스에 대한 제어권이 필요합니다. 프로세스를 제어하는 프로세스 핸들을 얻으면 되는데요 이 핸들을 얻기위해서 프로세스ID가 필요합니다. 여기서 사용되는 함수는 OpenProcess 라는 함수입니다. 프로세스ID는 작업관리자를 통해서 쉽게 확인이 가능합니다.

 

 

프로세스의 핸들(제어권) 을 얻었다면, 프로세스가 사용하는 메모리 내부에 접근이 가능합니다. VirtualAllocEx 함수를 사용하여 DLL경로 문자열 길이만큼 공간을 만들어주고, WriteProcessMemory 함수를 사용하여 메모리에 DLL 경로를 기록합니다. 왜 프로세스 내부에 주입할 DLL 경로를 기록할까요? 우리의 목적은 우리가 인젝션한 DLL이 프로세스에서 실행되는 것인데요. DLL을 사용하기 위해서는 제일 먼저 DLL을 로드해야겠죠?

 

DLL을 로드하기 위해선 Kernel32.dll 내부의 LoadLibraryW 함수를 사용해야 합니다. 이 함수를 사용하기 위해서 LoadLibraryW 함수를 호출해야 겠죠? 그런데 우리는 LoadLibraryW 함수의 주소를 모르기 때문에 호출이 불가능합니다. 그래서 GetModuleHandle 함수를 사용하여 Kernel32.dll 의 핸들을 얻어온 뒤에, GetProcAddress 함수를 사용하여 LoadLibrayW 함수의 주소를 얻어옵니다.

 

이제 DLL을 로드하기 위한 모든 작업이 끝났습니다! LoadLibraryW 함수를 호출하면 우리가 만든 DLL이 프로세스 내부에 올라가게 됩니다. 인젝션이 성공하게 되는것이죠! 

하지만 여기 문제점이 하나가 있습니다. LoadLibraryW 함수를 인젝션된 프로세스 자신이 직접! 호출해야된다는 것입니다. 여기서 하나의 꼼수? 를 사용하는데 CreateRemoteThread 함수를 사용하고, 원격에서 프로세스 내부에 쓰레드를 생성하여 쓰레드의 시작점으로 LoadLibraryW 함수의 주소를 넣어주면~~ 쓰레드의 생성과 함께 LoadLibraryW 함수가 호출되면서 우리의 DLL이 똮 로드(인젝션)되게 됩니다.

 

이제 Visual Studio 2017 을 사용하여 본격적으로 프로그램 제작을 해봅시다.

먼저 인코딩 문제가 발생할 수 도 있으니 유니코드 문자 집합으로 바꾸어 줍니다.

 

먼저 프로그램의 시작점인 메인함수를 작성해볼까요? 일단 본격적인 인젝션을 하는 함수인 Inject 함수는 뒤에서 만들예정입니다. 프로그램의 인자로 프로세스ID와 DLL 경로를 입력받도록 합니다. 그리고 Inject 함수의 리턴값 여부에 따라 성공여부를 알려주게 합니다.

 

인젝션을 하는 Inject 함수입니다. 위에서 설명한 Step 별로 나누어서 정리해 두었습니다. 각 함수들의 파라메터에 대해 자세하게 알아보고 싶으시다면 구글에 검색하셔서 관련문서를 찾아보시는걸 추천드립니다. 각각의 함수들의 리턴값에 대한 예외처리는 생략하였습니다. 아래에 전체코드를 첨부해 드릴게요~

#include "windows.h"
#include "tchar.h"

BOOL Inject(DWORD hwPID, LPCTSTR DllPath);

int _tmain(int argc,TCHAR *argv[]) {

    // 무조건 PID 와 DLL경로를 입력받도록 하기!
    if (argc != 3) {
        _tprintf(L"사용법 : %s [PID] [DLL경로]",argv[0]);
        return 0;
    }
    
    //Inject 함수의 인자로 PID와 DLL의 경로를 넘겨줍니다. 정상적으로 인젝션이 완료되면 TRUE를 반환합니다.
    if (Inject((DWORD)_tstol(argv[1]), argv[2]))
        _tprintf(L"%s 의 인젝션이 성공하였습니다. \n", argv[2]);
    else
        _tprintf(L"%s 의 인젝션이 실패하였습니다. \n", argv[2]);

    return 0;

}

BOOL Inject(DWORD hwPID, LPCTSTR DllPath) {

    //프로세스, 쓰레드, 모듈의 핸들을 담을 구조체 변수입니다.
    HANDLE hProcess = NULL;
    HANDLE hThread = NULL;
    HMODULE hMod = NULL;

    // DLL 경로를 기록한 메모리 주소를 넣을 포인터 변수입니다.
    LPVOID pRemoteBuf = NULL;

    // DLL 경로의 사이즈를 나타냅니다.
    DWORD dwBufSize = (DWORD)(_tcslen(DllPath) + 1) * sizeof(TCHAR);

    // 쓰레드 시작 루틴함수주소를 저장할 변수입니다.
    LPTHREAD_START_ROUTINE pThreadProc;

    // Step 1. 인젝션 할 프로세스 제어권 얻기
    hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, hwPID);

    // Step 2. 인젝션 할 DLL 경로를 해당 프로세스에 기록
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)DllPath, dwBufSize, NULL);

    // Step 3. 쓰여진 DLL을 프로세스에서 로드하기 위한 작업하기
    hMod = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");

    // Step 4. 쓰여진 DLL을 원격쓰레드 생성을 통해 프로세스에서 로드
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    // 쓰레드가 실행될때 까지 무한정 대기합니다.
    WaitForSingleObject(hThread, INFINITE);

    // 사용한 핸들들을 닫아줍니다.
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return 1;
}
 

 

 

이렇게 간단하게 인젝터를 제작해 보았습니다~ 인젝터를 만들었으니 우리가 인젝션할 DLL도 만들고, 제대로 인젝션이 되는지 실험을 해봐야겠죠. 고건 다음포스팅때 하도록 하겠습니다.  읽어주셔서 감사합니다