Masm 64 call native API (NtOpenFile/NtWrteFile)

Hello everybody,

For several weeks I have been trying to interact directly with the Windows native API. Indeed I have code C which runs without problem but impossible to do the same operations in asambly (MASM64).

NtWriteFile always returns “0xC000000D STATUS_INVALID_PARAMETER” while in C everything runs without worries

I am new to the forum, I registered because I found a lot of information on osronline, thank you for sharing

Sorry for my English. From Paris with love

ASM code:

OPTION CASEMAP:none

EXTERN ApiLookup: PROC

PUBLIC ntDllTest

OBJECT_ATTRIBUTES STRUC QWORD
oLength DWORD ? ; 4
DWORD ? ; 4
RootDirectory QWORD ? ; 8
ObjectName QWORD ? ; 8
Attributes DWORD ? ; 4
DWORD ? ; 4
SecurityDescriptor QWORD ? ; 8
SecurityQualityOfService QWORD ? ; 8 => Intra-structure alignment OK
OBJECT_ATTRIBUTES ENDS ; => Inter-structure alignment, total 48 and largest memeber size 8. 48 mod 8 = 0 OK

IO_STATUS_BLOCK struct QWORD
UNION u
Status DWORD ?
Pointer QWORD ?
ENDS
Information QWORD ?
IO_STATUS_BLOCK ends

UNICODE_STRING STRUC QWORD
woLength WORD ?
MaxLength WORD ?
DWORD ?
Buffer QWORD ?
UNICODE_STRING ENDS

.data
ntdll db “NTDLL.DLL”,0
NtCreateFile db “NtCreateFile”,0
NtWriteFile db “NtWriteFile”,0
NtClose db “NtClose”,0
NtOpenFile db “NtOpenFile”,0
NtReadFile db “NtReadFile”,0

align 16 ; There seems to be a problem in Win2k (at least) when api function is called to use unicode.
; Under certain conditions, if the unicode line isn’t word-aligned then function fails.
; Reading loaded file data posed no problem because the system will always load it onto the
; correct boundary. But the alignment of any given .DATA entry is based on what’s ahead of it.

file dw “",“D”,“o”,“s”,“D”,“e”,“v”,“i”,“c”,“e”,“s”,”",“D”,“:”,“",“r”,“o”,“o”,“t”,”_“,“t”,“m”,“p”,”",“y”,“o”,“l”,“o”,“.”,“t”,“x”,“t”,0
align 8
Message db “Hello world! I was written using dos syscall mouhahahahahaha…”;,0;, 0,0,0,0,0;,0,0,0
align 16
UnicodeFile UNICODE_STRING <>
align 16
ObjA OBJECT_ATTRIBUTES <>
align 16
Iosb IO_STATUS_BLOCK <>

.data?
buff db 128 dup(?) ; read if write works …
hFile dq ?

.code
ntDllTest PROC
;=== start prolog
;mov [rsp + 20h], r9 ; save the first four parameters on the stack in the shadow space allocated by caller
;mov [rsp + 18h], r8
;mov [rsp + 10h], rdx
;mov [rsp + 8h], rcx
;push r15 ; saves any non-volatile registers that plans to use during this function execution
;push r14
;push r13
sub rsp, 0Bh * 8h; + 8h ; called function with the maximum number of parameters * 8 => 11 * 8 + 8 to aligne stack on 16 bit (api call need it)
lea rbp, [rsp + 60h] ; establishes a frame pointer that points 128 bytes into the fixed allocation area

; init structs once
mov UnicodeFile.woLength, 64d ; 24 * 2 (24 caracter of 2 bytes (WCHAR))
mov UnicodeFile.MaxLength, 66h
lea rax, file
mov UnicodeFile.Buffer, rax

mov ObjA.oLength, 30h ; sizeof(OBJECT_ATTRIBUTES)
mov ObjA.RootDirectory, 0h ; NULL
mov ObjA.Attributes, 40h ; OBJ_CASE_INSENSITIVE
lea rax, UnicodeFile
mov ObjA.ObjectName, rax ; &UnicodeFilespec;
mov ObjA.SecurityDescriptor, 0h ; NULL;
mov ObjA.SecurityQualityOfService, 0h ; NULL;

; get address of NtCreateFile
lea rdx, NtCreateFile
lea rcx, ntdll
call ApiLookup
cmp rax, 0
jz fatal_error_exit

; create file
mov dword ptr [rsp + 50h], 0h ; Length of the EA buffer.
mov qword ptr [rsp + 48h], 0h ; Pointer to an EA buffer used to pass extended attributes.
mov dword ptr [rsp + 40h], 20h ; FILE_SYNCHRONOUS_IO_NONALERT
mov dword ptr [rsp + 38h], 5h ; FILE_OVERWRITE_IF
mov dword ptr [rsp + 30h], 0h ; The type of share access that the caller would like to use in the file
mov dword ptr [rsp + 28h], 80h ; FILE_ATTRIBUTE_NORMAL
mov qword ptr [rsp + 20h], 0h ; The initial allocation size in bytes for the file
lea r9, Iosb ; A pointer to a variable that receives the final completion status and information about the requested operation.
lea r8, ObjA ; A pointer to a structure already initialized with InitializeObjectAttributes.
mov edx, 2100000h ; MAXIMUM_ALLOWED | SYNCHRONIZE
lea rcx, hFile ; A pointer to a variable that receives the file handle if the call is successful.
call rax
;mov r10, rcx
;mov eax, 55h
;push rbp ; dummy … (this => ntdll => syscall, 16 byte aligne stack + 8 ret ntdll)
;syscall
;pop rbp
shr eax, 1Eh ; #define NT_ERROR(Status) ((((ULONG)(Status)) >> 30) == 3)
cmp eax, 3
jz fatal_error_exit
cmp hFile, 0
jz fatal_error_exit

; get address of NtWriteFile
lea rdx, NtWriteFile
lea rcx, ntdll
call ApiLookup
cmp rax, 0
jz fatal_error_exit

; write into
mov qword ptr [rsp + 40h], 0h ; key
mov qword ptr [rsp + 38h], 10h ; byte offset
mov dword ptr [rsp + 30h], 64d ; length
mov rcx, offset Message
mov qword ptr [rsp + 28h], rcx ; ptr to data buffer
lea rcx, Iosb
mov qword ptr [rsp + 20h], rcx ; IOSB address
xor r9, r9 ; APC context
xor r8, r8 ; APC entry point
xor rdx, rdx ; event Handle
mov rcx, hFile ; file Handle
call rax
mov qword ptr rcx, 0h
cmp rcx, rax ; #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
jl fatal_error_exit ; jl ? jump if less than, if second parameter is less than the first

; get address of NtClose
lea rdx, NtClose
lea rcx, ntdll
call ApiLookup
cmp rax, 0
jz fatal_error_exit

; close file
mov rcx, hFile ; file Handle
call rax

fatal_error_exit:
mov rax, 0DEADBEEFh

;=== start epilogue
lea rsp, [rbp - 80h] ; trim the stack to its fixed allocation
add rsp, 8h * 0Bh ;+ 8h ; deallocate the fixed part of the stack
;pop R13 ; restore any non-volatile registers that used during this function execution
;pop R14
;pop R15
ret ; return control to the caller
ntDllTest ENDP

end

================================================================================

I can also share my C code who works perfect (Create file/write into/close/reopen/read/close).

thank you in advance for your help

Is there any good reason you need to write that code in MASM?

xxxxx@gmail.com wrote:

For several weeks I have been trying to interact directly with the Windows native API. Indeed I have code C which runs without problem but impossible to do the same operations in asambly (MASM64).

NtWriteFile always returns “0xC000000D STATUS_INVALID_PARAMETER” while in C everything runs without worries

; write into
mov qword ptr [rsp + 40h], 0h ; key
mov qword ptr [rsp + 38h], 10h ; byte offset
mov dword ptr [rsp + 30h], 64d ; length

The ByteOffset parameter to NtWriteFile is a pointer to a LARGE_INTEGER
structure, not a number passed by value.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Building your code with /FA is your friend in this case…

Peter
OSR
@OSRDrivers

xxxxx@gmail.com wrote:

; init structs once
mov UnicodeFile.woLength, 64d ; 24 * 2 (24 caracter of 2 bytes (WCHAR))
mov UnicodeFile.MaxLength, 66h
lea rax, file
mov UnicodeFile.Buffer, rax

As a side note, your MaxLength should be 66d, not 66h. That won’t
affect anything here.

It is quite error-prone to compute these things by hand and embed a
hardcoded number. Better is something like:

file dw “",“D”,“o”,“s”,“D”,“e”,“v”,“i”,“c”,“e”,“s”,”",“D”,“:”,“",“r”,“o”,“o”,“t”,”_“,“t”,“m”,“p”,”",“y”,“o”,“l”,“o”,“.”,“t”,“x”,“t”,0
file_length equ $-file

However, I can’t get this to assemble, apparently because of an ml64
bug. It complains about operand sizes, even though the type and opattr
codes for those two symbols are both identical.

Better yet is to define a macro to create this from the string. You
could create something to let you do:

file UnicodeLiteral <\DosDevices\D:\root_tmp\yolo.txt>

that could iterate the characters, create the string, and define the length.

Ah, assembler. The good old days. Or were they?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Alex Grig
xxxxx@broadcom.com
Join Date: 14 Apr 2008
Posts To This List: 3085
Masm 64 call native API (NtOpenFile/NtWrteFile)
Is there any good reason you need to write that code in MASM?

Just to learn asambly and malware obfuscation technics. i want to makee syscalls. I can lookup on run time the number of syscall. I want to build a complet standalone shellcode without any lib for windows

Tim Roberts
xxxxx@probo.com
Join Date: 28 Jan 2005
Posts To This List: 11203
Masm 64 call native API (NtOpenFile/NtWrteFile)
xxxxx@gmail.com wrote:

For several weeks I have been trying to interact directly with the Windows
native API. Indeed I have code C which runs without problem but impossible to do
the same operations in asambly (MASM64).

NtWriteFile always returns “0xC000000D STATUS_INVALID_PARAMETER” while in C
everything runs without worries

; write into
mov qword ptr [rsp + 40h], 0h ; key
mov qword ptr [rsp + 38h], 10h ; byte offset
mov dword ptr [rsp + 30h], 64d ; length

The ByteOffset parameter to NtWriteFile is a pointer to a LARGE_INTEGER
structure, not a number passed by value.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

The byte offset origialy is set to 0, i just test with 10h, but your right!
With 0h byte offset (like in C) is didnt work is asm

Peter Viscarola (OSR)
xxxxx@osr.com
Join Date:
Posts To This List: 5718
List Moderator
Masm 64 call native API (NtOpenFile/NtWrteFile)

Building your code with /FA is your friend in this case…

Peter
OSR
@OSRDrivers

Thank you for /FA, i have the disassably window in VS2015 but with /FA i have this code for NtWriteFile:

mov QWORD PTR [rsp+64], 0
mov QWORD PTR [rsp+56], 0
mov eax, DWORD PTR MessageLength$[rbp]
mov DWORD PTR [rsp+48], eax
mov rax, QWORD PTR Message
mov QWORD PTR [rsp+40], rax
lea rax, QWORD PTR Iosb$[rbp]
mov QWORD PTR [rsp+32], rax
xor r9d, r9d
xor r8d, r8d
xor edx, edx
mov rcx, QWORD PTR FileHandle$[rbp]
call QWORD PTR NtWriteFile$[rbp]

64-32 rsp ? Weird? not? and for NtCreateFile :
mov DWORD PTR [rsp+80], 0
mov QWORD PTR [rsp+72], 0
mov DWORD PTR [rsp+64], 32 ; 00000020H
mov DWORD PTR [rsp+56], 0
mov DWORD PTR [rsp+48], 0
mov DWORD PTR [rsp+40], 128 ; 00000080H
mov QWORD PTR [rsp+32], 0
lea r9, QWORD PTR Iosb$[rbp]
lea r8, QWORD PTR ObjectAttributes$[rbp]
mov edx, 34603008 ; 02100000H
lea rcx, QWORD PTR FileHandle$[rbp]
call QWORD PTR NtCreateFile$[rbp]

i am gone explore this piste…

xxxxx@gmail.com wrote:

The byte offset origialy is set to 0, i just test with 10h, but your right!
With 0h byte offset (like in C) is didnt work is asm

0 in that case does not mean the integer 0. It means you are passing a
null pointer.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Tim Roberts
xxxxx@probo.com
Join Date: 28 Jan 2005
Posts To This List: 11204
Masm 64 call native API (NtOpenFile/NtWrteFile)
xxxxx@gmail.com wrote:

The byte offset origialy is set to 0, i just test with 10h, but your right!
With 0h byte offset (like in C) is didnt work is asm

0 in that case does not mean the integer 0. It means you are passing a
null pointer.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Totally agree with you

I always have the same error. With the option / FA the code is exactly the same as my code in MASM…

I do not know what to do. Do you have any leads please?

xxxxx@gmail.com wrote:

I always have the same error. With the option / FA the code is exactly the same as my code in MASM…

I do not know what to do. Do you have any leads please?

Have you changed the ByteOffset parameter to be a pointer to a 64-bit
integer, instead of passing an integer directly?


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

>Have you changed the ByteOffset parameter to be a pointer to a 64-bit

integer, instead of passing an integer directly?

Yes, i pass a 0h, like no ByteOffset need. Its an optional parameter, no change.
I try also passe valid ptr to QWORD, same error …

Why don’t you want simply to disassemble C code and see how it translates to assembly???

I did not look through your code thoroughly, but you seem to be passing function parameters on the stack. Although it is OK to do so under 32-bit Windows where API is based, IIRC, upon _stdcall convention (i.e the one where you pass all parameters on the stack), doing it under 64-bit OS is not going to work, because a callee expects to find first few parameters in registers ( which is true for both Windows and UNIX-like systems, although the actual ways of how the parameters are passed are different for MSFT and UNIX calling conventions)…

Anton Bassov

This code may crash:

call rax ; rax is NtCreateFile
mov qword ptr rcx, 0h ; BOOOOOOOMMMMM!!!
cmp rcx, rax ; #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
jl fatal_error_exit

RCX is volatile. When NtCreateFile has returned, the content of RCX is undefined, so accessing the address contained in RCX could cause an access violation. And as the comment suggests, an error is a negative NTSTATUS, so you should have:

call rax ; rax is NtCreateFile
test eax,eax ; NTSTATUS is a ULONG, DWORD or 32 bits long type.
js fatal_error_exit // if signed, the call has failed.

As Anton suggested, use the assembly generated by CL.EXE. DUMPBIN.EXE can disas the whole exec. And if the PDB file is present alongside the exec (in the same dir), you will get a disassembly with symbols, just like the ‘uf’ command in WinDbg, great.

Would this just compile ?

mov qword ptr rcx, 0h

This is probably better:

mov qword ptr [rcx], 0h

> Why don’t you want simply to disassemble C code and see how it translates to

assembly???

I did not look through your code thoroughly, but you seem to be passing function
parameters on the stack. Although it is OK to do so under 32-bit Windows where
API is based, IIRC, upon _stdcall convention (i.e the one where you pass all
parameters on the stack), doing it under 64-bit OS is not going to work, because
a callee expects to find first few parameters in registers ( which is true for
both Windows and UNIX-like systems, although the actual ways of how the
parameters are passed are different for MSFT and UNIX calling conventions)…

I know the calling conventions for 64 bit masm. There is only one fastcall

-> https://msdn.microsoft.com/en-us/library/ms235286.aspx

  1. Allocate stack space for the parameters that won’t be passed in registers.
  2. Align the stack pointer RSP on 16 byte boundary.
  3. Copy the 5th, 6th, ? arguments to the stack.
  4. Copy the first four arguments to the registers RAX, RDX, R8 and R9.
  5. Call the execute handler.

I have disassemble my C code, its the same as my MASM code…

mov DWORD PTR [rsp+80], 0
mov QWORD PTR [rsp+72], 0
mov DWORD PTR [rsp+64], 32 ; 00000020H
mov DWORD PTR [rsp+56], 0
mov DWORD PTR [rsp+48], 0
mov DWORD PTR [rsp+40], 128 ; 00000080H
mov QWORD PTR [rsp+32], 0
lea r9, QWORD PTR Iosb$[rbp]
lea r8, QWORD PTR ObjectAttributes$[rbp]
mov edx, 34603008 ; 02100000H
lea rcx, QWORD PTR FileHandle$[rbp]
call QWORD PTR NtCreateFile$[rbp]
mov DWORD PTR Status$[rbp], eax
; Line 125
mov eax, DWORD PTR Status$[rbp]
shr eax, 30
cmp eax, 3
jne SHORT $xxxxx@run_nt_nat
mov edx, DWORD PTR Status$[rbp]
lea rcx, OFFSET FLAT:??xxxxx@xxxxx@xxxxx@NtCreateFile?5FAILED?5?3?5status?5?$DN?50@
call printf
mov ecx, 3
call my_exit
$xxxxx@run_nt_nat:
; Line 126
mov eax, DWORD PTR Iosb$[rbp]
shr eax, 30
cmp eax, 3
jne SHORT $xxxxx@run_nt_nat
mov edx, DWORD PTR Iosb$[rbp]
lea rcx, OFFSET FLAT:??xxxxx@xxxxx@xxxxx@NtCreateFile?5FAILED?5?3?5Iosb?4Statu@
call printf
mov ecx, 3
call my_exit
$xxxxx@run_nt_nat:
; Line 148
lea rcx, OFFSET FLAT:??xxxxx@xxxxx@xxxxx@NtWriteFile?4?4?4?6?$AA@
call printf
; Line 149
mov QWORD PTR [rsp+64], 0
mov QWORD PTR [rsp+56], 0
mov eax, DWORD PTR MessageLength$[rbp]
mov DWORD PTR [rsp+48], eax
mov rax, QWORD PTR Message
mov QWORD PTR [rsp+40], rax
lea rax, QWORD PTR Iosb$[rbp]
mov QWORD PTR [rsp+32], rax
xor r9d, r9d
xor r8d, r8d
xor edx, edx
mov rcx, QWORD PTR FileHandle$[rbp]
call QWORD PTR NtWriteFile$[rbp]
mov DWORD PTR Status$[rbp], eax

> D. T.

xxxxx@gmail.com
Join Date: 16 Aug 2016
Posts To This List: 62
Masm 64 call native API (NtOpenFile/NtWrteFile)
This code may crash:

call rax ; rax is NtCreateFile
mov qword ptr rcx, 0h ; BOOOOOOOMMMMM!!!
cmp rcx, rax ; #define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
jl fatal_error_exit

RCX is volatile. When NtCreateFile has returned, the content of RCX is
undefined, so accessing the address contained in RCX could cause an access
violation. And as the comment suggests, an error is a negative NTSTATUS, so you
should have:

call rax ; rax is NtCreateFile
test eax,eax ; NTSTATUS is a ULONG, DWORD or 32 bits long type.
js fatal_error_exit // if signed, the call has failed.

As Anton suggested, use the assembly generated by CL.EXE. DUMPBIN.EXE can disas
the whole exec. And if the PDB file is present alongside the exec (in the same
dir), you will get a disassembly with symbols, just like the ‘uf’ command in
WinDbg, great.

I dont need the content of RCX after NtCreateFile. I just put 0 in RCX to compare
the return of NtCreateFile (RAX) with 0 (cmp rax, 0 ; MASM dont like it).
See my disassambled C code. its the same :confused:

> Yesterday 19:15

D. T.
xxxxx@gmail.com
Join Date: 16 Aug 2016
Posts To This List: 62
Masm 64 call native API (NtOpenFile/NtWrteFile)
Would this just compile ?

mov qword ptr rcx, 0h

This is probably better:

Not realy ^

mov qword ptr [rcx], 0h ;

–> Exception thrown at 0x00007FF7D7A212E9 in masm64_ntdll.exe: 0xC0000005:
Access violation writing location 0x00007FF9F8A951C4.

mov qword ptr rcx, 0h

RAX?=?00000000C000000D RBX?=?00007FF9F89F0000 RCX?=?0000000000000000 RDX?=?0000000000000000 RSI?=?00007FF7A3594022
RDI?=?00007FF9F8B327DF R8 ?=?000000733755FB98 R9 ?=?000000733755FC00 R10?=?0000000000000000 R11?=?0000000000000246
R12?=?00007FF9F8B423B0 R13?=?00000000001399D0 R14?=?000000000014B295 R15?=?0000000000000000 RIP?=?00007FF7A35912F0
RSP?=?000000733755FBA0 RBP?=?000000733755FC00 EFL?=?00000246

>I have disassemble my C code, its the same as my MASM code…

You do realize what you are actually saying, right - it is,basically, “I’ve got two identical pieces of ASM code; however, one of them works and another does not; the whole thing is 100% reproducible and happens to be as predictable as a clock”.

Do you really think something like that may be feasible???

Anton Bassov