I would be grateful if can explain me what's happening in the following example using printf, compiling with nasm and gcc. Why is "sud" only printed on the screen? I don't understand, also, why is "sudobor" printed on the screen when I exchange "push 'sud'" with "push 'sudo'"? Can someone, also explain why do i need to push esp? Is it a null, that is required to be at the end of the string in printf? Thank you advance.
This is string.s file:
section .data
section .text
global start
extern printf
start:
push ebp
mov ebp, esp
push 'bor'
push 'sud'
push esp
call printf
mov esp, ebp
pop dword ebp
ret
this is c file:
#include <stdio.h>
#include <stdlib.h>
extern void start();
int main(void) {
start();
}
First off, thanks for blowing my mind. When I first looked at your code, I didn't believe it would work at all. Then I tried it and reproduced your results. Now it makes perfect sense to me, albeit in a twisted way. :-) I'll try to explain it.
First, let's look at the more sane way to achieve this. Define a string in the data portion of the ASM file:
section .data
string: db "Hey, is this thing on?", 0
Then push the address of that string on the stack before calling printf:
push string
call printf
So, that first parameter to printf (last parameter pushed on the stack before the call) is the pointer to the format string. What your code did was push the string on the stack, followed by the stack pointer which then pointed to the string.
Next, I'm going to replace your strings so that they are easier to track in disassembly:
push '567'
push '123'
push esp
call printf
Assemble with nasm, and then disassemble with objdump:
nasm string.s -f elf32 -o string.o
objdump -d -Mintel string.o
When you push, e.g., '123', that gets converted to a 32-bit hex digit-- 0x333231 in this case. Note that the full 32 bits are 0x00333231.
3: 68 35 36 37 00 push 0x373635
8: 68 31 32 33 00 push 0x333231
d: 54 push esp
Pushing onto the stack decrements the stack pointer. Assuming an initial stack pointer of 0x70 (contrived for simplicity), this is the state of the stack before calling printf:
64: 68: 6c: 70:
68 00 00 00 31 32 33 00 35 36 37 00 ...
So, when print is called, it uses the first parameter as the string pointer and starts printing characters until it sees a NULL (0x00).
That's why this example only prints "123" ("sud" in your original).
So let's push "1234" instead of "123". This means we are pushing the value 0x34333231. When calling printf the stack now looks like:
64: 68: 6c: 70:
68 00 00 00 31 32 33 34 35 36 37 00 ...
Now there is no NULL gap between those 2 strings on the stack and this example will print "1234567" (or "sudobor" in your original).
Implications: Try pushing "5678" instead of "567". You will probably get a segmentation fault because printf will just keep reading characters to print until it tries to read memory it doesn't have permission to read. Also, try pushing a string that is longer than 4 characters (e.g., "push '12345'"). The assembler won't let you because it can't convert that to a 32-bit number.