Files
mirror-processhacker/2.x/trunk/ProcessHacker/procdb.c
T
wj32 514108d177 * switched to mxml opaque nodes to preserve whitespace
* settings.xml file looks better now due to some added whitespace

git-svn-id: svn://svn.code.sf.net/p/processhacker/code@3363 21ef857c-d57f-4fe0-8362-d861dc6d29cd
2010-07-15 02:13:42 +00:00

581 lines
16 KiB
C

/*
* Process Hacker -
* process database
*
* Copyright (C) 2010 wj32
*
* This file is part of Process Hacker.
*
* Process Hacker is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Process Hacker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Process Hacker. If not, see <http://www.gnu.org/licenses/>.
*/
#define PROCDB_PRIVATE
#include <phapp.h>
#include <settings.h>
#include <md5.h>
#include "mxml/mxml.h"
INT NTAPI PhpProcDbByFileNameCompareFunction(
__in PPH_AVL_LINKS Links1,
__in PPH_AVL_LINKS Links2
);
INT NTAPI PhpProcDbByHashCompareFunction(
__in PPH_AVL_LINKS Links1,
__in PPH_AVL_LINKS Links2
);
BOOLEAN PhProcDbInitialized = FALSE;
LIST_ENTRY PhProcDbListHead;
PH_AVL_TREE PhProcDbByFileName = PH_AVL_TREE_INIT(PhpProcDbByFileNameCompareFunction);
PH_AVL_TREE PhProcDbByHash = PH_AVL_TREE_INIT(PhpProcDbByHashCompareFunction);
PH_QUEUED_LOCK PhProcDbLock = PH_QUEUED_LOCK_INIT;
VOID PhProcDbInitialization()
{
InitializeListHead(&PhProcDbListHead);
if (PhCsEnableProcDb)
{
PhProcDbFileName = PhGetKnownLocation(CSIDL_APPDATA, L"\\Process Hacker 2\\procdb.xml");
if (PhProcDbFileName)
{
if (!NT_SUCCESS(PhLoadProcDb(PhProcDbFileName->Buffer)))
{
static PWSTR safeProcesses[] =
{
L"audiodg.exe", L"conhost.exe",
L"csrss.exe", L"dwm.exe", L"logonui.exe", L"lsass.exe", L"lsm.exe",
L"ntkrnlpa.exe", L"ntoskrnl.exe",
L"services.exe", L"smss.exe", L"spoolsv.exe", L"svchost.exe", L"taskeng.exe",
L"taskhost.exe", L"wininit.exe", L"winlogon.exe"
};
ULONG i;
PPH_STRING systemDirectory;
systemDirectory = PhGetSystemDirectory();
// Probably the first time the DB feature has been activated. Add some basic items.
for (i = 0; i < sizeof(safeProcesses) / sizeof(PWSTR); i++)
{
PPH_PROCDB_ENTRY entry;
PPH_STRING fileName;
fileName = PhConcatStrings(3, systemDirectory->Buffer, L"\\", safeProcesses[i]);
entry = PhAddProcDbEntry(fileName, TRUE, FALSE);
entry->Flags |= PH_PROCDB_ENTRY_SAFE;
PhDereferenceObject(fileName);
if (entry)
PhDereferenceProcDbEntry(entry);
}
PhDereferenceObject(systemDirectory);
}
}
PhProcDbInitialized = TRUE;
}
}
INT NTAPI PhpProcDbByFileNameCompareFunction(
__in PPH_AVL_LINKS Links1,
__in PPH_AVL_LINKS Links2
)
{
PPH_PROCDB_ENTRY entry1 = CONTAINING_RECORD(Links1, PH_PROCDB_ENTRY, Links);
PPH_PROCDB_ENTRY entry2 = CONTAINING_RECORD(Links2, PH_PROCDB_ENTRY, Links);
return PhStringCompare(entry1->FileName, entry2->FileName, TRUE);
}
INT NTAPI PhpProcDbByHashCompareFunction(
__in PPH_AVL_LINKS Links1,
__in PPH_AVL_LINKS Links2
)
{
PPH_PROCDB_ENTRY entry1 = CONTAINING_RECORD(Links1, PH_PROCDB_ENTRY, Links);
PPH_PROCDB_ENTRY entry2 = CONTAINING_RECORD(Links2, PH_PROCDB_ENTRY, Links);
return memcmp(entry1->Hash, entry2->Hash, PH_PROCDB_HASH_SIZE);
}
PPH_PROCDB_ENTRY PhpCreateProcDbEntry(
__in_opt PPH_STRING FileName,
__in_opt PUCHAR Hash
)
{
PPH_PROCDB_ENTRY entry;
entry = PhAllocate(sizeof(PH_PROCDB_ENTRY));
memset(entry, 0, sizeof(PH_PROCDB_ENTRY));
entry->RefCount = 1;
if (FileName)
{
PhReferenceObject(FileName);
entry->FileName = FileName;
}
if (Hash)
{
memcpy(entry->Hash, Hash, PH_PROCDB_HASH_SIZE);
}
return entry;
}
VOID PhReferenceProcDbEntry(
__inout PPH_PROCDB_ENTRY Entry
)
{
_InterlockedIncrement(&Entry->RefCount);
}
VOID PhDereferenceProcDbEntry(
__inout PPH_PROCDB_ENTRY Entry
)
{
if (_InterlockedDecrement(&Entry->RefCount) == 0)
{
if (Entry->FileName) PhDereferenceObject(Entry->FileName);
PhFree(Entry);
}
}
__assumeLocked BOOLEAN PhpLinkProcDbEntry(
__inout PPH_PROCDB_ENTRY Entry
)
{
PPH_AVL_LINKS links;
InsertTailList(&PhProcDbListHead, &Entry->ListEntry);
if (Entry->Flags & PH_PROCDB_ENTRY_MATCH_HASH)
links = PhAvlTreeAdd(&PhProcDbByHash, &Entry->Links);
else
links = PhAvlTreeAdd(&PhProcDbByFileName, &Entry->Links);
if (!links)
return TRUE;
else
return FALSE; // entry already exists
}
__assumeLocked VOID PhpUnlinkProcDbEntry(
__inout PPH_PROCDB_ENTRY Entry
)
{
if (Entry->Flags & PH_PROCDB_ENTRY_MATCH_HASH)
PhAvlTreeRemove(&PhProcDbByHash, &Entry->Links);
else
PhAvlTreeRemove(&PhProcDbByFileName, &Entry->Links);
RemoveEntryList(&Entry->ListEntry);
}
PPH_PROCDB_ENTRY PhpLookupProcDbEntryByFileName(
__in PPH_STRING FileName
)
{
PPH_PROCDB_ENTRY entry = NULL;
PH_PROCDB_ENTRY lookupEntry;
PPH_AVL_LINKS links;
lookupEntry.FileName = FileName;
PhAcquireQueuedLockShared(&PhProcDbLock);
links = PhAvlTreeSearch(&PhProcDbByFileName, &lookupEntry.Links);
if (links)
{
entry = CONTAINING_RECORD(links, PH_PROCDB_ENTRY, Links);
PhReferenceProcDbEntry(entry);
}
PhReleaseQueuedLockShared(&PhProcDbLock);
return entry;
}
PPH_PROCDB_ENTRY PhAddProcDbEntry(
__in PPH_STRING FileName,
__in BOOLEAN SaveHash,
__in BOOLEAN LookupByHash
)
{
PPH_PROCDB_ENTRY entry;
entry = PhpCreateProcDbEntry(FileName, NULL);
if (SaveHash)
{
if (NT_SUCCESS(PhMd5File(FileName->Buffer, entry->Hash)))
entry->Flags |= PH_PROCDB_ENTRY_HASH_VALID;
}
if (LookupByHash)
{
if (entry->Flags & PH_PROCDB_ENTRY_HASH_VALID)
entry->Flags |= PH_PROCDB_ENTRY_MATCH_HASH;
}
PhAcquireQueuedLockExclusive(&PhProcDbLock);
if (!PhpLinkProcDbEntry(entry))
{
PhReleaseQueuedLockExclusive(&PhProcDbLock);
PhDereferenceProcDbEntry(entry);
return NULL;
}
PhReleaseQueuedLockExclusive(&PhProcDbLock);
// Add a reference for the entry being in the global list.
PhReferenceProcDbEntry(entry);
return entry;
}
VOID PhRemoveProcDbEntry(
__in PPH_PROCDB_ENTRY Entry
)
{
PhAcquireQueuedLockExclusive(&PhProcDbLock);
PhpUnlinkProcDbEntry(Entry);
PhReleaseQueuedLockExclusive(&PhProcDbLock);
// Remove the reference added in PhAddProcDbEntry.
PhDereferenceProcDbEntry(Entry);
}
PPH_PROCDB_ENTRY PhLookupProcDbEntry(
__in PPH_STRING FileName
)
{
return PhpLookupProcDbEntryByFileName(FileName);
}
mxml_type_t PhpProcDbLoadCallback(
__in mxml_node_t *node
)
{
return MXML_OPAQUE;
}
NTSTATUS PhLoadProcDb(
__in PWSTR FileName
)
{
NTSTATUS status;
HANDLE fileHandle;
mxml_node_t *topNode;
mxml_node_t *currentNode;
status = PhCreateFileWin32(
&fileHandle,
FileName,
FILE_GENERIC_READ,
0,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(status))
return status;
topNode = mxmlLoadFd(NULL, fileHandle, PhpProcDbLoadCallback);
NtClose(fileHandle);
if (!topNode)
return STATUS_FILE_CORRUPT_ERROR;
if (!topNode->child)
{
mxmlDelete(topNode);
return STATUS_FILE_CORRUPT_ERROR;
}
currentNode = topNode->child;
while (currentNode)
{
if (currentNode->type == MXML_ELEMENT)
{
mxml_node_t *childNode;
PPH_STRING fileName = NULL;
PPH_STRING hashString = NULL;
PPH_STRING flagsString = NULL;
PPH_STRING desiredPriorityWin32String = NULL;
UCHAR hash[PH_PROCDB_HASH_SIZE];
ULONG64 flags;
ULONG64 desiredPriorityWin32 = 0;
PPH_PROCDB_ENTRY entry;
childNode = currentNode->child;
while (childNode)
{
if (STR_IEQUAL(childNode->value.element.name, "filename"))
{
fileName = PhGetOpaqueXmlNodeText(childNode);
}
else if (STR_IEQUAL(childNode->value.element.name, "hash"))
{
hashString = PhGetOpaqueXmlNodeText(childNode);
}
else if (STR_IEQUAL(childNode->value.element.name, "flags"))
{
flagsString = PhGetOpaqueXmlNodeText(childNode);
}
else if (STR_IEQUAL(childNode->value.element.name, "desiredprioritywin32"))
{
desiredPriorityWin32String = PhGetOpaqueXmlNodeText(childNode);
}
childNode = childNode->next;
}
if (!flagsString)
goto ContinueLoop;
if (!PhStringToInteger64(&flagsString->sr, 16, &flags))
goto ContinueLoop;
if (hashString)
{
if (hashString->Length / sizeof(WCHAR) != PH_PROCDB_HASH_SIZE * 2)
goto ContinueLoop;
PhHexStringToBuffer(hashString, hash);
}
if (desiredPriorityWin32String)
PhStringToInteger64(&desiredPriorityWin32String->sr, 16, &desiredPriorityWin32);
if (((ULONG)flags & PH_PROCDB_ENTRY_MATCH_HASH) && !hashString)
goto ContinueLoop;
else if (!((ULONG)flags & PH_PROCDB_ENTRY_MATCH_HASH) && !fileName)
goto ContinueLoop;
entry = PhpCreateProcDbEntry(fileName, hash);
entry->Flags = (ULONG)flags;
PhReferenceObject(fileName);
entry->FileName = fileName;
memcpy(entry->Hash, hash, PH_PROCDB_HASH_SIZE);
entry->DesiredPriorityWin32 = (ULONG)desiredPriorityWin32;
PhAcquireQueuedLockExclusive(&PhProcDbLock);
PhpLinkProcDbEntry(entry);
PhReleaseQueuedLockExclusive(&PhProcDbLock);
ContinueLoop:
if (fileName)
PhDereferenceObject(fileName);
if (hashString)
PhDereferenceObject(hashString);
if (flagsString)
PhDereferenceObject(flagsString);
if (desiredPriorityWin32String)
PhDereferenceObject(desiredPriorityWin32String);
}
currentNode = currentNode->next;
}
mxmlDelete(topNode);
return STATUS_SUCCESS;
}
NTSTATUS PhSaveProcDb(
__in PWSTR FileName
)
{
NTSTATUS status;
HANDLE fileHandle;
mxml_node_t *topNode;
PLIST_ENTRY listEntry;
topNode = mxmlNewElement(MXML_NO_PARENT, "database");
PhAcquireQueuedLockShared(&PhProcDbLock);
listEntry = PhProcDbListHead.Flink;
while (listEntry != &PhProcDbListHead)
{
PPH_PROCDB_ENTRY entry;
mxml_node_t *processNode;
mxml_node_t *childNode;
PPH_ANSI_STRING ansiString;
entry = CONTAINING_RECORD(listEntry, PH_PROCDB_ENTRY, ListEntry);
// Don't save the entry if we don't need to.
if (!(entry->Flags & PH_PROCDB_ENTRY_SAFE) && !(entry->Flags & PH_PROCDB_ENTRY_ENFORCE_PRIORITY))
{
goto ContinueLoop;
}
// Create the process element.
processNode = mxmlNewElement(topNode, "process");
// filename
if (entry->FileName)
{
childNode = mxmlNewElement(processNode, "filename");
ansiString = PhCreateAnsiStringFromUnicodeEx(entry->FileName->Buffer, entry->FileName->Length);
mxmlNewOpaque(childNode, ansiString->Buffer);
PhDereferenceObject(ansiString);
}
// hash
if (entry->Flags & PH_PROCDB_ENTRY_HASH_VALID)
{
PPH_STRING hashString;
childNode = mxmlNewElement(processNode, "hash");
hashString = PhBufferToHexString(entry->Hash, PH_PROCDB_HASH_SIZE);
ansiString = PhCreateAnsiStringFromUnicodeEx(hashString->Buffer, hashString->Length);
PhDereferenceObject(hashString);
mxmlNewOpaque(childNode, ansiString->Buffer);
PhDereferenceObject(ansiString);
}
// flags
{
PPH_STRING flagsString;
childNode = mxmlNewElement(processNode, "flags");
flagsString = PhIntegerToString64((ULONG64)entry->Flags, 16, FALSE);
ansiString = PhCreateAnsiStringFromUnicodeEx(flagsString->Buffer, flagsString->Length);
PhDereferenceObject(flagsString);
mxmlNewOpaque(childNode, ansiString->Buffer);
PhDereferenceObject(ansiString);
}
// desiredprioritywin32
{
PPH_STRING desiredPriorityWin32String;
childNode = mxmlNewElement(processNode, "desiredprioritywin32");
desiredPriorityWin32String = PhIntegerToString64((ULONG64)entry->DesiredPriorityWin32, 16, FALSE);
ansiString = PhCreateAnsiStringFromUnicodeEx(desiredPriorityWin32String->Buffer, desiredPriorityWin32String->Length);
PhDereferenceObject(desiredPriorityWin32String);
mxmlNewOpaque(childNode, ansiString->Buffer);
PhDereferenceObject(ansiString);
}
ContinueLoop:
listEntry = listEntry->Flink;
}
PhReleaseQueuedLockShared(&PhProcDbLock);
// Create the directory if it does not exist.
{
PPH_STRING fullPath;
ULONG indexOfFileName;
PPH_STRING directoryName;
fullPath = PhGetFullPath(FileName, &indexOfFileName);
if (fullPath)
{
if (indexOfFileName != -1)
{
directoryName = PhSubstring(fullPath, 0, indexOfFileName);
SHCreateDirectoryEx(NULL, directoryName->Buffer, NULL);
PhDereferenceObject(directoryName);
}
PhDereferenceObject(fullPath);
}
}
status = PhCreateFileWin32(
&fileHandle,
FileName,
FILE_GENERIC_WRITE,
0,
FILE_SHARE_READ,
FILE_OVERWRITE_IF,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
if (!NT_SUCCESS(status))
{
mxmlDelete(topNode);
return status;
}
mxmlSaveFd(topNode, fileHandle, MXML_NO_CALLBACK);
mxmlDelete(topNode);
NtClose(fileHandle);
return STATUS_SUCCESS;
}
VOID PhToggleSafeProcess(
__in PPH_PROCESS_ITEM ProcessItem
)
{
PPH_PROCDB_ENTRY entry;
if (!ProcessItem->FileName)
return;
entry = PhLookupProcDbEntry(ProcessItem->FileName);
if (entry)
{
if (!(entry->Flags & PH_PROCDB_ENTRY_SAFE))
{
// Update the hash.
if (NT_SUCCESS(PhMd5File(ProcessItem->FileName->Buffer, entry->Hash)))
{
entry->Flags |= PH_PROCDB_ENTRY_HASH_VALID;
}
else
{
entry->Flags &= ~PH_PROCDB_ENTRY_HASH_VALID;
}
}
else
{
entry->Flags &= ~PH_PROCDB_ENTRY_SAFE;
PhDereferenceProcDbEntry(entry);
entry = NULL; // prevent the entry from getting marked as safe later
}
}
else
{
entry = PhAddProcDbEntry(ProcessItem->FileName, TRUE, FALSE);
}
if (entry)
{
entry->Flags |= PH_PROCDB_ENTRY_SAFE;
PhDereferenceProcDbEntry(entry);
}
}