ELF生成使用libelf提示

我试图使用libelf生成一个简单的静态ELF,但我似乎有麻烦。

我不希望生成一个目标文件,然后将它链接到LD,而是我希望自己生成。

这个程序的主要目的是生成一个具有一个LOAD段的静态ELF并执行代码。

主要的问题不在于shellcode本身,而是可能在我尝试以错误的方式产生的一些头文件中。 当我尝试运行生成的ELF时,它会被杀死,就好像内核不能find它刚刚加载的段一样。

如果你们能暗示我,我会很喜欢的。

create_elf.3.c

#include <err.h> #include <fcntl.h> #include <libelf.h> #include <stdio.h> #include <stdlib.h> #include <sysexits.h> #include <unistd.h> unsigned char code[] = "\x0b\x58\x99\x52\x66\x68\x2d\x70" "\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61" "\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52" "\x51\x53\x89\xe1\xcd\x80"; int main(int argc, char *argv[]) { int fd; Elf *e; Elf_Scn *scn; Elf_Data *data; Elf32_Ehdr *ehdr; Elf32_Phdr *phdr; Elf32_Shdr *shdr; if (argc != 2) errx(EX_USAGE,"input... ./%s filename\n",argv[0]); if (elf_version(EV_CURRENT) == EV_NONE) errx(EX_SOFTWARE,"elf_version is ev_none, wtf? %s\n",elf_errmsg(-1)); if ((fd = open(argv[1], O_WRONLY | O_CREAT, 0777)) < 0) errx(EX_OSERR, "open %s\n",elf_errmsg(-1)); if ((e = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) errx(EX_SOFTWARE,"elf_begin %s\n",elf_errmsg(-1)); if ((ehdr = elf32_newehdr(e)) == NULL) errx(EX_SOFTWARE,"elf32_newehdr %s\n",elf_errmsg(-1)); /* without these definitions objdump/readelf/strace/elf loader will fail to load the binary correctly be sure to pick them carefully and correctly, preferred exactly like the ones like the system you are running on (so if you are running x86, pick the same values you seen on a regular readelf -a /bin/ls */ ehdr->e_ident[EI_DATA] = ELFDATA2LSB; ehdr->e_ident[EI_CLASS] = ELFCLASS32; ehdr->e_machine = EM_386; ehdr->e_type = ET_EXEC; ehdr->e_entry = 0x8040800; if ((phdr = elf32_newphdr(e,1)) == NULL) errx(EX_SOFTWARE,"elf32_newphdr %s\n",elf_errmsg(-1)); if ((scn = elf_newscn(e)) == NULL) errx(EX_SOFTWARE,"elf32_newscn %s\n",elf_errmsg(-1)); if ((data = elf_newdata(scn)) == NULL) errx(EX_SOFTWARE,"elf32_newdata %s\n",elf_errmsg(-1)); data->d_align = 4; data->d_off = 0LL; data->d_buf = code; data->d_type = ELF_T_WORD; // code :x data->d_size = sizeof(code); data->d_version = EV_CURRENT; if ((shdr = elf32_getshdr(scn)) == NULL) errx(EX_SOFTWARE,"elf32_getshdr %s\n",elf_errmsg(-1)); shdr->sh_name = 0; shdr->sh_type = SHT_PROGBITS; shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; shdr->sh_entsize = 0; // only used if we hold a table if (elf_update(e, ELF_C_NULL) < 0) errx(EX_SOFTWARE,"elf_update_1 %s\n",elf_errmsg(-1)); phdr->p_type = PT_LOAD; phdr->p_offset = ehdr->e_phoff; phdr->p_filesz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT); phdr->p_vaddr = 0x8040800; phdr->p_paddr = 0x8040800; phdr->p_align = 4; phdr->p_filesz = sizeof(code); phdr->p_memsz = sizeof(code); phdr->p_flags = PF_X | PF_R; elf_flagphdr(e, ELF_C_SET, ELF_F_DIRTY); if (elf_update(e, ELF_C_WRITE) < 0 ) errx(EX_SOFTWARE,"elf32_update_2 %s\n",elf_errmsg(-1)); elf_end(e); close(fd); return 1; } 

我会喜欢,如果有人能够暗示我这里有什么问题

谢谢

编辑

对不起,没有提供更多的细节,

ELF的一代似乎工作正常,我没有得到任何语法错误等,但是每当我尝试运行ELF我生成,例如./create_elf.3 foo14(和foo14是生成的ELF)

它被杀死,好像execve /内核不希望加载它我试着加载它的IDA,但IDA显示反汇编代码足够好

这里是来自readelf的输出

 readelf -a foo14 ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x8040800 Start of program headers: 52 (bytes into file) Start of section headers: 116 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 1 Size of section headers: 40 (bytes) Number of section headers: 2 Section header string table index: 0 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] <no-name> NULL 00000000 000000 000000 00 0 0 0 [ 1] <no-name> PROGBITS 00000000 000054 000020 00 AX 0 0 4 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) There are no section groups in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000034 0x08040800 0x08040800 0x00021 0x00021 RE 0x4 There is no dynamic section in this file. There are no relocations in this file. There are no unwind sections in this file. No version information found in this file. 

首先,在测试过程中用一些无害的代码替换包含(淘气)shell代码的代码片段是一个好主意,

 unsigned char code[] = { 0xBB, 0x2A, 0x00, 0x00, 0x00, /* movl $42, %ebx */ 0xB8, 0x01, 0x00, 0x00, 0x00, /* movl $1, %eax */ 0xCD, 0x80 /* int $0x80 */ }; 

在一个i386 GNU / Linux系统上,这个修改过的代码片段导致进程立即退出,退出代码为42。

以下ASCII艺术展示了正在构建的ELF可执行文件的布局:

 +----------------------------------+ <- LOADADDR (0x08048000) | The ELF Exec Header. | +----------------------------------+ | The ELF PHDR Table. | +----------------------------------+ <- ehdr->e_entry points here. | The ".text" section. | +----------------------------------+ <- The end of loadable region | The section name string table | for this object. | (optional). | +----------------------------------+ | Section headers: | | - Header for section ".text". | | - Section name string table | | header. | +----------------------------------+ 

部分名称字符串表是可选的。 它有助于更新readelf的输出。

 #define LOADADDR 0x08048000 

可执行文件将加载到由LOADADDR命名的虚拟地址。 LOADADDR的值是系统相关的 – 值为0x08048000似乎在我的系统上运行良好。

可执行代码片段放在PHDR表的后面。 ELF可执行文件头的e_entry字段保存控制将被传送到的虚拟地址。 因此,该领域的价值应该是:

 size_t ehdrsz, phdrsz; ehdrsz = elf32_fsize(ELF_T_EHDR, 1, EV_CURRENT); phdrsz = elf32_fsize(ELF_T_PHDR, 1, EV_CURRENT); /* ... */ ehdr->e_entry = LOADADDR + ehdrsz + phdrsz; 

代码段将使用数据类型ELF_T_BYTE和段类型SHT_PROGBITS ,对齐方式为1。

 if ((scn = elf_newscn(e)) == NULL) errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1)); if ((data = elf_newdata(scn)) == NULL) errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1)); data->d_align = 1; data->d_off = 0LL; data->d_buf = code; data->d_type = ELF_T_BYTE; data->d_size = sizeof(code); data->d_version = EV_CURRENT; 

节头表项的sh_addr字段保存节的数据开始的虚拟地址。

 if ((shdr = elf32_getshdr(scn)) == NULL) errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1)); shdr->sh_name = 1; /* Offset of ".text", see below. */ shdr->sh_type = SHT_PROGBITS; shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC; shdr->sh_addr = LOADADDR + ehdrsz + phdrsz; 

ELF程序头表中的唯一条目覆盖了要加载的区域,从ELF头开始并包含可执行代码。

 if ((phdr = elf32_newphdr(e,1)) == NULL) errx(EX_SOFTWARE,"elf32_newphdr %s\n", elf_errmsg(-1)); phdr->p_type = PT_LOAD; phdr->p_offset = 0; phdr->p_filesz = ehdrsz + phdrsz + sizeof(code); phdr->p_memsz = phdr->p_filesz; phdr->p_vaddr = LOADADDR; phdr->p_paddr = phdr->p_vaddr; phdr->p_align = 4; phdr->p_flags = PF_X | PF_R; 

段名字符串表是可选的,并且使得来自readelf的更好的输出成为可能 。 一个手卷字符串表格就足够了:

 unsigned char strtab[] = { 0, '.', 't', 'e', 'x', 't', 0, '.', 's', 'h', 's', 't', 'r', 't', 'a', 'b', 0 }; 

将字符串表添加到可执行文件的代码是:

 /* * Allocate a string table for section names. */ if ((scn = elf_newscn(e)) == NULL) errx(EX_SOFTWARE,"elf32_newscn %s\n", elf_errmsg(-1)); if ((data = elf_newdata(scn)) == NULL) errx(EX_SOFTWARE,"elf32_newdata %s\n", elf_errmsg(-1)); data->d_align = 1; data->d_off = 0LL; data->d_buf = strtab; data->d_type = ELF_T_BYTE; data->d_size = sizeof(strtab); data->d_version = EV_CURRENT; if ((shdr = elf32_getshdr(scn)) == NULL) errx(EX_SOFTWARE,"elf32_getshdr %s\n", elf_errmsg(-1)); shdr->sh_name = 7; /* Offset of ".shstrtab". */ shdr->sh_type = SHT_STRTAB; shdr->sh_flags = SHF_STRINGS; 

通过这些更改,您的程序创建的ELF二进制文件应该可以运行。

 % cc ac -lelf % ./a.out foo % ./foo; echo $? 42 

生成的可执行文件的结构如下所示:

 % readelf -a foo ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: Intel 80386 Version: 0x1 Entry point address: 0x8048054 Start of program headers: 52 (bytes into file) Start of section headers: 116 (bytes into file) Flags: 0x0 Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 1 Size of section headers: 40 (bytes) Number of section headers: 3 Section header string table index: 2 Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .text PROGBITS 08048054 000054 00000c 00 AX 0 0 1 [ 2] .shstrtab STRTAB 00000000 000060 000011 00 S 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) There are no section groups in this file. Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x08048000 0x08048000 0x00060 0x00060 RE 0x4 Section to Segment mapping: Segment Sections... 00 .text There is no dynamic section in this file. There are no relocations in this file. There are no unwind sections in this file. No version information found in this file. 

内核拒绝运行你的程序的原因很简单:

 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000034 0x08040800 0x08040800 0x00021 0x00021 RE 0x4 

这是无效的可执行文件,因为内核无法在虚拟地址0x08040800处将您的.text与偏移量0x34进行0x08040800 。 文件偏移量和VirtAddr必须具有相同的对齐方式。

通常情况下,第一个LOAD段只包含ELF头本身,即Offset为0(您将要设置大小为0x55== 0x21 + 0x34 ))。 或者,您可以安排Offset保持在0x000034并具有VirtAddr0x08040834