rokpoto.com
rokpoto.com

Reputation: 10718

Printf in Nasm behavior

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();

}

Upvotes: 5

Views: 14971

Answers (2)

igor rusakov
igor rusakov

Reputation: 1

NASM x64
windows x64
file  u01.asm :

    extern ExitProcess
    extern  _getch
    extern  printf

    Message    db "Console Message  %I64x  64", 0Dh, 0Ah, 0x0 , 0x0 ;
------
Start:
 sub   RSP, 0x20                              ; Align the stack to a multiple of 16 bytes
 and  rsp, 0xfffffffffffffff0  ; 

mov   rdx , 0xAEAE
lea   rcx ,  Message
call  printf

call  _getch   ; press any key

 xor   RCX, RCX
 call  ExitProcess
--------------------------
nasm  -Z ml.txt  -f win64  u01.asm -o u01.obj    (create  u01.obj)

golink /entry:Start   /console kernel32.dll ^ 
user32.dll api-ms-win-crt-conio-l1-1-0.dll ^ 
api-ms-win-crt-stdio-l1-1-0.dll ^
 msvcrt.dll  u01.obj  /fo p_01.exe        (create  p_01.exe)
-----------------------
c:\Windows\System32\msvcrt.dll    - in this dll contains  printf 
                           and many other function.
Copy this dll to your project directory.
-----------------------------------------------------------

Upvotes: 0

Multimedia Mike
Multimedia Mike

Reputation: 13216

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.

Upvotes: 13

Related Questions