Assembly Live Coding: Bootloader & XOR Cipher - Uncut Learning Session

NeuralNineAbout 6 min readSep 20, 2025Watch original
THE SUMMARYAI-generated

Key Concepts

  • Assembly language learning through unscripted coding sessions.
  • XOR cipher implementation in assembly.
  • Minimal bootloader creation in assembly for BIOS systems.
  • 16-bit real mode programming.
  • BIOS interrupts for screen output.
  • Segment registers (CS, DS, ES) and their role in memory addressing.
  • Index registers (SI) for string manipulation.
  • Boot sector structure (512 bytes, boot signature).
  • QEMU for bootloader testing.

XOR Cipher Implementation

Goal

Implement an XOR cipher where user input from stdin is XORed with a static mask, and the result is printed to stdout. The process should be reversible using the same mask.

Steps

  1. Project Setup: Create a file named exorcipher.asm and define the .text section with start as the entry point.
  2. Input Buffer: Allocate a 32-byte buffer in the .bss section named input_buffer using resb 32.
  3. XOR Mask: Define a 32-byte static XOR mask in the .data section named key using db "My fancy key for the cipher in asse".
  4. System Call for Input:
    • Move 0 into rax (syscall number for sys_read).
    • Move 0 into rdi (file descriptor for stdin).
    • Move input_buffer into rsi (pointer to the input buffer).
    • Move 32 into rdx (number of bytes to read).
    • Execute syscall.
  5. Register Allocation:
    • rsi: Pointer to input_buffer.
    • rdi: Pointer to key.
    • r8: Pointer to result_buffer (allocated using resb 32 in .bss).
  6. XOR Loop:
    • Move 4 into rcx (loop counter, since we process 8 bytes at a time).
    • Load 8 bytes from input_buffer into rax using mov rax, [rsi].
    • Load 8 bytes from key into rbx using mov rbx, [rdi].
    • XOR rax and rbx using xor rax, rbx.
    • Store the result from rax into result_buffer using mov [r8], rax.
    • Increment rsi, rdi, and r8 by 8 using add rsi, 8, add rdi, 8, and add r8, 8.
    • Decrement rcx using loop xor_loop.
  7. System Call for Output:
    • Move 1 into rax (syscall number for sys_write).
    • Move 1 into rdi (file descriptor for stdout).
    • Move result_buffer into rsi (pointer to the result buffer).
    • Move 8 into rdx (number of bytes to write).
    • Execute syscall.
  8. Exit:
    • Move 60 into rax (syscall number for sys_exit).
    • Move 0 into rdi (exit code).
    • Execute syscall.

Key Points

  • Registers can only hold 8 bytes (64 bits), so the 32-byte input is processed in four 8-byte chunks.
  • The loop instruction decrements rcx and jumps to the specified label if rcx is not zero.
  • Square brackets [] are used to dereference memory addresses. mov rax, [rsi] loads the value at the address pointed to by rsi into rax.
  • The XOR operation is symmetric, meaning applying it twice with the same key restores the original data.

Example

If the input is "hello wo" and the key is "My fancy", the XORed output will be a series of non-printable characters. Feeding this output back into the program with the same key will restore "hello wo".

Python Verification

Python can be used to verify the XOR cipher:

key = "My fancy"
message = "hello wo"
keyb = key.encode('ascii')
messageb = message.encode('ascii')
result = bytes(x ^ y for x, y in zip(keyb, messageb))
print(result.decode('ascii', errors='ignore'))

Minimal Bootloader Implementation

Goal

Create a minimal bootloader that prints "Hello World!" to the screen when the system boots, without relying on an operating system.

Steps

  1. Environment Setup:
    • Specify 16-bit real mode using bits 16.
    • Set the origin to 0x7c00 using org 0x7c00.
  2. Segment Register Setup:
    • Disable interrupts using cli.
    • Push the code segment register cs onto the stack using push cs.
    • Pop the value from the stack into the data segment register ds using pop ds.
  3. Message Definition:
    • Define the message "Hello World!" in the code section using message db "Hello World!", 0. The 0 is a null terminator.
  4. Print Loop:
    • Load the address of the message into the source index register si using mov si, msg.
    • print_loop: label:
      • Load a byte from the address pointed to by si into the al register using lodsb.
      • Test if the value in al is zero using test al, al.
      • If it is zero, jump to the done label using jz done.
      • Move 0x0e into the ah register (BIOS teletype function) using mov ah, 0x0e.
      • Call the BIOS interrupt 0x10 using int 0x10.
      • Jump back to the print_loop label using jmp print_loop.
  5. Halt:
    • done: label:
      • Disable interrupts using cli.
      • halt: label:
        • Halt the CPU using hlt.
        • Jump back to the halt label using jmp halt.
  6. Padding and Boot Signature:
    • Pad the remaining bytes with zeros using times 510 - ($ - $$) db 0.
    • Add the boot signature using dw 0xaa55.

Key Points

  • 16-bit Real Mode: The bootloader operates in 16-bit real mode, which has a segmented memory model.
  • BIOS Interrupts: The bootloader uses BIOS interrupt 0x10 to print characters to the screen. ah = 0x0e specifies the teletype function.
  • Segment Registers: In real mode, memory addresses are calculated as segment * 16 + offset. The code segment cs and data segment ds must be properly set up.
  • Boot Signature: The last two bytes of the boot sector must be 0xaa55 for the BIOS to recognize it as a bootable sector.
  • lodsb Instruction: This instruction loads a byte from memory (pointed to by si) into al and increments si.
  • cli and hlt: cli disables interrupts, and hlt halts the CPU.

Assembly and Testing

  1. Assemble: Use NASM to assemble the code into a binary file: nasm -f bin bootloader.asm -o bootloader.bin.
  2. Create Floppy Image: Use dd to create a floppy image: dd if=/dev/zero of=floppy.img bs=512 count=2880.
  3. Write Bootloader to Image: Use dd to write the bootloader to the floppy image: dd if=bootloader.bin of=floppy.img bs=512 count=1 conv=notrunc.
  4. Test with QEMU: Run the bootloader in QEMU: qemu-system-i386 -fda floppy.img. Use Ctrl+A X to exit QEMU.

Troubleshooting

  • Triple Fault: A triple fault indicates a serious error in the bootloader code, such as an invalid instruction.
  • VirtualBox Crashes: VirtualBox may crash due to system-specific issues. QEMU is a more reliable option for testing bootloaders.

Conclusion

The session successfully implemented an XOR cipher and a minimal bootloader in assembly. The bootloader was tested using QEMU, demonstrating the ability to print "Hello World!" without an operating system. The session highlighted the complexities of low-level programming and the importance of understanding memory addressing, segment registers, and BIOS interrupts.

AI summaries can miss context or contain errors. Check important details against the original video.

Go a little deeper.

Have a question about this video? Load its transcript to open the video chat.