NIVEL: Beginner
Test: WinXP SP3
Esto es una demostración de como puede aplicarse un IAT hook para interceptar funciones en un proceso. En este caso , se trata de un ejecutable que utiliza funciones de Opengl32 para crear una ventana y representar una imagen.
Para hacer uso de las funciones Opengl32 desde un ejecutable .EXE como en este caso, se puede hacer mediante vinculación dinámica (con DLL) o vinculación estática (con LIB).
Antes dejo unos enlaces donde consultar información:
http://msdn.microsoft.com/es-ar/library/253b8k2c.aspx
http://msdn.microsoft.com/es-es/library/1ez7dh12(v=vs.80).aspx
http://msdn.microsoft.com/es-ar/library/9se914de.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms681914(v=vs.85).aspx
http://msdn.microsoft.com/es-es/library/ms235636(v=vs.80).aspx
http://msdn.microsoft.com/es-es/library/9yd93633(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/ms681938(v=vs.85).aspx
http://msdn.microsoft.com/es-es/library/ms235627(v=vs.90).aspx
http://msdn.microsoft.com/es-es/library/ms235627(v=vs.80).aspx
http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/bed8a074-afe0-46a8-bdc5-5e4244ca55a6
http://msdn.microsoft.com/en-us/library/ms235627(v=vs.80).aspx
http://msdn.microsoft.com/es-es/library/abx4dbyh.aspx
http://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.80).aspx
Para construir este ejecutable de ejemplo, usé un código de ejemplo que ofrece el sitio oficial de Opengl..
http://www.opengl.org/archives/resources/code/samples/glut_examples/advanced/textrim.c
http://www.opengl.org/resources/libraries/glut/
http://www.opengl.org/archives/resources/code/samples/simple/
http://www.opengl.org/archives/resources/code/samples/glut_examples/examples/examples.html
http://www.opengl.org/archives/resources/code/samples/glut_examples/contrib/contrib.html
El programa de ejemplo original usa la vinculación estática, pero para poder realizar esta demostración tuvo que ser modificado XD.
Las modificaciones se dan para las llamadas a 3 funciones de Opengl32 que fueron elegidas para poder realizar la demostración.
las funciones son: glBegin, glBlendFunc y glViewport
Para saber acerca de estas funciones van a tener que recurrir a un manual de Opengl32, o busquen en el sitio oficial.
A estas 3 funciones se las desea interceptar para lograr cambiar el comportamiento del programa, la idea es cambiar lo que el programa muestra por pantalla.
Lo que se hace en el ejecutable es arreglar las llamadas a estas 3 funciones, que normalmente son llamadas por vinculación estática. En cambio ahora fueron modificadas para que la vinculación sea dinámica. Por eso se declararon punteros a función y se resuelven las direcciones correspondientes a las funciones dentro de Opengl32.DLL , en tiempo de ejecución. Esto se hace usando GetProcAddress.
Ahora que sabemos que el ejecutable de la demostración, hace uso de GetProcAddress para conseguir las direcciones de estas 3 funciones mencionadas, vamos a interceptar GetProcAddress para que devuelva otras direcciones. Digamos, las direcciones de nuestras funciones de reemplazo.
Esas funciones de reemplazo, o HOOKs, van a estar dentro de una DLL aparte que va a ser cargada al proceso. Una vez cargada en el proceso va a parchear la IAT en la entrada de GetProcAddress, de esa forma GetProcAddress queda interceptada.
Veamos el código del EXE:
Los includes necesarios, y las librerías estáticas requeridas para que compile.
Código
#pragma comment (lib, "opengl32.lib") #pragma comment (lib, "GLUT32/glut32.lib") #include <windows.h> #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include "GLUT32/glut.h" #include <gl\gl.h> #include <gl\glu.h> #include "texture.h"
Los punteros a función para las 3 funciones que vamos a interceptar.
Código
// Opengl32 es APIENTRY typedef void(__stdcall* t_glBegin)(GLenum); typedef void(__stdcall* t_glBlendFunc)(GLenum, GLenum); typedef void(__stdcall* t_glViewport)(GLint,GLint,GLsizei,GLsizei); // Declaramos unos punteros que van a contener la dirección de las funciones OGL32 que usemos. t_glBegin pOrig_glBegin = NULL; t_glBlendFunc pOrig_glBlendFunc = NULL; t_glViewport pOrig_glViewport = NULL;
Punto de entrada. Como podemos ver, la DLL se carga con un simple LoadLibrary. No hacemos inyección de DLL o cualquier otra forma de cargar una DLL. "glhack1.dll" es la DLL la cual se encarga del patch a la IAT y contiene los hooks de GPA y Opengl32.
Código
int main(int argc, char **argv) { SetConsoleTitle("glTest"); int opc=0; while( 1){ system("cls"); printf("Bienvenido!\n"); printf("1: Interceptar OPENGL32\n"); printf("2: NO Interceptar OPENGL32\n"); printf("3: Salir\n"); scanf("%d",&opc); if(opc == 1||opc == 2||opc == 3) break; } if(opc==3) ExitProcess(45); if(opc==1) //Al cargar la DLL, esta instala el hook a GPA para poder interceptar OGL32 { if(!LoadLibrary("glhack1.dll")){ MessageBox(0,0,0,0); ExitProcess(0); } } printf("\n"); printf("EXE: GPA 0x%X\n", GetProcAddress); printf("EXE: glBegin 0x%X\n", glBegin); if(GetModuleHandle("opengl32.dll")) { pOrig_glBegin = (t_glBegin)GetProcAddress(GetModuleHandle("opengl32.dll"), "glBegin"); pOrig_glBlendFunc = (t_glBlendFunc)GetProcAddress(GetModuleHandle("opengl32.dll"), "glBlendFunc"); pOrig_glViewport = (t_glViewport)GetProcAddress(GetModuleHandle("opengl32.dll"), "glViewport"); printf("EXE: glBegin 0x%X\n", pOrig_glBegin); printf("EXE: glBlendFunc 0x%X\n", pOrig_glBlendFunc); printf("EXE: glViewport 0x%X\n", pOrig_glViewport); } else { MessageBox(0,0,0,0); ExitProcess(0); return 0; } glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE); glutInitWindowSize(450, 450); glutCreateWindow("glTest"); // glutFullScreen(); init(argv[1]); glutDisplayFunc(display); glutKeyboardFunc(key); glutReshapeFunc(reshape); glutIdleFunc(tick); glutMainLoop(); return 0; /* ANSI C requires main to return int. */ } //
Estas funciones son originales del código que conseguí en opengl.org
Los cambios que se ven son que las llamadas a función originales para las 3 funciones elegidas,
fueron cambiadas por los punteros a función que creamos.
Código
void bfunc(void) { static int state; if (state ^= 1) { pOrig_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); } else { glDisable(GL_BLEND); } }
Código
void bfunc(void) { static int state; if (state ^= 1) { pOrig_glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); } else { glDisable(GL_BLEND); } }
Código
void display(void) { glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); glTranslatef(transx, transy, 0.f); glRotatef(rotx, 0., 1., 0.); glRotatef(roty, 1., 0., 0.); glScalef(scale, scale, 0.); pOrig_glBegin(GL_POLYGON); glTexCoord2f(0.0, 0.0); glVertex2f(-1.0, -1.0); glTexCoord2f(1.0, 0.0); glVertex2f(1.0, -1.0); glTexCoord2f(1.0, 1.0); glVertex2f(1.0, 1.0); glTexCoord2f(0.0, 1.0); glVertex2f(-1.0, 1.0); glEnd(); glPopMatrix(); glutSwapBuffers(); }
Código
void reshape(int w, int h) { pOrig_glViewport(-50, -50, w+120, h+120); }
La DLL hace un patch a la IAT del EXE, el tema es que esta función la traje de un conocido creador de hacks que se llama h1web.
http://50hz.ws/devel/iathook.c.txt
El tema es que tuve que modificar la función para que funcione correctamente. Los cambios fueron en el 4to parámetro y dentro de la función. El problema era que estas estructuras que recorre, contienen cadenas sin terminación en 0, por lo que STRCMP no encontraba en nombre de GetProcAddress.
Código
BOOL HookIAT(char* szModule, char* szFunc, DWORD dwOwn, DWORD& dwOrg) { DWORD dwBase = (DWORD)GetModuleHandle(NULL); PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)dwBase; PIMAGE_NT_HEADERS pNTHdr = (PIMAGE_NT_HEADERS)(dwBase + pDosHdr->e_lfanew); DWORD ImportData = (DWORD)pNTHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; PIMAGE_IMPORT_DESCRIPTOR pImportD = (PIMAGE_IMPORT_DESCRIPTOR)(dwBase + ImportData); while(pImportD->Name != 0) { // CUIDADO! En estas estructuras se encuentran strings no terminadas en 0 // por lo que STRCMP no sirve. // Un cambio fácil puede ser usar STRSTR que encuentra una cadena dentro de otra. // if(!strcmp((char*)(dwBase + pImportD->Name), szModule)) if(!strstr((char*)(dwBase + pImportD->Name), szModule)) break; pImportD++; } //printf("pImportD->Name: %s\n",(char*)(dwBase + pImportD->Name)); //system("pause"); if(pImportD->Name == 0) return FALSE; //printf("pImportD->Name: 0x%X\n",pImportD->Name); //system("pause"); PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)(dwBase + pImportD->FirstThunk); DWORD OrgFunc = (DWORD)GetProcAddress(GetModuleHandle(szModule), szFunc); while(pThunk->u1.Function != 0) { if(pThunk->u1.Function == (DWORD*)OrgFunc) { dwOrg = OrgFunc; DWORD dwOldProt = 0; VirtualProtect((void*)&pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProt); pThunk->u1.Function = (DWORD*)dwOwn; VirtualProtect((void*)&pThunk->u1.Function, 4, dwOldProt, &dwOldProt); if(pThunk->u1.Function == (DWORD*)dwOwn) return TRUE; else return FALSE; } pThunk++; } return FALSE; }
DLL: main.cpp
Código
// // By 85 // HookIAT (h1web, Thanks to Ashkbiz Danehkar) // elhacker.net // etalking.com.ar // 2013 // /////////////////////////////////////////////////////////////////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN #include<windows.h> #include<stdio.h> #include<stdlib.h> #include"opengl.h" /////////////////////////////////////////////////////////////////////////////////////////////////////////// FARPROC (WINAPI* pGetProcAddress) ( HMODULE hModule, LPCSTR lpProcName ); // FARPROC WINAPI newGetProcAddress(HMODULE hModule, LPCSTR lpProcName) { FARPROC nResult; nResult=GetProcAddress(hModule, lpProcName); if (HIWORD(lpProcName)) { if (!lstrcmp(lpProcName, "GetProcAddress")) { return (FARPROC) &newGetProcAddress; } else { CheckForOpenGlHook(&nResult, lpProcName); } } return nResult; } // BOOL HookIAT(char* szModule, char* szFunc, DWORD dwOwn, DWORD& dwOrg) { // Ya mostrado } // bool WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { if (fdwReason==DLL_PROCESS_ATTACH) { //printf("GetProcAddress 0x%X\n", GetProcAddress);// No refenciar GPA antes! if(!HookIAT("kernel32.dll","GetProcAddress",(DWORD)newGetProcAddress, (DWORD&)pGetProcAddress)){ MessageBox(0,0,0,0); return (false); } printf("\n"); printf("DLL: newGetProcAddress 0x%X\n", newGetProcAddress); printf("DLL: GetProcAddress 0x%X\n", GetProcAddress); printf("DLL: pGetProcAddress 0x%X\n", pGetProcAddress); } return (true); }
Los Hooks o funciones de gancho, como quieran decirles, son estas siguientes. Vemos que dentro de las de Opengl32 se hicieron cambios para modificar el resultado del programa.
El Hook de GetProcAddress se encarga de cambiar la dirección de retorno por una de los Hooks de Opengl32, y de inicializar los punteros a función con las direcciones de las originales.
DLL: opengl32.cpp
Código
// // By 85 // elhacker.net // etalking.com.ar // 2013 // /////////////////////////////////////////////////////////////////////////////////////////////////////////// #pragma comment(lib, "opengl32.lib") #include<windows.h> #include<stdio.h> #include "opengl.h" /////////////////////////////////////////////////////////////////////////////////////////////////////////// // Opengl32 es APIENTRY typedef void(__stdcall* t_glBegin)(GLenum); typedef void(__stdcall* t_glBlendFunc)(GLenum, GLenum); typedef void(__stdcall* t_glViewport)(GLint,GLint,GLsizei,GLsizei); t_glBegin pOrig_glBegin = NULL; t_glBlendFunc pOrig_glBlendFunc = NULL; t_glViewport pOrig_glViewport = NULL; bool once=false; bool oglSubtractive = false; bool NewDimension = true; int posx = 200; int posy = 200; int neww = 50; int newh = 50; // void __stdcall HOOK_glBegin(GLenum mode) { if(!once){ once=true; } if (mode==GL_POLYGON) { glClearColor(1.0, 1.0, 1.0, 1.0); glColor3f(0, 0, 0); } (*pOrig_glBegin)(mode); } void __stdcall HOOK_glBlendFunc(GLenum sfactor, GLenum dfactor) { if(oglSubtractive){ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); } else { glBlendFunc(sfactor,dfactor); } } void __stdcall HOOK_glViewport( GLint x,GLint y,GLsizei width,GLsizei height ) { if(NewDimension){ (*pOrig_glViewport)(posx,posy,neww,newh); } else { (*pOrig_glViewport)(x,y,width,height); } } void CheckForOpenGlHook(FARPROC* pProc,LPCTSTR lpProcName) { if (!strcmp(lpProcName,"glBegin")) { pOrig_glBegin = (t_glBegin)*pProc; *pProc = (FARPROC)&HOOK_glBegin; } else if(!strcmp(lpProcName,"glBlendFunc")) { pOrig_glBlendFunc = (t_glBlendFunc)*pProc; *pProc = (FARPROC)&HOOK_glBlendFunc; } else if(!strcmp(lpProcName,"glViewport")) { pOrig_glViewport = (t_glViewport)*pProc; *pProc = (FARPROC)&HOOK_glViewport; } }
Espero que les haya gustado. Es un tutorial básico por lo tanto sólamente usé un parche a la IAT. Se puede experimentar con otro tipo de Hook más avanzado.
El programa ofrece la opción de interceptar Opengl32 o no:
Archivos utilizados:
Resultados:
Si me preguntan por qué hacer que usen GetProcAddress cuando no era necesario, la respuesta es que algunos juegos lo han hecho así, y servía para demostrar la utilidad de interceptar GPA.
Interceptar Opengl32 se podía haber hecho parcheando la IAT en las entradas de Opengl32 también.
Proyecto vc6:
http://www.mediafire.com/?p673twh7in70ys1