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 API
和 System 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 windows
是 Native 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.dll
或gdi32.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中的系统调用?的主要内容,如果未能解决你的问题,请参考以下文章