Ensamblador en GNU/Linux Pablo Garaizar Sagarminaga Copyleft, all rights reversed Introducción ¿Para qué vale saber programar en ensamblador? Optimización en cuanto a tamaño. en cuanto a velocidad de ejecución (desenrollado de bucles, sin redundancias, etc.). Modificación de binarios (no tenemos el código fuente): adaptación de drivers cracking virus Pablo Garaizar Sagarminaga Copyleft, all rights reversed Ensamblador en UNIX Llamadas al sistema (syscalls): Pablo Garaizar Sagarminaga Copyleft, all rights reversed Ensamblador en UNIX Llamadas al sistema (syscalls): Normalmente los programas no hacen syscalls, llaman a funciones de la libC: ... push string call printf ... Programa en C printf: ... ... ... int 80h libC KERNEL Núcleo Pablo Garaizar Sagarminaga Copyleft, all rights reversed Ensamblador en UNIX Llamadas al sistema (syscalls): Diferentes maneras de utilizarlas en Linux, BSD, etc: open(path, flags, mode); Linux FreeBSD open: mov eax, mov ebx, mov ecx, mov edx, int 80h syscall: int 80h ret open: push dword mode push dword flags push dword path mov eax, 05h call syscall 05h path flags mode Pablo Garaizar Sagarminaga Copyleft, all rights reversed Ensamblador en UNIX Diferentes sintaxis para programar: Intel: nasm operación destino, fuente: mov eax, 05h AT&T: gas, as operación fuente, destino: movl $0x05, %eax Sufijos en los opcodes (l(ong), w(ord), b(byte)) indicando el tamaño de los operandos. Los registros han de ir precedidos de “%” y los literales de “$”. Diferencias en referencias: mov edx, [ebp+ecx*4+040000000h] = movl $0x4000000(%ebp,%ecx,4), %edx Pablo Garaizar Sagarminaga Copyleft, all rights reversed Primer programa con nasm section .text global _start msg db 'Hello, world!',0Ah len equ $ - msg _start: mov edx,len mov ecx,msg mov ebx,1 mov eax,4 int 80h mov eax,1 int 80h nasm –f elf hello.asm –o hello.o ld hello.o –o hello Pablo Garaizar Sagarminaga Copyleft, all rights reversed Ensamblar y enlazar Ensamblado y enlazado: Ensamblar con nasm: nasm –f elf fichero.asm –o fichero.o Enlazado: ld: ld -s --dynamic-linker /lib/ld-linux.so.2 -lc fichero.o -o fichero gcc: gcc fichero.o -o fichero Pablo Garaizar Sagarminaga Copyleft, all rights reversed Demostración Programar un "hola, mundo!" con nasm. Ensamblarlo y enlazarlo. Ejecutarlo. Pablo Garaizar Sagarminaga Copyleft, all rights reversed Programando en GNU/Linux Estructura de un programa con nasm: section .bss: datos sin inicializar: temporal resb 255 section .data: datos inicializados: contador db 0Ah ( contador EQU 0Ah) zerobuf: times 64 db 0 section .text: código del programa mov eax, 01h ... Pablo Garaizar Sagarminaga Copyleft, all rights reversed Programando en GNU/Linux Condiciones iniciales: Registros En kernels > 2.2: todos los registros a 0. En kernels < 2.2: ebp y ecx distintos de 0. Pila: [esp] = argc [esp+4] = argv[0] [esp+8] = argv[1] ... [esp+n*4] = envp Pablo Garaizar Sagarminaga Copyleft, all rights reversed Programando en GNU/Linux Estructura de un ejecutable en GNU/Linux (ELF): Pablo Garaizar Sagarminaga Copyleft, all rights reversed Herramientas disponibles Editores: BIEW, HTE, ELFe... Volcadores: od, objdump, ELFDump... Desensambladores: IDA (desde dosemu/wine), LDasm, DCC, Monodis, jad... Depuradores: GDB, Fenris, PrivateICE, front-ends de GDB... Trazadores: ltrace, strace, gccchecker... Pablo Garaizar Sagarminaga Copyleft, all rights reversed BIEW BIEW: Interfaz intuitivo, similar al HIEW o MC. Múltiples vistas: binario, octal, hexadecimal, desensamblado. Análisis de la cabecera de los ELF. Modificación sencilla del binario. Pablo Garaizar Sagarminaga Copyleft, all rights reversed Demostración Abrir un ejecutable con BIEW. Analizar sus secciones y cabeceras. Desensamblarlo. Editarlo. Comprobar el cambio en su funcionamiento. Pablo Garaizar Sagarminaga Copyleft, all rights reversed GDB GNU Debugger, escrito inicialmente por Richard Stallman. Orientado al debugging durante el desarrollo de software (junto con el código fuente). Optimizado para ejecutables con código fuente en C y C++ principalmente. Es un debugger de RING-3, que utiliza la syscall ptrace para funcionar. Interfaz de usuario en modo texto y mediante comandos. Muchos emplean GDB a través de un “front-end” que oculta los engorros de utilizar comandos. Pablo Garaizar Sagarminaga Copyleft, all rights reversed GDB Syscall ptrace: Permite a un programa (típicamente un debugger), “pegarse” o “attachearse” a otro proceso ya en ejecución con el objeto de analizar su comportamiento. Debugging en modo usuario. Tiene un historial de seguridad un tanto triste. Pablo Garaizar Sagarminaga Copyleft, all rights reversed GDB Front-ends: Xxgdb: el primero y más simple. GVD: GNU Visual Debugger, algo rústico. KDBG: wrapper para KDE del GDB. DDD: quizá el front-end más sofisticado, combinando las funcionalidades del GDB, con muchas ventajas del modo gráfico. Pablo Garaizar Sagarminaga Copyleft, all rights reversed GDB Primeros pasos, carga del ejecutable: gdb ejecutable gdb PID gdb –args ejecutable arg1 arg2 Ejecución del programa: set / show args path / show paths show / set / unset environment var run run parámetros attach PID detach Pablo Garaizar Sagarminaga Copyleft, all rights reversed GDB Breakpoints: info break break [TAB] [lista de símbolos] break *0x08040808 clear *0x08040808 Watchpoints: info watchpoints watch expresión (write) rwatch expresión (read) awatch expresión (acceso: r||w) Catchpoints: catch evento Pablo Garaizar Sagarminaga Copyleft, all rights reversed GDB Ejecutando instrucciones: n, next (una línea) s, step (una línea y entra) ni, nexti (una instrucción) si, stepi (una instr. y entra) finish c, continue Examinando la pila: frame n up, down info frame n bt, backtrace Pablo Garaizar Sagarminaga Copyleft, all rights reversed GDB Desensamblado: disassemble set disassembly-flavor intel set disassembly-flavor att Registros: info registers info all-registers Memoria: x 0x8048080 x/100 0x8048080 x/100s 0x8048080 Pablo Garaizar Sagarminaga Copyleft, all rights reversed Demostración Cargar un ejecutable con GDB. Desensamblarlo. Ejecutarlo y trazarlo. Pablo Garaizar Sagarminaga Copyleft, all rights reversed Técnicas anti-debugging Método 1: descriptores de ficheros. Cuando se carga un proceso en memoria, antes de que se ejecute se realizan diversas operaciones de lectura sobre varios ficheros implicados en la creación de la imagen del proceso en ejecución. El GDB abre más descriptores que los necesarios y el intento de cierre de los mismos produce un error: if(close(3)==-1) printf(“Hello shell...\n”); else printf(“Being traced...\n”); Pablo Garaizar Sagarminaga Copyleft, all rights reversed Técnicas anti-debugging Método 2: argumentos y variables de entorno. La documentación de la shell bash explica que el valor de la variable de entorno “_” contiene el nombre completo de cada aplicación que se haya ejecutado. Los diferentes debuggers modifican esa variable: shell strace ltrace fenris gdb argv[0] ./test ./test ./test ./test /home/user/test getenv(“_”) ./test /usr/bin/strace /usr/bin/ltrace /usr/bin/fenris (NULL) Pablo Garaizar Sagarminaga Copyleft, all rights reversed Técnicas anti-debugging Método 3: Identificación de procesos. Existe un identificador para el proceso interactivo que se llama proceso “líder” de la sesión: SID. La diferencia entre el proceso padre (PPID) y el proceso líder (SID) determinará si existe un programa intermedio desde el que se está ejecutando la aplicación: shell getsid 0x1968 getppid 0x1968 getpgid 0x3a6e getpgrp 0x3a6e gdb 0x1968 0x3a6f 0x3a70 0x3a70 strace 0x1968 0x3a71 0x3a71 0x3a71 ltrace 0x1968 0x3a73 0x3a73 0x3a73 fenris 0x1968 0x3a75 0x3a75 0x3a75 Pablo Garaizar Sagarminaga Copyleft, all rights reversed Técnicas anti-debugging Metodo 4: Forzar un falso desensamblado Mediante un salto a mitad de una instrucción, dado un alineamiento, conseguimos engañar al desensamblador: jmp antidebug + 2 antidebug: db 0666h call reloc reloc: pop esi ... Pablo Garaizar Sagarminaga Copyleft, all rights reversed Técnicas anti-debugging Método 5: Detectando Int 3. Cuando un debugger quiere parar en un punto determinado (breakpoint), introduce un Int 3 (0xCC) que se puede detectar: void foo(){ printf("Hello\n");} int main(){ if ((*(volatile unsigned *)((unsigned)foo + 3) & 0xff) == 0xcc) { printf("BREAKPOINT\n"); exit(1); } foo(); } Pablo Garaizar Sagarminaga Copyleft, all rights reversed Técnicas anti-debugging Método 6: Introduciendo Int 3. Podemos introducir 0xCCs en el código para “molestar” al debugger: #include <signal.h> void handler(int signo){} int main(){ signal(handler, SIGTRAP); __asm__("int3"); printf("Hello\n"); } Pablo Garaizar Sagarminaga Copyleft, all rights reversed Técnicas anti-debugging Método 7: Llamando a ptrace. Cuando estamos siendo debuggeados por GDB o strace/ltrace sólo es posible llamar a ptrace[PTRACE_TRACEME] una vez por proceso: if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) { printf("DEBUGGING... Bye\n"); return 1; } Pablo Garaizar Sagarminaga Copyleft, all rights reversed Demostración Compilar ejemplos de los métodos propuestos. Intentar depurarlos y trazarlos. Pablo Garaizar Sagarminaga Copyleft, all rights reversed Conclusiones Programar y conocer ensamblador nos da una potencia asombrosa, aunque es un trabajo arduo en ocasiones. GNU/Linux, y UNIX en general, no está afectado todavía por las problemáticas que normalmente requieren su uso: virus, cracking de programas comerciales, modificación de programas sin tener su código fuente, etc. Esto provoca que no haya muchas herramientas disponibles, ya que suelen ser creadas por grupos underground. Parece que todo esto esta progresivamente cambiando, así que se auguran desarrollos importantes. Pablo Garaizar Sagarminaga Copyleft, all rights reversed Referencias LinuxAssembly: http://linuxassembly.org The Int80h Page: http://int80h.org List of Linux/i386 system calls: http://www.lxhp.in-berlin.de/lhpsyscal.html Phrack Magazine: http://phrack.org Pablo Garaizar Sagarminaga Copyleft, all rights reversed