执行 shellcode 时出现 win32 c++ 访问冲突

win32 c++ acces violation when executing shellcode

我写这个函数是为了根据this talk打破x86指令集来找出汇编指令的长度。

/* shellcode is a pointer to a buffer contains assembly instructions
   size is the size of that buffer */
DWORD GetInstructionLength(BYTE* shellcode,SIZE_T size) {
    LPVOID RWXBuff = nullptr; //buffer with read,write,execute perm
    LPVOID RWBuff = nullptr;  //buffer with read,write perm
    HANDLE CurProc = GetCurrentProcess();
    DWORD Tmp;
    DWORD CurrOffset = 1;
    RWXBuff = VirtualAllocEx(CurProc, NULL, 32, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    RWBuff = RWXBuff; 
    for (int i = 0; i < 16; i++, RWBuff = static_cast<char*>(RWBuff) + 1);
    VirtualProtectEx(CurProc, RWBuff, 17, PAGE_READWRITE, &Tmp);
    void (*func)();

    for (;;CurrOffset++) {
        __try {
            LPVOID ShellcodeAddr = static_cast<char*>(RWXBuff) + 16 - CurrOffset;
            std::cout << "memcpy(" << ShellcodeAddr << ", " << (void*)shellcode << ", " << size << ")\n";
            memcpy(ShellcodeAddr, shellcode, size);
            std::cout << "memcpied" << std::endl;
            MEMORY_BASIC_INFORMATION minfo;
            VirtualQueryEx(GetCurrentProcess(), ShellcodeAddr, &minfo, sizeof(MEMORY_BASIC_INFORMATION));
            if (PAGE_EXECUTE_READWRITE == minfo.Protect)
                std::cout << "shellcode at " << ShellcodeAddr << "is executable" << std::endl;
            else
                std::cout << "shellcode at " << ShellcodeAddr << "is NOT executable" << std::endl;
            func = (void(*)())ShellcodeAddr;
            func();
            break;
        }
        __except (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) {
            std::cout << "Bug!" << std::endl;
            continue;
        }
    }
    VirtualFreeEx(CurProc, RWBuff, 0, MEM_RELEASE);
    return CurrOffset;
}

来电者:

int main()
{
    unsigned char shellcode = 0x90; // nop opcode
    unsigned char* buf = &shellcode;
    size_t size = helper::GetInstructionLength(buf, 1);
    std::cout << "Shellcode lenght = " << size << std::endl;
}

输出:

buffer with rwx at 0000023048E80000
buffer with rw- at 0000023048E80010
shellcode at 0000023048E8000F is NOT executable
Bug!
shellcode at 0000023048E8000E is NOT executable
Bug!
shellcode at 0000023048E8000D is NOT executable
Bug!
shellcode at 0000023048E8000C is NOT executable
Bug!
shellcode at 0000023048E8000B is NOT executable
Bug!
shellcode at 0000023048E8000A is NOT executable
Bug!
shellcode at 0000023048E80009 is NOT executable
Bug!
shellcode at 0000023048E80008 is NOT executable
Bug!
shellcode at 0000023048E80007 is NOT executable
Bug!
shellcode at 0000023048E80006 is NOT executable
Bug!
shellcode at 0000023048E80005 is NOT executable
Bug!
shellcode at 0000023048E80004 is NOT executable
Bug!
shellcode at 0000023048E80003 is NOT executable
Bug!
shellcode at 0000023048E80002 is NOT executable
Bug!
shellcode at 0000023048E80001 is NOT executable
Bug!
shellcode at 0000023048E80000 is NOT executable
Bug!
Bug!
...

但它不会工作,每次都会引发访问冲突,我不知道为什么。你能帮我解决这个问题吗? 因为我只有 3 个月的编码经验,如果这对你来说是错误的代码,我很抱歉

rwx at ...80000/
rw- at ...80010 - 在同一页
x86 内存保护以页面粒度 (4096B) 工作,因此您只需将整个页面更改为不可执行,包括 RWXBuff。

...80000 页的结尾是 80FFF,下一页的开头是 ...81000
(12 个页内偏移地址位 = 3 个十六进制数字。)


请注意,MSVC 有一个 FlushInstructionCache function, like GNU C's __builtin____clear_cache(). In GNU C, that's not optional even on x86, despite x86 having coherent instruction cache. The reason you need it for safety after storing machine-code bytes into a buffer is so the compiler knows those stores can't be optimized away (or reordered) if it doesn't see any data accesses to them

MSVC 是相同的:you need to call FlushInstructionCache 在 C 将机器代码字节分配给数组之后,在取消指向该数组的函数指针之前。

它通常可以在没有 x86(包括 x86-64)的情况下工作,不像在 ARM 和其他没有连贯 I-cache 的 ISA 上,但它可能因为它不能,尤其是如果您将不同的代码字节分配给缓冲区并再次调用它。 (这很可能会诱使编译器消除死存储,只做后面的存储。至少如果它确定内存是这个函数私有的,而不是从它正在调用的任何未知函数全局访问的。)