将二进制文件作为目标文件中的一个段

本文将展示,如何将一个二进制文件(如图片、音频等)作为目标文件中的一个段,该技巧主要应用在一些无文件系统的平台。


本次的实验场景为i386:x86-64 GNU/Linux,测试音频为nhxc.wav,测试程序为bin2obj.c

查看该平台的ELF文件相关信息

生成目标文件

1
$ gcc -c bin2obj.c -o bin2obj.o

查看该平台ELF文件相关信息

1
2
3
4
5
6
7
$ objdump -x bin2obj.o

bin2obj.o: file format elf64-x86-64
bin2obj.o
architecture: i386:x86-64, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x0000000000000000

由上可知,文件格式为elf64-x86-64CPU架构为architecture

转换

首先通过objcopy --help选项查看相关参数的意义:

1
2
3
4
5
6
$ objcopy --help
-I --input-target <bfdname> Assume input file is in format <bfdname>
-O --output-target <bfdname> Create an output file in format <bfdname>
-B --binary-architecture <arch> Set output arch, when input is arch-less
......
objcopy: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 a.out-i386-linux pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 plugin srec symbolsrec verilog tekhex binary ihex

由上可知,-I选项指定输入文件的格式,-O指定输出文件的格式,在supported targets中选择对应的格式;-B是指定目标文件的架构i386:x86-64,即上文objdump -x命令查询的architecture

转换:

1
$ objcopy -I binary -O elf64-x86-64 -B i386:x86-64 nhxc.wav audio.o

查看转换后生成的目标文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ objdump -x audio.o

audio.o: file format elf64-x86-64
audio.o
architecture: i386:x86-64, flags x00000010:
HAS_SYMS
start address x0000000000000000

Sections:
Idx Name Size VMA LMA File off Algn
0 .data 000fab0 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 g .data 0000000000000000 _binary_nhxc_wav_start
00000000000fab0 g .data 0000000000000000 _binary_nhxc_wav_end
00000000000fab0 g *ABS* 0000000000000000 _binary_nhxc_wav_size

可以看到file formatarchitecture信息与bin2obj.o的相同,_binary_nhxc_wav_start指向音频内容的起始地址,_binary_nhxc_wav_end指向音频内容的结尾地址,_binary_nhxc_wav_size指向文件大小的存储地址。

_binary_*_start/end/size*是二进制文件的文件名及后缀名。

测试

bin2obj.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <elf.h>

extern _binary_nhxc_wav_start;
extern _binary_nhxc_wav_end;
extern _binary_nhxc_wav_size;

int main() {
printf("binary to object:\n");

printf("elf head: %ld\n", sizeof(Elf64_Ehdr));
printf("_binary_nhxc_wav_size: %p\n_binary_nhxc_wav_end: %p\n_binary_nhxc_wav_size: %p\n", &_binary_nhxc_wav_start, &_binary_nhxc_wav_end, &_binary_nhxc_wav_size);

unsigned char * audio_buf = (unsigned char *)&_binary_nhxc_wav_start;
unsigned long size = (unsigned long)&_binary_nhxc_wav_size;

FILE *fp = fopen("./out.wav", "wb");
if (!fp) {
fprintf(stderr, "fopen failed!\n");
return -1;
}

fwrite(audio_buf, size, 1, fp);

fclose(fp);

return 0;
}

通过_binary_nhxc_wav_start_binary_nhxc_wav_size两个符号,读取音频文件。

编译并运行:

1
2
3
4
5
6
7
8
$ gcc -c bin2obj.c -o bin2obj.o
$ g++ bin2obj.o audio.o -o bin2obj
$ ./bin2obj
binary to object:
elf head: 64
_binary_nhxc_wav_size: 0x601040
_binary_nhxc_wav_end: 0x610af0
_binary_nhxc_wav_size: 0xfab0

比对写入的文件out.wav与原始文件nhxc.wav,完全一致:

1
2
155e62d81e84fa7493fefe82223bcc2a  nhxc.wav
155e62d81e84fa7493fefe82223bcc2a out.wav

查看audio.o:

1
2
3
4
5
6
$ hexdump -C audio.o | head -n 5
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............|
00000020 00 00 00 00 00 00 00 00 d0 fb 00 00 00 00 00 00 |................|
00000030 00 00 00 00 40 00 00 00 00 00 40 00 05 00 02 00 |....@.....@.....|
00000040 52 49 46 46 a8 fa 00 00 57 41 56 45 66 6d 74 20 |RIFF....WAVEfmt |

如程序输出,ELF文件头部信息结构体为64字节,而转换生成的目标文件中,音频内容始于0x40字节偏移(wav头始于RIFF,可以参考wav文件解析),而0x40正是十进制的64

Reference

  • 《程序员的自我修养——链接、装载与库》P68

About me

forthebadge

Creative Commons License This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。