Injecting code into 32bit binary with ASLRPorting the example from "Practical binary Analysis" book to a more general case
Disclamer: I am currently reading PBA book, so this refers to the content presented there.
The code can nonetheless be obtained freely.
This "article" started mostly as personal notes, which I have decided to publish since I think it could help people starting in this field.
I will assume you are reading/own the book and therefore you can reference this content with that.
IntroductionThe book illustrates various method to inject assembly code into an ELF file. It also provides a small tool (elfinject.c) to automatize the process. In particular we will use the "primary" method, meaning overwriting an existing section header that is not fundamental for the ELF correct execution (in our case.note.ABI-tag ), and the corresponding part in the program header.
The objective is to inject a simple "Hello word" into /bin/ls , without breaking it (meaning that ls should keep working as expected).
For more details you should read the book.
Since I am assuming that this method has already been presented many times - and in better ways -, the concept of this (and
what might be useful) is to show you the thoughts that lead to resolve the problem.
The problemThe book presents already a tool called elfinject.c, which automatize the task. Peeking at the source code, it should also work on 32bit elf files. But, as in most of articles, the example provided did not take into consideration ASLR. Furthermore, the assembly code is for x64 architecture.
I will assume zero knowledge about ASLR presence and functionality.
The machine I am working with is a virtualized Ubuntu (not the one used for the book), with default configuration, and no safety measure turned off.
michele@michele-VirtualBox:~/pba/code/chapter7$ uname -a Obviously, just running the example does not work. The first problem we have to face is the wrong assembly code. The provided hello word.s is the following:
BITS 64 The solutionHere's my 32bit version of the hello world assembly file:
BITS 32 Let's take a look at the new hello32.s, it starts with BITS 32 , this is the obvious first change.
The next change is in the registers saved:
push ecx r** ) and neither r** (more info about registers).
I don't save the eax and this will be clear later why.The second important problem was the interrupt call. x86 does not use syscall , so I had to use int 0x80 .
This also requires a different way to: 1. provide the arguments 2. choose the number of the system call (with respect to the x64 example). We cannot simply replace the 64bit registers with their 32bit part, because the order used is different (order). Furthermore, the call associated with write is not 1 but 4 (syscall list). Another issue was regarding the address of strings. Simply porting the code provided did not work, because the memory pointed to (- for example for the string hello -) was not being updated, or corretly referenced relatively to the address. So to reference it correcty, I had to use a little trick:
mov ecx, [esp] Another issue encountered was the presence of ASLR. As I said, I will assume zero knowledge about it, and how to work around it. Reading ls headers with readelf , it prints it as a a shared object :
michele@michele-VirtualBox:~/pba/code/chapter7$ readelf /bin/ls -h
…
...
(gdb) info file jmp to 0x403f86 (at the end of our assembly code) would work:
push 0x403f86 We can find the distance debugging it, and then we can implement a relative jump, with a similar method as for the data addresses:
mov eax, [esp] After all these modification, we are finally able to inject and run from the terminal. After having copied /bin/ls to ls_mod ,
we compile the assembler file with -f bin flag, and we inject it.
nasm -f bin hello32.s -o hello32.bin More resources - what is going on?What is happening is that due to ASLR, the addresses are being randomized. You can find more details here.Debugging the binary with dbg disables ASLR. This is why we always get the same "original" entry point with gdb, and also why the injection would work without a relative jump. ASLR can be re-enabled in gdb with set disable-randomization off .
The binary was listed as shared object ,
because comping with ASLR enabled results in a PIE
(Position-Independent Executable) binary.
|