在 Radare 中编写 ELF 标头?

逆向工程 雷达2 小精灵
2021-06-27 09:23:29

阅读基思塔克拉玛干的,“介绍ELF格式:ELF头”,他修改e_entry

e_entry字段列出了程序应该开始执行的文件中的偏移量。通常它指向您的 _start 方法(当然,如果您使用通常的东西编译它)。您可以将 e_entry 指向任何您喜欢的位置,作为示例,我将展示您可以调用一个在正常执行情况下不可能的函数。

也记录在 中man 5 elf,我想知道 Radare 是否具有重写 ELF 特定标头的任何功能,或者手动编写位是否是当前的方法?例如,我知道它会用ie.

2个回答

是的,显然你可以。radare2 具有处理二进制标头的内置功能。这包括读取、解析和修改二进制文件的标头。这对于elfpe文件没有什么不同,它对两者都适用。

TL; 博士

$ ./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 中的二进制文件,看看神奇的事情发生了。


雷达2时间!

查找函数地址

根据您的要求,我们希望通过将 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哪个是我们原始的入口点函数。我们可以通过使用ieradare2命令打印入口点来验证它

[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中询问如何做事。尽管它是一个非常可怕的框架,但它确实非常强大,并且具有适当的知识,可以做比起初看起来要多得多的事情。

阅读更多

我认为根据源代码不支持此功能 编辑:我错了,抱歉。

但是,如果你真的需要一个库/工具来做到这一点,我推荐LIEF