Hey! Corny matrix-styled dojo. Why not?
Let’s kick off today’s blog post by writing a very, very simple ASM program. Since I’m reviewing ASM stuff through pwn.college’s course, why not showcase how simple ASM can be? (In a way... math is simple too, but many would argue otherwise).
Our script will be called one-oh-one.s
, and our program will be called pausing
—since that’s what we’ll want at the end.
Let's start with a program that starts and ends. Simple enough, right? But for that, we need to pass execution to the OS via a syscall (remember those?). To do this, we can move the value 60 into the rax
register. Here's how:
Ah, much better. Now, our previously failing program is a success... of sorts. We made sure to run with Intel syntax, which is much cleaner (at least to my eyes). We also made the _start
label globally visible so that we can indicate where our program begins.
Now no more errors, and we’re ready to tackle the rest of the program. Easy peasy.
Right! While I was trying to set up a pause in my program, I ran into an issue. The syscall I thought was the right one for pausing was actually wrong. So, since I’m working on Linux with x86_64 architecture, I checked /usr/include/asm/unistd_64.h
to see which syscalls I should use.
There, I found the exit
syscall (60), which needs to be loaded into rax
(which we’re already doing). If we want to set an exit code, we need to load it into the rdi
register. Fair enough, we can do that. As for the pause syscall, we need syscall 34. I also discovered the alarm
syscall (37), which sends a SIGALRM
, ending the pause
syscall. Without this, we'd have to ctrl+c our way out of the program. We also learned that we need to pass a parameter through rdi
. Easy peasy.
Now that we have the tools, let’s create our pausing program, assemble it, link it, and run it while checking for our exit code:
Don't know about you, but I love this!
Now, let's review other concepts. For instance, in pwn.college, you can learn how to point to a specific memory value or the contents at that memory location.
Let's say there’s a memory position 12345
holding the value 42
.
If we do:
mov rdi, 12345
We're making it so that rdi will hold that number 12345.
But if we do:
mov rdi, [12345]
Now rdi
will hold the value at memory position 12345
, which is 42
, just like the value stored at that location.
I won’t dive too much into these basics because I’m sure you either already know them or want to experiment with them yourself. Just remember that the OS will have some defenses in place that may make it difficult, if not impossible, to access specific memory locations in your binary.
You can check which defenses have been enabled with a tool called checksec
:
Please take your time and check what each of these items does. Here's a quick rundown:
- RELRO: A security feature that makes it harder to modify certain parts of a program, like its GOT (Global Offset Table), preventing attacks on function pointers.
- STACK CANARY: A protective value placed on the stack to detect buffer overflow attacks before they overwrite important data.
- NX: No-Execute; prevents code from running in certain areas of memory, like the stack, to stop exploits that execute malicious code.
- PIE: Position Independent Executable; allows a program to run at random memory addresses, making it harder for attackers to predict the location of code.
- RPATH: A runtime setting in executables that specifies directories to search for dynamic libraries before default system paths.
- RUNPATH: Similar to
RPATH
, but it is used afterLD_LIBRARY_PATH
to specify directories for locating shared libraries at runtime. - Symbols: These represent function names, variable names, and other references in the program’s code, helpful for debugging or exploitation.
- FORTIFY: A set of compiler protections that enhance the safety of certain library functions, like checking buffer sizes to prevent overflows.
- Fortified: Refers to functions that have been modified with additional checks (from
FORTIFY_SOURCE
) to improve security against buffer overflows. - Fortifiable: Functions that can potentially be fortified using additional checks to prevent common vulnerabilities like buffer overflows.
- FILE: Refers to the executable or object file format, containing the machine code, symbols, and metadata that the operating system uses to load and run the program.
Like I said, a quick rundown, but it’s worth spending time learning about these defenses—what they do and how to set them up to adequately inspect a binary. Also, note that this applies to ELF files.
Alrighty, another short one! Hope you had fun.
No comments:
Post a Comment