windows-nt/Source/XPSP1/NT/windows/appcompat/shims/lib/common.cpp
2020-09-26 16:20:57 +08:00

1769 lines
36 KiB
C++

/*++
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 <stdio.h>
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(&lt);
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