INTERLUDE ✦/2023 Reversing STUDY

[ 악성코드 ] PreLab4

L_Chae 2023. 10. 19. 13:59

코드 인젝션 사용하는 이유 :

1. 메모리를 적게 차지함. (DLL 인젝션 보다는)

2. 탐지 가능성이 낮다. (DLL 인젝션 보다는) : DLL인젝션 같은 경우 인젝션 되고 이젝션되기 전까지는 메모리 공간을 차지하고 있기 때문에 탐지될 가능성이 있음. 공간도 크게 차지함(코드 인젝션 보다). 코드 인젝션의 경우 이런 측면에서 DLL 인젝션 보다 탐지 가능성이 낮음.

3. Code Injector 필요 

 


1. 코드 인젝션 하기

 

(0) CodeInjection.cpp 파일에서 dwSize = (DWORD)InjectCode - (DWORD)ThreadProc;

뒤에 dwSize = (DWORD)abs((int)dwSize);를 추가한다.

 

(1) Windows 10 가상머신에 접속한 후, Visual Studio를 구동하여 CodeInjection.cpp에 대응되는 CodeInjection.exe 파일을 생성한다. (빈 프로젝트, 유니코드 문자집합, Release 모드에서 생성함)

 

(2) notepad.exe를 실행한 후 Process Explorer를 실행하여 notepad.exePID값을 알아낸다.

 

(3) Windows PowerShell상에서 notepad.exePID를 첫 번째 인자로 설정하여 CodeInjection.exe을 실행한다.

 

(4) notepad.exe 프로세스 상에서 www.reversecore.com 문자를 담은 메시지 박스가 나타나는 것을 확인한다.

 

소스코드

더보기
// CodeInjection.cpp
// reversecore@gmail.com
// http://www.reversecore.com

#include "windows.h"
#include "stdio.h"

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" 로드, 전 prelab과는 다르게 하나씩 설정?중..
    if( !hMod )
        return 1;

    // GetProcAddress()									  hMod = user32.dll
    pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]);  // user32.dll에 있는 "MessageBoxA"의 주소를 가져옴
    if( !pFunc )
        return 1;

    // 최종적으로 MessageBoxA()를 띄움. 포인터를 통해 access해서 호출하는 방식.
    ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);

    return 0;
}

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"); // kernel32.dll에 정의된 함수를 사용. 사용했다고 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 : dwPID에 해당하는 프로세스를 open함 ex) notepad의 프로세스
    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,          // hProces
/*할당받은 공간을 가리키는 포인터*/   NULL,                 // lpAddress
     /*RemoteBuf[]*/                  dwSize,               // dwSize
                                      MEM_COMMIT,           // 실제 할당, commit하는 애(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(), 파라미터 삽입?
	// 둘이 빼면 Thread Proc함수의 내용이 남음
    dwSize = (DWORD)InjectCode - (DWORD)ThreadProc; //InjectCode : 인젝 함수의 시작주소, ThreadProc : 스레드 함수의 시작 주소.
	dwSize = (DWORD)abs((int)dwSize); // 추가 수정 : size가 항상 절대값(abs)이 나오게끔

	// size만큼 공간 할당. 시작 주소를 받게됨
    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;
}

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

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

    if( !LookupPrivilegeValue(NULL,           // lookup privilege on local system
                              lpszPrivilege,  // privilege to lookup 
                              &luid) )        // receives LUID of privilege
    {
        printf("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) )
    { 
        printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); 
        return FALSE; 
    } 

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

    return TRUE;
}

int main(int argc, char *argv[])
{
    DWORD dwPID     = 0; // PID 초기화

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

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

    // code injection
    dwPID = (DWORD)atol(argv[1]); // PID 값을 인자로 받아와 인젝션 실행함
    InjectCode(dwPID);

	return 0;
}

1. DLL 인젝션에 비해 코드 인젝션이 갖는 장점은 무엇인가?

내용 적게 차지 / 탐지가능성 낮음

 

2. 공격자가 코드 인젝션 기법을 악성 코드 삽입에 사용한다면, 이에 대한 방어로서 어떠한 기법들이 있을지 서술하시오.

런타임 메모리에서 검사

확실한 건 동적분석을 통해 악성 행위를 탐지