通过调用C/C++中的Windows API函数生成自包含的程序集子例程

Pie*_*las 2 c++ windows assembly winapi compilation

我已经完成了shellcode执行的一些实验,其中我编写了自己的shellcode,将其写入目标程序的内存中,我希望它在其中执行并使用新线程或线程劫持执行它.

这很好用,但手动编写shellcode非常耗时,因此我正在寻找一种方法,能够用C或C++编写一个在编译后完全自包含的函数.这意味着任何编译的函数都应该是独立可执行的.这样我就可以直接将它写入我的目标程序,准备用WriteProcessMemory执行.因此,推送shellcode将使用如下代码:

#include <Windows.h>
#include <iostream>

using namespace std;

BOOL MakeABeep() {
    return Beep(0x500, 0x500);
}
DWORD MakeABeepEnd() { return 0; }

int main() {
    DWORD pid = 0;
    cout << "PID: ";
    cin >> dec >> pid;
    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    if (!hProcess) { cout << "OpenProcess failed GLE = " << dec << GetLastError() << endl; return EXIT_FAILURE; }

    void* buf = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (!buf) { cout << "VirtualAllocEx failed GLE = " << dec << GetLastError() << endl; return EXIT_FAILURE; }

    SIZE_T size = (DWORD64)MakeABeep - (DWORD64)MakeABeepEnd;
    BOOL wpmStatus = WriteProcessMemory(hProcess, buf, MakeABeep, size, NULL);
    if (!wpmStatus) { cout << "WriteProcessMemory failed GLE = " << dec << GetLastError() << endl; return EXIT_FAILURE; }

    HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)buf, NULL, NULL, NULL);
    if (!hThread) { cout << "CreateRemoteThread failed GLE = " << dec << GetLastError() << endl; return EXIT_FAILURE; }
    WaitForSingleObject(hThread, INFINITE);

    VirtualFreeEx(hProcess, buf, 0, MEM_RELEASE);
    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

如果使用MSVC编译器的默认选项进行编译,则只jmp复制一堆指令,这些指令似乎是一个跳转表.为了避免这个问题,我在编译器选项中禁用了增量链接,现在函数中的任何代码都MakeABeep被正确复制,但对导入函数的调用除外.

在我的shellcode中,我按照调用约定的要求传递参数,然后我将要调用的函数的地址放在寄存器中rax,最后我用函数调用函数call rax.

是否有可能使编译器生成类似的东西?关键是生成的二进制文件必须具有可以独立执行的仅包含自包含的子例程.

例如,这是为MakeABeep函数生成的汇编代码: MakeABeep汇编代码 为了能够直接运行它,而不是mov rax, QWORD PTR [rip+0x?]编译器应该将Beep函数的完整地址移动到rax中.

请忽略与目标程序中不同地址可能未加载或加载的模块相关的问题,我只打算调用kernel32和ntdll中的函数,这些函数肯定是在不同的进程中加载​​和在同一地址.

谢谢您的帮助.

And*_*ers 5

编译器不知道Beep功能的完整地址.Beep函数存在于kernel32.dll中,此.DLL标记为ASLR兼容,理论上每次运行程序时都可以更改其地址.没有编译器功能允许您在.DLL中生成函数的真实地址,因为这样的功能非常无用.

我能想到的一个选择是使用在运行时用正确的函数地址替换的魔术cookie:

SIZE_T beepaddr = 0xff77ffffffff7001ull; // Magic value
((BOOL(WINAPI*)(DWORD,DWORD))beepaddr)(0x500, 0x500); // Call Beep()
Run Code Online (Sandbox Code Playgroud)

编译成

00011   b9 00 05 00 00   mov     ecx, 1280      ; 00000500H
00016   48 b8 01 70 ff ff ff ff 77 ff    mov     rax, -38280596832686079    ; ff77ffffffff7001H
00020   8b d1        mov     edx, ecx
00022   ff d0        call    rax
Run Code Online (Sandbox Code Playgroud)

然后,您必须编写一个包装器WriteProcessMemory,知道如何使用正确的地址查找和替换这些魔术值.

一些shell代码将有自己的迷你实现,GetModuleHandleGetProcAddress在PEB模块列表中查找模块,然后搜索导出目录.它们经常对名称使用迷你散列函数,因此它们不必处理字符串.

如果你注入了大量代码,你可能会厌倦这些黑客攻击,只需像其他人一样在远程进程中加载​​.DLL.