1769 lines
36 KiB
C++
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(<);
|
|
|
|
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
|