Yes, obviously you can. radare2 has built-in features to handle binary headers. This including reading, parsing and modifying the headers of the binary. And this is not different for elf
or pe
files, it will work great with both.
TL;DR
$ ./example.elf
[*] you ran this binary!
$ r2 -w -nn example.elf
[0x00000000]> .pf.elf_header.entry=0x0000063a
[0x00000000]> q
$ ./example.elf
[*] wow how did you manage to call this?
Creating our test file
As described in the article you linked in your question, it is easy to create a binary with a function that should never be executed under regular circumstances. Here's the exact code that was used in the linked article:
$ cat example.c
#include <stdio.h>
void never_call (void) {
printf ("[*] wow how did you manage to call this?\n");
return;
}
int main (int argc, char **argv) {
printf ("[*] you ran this binary!\n");
return 0;
}
As you can see, the function never_call
would, well... never be called. The program would execute the entrypoint which would execute the main
function and will return.
Now let's compile it using the command line used in the article, and execute the program:
$ gcc -Wall -o example.elf example.c
$ ./example.elf
[*] you ran this binary!
As we said, only main()
was executed. Now let's open the binary in radare2 to see the magic happens.
radare2 time!
Finding the address of the function
As you requested, we want to modify the entry point of the binary by modifying the pointed address in the elf header to be our never_call
function. So first, we need to find the address of never_call
in the binary.
$ r2 example.elf
[0x00000530]> f~never_call
0x0000063a 19 sym.never_call
We can see that the function never_call
is at address 0x0000063a. As you probably know by now, the f
command is used to list the flags that was marked by radare2, this including symbols as functions names. Then, we used ~
which is r2's internal grep and grepped for the relevant function.
Parsing the ELF Header
First, we need to seek to address 0
using s 0
and then and only then we can parse the header with a new command pf
. The command pf
is used to print formatted data such as structures, enums, and types. Let's load the format definition for elf64
using pfo elf64
and use the pf.
command to list the format definitions:
[0x00002400]> s 0 # Seek to pos 0 in the binary
[0x00000000]> pfo elf64 # Load a Format Definition File for elf
[0x00000000]> pf.
pf.elf_header [16]z[2]E[2]Exqqqxwwwwww ident (elf_type)type (elf_machine)machine version entry phoff shoff flags ehsize phentsize phnum shentsize shnum shstrndx
pf.elf_phdr [4]E[4]Eqqqqqq (elf_p_type)type (elf_p_flags)flags offset vaddr paddr filesz memsz align
pf.elf_shdr x[4]E[8]Eqqqxxqq name (elf_s_type)type (elf_s_flags_64)flags addr offset size link info addralign entsize
One of the loaded definitions is the elf_header
which holds the structure for the elf64 header. We can print the header like this:
[0x00000000]> pf.elf_header
ident : 0x00000000 = .ELF...
type : 0x00000010 = type (enum elf_type) = 0x3 ; ET_DYN
machine : 0x00000012 = machine (enum elf_machine) = 0x3e ; EM_AMD64
version : 0x00000014 = 0x00000001
entry : 0x00000018 = (qword)0x0000000000000530
phoff : 0x00000020 = (qword)0x0000000000000040
shoff : 0x00000028 = (qword)0x0000000000001948
flags : 0x00000030 = 0x00000000
ehsize : 0x00000034 = 0x0040
phentsize : 0x00000036 = 0x0038
phnum : 0x00000038 = 0x0009
shentsize : 0x0000003a = 0x0040
shnum : 0x0000003c = 0x001d
shstrndx : 0x0000003e = 0x001c
As you can see, radare2 printed the elf64 header in a readable format so now we can see that entry
, at 0x18, points to 0x530
which is our original entrypoint function. We can verify it by using ie
, a radare2 command to print the entrypooint:
[0x00000000]> ie
[Entrypoints]
vaddr=0x00000530 paddr=0x00000530 baddr=0x00000000 laddr=0x00000000 haddr=0x00000018 hvaddr=0x00000018 type=program
Indeed, you can see that the entry point is 0x530 and the haddr
, which is the header address, is 0x18.
Modifying the entry point
In order to modify this entry, we would need to open the file in writing mode. We can simply execute oo+
from our current session in order to re-open the file in write mode, or use the -w
argument to radare2
.
Then, we can simply use the pf
command to write to the parsed structure the address of never_call
function.
[0x00000000]> oo+
[0x00000000]> pf.elf_header.entry=0x0000063a
wv8 0x0000063a @ 0x00000018
This printed us a radare2 command to execute which will modify this address in the header. We can either execute it ourselves or use the .
command to "interpret the output of the command as r2 commands".
So instead of executing wv8 ...
, we will simply do:
[0x00000000]> .pf.elf_header.entry=0x0000063a
And now entry
should be overridden with 0x63a which is our never_call
function.
[0x00000000]> pf.elf_header
ident : 0x00000000 = .ELF...
type : 0x00000010 = type (enum elf_type) = 0x3 ; ET_DYN
machine : 0x00000012 = machine (enum elf_machine) = 0x3e ; EM_AMD64
version : 0x00000014 = 0x00000001
entry : 0x00000018 = (qword)0x000000000000063a
phoff : 0x00000020 = (qword)0x0000000000000040
shoff : 0x00000028 = (qword)0x0000000000001948
flags : 0x00000030 = 0x00000000
ehsize : 0x00000034 = 0x0040
phentsize : 0x00000036 = 0x0038
phnum : 0x00000038 = 0x0009
shentsize : 0x0000003a = 0x0040
shnum : 0x0000003c = 0x001d
shstrndx : 0x0000003e = 0x001c
[0x00000000]> pf.elf_header.entry
entry : 0x00000018 = (qword)0x000000000000063a
Executing
Great! We can now exit radare and execute the program.
$ ./example.elf
[*] wow how did you manage to call this?
Last words
This long answer explained every step in the way but can really be narrowed to a simple command .pf.elf_header.entry=0x0000063a
which sets the entry
in the elf header to be the desired address. In the TL;DR version I demonstrated the use of -w
to open the binary in write-mode
and the use of -nn
to load the binary structure (pfo elf64
, etc...). So simply, opening radare2 like this r2 -w -nn example.elf
and executing .pf.elf_header.entry=<address>
would solve your problem.
Don't be afraid to ask how to do things in radare2. Although it is quite a scary framework, it is really powerful and with proper knowledge, can do much more things than seems like at first.
Read more