如何修改ELF文件以改变文件部分的数据长度?

4
我正在尝试修改自己的ELF文件的可执行内容,以查看是否可能。我编写了一个程序来读取和解析ELF文件,搜索应该更新的代码,更改它,然后在更新部分头中的sh_size字段后将其写回。
但是,这并不起作用。如果我仅仅交换一些字节,就可以运行;但是如果我更改大小,它就会失败。我知道有些sh_offsets彼此直接相邻; 但是当我缩小可执行代码的大小时,这不应该事关紧要。
当然,我的程序中可能存在错误(或多个),但我已经费尽心思地检查过了。
与其寻求调试程序的帮助,我只是想知道,在减小大小时,除了sh_size字段之外,我是否需要更新其他内容以使其正常工作?是否有任何因素导致更改长度失败,而不是该字段?
编辑:
Andy Ross似乎是完全正确的。即使在这个非常简单的程序中,我也遇到了无法轻松修改以更新它将到达的偏移量的__libc_start_main中的某些间接寻址。
但我还是很好奇,什么是尝试解决这个问题的最佳途径?我知道我不能在每种情况下都解决这个问题,但是对于一些简单的程序,应该可以更新所需的内容使其运行。我应该尝试编写自己的虚拟机,还是尝试开发一个“调试器”,用INT 3替换每个疑似存在问题的指令?有任何想法吗?

这是什么类型的 ELF 文件?你是如何生成它的?它是动态链接的还是静态链接的可执行文件,或者是一些共享对象?请参阅 https://dev59.com/q2jWa4cB1Zd3GeqPtsP2#12551737。 - Basile Starynkevitch
64位LSB ELF可执行文件,x86-64架构,版本1(SYSV),动态链接(使用共享库),未剥离。 - csstudent2233
2个回答

7

这段文本很可能是使用相对偏移进行内部链接的。因此,一个函数可能试图跳转到“当前地址加194字节”的位置。如果你移动了某些东西,以使跳转目标现在变成了190字节,你显然会破坏程序。这在某些架构(例如x86-64而不是i686)的常量数据中也是如此。除了完全反汇编之外,没有简单的方法可以知道内部引用的位置,事实上,找到它们所有的方式是计算不可判定的(即尝试找出运行时计算分支的所有可能跳转目标是停机问题)。

基本上,在一般情况下这是无解的,因此,如果您有其他人的ELF二进制文件需要修补,您需要尝试其他技术。但是如果小心谨慎地操作,就可以制作一个库,其中所有内部引用都通过GOT / PLT进行,从而可以像这样将其切片并重新链接。您想要实现什么目标?


1
解决方法是:如果它缩小了,就用nops填充;如果它增长了,就用调用替换,并将真正的代码放在其他地方;希望没有人跳进修改后的代码部分。 - ninjalj
啊,当然。谢谢你的建议。实际上我只是在一个简单的“Hello World”程序上做这个;我不会想到它可能会搞乱一些初始化代码的相对偏移之类的东西。我的错误出现在:0x00000000004005f0 __libc_csu_init () => 0x00000000004005f0 <__libc_csu_init+0>: 6c ins BYTE PTR es:[rdi],dx 我不明白任何可能出现在 main 函数之前的相对偏移如何受到 main 函数大小的影响? - csstudent2233

4
你是否只更新了sh_size字段以使其工作?看起来你是在修补一个完全链接的二进制文件(ET_EXEC或ET_DYN)。请注意,静态链接完成后,.sh_size不再被用于任何事情。你可以去掉整个section table,二进制文件仍然可以正常工作。运行时最重要的是ELF中的segments而不是sections。
ELF代表可执行和链接格式,ELF具有“双重性质”,既是可执行文件也是链接形式。sections在(静态)link时间用于合并为segments;segments在执行时间(也称为runtime或dynamic linking time)使用。
当然,你没有告诉我们你缩小二进制文件的修补策略是什么,以及结果如何破坏。很可能Andy Ross的答案才是导致问题的真正原因。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接