[WinAPI] DLL Injection – 2. 코드 예제(VirtualAllocEx)

DLL 인젝션을 하기 전에 아래 사항을 확인해 주세요

  1. DLL 인젝션을 할 때 x86, x64 플랫폼을 맞춰주셔야 합니다. 인젝션 당할 프로세스가 x86인지, x64인지 확인하셔서 구분해주시기 바랍니다.(저는 Test.exe라는 프로그램을 x86으로 생성해서 인젝션 할 예정입니다.)
  2. 인젝션 할 프로그램은 관리자 권한이 있어야합니다.
  3. DLL 인젝션 하는 방법을 알려드리는 것이지 정보를 빼내오는 방법은 저도 모릅니다.
  4. 해당 방법은 Win32 프로그램에만 인젝션 가능합니다. UWP같은 프로그램은 작동하지 않습니다.(정확히 작동 유무를 모릅니다.)
  5. 해당 방법은 오래된 방법이고, 매우 잘 알려진 방법이므로 다른 프로그램에 인젝션을 할때 방어가 되어있을 수 있습니다.(=인젝션 안될 수 있습니다.)

주절 주절 썼는데, 제가 이때까지 실무에서 파악한 내용입니다. 부정확하거나, 놓친 내용이 있을 수 있습니다.

자, DLL 인젝션 코드를 알아보겠습니다.
우선 인젝션 작업을 할 MFCApplication1는 관리자 권한이 있어야합니다.
아래와 같이 UAC 실행수준을 requireAdministrator로 맞춰주세요

 MFCApplication1의 대화상자를 아래와 같이 만들어주세요. 인젝션 할 PID를 입력하는 부분입니다.

Injection 버튼을 더블 클릭 하셔서 아래와 같이 입력합니다.

UpdateData(TRUE);
 
TCHAR szDllPath[MAX_PATH] = _T( ".\\MFCLibrary.dll" ); 
DLLInject( m_uiPID, szDllPath );

그리고 DLLInject 함수를 아래와 같이 생성해줍니다.

BOOL CMFCApplication1Dlg::DLLInject( DWORD dwPID, LPCTSTR lpDllName )
{
    HANDLE hProcess = NULL;
 
    // 입력한 PID의 핸들
    hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ,
        FALSE,
        dwPID );
    // 핸들 못받아오면 FALSE
    if ( NULL == hProcess )
        return FALSE;
 
    TCHAR szDllPath[MAX_PATH];
    memset( szDllPath, 0, MAX_PATH );
    _tcscpy_s( szDllPath, MAX_PATH, lpDllName );
 
    // 가상 메모리 공간을 할당
    PVOID  lpAddr = ::VirtualAllocEx( hProcess, NULL, sizeof( szDllPath ), MEM_COMMIT, PAGE_READWRITE );
    // 할당 실패 시 FALSE 반환
    if ( NULL == lpAddr )
        return FALSE;
 
    // 지정된 프로세스의 메모리 영역에 DLL을 입력
    ::WriteProcessMemory( hProcess, lpAddr, (void*)szDllPath, sizeof( szDllPath ), NULL );
 
    HMODULE hKernel32 = ::GetModuleHandle( _T( "Kernel32" ) );
    LPTHREAD_START_ROUTINE lpKernel32Lib = (LPTHREAD_START_ROUTINE)::GetProcAddress( hKernel32, "LoadLibraryW" );
    // 해당 PID에 쓰레드 생성
    HANDLE hThread = ::CreateRemoteThread(
        hProcess, // 해당 PID
        NULL,
        0,
        lpKernel32Lib, // LoadLibrary 함수
        lpAddr, // 가상 메모리 공간
        0,
        NULL );
    // 쓰레드 생성 실패
    if( NULL == hThread )
        return FALSE;
 
    DWORD hLibModule = 0;  
    DWORD dwRes = ::WaitForSingleObject( hThread, INFINITE ); // 생성한 쓰레드가 종료될 때까지 대기
    ::GetExitCodeThread( hThread, &hLibModule ); // 쓰레드 종료 코드 받아오기
    ::CloseHandle( hThread ); // 쓰레드 핸들 종료
 
    // 해당 프로세스의 가상 공간 해제
    ::VirtualFreeEx( hProcess, lpAddr, sizeof( szDllPath ), MEM_RELEASE );
    // LoadLibraryW 는 실패시 NULL 반환(실패시 FALSE)
    if ( NULL == hLibModule )
        return FALSE;
 
    return TRUE;
}

자, 이제 확인해보겠습니다. 프로젝트를 빌드하고 인젝션 당할 Test.exe를 실행합니다.
저번 글에서 제가 다운받으라 했던 SysinternalsSuite에 procexp.exe라는 프로그램이 있습니다. 해당 프로그램을 실행 시켜 Test.exe를 찾습니다.

위와 같이 표시가 안되는 분들은 위에 톱니바퀴 모양으로 변경해 주셔야 합니다.

test.exe의 현재 PID는 16592네요

인젝션 작업을 할 MFCApplication1.exe를 실행해 주시고 아래와 16592를 입력해서 Injection버튼을 클릭합니다.

정상적으로 Injection이 되면 아래와 같이 우리가 생성해 줬던 MFCLibrary.dll이 Test.exe에 포함되는 것을 볼 수 있습니다.

이상으로 DLL 인젝션에 대해 알아봤습니다.