Al desarrollar exploits generalmente no tenemos el codigo fuente disponible, las aplicaciones escritas en lenguajes de alto nivel como Java
o C#
suelen decompilarse facilmente y obtener un codigo muy parecido al original, sin embargo cuando es escrito con un lenguaje mas de bajo nivel como C
o C++
este proceso es mas complicado y generalmente en su lugar usamos un desensamblador
Un desensamblador analiza el binario y lo convierte a instrucciones en ensamblador, algunos de ellos tienen la capacidad ordenar el codigo de forma en que sea mas intuitivo entender el flujo del programa, uno de los desensambladores mas conocidos es IDA
, sin embargo hay otras alternativas como Ghidra
, Radare2
o BinaryNinja
Open
Despues de instalar y abrir IDA
se nos muestra una ventana donde podemos abrir un nuevo archivo para analizarlo o cargar un proyecto antes analizado y no iniciar de 0
Para aprender a usarlo cargaremos el binario notepad.exe
de nuestro Windows 10, para ejecutables de windows de 32 bits (.exe
) y librerias (.dll
) usaremos la opción Portable executable for 80386
con las opciones por defecto que tiene IDA
Al cargar el binario notepad.exe
este empezará a analizarlo, este proceso suele tardar incluso hasta un par de minutos dependiendo del peso del archivo, cuando IDA
termine de analizarlo nos mostrará una ventana como la siguiente
Cuando terminamos de trabajar y cerramos el archivo tenemos la opción de guardar
el proyecto para renaudarlo mas tarde o simplemente cerrarlo sin guardar nada
View
La ventana que mas usaremos será la del desensamblado, en esta podemos organizar el codigo de 3 diferentes formas de acuerdo lo que necesitemos:
• Graph View: La vista grafica separa el codigo en funciones ordenando el codigo de la funcion en bloques
, esta vista es bastante intuitiva ya que se puede seguir el flujo
del programa con cada bloque que esta conectado por saltos
o condicionales
La flecha verde indica si la condición se cumplió a través de saltos condicionales como jz
y la flecha roja si esta no lo hizo, la flecha azul simplemente indica que solo hay un camino generalmente un jmp
sin ningun tipo de condición
• Text View: La vista de texto muestra todo el codigo de forma lineal
, de esta forma tambien podemos analizar el código pero es menos intuitivo seguir el flujo, algo curioso es que podemos alternar ente estra vista y la anterior usando la tecla Space
• Proximity View: La vista de proximidad es un poco mas avanzada y lo que hace es que nos permite ver las relaciones
entre funciones, variables globales y constantes, esta podemos encontrarla en View > Open subviews > Proximity browser
Basic Parts
Aunque lo que más usaremos sera el desensamblador a la izquierda podemos encontrar 2 ventanas, la primera de ellas nos muestra todas las funciones
existentes, cuando los simbolos no son cargados las funciones tendran como nombre sub_addr
, al hacer doble clic sobre alguno de ellos se nos mostrará su código desensamblado
Usando Ctrl + F
se nos abre un cuadro de busqueda donde podemos ingresar el nombre de una función
o su dirección para encontrarla rápidamente, ademas nos muestra datos como su sección, su dirección y la longitud que ocupa la función
Otra ventana a la izquierda es una previsualización del gráfico, esto nos sirve para desplazarnos rápidamente en el flujo de una función grande, el bloque
donde estamos trabajando se muestra con un cuadro con un contorno
de puntos
Podemos ir a bloques anteriormente analizados usando las flechas
en la barra
Basic Usage
Algo bastante util para el analisis es la opción Line prefixes
que nos mostrará detrás de cada función su dirección, esta la encontrarmos Options > General
Otra cosa bastante util es la pequeña pestaña con colores
presente en cada bloque
Aqui podemos elegir un color para colorear todo el bloque
, esto es util indentificar el flujo mas facilmente a la hora de analizarlo siguiendo un color especifico
Se puede usar cualquier colores pero para el analisis nos puede ayudar usar solo 2, en este caso verde
para caminos deseados y rojo
para caminos no deseados
Al presionar :
sobre una instrucción nos permite ingresar un comentario en esta
Presionando la tecla n
sobre una variable podemos cambiarle el nombre para hacerlo las identificable, en este caso pasamos de dword_addr
a my_variable
Esto funciona no solo para cambiar el nombre de variables sino también de funciones
La pestaña Imports
nos muestra todas las funciones importadas al binario desde una libreria externa (.dll
), y al lado de ella información sobre ella como su dirección
Al presionar x
sobre el nombre de una función o una variable se abrirá una pestaña con todas las referencias a este como llamadas en este caso a la función malloc()
Dynamic Usage
La forma de aprovechar IDA
es un analisis estatico para guiarnos en uno dinamico, sin embargo para saltar entre el debugger y el desensamblador necesitamos que la dirección base
coincida con la del proceso, gracias a protecciones como ASLR
la dirección cambia constantemente, podemos saber la dirección actual desde WinDbg
0:000> lm m notepad
Browse full module list
start end module name
00f60000 00f9f000 notepad (pdb symbols)
Desde Edit > Segments > Rebase program
podemos modificar la dirección base por la que nos muestra el debugger que esta siendo utilizada en tiempo de ejecucuón
Al hacer esto sincronizamos el analisis estatico y dinamico por lo que comparten direcciones, una forma de verlo es encontrando la una función dentro de notepad como StringLengthWorkerW
, con u
podemos ver su codigo desensamblado
0:000> x notepad!StringLengthWorkerW
00f78fc4 notepad!StringLengthWorkerW (void)
0:000> u notepad!StringLengthWorkerW
notepad!StringLengthWorkerW:
00f78fc4 8bff mov edi,edi
00f78fc6 55 push ebp
00f78fc7 8bec mov ebp,esp
00f78fc9 51 push ecx
00f78fca 56 push esi
00f78fcb baffffff7f mov edx,7FFFFFFFh
00f78fd0 b8a01df600 mov eax,offset notepad!`string' (00f61da0)
00f78fd5 57 push edi
Con la tecla g
podemos ir a esa dirección donde podemos ver que coincide y podemos seguir el flujo mas fácilmente desde IDA
en lugar de WinDbg
Como ejemplo vamos a establecer un breakpoint en la función CreateFileW
para correr el bloc de notas y detenernos cuando se crea un nuevo archivo
0:000> bp kernel32!CreateFileW
0:000> g
Si intentamos averiguar donde se llamó a la función en IDA
tendriamos que buscar las referencias a esta función, lamentablemente tenemos 20
posibilidades diferentes
Para ello crearmos un archivo y el programa se detiene en el breakpoint
, podemos ir hasta el siguiente ret
y salir de la función para tener una idea de la dirección
0:000> g
Breakpoint 0 hit
eax=00000000 ebx=00000001 ecx=0085ef4c edx=77931670 esi=0675bd48 edi=00000003
eip=774ac260 esp=0085ef70 ebp=0085f810 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
KERNEL32!CreateFileW:
774ac260 ff25a0435077 jmp dword ptr ds:0023:775043a0={KERNELBASE!CreateFileW (74c761a0)}
0:000> pt
eax=000003e0 ebx=00000001 ecx=a07b680f edx=00000000 esi=0675bd48 edi=00000003
eip=74c76201 esp=0085ef70 ebp=0085f810 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
KERNELBASE!CreateFileW+0x61:
74c76201 c21c00 ret 1Ch
0:000> p
eax=000003e0 ebx=00000001 ecx=a07b680f edx=00000000 esi=0675bd48 edi=00000003
eip=00f6865e esp=0085ef90 ebp=0085f810 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
notepad!SaveFile+0x8f:
00f6865e a39cf6f700 mov dword ptr [notepad!fp (00f7f69c)],eax ds:0023:00f7f69c=00000000
La dirección después de salir de la función es 0x00f6865e
, si ingresamos esto a IDA
nos lleva al bloque especifico donde se llamó a la función CreateFileW
, algo interesante es que IDA nos muestra como comentarios los argumentos que se le pasan al llamar a una función basandose en WinApis
para hacerlo mas legible
Seguimos el flujo de IDA y localizamos una llamada a ReadFile
dentro de la misma función que hizo una llamada a CreateFileW
, en el bloque podemos ver que el contenido de eax
en la direccion 0x00f650a4
es comentado como lpBuffer
que de acuerdo a la documentacion es el puntero al buffer que recibe los datos leidos
Establecemos un breakpoint en la llamada a ReadFile
y corremos el programa, al abrir un archivo se detiene en el breakpoint
y el segundo valor en el stack o el valor actual de eax que es 0x0073ea08
deberia ser el puntero de lpBuffer
0:000> bp 0x00f650a6
0:000> g
Breakpoint 0 hit
eax=0073ea08 ebx=00000880 ecx=09d33001 edx=00000000 esi=00c0b8fc edi=06832368
eip=00f650a6 esp=0073e9d8 ebp=0073ee0c iopl=0 nv up ei pl nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200217
notepad!UpdateEncoding+0x7e:
00f650a6 ff15c401f800 call dword ptr ds:0023:00f801c4={KERNEL32!ReadFile (774ac5e0)}
0:000> dds esp L5
0073e9d8 00000880
0073e9dc 0073ea08
0073e9e0 00000400
0073e9e4 0073ea00
0073e9e8 00000000
Después de ejecutar la función en la dirección 0x0073ea08
se deberia haber guardado el contenido del archivo, esto podemos comprobandolo usando da
0:000> p
eax=00000001 ebx=00000880 ecx=09a0d9d1 edx=77931670 esi=00c0b8fc edi=06832368
eip=00f650ac esp=0073e9ec ebp=0073ee0c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
notepad!UpdateEncoding+0x84:
00f650ac 85c0 test eax,eax
0:000> da 0073ea08
0073ea08 "test"