Sei sulla pagina 1di 278

C Y REVERSING (parte 1)

Realmente estoy empezando a escribir un tute al que pomposamente llame C Y REVERSING parte
1 y para los que lo estn leyendo, y tratando de ver de que va, les aclaro que la verdad no tengo la
menor idea de si esto va a ser un tute solo, o varios, y una muy vaga idea de lo que voy a hacer.
Quizas sea que como estoy de vacaciones en el fondo extrao un poco la accin, por eso hay que
ver si cuando vuelva al duro trajn diario, tendr aun fuerzas para seguir con esta serie, por eso,
prefer escribir esta parte sin obligarme a nada, si sale bien, bien, si gusta mejor, si tengo mas ganas
agregare mas, sino la dejare como parte 1 nica, como algo bsico para profundizar algn da.
La idea es Reversear desde cero, primero cdigo fuente en C, lo mas sencillo posible que nosotros
mismos haremos o sacaremos de Internet, y lo trataremos de Reversear en IDA, para encontrar en el
cdigo, nuestras variables, estructuras, funciones, etc, e ir complicando, para hacernos fuertes en
reversing, as cuando tengamos programas que Reversear sin el cdigo fuente como ocurre en la
realidad cotidiana, tendremos una gui de como hacerlo.
Como siempre en todos mis tutes les pido disculpas por mi total mal uso de vocabulario tcnico, de
cualquier manera trato de explicar en forma sencilla, y a pesar de algn error en el vocabulario
tcnico, lo mostrado es tan simple que igual se entiende, para el que sabe y le encante pavonearse
con palabras tcnicas difciles, disclpeme, yo se hacer las cosas que explico, las hago diariamente,
y para ello no necesito saber todos los nombres tcnicos, las hago y listo, aunque para explicar en
un tute a veces eso trae alguna imprecisiones y posiblemente algn purista iluminado se podr rer
de mi uso de las mismas.(como siempre me importa un pito jeje, mientras los que se inician me
entiendan esta todo bien)
Las herramientas que utilizaremos sern el IDA 5.5 mas HexRays que esta en mi web:
http://ricardonarvaja.net/WEB/OTROS/HERRAMIENTAS%20PRIVADAS/F-G-H-I-JK/IDA.Pro.Advanced.v5.5.incl.Hex.Rays.Decompiler.v1.1-iND.7z
Por supuesto el plugin HexRays verifica la licencia conectndose a Internet, as que ademas de
quitar la tilde CHECK FOR UPDATES que aparece en el splash screen del plugin cuando abrimos
un ejecutable en el IDA, conviene trabajar en una maquina virtual, de modo de hacer un snapshot,
para que si algn chequeo hace que se venza, podamos volver al snapshot anterior y vuelva a
funcionar.
Utilizaremos tambin el compilador de C y C++ llamado DEVC++ que aunque es un poquito
antiguo, es fcil de usar, el que quiere podr reemplazarlo por otro, no hay problema, si quieren
bajarse el DEVC++ en mi web esta aqu:
http://ricardonarvaja.net/WEB/OTROS/HERRAMIENTAS/A-B-C-D-E/devcpp-4.9.9.2_setup.exe
Bueno fuera de esto usaremos algunas pocas veces Ollydbg 1.10, algn editor de texto como el
Notepad ++ y no mucho mas segn mis clculos.
Para que no digan que robamos, lo cual es cierto jeje, la mayor parte de los cdigos fuente los
sacaremos del curso de C que se encuentra aqu:
http://www.nachocabanes.com/c/curso/

si alguien tiene ganas de complementar leyndolo a la misma vez que vamos aqu reverseando los
ejemplos pues adelante, es una buena lectura complementaria ya que aqu no haremos un curso de C
sino que usaremos los ejemplos para reversear.
Como siempre comenzaremos con el ejemplo mas sencillo del universo.
#include <stdio.h>
main()
{
printf("Hola");
}

Para los que no conocen C estas tres lineas que quizs sean el mnimo programa, se explican
sencillamente de la siguiente manera.
#include <stdio.h>

Esta linea se agrega para ampliar el lenguaje bsico de C, que no trae incluida en el mismo, la
funcin que permite imprimir en pantalla (printf) es como si fuera un import en python para los que
conocen el mismo, sin este include no podramos imprimir la salida en este caso el texto Hola
main()
{
}

La funcin main es la funcin principal del programa y dentro de las llaves que estn a
continuacin escribiremos el cdigo que queremos ejecutar, en este caso el printf para sacar por
pantalla el texto Hola Mundo
Esta es una explicacin muy bsica, como siempre el que quiera la explicacin completa, puede leer
la del curso de Cabanes en esta pagina.
http://www.nachocabanes.com/c/curso/cc01.php
Bueno abrimos el DEVC++ vamos a FILE-NEW-SOURCE FILE y ah nos abrir un espacio en
blanco para tipear o copiar y pegar nuestro cdigo fuente.

Luego yendo a FILE-SAVE AS lo guardamos fijndonos en cambiar para que lo guarde como
cdigo fuente en C.

Ahora lo compilamos yendo a EXECUTE-COMPILE

Si les aparece algn error, recuerden que en C cada orden debe terminar con punto y coma, en este
caso la orden que le damos para que imprima en pantalla a travs de la funcin printf debe tener un
punto y coma al final, si lo quitamos nos dar un error medio critico como vemos en la imagen de
abajo, as que cuidado con ello.
Tambin el DEVC++ da error si tienen instalado en la misma maquina el MINGW, as que
desinstalen temporalmente el MINGW y todo funcionara normalmente.

Error por falta del punto y coma

As que ya tenemos nuestro ejecutable, ser creado en la misma carpeta que tenemos el archivo
fuente .c, con el mismo nombre pero con extensin .exe.

Corramos el ejecutable dentro de una consola a ver si va bien.

En mi caso como puse un espacio en el nombre tuve que ejecutarlo al mismo entre comillas sino no
lo tomara, vemos el hola impreso en la consola as que funciono, ahora abramos este ejecutable en
IDA.
En este caso como nuestro main es una funcin sin argumentos y sin variables, al menos nosotros
no creamos ninguna, debemos separar las cosas que le agrego el compilador, para entender el
cdigo original, en este caso al verlo en IDA, que se abre directamente en el main y evita mostrar el
cdigo desde el entry point, que por supuesto no es creado por nosotros y es puramente creado por
el compilador para hacer los includes necesarios y poder crear un contexto adecuado para que
nuestro cdigo corra bien.

Aqu vemos que el compilador modifico nuestro main hacindolo una funcin de tres argumentos
que no son nuestros, sino del compilador, y tiene una variable. var_4 que creo el compilador la
cual si hacemos las cuentas devolver siempre 10 y la utilizara el compilador al cerrar el programa,
por supuesto nada de esto es cdigo prpio, vayamos a nuestro cdigo, propiamente dicho.
.text:004012BA mov dword ptr [esp], offset aHola ; "hola"
.text:004012C1 call printf
All vemos que pone en el stack un puntero a la string hola que esta en la seccin rdata, si hacemos
click en aHola, nos muestra que esta exactamente en 403000 y que es el argumento de la funcion

printf.

Si vamos a DEBUGGER y elegimos por ejemplo el debugger local de win32

y ponemos breakpoint alli.

Y damos START PROCESS, en la ventana del stack cuando se detiene cambiamos a JMP TO ESP
para que muestre el cdigo que apunta ESP, o sea el stack y no cualquier cosa como normalmente
hace , jeje.

.
Vemos el puntero a la string hola, que podemos verla en el hex dump, haciendo click derecho
FOLLOW IN HEX DUMP.

Y vemos que all esta.

Es importante que cuando reverseamos separemos la paja del trigo y no nos metamos con el cdigo
que agrega el compilador, ya vimos que lo anterior al main es creado por el mismo y dentro del
mismo main solo dos lineas pertenecen a nuestro cdigo.
Una forma de separar y comprender que agrega de mas el compilador, es variar un poco el cdigo
fuente a este:
# include <stdio.h>
main(){
funcion1();

}
funcion1(){
printf("hola");
}
Como vimos que al main, el compilador le agrega argumentos y variables que usa el mismo, dentro
de main creamos una funcion nuestra llamada funcion1, y dentro de ella si ponemos el printf, si la
compilamos y corremos vemos que funciona igual que la anterior pero al verla en el IDA.

Aqu vemos el main que es similar al anterior y dentro el llamado a nuestra funcin, que como
vemos en este caso no se le pasa argumento ninguno, renombremosla a funcion1 como en nuestro
cdigo, entrando a ella y click derecho en el nombre, rename o edit funcion y le cambiamos a
funcion1.

All le cambiamos el nombre.

Quedara as

Vemos que el nico cambio que hizo es ponerle el int delante, el resto vemos mucho mas limpio el
cdigo y lo que es estrictamente nuestro, no declaramos variables y aqu no hay, no hay argumentos
pasados a la funcion tan cual nuestro cdigo, vemos el puntero a hola, y el llamado a printf.
Si retrocedemos al main vemos que ahora nuestra funcin aqu tambin figura como funcion1.

Este ejemplo no aporta mucho para el reversing pero es importante que uno con la experiencia sepa
no darle importancia a lo que el compilador agrega al cdigo fuente, de forma de poder interpretar
solo las lineas del cdigo original del autor y no fruta, ademas normalmente los programas
contienen cientos de funciones dentro del main y es muy raro tener que reversear el main,
normalmente estaremos reverseando dichas funciones, pero es bueno ver la diferencia y saber que
en el main el compilador mete mucha mas fruta y ver cual es.
A partir de ahora todos los ejemplos que en el curso de C estn escribiendo cdigo en el main,
nosotros los haremos en una funcin dentro del main para poder reversear mejor y no confundir con
tanta fruta.
Para terminar el ejemplo 1 le agregamos la funcin getchar() que queda esperando hasta que se
aprete ENTER en la consola, para que no se cierre la misma y poder arrancar el ejecutable dando

doble click.

# include <stdio.h>
main(){
funcion1();
}
funcion1(){
printf("hola");
getchar();
}
Vemos que al compilarlo nuevamente no hay ninguna diferencia, solo el llamado a getchar para que
no se cierre al ejecutarlo fuera de la consola.

Si le damos ENTER se cerrara.


Veamos el siguiente ejemplo que sumara dos nmeros y reverseemoslo.
# include <stdio.h>
main(){
funcion1();
}

funcion1(){
int primerNumero;
int segundoNumero;
int suma;
primerNumero = 234;
segundoNumero = 567;
suma = primerNumero + segundoNumero;
printf("Su suma es %d", suma);
getchar();
}

Vemos la declaracin de tres variables enteras o int, una llamada primerNumero, otra
segundoNumero y otra suma, las dos primeras las inicializo con los valores 234 y 567 y la variable
suma quedara sin inicializar, y guardara el resultado de la misma.
printf("Su suma es %d", suma);
La instruccin printf solo imprime strings, pero acepta mediante format string reemplazar en este
caso el %d por un la string del numero que se guardo en suma, de esta forma como en suma quedara
el numero 801, y mediante format string se convertir en la string 801 y reemplazara al %d
quedando el mensaje de salida
Su suma es 801
Veamos si funciona primero, compilemos y ejecutemoslo con doble click ya que pusimos el
getchar() al final.

Bueno funciona veamoslo ahora en IDA.

Ese es el main ahora entremos en nuestra funcin.

Primero renombraremos la funcin tal como hicimos la vez anterior usando rename y ya queda con
nuestro nombre, lo mismo haremos con las variables locales, en este caso nosotros le ponemos el
mismo nombre que el cdigo fuente, pues lo hicimos nosotros y los tenemos, pero si fuera una
funcion de un programa que no tenemos el cdigo, habr que renombrar las variables con un

nombre aproximado a lo que interpretemos que hace cada una en el cdigo.


Vemos que var_4 se la inicializa con el valor hexa EA que en decimal es 234 o sea que sera
primerNumero, la renombramos, al igual que a segundoNumero y a suma.

All vemos nuestras variables locales, como la funcion no tiene argumentos, las variables solo
tienen sentido localmente as que no es necesario propagarlas como en el caso de renombrar
argumentos que veremos mas adelante.
El cdigo se interpreta fcilmente
mov
mov
mov
add
mov

[ebp+primerNumero], 0EAh
[ebp+segundoNumero], 237h
eax, [ebp+segundoNumero]
eax, [ebp+primerNumero]
[ebp+suma], eax

Luego de inicializar ambas variables se mueve a EAX el valor de segundoNumero y se le suma el


valor de primerNumero quedando el resultado en EAX, el cual se guarda en la variable suma.
mov eax, [ebp+suma]
mov [esp+4], eax
mov dword ptr [esp], offset aSuSumaEsD ; "Su suma es %d"
call printf
Vemos que aun no teniendo el cdigo fuente, viendo que las variables guardan miembros de una
suma, y una tercera guarda el resultado, pues ya tenemos la posibilidad de ponerles nombres
adecuados a la funcion de cada una.
A continuacin se mueve a EAX ese valor resultado de la suma, el cual se guarda en el stack para
pasarlo como argumento a printf, el otro argumento es un puntero a la string "Su suma es %d"que

se enva tambin al stack ya que en este caso printf recibe dos argumentos.
Si ponemos en el IDA un breakpoint en printf y acomodamos el stack con JMP TO ESP cuando se
detiene vemos en el primer lugar el puntero a la string Su suma es ... , y en segundo lugar el
segundo argumento 321 hexa que es el entero resultado de la suma, que la funcion printf convertir
a string y lo agregara el resto y mostrara en la consola como salida.

Si ponemos un BP apenas entrado en nuestra funcion.

Lgicamente como acabamos de detenernos al entrar en un call, lo primero que habr en el stack,
sera el return address donde volver al terminar de ejecutarse mi funcion, as que si en el mismo
hago JMP TO ESP y veo.

Si hago click derecho FOLLOW IN DISASSEMBLER ver donde volver al retornar de mi


funcion.

Por supuesto volver a la funcion main ya que mi funcion esta dentro de ella, debajo del return
address si mi funcion tuviera argumentos que se pasan con PUSH o se copian al stack antes de
entrar a la misma, lgicamente estaran debajo del return address, pues se pushean antes de entrar,
en este caso al no tener argumentos no veremos nada, aqu debajo marcamos la zona de argumentos
que no esta usada en este caso.

Justo arriba del return address encontraremos dependiendo del tipo de funcion, si lo primero que
hace al entrar en la misma es PUSH EBP, pues all estar guardado el ebp del main o de la funcion
que llamo a la nuestra, y al salir lo recuperar con POP EBP, o LEAVE.
Normalmente despus del primer PUSH EBP, reservara lugar para las variables en el stack.
.text:004012C1 push ebp
.text:004012C2 mov ebp, esp
.text:004012C4 sub esp, 18h
Iguala EBP con ESP y luego le resta a ESP un valor que depende de la cantidad de variables y lugar
necesario que el compilador decida que necesita, en este caso le resta 24 decimal o sea que desde la
direccin del stack donde pusheo el valor de EBP, hacia arriba quedara lugar para las 3 variables
dword y un poco mas de espacio.

Ahora al hacer JMP to ESP ya que ESP cambio, queda a la vista el espacio en rosado reservado, y
justo debajo en celeste el EBP guardado y el return address que habamos visto.
En ese lugar en rosado, debe guardar los valores con los que inicializa las variables si vamos
traceando al pasar por.
.text:004012C7 mov
.text:004012CE mov

[ebp+primerNumero], 0EAh
[ebp+segundoNumero], 237h

Vemos que inicializo las dos variables y el resultado lo guardara en la variable suma que toma su
valor cuando pasamos por aqu.
004012DB mov

[ebp+suma], eax

All vemos como guarda el 321 hexa, resultado de la suma, justo arriba de las dos variables
anteriores.

Resumiendo vemos en el stack que quedaron bien marcadas las 3 zonas, la amarilla de argumentos
no usada en este caso, la celeste con el EBP guardado y el return address y la rosa para las 3
variables locales y de ah hacia arriba habr lugar para lo que necesite, por ejemplo para pasar
argumentos a printf.

Vemos que lo que en el IDA descubrimos debuggeando, si paramos el debugger y miramos el


listado muerto y hacemos doble click en cualquiera de las variables de nuestra funcion, IDA nos
muestra lo mismo, sin tener que debuggear.

Como habamos visto el return address aqu llamado r es el limite, abajo del mismo esta la zona
amarilla de argumentos (en este caso no hay argumentos), arriba del return address esta el EBP
guardado (aqu llamado s por stored ebp) y arriba la zona rosada para las variables, all estn las
tres que creamos, primerNumero, segundoNumero y suma, mas arriba se utilizara en el resto de
nuestra funcion para pasar argumentos, en esta caso a printf.

Si queremos ver como decompila nuestra funcion el HexRays en pseudocode, vamos a VIEWOPEN SUBVIEWS-PSEUDOCODE.
int __cdecl funcion1()
{
printf("Su suma es %d", 801);
return getchar();
}

Vemos que interpreta la funcion (ya que la misma no tiene argumentos ni variables dado que los

valores que suma son constantes siempre), como una funcion que imprime el mensaje Su suma es
801 y nada mas.
En el caso que quisiramos ver como decompila el cdigo completo, hay que ir a FILE-PRODUCE
FILE-CREATE C FILE y tratara de hacer algo mas o menos aproximado a nuestro cdigo aunque
no es exacto ni mucho menos.
En C las variables se pueden inicializar al mismo momento que se crean en ese caso el cdigo
quedara as
# include <stdio.h>
main(){
funcion1();
}
funcion1(){
int primerNumero = 234;
int segundoNumero = 567;
int suma;
suma = primerNumero + segundoNumero;
printf("Su suma es %d", suma);
getchar();
}

Si compilan este y lo abren en IDA y entran a nuestra funcion vern que es exactamente igual al
anterior y no cambio nada.
El siguiente ejemplo usa la funcion scanf, para ingresar una string por teclado, y tal cual printf,
permite format string, por lo cual si el primer argumento es %d, transformara la string tipeada en un
numero, a la inversa de printf, eso si debemos colocar el & delante del nombre de la variable en
scanf ya explicaremos mas adelante porque.
# include <stdio.h>
main(){
funcionSuma();
}
funcionSuma(){
int primerNumero, segundoNumero, suma; /* Nuestras variables */
printf("Introduce el primer numero ");
scanf("%d", &primerNumero);
printf("Introduce el segundo numero ");
scanf("%d", &segundoNumero);
suma = primerNumero + segundoNumero;
printf("Su suma es %d", suma);

getchar();
}

Vemos que luego de declarar las tres variables como int, imprime el mensaje Introduce el primer
numero, all el usuario tipeara una string que debe representar un numero sino dar error, luego lo
mismo para el segundo numero, realizara la suma la cual guardara en la variable suma, e imprimir
la salida mediante printf, compilemos este cdigo y veamos nuestra funcion en IDA.
All estamos en el main y vemos nuestra funcion, entramos y la renombramos, lo mismo que las
variables como hicimos en el ejemplo anterior.

Quedara as:

Si hacemos click en cualquiera de las variables vemos que la disposicin de las misma no cambio
nada con respecto al caso anterior.

Todo lo explicado para el caso anterior aqu se cumple, tenemos el mismo r o return address, arriba
el stored ebp y arriba las tres variables, por supuesto aqu tampoco tenemos argumentos as que la
parte amarilla no la usamos.
lea eax, [ebp+primerNumero]
mov [esp+4], eax
mov dword ptr [esp], offset aD ; "%d"
call scanf
Vemos aqu como se aclara el sentido del & que tuvimos que colocar delante de los nombres de las

variables en el caso de scanf, el smbolo & significa la direccin de dicha variable en el stack no el
valor de la misma, obviamente la variable aun no tiene valor es solo un lugar a llenar en el stack,
por eso mediante el & que equivale al LEA lo que hacemos es darle la direccin en el stack donde
dicha variable esta, para que scanf escriba all y la llene con el valor que tipeamos.

En el primer ejemplo directamente inicializamos el valor de la variable con


mov [ebp+segundoNumero], 237h
pero esta variable es una variable local de mi funcion, solo tiene sentido dentro de la misma y no
tiene sentido dentro de scanf, de esta forma al hacer el LEA obtengo la direccin en el stack de la
variable
lea eax, [ebp+primerNumero]
Luego si se le pasa esa direccin en el stack que esta en EAX como argumento de scanf como en
nuestro ejemplo
En cualquier momento que dentro de scanf que se haga por ejemplo
mov [eax],678h
como EAX tiene la direccin de la variable en el stack, al escribir en el contenido de EAX en
realidad estamos inicializando la variable primerNumero y llenando su contenido.
Si lo debuggeamos lo veremos mas claro.

Al correr el debugger parara all.

Al cambiar con JMP TO ESP vemos en 22ff5c el return address, en 22ff58 el stored EBP, y justo
arriba deben ubicarse las tres variables que tienen aun cualquier valor pues no han sido inicializadas
La variable primer numero se encuentra en 22ff54 y vale 4000 que es la fruta que haba ah en el
stack, de esta forma al hacer el LEA, queda en EAX la direccin 22ff54 de la variable, pues eso es
lo que me interesa pasarle a scanf, para que escriba all y no el 4000 que es basura que hubiera
quedado en EAX de haber hecho mov en vez de lea.

De esta forma se ve claro que al pasarle el la direccin dela variable en EAX, en cualquier momento
que se haga
mov [EAX], XXX
sera lo mismo que hacer
mov [22ff54],XXX
Lo que guardara el valor XXX en dicha variable remplazando el 4000 que era basura y se
inicializara.
Luego si seguimos traceando vemos que en la consola nos pedir que tipeemos un numero, en mi
caso tipee 788 y ENTER.

Al volver veo que guardo en hexa dicho valor en la variable primerNumero.

Luego lo mismo guarda el segundoNumero justo arriba y la suma en la tercera variable, luego

imprime por consola la salida, lo que no funciona en este caso es el bloqueo mediante getchar, debe
quedar como que mantenemos apretado ENTER y continua y se cierra.
main(){
funcionSuma();
getchar();
}
funcionSuma(){
int primerNumero, segundoNumero, suma; /* Nuestras variables */
printf("Introduce el primer numero ");
scanf("%d", &primerNumero);
printf("Introduce el segundo numero ");
scanf("%d", &segundoNumero);
suma = primerNumero + segundoNumero;
printf("Su suma es %d", suma);

getchar();
}
Bueno solucionamos el problema agregando otro getchar el cual si detiene el programa hasta que
apretamos ENTER nuevamente.
Vemos que en este caso el pseudocode creado por el HexRays no puede evitar la creacin de 2
variables ya que all se debe guardar lo que tipea el usuario.
int __cdecl sub_4012C6()
{
int v1; // [sp+10h] [bp-8h]@1
int v2; // [sp+14h] [bp-4h]@1
printf("Introduce el primer numero ");
scanf("%d", &v2);
printf("Introduce el segundo numero ");
scanf("%d", &v1);
printf("Su suma es %d", v2 + v1);
return getchar();
}
Crea dos variables llamadas v1 y v2 y en el scanf le pone el & delante para obtener la direccin de
las mismas en el stack, es casi el mismo cdigo que el nuestro salvo que usa una variable menos
pues no guarda el resultado de la suma.
Bueno la verdad que me canse y no se si a alguien le interesa profundizar en reversing de cdigos
cada vez mas complejos, pero bueno sino quedara esta parte 1 como parte nica para el que le sirva,
veremos tambin las ganas que tenemos de seguir una parte 2 jejeje.
Un saludo a todos los Crackslatinos
Ricardo Narvaja

C Y REVERSING (parte 2) por Ricnar


Bueno ahora lo que sigue es ver un poco el tamao de las variables que declaramos, el que tiene
alguna duda de los sistemas numricos decimal, hexadecimal y octal no lo repasaremos aqu, puede
leer la parte dos del curso de Cabanes donde esta todo bien explicado y sencillo.
http://www.nachocabanes.com/c/curso/cc02.php
Lo que hemos visto al reversear es que al crear una variable como int, el sistema le asigna un lugar
en la memoria, que es un dword o sea 4 bytes, ademas si no especificamos nada y solo declaramos
int, este valor sera tomado como signed int, o sea que se considerara con signo, que en hexa
significa que desde 00000000 hasta 7fffffff se consideraran nmeros positivos y a partir de
80000000 hasta ffffffff se consideraran los negativos siendo el -1 igual a ffffffff, el -2 sera igual a
fffffffe y as hasta el mximo negativo que sera el 80000000.
Vemos que de esta forma usando int o signed int el mximo numero positivo es 7fffffff, ahora si
uno sabe que no va a utilizar nmeros negativos y necesita mas rango puede declarar una variable
como unsigned int y de esta forma siempre sera positiva desde 0 hasta ffffffff ampliando el rango y
eliminando la posibilidad de los negativos.
# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
int primerNumero;
signed int segundoNumero;
unsigned int tercerNumero;
primerNumero = -1;
segundoNumero = -2;
tercerNumero = 2147483650;
printf("El primer numero es %d, ", primerNumero);
printf("el segundo es %d, ", segundoNumero);
printf("el tercer numero es %d.", tercerNumero);
}
Si compilamos y ejecutamos esto vemos que el valor unsigned es mostrado como signed, pero eso
no es porque este mal declarado ya lo veremos sino porque el format string con %d solo convierte
signed int, para imprimir unsigned int debemos usar %u, pero compilemos y miremos un poco
primero este ejemplo.

Vemos que la tercera variable la imprimi tal cual fuera signed, ya que al pasarse del mximo valor
positivo que habamos visto que era 2147483647, por lo tanto 2147483650 corresponde al hexa
80000002 o sea a uno de los negativos mas altos pues el mximo negativo es 80000000h.
Veamos nuestro engendro en IDA

All vemos nuestras tres variables que ocupan un dword cada uno al ser las tres declaradas como int,
sea signed o unsigned.
mov

[ebp+var_C], 80000002h

All vemos que la variable unsigned le asigna el valor 80000002 como hemos visto.
# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
signed int primerNumero;

signed int segundoNumero;


unsigned int tercerNumero;
unsigned int suma;
primerNumero = -1;
segundoNumero = -2;
tercerNumero = 2147483650;
suma= tercerNumero + 2;
printf("El primer numero es %d, ", primerNumero);
printf("el segundo es %d, ", segundoNumero);
printf("el tercer numero es %u.", suma);
}
Ahora vamos a ver como maneja la suma agregamos una variable suma que sera unsigned y al valor
que haba colocado en tercer numero le sumo 2, si fuera negativo, el resultado seria diferente,
veamoslo en IDA, cambiemos tambin el %d por %u para que imprima los unsigned, y al compilar
y ejecutarlo vemos que la suma fue realizada correctamente y se muestra correctamente ahora.

Vemos que considero el numero como positivo y le sumo 2 y el resultado es correcto si hubiera
sumado dos a un numero negativo el resultado seria obviamente diferente.
main(){
funcion2();
getchar();
}
funcion2(){
char primerNumero;
short segundoNumero;
int tercerNumero;

primerNumero = -1;
segundoNumero = -2;
tercerNumero = 500;

printf("El primer numero es %d \n", primerNumero);


printf("el segundo es %d \n", segundoNumero);
printf("el tercer numero es %d \n", tercerNumero);
}

Si compilamos esto veremos en el IDA que char es el equivalente a byte, que short es el
equivalente a word y que int como ya vimos es el equivalente a dword.

All vemos como se acomodaran en el stack las tres variables declaradas, arriba del return address
y el stored ebp y como a cada una el compilador le reserva el espacio justo segn el tipo, para la
variable que es un int sera reservado un dword o dd como muestra el IDA hay 4 bytes de espacio
reservado (de 8 a 4), luego el short ocupara un word o dw en IDA (de 4 a 2) luego habr un byte
sin definir (de 2 a 1) y el char ocupara un byte (de 1 a 0).

Tambin tenemos la posibilidad de manejar floats, para poder imprimirlos se utiliza %f.
# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
char primerNumero;
short segundoNumero;
float tercerNumero;

primerNumero = -1;
segundoNumero = -2;

tercerNumero = 500;

printf("El primer numero es %d \n", primerNumero);


printf("el segundo es %d \n", segundoNumero);
printf("el tercer numero es %f \n", tercerNumero);
}

Para manejar floats utilizara las instrucciones de punto flotante que no explicaremos aqu, el que
quiera ver puede consultar en este link, no es nada del otro mundo FLD carga la variable float en el
stack de punto flotante que no es el mismo que el del programa, y luego FSTP guarda en [esp+4] en
el stack del programa ese valor para pasrselo como argumento al printf.
http://homepage.mac.com/eravila/asmix86a.html

Ahora tenemos el siguiente cdigo que usa la funcion sizeof() para ver el tamao que ocupa cada
variable en la memoria.
# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
char primerNumero;
short segundoNumero;
int tercerNumero;
float cuartoNumero;
primerNumero = -1;
segundoNumero = -2;

tercerNumero = 500;
cuartoNumero = 200;
printf("El tamanio del char es %d \n", sizeof(primerNumero));
printf("El tamanio del short es %d \n", sizeof(segundoNumero));
printf("El tamanio del int es %d \n", sizeof(tercerNumero));
printf("El tamanio del float es %d \n",sizeof(cuartoNumero));
}
Al ejecutarlo

Veamoslo en IDA.

Vemos que el tamao de las variables no se resuelve en tiempo de ejecucin del programa, sino que
lo resuelve el compilador y ya compila con el tamao correcto en cada caso reemplazndolo por la
constante correspondiente.
El tipo de datos CHAR
Vimos que el tipo de datos char ocupa un byte, pero ademas de poder almacenar un numero de un
byte cualquiera como vimos, se usa principalmente para almacenar un carcter.
# include <stdio.h>
main(){
funcion2();
getchar();
}

funcion2(){
char primerNumero;

primerNumero = 68;

printf("El primer numero es %d \n", primerNumero);


printf("El segundo es %c \n", primerNumero);
}
Vemos que guardamos el numero 68 decimal que en la tabla ASCII corresponde a la D, si lo
imprimimos como numero entero mostrara el 68, pero si usamos el format string con %c lo
imprimir como la letra D al convertirlo a carcter.

Si lo vemos en IDA vemos que es la misma variable la que le pasa al printf en ambos casos, solo
que en un caso al ser %d lo muestra como entero y en el otro al ser %c lo muestra como carcter
segn la tabla ASCII.

Vemos que estamos mirando ejemplos sencillos para ver como se manejan las variables e ir
familiarizndonos como se ven en IDA e ir incrementando de a poco la dificultad.

# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
char letra1, letra2;
printf("Teclea una letra ");
scanf("%c", &letra1);
letra2 = 'a';
printf("La letra que has tecleado es %c y la prefijada es %c",
letra1, letra2);
getchar();
}
Vemos dos variables definidas como char, la variable llamada letra1 tomara lo que teclea el usuario
usando la funcion scanf, a la cual se le pasa como argumentos %c para que convierta lo tipeado en
carcter, y el segundo argumento como vimos es la direccin de memoria de la variable letra1 con
el & delante, por supuesto lo veremos como LEA en el IDA, como ya explicamos.
Luego se inicializa la variable letra2 con la a, y se imprimen ambas, la a y el carcter tipeado por
el usuario.

Lo reversearemos como si no conociramos el cdigo y renombraremos las variables y la funcion a


nuestro gusto con los nombres que queramos segn el uso de cada una.

Como todava se supone que no se que hace la funcion, le pondr un nombre cualquiera.

Por ahora le puse como nombre funcion investigada, ahora comenzare a ver que hace cada variable,
para ponerle el nombre correspondiente., al marcar la var_1 se resaltan todos los lugares que la
misma se usa en esta funcion, tambin si es muy grande la funcion, podra apretar la X y me
mostrara en una lista sus referencias.

Obviamente si queremos saber donde se va a inicializar una variable, en el caso que no se inicialice
con una asignacin directa por ejemplo mediante un mov dentro de nuestra funcion, en ese caso
debemos ver donde hay un lea, ya que vimos que cuando el compilador usa el lea obtiene la
direccin de memoria en el stack donde esta ubicada dicha variable, y es seguro que a continuacin,
lo usara como argumento en alguna funcion o call para llenarla o asignarle un valor dentro de el
mismo. (como vimos en los ejemplos anteriores las variables locales solo se pueden inicializar en
forma directa dentro de nuestra funcion, en cualquier api, o call dentro de nuestra funcion, habr
que pasarle la direccin de memoria de dicha variable lo que se hace mediante el & y aqu se vera
como un LEA)
As que aunque la funcion sea largusima, buscar el lea sobre las referencias de la variable que no
esta inicializada en forma directa, nos llevara al punto donde se inicializara la misma.
lea eax, [ebp+var_1]
<----------------------mov [esp+4], eax
mov dword ptr [esp], offset aC ; "%c"
call scanf
As que aunque no tengamos el cdigo fuente y al ver que la direccin de la variable en el stack se
pasa a EAX y de all se guarda en el stack para usar como argumento de scanf, conociendo la api
scanf, nos damos cuenta que dicha variable guardara un carcter, dado que el otro argumento de
scanf es %c.
As que ya se para que sirve la variable, para guardar un carcter tipeado por el usuario, as que le
pondr un nombre acorde a su uso.

Y tomara su valor dentro de scanf.


Vemos que hasta ahora no ejecute el cdigo ni debuge nada y voy sacando conclusiones sobre la
funcion.

Al ver las referencias de la otra variable vemos que no necesita un lea pues se asigna en forma
directa dentro de nuestra funcion con un mov, se le asigna la constante 61 hexa que sabemos que
como la variable es char lo interpretara como la a por la tabla ASCII, as que cambiemos eso.

Si hago click derecho en el 61 hexa, veo que IDA me da la posibilidad de cambiar la representacin
segn el tipo de datos, as que como se que es un char lo cambio a la letra a que se ve en el men
desplegable. Y cambio el nombre de la variable a letra_a.

movsx eax, [ebp+letra_a]


mov [esp+8], eax
movsx eax, [ebp+caracter_tipeado]
mov [esp+4], eax
mov dword ptr [esp], offset aLaLetraQueHasT ; "La letra que has tecleado es %c y la pr"...
call printf
La ultima parte de la funcion manda como argumentos al stack a las dos variables, letra_a y
carcter_tipeado, y ademas tambin la string donde se realizara el format string usando %c en
ambos casos, con lo cual sabemos que imprimir la string.
La letra que has tecleado es %c y la prefijada es %c
pero reemplazara los dos %c por las dos variables una tendra la letra a y la otra letra tipeada por el
usuario que si por ejemplo tipeara una d quedara.
La letra que has tecleado es d y la prefijada es a
Con lo cual ya sabemos que hace la funcion, es una especie de juego para ver si en un solo tiro
acerts la letra prefijada, as que ahora que ya sabemos que hace la funcion le ponemos otro
nombre.

El cdigo fuente que arma el HexRays de nuestra funcion es el siguiente


int __cdecl juego_bastante_tonto()
{
char caracter_tipeado; // [sp+17h] [bp-1h]@1
printf("Teclea una letra ");
scanf("%c", &caracter_tipeado);
printf("La letra que has tecleado es %c y la prefijada es %c", caracter_tipeado, 97);
return getchar();
}
Vemos que como siempre simplifica al mximo y usa una sola variable char en la que guarda el
tipeo del usuario, la otra directamente la suprime y usa una constante 97 decimal, que es 61 en hexa
o sea la letra a, pues como vimos la variable char contiene el valor hexa que luego se transforma en
el printf al valor de la tabla ascII por el %c.
Con lo cual terminamos este sencillo caso de reversing jeje hasta la parte 3 (si hay je)
Saludos Crackslatinos.
Ricardo Narvaja

C Y REVERSING (parte 3) por Ricnar


Continuaremos paso a paso con diferentes cdigos en C, agregaremos ahora la comprobacin de si
se cumple una condicin o if . Dejamos para mas adelante el manejo de cadenas de caracteres, para
seguir el mismo orden que el Curso de C que estamos usando como base para reversear.
Veamos este cdigo:
# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
int numero;
printf("Escribe un numero: ");
scanf("%d", &numero);
if (numero>0) printf("El numero es positivo.\n");
getchar();
}
Vemos que luego de crear una variable int y mostrar el mensaje de que imprima un numero usando
printf, usa scanf para que el usuario tipee un numero el cual guarda en la variable pasandole la
direccin de la misma.
A continuacin el if, en el parntesis siguiente al mismo verifica la condicin, en este caso si el
numero tipeado es mayor que 0 o sea positivo y si es as va a ejecutar el printf que muestra el texto
El numero es positivo, sino continua el programa y va al getchar para luego de que tipeemos
ENTER se cierre.
Veamos como se ve este cdigo sencillo en el IDA, compilemos y abramoslo.

Si yo no conozco el cdigo fuente veo que es una funcion con una sola variable, as que le cambio
el nombre a la funcion, por algo propio, muchos objetan que cambiemos al iniciar el nombre de la
funcion, pero tenemos que entender que muchas veces estaremos reverseando funciones de
programas muy extensos, y el hecho de cambiarle el nombre har que resalte mas si estamos en otra
parte del programa y vemos que llama a la susodicha funcion, que con el nombre original sera una
mas entre todas las funciones con nombres numericos.

A continuacin miraremos la variable, al marcar la misma y apretar X para las referencias vemos
que hay un lea o sea que como ya explicamos all obtendr la direccin de la variable para pasrsela
como argumento a otra funcion o api que la inicializar, vayamos all.

lea eax, [ebp+var_4]


mov [esp+4], eax
mov dword ptr [esp], offset aD ; "%d"
call scanf
Como vemos EAX obtendr mediante el LEA la direccin de dicha variable int, y la misma se
inicializar con el valor que tipee el usuario usando scanf, el mismo ser un entero ya que como
argumento de la api se usa %d.
As que sabemos que la variable int creada sirve para eso, as que pongamosle un nombre acorde a
su uso.

Como dicho int al no aclarar nada es signed, puede ser positivo o negativo, por ello el if que
colocamos en el cdigo el compilador lo transforma en las dos instrucciones siguientes que
contienen la misma condicin de verificar si el numero es mayor que 0 y decidir.
Aqu compara si es mayor que 0.
cmp

[ebp+numero_tipeado], 0

y en la siguiente linea se tomara la decisin de saltar o no segn el resultado de esta comparacin,


vemos que el compilador elijio de todos los saltos condicionales posibles el JLE.
jle

short loc_4012FD

Los que no conocen de saltos condicionales pueden consultar


http://moisesrbb.tripod.com/unidad5.htm#u5111

All vern que el salto JLE toma en cuanta el signo al comparar, por lo cual a todos los efectos
saltara si es menor o igual a cero, y no saltara si es positivo o sea mayor que cero.
Vemos en la imagen que el IDA nos muestra una bifurcacin en el flujo del programa, que nos da la
idea de que puede ir por estos dos caminos, que son las flechas, una verde que muestra el camino si
la comparacin es cierta o sea es negativo y entonces saltea el printf y va directamente al getchar, y
la roja es el camino si la comprobacin es falsa o sea no es negativo, o sea es positivo, con lo cual
no salta y sigue el flujo del programa por el printf donde saca el mensaje de que el numero es
positivo.

Una vez que vemos que la funcion sirve para determinar si el numero tipeado es positivo,
cambiamos el nombre de la misma.

El HexRays es una herramienta que nos sirve como apoyo, veamos como interpreta el cdigo fuente
de nuestra funcin.
int __cdecl numero_tipeado_es_positivo()
{

int numero_tipeado; // [sp+14h] [bp-4h]@1


printf("Escribe un numero: ");
scanf("%d", &numero_tipeado);
if ( numero_tipeado > 0 )
printf("El numero es positivo.\n");
return getchar();
}
Vemos que en este caso la interpretacin es parecida.

Si cambiamos un poco el cdigo y el agregamos un else para que si es negativo imprima un cartel
de ello..
# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
int numero;
printf("Escribe un numero: ");
scanf("%d", &numero);
if (numero>0) printf("El numero es positivo.\n");
else printf("El numero es negativo.\n");
getchar();
}
if-else respeta la comparacin inicial que se realiza en el if, si esta es verdadera, ejecuta lo que esta
al lado del if (o entre llaves si son varias instrucciones), si es falsa ejecuta lo que esta al lado del
else ( o entre llaves si son varias instrucciones)
De esta forma si el numero es negativo va a ejecutar el printf que mostrara El numero es
negativo
No lo reversearemos de nuevo pero veamos en IDA la diferencia.

La comparacin es similar no cambio nada, lo nico que en el camino de la flecha verde o sea si la
comparacin es verdadera (numero negativo) en vez de ir directamente al getchar, ahora agrego un
bloquecito que imprime el mensaje de que El numero es negativo y luego si va al getchar.

En las comparaciones se utilizan los operadores relacionales siguientes:

Si alguien tiene duda de esto por supuesto puede profundizar en la pagina 3 del curso de Cabanes.
http://www.nachocabanes.com/c/curso/cc03.php
A continuacin veremos un switch en el cdigo siguiente, como siempre si quieren una completa y
perfecta explicacin de como funciona un switch en C, pues ah esta en la pagina 3 del curso de
Cabanes, aqu daremos una muy somera.
El switch se utiliza cuando hay una gran cantidad de posibilidades de bifurcacin, seria como si
evaluamos con muchos if uno tras otro, por ejemplo si un numero es cero, toma este camino, si es

uno otro camino, si es dos, otro camino, esto hara el cdigo engorroso si lo hacemos con muchos
if, de esta forma el switch evalu una sola vez una expresin y segn su resultado sigue el camino
correspondiente.
Veamos el cdigo as se entiende mas fcilmente.
# include <stdio.h>
main(){
funcion2();
getchar();
getchar();
getchar();
}
funcion2(){
char tecla;
printf("Pulse una tecla y luego Intro: ");
scanf("%c", &tecla);
switch (tecla)
{
case ' ': printf("Espacio.\n");
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '0': printf("Digito.\n");
break;
default: printf("Ni espacio ni digito.\n");
}

}
Se escribe a continuacin de switch la expresin a analizar, entre parntesis. Despus, tras varias
rdenes case se indica cada uno de los valores posibles. Los pasos (porque pueden ser varios) que
se deben dar si se trata de ese valor se indican a continuacin, terminando con break. Si hay que
hacer algo en caso de que no se cumpla ninguna de las condiciones, se detalla tras default.
Vemos que los casos que no tienen break continan ejecutando el siguiente que viene debajo pues
no salen, tal el caso de si aprieto 1, entra al case 1, como no hay cdigo ni break sigue al 2 y as
hasta abajo que muestra el cartel Digito y si sale porque hay un break, veamoslo en IDA a ver
como se ve el switch.
Vemos en el IDA la estructura que es un poco mas compleja.

Bueno vamos por partes, vemos abajo tres posibilidades ya que realmente en el cdigo C vimos que
o va a Espacio, o va a Digito o va a Ni espacio ni digito

Los tres casos estn claros en el IDA, puedo pintar de colores los bloques, que tambin es otra
ayuda para reversear as los vemos mas resaltados.

Renombro la funcion y vemos que hay dos variables, yo solo use una del tipo char que ocupa solo
un byte, la otra la agrego el compilador, as que si vemos la variable de 1 byte.

Vemos el LEA que obtiene la direccin de la variable para guardar el carcter que tipea el usuario,
vemos que %c hace que la guarde como carcter al pasar scanf.

A continuacin usa la variable dword que creo, para tomar el valor de nuestro carcter y
transformarlo en un dword usando MOVSX lo cual al ser un carcter tipeado podra haber
comparado directamente el byte original, seguramente alguna causa de economa de cdigo.
movsx eax, [ebp+caracter_tipeado]
mov

[ebp+var_8], eax

As que renombramos esta otra variable dword.

Vemos all que luego la compara con 20 que es el espacio, as que cambiamos el 20 por espacio
haciendo click derecho y eligiendo dicha opcin.

All vemos que cuando el carcter sea comparado y sea igual al espacio, ira al bloque verde que
imprime ESPACIO hasta aqu todo bien, cuando la comparacin anterior no sea verdadera, ira por
la flecha roja aqu.

Vemos que compara nuevamente con 20 lo cambiamos tambin a espacio., si es menor que 20 no es
ni espacio ni dgito por lo cual el JL me lleva al bloque rosado, con lo cual ya tenemos dos de las
tres posibilidades del switch.
Si no es menor que 20 seguir por la flecha roja a la siguiente comparacin.

All le resta 30 al valor tipeado y lo compara con nueve y si es mas alto JA nos manda al bloque
rosado ya que seria mas alto que 39 hexa que es el carcter 9, si es menor va al cartel amarillo que
imprime Dgito pues se supone que es un valor entre 30 y 39 o sea los caracteres del 0 al 9.
Los caracteres con valor hexa del 21 al 30 son suprimidos tambin pues al restar con 30 queda un
numero alto mas grande que 9 ya que el JA no considera signo, as que el resultado negativo
producto de esa resta para el JA son solo valores positivos mas grandes que 9 y saltan al bloque
rosado.
Veamos este otro switch

# include <stdio.h>
main(){
funcion2();
getchar();
getchar();
getchar();
}
funcion2(){
int opcion;
printf("Donde nacio?\n");
printf("1: Argentina\n");
printf("2: Espania\n");
printf("3: Colombia\n");
printf("4: Uruguay\n");
scanf("%d", &opcion);
switch (opcion)
{
case 1: printf("Elegiste Argentina.\n");
break;
case 2: printf("Elegiste Espania.\n");
break;
case 3: printf("Elegiste Colombia.\n");
break;
case 4: printf("Elegiste Uruguay.\n");
break;
default: printf("Nada.\n");
}
}
Vemos aqu un switch con un valor numrico que proviene del scanf y se guarda como int en la
variable opcin.

Vemos que es similar solo que en este caso compara directamente con el entero que tipeamos
aunque eso no evita que el compilador cree una nueva variable y copie el contenido de lo tipeado de
la nuestra a esa, sera que cuando usa switch lo debe hacer con una variable mas vaya a saber, la
cuestin es que es similar al caso anterior, pueden analizarlo si quieren.
Aqu el cdigo que nos devuelve el HexRays muy parecido al original

int __cdecl sub_4012D0()


{
int v1; // [sp+14h] [bp-4h]@1
printf("Donde nacio?\n");
printf("1: Argentina\n");
printf("2: Espania\n");
printf("3: Colombia\n");
printf("4: Uruguay\n");
scanf("%d", &v1);
if ( v1 == 2 )
return printf("Elegiste Espania.\n");
if ( v1 > 2 )
{
if ( v1 == 3 )
return printf("Elegiste Colombia.\n");
if ( v1 == 4 )
return printf("Elegiste Uruguay.\n");

}
else
{
if ( v1 == 1 )
return printf("Elegiste Argentina.\n");
}
return printf("Nada.\n");
}
Aqu vemos un ejemplo usando while, el cual se repite mientras la condicin sea verdadera o sea
mientras que el numero que tipeamos sea distinto de cero seguir loopeando y saldr del ciclo
cuando sea igual a cero.
# include <stdio.h>
main(){
funcion2();
}
funcion2(){
int numero;
printf("Teclea un numero (0 para salir): ");
scanf("%d", &numero);
while (numero!=0)
{
if (numero > 0) printf("Es positivo\n");
else printf("Es negativo\n");
printf("Teclea otro numero (0 para salir): ");
scanf("%d", &numero);
}
}
Veamoslo en el IDA.

Hasta aqu nada nuevo ya renombre la variable y como hemos visto el lea obtiene la direccin de la
misma que contendr el numero tipeado por el usuario usando scanf.

All vemos el ciclo que se repetir indefinidamente a no ser que la variable numero_tipeado valga
cero en ese caso ira al bloque verde que es la salida de mi funcion a travs del retn.
Dentro del loop hay una comparacin para ver si el numero es positivo o negativo y segn eso va al
bloque rosado o al celeste imprimiendo el cartel correspondiente.

Luego vuelve a leer otro valor tipeado por el usuario y a guardarlo nuevamente en la variable
numero_tipeado y vuelve al inicio del ciclo donde chequera si es cero para salir del mismo o seguir
repitindolo.

El cdigo fuente de la funcion que nos da el HexRays es el siguiente salvo que crea una variable
mas para el valor de retorno de scanf, que nosotros no creamos.
int __cdecl funcion_investigada()
{
int result; // eax@1
int numero_tipeado; // [sp+14h] [bp-4h]@1
printf("Teclea un numero (0 para salir): ");
result = scanf("%d", &numero_tipeado);
while ( numero_tipeado )
{
if ( numero_tipeado <= 0 )
printf("Es negativo\n");
else

printf("Es positivo\n");
printf("Teclea otro numero (0 para salir): ");
result = scanf("%d", &numero_tipeado);
}
return result;
}
El siguiente es un ejemplo con for

# include <stdio.h>
main(){
funcion2();
getchar();
}
funcion2(){
int contador;
for (contador=1; contador<=10; contador++)
printf("%d ", contador);
}
Es un ciclo que tiene como primer argumento el valor donde se inicia el contador, como segundo la
comparacin para chequear si continua loopeando, y el tercero es el incremento del contador.
En este caso el contador empezara desde 1 inclusive seguir loopeando mientras sea menor o igual a
10, incrementndose de a uno.
Como dentro del loop se imprime el valor del mismo contador, pues imprimir del 1 al 10 en este
caso.

Verlo en IDA es muy sencillo, la variable que renombramos cono contador, se inicializa en el valor
1, y se compara si es mayor que 10 (0a hexa) para ver si sale del ciclo y va al bloque verde.

Si es menor continua en el ciclo e imprime su valor y lo incrementa obteniendo en EAX su


direccin mediante lea y incrementando el contenido de EAX o sea el mismo valor del contador.

Adjunto tres ejemplos de ejecutables con goto, continue y break metidos en el medio, a ver si los
pueden reversear sin tener el cdigo fuente, cualquier duda ya saben que break sale del ciclo,
continue vuelve al inicio del ciclo sin resetear el contador y goto va a una etiqueta como un salto
directo a la misma.
Bueno que tenga suerte y hasta la prxima
Ricardo Narvaja
Salute Crackslatinos

C Y REVERSING (parte 4) por Ricnar


Seguiremos adelante con un ejemplo de arrays, tambin llamado tabla, vector, arreglo o matriz, el
cual es un conjunto de elementos en el cual todos son del mismo tipo, por ejemplo:
int ejemplo[4];
Define un array de cuatro elementos en los cuales cada uno es un int, si se necesita acceder a uno
de los elementos se har mediante un subindice entre corchetes comenzando con el valor cero para
el primer elemento y as sucesivamente, por ejemplo los elementos del array ejemplo son
ejemplo[0], ejemplo[1], ejemplo[2], ejemplo[3].
# include <stdio.h>
main(){
funcion();
getchar();
}
funcion(){

int numero[5];
int suma;

/* Un array de 5 nmeros enteros */


/* Un entero que ser la suma */

numero[0] = 200; /* Les damos valores */


numero[1] = 150;
numero[2] = 100;
numero[3] = -50;
numero[4] = 300;
suma = numero[0] + /* Y hallamos la suma */
numero[1] + numero[2] + numero[3] + numero[4];
printf("Su suma es %d", suma);
}
Vemos que declara un array de 5 nmeros enteros y a continuacin lo inicializa con sus
correspondientes valores, luego halla la suma de todos y lo imprime, veamos como se ve en el IDA
todo esto.

Tenemos all las variables que vamos a tratar de mostrar como array, es de mencionar que un array
como tiene todos los tipos de variable iguales, puede ser perfectamente declarado si uno reversea
este cdigo, como cinco variables int independientes ya que el IDA normalmente las trata as, y
para la funcionalidad, es similar escribir el cdigo como lo hemos hecho antes o escribirlo as.
(aunque en este caso como ya veremos no podremos usar un for para iterar por los campos del array
y habr que sumarlos uno a uno)
# include <stdio.h>
main(){
funcion();
getchar();
}
funcion(){

int suma;

/* Un entero que ser la suma */

int numero0 = 200;


/* Les damos valores */
int numero1 = 150;
int numero2 = 100;
int numero3 = -50;
int numero4 = 300;
suma = numero0 + /* Y hallamos la suma */
numero1 + numero2 + numero3 + numero4;
printf("Su suma es %d", suma);
}

Vemos si lo abrimos en IDA.

No hay ninguna diferencia as que se podra interpretar de ambas formas, volvamos al ejemplo
original con el array.
Volvamos al cdigo original, veamoslo en el IDA.

Renombraremos la variable var_2c como suma ya que vemos que guarda el resultado de la misma
y no es parte del array que esta pintado en verde, luego vamos a la tabla de variables, haciendo
doble click en cualquiera de ellas.

Ahora marcamos la var_28 que es la primera variable del array y apretamos asterisco, vemos que
en la ventana nos sale que el largo de cada elemento del array que lo toma de la variable actual
donde hicimos doble click o sea var_28 es de 4 bytes, y el mximo largo posible sin pisar las
siguientes variables es 1 ya que abajo esta la var_24 pero como nosotros queremos que nuestro
array abarque 5 dwords aunque pise las variables siguientes, en tamao o ARRAY SIZE ponemos 5
y damos OK y luego YES.

Vemos que quedo as

Ahora si vemos en el listado.

Vemos que hay una sola variable var_28 que la renombraremos a numero como en el cdigo
original y es un array, los campos subsiguientes del array se ven como ebp+numero, luego
ebp+numero+4, y as sucesivamente.

Bueno no hay mucho mas que decir de este cdigo, ya tenemos nuestro array, con su nombre
correcto, y sus campos creados en forma correcta, vemos que los suma y los guarda en la variable
suma la cual imprime mediante printf usando %d para mostrar su valor numrico.

Los arrays se pueden inicializar al mismo tiempo que se declaran como cualquier variable el cdigo
en ese caso seria as y el cdigo en el IDA es similar al visto anteriormente.
# include <stdio.h>
main(){
funcion();
getchar();
}
funcion(){

int suma;

/* Un entero que ser la suma */

int numero[5] =
/* Un array de 5 nmeros enteros */
{200, 150, 100, -50, 300};
suma = numero0 + /* Y hallamos la suma */
numero1 + numero2 + numero3 + numero4;
printf("Su suma es %d", suma);
}

Si utilizamos un for para sumar los miembros del array es mucho mas conveniente sobre todo si es
un array muy largo.

# include <stdio.h>
main(){
funcion();
getchar();
}
funcion(){
int suma=0;
int i;

/* Un entero que ser la suma */

int numero[5] ={200, 150, 100, -50, 300};

for(i=0;i<=4;i++) suma += numero[i];


printf("Su suma es %d", suma);
}

Vemos que tenemos que inicializar en este caso la variable suma con 0, pues si no dar error dentro
del for cuando quiera sumar la primera vez y no tenga valor, tambin debemos declarar la variable i
como int que sera el contador en el for tomando los valores de 0 a 4 para usarse como subindice de
numero[i] y sumar todos los miembros, luego al terminar el for imprimir la suma, veamos este
ejemplo en el IDA.

Bueno iremos renombrando las variables vemos que hay dos variable que se inicializan a cero, la
var_C y var_10.

Una rpida inspeccin de lo que hace var_C podemos marcarla y ver donde se usa o apretar X.

Vemos que mediante un lea dentro del loop obtiene la direccin de la misma y luego le va sumando
cosas con un add, ya veremos que, pero podemos colegir que es la variable suma ya que al salir del
loop se utiliza para imprimir su valor mediante printf con el mensaje La suma es....

La renombramos como suma.

La variable var_10 es el contador del loop la llamamos i como en el cdigo fuente, podramos
haberla llamado contador, se ve cuando se inicializa a cero y que el mximo valor sera cuatro, y
cuando sea mas grande que 4, saldr del loop a imprimir.
Definimos el array, realizamos los pasos como en el ejemplo anterior y quedara as.

Ahora si vemos el array con sus campos, veamos que hace dentro del loop que es lo que nos faltaba.

Vemos que EAX tomara el valor de la variable i que empieza en 0 y termina en 4 ya que es el
contador del loop.
mov

edx, [ebp+eax*4+numero]

En la primera vez que loopea como EAX vale 0 se tendr en EDX


mov

edx, [ebp + numero]

Cuando EAX valga 1


mov

edx, [ebp+1*4+numero]

que es igual a
mov

edx, [ebp + numero + 4]

y que siempre termina siendo EDX el campo actual ya que salta de cuatro en cuatro

As que EDX tendr los valores de los campos en dada interacin y a continuacin los suma aqu
obteniendo la direccin de la variable suma y sumndole EDX a su contenido, en cada loop se le
sumara el campo siguiente, al salir del loop la variable suma tendr la sumatoria de todos los
campos y se imprimir como en el ejemplo original.
lea eax, [ebp+suma]
add [eax], edx
CADENAS DE CARACTERES
Las cadenas de texto se crean como arrays de caracteres, lo cual veremos como manejar en el IDA.

Las misma estn formadas por una sucesin de caracteres terminada con un carcter nulo (\0), de
modo que tendremos que reservar una letra ms de las que necesitamos. Por ejemplo, para guardar
el texto Hola usaramos char saludo[5].
# include <stdio.h>
main(){
funcion();
getchar();
getchar();
}
funcion(){

char texto[40];

/* Para guardar hasta 39 letras */

printf("Introduce tu nombre: ");


scanf("%s", &texto);
printf("Hola, %s\n", texto);

}
All vemos que creamos un array sin inicializar que puede guardar hasta 39 caracteres ya que debe
poner el cero final, luego mediante printf se imprime el mensaje "Introduce tu nombre: "); y
luego a scanf se le pasa la direccin de la variable texto usando el & para que llene la misma, luego
se imprime lo que el usuario tipeo en la variable texto precedido de un Hola.
Lindo cdigo para un stack overflow jeje, as que compilemos y veamos el cdigo en IDA.

Vemos que hay una sola variable que es nuestro array llamado texto lo renombramos.

Si vemos las variables haciendo doble click en texto.

Vemos que mas abajo estn el stored ebp y el return address.

No hay que ser un genio para darse cuenta de que si el usuario tipea demasiados caracteres, como
no hay ningn chequeo ni nada, terminara pisando ambos y al continuar la ejecucin del programa y
llegar el retn y al salir de mi funcion habr redirigido el programa a la direccin que halla quedado
all en el return address pisado por mi, segn con que caracteres lo haya hecho, pero bueno que esto
no es un curso de exploits, aunque es bueno remarcarlo je.
En el caso de los arrays volveremos a usar el comando de IDA llamado ARRAY , apretando
asterisco cobre la variable texto.
All IDA se da cuenta que podra crear un ARRAY de hasta 56 bytes antes de pisar el stored ebp y
el return address, como nosotros sabemos que nuestro array es menor elegimos 40 de largo igual el
procesador reserva un poco mas de lugar del necesario, si eligiramos 56 funcionaria de la misma
forma.

De esta forma quedo el cdigo as, no se aprecian camios pues no existe el usa de caracteres
intermedios del array, si fuera ese el caso si se accediera a un carcter especifico por posicin
usando los corchetes como subindices, el cdigo habra cambiado, lo cual veremos en el siguiente
ejemplo.

El cdigo es el siguiente:
# include <stdio.h>
main(){
funcion();
getchar();
getchar();
}

funcion(){
char texto[40];
printf("Introduce tu nombre: ");
scanf("%s", texto);
printf("Hola, %s. Tu inicial es %c\n", texto, texto[0]);
}
Vemos que luego de crear el array de 40 caracteres inclusive el cero final, luego hace un printf
usando un doble format string, primero con %s y el texto tipeado completo como string, y luego
%c con solo el primer carcter que obtiene usando el subindice cero (texto[0])
Si vemos en el IDA el cdigo y renombramos convenientemente.

Vemos que le pasa la direccin de la variable texto mediante el LEA para qiue el usuario lo llene
usando scanf.

Vemos que con movsx mueve el primer byte de lo tipeado y lo pasa como argumento al stack para
el format string %c, luego pasa la direccin a la string completa con lea, para hacer el otro format
string %s.

Definiendo el array.

Al ver el cdigo vemos que no hubo variacin ya que usa solo el primer carcter si cambiamos el
cdigo para que use el segundo.
# include <stdio.h>
main(){
funcion();
getchar();
getchar();
}
funcion(){

char texto[40];
printf("Introduce tu nombre: ");
scanf("%s", texto);
printf("Hola, %s. Tu inicial es %c\n", texto, texto[0]);
printf("Hola, %s. Tu segunda letra es %c\n", texto, texto[1]);
}
Vemos en el IDA que ahora tuvo que crear una variable para el segundo carcter, as que creo dos
variables de un byte lo cual no es lo mas aproximado al cdigo nuestro ya que nosotros usamos los
dos primeros caracteres de un array y no dos variables char sueltas.

Si marcamos la variable superior y definimos el array de 40 caracteres de largo.

Vemos que ahora si tenemos una sola variable array y si vemos el cdigo, este cambio.

Vemos las dos flechas rojas leen el primer carcter y el segundo carcter, pero esta vez como primer
y segundo campo de un array y no como variables separadas el resto es similar al ejemplo anterior.
Bueno en la parte 5 seguimos con mas arrays y estructuras.
Hasta la parte 5
Ricardo Narvaja

C Y REVERSING (parte 5) por Ricnar


Si no se dieron cuenta en los ejercicios anteriores pueden probar y vern que scanf a pesar de que te
deja tipear despus de un espacio, solo lee hasta all, por ejemplo.
# include <stdio.h>
main(){
funcion();
getchar();
getchar();
getchar();
}
funcion(){
char texto[40];

/* Para guardar hasta 39 letras */

printf("Introduce tu nombre: ");


scanf("%s", &texto);
printf("Hola, %s\n", texto);
}
Si ejecuto esto y tipeo pepe genio

Veo que imprime solo pepe.


Si queremos que un programa haga entrada por teclado sin detenerse en un espacio deberemos usar
gets() en vez de scanf, aqu el ejemplo y para tener una mejor salida si es solo imprimir en pantalla
y no necesita format string, podemos usar puts() en vez de printf.
# include <stdio.h>
main(){
funcion();
getchar();
}
funcion(){

char texto[40];

/* Para guardar hasta 39 letras */

puts("Introduce tu nombre: ");


gets(texto);
printf("Hola, %s\n", texto);
}
El printf final como no es solo imprimir en pantalla sino que tiene un format string, no podemos
reemplazarlo por un puts, si corremos este ejemplo vemos que ahora respeta los espacios.

Si lo vemos en IDA.

Vemos en amarillo que le pasa como argumento la direccin donde se encuentra la string al puts,
aunque no lo hayamos especificado con & en el cdigo fuente, y que el gets tambin usa la
direccin de nuestra variable texto como argumento, en estos casos aun sin ser necesario en el
cdigo fuente aclararlo ya gets y puts interpretan que necesitan la direccin de la string, por
supuesto el printf para hacer el format string con un %s necesita tambin la direccin de la misma o
sea de la variable texto y tambin usa lea para hallarla.
Si le agregamos la siguiente linea
# include <stdio.h>
main(){
funcion();
getchar();

}
funcion(){
char texto[40];

/* Para guardar hasta 39 letras */

puts("Introduce tu nombre: ");


gets(texto);
printf("Hola, %s\n", texto);
printf("Has tecleado %d letras", strlen(texto));
}
Vemos que usara strlen para hallar el largo de la string que tipeamos y luego pasara ese valor
numrico como format string %d dentro del mensaje Has tecleado XXX letras.

Por supuesto el espacio vaco es un carcter valido por eso cuenta 9 letras.

Bueno a strlen se le pasa tambin la direccin de nuestra variable texto, y el resultado que devuelve
EAX en rosado, lo pasa como argumento de printf para hacer el format string e imprimir el numero
de caracteres con %d.
Copio este texto del curso de Cabanes que explica como copiar cadenas y esta claro

Asignando a una cadena el valor de otra: strcpy, strncpy; strcat


Cuando queremos dar a una variable el valor de otra, normalmente usamos construcciones como a
=2, o como a = b. Pero en el caso de las cadenas de texto, esta NO es la forma correcta, no podemos
hacer algo como saludo="hola" ni algo como texto1=texto2. Si hacemos algo as, haremos que las
dos cadenas estn en la misma posicin de memoria, y que los cambios que hagamos a una de ellas
se reflejen tambin en la otra. La forma correcta de guardar en una cadena de texto un cierto valor
es:
strcpy (destino, origen);
Es decir, debemos usar una funcin llamada strcpy (string copy, copiar cadena), que se encuentra
tambin en string.h. Vamos a ver dos ejemplos de su uso:
strcpy (saludo, "hola");
Es nuestra responsabilidad que en la cadena de destino haya suficiente espacio reservado para
copiar lo que queremos. Si no es as, estaremos sobreescribiendo direcciones de memoria en las que
no sabemos qu hay.
Para evitar este problema, tenemos una forma de indicar que queremos copiar slo los primeros n
bytes de origen, usando la funcin strncpy, as:
strncpy (destino, origen, n);
Vamos a ver un ejemplo, que nos pida que tecleemos una frase y guarde en otra variable slo las 4
primeras letras:
# include <stdio.h>
# include <string.h>

main(){
funcion();
getchar();
}
funcion(){
char texto1[40], texto2[40], texto3[10];
printf("Introduce un frase: ");
gets(texto1);
strcpy(texto2, texto1);
printf("Una copia de tu texto es %s\n", texto2);
strncpy(texto3, texto1, 4);
printf("Y sus 4 primeras letras son %s\n", texto3);

El problema con strncpy es que copia solo 4 caracteres no pone el cero final como si hace strcpy
que copia hasta que encuentra el cero final incluyndolo y de esta forma si en la variable haba
basura se mostrara como vemos en el ejemplo, despus del cuarto carcter.
# include <stdio.h>
# include <string.h>

main(){
funcion();
getchar();
}
funcion(){
char texto1[40], texto2[40], texto3[10];

printf("Introduce un frase: ");


gets(texto1);
strcpy(texto2, texto1);
printf("Una copia de tu texto es %s\n", texto2);
strncpy(texto3, texto1, 4);
texto3[4] = '\0';
printf("Y sus 4 primeras letras son %s\n", texto3);

}
All le ponemos el cero final manualmente y si lo ejecutamos.

Vemos que el cero que colocamos al final corta la string, de esta forma no importa que haya habido
basura anterior.

Aqu vemos cuatro variables, nosotros creamos 3 que son arrays de caracteres, as que vayamos
observando con cuidado para crear los arrays en forma correcta, la primera variable que vemos que
usa es var_38 que es para guardar el primer texto que tipea el usuario, en nuestro cdigo dicha
variable se llama texto1 y tiene 40 caracteres de largo incluyendo el cero final.
As que vamos a la tabla de variables con doble click en una de ellas.

Vemos que usando todo el espacio disponible hasta el stored ebp, este array podra tener de largo
56 y no habra problema, pues no pisa nada, si reverseamos y no sabemos el largo exacto porque no
tenemos el cdigo fuente, poner 56 seria correcto, pues abarcamos todo el espacio disponible y
funcionaria igual, como sabemos que es 40 el largo lo haremos as pero ambas posibilidades son
correctas.

La siguiente variable que usa es var_68 que la usa en el strcpy para obtener una copia del texto
tipeado en otra variable, en nuestro cdigo se llama texto2 y tambin su largo es 40.

El espacio mximo posible es 48 sin pisar nada, podramos dejarlo as, pero ponemos 40 para
quedar igual al cdigo fuente.
La tercer variable es la que guardara los 4 bytes y esa es texto3 que tiene largo de 10 caracteres.

Vemos que sin pisar otra variable tendra como largo 4 ya que a continuacin viene la var_74 que
en realidad es el quinto carcter de nuestro array texto3 que usa para ponerle el cero final, as que si
abarcamos con el tamao igual a 10, nos quedaremos con solo 3 variables, y la cuarta var_74
desaparecer y pasara a ser un campo del array, donde se pone el cero.

Ahora si vemos el cdigo:

Vemos que ahora si tenemos nuestras tres variables tipo array como en el cdigo fuente y al
convertir texto3 en array desapareci la variable de mas que usaba para modificar un campo
intermedio del mismo para poner un cero en el 5 lugar, ahora mostrndose como campo del mismo
array.
Puse dos ejercicios con arrays para reversear a ver quien me manda a mi privado el cdigo fuente

aproximado reverseado.
Hasta la parte siguiente
Ricardo Narvaja

C Y REVERSING (parte 6) por Ricnar


MOLDES O CASTING ( CASTEO)
En C tenemos la posibilidad de forzar y transforma una variable que esta declarada de un tipo a
otro tipo usando el llamado casteo o moldeo de la misma, veamos varios ejemplos:
Ejemplo 1:
Veamos este cdigo:
# include <stdio.h>
main(){
funcion1();
getchar();
}
funcion1(){
float a = 5.25;
int b = (int)a;
printf ("%d\n", b);
}
Es una variable llamada a declarada como float y asignado un float 5.25 que se castea ponindole
el (int) delante y a no cambia sigue siendo float pero al usar (int)a convertir el valor a int y lo
asignara en b como int, vemos al imprimir el valor de b que vale 5.

Si arrancamos el debugger del IDA y paramos en el inicio

Cambiamos el stack para ver el stack de punto flotante.

Cambiamos el valor para que lo muestre como punto flotante (yo ya lo haba hecho en la primera
imagen, lo volv a repetir para que vea como se hace.
Vemos que var_4 es inicializada con el valor 5.25 float, as que la renombramos como en el cdigo
fuente con el nombre a, recordamos que el sizeof en C de una variable float es 4, as que dword
esta correcto en IDA como largo de la variable a.

FLD carga el valor de a en ST0 del stack de punto flotante.


Hay un par de instrucciones con el Control Word que no son relativas a nuestro valor y aqu.
fistp [ebp+var_8]
FIST convierte a int el float y lo popea de ST0 a var_8 en este caso.
Quiere decir que podemos renombrar var_8 con el nombre b como en el cdigo fuente, las otras
dos variables auxiliares son usadas para trabajar con el control word del punto flotante y no son del
cdigo fuente nuestro as que las dejamos como estn.
Luego de otra instruccin con el control word seguimos la variable b que la pasa al stack para
usarlo como argumento de printf, ya vimos que es un int que vale 5 y all mediante el format string
%d imprimir su valor.
.text:004012EF mov eax, [ebp+b]
.text:004012F2 mov [esp+4], eax
.text:004012F6 mov dword ptr [esp], offset aD ; "%d\n"
.text:004012FD call printf

El siguiente ejemplo es un casteo de char a int.


# include <stdio.h>

main(){
funcion1();
getchar();
}
funcion1(){
char b = 'a';
int x = (int)b;
printf ("%d", x);

All vemos el casteo de la variable char a int y imprime el valor ASCII del carcter a en decimal,
veamos como se ve en el IDA.

All vemos como mueve a la variable declarada como char que aqu es un byte el valor 61h que es
la a minscula lo cambiamos para que muestre la a con el men del click derecho y renombramos la
variable como b.
movsx eax, [ebp+b]
mov [ebp+var_8], eax
All vemos como toma el valor y lo convierte a int mediante el movsx y lo guarda en var_8 que es
la otra variable int as que la renombramos a x.

Luego vemos que sencillamente imprime su valor.


Muchas veces al imprimir el valor haciendo printf este hace un casting on the fly para hacer format
string, depende de lo que le coloquemos luego del %, pero hay que saber que esto es solo visual, si
una variable no fue casteada antes, y solo se muestra por consola su salida en un formato diferente,
si luego se siguen realizando operaciones con la misma variable sin castear, esta seguir en su tipo
original, por lo cual es importante realizarlo antes del printf en nuestro codigo fuente, en el caso
anterior vemos que al pasar la variable x casteada como int al hacer printf se vera el valor int y
ademas x queda siendo una variable int, mientras que si no casteamos antes y hacemos el printf de
la variable b que es un char si ponemos que en el printf lo muestre como %d lo har, mostrara el
valor como si fuera un int, por el casting on the fly, pero no transformara la variable b la cual
seguir siendo de tipo char y para futuras operaciones podra fallar si la usamos como int.

Aqu vemos ese ejemplo claramente el resultado de ambas variables mostrado es similar por el
casteo al vuelo para hacer printf, pero si vemos en IDA veremos que x y b son variables de
diferente tamao y tipo.

# include <stdio.h>

main(){
funcion1();
getchar();
}
funcion1(){
char b = 'a';
int x = (int)b;
printf ("%d\n", x);
printf ("%d\n", b);
}

Vemos que en el primer printf que usa la variable x que es un int, la pasa directamente como
argumento tipo dword para el printf usando %d, mientras que en el segundo printf, lo convierte
temporalmente a dword al vuelo, sin cambiar la variable que es un byte al moverlo a EAX el cual
queda como dword y es pasado como argumento de printf y %d, por lo tanto el casteo es on the
fly y no fue guardado ni cambiado nada, b sigue siendo un char de un byte y seguir por el resto
del programa siendo de un byte, as que no hay que confundirse, no pensar que porque printf lo
muestre como int la variable es un int, aunque visualmente el resultado sea el mismo.
Bueno terminamos lo de casting, quisiera que resuelvan los ejercicios ambos tienen casting, y
quisiera que no solo alguien me enviara el cdigo fuente sino el tute con el reversing explicado as
me salvo de hacerlo yo, jeje
Hasta la parte 7
Ricardo Narvaja

C Y REVERSING (parte 7) por Ricnar

Tablas bidimensionales
Podemos declarar tablas de dos o ms dimensiones. Por ejemplo, si queremos guardar datos de dos
grupos de alumnos, cada uno de los cuales tiene 20 alumnos, tenemos dos opciones:
1) Podemos usar int datosAlumnos[40] y entonces debemos recordar que los 20 primeros datos
corresponden realmente a un grupo de alumnos y los 20 siguientes a otro grupo.
2) O bien podemos emplear int datosAlumnos[2][20] y entonces sabemos que los datos de la forma
datosAlumnos[0][i] son los del primer grupo, y los datosAlumnos[1][i] son los del segundo.
En cualquier caso, si queremos indicar valores iniciales, lo haremos entre llaves, igual que si fuera
una tabla de una nica dimensin. Vamos a verlo con un ejemplo de su uso:
PD :Esta explicacin la copie del curso de C de Cabanes el que necesita la explicacin completa la
puede obtener aqu:
http://www.nachocabanes.com/c/curso/cc05.php
El primer ejemplo es este:
# include <stdio.h>

main(){
funcion1();
getchar();
}
funcion1(){
int notas[2][10] =
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20 };
printf("La nota del tercer alumno del grupo 1 es %d",
notas[0][2]);

}
Vemos que crea una tabla de dos dimensiones o sea 2 x 10, y le asigna los 20 valores tipo int, los 10
primeros corresponden al primer grupo de alumnos, y los 10 siguientes al segundo grupo de
alumnos, luego imprime la nota del tercer alumno del primer grupo que como los campos se
numeran desde cero, sera el valor de la tabla notas (0,2).

Bueno aqu el IDA la embarro bastante, por supuesto no interpreto como array de dos dimensiones
o tabla, si no pescaba los array de una dimensin menos pescara los de dos jeje.
Vemos que creo una variable de un byte y otra de un word, si vemos el espacio que hay entre
variables, vemos que es correcto, pues.

Vemos apretando asterisco en la primera variable, que la misma podra llegar a extenderse 8 bytes
hasta llegar a la otra, ya que como IDA la defini como byte, el mximo largo 8 se refiere a 8 del
mismo tipo o sea 8 bytes.

Si hacemos lo mismo en la siguiente que esta definida como dword.

Vemos que su largo es 20 dwords o sea 80 bytes o sea que el espacio total es 80.
Bueno cambiaremos la primera variable superior a dword y luego apretamos asterisco y le damos el
espacio que sea la suma de los 2 dwords de la primera (8 bytes) mas los 20 dwords de la segunda
variable (80 bytes) as que en total sern 22 dwords o sea 88 bytes un poquito mas largo que el
nuestro pero no hay problema mientras no pisemos el stored_ebp y el return address.
Para cambiar a dword una variable en IDA, nos situamos en la definicin de la misma donde dice
db (byte) y apretamos la tecla D, primero cambiara a dw(no confundir eso es word) y luego
cambia a dd (dword)

Apretando la tecla D vemos que quedo dd como la otra variable

Si vemos en el cdigo:

Ahora las dos variables son DWORD, ahora volvamos a la tabla de variables y apretemos * y
pongamos el largo mximo 22 dwords.
Me olvide de decir que otra forma de saber cuanto es el largo mximo desde esta primera variable,
hasta el stored_ebp sin pisarlo, sin hacer cuentas es eliminar la segunda variable ya que la vamos a
pisar, haciendo click derecho y undefine, as desaparece.
Ahora si apretamos asterisco en la primera vemos que nos dice 22 dwords el espacio mximo ya
que no cuenta la segunda variable como pisada ya que no existe y cuenta hasta el stored_ebp.

Ahora si quedo una sola variable y todo completo.

De cualquier forma vemos que a diferencia de nuestro cdigo quedo un array unidimensional con
un solo index que lo recorre entero, mientras que en nuestro cdigo fuente era un array
bidimensional con dos indices que recorran los dos grupos de 10 alumnos cada uno.

Quiere decir que donde nuestro cdigo muestra imprimir el elemento (0,2) de una tabla de dos
dimensiones de 10 elementos cada campo, en el cdigo del IDA sera un solo indice de 0 a 19 y el
buscado sera el tercer elemento de este array.
Veamos como terminar de interpretarlo as, y luego veremos que podemos hacer para poner doble
indice ya que el IDA no nos deja la posibilidad usando array.
Vemos que inicializara usando memcpy, que necesita tres argumentos, el tamao de lo que va a
copiar en bytes, la direccin desde donde copiar o fuente, y la direccin de destino adonde copiara o
destination.

Sera el size de la copia 50h o sea 80 bytes que es el largo de las 20 notas en dword, si vamos a
402000 que es la direccin de la fuente veremos las notas.

All las vemos, podemos tambin aqu darle formato de array, primero cambiamos db por dd ya que
son dwords all en 402000 apretando D dos veces.

Ahora apretamos asterisco.

El array element es 4 porque cada elemento es un dword, el mximo posible size antes de pisar algo
(en este caso otra referencia) es 21 dwords, pero como sabemos que son 20 los datos, cambiamos a
20, y como son dos grupos de a 10, podemos poner en la segunda linea 10, para que muestre como
dos grupos de a 10, lo cual no tiene efecto en el index, seguir siendo un array unidimensional pero
aqu en la memoria la creacin de arrays es mas flexible al no haber variables que respetar as que
lo muestra correctamente en dos lineas.

Por ultimo al poner -1 en el ancho mostrado de cada elemento los agrupa para que se vean mas
compactos y no deja espacio entre cada uno.

As se ve mejor en la memoria acomodados los datos.


Si posamos el mouse en la flechita que nos muestra la referencia desde donde es llamado este array
vemos el cdigo en el popup y marcado cuando llama a 402000 para pasarlo a EDX y usarlo como
direccin de la fuente de datos del memcpy, por supuesto el destination o donde copiara estos datos
sera nuestra la direccin de la variable notas en el stack, y el largo 80 bytes o sea 50h.

Luego de copiar la data leer la tercer nota, ya que desde el inicio de notas, como son dwords, la
tercera estar 8 bytes mas adelante y luego imprimir ese valor.
mov

eax, [ebp+notas+8]

Si alguien probo vemos que cambiando en la memoria la forma de visualizacin por dos lineas de
10, mejoro la misma, pero en el stack cuando creamos nuestra variable notas si pones 10 en el
mismo casillero no hace un array bidimensional ni modifica la forma de usar los indices, as que
veremos si podemos acomodarlo de otra forma para que se adapte mejor a nuestro cdigo ya que
revisando los libros no encontr forma de hacerlo con arrays, as que les mostrare como lo hago yo,
no se si es la mejor forma pero al menos quedara con doble subindice de la misma forma que el
cdigo original.

Cambiemos notas de nuevo a que sea una variable dword como era originalmente, este mtodo se
puede aplicar sin necesidad de hacer desaparecer la otra variable que haba, funciona igual y la pisa
tal cual como los arrays.

Abramos la ventana de estructuras, no tiemblen que no es tan difcil como lo pintan jeje

Ah vemos que apretando INS o la tecla INSERT creamos una estructura nueva, lo hacemos con
estructuras porque un array bidimensional es una forma de estructura, lo cual no hemos visto aun,
pero sabemos que una estructura puede contener diferentes tipos de campos mezclados, y en nuestro
caso contendr dos arrays de 10 dwords cada campo.

No importa el nombre demos OK.


D/A/* : create structure member (data/ascii/array)
Vemos en la aclaracin que para agregar algo debemos apretar D para datos, A para ASCII y
asterisco para arrays, hagamos esto ultimo.

Vemos que nos creo un array de un solo byte arriba nos dice el tamao de la estructura que por
ahora es uno.

Marcando all y apretando la D nos deja cambiarlo a DWORD ahora apretamos asterisco de nuevo.

All vemos que cada elemento sera un dword y que el tamao mximo aqu no esta limitado pues
estamos en una definicin de estructura fuera del programa, igual sabemos que cada array debe
tener 10 dwords as que ponemos 10.

ya tenemos el primer array para agregar un segundo debemos ir al final a ends y apretar de nuevo
asterisco y repetir lo mismo quedara as.

Listo ya esta creada ahora volvamos a las variables.

Marquemos notas y apretemos ALT mas Q.

Ah sale la que creamos para elegir, damos OK.

Si pasamos el mouse por notas vemos que ahora nos muestra la definicin de la estructura.

Vemos que ahora si la variable notas tiene doble subindice, ya que el primer grupo de 10 se llama
field0, y el segundo field1, podemos renombrarlos en la misma estructura.

Ahora si vemos una estructura que es un array bidimensional con dos indices, ah vemos que al leer
la nota del primer grupo, usa el primer array grupo_1 y indexa por este hasta el tercer elemento del
mismo que sera el que esta 8 bytes mas adelante, no imprimimos un elemento del segundo grupo
pero por ejemplo si lo hubiramos hecho este aparecera como notas.grupo_2+ x, usando el otro
indice.
Lo mas bonito es que como ya tenemos definida la estructura podemos ir a 402000 y cambiar a que
en vez de un array sea la misma estructura esta, apretando ALT mas Q y seleccionndola, quedara
as.

Vemos que marca al final de cada uno el nombre de los arrays grupo_1 y grupo_2.
Bueno esto ademas de servirnos para ver arrays bi o multidimensionales, no sirvi para iniciarnos
con simples estructuras, las cuales profundizaremos en la parte siguiente.
Hasta la prxima
Ricardo Narvaja

C Y REVERSING (parte 8) por Ricnar

ESTRUCTURAS
Hemos utilizado en la parte anterior en IDA estructuras para solucionar parcialmente, la poca
posibilidad que al menos yo conozco en el mismo para manejar arrays o tablas de varias
dimensiones.
Ahora veremos el tema estructuras en C cdigos de ejemplos y reversearemos los mismos.

Una estructura es una agrupacin de datos, los cuales no necesariamente son del mismo tipo. Se
definen con la palabra struct.
Para acceder a cada uno de los datos, tanto si queremos leer su valor como si queremos cambiarlo,
se debe indicar el nombre de la variable o instancia y el del dato (o campo) separados por un punto:
Veamos un ejemplo:
# include <stdio.h>

main(){
funcion1();
getchar();
}
funcion1(){
struct mia
{
char inicial;
int edad;
float nota;
} persona;
persona.inicial = 'J';
persona.edad = 20;
persona.nota = 7.5;
printf("La edad es %d", persona.edad);

}
Vemos resaltado la definicin de la estructura llamada mia, y sus campos, al final del mismo se
coloca el nombre de la instancia en este caso persona, el nombre mia puede no estar si no vamos a
realizar mas de una instancia de este mismo tipo, en el caso de que lo coloquemos podremos
instanciar mas variables con la misma estructura, la cual tiene tres campos un char llamado inicial,
un int llamado edad y un float llamado nota, luego vemos como se inicializan los tres campos y
luego como se imprime persona.edad.

Si lo ejecutamos vemos.

Si quisiramos realizar otra instancia en mia, podramos agregar este cdigo:


# include <stdio.h>

main(){
funcion1();
getchar();
}
funcion1(){
struct mia
{
char inicial;
int edad;
float nota;
} persona,pepe;
persona.inicial = 'J';
persona.edad = 20;
persona.nota = 7.5;
printf("La edad es %d\n", persona.edad);
pepe.edad=30;
printf("La edad de pepe es %d\n", pepe.edad);
}

Esta seria una posibilidad en la declaracin de las estructuras, poner las instancias separadas por
comas en este caso le agregamos la instancia pepe, pero en el caso que quisiramos agregar otra
instancia mas adelante en el programa, podramos hacerlo as.

# include <stdio.h>

main(){

funcion1();
getchar();
}
funcion1(){
struct mia
{
char inicial;
int edad;
float nota;
} persona;
persona.inicial = 'J';
persona.edad = 20;
persona.nota = 7.5;
printf("La edad es %d\n", persona.edad);
struct mia pepe;
pepe.edad=30;
printf("La edad de pepe es %d\n", pepe.edad);
}
Vemos que ahora quitamos pepe de la definicin de estructura mia, pero lo agregamos
posteriormente como otra instancia, como vemos en la instruccin resaltada y haciendo uso del
nombre de la estructura para saber que se agrega a ella, si no tuviera nombre no podramos
adicionarle mas instancias al vuelo que las definidas originalmente.

Veamos el caso mas sencillo en el IDA


main(){
funcion1();
getchar();
}
funcion1(){
struct mia
{
char inicial;
int edad;
float nota;
} persona;
persona.inicial = 'J';
persona.edad = 20;
persona.nota = 7.5;
printf("La edad es %d", persona.edad);

}
Como vemos siempre IDA nos muestra todas variables sueltas

En este caso al campo de la estructura llamado inicial que era un char aqu lo muestra como una
variable char suelta, a los otros dos campos int, llamados edad y nota tambin.
Bueno debemos ir a la ventana estructuras y crear una estructura acorde a los tres campos que
tenemos en ella, sabemos que un array no es pues el mismo no puede contener diferentes tipos de
datos, podran ser solo int o solo char, por ejemplo, pero en este caso hay un campo char y dos int
as que no tenemos duda al reversear que es una estructura.
En la ventana de estructuras apretamos INSERT y ponemos el nombre de la misma en este caso mia
y damos OK.

Ahora hay que agregar un char en este caso apretamos la D y sale ya directamente un campo db.

El nombre de este campo es inicial as que lo renombramos.

Ahora vamos a ends y apretamos D de nuevo pero ahora luego que aparece el nuevo campo sera
db, vamos alli y apretando D nuevamente hasta cambiar a dd.
Debera quedar as ya que edad y nota son dwords.

Ya tenemos la estructura creada ahora vayamos a nuestras variables.

Marcamos la variable superior y apretamos ALT mas Q.

Elegimos la estructura mia que esta all y apretamos OK.

Listo vemos que al lado de var_18 donde dice el tipo de datos, ahora dice mia y que si ponemos el
mouse sobre var_18 nos muestra como es la estructura que tiene 9 bytes de largo ya que tiene un
char de un byte y dos dwords de 4 bytes cada uno.
Ahora renombramos var_18 con el nombre de nuestra instancia de la estructura, la llamamos
persona como en el cdigo fuente.

Si vemos el cdigo vemos que para que sea perfecto hemos cometido una falla.

Vemos la instancia persona del tipo mia bien creada pero el indice vemos que no guarda en
persona.edad el valor sino en persona.edad + 3 y esto es por lo siguiente, a veces hay que tener
cuidado pues las variables originales no estaban pegadas como nosotros armamos la estructura si las
vemos

Vemos que luego de la primera variable que era un byte y que es el primer campo hay tres bytes sin
usar y luego si el segundo campo, as que si nosotros creamos la estructura acorde a esto debemos
meter 3 bytes de relleno, que aunque no usemos, mantendrn los indices en forma correcta,
volvamos a la definicin de la estructura mia.

La borramos y la creamos nuevamente ahora luego del primer campo inicial que es un byte
agregamos tres campos mas de byte con cualquier nombre y luego los otros dos campos dwords
edad y nota.

Volvemos a la definicin de las variables si no nos aparece ninguna variable, lo cual es lo mas
posible y para que quede el cdigo como original, en la funcion nuestra, apretamos click derecho
UNDEFINE

Despus en la misma linea click derecho CODE

y al final click derecho CREATE FUNCION

Este es un truco que siempre funciona y restaura una funcion a como era originalmente sin tener
que cargar todo de nuevo ni reiniciar si nos mandamos alguna macana.

Apretando la barra espaciadora se cambia a modo grfico, ahora vamos a las variables y le
asignamos a var_18 con ALT mas Q la estructura mia.

Ahora veamos el cdigo, renombrando var_18 como persona.

Vemos que los indices quedaron correctos y inicializa correctamente persona.inicial con 4ah que
podemos hacer click derecho y cambiarlo al carcter J, luego persona.edad con 14h y
persona.nota con el valor float que corresponde a 7.5 que tambin cambiaremos marcndolo y
yendo a EDIT-OPERAND TYPE-NUMBR-FLOATING POINT.

Ahora si que quedo perfecto jeje.


No quiero meter mil ejemplos todos juntos en una sola parte para ir de a poquito con este tema en
la parte siguiente veremos mas sobre estructuras y ejemplos mas complicados pero quiero que
digieran bien antes esta parte para luego continuar sin atorarse.
Hasta la parte siguiente:
Ricardo Narvaja

C Y REVERSING (parte 9) por Ricnar


ARRAY DE ESTRUCTURAS
Como ya vimos lo que es un array y ya vimos lo que es una estructura, pues es fcil deducir que un
array de estructuras, es un array cuyos campos son todos iguales y son estructuras jeje.
Si al cdigo del ejemplo anterior lo cambiamos un poco
#include <stdio.h>
main(){
funcion1();
getchar();
}
funcion1(){
struct mia
{
char inicial;
int edad;
float nota;
} persona[20];
persona[1].inicial = 'J';
persona[2].edad = 20;
persona[11].nota = 7.5;
printf("La edad es %d", persona[2].edad);
Vemos que ahora persona es un array de 20 campos del tipo mia cada uno, o sea que habr un
persona[0] donde se podr setear su inicial, edad y nota, un persona[1], etc as sucesivamente, si lo
corremos funciona pues imprimimos persona[2].edad que lo acabamos de inicializar antes.

Veamos en IDA a ver como podemos mostrarlo en una forma que respete los indices y nos
manejemos como la misma variable que el cdigo fuente o sea un array de estructuras.

Las variables son como en el ejemplo anterior, una de un byte y dos dwords y como vimos el IDA
crea variables, para los campos de un array o una estructura que va a usar, los otros que no usa
quedan indefinidos, vemos la var_EC que es un char para guardar la letra J en persona[1].inicial,
luego un montn de bytes indefinidos que son los campos que no usa o no se inicializan, luego lo
mismo los dos dwords para guardar los valores de persona[2].edad y persona[11].nota.

Vamos a estructuras y creamos la misma que antes con los tres bytes basura

Vemos que considerando los tres bytes basura la estructura tiene 0Ch de largo o sea 12 y que si la
hacemos de ese tamao, luego hay 4 bytes que son el primer char del segundo campo, luego los tres
bytes basura o sea 4 bytes en total y ya viene la segunda variable que es un dword sin estar
desfasada as que poner los bytes de relleno parece estar bien sigamos.

Hagamos que var_EC sea del tipo de la estructura mia.

Vemos que coincide pues una ves que var_EC es del tipo mia y abarca 12 bytes como dije, en
amarillo se ve en la imagen que viene el byte que corresponde al siguiente campo, sera
persona[2].inicial, luego en rosa los tres bytes basura y en verde que es lo importante viene el
dword para persona[2].edad que es el que debe coincidir para var_DC.
Ahora como cuando hicimos los arrays vimos que como son todos los campos similares cuando
definimos el primero, solo hay que apretar asterisco en el mismo y poner el largo, en este caso,
ponemos el cursor en var_EC, apretamos asterisco y nos sale.

Si no tenemos ganas de hacer cuentas como sabemos que las otras dos variables que hay debajo
pertenecen a este array de estructuras, las undefinimos para que desaparezcan y luego volvemos a
apretar el asterisco.

Vemos que nos dice que puede haber solo 19 campos como mximo, jeje hemos cometido un error,
y eso es porque no nos dimos cuenta que la primera variable que creo IDA var_EC, corresponde a
persona[1], pero hay un persona[0] justo arriba de 12 bytes que no esta dentro del array que
creamos.

Ah esta si contamos 12 hacia arriba desde var_EC, encontramos el inicio verdadero que esta en
F8, all apretamos ALT mas Q y le asignamos un tipo de estructura mia.

Ahora podemos undefinir la variable var_EC para ver cuanto espacio queda al apretar asterisco.

Ahora si ponemos 20 y vemos que cada campo tiene 12 bytes de largo.

Renombramos a persona.

All vemos que ahora los indices coinciden pues como cada campo tiene 0ch de largo al dividir el
indice por 0c nos dice que campo es.
Como 0Ch / 0Ch es igual a 1
persona.inicial+0Ch es persona[1].inicial
Como 18h / 0Ch es igual a 2
persona.nota+18h es persona[2].nota
Como 84h / 0Ch es igual a Bh o sea 11
persona.edad+84h es persona[11].edad
Coinciden los indices con la estructura de nuestro cdigo fuente.
Ah puse un ejemplo para reversear que incluye en un solo ejecutable casi todo lo que vimos hasta
ahora, tiene switchs, estructuras, strings, loops, bah un poco de todo veamos si pueden hacerlo, sino
lo har yo en la parte siguiente.
Suerte que es bravo jeje
Hasta la parte 10
Ricardo Narvaja

C Y REVERSING (parte 10) por Ricnar


MANEJO DE ARCHIVOS O FICHEROS
Poco a poco vamos avanzando en diferentes temas relativos a C y su reverseo, ahora nos toca el
tema de manejo de archivos, la lectura y escritura en ellos.
#include <stdio.h>
#include <string.h>
main(){
funcion();
getchar();
}

funcion()
{
FILE* fichero;
fichero = fopen("prueba.txt", "wt");
fputs("Esto es una lnea\n", fichero);
fputs("Esto es otra", fichero);
fputs(" y esto es continuacin de la anterior\n", fichero);
fclose(fichero);
}
Para definir una variable del tipo fichero, debemos usar la palabra FILE seguida del asterisco y a
continuacin el nombre de dicha variable, el asterisco significa puntero y en este caso apunta a una
estructura que controla el objeto archivo.
A continuacin para abrir el fichero se usa fopen, a la cual se le pasan como argumentos el nombre
del archivo y el tipo de acceso, w si es para escribir en el, r si es para leer, tambin en este caso
vemos que usa la letra t para aclarar que se un archivo de texto.
En el caso de un archivo abierto para escritura de no existir el mismo se creara y si ya exista se
borrara su contenido y creara vaco nuevamente. (mas adelante se vera como agregar informacin
sin borrar la existente)
Luego se usa fputs para escribir en el archivo, casi es forma similar como hacamos para escribir en
pantalla con puts, solo que aqu habr que especificar los saltos de linea por eso cada string que se
enva a fputs debe terminar en \n si queremos que la siguiente se agregue en la prxima linea, sino
lo har a continuacin, el otro argumento enviando a fputs debe ser el nombre de la variable tipo
fichero.
Al terminar de escribir en el lo cerraremos con fclose(), pasandole tambin como argumento el
nombre de la variable tipo fichero.
Bueno todo muy lindo, si lo corremos vemos que en la misma carpeta donde corre el ejecutable se
crea el archivo de texto llamado prueba.txt, si lo abrimos vemos su contenido.
Esto es una lnea
Esto es otra y esto es continuacin de la anterior

Veamos el cdigo en IDA, renombrando convenientemente.

Pongamos un breakpoint al inicio de nuestra funcion para mirar un poco como funciona.

Si vemos las variables, la variable fichero es un dword ya que es un puntero y luego ya se


encuentra el stored ebp y return address no hay mas nada.

Ahora traceemos un poco.

Cuando llegamos a fopen si miramos en el stack cambindolo con JMP TO ESP para que se
actualice, vemos los argumentos que se le pasan a la misma.

Un puntero a la string Prueba.txt y el otro un puntero a la string wt como en nuestro cdigo fuente.
fichero = fopen("prueba.txt", "wt");
Vemos que el resultado de la llamada a fopen es guardado en la variable fichero, lo mismo vemos
en el IDA al volver de fopen guarda EAX en fichero.
mov

[ebp+fichero], eax

Si vemos la definicin de fopen


http://c.conclase.net/librerias/?ansifun=fopen
Vemos que dice que retorna un puntero
Valor de retorno:
La funcin fopen retorna un puntero al objeto controlando el stream. Si el proceso de
apertura no es realizado a cabo, entonces retorna un puntero nulo.
Bueno vamos viendo ya sabemos que este valor es un puntero, all vemos en EAX su valor.

Podemos verlo tanto en una ventana de desensamblado, haciendo click derecho en EAX y eligiendo
JUMP IN A NEW WINDOW, como en una nueva ventana HEX, que se abre con VIEW-OPEN

SUBVIEWS-HEX DUMP y luego en ella se hace click derecho y se elije que se sincronice con
EAX.

All vemos dicha zona vaca, la misma corresponde a la seccin data de la msvcrt.dll.

Si traceamos hasta pasar el fputs, vemos que se le pasan como argumentos la variable fichero y la
string a guardar en el archivo, vemos que la zona que tenia ceros ahora ya tiene valores.

El tema es que FILE * es un puntero a una estructura que controla el archivo, para el reversing no es
necesario estudiar como es la estructura pues solo se pasa el puntero a la misma y el sistema hace
todo el resto y el programa siempre maneja el puntero pasndoselo como argumento a las apis, pero
veremos por curiosidad un par de cositas sobre ella.

Cuando vamos a la ventana de estructuras y apretamos INS o INSERT.

De la lista desplegable elegimos la estructura FILE.

Quedara agregado

Si lo expandimos apretando +

All vemos la estructura que ocupa 0x20 bytes, as que vamos al listado donde esta el inicio de la
estructura (EAX apuntaba a ella al volver de fopen) y apretamos ALT mas Q.

Su buscamos en el archivo sdio.h que estar en nuestra maquina, ah esta definida la estructura y
coincide aunque no aclara mucho que es cada campo.

Lo mas importante es esto:

All vemos el primer campo es un puntero, la direccin puede variar de maquina en maquina, pero
veamos a donde apunta.

All termina la string Esto es una linea as que este puntero, marca adonde termina el buffer
actualmente o sea a partir de aqu cuando agreguemos mas strings lo har en esta direccin, el otro
puntero llamado base marca el inicio de todo el buffer o sea all debe estar el inicio de la string Esto
es una linea

All esta inicio de buffer y fin del buffer el cual cambiara cuando agreguemos mas data pasando por
mas fputs.

Si seguimos traceando pasando por los restantes fputs veremos como el buffer va creciendo y las
siguientes strings se van apendeando a continuacion, cambiando el puntero de fin de buffer.

As que la creacin del fichero y el manejo de la memoria para escribir en el mismo, es manejado
por esta estructura, donde se almacenan los punteros a las strings que se guardan en un buffer de
sistema.
LECTURA DE UN ARCHIVO
Si queremos leer de un fichero, los pasos son similares, slo que lo abriremos para lectura (el modo
de lectura tendr una r, de read, en lugar de w que se usaba para escritura), y leeremos con
fgets:
Vemos un cdigo que lee el archivo que creamos en el ejemplo anterior (asegrense de que aun
exista jeje)
#include <stdio.h>
#include <string.h>
main(){
funcion();
getchar();
}

funcion()
{

FILE* fichero;
char nombre[80] = "prueba.txt";
char linea[81];
fichero = fopen(nombre, "rt");
if (fichero == NULL)
{
printf("No existe el fichero!\n");
exit(1);
}
fgets(linea, 80, fichero);
puts(linea);
fclose(fichero);

}
Vemos que en este caso creo dos arrays de caracteres uno de largo 80, al cual le asigno la string del
nombre del archivo, podra haberle pasado la string en forma directa como en el caso anterior,
funcionaria sin problemas, hubiera quedado as.

Volvamos al cdigo original, vemos que crea un array de 80 caracteres para leer del archivo, en este

caso independientemente que el sistema mediante la estructura file guarde las strings leidas en un
buffer, nosotros debemos crear una variable propia donde copiara lo ledo, pues debemos
almacenarlo en una variable manejable por mi.
#include <stdio.h>
#include <string.h>
main(){
funcion();
getchar();
}

funcion()
{
FILE* fichero;
char nombre[80] = "prueba.txt";
char linea[81];
fichero = fopen(nombre, "rt");
if (fichero == NULL)
{
printf("No existe el fichero!\n");
exit(1);
}
fgets(linea, 80, fichero);
puts(linea);
fclose(fichero);

}
Vemos que en este caso a fgets le debemos pasar un array de caracteres vaco que sera la variable
que contendr lo ledo, aqu la variable se llama linea, los otros dos argumentos son el tamao de lo
ledo y el puntero al fichero.
Si ejecutamos el programa vemos que fgets leer hasta que encuentra un salto de linea y ah
terminara aunque haya mas caracteres y aunque no lea los 80 que le pasamos como maximo.

Tambin vemos que al abrir el fichero si es devuelto NULL o sea que no existe el archivo se sale
del programa mediante exit.
if (fichero == NULL)
{
printf("No existe el fichero!\n");

exit(1);
Bueno veamoslo en IDA y reverseemoslo.

Bueno aqu tenemos unas cuantas variables vamos a ir renombrando y despejando el camino,
empecemos con la var_C, apretando la tecla X vemos el momento en que se inicializa.

Vemos que se inicializa al volver de fopen por lo tanto var_C es nuestra variable fichero el puntero

a la estructura file, le ponemos el nombre.

Vemos que en las cuatro variables consecutivas var_68, var_64,var_60 y var_5e va leyendo el
nombre del archivo que esta en 403000 en adelante y lo va copiando a dichas variables, si vemos en
403000.

Si queremos ver la string marcamos la zona y hacemos undefine.

As que esta inicializando mi variable array con el nombre del archivo Prueba.txt, y luego el resto
que no usa lo pone a cero usando memset, vemos que en EDX usando lea le pasa la direccin desde
donde tiene que llenar con ceros, luego el tamao o sea 45h (69 decimal) y luego el valor con el
cual va a llenar o sea el cero, esto no es parte del cdigo nuestro lo agrego el compilador para
inicializar correctamente el array con la string Prueba.txt y asegurarse que el resto quede vaco.

As que nuestro array que habamos llamado nombre


char nombre[80] = "prueba.txt";
comenzara en var_68, y las restantes variables que hay debajo hasta fichero son campos del mismo
con las cual trabaja, pero pertenecen al mismo array, as que undefinimos las variables que hay
debajo.

Vemos que hay lugar para 92, podemos tomarlo no hay problema aunque sea mas largo no afectara.

La otra variable que hay mas arriba llamada var_c8 es el otro array donde escribir lo que lee del
archivo.

Si vemos donde la usa, vemos que con lea le pasa su direccin a fgets para llenar su contenido con
lo que lee del archivo, y luego le pasa la misma direccin a puts para imprimirlo por consola.

As que ese es el otro array que en nuestro cdigo habamos llamado linea
char linea[81];
As que creamos el array en dicha variable.

Nos quedaran las variables como en nuestro cdigo fuente con los dos arrays y el puntero fichero.

Si vemos el cdigo ahora, vemos que es mas fcil interpretar que esta copiando partes del nombre
del archivo cuando escribe en nombre, nombre+4, nombre+8 etc

Recordamos que haba una comprobacin de si fichero era NULL, si era as iba a exit, eso esta all.

La variable fichero se comporta en forma similar que en el ejemplo anterior, al pasar por fopen, se
guarda el puntero a la estructura file, en ebp+ fichero.

Si vemos la direccin en EAX

si vemos esa zona abriendo una ventana HEX y sincronizando con EAX.

Vemos que aun esta vaca pero al igual que en el caso anterior a medida que vaya pasando por fgets
en este caso se ira llenado con los campos de la estructura los cuales se manejaran de la misma
forma que en el caso anterior, manteniendo punteros al inicio del buffer y al final temporal del
mismo.

Luego para terminar como vimos fgets leer del archivo y guardara en var_c8 que ahora se llamara
linea y luego con puts mostrara lo que ley del archivo imprimiendo en la consola.
El adjunto a continuacion es casi similar a este ultimo ejemplo y lee el mismo archivo Prueba.txt
me gustara que vieran la diferencia con el que acabo de explicar.
Hasta la parte siguiente
Ricardo Narvaja

FUNCIONES-PARAMETROS-VARIABLES LOCALES Y GLOBALES VALORES DE


RETORNO.
Hemos realizado algunos ejemplos hasta ahora con una funcin principal main que llamaba a otra
funcin que hacia todo el trabajo, pero en general los programas dividen el trabajo en muchas
funciones que hacen pequeas partes cada una.
Del Curso de Cabanes, sobre la importancia de realizar varias funciones que realicen partes del
trabajo en vez de una sola muy complicada.

Veamos como ejemplo un programa como el que hemos realizado anteriormente:


# include <stdio.h>
main(){
funcion();
getchar();
}
funcion(){

int suma;

/* Un entero que ser la suma */

int numero0 = 200;


/* Les damos valores */
int numero1 = 150;
int numero2 = 100;
int numero3 = -50;
int numero4 = 300;
suma = numero0 +numero1 + numero2 + numero3 + numero4;
printf("Su suma es %d", suma);
}
Vemos que se llama a funcion dentro de main y la misma esta declarada despus, en DEV C++ esto
funciona perfectamente pero es bueno saber que otros compiladores no permiten hacer esto, y la
forma correcta para que funcione es siempre es declarar las funciones luego de los include..

# include <stdio.h>
funcion(){
int suma;

/* Un entero que ser la suma */

int numero0 = 200;


/* Les damos valores */
int numero1 = 150;
int numero2 = 100;
int numero3 = -50;
int numero4 = 300;
suma = numero0 +numero1 + numero2 + numero3 + numero4;
printf("Su suma es %d", suma);
}
main(){
funcion();
getchar();
}

Vemos que funciona igual, as que dado que esta forma es mas genrica y aceptada por todos los
compiladores, la adoptaremos y definiremos al inicio despus de los includes, todas las funciones
antes de usarlas.
Si compilan y ven en el IDA vern que quedara similar a como lo hacamos antes, no cambia nada.
PARAMETROS DE UNA FUNCION
Si trabajamos en forma modular, cada funcion realizara pequeas tareas, es muy probable que

debamos pasarle parametros o argumentos para que realice la misma, veamos este ejemplo, una
funcion que le pasamos un numero real y lo imprime en pantalla.

# include <stdio.h>
escribeNumeroReal(float n){
printf("%4.2f", n);
}
main(){
float x=5.1;
escribeNumeroReal(x);
getchar();
}
All vemos que al definir la funcion escribeNumeroReal entre parntesis se colocara el parmetro
que recibir definiendo al mismo tiempo el tipo de datos del mismo, en este caso float n, y cuando
se llame a la funcion de otra parte del programa se colocara entre parntesis en este caso una
variable float que le pasamos o una constante que debe coincidir en tipo con la esperada por la
funcion, para que la misma lo acepte y lo imprima, el cual es el parmetro o argumento pasado a la
misma.
float x=5.1;
escribeNumeroReal(x);
Es necesario que se le puedan pasar parametros a las funciones, porque como ya vimos las variables
locales solo tienen sentido dentro de la funcion donde estan definidas, as que para poder pasar
valores entre funciones, se utilizan los parametros, y tambin lo que veremos luego, los valores de
retorno.

Al compilarlo y ejecutarlo vemos que imprime el valor del float, veamoslo en IDA.

Vemos el main y vemos lo resaltado en la imagen en verde que es lo nico diferente a los main que

venamos haciendo que solo llamaban a una funcion sin parametros.

# include <stdio.h>
escribeNumeroReal(float n){
printf("%4.2f", n);
}
main(){
float x=5.1;
escribeNumeroReal(x);
getchar();
}
Vemos que en el main ademas de las variables y argumentos que creaba el compilador siempre, hay
una variable local mas llamada var_4, vemos que se inicializa con el valor 40A33333h, vayamos al
men de IDA,

Bueno redondeando es el float 5.1 en el cdigo la variable float que se inicializa con dicho valor se
llama x, as que la renombramos.
# include <stdio.h>
escribeNumeroReal(float n){
printf("%4.2f", n);
}
main(){
float x=5.1;
escribeNumeroReal(x);
getchar();
}

Ahora esa variable x es una variable absolutamente local que tiene validez solo dentro del main, si
dentro de la funcion en 401290 quisiera usar el valor float 5.1, al llamar a la variable x, esta no
existir y me dar error, para pasar valores a funciones como vimos se usan los parmetros, y segn
el compilador los mismos se pushean antes de la llamada a la funcion, o como este compilador en
vez de pushear los guarda en el stack, es similar PUSH EAX a hacer MOV [ESP], EAX, aunque
a mi me gustan mas los compiladores que pushean los parametros para mi se ve mas claro.
PUSH EBX
PUSH EAX
call sub_401290
Uno ve los PUSH y sabe por la cantidad los parametros de una funcion a simple vista, mientras que
pasarlos con
mov [esp+4], eax
mov eax, [ebp+x]
mov [esp], eax
call sub_401290
Es menos visual para mi gusto, pero bueno entremos a la funcion.

Si hacemos doble click en arg_0, recordemos que la zona de variables locales era por encima del
stored_ebp y del return address, en este caso no hay variables locales por eso no hay nada por
encima.

Vemos el primer parametro arg_0, la zona de parametros estar siempre por DEBAJO del return
address, lo cual tiene cierta lgica pues si pusheo el parametro antes de entrar a la funcion, este se
guarda en el stack, y luego al entrar a la funcion se guarda encima del mismo el return address
(despus de todo es un CALL y al entrar en el siempre se guarda el return address), y luego
comenzara la funcion generalmente con PUSH EBP, y ah guarda el stored ebp, y luego declarara
las variables arriba del mismo.
As que si hay un solo parametro IDA lo llamara arg_0 y sera el que se pushea justo antes de llamar
a la funcion, si hay mas parametros como en este otro caso:
PUSH EBX
PUSH EAX
call sub_401290
PUSH EBX es ejecutado primero por lo tanto su valor estar mas abajo en el stack que PUSH EAX
en este caso IDA llamara a este segundo parametro arg_4 y estar debajo de arg_0 y as
sucesivamente cuantos mas parametros tenga crecer hacia abajo. (arg_8, arg_C)

All en el rectngulo rojo ira un segundo parametro arg_4 si lo hubiera, bueno volvamos a nuestra
funcion.
Vimos que los parametros se utilizan para pasar valores entre funciones, ya que las variables locales
no sirven para ello, as que al reversear es importante determinar, cual es el valor que se le esta
pasando y renombrarla de acuerdo a eso, en este caso sabemos que arg_0 tiene el mismo valor que
la variable x del main, as que podra renombrarla como x ya que es un parametro que tiene su
valor.

Es importante que el nuevo nombre aparezca en la definicin de la funcion

Si no aparece y queda as

Hacemos click derecho SET FUNCION TYPE y lo arreglara tratando de usar los parametros
renombrados.

A la vez podemos renombrar la funcion a

y all quedara mejor

Si hemos hecho aparecer el nuevo nombre del parametro en la definicin de la funcion, al volver al
main vemos que nos muestra el nombre que le pusimos dentro al parametro, y que como vemos
coincide con el valor de la variable local x. De cualquier manera podra ponersele un nombre
aclaratorio diferente ya que la funcion puede llamarse desde distintos lugares del programa y con
distintos valores, que no siempre sern el de x, lo veremos en el proximo ejemplo.

VALOR DE RETORNO DE UNA FUNCION


Ya vimos los parametros de una funcion para poder pasarle valores, pero hay casos en que
necesitamos ademas de poder pasarle parametros, que la funcion nos devuelva algn resultado que
necesitamos usar en otra parte del programa, por ejemplo si tengo una funcion que le paso dos int y
la misma multiplica los dos valores, puedo necesitar que me devuelva ese resultado para usarlo.

Vemos que la funcion llamada cuadrado, tiene como parametro un int n y el valor de retorno que
se encuentra a continuacion de la palabra return sera el valor de n * n o sea el cuadrado de n.
Vemos que la funcion es llamada desde el main desde aqu.

numero= 5;
resultado = cuadrado(numero);
Se llama a la funcion cuadrado, pero se guarda el valor que devuelve o valor de retorno, por lo
cual cuando se define la funcion cuadrado se debe poner el tipo de valor que devolver delante del
nombre de la misma.
int cuadrado ( int n ) {
return n*n;
}
Por lo tanto cuando se almacena la variable resultado debe ser declarada del mismo tipo o sea int
tambin.
#include <stdio.h>

int cuadrado ( int n ) {


return n*n;
}
main() {
int numero;
int resultado;

numero= 5;
resultado = cuadrado(numero);
printf("El cuadrado del numero es %d", resultado);
printf(" y el de 3 es %d", cuadrado(3));
getchar();
}
As que deben coincidir el tipo del valor de retorno y el tipo de la variable local donde lo guardara.
Si una funcion esta especificada como tipo void significa que no devolver ningn resultado.

Bueno volvamos a nuestro ejemplo


#include <stdio.h>

int cuadrado ( int n ) {


return n*n;
}
main() {
int numero;
int resultado;
numero= 5;
resultado = cuadrado(numero);
printf("El cuadrado del numero es %d", resultado);
printf(" y el de 3 es %d", cuadrado(3));
getchar();
}

Vemos que hay una segunda llamada a la funcion cuadrado, pasandole la constante 3 como
parametro, en este caso no se le pasa el valor de una variable sino directamente una constante, que
lgicamente debe coincidir con el tipo esperado por la funcion.
El valor devuelto por la misma en este caso no es almacenado sino usado directamente dentro del
printf que espera el valor de retorno para realizar el format string e imprimir el resultado del
cuadrado de 3.

Compilemos y veamoslo en IDA.

Vemos all que la funcion es llamada dos veces, la primera es aqu y var_4 corresponde a la
variable numero que se inicializa con el valor 5 y se le pasa como parametro.

Renombrando

Vemos que le pasa como parametro el valor de numero que es 5, entremos a la funcion.

Vemos que el parametro nico sera arg_0 la funcion lee este valor lo pasa a EAX y luego lo
multiplica por si mismo devolviendo en EAX el valor de retorno.
Podemos renombrar a la funcion como cuadrado.

All vemos el valor de retorno que devolver en EAX.


Ahora el tema es el nombre del parametro ya que si le pongo el nombre numero pues en la primera
llamada tiene ese valor, en la segunda vez que la llame cuando tenga el valor 3, ya no coincidir.

En estos casos es difcil aconsejar pues a veces nosotros al reversear necesitamos seguir un
parametro que vemos en una funcion, hacia las funciones padres y muchas veces conviene ponerle
un nombre e ir hacia atrs con el mismo nombre, para no perdernos y poder seguir identificando el
mismo valor que nos interesa.

Ahora propago el nombre haciendo click derecho SET FUNION TYPE.

Quedara asi

Si vamos al main aparece el nombre que le pusimos, y en la primera llamada coinciden ya que toma
el valor de la variable numero, en la segunda bueno, igual me da la idea que al parametro numero
interno se le pasa el valor 3, ya se que el nombre del parametro interno es una ayuda y que no
necesariamente tiene que coincidir 100% con las variables locales del main, pero es bueno que
tenga el nombre alguna relacin para orientarnos.

Hay reversers que no le ponen exactamente el mismo nombre sino que le ponen por ejemplo con

maysculas.

O por ah alguna convencin propia como Arg_numero o algo as.


Vemos que en la primera llamada el valor de retorno de la funcion que devuelve en EAX lo guarda
en var_8 asi que renombraremos la misma a resultado.

Luego ese valor guardado en resultado lo pasa como parametro de printf para imprimirlo.

En la segunda llamada vemos que no guarda el valor de retorno en ninguna variable directamente lo
guarda en el stack como parametro de printf directamente, que era lo que pasaba en nuestro cdigo
fuente.

printf(" y el de 3 es %d", cuadrado(3));


La siguiente definicin se la copiamos a Cabanes

Variables locales y variables globales


Las variables se pueden declarar dentro de un bloque (una funcin), y entonces slo ese bloque las
conocer, no se podrn usar desde ningn otro bloque del programa. Es lo que llamaremos
variables locales.
Por el contrario, si declaramos una variable al comienzo del programa, fuera de todos los bloques
de programa, ser una variable global, a la que se podr acceder desde cualquier parte.

En general, deberemos intentar que la mayor cantidad de variables posible sean locales (lo ideal
sera que todas lo fueran). As hacemos que cada parte del programa trabaje con sus propios datos, y
ayudamos a evitar que un error en un trozo de programa pueda afectar al resto. La forma correcta de
pasar datos entre distintos trozos de programa es usando los parmetros de cada funcin y los
valores de retorno.
En el ejemplo anterior
#include <stdio.h>

int cuadrado ( int n ) {


return n*n;
}
main() {
int numero;
int resultado;

numero= 5;
resultado = cuadrado(numero);
printf("El cuadrado del numero es %d", resultado);
printf(" y el de 3 es %d", cuadrado(3));
getchar();
}
si lo cambiamos a

Vemos que numero y resultado son variables globales por eso son reconocidas en cualquier parte
del programa.

All vemos las dos variables globales, podemos renombrarlas igual, si hacemos doble click en el
nombre nos lleva a la seccin bss adonde se guardan.

La funcion cuadrado no ha cambiado ya que se le pasan los valores como parametro.

Arreglndola

Volviendo al main el valor de retorno se guarda en resultado.

De cualquier forma dentro de cuadrado podramos acceder a las variables globales directamente.
#include <stdio.h>
int numero;
int resultado;
void cuadrado(){
printf("El cuadrado del numero es %d",(numero*numero));
}
main() {

numero= 5;
cuadrado();
getchar();
}
En este ejemplo numero es una variable global y la funcion cuadrado no tiene parametros ni valor
de retorno, si lo compilo y veo en IDA.

All veo la variable global numero la renombro.

Veo que la funcion cuadrado no tiene parametros si entro en ella,

Veo que no tiene argumentos ni variables y que usa el valor de la variable global numero que esta
guardado en la seccin bss.

Tambin me muestra a la derecha la referencia desde donde es llamada dicha variable.

Bueno esto es todo por ahora con el tema funciones no les dar ejercicios aun, los dejare descansar
un poco.
Ricardo Narvaja

Punteros y gestin dinmica de memoria


Por qu usar estructuras dinmicas?
(Del curso de C de Cabanes)
Hasta ahora tenamos una serie de variables que declaramos al principio del programa o de cada
funcin. Estas variables, que reciben el nombre de ESTTICAS, tienen un tamao asignado desde
el momento en que se crea el programa.
Este tipo de variables son sencillas de usar y rpidas... si slo vamos a manejar estructuras de datos
que no cambien, pero resultan poco eficientes si tenemos estructuras cuyo tamao no sea siempre el
mismo.
Es el caso de una agenda: tenemos una serie de fichas, e iremos aadiendo ms. Si reservamos
espacio para 10, no podremos llegar a aadir la nmero 11, estamos limitando el mximo. Una
solucin sera la de trabajar siempre en el disco: no tenemos lmite en cuanto a nmero de fichas,
pero es muchsimo ms lento.
Lo ideal sera aprovechar mejor la memoria que tenemos en el ordenador, para guardar en ella todas
las fichas o al menos todas aquellas que quepan en memoria.
Una solucin tpica (pero mala) es sobredimensionar: preparar una agenda contando con 1000
fichas, aunque supongamos que no vamos a pasar de 200. Esto tiene varios inconvenientes: se
desperdicia memoria, obliga a conocer bien los datos con los que vamos a trabajar, sigue pudiendo
verse sobrepasado, etc.
La solucin suele ser crear estructuras DINMICAS, que puedan ir creciendo o disminuyendo
segn nos interesen.
Todas estas estructuras tienen en comn que, si se programan bien, pueden ir creciendo o
decreciendo segn haga falta, al contrario que un array, que tiene su tamao prefijado.
En todas ellas, lo que vamos haciendo es reservar un poco de memoria para cada nuevo elemento
que nos haga falta, y enlazarlo a los que ya tenamos. Cuando queramos borrar un elemento,
enlazamos el anterior a l con el posterior a l (para que no se rompa nuestra estructura) y
liberamos la memoria que estaba ocupando.
Qu son los punteros?
Un puntero no es ms que una direccin de memoria. Lo que tiene de especial es que normalmente
un puntero tendr un tipo de datos asociado: por ejemplo, un puntero a entero ser una direccin
de memoria en la que habr almacenado (o podremos almacenar) un nmero entero.
Vamos a ver qu smbolo usamos en C para designar los punteros:
int num; //donde "num" es un nmero entero
int *pos; //donde "pos" es un "puntero a entero" (direccin de memoria en la que podremos
guardar un entero)
Esta nomenclatura ya la habamos utilizado aun sin saber que era eso de los punteros. Por ejemplo,
cuando queremos acceder a un fichero, hacemos

FILE* fichero;
Antes de entrar en ms detalles, y para ver la diferencia entre trabajar con arrays o con punteros,
vamos a hacer dos programas que pidan varios nmeros enteros al usuario y muestren su suma. El
primero emplear un array (una tabla, de tamao predefinido) y el segundo emplear memoria
que reservaremos durante el funcionamiento del programa.
#include <stdio.h>
main() {
int datos[100]; /* Preparamos espacio para 100 numeros */
int cuantos; /* Preguntaremos cuantos desea introducir */
int i;
/* Para bucles */
long suma=0; /* La suma, claro */
do {
printf("Cuantos numeros desea sumar? ");
scanf("%d", &cuantos);
if (cuantos>100) /* Solo puede ser 100 o menos */
printf("Demasiados. Solo se puede hasta 100.");
} while (cuantos>100); /* Si pide demasiado, no le dejamos */
/* Pedimos y almacenamos los datos */
for (i=0; i<cuantos; i++) {
printf("Introduzca el dato nmero %d: ", i+1);
scanf("%d", &datos[i]);
}
/* Calculamos la suma */
for (i=0; i<cuantos; i++)
suma += datos[i];
printf("Su suma es: %ld\n", suma);
}
Bueno el programa es similar a los que vimos anteriormente tiene un array de tamao 100,
preparado para ingresar y almacenar datos, y dentro de un do-while que es igual a un while pero
con la condicin al final.
El punto en que comienza a repetirse se indica con la orden do, as:
do
sentencia;
while (condicin);
O sea que aqu se repetira sentencia mientras la condicin sea verdadera, volvamos a nuestro
ejemplo.

#include <stdio.h>

main() {
int datos[100]; /* Preparamos espacio para 100 numeros */
int cuantos; /* Preguntaremos cuantos desea introducir */
int i;
/* Para bucles */
long suma=0; /* La suma, claro */
Aqu esta donde declaramos las variables, el array de enteros de tamao 100 se llama datos, una
variable int llamada cuantos para guardar cuantos datos vamos a ingresar, un int llamado i como
contador para los ciclos, y un long llamado suma, que lo inicializamos a cero, para guardar el
resultado.

do {
printf("Cuantos numeros desea sumar? ");
scanf("%d", &cuantos);
if (cuantos>100) /* Solo puede ser 100 o menos */
printf("Demasiados. Solo se puede hasta 100.");
} while (cuantos>100); /* Si pide demasiado, no le dejamos */
Luego en este while se repetir lo que esta resaltado mientras cuantos sea mayor que 100, o sea te
volver a preguntar la cantidad eternamente, mientras pongas una cantidad mayor que 100, cuando
pongas una cantidad menor, te dejara seguir y salir del while.

for (i=0; i<cuantos; i++) {


printf("Introduzca el dato nmero %d: ", i+1);
scanf("%d", &datos[i]);
}
Luego tenemos un for donde el contador es i, el cual se inicializa en cero y se incrementa de uno en
uno hasta llegar al mximo que es cuantos, dentro de este for, se van introduciendo los campos del
array usando scanf, y guardndolo en los campos del array datos[].
for (i=0; i<cuantos; i++)
suma += datos[i];
printf("Su suma es: %ld\n", suma);
Luego otro for ira sumando todos los campos y guardando en suma y al final lo imprimir.
Si compilamos y lo vemos en IDA, vemos all donde marca la flecha roja donde comienza
realmente nuestro programa ya que lo anterior es agregado del compilador.

Dentro del ciclo

Vemos all resaltada la variable cuantos que es donde mediante scanf se guarda la cantidad de datos
que vamos a ingresar, la renombramos y vemos que la compara si es menor o igual que 64h o sea
100 decimal, as que cambiamos el 64h por 100 decimal para que sea mas visible.

Voy coloreando los bloques que pertenecen al loop

Veo que dentro del mismo si es menor o igual que 100, saltea el printf que dice que son
Demasiados... siguiendo el camino de la flecha verde.

Luego ya comprueba la condicin de salida volviendo a comprobar si cuantos es menor que 64h el
cual cambiamos a 100 decimal, esto nos hace ver que es un while pues no hay contador y saldr
dependiendo de lo que tipee el usuario, por el camino de la flecha verde, sino va a un jmp que
vuelve al inicio del while.

A continuacion vemos un tpico for con su contador que lo llamamos i en el cdigo fuente, vemos
como lo inicia al principio, luego compara la condicin de salida y al final lo incrementa.

Lo renombramos a i.

Vemos la condicin de salida marcada con la flecha, cuando i sea mayor o igual que cuantos, saldr
por all, sino continuara por el siguiente bloque rosado.

La variable var_198 es el inicio de nuestro array debemos definirlo en las variables, aqu vemos
como pasa a EDX la direccin inicial y le va sumando i*4 ( obtenido con shl eax,2), para ir
salteando de cuatro en cuatro los campos del array y guardar consecutivamente en cada uno,
tambin deducimos que all debe haber un array ya que al mirar la zona de las variables, vemos
mucho espacio vaco entre var_198 y lo siguiente definido.

Llega hasta el stored ebp y return address.

As que no hay nada que se pueda pisar hacia abajo, y hacia arriba esta todo ya definido as que
apretamos asterisco, vemos que hay espacio para 102 enteros, como ya sabemos que hay un
mximo de 100 porque el mismo programa nos muestra en los mensajes que el mximo es 100, le
ponemos 100.

Quedara as.

Luego hay otro for que usa el mismo contador i para lo cual lo vuelve a inicializar a cero.

Vemos el for pintado de verde, al inicio la iniclializacion del contador i, luego la comparacin de la
condicin de salida si i es mayor o igual que cuantos, y al final el incremento del contador y vuelta
al inicio.

Vemos que dentro del for va tomando la var_198 que es nuestro array que debemos renombrar
como datos y va leyendo los valores de cada campo del mismo saltando de 4 en 4 usando i como
contador y multiplicndolo por 4 aqu.
mov

edx, [ebp+eax*4+datos]

As que en EDX tendremos el valor de cada campo al loopear y eso lo guarda en la variable
var_1a4 que sera la variable suma ya que con lea obtiene la direccin y le suma siempre EDX que
es valor del campo actual, a la salida tendremos all la suma de todos valores de los campos del
array
As que renombramos dicha variable var_1a4 como suma.

Al salir del for imprime el valor de suma.


Ahora veremos un ejemplo de un programa que es similar, vemos que el ejemplo anterior no es
eficiente, ya que se puede sumar a medida que se lee y ademas pongamosle que tengamos que
almacenar para realizar clculos mas complejos, en realidad reservamos 100 dwords de memoria
para sumar por ah dos o tres nmeros lo cual es un desperdicio, veamos como se hace reservando
la memoria justa usando los nuevos conceptos.
#include <stdio.h>
#include <stdlib.h>
main() {
int* datos; /* Necesitaremos espacio para varios numeros */
int cuantos; /* Preguntaremos cuantos desea introducir */
int i;
/* Para bucles */
long suma=0; /* La suma, claro */
do {
printf("Cuantos numeros desea sumar? ");
scanf("%d", &cuantos);
datos = (int *) malloc (cuantos * sizeof(int));
if (datos == NULL) /* Si no hay espacio, avisamos */
printf("No caben tantos datos en memoria.");
} while (datos == NULL); /* Si pide demasiado, no le dejamos */
/* Pedimos y almacenamos los datos */
for (i=0; i<cuantos; i++) {
printf("Introduzca el dato nmero %d: ", i+1);
scanf("%d", datos+i);
}
/* Calculamos la suma */
for (i=0; i<cuantos; i++)
suma += *(datos+i);
printf("Su suma es: %ld\n", suma);
free(datos);

}
Si lo ejecutamos vemos que funciona en forma similar al anterior.

Ahora veamos el cambio en el cdigo fuente.


main() {
int* datos; /* Necesitaremos espacio para varios numeros */
int cuantos; /* Preguntaremos cuantos desea introducir */
int i;
/* Para bucles */
long suma=0; /* La suma, claro */

Cuando declaramos int* datos; en vez de int datos[100]; La diferencia es que ahora no
reservamos 100 dwords fijos en la memoria sino que tenemos una variable que es un puntero a una
zona que habr ints pero no sabemos cuantos, los mismos podrn ser tantos como la memoria de la
maquina nos lo permita.
As que datos ahora es una variable puntero a int, y por ahora no esta inicializada cuando lo
hagamos guardara una direccin o sea un puntero al int o a los ints., se debe ver bien esta diferencia
no es lo mismo que una variable guarde datos que guarde un puntero adonde estarn los datos.
do {
printf("Cuantos numeros desea sumar? ");
scanf("%d", &cuantos);
datos = (int *) malloc (cuantos * sizeof(int));
if (datos == NULL) /* Si no hay espacio, avisamos */
printf("No caben tantos datos en memoria.");
} while (datos == NULL); /* Si pide demasiado, no le dejamos */
Aqu esta la clave del asunto, al igual que antes nos pregunta cuantos nmeros vamos a sumar y lo
guarda en la variable cuantos.
Ahora realizamos la cuenta de cuanto espacio necesitaremos en la memoria segn la cantidad de
enteros a sumar, la misma sera la cantidad de enteros que esta en cuantos por el tamao que ocupa
en bytes cada int, lo que se halla haciendo sizeof(int) lo que nos devuelve 4.
cuantos * sizeof(int)

As que esto nos dar el tamao en bytes que necesitamos en memoria, el cual se pasa a la funcion
malloc que reserva dicho tamao y nos devuelve un puntero al mismo.
datos = (int *) malloc (cuantos * sizeof(int));
Como datos es un int * conviene castear el resultado de malloc ya que realmente devuelve un
puntero a la memoria que acaba de reservar, pero es un numero, as que como vimos en la parte de
casting forzamos a que el programa lo interprete como int * de forma de que no haya problemas de
asignacin en datos y nos de error al compilar.

El resto del programa es similar:


/* Pedimos y almacenamos los datos */
for (i=0; i<cuantos; i++) {
printf("Introduzca el dato nmero %d: ", i+1);
scanf("%d", datos+i);
}

/* Calculamos la suma */
for (i=0; i<cuantos; i++)
suma += *(datos+i);
Vemos primero que nada, que en este caso la funcion scanf no necesita & delante de donde
guardara los datos, sabemos que ello hacia que se halle la direccin a la variable donde se
guardaran los datos, en este caso la misma ya es un puntero asi que no necesita &.
Lo siguiente diferencia es que antes en un array recorramos los campos del mismo como datos[0],
datos[1] y eso iba saltando entre los mismos, aqu sera *datos, el segundo *(datos+1), el tercero
ser *(datos+2) y as en adelante. Por eso, donde antes hacamos suma += datos[i]; ahora usamos
suma += *(datos+i); el significado es que siempre sera un puntero e ira recorriendo la memoria
apuntando al dato actual, sumndole valores constantes al puntero inicial, con lo cual se obtiene un
nuevo puntero.

Tambin aparece una llamada a free que es lo contrario de malloc, liberara la zona reservada de
memoria para que se pueda disponer de ella, ya que no la usamos mas.

Vemos el programa en el IDA a ver las diferencias.

Vemos que una vez que guarda en cuantos la cantidad de nmeros a sumar, lo multiplica por 4 para
hallar el tamao de bytes a reservar en la memoria, y eso lo pasa como argumento a malloc que nos
devolver el puntero a la zona reservada de dicho tamao, el puntero lo guarda en var_4 que es
nuestro puntero int * llamado datos as que lo renombramos, aqu no hay desperdicio de memoria
pues el tamao de un puntero es un dword que es lo que vemos que reservo para guardar ese valor.

Aqu vemos como maneja el puntero cuando guarda los datos dentro del for

La variable i sera igual que antes, el contador, lo multiplica por 4 y lo suma al puntero datos, de esa
forma cuando i valga 0, el puntero datos apuntara al inicio de la zona reservada, luego cuando i
valga 1, sera por lo tanto i*4, y eso se le sumara al puntero al inicio con lo cual obtendremos un
nuevo puntero al segundo int ya que vamos barriendo de 4 en cuatro, sumndole al puntero original,
para que quede mas claro lo debuggearemos.

Arranco el programa y me pide cuanto nmeros sumare le pongo como ejemplo 4, luego parara en
el llamado a malloc.

El 4 que ingrese lo guarda en cuantos y lo multiplica por 4 para hallar el tamao a reservar que sera
16, o sea 10 hexa como vemos en EAX y en el stack si hacemos JMP TO ESP, ese sera el tamao de
la memoria a reservar, pasemos el call malloc con f8.

All vemos que en EAX nos devuelve un puntero adonde reservo la memoria, la direccin puede
cambiar de maquina en maquina pero si miro dicha zona.

All hay lugar para 4 dwords y esta relleno con 0D F0 AD BA (bad food) porque el programa esta
siendo debuggeado para que veamos bien la zona reservada, sino serian ceros, jeje y donde termina
la zona reservada vemos ABABABAB (tambin solo en modo debug).
Si traceamos y llegamos hasta dentro del for aqu

Vemos que i vale 0 y se mueve a EAX.

Luego con el SHL EAX, 2 lo multiplica por 4, el resultado sera cero.

Luego toma nuestro puntero datos y le suma el resultado obtenido o sea cero, recordemos que es
un int * asi que al sumarle una constante dar un puntero en este caso al sumarle cero dar el mismo
valor que apunta al primer int de la zona de memoria reservada donde guardara lo que tipeamos,
apretamos f8 hasta que pasamos el scanf.

All guardara el primer valor 21 hexa que es 33 decimal.

Si repetimos traceando con f8 y llegamos al mismo bloque nuevamente.

Ahora el contador i vale 1, lo multiplica por 4 con el SHL lo que dara 4.

Lo suma al puntero que tenemos en datos.

Quedando en EAX un nuevo puntero al segundo int a guardar.

Si pasamos el scanf con f8 veremos como guarda all el segundo valor.

As que lo que se indexa aqu es el puntero, y se le van sumando en este caso de 4 en 4 para ir
guardando en la zona reservada en forma correcta, lo mismo cuando lo lea, tomara el puntero a
dicha zona y le ira sumando cuatro para leer los valores guardados, si ponemos un BP en el call a
free al final luego de guardar los cuatro valores y realizar la suma.

Al llegar al free se le pasa como argumento el puntero a la zona reservada

Si lo pasamos con f8 vemos que dicha zona cambio y ya no tiene nuestros datos sino punteros, eso
se estudiara mas adelante el mecanismo que tiene el sistema para saber como una zona esta libre
para reservar o no.

All hay un ejemplo de punteros a resolver lo nico molesto es que usa floats y eso a veces es un
poco molesto de ver en IDA pero bueno es as la vida jeje.
Hasta la prxima parte
Ricnar

MAS SOBRE PUNTEROS-INCREMENTAR PUNTEROS


Tratemos de reforzar bien el concepto de punteros aunque seamos redundantes.
Es sencillo entender que si declaramos una variable int
int pepe =5
y hacemos
pepe ++
el valor de pepe valdr 6, eso esta bien claro, el cdigo completo

#include <stdio.h>
#include <stdlib.h>
main() {
int pepe =5;

pepe ++;
printf ("%d",pepe);
getchar();
}
Si lo corremos muestra que pepe vale 6

Ahora veamos el siguiente cdigo:


#include <stdio.h>
#include <stdlib.h>
main() {
int * punt;

punt = (int *) malloc (sizeof(int));


*punt = 3;

printf ("%x \n",punt);


punt ++;
printf ("%x\n",punt);
getchar();
}

declaramos una variable llamada punt que es un puntero


int * punt;
Lo inicializamos, por supuesto tendr una direccin de memoria que devuelve el malloc
punt = (int *) malloc (sizeof(int));
guardamos un 3 en su contenido o sea en la direccin adonde apunta
*punt = 3;
Y luego incrementamos punt
punt ++;
Nosotros hemos incrementado el valor de punt. Como punt es un puntero, estamos
modificando una direccin de memoria. Por ejemplo, si punt se refera a la posicin de memoria
nmero 10.000 de nuestro ordenador, ahora ya no es as, ahora es otra posicin de memoria distinta.
Como ya sabemos, el espacio que ocupa una variable en C depende del sistema operativo. As, en
un sistema operativo de 32 bits, un int ocupara 4 bytes, de modo que la operacin
punt++;
hara que pasramos de mirar la posicin 10.000 a la 10.004.

Por si alguien tiene dudas ejecutemoslo:

Como le puse %x me mostrara el valor hexadecimal de punt, vemos que en mi maquina (en otras
variara), punt vale 0x3e2480 y luego de incrementarlo valdr 0x3e2484 ya que se incrementa de
cuatro en cuatro para apuntar a un segundo int, que no hemos inicializado.
Si quisiramos incrementar el 3 que guardamos en el contenido de punt deberamos hacer.
(*punt) ++;
En este codigo
#include <stdio.h>
#include <stdlib.h>
main() {
int * punt;

punt = (int *) malloc (sizeof(int));


*punt = 3;

printf ("%x \n",punt);


printf ("%x\n",*punt);
(*punt) ++;
printf ("%x\n",*punt);

getchar();
}
Vemos que primero imprimo el valor del puntero all resaltado en verde, luego imprimo su
contenido y luego lo incremento de 3 a 4 y luego lo imprimo nuevamente.
printf ("%x \n",punt);
printf ("%x\n",*punt);
(*punt) ++;
printf ("%x\n",*punt);

All vemos que se incremento de 3 a 4.


La conclusin de todo esto es que para incrementar punteros usaremos
punt ++
y para incrementar su contenido
(* punt) ++
Lo mismo que pasa asignar valores a un puntero se har directamente en punt y para asignar en su
contenido se har en *punt.

Asignando valores a punt


punt = (int *) malloc (sizeof(int));
Asignando valores a su contenido
*punt = 3;
Es muy importante que esto quede claro, pues es la piedra fundamental de todo lo que sigue, por si
alguno le quedo alguna duda veamoslo en IDA.
All vemos el cdigo luego de lo agregado por el compilador

Vemos que llama a malloc con el size 4 y que el resultado lo devuelve en var_4 que sera nuestro
punt, as que lo renombramos, sabemos que punt es un puntero o sea que sera una direccin de
memoria, si ponemos un breakpoint all.

Vemos que EAX tiene el valor de punt que en mi maquina es 0x3e3ce0 y que como lo arrancamos
en un debugger y le pedimos 4 bytes en la memoria el contenido de 0x3e3ce0 tendr BAAD FOOD
jeje como dijimos en las partes anteriores y ABABABAB donde termina la zona reservada.

La cuestin es que luego de guardar EAX en punt lo que corresponde a nuestro cdigo fuente
punt = (int *) malloc (sizeof(int));
Lo vuelve a mover a EAX y guarda en su contenido, donde hace
004012CC mov

dword ptr [eax], 3

esta realizando lo que en nuestro cdigo fuente era


*punt = 3;
Y modificando el contenido de punt, si pasamos con f8 dicha instruccin.

Vemos que guardo el 3.


Luego vemos los tres printf

En el primero lee el valor de punt y lo imprime.


.text:004012D2 mov eax, [ebp+punt]
.text:004012D5 mov [esp+4], eax
.text:004012D9 mov dword ptr [esp], offset asc_403000 ; "%x \n"
.text:004012E0 call printf
Si traceamos con f8 y lo pasamos al printf vemos en la consola el valor de punt.

El siguiente printf debera imprimir el contenido de punt o sea *punt.


.text:004012E5 mov eax, [ebp+punt]
.text:004012E8 mov eax, [eax]
.text:004012EA mov [esp+4], eax
.text:004012EE mov dword ptr [esp], offset asc_403005 ; "%x\n"
.text:004012F5 call printf
All vemos la instruccin donde obtiene el contenido de punt o sea el valor 3 si pasamos el printf
con f8, vemos en la consola el valor de *punt.

En el ultimo printf incrementbamos *punt de 3 a 4 y lo imprimamos veamos.


.text:004012FA mov eax, [ebp+punt]
.text:004012FD inc dword ptr [eax
En EAX estar punt y incrementara su contenido mediante la instruccin en verde, pasando el
mismo a 4 si pasamos con f8 el INC.

Que fue el equivalente en nuestro cdigo de


(*punt) ++;
Luego mueve el 4 a EAX y lo imprime.
.text:00401302 mov eax, [eax]
.text:00401304 mov [esp+4], eax
.text:00401308 mov dword ptr [esp], offset asc_403005 ; "%x\n"
.text:0040130F call printf.

Al llegar a este punto muchos que trabajaron en bajo nivel siempre se preguntan, que diferencia hay
entre & y *, ya que ambos son direcciones de memoria, y realmente la diferencia es mas de C que
de bajo nivel, pero si entendimos todo hasta aqu vemos que & es usando para pasar direcciones de
memoria de variables como argumento a otras funciones donde dicha variable no esta definida, o
para incrementar el valor de dicha variable, es similar al LEA aunque en la practica nos da una
direccin de memoria cuyo contenido es una variable, en cambio * es tambin una direccin de
memoria pero en esta caso la variable puntero contendr una direccin de memoria, en cuyo
contenido podremos trabajar, la diferencia es muy fina y es posible que todava haya dudas,
veremos como usar ambas a la vez en el siguiente ejemplo.

#include <stdio.h>
void duplica(int *x) {
*x = *x * 2;
}
main() {
int n = 5;
printf("n vale %d\n", n);
duplica(&n);
printf("Ahora n vale %d\n", n);

getchar();
}
Vemos que la funcion duplica tiene definido que recibe un puntero (int *) y busca su contenido y lo
multiplica por dos, modificando el contenido all mismo sin devolver el valor.
void duplica(int *x) {
*x = *x * 2;
}
En el main cuando se llama a la funcion duplica se pasa una direccin de memoria de la variable n
que vale 5, por ejemplo le pasa 0x10000, al pasarle la direccin de memoria a duplica, esta la toma,
busca el contenido de 0x10000, que es 5 y lo multiplica por dos y lo modifica all mismo,
guardando en 0x10000 el 10 decimal.
main() {
int n = 5;
printf("n vale %d\n", n);
duplica(&n);
printf("Ahora n vale %d\n", n);
Por eso en el printf aparece n con un nuevo valor que no se cambio en la funcion main, este es un
ejemplo de lo que decamos en las primeras partes del curso, que para modificar el valor de una
variable local, ingresando en otra funcion, donde dicha variable no esta definida, la nica forma es
pasarle con & o LEA su direccin y que luego dentro de la misma se trabaje en el contenido de
dicha variable, lo cual se realiza por ejemplo si la direccin esta en EAX usando [EAX] para
modificar su contenido y eso es lo que hace duplica usando * para modificar el contenido de una
direccin.
O sea este es el ejemplo clsico de como modificar el valor de una variable local usando & o LEA,
dentro de otra funcion y por eso cuando estamos reverseando una funcion, y vemos que en IDA al
apretar X en una variable local, si ademas de la asignaciones directas, hay un LEA, enseguida nos
fijamos en esa instruccin, pues all puede cambiar de valor de dicha variable al entrar en otra
subfuncion, o al modificar el contenido de la memoria.

Si lo vemos en IDA.

Vemos que n vale 5 y usa printf para imprimir su valor.

Luego le pasa la direccin de memoria de la variable n, si ponemos un breakpoint all, vemos que
en EAX estar la misma, la cual se pasa la la funcion duplica, entramos en ella.

Si traceamos vemos que lgicamente arg_0 sera la direccin de memoria de n, aqu n no tiene
significado pues es una variable local, pero si podemos trabajar con su direccin de memoria y
cambiar su contenido.

Aqu el argumento direccion_de_n es un puntero, vemos que lo mueve a EDX y a EAX.

Luego mueve a EAX el contenido de dicha direccin de memoria que sera el valor de n que estar
en el contenido de EAX.

Luego con add EAX,EAX duplica el valor. y lo vuelve a guardar en el contenido de EDX o sea
reemplaza el 5 por el 10.

Al ejecutar el mov [EDX],EAX cambiara a 10 decimal o sea 0a hexa.

Y all volvemos a main.

Ahora aqu movemos el valor de n a EAX pero este ya no vale 5 sino vale 0a hexa jeje, dentro de
duplica se cambio su valor.

Y si por eso cuando se pasa como argumento de una funcion una direccin de memoria de una
variable local, que se hallo con LEA, podemos esperar que dentro de dicha funcion se cambie el
valor de la variable local.
Bueno no quiero seguir mas hasta que no digieran bien esto les recomiendo que repasen bien, los
ejemplos de punteros pues se va a venir mas difcil si no entendieron bien esto.
Hasta la parte siguiente, adjunto un ejemplo para reversear para que practiquen
Ricardo Narvaja

SEGUIMOS CON MAS SOBRE PUNTEROS


Cuando declaramos un dato como array, existe una similitud con un puntero, lo veremos en el
ejemplo a continuacion:
#include <stdio.h>

main() {
int datos[10];
datos[0]=5 ;
printf ("%d ", datos[0]);
getchar();
}

Sabemos que para asignar valores a los campos del array normalmente usamos datos[x] , como en
el ejemplo datos[0], pero en si que pasa si imprimimos el valor de la variable datos solo sin
subindice, realmente datos tiene algn valor?
#include <stdio.h>
main() {
int datos[10];
int i;
printf ("%x ", datos);

getchar();
}

Vemos que cuando declaramos un array, al utilizar el nombre solo sin subindices contiene la
direccin de memoria donde se inicia el array, al cual se le pueden asignar y leer valores como si

fuera una variable puntero.


#include <stdio.h>
main() {
int datos[10];
int i;
printf ("%x\n", datos);

for (i=0; i<10; i++)


*(datos+i) = i*2;
for (i=0; i<10; i++)
printf ("%d ", *(datos+i));

getchar();
}
Vemos que le asignamos valores a sus campos, tal cual datos fuera puntero, y los lee de la misma
forma, la asignacin directa tambin puede realizarse como puntero.
#include <stdio.h>
main() {
int datos[10];
int i;
printf ("%x\n", datos);
*(datos)= 20;
printf ("%d ", *(datos));
getchar();
}

Aqu lo asignamos como puntero y lo leemos en la forma tradicional


#include <stdio.h>
main() {
int datos[10];
int i;
printf ("%x\n", datos);
*(datos)= 20;
printf ("%d ", *(datos));
*(datos+1)= 40;
printf ("%d ", datos[1]);
getchar();
}

Punteros y estructuras (choreado de Cabanes)


Igual que creamos punteros a cualquier tipo de datos bsico, le reservamos memoria con malloc
cuando necesitamos usarlo y lo liberamos con free cuando terminamos de utilizarlo, lo mismo
podemos hacer si se trata de un tipo de datos no tan sencillo, como un struct.
Eso s, la forma de acceder a los campos del struct cambiar ligeramente. Para un dato que sea un
nmero entero, ya sabemos que lo declararamos con int *n y cambiaramos su valor haciendo algo

como *n=2, de modo que para un struct podramos esperar que se hiciera algo como
*persona.edad = 20. Pero esa no es la sintaxis correcta: deberemos utilizar el nombre de la variable
y el del campo, con una flecha (->) entremedio, as: persona->edad = 20. Vamos a verlo con un par
de ejemplos para que se aclare bien:

#include <stdio.h>
main() {
/* Primero definimos nuestro tipo de datos */
struct datosPersona {
char nombre[30];
char email[25];
int edad;
};
/* La primera persona ser esttica */
struct datosPersona persona1;

/* Damos valores a la persona esttica */


strcpy(persona1.nombre, "Juan");
strcpy(persona1.email, "j@j.j");
persona1.edad = 20;

printf("Primera persona: %s, %s, con edad %d\n",


persona1.nombre, persona1.email, persona1.edad);

getchar();
}

Este es el caso clsico que ya habamos visto en partes anteriores, la persona, esta instanciada como
variable esttica en el stack, veamoslo en IDA as vemos la diferencia con el caso dinmico que
veremos luego.

Vemos la linea roja que separa lo agregado por el compilador, asimismo la variable var_4c es
creada por el mismo, la primera variable nuestra es la var_48

All vemos cuando inicializa la variable var_48, en celeste vemos que copia la string Juan, luego
vemos en amarillo que se suma 1eh desde var_48 y copia otra string con el mail, esto nos podra
hacer pensar que es un array de strings, pero luego vemos la var_10 a la cual se le asigna el valor
14, y pensamos que todo esto es una estructura ya que si vemos las variables.

Ya ver espacio vaco no definido entre variables, es sospechoso de estructura o array estatico, una
asignacin de un valor en un espacio intermedio no definido, entre las variables como aqu en
var_48 + 1e, es tambin sospechoso de array o estructura, pero al seguir hacia abajo y ver que
donde asigna el valor a la var_10 esta consecutiva al espacio no definido y es de un tipo diferente
podemos concluir que no es un array pues tiene campos de diferentes tipos, as que estamos en
presencia de una estructura.
As que vayamos a la pestaa de creacin de estructuras, sabemos que para escribir la segunda
string le suma 1eh as que el largo de la primera sera 30, creamos la estructura y agregamos con
asterisco un array.

Como tomo como char o sea un byte el inicio, luego al colocar 30 bytes de largo, pues creara el
array de caracteres correspondiente, le pongo el nombre correcto.

Vuelvo a las variables y aprieto asterisco en la primera solo para que me diga el espacio en bytes
que hay hasta la var_10, no creo nada.

Veo que hay 56 bytes, as que si le resto los 30 del primer campo, quedaran 26 bytes, as que
cancelo la ventana y vuelvo adonde estan las estructuras y creo un segundo campo, que sea un array
de 26 hexa de largo.

Vemos que en el mismo nombre nos indica que esta en la posicin 1eh desde el inicio, as que lo
creamos parece que vamos bien.

Ahora agregamos el tercer campo que es un int que debera coincidir con la var_10.

All tenemos armada la estructura as que ahora asignemos a la variable var_48, apretando ALT mas
Q sobre la misma.

Nos queda renombrar la estructura pongamosle el nombre original ya que lo sabemos.

As que ahora var_48 es del tipo datosPersona, podemos renombrar la variable var_48 ya que es
una instancia de datosPersona y en nuestro cdigo la llambamos persona1, as que lo hacemos.

Renombrada

Por supuesto persona1 esta creada en el stack en forma esttica usando el lugar que
predeterminamos en el mismo para cada campo.

Pongamos un breakpoint all para ver si coincide al ejecutar el programa.

Bueno vemos que va a pasar como argumento de strcpy el puntero a donde se encuentra la string
Juan, en este caso 403000.
Luego lee la direccin de persona1 con lea para pasrsela como argumento a strcpy la cual copiara
la misma all, pasemos el strcpy con f8.
Si hago click en persona1

Veo el stack, la palabra Juan y el cero final de la string

Para aclarar asigno all a 22ff30 la estructura que cree con ALT mas Q.

Ugh quedo feo veamos como arreglarlo aunque el hecho de que tenga basura entre medio de las
strings complica a que el IDA lo reconozca bien, pero bueno yendo a la definicin de la estructura y
poniendo visible el men.

Al marcar los campos de strings y apretar all se pone verde y muestra char, aunque todava no
queda bien.

Queda un poco mejor pero no como string, ahora un truco medio de la galera si originalmente en

vez de asterisco encima de nombre apretamos la A.

y aceptamos vemos que cambio el tipo a string C

Hago lo mismo con la otra.

Bueno al menos vemos las strings, el tema de que no las agrupa debe ser por la basura intermedia,
pero al menos vemos que coinciden, donde empieza email esta la string del mismo y donde empieza
edad esta el 14h.

Vemos que luego saca las direcciones del primer campo nombre y le suma 1eh para obtener la
direccin del segundo campo email y los imprime, ahora veremos el mismo caso pero dinamico.

#include <stdio.h>
main() {
/* Primero definimos nuestro tipo de datos */
struct datosPersona {
char nombre[30];
char email[25];
int edad;
};
/* La segunda persona ser dinamica */
struct datosPersona *persona2;

/* Ahora a la dinmica */
persona2 = (struct datosPersona*)
malloc (sizeof(struct datosPersona));
strcpy(persona2->nombre, "Pedro");
strcpy(persona2->email, "p@p.p");
persona2->edad = 21;

/* Mostramos los datos y liberamos la memoria */


printf("Segunda persona: %s, %s, con edad %d\n",

persona2->nombre, persona2->email, persona2->edad);


free(persona2);

getchar();
}

Vemos que la estructura es la misma que antes, pero en vez de instanciarla en forma esttica, lo
hacemos en forma dinmica, ahora persona2 sera un puntero a la estructura.
struct datosPersona *persona2;
Vemos como reserva el espacio necesario ya que sizeof le devuelve al compilador el tamao de la
estructura, as que eso sera una constante, por supuesto malloc devolver un puntero a la zona
donde reservo dicho espacio, luego para que no haya problemas al compilar castea el tipo a (struct
datosPersona*) como puntero a esa estructura.
persona2 = (struct datosPersona*)
malloc (sizeof(struct datosPersona));
As que tenemos nuestro puntero en persona2, y el espacio necesario reservado al que apunta,
como habamos visto en el caso de las estructuras dinmicas se debe usar la flecha para acceder a
los campos.

strcpy(persona2->nombre, "Pedro");
strcpy(persona2->email, "p@p.p");
persona2->edad = 21;
Luego imprime la salida tambin usando la flecha para acceder a los campos y luego cuando
termina libera la memoria reservada.
printf("Segunda persona: %s, %s, con edad %d\n",
persona2->nombre, persona2->email, persona2->edad);
free(persona2);

Funciona perfectamente

Veamos este nuevo ejemplo en IDA

Vemos donde empieza nuestro cdigo debajo de la linea, lo primero que observamos es que no se
esta reservando lugar en el stack para la estructura como antes, solo una variable var_4 que es un
dword es usada, la otra var_8 la usa el compilador.

Vemos que no hay espacio no definido, las variables estan consecutivas, y la var_4 es un dword y a
continuacion ya se encuentra el stored_ebp, as que no hay lugar vaco para arrays ni estructuras
estticas aqu, solo nuestra var_4 que ocupa 4 bytes y nada mas.

Lo primero que hace es llamar a malloc reservando el espacio 3ch que es el tamao de nuestra
estructura, el puntero lo guarda en la variable que podemos renombrar como persona2.

Luego trabaja todo con punteros as que aqu no hay que hacer lea ni nada, le pasamos el puntero
persona2 a strcpy, IDA reconoce que es un puntero y por eso nos muestra char * llenamos el
campo nombre.

Luego a dicho puntero le suma 1eh obteniendo un puntero al segundo campo email de la estructura
tambin aqu IDA nos muestra que el argumento es un char *.
mov
mov

eax, [ebp+persona2]
dword ptr [eax+38h], 15h

Luego lee el puntero a persona2 nuevamente le suma 38h y all asigna el valor 15 al campo edad.
Aqu no podemos en el anlisis esttico, asignar nuestra estructura a nada, pues no esta en el stack y
es dinmica o sea creada el correr el programa, lo debuggearemos para ver si quedo todo bien.

Al pasar malloc EAX tiene el valor del puntero en mi maquina 0x3e3d78, all reservo 3ch de
espacio veamos en el DUMP hagamos click derecho SINCRONIZE WITH EAX.

All esta, traceemos con f8 y vayamos copiando, vemos como llena los campos de la estructura con
las strings Pedro, el mail y el dword 15.

Si vemos la estructura en el desensamblado

Bueno no me deja me da error, pero a no asustarse que yo estoy temblando seguro que como no la
guardamos no nos deja, pero bueno se crea en un minuto de nuevo ya sabemos que si el campo es
una string apretamos A en vez de asterisco.
Creamos la estructura nuevamente, usamos A en vez de asterisco en los campos que sern strings.

Vemos que los datos corresponden aunque quizs por el mismo motivo que cuando es esttica,
demasiada basura intermedia, no nos crea el formato mas cmodo, pero vemos bien que esta todo
correcto.

Luego primero le pasa como argumento el valor de la edad, que obtiene a partir del puntero
persona2 al cual le suma 38h y obtiene un puntero al campo edad y al cual le halla el contenido 15
y lo guarda en el stack, para los otros campos ya que son strings solo es necesario el puntero al
inicio de la misma as que le pasa puntero2 para el nombre y le suma 1eh para apuntar al mail,
luego hace free de persona2 para liberar la memoria reservada.

Hay un ejercicio para solucionar casi igual a ambos de esta parte pero juntos.
Hasta la parte siguiente
Ricardo Narvaja

LISTAS ENLAZADAS
Voy a copiar la teora del curso de Cabanes ya que esta mucho mejor explicado de lo que lo hara yo
y continuare con el ejemplo en el IDA.
Estructuras dinmicas habituales 1: las listas enlazadas
Ahora vamos a ver un tipo de estructura totalmente dinmica (que puede aumentar o disminuir
realmente de tamao durante la ejecucin del programa). Estas son las llamadas listas.
Ahora el truco consistir en que dentro de cada dato almacenaremos todo lo que nos interesa,
pero tambin una referencia que nos dir dnde tenemos que ir a buscar el siguiente.
Sera algo como:
(Posicin: 1023).
Nombre : 'Nacho Cabanes'
Web : 'www.nachocabanes.com'
SiguienteDato : 1430
Este dato est almacenado en la posicin de memoria nmero 1023. En esa posicin guardamos el
nombre y la direccin (o lo que nos interese) de esta persona, pero tambin una informacin extra:
la siguiente ficha se encuentra en la posicin 1430.
As, es muy cmodo recorrer la lista de forma secuencial, porque en todo momento sabemos dnde
est almacenado el siguiente dato. Cuando lleguemos a uno para el que no est definido cual es el
siguiente dato, quiere decir que se ha acabado la lista.
Por tanto, en cada dato tenemos un enlace con el dato siguiente. Por eso este tipo de estructuras
recibe el nombre de listas simplemente enlazadas o listas simples. Si tuviramos enlaces hacia el
dato siguiente y el posterior, se tratara de una lista doblemente enlazada o lista doble, que
pretende hacer ms sencillo el recorrido hacia delante o hacia atrs.
Con este tipo de estructuras de informacin, hemos perdido la ventaja del acceso directo: ya no
podemos saltar directamente a la ficha nmero 500. Pero, por contra, podemos tener tantas fichas
como la memoria nos permita, y eliminar una (o varias) de ellas cuando queramos, recuperando
inmediatamente el espacio que ocupaba.
Para aadir una ficha, no tendramos ms que reservar la memoria para ella, y el compilador de C
nos dira le he encontrado sitio en la posicin 4079. Entonces nosotros iramos a la ltima ficha y
le diramos tu siguiente dato va a estar en la posicin 4079.
Esa es la idea intuitiva. Ahora vamos a concretar cosas en forma de programa en C.
Primero veamos cmo sera ahora cada una de nuestras fichas:
struct f { /* Estos son los datos que guardamos: */
char nombre[30]; /* Nombre, hasta 30 letras */
char direccion[50]; /* Direccion, hasta 50 */
int edad; /* Edad, un numero < 255 */
struct f* siguiente; /* Y direccin de la siguiente */
};

La diferencia con un struct normal est en el campo siguiente de nuestro registro, que es el
que indica donde se encuentra la ficha que va despus de la actual, y por tanto ser otro puntero a
un registro del mismo tipo, un struct f *.
Un puntero que no apunta a ningn sitio tiene el valor NULL (realmente este identificador es una
constante de valor 0), que nos servir despus para comprobar si se trata del final de la lista: todas
las fichas apuntarn a la siguiente, menos la ltima, que no tiene siguiente, y apuntar a NULL.
Entonces la primera ficha definiramos con
struct f *dato1; /* Va a ser un puntero a ficha */
y la comenzaramos a usar con
dato1 = (struct f*) malloc (sizeof(struct f)); /* Reservamos memoria */
strcpy(dato1->nombre, "Pepe"); /* Guardamos el nombre, */
strcpy(dato1->direccion, "Su casa"); /* la direccin */
dato1->edad = 40; /* la edad */
dato1->siguiente = NULL; /* y no hay ninguna ms */
(No debera haber nada nuevo: ya sabemos cmo reservar memoria usando malloc y como
acceder a los campos de una estructura dinmica usando ->).
Ahora que ya tenemos una ficha, podramos aadir otra ficha detrs de ella. Primero guardamos
espacio para la nueva ficha, como antes:
struct f *dato2;
dato2 = (struct f*) malloc (sizeof(struct f)); /* Reservamos memoria */
strcpy(dato2->nombre, "Juan"); /* Guardamos el nombre, */
strcpy(dato2->direccion, "No lo s"); /* la direccin */
dato2->edad = 35; /* la edad */
dato2->siguiente = NULL; /* y no hay ninguna ms */
y ahora enlazamos la anterior con ella:
dato1->siguiente = dato2;
Si quisiramos introducir los datos ordenados alfabticamente, basta con ir comparando cada nuevo
dato con los de la lista, e insertarlo donde corresponda. Por ejemplo, para insertar un nuevo dato
entre los dos anteriores, haramos:
struct f *dato3;
dato3 = (struct f*) malloc (sizeof(struct f)); /* La tercera */
strcpy(dato3->nombre, "Carlos");
strcpy(dato3->direccion, "Por ah");
dato3->edad = 14;
dato3->siguiente = dato2; /* enlazamos con la siguiente */
dato1->siguiente = dato3; /* y la anterior con ella */
printf("La lista inicialmente es:\n");

La estructura que hemos obtenido es la siguiente


Dato1 - Dato3 - Dato2 - NULL
Grficamente:

Es decir: cada ficha est enlazada con la siguiente, salvo la ltima, que no est enlazada con
ninguna (apunta a NULL).
Si ahora quisiramos borrar Dato3, tendramos que seguir dos pasos:
1.- Enlazar Dato1 con Dato2, para no perder informacin.
2.- Liberar la memoria ocupada por Dato3.
Esto, escrito en "C" sera:
dato1->siguiente = dato2; /* Borrar dato3: Enlaza Dato1 y Dato2 */
free(dato3); /* Libera lo que ocup Dato3 */
Hemos empleado tres variables para guardar tres datos. Si tenemos 20 datos, necesitaremos 20
variables? Y 3000 variables para 3000 datos?
Sera tremendamente ineficiente, y no tendra mucho sentido. Es de suponer que no sea as. En la
prctica, basta con dos variables, que nos indicarn el principio de la lista y la posicin actual, o
incluso slo una para el principio de la lista.
Por ejemplo, una rutina que muestre en pantalla toda la lista se podra hacer de forma recursiva as:
void MuestraLista ( struct f *inicial ) {
if (inicial!=NULL) { /* Si realmente hay lista */
printf("Nombre: %s\n", inicial->nombre);
printf("Direccin: %s\n", inicial->direccion);
printf("Edad: %d\n\n", inicial->edad);
MuestraLista ( inicial->siguiente ); /* Y mira el siguiente */
}
}
Lo llamaramos con "MuestraLista(dato1)", y a partir de ah el propio procedimiento se encarga de
ir mirando y mostrando los siguientes elementos hasta llegar a NULL, que indica el final.
Antes de seguir, vamos a juntar todo esto en un programa, para comprobar que realmente funciona:
aadimos los 3 datos y decimos que los muestre desde el primero; luego borramos el del medio y
los volvemos a mostrar:
Hasta aqu la explicacin terica que creo se entiende perfectamente, as que vayamos al cdigo
fuente a explicarlo y luego a verlo en IDA.

Veamoslo en IDA ahora veremos que no es tan complicado como parece.

Vemos que en el main no tenemos variables locales, ya que las que estan all son las que siempre
crea y usa el procesador y no son nuestras, si recordamos el cdigo fuente veamos que no hay
variables locales definidas dentro de main, las que usa son todas globales.

Aqu comienza lo nuestro, reserva 58 hexa de tamao que es 88 decimal para la primera ficha o
ficha1, recordamos que era 30 para el primer campo, 50 para el segundo, 4 para la edad y 4 para el
puntero al siguiente lo que da 88 decimal.

El puntero que en nuestro cdigo llambamos dato1 lo guarda en la variable global 404080, as que
renombramos all a dato1.

Luego llama a strcpy pasandole el puntero a la string Pepe como fuente y como destination el
puntero dato1, para que inicialice copiando all el primer campo de la ficha.
Luego corre el puntero desde el inicio 1Eh o sea 30 decimal mas adelante para escribir la direccin
que era el segundo campo.

Podemos ponerlo en decimal usando el men del botn derecho all.

Luego guarda la edad y el cero en el campo siguiente ya que es la ultima ficha creada en esta lista,
sumndole 50 al puntero dato1 y escribiendo en el contenido que sera el campo edad y luego
sumndole 54 al puntero dato1 y escribiendo en el contenido que sera el campo siguiente.

Podemos pasarlo a decimal as se ve correctamente la edad y apretando punto y coma puedo agregar
algn comentario adicional como que el 40 es la edad y que el cero es el que marca la condicin de
ultimo.

Vemos que hace exactamente lo mismo con la segunda ficha o ficha2 en la lista, reserva la
memoria le copia los datos y pone el cero en el campo siguiente, lo que queda ahora es ver como
arregla el campo siguiente de la ficha1 para quitar el cero y poner el puntero a la ficha2.

Ah esta mueve a EDX dato1 el puntero a la ficha1 y a EAX dato2 el puntero a ficha2 y luego
escribe en el contenido de dato1 mas 54h o sea en el campo siguiente de la ficha1, el puntero dato2
para que apunte a la ficha2 y se mantenga la lista enlazada.
Luego realiza el mismo trabajo con la ficha3.

Y luego arregla los punteros haciendo que esta ficha3 se incluya en medio de las dos existentes,
quede como la segunda en la lista enlazada, vemos primero que al siguiente de la ficha3 le mueve el
puntero a la ficha2, y luego al siguiente de la ficha1 hace que apunte a ficha3 para que queden en la
lista en el orden
ficha1--> ficha3 ficha2

mov dword ptr [esp], offset aLaListaInicial ; "La lista inicialmente es:\n"
call printf
Luego de imprimir el mensaje anterior llama a la funcion que en nuestro cdigo se llamaba
MuestraLista, as que la renombramos.

Entrando en ella vemos que tiene un argumento ya que se le pasaba el puntero a la ficha1 llamado
dato1.

Sabemos que dentro de la funcion tomara los valores de dato1, dato2 o dato3 lo llamaremos datoX
para hacerlo bien genrico.
Luego propagamos la definicin de la funcion con el argumento datoX, yendo a Set Function Type.

As propago el nombre del argumento hacia main, veo que en el main aparece, como que le paso
dato1 como argumento y que la funcion lo recibe como datoX ya que es genrico para los
diferentes llamados que hay a la misma.

Aqu se fija si datoX es cero eso solo puede ocurrir si no hay lista o si la ficha es la ultima, en este
caso datoX vale dato1 y es distinto de cero.

As que usando ese puntero a dato1 llama a printf haciendo format string %s imprimiendo el
campo nombre, luego le suma 30 para hallar el puntero al campo direccin y mediante format
string imprimir la misma.

Luego le suma 50h al puntero dato1 y lee la edad y hace format string usando %d para imprimir la
misma.

Luego llama a la misma funcion recursivamente, pasandole como argumento el campo siguiente
que esta 54h a partir de dato1, y vuelve a repetir el mismo proceso para imprimir los datos de la
siguiente ficha de la lista y as sucesivamente hasta que llegue a la ultima que tiene valor cero en el
campo siguiente y por eso sale y vuelve al main.

Luego elimina la ficha3, para ello arregla los punteros y al siguiente de la ficha1, le guarda el
puntero dato2, y hace free() de dato3 con lo cual la elimina.

Luego imprime nuevamente toda la lista pasandole el puntero a ficha1, ahora solo imprimir la
ficha1, y la ficha2, ya que la ficha3 desapareci por el free() y el arreglo de los punteros siguiente.

Eso es todo sobre el ejemplo de lista simplemente enlazada veremos si alguno es guapo y reversea
el ejercicio que es una lista simplemente enlazada mas compleja que este jeje.
Hasta la parte siguiente:
ricnar

Operaciones con bits


Podemos hacer desde C operaciones entre bits de dos nmeros (AND, OR, XOR, etc). Vamos
primero a ver qu significa cada una de esas operaciones.

Veamoslo todos en un mismo ejemplo.


#include <stdio.h>
int main() {
int a = 67;
int b = 33;
printf("La variable a vale %d\n", a);
printf("y b vale %d\n\n", b);
printf(" El complemento de a es: %d\n", ~a);
printf(" El producto logico de a y b es: %d\n", a&b);
printf(" Su suma logica es: %d\n", a|b);
printf(" Su suma logica exclusiva es: %d\n", a^b);
printf(" Desplacemos a a la izquierda: %d\n", a << 1);
printf(" Desplacemos a a la derecha: %d\n", a >> 1);
getchar();
return 0;
}
Al correrlo vemos los resultados de las operaciones.

Llevado a binario podemos comprobar fcilmente las operaciones:


A=1000011 = 43h
B=100001 = 21h
El complemento de A o sea NOT (A) es FFFFFFFFh 43h = FFFFFFBCh
Tengo justo un Olly abierto que es rpido para hacer conversiones y veo que FFFFFFBCh es -68h si
lo tomamos con signo.

Luego hace el producto lgico o AND entre ambos, los encolumno en forma binaria.
Sabemos que mirando columna por columna solo tendr la misma resultado uno si ambos bits de la
misma son 1 sino cualquier otra combinacin da cero.
A=1000011
B=0100001
-------------C=0000001
All C sera el resultado y valdr uno ya que solo la ultima columna de la operacin tiene ambos bits
encendidos en uno, de esta forma el producto lgico o AND entre ambos da 1h.
Luego viene la suma lgica o OR encolumnamos en binario nuevamente sabiendo en este caso que
la suma lgica mientras que haya un uno en algn bit de una columna ya el resultado sera 1.
A=1000011
B=0100001
-------------C=1100011
Que es igual a 63h o sea 99
Luego hacer la suma exclusiva o XOR que sabemos que solo da 1 si ambos bits de la columna son
distintos.

A=1000011
B=0100001
-------------C=1100010
Que es igual a 62h o sea 98
Luego viene el desplazamiento a la izquierda de A , corremos todo para la izquierda un lugar y le
agregamos un cero por la derecha para rellenar el espacio que quedo vaco al correr.
A=1000011
C=10000110
O sea el resultado es 86h o sea 134
Lo mismo el desplazamiento hacia la derecha
A=1000011 -- al desplazar a la derecha ese byte se cae fuera del limite
C=0100001 - y es rellenado con un cero por el otro lado.
Que es 33 decimal
Es de notar que en el desplazamiento hay que tener en cuenta el limite mximo a cada lado pues al
desplazar a la izquierda como solo desplazamos uno no pasamos el limite mximo de bits, si lo
hiciramos, los que se cayeran fuera se perderan siendo reemplazados por los ceros que se rellenan
por el otro lado, en el caso del desplazamiento a la derecha es igual solo que all estamos en el
limite, as que al desplazar solo un lugar el ultimo byte se cae fuera siendo rellenado con un cero
por delante.
Bueno todo muy lindo veamos como se ve todo esto en IDA.

All vemos donde empieza realmente lo nuestro y las dos variables que son int o sea ocupan un
dword cada una, comencemos a renombrar.

Lo pusimos en decimal como en el cdigo fuente y le cambiamos los nombres obvio que si no
tenemos el fuente y estamos reverseando le pondremos los nombres que se nos antoje.

Lo primero que hace es imprimir los valores de a y b en dos printf usando %d en el format string.

Luego imprime el valor del complemento de a, all vemos como hace NOT que es la instruccin
para hallarlo.

Luego hace el producto lgico o AND para ellos mueve b a EAX y luego hace AND de EAX con a,
el resultado lo manda a imprimir con printf.

De la misma forma halla la suma lgica OR y la suma lgica exclusiva XOR y los manda a
imprimir.

Luego realiza el desplazamiento a la izquierda que en el caso de desplazar uno es equivalente a


multiplicar por 2, recordemos que a vala 67 y el resultado era 134.

Si en el cdigo fuente cambiramos que se desplace dos posiciones vemos que ahora si necesita
usar el SHL o SAL que es la instruccin de desplazamiento a la izquierda.

http://ensam1.blogspot.com/2005/09/64-corrimiento-y-rotacion.html
all mas de SHL SAL SHR y SAR

y all usa SAR para el ultimo desplazamiento a la derecha y con eso termina el ejemplo que es bien
sencillo.
Una leccin sencilla y un ejercicio sencillo para reversear sobre el tema jeje.
Hasta la parte siguiente
ricnar

INCLUDE Y DEFINE
Aqu copiare directo de la teora de Cabanes que esta muy bien explicada, esto que viene sobretodo
las macros son mas importantes para el que programa que para un reverser, ya que estas macros son
resueltas antes de compilar y no se podr ver nada diferente en el IDA, si se define una macro se
vera en el IDA el resultado de aplicar la misma directamente y sera difcil adivinar de donde
provino pues no veremos cdigo ni nada, pero es bueno conocer del tema aunque sea por encima as
que aqu va la teora,
Directivas del preprocesador
Desde el principio hemos estado manejando cosas como
#include <stdio.h>
Y aqu hay que comentar bastante ms de lo que parece. Ese include no es una orden del lenguaje
C, sino una orden directa al compilador (una directiva). Realmente es una orden a una cierta parte
del compilador que se llama preprocesador. Estas directivas indican una serie de pasos que se
deben dar antes de empezar realmente a traducir nuestro programa fuente.
Aunque include es la directiva que ya conocemos, vamos a comenzar por otra ms sencilla, y que
nos resultar til cuando lleguemos a sta.
Constantes simblicas: #define
La directiva define permite crear constantes simblicas. Podemos crear una constante haciendo
#define MAXINTENTOS 10
y en nuestro programa lo usaramos como si se tratara de cualquier variable o de cualquier valor
numrico:
if (intentoActual >= MAXINTENTOS) ...
El primer paso que hace nuestro compilador es reemplazar esa falsa constante por su valor, de
modo que la orden que realmente va a analizar es
if (intentoActual >= 10) ...
pero a cambio nosotros tenemos el valor numrico slo al principio del programa, por lo que es
muy fcil de modificar, mucho ms que si tuviramos que revisar el programa entero buscando
dnde aparece ese 10.
Comparado con las constantes de verdad, que ya habamos manejado (const int
MAXINTENTOS=10;), las constantes simblicas tienen la ventaja de que no son variables, por lo
que no se les reserva memoria adicional y las comparaciones y dems operaciones suelen ser ms
rpidas que en el caso de un variable.
Vamos a ver un ejemplo completo, que pida varios nmeros y muestre su suma y su media:

#include <stdio.h>
#define CANTIDADNUMEROS 5
int main() {
int numero[CANTIDADNUMEROS];
int suma=0;
int i;
for (i=0; i<CANTIDADNUMEROS; i++) {
printf("Introduzca el dato nmero %d: ", i+1);
scanf("%d", &numero[i]);
}
for (i=0; i<CANTIDADNUMEROS; i++)
suma += numero[i];
printf("Su suma es %d\n", suma);
printf("Su media es %4.2f\n", (float) suma/CANTIDADNUMEROS);
getchar();
getchar();
return 0;
}

Vemos que es un programita muy sencillo crea un array de enteros de tamao 5, ya que el define del
inicio hace que CANTIDADNUMEROS sea igual a 5 en todos los casos
int numero[CANTIDADNUMEROS];
sera igual a
int numero[5];
y as el preprocesador reemplazara donde encuentre la palabra CANTIDADENUMEROS por el
valor 5 y lo compilara.
De esta forma el cdigo anterior seria equivalente a
#include <stdio.h>
int main() {
int numero[5];
int suma=0;
int i;
for (i=0; i<5; i++) {
printf("Introduzca el dato nmero %d: ", i+1);
scanf("%d", &numero[i]);
}

for (i=0; i<5; i++)


suma += numero[i];
printf("Su suma es %d\n", suma);
printf("Su media es %4.2f\n", (float) suma/5);
getchar();
getchar();
return 0;

Si compilamos de nuevo el original con el define y lo vemos en IDA.

All vemos las variables y argumentos sabemos que hay variables y argumentos que agrego el
compilador, pero si no estamos seguros cuales son, pues empecemos por la superior una por una a
ver que es cada una.

Apretando la X en la primera vemos de donde es llamada y eso es aqu:

As que esa solo trabaja en la parte anterior a que empiece el cdigo real, as que es agregada por el
compilador.
La siguiente ni siquiera tiene referencias as que lo mismo es creada por el compilador

La tercera ya es usada en el cdigo real veamos que es.

Vemos que es inicializada con cero aqu y luego comparada con 4.

Se ve claramente que es el contador de un loop, mas adelante hay otra iniclializacion con cero, y
compara con cuatro, as que es posible que se reuse varias veces como contador.

Dentro del loop la vemos usada tres veces, la primera para pasarle al printf el numero de dato que
debemos ingresar ya que hace format string usando el valor del contador mas uno, as cuando en el
loop el contador valga cero, sin cambiar el valor de la variable se mover a EAX y all on the fly se
incrementa a uno y luego se imprime Introduzca el dato numero 1.
Se entiende que el on the fly lo uso porque contador no cambia su valor por el INC sino que lo
hace al vuelo incrementando en un registro.
lea edx, [ebp+var_28]
mov eax, [ebp+var_30]
shl eax, 2
Aqu lo usa para recorrer un array de enteros, aunque no tenga el cdigo fuente me doy cuenta que
levanta la direccin del inicio de un array con el LEA y mueve a EAX el valor de contador y lo
multiplica por 4 (SHL EAX, 2)
As que cuando el contador valga 0 luego del SHL, tendremos que EAX valdr 0, en el prximo
ciclo cuando contador valga 1, EAX valdr 4 y as al sumarlo a la direccin de inicio del array
podremos usarlo como indice para recorrer los campos del mismo.
lea eax, [edx+eax]
mov [esp+4], eax
mov dword ptr [esp], offset aD ; "%d"
call scanf
Eso es lo que hace suma usando LEA la direccin de inicio del array que estaba en EDX con EAX
obteniendo la direccin del campo actual del array y eso lo pasa como argumento a scanf para que
ingresemos los datos y vayamos llenando los campos del array en cada ciclo del loop, as que
podemos ir arreglando todo esto, podemos renombrar la var_30 a contador.

Y la var_28 vimos que era un array ya que ah en el loop se inicializaban los campos, sabemos que
nos pide 5 datos enteros y que el for sale cuando es mas grande que 4.

Por lo tanto podemos suponer que el array tiene 5 campos enteros de cualquier forma si uno usa
todo el espacio vaco y no hay ninguna otra referencia en el programa que nos diga lo contrario, no
esta mal, as que vamos a crear el array.

Verificamos que el tamao de cada campo sera correcto en este caso 4 ya que es un array de enteros,
cambiamos el array size a 5 y apretamos OK.

Quedo un poco de espacio vaco que dejo el compilador pero no hay problema renombro la variable

array a datos.
Una vez que sale del loop que inicializa los datos en el array el cual pinte de rosado, vemos que se
vuelve a poner a cero la variable contador y parece haber otro loop-

Y si hay un segundo loop all pintado de amarillo

Mientras que contador sea menor o igual a cuatro se repetir el loop yendo por los bloques
amarillos, veamos que hace dentro del mismo.

Lee el contador del loop


mov

eax, [ebp+contador]

Mueve a EDX los valores ingresados en cada campo


mov

edx, [ebp+eax*4+datos]

Creo que es fcil de ver que cuando contador vale


contador=0
mov

edx, [ebp+0*4+datos]

mov

edx, [ebp+datos]

Quedara en EDX el valor guardado en el primer campo del array


contador=1
mov

edx, [ebp+1*4+datos]

mov

edx, [ebp+4+datos]

Quedara en EDX el valor guardado en el segundo campo del array y as sucesivamente.


Luego usa una var_2c para ir sumando todos los valores guardados en los campos.
lea eax, [ebp+var_2C]
add [eax], edx
As que vemos que la var_2c que nos quedaba renombrar podemos llamarla suma ya que all
guarda la suma de los valores que ingresamos.

Luego incrementa el contador y va al inicio del loop donde se fijara si el mismo es mayor que
cuatro para salir del mismo.

Luego de salir del loop imprime usando printf el valor de suma usando format string.

Luego usando el stack de punto flotante carga el valor de la suma en el mismo, y luego un 5 que lo
guardo como variable global en 403044, luego har la divisin entre ambos lo cual es la media, la
cual imprimir.
El chiste de todo esto es que nunca vemos CANTIDADNUMEROS sino que para nosotros al
reversear sera 5 la variable global y sera cinco la cantidad de veces que loopeara y ni nos enteramos
de lo que hizo el preprocesador con el define, reverseando llegaramos a esto:
#include <stdio.h>
int main() {
int datos[5];
int suma=0;
int contador;
for (contador=0; contador<5; contador++) {
printf("Introduzca el dato nmero %d: ", contador+1);
scanf("%d", &datos[contador]);
}
for (contador=0; contador<5; contador++)
suma += datos[contador];
printf("Su suma es %d\n", suma);
printf("Su media es %4.2f\n", (float) suma/5);
getchar();
getchar();
return 0;

A define tambin se le puede dar tambin un uso ms avanzado: se puede crear macros, que en
vez de limitarse a lo antes visto, pueden comportarse como pequeas rdenes, ms rpidas que una
funcin. Un ejemplo podra ser:
#define SUMA(x,y) x+y
aqu el cdigo
#include <stdio.h>
#define SUMA(x,y) x+y
int main() {
int n1, n2;
printf("Introduzca el primer dato: ");
scanf("%d", &n1);
printf("Introduzca el segundo dato: ");
scanf("%d", &n2);
printf("Su suma es %d\n", SUMA(n1,n2));
getchar();
getchar();
return 0;
}
Lo que har el preprocesador es un replace antes de compilar, o sea donde halle el texto SUMA(x,y)
lo reemplazara por el texto x+y y para el reverser el cdigo sera.
#include <stdio.h>
int main() {
int n1, n2;
printf("Introduzca el primer dato: ");
scanf("%d", &n1);
printf("Introduzca el segundo dato: ");

scanf("%d", &n2);
printf("Su suma es %d\n", (n1+n2));
getchar();
getchar();
return 0;
}

Si lo vemos en IDA vemos que no existe ninguna funcion SUMA ni nada por el estilo, el
preprocesador reemplazo texto y quedo una suma directa entre dos variables int.

As que el tema macros a pesar de ser til para programar es como magia anterior a la compilacin,
puro replace de texto que realmente el reverser nunca vera, solo hallara, si lo hace correctamente el
cdigo que el preprocesador ya manipulo y esta listo para la compilacin final que sera igualmente
funcional que el original aunque menos elegante posiblemente.
Volvamos a la teora de Cabanes:
Inclusin de ficheros: #include
Ya nos habamos encontrado con esta directiva. Lo que hace es que cuando llega el momento de que
nuestro compilador compruebe la sintaxis de nuestro fuente en C, ya no existe ese include, sino
que en su lugar el compilador ya ha insertado los ficheros que le hemos indicado.
Y eso de por qu se escribe <stdio.h>, entre < y >? No es la nica forma de usar #include.
Podemos encontrar lneas como
#include <stdlib.h>
y como
#include "misdatos.h"
El primer caso es un fichero de cabecera estndar del compilador. Lo indicamos entre < y > y as el
compilador sabe que tiene que buscarlo en su directorio (carpeta) de includes. El segundo caso es
un fichero de cabecera que hemos creado nosotros, por lo que lo indicamos entre comillas, y as el
compilador sabe que no debe buscarlo entre sus directorios, sino en el mismo directorio en el que
est nuestro programa.
Vamos a ver un ejemplo: declararemos una funcin suma dentro de un fichero .h y lo
incluiremos en nuestro fuente para poder utilizar esa funcin suma sin volver a definirla. El
fichero de cabecera se llamara c096.h:
Lo creamos como archivo de texto lo renombramos a c096.h, le pegamos este contenido,
colocandolo en la misma carpeta donde estar el archivo fuente que lo llamara.
int suma(int x,int y) {
return x+y;
}

(Nota: si somos puristas, esto no es correcto del todo. Un fichero de cabecera no debera contener
los detalles de las funciones, slo su cabecera, lo que habamos llamado el prototipo, y la
implementacin de la funcin debera estar en otro fichero, pero eso lo haremos dentro de poco).
Un fuente que utilizara este fichero de cabecera, lo creamos y lo guardamos en la misma carpeta del
anterior.
#include <stdio.h>
#include "c096.h"
int main() {
int n1, n2;
printf("Introduzca el primer dato: ");
scanf("%d", &n1);
printf("Introduzca el segundo dato: ");
scanf("%d", &n2);
printf("Su suma es %d\n", suma(n1,n2));
getchar();
getchar();
return 0;
}

Vemos que encuentra perfectamente al archivo c096.h y usa la funcion suma que esta en el mismo.

Vemos en el IDA que el reverser no ve que la funcion usada no esta en el .c del fuente.

Solo aparece una funcion mas que es la que har la suma pero podra ser una funcion incluida en el
.c perfectamente y no podramos diferenciarlo aqu.

All la funcion que realiza la suma.


Bueno esta semana no habr ejercicios porque el tema es poco dado para el reversing aunque haba
que conocerlo, as que los veo la semana que viene con la siguiente parte.
Ricnar

Uniones y campos de bits


Nos quedan ver algunos detalles del lenguaje C que aunque no tan usados es buenos conocer y ver
como los veremos en IDA despus de compilados, para el reversing.
Le cedo la palabra a Cabanes sobre el tema Uniones, copio la explicacin.
Conocemos lo que es un struct: un dato formado por varios trozos de informacin de distinto tipo.
Pero C tambin tiene dos tipos especiales de struct, de manejo ms avanzado. Son las uniones y
los campos de bits.
Una unin recuerda a un struct normal, con la diferencia de que sus campos comparten el
mismo espacio de memoria:
union {
char letra; /* 1 byte */
int numero; /* 4 bytes */
} ejemplo;
En este caso, la variable ejemplo ocupa 4 bytes en memoria (suponiendo que estemos trabajando
en un compilador de 32 bits, como lo son la mayora de los de Windows y Linux). El primer byte
est compartido por letra y por numero, y los tres ltimos bytes slo pertenecen a numero.
Si hacemos
ejemplo.numero = 25;
ejemplo.letra = 50;
printf("%d", ejemplo.numero);
Veremos que ejemplo.numero ya no vale 25, puesto que al modificar ejemplo.letra estamos
cambiando su primer byte. Ahora ejemplo.numero valdra 50 o un nmero mucho ms grande,
segn si el ordenador que estamos utilizando almacena en primer lugar el byte ms significativo o el
menos significativo.
#include <stdio.h>

int main() {
union {
char letra; /* 1 byte */
int numero; /* 4 bytes */
} ejemplo;
int n1, n2;
ejemplo.numero = 25;
ejemplo.letra = 50;
printf("%d", ejemplo.numero);

getchar();

getchar();
return 0;
}

Vemos que el campo ejemplo.numero comparte memoria con ejemplo.letra y al cambiar el valor
de este ultimo afectamos el valor del primero veamoslo en IDA a ver como se ve y si hay
posibilidad de reversear esto.

Vemos que IDA no ha hecho demasiado por nuestra union la ha puesto como una sola variable int
llamada var_4 y listo, un reverser le costara en este ejemplo darse cuanta que hay una union all,
pero hay algunas pistas como por ejemplo que cuando inicializa vemos que escribe un dword 19,
siendo que es lgico ya que es un int, pero en la siguiente linea le escribe un byte solo a la misma

variable var_4
mov
mov

[ebp+var_4], 19h
byte ptr [ebp+var_4], 32h

No tiene mucho sentido si es solo una variable int inicializar su valor con un dword 19h y luego
escribir un solo byte en la misma en este caso el 32h, ah sospechamos de que hay una union entre
dos variables una int y una char.
En IDA para definir la unin vamos a la pestaa estructuras y agregamos una como siempre.

Solo que esta vez marcamos la tilde Create union y agregamos dos campos uno dword y uno de un
solo byte, el tamao de la unin siempre sera el del campo mas grande en este caso 4 pues hay un
dword.

Los renombramos como deseamos

Yendo a la ventana de variables apretando ALT mas Q encima de var_4, elegimos que sea del tipo
de la unin que hemos creado.

Cambiamos el nombre a la variable tambin.

Igual a pesar de todo el trabajo IDA no logra marcar los campos de las uniones como diferentes y
sigue tomando como una sola variable aunque del tipo unin.

Aunque si pasamos el mouse por dicha variable veremos que nos muestra como son los campos de
dicha unin.

Enumeraciones
Una enumeracin es un conjunto de constantes enteras. A la enumeracin se le puede asignar un
nombre, que se comportar como un nuevo tipo de dato que solo podr contener los valores
especificados en la enumeracin.
enum dia { DOM, LUN, MART, MIER, JUEV, VIER, SAB } diaX;
int main() {
diaX=LUN;
printf ("%d", diaX);
getchar();
}
All hay un tpico caso de enumeracin donde se relacionan por orden los das de la semana con un
numero, si no especificamos nada, sera DOM=0, LUN=1 etc, si corremos este programa vemos que
su salida es 1, ya que LUN esta relacionado con el numero 1, para el tipo de variable dia.

Si queremos agregar mas variables del tipo da podemos hacerlo, todas respetaran la relacin que
definimos al inicio entre los das y un numero entero.

Tambin se podra haber hecho.

Si lo vemos en IDA no veremos mucho mas que esto

La variable global diaX ya que fue definida antes del main, esta en 404060, podemos renombrarla.

En cambio la var_8 que es tambin del tipo da pero definida localmente, la renombraremos a
festivo.

Ahora vamos a la pestaa enumeraciones y agregamos una apretando INS tal cual hacemos con las
estructuras.

Ponemos decimal ya que queremos que se asocie con enteros decimales para que sea mas sencillo.
Apretando N vamos agregando los campos DOM sera 0 y as sucesivamente, salvo MIER que sera
9.

Ah esta lista vamos a las variables y elegimos el tipo de enumeracin que creamos

Si vamos al cdigo parece que nada cambio y es as, pero si hacemos click derecho en el 9.

Nos da la opcin de poner MIER como definimos en la enum para el 9.

Lo mismo para la variable global diaX la marcamos y

Y nos quedan cambiadas las constantes

CAMPOS DE BITS
Un campo de bits es un elemento de un registro (struct), que se define basndose en su tamao en
bits. Se define de forma muy parecida (pero no igual) a un "struct" normal, indicando el nmero de
bits que se debe reservar a cada elemento:

struct campo_de_bits {
int bit_1 : 1;
int bits_2_a_5 : 4;
int bit_6 : 1;
int bits_7_a_16 : 10;
} variableDeBits;
Esta variable ocupara 1+4+1+10 = 16 bits (2 bytes). Los campos de bits pueden ser interesantes
cuando queramos optimizar al mximo el espacio ocupado por nuestro datos.

#include <stdio.h>

int main() {
struct campo_de_bits {
int bit_1 : 1;
int bits_2_a_5 : 4;
int bit_6 : 1;
int bits_7_a_16 : 10;
} variableDeBits;

variableDeBits.bit_1 = 0;
variableDeBits.bits_2_a_5 = 3;
variableDeBits.bit_6 =0;
printf("%d", variableDeBits);

getchar();
getchar();
return 0;
}
Bueno vemos un campo de bits, y como asignamos valores, vemos que al asignar diferentes valores
a cada campo, el valor numrico de la variableDeBits cambia, obviamente pues estamos
cambiando sus bits.
En el ejemplo valdr 6 pues estamos asignando el decimal 3 que es 11 a la posicin 2 a 5 o sea que
0011 va al bit 2 a 5
o sea que variableDeBits quedara en binario 000110 que es el 6 decimal.

Si lo vemos en IDA no ganamos mucho para reversear tambin es trabajado todo sobre una solo
variable y realizadas las operaciones usando ANDs y ORs para cambiar los resultados de los bits.

Existe en la pestaa ENUM la opcin de crear campos de bits, pero realmente no aporta mucho al
reversing as que lo vamos solo a mencionar sin entrar en detalles, pues IDA aunque lo definas no
cambia el listado as que para el reverser no aporta mucho solo es bueno para conocer el tema.
Bueno hemos terminado con lo bsico para empezar a reversear a partir de la semana que viene
comenzaremos reverseando pequeos ejemplos que iremos programando nosotros mismos,
asimismo habr ejercicios de reversing ahora que terminamos la cucharada mas difcil que es ver la
base terica, el resto es remar con dos tenedores como remos jeje.
Hasta la prxima
Ricnar

REVERSEANDO Y PRACTICANDO
Antes de seguir con las practicas quera mostrar un par de detalles que me falto explicar y que sern
necesarios en estos ejemplos de reversing.
Lo primero es como se ingresan en C argumentos por consola.
#include <windows.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
int i;
if (argc >1){
printf("Cantidad de argumentos es %d\n", argc);
for (i=0; i<argc; i++) printf("argumentos son %s\n", argv[i]);
}
else {
printf("Tipear argumentos para probar\n");
}

getchar();

}
C provee un mecanismo para pasar argumentos desde la lnea de comandos al programa que se va a
ejecutar. Cuando el programa comienza su ejecucin, la rutina main es llamada con dos argumentos:
un contador y un puntero a un array de strings. El contador es llamado por convencin argc y el
apuntador argv. El uso de de argv es un poco truculento. Dado que argv es un puntero a un array de
strings, la primera cadena de caracteres es referenciada por argv[0] (o *argv). La segunda cadena is
referenciada por argv[1] (o *(argv + 1)), la tercera por argv[2], y as sucesivamente.
La primera cadena de caracteres, argv[0], contiene el nombre del programa. Los argumentos
comienzan realmente con argv[1].
Vemos que las argc y argv no necesitan ser declaradas como dijimos siempre el compilador las
maneja, si vemos este cdigo en el IDA.

Vemos que el main ya tiene como argumentos argc y argv que son dos dwords ya que el primero es
un contador o sea un int y el segundo es un puntero a un array de punteros a strings, por lo tanto
tambin es un int.

Vemos que compara la cantidad de argumentos con uno, ya que siempre el primer argumento es el
mismo nombre del ejecutable o sea que siempre sera 1 como mnimo.
Vemos que si es menor o igual que uno tomara el camino de la flecha verde y me pedir que tipee
argumentos para probar, mientras que si tipee alguno, ira por el camino rojo y imprimir cuantos
argumentos tipeamos.

Luego pondr la var_4 a cero que es el contador del for la puedo renombrar a i.

All esta coloreado el loop se mantendr loopeando mientras i sea menor que argc.

Vemos como toma el contador i y lo multiplica por 4 dentro del LEA, o sea que EDX se usara para
sumar al puntero, y poder recorrer todos los punteros a las strings, poniendo un BP aqu y tipeando
algunos argumentos.

En IDA en las Opciones del proceso o Process Options colocamos los argumentos y arrancamos el
debugger.

Vemos que luego de imprimir la cantidad de argumentos para en el BP, a EAX pasa el puntero al
array, vemos en el dump que el contenido de EAX o sea el primer campo, es otro puntero.

Como EDX es cero ya que es el primer ciclo del loop


mov

eax, [edx+eax]

EAX tendr el valor de dicho primer puntero que apuntara a la primera string de los argumentos.

Si le doy RUN para de nuevo en el BP y al llegar aqu


004012EF mov

eax, [edx+eax]

En este caso EDX vale 4 ya que i vala 1 y lo multiplica por 4, por lo tanto le sumara al puntero al
array, cuatro lo que apuntara al segundo campo o sea al segundo puntero a la string de los
argumentos.

O sea que confirmamos que argv es un puntero a un array de punteros a strings.


Bueno quera mostrar un poco como se ingresaban argumentos por consola, ya que algunos
ejemplos los usan.
All van 3 ejemplos sencillos y uno mas complejo, en este ultimo aparece un tipo de datos
avanzado llamado HANDLE, se usa para casos muy especficos como el valor que devuelve
OpenProcess en este caso, si ponemos que sea un int no funcionara.
HANDLE phandle = OpenProcess(PROCESS_ALL_ACCESS,0,pid);

All vemos que la api esta definida de esa forma y devuelve un tipo HANDLE, el resto es conocido,
como ayuda les dir que antes de pedir el handle a OpenProcess se llama a una funcion que eleva
los privilegios de nuestro proceso, si no muchas veces no tendr los suficientes privilegios para leer
la memoria de ciertos procesos.
Hasta la practica 2 y que se diviertan.
Ricardo Narvaja

Potrebbero piacerti anche