#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include #include #include // ========== Auxiliary debug function #define MYDEBUG 0 #if (MYDEBUG > 0) void DEBUGSTR( char * szFormat, ...) { // sort of OutputDebugStringf char szBuffer[1024]; va_list pArgList; va_start(pArgList, szFormat); _vsnprintf(szBuffer, sizeof(szBuffer), szFormat, pArgList); va_end(pArgList); OutputDebugString(szBuffer); } #else #define DEBUGSTR //DEBUGSTR #endif // ========== Global variables and constants // Macro for adding pointers/DWORDs together without C arithmetic interfering #define MakePtr( cast, ptr, addValue ) (cast)( (DWORD)(ptr)+(DWORD)(addValue)) HINSTANCE hDllInstance; // Dll instance handle BOOL bWarn = FALSE; // warning status // ========== Hooking API functions // // References about API hooking (and dll injection): // - Matt Pietrek ~ Windows 95 System Programming Secrets. // - Jeffrey Richter ~ Programming Applications for Microsoft Windows 4th ed. //----------------------------------------------------------------------------- // HookAPIOneMod // Substitute a new function in the Import Address Table (IAT) of the // specified module . // Return FALSE on error and TRUE on success. //----------------------------------------------------------------------------- BOOL HookAPIOneMod( HMODULE hFromModule, // Handle of the module to intercept calls from PSTR pszFunctionModule, // Name of the module to intercept calls to PSTR pszOldFunctionName, // Name of the function to intercept calls to PROC pfnNewFunction // New function (replaces old one) ) { PROC pfnOldFunction; PIMAGE_DOS_HEADER pDosHeader; PIMAGE_NT_HEADERS pNTHeader; PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_THUNK_DATA pThunk; // Verify that a valid pfn was passed if ( IsBadCodePtr(pfnNewFunction) ) { DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } // Verify that the module and function names passed are valid pfnOldFunction = GetProcAddress( GetModuleHandle(pszFunctionModule), pszOldFunctionName ); if ( !pfnOldFunction ) { DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } // Tests to make sure we're looking at a module image (the 'MZ' header) pDosHeader = (PIMAGE_DOS_HEADER)hFromModule; if ( IsBadReadPtr(pDosHeader, sizeof(IMAGE_DOS_HEADER)) ) { DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } if ( pDosHeader->e_magic != IMAGE_DOS_SIGNATURE ) { DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } // The MZ header has a pointer to the PE header pNTHeader = MakePtr(PIMAGE_NT_HEADERS, pDosHeader, pDosHeader->e_lfanew); // More tests to make sure we're looking at a "PE" image if ( IsBadReadPtr(pNTHeader, sizeof(IMAGE_NT_HEADERS)) ) { DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } if ( pNTHeader->Signature != IMAGE_NT_SIGNATURE ) { DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } // We know have a valid pointer to the module's PE header. // Get a pointer to its imports section pImportDesc = MakePtr(PIMAGE_IMPORT_DESCRIPTOR, pDosHeader, pNTHeader->OptionalHeader. DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]. VirtualAddress); // Bail out if the RVA of the imports section is 0 (it doesn't exist) if ( pImportDesc == (PIMAGE_IMPORT_DESCRIPTOR)pNTHeader ) { return TRUE; } // Iterate through the array of imported module descriptors, looking // for the module whose name matches the pszFunctionModule parameter while ( pImportDesc->Name ) { PSTR pszModName = MakePtr(PSTR, pDosHeader, pImportDesc->Name); if ( stricmp(pszModName, pszFunctionModule) == 0 ) break; pImportDesc++; // Advance to next imported module descriptor } // Bail out if we didn't find the import module descriptor for the // specified module. pImportDesc->Name will be non-zero if we found it. if ( pImportDesc->Name == 0 ) return TRUE; // Get a pointer to the found module's import address table (IAT) pThunk = MakePtr(PIMAGE_THUNK_DATA, pDosHeader, pImportDesc->FirstThunk); // Blast through the table of import addresses, looking for the one // that matches the address we got back from GetProcAddress above. while ( pThunk->u1.Function ) { // double cast avoid warning with VC6 and VC7 :-) if ( (DWORD) pThunk->u1.Function == (DWORD)pfnOldFunction ) { // We found it! DWORD flOldProtect, flNewProtect, flDummy; MEMORY_BASIC_INFORMATION mbi; // Get the current protection attributes VirtualQuery(&pThunk->u1.Function, &mbi, sizeof(mbi)); // Take the access protection flags flNewProtect = mbi.Protect; // Remove ReadOnly and ExecuteRead flags flNewProtect &= ~(PAGE_READONLY | PAGE_EXECUTE_READ); // Add on ReadWrite flag flNewProtect |= (PAGE_READWRITE); // Change the access protection on the region of committed pages in the // virtual address space of the current process if( !VirtualProtect(&pThunk->u1.Function, sizeof(PVOID), flNewProtect, &flOldProtect )) { DEBUGSTR("...No access (LastError=%d)", GetLastError()); return TRUE; } // Overwrite the original address with the address of the new function if ( !WriteProcessMemory(GetCurrentProcess(), &pThunk->u1.Function, &pfnNewFunction, sizeof(pfnNewFunction), NULL) ) { DEBUGSTR("error: %s(%d) LastError=%d", __FILE__, __LINE__, GetLastError()); return FALSE; } // Put the page attributes back the way they were. VirtualProtect( &pThunk->u1.Function, sizeof(PVOID), flOldProtect, &flDummy); return TRUE; } pThunk++; // Advance to next imported function address } return TRUE; // Function not found } //----------------------------------------------------------------------------- // HookAPIAllMod // Substitute a new function in the Import Address Table (IAT) of all // the modules in the current process. // Return FALSE on error and TRUE on success. //----------------------------------------------------------------------------- BOOL HookAPIAllMod( PSTR pszFunctionModule, // Module to intercept calls to PSTR pszOldFunctionName, // Function to intercept calls to PROC pfnNewFunction // New function (replaces old function) ) { HANDLE hModuleSnap = NULL; MODULEENTRY32 me = {0}; BOOL fOk; // Take a snapshot of all modules in the current process. hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); if (hModuleSnap == (HANDLE)-1) { DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } // Fill the size of the structure before using it. me.dwSize = sizeof(MODULEENTRY32); // Walk the module list of the modules for (fOk = Module32First(hModuleSnap, &me); fOk; fOk = Module32Next(hModuleSnap, &me) ) { // We don't hook functions in our own module if (me.hModule != hDllInstance) { DEBUGSTR("Hooking in %s", me.szModule); // Hook this function in this module if (!HookAPIOneMod(me.hModule, pszFunctionModule, pszOldFunctionName, pfnNewFunction) ) { CloseHandle (hModuleSnap); DEBUGSTR("error: %s(%d)", __FILE__, __LINE__); return FALSE; } } } CloseHandle (hModuleSnap); return TRUE; } //----------------------------------------------------------------------------- // CaseFileNameOk // Test if the filename match the filename retained by the system in a // case-sensitive manner. // Return TRUE if the filenames match, FALSE otherwise. // If bWarn == TRUE, the function warns and always returns TRUE. //----------------------------------------------------------------------------- BOOL CaseFileNameOk( const char *Path ) { TCHAR LongPath[MAX_PATH]; TCHAR ShortPath[MAX_PATH]; DWORD len; TCHAR *pF, *pL, *pS, *pM; int stop; len = GetShortPathName(Path, ShortPath, MAX_PATH); len = GetLongPathName(ShortPath, LongPath, MAX_PATH); len = GetShortPathName(LongPath, ShortPath, MAX_PATH); if ( len == 0 ) return FALSE; DEBUGSTR("+F=%s", Path); DEBUGSTR("+S=%s", ShortPath); DEBUGSTR("+L=%s", LongPath); pF = (TCHAR *) Path + 2; pL = LongPath + 2; pS = ShortPath + 2; pM = pF; stop = 0; while(1) { while( *++pF == *++pL ) { if(*pF == '\\') { pM = pF; while(*++pS != '\\') {} stop = 0; } if(*pF==0) { stop = 0; goto end; } } if (stop == 1) { goto end; } else { stop = 1; } pF = pM; while ( *++pF == *++pS ) { if(*pF == '\\') { pM = pF; while(*++pL != '\\') {} stop = 0; } if(*pF==0) { stop = 0; goto end; } } if (stop == 1) { goto end; } else { stop = 1; } pF = pM; } end: if ( stop ) { if (bWarn) { warn("Warning: case sensitive mismatch between\nFile =%s\nLong =%s\nShort=%s\n ", Path, LongPath, ShortPath); return TRUE; } else { return FALSE; } } return TRUE; } //----------------------------------------------------------------------------- // My_CreateFileA( // It is the new function that must replace the original CreateFileA( function. // This function have exactly the same signature as the original one. //----------------------------------------------------------------------------- HANDLE WINAPI My_CreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { HANDLE hFile; hFile = CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile); if ( !CaseFileNameOk(lpFileName) ) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; SetLastError(ERROR_FILE_NOT_FOUND); } return hFile; } //----------------------------------------------------------------------------- // My_stati64 // It is the new function that must replace the original _stati64 function. // This function have exactly the same signature as the original one. //----------------------------------------------------------------------------- __int64 My_stati64( const char *path, struct _stati64 *buffer ) { __int64 status; status = _stati64( path, buffer ); DEBUGSTR("_stati64_path=%s status=%d", path, status); if ( status == 0 ) { // file-status information is obtained if ( !CaseFileNameOk(path) ) { status = -1; errno = ENOENT; } } return status; } //----------------------------------------------------------------------------- // My_stat // It is the new function that must replace the original _stat function. // This function have exactly the same signature as the original one. //----------------------------------------------------------------------------- int My_stat( const char *path, struct _stat *buffer ) { int status; DEBUGSTR("_stat_path=%s", path); status = _stat( path, buffer ); if ( status == 0 ) { // file-status information is obtained if ( !CaseFileNameOk(path) ) { status = -1; errno = ENOENT; } } return status; } //----------------------------------------------------------------------------- // My_GetFileAttributesA // It is the new function that must replace the original GetFileAttributesA function. // This function have exactly the same signature as the original one. //----------------------------------------------------------------------------- DWORD WINAPI My_GetFileAttributesA( LPCTSTR lpFileName ) { DWORD result; DEBUGSTR("GetFileAttributes_path=%s", lpFileName); result = GetFileAttributesA(lpFileName); if ( !CaseFileNameOk(lpFileName) ) { result = 0xFFFFFFFF; SetLastError(ERROR_FILE_NOT_FOUND); } return result; } //----------------------------------------------------------------------------- // My_rmdir // It is the new function that must replace the original _rmdir function. // This function have exactly the same signature as the original one. //----------------------------------------------------------------------------- int My_rmdir( const char *dirname ) { int result; DEBUGSTR("_rmdir_path=%s", dirname); if ( !CaseFileNameOk(dirname) ) { result = -1; errno = ENOENT; } else { result = _rmdir(dirname); } return result; } //----------------------------------------------------------------------------- // My_chdir // It is the new function that must replace the original _chdir function. // This function have exactly the same signature as the original one. //----------------------------------------------------------------------------- int My_chdir( const char *dirname ) { int result; DEBUGSTR("_chdir_path=%s", dirname); if ( !CaseFileNameOk(dirname) ) { result = -1; errno = ENOENT; } else { result = _chdir(dirname); } return result; } // ========== Initialisation //----------------------------------------------------------------------------- // DllMain() // Function called by the system when processes and threads are initialized // and terminated. //----------------------------------------------------------------------------- BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { BOOL bResult = TRUE; int i; char szMsvcrt[3][16] = { "MSVCRT.dll", "MSVCRT70.dll", "MSVCRT71.dll" }; switch( dwReason ) { case DLL_PROCESS_ATTACH: hDllInstance = hInstance; // save Dll instance handle DEBUGSTR("hDllInstance = 0x%.8x", hDllInstance); bResult &= HookAPIAllMod("KERNEL32.dll", "CreateFileA", (PROC)My_CreateFileA); DEBUGSTR("CreateFileA = %d", bResult); bResult &= HookAPIAllMod("KERNEL32.dll", "GetFileAttributesA", (PROC)My_GetFileAttributesA); DEBUGSTR("GetFileAttributesA = %d", bResult); for (i=0; i<3; i++) { if ( GetModuleHandle(szMsvcrt[i]) ) { bResult &= HookAPIAllMod(szMsvcrt[i], "_stati64", (PROC)My_stati64); bResult &= HookAPIAllMod(szMsvcrt[i], "_stat", (PROC)My_stat); bResult &= HookAPIAllMod(szMsvcrt[i], "_rmdir", (PROC)My_rmdir); bResult &= HookAPIAllMod(szMsvcrt[i], "_chdir", (PROC)My_chdir); DEBUGSTR("%s functions = %d", szMsvcrt[i], bResult); } } case DLL_PROCESS_DETACH: break; } return (bResult); } MODULE = Win32::StrictFileNames PACKAGE = Win32::StrictFileNames void _warn_on() CODE: bWarn = TRUE;