|=-------------=[ Recuperer la sys_call_table sur amd64(x86_64) ]=------------=| |=----------------------------------------------------------------------------=| |=-----------------------=[ pouik ]=----------------------=| --[ Sommaire 1 - Introduction 2 - IDT 3 - System_call 4 - Sys_call_table 5 - Test 6 - Conclusion --[ 1 Introduction Certains se rappelent surement l'article de sd & devik dans phrack 58 [1] qui introduisait pour la premiere fois(ou c'etait silvio ?) la facon de trouver la sys_call_table sous x86 en userland via /dev/(k)mem et de l'utiliser pour la creation de rootkits sous linux. Cet article a pour but de montrer comment recuperer la sys_call_table pour la nouvelle vague des processeurs, j'ai nomme l'amd64. La plupart des distributions linux ont leur kernel de compile avec l'option CONFIG_IA32_EMULATION=y qui permet de faie tourner des programmes 32 bits sous 64 bits, donc d'assurer la compatabilite des programmes codes sous archi 32 bits. Les devs du kernel ont donc introduit une ia32_sys_call_table et c'est elle qui va nous interesser tout au long de cet article magnifique :p Ladies and gentlemens let's go ! --[ 2 IDT Sous x86, recuperer la table des descripteurs d'interruptions se fait par l'instruction asm : asm("sidt %0" : "=m" (idtr)); qui remplit la structure idtr qui est : struct { unsigned short limit; unsigned int base; } __attribute__((packed)) idtr; Ici, sous amd64, la base ne doit pas faire 4 octets mais 8 octets donc on utilisera une structure du type : struct { unsigned short limit; unsigned long base; } __attribute__((packed)) idtr; --[ 3 System_call L'idee de sd & devik etait d'ensuite recuperer le descripteur d'interruption correspondant au int $0x80 de la facon suivant : readkmem (&idt,idtr.base+8*0x80,sizeof(idt)); sys_call_off = (idt.off2 << 16) | idt.off1; Ok ici ca marche sauf qu'on doit lire 16 bits : readkmem (&idt,idtr.base+16*0x80,sizeof(idt)); sys_call_off = (idt.off2 << 16) | idt.off1; Arretons nous maintenant 1 minutes ici, comme dit dans l'introduction les devs du kernel ont introduit la compatibilite des applications 32 bits, donc ici le system_call devrait etre en ia32 : promethee kernel # pwd /usr/src/linux/arch/x86_64/kernel promethee kernel # grep SYSCALL_VECTOR * i8259.c: if (vector != IA32_SYSCALL_VECTOR) io_apic.c: if (current_vector == IA32_SYSCALL_VECTOR) traps.c: set_system_gate(IA32_SYSCALL_VECTOR, ia32_syscall); Voila bingo, notre system_call que l'on va recuperer est en fait ia32_syscall. --[ 4 Sys_call_table Bon maintenant que l'on a ia32_syscall, suivons un peu son execution : (gdb) disassemble ia32_syscall Dump of assembler code for function ia32_syscall: 0xffffffff8021f6fc : swapgs 0xffffffff8021f6ff : sti 0xffffffff8021f700 : mov %eax,%eax 0xffffffff8021f702 : push %rax 0xffffffff8021f703 : cld 0xffffffff8021f704 : sub $0x48,%rsp 0xffffffff8021f708 : mov %rdi,0x40(%rsp) 0xffffffff8021f70d : mov %rsi,0x38(%rsp) 0xffffffff8021f712 : mov %rdx,0x30(%rsp) 0xffffffff8021f717 : mov %rcx,0x28(%rsp) 0xffffffff8021f71c : mov %rax,0x20(%rsp) 0xffffffff8021f721 : mov %gs:0x10,%r10 0xffffffff8021f72a : sub $0x1fd8,%r10 0xffffffff8021f731 : orl $0x2,0x14(%r10) 0xffffffff8021f736 : testl $0x181,0x10(%r10) 0xffffffff8021f73e : jne 0xffffffff8021f768 End of assembler dump. (gdb) disassemble ia32_tracesys Dump of assembler code for function ia32_tracesys: 0xffffffff8021f768 : sub $0x30,%rsp 0xffffffff8021f76c : mov %rbx,0x28(%rsp) 0xffffffff8021f771 : mov %rbp,0x20(%rsp) 0xffffffff8021f776 : mov %r12,0x18(%rsp) 0xffffffff8021f77b : mov %r13,0x10(%rsp) 0xffffffff8021f780 : mov %r14,0x8(%rsp) 0xffffffff8021f785 : mov %r15,(%rsp) 0xffffffff8021f789 : movq $0xffffffffffffffda,0x50(%rsp) 0xffffffff8021f792 : mov %rsp,%rdi 0xffffffff8021f795 : callq 0xffffffff8020c684 0xffffffff8021f79a : mov 0x30(%rsp),%r11 0xffffffff8021f79f : mov 0x38(%rsp),%r10 0xffffffff8021f7a4 : mov 0x40(%rsp),%r9 0xffffffff8021f7a9 : mov 0x48(%rsp),%r8 0xffffffff8021f7ae : mov 0x58(%rsp),%rcx 0xffffffff8021f7b3 : mov 0x60(%rsp),%rdx 0xffffffff8021f7b8 : mov 0x68(%rsp),%rsi 0xffffffff8021f7bd : mov 0x70(%rsp),%rdi 0xffffffff8021f7c2 : mov 0x78(%rsp),%rax 0xffffffff8021f7c7 : mov (%rsp),%r15 0xffffffff8021f7cb : mov 0x8(%rsp),%r14 0xffffffff8021f7d0 : mov 0x10(%rsp),%r13 0xffffffff8021f7d5 : mov 0x18(%rsp),%r12 0xffffffff8021f7da : mov 0x20(%rsp),%rbp 0xffffffff8021f7df : mov 0x28(%rsp),%rbx 0xffffffff8021f7e4 : add $0x30,%rsp 0xffffffff8021f7e8 : jmpq 0xffffffff8021f740 End of assembler dump. (gdb) disassemble ia32_do_syscall Dump of assembler code for function ia32_do_syscall: 0xffffffff8021f740 : cmp $0x13c,%eax 0xffffffff8021f745 : ja 0xffffffff8021f7ed 0xffffffff8021f74b : mov %edi,%r8d 0xffffffff8021f74e : mov %ebp,%r9d 0xffffffff8021f751 : xchg %ecx,%esi 0xffffffff8021f753 : mov %ebx,%edi 0xffffffff8021f755 : mov %edx,%edx 0xffffffff8021f757 : callq *0xffffffff804f6110(,%rax,8) End of assembler dump. Donc voila, apres etre passer en ia32_tracesys, on saute en ia32_do_syscall, qui fais un appel : callq *0xffffffff804f6110(,%rax,8) promethee linux # grep ffffffff804f6110 /boot/System.map ffffffff804f6110 R ia32_sys_call_table Et hop dans le mille, voila notre belle ia32_sys_call_table :p Pour la recuperer a partir du system_call, sd & devik lisaient 256 caracteres et recherchaient le pattern "\xff\x14\x85" qui correspondait au call : readkmem (sc_asm,sys_call_off,CALLOFF); p = (char*)memmem (sc_asm,CALLOFF,"\xff\x14\x85",3); sct = *(unsigned*)(p+3); pourquoi ne pas appliquer cette methode ? (gdb) x/xw ia32_do_syscall+23 0xffffffff8021f757 : 0x10c514ff Il nous suffira de rechercher "\xff\x14\xc5" comme pattern a partir de ia32_syscall. Si vous avez bien lu jusqu'ici, vous pensez p-e qu'il y a une erreur puisque nous avons avons normalement un saut de plus a faire via ia32_tracesys pour arriver a ia32_dosyscall ? En fait vous avez raison, mais on va profiter que ces deux fonctions se suivent en memoire pour sauter tracesys. Ce qui donne donc : readkmem (sc_asm,sys_call_off,CALLOFF); p = (char*)memmem (sc_asm,CALLOFF,"\xff\x14\xc5",3); sct = *(unsigned long*)(p+3); sct = (sct & 0x00000000ffffffff) | 0xffffffff00000000; On applique aussi un petit mask pour schinter la partie qui ne nous interesse pas. Bonus Track : Une autre maniere de recuperer ia32_sys_call_table est d'utiliser la fonction walk_krstab introduit dans le rookit phalanx car : promethee linux # grep ia32_sys_call_table /boot/System.map ffffffff804f6110 R ia32_sys_call_table ffffffff8055feb0 r __ksymtab_ia32_sys_call_table ffffffff8056c9d0 r __kstrtab_ia32_sys_call_table Pour plus d'info voir article precedent. --[ 5 Test promethee linux # grep ia32_syscall /boot/System.map ffffffff8021f6fc T ia32_syscall ffffffff804f6af8 r ia32_syscall_end promethee linux # grep ia32_sys_call_table /boot/System.map ffffffff804f6110 R ia32_sys_call_table ffffffff8055feb0 r __ksymtab_ia32_sys_call_table ffffffff8056c9d0 r __kstrtab_ia32_sys_call_table Testons avec la premiere methode : promethee linux # zeppoo -s -d /dev/mem Kernel : 2.6.17.11 Kernel : 2.617110 proc : x86_64 KERNEL_START 0xffffffff80000000 KERNEL_END 0xffffffff81000000 PAGE_OFFSET 0xffff810000000000 PAGE_MAX 0xffffffffffffffff Memory : /dev/mem IDTR BASE 0xffffffff8067e000 LIMIT 0xfff idt80: flags = 238 flags=EE sel=10 off1=f6fc off2=8021 SYSTEM_CALL : 0xffffffff8021f6fc Sys Call Table 0xffffffff804f6110 Avec la seconde methode : promethee linux # zeppoo -s -d /dev/mem -m Kernel : 2.6.17.11 Kernel : 2.617110 proc : x86_64 KERNEL_START 0xffffffff80000000 KERNEL_END 0xffffffff81000000 PAGE_OFFSET 0xffff810000000000 PAGE_MAX 0xffffffffffffffff Memory : /dev/mem IDTR BASE 0xffffffff8067e000 LIMIT 0xfff idt80: flags = 238 flags=EE sel=10 off1=f6fc off2=8021 SYSTEM_CALL : 0xffffffff8021f6fc ????kstrtab = 0xffffffff8056c9d0 ??????Sys Call Table 0xffffffff804f6110 --[ 6 Conclusion Voila la fin de ce petit article, tous les codes sources correspondant sont bien sur disponible sur http://www.zeppoo.net et sur le svn (svn checkout https://subversion.cru.fr/zeppoo/trunk/ zeppoo) dans le repertoire libzeppoo. Voila maintenant que vous avez la sys_call_table, les memes techniques que pour x86 peuvent reprendre leurs cours :p Reference : [1] http://phrack.org/archives/58/p58-0x07 Thanks : n0name, Yperite, tbowan et aryliin pour me supporter :p "la fin de ton monde, le commencement du mien"