使文本段可写,ELF

11
我需要让一个可执行ELF文件的.text段可写。我需要修改的程序是用C语言编写的,我可以编译它。有什么建议吗?
非常感谢。

不够清晰。.text通常是代码。在你的软件/固件中放置在哪里? - LPs
也许这是一个重复的问题,但在其他问题中,他们分享的解决方案并不起作用。 - Cronovirus
1
是的,我需要在运行时修改代码,因此文本段必须是可写的。 - Cronovirus
3
我不确定为什么这篇文章会吸引关闭票。原作者已经非常清楚他想要做什么——编译和链接一个C程序,使得产生的ELF二进制文件具有可写的文本段。也许他想要编写自修改代码。但是谁关心为什么,这是一个合理的问题。 - abligh
1个回答

13

下面的答案中,我将使用以下测试程序:

#include <stdio.h>
#include <stdlib.h>

int
main (int argc, char **argv)
{
  printf ("Hello world\n");
  void *m = main;
  *((char *) m) = 0;
  exit (0);
}

编译方式:

$ gcc -g -o test test.c

如预期:

$ gdb test
...
(gdb) run
Starting program: /home/amb/so/test
Hello world

Program received signal SIGSEGV, Segmentation fault.
0x00000000004005a2 in main (argc=1, argv=0x7fffffffe628) at test.c:9
9       *((char *)m) = 0;
(gdb)

这里的明显路径是使用-Wl标志将-N或(又名--omagic)传递给链接器,即gcc ... -Wl,--omagic ...,但这可能会产生其他不良后果(例如禁用共享库)。从man页面中可以看到:

   -N
   --omagic
       Set the text and data sections to be readable and writable.  Also, do not page-align the
       data segment, and disable linking against shared libraries.  If the output format
       supports Unix style magic numbers, mark the output as "OMAGIC". Note: Although a
       writable text section is allowed for PE-COFF targets, it does not conform to the format
       specification published by Microsoft.

让我们试一试:

$ gcc --static -g -Wl,--omagic -o test test.c
$ ./test
Hello world
$

虽然这样可以正常工作,但你失去了动态库支持。

为了保留动态库支持并保持可写的文本段,你应该尝试使用以下命令:

objcopy --writable-text ...

来自手册页面:

   --writable-text
       Mark the output text as writable.  This option isn't meaningful for all object file
       formats.

这应该可以工作,但实际上并没有,如objdump将验证。因此,这里提供了一种比--writable-text更进一步的解决方案,正如OP在评论中所述,它似乎并没有像说明书上说的那样运行。

让我们看看这些部分是如何标记的:

$ gcc -g -o test test.
$ objdump -h test | fgrep -A1 .text
  12 .text         00000192  0000000000400490  0000000000400490  00000490  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE

现在让我们去掉那个“READONLY”标志:
$ objcopy --set-section-flags .text=contents,alloc,load,code test test1
$ objdump -h test1 | fgrep -A1 .text
 12 .text         00000192  0000000000400490  0000000000400490  00000490  2**4
                  CONTENTS, ALLOC, LOAD, CODE

现在,如您要求的那样,READONLY已经消失了。

但是:

 $ gdb test1
 ...
(gdb) run
Starting program: /home/amb/so/test1
Hello world

Program received signal SIGSEGV, Segmentation fault.
0x00000000004005a2 in main (argc=1, argv=0x7fffffffe628) at test.c:9
9       *((char *)m) = 0;
(gdb)

我猜测这里的问题是,除了ELF部分名称之外,其他东西在实际加载时使该部分变为只读。这可能是为什么人们建议您使用mprotect的原因。很抱歉没有更多帮助。


@Cronovirus - 嗯,可靠的方法(显然在链接时不可用)是使用mprotect更改其内存保护。如果您无法更改源代码,则可以尝试使用LD_PRELOAD来实现。 - abligh
@Cronovirus - 我已经更新了答案,展示了如何使用--set-section-flag将代码段设置为可写;这在ELF中是可写的,但我怀疑它实际上并没有做到你需要的。 - abligh
1
真正的解决方案:使用hexedit打开二进制文件,查看文件偏移量0x1C(通常为0x34)处的值,然后遍历0x20字节结构(大小在文件偏移量0x2a处列出),直到找到您在先前转储中确定包含.text部分的结构。倒数第二个长整型值将是00000005(05 00 00 00),需要添加写入,从而变为00000007(07 00 00 00)。现在它将按预期工作,没有任何限制,例如-Wl,--omagic与共享库问题。有点技术性,但只需几秒钟即可完成。 - Gregory Morse
@abligh...非常感谢您的帮助和知识,尽管我正在Android用户空间进行实验,但当我写入非只读文本部分时仍然出现段错误...唉... - sol
@GregoryMorse ... 它有效了,谢谢。非常酷...很好找到一些方法在编译/汇编时完成此操作,而无需使用omagic等...或如何使objcopy正确执行它...或某些链接器选项...正在寻找。 - sol
显示剩余7条评论

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