Personal.X-Istence.com

Bert JW Regeer (畢傑龍)

Introduction to Intel Assembly

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:

./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[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.