0

Code:

.text
.global _start
_start: 
    push    $0x0a           # Higher value of rsp
    push    $'D'            # |
    push    $'L'            # |
    push    $'R'            # |
    push    $'O'            # |
    push    $'W'            # | decreasing value of rsp
    push    $0x20           # |
    push    $'O'            # |
    push    $'L'            # |
    push    $'L'            # |
    push    $'E'            # |
    push    $'H'            # V
   
    mov     %rsp, %rcx      # rcx points to address in rsp
    mov     $96, %rdx       # 12 * 8 bytes, each push is 8 bytes
    mov     $4, %rax        # sys_write
    mov     $1, %rbx        # stdout
    int     $0x80           # interrupt
    
exit:
    mov     $1, %rax
    mov     $0, %rbx
    int     $0x80

In the above code, rsp will point ahead of character 'H'. By having rcx point to that address, I tried printing 96 bytes from that location.

This means, I tried to treat the entire stack as a string of 96 bytes, starting at rsp.

I assumed that sys_write increases the address in rcx to traverse the bytes of a string. If that is the case, this method should print something.

Nah, nothing got printed. Not even garbage. Some 0 chars might've been printed, but I won't know that. Why did this not work?

  • 1
    You should push up to 8 bytes per push, otherwise yes you will display zero bytes. Ie `push $'rld!\n'` \ `push $'Hello Wo'`. (Except there's no 8-byte push immediate? May need `mov ..., %rax` \ `push %rax`.) Not sure what else to change to make it work. – ecm May 22 '21 at 07:20
  • Oh you're using interrupt 80h in a 64-bit program. Refer to https://stackoverflow.com/questions/46087730/what-happens-if-you-use-the-32-bit-int-0x80-linux-abi-in-64-bit-code – ecm May 22 '21 at 07:32
  • 1
    @ecm: Right, only `mov` allows a 64-bit immediate, and `pushl` (push dword) isn't encodeable in 64-bit mode. Usually the best way is `mov reg, imm64` / `push reg`. Unfortunately GAS doesn't have syntax for using a string as a multi-character immediate the way NASM does, so you have to write the number manually, or `mov $'H' | 'e'<<8 | 'l'<<16 | ..., %rax`, because `mov $'Hello\n', %rax` totally confuses GAS. – Peter Cordes May 22 '21 at 07:38
  • 2
    To make it work 1. change register names **R?X** to their 32bit counterparts **E?X** 2. change string size from $96 to 12*4 3. link the executable as 32bit. and it will print `HELLO WORLD`. As @ecm says, you can store 4 or 8 characters together in each push. When the pushed value is only one-byte immediate, it is zero-extended (padded) with NULs to 32 or 64 bits. Those NUL bytes are interpreted by **sys_write** as zero-width nonprintable characters, in other words: skipped. That's why it works and prints noninterleaved result. – vitsoft May 22 '21 at 07:44
  • @vitsoft: You wrote "to 32 or 64 bytes" but you meant bits. – ecm May 22 '21 at 07:45
  • Ooops, I managed to fix it in 5 minutes, thank you @ecm :-) – vitsoft May 22 '21 at 07:47
  • 2
    @vitsoft: `sys_write` doesn't do any interpreting, it's fine with binary data as you can verify with `./a.out | hexdump -C`. It's *terminal emulators* that treat `0` bytes as zero-width characters with no effect. (Story time: I learned this the hard way decades ago in an undergrad course, I had something like `word%c` to pluralize or not with a ternary like `x == 1 ? 0 : 's'` because that seemed more efficient to me than %s. It looked right on screen of course, but the automatic grading script saw that line of input as ending at the 0 byte, apparently using implicit-length C strings.) – Peter Cordes May 22 '21 at 08:27

0 Answers0