/*++ Copyright (c) 2000-2001 Microsoft Corporation Module Name: Common.cpp Abstract: Common functions for all modules Notes: None History: 12/15/1999 linstev Created 01/10/2000 linstev Format to new style 03/14/2000 robkenny Added StringWiden and StringNWiden, StringSubstituteRoutine[A|W] was not using the proper compare routine when calling recursively. 07/06/2000 t-adams Added IsImage16Bit 10/18/2000 a-larrsh Move PatternMatch to common removing redundent code in shims. 10/25/2000 linstev Cleaned up 08/14/2001 robkenny Moved code inside the ShimLib namespace. 09/11/2001 mnikkel Modified DebugPrintfList, DebugPrintf, ShimLogList and ShimLog to retain LastError 09/25/2001 rparsons Modified logging code to use NT calls. Added critical section. 10/18/2001 rparsons Removed critical section, added mutex for logging. --*/ #include "ShimHook.h" #include "ShimLib.h" #include "ShimHookMacro.h" #include namespace ShimLib { BOOL g_bFileLogEnabled = FALSE; // enable/disable file logging WCHAR g_wszFileLog[MAX_PATH]; // name of the log file HANDLE g_hMemoryHeap = INVALID_HANDLE_VALUE; BOOL g_bDebugLevelInitialized = FALSE; DEBUGLEVEL g_DebugLevel = eDbgLevelBase; inline HANDLE GetHeap() { if (g_hMemoryHeap == INVALID_HANDLE_VALUE) { g_hMemoryHeap = HeapCreate(0, 0, 0); } return g_hMemoryHeap; } void * __cdecl ShimMalloc(size_t size) { HANDLE heap = GetHeap(); void* memory = HeapAlloc(heap, HEAP_ZERO_MEMORY, size); return memory; } void __cdecl ShimFree(void * memory) { HANDLE heap = GetHeap(); HeapFree(heap, 0, memory); } void * __cdecl ShimCalloc( size_t num, size_t size ) { size_t nBytes = size * num; void * callocMemory = ShimMalloc(nBytes); ZeroMemory(callocMemory, nBytes); return callocMemory; } void * __cdecl ShimRealloc(void * memory, size_t size) { if (memory == NULL) return ShimMalloc(size); HANDLE heap = GetHeap(); void * reallocMemory = HeapReAlloc(heap, 0, memory, size); return reallocMemory; } DEBUGLEVEL GetDebugLevel() { CHAR cEnv[MAX_PATH]; if (g_bDebugLevelInitialized) { return g_DebugLevel; } g_DebugLevel = eDbgLevelBase; if (GetEnvironmentVariableA( szDebugEnvironmentVariable, cEnv, MAX_PATH)) { CHAR c = cEnv[0]; if ((c >= '0') || (c <= '9')) { g_DebugLevel = (DEBUGLEVEL)((int)(c - '0')); } } g_bDebugLevelInitialized = TRUE; return g_DebugLevel; } /*++ Function Description: Assert that prints file and line number. Arguments: IN dwDetail - Detail level above which no print will occur IN pszFmt - Format string Return Value: None History: 11/01/1999 markder Created --*/ #if DBG VOID DebugAssert( LPCSTR szFile, DWORD dwLine, BOOL bAssert, LPCSTR szHelpString ) { if (!bAssert ) { DPF("ShimLib", eDbgLevelError, "\n"); DPF("ShimLib", eDbgLevelError, "ASSERT: %s\n", szHelpString); DPF("ShimLib", eDbgLevelError, "FILE: %s\n", szFile); DPF("ShimLib", eDbgLevelError, "LINE: %d\n", dwLine); DPF("ShimLib", eDbgLevelError, "\n"); DebugBreak(); } } /*++ Function Description: Print a formatted string using DebugOutputString. Arguments: IN dwDetail - Detail level above which no print will occur IN pszFmt - Format string Return Value: None History: 11/01/1999 markder Created --*/ VOID DebugPrintfList( LPCSTR szShimName, DEBUGLEVEL dwDetail, LPCSTR pszFmt, va_list vaArgList ) { // This must be the first line of this routine to preserve LastError. DWORD dwLastError = GetLastError(); extern DEBUGLEVEL GetDebugLevel(); char szT[1024]; szT[1022] = '\0'; _vsnprintf(szT, 1022, pszFmt, vaArgList); // make sure we have a '\n' at the end of the string int len = lstrlen(szT); if (szT[len-1] != '\n') { lstrcpy(&szT[len], "\n"); } if (dwDetail <= GetDebugLevel()) { switch (dwDetail) { case eDbgLevelError: OutputDebugStringA ("[FAIL] "); break; case eDbgLevelWarning: OutputDebugStringA ("[WARN] "); break; case eDbgLevelInfo: OutputDebugStringA ("[INFO] "); break; } OutputDebugStringA(szShimName); OutputDebugStringA(" - "); OutputDebugStringA(szT); } // This must be the last line of this routine to preserve LastError. SetLastError(dwLastError); } VOID DebugPrintf( LPCSTR szShimName, DEBUGLEVEL dwDetail, LPCSTR pszFmt, ... ) { // This must be the first line of this routine to preserve LastError. DWORD dwLastError = GetLastError(); va_list vaArgList; va_start(vaArgList, pszFmt); DebugPrintfList(szShimName, dwDetail, pszFmt, vaArgList); va_end(vaArgList); // This must be the last line of this routine to preserve LastError. SetLastError(dwLastError); } #endif // DBG /*++ Function Description: Prints a log in the log file if logging is enabled Arguments: IN pszFmt - Format string Return Value: none History: 03/03/2000 clupu Created --*/ #define MAX_LOG_LENGTH 1024 char g_szLog[MAX_LOG_LENGTH]; /*++ Function Description: Prints a log in the log file if logging is enabled Arguments: IN wszShimName - Name of shim that string originates from IN dwDetail - Detail level above which no print will occur IN pszFmt - Format string Return Value: none History: 03/03/2000 clupu Created 09/25/2001 rparsons Converted to NT calls --*/ void ShimLogList( LPCSTR szShimName, DEBUGLEVEL dwDbgLevel, LPCSTR pszFmt, va_list arglist ) { // // This must be the first line of this routine to preserve LastError. // DWORD dwLastError = GetLastError(); int nLen = 0; NTSTATUS status; SYSTEMTIME lt; UNICODE_STRING strLogFile = {0}; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; LARGE_INTEGER liOffset; char szNewLine[] = "\r\n"; DWORD dwWaitResult; HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hLogMutex; // // Convert the path to the log file from DOS to NT. // RtlInitUnicodeString(&strLogFile, g_wszFileLog); status = RtlDosPathNameToNtPathName_U(strLogFile.Buffer, &strLogFile, NULL, NULL); if (!NT_SUCCESS(status)) { DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to convert log file '%ls' to NT path", status, g_wszFileLog); return; } // // Attempt to get a handle to our log file. // InitializeObjectAttributes(&ObjectAttributes, &strLogFile, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtCreateFile(&hFile, FILE_APPEND_DATA | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); RtlFreeUnicodeString(&strLogFile); if (!NT_SUCCESS(status)) { DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to open log file %ls", status, g_wszFileLog); return; } SetFilePointer(hFile, 0, NULL, FILE_END); // // Print a header consisting of data, time, app name, and shim name. // GetLocalTime(<); sprintf(g_szLog, "%02d/%02d/%04d %02d:%02d:%02d %s %d - ", lt.wMonth, lt.wDay, lt.wYear, lt.wHour, lt.wMinute, lt.wSecond, szShimName, dwDbgLevel); nLen = lstrlen(g_szLog); // // Write the header out to the file. // IoStatusBlock.Status = 0; IoStatusBlock.Information = 0; liOffset.LowPart = 0; liOffset.HighPart = 0; // // Get a handle to the mutex and attempt to get ownership. // hLogMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "SHIMLIB_LOG_MUTEX"); if (!hLogMutex) { DPF("ShimLib", eDbgLevelError, "[ShimLogList] %lu Failed to open logging mutex", GetLastError()); goto exit; } dwWaitResult = WaitForSingleObject(hLogMutex, 500); if (WAIT_OBJECT_0 == dwWaitResult) { // // Write the header to the log file. // status = NtWriteFile(hFile, NULL, NULL, NULL, &IoStatusBlock, (PVOID)g_szLog, (ULONG)nLen, &liOffset, NULL); if (!NT_SUCCESS(status)) { DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to write header to log file", status); goto exit; } // // Format our string using the specifiers passed. // _vsnprintf(g_szLog, MAX_LOG_LENGTH - 1, pszFmt, arglist); g_szLog[MAX_LOG_LENGTH - 1] = 0; // // Write the actual data out to the file. // IoStatusBlock.Status = 0; IoStatusBlock.Information = 0; liOffset.LowPart = 0; liOffset.HighPart = 0; nLen = lstrlen(g_szLog); status = NtWriteFile(hFile, NULL, NULL, NULL, &IoStatusBlock, (PVOID)g_szLog, (ULONG)nLen, &liOffset, NULL); if (!NT_SUCCESS(status)) { DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to make entry in log file", status); goto exit; } // // Now write a new line to the log file. // IoStatusBlock.Status = 0; IoStatusBlock.Information = 0; liOffset.LowPart = 0; liOffset.HighPart = 0; nLen = lstrlen(szNewLine); status = NtWriteFile(hFile, NULL, NULL, NULL, &IoStatusBlock, (PVOID)szNewLine, (ULONG)nLen, &liOffset, NULL); if (!NT_SUCCESS(status)) { DPF("ShimLib", eDbgLevelError, "[ShimLogList] 0x%X Failed to write new line to log file", status); goto exit; } } // // Dump it out to the debugger on checked builds. // #if DBG DebugPrintf(szShimName, dwDbgLevel, g_szLog); DebugPrintf(szShimName, dwDbgLevel, "\n"); #endif // DBG exit: if (INVALID_HANDLE_VALUE != hFile) { NtClose(hFile); hFile = INVALID_HANDLE_VALUE; } if (hLogMutex) { ReleaseMutex(hLogMutex); } // // This must be the last line of this routine to preserve LastError. // SetLastError(dwLastError); } /*++ Function Description: Initializes the support for file logging. Arguments: IN pszShim - the name of the shim DLL Return Value: TRUE if successful, FALSE if failed History: 03/03/2000 clupu Created --*/ BOOL InitFileLogSupport( char *pszShim ) { BOOL fReturn = FALSE; WCHAR wszAppPatch[MAX_PATH]; WCHAR* pwsz = NULL; HANDLE hFile = INVALID_HANDLE_VALUE; HANDLE hLogMutex = NULL; DWORD dwLen = 0; NTSTATUS status; UNICODE_STRING strLogFile = {0}; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; // // Attempt to create a mutex. If the mutex already exists, // we don't need to go any further as the log file has // already been created. // hLogMutex = CreateMutex(NULL, TRUE, "SHIMLIB_LOG_MUTEX"); if (!hLogMutex) { DPF("ShimLib", eDbgLevelError, "[InitFileLogSupport] Failed to create logging mutex"); return FALSE; } DWORD dwLastError = GetLastError(); if (ERROR_ALREADY_EXISTS == dwLastError) { fReturn = TRUE; goto exit; } // // We'll create the log file in %windir%\AppPatch. // if (!GetSystemWindowsDirectoryW(g_wszFileLog, MAX_PATH)) { DPF("ShimLib", eDbgLevelError, "[InitFileLogSupport] Failed to get windir path"); goto exit; } lstrcatW(g_wszFileLog, L"\\AppPatch\\"); dwLen = lstrlenW(g_wszFileLog); pwsz = g_wszFileLog + dwLen; // // Query the environment variable and get the name of our log file. // if (!GetEnvironmentVariableW(wszFileLogEnvironmentVariable, pwsz, (MAX_PATH - dwLen) * sizeof(WCHAR))) { goto exit; } // // Convert the path to the log file from DOS to NT. // RtlInitUnicodeString(&strLogFile, g_wszFileLog); status = RtlDosPathNameToNtPathName_U(strLogFile.Buffer, &strLogFile, NULL, NULL); if (!NT_SUCCESS(status)) { DPF("ShimLib", eDbgLevelError, "[InitFileLogSupport] 0x%X Failed to convert log file '%ls' to NT path", status, g_wszFileLog); goto exit; } // // Attempt to create the log file. If it exists, the contents will be cleared. // InitializeObjectAttributes(&ObjectAttributes, &strLogFile, OBJ_CASE_INSENSITIVE, NULL, NULL); status = NtCreateFile(&hFile, GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN_IF, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); RtlFreeUnicodeString(&strLogFile); if (!NT_SUCCESS(status)) { DPF("ShimLib", eDbgLevelError, "[InitFileLogSupport] 0x%X Failed to open log file %ls", status, g_wszFileLog); goto exit; } NtClose(hFile); // // Turn on the flag that tells everyone that logging is enabled. // Release the mutex so that others can use it. // g_bFileLogEnabled = TRUE; fReturn = TRUE; exit: ReleaseMutex(hLogMutex); return fReturn; } /*++ Function Description: Determine the drive type a file resides on. Arguments: IN lpFileName - Filename or relative filename Return Value: See GetDriveType in MSDN History: 10/25/2000 linstev Created --*/ UINT GetDriveTypeFromFileNameA(LPCSTR lpFileName, char *lpDriveLetter) { WCHAR * lpwszFileName = ToUnicode(lpFileName); if (lpwszFileName) { WCHAR szDrive; UINT uType = GetDriveTypeFromFileNameW(lpwszFileName, &szDrive); if (lpDriveLetter) { *lpDriveLetter = (char) szDrive; } free(lpwszFileName); return uType; } else { return DRIVE_UNKNOWN; } } /*++ Function Description: Determine the drive type a file resides on. Arguments: IN lpFileName - Filename or relative filename Return Value: See GetDriveType in MSDN History: 10/25/2000 linstev Created --*/ UINT GetDriveTypeFromFileNameW(LPCWSTR lpFileName, WCHAR *lpDriveLetter) { if (lpFileName && (lpFileName[0] == L'\\') && (lpFileName[1] == L'\\')) { // UNC naming - always network if (lpDriveLetter) { *lpDriveLetter = L'\0'; } return DRIVE_REMOTE; } WCHAR cDrive; if (lpFileName && lpFileName[0] && (lpFileName[1] == L':')) { // Format is Drive:Path\File, so just take the drive cDrive = lpFileName[0]; } else { // Must be a relative path cDrive = 0; WCHAR *wzCurDir = NULL; DWORD dwCurDirSize = GetCurrentDirectoryW(0, wzCurDir); if (!dwCurDirSize) { goto EXIT; } wzCurDir = (LPWSTR) LocalAlloc(LPTR, dwCurDirSize * sizeof(WCHAR)); if (!wzCurDir) { goto EXIT; } dwCurDirSize = GetCurrentDirectoryW(dwCurDirSize, wzCurDir); if (!dwCurDirSize || wzCurDir[0] == L'\\') { goto EXIT; } cDrive = wzCurDir[0]; EXIT: if (wzCurDir) { LocalFree(wzCurDir); } } if (lpDriveLetter) { *lpDriveLetter = L'\0'; } if (cDrive) { WCHAR wzDrive[4]; wzDrive[0] = cDrive; wzDrive[1] = L':'; wzDrive[2] = L'\\'; wzDrive[3] = L'\0'; if (lpDriveLetter) { *lpDriveLetter = (WCHAR)cDrive; } return GetDriveTypeW(wzDrive); } else { return DRIVE_UNKNOWN; } } /*++ Function Description: Widen and duplicate a string into malloc memory. Arguments: IN strToCopy - String to copy Return Value: String in malloc memory History: 03/07/2000 robkenny Created 05/16/2000 robkenny Moved MassagePath (shim specific) routines out of here. --*/ WCHAR * ToUnicode(const char *strToCopy) { if (strToCopy == NULL) { return NULL; } // Get the number of characters in the resulting string, includes NULL at end int nChars = MultiByteToWideChar(CP_ACP, 0, strToCopy, -1, NULL, 0); WCHAR *lpwsz = (WCHAR *) malloc(nChars * sizeof(WCHAR)); if (lpwsz) { nChars = MultiByteToWideChar(CP_ACP, 0, strToCopy, -1, lpwsz, nChars); // If MultibyteToWideChar failed, return NULL if (nChars == 0) { free(lpwsz); lpwsz = NULL; } } return lpwsz; } /*++ Function Description: Convert a WCHAR string to a char string Arguments: IN lpOld - String to convert to char Return Value: char string in malloc memory History: 06/19/2000 robkenny Created --*/ char * ToAnsi(const WCHAR *lpOld) { if (lpOld == NULL) { return NULL; } // Get the number of bytes necessary for the WCHAR string int nBytes = WideCharToMultiByte(CP_ACP, 0, lpOld, -1, NULL, 0, NULL, NULL); char *lpsz = (char *) malloc(nBytes); if (lpsz) { nBytes = WideCharToMultiByte(CP_ACP, 0, lpOld, -1, lpsz, nBytes, NULL, NULL); // If WideCharToMultibyte failed, return NULL if (nBytes == 0) { free(lpsz); lpsz = NULL; } } return lpsz; } /*++ Function Description: Duplicate the first nChars of strToCopy string into malloc memory. Arguments: IN strToCopy - String to copy IN nChar - Number of chars to duplicate, does not count NULL at end. Return Value: String in malloc memory History: 06/02/2000 robkenny Created --*/ char * StringNDuplicateA(const char *strToCopy, int nChars) { if (strToCopy == NULL) { return NULL; } size_t nBytes = (nChars + 1) * sizeof(strToCopy[0]); char *strDuplicate = (char *) malloc(nBytes); if (strDuplicate) { memcpy(strDuplicate, strToCopy, nBytes); strDuplicate[nChars] = 0; } return strDuplicate; } /*++ Function Description: Duplicate a string into malloc memory. Arguments: IN strToCopy - String to copy Return Value: String in malloc memory History: 01/10/2000 linstev Updated 02/14/2000 robkenny Converted from VirtualAlloc to malloc 06/02/2000 robkenny Use StringNDuplicateA --*/ char * StringDuplicateA(const char *strToCopy) { if (strToCopy == NULL) { return NULL; } char *strDuplicate = StringNDuplicateA(strToCopy, strlen(strToCopy)); return strDuplicate; } /*++ Function Description: Duplicate the first nChars of strToCopy string into malloc memory. Arguments: IN strToCopy - String to copy IN nChar - Number of chars to duplicate, does not count NULL at end. Return Value: String in malloc memory History: 06/02/2000 robkenny Created --*/ WCHAR * StringNDuplicateW(const WCHAR *strToCopy, int nChars) { if (strToCopy == NULL) { return NULL; } size_t nBytes = (nChars + 1) * sizeof(strToCopy[0]); WCHAR *strDuplicate = (WCHAR *) malloc(nBytes); if (strDuplicate) { memcpy(strDuplicate, strToCopy, nBytes); strDuplicate[nChars] = 0; } return strDuplicate; } /*++ Function Description: Duplicate a string into malloc memory. Arguments: IN strToCopy - String to copy Return Value: String in malloc memory History: 01/10/2000 linstev Updated 02/14/2000 robkenny Converted from VirtualAlloc to malloc 06/02/2000 robkenny Use StringNDuplicateW --*/ WCHAR * StringDuplicateW(const WCHAR *strToCopy) { if (strToCopy == NULL) { return NULL; } WCHAR *wstrDuplicate = StringNDuplicateW(strToCopy, wcslen(strToCopy)); return wstrDuplicate; } /*++ Function Description: Skip leading whitespace Arguments: IN str - String to scan Return Value: None History: 01/10/2000 linstev Updated --*/ VOID SkipBlanksW(const WCHAR *& str) { if (str) { // Skip leading whitespace static const WCHAR *WhiteSpaceString = L" \t"; str += wcsspn(str, WhiteSpaceString); } } /*++ Function Description: Find the first occurance of strCharSet in string Case insensitive Arguments: IN string - String to search IN strCharSet - String to search for Return Value: First occurance or NULL History: 12/01/1999 robkenny Created 12/15/1999 linstev Reformatted --*/ char* __cdecl stristr( IN const char* string, IN const char* strCharSet ) { char *pszRet = NULL; long nstringLen = strlen(string) + 1; long nstrCharSetLen = strlen(strCharSet) + 1; char *szTemp_string = (char *) malloc(nstringLen); char *szTemp_strCharSet = (char *) malloc(nstrCharSetLen); if ((!szTemp_string) || (!szTemp_strCharSet)) { goto Fail; } strcpy(szTemp_string, string); strcpy(szTemp_strCharSet, strCharSet); _strlwr(szTemp_string); _strlwr(szTemp_strCharSet); pszRet = strstr(szTemp_string, szTemp_strCharSet); if (pszRet) { pszRet = ((char *) string) + (pszRet - szTemp_string); } Fail: if (szTemp_string) { free(szTemp_string); } if (szTemp_strCharSet) { free(szTemp_strCharSet); } return pszRet; } /*++ Function Description: Find the first occurance of strCharSet in string Case insensitive Arguments: IN string - String to search IN strCharSet - String to search for Return Value: First occurance or NULL History: 12/01/1999 robkenny Created 12/15/1999 linstev Reformatted 05/04/2001 maonis Changed to use more efficient implementation. --*/ #define _UPPER 0x1 /* upper case letter */ #define iswupper(_c) (iswctype(_c,_UPPER)) WCHAR* __cdecl wcsistr( IN const WCHAR* wcs1, IN const WCHAR* wcs2 ) { wchar_t *cp = (wchar_t *) wcs1; wchar_t *s1, *s2; wchar_t cs1, cs2; while (*cp) { s1 = cp; s2 = (wchar_t *) wcs2; cs1 = *s1; cs2 = *s2; if (iswupper(cs1)) cs1 = towlower(cs1); if (iswupper(cs2)) cs2 = towlower(cs2); while ( *s1 && *s2 && !(cs1-cs2) ) { s1++, s2++; cs1 = *s1; cs2 = *s2; if (iswupper(cs1)) cs1 = towlower(cs1); if (iswupper(cs2)) cs2 = towlower(cs2); } if (!*s2) return(cp); cp++; } return(NULL); } /*++ Function Description: Find the next token in a string. See strtok in MSDN. Implemented here so we don't need CRT. Arguments: OUT strToken - string containing token(s) IN strDelimit - token list Return Value: Return a pointer to the next token found. History: 04/19/2000 linstev Created --*/ char * __cdecl _strtok( char *strToken, const char *strDelimit ) { unsigned char *str = (unsigned char *)strToken; const unsigned char *ctrl = (const unsigned char *)strDelimit; unsigned char map[32]; int count; char *token; static char *nextoken; // Clear strDelimit map for (count = 0; count < 32; count++) { map[count] = 0; } // Set bits in delimiter table do { map[*ctrl >> 3] |= (1 << (*ctrl & 7)); } while (*ctrl++); // If strToken==NULL, continue with previous strToken if (!str) { str = (unsigned char *)nextoken; } // Find beginning of token (skip over leading delimiters). Note that // there is no token iff this loop sets strToken to point to the terminal // null (*strToken == '\0') while ((map[*str >> 3] & (1 << (*str & 7))) && *str) { str++; } token = (char *)str; // Find the end of the token. If it is not the end of the strToken, // put a null there. for (; *str; str++) { if (map[*str >> 3] & (1 << (*str & 7))) { *str++ = '\0'; break; } } // Update nextoken (or the corresponding field in the per-thread data // structure nextoken = (char *)str; // Determine if a token has been found if (token == (char *)str) { return NULL; } else { return token; } } /*++ Function Description: Copy lpSrc into lpDest without overflowing the buffer Arguments: OUT lpDest Destination string IN nDestChars Size in chars of lpDest IN lpSrc Original string IN nSrcChars Number of chars to copy Return Value: Returns the number of chars copied into lpDest History: 04/19/2000 Robkenny Created --*/ int SafeStringCopyW( WCHAR *lpDest, DWORD nDestChars, const WCHAR *lpSrc, DWORD nSrcChars ) { size_t nCharsToCopy = __min(nSrcChars, nDestChars); if (nCharsToCopy > 0) { memcpy(lpDest, lpSrc, nCharsToCopy*sizeof(WCHAR)); // Make sure string is properly terminated if (lpSrc[nSrcChars-1] == 0) { lpDest[nCharsToCopy-1] = 0; } } return nCharsToCopy; } /*++ Function Description: Tests whether an executable is 16-Bit. Arguments: IN szImageName - The name of the executable image. Return Value: TRUE if executable image is found to be 16-bit, FALSE otherwise. History: 07/06/2000 t-adams Created --*/ BOOL IsImage16BitA(LPCSTR lpApplicationName) { DWORD dwBinaryType; if (GetBinaryTypeA(lpApplicationName, &dwBinaryType)) { return (dwBinaryType == SCS_WOW_BINARY); } else { return FALSE; } } /*++ Function Description: Tests whether an executable is 16-Bit. Arguments: IN wstrImageName - The name of the executable image. Return Value: TRUE if executable image is found to be 16-bit, FALSE otherwise. History: 07/06/2000 t-adams Created --*/ BOOL IsImage16BitW(LPCWSTR lpApplicationName) { DWORD dwBinaryType; if (GetBinaryTypeW(lpApplicationName, &dwBinaryType)) { return (dwBinaryType == SCS_WOW_BINARY); } else { return FALSE; } } /*++ Function Description: Match these two strings, with wildcards. ? matches a single character * matches 0 or more characters The compare is case in-sensitive Arguments: IN pszPattern - Pattern for matching. IN pszTestString - String to match against. Return Value: TRUE if pszTestString matches pszPattern. History: 01/09/2001 markder Replaced non-straightforward version. --*/ BOOL PatternMatchW( IN LPCWSTR pszPattern, IN LPCWSTR pszTestString) { // // March through pszTestString. Each time through the loop, // pszTestString is advanced one character. // BOOL bDone = TRUE; while (bDone) { // // If pszPattern and pszTestString are both sitting on a NULL, // then they reached the end at the same time and the strings // must be equal. // if (*pszPattern == L'\0' && *pszTestString == L'\0') { return TRUE; } if (*pszPattern != L'*') { // // Non-asterisk mode. Look for a match on this character. // switch (*(pszPattern)) { case L'?': // // Match on any character, don't bother comparing. // pszPattern++; break; case L'\\': // // Backslash indicates to take the next character // verbatim. Advance the pointer before making a // comparison. // pszPattern++; default: // // Compare the characters. If equal, continue traversing. // Otherwise, the strings cannot be equal so return FALSE. // if (towupper(*pszPattern) == towupper(*pszTestString)) { pszPattern++; } else { return FALSE; } } } else { // // Asterisk mode. Look for a match on the character directly // after the asterisk. // switch (*(pszPattern + 1)) { case L'*': // // Asterisks exist side by side. Advance the pattern pointer // and go through loop again. // pszPattern++; continue; case L'\0': // // Asterisk exists at the end of the pattern string. Any // remaining part of pszTestString matches so we can // immediately return TRUE. // return TRUE; case L'?': // // Match on any character. If the remaining parts of // pszPattern and pszTestString match, then the entire // string matches. Otherwise, keep advancing the // pszTestString pointer. // if (PatternMatchW(pszPattern + 1, pszTestString)) { return TRUE; } break; case L'\\': // // Backslash indicates to take the next character // verbatim. Advance the pointer before making a // comparison. // pszPattern++; break; } if (towupper(*(pszPattern + 1)) == towupper(*pszTestString)) { // // Characters match. If the remaining parts of // pszPattern and pszTestString match, then the entire // string matches. Otherwise, keep advancing the // pszTestString pointer. // if (PatternMatchW(pszPattern + 1, pszTestString)) { return TRUE; } } } // // No more pszTestString left. Must not be a match. // if (!*pszTestString) { return FALSE; } pszTestString++; } return FALSE; } /*++ Function Description: Determine if the current process is a SafeDisc process. We do this by simply by testing if both an .EXE and .ICD extension exist for the process name. Arguments: None. Return Value: TRUE if Safedisc 1.x is detected. History: 01/23/2001 linstev Created --*/ BOOL bIsSafeDisc1() { BOOL bRet = FALSE; WCHAR szFileName[MAX_PATH+1]; if (GetModuleFileNameW(NULL, szFileName, MAX_PATH)) { // // Find the extension: first '.' after '\' // WCHAR *lpExtension = wcsrchr(szFileName, L'.'); if (lpExtension && (lpExtension > wcsrchr(szFileName, L'\\'))) { // // Detect SafeDisc 1.X, just look for an .ICD file with the same // name // if (_wcsicmp(lpExtension, L".EXE") == 0) { // Current file is .EXE, check for corresponding .ICD wcscpy(lpExtension, L".ICD"); bRet = GetFileAttributesW(szFileName) != 0xFFFFFFFF; } } } if (bRet) { DPF("ShimLib", eDbgLevelInfo, "SafeDisc detected: %S", szFileName); } return bRet; } /*++ Function Description: Determine if the current process is a SafeDisc process. We do this running the image header and looking for a particular signature. Arguments: None. Return Value: TRUE if Safedisc 2 is detected. History: 07/28/2001 linstev Created --*/ BOOL bIsSafeDisc2() { PPEB Peb = NtCurrentPeb(); PLIST_ENTRY LdrHead; PLIST_ENTRY LdrNext; DWORD dwCnt = 0; // // Use the try-except in case the module list changes while we're looking at it // __try { // // Loop through the loaded modules. We use a count to make sure we // aren't looping infinitely // LdrHead = &Peb->Ldr->InMemoryOrderModuleList; LdrNext = LdrHead->Flink; while ((LdrNext != LdrHead) && (dwCnt < 256)) { PLDR_DATA_TABLE_ENTRY LdrEntry; LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); if ((SSIZE_T)LdrEntry->DllBase > 0) { // // A user mode dll, now check for temp name // WCHAR *wzName = LdrEntry->BaseDllName.Buffer; DWORD dwLen; if (wzName && (dwLen = wcslen(wzName)) && (dwLen > 4) && (_wcsicmp(wzName + dwLen - 4, L".tmp") == 0)) { // // Name ends in .tmp, so detect SafeDisc // DWORD_PTR hMod = (DWORD_PTR) LdrEntry->DllBase; PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER) hMod; PIMAGE_NT_HEADERS pINTH = (PIMAGE_NT_HEADERS)(hMod + pIDH->e_lfanew); PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY) (hMod + pINTH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); LPSTR pName = (LPSTR)(hMod + pExport->Name); if (_stricmp(pName, "SecServ.dll") == 0) { // // Export name says this is SafeDisc // DPF("ShimLib", eDbgLevelInfo, "SafeDisc 2 detected"); return TRUE; } } } dwCnt++; LdrNext = LdrEntry->InMemoryOrderLinks.Flink; } } __except(EXCEPTION_EXECUTE_HANDLER) { DPF("ShimLib", eDbgLevelError, "Exception encounterd while detecting SafeDisc 2"); } return FALSE; } /*++ Function Description: Determine if the current process is NTVDM. Arguments: None. Return Value: TRUE if NTVDM is detected. History: 01/14/2002 clupu Created --*/ BOOL IsNTVDM( void ) { PLDR_DATA_TABLE_ENTRY Entry; PLIST_ENTRY Head; PPEB Peb = NtCurrentPeb(); Head = &Peb->Ldr->InLoadOrderModuleList; Head = Head->Flink; Entry = CONTAINING_RECORD(Head, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (_wcsicmp(Entry->FullDllName.Buffer, L"ntvdm.exe") == 0) { return TRUE; } return FALSE; } }; // end of namespace ShimLib