Test: WinXP SP3
Este tutorial te va a enseñar como crear algo conocido como ‘Wrapper’ de una DLL, cuya traducción es ‘envoltura’ pero se conoce con el término del inglés directamente. Las wrappers son versiones propias de DLL’s conocidas. En este caso vamos a hacer un wrapper de una DLL muy conocida llamada PSAPI.DLL.
Si no la conocen investíguenla XD, pero se trata de una DLL bastante común encontrarla cargada en muchos procesos.
La técnica de construir wrappers tiene al menos 2 objetivos:
1: Hooking. ya que nuestra propia versión de la DLL va a contener nuestras propias versiones de las funciones originales, y al mismo
Tiempo necesitamos llamar a las originales dentro de las nuestras.
El tema es que podemos ejecutar nuestro código antes de que se ejecute el código original.
2: Cargar una DLL. Lo que estamos haciendo al crear nuestra propia versión de una DLL conocida es hacer que al programa víctima se le cargue nuestra DLL en lugar de la original. Esta carga no la hace el programa víctima sino el sistema operativo. El SO detecta nuestra DLL en la misma ubicación que el archivo ejecutable del programa y la carga automáticamente, ya que existe una dependencia real con la DLL.
En cuanto a seguridad, es una técnica no efectiva para crear un hack para un programa que tenga seguridad. Sería muy fácil validar los archivos en disco que se cargan al proceso (con MD5 por ejemplo), o validar las rutas, etc etc
Mientras no sea para nada de hacking, tiene cierto atractivo esta idea XD.
Acerca de como se crea algo así.. Bueno lo primero es conocer la DLL original que queremos reemplazar, es decir todas sus funciones o al menos las que el proceso víctima utilice (dependencias).
Esto significa que debemos conocer los prototipos de estas funciones, incluyendo su convención de llamada y demás.
Teniendo eso en primer lugar, vamos a considerar que también necesitamos usar la DLL original porque dependemos de las funciones originales. Esto se resuelve cambiándole el nombre a la DLL original. En el caso de PSAPI.DLL lo podemos cambiar a PSAPI2.DLL.
Luego vamos a necesitar cargar la DLL original renombrada y hacer un enlazamiento dinámico con ella, y conseguir las direcciones originales de las funciones que necesitamos. Obviamente esto lo hacemos con LoadLibrary + GetProcAddress.
Luego en nuestra propia DLL ponemos las funciones de reemplazo respetando los prototipos y le ponemos las direcciones de retorno a las originales que importamos dinámicamente con GetProcAddress.
Utilizamos obviamente punteros a función para los retornos en las funciones de reemplazo (hooks).
La convención de llamada usada en PSAPI.DLL para las funciones es __stdcall, por eso podemos hacer que todas las funciones en nuestro proyecto sean por defecto __stdcall para no tener que especificarlo explícitamente en cada una.
Veamos a PSAPI.DLL
Veamos sus exportaciones.
Vamos a concentrarnos en una de sus funciones sólamente, por ejemplo EnumProcesses
Necesitamos un prototipo de función, definamos un tipo de dato correspondiente a esta función.
Código
typedef BOOL (* EnumProcesses_t)(DWORD*,DWORD,DWORD*);
Creamos un puntero a función que va a servir de retorno a la original dentro de la función de reemplazo.
Código
BOOL (* pEnumProcesses)(DWORD*,DWORD,DWORD*);
Creamos la función de reemplazo (vemos que retorna con el puntero a la original)
Código
BOOL newEnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned) { return pEnumProcesses(pProcessIds, cb, pBytesReturned); }
Ahora creamos otra instancia más (no realmente necesaria en este caso), esta función va a ser la que se exporte para que pueda ser usada por el programa víctima.
Código
BOOL _EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned) { return newEnumProcesses(pProcessIds, cb, pBytesReturned); }
Y básicamente hacer lo mismo con las demás funciones XD.
Para compilar este proyecto se requiere del archivo psapi.h que contiene los tipos de datos usados en las funciones de reemplazo. No se requiere psapi.lib que es una librería de enlazamiento estático.
Vamos a necesitar un archivo .DEF que debe ser incluído en el proyecto.
Hay unas reglas para usar el .DEF y estas son:
1: se debe empezar poniendo LIBRARY y seguido el nombre de la DLL.
2: se debe poner EXPORTS
3: se deben poner uno por uno, los nombres que deseamos para cada símbolo, seguido de cada
Nombre debe ir el signo ‘=’ y el nombre real del símbolo, el cual obtenemos del desensamblado.
4: seguido a lo anterior se deja un espacio y se coloca un ‘arroba' y el número de índice de exportación.
Pero veamos una imagen para entender como se hace:
Veamos el código de la DLL:
En main.cpp se encuentran los prototipos de las funciones y los punteros a funciones. Para no poner todo veamos sólo el caso de EnumProcesses
Código
typedef BOOL (* EnumProcesses_t)(DWORD*,DWORD,DWORD*);
Código
BOOL (* pEnumProcesses)(DWORD*,DWORD,DWORD*);
Luego tenemos las funciones de reemplazo. Estas retornan un puntero a función.
Código
BOOL newEnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned) { return pEnumProcesses(pProcessIds, cb, pBytesReturned); }
Luego se muestra un código que se trata de una clase y la creación de un objeto de tipo de la clase, esto busca que se inicializen los punteros a función cuando el programa es ejecutado, ya que cuando se crea el objeto se ejecuta la rutina que inicializa los punteros a función.
Código
/* class Inicializar { public: Inicializar() { pEnumProcesses = 0; pEnumProcessModules = 0; pGetModuleBaseNameA = 0; pGetModuleBaseNameW = 0; pGetModuleFileNameExA = 0; pGetModuleFileNameExW = 0; pGetModuleInformation = 0; pEmptyWorkingSet = 0; pQueryWorkingSet = 0; pInitializeProcessForWsWatch = 0; pGetWsChanges = 0; pGetMappedFileNameW = 0; pGetMappedFileNameA = 0; pEnumDeviceDrivers = 0; pGetDeviceDriverBaseNameA = 0; pGetDeviceDriverBaseNameW = 0; pGetDeviceDriverFileNameA = 0; pGetDeviceDriverFileNameW = 0; pGetProcessMemoryInfo = 0; pEnumPageFilesA =0; pEnumPageFilesW =0; pGetPerformanceInfo =0; pGetProcessImageFileNameA =0; pGetProcessImageFileNameW=0; HINSTANCE myDLL; myDLL = LoadLibrary("psapi2.dll"); if(myDLL != NULL) { pEnumProcesses = (EnumProcesses_t)GetProcAddress(myDLL, "EnumProcesses"); pEnumProcessModules = (EnumProcessModules_t)GetProcAddress(myDLL, "EnumProcessModules"); pGetModuleBaseNameA = (GetModuleBaseNameA_t)GetProcAddress(myDLL, "GetModuleBaseNameA"); pGetModuleBaseNameW = (GetModuleBaseNameW_t)GetProcAddress(myDLL, "GetModuleBaseNameW"); pGetModuleFileNameExA = (GetModuleFileNameExA_t)GetProcAddress(myDLL, "GetModuleFileNameExA"); pGetModuleFileNameExW = (GetModuleFileNameExW_t)GetProcAddress(myDLL, "GetModuleFileNameExW"); pGetModuleInformation = (GetModuleInformation_t)GetProcAddress(myDLL, "GetModuleInformation"); pEmptyWorkingSet = (EmptyWorkingSet_t)GetProcAddress(myDLL, "EmptyWorkingSet"); pQueryWorkingSet = (QueryWorkingSet_t)GetProcAddress(myDLL, "QueryWorkingSet"); pInitializeProcessForWsWatch = (InitializeProcessForWsWatch_t)GetProcAddress(myDLL, "InitializeProcessForWsWatch"); pGetWsChanges = (GetWsChanges_t)GetProcAddress(myDLL, "GetWsChanges"); pGetMappedFileNameW = (GetMappedFileNameW_t)GetProcAddress(myDLL, "GetMappedFileNameW"); pGetMappedFileNameA = (GetMappedFileNameA_t)GetProcAddress(myDLL, "GetMappedFileNameA"); pEnumDeviceDrivers = (EnumDeviceDrivers_t)GetProcAddress(myDLL, "EnumDeviceDrivers"); pGetDeviceDriverBaseNameA = (GetDeviceDriverBaseNameA_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameA"); pGetDeviceDriverBaseNameW = (GetDeviceDriverBaseNameW_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameW"); pGetDeviceDriverFileNameA = (GetDeviceDriverFileNameA_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameA"); pGetDeviceDriverFileNameW = (GetDeviceDriverFileNameW_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameW"); pGetProcessMemoryInfo = (GetProcessMemoryInfo_t)GetProcAddress(myDLL, "GetProcessMemoryInfo"); pEnumPageFilesA =(EnumPageFilesA_t)GetProcAddress(myDLL, "EnumPageFilesA"); pEnumPageFilesW =(EnumPageFilesW_t)GetProcAddress(myDLL, "EnumPageFilesW"); pGetPerformanceInfo =(GetPerformanceInfo_t)GetProcAddress(myDLL, "GetPerformanceInfo"); pGetProcessImageFileNameA =(GetProcessImageFileNameA_t)GetProcAddress(myDLL, "GetProcessImageFileNameA"); pGetProcessImageFileNameW =(GetProcessImageFileNameW_t)GetProcAddress(myDLL, "GetProcessImageFileNameW"); } } }; // Inicializar Init; */
Eso está comentado porque ya se hace en 'DllMain' directamente, junto con un LOG de información.
Código
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: { pEnumProcesses = 0; pEnumProcessModules = 0; pGetModuleBaseNameA = 0; pGetModuleBaseNameW = 0; pGetModuleFileNameExA = 0; pGetModuleFileNameExW = 0; pGetModuleInformation = 0; pEmptyWorkingSet = 0; pQueryWorkingSet = 0; pInitializeProcessForWsWatch = 0; pGetWsChanges = 0; pGetMappedFileNameW = 0; pGetMappedFileNameA = 0; pEnumDeviceDrivers = 0; pGetDeviceDriverBaseNameA = 0; pGetDeviceDriverBaseNameW = 0; pGetDeviceDriverFileNameA = 0; pGetDeviceDriverFileNameW = 0; pGetProcessMemoryInfo = 0; pEnumPageFilesA =0; pEnumPageFilesW =0; pGetPerformanceInfo =0; pGetProcessImageFileNameA =0; pGetProcessImageFileNameW=0; HINSTANCE myDLL; myDLL = LoadLibrary("psapi2.dll"); if(myDLL != NULL) { pEnumProcesses = (EnumProcesses_t)GetProcAddress(myDLL, "EnumProcesses"); pEnumProcessModules = (EnumProcessModules_t)GetProcAddress(myDLL, "EnumProcessModules"); pGetModuleBaseNameA = (GetModuleBaseNameA_t)GetProcAddress(myDLL, "GetModuleBaseNameA"); pGetModuleBaseNameW = (GetModuleBaseNameW_t)GetProcAddress(myDLL, "GetModuleBaseNameW"); pGetModuleFileNameExA = (GetModuleFileNameExA_t)GetProcAddress(myDLL, "GetModuleFileNameExA"); pGetModuleFileNameExW = (GetModuleFileNameExW_t)GetProcAddress(myDLL, "GetModuleFileNameExW"); pGetModuleInformation = (GetModuleInformation_t)GetProcAddress(myDLL, "GetModuleInformation"); pEmptyWorkingSet = (EmptyWorkingSet_t)GetProcAddress(myDLL, "EmptyWorkingSet"); pQueryWorkingSet = (QueryWorkingSet_t)GetProcAddress(myDLL, "QueryWorkingSet"); pInitializeProcessForWsWatch = (InitializeProcessForWsWatch_t)GetProcAddress(myDLL, "InitializeProcessForWsWatch"); pGetWsChanges = (GetWsChanges_t)GetProcAddress(myDLL, "GetWsChanges"); pGetMappedFileNameW = (GetMappedFileNameW_t)GetProcAddress(myDLL, "GetMappedFileNameW"); pGetMappedFileNameA = (GetMappedFileNameA_t)GetProcAddress(myDLL, "GetMappedFileNameA"); pEnumDeviceDrivers = (EnumDeviceDrivers_t)GetProcAddress(myDLL, "EnumDeviceDrivers"); pGetDeviceDriverBaseNameA = (GetDeviceDriverBaseNameA_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameA"); pGetDeviceDriverBaseNameW = (GetDeviceDriverBaseNameW_t)GetProcAddress(myDLL, "GetDeviceDriverBaseNameW"); pGetDeviceDriverFileNameA = (GetDeviceDriverFileNameA_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameA"); pGetDeviceDriverFileNameW = (GetDeviceDriverFileNameW_t)GetProcAddress(myDLL, "GetDeviceDriverFileNameW"); pGetProcessMemoryInfo = (GetProcessMemoryInfo_t)GetProcAddress(myDLL, "GetProcessMemoryInfo"); pEnumPageFilesA =(EnumPageFilesA_t)GetProcAddress(myDLL, "EnumPageFilesA"); pEnumPageFilesW =(EnumPageFilesW_t)GetProcAddress(myDLL, "EnumPageFilesW"); pGetPerformanceInfo =(GetPerformanceInfo_t)GetProcAddress(myDLL, "GetPerformanceInfo"); pGetProcessImageFileNameA =(GetProcessImageFileNameA_t)GetProcAddress(myDLL, "GetProcessImageFileNameA"); pGetProcessImageFileNameW =(GetProcessImageFileNameW_t)GetProcAddress(myDLL, "GetProcessImageFileNameW"); DisableThreadLibraryCalls (myDLL); } if(!pEnumProcesses||!pEnumProcessModules ||!pGetModuleBaseNameA ||!pGetModuleBaseNameW ||!pGetModuleFileNameExA ||!pGetModuleFileNameExW ||!pGetModuleInformation ||!pEmptyWorkingSet ||!pQueryWorkingSet ||!pInitializeProcessForWsWatch ||!pGetWsChanges ||!pGetMappedFileNameW ||!pGetMappedFileNameA ||!pEnumDeviceDrivers ||!pGetDeviceDriverBaseNameA ||!pGetDeviceDriverBaseNameW ||!pGetDeviceDriverFileNameA ||!pGetDeviceDriverFileNameW ||!pGetProcessMemoryInfo ||!pEnumPageFilesA ||!pEnumPageFilesW ||!pGetPerformanceInfo ||!pGetProcessImageFileNameA ||!pGetProcessImageFileNameW) { printf("Wrapper not installed!\n"); system("pause"); } else { extern DWORD AddEnumProcesses; printf("DLL\n"); printf("EnumProcesses 0x%x\n",AddEnumProcesses); printf("newEnumProcesses 0x%x\n",newEnumProcesses); printf("hModule 0x%x\n",hModule); printf("PSAPI2.DLL 0x%x\n",GetModuleHandle("psapi2.dll")); printf("pEnumProcesses 0x%x\n",pEnumProcesses); system("pause"); } } break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } ///////////////////////////////
En exports.cpp sólamente tenemos las funciones de exportación, son las cuales son referenciadas en el archivo .DEF. Estas funciones retornan las funciones de reemplazo. Esto de tener 2 instancias no es necesario como ya se dijo, pero si lo es si se desea utilizar otro método que después voy a exponer.
Código
BOOL _EnumProcesses(DWORD* pProcessIds, DWORD cb, DWORD* pBytesReturned) { return newEnumProcesses(pProcessIds, cb, pBytesReturned); }
Veamos el código del programa ‘Dummy’ que actúa como el programa víctima:
Código
// // By 85 // elhacker.net // etalking.com.ar // david_bs@live.com // 2013 // #pragma comment(lib,"psapi.lib") #include<windows.h> #include<stdio.h> ////////////////////// #define DLLIMPORTING extern "C" __declspec (dllimport) DLLIMPORTING BOOL WINAPI EnumProcesses(DWORD *,DWORD,DWORD *); DLLIMPORTING BOOL WINAPI EnumProcessModules(HANDLE,HMODULE *,DWORD,LPDWORD); DLLIMPORTING DWORD WINAPI GetModuleFileNameExA(HANDLE,HMODULE,LPSTR,DWORD); DLLIMPORTING DWORD WINAPI GetModuleFileNameExW(HANDLE,HMODULE,LPWSTR,DWORD); DLLIMPORTING DWORD WINAPI GetModuleBaseNameA(HANDLE hProcess, HMODULE hModule, LPSTR lpBaseName, DWORD nSize); DLLIMPORTING DWORD WINAPI GetModuleBaseNameW(HANDLE hProcess,HMODULE hModule,LPWSTR lpwBaseName,DWORD nSize); #ifdef UNICODE #define GetModuleFileNameEx GetModuleFileNameExW #define GetModuleBaseName GetModuleBaseNameW #else #define GetModuleFileNameEx GetModuleFileNameExA #define GetModuleBaseName GetModuleBaseNameA #endif ////////////////////// // To ensure correct resolution of symbols, add Psapi.lib to TARGETLIBS // and compile with -DPSAPI_VERSION=1 void PrintProcessNameAndID( DWORD processID ) { TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>"); // Get a handle to the process. HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); // Get the process name. if (NULL != hProcess ) { HMODULE hMod; DWORD cbNeeded; if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) ) { GetModuleBaseName( hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR) ); } } // Print the process name and identifier. printf( TEXT("%s (PID: %u)\n"), szProcessName, processID ); // Release the handle to the process. CloseHandle( hProcess ); } ////////////////////// int main(void) { ////////////////////////// // Get the list of process identifiers. DWORD aProcesses[1024], cbNeeded, cProcesses; unsigned int i; if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) ) { return 1; } // Calculate how many process identifiers were returned. cProcesses = cbNeeded / sizeof(DWORD); // Print the name and process identifier for each process. for ( i = 0; i < cProcesses; i++ ) { if( aProcesses[i] != 0 ) { PrintProcessNameAndID( aProcesses[i] ); } } system("pause"); return 0; } //
El programa víctima si requiere que se referencie la librería estática psapi.lib
Los archivos:
El resultado
Proyecto VC++6:
http://www.mediafire.com/?q9ha83cs9oayhwm
Hasta luego