OllyTheBugger
Cuenta la leyenda que en algunas ocasiones en las frias mañanas de invierno ( y una mierda, a ver si nieva ya de una vez! ) es posible encuentrarse con un bug de esos que no sabes ni por donde agarrarlo, el caso que os presentamos es real y nos tuvo entretenidos a Victor y a mi un buen rato …
Una de las aplicaciones que usamos en un entorno de producción estaba devolviendo unos valores de retorno inesperados, la aplicacion era muy sencilla y hacÃa uso de otras librerÃas también programadas por nosotros, con lo cual nos pusimos a depurar para ver dónde podÃa encontrarse el problema exactamente y ver cuantos programas podÃan estar afectados por el fallo.
El primer paso fue agregar una int 3 al propio programa, ya que al ser lanzado este desde otro, haciendo uso del JIT podÃamos seguir depurando el programa de manera más cómoda y de la forma más parecida a la real en la que este se cargaba en el entorno de producción.
Â
El desensamblado del programa es el que sigue a continuación:
Â
Â
.text:00401000Â Â Â Â ; int __cdecl main(int argc,const char **argv,const char *envp)
.text:00401000    _main          proc near              ; CODE XREF: start+16Ep
.text:00401000
.text:00401000Â Â Â Â var_11CÂ Â Â Â Â Â Â Â = dword ptr -11Ch
.text:00401000Â Â Â Â var_10CÂ Â Â Â Â Â Â Â = dword ptr -10Ch
.text:00401000    Buffer         = byte ptr -108h
.text:00401000Â Â Â Â var_4Â Â Â Â Â Â Â Â Â Â = dword ptr -4
.text:00401000    argc           = dword ptr 4
.text:00401000    argv           = dword ptr 8
.text:00401000    envp           = dword ptr 0Ch
.text:00401000
.text:00401000 000                sub    esp, 10Ch
.text:00401006 10C                mov    eax, dword_407030
.text:0040100B 10C                mov    [esp+10Ch+var_4], eax
.text:00401012 10C                mov    eax, [esp+10Ch+argc]
.text:00401019 10C                push   ebp
.text:0040101A 110                xor    ebp, ebp
.text:0040101C 110                cmp    eax, 2
.text:0040101F 110                mov    [esp+110h+var_10C], ebp
.text:00401023 110                jnz    loc_40112F
.text:00401029 110                push   ebx
.text:0040102A 114                push   esi
.text:0040102B 118                push   edi
.text:0040102C 11C                int    3              ; <- Nuestra Int 3 🙂
.text:0040102D 11C                call   CualInitGAS   Â
.text:00401032 11C                mov    ebx, eax
.text:00401034 11C                lea    eax, [esp+11Ch+Buffer]
.text:00401038 11C                push   eax            ; lpBuffer
.text:00401039 120                push   104h           ; nBufferLength
.text:0040103E 124                call   ds:GetCurrentDirectoryA
.text:00401044 11C                lea    edi, [esp+11Ch+Buffer]
.text:00401048 11C                dec    edi
.text:00401049 11C                lea    esp, [esp+0]
.text:00401050
.text:00401050Â Â Â Â loc_401050:Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â ; CODE XREF: _main+56j
.text:00401050 11C                mov    al, [edi+1]
.text:00401053 11C                inc    edi
.text:00401054 11C                test   al, al
.text:00401056 11C                jnz    short loc_401050
.text:00401058 11C                mov    ecx, ds:dword_405144
.text:0040105E 11C                mov    dx, ds:word_405148
.text:00401065 11C                lea    eax, [esp+11Ch+Buffer]
.text:00401069 11C                mov    [edi], ecx
.text:0040106B 11C                push   ebx
.text:0040106C 120                push   eax
.text:0040106D 124                mov    [edi+4], dx
.text:00401071 124                call   CualSetTempPath
.text:00401076 124                push   ebp
.text:00401077 128                push   offset a_PluginsS; ".\\PLUGINS\\S.QAL"
.text:0040107C 12C                push   ebx
.text:0040107D 130                call   LoadPlugin
.text:00401082 130                push   ebp
.text:00401083 134                push   offset a_PluginsA ; ".\\PLUGINS\\A.QAL"
.text:00401088 138                push   ebx
.text:00401089 13C                call   LoadPlugin
.text:0040108E 13C <-!!!          mov    ecx, [esp+13Ch+argv]         ; <- Accediendo a argv
.text:00401095 13C                mov    edx, [ecx+4]                 ; <- argv[1] !!!
.text:00401098 13C                push   edx
.text:00401099 140                push   ebx
.text:0040109A 144                call   CualOpenFile                Â
.text:0040109F 144                mov    esi, eax
.text:004010A1 144                add    esp, 28h
Â
Â
Con el Ollydbg empezamos a depurar el programa hasta que llegamos al momento en el que se accedia a argv[1] viendo que se producÃa una excepcion, ECX contenÃa en ese momento el valor 0x2, con lo cual el cálculo de la posición del array de punteros a los parametros se estaba realizando mal, vimos que el dato que en realidad se habÃa obtenido era argc!, ¿como podÃa ser?, en la pila argc y el puntero a argv están contiguos, lo cual querÃa decir que la pila se habÃa movido 4 bytes y se habÃa quedado desajustada.
Lo primero que pensamos es que alguna de las funciones que usabamos no se estaba llamando con la convención correcta, en el código están mezcladas funciones stdcall (todas las del API de Win32) y cdecl (las de nuestra librerÃa), quizá en algun momento estabamos llamando a alguna de la manera que no se debÃa y como resultado la pila se quedaba en ese estado. Una vez comprobamos que no era el caso, pasamos a abrir el programa con el IDA, que harÃamos sin el! :-), si observamos el código,vemos que tras las llamadas a CualSetTempPath y las siguientes a LoadPlugin se acaba arreglando la pila de golpe ( en el add esp,28h ), esto es debido a que el binario estaba compilado con optimización ( /O2 concretamente ), antes de que la pila esté como deberÃa estar es donde ese calcula el puntero, esto nos hizo pensar que quizá podrÃa ser un bug del compilador que estuviera prediciendo mal el desplazamiento que tenÃa que aplicar sobre ESP, en el código observamos que accede a 13Ch + argv lo cual viene a ser 144h, nos pusimos a echar cuentas del estado de la pila en ese momento, despues de un par de minutos discutiendo por que pushes no habÃamos contado nos acordamos de que el IDA tiene una opcion para mostrartelo auto-mágicamente! ( podeÃs activarla en General -> Options -> Stack Pointer ) y se muestra la columna esa que veÃs a la izquierda del código. Cual fue nuestra sorpresa cuando vimos que no, que la posicion a la que accedÃa era la correcta…
¿Qué narices podÃa estar pasando?, between whistles and flutes nos pusimos a depurar también en otro ordenador y aquà fue cuando casi nos da un patatús,,, el programa en el otro ordenador llegaba a ese punto sin problemas!!!, el binario era el mismo exactamente y la instalación parecida si no idéntica, el fallo tenÃa que estar en algo del sistema operativo o en el debugger!, después de mirar un poco más, llegamos a la raiz del asunto … parecÃa que el Ollydbg a la hora de obtener el contexto del proceso con la opción de Just In Time debugging, no se detenÃa en la interrupción, sino que lo hacÃa una instrucción antes, gracias a esto la instrucción previa «push edi» se ejecutaba dos veces, dejando la pila movida los 4 bytes que comentabamos antes y dejandonos a nosotros con el fallo real por localizar :-(.
La versión afectada por este fallo es la 1.09d, por cierto, ¿alguien sabe si se espera la v2 y para cuando? 😉