阅读基思塔克拉玛干的,“介绍ELF格式:ELF头”,他修改e_entry
,
该
e_entry
字段列出了程序应该开始执行的文件中的偏移量。通常它指向您的 _start 方法(当然,如果您使用通常的东西编译它)。您可以将 e_entry 指向任何您喜欢的位置,作为示例,我将展示您可以调用一个在正常执行情况下不可能的函数。
也记录在 中man 5 elf
,我想知道 Radare 是否具有重写 ELF 特定标头的任何功能,或者手动编写位是否是当前的方法?例如,我知道它会用ie
.
阅读基思塔克拉玛干的,“介绍ELF格式:ELF头”,他修改e_entry
,
该
e_entry
字段列出了程序应该开始执行的文件中的偏移量。通常它指向您的 _start 方法(当然,如果您使用通常的东西编译它)。您可以将 e_entry 指向任何您喜欢的位置,作为示例,我将展示您可以调用一个在正常执行情况下不可能的函数。
也记录在 中man 5 elf
,我想知道 Radare 是否具有重写 ELF 特定标头的任何功能,或者手动编写位是否是当前的方法?例如,我知道它会用ie
.
是的,显然你可以。radare2 具有处理二进制标头的内置功能。这包括读取、解析和修改二进制文件的标头。这对于elf
或pe
文件没有什么不同,它对两者都适用。
$ ./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?
正如您在问题中链接的文章中所述,很容易创建一个二进制文件,其中包含一个在正常情况下永远不应执行的函数。这是链接文章中使用的确切代码:
$ 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;
}
如您所见,该函数never_call
将……永远不会被调用。该程序将执行入口点,该入口main
点将执行该函数并返回。
现在让我们使用文章中使用的命令行编译它,并执行程序:
$ gcc -Wall -o example.elf example.c
$ ./example.elf
[*] you ran this binary!
正如我们所说,只有main()
被处决。现在让我们打开radare2 中的二进制文件,看看神奇的事情发生了。
查找函数地址
根据您的要求,我们希望通过将 elf 标头中的指向地址修改为我们的never_call
函数来修改二进制文件的入口点。所以首先,我们需要找到never_call
二进制文件中的地址。
$ r2 example.elf
[0x00000530]> f~never_call
0x0000063a 19 sym.never_call
我们可以看到该函数never_call
位于地址0x0000063a。正如你可能已经知道,该f
命令用于列出的标志是被打上了radare2,这包括符号功能名称。然后,我们使用~
which 是 r2 的内部grep并为相关函数 greped。
解析 ELF 头
首先,我们需要寻求地址0
使用s 0
,然后只有这样,我们可以分析一个新的命令头pf
。该命令pf
用于打印格式化数据,例如结构、枚举和类型。让我们加载格式定义以供elf64
使用pfo elf64
并使用pf.
命令列出格式定义:
[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
加载的定义之一是elf_header
保存 elf64 标头结构的 。我们可以像这样打印标题:
[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
如您所见,radare2 以可读格式打印了 elf64 标头,因此现在我们可以看到entry
,在 0x18 处,指向0x530
哪个是我们原始的入口点函数。我们可以通过使用ie
radare2命令打印入口点来验证它:
[0x00000000]> ie
[Entrypoints]
vaddr=0x00000530 paddr=0x00000530 baddr=0x00000000 laddr=0x00000000 haddr=0x00000018 hvaddr=0x00000018 type=program
确实,您可以看到入口点是 0x530,haddr
头地址是 0x18。
修改入口点
为了修改这个条目,我们需要以写入模式打开文件。我们可以简单地执行oo+
从我们当前的会话,以便重新以写模式打开文件,或者使用-w
参数radare2
。
然后,我们可以简单地使用pf
命令将never_call
函数的地址写入解析后的结构中。
[0x00000000]> oo+
[0x00000000]> pf.elf_header.entry=0x0000063a
wv8 0x0000063a @ 0x00000018
这向我们打印了一个radare2命令来执行,它将修改标题中的这个地址。我们既可以自己执行,也可以使用.
命令“将命令的输出解释为 r2 命令”。
因此wv8 ...
,我们将简单地执行以下操作,而不是执行:
[0x00000000]> .pf.elf_header.entry=0x0000063a
现在entry
应该被我们的never_call
函数0x63a 覆盖。
[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
执行
伟大的!我们现在可以退出radare并执行程序。
$ ./example.elf
[*] wow how did you manage to call this?
这个长答案解释了每一步,但实际上可以缩小为一个简单的命令.pf.elf_header.entry=0x0000063a
,该命令entry
将 elf 标头中的 设置为所需的地址。在TL;DR版本中,我演示了使用-w
打开二进制文件write-mode
和使用-nn
加载二进制结构(pfo elf64
等...)。很简单,像这样打开radare2r2 -w -nn example.elf
并执行.pf.elf_header.entry=<address>
将解决您的问题。
不要害怕在radare2中询问如何做事。尽管它是一个非常可怕的框架,但它确实非常强大,并且具有适当的知识,可以做比起初看起来要多得多的事情。