一段自己打印自己的C程序

今天发现了一段很有意思的C程序(http://blog.chinaunix.net/uid-233938-id-162628.html),实现的功能就是自己打印自已的内容。

这其中的道理其实是在程序链接阶段做了手脚!先看程序如下:  

test.c 

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

extern char * _binary_test_c_start;
int main()
{
        printf("%s", (char *)&_binary_test_c_start);
}

Makefile内容为:

#Makefile
test: test.obj test.c
        gcc -o test test.c test.obj
test.obj:test.c
        objcopy --input-target=binary --binary-architecture=i386 --output-target=elf64-x86-64 test.c test.obj

clean:
        @-rm test.obj test

这其中最重要的还是使用objcopy的技巧,没想到objcopy这么强大,可以把纯C文本转化成目标文件,那么文件内容就被插入最终生成的test.obj目标文件中。我们可以使用readelf看下生成的test.obj中的section:

$ readelf --section-headers --wide test.obj
There are 5 section headers, starting at offset 0x190:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .data             PROGBITS        00000000 000034 0000a9 00  WA  0   0  1
  [ 2] .symtab           SYMTAB          00000000 0000e0 000050 10      3   2  4
  [ 3] .strtab           STRTAB          00000000 000130 00003d 00      0   0  1
  [ 4] .shstrtab         STRTAB          00000000 00016d 000021 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  p (processor specific)

仔细观察section .data的Size为0xa9,这正是test.c的文件大小。我们可以dump下.data的内容:

$ readelf --hex-dump=1 test.obj

Hex dump of section '.data':
  0x00000000 23696e63 6c756465 203c7374 64696f2e #include <stdio.
  0x00000010 683e0a23 696e636c 75646520 3c737464 h>.#include <std
  0x00000020 6c69622e 683e0a20 0a657874 65726e20 lib.h>. .extern
  0x00000030 63686172 205f6269 6e617279 5f746573 char _binary_tes
  0x00000040 745f635f 73746172 743b0a69 6e74206d t_c_start;.int m
  0x00000050 61696e28 696e7420 61726763 2c206368 ain(int argc, ch
  0x00000060 6172202a 61726776 5b5d290a 7b0a0970 ar *argv[]).{..p
  0x00000070 72696e74 66282225 73222c20 28636861 rintf("%s", (cha
  0x00000080 72202a29 265f6269 6e617279 5f746573 r *)&_binary_tes
  0x00000090 745f635f 73746172 74293b0a 09726574 t_c_start);..ret
  0x000000a0 75726e20 303b0a7d 0a                urn 0;.}.

可以看到section .data中存储的正是test.c的文件内容。

查看一下test.obj的符号表:

[22:36:50@astrol:/tmp/print]$ readelf --syms --wide test.obj

Symbol table '.symtab' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 SECTION LOCAL  DEFAULT    1
     2: 00000000     0 NOTYPE  GLOBAL DEFAULT    1 _binary_test_c_start
     3: 000000a9     0 NOTYPE  GLOBAL DEFAULT    1 _binary_test_c_end
     4: 000000a9     0 NOTYPE  GLOBAL DEFAULT  ABS _binary_test_c_size

可以看到这其中的_binary_test_c_start正是test.c使用到的。

到这里可能还有童鞋问,为什么是_binary_test_c_start呢?这就得了解objcopy的-B选项了:

 -B bfdarch
       --binary-architecture=bfdarch
           Useful when transforming a architecture-less input file into an object file.  In this case the output architecture can be set to
           bfdarch.  This option will be ignored if the input file has a known bfdarch.  You can access this binary data inside a program by
           referencing the special symbols that are created by the conversion process.  These symbols are called _binary_objfile_start,
           _binary_objfile_end and _binary_objfile_size.  e.g. you can transform a picture file into an object file and then access it in your
           code using these symbols.

这下就一切都明了了吧,其中的objfile是变化的!

需要了解的一点是,符号表中Value是symbol的地址,而不是symbol所指代的变量的值。所以我们在程序中声明_binary_test_c_start为任何type都可以,只要最终通过取地址符&获取其地址就可以。

好了,本文就到这里。其实程序本身没什么价值,就是觉得好玩而已,但是我觉得熟悉这些工具才是最重要的,不是吗? 某种程度上就是增加了解决问题的途径!

参考链接:

Embedding binary data in executables

Understanding the ELF

Linux中使用C語言載入data Object 檔案資料

Linux中使用C語言載入data Object 檔案資料 (續)

Include binary file with GNU ld linker script

linking arbitrary data using GCC ARM toolchain

Not yet reviewed by OSv team. Might contain errors and inaccuracies.

How does the `--defsym` linker flag work to pass values to source code?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值