1081 lines
28 KiB
C++
1081 lines
28 KiB
C++
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
VerifLog.cpp
|
|
|
|
Abstract:
|
|
|
|
This module implements the code for manipulating the AppVerifier log file.
|
|
|
|
Author:
|
|
|
|
dmunsil created 04/26/2001
|
|
|
|
Revision History:
|
|
|
|
08/14/2001 robkenny Moved code inside the ShimLib namespace.
|
|
09/21/2001 rparsons Logging code now uses NT calls.
|
|
09/25/2001 rparsons Added critical section.
|
|
--*/
|
|
|
|
#include "avrfutil.h"
|
|
#include "ShimHook.h"
|
|
#include "VerifLog.h"
|
|
#include "strsafe.h"
|
|
#include "string.h"
|
|
|
|
|
|
namespace ShimLib
|
|
{
|
|
|
|
|
|
typedef struct _VLOG_GLOBAL_DATA {
|
|
BOOL bLoggingDisabled; // was logging disabled?
|
|
PRTL_CRITICAL_SECTION pcsLogging;
|
|
WCHAR szSessionLog[MAX_PATH];
|
|
WCHAR szProcessLog[MAX_PATH];
|
|
} VLOG_GLOBAL_DATA, *PVLOG_GLOBAL_DATA;
|
|
|
|
|
|
PVLOG_GLOBAL_DATA g_pData = NULL;
|
|
HANDLE g_hMap = NULL; // mapping handle for global data
|
|
BOOL g_bVerifierLogInited = FALSE; // have we been through the init sequence?
|
|
BOOL g_bLoggingDisabled = TRUE; // have we been through the init sequence?
|
|
BOOL g_bLogBreakIn = FALSE;
|
|
CString g_strSessionLog;
|
|
CString g_strProcessLog;
|
|
PRTL_CRITICAL_SECTION g_pcsLogging;
|
|
LPVOID g_pDllBase; // our own DLL base
|
|
LPVOID g_pDllEnd; // one past the DLL's last byte
|
|
DWORD g_dwSizeOfImage; // our own DLL image size
|
|
|
|
|
|
void
|
|
CheckForDebuggerBreakIn(
|
|
void
|
|
)
|
|
{
|
|
WCHAR szExe[MAX_PATH];
|
|
|
|
if (!GetCurrentExeName(szExe, ARRAYSIZE(szExe))) {
|
|
g_bLogBreakIn = FALSE;
|
|
}
|
|
|
|
g_bLogBreakIn = GetShimSettingDWORD(L"General", szExe, AV_BREAKIN, FALSE);
|
|
}
|
|
|
|
BOOL
|
|
GetModuleNameAndOffset(
|
|
LPVOID lpAddress, // IN return address to search for
|
|
LPWSTR lpwszModuleName, // OUT name of module that contains address
|
|
DWORD dwBufferChars, // IN size in chars of module name buffer
|
|
PDWORD pdwOffset // OUT offset within module
|
|
)
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
PLIST_ENTRY LdrHead;
|
|
PLIST_ENTRY LdrNext;
|
|
DWORD i;
|
|
BOOL bRet = FALSE;
|
|
|
|
if (!lpAddress || !lpwszModuleName || !pdwOffset) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// search for the module
|
|
//
|
|
|
|
LdrHead = &Peb->Ldr->InMemoryOrderModuleList;
|
|
|
|
LdrNext = LdrHead->Flink;
|
|
|
|
while (LdrNext != LdrHead) {
|
|
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry;
|
|
|
|
LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
|
|
|
//
|
|
// Is this it?
|
|
//
|
|
if (lpAddress >= LdrEntry->DllBase && lpAddress < ((PBYTE)(LdrEntry->DllBase) + LdrEntry->SizeOfImage)) {
|
|
|
|
//
|
|
// we special-case shimeng and ntdll, because we don't want them to be filtered out by the
|
|
// "system dll" filter.
|
|
//
|
|
if (_wcsicmp(LdrEntry->BaseDllName.Buffer, L"shimeng.dll") == 0 || _wcsicmp(LdrEntry->BaseDllName.Buffer, L"ntdll.dll") == 0) {
|
|
StringCchCopyW(lpwszModuleName, dwBufferChars, L"?");
|
|
*pdwOffset = 0;
|
|
|
|
} else {
|
|
|
|
StringCchCopyW(lpwszModuleName, dwBufferChars, LdrEntry->BaseDllName.Buffer);
|
|
|
|
*pdwOffset = (DWORD)((PBYTE)lpAddress - (PBYTE)(LdrEntry->DllBase));
|
|
}
|
|
bRet = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
|
|
}
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
void
|
|
GetCallingModule(
|
|
LPWSTR szModule,
|
|
DWORD dwChars,
|
|
PDWORD pdwOffset
|
|
)
|
|
{
|
|
PVOID apRetAddresses[10];
|
|
USHORT unAddresses, i;
|
|
BOOL bFound = FALSE;
|
|
ULONG ulHash;
|
|
|
|
//
|
|
// On W2K, RtlCaptureStackBackTrace tries to dereference the fourth
|
|
// argument (the returned hash) without ensuring that it's valid.
|
|
// This causes on an access violation. On XP, the problem has been
|
|
// fixed. We get a hash value back, but we'll never use it.
|
|
//
|
|
unAddresses = RtlCaptureStackBackTrace(3, 10, apRetAddresses, &ulHash);
|
|
|
|
for (i = 0; i != unAddresses; i++) {
|
|
PVOID pAddress = apRetAddresses[i];
|
|
|
|
if (pAddress < g_pDllBase || pAddress >= g_pDllEnd) {
|
|
bFound = GetModuleNameAndOffset(pAddress, szModule, dwChars, pdwOffset);
|
|
if (bFound) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bFound) {
|
|
if (pdwOffset) {
|
|
*pdwOffset = 0;
|
|
}
|
|
if (szModule) {
|
|
StringCchCopyW(szModule, dwChars, L"?");
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Initializes the globals holding this module's base address and size
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
History:
|
|
|
|
09/26/2001 dmunsil Created
|
|
|
|
--*/
|
|
void
|
|
GetCurrentModuleInfo(void)
|
|
{
|
|
PPEB Peb = NtCurrentPeb();
|
|
PLIST_ENTRY LdrHead;
|
|
PLIST_ENTRY LdrNext;
|
|
DWORD i;
|
|
|
|
//
|
|
// the base address is just the hInst
|
|
//
|
|
g_pDllBase = (LPVOID)g_hinstDll;
|
|
|
|
//
|
|
// now go find the size of the image by looking through the
|
|
// loader's module list
|
|
//
|
|
|
|
LdrHead = &Peb->Ldr->InMemoryOrderModuleList;
|
|
|
|
LdrNext = LdrHead->Flink;
|
|
|
|
while (LdrNext != LdrHead) {
|
|
|
|
PLDR_DATA_TABLE_ENTRY LdrEntry;
|
|
|
|
LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
|
|
|
|
//
|
|
// Is this it?
|
|
//
|
|
if (LdrEntry->DllBase == g_pDllBase) {
|
|
g_dwSizeOfImage = LdrEntry->SizeOfImage;
|
|
g_pDllEnd = (PVOID)((PBYTE)g_pDllBase + g_dwSizeOfImage);
|
|
break;
|
|
}
|
|
|
|
LdrNext = LdrEntry->InMemoryOrderLinks.Flink;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Initializes the support for file logging.
|
|
|
|
Return Value:
|
|
|
|
TRUE if successful, FALSE if failed
|
|
|
|
History:
|
|
|
|
04/26/2001 dmunsil Created
|
|
09/27/2001 rparsons Converted to use NT calls
|
|
|
|
--*/
|
|
BOOL
|
|
InitVerifierLogSupport(void)
|
|
{
|
|
CString strProcessPath;
|
|
CString strProcessName;
|
|
CString strTemp;
|
|
SYSTEMTIME LocalTime;
|
|
CString strTime;
|
|
CString strShared;
|
|
char *szTemp;
|
|
int nTemp;
|
|
BOOL bAlreadyInited;
|
|
BOOL bSuccess = FALSE;
|
|
DWORD cchSize;
|
|
DWORD dwID;
|
|
DWORD dwErr;
|
|
WCHAR wszVLogPath[MAX_PATH];
|
|
NTSTATUS status;
|
|
UNICODE_STRING strLogFile = {0};
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// if we've already been inited, get out
|
|
//
|
|
if (g_bVerifierLogInited) {
|
|
return FALSE;
|
|
}
|
|
g_bVerifierLogInited = TRUE;
|
|
|
|
CheckForDebuggerBreakIn();
|
|
|
|
//
|
|
// get the current module's base address and size
|
|
//
|
|
GetCurrentModuleInfo();
|
|
|
|
//
|
|
// first check for a shared memory block
|
|
//
|
|
dwID = GetCurrentProcessId();
|
|
strShared.Format(L"VeriferLog_%08X", dwID);
|
|
|
|
g_hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, strShared.GetAnsi());
|
|
if (g_hMap) {
|
|
bAlreadyInited = TRUE;
|
|
} else {
|
|
bAlreadyInited = FALSE;
|
|
g_hMap = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
NULL,
|
|
PAGE_READWRITE,
|
|
0,
|
|
sizeof(VLOG_GLOBAL_DATA),
|
|
strShared.GetAnsi());
|
|
}
|
|
|
|
if (!g_hMap) {
|
|
DPF("VerifierLog", eDbgLevelError, "Cannot get shared global data.");
|
|
g_bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
g_pData = (PVLOG_GLOBAL_DATA)MapViewOfFile(g_hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
|
|
if (!g_pData) {
|
|
DPF("VerifierLog", eDbgLevelError, "Cannot map shared global data.");
|
|
g_bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
if (bAlreadyInited) {
|
|
if (g_pData->szProcessLog[0] == 0 || g_pData->szSessionLog[0] == 0) {
|
|
g_bLoggingDisabled = TRUE;
|
|
g_pData->bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
g_bLoggingDisabled = g_pData->bLoggingDisabled;
|
|
|
|
g_strSessionLog = g_pData->szSessionLog;
|
|
g_strProcessLog = g_pData->szProcessLog;
|
|
g_pcsLogging = g_pData->pcsLogging;
|
|
return TRUE;
|
|
} else {
|
|
//
|
|
// we need to init the file mapping, so temporarily disable logging, just in case.
|
|
//
|
|
g_pData->bLoggingDisabled = TRUE;
|
|
|
|
//
|
|
// Initialize the critical section. We allocate memory so that we'll point
|
|
// to one CS for every shim. Note that we don't free this as there are
|
|
// several places we can exit.
|
|
//
|
|
g_pcsLogging = (PRTL_CRITICAL_SECTION)ShimMalloc(sizeof(RTL_CRITICAL_SECTION));
|
|
|
|
if (!g_pcsLogging) {
|
|
DPF("VerifierLog", eDbgLevelError, "No memory for critical section.");
|
|
return FALSE;
|
|
}
|
|
|
|
status = RtlInitializeCriticalSectionAndSpinCount(g_pcsLogging, 0x80000000 | 4000);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DPF("VerifierLog", eDbgLevelError, "Failed to init critical section.");
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// First, check that log directory exists; if not, we're not logging
|
|
//
|
|
cchSize = GetAppVerifierLogPath(wszVLogPath, ARRAYSIZE(wszVLogPath));
|
|
|
|
if (cchSize > ARRAYSIZE(wszVLogPath) || cchSize == 0) {
|
|
DPF("VerifierLog", eDbgLevelError, "Buffer for log path is too small.");
|
|
g_bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
if (GetFileAttributesW(wszVLogPath) == -1) {
|
|
DPF("VerifierLog", eDbgLevelInfo, "No log directory %ls. Logging disabled.", wszVLogPath);
|
|
g_bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Next, check for the existence of session.log. If it's not there,
|
|
// we're not logging
|
|
//
|
|
g_strSessionLog = wszVLogPath;
|
|
g_strSessionLog += L"\\session.log";
|
|
if (GetFileAttributesW(g_strSessionLog.Get()) == -1) {
|
|
DPF("VerifierLog", eDbgLevelInfo, "No session log file '%ls'. Logging disabled.", g_strSessionLog.Get());
|
|
g_bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// get the process log file name
|
|
//
|
|
if (strProcessPath.GetModuleFileNameW(NULL) == 0) {
|
|
DPF("VerifierLog", eDbgLevelError, "Cannot get module file name.");
|
|
g_bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// strip out just the name minus path and extension
|
|
//
|
|
strProcessPath.SplitPath(NULL, NULL, &strProcessName, NULL);
|
|
|
|
//
|
|
// combine into log name, find first available
|
|
//
|
|
nTemp = 0;
|
|
do {
|
|
g_strProcessLog.Format(L"%ls\\%ls%d.%ls", wszVLogPath, strProcessName.Get(), nTemp, L"log");
|
|
nTemp++;
|
|
} while (GetFileAttributesW(g_strProcessLog.Get()) != -1);
|
|
|
|
//
|
|
// Convert the path to the log file from DOS to NT.
|
|
//
|
|
bSuccess = RtlDosPathNameToNtPathName_U(g_strProcessLog.Get(), &strLogFile, NULL, NULL);
|
|
|
|
if (!bSuccess) {
|
|
DPF("VerifierLog",
|
|
eDbgLevelError,
|
|
"Failed to convert log file '%ls' to NT path",
|
|
g_strProcessLog.Get());
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Attempt to get a handle to our log file.
|
|
// Truncate the file if it already exists.
|
|
//
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&strLogFile,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = NtCreateFile(&hFile,
|
|
GENERIC_ALL | 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("VerifierLog", eDbgLevelError, "0x%X Failed to open log file %ls",
|
|
status, g_strProcessLog.Get());
|
|
g_bLoggingDisabled = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
NtClose(hFile);
|
|
|
|
//
|
|
// put the info in the session log and the process log
|
|
//
|
|
g_pData->bLoggingDisabled = FALSE;
|
|
g_bLoggingDisabled = FALSE;
|
|
|
|
//
|
|
// I realize these pointers point to process-specific memory, but since
|
|
// this mapping is only shared by this process, it seems safe.
|
|
//
|
|
StringCchCopyW(g_pData->szProcessLog, ARRAYSIZE(g_pData->szProcessLog), g_strProcessLog);
|
|
StringCchCopyW(g_pData->szSessionLog, ARRAYSIZE(g_pData->szSessionLog), g_strSessionLog);
|
|
|
|
GetLocalTime(&LocalTime);
|
|
strTime.Format(L"%d/%d/%d %d:%02d:%02d",
|
|
LocalTime.wMonth,
|
|
LocalTime.wDay,
|
|
LocalTime.wYear,
|
|
LocalTime.wHour,
|
|
LocalTime.wMinute,
|
|
LocalTime.wSecond
|
|
);
|
|
|
|
strTemp.Format(L"# LOG_BEGIN %ls '%ls' '%ls'", strTime.Get(),
|
|
strProcessPath.Get(), g_strProcessLog.Get());
|
|
szTemp = strTemp.GetAnsi();
|
|
WriteToProcessLog(szTemp);
|
|
WriteToSessionLog(szTemp);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
clean up all our shared file resources
|
|
|
|
History:
|
|
|
|
04/26/2001 dmunsil Created
|
|
|
|
--*/
|
|
void
|
|
ReleaseVerifierLogSupport(void)
|
|
{
|
|
g_bLoggingDisabled = TRUE;
|
|
if (g_pData) {
|
|
UnmapViewOfFile(g_pData);
|
|
g_pData = NULL;
|
|
if (g_hMap) {
|
|
CloseHandle(g_hMap);
|
|
g_hMap = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Logs a problem that the verifier has found
|
|
|
|
History:
|
|
|
|
04/26/2001 dmunsil Created
|
|
|
|
--*/
|
|
|
|
void
|
|
CVerifierLog::VLog(
|
|
VLOG_LEVEL eLevel,
|
|
DWORD dwLogNum,
|
|
LPCSTR pszFmt,
|
|
...
|
|
)
|
|
{
|
|
char szT[1024];
|
|
char *szTemp;
|
|
int nLen;
|
|
int nRemain;
|
|
va_list arglist;
|
|
DWORD dwOffset = 0;
|
|
WCHAR szModule[256];
|
|
|
|
if (g_bLoggingDisabled) {
|
|
return;
|
|
}
|
|
|
|
GetCallingModule(szModule, 256, &dwOffset);
|
|
|
|
StringCchPrintfA(szT,
|
|
ARRAYSIZE(szT),
|
|
"| %ls %d | %d %ls %X'",
|
|
m_strShimName.Get(),
|
|
dwLogNum,
|
|
eLevel,
|
|
szModule,
|
|
dwOffset);
|
|
|
|
nLen = lstrlen(szT);
|
|
szTemp = szT + nLen;
|
|
nRemain = 1024 - nLen;
|
|
|
|
if (nRemain > 0) {
|
|
va_start(arglist, pszFmt);
|
|
StringCchVPrintfA(szTemp, nRemain, pszFmt, arglist);
|
|
va_end(arglist);
|
|
}
|
|
|
|
WriteToProcessLog(szT);
|
|
|
|
if (g_bLogBreakIn) {
|
|
OutputDebugString(szT);
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Dumps the header for a shim that tells how many log entries it has.
|
|
|
|
History:
|
|
|
|
04/26/2001 dmunsil Created
|
|
|
|
--*/
|
|
void
|
|
CVerifierLog::DumpShimHeader(void)
|
|
{
|
|
char szT[1024];
|
|
|
|
if (m_bHeaderDumped) {
|
|
return;
|
|
}
|
|
|
|
StringCchPrintfA(szT, ARRAYSIZE(szT), "# SHIM_BEGIN %ls %d", m_strShimName.Get(), m_dwEntries);
|
|
WriteToProcessLog(szT);
|
|
|
|
m_bHeaderDumped = TRUE;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Dumps into the log the text string associated with
|
|
each log entry. These are dumped before logging begins, just to
|
|
provide the strings necessary for the verifier UI to display them
|
|
|
|
Return Value:
|
|
|
|
History:
|
|
|
|
04/26/2001 dmunsil Created
|
|
|
|
--*/
|
|
void
|
|
CVerifierLog::DumpLogEntry(
|
|
DWORD dwLogNum,
|
|
UINT unResTitle,
|
|
UINT unResDescription,
|
|
UINT unResURL
|
|
)
|
|
{
|
|
WCHAR szRes[1024];
|
|
char szLine[4096];
|
|
|
|
if (g_bLoggingDisabled) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// dump the header, if necessary
|
|
//
|
|
DumpShimHeader();
|
|
|
|
if (!VLogLoadString(g_hinstDll, unResTitle, szRes, 1024)) {
|
|
DPF("VerifierLog", eDbgLevelError, "No string resource found for title.");
|
|
szRes[0] = 0;
|
|
}
|
|
StringCchPrintfA(szLine, ARRAYSIZE(szLine), "# LOGENTRY %ls %d '%ls", m_strShimName.Get(), dwLogNum, szRes);
|
|
WriteToProcessLog(szLine);
|
|
|
|
if (!VLogLoadString(g_hinstDll, unResDescription, szRes, 1024)) {
|
|
DPF("VerifierLog", eDbgLevelWarning, "No string resource found for description.");
|
|
szRes[0] = 0;
|
|
}
|
|
if (szRes[0]) {
|
|
WriteToProcessLog("# DESCRIPTION BEGIN");
|
|
StringCchPrintfA(szLine, ARRAYSIZE(szLine), "%ls", szRes);
|
|
WriteToProcessLog(szLine);
|
|
WriteToProcessLog("# DESCRIPTION END");
|
|
}
|
|
|
|
if (!VLogLoadString(g_hinstDll, unResURL, szRes, 1024)) {
|
|
DPF("VerifierLog", eDbgLevelWarning, "No string resource found for URL.");
|
|
szRes[0] = 0;
|
|
}
|
|
|
|
if (szRes[0]) {
|
|
StringCchPrintfA(szLine, ARRAYSIZE(szLine), "# URL '%ls", szRes);
|
|
WriteToProcessLog(szLine);
|
|
}
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Writes a line of text to the process log file
|
|
|
|
Return Value:
|
|
|
|
History:
|
|
|
|
04/26/2001 dmunsil Created
|
|
09/21/2001 rparsons Converted to NT calls
|
|
|
|
--*/
|
|
void
|
|
WriteToProcessLog(
|
|
LPCSTR szLine
|
|
)
|
|
{
|
|
int nLen = 0;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER liOffset;
|
|
UNICODE_STRING strLogFile = {0};
|
|
NTSTATUS status;
|
|
char szNewLine[] = "\r\n";
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
if (g_bLoggingDisabled) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Convert the path to the log file from DOS to NT.
|
|
//
|
|
bSuccess = RtlDosPathNameToNtPathName_U(g_strProcessLog.Get(), &strLogFile, NULL, NULL);
|
|
|
|
if (!bSuccess) {
|
|
DPF("VerifierLog",
|
|
eDbgLevelError,
|
|
"[WriteToProcessLog] Failed to convert log file '%ls' to NT path",
|
|
g_strProcessLog.Get());
|
|
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("VerifierLog", eDbgLevelError, "[WriteToProcessLog] 0x%X Failed to open log file %ls",
|
|
status, g_strProcessLog.Get());
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure we have no '\n' or '\r' at the end of the string.
|
|
//
|
|
nLen = lstrlen(szLine);
|
|
|
|
while (nLen && (szLine[nLen - 1] == '\n' || szLine[nLen - 1] == '\r')) {
|
|
nLen--;
|
|
}
|
|
|
|
//
|
|
// Write the data out to the file.
|
|
//
|
|
IoStatusBlock.Status = 0;
|
|
IoStatusBlock.Information = 0;
|
|
|
|
liOffset.LowPart = 0;
|
|
liOffset.HighPart = 0;
|
|
|
|
//
|
|
// Enter a critical section to ensure that log entries are in the proper order.
|
|
//
|
|
RtlEnterCriticalSection(g_pcsLogging);
|
|
|
|
status = NtWriteFile(hFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(PVOID)szLine,
|
|
(ULONG)nLen,
|
|
&liOffset,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DPF("VerifierLog", eDbgLevelError, "[WriteToProcessLog] 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("VerifierLog", eDbgLevelError, "[WriteToProcessLog] 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("VerifierLog", eDbgLevelInfo, szLine);
|
|
DebugPrintf("VerifierLog", eDbgLevelInfo, szNewLine);
|
|
#endif // DBG
|
|
|
|
exit:
|
|
|
|
if (INVALID_HANDLE_VALUE != hFile) {
|
|
NtClose(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
RtlLeaveCriticalSection(g_pcsLogging);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Writes a line of text to the session log file
|
|
|
|
Return Value:
|
|
|
|
History:
|
|
|
|
04/26/2001 dmunsil Created
|
|
09/21/2001 rparsons Converted to NT calls
|
|
|
|
--*/
|
|
void
|
|
WriteToSessionLog(
|
|
LPCSTR szLine
|
|
)
|
|
{
|
|
int nLen = 0;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER liOffset;
|
|
UNICODE_STRING strLogFile = {0};
|
|
NTSTATUS status;
|
|
char szNewLine[] = "\r\n";
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
BOOL bSuccess = FALSE;
|
|
|
|
if (g_bLoggingDisabled) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Convert the path to the log file from DOS to NT.
|
|
//
|
|
bSuccess = RtlDosPathNameToNtPathName_U(g_strSessionLog.Get(), &strLogFile, NULL, NULL);
|
|
|
|
if (!bSuccess) {
|
|
DPF("VerifierLog",
|
|
eDbgLevelError,
|
|
"[WriteToSessionLog] Failed to convert log file '%ls' to NT path",
|
|
g_strSessionLog.Get());
|
|
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("VerifierLog", eDbgLevelError, "[WriteToSessionLog] 0x%X Failed to open log file %ls",
|
|
status, g_strProcessLog.Get());
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make sure we have no '\n' or '\r' at the end of the string.
|
|
//
|
|
nLen = lstrlen(szLine);
|
|
|
|
while (nLen && (szLine[nLen - 1] == '\n' || szLine[nLen - 1] == '\r')) {
|
|
nLen--;
|
|
}
|
|
|
|
//
|
|
// Write the data out to the file.
|
|
//
|
|
IoStatusBlock.Status = 0;
|
|
IoStatusBlock.Information = 0;
|
|
|
|
liOffset.LowPart = 0;
|
|
liOffset.HighPart = 0;
|
|
|
|
//
|
|
// Enter a critical section to ensure that log entries are in the proper order.
|
|
//
|
|
RtlEnterCriticalSection(g_pcsLogging);
|
|
|
|
status = NtWriteFile(hFile,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
(PVOID)szLine,
|
|
(ULONG)nLen,
|
|
&liOffset,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
DPF("VerifierLog", eDbgLevelError, "[WriteToSessionLog] 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("VerifierLog", eDbgLevelError, "[WriteToSessionLog] 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("VerifierLog", eDbgLevelInfo, szLine);
|
|
DebugPrintf("VerifierLog", eDbgLevelInfo, szNewLine);
|
|
#endif // DBG
|
|
|
|
exit:
|
|
|
|
if (INVALID_HANDLE_VALUE != hFile) {
|
|
NtClose(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
RtlLeaveCriticalSection(g_pcsLogging);
|
|
}
|
|
|
|
|
|
int VLogLoadString(
|
|
HMODULE hModule,
|
|
UINT wID,
|
|
LPWSTR lpBuffer, // Unicode buffer
|
|
int cchBufferMax)
|
|
{
|
|
HRSRC hResInfo;
|
|
HANDLE hStringSeg;
|
|
LPWSTR lpsz;
|
|
int cch;
|
|
|
|
/*
|
|
* Make sure the parms are valid.
|
|
*/
|
|
if (lpBuffer == NULL) {
|
|
DPF("VLogLoadString", eDbgLevelWarning, "LoadStringOrError: lpBuffer == NULL");
|
|
return 0;
|
|
}
|
|
|
|
|
|
cch = 0;
|
|
|
|
/*
|
|
* String Tables are broken up into 16 string segments. Find the segment
|
|
* containing the string we are interested in.
|
|
*/
|
|
if (hResInfo = FindResourceW(hModule, (LPWSTR)ULongToPtr( ((LONG)(((USHORT)wID >> 4) + 1)) ), (LPWSTR)RT_STRING)) {
|
|
|
|
/*
|
|
* Load that segment.
|
|
*/
|
|
hStringSeg = LoadResource(hModule, hResInfo);
|
|
|
|
/*
|
|
* Lock the resource.
|
|
*/
|
|
if (lpsz = (LPWSTR)LockResource(hStringSeg)) {
|
|
|
|
/*
|
|
* Move past the other strings in this segment.
|
|
* (16 strings in a segment -> & 0x0F)
|
|
*/
|
|
wID &= 0x0F;
|
|
while (TRUE) {
|
|
cch = *((WCHAR *)lpsz++); // PASCAL like string count
|
|
// first WCHAR is count of WCHARs
|
|
if (wID-- == 0) break;
|
|
lpsz += cch; // Step to start if next string
|
|
}
|
|
|
|
/*
|
|
* chhBufferMax == 0 means return a pointer to the read-only resource buffer.
|
|
*/
|
|
if (cchBufferMax == 0) {
|
|
*(LPWSTR *)lpBuffer = lpsz;
|
|
} else {
|
|
|
|
/*
|
|
* Account for the NULL
|
|
*/
|
|
cchBufferMax--;
|
|
|
|
/*
|
|
* Don't copy more than the max allowed.
|
|
*/
|
|
if (cch > cchBufferMax)
|
|
cch = cchBufferMax;
|
|
|
|
/*
|
|
* Copy the string into the buffer.
|
|
*/
|
|
RtlCopyMemory(lpBuffer, lpsz, cch*sizeof(WCHAR));
|
|
}
|
|
|
|
/*
|
|
* Unlock resource, but don't free it - better performance this
|
|
* way.
|
|
*/
|
|
UnlockResource(hStringSeg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Append a NULL.
|
|
*/
|
|
if (cchBufferMax != 0) {
|
|
lpBuffer[cch] = 0;
|
|
}
|
|
|
|
return cch;
|
|
}
|
|
|
|
|
|
|
|
|
|
}; // end of namespace ShimLib
|