glTest1: Problema con la convención de llamada

NIVEL: Beginner
Test: WinXP SP3

Link original:
http://foro.elhacker.net/programacion_cc/gltest1_problema_con_la_convencion_de_llamada-t384879.0.html

Hola de nuevo,
Bueno voy a contar una experiencia que le puede servir de ayuda si les pasa algo parecido.
Cuando estaba armando el proyecto para este tema:
http://foro.elhacker.net/programacion_cc/gltest_1_interceptar_opengl32_con_gpa-t384878.0.html

Antes de publicarlo, había tenido un problema con la convención de llamada de una función. En realidad se trata de un error mio en el código, pero que había pasado desapercibido y cuando el programa se cerraba no entendía por qué, lo que me llevó a tener que depurarlo.

Primero muestro la imagen del error en el código:

Opengl32 es __stdcall y al no especificarlo en el puntero, la llamada iba a ser __cdecl, que es la que usa el compilador VC++ por defecto. (aunque hay una opción para cambiar esto).

Antes es importante entender las características de las dos convenciones de llamada involucradas.

__cdecl
http://msdn.microsoft.com/en-us/library/zkwh89ks(v=vs.80).aspx

__stdcall
http://msdn.microsoft.com/en-us/library/zxk0tw93(v=vs.71).aspx

Esto da más información
http://en.wikipedia.org/wiki/X86_calling_conventions
http://www.codeproject.com/Articles/1388/Calling-Conventions-Demystified

Y esto tiene que ver directamente con la pila, por lo que si no saben acerca de ella, es una estructura con un ordenamiento LIFO y es usada por el programa para guardar direcciones. Las direcciones son de parámetros, de retorno, y de variables locales. Los registros del procesador relacionados a la pila son ESP y EBP (Apuntadores).
No menciono SS que es de segmento, sólo ESP y EBP.
EBP es el registro que se usa para referenciar la base de la pila,
ESP apunta siempre al tope de la pila. El tope de la pila es la última posición,  no la que le sigue..
ESP se modifica automáticamente para agrandar o achicar la pila (Es el puntero de pila).  EBP cambia según el ‘stack frame’ actual, pero siempre se guarda una copia del frame anterior. No cambia dentro del frame como lo puede hacer ESP, ya que es necesario para referenciar la base de la pila. Pero el programador si puede alterar EBP, y en esta demostración se
trata de eso XD.

Esta es la representación de una pila (relativa a EBP , no a ESP)

HIGH MEMORY (crece de arriba hacia abajo)
EBP+18h   Quinto Parámetro
EBP+14h   Cuarto Parámetro
EBP+10h   Tercer Parámetro
EBP+0Ch   Segundo parámetro
EBP+08h   Primer Parámetro
EBP+04h   Direccion de retorno
EBP+0     Valor original de EBP
EBP-4h    Primera variable local
EBP-8h    Segunda variable local  <—————————–> ESP apunta a la última
LOW MEMORY

En el caso de la función glBegin que tiene un parámetro sólo, entonces nuestra pila inicial cuando se llama a nuestro Hook de glBegin es así:

HIGH MEMORY (crece de arriba hacia abajo)
EBP+08h   Primer Parámetro       //cmp dword ptr [ebp+0x8], 0x00000009 //mode
EBP+04h   Direccion de retorno   // return address (0x401318 (EXE))
EBP+0     Valor original de EBP // agregado
LOW MEMORY

El tema es que dentro de la función de hook de glBegin el compilador agregó código que modifica el EBP. Este es el código insertado:

push ebp
mov ebp, esp

de esa forma cambia la pila, porque se agregó un elemento más (EBP), y cambia EBP mismo.

Supongamos que ESP se desplaza 4 posiciones,
eso significa que si el primer parámetro es EBP+0x8, desde la perspectiva de EBP, es ESP+0x14 desde la perspectiva de ESP.

Un comentario, ESP cambia cuando se usan las instrucciones PUSH y POP

*–ESP = value;   // push
value = *ESP++;   // pop

y también cuando vemos:

ADD ESP, X

o

SUB ESP, X

, que se usan para agrandar y achicar la pila.

Este es el desensamblado del Hook a glBegin

Con respecto a la 2da imagen, vamos a hacer una explicación paso por paso:

1: se guarda el registro EBP anterior (porque se ha modificado)
2: se ejecuta el código intermedio de la función normalmente.
3: se restaura el EBP
4: se limpia el stack por el valor de lo que había demás en la pila hasta el momento,
Ver en la imagen, se calcula 0x10 lo cual es (4*4bytes), osea los 4 PUSHs del
principio. La pila ahora queda equilibrada con sus datos originales intactos. la dirección de retorno y el primer parámetro (tiene uno sólo glBegin).
5: Se utiliza un salto incondicional para ir hacia la función original.
(No se usa CALL).

Lo de calcular cuanto se debe limpiar se hace obteniendo la información del depurador (OllyDBG) con el programa en ejecución, y obviamente traceando cada línea, y observando la pila. Yo en realidad lo hice con OllyDBG porque no sabía del error en el código (no me había dado cuenta), pero para la demostración me resulta más fácil observar el desensamblado directamente con este explorador PE, ahora que se lo que pasaba XD.

En conclusión, el arreglo se hacía con inline ASM en la función de reemplazo (Hook) de glBegin

int glBegin_saved_ebp;

///////////////////////////////////////////////////////////////////////////////////////////////////////////

void __stdcall HOOK_glBegin(GLenum mode)
{
//--------------------------------------
__asm mov eax, [esp+0xc] // calculado en esta posición
__asm mov glBegin_saved_ebp, eax // guardamos el ebp anterior
//--------------------------------------

if(!once){

printf("\n");
printf("DLL -> HOOK_glBegin: ebp guardado!\n");
system("pause");
once=true;
}

if (mode==GL_POLYGON)
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glColor3f(0, 0, 0);
}

//--------------------------------------
__asm mov ebp,glBegin_saved_ebp // restauramos el ebp
__asm add esp,0x10 // limpiamos stack (calculado como 4 * 4 bytes = 0x10)
__asm jmp pOrig_glBegin // saltamos a la original
//--------------------------------------
//(*pOrig_glBegin)(mode);
}

Otra información que es importante conocer:
Prólogo y Epílogo
http://msdn.microsoft.com/es-ar/library/tawsa7cb(v=vs.80).aspx
http://msdn.microsoft.com/es-es/library/8ydc79k6(v=vs.80).aspx
http://msdn.microsoft.com/es-es/library/t2wt9aez(v=vs.110).aspx
http://msdn.microsoft.com/es-es/library/21d5kd3a.aspx

Más información
http://en.wikipedia.org/wiki/Call_stack
http://es.wikipedia.org/wiki/Pila_de_llamadas
http://securityetalii.es/tag/marco-de-pila/
http://learnassembler.com/procedim.html
http://msdn.microsoft.com/es-es/library/5sds75we(v=vs.90).aspx
http://www.pmzone.org/chapter06.html

“ENTER” y “LEAVE” para crear y liberar el stack frame.
http://en.wikipedia.org/wiki/X86_instruction_listings#Added_with_80186.2F80188

Así que todo resultó ser un problema del código, que faltaba __stdcall, no era necesario ningún arreglo con ASM XD.
Pero espero que haya resultado instructivo al menos.

Saludos

CODE VC++6
http://www.mediafire.com/?xzxvrc3ri3toz9t

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: