Windows 7 x86使用sysenter执行直接系统调用

时间:2013-11-03 13:38:52

标签: windows kernel system-calls sysenter

我正在尝试编写一些程序来直接调用系统调用,而无需通过ntdll.dll

我的代码(Visual Studio语法......):

#include <windows.h>

int main()
{
   _asm{
        push arg1
        push arg2
        push arg3
        mov eax,syscall_id
        mov edx,esp
        _emit 0xf
        _emit 0x34 //sysenter opcodes...
       }

当程序尝试执行sysenter指令时,程序会因此访问冲突而崩溃:

CALL DWORD PTR DS:[EAX+EDX*4] // Access Violation when reading [00000128]

EAX == 0x00000000
EDX == 0x0000004D

我试图在所需的系统调用中使用内核调试器放置一个hw断点,并且执行流程没有到达那里......

我猜这个问题与堆栈顺序/深度有关。

非常感谢!

解决:

我想问题是我试图在不加载user32和gdi32 dll的情况下执行win32k系统调用。

刚补充说:

LoadLibraryW(L"user32.dll");
LoadLibraryW(L"gdi32.dll");

问题解决了..

如果有人更好地了解为什么在没有加载这些dll的情况下发生这种情况,我将很高兴知道:)

1 个答案:

答案 0 :(得分:4)

SYSENTER和SYSEXIT操作码的行为与普通的call / ret操作码不同。

SYSENTER和SYSEXIT peform都会跳转到预定义的地址(一个在内核空间,另一个在用户空间)。

具体来说,SYSEXIT设置为跳转到ntdll中的KiFastSystemCallRet。通常在ntdll中导出的系统过程调用KiFastSystemCall,而不是直接使用SYSENTER。让我们看看ntdll中的KiFastSystemCall和KiFastSystemCallRet:

KiFastSystemCall:     mov edx, esp
                      sysenter
KiFastSystemCallRet:  retn

您的代码将导致系统过程从内核返回到RETN指令,这意味着返回到arg3中的任何地址。我不知道为什么加载user32和gdi32会改变任何东西,也许它与arg3的值有关。

无论如何,自己调用内核系统过程最安全的方法是使用KiFastSystemCall。您也可以编写自己的代码,记住从内核模式返回时的第一个操作码是RETN,因此您的代码需要在堆栈顶部有返回地址。