ED 220: Intro to 64-bit Assembler (15 pts + 25 extra)

What You Need

Purpose

To learn the basics of 64-bit Assembly programming, making several simple programs.

Installing Yasm

Yasm is a rewrite of the nasm assembler, and we need it. Execute these commands in a Terminal to get it.
sudo apt update
sudo apt install yasm

Understanding Syscall 1: Write

From the Linux Syscall Table, this call is specified as:

So to write text to the console, we must do these things:

Program 1: ABC

This is the simplest program I know in Assembler. It uses syscall once to print the letters 'ABCDEFGH'.

In a Terminal window, execute this command:

nano abc1.asm
Enter this code in the editor.
section .text
    global _start

    _start:
        mov  rax, 0x4142434445464748    ; 'ABCDEFGH'
        push rax
        mov  rdx, 0x8     ; length of string is 8 bytes
        mov  rsi, rsp     ; Address of string is RSP because string is on the stack
        mov  rax, 0x1     ; syscall 1 is write
        mov  rdi, 0x1     ; stdout has a file descriptor of 1
        syscall           ; make the system call

Save the file with Ctrl+X, Y, Enter.

Execute these commands to compile, link, and run the program:

yasm -f elf64 abc1.asm 
ld -o abc1.out abc1.o 
./abc1.out 
The program prints out the letters in reverse order, and then crashes with a "Segmentation fault" message, as shown below.

Program 2: ABC & Exit

The program crashes at the end rather than exiting normally. To fix that, we need to add a second syscall, to "exit".

The Linux Syscall Table, specifies the "exit" call as:

So to exit, we must do these things:

In a Terminal window, execute these commands:
cp abc1.asm abc2.asm
nano abc2.asm
Add these lines at the bottom of the program:
        mov  rax, 0x3c    ; syscall 3c is exit
        syscall           ; make the system call

Save the file with Ctrl+X, Y, Enter.

Execute these commands to compile, link, and run the program:

yasm -f elf64 abc2.asm 
ld -o abc2.out abc2.o 
./abc2.out 
The program prints out the letters in reverse order, and then exits normally, as shown below.

Program 3: ABC in Order

Let's fix our program to print the letters in order. To do that, all we need to do is specify the hexadecimal ASCII codes in reverse order. In a Terminal window, execute these commands:
cp abc2.asm abc3.asm
nano abc3.asm
The 5th line of the program is:
        mov  rax, 0x4142434445464748    ; 'ABCDEFGH'
Change it to:
        mov  rax, 0x4847464544434241    ; 'ABCDEFGH' reversed

Save the file with Ctrl+X, Y, Enter.


Flag ED 220.1: File Information (5 pts)

Execute these commands to compile, link, run the program, and show information about the executable file:
yasm -f elf64 abc3.asm 
ld -o abc3.out abc3.o 
./abc3.out 
file abc3.out 
The program prints out the letters in the correct order, and then exits normally.

The flag is covered by a green box in the image below.


Program 4: "Hello World" Using a .data Section

This program stores a string in the .data section and references it.

In a Terminal window, execute this command:

nano hello.asm
Enter this code in the editor.
section .data
    string1 db  "Hello World!",10   ; '10' at end is line feed

section .text
    global _start

    _start:
        mov  rdx, 0xd               ; length of string is 13 bytes
        mov  rsi, dword string1     ; set rsi to pointer to string
        mov  rax, 0x1               ; syscall 1 is write
        mov  rdi, 0x1               ; stdout has a file descriptor of 1
        syscall                     ; make the system call

        mov  rax, 0x3c              ; syscall 3c is exit
        syscall                     ; make the system call

Save the file with Ctrl+X, Y, Enter.

Execute these commands to compile, link, and run the program:

yasm -f elf64 hello.asm 
ld -o hello.out hello.o 
./hello.out 
The program prints out the message, as shown below.

Program 5: "Echo" Using a .data Section

This program uses syscall 0 to read text from stdin and prints it out.

In a Terminal window, execute this command:

nano read.asm
Enter this code in the editor.
section .data
    string1 db  "AAAABBBBCCX"       ; Reserve space for 10 characters

section .text
    global _start

    _start:
        mov  rdx, 0xa               ; length of string is 10 bytes
        mov  rsi, dword string1     ; set rsi to pointer to string
        mov  rax, 0x0               ; syscall 0 is read
        mov  rdi, 0x0               ; stdin has a file descriptor of 0
        syscall                     ; make the system call

        mov  rdx, 0xa               ; length of string is 10 bytes
        mov  rsi, dword string1     ; set rsi to pointer to string
        mov  rax, 0x1               ; syscall 1 is write
        mov  rdi, 0x1               ; stdout has a file descriptor of 1
        syscall                     ; make the system call

        mov  rax, 0x3c              ; syscall 3c is exit
        syscall                     ; make the system call

Save the file with Ctrl+X, Y, Enter.

Execute these commands to compile, link, and run the program:

yasm -f elf64 read.asm 
ld -o read.out read.o 
./read.out 
The program waits for input. Type APPLE and press Enter.

The program prints out "APPLE", followed by some extra characters, as shown below.

If we were programming students, the next step would be to clean this thing up and get rid of the extra characters, make it calculate the string length automatically, etc.

But we have a different goal--to criticize code and its exploitable weaknesses--so we'll move on to other things.

Program 6: Sloppy Caesar Cipher

This program is like "Echo" but it increments each byte of the input before printing it out. It doesn't work correctly for "Z" or "z", which should wrap around to "A" or "a", and has other flaws.

In a Terminal window, execute this command:

nano caesar.asm
Enter this code in the editor.
section .data
    string1 db  "AAAABBBB"           ; Reserve space for 8 characters

section .text
    global _start

    _start:
        mov  rdx, 0x8                ; length of string is 8 bytes
        mov  rsi, dword string1      ; set rsi to pointer to string
        mov  rax, 0x0                ; syscall 1 is read
        mov  rdi, 0x0                ; stdin has a file descriptor of 0
        syscall                      ; make the system call

        mov  rbx, dword string1      ; set rbx to pointer to string
        mov  rcx, [rbx]              ; Put string value into rcx
        add  rcx, 0x0101010101010101 ; Add 1 to each byte, not fixing rollover
        mov  [rbx], rcx              ; Put modified byte on string

        mov  rdx, 0x8                ; length of string is 8 bytes
        mov  rsi, dword string1      ; set rsi to pointer to string
        mov  rax, 0x1                ; syscall 1 is write
        mov  rdi, 0x1                ; stdout has a file descriptor of 1
        syscall                      ; make the system call

        mov  rax, 0x3c               ; syscall 3c is exit
        syscall                      ; make the system call

Save the file with Ctrl+X, Y, Enter.

Execute these commands to compile, link, and run the program:

yasm -f elf64 caesar.asm 
ld -o caesar.out caesar.o 
./caesar.out 
There's a warning message saying a value is too large to fit into a 32-bit field, but the program compiles.

The program waits for input. Type HELLO and press Enter.

The program prints out "IFMMO", followed by some extra characters, as shown below.

The program encrypted the first 4 letters, but not the "O".

Let's see what the compiler actually did.

Examining Assembly Code with Objdump

Execute this command:
objdump -d caesar.out
As shown below, the compiler changed the "add" instruction to one that only adds a 32-bit value to rcx, not the 64-bit value we wanted.

Consulting the Intel 64 and IA-32 Architectures Software Developer's Manual, I found these ADD instructions:

That's hard to understand, but I think it means we can do a 64-bit add, but not with an immediate value. We need to use a register.

Program 7: Improved Caesar Cipher

In a Terminal window, execute this command:
cp caesar.asm caesar2.asm
nano caesar2.asm
In the editor, change this line:
        add  rcx, 0x0101010101010101  ; Add 1 to each byte, not fixing rollover
To this:
        mov  r8, 0x0101010101010101  ; Put value in r8
        add  rcx, r8                 ; Add using registers

Save the file with Ctrl+X, Y, Enter.


Flag ED 220.2: Extra Characters (10 pts)

Execute these commands to compile, link, and run the program:
yasm -f elf64 caesar2.asm 
ld -o caesar2.out caesar2.o 
./caesar2.out 
The program compiles without the warning message.

The program waits for input. Type HELLO and press Enter.

The program prints out "IFMMP", as it should!

The flag is covered by a green box in the image below.


Flag ED 220.3: Caesar Again (5 pts extra)

This program moves each letter three spaces lower in the alphabet, so D becomes A.

Enter the program shown below. A green box covers one command. Figure out what that command should be. That's the flag.

CCSF Students: Email in the image above showing your source code, not the image below showing the program running.

To test your answer, compile the code and run it on the input DARKNESS

You should see the output shown below.


Flag ED 220.4: Loop (10 pts extra)

This program prints "1" eleven times.

Enter the program shown below. A green box covers one command. Figure out what that command should be. That's the flag.

To test your answer, compile the code and run it. You should see the output shown below.


Flag ED 220.5: GOOD (10 pts extra)

This program prints "GOOD".

Enter the program shown below. A green box covers one command. Figure out what that command should be. That's the flag.

To test your answer, compile the code and run it. You should see the output shown below.


Sources

Linux Syscall Table

Basics & Sample Programs

Intel 64 and IA-32 Architectures Software Developer's Manual



Text corrections 11-10-18
Rewritten for new flag format and extra credit added 10-2-19
ED 220.3 image instructions added 11-5-19