- 論壇徽章:
- 1
|
/////////////////////////////////////////////////////////////////////////////////////
最近會(huì)貼一些當(dāng)初學(xué)習(xí)unix病毒和elf結(jié)構(gòu)時(shí)的文章,幫助大家理解elf格式,以及
利用這些知識(shí)做一些比較有趣的事情
/////////////////////////////////////////////////////////////////////////////////////
從core映像文件中重新構(gòu)造ELF可執(zhí)行文件
------------------------------------------------
- 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內(nèi)核的改變
緒論
進(jìn)程映像
core映像
重建可執(zhí)行文件
重建失敗的一些例子
實(shí)現(xiàn)
2.0到2.2內(nèi)核的改變
------------------------------
本文主要是針對(duì)linux的2.0.x內(nèi)核,但是這些代碼應(yīng)該也可以在2.2.x執(zhí)行.2.0.x內(nèi)核和2.2.x內(nèi)
核的內(nèi)存映像是有區(qū)別的,包括ELF的core dump的映像我想也有所改變.譯者注:我盡力調(diào)試此文檔
使它可以適合2.2.x內(nèi)核.
緒論
------------
這篇文檔實(shí)踐并講述了在給定一個(gè)core dump或者進(jìn)程映像的快照文件下重新構(gòu)造ELF可格式的二進(jìn)制
可執(zhí)行文件的技術(shù).對(duì)于本文的讀者,ELF格式的相關(guān)知識(shí)是必要的.
進(jìn)程映像
-----------------
簡(jiǎn)單來說,一個(gè)core映像就是進(jìn)程映像發(fā)生dump的那個(gè)時(shí)候的快照.進(jìn)程映像包括了許多可加載的程序段
或虛擬內(nèi)存區(qū).這在一個(gè)ELF格式的二進(jìn)制文件里涉及程序頭,在linux內(nèi)核里涉及到vm_area_struct
結(jié)構(gòu).一個(gè)core dump就是vm_area_struct的dump,而相應(yīng)的可執(zhí)行程序頭和共享庫用來創(chuàng)建進(jìn)程
映像.在linux里,一組vm_area_struct為proc偽文件系統(tǒng)提供了內(nèi)存映像.我們看一下下面這個(gè)例子,
這是一個(gè)擁了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
從上面可以看到,我舉了一個(gè)login程序的例子,首先的兩塊內(nèi)存區(qū)域用虛擬地址08048000-0804d000
和0804d000-0804e000分別對(duì)應(yīng)了文本段和數(shù)據(jù)段.注意一下也是有權(quán)限設(shè)置的.同時(shí)內(nèi)存區(qū)域僅僅
由頁邊界來決定.所有的core dump或映像內(nèi)存區(qū)域都取決于頁邊界.意思是最小的內(nèi)存區(qū)域就是一個(gè)
頁的長(zhǎng)度.需要注意的是由ELF格式的程序頭表現(xiàn)的程序段是和頁邊界無關(guān)的,所以程序段不會(huì)在虛擬
內(nèi)存區(qū)域產(chǎn)生映像.后面幾個(gè)區(qū)域是動(dòng)態(tài)鏈接相關(guān)的庫的加載,最后一行是棧.
CORE映像
--------------
core映像就是進(jìn)程dump出來的映像,具有一些額外寄存器的節(jié)和一些有用的信息.在一個(gè)ELF的core
映像里,進(jìn)程映像的的內(nèi)存區(qū)域相對(duì)應(yīng)程序段,所以一個(gè)core文件擁有一個(gè)針對(duì)每個(gè)虛擬內(nèi)存空間的
程序頭列表.關(guān)于寄存器的信息存儲(chǔ)在ELF二進(jìn)制格式的notes節(jié)里.從一個(gè)core dump或者進(jìn)程映像
里來重建可執(zhí)行文件,我們可以忽略寄存器且把精力僅僅集中在內(nèi)存區(qū)域上.
重建可執(zhí)行文件
--------------------------
從一個(gè)core dump的文件里重建可執(zhí)行文件我們必須從core映像中提取ELF可執(zhí)行所需的文本段和
數(shù)據(jù)段對(duì)應(yīng)的內(nèi)存區(qū)域.當(dāng)在加載代碼段的時(shí)候,ELF頭和程序頭也同時(shí)加載進(jìn)內(nèi)存了(這樣可以提高
效率)所以我們可以利用這些來創(chuàng)建可執(zhí)行映像.可執(zhí)行的ELF頭包括一些象真實(shí)的代碼段和數(shù)據(jù)段的
起始地址和大小這樣的信息(記住,內(nèi)存區(qū)域取決于頁邊界).
現(xiàn)在,假如我們只在我們重建的文件中用到代碼段和數(shù)據(jù)段,導(dǎo)致的結(jié)果就使我們的可執(zhí)行程序只可
以工作在被創(chuàng)建這個(gè)程序的系統(tǒng)上.因?yàn)镻LT可能擁有一個(gè)共享庫函數(shù)指向它的加載值.移動(dòng)二進(jìn)制
程序會(huì)使庫函數(shù)不同的位置,或者使函數(shù)改變位置.所以,只能在重建的系統(tǒng)上運(yùn)行,要使可以運(yùn)行在
系統(tǒng)就必須使整個(gè)映像(棧除外)包括在重建的可執(zhí)行程序里,這在下面的程序可以反應(yīng)出來.
重建失敗的一些例子
--------------------------
重建的一些問題,進(jìn)程映像的快照是實(shí)時(shí)運(yùn)行的,并不是起始時(shí)間,所以數(shù)據(jù)段的值可能會(huì)被改掉,數(shù)據(jù)
段是可寫的.看看下面的代碼
static int i = 0;
int main()
{
if (i++) exit(0);
printf("Hi\n");
}
在這個(gè)例子中,重建映像會(huì)導(dǎo)致程序立即退出,因?yàn)樗揽咳肿兞縤的初始值來判定程序的流程.
實(shí)現(xiàn)
----------------------
其實(shí)重建可執(zhí)行映像沒用到很高深的理論,它只是把一個(gè)只有執(zhí)行權(quán)限的可執(zhí)行程序復(fù)制出來而已.
創(chuàng)建一個(gè)core dump不難,只需要給進(jìn)程發(fā)送一個(gè)SIGSEGV信號(hào),core映像就從進(jìn)程映像中被拷貝
到了proc文件系統(tǒng)里了.
--
[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 <-驗(yàn)證該程序的輸出(e4gle:好像殺不掉了,所以為了便于測(cè)試我采用后臺(tái)運(yùn)行它,再給它發(fā)送一個(gè)SIGSEGV信號(hào))
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 <-使它c(diǎn)ore dump
[e4gle@linux]$ gcc -o core_reconstruct core_reconstruct.c
[e4gle@linux]$ ./core_reconstruct <-我們寫的提取例程來從core中提出可執(zhí)行映像
[e4gle@linux]$ ./a.out <-測(cè)試我們提取出來的可執(zhí)行文件
Hi
Hi
Hi
.
.
.
以下是提取core文件到可執(zhí)行程序的例程.(e4gle:這個(gè)程序還是很容易理解的:)
- --------------------------------- CUT ---------------------------------------
- #include <stdio.h>;
- #include <stdlib.h>;
- #include <unistd.h>;
- #include <fcntl.h>;
- #include <elf.h>;
- #include <stdarg.h>;
- #include <string.h>;
- void die(const char *fmt, ...)
- {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- va_end(ap);
- fputc('\n', stderr);
- exit(1);
- }
- #define PAGE_SIZE 4096
- static char shstr[] =
- "\0"
- ".symtab\0"
- ".strtab\0"
- ".shstrtab\0"
- ".interp\0"
- ".hash\0"
- ".dynsym\0"
- ".dynstr\0"
- ".rel.got\0"
- ".rel.bss\0"
- ".rel.plt\0"
- ".init\0"
- ".plt\0"
- ".text\0"
- ".fini\0"
- ".rodata\0"
- ".data\0"
- ".ctors\0"
- ".dtors\0"
- ".got\0"
- ".dynamic\0"
- ".bss\0"
- ".comment\0"
- ".note"
- ;
- char *xget(int fd, int off, int sz)
- {
- char *buf;
- if (lseek(fd, off, SEEK_SET) < 0) die("Seek error");
- buf = (char *)malloc(sz);
- if (buf == NULL) die("No memory");
- if (read(fd, buf, sz) != sz) die("Read error");
- return buf;
- }
- void do_elf_checks(Elf32_Ehdr *ehdr)
- {
- if (strncmp(ehdr->;e_ident, ELFMAG, SELFMAG)) die("File not ELF");
- if (ehdr->;e_type != ET_CORE) die("ELF type not ET_CORE");
- if (ehdr->;e_machine != EM_386 && ehdr->;e_machine != EM_486)
- die("ELF machine type not EM_386 or EM_486");
- if (ehdr->;e_version != EV_CURRENT) die("ELF version not current");
- }
- int main(int argc, char *argv[])
- {
- Elf32_Ehdr ehdr, *core_ehdr;
- Elf32_Phdr *phdr, *core_phdr, *tmpphdr;
- Elf32_Shdr shdr;
- char *core;
- char *data[2], *core_data[3];
- int prog[2], core_prog[3];
- int in, out;
- int i, p;
- int plen;
- if (argc >; 2) die("usage: %s [core-file]");
- if (argc == 2) core = argv[1];
- else core = "core";
- in = open(core, O_RDONLY);
- if (in < 0) die("Coudln't open file: %s", core);
- if (read(in, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) die("Read error");
- do_elf_checks(&ehdr);
- if (lseek(in, ehdr.e_phoff, SEEK_SET) < 0) die("Seek error");
- phdr = (Elf32_Phdr *)malloc(plen = sizeof(Elf32_Phdr)*ehdr.e_phnum);
- if (read(in, phdr, plen) != plen) die("Read error");
- for (i = 0; i < ehdr.e_phnum; i++)
- printf("0x%x - 0x%x (%i)\n",
- phdr[i].p_vaddr, phdr[i].p_vaddr + phdr[i].p_memsz, phdr[i].p_memsz);
- /*
- copy segments (in memory)
- prog/data[0] ... text
- prog/data[1] ... data
- prog/data[2] ... dynamic
- */
- for (i = 0, p = 0; i < ehdr.e_phnum; i++) {
- if (
- phdr[i].p_vaddr >;= 0x8000000 &&
- phdr[i].p_type == PT_LOAD
- ) {
- prog[p] = i;
- if (p == 1) break;
- ++p;
- }
- }
- if (i == ehdr.e_phnum) die("Couldnt find TEXT/DATA");
- for (i = 0; i < 2; i++) data[i] = xget(
- in,
- phdr[prog[i]].p_offset,
- (phdr[prog[i]].p_memsz + 4095) & 4095
- );
- core_ehdr = (Elf32_Ehdr *)&data[0][0];
- core_phdr = (Elf32_Phdr *)&data[0][core_ehdr->;e_phoff];
- for (i = 0, p = 0; i < core_ehdr->;e_phnum; i++) {
- if (core_phdr[i].p_type == PT_LOAD) {
- core_prog[p] = i;
- if (p == 0) {
- core_data[0] = &data[0][0];
- } else {
- core_data[1] = &data[1][
- (core_phdr[i].p_vaddr & 4095)
- ];
- break;
- }
- ++p;
- }
- }
- if (i == core_ehdr->;e_phnum) die("No TEXT and DATA segment");
- for (i = 0; i < core_ehdr->;e_phnum; i++) {
- if (core_phdr[i].p_type == PT_DYNAMIC) {
- core_prog[2] = i;
- core_data[2] = &data[1][64];
- break;
- }
- }
- if (i == core_ehdr->;e_phnum) die("No DYNAMIC segment");
- out = open("a.out", O_WRONLY | O_CREAT | O_TRUNC);
- if (out < 0) die("Coudln't open file: %s", "a.out");
- core_ehdr->;e_shoff =
- core_phdr[core_prog[2]].p_offset +
- core_phdr[core_prog[2]].p_filesz +
- sizeof(shstr);
- /*
- text
- data
- bss
- dynamic
- shstrtab
- */
- core_ehdr->;e_shnum = 6;
- core_ehdr->;e_shstrndx = 5;
- for (i = 0; i < 2; i++) {
- Elf32_Phdr *p = &core_phdr[core_prog[i]];
- int sz = p->;p_filesz;
- if (lseek(out, p->;p_offset, SEEK_SET) < 0) goto cleanup;
- if (write(out, core_data[i], sz) != sz) goto cleanup;
- }
- if (write(out, shstr, sizeof(shstr)) != sizeof(shstr)) goto cleanup;
- memset(&shdr, 0, sizeof(shdr));
- if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;
- /*
- text section
- */
- tmpphdr = &core_phdr[core_prog[0]];
- shdr.sh_name = 95;
- shdr.sh_type = SHT_PROGBITS;
- shdr.sh_addr = tmpphdr->;p_vaddr;
- shdr.sh_offset = 0;
- shdr.sh_size = tmpphdr->;p_filesz;
- shdr.sh_flags = SHF_ALLOC | SHF_EXECINSTR;
- shdr.sh_link = 0;
- shdr.sh_info = 0;
- shdr.sh_addralign = 16;
- shdr.sh_entsize = 0;
- if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;
- /*
- data section
- */
- tmpphdr = &core_phdr[core_prog[1]];
- shdr.sh_name = 115;
- shdr.sh_type = SHT_PROGBITS;
- shdr.sh_addr = tmpphdr->;p_vaddr;
- shdr.sh_offset = tmpphdr->;p_offset;
- shdr.sh_size = tmpphdr->;p_filesz;
- shdr.sh_flags = SHF_ALLOC | SHF_WRITE;
- shdr.sh_link = 0;
- shdr.sh_info = 0;
- shdr.sh_addralign = 4;
- shdr.sh_entsize = 0;
- if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;
- /*
- dynamic section
- */
- for (i = 0; i < core_ehdr->;e_phnum; i++) {
- if (core_phdr[i].p_type == PT_DYNAMIC) {
- tmpphdr = &core_phdr[i];
- break;
- }
- }
- shdr.sh_name = 140;
- shdr.sh_type = SHT_PROGBITS;
- shdr.sh_addr = tmpphdr->;p_vaddr;
- shdr.sh_offset = tmpphdr->;p_offset;
- shdr.sh_size = tmpphdr->;p_memsz;
- shdr.sh_flags = SHF_ALLOC;
- shdr.sh_link = 0;
- shdr.sh_info = 0;
- shdr.sh_addralign = 4;
- shdr.sh_entsize = 8;
- if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;
- /*
- bss section
- */
- shdr.sh_name = 149;
- shdr.sh_type = SHT_PROGBITS;
- shdr.sh_addr = tmpphdr->;p_vaddr + tmpphdr->;p_filesz;
- shdr.sh_offset = tmpphdr->;p_offset + tmpphdr->;p_filesz;
- shdr.sh_size = tmpphdr->;p_memsz - tmpphdr->;p_filesz;
- shdr.sh_flags = SHF_ALLOC;
- shdr.sh_link = 0;
- shdr.sh_info = 0;
- shdr.sh_addralign = 1;
- shdr.sh_entsize = 0;
- if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;
- /*
- shstrtab
- */
- shdr.sh_name = 17;
- shdr.sh_type = SHT_STRTAB;
- shdr.sh_addr = 0;
- shdr.sh_offset = core_ehdr->;e_shoff - sizeof(shstr);
- shdr.sh_size = sizeof(shstr);
- shdr.sh_flags = 0;
- shdr.sh_link = 0;
- shdr.sh_info = 0;
- shdr.sh_addralign = 1;
- shdr.sh_entsize = 0;
- if (write(out, &shdr, sizeof(shdr)) != sizeof(shdr)) goto cleanup;
- return 0;
- cleanup:
- unlink("a.out");
- die("Error writing file: %s", "a.out");
- return 1; /* not reached */
- }
復(fù)制代碼 |
|