This semester I am taking a class on Intel assembly, because I want more of an insight into how the computer works, and it will allow me to better reverse engineer new viruses and spyware. The class is also required if one is a Software Engineering major, so that means I have to take it.
The professor who teaches it absolutely sucks at teaching. He gets up in front of the class and mumbles through some powerpoint slides, which provide no real information, and then goes on and on about his days at Motorolla. It really sucks. Oh, best part is this quote:
"I think that is how Intel processors do it. I don't know I have not read up on it yet"
Well, we had our first assignment. Sum two numbers and then output them to the screen. We were supposed to write inline assembly using Visual Studio C++, but if we are to do an assembly class, then we should learn how to do write assembly, not have some parts assembly and other parts the compiler. Sure it makes it easy as you will get immediate access to the standard C library, but if you want that, you can just link against it.
The following code examples were written on Mac OS X, and will work on FreeBSD. Linux uses a different calling convention for it's syscalls, and as such this code will not run on Linux, unless it is modified. Do note, you need an Intel Mac for this to work. This is Intel assembly.
Compile the code with (Mac OS X):
nasm -f macho addnumbers.asm ld -o addnumbers addnumbers.o
nasm -f elf addnumbers.asm ld -o addnumbers addnumbers.o
Then you can run it with:
./addnumbers 1 5
As you can see in the comments of the source code, there are still some limitations, but the rest of the source code should be made readable by the comments that are provided.
Porting to Linux: Please double check that all the syscall numbers are the same. There are some differences between Linux and FreeBSD/Mac OS X in that regard.
; File: addnumbers.asm ; Bert JW Regeer ; ; 2008-01-27 ; ; Function: ; Add numbers together that are provided as arguments to the program in argv and argv. ; ; Known limitations: ; As of right now, the numbers that are provided may not add up to anything more than 9. ; This will hopefully be fixed in the next revision. Floating point numbers will not work. ; ; Todo: ; Write conversion routine, to convert a string of numbers into a real integer on which ; math may be performed. section .data ; Define some strings that are going to be used throughout the program ; This string is to let the user know they failed to provide the proper amount of arguments. args db "You failed to provide the proper amount of arguments", 0xa largs equ $ - args ; This string contains part of the output that we are going to send to the terminal. The last two ; bytes will be filled automatically by the program, before it is output to stdout. msg db 'Answer: ', 0, 0 lmsg equ $ - msg section .text global start ; Linker defined entry point. Mac OS X this is start. FreeBSD and others _start. global _start _start: start: ; Start the program here. add esp, byte 8 ; We don't care about argc or argv pop ecx ; Get the first argument or argv jecxz exit ; If there was no argument. Exit. Let the user know why ; Change the number from a character to an actual dword mov eax, dword [ecx] ; Move the character into eax so we can manipulate it sub eax, 0x30 ; Remove 0x30 from the character. To make it an actual number, not an ASCII number. pop ecx ; get the second argument or argv jecxz exit ; If there was no second argument. Exit. Let the user know why mov ebx, dword [ecx] ; Move the character into ebx so we can manipulate it sub ebx, 0x30 ; Remove 0x30 from the character. To make it an actual number, not an ASCII number. add eax, ebx ; Add the two numbers together add eax, 0x30 ; Make it an ASCII number again mov [msg+lmsg-2], eax ; Replace the null character in the msg with the answer mov [msg+lmsg-1], dword 0xa ; Add an newline character so that when it spits it out it is neatly formatted ; Call sys_write push dword lmsg ; Push the length of the string push msg ; Push the location of the string in memory push dword 0x1 ; Push the file descriptor to write to mov eax,4 ; Move the syscall number into eax push eax ; Push the syscall onto the stack int 0x80 ; Interrupt 80, go to kernel add esp, byte 16 ; Advance the stack pointer jmp done ; Program is done. Jump to done exit: ; Call sys_write push dword largs ; Push the length of the string push dword args ; Push the location of the string in memory push dword 0x1 ; Push the file descriptor to write to mov eax,4 ; Move the syscall number into eax push eax ; Push the syscall onto the stack int 0x80 ; Interrupt 80, go to kernel add esp, byte 16 ; Advance esp past the part we were just at done: ; sys_exit push dword 0x1 ; Push the value to return to the operating system mov eax,1 ; Move the syscall number into eax push eax ; Push the syscall onto the stack int 0x80 ; Interrupt 80, go to kernel ; We never return to this function, so no need to clean the stack.