/*++ Copyright (c) 2000-2002 Microsoft Corporation Module Name: EmulateToolHelp32.cpp Abstract: We've discovered 2 incompatibilities between the 9x and NT Toolhelp implementation so far that affect apps. 1) On 9x for the szExeFile field in PROCESSENTRY32 it simply uses the name of the executable module (which includes the full path and the executable name); on NT this is the image name. Nuclear Strike looks for '\' in szExeFile. 2) On 9x the cntUsage field of PROCESSENTRY32 is always non-zero while on NT it's always 0. We make it 1. there are others (like on NT the th32ModuleID is always 1 while on 9x it's unique for each module) but we haven't seen any apps having problems with those so we aren't putting them in. Notes: This is a general purpose shim. History: 11/14/2000 maonis Created 02/18/2002 mnikkel modified to use strsafe.h --*/ #include "precomp.h" // The toolhelp APIs are lame - they define all the APIs to the W version when UNICODE is defined. // We want to hook the ANSI version so undefine UNICODE. #ifdef UNICODE #undef UNICODE #include #endif typedef BOOL (WINAPI *_pfn_Process32First)(HANDLE hSnapshot, LPPROCESSENTRY32 lppe); typedef BOOL (WINAPI *_pfn_Process32Next)(HANDLE hSnapshot, LPPROCESSENTRY32 lppe); IMPLEMENT_SHIM_BEGIN(EmulateToolHelp32) #include "ShimHookMacro.h" APIHOOK_ENUM_BEGIN APIHOOK_ENUM_ENTRY(Process32First) APIHOOK_ENUM_ENTRY(Process32Next) APIHOOK_ENUM_END /*++ MSDN says the szExeFile field of PROCESSENTRY32 is supposed to contain the "Path and filename of the executable file for the process". But really, a process doesn't really have a path - only modules in the process do. NT does it right (it takes the image name), 9x doesn't. --*/ BOOL GetProcessNameFullPath(DWORD dwPID, LPSTR szExeName) { BOOL bRet = FALSE; HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID); if (hModuleSnap != INVALID_HANDLE_VALUE) { MODULEENTRY32 me32; me32.dwSize = sizeof(MODULEENTRY32); // The first module in the process is the one we want. if (Module32First(hModuleSnap, &me32)) { if (StringCchCopyA(szExeName, MAX_PATH, me32.szExePath) == S_OK) { bRet = TRUE; } } CloseHandle (hModuleSnap); } return bRet; } /*++ This stub function skips the first few processes that don't apply in 9x and returns the first 9x-like process with the full path and name of the executable in lppe->szExeFile. --*/ BOOL APIHOOK(Process32First)( HANDLE hSnapshot, LPPROCESSENTRY32 lppe ) { // Skip till we find the first one we can get the module path and name. BOOL bRet = ORIGINAL_API(Process32First)(hSnapshot, lppe); // The 1st process in [System Process], we ignore it. if (!bRet) { return bRet; } // We can't get the first (or first few) process's module list - we return the first one we can. bRet = ORIGINAL_API(Process32Next)(hSnapshot, lppe); while (bRet) { if (GetProcessNameFullPath(lppe->th32ProcessID, lppe->szExeFile)) { DPFN(eDbgLevelInfo, "[APIHook_Process32First] the 1st process name is %s\n", lppe->szExeFile); lppe->cntUsage = 1; return TRUE; } bRet = ORIGINAL_API(Process32Next)(hSnapshot, lppe); } return bRet; } /*++ This stub function calls the API and get the full path and the name of the executable and put it in lppe->szExeFile. --*/ BOOL APIHOOK(Process32Next)( HANDLE hSnapshot, LPPROCESSENTRY32 lppe ) { BOOL bRet = ORIGINAL_API(Process32Next)(hSnapshot, lppe); if (bRet) { if (!GetProcessNameFullPath(lppe->th32ProcessID, lppe->szExeFile)) { return FALSE; } DPFN(eDbgLevelInfo, "[APIHook_Process32Next] process name is %s\n", lppe->szExeFile); lppe->cntUsage = 1; } return bRet; } HOOK_BEGIN APIHOOK_ENTRY(KERNEL32.DLL, Process32First) APIHOOK_ENTRY(KERNEL32.DLL, Process32Next) HOOK_END IMPLEMENT_SHIM_END