-[ 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 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 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 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 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=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 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; iIDC 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*