簡體   English   中英

C++ SYSENTER x86 在內聯匯編中調用

[英]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?

sysenterint 2eh 2eh 的“繼任者”,並在 Windows XP 時代引入。
Windows 的 64 位版本不使用它,實際上它已被刪除,因為:

  1. sysentersysret在 AMD CPU 的長模式下是非法的(無論兼容模式如何)。
  2. 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 2WOW32Reserved字段,您應該使用這些。
請注意,64 位系統調用約定與通常的約定略有不同:特別是它假設syscall它自己的 function 中,因此它期望堆棧上的參數向上移動 8。另外,第一個參數必須在r10中,而不是rcx中。

例如,如果您內聯syscall指令,則堆棧上的第一個參數(如果有)必須位於rsp + 28h而不是rsp + 20h

32 位兼容模式系統調用約定也不同,您需要將eaxecx都設置為特定值。
我沒有深入了解ecx的確切用途,但它可能與稱為Turbo thunks的優化有關,並且必須設置為特定值。
請注意,雖然系統調用編號非常不穩定,但 turbo thunk 甚至更多,因為它們可以被管理員禁用。


1我沒有明確的來源,在sysenter版本上它只是零,它使系統輸入錯誤。

2call 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM