1

I am trying to write a test program in AT&T syntax assembly that pushes the ascii value of some characters to the stack, then prints them. My code is as follows:

.text
.global main
main:
    push $0
    push $10
    push $65
    mov $4,%eax
    mov $1,%ebx
    mov %esp,%ecx
    mov $3,%edx
    int $0x80

    mov $1,%eax
    int $0x80

What I would expect this program to do is push 0, 10, and 65 to the stack so that the stack is 65, 10, 0 (capital A, new-line, end of string). Set eax and ebx to the values for writing to stdout, and setting ecx to the stack pointer so that it writes what's on the stack, while edx is the length of what's on the stack(i.e. 3). When I run this, absolutely nothing happens. Can you help me understand what I'm doing wrong and what is actually happening here?

JFMR
  • 23,265
  • 4
  • 52
  • 76
user2649681
  • 750
  • 1
  • 6
  • 23
  • `push` stores 4 bytes into memory (in 32 bit mode). You should thus see your letter `A` only. A single `push $0x0a41` should get you the line feed too. Note that `write()` or your terminal don't particularly care about a terminating zero, so just printing two bytes is enough. – Jester Jan 18 '18 at 17:25
  • With my initial code, or with the single push version you suggest, nothing is printed to the console. – user2649681 Jan 18 '18 at 17:48
  • 1
    Both work here as described. Run your code through `strace` then. – Jester Jan 18 '18 at 17:50
  • Run it in debugger, and check value of `rsp/esp` after each `push`, put it into question. If it moves by `8`, you are compiling this 32b linux API assembler source as 64b binary, which will not work, you will need to use `syscall` instead of `int 0x80`, and fix the arguments (`syscall` services are different). If you don't have debugger, just try to add `mov rax,rcx`, that should fail in 32b mode, but will compile in 64b). And you absolutely need debugger, and learn to work with it. – Ped7g Jan 18 '18 at 18:10
  • 1
    And in case you are compiling it by accident as 64b binary: check this answer, why it will not work: https://stackoverflow.com/q/46087730/4271923 – Ped7g Jan 18 '18 at 18:12
  • You need to revise your definition of "absolutely nothing". Use `strace ./a.out` to see the `write()` and `exit()` system calls your process makes, and/or pipe into `hexdump -C` to see the bytes it writes to stdout. – Peter Cordes Jan 28 '18 at 05:13

1 Answers1

3

Each push instruction pushes 4 bytes onto the stack. So, this is how the stack looks like before the int 0x80:

   ....
----------  (highest address)
|  0x00  |
---------- 
|  0x00  |
---------- 
|  0x00  |
---------- 
|  0x00  |
---------- <-- push $0
|  0x00  |
---------- 
|  0x00  |
---------- 
|  0x00  |
---------- 
|  0x0A  | 
---------- <-- push $10
|  0x00  |
---------- 
|  0x00  |
---------- 
|  0x00  |
---------- 
|  0x41  |
---------- <-- push $65 <-- ESP

However, at that moment you would like the stack to look like:

   ....
---------- 
|  0x00  |
---------- 
|  0x0A  |
---------- 
|  0x41  |
---------- <-- ESP

This can be achieved by replacing your push instructions by the following:

push $0x0a41

That is:

.text
.global main
main:
    push $0x0a41

    mov $4,%eax    ;system call write()
    mov $1,%ebx    ;file descriptor (stdout)
    mov %esp,%ecx  ;string
    mov $3,%edx    ;length of the string
    int $0x80

    mov $1,%eax
    int $0x80

In the edx register you are already specifying the length of the string (i.e.: 3), the NUL character is pointless (you are actually sending it to stdout).

JFMR
  • 23,265
  • 4
  • 52
  • 76