Windows NtCreateFile 返回 STATUS_INVALID_PARAMETER使用未记录的 NT 系统调用接口

问题描述

我认为编写一个使用未公开的 Windows NT 系统调用接口的汇编程序(用于 64 位 x86,NASM)可能会很有趣/有趣。不幸的是,似乎我在某处错误地设置了结构或其他参数,导致 NtCreateFile 返回 STATUS_INVALID_ParaMETER (0xC000000D)。我怀疑这也可能是因为我可能将参数放在错误的顺序中。我关于如何使用系统调用接口的信息来自另一个 SO 问题,其中有人提到在 ntdll 中反编译该函数,这使我意识到我可以以类似于 Linux 的方式调用系统调用,即将幻数放入RAX 然后使用 syscall 指令(int 2e 也有效)。这会产生与直接调用 NtCreateFile 相同的结果。在阅读了 this Microsoft 文档后,我弄清楚了放入参数的顺序。NtCreateFile 的幻数来自 here。这是程序:

BITS 64

section .data
;; Declare a quadword (pointer size) to store the file handle in
file: resq 1

;; Declare the memory necessary for an OBJECT_ATTRIBUTES structure (five quadwords)
;; typedef struct _OBJECT_ATTRIBUTES {
;;   ULONG           Length;
;;   HANDLE          RootDirectory;
;;   PUNICODE_STRING ObjectName;
;;   ULONG           Attributes;
;;   PVOID           SecurityDescriptor;
;;   PVOID           SecurityQualityOfService;
;; } OBJECT_ATTRIBUTES;
atrs: resq 5

;; Declare a UNICODE_STRING for the file path,and the text to be written
fname:
    resq 2

fname_str: dw "\SystemRoot\test.txt",0

;; An IO_STATUS_BLOCK
iostat: resq 3

section .code
;; The signature of NtCreateFile:
;; __kernel_entry NTSTATUS NtCreateFile(
;;   PHANDLE            FileHandle,;;   ACCESS_MASK        DesiredAccess,;;   POBJECT_ATTRIBUTES ObjectAttributes,;;   PIO_STATUS_BLOCK   IoStatusBlock,;;   PLARGE_INTEGER     AllocationSize,;;   ULONG              FileAttributes,;;   ULONG              ShareAccess,;;   ULONG              Createdisposition,;;   ULONG              CreateOptions,;;   PVOID              EaBuffer,;;   ULONG              EaLength
;; );

;; Still counting this as a win since this function is just for a structure
extern RtlInitUnicodeString

global mainCRTstartup
mainCRTstartup:
    ;; Initialize a Unicode string
    lea rcx,[fname] ;; The memory for the structure
    lea rdx,[fname_str] ;; The string
    call RtlInitUnicodeString

    ;; NtCreateFile parameters
    mov r10,rcx ;; Save rcx (this is what happens in ntdll,not sure why yet)
    mov eax,55h ;; NtCreateFile's number is 0x55 in all versions thus far
    lea rcx,[file] ;; Put the file handle address into rcx
    mov rdx,40100000h ;; The desired file access,which is GENERIC_WRITE | SYNCHRONIZE

    ;; Set up attributes for the handle
    mov rbx,40 ;; Structure size,40 bytes/5 quadwords
    mov QWORD [atrs],rbx
    xor rbx,rbx ;; Set RootDirectory to NULL
    mov QWORD [atrs + 8],rbx
    mov rbx,QWORD [fname] ;; Move the start of the filename data to ObjectName
    mov QWORD [atrs + 16],40h
    mov QWORD [atrs + 12],rbx ;; OBJ_CASE_INSENSITIVE

    mov r8,atrs ;; Move the pointer to the OBJECT_ATTRIBUTES structure to r8
    xor r9,r9 ;; NULL

    ;; Now that we've reached 4 arguments,stuff goes on the stack in reverse
    xor rbx,rbx ;; Zero rbx for general use as zero
    push rbx ;; EaLength,not used
    push rbx ;; EaBuffer,NULL
    mov rsi,0x20 ;; CreateOptions is FILE_SYNCHRONOUS_IO_NONALERT
    push rsi
    xor rsi,rsi ;; FILE_OVERWRITE_IF (create/overwrite)
    mov rsi,5
    push rsi
    push rbx ;; We don't have other threads
    mov rsi,80h
    push rsi ;; FILE_ATTRIBUTE_norMAL
    push rbx ;; NULL
    syscall ;; Jump into kernel mode and call NtCreateFile

    ;; Exit
    mov eax,2ch ;; NtTerminateProcess
    xor ecx,ecx ;; We want to kill this process,not another one
    xor edx,edx ;; Exit with code 0
    syscall ;; Enter kernel mode again

我用这些命令组装和链接它:

nasm -o ntsyscall.obj -fwin64 ntsyscall.s
cl ntsyscall.obj -link -entry:mainCRTstartup -subsystem:console -largeaddressaware:no -debug ntdll.lib # ntdll's RtlInitUnicodeString is used

我还编写了这个 C 程序,它正在尝试同样的事情并按预期工作:

#define _AMD64_

#include <ntdef.h>

/* DeFinitions for symbols and structures needed */
#define GENERIC_WRITE 0x40000000L
#define SYNCHRONIZE 0x00100000L
#define FILE_SYNCHRONOUS_IO_NONALERT 0x20
#define FILE_OVERWRITE_IF 0x5
#define FILE_ATTRIBUTE_norMAL 0x80

typedef struct _IO_STATUS_BLOCK {
    union {
        long Status;
        long Pointer;
    } DUMMYUNIONNAME;
    unsigned long *information;
} IO_STATUS_BLOCK,*PIO_STATUS_BLOCK;

extern long NtCreateFile(HANDLE *file_ret,unsigned long desired_access,OBJECT_ATTRIBUTES *oattrs,IO_STATUS_BLOCK *iostat,LARGE_INTEGER *alloc_size,unsigned long fattrs,unsigned long share_access,unsigned long create_disp,unsigned long create_opts,void *ea_buf,unsigned long ea_len);
extern void RtlFillMemory(void *dst,unsigned long n,int c);
extern void RtlInitUnicodeString(UNICODE_STRING *dst,unsigned short *src);

int mainCRTstartup(void)
{
    HANDLE file;
    OBJECT_ATTRIBUTES atrs;
    UNICODE_STRING fname;
    //UNICODE_STRING ftext;
    IO_STATUS_BLOCK iostat;

    /* Initialize obsoletely designed Windows structures (not a fan of the Windows API) */
    RtlFillMemory(&atrs,sizeof(atrs),0);
    RtlInitUnicodeString(&fname,L"\\SystemRoot\\test.txt");
    atrs.Length = sizeof(atrs);
    atrs.RootDirectory = NULL;
    atrs.ObjectName = &fname;
    atrs.Attributes = OBJ_CASE_INSENSITIVE;

    /* Call the function */
    NtCreateFile(&file,GENERIC_WRITE | SYNCHRONIZE,&atrs,&iostat,NULL,FILE_ATTRIBUTE_norMAL,FILE_OVERWRITE_IF,FILE_SYNCHRONOUS_IO_NONALERT,0);

    /* Return to the program loader */
    return 0;
}

编译
cl ntsyscall_equiv.c -link -entry:mainCRTstartup -debug ntdll.lib

如果有人能帮忙解决这个问题,非常感谢。

解决方法

如果您实际上正在编写生产代码,则不应自己调用系统调用门;导入并调用 NTDLL 中的函数。系统调用门可以在补丁版本中更改并且之前已经更改。

NTDLL 中实际的系统调用门是一个只包含系统调用指令的函数;您的代码无法运行,因为您尝试自己完成,而您的堆栈与内核期望的不一致。

服务包中的系统调用号已更改;同样,理论上你可能会在那里遇到问题。