0

I am new to assembly language. I have the following assembly code:

.intel_syntax noprefix
.bits 32

.global asm0

asm0:
    push    ebp
    mov ebp,esp
    mov eax,DWORD PTR [ebp+0x8]
    mov ebx,DWORD PTR [ebp+0xc]
    mov eax,ebx
    mov esp,ebp
    pop ebp 
    ret

I want to figure out what the following command returns: asm0(0x2a,0x4f).

I am running Ubuntu, and I have already downloaded NASM. I've been reading about the syntax of Assembly code here: https://www.tutorialspoint.com/assembly_programming/assembly_basic_syntax.htm, but I cannot figure this out still. I have coded with C++ for three years, yet this is still confusing me. Could anyone with some more experience please help me figure this out?

EDIT: The code above says push and move, so it sort of looks like a stack data structure? Then it returns at the end. But, there are no parameters to this function. How would this work?

Error message when running gcc -m32 -g main.c assembly.s

myName@myName:~/Downloads$ gcc -m32 -g main.c file.S

main.c: In function ‘main’:

main.c:5:17: warning: implicit declaration of function ‘asm0’ [-Wimplicit-
function-declaration]

    printf("%d", asm0(123,456));
                 ^~~~

file.S: Assembler messages:

file.S:2: Error: unknown pseudo-op: `.bits'
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • You can just try it, and single-step it with a debugger. Unless it crashes because it destroys the caller's ebx (a call-preserved register according to the i386 System V calling convention.) – Peter Cordes Oct 05 '18 at 23:30
  • `push` operates on the call stack, not exactly a stack data structure. http://felixcloutier.com/x86/PUSH.html – Peter Cordes Oct 05 '18 at 23:31
  • Which debugger do you suggest I use? I am pretty much clueless on how to use NASM. –  Oct 05 '18 at 23:31
  • GDB is ok. See the bottom of https://stackoverflow.com/tags/x86/info. See the "calling convention" section of the x86 tag wiki to understand how asm functions get their args. https://github.com/cs01/gdbgui also mostly ok for asm, if you want a GUI. – Peter Cordes Oct 05 '18 at 23:33
  • I can't get gdbgui to work. It keeps saying gdbgui command not found, after I install it. Is there any easier way to figure out the output of this code? –  Oct 06 '18 at 00:05
  • Maybe an online compiler or something? –  Oct 06 '18 at 00:08
  • Well obviously you can just printf the return value from C if you write a caller for it, or single-step it in regular GDB. `gdb ./a.out`. To understand how it works, you *definitely* want to single-step it in GDB and watch registers change. `layout reg` and `si`. Or you can read the linked duplicates to understand how args are passed in the i386 System V C calling convention – Peter Cordes Oct 06 '18 at 00:08
  • I am still very confused. The program that I have in my original post is in a file called "file.S"; is it possible to make this a .C file? I've searched for decompilers, but I think they are all paid? –  Oct 06 '18 at 00:57
  • You have the code for a *function*, not a whole program. You should write a C program that calls it, like `int main(){printf("%d", asm0(123,456));}`, and `gcc -m32 -g main.c file.S`. – Peter Cordes Oct 06 '18 at 01:09
  • I am doing this, but I get the error message saying that I have an implicit declaration of the function asm0, as I never defined it in the C program, I believe. I updated the original post for your convenience. –  Oct 06 '18 at 01:43
  • I believe I need to do the declaration at the start of the C program. So it should be something like this?: int asm0(int, int); EDIT: I tried this, and that warning message is gone. now, all I have is the "file.S:2: Error: unknown pseudo-op: `.bits'" message –  Oct 06 '18 at 01:48
  • Yeah I left out `#include ` and a prototype for `int asm0(int,int);`. I hadn't noticed that `.bits 32` isn't a valid GAS directive, so your function doesn't assemble. Simply remove that line and it works fine if you compile with `gcc -no-pie -fno-pie -m32 main.c asm0.s`. Turns out gcc does depend on the function not clobbering EBX if you make a PIE executable by default. IDK where you got that code from, but the equivalent GAS directive is `.code32`. (But you don't need that, `.code32` is already the default when assembling into a 32-bit object file.) – Peter Cordes Oct 06 '18 at 01:51
  • Okay, sorry for asking so many questions. I am very new to this. I'm getting an a.out file now, but it has many unreadable characters. Shouldn't it just be like one integer output or something? Also, I assume for my hexadecimal values, converting to integers for the sake of passing them into this program and then converting it back will be sufficient? Thank you so so much // EDIT: using gcc -no-pie -fno-pie -m32 main.c file.s, I get an a.out executable file, which gives me "456" when I run it. is this correct ? –  Oct 06 '18 at 01:54
  • `a.out` is an executable. You don't view its contents, you run it. Or disassemble it with `objdump -drwC -Mintel a.out` to see the compiler-generated asm for `main`, or run `gdb ./a.out` and single step it. – Peter Cordes Oct 06 '18 at 01:57

1 Answers1

0

Your function looks for its 2 args on the stack, above the return address. Notice the accesses to [ebp+8] and [ebp+12], after setting up a traditional stack frame.

You don't declare args in asm; that's a high level concept that you implement yourself in the code in asm.

push operates on the call stack, not exactly a stack data structure. https://felixcloutier.com/x86/PUSH.html. It just decreases ESP and stores to [esp]. It's doing that to save/restore the caller's EBP as part of making a traditional stack frame. This is super-basic stuff, read some more tutorials or guides if you haven't seen this yet.


You can easily just try the code and find what it does by calling it from a C program.

Except you can't because your asm won't assemble. .bits 32 is not a valid GAS directive, but this is pretty clearly GNU assembler (GAS) syntax otherwise. I'm not aware of any assemblers that have a .intel_syntax noprefix directive other than GAS itself and clang's built-in assembler, and neither of them support .bits 32. So I'm guessing this function was from a tutorial or something and was hand-ported to GAS from NASM syntax (bits 32) or something without actually testing. But you don't need and shouldn't use that directive, or the GAS equivalent .code32. 32-bit code is already the default when assembling into a 32-bit object file, so .code32 would let you assemble 32-bit code into a 64-bit object file if you just used gcc foo.s without -m32, and then you'd have hard-to-debug problems are runtime instead of an obvious error at assemble time.

So remove the .bits 32 line, that solves the error message in the question.

To get rid of the warning messages, expand the one-liner I wrote in a comment by providing the proper headers and prototype.

#include <stdio.h>
int asm0(int,int);

int main(){
    printf("%d\n", asm0(123,456));
}

The function is also buggy: it clobbers ebx, which is a call-preserved register in the i386 System V calling convention. (Like in all the normal x86 calling conventions.)

gcc on Linux distros that enable default-PIE makes code that depends on EBX being call-preserved, and actually crashes. (you could still single-step it with a debugger, though, because the crash happens after asm0 returns.)

But you can make an executable that happens to work and will let you just run it to see which arg it returns:

peter@volta:/tmp$ gcc -g -Og -no-pie -fno-pie -m32 main.c asm0.s 
peter@volta:/tmp$ ./a.out 
456

asm0 returns its 2nd arg (by copying it to EAX, the return-value register). It loads it to EBX, then copies EBX to EAX, overwriting the result of loading the first arg.

You can watch this happen with gdb ./a.out

(gdb)  layout reg
       b main
       r
       si
       (press return to single-step one instruction at a time through the code, including into your function)

GDB highlights which registers were changed on each step.

See also the bottom of https://stackoverflow.com/tags/x86/info for more debugging tips.


Further reading:

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847