[英]C++ SYSENTER x86 calls in inline assembly
我即將學習 x86 上的 sysenter 是如何工作的。 我在 x86 平台上創建了一個簡單的控制台應用程序,它應該在內聯匯編中手動調用 NtWriteVirtualMemory function。
我從這里的代碼開始,但似乎編譯器不理解操作碼“sysenter”,所以我決定用 sysenter 的字節_emit
它們。(也許我需要在我的項目設置中更改一些東西?)它編譯但是當它即將調用 function 視覺工作室給我一個錯誤,我的ret
在執行時是一條非法指令,程序停止。
有人知道如何正確地做到這一點嗎?
#include <windows.h>
#include <iostream>
__declspec(naked) void __KiFastSystemCall()
{
__asm
{
mov edx, esp
// need to emit "sysenter" because of syntaxerrors, "Opcode"; "newline"
_emit 0x0F
_emit 0x34
ret // illegal instructiona after execute?
}
}
void Test_NtWriteVirtualMemory(HANDLE hProcess, PVOID BaseAddress, PVOID Buffer, SIZE_T sizeToWrite, SIZE_T* NumberOfBytesWritten)
{
__asm
{
push NumberOfBytesWritten
push sizeToWrite
push Buffer
push BaseAddress
push hProcess
mov eax, 0x3A // Syscall ID NtWriteVirtualMemory in Windows10
mov edx, __KiFastSystemCall
call edx
add esp, 0x14 // 5 push * 4 bytes 20 dec
retn
}
}
void Test_NtWriteVirtualMemory(HANDLE hProcess, PVOID BaseAddress, PVOID Buffer, SIZE_T sizeToWrite, SIZE_T* NumberOfBytesWritten)
{
__asm
{
push NumberOfBytesWritten
push sizeToWrite
push Buffer
push BaseAddress
push hProcess
mov eax, 0x3a // Syscall ID NtWriteVirtualMemory in Windows10
mov edx, 0x76F88E00
call edx
ret 0x14
}
}
int main()
{
std::cout << "Test Hello World\n";
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessId("MyGame.exe"));
if (hProcess == NULL)
return false;
DWORD TestAddress = 0x87A0B4; // harcoded
DWORD TestValue = 4;
Test_NtWriteVirtualMemory(hProcess, (PVOID)TestAddress, (PVOID)TestValue, sizeof(DWORD), NULL);
CloseHandle(hProcess);
return 0;
}
您是否有僅32 位版本的 Windows?
sysenter
是int 2eh
2eh 的“繼任者”,並在 Windows XP 時代引入。
Windows 的 64 位版本不使用它,實際上它已被刪除,因為:
sysenter
和sysret
在 AMD CPU 的長模式下是非法的(無論兼容模式如何)。IA32_SYSENTER_CS
MSR 被 64 位版本的 Windows 1保留為零。sysenter
時導致 #GP 錯誤。__KiFastSystemCall
,您應該會看到調試器在執行sysenter
時捕獲代碼為0xc0000005的異常。 因此,為了使用sysenter
,您必須擁有真正的 32 位版本的 Windows。
在 64 位版本的 Windows 上運行 32 位程序將不起作用,這是兼容模式(通過 WOW64 機器完成)。
如果除了擁有 64 位版本的 Windows 之外,您還擁有 AMD CPU,那么它就不會工作兩倍。
Windows 64 位對 64 位程序使用syscall
或間接調用TEB 2的WOW32Reserved
字段,您應該使用這些。
請注意,64 位系統調用約定與通常的約定略有不同:特別是它假設syscall
在它自己的 function 中,因此它期望堆棧上的參數向上移動 8。另外,第一個參數必須在r10
中,而不是rcx
中。
例如,如果您內聯syscall
指令,則堆棧上的第一個參數(如果有)必須位於rsp + 28h
而不是rsp + 20h
。
32 位兼容模式系統調用約定也不同,您需要將eax
和ecx
都設置為特定值。
我沒有深入了解ecx
的確切用途,但它可能與稱為Turbo thunks
的優化有關,並且必須設置為特定值。
請注意,雖然系統調用編號非常不穩定,但 turbo thunk 甚至更多,因為它們可以被管理員禁用。
1我沒有明確的來源,在我的sysenter
版本上它只是零,它使系統輸入錯誤。
2即call DWORD [fs:0c0h]
,這將指向一個代碼,該代碼將跳轉到一個 64 位代碼段的門描述符,然后將執行syscall
由於您在 ret 指令而不是 sysenter 指令上獲得了非法指令,因此您知道 sysenter 指令已被 CPU 正確解碼。 您的呼叫進入 kernel 模式,但 kernel 不喜歡您的系統調用調用。
可能它依賴於用戶空間來幫助保存一些寄存器,因為sysenter
非常小。 從 kernel 返回后檢查堆棧指針,在讓ret
執行之前單步執行。
我只是推測問題所在,但是將系統調用門包裝在另一個 function 調用中在我看來是錯誤的。 正如我在評論中所說,不要這樣做,因為系統調用號可能會改變你。
在 Linux 下,32 位進程通過 VDSO(由內核注入到其地址空間的庫)調用以獲得最佳系統調用指令,其使用方式與 kernel 想要的方式相匹配。 ( sysenter
不保留堆棧指針,因此用戶空間必須提供幫助。)
也許如果你想玩這個指令,你最好編寫一個玩具操作系統。
抱歉,這不是很多答案,但並非完全不合理。
在 x86 Windows 中進行系統調用與 x64 不同。 您需要在ret中指定正確的 arguments 長度,否則您將獲得非法指令和/或運行時 esp。
此外,我不建議您使用內聯匯編,而是在 .asm 文件中或作為 shellcode 使用它。
要對 x86 Windows 進行正確的 x86 系統調用:
mov eax, SYSCALL_INDEX
call sysentry
ret ARGUMENTS_LENGTH_SIZE
mov edx,esp
sysenter
retn
要在 x64 Windows 上進行正確的 x64 系統調用:
mov eax, SYSCALL_INDEX
mov r10,rcx
syscall
retn
以上將在任何 x86 和 x64 Windows(已測試)上 100% 正確工作。 雖然不能幫助你進行內聯匯編,因為我從來沒有那樣使用過它。
享受。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.