En este artículo repasaremos unas cuantas técnicas anti-debugging que se suelen utilizar para evitar que nuestros programas sean trazados. Muchas de estas ideas, bonitas por su sencillez, han sido recolectadas de varios documentos escritos por gente importante en este campo como Silvio Césare o Iñaki López. El mundo del anti-debugging es apasionante: la eterna lucha entre el cazador y la huidiza presa, que gira en espiral hasta no poder distinguir si es el depurador quién ha sido cazado y engañado por el programa, o si finalmente quien traza el programa es capaz de seguir su ejecución paso a paso. Está claro que ninguna de estas técnicas evitarán que alguien con conocimientos trace vuestros programas, pero es curioso ver como tan pocas líneas pueden complicar la vida a muchos analistas novatos 😉
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.
En principio, un proceso debería «nacer» con los descriptores 0 (entrada estándar), 1 (salida estándar) y 2 (salida de error) creados y el resto sin crear.
Debuggers coomo el GDB abren más descriptores que los necesarios y el intento de cierre de los mismos NO produce un error (como cabría esperar si estuviéramos en un entorno normal, donde esos ficheros no existen). En este ejemplo, nada más comenzar, se intenta cerrar el descriptor de fichero 3. Si esto no provoca un error (-1), es que algo está yendo mal:
#include <stdio.h> int main( int argc, char *argv[] ) { if(close(3) == -1) { printf("OK\n"); } else { printf("traced!\n"); exit(-1); } return 0; }
Probamos a ver si esto es cierto…
$ ./antidebug1 OK $ gdb ./antidebug1 GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) run Starting program: /home/txipi/projects/ensamblador/antidebug/antidebug1 traced! Program exited with code 0377. (gdb)
Como vemos, esta técnica ha logrado detectar al GDB, pero no tiene tanta suerte con otros trazadores como ltrace o strace:
$ strace ./antidebug1 execve("./antidebug1", ["./antidebug1"], [/* 29 vars */]) = 0 uname({sys="Linux", node="elektron", ...}) = 0 brk(0) = 0x804a000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7fb5000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7fb4000 open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=61851, ...}) = 0 mmap2(NULL, 61851, PROT_READ, MAP_PRIVATE, 3, 0) = 0xa7fa4000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/tls/i686/cmov/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\3\3\1\240O\1"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1241580, ...}) = 0 mmap2(NULL, 1247388, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xa7e73000 mmap2(0xa7f9a000, 28672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x127) = 0xa7f9a000 mmap2(0xa7fa1000, 10396, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xa7fa1000 close(3) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7e72000 mprotect(0xa7f9a000, 20480, PROT_READ) = 0 set_thread_area({entry_number:-1 -> 6, base_addr:0xa7e726c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 munmap(0xa7fa4000, 61851) = 0 close(3) = -1 EBADF (Bad file descriptor) fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7fb3000 write(1, "OK\n", 3OK ) = 3 munmap(0xa7fb3000, 4096) = 0 exit_group(0) = ? Process 4947 detached
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. En un entorno «sano», esta variable debería ser igual que el valor de argv[0]. Los diferentes debuggers modifican esta variable de sitintas formas:
argv[0] getenv("_") shell ./test ./test strace ./test /usr/bin/strace ltrace ./test /usr/bin/ltrace fenris ./test /usr/bin/fenris gdb /home/user/test (NULL)
Veamos cómo utilizaríamos esta técnica con un ejemplo:
#include <stdio.h> #include <string.h> int main( int argc, char *argv[] ) { printf("getenv(_): %s\n", getenv("_")); printf("argv[0]: %s\n", argv[0]); if(strcmp(argv[0], (char *)getenv("_"))) { printf("traced!\n"); exit(-1); } printf("OK\n"); return 0; }
Lo compilamos, y lo probamos:
$ ./antidebug2 getenv(_): ./antidebug2 argv[0]: ./antidebug2 OK
Todo bien, veamos con ltrace:
$ ltrace ./antidebug2 __libc_start_main(0x8048404, 1, 0xaf8de1f4, 0x80484a0, 0x8048500 <unfinished ...> getenv("_") = "/usr/bin/ltrace" printf("getenv(_): %s\n", "/usr/bin/ltrace"getenv(_): /usr/bin/ltrace ) = 27 printf("argv0: %s\n", "./antidebug2"argv0: ./antidebug2 ) = 22 getenv("_") = "/usr/bin/ltrace" strcmp("./antidebug2", "/usr/bin/ltrace") = -1 printf("traced!\n"traced! ) = 8 exit(-1 <unfinished ...> +++ exited (status 255) +++
¡Pillados! A ver con strace:
$ strace ./antidebug2 execve("./antidebug2", ["./antidebug2"], [/* 29 vars */]) = 0 uname({sys="Linux", node="elektron", ...}) = 0 brk(0) = 0x804a000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7fa0000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7f9f000 open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=61851, ...}) = 0 mmap2(NULL, 61851, PROT_READ, MAP_PRIVATE, 3, 0) = 0xa7f8f000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/tls/i686/cmov/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\3\3\1\240O\1"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1241580, ...}) = 0 mmap2(NULL, 1247388, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xa7e5e000 mmap2(0xa7f85000, 28672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x127) = 0xa7f85000 mmap2(0xa7f8c000, 10396, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xa7f8c000 close(3) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7e5d000 mprotect(0xa7f85000, 20480, PROT_READ) = 0 set_thread_area({entry_number:-1 -> 6, base_addr:0xa7e5d6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 munmap(0xa7f8f000, 61851) = 0 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7f9e000 write(1, "getenv(_): /usr/bin/strace\n", 27getenv(_): /usr/bin/strace ) = 27 write(1, "argv0: ./antidebug2\n", 22argv0: ./antidebug2 ) = 22 write(1, "traced!\n", 8traced! ) = 8 munmap(0xa7f9e000, 4096) = 0 exit_group(-1) = ? Process 4961 detached
Pillados también 😀 Última prueba con GDB:
$ gdb ./antidebug2 GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) run Starting program: /home/txipi/projects/ensamblador/antidebug/antidebug2 getenv(_): /usr/bin/gdb argv0: /home/txipi/projects/ensamblador/antidebug/antidebug2 traced! Program exited with code 0377. (gdb)
Tercera pillada. Parece que este método es simple pero efectivo 🙂
Método 3: Identificación de procesos.
Existe un identificador para el proceso interactivo que se llama proceso «líder» de la sesión, el 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.
Siguiendo de cerca estos valores mediante getsid(), getppid(), getpgid() y getpgrp() podremos detectar si algo raro pasa en e entorno:
#include <stdio.h> #include <unistd.h> int main( int argc, char *argv[] ) { printf("getsid: %d\n", getsid(getpid())); printf("getppid: %d\n", getppid()); if(getsid(getpid()) != getppid()) { printf("traced!\n"); exit(-1); } printf("OK\n"); return 0; }
Lo probamos:
$ echo $$ 4922 $ ./antidebug3 getsid: 4922 getppid: 4922 OK
Como vemos, lanzo el programa desde una shell con PID 4922, que es lo que debería dar la salida de getsid() y de getppid(). Veamos qué pasa cuando se ponen ltrace o strace por medio:
$ ltrace ./antidebug3 __libc_start_main(0x8048434, 1, 0xafb27c34, 0x80484c0, 0x8048520 <unfinished ...> getpid() = 4980 getsid(4980, 0xa7f89ff4, 0x8048520, 0xa7f89ff4, 0) = 4922 printf("getsid: %d\n", 4922getsid: 4922 ) = 13 getppid() = 4979 printf("getppid: %d\n", 4979getppid: 4979 ) = 14 getpid() = 4980 getsid(4980, 4979, 0x8048520, 0xa7f89ff4, 0) = 4922 getppid() = 4979 printf("traced!\n"traced! ) = 8 exit(-1 <unfinished ...> +++ exited (status 255) +++
Pillado, y…
$ strace ./antidebug3 execve("./antidebug3", ["./antidebug3"], [/* 29 vars */]) = 0 uname({sys="Linux", node="elektron", ...}) = 0 brk(0) = 0x804a000 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7f3e000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7f3d000 open("/etc/ld.so.cache", O_RDONLY) = 3 fstat64(3, {st_mode=S_IFREG|0644, st_size=61851, ...}) = 0 mmap2(NULL, 61851, PROT_READ, MAP_PRIVATE, 3, 0) = 0xa7f2d000 close(3) = 0 access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory) open("/lib/tls/i686/cmov/libc.so.6", O_RDONLY) = 3 read(3, "\177ELF\1\1\1\3\3\1\240O\1"..., 512) = 512 fstat64(3, {st_mode=S_IFREG|0755, st_size=1241580, ...}) = 0 mmap2(NULL, 1247388, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xa7dfc000 mmap2(0xa7f23000, 28672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x127) = 0xa7f23000 mmap2(0xa7f2a000, 10396, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xa7f2a000 close(3) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7dfb000 mprotect(0xa7f23000, 20480, PROT_READ) = 0 set_thread_area({entry_number:-1 -> 6, base_addr:0xa7dfb6c0, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0 munmap(0xa7f2d000, 61851) = 0 getpid() = 4982 getsid(4982) = 4922 fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0 mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xa7f3c000 write(1, "getsid: 4922\n", 13getsid: 4922 ) = 13 getppid() = 4981 write(1, "getppid: 4981\n", 14getppid: 4981 ) = 14 getsid(4982) = 4922 getppid() = 4981 write(1, "traced!\n", 8traced! ) = 8 munmap(0xa7f3c000, 4096) = 0 exit_group(-1) = ? Process 4982 detached
… pillado :-D.
Método 4: falso desensamblado
Esta no es una técnica anti-debugging en sí, sino un método para dificultar el análisis «en frío» o lo que se conoce como «código muerto», es decir, sin ejecutar el programa. Consiste en saltar en mitad de una instrucción, rompiendo todo el alineamiento hasta ese punto y engañando a los desensambladores más básicos. El código se ejecutará a partir de ese punto, y el desensamblador seguirá equivocándose desde ahí. Es probable que si se llega a un conjunto de NOPs, nuevamente el desensamblado vuelva a ser correcto, perdiéndose el efecto.
Veámoslo con un ejemplo del propio Silvio Cesare:
jmp antidebug1 + 2 antidebug1: .short 0xc606 call reloc reloc: popl %esi jmp antidebug2 antidebug2: addl $(data - reloc),%esi movl 0(%esi),%edi pushl %esi jmp *%edi data: .long 0
Como vemos, se salta a antidebug1 + 2 bytes, es decir, se obvian los bytes 0xc6 y 0x06 que están previamente en el código ensamblador. El desensamblador (objdump, por ejemplo), seguirá buscando la siguiente instrucción nada más terminar el jmp, y desesamblará el 0xc606:
$ objdump -d a.out . . . 8048340: 55 pushl %ebp 8048341: 89 e5 movl %esp,%ebp 8048343: eb 02 jmp 0x8048347 8048345: 06 pushl %es 8048346: c6 e8 00 movb $0x0,%al 8048349: 00 00 addb %al,(%eax) 804834b: 00 5e eb addb %bl,0xffffffeb(%esi) 804834e: 00 81 c6 0f 00 addb %al,0xfc6(%ecx) 8048353: 00 8048354: 00 8b 7e 00 56 addb %cl,0xff56007e(%ebx) 8048359: ff 804835a: e7 00 outl %eax,$0x0 804835c: 00 00 addb %al,(%eax) 804835e: 00 89 ec 5d c3 addb %cl,0x90c35dec(%ecx) 8048363: 90 8048364: 90 nop . . .
Como se puede observar, a partir del salto (en 0x8048343), el desesamblado es incorrecto 🙂
Método 5: Detectando Int 3.
Cuando un debugger quiere parar en un punto determinado, es decir, poner un breakpoint o punto de ruptura, introduce un «int 3» (0xCC). En versiones antiguas de GDB, este 0xCC se podía detectar:
void foo(){ printf("Hello\n");} int main(){ if ((*(volatile unsigned *)((unsigned)foo + 3) & 0xff) == 0xcc) { printf("BREAKPOINT\n"); exit(1); } foo(); }
Veámoslo en acción:
$ ./antidebug5 Hello
La idea es que cuando alguien ponga un breakpoint en la función foo, GDB pondrá un 0xCC en su dirección de memoria y en el if previo antes de llamar a esa función, lo detectemos:
$ gdb ./antidebug GDB is free software and you are welcome to distribute copies of it under certain conditions; type "show copying" to see the conditions. There is absolutely no warranty for GDB; type "show warranty" for details. GDB 4.16 (i586-debian-linux), Copyright 1996 Free Software Foundation, Inc. (gdb) break foo Breakpoint 1 at 0x8048373: file antidebug5.c, line 3. (gdb) run Starting program: /home/txipi/projects/ensamblador/antidebug/antidebug5 BREAKPOINT Program exited with code 01. (gdb)
Pero en las versiones actuales de GDB, esto no pasa:
$ gdb ./antidebug5 GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) break foo Breakpoint 1 at 0x804839a (gdb) x/10 0x804839a 0x804839a <foo+6>: 0x042404c7 0xe8080485 0xffffff06 0x8955c3c9 0x80483aa <main+2>: 0x08ec83e5 0xb8f0e483 0x00000000 0x97a1c429 0x80483ba <main+18>: 0x25080483 0x000000ff (gdb) x/10 0x8048394 0x8048394 <foo>: 0x83e58955 0x04c708ec 0x04850424 0xff06e808 0x80483a4 <foo+16>: 0xc3c9ffff 0x83e58955 0xe48308ec 0x0000b8f0 0x80483b4 <main+12>: 0xc4290000 0x048397a1 (gdb) disas 0x8048394 Dump of assembler code for function foo: 0x08048394 <foo+0>: push %ebp 0x08048395 <foo+1>: mov %esp,%ebp 0x08048397 <foo+3>: sub $0x8,%esp 0x0804839a <foo+6>: movl $0x8048504,(%esp) 0x080483a1 <foo+13>: call 0x80482ac <printf@plt> 0x080483a6 <foo+18>: leave 0x080483a7 <foo+19>: ret End of assembler dump. (gdb) run Starting program: /home/txipi/projects/ensamblador/antidebug/antidebug5 Breakpoint 1, 0x0804839a in foo () (gdb) cont Continuing. Hello Program exited with code 06. (gdb)
No hay rastro de 0xCC por ningún lado, por lo que el método no funciona ;-(.
Método 6: introducir int 3
Podemos hacer lo contrario que en el método anterior e introducir 0xCCs en el código para «molestar» a quien lo esté depurando con falsos breakpoints.
En este ejemplo, capturamos la señal SIGTRAP para que el programa no casque en ejecución normal, y lanzamos una «int 3» que dispara una SIGTRAP dentro del depurador, de forma parecida a un breakpoint:
#include <signal.h> void handler(int signo){} int main(){ signal(handler, SIGTRAP); asm("int3"); printf("Hello\n"); }
Veamos si funciona:
$ ./antidebug6 OK
El programa se ejecuta normalmente. Pero si lo ejecutamos a través del GDB…
$ gdb ./antidebug6 GNU gdb 6.4.90-debian Copyright (C) 2006 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1". (gdb) run Starting program: /home/txipi/projects/ensamblador/antidebug/antidebug6 Program received signal SIGTRAP, Trace/breakpoint trap. 0x080483be in main () (gdb)
Vemos como salta la señal SIGTRAP y el GDB piensa que es un breakpoint introducido por el usuario 🙂
Método 7: llamando a ptrace()
Cuando estamos siendo debuggeados por un debugger en modo usuario (RING-3 en arquitectura IA-32), como GDB o strace/ltrace, sólo es posible llamar a ptrace una vez por proceso. Por lo tanto, si intentamos trazarnos a nosotros mismos y esto provoca un error, es que ya hay otro proceso trazándonos:
#include <sys/ptrace.h> int main( int argc, char *argv[] ) { if( ptrace(PTRACE_TRACEME, 0, 1, 0) < 0 ) { printf("traced!\n"); return 1; } printf("OK\n"); return 0; }
Lo probamos:
$ ./antidebug7 OK
Probamos ahora con cualquier debugger que use ptrace():
$ ltrace ./antidebug7 __libc_start_main(0x8048394, 1, 0xafa71384, 0x8048400, 0x8048460 <unfinished ...> ptrace(0, 0, 1, 0, 0) = -1 printf("traced!\n"traced! ) = 8 +++ exited (status 1) +++
Y vemos cómo ha sido pillado fácilmente 🙂
Esta técnica es la que usaba el protector de binarios Shiva para evitar ser trazado: creaba un proceso hijo con fork() y cada proceso trazaba al otro, de tal forma que no era posible usar un debugger basado en ptrace() para ninguno de los dos procesos, porque ya estaban siendo trazados por un proceso. Hablo en pasado porque el protector Shiva hace bastante tiempo que dejó de funcionar en los núcleos actuales de GNU/Linux. Una pena ;-(
Técnicas anti-debugging sencillas para GNU/Linux
En este post se enumeran unas cuantas técnicas anti-debugging que se suelen utilizar para evitar que los programas sean trazados. "..El mundo del anti-debugging es apasionante: la eterna lucha entre el cazador y la huidiza presa, que gira en…
No entiendo, en el mundo GNU nadie necesita tracear un programa, tiene los fuentes a su disposición.
Este artículo me remonta a los tiempos del crack y del serialnumbergenerator de windows, menos mal que ya pasaron a la historia.
Moskis, el cierre del autor lo deja entrever:
Hablo en pasado porque [el protector Shiva] hace bastante tiempo que dejó de funcionar en [los núcleos actuales de] GNU/Linux. Una pena ;-(
@moskis: en el mundo GNU no, pero el mundo de Linux no solamente es el mundo GNU, también hay drivers privativos de linuxant, vmware para GNU/Linux y demás software que históricamente se ha querido crackear. Además de binarios con burneye, shiva, y demás, utilizados en intrusiones a sistemas
te sales txipi 😉
Con respecto a lo de depurar, muchas veces es necesario depurar aunque tengas los fuentes, cuando una aplicación falla, en un crash por ejemplo, por mucho que tengas los fuentes, vete a saber donde casca =D
Ese David Salgado AKA WinDbg-man 😀
Todo un honor leerte por esta mi humilde morada 😉
Pingback: A Geek’s Page » Blog Archive » Linux下的反调试技术
Hello txipi! Thanks for your response on my blog. ;=)
In fact, what I can understand here is only your C code. I have a question about one piece of the code. Why gdb/strace modifies the environment variable $_?
Thank you!
le incorpore algunas de estas rutinas al wargame de seguridad que se jugo en la campusparty-co y eso lo volvio mucho mas interesante. muchas gracias!