I'm currently trying to learn some basic x86 assembly and running into some difficulties running it on Windows. All I'm trying to do is set the exit status. I'm aware of how to do this if I was on Linux:
global _start
_start:
mov eax,1
mov ebx, 52
int 0x80
Obviously this will give exit status 52. How do I do this on Windows? I looked all over but couldn't find a clear answer.
How do I do this on Windows? I looked all over but couldn't find a clear answer.
That is the issue here: There is no clear answer. Windoze is a mess. Use Linux and be happy. :−)
As it’s already been mentioned, there is no universal method to interface with the OS.
On WinD0S, for instance, it would’ve been int 21h:
segment code
start:
mov ah, 0x4C ; function `exit – terminate with return code`
mov al, 52 ; return code
int 0x21 ; DOS kernel interrupt
What you probably wanna do is, as David Wohlferd already pointed out, use ExitProcess:
global start
extern _ExitProcess#4
section .text
start:
push 52
call _ExitProcess#4
On a GNU/Linux Debian(-like) system with a wine(1) installation the steps look like:
nasm -f win32 -o exitdemo.obj exitdemo.asm
i686-w64-mingw32-ld exitdemo.obj -lkernel32 -o exitdemo.exe
wine ./exitdemo.exe; echo $?
Related
I've found a number of 64-bit assembler "Hello World" tutorials online for Windows, but none of them appear to work on my machine with the latest Windows 10 updates. In particular, ExitProcess continues to work, but WriteFile and WriteConsoleA silently fail.
Can someone describe the new ABI for this system call, or provide a fully qualified nasm/yasm example for _write, write, fprintf, printf, or similar functions with a more stable interface? (I am not interested in MessageBoxA or other graphical functions at the moment.) Please note any DLL's required aside from kernel32.dll, msvcrt.dll that declare these external functions.
In the given example you should use 64-bit space for variables stdout and bytesWritten:
stdout dq 0
bytesWritten dq 0
or else saved data in stdout will be overwritten by WriteFile function.
Also you should restore stack after calling WriteFile function:
add rsp,8
or it'll stay corrupt.
Evidently the online 64-bit Windows assembler tutorials do work, however, critical linker flags /entry and /[subsystem:]console were being mangled by Git Bash. So I've adopted the protection of wrapping the linker call in a PowerShell call, to ensure that the linker (either golink or link.exe) uses the correct mode, console, no matter which shell environment is running the linker command.
vsexec.bat:
:: Execute the specified command within a Visual Studio context,
:: where the necessary environment variables are sufficiently configured.
::
:: Usage: vsexec.bat <command>
::
:: Requires a Command Prompt or PowerShell context to operate.
call "C:\\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" amd64 %*
hello.asm:
extern GetStdHandle
extern WriteFile
extern ExitProcess
section .rodata
msg db "Hello World!", 0x0d, 0x0a
msg_len equ $-msg
stdout_query equ -11
status equ 0
section .data
stdout dw 0
bytesWritten dw 0
section .text
global start
start:
mov rcx, stdout_query
call GetStdHandle
mov [rel stdout], rax
mov rcx, [rel stdout]
mov rdx, msg
mov r8, msg_len
mov r9, bytesWritten
push qword 0
call WriteFile
mov rcx, status
call ExitProcess
Build steps:
$ nasm -f win64 hello.asm
$ powershell -Command "~\\vsexec.bat link /entry:start /subsystem:console hello.obj kernel32.lib"
Trace:
$ hello.exe
Hello World!
Final notes: I'm not sure what the exact proper stack and return policy is. The Windows documentation suggests that the stack be A) aligned to 16 bytes, B) provide 32 bytes per Windows API call, C) perform a ret at the end of each subroutine. However, when I try to do this, then I get segfaults. Not sure if nasm/link.exe are automatically performing some of the stack management work on my behalf or what, I guess I could check objdump -xDz hello.exe output to examine this further.
I decided to learn assembly today, because it seemed like it's a pretty powerfull tool, but I didn't know where to start learning it, so I googled it and found this:
https://www.tutorialspoint.com/assembly_programming
It told me to install NASM and MinGW for compiling and linking, so I downloaded and installed it and made sure that both of them are working properly.
I copied the given code
section .text
global _start ;must be declared for linker (ld)
_start: ;tells linker entry point
mov edx,len ;message length
mov ecx,msg ;message to write
mov ebx,1 ;file descriptor (stdout)
mov eax,4 ;system call number (sys_write)
int 0x80 ;call kernel
mov eax,1 ;system call number (sys_exit)
int 0x80 ;call kernel
section .data
msg db 'Hello, world!', 0xa ;string to be printed
len equ $ - msg ;length of the string
and pasted it into an empty document called "hello.asm" and compiled it by writing
nasm -f elf hello.asm
(later nasm -f win32 hello.asm)
and afterwards
ld hello.o -o hello.exe
(later ld hello.obj -o hello.exe)
and it successfully created a .exe file both times , but when I tried to execute it, it only opened the windows command prompt and a new window opened that said "hello.exe doesn't work anymore".
I know this won't output anything, but shouldn't it at least run ?
What did I do wrong ?
Using:
Windows 7 professional 64bit
AMD FX 4350
nasm-2.12.02
MinGW
You're going to need a different tutorial, as user tkausl pointed out this tutorial is for Linux x86_64 bit.
For windows, you can still use the NASM assembler and MinGW if you wish, but your code is going to look different because of the different calls and will also require you to use external libraries.
I recommend using the MASM for Windows however, as it is designed by Microsoft, and also included in the MASM32v8 package which has other tools. You can get MASM from here: http://www.masm32.com/
There is also a tutorial for Windows Assembly:
https://www-s.acm.illinois.edu/sigwin/old/workshops/winasmtut.pdf
However, if you are intent on using the NASM assembler, then you can refer to the answer posted by caffiend here:
How to write hello world in assembler under Windows?
Starting to learn assembly, I was given some Hello World assembly code created during the class on Linux. I would like to get it to work for 64-bit Mac OS X.
code.asm:
SECTION .data
hola: db "Hola!",10
tam: equ $-hola
SECTION .text
global main
main:
mov edx,tam
mov ecx,hola
mov ebx,1
mov eax,4
int 0x80
mov ebx,0
mov eax,1
int 0x80
This is what I do:
nasm -f macho32 -o object.o code.asm
gcc -m32 -o program object.o
Which tells me:
Undefined symbols for architecture i386: "_main", referenced from:
start in crt1.10.6.o ld: symbol(s) not found for architecture i386
Searching for this error, I found this question: nasm and gcc: 32 bit linking failed (64 bit Mac OS X)
One answer says
The problem you're having is that you're creating a 32-bit Linux(ELF)
object file which isn't compatible with the Mac OS X object format.
Try switching '-f elf' to '-f macho32'.
But I'm definitely using -f macho32. So what would the problem be then?
I've been trying to teach myself some entry-level Assembly programming too, and I ran into similar issues. I had originally compiled using nasm with elf, but that didn't work when I tried to use ld to link the object file and create the executable.
I think the answer you main question "what would the problem be then?" [to get this to run on 64bit MacOSX] is: You are using -f macho32 but expecting it to run on a 64bit machine, you need to change the command option to be -f macho64. Of course, this will not resolve the fact that your assembly code is written for a different architecture (more on that in a bit).
I found this handy answer on the right command to use in this instance to compile and link your code (after you refactor your assembly code to use the proper syntax instead of *nix as duskwuff stated): nasm -f macho64 main.asm -o main.o && ld -e _main -macosx_version_min 10.8 -arch x86_64 main.o -lSystem
After some searching, here's what I learned...
On Mac 64bit, it might be better to use the as assembler instead of nasm (if you want something more native), but if you want more portable code (learn the differences).
nasm doesn't come with the macho64 output type installed by default
Assembly is a pain in the keister (this aside)
Now that my learning rant is out of the way...
Here is the code which should operate on MacOSX 64 using nasm (if you have updated nasm with macho64, credit to Dustin Schultz):
section .data
hello_world db "Hello World!", 0x0a
section .text
global start
start:
mov rax, 0x2000004 ; System call write = 4
mov rdi, 1 ; Write to standard out = 1
mov rsi, hello_world ; The address of hello_world string
mov rdx, 14 ; The size to write
syscall ; Invoke the kernel
mov rax, 0x2000001 ; System call number for exit = 1
mov rdi, 0 ; Exit success = 0
syscall ; Invoke the kernel
Working code I used with the as assembler native to MacOSX64:
.section __TEXT,__text
.global start
start:
movl $0x2000004, %eax # Preparing syscall 4
movl $1, %edi # stdout file descriptor = 1
movq str#GOTPCREL(%rip), %rsi # The string to print
movq $100, %rdx # The size of the value to print
syscall
movl $0, %ebx
movl $0x2000001, %eax # exit 0
syscall
.section __DATA,__data
str:
.asciz "Hello World!\n"
Compile command: as -arch x86_64 -o hello_as_64.o hello_as_64.asm
Link Command: ld -o hello_as_64 hello_as_64.o
Execute Command: ./hello_as_64
Some helpful resources I found along my journey:
AS OSX Assembler Reference: https://developer.apple.com/library/mac/documentation/DeveloperTools/Reference/Assembler/Assembler.pdf
Writing 64 Bit Assembly on Mac OSX: http://www.idryman.org/blog/2014/12/02/writing-64-bit-assembly-on-mac-os-x/
Couldn't link object file using ld:
Can't link object file using ld - Mac OS X
OSX i386 SysCalls: http://www.opensource.apple.com/source/xnu/xnu-1699.26.8/osfmk/mach/i386/syscall_sw.h
OSX Master System Call Definitions: http://www.opensource.apple.com/source/xnu/xnu-1504.3.12/bsd/kern/syscalls.master
OSX Syscall: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/syscall.2.html
You would need to:
Change the label name from main to _main (in both places). Symbol naming works a little bit differently under Mac OS X.
Change the way you pass arguments to the system call. Mac OS X uses a different calling convention for the kernel from Linux; this code is not portable! I don't know as there's any official documentation for how it does work, but looking at the disassembly in GDB for a standard library function like _exit() may be instructive.
Here's _exit on my system, for instance:
<_exit+0>: mov $0x40001,%eax
<_exit+5>: call 0x96f124c2 <_sysenter_trap>
<_exit+10>: jae 0x96f10086 <_exit+26>
<_exit+12>: call 0x96f1007d <_exit+17>
<_exit+17>: pop %edx
<_exit+18>: mov 0x15a3bf9f(%edx),%edx
<_exit+24>: jmp *%edx
<_exit+26>: ret
<_exit+27>: nop
The extra bit set in 0x40001 is... weird, but can be safely ignored here.
The stuff following the call to _sysenter_trap is for error handling.
_sysenter_trap is:
<_sysenter_trap+0>: pop %edx
<_sysenter_trap+1>: mov %esp,%ecx
<_sysenter_trap+3>: sysenter
<_sysenter_trap+5>: nop
All things considered, you're probably better off linking to libSystem (the OS X equivalent of libc) instead of trying to call the kernel directly.
I've wrote a blog post on this topic: https://cs-flex.hashnode.dev/linux-assembly-on-macos
You have 3 main options:
VM -- i don't recommend
Renting a Linux server, not a bad option if you don't mind paying ~20-30$ a month
(My personal best option) using Docker to create a Linux container, that shares a folder (volume) and run assembler there. If you didn't use Docker before -- i still think this option is the best one.
You can read details in my blog post (especially if you didn't use Docker before). But in short, all you will need is this two files:
# Dockerfile
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y gcc
RUN apt-get install -y make
# docker-compose.yml
version: "3"
services:
linux:
image: linux-image
container_name: linux-container
build:
context: .
command: sleep 1000
volumes:
- .:/code
You will be able to run container and connect to it via
docker-compose up # build and run docker container
docker exec -it linux-container bash # "ssh" into container
after this you all your code in the folder with docker files will be "linked" to the folder /code/ inside `Docker. Therefore you can execute it inside docker container as if you were running Linux.
When attempting to run the following assembly program:
.globl start
start:
pushq $0x0
movq $0x1, %rax
subq $0x8, %rsp
int $0x80
I am receiving the following errors:
dyld: no writable segment
Trace/BPT trap
Any idea what could be causing this? The analogous program in 32 bit assembly runs fine.
OSX now requires your executable to have a writable data segment with content, so it can relocate and link your code dynamically. Dunno why, maybe security reasons, maybe due to the new RIP register. If you put a .data segment in there (with some bogus content), you'll avoid the "no writable segment" error. IMO this is an ld bug.
Regarding the 64-bit syscall, you can do it 2 ways. GCC-style, which uses the _syscall PROCEDURE from libSystem.dylib, or raw. Raw uses the syscall instruction, not the int 0x80 trap. int 0x80 is an illegal instruction in 64-bit.
The "GCC method" will take care of categorizing the syscall for you, so you can use the same 32-bit numbers found in sys/syscall.h. But if you go raw, you'll have to classify what kind of syscall it is by ORing it with a type id. Here is an example of both. Note that the calling convention is different! (this is NASM syntax because gas annoys me)
; assemble with
; nasm -f macho64 -o syscall64.o syscall64.asm && ld -lc -ldylib1.o -e start -o syscall64 syscall64.o
extern _syscall
global start
[section .text align=16]
start:
; do it gcc-style
mov rdi, 0x4 ; sys_write
mov rsi, 1 ; file descriptor
mov rdx, hello
mov rcx, size
call _syscall ; we're calling a procedure, not trapping.
;now let's do it raw
mov rax, 0x2000001 ; SYS_exit = 1 and is type 2 (bsd call)
mov rdi, 0 ; Exit success = 0
syscall ; faster than int 0x80, and legal!
[section .data align=16]
hello: db "hello 64-bit syscall!", 0x0a
size: equ $-hello
check out http://www.opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/syscall_sw.h for more info on how a syscall is typed.
The system call interface is different between 32 and 64 bits. Firstly, int $80 is replaced by syscall and the system call numbers are different. You will need to look up documentation for a 64-bit version of your system call. Here is an example of what a 64-bit program may look like.
So i was wondering if there is any? I know afd on windows but not sure anything about mac?
And this his how i am using nasam on the following code: nasm a.asm -o a.com -l a.lst
[org 0x100]
mov ax, 5
mov bx, 10
add ax, bx
mov bx, 15
add ax, bx
mov ax, 0x4c00
int 0x21
On windows i know a debugger name afd which help me to step through each statement but not sure how i can do this using gdb.
And neither i am able to execute this .com file, am i supposed to make some other file here?
Why are you writing 16-bit code that makes DOS syscalls? If you want to know how to write asm that's applicable to your OS, take a look the code generated by "gcc -S" on some C code... (Note that code generated this way will have operands reversed, and is meant to be assembled with as instead of nasm)
Further, are you aware what this code is doing? It reads to me like this:
ax = 5
bx = 10
ax += bx
bx = 15
ax += bx
ax = 0x4c00
int 21h
Seems like this code is equivalent to:
mov bx, 15
mov ax, 4c00
int 21h
Which according to what I see here, is exit(0). You didn't need to change bx either...
But. This doesn't even apply to what you were trying to do, because Mac OS X is not MS-DOS, does not know about DOS APIs, cannot run .COM files, etc. I wasn't even aware that it can run 16 bit code. You will want to look at nasm's -f elf option, and you will want to use registers like eax rather than ax.
I've not done assembly programming on OS X, but you could theoretically do something like this:
extern exit
global main
main:
push dword 0
call exit
; This will never get called, but hey...
add esp, 4
xor eax, eax
ret
Then:
nasm -f elf foo.asm -o foo.o
ld -o foo foo.o -lc
Of course this is relying on the C library, which you might not want to do. I've omitted the "full" version because I don't know what the syscall interface looks like on Mac. On many platforms your entry point is the symbol _start and you do syscalls with int 80h or sysenter.
As for debugging... I would also suggest GDB. You can advance by a single instruction with stepi, and the info registers command will dump register state. The disassemble command is also helpful.
Update: Just remembered, I don't think Mac OS X uses ELF... Well.. Much of what I wrote still applies. :-)
Xcode ships with GDB, the GNU Debugger.
Xcode 4 and newer ships with LLDB instead.
As others have said, use GDB, the gnu debugger. In debugging assembly source, I usually find it useful to load a command file that contains something like the following:
display/5i $pc
display/x $eax
display/x $ebx
...
display/5i will display 5 instructions starting with the next to be executed. You can use the stepi command to step execution one instruction at a time. display/x $eax displays the contents of the eax register in hex. You will also likely want to use the x command to examine the contents of memory: x/x $eax, for example, prints the contents of the memory whose address is stored in eax.
These are a few of many commands. Download the GDB manual and skim through it to find other commands you may be interested in using.
IDA Pro does work on the Mac after a fashion (UI still runs on Windows; see an example).