DLL Injection

컴컴한해커·2025년 1월 9일

리버스 엔지니어링

목록 보기
10/18

📌 DLL Injection이란?

실행 중인 다른 프로세스에 특정 DLL 파일을 강제로 삽입하는 것
= 다른 프로세스에 LoadLibrary() API를 스스로 호출하도록 명령하여 원하는 DLL을 로딩하도록 하는 것

참고사항

  • 프로세스에 DLL이 로딩되면 자동으로 DllMain() 함수가 실행된다. 그래서 DLL이 주입되는 것 만으로 DLL이 로딩되는 것이다.

📌 활용 예시

  1. 기능 개선 및 버그 패치
    • 프로그램의 소스코드가 없거나 수정이 여의치 않을 때 DLL Injection으로 새로운 기능 추가, 기존 문제의 코드 수정
  2. 메시지 후킹
    • 이전에 봤던 SetWindowHookEx()가 호출되고 키보드 훅 체인에 KeyboardProc()이 추가된다.
  3. API 후킹
    • 후킹 함수를 DLL 형태로 만든 후 원하는 프로세스에 인젝션하여 사용
  4. 기타 응용 프로그램
    • 주로 PC를 감시하거나 관리하기 위한 애플리케이션에서 사용된다.
  5. 악성 코드
    • 정상적인 프로세스에 몰래 들어가 백도어 포트를 열어서 외부에서 접속을 시도, 키로깅 기능으로 개인 정보 훔치기 등등

📌 구현 방법

  1. 원격 스레드 생성(CreateRemoteThread() API)
  2. 레지스트리 이용(AppInit_DLLs 값)
  3. 메세지 후킹(SetWindowsHookEx() API)

📢 원격 스레드 생성


InjectDll을 이용해서 myhack.dll을 인젝션하고 DebugView로 확인해 보았다. DebugView에 나온 1108은 notepad.exe의 PID이다. 성공적으로 되었음을 확인 할 수 있다. 이 말은 즉, DllMain() 함수의 OutputDebugString() API가 호출된 것이다.

확인해보면 DLL이 인젝션 된 것을 확인 할 수 있다.

📢 코드 분석

Myhack.cpp

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

#pragma comment(lib, "urlmon.lib")

#define DEF_URL     	(L"http://www.naver.com/index.html")
#define DEF_FILE_NAME   (L"index.html")

HMODULE g_hMod = NULL;

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    TCHAR szPath[_MAX_PATH] = {0,};

    if( !GetModuleFileName( g_hMod, szPath, MAX_PATH ) )
        return FALSE;
	
    TCHAR *p = _tcsrchr( szPath, '\\' );
    if( !p )
        return FALSE;

    _tcscpy_s(p+1, _MAX_PATH, DEF_FILE_NAME);

    URLDownloadToFile(NULL, DEF_URL, szPath, 0, NULL);

    return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    HANDLE hThread = NULL;

    g_hMod = (HMODULE)hinstDLL;

    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH : 
        OutputDebugString(L"<myhack.dll> Injection!!!");
        hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
        CloseHandle(hThread);
        break;
    }

    return TRUE;
}

DLL이 로딩될 때 디버그 문자열 "myhack.dll Injection!!!"을 출력하고 스레드(ThreadProc)을 실행한다. 이 때 ThreadProc()의 내용은 URLDownloadToFile() API를 호출하여 index.html 파일을 다운받는다.

InjectDLL.cpp

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

BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) 
{
    TOKEN_PRIVILEGES tp;
    HANDLE hToken;
    LUID luid;

    if( !OpenProcessToken(GetCurrentProcess(),
                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, 
			              &hToken) )
    {
        _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
        return FALSE;
    }

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError() ); 
        return FALSE; 
    }

    tp.PrivilegeCount = 1;
    tp.Privileges[0].Luid = luid;
    if( bEnablePrivilege )
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    else
        tp.Privileges[0].Attributes = 0;

    // Enable the privilege or disable all privileges.
    if( !AdjustTokenPrivileges(hToken, 
                               FALSE, 
                               &tp, 
                               sizeof(TOKEN_PRIVILEGES), 
                               (PTOKEN_PRIVILEGES) NULL, 
                               (PDWORD) NULL) )
    { 
        _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

    if( GetLastError() == ERROR_NOT_ALL_ASSIGNED )
    {
        _tprintf(L"The token does not have the specified privilege. \n");
        return FALSE;
    } 

    return TRUE;
}

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
{
    HANDLE hProcess = NULL, hThread = NULL;
    HMODULE hMod = NULL;
    LPVOID pRemoteBuf = NULL;
    DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
    LPTHREAD_START_ROUTINE pThreadProc;

    // #1. dwPID 를 이용하여 대상 프로세스(notepad.exe)의 HANDLE을 구한다.
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
        return FALSE;
    }

    // #2. 대상 프로세스(notepad.exe) 메모리에 szDllName 크기만큼 메모리를 할당한다.
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

    // #3. 할당 받은 메모리에 myhack.dll 경로("c:\\myhack.dll")를 쓴다.
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);

    // #4. LoadLibraryA() API 주소를 구한다.
    hMod = GetModuleHandle(L"kernel32.dll");
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
	
    // #5. notepad.exe 프로세스에 스레드를 실행
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

int _tmain(int argc, TCHAR *argv[])
{
    if( argc != 3)
    {
        _tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]);
        return 1;
    }

    // change privilege
    if( !SetPrivilege(SE_DEBUG_NAME, TRUE) )
        return 1;

    // inject dll
    if( InjectDll((DWORD)_tstol(argv[1]), argv[2]) )
        _tprintf(L"InjectDll(\"%s\") success!!!\n", argv[2]);
    else
        _tprintf(L"InjectDll(\"%s\") failed!!!\n", argv[2]);

    return 0;
}

main 함수를 보면 프로그램의 파라미터를 체크한 후 InjectDLL() 함수를 호출한다. 코드에 대한 설명은 주석으로 처리되어있지만, 몇몇 중요한 부분은 더 자세히 봐야한다.
1. Windows 운영체제는 Debug API를 제공하여 다른 프로세스 메모리에 접근 할 수 있다. 대표적인 Debug API로는 VirtualAllocEx(), VirtualFreeEx(),WriteProcessMemory(),ReadProcessMemory()가 있다.
2. DLL 인젝션 기법은 OS 핵심 DLL들이 자신만의 고유한 주소에 로딩되는 것을 보장된다는 Windows OS 특성을 이용한 것이다. 이것이 Windows 취약점으로도 작용한다.
3. 프로세스에 DLL을 인젝션하려고 하는데, 왜 뜬금없이 스레드 실행 함수가 나타났을까? CreateRemoteThread를 호출해서 lpStartAddress에 LoadLibrary 주소를 입력하고 lpParameter에 인젝션을 원하는 DLL의 경로 문자열 주소를 주면 함수를 호출하는 것이지만 이 말은 즉, DLL을 인젝션했다고 볼 수도 있기 때문이다.

📢 레지스트리키 변경(AppInit_DLLs)


AppInit_DLLs에 인젝션을 원하는 DLL 경로의 문자열을 쓰고 LoadAppInit_DLLs의 항목 값을 1로 변경한 후 재부팅하면 모든 프로세스에 해당 DLL을 인젝션한다. 이 방법은 user32.dll이 프로세스에 로딩될 때 가능한 방법으로 user32.dll을 로딩하는 프로세스에만 해당된다.

📢 메세지 후킹(SetWindowsHookEx())

SetWindowsHookEx() API를 이용하여 메세지 훅을 설치하면 OS에서 hook procedure를 담고 있는 DLL을 프로세스에 강제로 인젝션한다. 자세한 내용은 메세지 후킹의 내용을 참고할 것.

0개의 댓글