This is a new technical blog. Or, mostly technical―we’ll see.
As a sucker for arcane knowledge, I’ve been learning some lower-level computer-type things for the first time this year. I can recommend two items:
- The Elements of Computing Systems: Building a Modern Computer from First Principles by Noam Nisan and Shimon Shocken; and
- this delightful YouTube video on “How a CPU Works”.
With a little more understanding, some hacking concepts started to make more sense.1 Jon Erickson’s Hacking: The Art of Exploitation gives a great sense of how programs interact with memory, and how low-level understanding can be used to manipulate execution. I really like the way he introduces assembly.
Flipping bits 1: to exit or not to exit
There are lots of assembly language tutorials out there. All we’re going to do here is flip a few bits by putting data in a couple of CPU registers.
global _start section .text _start: mov ebx, 1 mov ecx, 2
Assemble (is that the word?) and run this. We suspect this is either not going to do anything, or crash, or both.
$ nasm -f elf64 mov.s && ld -o mov mov.o
When we run this, sure enough, we get something fun: a segmentation fault!
$ ./mov Segmentation fault
This time, we’ll write a program that exits before it attempts to trample on other memory addresses.
global _start section .text _start: mov ebx, 0x1 mov ecx, 0x2 mov eax, 60 syscall
This second program does some meaningless bit flipping–but then it exits more or less gracefully.
The magic happens in the
That is where the kernel looks for system calls.
And according to Linux,
60 is the system call for
Flipping bits 2: a look inside
mov-2 programs do do some stuff.
Let’s prove it by looking at the execution of
mov-2 with gdb.
$ nasm -f elf64 mov-2.s && ld mov-2.o -o mov-2 $ gdb -q ./mov-2
This will drop us into the debugger shell. Let’s set a breakpoint and run the program.
$ gdb -q ./mov-2 Reading symbols from ./mov-2... (No debugging symbols found in ./mov-2) (gdb) break _start Breakpoint 1 at 0x401000 (gdb) run Starting program: /home/user/code/asm/mov-2 Breakpoint 1, 0x0000000000401000 in _start () (gdb)
Let’s view the assembly instructions.
(gdb) disassemble Dump of assembler code for function _start: => 0x0000000000401000 <+0>: mov ebx,0x1 0x0000000000401005 <+5>: mov ecx,0x2 0x000000000040100a <+10>: mov eax,0x3c 0x000000000040100f <+15>: syscall End of assembler dump.
nexti (or just
ni as in “next instruction”) we can execute these assembly instructions one by one.
And we can inspect the CPU registers after each instruction with
inspect registers (or just
(gdb) inspect registers rax 0x0 0 rbx 0x0 0 rcx 0x0 0 rdx 0x0 0 [--snip--] (gdb) next instruction (gdb) inspect registers rax 0x0 0 rbx 0x1 1 rcx 0x0 0 rdx 0x0 0 [--snip--] (gdb) next instruction (gdb) inspect registers rax 0x0 0 rbx 0x1 1 rcx 0x2 2 rdx 0x0 0 [--snip--]
Lovely! We’ve flipped some bits! Now to figure out which bits are the best ones to flip―