선릉역 1번 출구

Code Injection 본문

Hacking & Security/Malicious code and Reversing

Code Injection

choideu 2021. 12. 16. 01:47

code 인젝션이란?

상대방 프로세스에 독립 실행 코드를 삽입한 후 실행하는 기법임

CreateRemoteThread() API를 이용해 원격 스레드 형태로 실행해 Thread injection이라고도 부름

인젝션 대상이 되는 target.exe process에 code와 data를 삽입함

code = 스레드 프로시져 형식, data = 스레드의 파라미터로 전달함

 

주의해야 할 내용

DLL 인젝션의 경우 DLL을 삽입하면 DLL 안에 코드와 데이터가 모두 존재하기 때문에 코드가 정상적으로 실행됨

그러나 Code 인젝션의 경우 필요한 코드를 인젝션하는 것이기 때문에 데이터도 같이 인젝션을 해주어야 함

그래야 코드의 실행이 정상적으로 가능해짐 -> 코드 인젝션이 DLL 인젝션보다 고려해야 할 사항이 많음

 

그럼에도 불구하고 코드 인젝션을 사용하는 이유는?

  1. 메모리를 적게 차지함 (DLL에 비해 메모리를 적게 차지함)
  2. 흔적을 찾기가 어려움 (DLL은 해당 프로세스 메모리에 흔적을 남김)

실행 방법

codeinjection.exe PID = 파라미터 2개임

 

1. Main()

int main(int argc, char *argv[])
{
    DWORD dwPID     = 0;

	if( argc != 2 )
	{
	    printf("\n USAGE  : %s <pid>\n", argv[0]);
		return 1;
	}

    // code injection
    dwPID = (DWORD)atol(argv[1]);
    InjectCode(dwPID);

	return 0;
}

argc의 개수가 2가 아니면 return 1을 반환

dwPID = argv[1]인 PID를 atol을 통해 Long형 데이터 타입으로 변환 //여기서 PID는 상대방 프로세스로 내가 injection하고 싶은 프로세스라고 생각하면됨

InjectCode()함수에 dwPID를 매개변수로 주어 실행시킴

 

2. ThreadProc() = 인젝션할 코드(스레드 함수)

typedef struct _THREAD_PARAM 
{
    FARPROC pFunc[2];               // LoadLibraryA(), GetProcAddress()
    char    szBuf[4][128];          // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
} THREAD_PARAM, *PTHREAD_PARAM;

typedef HMODULE (WINAPI *PFLOADLIBRARYA)
(
    LPCSTR lpLibFileName
);

typedef FARPROC (WINAPI *PFGETPROCADDRESS)
(
    HMODULE hModule,
    LPCSTR lpProcName
);

typedef int (WINAPI *PFMESSAGEBOXA)
(
    HWND hWnd,
    LPCSTR lpText,
    LPCSTR lpCaption,
    UINT uType
);

DWORD WINAPI ThreadProc(LPVOID lParam)
{
    PTHREAD_PARAM   pParam      = (PTHREAD_PARAM)lParam;
    HMODULE         hMod        = NULL;
    FARPROC         pFunc       = NULL;

    // LoadLibrary()
    hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]);    // "user32.dll"
    if( !hMod )
        return 1;

    // GetProcAddress()
    pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);  // "MessageBoxA"
    if( !pFunc )
        return 1;

    // MessageBoxA()
    ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);

    return 0;
}

독립 실행 코드를 인젝션하는 것이기 때문에 코드와 코드에서 참조하는 데이터를 같이 인젝션하는 것이 핵심임

여기 ThreadProc함수에서는 API를 호출하지 않고 전부 스레드 파라미터로 넘어온 THREA_PARAM 구조체에서 가져다 사용하고 있음

 

3. InjectCode()

BOOL InjectCode(DWORD dwPID)
{
    HMODULE         hMod            = NULL;
    THREAD_PARAM    param           = {0,};
    HANDLE          hProcess        = NULL;
    HANDLE          hThread         = NULL;
    LPVOID          pRemoteBuf[2]   = {0,};
    DWORD           dwSize          = 0;

    hMod = GetModuleHandleA("kernel32.dll");

    // set THREAD_PARAM
    param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
    param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
    strcpy_s(param.szBuf[0], "user32.dll");
    strcpy_s(param.szBuf[1], "MessageBoxA");
    strcpy_s(param.szBuf[2], "www.reversecore.com");
    strcpy_s(param.szBuf[3], "ReverseCore");

    // Open Process
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS,   // dwDesiredAccess
                                  FALSE,                // bInheritHandle
                                  dwPID)) )             // dwProcessId
    {
        printf("OpenProcess() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for THREAD_PARAM
    dwSize = sizeof(THREAD_PARAM);
    if( !(pRemoteBuf[0] = VirtualAllocEx(hProcess,          // hProcess
                                      NULL,                 // lpAddress
                                      dwSize,               // dwSize
                                      MEM_COMMIT,           // flAllocationType
                                      PAGE_READWRITE)) )    // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !WriteProcessMemory(hProcess,                       // hProcess
                            pRemoteBuf[0],                  // lpBaseAddress
                            (LPVOID)&param,                 // lpBuffer
                            dwSize,                         // nSize
                            NULL) )                         // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    // Allocation for ThreadProc()
    dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;
    if( !(pRemoteBuf[1] = VirtualAllocEx(hProcess,          // hProcess
                                      NULL,                 // lpAddress
                                      dwSize,               // dwSize
                                      MEM_COMMIT,           // flAllocationType
                                      PAGE_EXECUTE_READWRITE)) )    // flProtect
    {
        printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !WriteProcessMemory(hProcess,                       // hProcess
                            pRemoteBuf[1],                  // lpBaseAddress
                            (LPVOID)ThreadProc,             // lpBuffer
                            dwSize,                         // nSize
                            NULL) )                         // [out] lpNumberOfBytesWritten
    {
        printf("WriteProcessMemory() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    if( !(hThread = CreateRemoteThread(hProcess,            // hProcess
                                       NULL,                // lpThreadAttributes
                                       0,                   // dwStackSize
                                       (LPTHREAD_START_ROUTINE)pRemoteBuf[1],     // dwStackSize
                                       pRemoteBuf[0],       // lpParameter
                                       0,                   // dwCreationFlags
                                       NULL)) )             // lpThreadId
    {
        printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
        return FALSE;
    }

    WaitForSingleObject(hThread, INFINITE);	

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
}

 

InjectCode() 함수의 앞부분은 THREAD_PARAM 구조체 변수 세팅임 그 다음 API 함수 호출이 이어짐

OpenProcess()

//data: THREAD_PARAM
VirtualAllocEx()
WriteProcessMemory()

//code: ThreadProc()
VirtualAllocEx()
WriteProcessMemory()

CreateRemoteThread()

api 함수 호출 flow

상대방 프로세스에 data와 code를 각각 메모리 할당하고 인젝션 해주는 것임

마지막으로 CreateRemoteThread를 사용해 원격 스레드를 실행시킴

 

결론

DLL인젝션은 규모가 크고 복잡한 일을 수행할 때 사용함

Code 인젝션은 규모가 작고 간단한 일을 수행할 때 사용함

 

DLL 인젝션과의 차이는 VirtualAlloEx와 WriteProcessMemory임

 

참고 사이트

https://siveriness.tistory.com/28

 

코드 인젝션

DLL인젝션과의 가장 큰 차이점 DLL인젝션의 경우, DLL인젝션 기법으로 DLL을 통째로 상대방 프로세스 메모리에 삽입시키므로, 코드와 데이터가 같이 메모리에 존재하기 때문에 코드는 정상적으로

siveriness.tistory.com

https://leehahoon.tistory.com/entry/Code-Injection

 

'Hacking & Security > Malicious code and Reversing' 카테고리의 다른 글

Hello World! Reversing  (0) 2021.12.24
Reverse Engineering  (0) 2021.12.23
DLL Injection  (0) 2021.12.15
Anti Disassembly  (0) 2021.12.15
Windows Message Hooking(2)  (0) 2021.12.12
Comments