8

I am on windows 10, with Cygwin installed. I have been using Cygwin for the compilation/assembly of c and assembly programs using Cygwin-installed "gcc" and "nasm". From what I know, nasm has a -f win64 mode, so it can assemble 64-bit programs. Now, for x64 assembly programming on windows, it seems youtube has a lack of tutorials. Most assembly programming tutorials on youtube are either for x64 linux or x32 windows, and I need to be able to print a string to the console on x64 windows, without the use of any external functions such as C's "printf".

StackOverflow links that didn't work for me:

64-bit windows assembler

As far as I know, nasm does support 64-bit windows using the -f win64 extension. Also, the answers have nothing to do with how to write an actual program in assembly on x64 bit windows

How to write hello world in assembler under Windows?

All answers that give code give only code for outdated windows versions (32-bit), except one. The one answer that works for 64 bit I tried, but the command to link the object file gave an error for me, that the system could not find the path specified.

windows x64 assembler?

This site does not contain any code, which is what I need. Also, I am trying to write the Hello World program in nasm.

64 bit assembly, when to use smaller size registers

The question actually contains code for a hello world program, but when executed under cygwin on windows 10 (my device), I get a segmentation fault.

Why does Windows64 use a different calling convention from all other OSes on x86-64?

I have tried disassembling a C Hello World Program with objdump -d, but this calls printf, a prebuilt C function, and I am trying to avoid using external functions.

I have tried other external functions (such as Messagebox), but they return errors because something does not recognize the functions. Also, I am trying not to use any extern functions if possible, only syscall, sysenter, or interrupts.

CODE:

Attempt #1:

    section .data
    msg db "Hello, World!", 10, 0
    section .text
    mov rax, 1
    mov rdi, 1
    mov rsi, msg
    mov rdx, 14
    syscall
    mov rax, 60
    mov rdi, 0
    syscall

Problem: Assembles and links properly, but throws a segmentation fault when run.

Attempt #2:

    section .text
    mov ah, 0eh
    mov al, '!', 0
    mov bh, 0
    mov bl, 0
    int 10h

Problem: Does not assemble properly; says "test.asm:3: error: invalid combination of opcode and operands"

Attempt #3:

    section .text
    msg db 'test', 10, 0
    mov rdi, 1
    mov rsi, 1
    mov rdx, msg
    mov rcx, 5
    syscall
    mov rdi, 60
    mov rsi, 0
    syscall

Problem: Assembles and links properly, but throws a segmentation fault when run.

Attempt #4:

    section .text
    mov ah, 0eh
    mov al, '!'
    mov bh, 0
    mov bl, 0
    int 10h

Problem: Assembles and links properly, but throws a segmentation fault when run.

I just need an example of a hello world assembly program that works on 64-bit Windows 10. 32-bit programs seem to return errors when I run them. If it is not possible to print a string to the console without using external functions, an example of a program that uses external functions and works on 64-bit Windows 10 would be nice.

I heard that syscalls in windows have far different addresses than those in linux.

Eagterrian Knight
  • 316
  • 1
  • 4
  • 12
  • 1
    [This MASMForum link](http://masm32.com/board/index.php?topic=6486.0) will help you. It is the key to the best MASM64 Windows source available. – zx485 Apr 20 '18 at 18:55
  • Attempt 2 is 16bit DOS code, will not work as windows executable, and `mov al,imm8` instruction can contain only single 8 bit immediate value, so you have to pick if you want number `'!'` (`33` IIRC ASCII table) or number `0`, you can't assign to register `al` both at the same time. Attemp 3: unless you specify with some label where the code starts, it may start at the beginning of `.text` section, executing string data "test" as instructions (too lazy to check what kind of instructions they form). Labels like `_start:` are often used in cooperation with linker to specify entry point into code – Ped7g Apr 20 '18 at 19:14
  • Attemp 4 is again 16 bit DOS code (using BIOS interrupt `int 10h`), so you can use this one under some DOS emulator/VM, like dosbox. .. (and I guess attempt 1 and 3 are using linux or OSX syscalls, but I don't know how windows 64b system calls work, so I wouldn't recognize even correct way, I use only linux for last decade and sometimes DOS in dosbox) – Ped7g Apr 20 '18 at 19:16
  • Is there a special flag to set with ld to specify where the start of the program is? I tried putting _start in there and putting the message within section .data and it still says "Illegal instruction", so I am assuming that ld still doesn't know where to start. – Eagterrian Knight Apr 20 '18 at 19:43
  • This question looks a little like [this](https://stackoverflow.com/a/41622071/2189500). – David Wohlferd Apr 21 '18 at 03:31
  • Oh, can someone give an example of using external functions then or a system dll to perform the operation of a simple syscall like write? Interesting that microsoft doesn't publish its own syscalls. – Eagterrian Knight Apr 21 '18 at 08:11

2 Answers2

6

Attempt #1:

Attempt #2:

Internally Windows uses either interrupts or system calls. However they are not officially documented and they may change anytime Microsoft wants to change them:

Only a few .dll files directly use the system call instructions; all programs are directly or indirectly accessing these .dll files.

A certain sequence (for example mov rax, 5, syscall) might have the meaning "print out a text" before a Windows update and Microsoft might change the meaning of this sequence to "delete a file" after a Windows update.

To do this Microsoft simply has to replace the kernel and the .dll files which are officially "allowed" to use the syscall instruction.

The only way to call Windows operating system functions in a way that it is guaranteed that your program is still working after the next Windows update is to call the functions in the .dll files - just the same way you would do this in a C program.

Example (32-bit code):

push 1
push msg
push 14
call _write
mov dword [esp], 0
call _exit
Community
  • 1
  • 1
Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
  • 5
    The author requested 64 bit code. Is the C-level Windows API in x86_64 still a 32 bit interface? Which DLL's provide `_write` and `_exit`? – mcandre Jun 18 '18 at 17:01
  • @mcandre The main aspect of my answer was the "platform independent" part: Directly using system calls will not work. The x86_32 example code is only there to make the last part of the answer clearer and not to give a "valid" example of code. – Martin Rosenau Jun 18 '18 at 18:50
  • 1
    Been a while since I posted this, but I figured out which dll files to use and how to include them. C:\\Windows\\System32\\msvcrt.dll seems to provide the entire C standard library, and all you have to do is say "extern [whatever C function]" and link msvcrt.dll with your object file. It doesn't use syscalls, but considering what you said about syscalls in windows being unstable, I'm okay with using C's standard library in assembly. That's likely the only way to make it platform independent anyhow. – Eagterrian Knight Sep 27 '18 at 10:14
  • 1
    Just 1 question: do you know if there are in fact lower level dll files I could be using in place of msvcrt.dll? The fact that sizeof in C varies from implementation to implementation somewhat concerns me. I found user32.dll and kernel32.dll in System32, do these provide the windows library? – Eagterrian Knight Sep 27 '18 at 10:35
  • 1
    solved my own problem. there's a thing that comes with Visual Studio called dumpbin that, with /exports, allows you to see all functions that come with a .dll file. And it turns out that kernel32.dll does indeed provide the windows api. – Eagterrian Knight Sep 27 '18 at 11:19
  • Syscalls numbers indeed change: https://hfiref0x.github.io/syscalls.html but the question is an excersise in reverse engineering and not normal code. – nponeccop Nov 20 '19 at 13:26
2

Take a look at ReactOS source code. This Program is using NT API to print Hello, world!\n to console.

const char *msg = "Hello, world!\n";
HANDLE hStdout = NtCurrentPeb()->ProcessParameter->StandardOutput;
static IO_STATUS_BLOCK IoStatusBlock;
NtWriteFile(hStdout,
            NULL, 
            NULL, 
            NULL, 
            &IoStatusBlock, 
            (PVOID)msg,
            14,
            NULL,
            NULL);
// NtTerminateProcess(NtCurrentProcess(), 0);
RtlExitUserProcess(0);
extern NtWriteFile
; extern NtTerminateProcess
extern RtlExitUserProcess

%define NtCurrentPeb() gs:[0x60]
%define ProcessParameter 32
%define StandardOutput 40
%define NtCurrentProcess() -1

section .rdata
        msg db `Hello, world!\n\0`
section .bss
        IoStatusBlock resb 16
section .text
        global main
main:
        sub rsp, 72
        mov rcx, NtCurrentPeb()
        mov rcx, ProcessParameter[rcx]
        mov rcx, StandardOutput[rcx]
        xor edx, edx
        xor r8d, r8d
        xor r9d, r9d
        mov qword 32[rsp], IoStatusBlock
        mov qword 40[rsp], msg
        mov qword 48[rsp], 14
        mov qword 56[rsp], 0
        mov qword 64[rsp], 0
        call NtWriteFile

        // mov rcx, NtCurrentProcess()
        // xor edx, edx
        // call NtTerminateProcess
        xor ecx, ecx
        call RtlExitUserProcess
        int3

Explanation

First we extract StandardOutput handle by accessing _RTL_USER_PARAMETERS from PEB.
This is hStdout returned from GetStdHandle(STD_OUTPUT_HANDLE);

call NtWriteFile to write into StandardOutput handle.
WriteFile call NtWriteFile.
WriteConsole* call NtDeviceIoControlFile.

the NtDeviceIoControlFile is more complex than NtWriteFile calls.

call RtlExitUserProcess to exit.
Actually we can just do TerminateProcess(GetCurrentProcess(), 0); rather than ExitProcess(0);.
The second one is much safer, it does cleanup before Terminate self process.
Rtl is not system call, it stands for RunTimeLibrary. It's extended C lib.
It calls NtTerminateProcess inside that similar to kill(), Nt never had _exit() similar system call.

Calling Convention

syscall instruction clobber R11for rflags and RCX for RIP so the first argument is on R10.

System Call Number Arg0 Arg1 Arg2 Arg3
RAX R10 RDX R8 R9

About the stack argument, it's started from 40[rsp] due to shadow store (32 bytes), and 8 bytes from call instruction.

So if you want libc-free program. You can change from RCX to R10, pass the stack argument from 40[rsp], and call NtXxx to mov eax, SYSCALL_NR.

This program is stil depends on ntdll.dll because NT never had a stable system call number.

Compability

May work from Windows 8 and above. See https://www.mandiant.com/resources/blog/monitoring-windows-console-activity-part-one

Ex-Kyuto
  • 29
  • 5
  • The `syscall` instruction itself clobbers RCX, but the DLL interface like `call NtWriteFile` still follows the function-calling convention, with the first arg in RCX. That's what your example shows. (The DLL function probably does `mov eax, call_number` / `mov r10, rcx` / `syscall` / `ret` and maybe some other library shenanigans, same as on Linux with libc wrapper functions for system calls.) So you'd only need to care about the special calling convention if you were inlining `syscall` manually, with the unstable call number from https://j00ru.vexillium.org/syscalls/nt/64/ – Peter Cordes Jan 09 '23 at 04:30
  • Ah thanks, edited. Also Windows Internals structure may changed on alter version. see https://ntdiff.github.io/ – Ex-Kyuto Jan 09 '23 at 05:00