You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

问询:生成调用Windows API的自包含汇编子程序及C/C++函数独立执行方案

Turn C/C++ Functions into Self-Contained Executable Code + API-Calling Assembly Routines

Got it, let's tackle this problem head-on—you've already got shellcode execution working via thread creation or hijacking, but hand-writing shellcode is a massive time sink. Here's how to convert your C/C++ functions into fully self-contained, runnable machine code, plus a self-contained assembly routine to call Windows APIs without relying on import tables:

1. Compile C/C++ Functions to Standalone Shellcode

Instead of writing bytes by hand, we can compile your functions directly into dependency-free machine code. Here's the step-by-step:

Step 1: Write a Minimal CRT-Free C Function

Avoid using standard library functions that pull in C Runtime (CRT) dependencies. For example, a simple message box function (we'll handle API resolution manually later):

void MessageBoxShellcode() {
    typedef int(WINAPI* MessageBoxAPtr)(void*, const char*, const char*, unsigned int);
    MessageBoxAPtr pMessageBoxA;
    // We'll fill pMessageBoxA with our assembly routine below
}

Step 2: Compile with No Dependencies

Use MinGW (or Clang) to compile without CRT, exceptions, or unwind tables—this ensures the output has no external dependencies:

gcc -c -O2 -fno-asynchronous-unwind-tables -fno-exceptions -fno-rtti -nostdlib your_function.c -o your_function.o

Step 3: Extract Pure Machine Code

Use objcopy to pull out just the .text (code) section from the object file—this gives you raw, self-contained machine code:

objcopy -O binary -j .text your_function.o your_function.bin

This .bin file is ready to write into target process memory, just like your hand-written shellcode.

Note: If your function needs Windows APIs, the compiled code won't have import table entries (which is what we want for self-containment). We need to dynamically resolve API addresses at runtime, which brings us to the assembly routine.

2. Self-Contained Assembly Routine: Resolve Windows APIs via PEB

To call Windows APIs without an import table, we'll traverse the Process Environment Block (PEB) to find kernel32.dll's base address, then walk its export table to get our target API's address. Here's a x86 assembly implementation:

; Input: EBX = pointer to ASCII API name string
; Output: EAX = address of the API function
GetProcAddressByPEB:
    push ebp
    mov ebp, esp
    push ebx ecx edx esi edi

    ; Grab PEB address from FS register
    mov eax, fs:[0x30]
    ; Get PEB_LDR_DATA structure
    mov eax, [eax + 0xC]
    ; Walk to kernel32.dll in the module list
    mov eax, [eax + 0x14]  ; InMemoryOrderModuleList
    mov eax, [eax]         ; Next entry (ntdll.dll)
    mov eax, [eax]         ; Next entry (kernel32.dll)
    mov ebx, [eax + 0x10]  ; kernel32.dll base address

    ; Locate PE header
    mov edx, [ebx + 0x3C]  ; e_lfanew (offset to PE header)
    add edx, ebx
    ; Get export table RVA
    mov edx, [edx + 0x78]  ; Export table offset
    add edx, ebx

    ; Pull export table details
    mov ecx, [edx + 0x18]  ; Number of exported functions
    mov esi, [edx + 0x20]  ; RVA of function names
    add esi, ebx
    mov edi, [edx + 0x24]  ; RVA of function addresses
    add edi, ebx

FindAPI:
    dec ecx
    mov eax, [esi + ecx*4] ; Get current function name RVA
    add eax, ebx           ; Convert to absolute address
    push ecx               ; Save counter
    mov ecx, ebx           ; ECX = pointer to our target API name

CompareName:
    mov dl, [eax]          ; Current char from export name
    cmp dl, [ecx]          ; Compare to target name char
    jne NextName           ; Mismatch, move to next
    test dl, dl            ; Check if we hit null terminator
    jz FoundAPI            ; Match found!
    inc eax
    inc ecx
    jmp CompareName

NextName:
    pop ecx
    jnz FindAPI            ; Keep searching if we haven't hit 0
    xor eax, eax           ; Return 0 if API not found
    jmp End

FoundAPI:
    pop ecx
    mov eax, [edi + ecx*4] ; Get function address RVA
    add eax, ebx           ; Convert to absolute address

End:
    pop edi esi edx ecx ebx
    pop ebp
    ret

Integrate This with Your C/C++ Function

Link this assembly routine into your C code, then use it to resolve API addresses at runtime:

// Declare the assembly function
extern void* GetProcAddressByPEB(const char* apiName);

void SelfContainedMessageBox() {
    typedef int(WINAPI* MessageBoxAPtr)(void*, const char*, const char*, unsigned int);
    MessageBoxAPtr pMessageBoxA = (MessageBoxAPtr)GetProcAddressByPEB("MessageBoxA");
    
    if (pMessageBoxA) {
        pMessageBoxA(NULL, "Self-contained API call success!", "Win", MB_OK);
    }
}

Recompile this with the same no-dependency flags, extract the machine code, and you've got a fully self-contained function that calls Windows APIs without imports.

3. Execute the Code Like Your Manual Shellcode

This compiled machine code works exactly like your hand-written shellcode:

  • Use VirtualAllocEx to allocate executable memory in the target process (set PAGE_EXECUTE_READWRITE).
  • Write the .bin content into that memory with WriteProcessMemory.
  • Either start a new thread with CreateRemoteThread or hijack an existing thread by modifying its context (set EIP/RIP to the memory address of your code).

内容的提问来源于stack exchange,提问作者Pierre Ciholas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:25:52