问题:为什么程序在shellcode成功完成它的目标后会抛出一个违反执行位置异常?
描述:我的目标是使用调用 Windows API 函数的 x86 shellcode 将 DLL 加载和卸载到当前程序中。虽然程序成功地完成了这个目标,但 Visual Studio 然后告诉我执行位置存在违规行为。我知道程序成功执行,因为测试 DLL 文件在附加和分离时打印。另一个需要注意的重要细节是这仅在调用卸载函数时发生,加载函数绝对没有问题。(如果这很重要,我正在使用 C++20 在 Visual Studio 2019 的 Windows 10 上执行此操作)
我知道 shellcode 没有正确设置堆栈帧,但在将执行返回到被调用函数之前,我确保 ESP 已恢复正常。我保存了 EAX 并在卸载功能中将其恢复正常。我制作这个测试程序的最终目标是生成可用于我正在开发的 dll 注入程序中的远程线程上下文修补方法的 shellcode。我还多次验证了用于查找返回地址的偏移量。任何帮助表示赞赏,谢谢!
这是控制台输出。
随附的!DLLMain 位于 0x79EF134D
已分离!
这是抛出的异常。
在 Shellcode DLL
Loading.exe 中的 0x9269D814 处引发异常:0xC0000005:执行
位置 0x9269D814 的访问冲突。
这是主文件,它只有大约 120 行。
const dword follow_relative_jump(const pbyte pointer)
{
if (pointer)
{
if (pointer[0] == 0xE9 || pointer[0] == 0xEB)
{
return reinterpret_cast<dword>(pointer + 5 + reinterpret_cast<psdword>(pointer + 1)[0]);
}
}
return reinterpret_cast<dword>(pointer);
}
void load_dll(const dword path_address)
{
/*
68 90 90 90 90 -> push 0x???????? (return address buffer)
68 90 90 90 90 -> push 0x???????? (LoadLibraryA() address buffer)
68 90 90 90 90 -> push 0x???????? (DLL path address buffer)
FF 54 24 04 -> call [esp + 4] (calling LoadLibraryA())
83 C4 08 -> add esp, 8 (cleaning up the stack, except for return address)
C3 -> ret (return to return address that was pushed first, it should pop it off the stack and return ESP to normal)
*/
std::vector<byte> shellcode = {
0x68, 0x90, 0x90, 0x90, 0x90,
0x68, 0x90, 0x90, 0x90, 0x90,
0x68, 0x90, 0x90, 0x90, 0x90,
0xFF, 0x54, 0x24, 0x04,
0x83, 0xC4, 0x08,
0xC3
};
// Offset is the distance from the function prologue to the next instruction after the call to load_dll()
reinterpret_cast<pdword>(shellcode.data() + 1)[0] = follow_relative_jump(reinterpret_cast<pbyte>(&load_dll)) + 0x22C;
reinterpret_cast<pdword>(shellcode.data() + 6)[0] = reinterpret_cast<dword>(&LoadLibraryA);
reinterpret_cast<pdword>(shellcode.data() + 11)[0] = path_address;
if (const auto allocation = VirtualAlloc(NULL, shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE))
{
memcpy(allocation, shellcode.data(), shellcode.size());
reinterpret_cast<void(__cdecl*)()>(allocation)();
VirtualFree(allocation, shellcode.size(), MEM_FREE);
}
}
void unload_dll(const dword path_address)
{
/*
68 90 90 90 90 -> push 0x???????? (return address buffer)
50 -> push eax (save EAX so we can set it back later)
68 90 90 90 90 -> push 0x???????? (GetModuleHandleA() address buffer)
68 90 90 90 90 -> push 0x???????? (DLL path address buffer)
FF 54 24 04 -> call [esp + 4] (calling GetModuleHandleA())
83 C4 08 -> add esp, 8 (clean up the stack, except for return address and saved EAX)
68 90 90 90 90 -> push 0x???????? (FreeLibrary() address buffer)
50 -> push eax (Handle to module returned by GetModuleHandleA() in EAX)
FF 54 24 04 -> call [esp + 4] (calling FreeLibrary())
83 C4 08 -> add esp, 8 (clean up stack, except for return address and saved EAX)
58 -> pop eax (set back EAX to what it was before)
C3 -> ret (return to return address that was pushed first, it should pop it off the stack and return ESP to normal)
*/
std::vector<byte> shellcode = {
0x68, 0x90, 0x90, 0x90, 0x90,
0x50,
0x68, 0x90, 0x90, 0x90, 0x90,
0x68, 0x90, 0x90, 0x90, 0x90,
0xFF, 0x54, 0x24, 0x04,
0x83, 0xC4, 0x08,
0x68, 0x90, 0x90, 0x90, 0x90,
0x50,
0xFF, 0x54, 0x24, 0x04,
0x83, 0xC4, 0x08,
0x58,
0xC3
};
// Offset is the distance from the function prologue to the next instruction after the call to unload_dll()
reinterpret_cast<pdword>(shellcode.data() + 1)[0] = follow_relative_jump(reinterpret_cast<pbyte>(&unload_dll)) + 0x2AF;
reinterpret_cast<pdword>(shellcode.data() + 7)[0] = reinterpret_cast<dword>(&GetModuleHandleA);
reinterpret_cast<pdword>(shellcode.data() + 12)[0] = path_address;
reinterpret_cast<pdword>(shellcode.data() + 24)[0] = reinterpret_cast<dword>(&FreeLibrary);
if (const auto allocation = VirtualAlloc(NULL, shellcode.size(), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE))
{
memcpy(allocation, shellcode.data(), shellcode.size());
reinterpret_cast<void(__cdecl*)()>(allocation)();
VirtualFree(allocation, shellcode.size(), MEM_FREE);
}
}
int main()
{
const char* path = "C:\\Users\\maxbd\\Desktop\\test.dll";
load_dll(reinterpret_cast<dword>(path));
unload_dll(reinterpret_cast<dword>(path));
static_cast<void>(std::getchar());
return 0;
}