【问题标题】:Linux: update embedded resource from executableLinux:从可执行文件更新嵌入式资源
【发布时间】:2014-10-30 09:25:33
【问题描述】:

我有一个可执行文件,我使用objcopy 方法在其中嵌入了一个二进制文件资源

objcopy --input binary --output elf32-i386 --binary-architecture i386 data.txt data.o

链接到data.o 并使用

extern char _binary_data_txt_start
extern char _binary_data_txt_end

现在是否可以在可执行文件中更新这些数据?更新后的数据可以有完全相同的大小,我只需要更改一些位。

在 windows PE 文件中,使用 UpdateResource() 非常简单

【问题讨论】:

  • 谢谢你,没有发表评论的匿名投票者。
  • 您希望更新的数据是持久的,还是只想将其用作运行时存储?我现在猜是前者。上面的命令和名字好像跟this LJ article中的例子一样
  • 我看到 大量 安全问题。 SELinux 很可能会成为一个问题,而像tripwire 这样的HIDS 将达到天花板。您能否详细说明一下这个用例,以便我们找到适合您需求的解决方案,而不会惊慌使用不太友好的霰弹枪的友好家伙的数字等效项?
  • 不在运行时!我希望它在我正在创建的可执行文件中持久存在。用例是一个简单的自解压器安装程序,我想在不重新编译的情况下对其进行更新。
  • @shoosh:你确定你不想深入了解shar

标签: linux gcc elf


【解决方案1】:

没有什么特别的,也没有什么难的。我会在下面给你正确的顺序,但首先让我稍微纠正一下你的嵌入方法。让我们不要显式使用objcopy,让我们使用 GNU LD 来获取 ELF 文件中的正确条目。

让我们开始吧。这是test-emb.c 文件:

#include <stdio.h>

extern unsigned char data[] asm("_binary_data_txt_start");

int
main (void)
{
  fprintf(stderr, "%u, %u, %u\n", data[0] - '0', data[1] - '0', data[2] - '0');
  return 0;
}

这是名为data.txt的资源

12345678

这是另一个名为newdata.txt的资源

98765432

现在编译链接:

$ gcc test-emb.c -c -m32
$ gcc -o test-emb test-emb.o -Wl,--format=binary -Wl,data.txt -Wl,--format=default -m32

试试:

$ ./test-emb 
1, 2, 3

现在开始跳舞。第一步:确定数据段的逻辑地址和物理地址:

$ readelf -S test-emb | grep "\.data" | awk '{print $4}'
080496b8

$ readelf -S test-emb | grep "\.data" | awk '{print $5}'
0006b8

第二步:二进制数据的起始和大小:

$ readelf -s test-emb | grep _binary_data_txt_start | awk '{print $2}'
080496c0

$readelf -s test-emb | grep _binary_data_txt_size | awk '{print $2}'
00000009

第三步:做数学。我们确实需要:找到数据中二进制数据的偏移量,并将其转换为物理起点:

$ echo $((0x080496c0 - 0x080496b8))
8
echo $((0x0006b8 + 8))
1728

第四步:实际替换(计数值为二进制数据大小,taht为9):

cat newdata.txt | dd of=test-emb bs=1 seek=1728 count=9 conv=notrunc

现在再次检查:

$ ./test-emb 
9, 8, 7

一切正常。您可以轻松地将这个方法折叠到脚本中,而不是更难使用,即 Windows 下的 UpdateResource,但我想让您了解事情的进展情况。

【讨论】:

  • 如果newdata.txt的文件大小大于data.txt的大小,如何更新资源?我可以将数据复制到找到的位置吗?我应该手动处理搬迁吗?
  • 是的,在这种情况下你遇到了麻烦。即使在成功的情况下,您也可能会遇到代码本身的问题(比如它需要精确的资源大小),不仅是重定位,甚至可能需要汇编代码破解。如果可能的话,我会避免这样的改变。
  • 更新固定大小数据的绝佳答案,同时在大小发生变化时仍然能够进行完全重新链接。要加快 dd 命令的速度,您可以删除 bs=1 并放入 oflag=seek_bytes,count_bytes。如果您有长符号,您可能需要将 --wide 选项添加到 readelf。查找 .data(或 .rodata?)部分的另一种方法是首先找到符号条目以读取第 7 列(符号之前的最后一列):这是部分编号。还有一种方式是 objdump --wide --section=.data --section=.rodata --section-headers --syms --demangle 而nm不会显示section的地址。
  • 对我之前的评论的勘误,对于 dd,它是 iflag=count_bytes oflag=seek_bytes
【解决方案2】:

现在是否可以在可执行文件中更新这些数据?更新后的数据可以有完全相同的大小,我只需要更改一些位。

当然:去做吧:

int main()
{
    unsigned char *cp = (unsigned char*) _binary_data_txt_start
    cp[0] = 'a';    // change first byte to 0x41
    cp[42] += 3;    // increment 43rd byte by 3
}

注意:如果您的 _binary_data_txt_start 最终出现在 .rodata 中,您可能必须先 mprotectPROT_READ|PROT_WRITE 所在的页面。

注意:如果您希望更新的数据persist 以便下次执行二进制文件,那么 harper 的回答是正确的:只需使用 fopen,寻找文件中的正确位置,然后写入那里的数据。

剩下最后一个问题:如何找到正确的地方。如果是您的问题,请参阅libelfdocumentation

【讨论】:

    【解决方案3】:

    当您想更新二进制文件中的日期时,您只需使用您喜欢的方式打开文件,例如 fopen iostream 或其他任何方式。

    您还可以在可执行文件运行时修改数据。要修改进程内存中的资源,您必须确保它在可写部分中。在您的 MAP 文件中验证这一点。

    您可以使用objcopy 命令的--rename-section 参数控制该部分:

    objcopy -I binary -O elf32-i386 --rename-section .rodata=.data data.txt data.o
    

    如果您真的想在 elf 文件作为进程加载之前更改它的内容,那么您将不得不读取 elf 标头来定位资源数据。使用 --rename-section 将 data.txt 放在具有自己名称的部分中时,会更容易找到它。

    编辑:

    elf 文件格式太复杂,无法仅在 Stackoverflow 答案中描述。您可以在Wiki page 找到基本描述和必要规范的链接。

    但修改链接器输出文件最简单的方法是生成data.txt 的新版本并运行链接器。

    【讨论】:

    • 我对运行时不感兴趣,只是为了更改磁盘上的可执行文件。 objcopy能否准确告诉我elf文件中数据的偏移量和大小?
    • objectcopy 处理 .o 文件。链接后它可以告诉您的所有内容都将不再相关。
    • 那么这如何回答我的问题,显然是关于编辑链接的可执行文件?
    猜你喜欢
    • 1970-01-01
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    • 1970-01-01
    相关资源
    最近更新 更多