免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 28846 | 回复: 16
打印 上一主题 下一主题

从core映像文件中重新构造ELF可执行文件 [复制链接]

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2003-01-30 21:52 |只看该作者 |倒序浏览
/////////////////////////////////////////////////////////////////////////////////////
最近会贴一些当初学习unix病毒和elf结构时的文章,帮助大家理解elf格式,以及
利用这些知识做一些比较有趣的事情
/////////////////////////////////////////////////////////////////////////////////////

从core映像文件中重新构造ELF可执行文件
------------------------------------------------

        - Silvio Cesare  <silvio@big.net.au>;
        - December 1999
        - http://www.big.net.au/~silvio
        - http://virus.beergrave.net/
整理:e4gle<e4gle@whitecell.org>; from www.whitecell.org

目录
-----------------

2.0到2.2内核的改变
绪论
进程映像
core映像
重建可执行文件
重建失败的一些例子
实现


2.0到2.2内核的改变
------------------------------

本文主要是针对linux的2.0.x内核,但是这些代码应该也可以在2.2.x执行.2.0.x内核和2.2.x内
核的内存映像是有区别的,包括ELF的core dump的映像我想也有所改变.译者注:我尽力调试此文档
使它可以适合2.2.x内核.


绪论
------------

这篇文档实践并讲述了在给定一个core dump或者进程映像的快照文件下重新构造ELF可格式的二进制
可执行文件的技术.对于本文的读者,ELF格式的相关知识是必要的.


进程映像
-----------------

简单来说,一个core映像就是进程映像发生dump的那个时候的快照.进程映像包括了许多可加载的程序段
或虚拟内存区.这在一个ELF格式的二进制文件里涉及程序头,在linux内核里涉及到vm_area_struct
结构.一个core dump就是vm_area_struct的dump,而相应的可执行程序头和共享库用来创建进程
映像.在linux里,一组vm_area_struct为proc伪文件系统提供了内存映像.我们看一下下面这个例子,
这是一个拥了libc的典型的映像:

[e4gle@linux]# cat /proc/31189/maps
08048000-0804d000 r-xp 00000000 03:08 243714     /bin/login
0804d000-0804e000 rw-p 00004000 03:08 243714     /bin/login
0804e000-0805a000 rwxp 00000000 00:00 0
40000000-40013000 r-xp 00000000 03:08 304059     /lib/ld-2.1.3.so
40013000-40014000 rw-p 00012000 03:08 304059     /lib/ld-2.1.3.so
40014000-40016000 rw-p 00000000 00:00 0
40016000-40018000 r-xp 00000000 03:08 96347      /lib/security/pam_securetty.so
40018000-40019000 rw-p 00001000 03:08 96347      /lib/security/pam_securetty.so
40019000-4001a000 r-xp 00000000 03:08 96341      /lib/security/pam_nologin.so
4001a000-4001b000 rw-p 00000000 03:08 96341      /lib/security/pam_nologin.so
4001c000-40021000 r-xp 00000000 03:08 304068     /lib/libcrypt-2.1.3.so
40021000-40022000 rw-p 00004000 03:08 304068     /lib/libcrypt-2.1.3.so
40022000-40049000 rw-p 00000000 00:00 0
40049000-40050000 r-xp 00000000 03:08 304304     /lib/libpam.so.0.72
40050000-40051000 rw-p 00006000 03:08 304304     /lib/libpam.so.0.72
40051000-40053000 r-xp 00000000 03:08 304075     /lib/libdl-2.1.3.so
40053000-40055000 rw-p 00001000 03:08 304075     /lib/libdl-2.1.3.so
40055000-40057000 r-xp 00000000 03:08 304307     /lib/libpam_misc.so.0.72
40057000-40058000 rw-p 00001000 03:08 304307     /lib/libpam_misc.so.0.72
40058000-40059000 rw-p 00000000 00:00 0
40059000-40146000 r-xp 00000000 03:08 304066     /lib/libc-2.1.3.so
40146000-4014a000 rw-p 000ec000 03:08 304066     /lib/libc-2.1.3.so
bfff9000-c0000000 rwxp ffffa000 00:00 0


从上面可以看到,我举了一个login程序的例子,首先的两块内存区域用虚拟地址08048000-0804d000
和0804d000-0804e000分别对应了文本段和数据段.注意一下也是有权限设置的.同时内存区域仅仅
由页边界来决定.所有的core dump或映像内存区域都取决于页边界.意思是最小的内存区域就是一个
页的长度.需要注意的是由ELF格式的程序头表现的程序段是和页边界无关的,所以程序段不会在虚拟
内存区域产生映像.后面几个区域是动态链接相关的库的加载,最后一行是栈.


CORE映像
--------------

core映像就是进程dump出来的映像,具有一些额外寄存器的节和一些有用的信息.在一个ELF的core
映像里,进程映像的的内存区域相对应程序段,所以一个core文件拥有一个针对每个虚拟内存空间的
程序头列表.关于寄存器的信息存储在ELF二进制格式的notes节里.从一个core dump或者进程映像
里来重建可执行文件,我们可以忽略寄存器且把精力仅仅集中在内存区域上.


重建可执行文件
--------------------------

从一个core dump的文件里重建可执行文件我们必须从core映像中提取ELF可执行所需的文本段和
数据段对应的内存区域.当在加载代码段的时候,ELF头和程序头也同时加载进内存了(这样可以提高
效率)所以我们可以利用这些来创建可执行映像.可执行的ELF头包括一些象真实的代码段和数据段的
起始地址和大小这样的信息(记住,内存区域取决于页边界).

现在,假如我们只在我们重建的文件中用到代码段和数据段,导致的结果就使我们的可执行程序只可
以工作在被创建这个程序的系统上.因为PLT可能拥有一个共享库函数指向它的加载值.移动二进制
程序会使库函数不同的位置,或者使函数改变位置.所以,只能在重建的系统上运行,要使可以运行在
系统就必须使整个映像(栈除外)包括在重建的可执行程序里,这在下面的程序可以反应出来.


重建失败的一些例子
--------------------------

重建的一些问题,进程映像的快照是实时运行的,并不是起始时间,所以数据段的值可能会被改掉,数据
段是可写的.看看下面的代码

        static int i = 0;

        int main()
        {
                if (i++) exit(0);
                printf("Hi\n");
        }

在这个例子中,重建映像会导致程序立即退出,因为它依靠全局变量i的初始值来判定程序的流程.

实现
----------------------

其实重建可执行映像没用到很高深的理论,它只是把一个只有执行权限的可执行程序复制出来而已.
创建一个core dump不难,只需要给进程发送一个SIGSEGV信号,core映像就从进程映像中被拷贝
到了proc文件系统里了.

--

[e4gle@linux]$ cat test_harness.c
int main()
{
        for (;;) printf("Hi\n");
}
[e4gle@linux]$ gcc test_harness.c -o test_harness
[e4gle@linux]$ ./test_harness   <-验证该程序的输出(e4gle:好像杀不掉了,所以为了便于测试我采用后台运行它,再给它发送一个SIGSEGV信号)
Hi
Hi
Hi
.
.
.
[e4gle@linux]$ ./test_harness >;/dev/null &
[1] 15254
[e4gle@linux]# ps -eaf|grep test_harness
root     15254 15229 99 17:16 pts/3    00:00:19 ./test_harness
root     15256 15229  0 17:17 pts/3    00:00:00 grep test_harness
[e4gle@linux]# kill -SIGSEGV 15254       <-使它core dump

[e4gle@linux]$ gcc -o core_reconstruct core_reconstruct.c
[e4gle@linux]$ ./core_reconstruct   <-我们写的提取例程来从core中提出可执行映像
[e4gle@linux]$ ./a.out            <-测试我们提取出来的可执行文件
Hi
Hi
Hi
.
.
.

以下是提取core文件到可执行程序的例程.(e4gle:这个程序还是很容易理解的:)

  1. --------------------------------- CUT ---------------------------------------

  2. #include <stdio.h>;
  3. #include <stdlib.h>;
  4. #include <unistd.h>;
  5. #include <fcntl.h>;
  6. #include <elf.h>;
  7. #include <stdarg.h>;
  8. #include <string.h>;

  9. void die(const char *fmt, ...)
  10. {
  11.         va_list ap;

  12.         va_start(ap, fmt);
  13.         vfprintf(stderr, fmt, ap);
  14.         va_end(ap);
  15.         fputc('\n', stderr);
  16.         exit(1);
  17. }

  18. #define PAGE_SIZE        4096

  19. static char shstr[] =
  20.         "\0"
  21.         ".symtab\0"
  22.         ".strtab\0"
  23.         ".shstrtab\0"
  24.         ".interp\0"
  25.         ".hash\0"
  26.         ".dynsym\0"
  27.         ".dynstr\0"
  28.         ".rel.got\0"
  29.         ".rel.bss\0"
  30.         ".rel.plt\0"
  31.         ".init\0"
  32.         ".plt\0"
  33.         ".text\0"
  34.         ".fini\0"
  35.         ".rodata\0"
  36.         ".data\0"
  37.         ".ctors\0"
  38.         ".dtors\0"
  39.         ".got\0"
  40.         ".dynamic\0"
  41.         ".bss\0"
  42.         ".comment\0"
  43.         ".note"
  44. ;

  45. char *xget(int fd, int off, int sz)
  46. {
  47.         char *buf;

  48.         if (lseek(fd, off, SEEK_SET) < 0) die("Seek error");
  49.         buf = (char *)malloc(sz);
  50.         if (buf == NULL) die("No memory");
  51.         if (read(fd, buf, sz) != sz) die("Read error");
  52.         return buf;
  53. }


  54. void do_elf_checks(Elf32_Ehdr *ehdr)
  55. {
  56.         if (strncmp(ehdr->;e_ident, ELFMAG, SELFMAG)) die("File not ELF");
  57.         if (ehdr->;e_type != ET_CORE) die("ELF type not ET_CORE");
  58.         if (ehdr->;e_machine != EM_386 && ehdr->;e_machine != EM_486)
  59.                 die("ELF machine type not EM_386 or EM_486");
  60.         if (ehdr->;e_version != EV_CURRENT) die("ELF version not current");
  61. }

  62. int main(int argc, char *argv[])
  63. {
  64.         Elf32_Ehdr ehdr, *core_ehdr;
  65.         Elf32_Phdr *phdr, *core_phdr, *tmpphdr;
  66.         Elf32_Shdr shdr;
  67.         char *core;
  68.         char *data[2], *core_data[3];
  69.         int prog[2], core_prog[3];
  70.         int in, out;
  71.         int i, p;
  72.         int plen;

  73.         if (argc >; 2) die("usage: %s [core-file]");

  74.         if (argc == 2) core = argv[1];
  75.         else core = "core";
  76.         in = open(core, O_RDONLY);
  77.         if (in < 0) die("Coudln't open file: %s", core);

  78.         if (read(in, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) die("Read error");
  79.         do_elf_checks(&ehdr);

  80.         if (lseek(in, ehdr.e_phoff, SEEK_SET) < 0) die("Seek error");
  81.         phdr = (Elf32_Phdr *)malloc(plen = sizeof(Elf32_Phdr)*ehdr.e_phnum);
  82.         if (read(in, phdr, plen) != plen) die("Read error");

  83. for (i = 0; i < ehdr.e_phnum; i++)
  84. printf("0x%x - 0x%x (%i)\n",
  85. phdr[i].p_vaddr, phdr[i].p_vaddr + phdr[i].p_memsz, phdr[i].p_memsz);

  86. /*
  87.         copy segments (in memory)

  88.         prog/data[0] ... text
  89.         prog/data[1] ... data
  90.         prog/data[2] ... dynamic
  91. */
  92.         for (i = 0, p = 0; i < ehdr.e_phnum; i++) {
  93.                 if (
  94.                         phdr[i].p_vaddr >;= 0x8000000 &&
  95.                         phdr[i].p_type == PT_LOAD
  96.                 ) {
  97.                         prog[p] = i;
  98.                         if (p == 1) break;
  99.                         ++p;
  100.                 }
  101.         }
  102.         if (i == ehdr.e_phnum) die("Couldnt find TEXT/DATA");

  103.         for (i = 0; i < 2; i++) data[i] = xget(
  104.                 in,
  105.                 phdr[prog[i]].p_offset,
  106.                 (phdr[prog[i]].p_memsz + 4095) & 4095
  107.         );

  108.         core_ehdr = (Elf32_Ehdr *)&data[0][0];
  109.         core_phdr = (Elf32_Phdr *)&data[0][core_ehdr->;e_phoff];

  110.         for (i = 0, p = 0; i < core_ehdr->;e_phnum; i++) {
  111.                 if (core_phdr[i].p_type == PT_LOAD) {
  112.                         core_prog[p] = i;
  113.                         if (p == 0) {
  114.                                 core_data[0] = &data[0][0];
  115.                         } else {
  116.                                 core_data[1] = &data[1][
  117.                                         (core_phdr[i].p_vaddr & 4095)
  118.                                 ];
  119.                                 break;
  120.                         }
  121.                         ++p;
  122.                 }
  123.         }
  124.         if (i == core_ehdr->;e_phnum) die("No TEXT and DATA segment");
  125.         for (i = 0; i < core_ehdr->;e_phnum; i++) {
  126.                 if (core_phdr[i].p_type == PT_DYNAMIC) {
  127.                         core_prog[2] = i;
  128.                         core_data[2] = &data[1][64];
  129.                         break;
  130.                 }
  131.         }
  132.         if (i == core_ehdr->;e_phnum) die("No DYNAMIC segment");

  133.         out = open("a.out", O_WRONLY | O_CREAT | O_TRUNC);
  134.         if (out < 0) die("Coudln't open file: %s", "a.out");

  135.         core_ehdr->;e_shoff =
  136.                 core_phdr[core_prog[2]].p_offset +
  137.                 core_phdr[core_prog[2]].p_filesz +
  138.                 sizeof(shstr);

  139. /*
  140.         text
  141.         data
  142.         bss
  143.         dynamic
  144.         shstrtab
  145. */
  146.         core_ehdr->;e_shnum = 6;
  147.         core_ehdr->;e_shstrndx = 5;

  148.         for (i = 0; i < 2; i++) {
  149.                 Elf32_Phdr *p = &core_phdr[core_prog[i]];
  150.                 int sz = p->;p_filesz;

  151.                 if (lseek(out, p->;p_offset, SEEK_SET) < 0) goto cleanup;
  152.                 if (write(out, core_data[i], sz) != sz) goto cleanup;
  153.         }

  154.         if (write(out, shstr, sizeof(shstr)) != sizeof(shstr)) goto cleanup;

  155.         memset(&shdr, 0, sizeof(shdr));
  156.         if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;

  157. /*
  158.         text section
  159. */
  160.         tmpphdr = &core_phdr[core_prog[0]];

  161.         shdr.sh_name = 95;
  162.         shdr.sh_type = SHT_PROGBITS;
  163.         shdr.sh_addr = tmpphdr->;p_vaddr;
  164.         shdr.sh_offset = 0;
  165.         shdr.sh_size = tmpphdr->;p_filesz;
  166.         shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR;
  167.         shdr.sh_link = 0;
  168.         shdr.sh_info = 0;
  169.         shdr.sh_addralign = 16;
  170.         shdr.sh_entsize = 0;

  171.         if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;

  172. /*
  173.         data section
  174. */
  175.         tmpphdr = &core_phdr[core_prog[1]];

  176.         shdr.sh_name = 115;
  177.         shdr.sh_type = SHT_PROGBITS;
  178.         shdr.sh_addr = tmpphdr->;p_vaddr;
  179.         shdr.sh_offset = tmpphdr->;p_offset;
  180.         shdr.sh_size = tmpphdr->;p_filesz;
  181.         shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
  182.         shdr.sh_link = 0;
  183.         shdr.sh_info = 0;
  184.         shdr.sh_addralign = 4;
  185.         shdr.sh_entsize = 0;

  186.         if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;

  187. /*
  188.         dynamic section
  189. */
  190.         for (i = 0; i < core_ehdr->;e_phnum; i++) {
  191.                 if (core_phdr[i].p_type == PT_DYNAMIC) {
  192.                         tmpphdr = &core_phdr[i];
  193.                         break;
  194.                 }
  195.         }

  196.         shdr.sh_name = 140;
  197.         shdr.sh_type = SHT_PROGBITS;
  198.         shdr.sh_addr = tmpphdr->;p_vaddr;
  199.         shdr.sh_offset = tmpphdr->;p_offset;
  200.         shdr.sh_size = tmpphdr->;p_memsz;
  201.         shdr.sh_flags = SHF_ALLOC;
  202.         shdr.sh_link = 0;
  203.         shdr.sh_info = 0;
  204.         shdr.sh_addralign = 4;
  205.         shdr.sh_entsize = 8;

  206.         if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;

  207. /*
  208.         bss section
  209. */
  210.         shdr.sh_name = 149;
  211.         shdr.sh_type = SHT_PROGBITS;
  212.         shdr.sh_addr = tmpphdr->;p_vaddr + tmpphdr->;p_filesz;
  213.         shdr.sh_offset = tmpphdr->;p_offset + tmpphdr->;p_filesz;
  214.         shdr.sh_size = tmpphdr->;p_memsz - tmpphdr->;p_filesz;
  215.         shdr.sh_flags = SHF_ALLOC;
  216.         shdr.sh_link = 0;
  217.         shdr.sh_info = 0;
  218.         shdr.sh_addralign = 1;
  219.         shdr.sh_entsize = 0;

  220.         if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;

  221. /*
  222.         shstrtab
  223. */

  224.         shdr.sh_name = 17;
  225.         shdr.sh_type = SHT_STRTAB;
  226.         shdr.sh_addr = 0;
  227.         shdr.sh_offset = core_ehdr->;e_shoff - sizeof(shstr);
  228.         shdr.sh_size = sizeof(shstr);
  229.         shdr.sh_flags = 0;
  230.         shdr.sh_link = 0;
  231.         shdr.sh_info = 0;
  232.         shdr.sh_addralign = 1;
  233.         shdr.sh_entsize = 0;

  234.         if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;

  235.         return 0;

  236. cleanup:
  237.         unlink("a.out");
  238.         die("Error writing file: %s", "a.out");
  239.         return 1; /* not reached */
  240. }
复制代码

论坛徽章:
0
2 [报告]
发表于 2003-02-02 02:50 |只看该作者

从core映像文件中重新构造ELF可执行文件

酷!解密用!

能不能给讲讲进程如何调用so?

论坛徽章:
0
3 [报告]
发表于 2003-02-14 09:20 |只看该作者

从core映像文件中重新构造ELF可执行文件

hakgjianga

论坛徽章:
0
4 [报告]
发表于 2003-06-24 21:05 |只看该作者

从core映像文件中重新构造ELF可执行文件


原理应该是把CORE中的数据段与命令段重新组合成ELF文件

论坛徽章:
0
5 [报告]
发表于 2003-11-24 14:53 |只看该作者

从core映像文件中重新构造ELF可执行文件

大侠,怎样通过core文件找到程序出错的地方

论坛徽章:
0
6 [报告]
发表于 2005-05-16 01:23 |只看该作者

从core映像文件中重新构造ELF可执行文件

debian kernel 2.4.27最后生成的a.out不能执行,出现段错误。

论坛徽章:
0
7 [报告]
发表于 2007-12-05 14:46 |只看该作者
顶,太好的帖子了,谢谢LZ。

论坛徽章:
0
8 [报告]
发表于 2007-12-05 14:47 |只看该作者
ls挖坟

论坛徽章:
0
9 [报告]
发表于 2007-12-06 11:08 |只看该作者
太好了,一直没理解ELF文件呢,正好学习,感谢楼主

论坛徽章:
0
10 [报告]
发表于 2010-08-17 11:13 |只看该作者
问一个沉积了好几年的帖子。
上面的例子,在2.6内核下运行生成的./a.out,显示段错误。哪位大侠说下应该怎么改一下!感谢
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP