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
or (FreeBSD)
nasm -f elf addnumbers.asm
ld -o addnumbers addnumbers.o
Then you can run it with:
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[1] and argv[2].
;
; 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[0]
pop ecx ; Get the first argument or argv[1]
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[2]
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.