ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 23 DLL 인젝션
    리버싱 엔지니어링/리버싱 핵심 원리 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을 프로세스에 강제로 인젝션

     

    댓글

Designed by Tistory.