elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.

 

 


Tema destacado: Trabajando con las ramas de git (tercera parte)


+  Foro de elhacker.net
|-+  Programación
| |-+  Programación C/C++ (Moderadores: Eternal Idol, Littlehorse, K-YreX)
| | |-+  glTest1: Problema con la convención de llamada
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: glTest1: Problema con la convención de llamada  (Leído 2,456 veces)
85

Desconectado Desconectado

Mensajes: 206



Ver Perfil WWW
glTest1: Problema con la convención de llamada
« en: 7 Marzo 2013, 03:31 am »

NIVEL: Beginner
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

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:

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

y también cuando vemos:
Citar
ADD ESP, X
o
Citar
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
  1.  
  2. int glBegin_saved_ebp;
  3.  
  4. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  5.  
  6. void __stdcall HOOK_glBegin(GLenum mode)
  7. {
  8. //--------------------------------------
  9. __asm mov eax, [esp+0xc] // calculado en esta posición
  10. __asm mov glBegin_saved_ebp, eax // guardamos el ebp anterior
  11. //--------------------------------------
  12.  
  13. if(!once){
  14.  
  15. printf("\n");
  16. printf("DLL -> HOOK_glBegin: ebp guardado!\n");
  17. system("pause");
  18. once=true;
  19. }
  20.  
  21. if (mode==GL_POLYGON)
  22. {
  23.    glClearColor(1.0, 1.0, 1.0, 1.0);
  24.    glColor3f(0, 0, 0);
  25. }
  26.  
  27. //--------------------------------------
  28. __asm mov ebp,glBegin_saved_ebp // restauramos el ebp
  29.        __asm add esp,0x10 // limpiamos stack (calculado como 4 * 4 bytes = 0x10)
  30.        __asm jmp pOrig_glBegin // saltamos a la original
  31. //--------------------------------------
  32. //(*pOrig_glBegin)(mode);
  33. }
  34.  

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


« Última modificación: 8 Marzo 2013, 01:36 am por 85 » En línea

Me cerraron el Windows Live Spaces, entonces me creé un WordPress XD
http://etkboyscout.wordpress.com/
85

Desconectado Desconectado

Mensajes: 206



Ver Perfil WWW
Re: glTest1: Problema con la convención de llamada
« Respuesta #1 en: 7 Marzo 2013, 23:22 pm »

Voy a poner algo más a continuación para que no se haga tan largo el primer posteo XD, acerca del arreglo (FIX) con ensamblador en línea, yo había dicho que no se podía hacer con CALL, en realidad quise decir que no se podía hacer DIRECTAMENTE así como estaba la pila en esa situación.
Pero si arreglamos la pila si es posible de hecho lo acabo de hacer, y me pareció muy instructivo mostrárselos , veamos

Otra cosa, para hacer algo tan sencillo como esto quizás no haga falta tener que utilizar el depurador (OllyDBG por ejemplo), quiero decir, mucha gente que conozco nunca duda en ir directamente al OllyDBG si se trata de depurar algo, pero se que muchos programadores no recurren a este tipo de depuración DINÁMICA por así decirlo, sino más bien se quedan con una depuración NO DINÁMICA por así decirlo también, que se trata de solamente hacer que el programa se detenga y arroje resultados por pantalla, por archivo de texto o por MessageBox.
Para que se detenga el programa (lo que equivale a BreakPoint en el depurador), se puede usar un simple 'system("pause")', o un MessageBox que interrumpe el programa.

Volviendo al tema, sin necesidad de OllyDBG en esta ocasión, arreglé el code para que funcione con CALL. Este es el código que fue depurado con logs de información:

Código
  1. // DEPURAMIENTO POR MEDIO DE LOGs
  2. int test1,test2,test3,test4,test5;
  3. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  4.  
  5. void __stdcall HOOK_glBegin(GLenum mode)
  6. {
  7. __asm mov eax, [esp+0x0] // calculado en esta posición
  8. __asm mov glBegin_saved_ebp, eax // guardamos el ebp anterior
  9. //////////////////////////////////////////////////////////////////
  10.  
  11. if(!once){
  12.  
  13. printf("\n");
  14. printf("DLL -> HOOK_glBegin: ebp guardado!\n");
  15. system("pause");
  16. once=true;
  17. }
  18.  
  19. if (mode==GL_POLYGON)
  20. {
  21.    glClearColor(1.0, 1.0, 1.0, 1.0);
  22.    glColor3f(0, 0, 0);
  23. }
  24.  
  25. //HIGH MEMORY (crece de arriba hacia abajo)
  26. //EBP+08h   Primer Parámetro       //cmp dword ptr [ebp+0x8], 0x00000009 //mode
  27. //EBP+04h   Direccion de retorno   // return address (0x401318 (EXE))
  28. //EBP+0     Valor original de EBP // agregado
  29. //LOW MEMORY
  30.  
  31. __asm mov eax, [ebp+0x4]
  32. __asm mov test1, eax
  33. __asm mov eax, [ebp+0x8]
  34. __asm mov test2, eax
  35.  
  36. // printf("ret: 0x%X  arg: 0x%X\n",test1, test2);
  37. // system("pause");
  38.  
  39. //__asm mov eax, ebp
  40. //__asm mov test5, eax
  41. //printf("test5: 0x%X  ebp: 0x%X\n",test5, glBegin_saved_ebp);
  42. //system("pause");
  43.  
  44. //__asm add esp, 0x14 //0x10= deja esp en ret
  45. __asm add esp, 0xc //deja esp sin los 3 valores iniciales (lista para recargarla)
  46.  
  47.    //__asm mov eax, [esp+0x0]
  48. //__asm mov test5, eax
  49. //printf("test5: 0x%X\n",test5);
  50. //system("pause");
  51.  
  52. __asm mov ebp, glBegin_saved_ebp
  53. __asm push test2
  54. __asm call pOrig_glBegin
  55. // __asm add esp, 0x4// llamada a glBegin (__stdcall), se encarga de limpiar la stack
  56.  
  57. //__asm mov eax, [ebp+0x4]
  58. //__asm mov test3, eax
  59. //__asm mov eax, [ebp+0x8]
  60. //__asm mov test4, eax
  61. //printf("ret: 0x%X  arg: 0x%X\n",test3, test4);
  62. //system("pause");
  63.  
  64. //__asm mov eax, ebp
  65. //__asm mov test5, eax
  66. //printf("test5: 0x%X  ebp: 0x%X\n",test5, glBegin_saved_ebp);
  67. //system("pause");
  68.  
  69.  
  70. //__asm mov ebp, glBegin_saved_ebp
  71. __asm push test1
  72. //__asm ret 0x4
  73. __asm ret
  74.  
  75. //__asm add esp, 0x4
  76. //__asm jmp test1
  77. //(*pOrig_glBegin)(mode);
  78. }
  79.  

En realidad no estoy diciendo que no se use OllyDBG, estoy diciendo que para una pabada como esta no hace falta, depende de lo que haga falta.
Por ejemplo, para este caso necesitamos saber los valores de ESP y EBP, y
de algún registro que usemos para guardar datos, por ejemplo EAX.
Aparte siempre tengo un explorador PE o un desensamblador a mano.

Como ya había dicho, la situación de la pila era algo así:
Código:
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 (push ebp)
LOW MEMORY

con lo cual para llamar a CALL se debe pasar el parámetro de la función,  y CALL pone la dirección de retorno en la pila y al ejecutarse la función, se hace el ret limpiando dicha dirección. Como se trata de glBegin que es __stdcall, la propia función limpia el parámetro que estaba en la pila, por eso en realidad se hace ret 0x4.

El tema es que para llamar a CALL antes debía arreglarse la pila, por lo cual se borraron los 3 elementos iniciales (add esp, 0xC).
Luego se hace el CALL ya mencionado, se restaura el EBP modificado y por último se debe poner la dirección de retorno en la pila y hacer un ret. Fin de la historia.

Código
  1. int glBegin_saved_ebp;
  2. int retaddress, param1;
  3.  
  4. ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  5.  
  6. void __stdcall HOOK_glBegin(GLenum mode)
  7. {
  8. //--------------------------------------
  9. __asm mov eax, [esp+0x0] // calculado en esta posición
  10. __asm mov glBegin_saved_ebp, eax // guardamos el ebp anterior
  11. //--------------------------------------
  12.  
  13. if(!once){
  14.  
  15. printf("\n");
  16. printf("DLL -> HOOK_glBegin: ebp guardado!\n");
  17. system("pause");
  18. once=true;
  19. }
  20.  
  21. if (mode==GL_POLYGON)
  22. {
  23.    glClearColor(1.0, 1.0, 1.0, 1.0);
  24.    glColor3f(0, 0, 0);
  25. }
  26.  
  27. __asm mov eax, [ebp+0x4]
  28. __asm mov retaddress, eax // copia de retaddress
  29. __asm mov eax, [ebp+0x8]
  30. __asm mov param1, eax // copia del parámetro 1
  31. __asm add esp, 0xc //deja stack sin los 3 valores iniciales (lista para recargarla)
  32.  
  33. __asm mov ebp, glBegin_saved_ebp // restaura ebp
  34. __asm push param1 // pasa parámetro 1
  35. __asm call pOrig_glBegin // guarda dirección de retorno en stack y luego se hace pop en el ret
  36. // __asm add esp, 0x4// llamada a glBegin (__stdcall), se encarga de limpiar la stack (ej: ret 0x4)
  37.  
  38. __asm push retaddress // pasa retaddress
  39. __asm ret // vuelve al exe (pop retaddress)
  40. //(*pOrig_glBegin)(mode);
  41. }
  42.  

Si quieren revisar el código nuevo:
http://www.mediafire.com/?bd45oh8qny515b2

Ya le encontré una utilidad a este ARREGLO o FIX con ASM. En otro momento voy a comentar de que se trata.


Hasta Luego




« Última modificación: 7 Marzo 2013, 23:26 pm por 85 » En línea

Me cerraron el Windows Live Spaces, entonces me creé un WordPress XD
http://etkboyscout.wordpress.com/
Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

Mensajes similares
Asunto Iniciado por Respuestas Vistas Último mensaje
Problema pasando parametros a llamada remota :P
Programación Visual Basic
F3B14N 2 1,986 Último mensaje 10 Junio 2010, 20:24 pm
por F3B14N
Convención sobre codificación Batch
Scripting
leogtz 5 4,122 Último mensaje 15 Agosto 2010, 07:15 am
por leogtz
Problema con llamada de funciones (SOLUCIONADO)
Java
dato000 4 4,628 Último mensaje 14 Febrero 2011, 21:05 pm
por dato000
Ayudenme problema con llamada entrante o Ras
Redes
Freedom_for_all 1 2,031 Último mensaje 6 Marzo 2013, 00:13 am
por OmarHack
Problema llamada Skype LInux
GNU/Linux
AlbertoPerez 2 1,811 Último mensaje 11 Diciembre 2015, 20:28 pm
por AlbertoPerez
WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines