Test: WinXP SP3
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)
Citar
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
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í:
Citar
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: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
Código:
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
Citar
*--ESP = value; // push
value = *ESP++; // pop
value = *ESP++; // pop
y también cuando vemos:
Citar
ADD ESP, X
oCitar
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
Código
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