Writing ELF headers in Radare?
up vote
5
down vote
favorite
Reading Keith Makan's, "Introduction to the ELF Format : The ELF Header", he modifies e_entry
,
The
e_entry
field lists the offset in the file where the program should start executing.Normally it points to your _start method (of course if you compiled it with the usual stuff). You can point the e_entry anywhere you like, as an example I'm going to show that you can call a function that would other wise be impossible under normal execution.
Also documented in man 5 elf
, I'm wondering if Radare has any functionality to rewrite ELF-specific headers or if writing the bits manually is the current way to do this? For example, I know it'll show the entry point with ie
.
radare2 elf
add a comment |
up vote
5
down vote
favorite
Reading Keith Makan's, "Introduction to the ELF Format : The ELF Header", he modifies e_entry
,
The
e_entry
field lists the offset in the file where the program should start executing.Normally it points to your _start method (of course if you compiled it with the usual stuff). You can point the e_entry anywhere you like, as an example I'm going to show that you can call a function that would other wise be impossible under normal execution.
Also documented in man 5 elf
, I'm wondering if Radare has any functionality to rewrite ELF-specific headers or if writing the bits manually is the current way to do this? For example, I know it'll show the entry point with ie
.
radare2 elf
1
You absolutely want to do this with radare2? If not, have a look at the bfd project (used by binutils).
– 0xC0000022L♦
Nov 19 at 22:15
2
@0xC0000022L I'm not sure there is a way to do this, generally speaking I assume that Radare can't do something, and then I ask and I find out it can (a huge value of this site). I assume it can only parse ELF. If it can only parse ELF, I think a workaround is a great contribution because I'm pretty spankin new to RE, and I'm sure others are testing the waters just like me. If Radare grows the ability later, I will just come back and mark the newer answer as chosen.
– Evan Carroll
Nov 19 at 22:18
add a comment |
up vote
5
down vote
favorite
up vote
5
down vote
favorite
Reading Keith Makan's, "Introduction to the ELF Format : The ELF Header", he modifies e_entry
,
The
e_entry
field lists the offset in the file where the program should start executing.Normally it points to your _start method (of course if you compiled it with the usual stuff). You can point the e_entry anywhere you like, as an example I'm going to show that you can call a function that would other wise be impossible under normal execution.
Also documented in man 5 elf
, I'm wondering if Radare has any functionality to rewrite ELF-specific headers or if writing the bits manually is the current way to do this? For example, I know it'll show the entry point with ie
.
radare2 elf
Reading Keith Makan's, "Introduction to the ELF Format : The ELF Header", he modifies e_entry
,
The
e_entry
field lists the offset in the file where the program should start executing.Normally it points to your _start method (of course if you compiled it with the usual stuff). You can point the e_entry anywhere you like, as an example I'm going to show that you can call a function that would other wise be impossible under normal execution.
Also documented in man 5 elf
, I'm wondering if Radare has any functionality to rewrite ELF-specific headers or if writing the bits manually is the current way to do this? For example, I know it'll show the entry point with ie
.
radare2 elf
radare2 elf
edited Nov 19 at 22:14
0xC0000022L♦
7,76942963
7,76942963
asked Nov 19 at 22:06
Evan Carroll
71716
71716
1
You absolutely want to do this with radare2? If not, have a look at the bfd project (used by binutils).
– 0xC0000022L♦
Nov 19 at 22:15
2
@0xC0000022L I'm not sure there is a way to do this, generally speaking I assume that Radare can't do something, and then I ask and I find out it can (a huge value of this site). I assume it can only parse ELF. If it can only parse ELF, I think a workaround is a great contribution because I'm pretty spankin new to RE, and I'm sure others are testing the waters just like me. If Radare grows the ability later, I will just come back and mark the newer answer as chosen.
– Evan Carroll
Nov 19 at 22:18
add a comment |
1
You absolutely want to do this with radare2? If not, have a look at the bfd project (used by binutils).
– 0xC0000022L♦
Nov 19 at 22:15
2
@0xC0000022L I'm not sure there is a way to do this, generally speaking I assume that Radare can't do something, and then I ask and I find out it can (a huge value of this site). I assume it can only parse ELF. If it can only parse ELF, I think a workaround is a great contribution because I'm pretty spankin new to RE, and I'm sure others are testing the waters just like me. If Radare grows the ability later, I will just come back and mark the newer answer as chosen.
– Evan Carroll
Nov 19 at 22:18
1
1
You absolutely want to do this with radare2? If not, have a look at the bfd project (used by binutils).
– 0xC0000022L♦
Nov 19 at 22:15
You absolutely want to do this with radare2? If not, have a look at the bfd project (used by binutils).
– 0xC0000022L♦
Nov 19 at 22:15
2
2
@0xC0000022L I'm not sure there is a way to do this, generally speaking I assume that Radare can't do something, and then I ask and I find out it can (a huge value of this site). I assume it can only parse ELF. If it can only parse ELF, I think a workaround is a great contribution because I'm pretty spankin new to RE, and I'm sure others are testing the waters just like me. If Radare grows the ability later, I will just come back and mark the newer answer as chosen.
– Evan Carroll
Nov 19 at 22:18
@0xC0000022L I'm not sure there is a way to do this, generally speaking I assume that Radare can't do something, and then I ask and I find out it can (a huge value of this site). I assume it can only parse ELF. If it can only parse ELF, I think a workaround is a great contribution because I'm pretty spankin new to RE, and I'm sure others are testing the waters just like me. If Radare grows the ability later, I will just come back and mark the newer answer as chosen.
– Evan Carroll
Nov 19 at 22:18
add a comment |
2 Answers
2
active
oldest
votes
up vote
12
down vote
accepted
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
- radare.today | Parsing a fileformat with radare2
- r2book | Types
1
Wow, this is really cool. From the UI standpoint, it would be nice if the.pf.elf_header.entry=0x0000063a
didn't write to$$
but to@ 0
. Got it right the second time though.
– Evan Carroll
Nov 20 at 9:46
I just used this to answer another question on the site, thanks again for answering all my questions on re.se. I'll keep them coming. =) reverseengineering.stackexchange.com/a/19936/22669
– Evan Carroll
Nov 20 at 22:58
add a comment |
up vote
2
down vote
I don't think this feature is supported according to the source code.
Edit: I was wrong, sorry about that.
However, if you really need a library/tool to do that, I recommend LIEF.
New contributor
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
12
down vote
accepted
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
- radare.today | Parsing a fileformat with radare2
- r2book | Types
1
Wow, this is really cool. From the UI standpoint, it would be nice if the.pf.elf_header.entry=0x0000063a
didn't write to$$
but to@ 0
. Got it right the second time though.
– Evan Carroll
Nov 20 at 9:46
I just used this to answer another question on the site, thanks again for answering all my questions on re.se. I'll keep them coming. =) reverseengineering.stackexchange.com/a/19936/22669
– Evan Carroll
Nov 20 at 22:58
add a comment |
up vote
12
down vote
accepted
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
- radare.today | Parsing a fileformat with radare2
- r2book | Types
1
Wow, this is really cool. From the UI standpoint, it would be nice if the.pf.elf_header.entry=0x0000063a
didn't write to$$
but to@ 0
. Got it right the second time though.
– Evan Carroll
Nov 20 at 9:46
I just used this to answer another question on the site, thanks again for answering all my questions on re.se. I'll keep them coming. =) reverseengineering.stackexchange.com/a/19936/22669
– Evan Carroll
Nov 20 at 22:58
add a comment |
up vote
12
down vote
accepted
up vote
12
down vote
accepted
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
- radare.today | Parsing a fileformat with radare2
- r2book | Types
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
- radare.today | Parsing a fileformat with radare2
- r2book | Types
edited Nov 21 at 6:34
Evan Carroll
71716
71716
answered Nov 20 at 7:36
Megabeets
6,2681835
6,2681835
1
Wow, this is really cool. From the UI standpoint, it would be nice if the.pf.elf_header.entry=0x0000063a
didn't write to$$
but to@ 0
. Got it right the second time though.
– Evan Carroll
Nov 20 at 9:46
I just used this to answer another question on the site, thanks again for answering all my questions on re.se. I'll keep them coming. =) reverseengineering.stackexchange.com/a/19936/22669
– Evan Carroll
Nov 20 at 22:58
add a comment |
1
Wow, this is really cool. From the UI standpoint, it would be nice if the.pf.elf_header.entry=0x0000063a
didn't write to$$
but to@ 0
. Got it right the second time though.
– Evan Carroll
Nov 20 at 9:46
I just used this to answer another question on the site, thanks again for answering all my questions on re.se. I'll keep them coming. =) reverseengineering.stackexchange.com/a/19936/22669
– Evan Carroll
Nov 20 at 22:58
1
1
Wow, this is really cool. From the UI standpoint, it would be nice if the
.pf.elf_header.entry=0x0000063a
didn't write to $$
but to @ 0
. Got it right the second time though.– Evan Carroll
Nov 20 at 9:46
Wow, this is really cool. From the UI standpoint, it would be nice if the
.pf.elf_header.entry=0x0000063a
didn't write to $$
but to @ 0
. Got it right the second time though.– Evan Carroll
Nov 20 at 9:46
I just used this to answer another question on the site, thanks again for answering all my questions on re.se. I'll keep them coming. =) reverseengineering.stackexchange.com/a/19936/22669
– Evan Carroll
Nov 20 at 22:58
I just used this to answer another question on the site, thanks again for answering all my questions on re.se. I'll keep them coming. =) reverseengineering.stackexchange.com/a/19936/22669
– Evan Carroll
Nov 20 at 22:58
add a comment |
up vote
2
down vote
I don't think this feature is supported according to the source code.
Edit: I was wrong, sorry about that.
However, if you really need a library/tool to do that, I recommend LIEF.
New contributor
add a comment |
up vote
2
down vote
I don't think this feature is supported according to the source code.
Edit: I was wrong, sorry about that.
However, if you really need a library/tool to do that, I recommend LIEF.
New contributor
add a comment |
up vote
2
down vote
up vote
2
down vote
I don't think this feature is supported according to the source code.
Edit: I was wrong, sorry about that.
However, if you really need a library/tool to do that, I recommend LIEF.
New contributor
I don't think this feature is supported according to the source code.
Edit: I was wrong, sorry about that.
However, if you really need a library/tool to do that, I recommend LIEF.
New contributor
edited Nov 20 at 19:43
New contributor
answered Nov 19 at 23:36
wisk
493
493
New contributor
New contributor
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2freverseengineering.stackexchange.com%2fquestions%2f19921%2fwriting-elf-headers-in-radare%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
You absolutely want to do this with radare2? If not, have a look at the bfd project (used by binutils).
– 0xC0000022L♦
Nov 19 at 22:15
2
@0xC0000022L I'm not sure there is a way to do this, generally speaking I assume that Radare can't do something, and then I ask and I find out it can (a huge value of this site). I assume it can only parse ELF. If it can only parse ELF, I think a workaround is a great contribution because I'm pretty spankin new to RE, and I'm sure others are testing the waters just like me. If Radare grows the ability later, I will just come back and mark the newer answer as chosen.
– Evan Carroll
Nov 19 at 22:18