Ich war daran interessiert, einen Windows-API-Aufruf in Assembly ohne Importe durchzuführen (als pädagogische Übung), also habe ich die folgende FASM-Assembly geschrieben, um das zu tun, was NtDll!NtCreateFile tut. Es ist eine grobe Demonstration auf meiner 64-Bit-Version von Windows (Win10 1803 Version 10.0.17134) und stürzt nach dem Aufruf ab, aber der Rückgabewert des Systemaufrufs ist Null, sodass er erfolgreich ist. Alles wird gemäß der Windows x64-Aufrufkonvention eingerichtet, dann wird die Systemaufrufnummer in RAX geladen, und dann ist es die Syscall-Assembler-Anweisung, um den Aufruf auszuführen. Mein Beispiel erstellt die Datei c:\HelloWorldFile_FASM, also muss sie "als Administrator" ausgeführt werden.
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
Ich habe die Dokumentation für Ntdll!NtCreateFile verwendet, und ich habe auch den Kernel-Debugger verwendet, um viele Parameter anzusehen und zu kopieren.
__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
);
Wenn Sie unter Windows Assembler programmieren, führen Sie keine manuellen Systemaufrufe durch. Sie verwenden NTDLL und die native API, um das für Sie zu erledigen.
Die native API ist einfach ein Wrapper um die Kernelmode-Seite der Dinge. Es führt lediglich einen Systemaufruf für die richtige API durch.
Sie sollten NIEMALS einen manuellen Systemaufruf durchführen müssen, damit Ihre gesamte Frage überflüssig ist.
Syscall-Codes von Linux ändern sich nicht, die von Windows tun es, deshalb müssen Sie sich durch eine zusätzliche Abstraktionsschicht (auch bekannt als NTDLL) arbeiten.
BEARBEITEN:
Auch wenn Sie auf Assembly-Ebene arbeiten, haben Sie immer noch vollen Zugriff auf die Win32-API, es gibt keinen Grund, zunächst die NT-API zu verwenden! Importe, Exporte usw. funktionieren in Assemblerprogrammen einwandfrei.
BEARBEITEN2:
Wenn Sie WIRKLICH manuelle Syscalls durchführen wollen, müssen Sie NTDLL für jede relevante Windows-Version umkehren, eine Versionserkennung (über das PEB) hinzufügen und für jeden Aufruf eine Syscall-Suche durchführen.
Das wäre allerdings albern. NTDLL gibt es aus einem bestimmten Grund.
Leute haben den Reverse-Engineering-Teil bereits durchgeführt:siehe https://j00ru.vexillium.org/syscalls/nt/64/ für eine Tabelle mit Systemrufnummern für jeden Windows-Kernel. (Beachten Sie, dass sich die späteren Zeilen sogar zwischen den Versionen von Windows 10 ändern.) Auch dies ist eine schlechte Idee außerhalb von Experimenten nur für den persönlichen Gebrauch auf Ihrem eigenen Computer, um mehr über asm und/oder Windows-Interna zu erfahren. Fügen Sie keine Systemaufrufe in Code ein, den Sie an andere weitergeben.
Die andere Sache, die Sie über die Windows-Syscall-Konvention wissen müssen, ist, dass die Syscall-Tabellen meines Wissens als Teil des Build-Prozesses generiert werden. Das bedeutet, dass sie sich einfach ändern können – niemand verfolgt sie. Wenn jemand oben in der Liste ein neues hinzufügt, spielt es keine Rolle. NTDLL funktioniert immer noch, also funktioniert jeder andere, der NTDLL aufruft, immer noch.
Sogar der Mechanismus, der zum Ausführen von Systemaufrufen verwendet wird (also int oder sysenter), ist nicht in Stein gemeißelt und hat sich in der Vergangenheit geändert, und ich denke, dass einst dieselbe Windows-Version verschiedene DLLs verwendete, die je nach unterschiedliche Eingabemechanismen verwendeten CPU in der Maschine.
Windows-Systemaufrufe werden durch Aufrufen von System-DLLs wie kernel32.dll
ausgeführt oder gdi32.dll
, was mit gewöhnlichen Unterprogrammaufrufen erfolgt. Die Mechanismen zum Einfangen in die privilegierte Ebene des Betriebssystems sind nicht dokumentiert, aber das ist in Ordnung, da DLLs wie kernel32.dll
mach das für dich.
Und mit Systemaufrufen beziehe ich mich auf dokumentierte Windows-API-Einstiegspunkte wie CreateProcess()
oder GetWindowText()
. Gerätetreiber verwenden im Allgemeinen eine andere API als das Windows DDK.