흰싸라기 2022. 4. 5. 01:45

 DLL 인젝션 

: 실행 중인 다른 프로세스에 특정 DLL 파일을 강제로 삽입하는 것

 

  • 다른 프로세스에게 LoadLibrary() API를 스스로 호출하도록 명령해 사용자가 원하는 DLL을 로딩하는 것
  • 일반 DLL 로딩과 다른 점은 로딩 대상이 되는 프로세스가 내 자신이냐 아니면 다른 프로세스냐 하는 것 

  • notepad 프로세스에 myhack.dll을 강제 삽입
  • kernel32.dll 등과 같이 notepad.exe 프로세스 메모리에 대한 정당한 접근 권한을 가짐
    • 사용자가 원하는 일 수행 가능 

 

 

 DLL 인젝션 활용 예 

 기능 개선 및 버그 패치

  • 소스코드가 없거나 수정이 어려울 때 DLL 인젝션을 사용해 새로운 기능을 추가(PlugnIn 개념)하거나, 문제가 있는 코드와 데이터를 수정 가능

 

 메시지 후킹

  • 메시지 후킹(Message Hooking) 기능도 일종의 DLL 인젝션 기법
  • 등록된 후킹 DLL을 OS에서 직접 인젝션시켜준다는 차이점 존재 

 

 API 후킹

  • 후킹 함수를 DLL 형태로 만든 후 후킹을 원하는 프로세스에 간단히 인젝션하는 것만으로 API 후킹이 완성
    • 삽입된 DLL은 해당 프로세스의 메모리에 대한 접근 권한을 갖고 있다는 특성을 활용

 

 기타 응용 프로그램

  • PC 사용자들을 감시, 관리하기 위한 애플리케이션에서 사용되기도 함
    • 특정 프로그램의 실행 차단, 유해사이트 접속 차단, PC 사용 모니터링 프로그램 등

 

 악성 코드

  • DLL 인젝션 기법을 악성 코드에 적극적 활용
  • 정상적인 프로세스에 몰래 숨어들어가 백도어 포트(Backdoor port)를 열어서 외부에서 접속을 시도하거나, 키로깅 기능으로 사용자의 개인 정보를 훔침

 

 DLL 인젝션 구현 방법 

 

DLL 인젝션 방법

  • 원격 스레드 생성(CreateRemoteThread() API)
  • 레지스트리 이용(AppInit_DLLs 값)
  • 메시지 후킹(SetWindowsHookEx() API)

 

 CreateRemoteThread() 

 실습 예제 myhack.dll 

: notepad.exe 프로세스에 myhack.dll을 인젝션

 

실습 파일 복사

  • 실습 파일(InjectDll.exe, myhack.dll)을 각자 작업 폴더에 복사

 

notepad.exe 프로그램 실행

  • notepad.exe(메모장)을 실행한 후 Process Explorer를 실행해 프로세스의 PID를 알아냄

  • notepad.exe 프로세스의 PID = 9416

Debug View 실행

 

myhack.dll 인젝션

  • InjectDll.exe 프로그램은 DLL 인젝션 유틸리티
  • InjectDll.exe 실행 
    • cmd 권리자 권한으로 실행
    • 명령어 : InjectDll.exe [notepad.exe PID] [Inject할 DLL]

 

DLL 인젝션 확인

  • DebugView 로그

  • Process Explorer
    • View - Show Lower Pane
    • View - Lower Pane Views - DLLs

 

결과 확인

  • index.html이 work 파일에 다운로드됨

  • index.html 실행

 

 예제 소스코드 분석 

Myhack.cpp

: myhack.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 Main() : DLL이 로딩(DLL_PROCESS_ATTACH)될 때 디버그 문자열("<myhack.dll> Injection!!!")을 출력하고 스레드(ThreadProc)를 실행
  • ThreadProc() : urlmon!URLDownloadToFile() API를 호출해 네이버 사이트의 index.html 파일을 다운받음
  • notepad.exe 프로세스에 myhack.dll이 인젝션되면 결국 URLDownloadToFile() API가 호출
    • DLL 인젝션이 발생하면 해당 DLLMain() 함수가 호출되기 때문

 

 

InjectDll.cpp

: myhack.dll 소스코드(myhack.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() 함수 호출
  • InjectDll() : 대상 프로세스(notepad.exe)로 하여금 스스로 LoadLibrary("myhack.dll") API를 호출하도록 명령하는 기능
    • 대상 프로세스 핸들 구하기
      • hProcess = OpenProcesss(PROCESS_ALL_ACCESS, FALSE, dwPID)
      • OpenProcess() API를 이용해 PROCESS_ALL_ACCESS 권한의 notepad.exe 프로세스 핸들을 구함(dwPID 이용)
      • 프로세스 핸들(hProcess)을 이용해 프로세스 제어 가능
    • 대상 프로세스 메모리에 인젝션할 DLL 경로 써주기
      • pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
      • 프로세스에 로딩한 DLL 파일의 경로를 알려줘야함
      • VirtyalAllocEX() API를 이용해 상대 프로세스 메모리 공간에 버퍼 할당
      • 버퍼 크기는 DLL 파일 경로 문자열 길이로 지정(\0 포함)
      • WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
      • 할당받은 버퍼주소(pRemoteBuf)에 WriteProcessMemory() API를 이용해 DLL 경로 문자열을 써줌
      • 상대방 프로세스의 메모리 공간에 쓰는 것
    • LoadLibraryW() API 주소 구하기
      • hMod = GetModuleHandle(L"kernel32.dll");     pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
      • 위 코드는 InjectDll.exe에 로딩된 kernel32.dll의 LoadLibrary() API 주소
      • notepad.exe에 로딩된 kernel32.dll의 LoadLibrary() API 시작 주소가 필요
      • Windows 운영체제에서 kernel32.dll은 프로세스마다 같은 주소에 로딩됨
    • 대상 프로세스에 원격 스레드(Remote Thread)를 실행함
      • hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
        • pThreadProc : notepad.exe 프로세스 메모리 내의 LoadLibraryW() 주소
        • pRemoteBuf : notepad.exe 프로세스 메모리내의 DLL 파일 경로 문자열 주소
      • CreateRemoteThread() API : 다른 프로세스에게 스레드를 실행시켜주는 함수
        • LoadLibraryW() 호출하도록 만드는 역할을 하도록 수정
        • IpStartAddress (스레드 함수 주소): 'LoadLibrary() 주소'
        • IpParameter(스레드 파라미터 주소) : 인젝션을 원하는 'DLL의 경로 문자열 주소' 

 

 디버깅 방법 

: DLL 파일이 인젝션되는 순간부터 디버깅할 수 있는 방법

 

notepad.exe 실행 후 OllyDbg를 통해 Attach

 

  • Attach하면 일시적으로 멈춤 → 실행[F9]

옵션 다이얼 로그의 Events 메뉴의 "Pause on new module(DLL)" 항목 체크

  • 새로운 DLL이 로딩될 때마다 해당 DLL의 EP에서 멈춤
  • DLL 인젝션이 될 때도 마찬가지로 해당 DLL의 EP에서 멈춤

myhack.dll 파일 notepad.exe 프로세스에 인젝션

  • myhack.dll이 임포트하는 dll이 로딩될 때도 멈춤
  • 따라서 myhack.dll의 EP코드가 나타날 때까지 실행[F9]

  • myhack.dll 모듈의 EP 도착, 디버깅 진행하면 됨(Pause on new module(DLL) uncheck로 수정)

 

 

 AppInit_DLLS 

: 레지스트리(Registry) 이용

 

  • Windows 운영체제에서 기본으로 제공하는 AppInit_DLLs, LoadAppInit_DLLs 레지스트리 항목 존재
  • AppInit_DLLS 항목에 인젝션을 원하는 DLL 경로 문자열을 쓰고 LoaAppInit_DLLs 항목의 값을 1로 변경 후 재부팅하면, 실행되는 모든 프로세스에 해당 DLL을 인젝션

 

 예제 소스코드 분석 

Myhack2.cpp

// myhack2.cpp

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

#define DEF_CMD  L"c:\\Program Files\\Internet Explorer\\iexplore.exe" 
#define DEF_ADDR L"http://www.naver.com"
#define DEF_DST_PROC L"notepad.exe"

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    TCHAR szCmd[MAX_PATH]  = {0,};
    TCHAR szPath[MAX_PATH] = {0,};
    TCHAR *p = NULL;
    STARTUPINFO si = {0,};
    PROCESS_INFORMATION pi = {0,};

    si.cb = sizeof(STARTUPINFO);
    si.dwFlags = STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;

    switch( fdwReason )
    {
    case DLL_PROCESS_ATTACH : 
        if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
            break;
   
        if( !(p = _tcsrchr(szPath, '\\')) )
            break;

        if( _tcsicmp(p+1, DEF_DST_PROC) )
            break;

        wsprintf(szCmd, L"%s %s", DEF_CMD, DEF_ADDR);
        if( !CreateProcess(NULL, (LPTSTR)(LPCTSTR)szCmd, 
                            NULL, NULL, FALSE, 
                            NORMAL_PRIORITY_CLASS, 
                            NULL, NULL, &si, &pi) )
            break;

        if( pi.hProcess != NULL )
            CloseHandle(pi.hProcess);

        break;
    }
   
    return TRUE;
}
  • 현재 자신을 로딩한 프로세스 이름이 'notepad.exe'와 같다면 IE를 숨김 모드로 실행시켜 Naver 사이트에 접속

 

 실습 예제 

레지스트리 값 입력

regedit.exe 실행후 아래 경로로 이동

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows

 

AppInit_DLLs 값을 dll 경로로 편집

LoadAppInit_DLLs 항목 값을 1로 변경

 

 SetWindowsHookEx() 

: 메시지 후킹

 

  • SetWindowsHookEx() API를 이용해 메시지 훅을 설치하면 OS에서 hook procedure를 담고있는 DLL을 프로세스에 강제로 인젝션