linux 病毒virus解毒
阿新 • • 發佈:2018-07-04
計算 des rcm 字符串 ava ati esp tao c++ disinfect.c
/* * = 編譯: * gcc -O2 disinfect.c -o disinfect * ./disinfect <executable> */ #include <stdarg.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <fcntl.h> #include <dirent.h> #include <sys/mman.h> #include <sys/stat.h> #include <elf.h> #include <errno.h> //elf 相關信息 typedef struct elfdesc { Elf64_Ehdr *ehdr; Elf64_Phdr *phdr; Elf64_Shdr *shdr; Elf64_Addr textVaddr; Elf64_Addr dataVaddr; //程序頭偏移 Elf64_Addr dataOff; size_t textSize; size_t dataSize; uint8_t *mem; struct stat st; char *path; } elfdesc_t; //緩沖區 #define TMP ".disinfect_file.xyz" //如果找到了push/ret 同時地址在正常x86_64範圍內 //說明正常 //判斷是否在正常範圍內 //770CD526 68 00000000 PUSH 0x0 //770CD52B C3 RETN uint32_t locate_orig_entry(elfdesc_t *elf) { uint32_t i, entry; uint8_t *mem = elf->mem; for (i = 0; i < elf->st.st_size; i++) { if (mem[0] == 0x68 && mem[5] == 0xc3) { entry = *(uint32_t *)&mem[1]; if (entry >= 0x400000 && entry < 0x4fffff) return entry; } } //沒有找到 return 0; } //770CD53A |. 31ED XOR EBP,EBP //770CD53C |. 49 DEC ECX //770CD53D |. 89D1 MOV ECX,EDX //770CD53F |. 5E POP ESI //770CD540 |. 48 DEC EAX //770CD541 |. 89E2 MOV EDX,ESP uint32_t locate_glibc_init_offset(elfdesc_t *elf) { uint32_t i; uint8_t *mem = elf->mem; for (i = 0; i < elf->st.st_size; i++) { if ( mem[i + 0] == 0x31 && mem[i + 1] == 0xed && mem[i + 2] == 0x49 && mem[i + 3] == 0x89 && mem[i + 4] == 0xd1 && mem[i + 5] == 0x5e && mem[i + 6] == 0x48 && mem[i + 7] == 0x89 && mem[i + 8] == 0xe2) return i; } return 0; } //移除 PLT/GOT hooks int disinfect_pltgot(elfdesc_t *elf) { //文件頭 Elf64_Ehdr *ehdr = elf->ehdr; //程序頭 Elf64_Phdr *phdr = elf->phdr; //節表頭 Elf64_Shdr *shdr = elf->shdr; //映射基址 uint8_t *mem = elf->mem; //動態庫符號表基地 Elf64_Sym *symtab = NULL; //重定位表 Elf64_Rela *rela = NULL; Elf64_Addr addr = 0, plt_addr = 0; Elf64_Off plt_off = 0, gotoff = 0; size_t plt_size = 0, symtab_size = 0, rela_size = 0; //字符串表基址 char *shstrtab = (char *)&mem[shdr[elf->ehdr->e_shstrndx].sh_offset]; char *strtab = NULL; uint8_t *gotptr, *plt; int i, j, symindex = 0, c = 0; //遍歷所有的節表 for (i = 0; i < ehdr->e_shnum; i++) { //類型 switch(shdr[i].sh_type) { //動態庫中符號 case SHT_DYNSYM: //符號表 symtab = (Elf64_Sym *)&mem[shdr[i].sh_offset]; symtab_size = shdr[i].sh_size; //對於符號表段sh_link記錄的是符號表使用的串表所在段(一般是.strtab)對應段表項在段表內的索引 //.strtab偏移 strtab = (char *)&mem[shdr[shdr[i].sh_link].sh_offset]; break; //重定位所使用的節的節表索引 case SHT_RELA: if (!strcmp(&shstrtab[shdr[i].sh_name], ".rela.plt")) { //重定位表 rela = (Elf64_Rela *)&mem[shdr[i].sh_offset]; //大小 rela_size = shdr[i].sh_size; } break; //程序數據 case SHT_PROGBITS: if (!strcmp(&shstrtab[shdr[i].sh_name], ".plt")) { //plt相關 plt_off = shdr[i].sh_offset; plt_addr = shdr[i].sh_addr; plt_size = shdr[i].sh_size; } break; } } if (plt_off == 0 || symtab == NULL || rela == NULL) { printf("沒有找到relocation/symbol/plt info!!!\n"); return -1; } //第一個PLT地址 plt = &mem[plt_off]; //遍歷所有重定位表條目 for (i = 0; i < rela_size/sizeof(Elf64_Rela); i++) { //高24 位表示重定位符號對應符號表項在符號表內有索引 symindex = ELF64_R_SYM(rela->r_info); //在symtab中找到puts函數符號 if (!strcmp(&strtab[symtab[ELF64_R_SYM(rela->r_info)].st_name], "puts")) { printf("嘗試消毒PLT/GOT!!!\n"); gotoff = elf->dataOff + (rela->r_offset - elf->dataVaddr); gotptr = &mem[gotoff]; addr = gotptr[0] + (gotptr[1] << 8) + (gotptr[2] << 16) + (gotptr[3] << 24); if (!(addr >= plt_addr && addr < plt_addr + plt_size)) { for (c = 0, j = 0; j < plt_size; j += 16, c++) { //判斷索引號 if (c == symindex) { printf("成功消毒PLT/GOT表!!!\n"); *(uint32_t *)gotptr = plt_addr + j + 6; return 0; } } } printf(" PLT/GOT表解毒失敗!!!\n"); return -1; } } return 0; } //嘗試消毒 一般64位的代碼加載基址為0x400000 int disinfect(elfdesc_t *elf) { size_t paddingSize; Elf64_Phdr *phdr = elf->phdr; Elf64_Shdr *shdr = elf->shdr; uint32_t text_offset = 0; char *strtab = NULL; uint8_t *mem = elf->mem; int i, textfound, fd; ssize_t c, last_chunk; //如果大於0x400000 if (elf->textVaddr >= 0x400000) { printf("不是所要消除的特征!!!\n"); return -1; } //0x400000-代碼段的基址(病毒用的逆向text技術) //計算差值 paddingSize = 0x400000 - elf->textVaddr; //如果存在 hook移除 int ret = disinfect_pltgot(elf); //移除magic 標記 *(uint32_t *)&elf->ehdr->e_ident[EI_PAD] = 0x00000000; // PT_PHDR, PT_INTERP 前移 phdr[0].p_offset -= paddingSize; phdr[1].p_offset -= paddingSize; //phdr設置回正常 for (textfound = 0, i = 0; i < elf->ehdr->e_phnum; i++) { if (textfound) { phdr[i].p_offset -= paddingSize; continue; } if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == 0 && phdr[i].p_flags & PF_X) { if (phdr[i].p_paddr == phdr[i].p_vaddr) { phdr[i].p_vaddr += paddingSize; phdr[i].p_paddr += paddingSize; } else phdr[i].p_vaddr += paddingSize; //重置文本段大小 phdr[i].p_filesz -= paddingSize; phdr[i].p_memsz -= paddingSize; phdr[i].p_align = 0x200000; phdr[i + 1].p_align = 0x200000; textfound = 1; } } //偏移 text_offset = locate_glibc_init_offset(elf); //校正節表 strtab = (char *)&mem[shdr[elf->ehdr->e_shstrndx].sh_offset]; for (i = 0; i < elf->ehdr->e_shnum; i++) { //只要處理感染部分代碼 if (!strcmp(&strtab[shdr[i].sh_name], ".text")) { //保持不變 if (text_offset == 0) continue; shdr[i].sh_offset = text_offset - paddingSize; shdr[i].sh_addr = (text_offset - paddingSize) + 0x400000; continue; } shdr[i].sh_offset -= paddingSize; } //設置phdr和shdr表 elf->ehdr->e_shoff -= paddingSize; elf->ehdr->e_phoff -= paddingSize; //設回正常OEP elf->ehdr->e_entry = 0x400000 + text_offset; elf->ehdr->e_entry -= paddingSize; //重建elf if ((fd = open(TMP, O_CREAT | O_TRUNC | O_WRONLY, elf->st.st_mode)) < 0) return -1; if ((c = write(fd, mem, sizeof(Elf64_Ehdr))) != sizeof(Elf64_Ehdr)) return -1; mem += paddingSize + sizeof(Elf64_Ehdr); last_chunk = elf->st.st_size - (paddingSize + sizeof(Elf64_Ehdr)); if ((c = write(fd, mem, last_chunk)) != last_chunk) return -1; if (fchown(fd, elf->st.st_uid, elf->st.st_gid) < 0) return -1; //重命名 rename(TMP, elf->path); return 0; } //加載文件 int load_executable(const char *path, elfdesc_t *elf) { uint8_t *mem; Elf64_Ehdr *ehdr; Elf64_Phdr *phdr; Elf64_Shdr *shdr; int fd; struct stat st; int i; if ((fd = open(path, O_RDONLY)) < 0) { perror("open"); return -1; } fstat(fd, &st); mem = mmap(NULL, st.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); if (mem == MAP_FAILED) { perror("mmap"); return -1; } ehdr = (Elf64_Ehdr *)mem; phdr = (Elf64_Phdr *)&mem[ehdr->e_phoff]; shdr = (Elf64_Shdr *)&mem[ehdr->e_shoff]; elf->st = st; //為了找到以0 為偏移的代碼段 for (i = 0; i < ehdr->e_phnum; i++) { //非0 為1 0 還是0 switch(!!phdr[i].p_offset) { case 0: elf->textVaddr = phdr[i].p_vaddr; elf->textSize = phdr[i].p_filesz; break; case 1: elf->dataOff = phdr[i].p_offset; elf->dataVaddr = phdr[i].p_vaddr; elf->dataSize = phdr[i].p_filesz; break; } } elf->mem = mem; elf->ehdr = ehdr; elf->phdr = phdr; elf->shdr = shdr; elf->path = (char *)path; return 0; } //檢查是否為病毒 int test_for_skeksi(elfdesc_t *elf) { uint32_t magic = *(uint32_t *)&elf->ehdr->e_ident[EI_PAD]; return (magic == 0x15D25); } int main(int argc, char **argv) { elfdesc_t elf; if (argc < 2) { printf("Usage: %s <executable>\n", argv[0]); exit(0); } //加載 elf 同時保存相關信息 if (load_executable(argv[1], &elf) < 0) { printf("加載失敗: %s\n", argv[1]); exit(-1); } //檢查病毒 if (test_for_skeksi(&elf) == 0) { printf("File: %s, 沒有感染virus\n", argv[1]); exit(-1); } printf("File: %s, 已經感染virus! 嘗試消毒!\n", argv[1]); if (disinfect(&elf) < 0) { printf("消毒失敗 file: %s\n", argv[1]); exit(-1); } printf("消毒成功: %s\n", argv[1]); }
linux 病毒virus解毒