Linux:从可执行文件更新嵌入式资源

时间:2014-10-30 09:25:33

标签: linux gcc elf

我有一个可执行文件,我使用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()

非常简单

3 个答案:

答案 0 :(得分:3)

没什么特别的,也没什么难的。我将在下面给出正确的顺序,但首先让我稍微纠正你的嵌入方法。让我们不要明确使用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,但我想让您了解事情的进展情况。

答案 1 :(得分:2)

当您想要更新二进制文件中的日期时,您只需打开一个您喜欢的文件fopen iostream即可。

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

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

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的新版本并运行链接器。

答案 2 :(得分: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,则您可能必须mprotect首先PROT_READ|PROT_WRITE所在的网页。

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

这留下了最后一个问题:如何找到正确的地方。如果 是您的问题,请参阅libelf documentation