2

In linux you can print something using the system call number 4:

mov eax,4       ;system call number
mov ebx,0       ;file descriptor
mov ecx,msg     ;adress of message in data segment
mov edx,length  ;length of message

But, How do you print something from stack segment?

I tried this:

push 'H'
push 'e'
push 'l'
push 'l'
push 'o'
push ' '
push 'w'
push 'o'
push 'r'
push 'l'
push 'd'
mov eax,4       ;system call number
mov ebx,0       ;file descriptor
mov ecx,ebp     ;adress of message
mov edx,11      ;length of message

But doesn't print anything.

EDIT: I made some changes to my code and now it is so:

section .data
msg: db "Hola mundo",0Ah
ok: db "OK",0Ah

section .text
global _start
_start:
push 'leH'
push 'w ol'
push 'dlro'
mov eax,4       ;system call number
mov ebx,1       ;file descriptor
mov ecx,esp     ;adress of message in data segment
mov edx,11      ;length of message
mov eax,1
xor ebx,ebx     ;same as move ebx,0 but better
int 0x80

EDIT 2 (still not working)

section .data
msg: db "Hello world",0Ah

section .text
global _start
_start:
push 'leH'
push 'w ol'
push 'dlro'
mov eax,4       ;system call number
mov ebx,1       ;file descriptor
mov ecx,esp     ;adress of message in data segment
mov edx,11      ;length of message
int 0x80
mov eax,1
xor ebx,ebx     ;same as move ebx,0 but better
int 0x80

Responding to the comment, I assemble and compile with:

nasm -f elf64 hello.asm && ld hello.o && ./a.out

I'm working in Ubuntu 64-bit Linux.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
Adrian
  • 1,558
  • 1
  • 13
  • 31
  • `ebp` is not the stack pointer, `esp` is. Also, `push` will use 4 bytes per character so that won't quite work. – Jester Oct 14 '17 at 21:17
  • Not to mention using `push` will reverse your string. – Jester Oct 14 '17 at 21:24
  • @Jester the reversing of the string is something I wanted to test – Adrian Oct 14 '17 at 21:25
  • As Jester pointed out should be _ESP_ and _EBP_ and if you want to print that string out backwards the Pushes could be replaced with `push 'leH'` `push 'w ol'` `push 'dlro'` . As well EBX should be 1 not 0 for standard output. (0 is standard input) – Michael Petch Oct 14 '17 at 22:28
  • I made the changes but the program still doesn't work – Adrian Oct 14 '17 at 22:37
  • Your code doesn't include an `int 0x80` or `syscall` instruction. Of course it doesn't print anything. See https://stackoverflow.com/questions/2535989/what-are-the-calling-conventions-for-unix-linux-system-calls-on-x86-64, and use `strace` to see what args you're actually passing. Also note that `0` is stdin, not stdout. – Peter Cordes Oct 14 '17 at 22:39
  • @PeterCordes I included the int 0x80 instruction. I only was showing a snippet code – Adrian Oct 14 '17 at 22:41
  • 1
    Are you assembling this as a 64-bit program or 32-bit? This code won't run properly as 64-bit code because `int 0x80` emulation in 64-bit code can't handle 64--bit addresses which are required when using the stack. The stack addresses require the full 64-bit address in _RSP_. That can only be done if you use the 64-bit System V Linux system call interface via the `syscall` instruction. Infromation on using that can be found here in [Ryan Chapman's blog](http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/). – Michael Petch Oct 14 '17 at 23:16
  • Your edited code is missing `int 0x80` **before** `mov eax,1` so that the output system call is done. Your code sets up a write system call but then does nothing and then sets up the exit system call and executes that with `int 0x80` – Michael Petch Oct 14 '17 at 23:22
  • If you are assembling this with `-felf64` using _NASM_ this code won't work for the reason I gave above. Can you show us how you assemble and link this code to a final executable. I'd expect you'd see nothing and the program exit if you were in fact building this as a 64-bit executable. – Michael Petch Oct 14 '17 at 23:33
  • @MichaelPetch I was assembling and linking (and executing) with `nasm -f elf64 hello.asm && ld hello.o && ./a.out` and I'm working in ubuntu linux 64 bit – Adrian Oct 14 '17 at 23:48
  • @MichaelPetch Please post your comment as answer, I would like to accept it – Adrian Oct 14 '17 at 23:48
  • near duplicate: https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code or https://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-system-gnu-toolchain (probably the latter since your stack pushes only work in 32-bit mode.) But see the former for a `write` system call with a string on the stack. – Peter Cordes Oct 15 '17 at 02:03

1 Answers1

2

The question was originally lacking a key piece of information. You assemble and link with:

nasm -f elf64 hello.asm && ld hello.o && ./a.out

This generates a 64-bit executable. int 0x80 shouldn't be used in 64-bit executables. In 64-bit programs that stack pointer can't be represented in just the 32-bit ESP register. The only way to use 64-bit pointers is to use the syscall instruction. Ryan Chapman's blog has good information on using the 64-bit system call interface via the syscall instruction.

If you modify your code to conform to that interface it could look something like:

section .text
global _start
_start:
    mov dword [rsp-4], `rld\n`
                    ; Use back ticks instead of single quotes
                    ; To parse the string like C. We can write safely
                    ; in the 128 bytes below RSP because of the
                    ; Linux red zone.
    mov dword [rsp-8], 'o Wo'
    mov dword [rsp-12],'Hell'
    mov eax,1       ;Write system call number
    mov edi,eax     ;file descriptor (1 = stdout)
    lea rsi,[rsp-12];address of message on the stack
    mov edx,12      ;length of message
    syscall
    mov eax,60      ;Exit system call
    xor edi,edi     ;RDI=0
    syscall

This code writes constants to 32-bit registers as they are zero extended to the entire 64-bit register. We can't push 64-bit immediate values, but we can write the string onto the stack directly as 32-bit DWORD values. We don't need to adjust RSP because Linux has a 128 byte red zone that is protected from being clobbered by asynchronous events and signals.

If you want to use C style strings with C escape characters in it, NASM supports backtick instead of single quote to make the distinction. This allows you to use something like \n as a character denoting the new line character. I mention this as it seems you wanted to place a newline character in some of the code that was posted in your question that has since been removed.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • 1
    For code-density, you can actually mix `push` and `mov`, e.g. `push 'Hell'` / `mov dword [rsp+4], 'o Wo'`. As a bonus, you end up with rsp pointing to the front of your string, so you only need a `mov` instead of `lea`. Also an easy way to get a null-terminated string for `execve` without having a literal 0 byte in your machine code. But +1 for a good non-`push` example which is much simpler to understand! – Peter Cordes Oct 15 '17 at 02:07
  • (actually for code-density you'd want `push imm32` to push the end of the string, then `mov reg, imm64` / `push reg` to push the next 8 bytes. 12 immediate bytes and 2 opcode + 1 prefix byte. And you can `lea rsi, [rsp-8]` / syscall and restore RSP after push with a dummy `pop`.) – Peter Cordes May 01 '21 at 08:26