原文出处:
http://bbs.sjtu.edu.cn/bbscon?board=Security&file=M.1134227822.A
发信人: PLL(板凳帮:帮主), 信区: Crack 标 题: 使用BFD操作ELF 发信站: 饮水思源 (2005年12月10日23:17:08 星期六), 站内信件 使用BFD操作ELF 作者:alert7 <mailto:[email protected] [email protected] > 主页: http://www.xfocus.org 时间: 2001-9-21 ★1. 前言 BFD是Binary File Descriptor的简称。我们可以使用它来方便的操作应用程序, 可以在你不了解程序文件格式的情况下,读写ELF header,program header table,section header table还有各个section等等。当然也可以是其他的BFD 支持的object文件(比如说是COFF,a.out等等)。如果想让BFD支持新的文件格式的话, 只需把一个新的BFD的后端加到它的库里。 对每一个文件格式来说,BFD都分两个部分:一个前端(front end)和一个后端(back ends)。 .BFD的前端给用户提供接口。它管理内存和规范数据结构。前端也决定了哪个后端被使 用 和什么时候后端的例程被调用。 .BFD的前端维持了它的语言形式。后端也可能是它们自己使用的信息。 ★2. 如何使用BFD 为了使用BFD,需要包括'bfd.h'并且连接的时候需要和静态库'libbfd.a'或者 动态库'libbfd.so'一起连接。 下面看一个简单的例子: [alert7@redhat62 elf]$ cat test1.c /*test1.c only for alert7 test*/ #include <stdio.h> #include <stdlib.h> #include <bfd.h> #include <strings.h> #include <linux/elf.h> #define nonfatal(s) {perror(s); return;} #define fatal(s) {perror(s); exit(-1);} #define bfd_nonfatal(s) {bfd_perror(s); return;} #define bfd_fatal(s) {bfd_perror(s); exit(-1);} main(int argc, char *argv[]) { bfd *ibfd; char *filename; char **matching; if (argc<2) exit(-1); filename = argv[1]; bfd_init(); ibfd = bfd_openr(filename, NULL); if (ibfd == NULL) { bfd_nonfatal("openr"); } if (bfd_check_format_matches(ibfd, bfd_object, &matching)) { int sections_count=bfd_count_sections(ibfd); printf("%s section count %d\n",argv[1],sections_count); } else { bfd_fatal("format_matches"); } bfd_close(ibfd); } [alert7@redhat62 elf]$ gcc -c test1.c [alert7@redhat62 elf]$ gcc -o test1 test1.o -lbfd -liberty [alert7@redhat62 elf]$ ./test1 /lib/ld-linux.so.2 /lib/ld-linux.so.2 section count 19 ★3. BFD一些基本的函数 bfd_init(void) 初始化BFD。 bfd *bfd_openr(CONST char *filename, CONST char *target); 以目标target打开filename文件。返回指向bfd的指针。target由 Calls_bfd_find_target函数来解析。 如果错误发生,返回NULL,那些错误可能是bfd_error_no_memory, bfd_error_invalid_target or system_call error. boolean bfd_set_default_target (const char *name); 设置默认的target向量,用来查找一个匹配的BFD。 boolean bfd_check_format(bfd *abfd, bfd_format format); 确定是否是BFD支持的文件格式(例如是bfd_object, bfd_archive或者bfd_core之一). 假如在该调用之前已经设置了特别的target,那么仅仅和target相关联的格式被检查。 假如target没有被设置或者被设置为默认的,所有已知的后端将被询问是否跟他们匹配 。 函数成功返回true,否则false,以下是一些错误代码: bfd_error_invalid_operation - 假如格式不是bfd_object, bfd_archive和 bfd_core之一。 bfd_error_system_call - 假如一个错误发生在读期间 - 甚至一些文件错误促发 bfd_error_system_calls. file_not_recognised - 没有后端可以认识该文件格式。 bfd_error_file_ambiguously_recognized - 超过一个的后端能认识该文件格式。 boolean bfd_check_format_matches(bfd *abfd, bfd_format format, char ***matching); 类似bfd_check_format, 除了当false返回时,bfd_errno被设置为 bfd_error_file_ambiguously_recognized.在这种情况下,假如matching不等于NULL, 它被填充为以NULL为结尾的一个已经注册了的所有的格式名称的列表。然后用户可以从 中 选择一个格式再试一次。 当不需要时候,用户应该free指向列表指针的内存。 bfd *bfd_openw(CONST char *filename, CONST char *target); 使用格式的target,为文件filename创建一个BFD。返回指向BFD的指针。 错误可能是bfd_error_system_call, bfd_error_no_memory, bfd_error_invalid_target. boolean bfd_close(bfd *abfd); Close BFD.假如以写打开的BFD,未完成的操作会被完成,然后文件才会被关闭。假如创 建文件是 可执行的,将会调用chmod使它一致。所有BFD相关的内存被释放。 所有OK的话返回true,否则false. bfd_get_symtab_upper_bound 返回所有标号需要的字节大小(也包括一个NULL指针)。假如没有标号,将返回为0。 错误返回-1。 bfd_canonicalize_symtab 从BFD中读标号。返回标号指针的个数(不包括NULL)。 boolean bfd_get_section_contents (bfd *abfd, asection *section, PTR location, file_ptr offset, bfd_size_type count); 把BFD abfd section中的数据读入内存location开始地址。数据从section的偏移量offs et 开始读,count为要读数据的字节数。 boolean bfd_set_section_contents (bfd *abfd, asection *section, PTR data, file_ptr offset, bfd_size_type count); 把内存中data地址开始的数据设置为abfd BFD的节section的内容,数据从section的偏 移量 offset开始写,count为要写的数据字节数。 void bfd_map_over_sections(bfd *abfd, void (*func)(bfd *abfd, asection *sect, PTR obj), PTR obj); 为BFD abfd相关的每个section调用私有的函数func,该函数将会被如下调用: func(abfd, the_section, obj); 该函数看起来就象执行了如下代码: section *p; for (p = abfd->sections; p != NULL; p = p->next) func(abfd, p, ...) 更多的bfd的库函数说明可以在以下找到: http://www.gnu.org/manual/bfd-2.9.1/html_node/bfd_toc.html ★4. 通过BFD读符号表中符号 /*test2.c only for alert7 test*/ #include <stdio.h> #include <stdlib.h> #include <bfd.h> #include <strings.h> #include <linux/elf.h> #define nonfatal(s) {perror(s); return;} #define fatal(s) {perror(s); exit(-1);} #define bfd_nonfatal(s) {bfd_perror(s); return;} #define bfd_fatal(s) {bfd_perror(s); exit(-1);} main(int argc, char *argv[]) { bfd *ibfd; char *filename; char **matching; if (argc<2) exit(-1); filename = argv[1]; bfd_init(); ibfd = bfd_openr(filename, NULL); if (ibfd == NULL) { bfd_nonfatal("openr"); } if (bfd_check_format_matches(ibfd, bfd_object, &matching)) { long storage_needed; asymbol **symbol_table; long number_of_symbols; long i; symbol_info symbolinfo ; storage_needed = bfd_get_symtab_upper_bound (ibfd); if (storage_needed < 0) bfd_nonfatal("bfd_get_symtab_upper_bound"); if (storage_needed == 0) { return ; } symbol_table = (asymbol **) xmalloc (storage_needed); number_of_symbols = bfd_canonicalize_symtab (ibfd, symbol_table); if (number_of_symbols < 0) bfd_nonfatal("bfd_canonicalize_symtab"); printf("Scanning %i symbols\n", number_of_symbols); for(i=0;i<number_of_symbols;i++) { if (symbol_table[i]->section==NULL) continue; printf("Section %s ",symbol_table[i]->section->name); bfd_symbol_info(symbol_table[i],&symbolinfo); printf("Symbol \"%s\" value 0x%x\n", symbolinfo.name, symbolinfo.value); } } else { bfd_fatal("format_matches"); } bfd_close(ibfd); } /* EOF */ 以上小程序打印符号的name和value还有相关的section. [alert7@redhat62 elf]$ gcc -c test2.c [alert7@redhat62 elf]$ gcc -o test2 test2.o -lbfd -liberty [alert7@redhat62 elf]$ ./test2 helo Scanning 75 symbols Section .interp Symbol "" value 0x80480f4 Section .note.ABI-tag Symbol "" value 0x8048108 Section .hash Symbol "" value 0x8048128 ...... Section *ABS* Symbol "initfini.c" value 0x0 Section .text Symbol "gcc2_compiled." value 0x804841c Section *ABS* Symbol "helo.c" value 0x0 ...... Section *ABS* Symbol "_end" value 0x8049540 Section .rodata Symbol "_IO_stdin_used" value 0x804843c Section .data Symbol "__data_start" value 0x8049448 Section *UND* Symbol "__gmon_start__" value 0x0 再看看objdump输出什么 [alert7@redhat62 elf]$ objdump -t helo helo: file format elf32-i386 SYMBOL TABLE: 080480f4 l d .interp 00000000 08048108 l d .note.ABI-tag 00000000 08048128 l d .hash 00000000 ...... 00000000 l df *ABS* 00000000 initfini.c 08048344 l .text 00000000 gcc2_compiled. 00000000 l df *ABS* 00000000 init.c ...... 08049540 g O *ABS* 00000000 _end 0804843c g O .rodata 00000004 _IO_stdin_used 08049448 g .data 00000000 __data_start 00000000 w *UND* 00000000 __gmon_start__ (输出太多了,占太多大家宝贵的硬盘空间不好,所以只保留了以上输出) 其实objdump也是使用BFD的 [alert7@redhat62 elf]$ ldd /usr/bin/objdump libopcodes-2.9.5.0.22.so => /usr/lib/libopcodes-2.9.5.0.22.so (0x4001c000) libbfd-2.9.5.0.22.so => /usr/lib/libbfd-2.9.5.0.22.so (0x40032000) libdl.so.2 => /lib/libdl.so.2 (0x40077000) libc.so.6 => /lib/libc.so.6 (0x4007b000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) ★5. 写标号 演示如何进行写操作 /*test3.c for just show wirte operation*/ #include <stdio.h> #include <bfd.h> main() { bfd *abfd; asymbol *ptrs[2]; asymbol *new; abfd = bfd_openw("foo",NULL); if (abfd==NULL) exit(0); bfd_set_format(abfd, bfd_object); new = bfd_make_empty_symbol(abfd); new->name = "dummy_symbol"; new->section = bfd_make_section_old_way(abfd, ".text"); new->flags = BSF_GLOBAL; new->value = 0x12345; ptrs[0] = new; ptrs[1] = (asymbol *)0; bfd_set_symtab(abfd, ptrs, 1); bfd_close(abfd); } [alert7@redhat62 elf]$ gcc -c test3.c [alert7@redhat62 elf]$ gcc -o test3 test3.o -lbfd -liberty [alert7@redhat62 elf]$ ./test3 [alert7@redhat62 elf]$ ls -ls foo 4 -rw-rw-r-- 1 alert7 alert7 350 Sep 21 09:56 foo [alert7@redhat62 elf]$ file foo foo: ELF 32-bit LSB relocatable, no machine, version 1, not stripped [alert7@redhat62 elf]$ nm foo 00012345 T dummy_symbol ★ 后记 本文只用来抛砖引玉,如果想用BFD写个实际有用的程序(比如实现绿盟月刊第14期上有 篇 wangdb写的《如何修改动态库符号表》上的功能),需要自己好好研究研究那些BFD的库 函数。 No pain,No gain... 不过虽然BFD功能很强大,但毕竟是别人封装过的,所以或许在自己琢磨那些库函数的时 候, 就已经头晕了,而且不灵活,很不爽。我就是这样啦:( 封装了的库函数会屏蔽底层操作的细节,追求技术的你还是不要用它吧,但这就需要一 些 ELF的知识。 参考 BFD manual http://www.gnu.org/manual/bfd-2.9.1/ -- 我们是害虫,我们是害虫。我们是团结的害虫,我们是战斗的害虫。 ※ 来源:·饮水思源 bbs.sjtu.edu.cn·[FROM: 202.120.6.36]