Hace un tiempo vimos como escribir un sencillo antidebugger y como esquivarlo,
en ese momento lo evitamos eliminando la llamada a ptrace directamente del
binario, ahora veremos como "cazar" la llamada y reemplazarla por la nuestra
propia sin tener que tocar el archivo.
Priemero crearemos un archivo con la función que lo reemplazará
[faketrace.c]:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 | /* Cabeceras estándar. */
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
/* Definiciones relativas a ptrace. */
#include <sys/ptrace.h>
/* Si no lo queremos cazar, estas cabeceras nos proporcionan
funciones para dejar seguir por su "caudal normal" a la
función.
*/
#include <sys/types.h>
#define __USE_GNU
#include <dlfcn.h>
/* Cadenas de error, para más realismo. */
#include <errno.h>
/* Tenemos en cuenta si ya nos estamos traceando,
para hacerlo más creible.
*/
int is_self_tracing = 0;
/* La función que se llamará realmente cuando se haga ptrace. */
long ptrace(enum __ptrace_request request, ... ){
/* firma real de 'man ptrace':
long ptrace(enum __ptrace_request request, pid_t pid,
void *addr, void *data);
*/
/* Tomamos la lista de argumentos. */
va_list args;
va_start(args, 3);
pid_t pid = va_arg(args, pid_t);
void *addr = va_arg(args, void*);
void *data = va_arg(args, void*);
va_end(args);
/* Si es del tipo que queremos "cazar", un traceo a si mismo. */
if (request == PTRACE_TRACEME){
/* Si ya se ha intentado trazar antes, lo declaramos
como error de permisos y devolvemos -1. */
if (is_self_tracing){
errno = EPERM;
return -1;
}
/* Sino, anotamos que se esté trazando y devolvemos 0. */
else{
is_self_tracing = 1;
return 0;
}
}
/* Si no nos interesa. */
else{
/* Conseguimos la función "real". */
long (*real_ptrace)(enum __ptrace_request request, pid_t pid,
void *addr, void *data) = dlsym(RTLD_NEXT, "ptrace");
/* Y le enviamos los parámetros. */
return real_ptrace(request, pid, addr, data);
}
}
|
Para compilarlo hay que enviar un par de flags:
| gcc -fPIC -shared faketrace.c -o libft.so
|
Y ya podemos lanzarlo contra el programa de la otra vez, teniendo en cuenta que
hay que hacer "PRELOAD" de la librería "falsa" (setear la variable de entorno
LD_PRELOAD al path de la librería), pero dentro del ltrace, para evitar
tropezar con el:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | $ ltrace env LD_PRELOAD=`pwd`/libft.so ./anti_example AAAAAAAA
__libc_start_main(0x4013e0, 4, 0x7fffdce7d678, 0x403680, 0x403710 <unfinished ...>
strrchr("env", '/') = NULL
setlocale(6, "") = "gl_ES.UTF-8"
bindtextdomain("coreutils", "/usr/share/locale") = "/usr/share/locale"
textdomain("coreutils") = "coreutils"
__cxa_atexit(0x401f40, 0, 0, 0x736c6974756572, 3) = 0
getopt_long(4, 0x7fffdce7d678, "+iu:0", 0x00403be0, NULL) = -1
getopt_long(4, 0x7fffdce7d678, "+iu:0", 0x00403be0, NULL) = -1
strchr("LD_PRELOAD=/home/kenkeiras/faket"..., '=') = "=/home/kenkeiras/faketrace/libft"...
putenv("LD_PRELOAD=/home/kenkeiras/faket"...) = 0
strchr("./anti_example", '=') = NULL
execvp(0x7fffdce7f31c, 0x7fffdce7d688, 0, 1024, 65535 <unfinished ...>
--- Called exec() ---
__libc_start_main(0x400684, 2, 0x7fff279e6eb8, 0x4007b0, 0x400840 <unfinished ...>
ptrace(0, 0, 0, 0, 0x400840) = 0
strcmp("AAAAAAAA", "02468KMOQSk") = 17
puts("Error :/"Error :/
)
|
Listo, y sin tocar el binario.
Referencias:
Stack overflow: UNIX ptrace() block child's system calls
Revista Occams Razor, Número 1