Copy Link
Add to Bookmark
Report

SET 033 0X0D

  

-[ 0x0D ]--------------------------------------------------------------------
-[ IDA para neofitos ]-------------------------------------------------------
-[ by FCA00000 ]-----------------------------------------------------SET-33--

IDA_info

En este articulo se cuenta informacion relevante al desensamblador
IDA = The Interactive Disassembler, y modos de mejorarlo.
Si has leido alguno de los textos escritos por mi, habras encontrado que
continuamente hago referencia a este desensamblador.
Tras muchas horas pasadas con este programa, creo que seria util compartir
con vosotros lo que yo he aprendido.

En la primera parte explico como hacerlo funcionar. Luego comento algunos
trucos que a mi me resultan utiles, y al final cuento varias maneras de
mejorarlo, completando la funcionalidad que se echa de menos.

----------------
Yo lo uso porque es el mejor para desensamblar programas de ARM, usado en
moviles Symbian. Tambien es capaz de desensamblar codigo de otros
procesadores, tales como Z80, 80x86, NET, Playstation, XBox, e incluso
Java y programas de Linux y Windows CE.

La ultima version que yo conozco es IDA Pro Advanced 5.0
Es una lastima que este disponible en tantos sitios de warez: eso solo hace
que los autores (www.Datarescue.com) pierdan interes en seguir desarrollando
su producto. Como siempre digo: si usas una aplicacion, comprala.

Recuerda que algunas leyes prohiben el desensamblado de programas ajenos.
En general se admite que desensambles programas hechos por ti.
Por tanto, desensamblar un virus que te ha infectado es ilegal :-%

----------------
Este parrafo explica por encima como manejarse con IDA. Obviamente la
mejor manera de adquirir practica es experimentando, asi que no te
extranye si vamos un poco rapido al principio.

Tras iniciar el programa lo primero que se hace es abrir el programa que
quieres desensamblar.
Si es capaz de identificar el procesador para el que esta escrito el
programa, lo sugiere. Si no, solicita el tipo de procesador.
La mayoria de las aplicaciones que yo he destripado han sido identificadas
correctamente. He tenido dificultades con los programas del ZX-Spectrum,
porque no entiende el formato, pero eso es normal.
Tambien da problemas con los programas extraidos de la memoria de los
moviles Symbian, pues la cabecera es incorrecta, y se lia al encontrar el
punto de entrada.

Este problema es facilmente solucionable eligiendo el tipo de fichero
como Binary en lugar de EPOC.
Esto me permite escribir el punto de entrada, y la direccion de carga, que
para Symbian resulta ser 0x64 bytes menos de lo que indica el programa a
desensamblar. Para los programas del Spectrum, hay que mirar el formato en
el que se guardan. El particular el formato SNA contiene una cabecera que
ocupa 48 bytes, seguido por el volcado de la memoria a partir de la
direccion 0x4000.

Entonces empieza automaticamente a desensamblar el programa, y a continuacion
presenta el listado; bien en modo de arbol de rutinas, bien en modo texto.

Lo importante de un desensamblado es poder seguir el flujo de informacion.
Si una rutina llama a otra, puedes hacer doble-click para verla.
Pulsando "escape" vuelves a la inicial.
Seleccionando una rutina, o una etiqueta, puedes pulsar "X" para ver desde
donde se llama; posiblemente desde varios sitios.
El menu View-OpenSubviews->FunctionCalls habilita una ventana flotante para
hacer mas facil esta tarea. Asi sabes de donde vienes, a donde vas, y
porque tan rapido.

Puedes abrir una ventana nueva (Alt+Enter) para investigar 2 rutinas a la vez.

Con las teclas F12 y CTRL+F12 puedes ver el flujo completo de las rutinas,
presentado en la aplicacion WinGraph (licencia GPL).
Eso si, el grafico completo de todas las rutinas puede que sea bestialmente
grande.
Mas eficiente es colocar el cursor en una rutina determinada y usar el boton
derecho para sacar el grafico parcial de "Chart of xrefs to".

IDA permite escribir comentarios en el codigo pulsando ";".
Para renombrar las subrutinas y constantes hay que marcarlas con el cursor y
pulsar "N".
Si quieres ver un dato en otra base numerica (Hex/Dec/Oct/Bin/ASCII) solo
hay que marcarla y pulsar el boton derecho del raton.

Una parte fundamental del desensamblado es saber la representacion real de
los datos. Para ello puedes abrir la vista HexDump para ver en hexadecimal
los codigos de las instrucciones.
Posiblemente tambien quieras usar BotonDerecho->SynchronizeWith->IDA View-A
para ver exactamente los datos de la instruccion que estas analizando.
Es una lastima que no permita modificar los datos en vivo, pero cualquier
editor hexadecimal te sirve para esto.

Normalmente los programas llaman a otras rutinas del sistema operativo.
IDA sabe cuales son estas rutinas y sus parametros.
Un ejemplo de desensamblado de un programa en Windows tiene las instrucciones
.text:000129FB push offset SourceString ; SourceString
.text:00012A00 push [ebp+DestinationString] ; DestinationString
.text:00012A03 mov [ecx], eax
.text:00012A05 call ds:RtlInitUnicodeString

y luego
.rdata:00013018 ; void __stdcall RtlInitUnicodeString(PUNICODE_STRING
DestinationString,PCWSTR SourceString)
.rdata:00013018 RtlInitUnicodeString dd ? ; DATA XREF: sub_129A05

O sea, que sabe que 00012A05 llama a RtlInitUnicodeString y que los
argumentos se definen en 000129FB y 00012A00

No solo esto, sino que es capaz de decir las librerias importadas por un
programa. Puede extaer informacion de modulos compilados con informacion de
debug, usados comunmente en Windows y Linux.

Tambien entiende constantes alfanumericas en varias implementaciones.
Una palabra, por ejemplo "Ayax" se puede representar con los bytes:
41 79 61 78 00 -> un byte por letra y el terminador 00 , tipico de lenguaje C
41 79 61 78 24 -> un byte por letra y el terminador 00 , usado en MS-DOS
41 00 79 00 61 00 78 00 00 00 -> dos bytes por letra y el terminador
doble-00 , tipico de unicode
04 00 00 00 41 00 79 00 61 00 78 00 00 00 -> cuatro bytes indicando la
longitud, seguidos por dos bytes por letra
y el terminador doble-00 , tipico de unicode de
Symbian, llamado Unicode-Width-Pascal-4
Hay otros mas pero estos son los que IDA reconoce sin muchos problemas.
Bueno, el ultimo formato si da problemas, pero los veremos despues.

Para buscar un texto, instruccion, constante o cualquier otra cosa, usas las
teclas Alt-T y lo escribes en el cuadro de dialogo. Para buscar datos en la
ventana de representacion hexadecimal usas Alt-B

Otra opcion bastante potente es exportar los datos desensamblados. En el
menu Options->General->Analysis->TargetAssembler puedes elegir un ensamblador
. Luego en el menu File->ProduceFile->Create_ASM_File puedes extraer el
listado, y ensamblarlo de nuevo.
En Symbian hay que hacerle algunos ajustes, pero acaba funcionando igual
que el original. Todo depende del tiempo que quieras dedicarle.

Otra manera de extraer datos es usando el menu
ProduceFile-Dump_database_to_IDC_file
El archivo generado contendra la informacion relevante para IDA: inicio de
las rutinas, nombres dados a las variables, comentarios, referencias,
constantes, puntos de entrada, segmentos, ...
Esto tiene un gran utilidad para hacer que IDA haga cosas que no estaban
consideradas por los creadores de la herramienta, y sera usado en el tercer
apartado de este articulo.

Otras ventanas utiles son las que muestran en una lista todas las funciones
importadas y exportadas, o internas. Similarmente se puede ver el listado de
todas las cadenas de texto: Shift-F12 . No te olvides de pulsar la etiqueta
"String" para ordenarlas.

Yo suelo abrir la ventana de desensamblado, el FunctionCalls, y el volcado
Hexadecimal. A veces abro 2 desensamblados. Con esto, me apanyo, aunque un
monitor de 20 pulgadas no me vendria mal.

Es bastante util el menu Jump-MarkPosition (Alt-M) para marcar una posicion.
Luego se puede volver a ella usando Ctrl-M . Obviamente lo mejor es que se
pueden marcar varias posiciones y darles nombre que sean intuitivos.
Como no todo podia ser perfecto, esta informacion no se guarda en el IDC .

Bueno; hasta aqui es un minimo manual de uso.

---------------
Ahora comentare algunos trucos que yo encuentro utiles. Estan motivados por
el uso que yo le doy a esta herramienta, habitualmente desensamblando
programas para Symbian.

Lo primero es que el desensamblado automatico empieza por la rutina de
inicio, y desensambla en forma de arbol: si A llama a B, entonces B tambien
sera desensamblada. Esto se llama desensamblado de flujo lineal.
Pero si alguna rutina no esta llamada desde otra, quizas no la desensambla.
IDA tiene una metodologia de desensamblado llamada "Recursive traversal"
en la que intenta everiguar si un trozo de bytes es codigo o datos.
Pero como todo artifacto fabricado por humanos, a veces falla.

Este problema no se percibe normalemente al desensamblar un programa, pero
si en las librerias.
La solucion es marcar la primera linea del programa (Alt-L), hacer scroll
hasta la ultima (Ctrl-PageDown), y pulsar "c" para convertirlo en codigo.
Lo mismo sirve para convertirlo en una cadena de caracteres, si notamos que
en realidad son caracteres imprimibles.

El segundo se refiere a la busqueda. Normalmente pulsas Alt-T para decir el
texto que quieres buscar, y luego Ctrl-T sigue la busqueda.
Pero es mucho mas eficiente poner el cursor en la palabra a buscar, y
pulsar Alt-FlechaAbajo, o Alt-FlechaArriba . Por supuesto esto sirve para
cualquier palabra: datos, nombres de rutinas, o incluso instrucciones.
Por poner un ejemplo, suponer que estas analizando una rutina que usa el
registro R8, y quieres ver donde se ha inicializado. Asi que pones el cursor
en la palabra "R8" y Alt-FlechaArriba te lleva a la instruccion anterior
que usa tal registro. No va a resolver el hambre en el mundo, pero acelera
el trabajo de analisis.


Otro truco tiene que ver con las constantes. Es posible asignarles nombres,
o elegir de los nombres estandar de Windows, que quizas esten definidos en
otro sitio. (Nota: ?Porque solo funcionara en Windows, y no en Symbian?)
Supongamos este codigo en windows:
.text:0001290F call ds:KfReleaseSpinLock
.text:00012915 mov eax, 0C000009Ah

Ahora bien: ?que es el valor 0x0C000009Ah? Desde luego no parece elegido al
azar.
Asi que pongo el cursor sobre este valor, pulso el boton derecho, y digo
"Use standard symbolic constants".
Me sugiere utilizar el simbolo STATUS_INSUFFICIENT_RESOURCES , lo cual es
consistente con la funcion KfReleaseSpinLock

Esto desde luego ayuda a saber lo que esta haciendo el programa.

Claro que a veces hay que romperse un poco la cabeza: el valor 0x39h en
windows tiene estos posibles significados:
ATM_CAUSE_BEARER_CAPABILITY_UNAUTHORIZED
CR_INVALID_CONFLICT_LIST
DPFLTR_IDEP_ID
FILE_DEVICE_KSEC
LANG_HINDI
....
y por supuesto, mirando el codigo alrededor:
00012BC8 mov di, [ecx]
00012BCB cmp di, 30h
00012BCF jb short loc_12BD7
00012BD1 cmp di, 39h
00012BD5 jbe short loc_12BDE

nos da la pista de que se compara si el valor de di esta entre "0" y "9",
es decir, verifica que es numerico.
O sea, que no es ninguna de estas constantes, sino simplemente el valor "9".

Si hubieramos elegido LANG_HINDI , al hacer doble-click el propio IDA abre
la ventana de Enumeraciones de tipo MACRO_LANG que nos indica que otros
posibles valores son:
LANG_AFRIKAANS = 36h
LANG_GEORGIAN = 37h
LANG_FAEROESE = 38h
LANG_HINDI = 39h
LANG_MALAY = 3Eh
.....

y esto tambien ayuda a la hora de cambiar una variable por otra. Supongamos
que en otro programa se chequea que el lenguaje de instalacion es LANG_HINDI,
pero nuestro ordenador esta instalado en lenguaje LANG_MALAY . Entonces hay
que cambiar la comparacion
cmp di, 39h
por
cmp di, 3Eh

pero vamos, dudo que existan protecciones de este tipo.


A lo que iba, que en seguida me despisto.
A veces la variable no esta conocida en ninguna de las librerias por defecto.
Bueno, en esta ventana "Enums" es posible definir nuevos simbolos y usarlos
en cualquiera de las posteriores sesiones de desensamblado.

-------------------
Otro truco: habras notado que si seleccionas una palabra, se marca en color
amarillo. Esto permite identificar rapidamente en pantalla todas las
ocurrencias de dicha palabra. Pero si pinchas en cualquier otro punto,
pierdes la seleccion. Para evitar esto puedes usar el icono
"Lock the current highlight".

Junto a este icono hay otro con muchos colorines que muestra un mapa del
programa. En color Azul oscuro corresponde al codigo desensamblado, en rosa
las funciones importadas, en gris los datos, burdeos para el codigo que no
tiene sentido, marron para las areas vacias, y azul claro para las funciones
de librerias.
Pues bien, es posible adecuar este area para que muestre otros tipos de
informaciones. Para ello se usa el dropdown llamado "Aditional display".
Por ejemplo, seleccionando "Marked positions" nos muestra un punto rojo en
el sitio donde hayamos puesto marcas.
Esto sirve para tener una vision de zonas de programa. Lastima que desde
esta ventana no se puede saltar a la direccion donde esta el codigo.

Cuando desensamblas un programa es bastante frecuente tener que mirar a la
vez en 4 sitios, asi que una navegacion eficiente es fundamental.

Ah, cuando buscas algo a lo largo del programa, esta ventanita muestra un
cursor que indica donde esta buscando. Asi sabes el porcentaje que lleva
analizado.

Otros de los puntos fuertes de IDA es que incluye un debugger. No lo he
usado apenas asi que no voy a comentar nada. Mi esperanza era que fuese
capaz de simular procesadores ARM, pero solo funciona para Windows y Linux,
los cuales, como digo, no he investigado en profundidad.

El mejor truco que puedo recomendar es usar un monitor grande. O dos. No se
como lo hago, pero nunca tengo suficiente espacio para trabajar comodamente.

Estos consejos ayudan a manejarse con el programa, pero la verdadera
potencia es que deja varias puertas abiertas para complementar la tarea.

-------------------
Para comprender un programa, un debugger casi nunca es suficiente por si
mismo. A menudo hay que realizar tareas especificas que los disenyadores
de IDA no habian previsto.

Por ejemplo, suponer que quiero buscar todas aquellas variables que se
inician con un valor entre 0x30 y 0x39 .
La unica manera es usar la opcion "Buscar" para localizar 0x3 (notar que
me he dejado el ultimo digito) y anotar uno por uno los datos. Pero esto
tambien encuentra datos tales como 0x3F , 0x399999 , ...

Gracias a que puedo extraer la informacion en un fichero ASM, puedo hacer un
mini-programa en Perl (o usando grep , o AWK) para buscar esas ocurrencias:
grep "0x3?" salida.asm


Supongamos una tarea ligeramente mas compleja: sacar aquellas rutinas que:
-comparan un valor con otro
-siguen 4 o menos instrucciones
-saltan a otro sitio si el resultado no es cero

Un ejemplo seria:
01002E81 cmp dword_1008828, ebx
01002E87 mov eax, [ebp+wParam]
01002E8A jnz short loc_1002EF1

Esto se puede hacer tambien el Perl, algo asi:

-lee linea
-si es "cmp" entonces haz cnt=1
-si es "jnz" y cnt>0 entonces haz cnt=-1 , y muestra la direccion
-si cnt>0 entonces incrementa cnt
-si cnt>4 entonces cnt=-1

No es tan dificil, sobre todo haciendolo en Perl.


Otro caso: tomar una rutina, y ver desde donde es llamada, hasta 3 niveles
de profundidad.
Para verlo mas claro suponer que la rutina se llama A. Entonces son validas:
- B, si llama a A
- C, si llama a B
- D, si llama a C

pero si E llama a D, entonces A esta mas alla de 3 niveles, y no vale.

Ahora las cosas se le complican a Perl, pues solo es capaz de leer lineas
secuencialmente; no puede ir hacia atras, y mucho menos hacer arboles de
llamadas.
Bueno, si que se puede hacer, pero yo no se como :-)

Lo que necesitamos es un sistema que pueda moverse por el codigo adelante y
hacia atras, siguiendo flujos de subrutinas y multiples caminos. ?Quien
puede hacer esto? Pues el propio IDA.

Sus desarrolladores tuvieron la misma idea, y decidieron permitir que se
pudiera re-programar. Para ello inventaron un lenguaje llamado idc que es
muy parecido al lenguaje C .

Para saber las cosas que se pueden hacer es bueno abrir el fichero de ayuda,
y saltar al tema "Index of IDC functions". Presenta unas 400 funciones, pero
tranquilo, que la mitad no se usan. El resto contiene muchas que son
parecidas entre si: ej, GetStrucNextOff es similar a GetStrucPrevOff .

Las funciones tambien estan documentadas en el fichero idc.idc , por si
quieres imprimirlas todas.

Vamos con un ejemplo sencillo: strstr
La declaracion es:
long strstr(string str,string substr); // find a substring, -1 = not found

O sea, que necesita un primer argumento con una frase, y un segundo
argumento con la palabra a buscar.
Asi, este programa:


#include <idc.idc>
static main(void)
{
auto frase, palabra;
frase = "Hay palabras dentro de esta frase.";
palabra = "palabra";
if( strstr(frase,palabra) >= 1)
{
Message("Encontrado!");
}
else
{
Message("No encontrado!");
}
return;
}


Al cargarlo y ejecutarlo con F2 mostrara el mensaje "Encontrado!" en la
ventana que esta en la parte inferior de IDA, llamada "Messages Window".
Si no ves esta area, amplia la barra inferior de la aplicacion.

Tambien se puede usar la funcion
Warning
para mostarlo en una ventana de dialogo.

Otro programilla; esta vez le solicito al usuario una direccion de programa,
y le digo a IDA que desensamble 8 bytes a partir de ahi:

#include <idc.idc>
static main(void)
{
auto mi_dir;
mi_dir=0x0;
mi_dir=AskAddr(mi_dir,"Introduce una direccion");
AnalyzeArea(mi_dir,mi_dir+8);
return;
}


Un poco mas amigable es que el usuario ponga el cursor en una direccion, y
desensamble desde ahi.
Para ello se usa

long ScreenEA(); // the current screen ea (linear address of cursor)

de este modo:
mi_dir=ScreenEA(); // en vez de mi_dir=0x0;

Para el caso que he propuesto de las busquedas en 3 niveles, debo usar
long RfirstB (long To); // Get first code xref to "To"
que busca referencias hacia esta direccion

y sus hermana
long RnextB (long To,long current); // Get next code xref to "To"
que busca posteriores referencias.

Nota: existen variaciones de estas funciones que buscan referencias desde
codigo/datos, adelante/atras, estandar/extendidas.

El programa queda asi:

#include <idc.idc>
static main(void)
{
auto mi_dir;
AnalyzeArea(INF_MIN_EA,INF_MAX_EA); // primero hay que analizar el codigo
// completo para que IDA marque las referencias
mi_dir=ScreenEA();
mi_dir=AskAddr(mi_dir,"Introduce una direccion");
for ( x=RfirstB(ea); x != BADADDR; x=RnextB(ea,x) ) // por cada direccion
{ // que apunta a mi_dir ...
Message("Nivel 1: " + atoa(x) + "\n"); // imprimela
for ( y=RfirstB(x); y != BADADDR; y=RnextB(ea,y) ) // y busca aquellas que
{ // la re-referencian
Message("Nivel 2: " + atoa(y) + "\n"); // imprime las de segundo nivel
for ( z=RfirstB(y); z != BADADDR; z=RnextB(ea,z) ) // y sigue buscando las
{ // que la re-re-referencian
Message("Nivel 3: " + atoa(z) + "\n");
}
}
return;
}

Esto saca un listado, que es posible mandar a un fichero usando la variable
set IDALOG=c:\refs3Nivel.txt

Mas elegante es mandarlo a un fichero nombrado como la direccion misma:
nombre_archivo = "c:\\" + "refs_" + atoa(mi_dir) + ".txt"

my_archivo = fopen(nombre_archivo, "w");
.....
dato_hacia_fichero = "Nivel 3: " + atoa(z) + "\n";
writestr(my_archivo, dato_hacia_fichero);
....
fclose(my_archivo);

Como ves, bastante parecido al lenguaje C . Esto simplifica la curva de
aprendizaje, suponiendo que ya sabes programar en C , claro.

-------------------
Antes he dicho que no es posible guardar las marcas que pones cuando estas
trabajando en el programa. Bueno, es posible hacerlo en lenguaje idc :
#include <idc.idc>
static main(void)
{
for (slot = 1; slot < 1023; slot = slot + 1)
{
pos = GetMarkedPos(slot);
if (pos>=0)
{
Message("Marca " + slot + " en direccion " + ltoa(pos) +
" con nombre " + GetMarkComment(slot) + "\n");
}
}
}


La mayoria de las cosas que se hacen a mano es posible tambien hacerlas
desde un programa.
Hemos visto antes que se le pueden dar nombre a las constantes.
?Quieres saber como se hace desde idc ? Muy facil; usando

success SetConstName(long const_id,string name);
// ** set a comment of a symbolic constant
// arguments: const_id - id of const
// cmt - new comment for the constant
// repeatable - 0:set regular comment
// 1:set repeatable comment
// returns: 1-ok,0-failed


Por ejemplo:
SetConstName(0xFCA00000, "El_autor" );
hara que todas las ocurrencias de 0xFCA00000 pasen a llamarse "El_autor" .

Unas funciones muy utiles son las que convierten uno o mas bytes en otro
tipo de datos.
Por ejemplo, en Symbian es comun que los bytes
10 00 ?? ??
se refieran a un UID - identificador de aplicacion.
Obviamente IDA no es consciente de ello, pero podemos forzarle:

dir_base = BeginEA();
dir_data = Dword (dir_base + 4 * 0x12 ); // el valor en la
// direccion (base+4*0x12) dice donde empieza el
// segmento de datos
dir_end = MaxEA(); // final del fichero analizado
for (dir=dir_data; dir<dir_end; dir=dir+4 ) // recorre el programa
{
dato = Dword(dir); // considera 4 bytes como un double-word, en memoria
if (dato>=0x10000000 && dato<=0x1000FFFF ) // si es 1000???? , entonces...
{
MakeWord(dir); // convierte los 4 bytes en una Double-Word, en el listado
MakeName( dir, "UID_" + ltoa(dir,16) ) ; // le da nombre "UID_1000????"
}
}


-------------------
Si no nos gustan los nombres de subrutinas que IDA ha generado se pueden
cambiar con
MakeName (long ea,string name); // assign a name to a location

Incluso a veces no me gustan los nombres de las instrucciones, asi que los
renombro usando la funcion
SetManualInsn (long ea, string insn);

Este es un caso que yo necesito usar porque mi compilador no es capaz de
entender la instruccion
STMFD SP!, ...
sino que quiere que se llame
STMFD! SP, ...

Por eso tengo una rutina que hace

for (my_dir=dir_base; my_dir<dir_end; my_dir=my_dir+4 )
{
ins=GetManualInsn(my_dir);
if(strstr(ins,"STMFD SP!")
{
ins_nueva="STMFD! SP,"; // nueva instrucci;on
ins_nueva=ins_nueva+substr(ins,11,-1); // concatena los caracteres
// desde 11 hasta el final
SetManualInsn(my_dir,ins_nueva);
}
}


Ah, y podemos generar el fichero ASM con todo el desensamblado:
GenerateFile( OFILE_ASM,
fopen("salida.ASM","w"),
dir_base,
dir_end,
GENFLG_MAPDMNG);

?Recordais que he dicho que no se puede modificar el programa binario
cargado? Menti. Solo hay que usar la funcion
void PatchByte (long ea,long value);

Vamos con otro ejemplo: supongamos que quiero parchear un programa para que
cambie unos bytes por otros.
Para ello me invento un formato:
replace:3B284ADD3C202060CDA800682D18-FF284ADD3C202060CDA800682D18
(en realidad no me lo he inventado: es el formato usado para parchear
programas del Siemens-SX1. Notar que un fichero puede contener varias
lineas con varios parches.)

La cadena inicial antes de ":" es lo que hay que buscar, y la cadena que
sigue son los nuevos datos.
Cada 2 caracteres se combinan para generar un byte. Por ejemplo 3B significa
el byte 0x3B .

Como veis, en este caso la cadena inicial y final solo se diferencian en el
primer byte. Los restantes son para comprobar que solo queremos cambiar
esta occurencia: si encuentra
3B211111111111
entonces no lo cambiara.

Para aplicar este parche a un fichero cargado en IDA hago un programilla:

#include <idc.idc>
static main(void)
{
parchea();
}

static parchea()
{
auto patchFileName, patchFile, string;
patchFileName = AskFileEx(0,"*.sxp","Elige el archivo con el parche");
patchFile = fopen(patchFileName, "rb"); // abre el archivo con los parches
while(string != -1) // sigue mientras no llegue al final del fichero
{
cad_leida = readstr(patchFile); // lee una cadena completa del fichero
if (strstr(cad_leida,"replace:") ==0) // si include la palabra "replace:" ...
{
pos_destino = strstr( cad_leida, "-" ); // busca el separador
cadena_original=substr( cad_leida, 8, pos_destino); // parte anterior al separador
cadena_destino=substr( cad_leida, pos_destino,-8); // parte posterior al separador

dir_base = BeginEA(); // empieza desde el inicio

encontrado=FindBinary(dir_base, SEARCH_NEXT, cadena_original); // busca
while(encontrado != BADADDR) // si lo encuentra...
{
for (i=1; i<strlen(cadena_destino); i=i+2) // parte la cadena destino en trozo de 2 letras
{
nuevo_byte_ascii=substr(cadena_destino,i,i+1); // obtiene este trozo
nuevo_byte_hex = xtol(nuevo_byte_ascii); // lo convierte a Hex
PatchByte(encontrado+i,nuevo_byte_hex); // lo modifica en IDA
}
}
}
}
}

Para ejecutar este script (y todos los demas) hay que ir al menu
File->IDC File
y cargarlo.

-------------------
Un poco mas comodo es usar una tecla de acceso rapido. El fichero ida.idc se
ejecuta al iniciar la aplicacion, asi que podemos incluir

AddHotkey("Shift-P","parchea");

Y en cualquier momento puedo pulsar Shift-P para invocarlo.

Si te fastidia ir al menu "File->IDC File" puedes redefinir todas las
teclas de acceso directo en el fichero idagui.cfg
La mayoria de los menus no tienen teclas asignadas, pero puedes hacerlo.
Por ejemplo, este menu de cargar ficheros IDC tiene en idagui.cfg la linea
"Execute" = 0 // Execute IDC file
que puedes cambiar a
"Execute" = "Shift-F3" // Execute IDC file

Eso si, ten cuidado de no asignar varias acciones a la misma tecla.
Y recuerda que puedes cambiarlas sobre la marcha en un script usando
AddHotkey .

Tambien puedes ejecutar un comando cualquiera con el menu Shift-F2 .
Esto es util si no quieres hacer un programa completo.

-------------------
A veces queremos parchear el fichero binario directamente en el disco.
Para ello se usan las funciones fgetc/fputc como en cualquir programa en
lenguaje C.

Si esto no es suficiente puedes usar la funcion Exec para ejecutar cualquier
otro programa. Uno de estos casos esta provocado por el hecho de que IDA no
es capaz de recompilar codigo ensamblador para procesadores ARM.
Supongamos que tengo un programa original en ARM con una rutina que hace
MOV R0, #0x28
lo cual se escribe con los bytes
28 00 A0 E3

Pretendo cambiarlo por
MOV R0, #0x29
pero IDA no sabe como ensamblarlo. Por eso defino una combinacion de teclas
SHift-K
que invoca a un script que hace:

nueva_instruccion=AskStr("","Nueva instruccion?"); // pide instruccion, ej. "MOV R0, #0x29"
my_archivo = fopen("instruccion.asm", "w"); // abre fichero de salida
writestr(my_archivo, nueva_instruccion); // escribe el codigo assembler
fclose(my_archivo);
Exec("compilaARM.bat instruccion.asm"); // compila la instruccion
// con un compilador externo

my_archivo = fopen("instruccion.bin", "r"); // mira si se ha generado correctamente
if(my_archivo==0) // si no, se queja
Message("Archivo no existe\n");
else // todo correcto
{
ea=ScreenEA(); // mira donde tiene que parchear
for(i=0;i<4;i=i+1) // cada instruccion ARM ocupa 4 bytes
{
fgetc(my_archivo); // que extrae del fichero
PatchByte(ea+i); // y mete en la memoria de IDA
}
fclose(my_archivo);
}



Otro ejemplo en el que uso la funcion Exec es cuando necesito hacer muchas
cosas sobre el fichero desensamblado. Un caso tipico es un programa que
llama a otra rutina externa, y quiero saber los registros que modifica.
Veamoslo ma claro:
Tengo una rutina que hace
MOV R1, R5
BL TTime::MicroSecondsFrom(TTime)

y quiero saber si TTime::MicroSecondsFrom(TTime) altera el registro R4 .
En tal caso debere guardarlo antes de llamarla si no quiero perder su valor.

Esta rutina esta en otra libreria Euser.dll , lo que me obligaria a cargarlo,
desensamblarlo con IDA, e investigar un poco.

En lugar de eso, hago un fichero IDC que
-invoca otra sesion de IDA, usando Exec
-carga Euser.dll, usando la opcion input-file
-lo desensambla, si no lo esta previamente, usando AnalyzeArea
-salta a la rutina TTime::MicroSecondsFrom() , usando LocByName
-desde el principio hasta el fina de la rutina ...
-mira si aparece la palabra R4 , usando strstr
-en tal caso asume que se esta modificando.

una excepcion a esto es cuando la primera instruccion es del tipo
STMFD SP!, {R4-R6,LR}
que indica que la propia rutina invocada se encarga de guardar los registros
que va a modificar.
Es facil de detectar: si la primera linea en assembler de la funcion
contiene la palabras "STM" y "R4" entonces se que se guarda.

Por supuesto, esto no es del todo correcto: podria suceder que
TTime::MicroSecondsFrom() llame a otra rutina
TInt64::operator_substract(TInt64 const &)
y que esta modificara R4.

Pero lo importante es que Exec permite automatizar este proceso.

---------------------
No incluyo el codigo de este programa idc porque es demasiado largo.
Si quieres ver muchos ejemplos de scripts, mira en el directorio
IDA\idc\*.idc
Yo tambien he encontrado algunos ejemplos en foros dedicados a reprogramacion
de moviles.
Y el sitio adecuado para preguntar es www.openrce.org , que tiene una bonita
coleccion de scripts. Mira tambien la seccion de links porque apunta a
varios proyectos de open-source muy interesantes.

---------------------
En otro articulo comente que IDA es capaz de desensamblar programas de
Symbian pero para algunas de las rutinas externas no sabe su nombre correcto.
Por ejemplo cree que la rutina
CPeriodic::Start
se llama
CListBoxData::Reserved_2



En aquel momento yo no sabia donde estaba esta definicion. Bueno, pues la
he encontrado.
Con la version original de IDA (ojo, no con la version pirata que circula
por ahi) se incluyen en el directorio idsutil3 un par de programillas muy
simples.
El primero en el que centrare mi atencion es ZIPIDS.EXE para descomprimir
los ficheros de tipo IDS :
ZIPIDS -u C:\Ida\ids\epoc6\arm\euser.ids
File: euser.ids ... {1875 entries [0/0/0]} unpacked

y genera

euser.idt
con este contenido:
;DECLARATION
;ALIGNMENT 2

; Module Name and Description
0 Name=EUSER
;---------------------------------------
1 Name=memset
2 Name=memcpy
3 Name=newL__5CBaseUi
4 Name=ASin__4MathRdRCd
5 Name=ATan__4MathRdRCd
6 Name=ATan__4MathRdRCdT2
.....
302 Name=DaysInMonth__C5TTime
.....
1644 Name=__t13CArrayFixFlat1Zii
1645 Name=_._t13CArrayFixFlat1Z4TUid
1646 Name=__t13CArrayFixFlat1Z4TUidi
;------------------EOF------------------

Recordar que en Symbian (y tambien en las DLL de windows) cuando un progama
quiere invocar a una rutina, no la llama por el nombre, sino por un indice
relativo a la libreria.
Por ejemplo, si un programa quiere llamar a la funcion TTime::DaysInMonth
entonces incluye una referencia a EUSER y llama a la funcion 302
Para solucionar el problema cuando la misma rutina existe varias veces con
el mismo nombre pero con distintos argumentos, es necesario incluir el tipo
de variables. Por eso el nombre es "decorado" con indicadores y obtiene el
nombre
DaysInMonth__C5TTime

Este proceso se llama "mangling".

Como digo, esta es la lista que esta dentro del fichero euser.ids que
es el que IDA usa para nombrar las rutinas.

Pero el telefono (y el emulador) usa los de la libreria real euser.lib
que puedo investigar con
AR2IDT.EXE C:\Symbian\6.1\Series60\Epoc32\Release\armi\urel\euser.lib
que produce
0 Name=EUSER
1 Name=memset
2 Name=memcpy
3 Name=newL__5CBaseUi
......
302 Name=DaysInMonth__C5TTime
.....
1644 Name=__t13CArrayFixFlat1Zii
1645 Name=_._t13CArrayFixFlat1Z4TUid
1646 Name=__t13CArrayFixFlat1Z4TUidi
1647 Name=__7RRegioniP5TRecti
1648 Name=ChunkHeap__8UserHeapR6RChunkii
......
1678 Name=Start__20CActiveSchedulerWait
1679 Name=_._20CActiveSchedulerWait

Por si no te has dado cuenta, en la referencia incluida por defecto en IDA
existen solo 1646, pero en la libreria real existen las rutinas hasta 1679.
Esto quiere decir que si un programa usa alguna de esas 33 rutinas IDA no
es capaz de decir su nombre.

Para resolverlo se usa primero el programa
AR2IDT euser.dll
para producir euser.idt
y luego
ZIPIDS euser.idt
para generar correctamente euser.ids

De todos modos, las instrucciones estan en el fichero readme.txt

Obviamente el fichero de texto euser.idt se puede alterar si no te gustan
los nombres de las funciones. En particular yo uso los nombres "de-mangleados"
que obtengo de los ficheros del SDK.
Sin extenderme mucho: el programa
dumpbin /ALL cone.lib
produce
..........
Symbol name : DaysInMonth__C5TTime
(public: static int TTime::DaysInMonth(void))
Ordinal : 302
................

Un simple script en Perl permite incluir el nombre real en euser.ids para
hacerlo mas entendible.

Esto se puede usar en cualquier programa. Por ejemplo si quieres desensamblar
un programa en Linux; aunque IDA conoce los nombres de algunas librerias, hay
demasiadas como para que la incluya todas, por lo que es posible generarlas
a medida que las necesites.

---------------
Hay una tecnica bastante usada en programas de Windows que hace mas dificil
la vida del "analista de programas". En vez de llamar a una rutina externa,
la incluyen dentro del propio programa.
Este proceso se llama in-lining.

Por ejemplo la rutina strlen() es bastante simple. Para invocarla hay que
incluir su libreria, usar una referencia al indice, definir un punto de
entrada, e invocar a esta rutina.
Se ahorra algo de tiempo (e incluso de espacio) si incluyes su codigo
directamente en tu programa.
Probablemente se puede hacer en menos de 10 bytes.
Pero claro, el que desensambla el programa se encuentra con una rutina que
complica (aunque solo ligeramente) la interpretacion del desensamblado.

IDA es capaz de encontrar tales rutinas. Para ello usa algo llamado
firma (signature), que no es mas que los primeros bytes de la rutina original.
Veamos el fichero flair/startup/pe_vc6.pat
que contiene las firmas generadas por el compilador VisualC 6.0
Una de las lineas dice:

558BEC6AFF68..... __except_handler

lo cual significa que la rutina __except_handler comienza por estos bytes.
Cuando IDA encuentra estos bytes, sugiere renombrar la rutina encontrada
como "__except_handler_XXX"

Para automatizar el proceso se usa
plb
que a partir de una libreria, genera un fichero de muestras PAT
con este, genera un fichero de firmas SIG

---------------
Un segundo uso es agrupar secuencias de bytes.
Suponer que te das cuenta de que a menudo aparecen las instrucciones
LDR R0, [R3]
ADD R0, #1
STR R0, [R3]

que en ARM se escribe como

00 00 93 E5
01 00 80 E2
00 00 83 E5

Esta instruccion es lo mismo que la pseudo-instruccion

INC [R3]

Asi que creas un PAT con la linea

000093E5010080E2000083E5.... INC [R3]

?ves por donde van los tiros? Se puede hacer el programa mas breve, con lo
cual es mas facil de entender.
Esto es una tecnica usada en re-compiladores que generan codigo C a partir
del listado en ensamblador.
Para ver un desarrollo de esta idea recomiendo el excelente programa
http://desquirr.sourceforge.net
que incluye una explicacion bastante detallada.

Por supuesto, el documento de obligada lectura es el decompilador DCC de
Cristina Cifuentes y su tesis doctoral, en
http://www.it.uq.edu.au

En general esto de las muestras y las firmas es util cuando quieres sacarle
mas partido a IDA y conoces el compilador usado para construir el
programa que pretendes desensamblar.

---------------
Otra de las incomodidades de programas desensamblados es el uso de
sentencias "switch"/"case".
En lenguaje C es comun usar algo asi:
switch(origen)
{
case 0: destino=8; break;
case 1: destino=88; break;
case 2: destino=888; break;
case 3: destino=8888; break;
}

El compilador traduce este codigo en:
LDR R0, [origen] // lee el valor origen
LDR R1, =tabla // crea una tabla de 4 posibilidades
MOV R0, R0*4 // cada valor de la tabla contiene un puntero a
ADD R0, R1 // una subrutina.
BX [R0] // lee puntero[origen] , y salta a donde le dicen
tabla:
DCD rutina0 // puntero a la subrutina de "case 0"
DCD rutina1
DCD rutina2
DCD rutina3

rutina0: // autentica rutina que ejecuta "case 0"
MOV R0, 8
B break
rutina1:
MOV R0, 88
B break
rutina2:
MOV R0, 888
B break
rutina3:
MOV R0, 8888
B break

break:
STR R0, [destino]

Visto de otro modo: hace
jmp rutina[origen*4]

Desde el punto de vista del compilador esto tiene perfecto sentido. Lo malo
es que IDA lo unico que ve es una tabla con valores, y cree que estamos
en un bloque de datos.
Si es capaz de interpretar correctamente el codigo rutina0, ... rutina3 ,
pero desconoce desde donde se referencian.
Le podemos ayudar haciendo un script que a partir de una direccion
busque si hay una tabla de saltos en las siguientes 8 instrucciones:

for (pos = inicio; pos < inicio+8*4; pos = pos + 4) // instruccion de 4 bytes
{
ins=GetManualInsn(pos);
if(strstr(ins,"DCD")) // hay un puntero?
{ // veamos si apunta a una rutina
// la instruccion es DCD xxxxxx
valor_apuntado = Dword(substr(ins,5,-1)); // extraer xxxxxx
if( val>pos && val<(pos+80)) // si salta a alguna rutina cercana ...
{
result=MakeCode(valor_apuntado); // miro si es un trozo de codigo
if(result>0) // si se ha podido convertir en codigo ...
{
AddCodeXref(pos,val,fl_F); // entonces marco la referencia
MakeName( pos, "salto_a_" + ltoa(val,16) ) ; // y le doy un nombre bonito
MakeComm ( pos, "salto " + ltoa(val,16) ); // y un comentario
}
}
}
}


Por otro lado tambien podria usar la tecnica de las firmas. El bloque

LDR R0, [origen]
LDR R1, =tabla
MOV R0, R0*4
ADD R0, R1
BX [R0]

es sintoma de que hay una aceso indexado, y aparece siempre relacionado
con una tabla de saltos.
Asi que hago un script que en cada puntero a rutina, lo comenta con:
DCD rutinaX ; case X
que me sirve para saber cual valor saltara a esta rutina.


Bueno, esto ayuda a IDA a entender mejor el listado, pero quizas podria
facilitarme la tarea A MI.
Para que esto sea posible, debo recuperar la estructura "switch"/"case"
inicial. Durante este proceso puedo tengo que transformar el codigo
desensamblado en algo parecido a lenguaje C, pero lo malo es que luego
no podria ensamblarlo de nuevo con un ensamblador: necesitaria un
compilador. El asunto es lo suficientemente complejo para explicarlo
aqui, por lo que os emplazo a un proximo numero de SET.

---------------
Un tema que he investigado poco pero que promete mucho es el de los
reconocedores de archivos.
En vez de investigar un programa a veces necesitas analizar los datos que
ha producido.
Uno de los scripts incluidos en IDA sirve para desmenuzar ficheros de
tipo AIF que contienen los recursos (iconos, strings, menus) que usa una
aplicacion Symbian.
Cuando IDA reconoce que el fichero es de este tipo, procesa toda la
estructura y la presenta de manera comprensible.
La manera de programar nuevos reconocedores es en lenguaje C leyendo byte
por byte en fichero, y definiendo estructuras conteniendo esos bytes.
En general el tema depende del tipo de fichero y debo decir que a mi me
resulta mas facil hacerme un programa en C sin usar IDA en absoluto.

Quizas en un proximo articulo, aunque me parece que es un tema que
interesara a pocos de vosotros (asumiendo que lo que yo cuento interesa
a alguien, que a veces lo dudo)

Otros temas demasiados avanzados para contar en esta introduccion son:
-modulos, para definir otros procesadores y las instrucciones que usan.
Aunque IDA soporta 35 familias de procesadores, quien sabe lo que los
usuarios necesitaran en el futuro.
-plugins, para compilar funcionalidad que le falta a IDA, por ejemplo para
relacionarlo con un debugger o un emulador y analizar el codigo sobre la
marcha.
-wingraph, que es la aplicacion usada para mostrar las referencias entre
rutinas en forma de arbol. Tiene algunos defectos que me gustaria
modificar, por ejemplo poder colapsar algunas ramas inutiles. El fuente
esta disponible y el formato usado (gdl-VCG) es facil de entender.
-sustitucion de grupos de instrucciones mediante macros
-regeneracion de codigo para posterior recompilacion

Lo que debe quedar claro es que el programa puede parecer caro, pero es
porque lo vale.

Para aprender tecnicas de desensamblado pueden ser utiles los libros de
la editorial "No Starch Press", en particular:
Hacking: The Art of Exploitation
Hacker Disassembling Uncovered, by Kris Kaspersky

Reversing: Secrets of Reverse Engineering, by Eldad Eilam
Reverse Compilation Techniques, by Cristina Cifuentes

Por ultimo me gustaria incluir un comentario que he leido en uno de estos
libros de desensamblado:
"It will become more understandable after studying the disassembled listing."

Y mi propio comentario: me extranyaria mucho. En general el codigo
desensamblado es el ultimo recurso yo que suelo usar.

*EOF*

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT