Estaba interesado en hacer una llamada a la API de Windows en ensamblado sin importaciones (como un ejercicio educativo), así que escribí el siguiente ensamblado FASM para hacer lo que hace NtDll!NtCreateFile. Es una demostración aproximada en mi versión de Windows de 64 bits (Win10 1803, versión 10.0.17134), y se bloquea después de la llamada, pero el valor de retorno de la llamada al sistema es cero, por lo que tiene éxito. Todo está configurado según la convención de llamadas de Windows x64, luego el número de llamada del sistema se carga en RAX y luego es la instrucción de ensamblaje syscall para ejecutar la llamada. Mi ejemplo crea el archivo c:\HelloWorldFile_FASM, por lo que debe ejecutarse "como administrador".
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
Usé la documentación para Ntdll!NtCreateFile, y también usé el depurador del kernel para ver y copiar muchos de los parámetros.
__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
);
Si está haciendo programación de ensamblaje en Windows, no realiza llamadas al sistema manuales. Utiliza NTDLL y la API nativa para hacerlo por usted.
La API nativa es simplemente un envoltorio alrededor del lado del modo kernel de las cosas. Todo lo que hace es realizar una llamada al sistema para la API correcta.
NUNCA debería necesitar llamar al sistema manualmente para que toda su pregunta sea redundante.
Los códigos de llamada al sistema de Linux no cambian, los de Windows sí, es por eso que necesita trabajar a través de una capa de abstracción adicional (también conocida como NTDLL).
EDITAR:
Además, incluso si está trabajando en el nivel de ensamblaje, todavía tiene acceso completo a la API de Win32, ¡no hay razón para usar la API de NT para empezar! Las importaciones, exportaciones, etc. funcionan muy bien en los programas de ensamblaje.
EDIT2:
Si REALMENTE desea realizar llamadas de sistema manuales, necesitará invertir NTDLL para cada versión relevante de Windows, agregar detección de versión (a través de PEB) y realizar una búsqueda de llamada de sistema para cada llamada.
Sin embargo, eso sería una tontería. NTDLL está ahí por una razón.
La gente ya ha hecho la parte de ingeniería inversa:consulte https://j00ru.vexillium.org/syscalls/nt/64/ para obtener una tabla de números de llamadas al sistema para cada kernel de Windows. (Tenga en cuenta que las últimas filas cambian incluso entre versiones de Windows 10). Nuevamente, esta es una mala idea fuera de los experimentos de uso personal en su propia máquina para aprender más sobre asm y/o las partes internas de Windows. No incorpores llamadas al sistema en código que distribuyas a nadie más.
La otra cosa que necesita saber sobre la convención de llamada al sistema de Windows es que, según tengo entendido, las tablas de llamada al sistema se generan como parte del proceso de compilación. Esto significa que simplemente pueden cambiar, nadie los rastrea. Si alguien agrega uno nuevo al principio de la lista, no importa. NTDLL todavía funciona, por lo que todos los demás que llamen a NTDLL seguirán funcionando.
Incluso el mecanismo utilizado para realizar syscalls (que int, o sysenter) no está fijado en piedra y ha cambiado en el pasado, y creo que alguna vez la misma versión de Windows usaba diferentes DLL que usaban diferentes mecanismos de entrada según el CPU en la máquina.
Las llamadas al sistema de Windows se realizan llamando a archivos DLL del sistema como kernel32.dll
o gdi32.dll
, que se realiza con llamadas a subrutinas ordinarias. Los mecanismos para atrapar en la capa privilegiada del sistema operativo no están documentados, pero está bien porque las DLL como kernel32.dll
hacer esto por ti.
Y por llamadas al sistema, me refiero a puntos de entrada documentados de la API de Windows como CreateProcess()
o GetWindowText()
. Los controladores de dispositivos generalmente usarán una API diferente del DDK de Windows.