Windows和本机API中的系统调用?

Posted

技术标签:

【中文标题】Windows和本机API中的系统调用?【英文标题】:System Calls in windows & Native API? 【发布时间】:2011-01-30 04:47:22 【问题描述】:

最近我在 *NIX 操作系统中使用了大量的汇编语言。我想知道 windows 域。


linux 中的调用约定:

mov $SYS_Call_NUM, %eax
mov $param1 , %ebx
mov $param2 , %ecx
int $0x80

就是这样。这就是我们应该如何在 linux 中进行系统调用。

linux中所有系统调用的引用:

关于哪些 $SYS_Call_NUM 和哪些参数我们可以使用这个参考:http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html

官方参考:http://kernel.org/doc/man-pages/online/dir_section_2.html


Windows 中的调用约定:

???

Windows中所有系统调用的引用:

???

非官方:http://www.metasploit.com/users/opcode/syscalls.html,但是除非我知道调用约定,否则我如何在汇编中使用它们。

官方:???

如果你说,他们没有记录。那么如何在不知道系统调用的情况下为 windows 编写 libc 呢?一个人将如何进行 Windows 汇编编程?至少在驱动程序编程中需要知道这些。对吗?

现在,所谓的 Native API 怎么样了? Native APISystem calls for windows 是指同一事物的不同术语吗?为了确认我比较了来自两个非官方来源的这些

系统调用:http://www.metasploit.com/users/opcode/syscalls.html

原生 API:http://undocumented.ntinternals.net/aindex.html

我的观察:

    所有系统调用都以字母 Nt 开头,因为 Native API 包含许多不以字母 Nt 开头的函数。 System Call of windowsNative API 的子集。系统调用只是 Native API 的一部分。

谁能证实这一点并解释一下。

编辑:

还有另一个答案。这是第二个答案。我真的很喜欢它,但我不知道为什么回答者删除了它。我要求他重新发布他的答案。

【问题讨论】:

另请阅读:***.com/questions/919051/… 您的 metasploit 链接已损坏... “如何为 windows 编写 libc” - 使用 documented API。 Linux 没有 API,系统调用是最接近的东西,所以这就是你需要在 Linux 上使用的东西。不是因为这是与操作系统交互的绝佳方式,而是因为它是唯一的方式。 【参考方案1】:

如果您在 Windows 下进行汇编编程,则无需进行手动系统调用。您可以使用 NTDLL 和 Native API 来为您完成这项工作。

Native API 只是内核模式方面的包装器。它所做的只是为正确的 API 执行系统调用。

您永远不需要手动系统调用,因此您的整个问题都是多余的。

Linux 系统调用代码不会改变,Windows 会改变,这就是为什么您需要通过一个额外的抽象层(又名 NTDLL)来工作。

编辑:

此外,即使您在汇编级别工作,您仍然可以完全访问 Win32 API,没有理由一开始就使用 NT API!导入、导出等在汇编程序中都可以正常工作。

EDIT2:

如果您真的想要进行手动系统调用,您将需要为每个相关的 Windows 版本反转 NTDLL,添加版本检测(通过 PEB),并为每个调用执行系统调用查找。

但是,那将是愚蠢的。 NTDLL 的存在是有原因的。

人们已经完成了逆向工程部分:请参阅https://j00ru.vexillium.org/syscalls/nt/64/ 以获取每个 Windows 内核的系统调用号表。 (请注意,即使在 Windows 10 版本之间,后面的行也会发生变化。)同样,在您自己的机器上进行仅供个人使用的实验以了解有关 asm 和/或 Windows 内部的更多信息之外,这是一个坏主意。不要将系统调用内联到您分发给其他人的代码中。

【讨论】:

一篇简短而简洁的文章,是对这个答案的一个很好的补充:codeproject.com/KB/system/Win32.aspx 那篇文章不是你要找的。它显示了如何执行手动系统调用,您不应该这样做,因为数字会随着 Windows 版本和服务包的变化而变化。正如我已经说过的,您应该使用 Windows API 为您执行调度。 NTDLL 自动映射到所有进程。因此,即使由于某种原因您不能使用导入(我想不出任何合理的时间会出现这种情况),您仍然可以获取 NTDLL 的句柄并手动枚举 EAT 以获得必要的函数指针(尽管你不应该这样做)。 @claws 这篇文章非常很有趣,非常感谢 这并不是专门针对这个答案的作者,但我不明白为什么这些“不要这样做”的答案在 SO 上如此普遍,这不应该成为“专业和爱好者程序员”的网站?为什么一方面是这个网站encourage extreme research effort,但是当你遇到这些低层次的好奇心问题时,我会看到源源不断的“你不需要知道!!!”。谁又是这个网站的真正目标受众?需要牵手的人还是真正愿意深挖的人? 这里是最初在 2010 年 3 月 22 日 5:09 用爪子连接的北极圈的新位置:codeproject.com/Articles/33870/…【参考方案2】:

您需要了解的有关 Windows 系统调用约定的另一件事是,据我了解,系统调用表是作为构建过程的一部分生成的。这意味着它们可以简单地改变——没有人跟踪它们。如果有人在列表顶部添加了一个新的,那没关系。 NTDLL 仍然可以工作,所以其他调用 NTDLL 的人仍然可以工作。

甚至用于执行系统调用的机制(int 或 sysenter)也不是一成不变的,而且在过去发生了变化,我认为曾几何时,相同版本的 windows 使用不同的 DLL,这些 DLL 使用不同的入口机制取决于机器中的 CPU。

【讨论】:

+1 因为我觉得这很有趣。您是否有任何资源(互联网或书籍)可以让您了解有关这种 Win32 API/系统调用内部结构的更多信息? 它有点过时了,但我认为 Inside Windows 2000 涵盖了这一点。 nynaeve.net/?p=48 进行了很好的讨论,并且还演示了在 x86 和 x64 上的 Windows 上使用的最后三个系统调用约定。 IA64 可能又做了一些完全不同的事情,当然还有 Alpha、PowerPC、MIPS 和其他可能。当然,通常的警告适用 - 所有未记录且可能更改。【参考方案3】:

我有兴趣在没有导入的情况下在程序集中进行 Windows API 调用(作为教育练习),因此我编写了以下 FASM 程序集来执行 NtDll!NtCreateFile 的工作。在我的64位版本Windows(Win10 1803 Version 10.0.17134)上做个粗略的演示,调用后就crash掉了,但是syscall的返回值为零所以成功了。一切都按照 Windows x64 调用约定进行设置,然后将系统调用号加载到 RAX 中,然后是运行调用的 syscall 汇编指令。我的示例创建了文件 c:\HelloWorldFile_FASM,因此它必须以“管理员身份”运行。

format PE64 GUI 4.0


entry start


section '.text' code readable executable


 start: 
 ;puting the first four parameters into the right registers

                            mov rcx, _Handle
                            mov rdx, [_access_mask]
                            mov r8, objectAttributes
                            mov r9, iostatusBlock

 ;I think we need 1 stack word of padding:

                            push 0x0DF0AD8B


 ;pushing the other params in reverse order:

                            push [_eaLength]
                            push [_eaBuffer]
                            push [_createOptions]
                            push [_createDisposition]
                            push [_shareAcceses]
                            push [_fileAttributes]
                            push [_pLargeInterger]

 ;adding the shadow space (4x8)

 ;                               push 0x0
 ;                               push 0x0
 ;                               push 0x0
 ;                               push 0x0

 ;pushing the 4 register params into the shadow space for ease of debugging

                            push r9
                            push r8
                            push rdx
                            push rcx

 ;now pushing the return address to the stack:

                            push endOfProgram

                            mov r10, rcx ;copied from ntdll!NtCreateFile, not sure of the reason for this
                            mov eax, 0x55
                            syscall

 endOfProgram:
                            retn




 section '.data' data readable writeable

 ;parameters------------------------------------------------------------------------------------------------

 _Handle                         dq      0x0
 _access_mask                    dq      0x00000000c0100080
 _pObjectAttributes              dq      objectAttributes        ; at 00402058
 _pIoStatusBlock                 dq           ioStatusBlock
 _pLargeInterger                 dq      0x0
 _fileAttributes                 dq      0x0000000000000080
 _shareAcceses                   dq      0x0000000000000002
 _createDisposition              dq      0x0000000000000005
 _createOptions                  dq      0x0000000000000060
 _eaBuffer                       dq      0x0000000000000000       ; "optional" param
 _eaLength                       dq      0x0000000000000000

 ;----------------------------------------------------------------------------------------------------------


                            align   16
 objectAttributes:
 _oalength                       dq      0x30
 _rootDirectory                  dq      0x0
 _objectName                     dq           unicodeString
 _attributes                     dq      0x40
 _pSecurityDescriptor            dq      0x0
 _pSecurityQualityOfService      dq      securityQualityOfService


 unicodeString:
 _unicodeStringLength            dw      0x34
 _unicodeStringMaxumiumLength    dw      0x34, 0x0, 0x0
 _pUnicodeStringBuffer           dq      _unicodeStringBuffer


 _unicodeStringBuffer            du      '\??\c:\HelloWorldFile_FASM'       ; may need to "run as adinistrator" for the file create to work.



 ioStatusBlock:
 _status_pointer                 dq      0x0
 _information                    dq      0x0


 securityQualityOfService:
 _sqlength                       dd      0xC
 _impersonationLevel             dd      0x2
 _contextTrackingMode            db      0x1
 _effectiveOnly                  db      0x1, 0x0, 0x0

我使用了 Ntdll!NtCreateFile 的文档,还使用内核调试器查看并复制了很多参数。

__kernel_entry NTSTATUS NtCreateFile(
  OUT PHANDLE                      FileHandle,
  IN ACCESS_MASK                   DesiredAccess,
  IN POBJECT_ATTRIBUTES            ObjectAttributes,
  OUT PIO_STATUS_BLOCK             IoStatusBlock,
  IN PLARGE_INTEGER AllocationSize OPTIONAL,
  IN ULONG                         FileAttributes,
  IN ULONG                         ShareAccess,
  IN ULONG                         CreateDisposition,
  IN ULONG                         CreateOptions,
  IN PVOID EaBuffer                OPTIONAL,
  IN ULONG                         EaLength
);

【讨论】:

感谢有关 mov r10, rcx 行的信息;我还没有调查过。我的 windows 版本是 10.0.17134 Build 17134 。 @PeterCordes 我并不是说我建议直接使用系统调用号(因为它们确实会改变),但系统调用号是unofficially documented。 Windows 版本 10.0.17134 是 Windows(1803),NtCreateFile 的系统调用是 0x55。到目前为止,所有 5 个已发布的 Win 10 版本都使用 0x55。 (Win10之前已经使用了0x55以外的系统调用号) mov r10,rcx 是因为系统调用接口通过 r10 而不是 RCX 传递第一个参数。常规 64 位函数调用使用 RCX、RDX、R8 和 R9。 Syscall 使用 R10、RDX、R8 和 R9。原因是 syscall 指令覆盖了 RCX(它也覆盖了 R11)。微软选择使用 R10 作为系统调用的第一个参数,因此你会看到 mov r10, rcx 额外填充(即push 0x0DF0AD8B)的原因是出于性能原因保持堆栈对齐。如果在系统调用之前完成了偶数次推送,则应该添加 8 个字节(四字)的填充(否则您不需要填充)。这是因为在start被执行时,堆栈错位了8,因为返回地址在堆栈上。 至于你的代码为什么失败,是因为你在syscall之后没有清理堆栈。您推送了相当于 13 个四字的值来设置系统调用,您需要在系统调用之后调整堆栈,以在执行 ret 之前将堆栈指针恢复到之前的状态。 13*8=104 字节在系统调用之前放入堆栈。在系统调用之后,您只需执行 add rsp, 104 即可清理堆栈。【参考方案4】:

Windows 系统调用是通过调用诸如kernel32.dllgdi32.dll 等系统DLL 来执行的,这是通过普通的子程序调用来完成的。陷入操作系统特权层的机制没有记录,但这没关系,因为像 kernel32.dll 这样的 DLL 会为您执行此操作。

通过系统调用,我指的是记录在案的 Windows API 入口点,例如 CreateProcess()GetWindowText()。设备驱动程序通常使用与 Windows DDK 不同的 API。

【讨论】:

1.我将如何在汇编语言编程中使用这些? 2. Windows API 和系统调用不是一回事。 WINDOWS API使用系统调用。 linux 域上类似的类比是 POSIX API(WINDOWS API)使用 linux 内核(windows 内核)提供的系统调用。所以,我关心的是 true 系统调用。 我了解 API 调用和进入操作系统特权层的陷阱(你称之为“真正的”系统调用)之间存在差异。这些“真正的”系统调用是 Windows 系统 DLL 的实现细节。您可以通过反汇编 DLL 并查看程序集来弄清楚它们是如何工作的。或者您可以编写您的汇编代码以适当地调用 DLL……这是可能的。【参考方案5】:

Windows 中的官方调用约定:http://msdn.microsoft.com/en-us/library/7kcdt6fy.aspx

(希望这个链接在未来仍然存在;如果没有,只需在 MSDN 上搜索“x64 Software Conventions”)。

Linux 和 Windows x86_64 中的函数调用约定不同。在这两种 ABI 中,参数最好通过寄存器传递,但使用的寄存器不同。有关 Linux ABI 的更多信息,请访问http://www.x86-64.org/documentation/abi.pdf

【讨论】:

这没有回答问题,这是关于调用内核模式的约定。这是针对用户模式函数调用约定的,这是完全不同的。并完整记录。

以上是关于Windows和本机API中的系统调用?的主要内容,如果未能解决你的问题,请参考以下文章

Windows系统调用中API从3环到0环(下)

有没有办法通过本机 C# 调用检测系统上安装了哪些 Windows 应用商店应用程序?

小程序中调用api时显示网络超时怎么办

Windows有没有提供无视权限读取文件的api

在windows中如何将本地文件放入Linux系统指定目录

是否可以从在 Wine 中运行的 Windows 应用程序调用本机 Linux API?