diff --git a/trunk/CHANGELOG.txt b/trunk/CHANGELOG.txt
index 491af158a..5465225fb 100644
--- a/trunk/CHANGELOG.txt
+++ b/trunk/CHANGELOG.txt
@@ -12,6 +12,7 @@ Process Hacker
* Terminator test: TP1a (TP1, alternative method)
* Terminator test: TT1a (TT1, alternative method)
* Thread wait analysis now detects NtQueryObject hangs
+ * Better file object names without KProcessHacker
* Small performance improvements
* FIXED:
* Broken system thread start addresses due to sign-extending
diff --git a/trunk/NProcessHacker/NProcessHacker.vcproj b/trunk/NProcessHacker/NProcessHacker.vcproj
index 53ba1dc9d..c46ce8d8f 100644
--- a/trunk/NProcessHacker/NProcessHacker.vcproj
+++ b/trunk/NProcessHacker/NProcessHacker.vcproj
@@ -184,6 +184,10 @@
RelativePath=".\nph.c"
>
+
+
@@ -218,6 +222,10 @@
RelativePath=".\nph.h"
>
+
+
diff --git a/trunk/NProcessHacker/Release/NProcessHacker.dll b/trunk/NProcessHacker/Release/NProcessHacker.dll
index 9317cd04c..a3b590f0f 100644
Binary files a/trunk/NProcessHacker/Release/NProcessHacker.dll and b/trunk/NProcessHacker/Release/NProcessHacker.dll differ
diff --git a/trunk/NProcessHacker/nativedefs.h b/trunk/NProcessHacker/nativedefs.h
index e5a8eee10..af754046d 100644
--- a/trunk/NProcessHacker/nativedefs.h
+++ b/trunk/NProcessHacker/nativedefs.h
@@ -3,6 +3,15 @@
#include "nph.h"
+typedef enum _OBJECT_INFORMATION_CLASS
+{
+ ObjectBasicInformation,
+ ObjectNameInformation,
+ ObjectTypeInformation,
+ ObjectAllInformation,
+ ObjectDataInformation
+} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;
+
typedef struct _UNICODE_STRING UNICODE_STRING, *PUNICODE_STRING;
typedef struct _CLIENT_ID
@@ -38,6 +47,11 @@ typedef struct _UNICODE_STRING
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
+typedef struct _OBJECT_NAME_INFORMATION
+{
+ UNICODE_STRING Name;
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
#define NTDEVICEIOCONTROLFILE_ARGS \
HANDLE FileHandle, \
HANDLE Event, \
@@ -101,6 +115,17 @@ typedef NTSTATUS (NTAPI *_NtOpenThread)(
NTOPENTHREAD_ARGS
);
+#define NTQUERYOBJECT_ARGS \
+ HANDLE Handle, \
+ OBJECT_INFORMATION_CLASS ObjectInformationClass, \
+ PVOID ObjectInformation, \
+ ULONG Length, \
+ PULONG ReturnLength
+
+typedef NTSTATUS (NTAPI *_NtQueryObject)(
+ NTQUERYOBJECT_ARGS
+ );
+
#define NTREADVIRTUALMEMORY_ARGS \
HANDLE ProcessHandle, \
PVOID BaseAddress, \
diff --git a/trunk/NProcessHacker/nph.c b/trunk/NProcessHacker/nph.c
index 220d55008..0d376d8b0 100644
--- a/trunk/NProcessHacker/nph.c
+++ b/trunk/NProcessHacker/nph.c
@@ -54,6 +54,11 @@ PVOID PhGetProcAddress(PWSTR LibraryName, PSTR ProcName)
return GetProcAddress(GetModuleHandle(LibraryName), ProcName);
}
+VOID PhVoid()
+{
+ return;
+}
+
BOOL WINAPI DllMain(
HINSTANCE hinstDLL,
DWORD fdwReason,
@@ -65,6 +70,8 @@ BOOL WINAPI DllMain(
case DLL_PROCESS_ATTACH:
if (!NT_SUCCESS(PhvInit()))
return FALSE;
+ if (!NT_SUCCESS(PhObjInit()))
+ return FALSE;
if (!NT_SUCCESS(KphInit()))
return FALSE;
diff --git a/trunk/NProcessHacker/nph.h b/trunk/NProcessHacker/nph.h
index 0126f580b..c28aab5ac 100644
--- a/trunk/NProcessHacker/nph.h
+++ b/trunk/NProcessHacker/nph.h
@@ -63,5 +63,6 @@ NPHAPI PVOID PhAlloc(SIZE_T Size);
NPHAPI PVOID PhRealloc(PVOID Memory, SIZE_T Size);
NPHAPI VOID PhFree(PVOID Memory);
PVOID PhGetProcAddress(PWSTR LibraryName, PSTR ProcName);
+NPHAPI VOID PhVoid();
#endif
diff --git a/trunk/NProcessHacker/obj.c b/trunk/NProcessHacker/obj.c
new file mode 100644
index 000000000..7914ba46f
--- /dev/null
+++ b/trunk/NProcessHacker/obj.c
@@ -0,0 +1,151 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 2009 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 .
+ */
+
+#include "obj.h"
+
+ULONG PhpQueryFileObjectThreadStart(
+ PVOID Parameter
+ );
+
+_NtQueryObject NtQueryObject = NULL;
+
+HANDLE QueryFileObjectThreadHandle = NULL;
+PVOID QueryFileObjectFiber = NULL;
+CRITICAL_SECTION QueryFileObjectCs;
+HANDLE QueryFileObjectStartEvent = NULL;
+HANDLE QueryFileObjectCompletedEvent = NULL;
+HANDLE QueryFileObjectFileHandle;
+PH_QUERY_FILE_OBJECT_BUFFER QueryFileObjectBuffer;
+
+NTSTATUS PhObjInit()
+{
+ if (!(NtQueryObject = (_NtQueryObject)
+ PhGetProcAddress(L"ntdll.dll", "NtQueryObject")))
+ return STATUS_PROCEDURE_NOT_FOUND;
+
+ InitializeCriticalSection(&QueryFileObjectCs);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS PhQueryNameFileObject(
+ HANDLE FileHandle,
+ POBJECT_NAME_INFORMATION FileObjectNameInformation,
+ ULONG FileObjectNameInformationLength,
+ PULONG ReturnLength
+ )
+{
+ ULONG waitResult;
+
+ EnterCriticalSection(&QueryFileObjectCs);
+
+ /* Create a query thread if we don't have one. */
+ if (!QueryFileObjectThreadHandle)
+ {
+ ULONG threadId;
+
+ QueryFileObjectThreadHandle = CreateThread(
+ NULL, 0, (LPTHREAD_START_ROUTINE)PhpQueryFileObjectThreadStart, NULL, 0, NULL);
+
+ if (!QueryFileObjectThreadHandle)
+ {
+ LeaveCriticalSection(&QueryFileObjectCs);
+ return STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* Create the events if they don't exist. */
+ if (!QueryFileObjectStartEvent)
+ if (!(QueryFileObjectStartEvent = CreateEvent(NULL, FALSE, FALSE, NULL)))
+ return STATUS_UNSUCCESSFUL;
+ if (!QueryFileObjectCompletedEvent)
+ if (!(QueryFileObjectCompletedEvent = CreateEvent(NULL, FALSE, FALSE, NULL)))
+ return STATUS_UNSUCCESSFUL;
+
+ /* Initialize the work context. */
+ QueryFileObjectFileHandle = FileHandle;
+ QueryFileObjectBuffer.Length = FileObjectNameInformationLength;
+ QueryFileObjectBuffer.Name = FileObjectNameInformation;
+ /* Allow the worker thread to start. */
+ SetEvent(QueryFileObjectStartEvent);
+ /* Wait for the work to complete, with a timeout of 1 second. */
+ waitResult = WaitForSingleObject(QueryFileObjectCompletedEvent, 1000);
+
+ /* Return normally if the work was completed. */
+ if (waitResult == WAIT_OBJECT_0)
+ {
+ NTSTATUS status;
+ ULONG returnLength;
+
+ /* Copy the status information before we leave the critical section. */
+ status = QueryFileObjectBuffer.Status;
+ returnLength = QueryFileObjectBuffer.ReturnLength;
+ LeaveCriticalSection(&QueryFileObjectCs);
+
+ if (ReturnLength)
+ *ReturnLength = returnLength;
+
+ return status;
+ }
+ /* Kill the worker thread if it took too long. */
+ else if (waitResult == WAIT_TIMEOUT)
+ {
+ /* Kill the thread. */
+ if (TerminateThread(QueryFileObjectThreadHandle, 1))
+ {
+ QueryFileObjectThreadHandle = NULL;
+
+ /* Delete the fiber (and free the thread stack). */
+ DeleteFiber(QueryFileObjectFiber);
+ QueryFileObjectFiber = NULL;
+ }
+
+ LeaveCriticalSection(&QueryFileObjectCs);
+ return STATUS_UNSUCCESSFUL;
+ }
+}
+
+ULONG PhpQueryFileObjectThreadStart(
+ PVOID Parameter
+ )
+{
+ QueryFileObjectFiber = ConvertThreadToFiber(Parameter);
+
+ while (TRUE)
+ {
+ /* Wait for work. */
+ if (WaitForSingleObject(QueryFileObjectStartEvent, INFINITE) != WAIT_OBJECT_0)
+ continue;
+
+ QueryFileObjectBuffer.Status = NtQueryObject(
+ QueryFileObjectFileHandle,
+ ObjectNameInformation,
+ QueryFileObjectBuffer.Name,
+ QueryFileObjectBuffer.Length,
+ &QueryFileObjectBuffer.ReturnLength
+ );
+
+ /* Work done. */
+ SetEvent(QueryFileObjectCompletedEvent);
+ }
+
+ return 0;
+}
diff --git a/trunk/NProcessHacker/obj.h b/trunk/NProcessHacker/obj.h
new file mode 100644
index 000000000..49a26c473
--- /dev/null
+++ b/trunk/NProcessHacker/obj.h
@@ -0,0 +1,45 @@
+/*
+ * Process Hacker Library
+ *
+ * Copyright (C) 2009 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 .
+ */
+
+#ifndef _OBJ_H
+#define _OBJ_H
+
+#include "nph.h"
+#include "nativedefs.h"
+
+typedef struct _PH_QUERY_FILE_OBJECT_BUFFER
+{
+ NTSTATUS Status;
+ ULONG Length;
+ ULONG ReturnLength;
+ POBJECT_NAME_INFORMATION Name;
+} PH_QUERY_FILE_OBJECT_BUFFER, *PPH_QUERY_FILE_OBJECT_BUFFER;
+
+NTSTATUS PhObjInit();
+
+NPHAPI NTSTATUS PhQueryNameFileObject(
+ HANDLE FileHandle,
+ POBJECT_NAME_INFORMATION FileObjectNameInformation,
+ ULONG FileObjectNameInformationLength,
+ PULONG ReturnLength
+ );
+
+#endif
diff --git a/trunk/ProcessHacker.Native/Api/Extensions.cs b/trunk/ProcessHacker.Native/Api/Extensions.cs
index c6e7aea9d..c2dd19390 100644
--- a/trunk/ProcessHacker.Native/Api/Extensions.cs
+++ b/trunk/ProcessHacker.Native/Api/Extensions.cs
@@ -61,6 +61,52 @@ namespace ProcessHacker.Native.Api
}
}
+ private static string GetObjectNameNt(ProcessHandle process, IntPtr handle, GenericHandle dupHandle)
+ {
+ int retLength;
+ int baseAddress = 0;
+
+ if (KProcessHacker.Instance != null)
+ {
+ KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectNameInformation,
+ IntPtr.Zero, 0, out retLength, out baseAddress);
+ }
+ else
+ {
+ Win32.NtQueryObject(dupHandle, ObjectInformationClass.ObjectNameInformation,
+ IntPtr.Zero, 0, out retLength);
+ }
+
+ if (retLength > 0)
+ {
+ using (MemoryAlloc oniMem = new MemoryAlloc(retLength))
+ {
+ if (KProcessHacker.Instance != null)
+ {
+ if (KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectNameInformation,
+ oniMem, oniMem.Size, out retLength, out baseAddress) >= NtStatus.Error)
+ throw new Exception("ZwQueryObject failed.");
+ }
+ else
+ {
+ if (Win32.NtQueryObject(dupHandle, ObjectInformationClass.ObjectNameInformation,
+ oniMem, oniMem.Size, out retLength) >= NtStatus.Error)
+ throw new Exception("NtQueryObject failed.");
+ }
+
+ var oni = oniMem.ReadStruct();
+ var str = oni.Name;
+
+ if (KProcessHacker.Instance != null)
+ str.Buffer = str.Buffer.Increment(-baseAddress + oniMem);
+
+ return str.Read();
+ }
+ }
+
+ throw new Exception("NtQueryObject failed.");
+ }
+
public static ObjectInformation GetHandleInfo(this SystemHandleInformation thisHandle)
{
using (ProcessHandle process = new ProcessHandle(thisHandle.ProcessId,
@@ -147,77 +193,65 @@ namespace ProcessHacker.Native.Api
}
}
- if (KProcessHacker.Instance != null && info.TypeName == "File")
+ // Get the object's name. If the object is a file we must take special
+ // precautions so that we don't hang.
+ if (info.TypeName == "File")
{
- // use KProcessHacker for files
- info.OrigName = KProcessHacker.Instance.GetFileObjectName(thisHandle);
- }
- else if (info.TypeName == "File" && (int)thisHandle.GrantedAccess == 0x0012019f)
- {
- // KProcessHacker not available, fall back to using hack (i.e. not querying the name at all)
- }
- else
- {
- int baseAddress = 0;
-
if (KProcessHacker.Instance != null)
{
- KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectNameInformation,
- IntPtr.Zero, 0, out retLength, out baseAddress);
+ // Use KProcessHacker for files to avoid hangs.
+ info.OrigName = KProcessHacker.Instance.GetFileObjectName(thisHandle);
}
else
{
- Win32.NtQueryObject(objectHandle, ObjectInformationClass.ObjectNameInformation,
- IntPtr.Zero, 0, out retLength);
- }
-
- if (retLength > 0)
- {
- using (MemoryAlloc oniMem = new MemoryAlloc(retLength))
+ try
{
- if (KProcessHacker.Instance != null)
+ // Use NProcessHacker.
+ using (MemoryAlloc oniMem = new MemoryAlloc(0x4000))
{
- if (KProcessHacker.Instance.ZwQueryObject(process, handle, ObjectInformationClass.ObjectNameInformation,
- oniMem, oniMem.Size, out retLength, out baseAddress) >= NtStatus.Error)
- throw new Exception("ZwQueryObject failed.");
+ if (NProcessHacker.PhQueryNameFileObject(
+ objectHandle, oniMem, oniMem.Size, out retLength) >= NtStatus.Error)
+ throw new Exception("PhQueryNameFileObject failed.");
+
+ var oni = oniMem.ReadStruct();
+
+ info.OrigName = oni.Name.Read();
}
- else
- {
- if (Win32.NtQueryObject(objectHandle, ObjectInformationClass.ObjectNameInformation,
- oniMem, oniMem.Size, out retLength) >= NtStatus.Error)
- throw new Exception("NtQueryObject failed.");
- }
-
- var oni = oniMem.ReadStruct();
- var str = oni.Name;
-
- if (KProcessHacker.Instance != null)
- str.Buffer = str.Buffer.Increment(-baseAddress + oniMem);
-
- info.OrigName = str.Read();
+ }
+ catch (DllNotFoundException)
+ {
+ // KProcessHacker and NProcessHacker not available. Fall back to using hack
+ // (i.e. not querying the name at all if the access is 0x0012019f)
+ if ((int)thisHandle.GrantedAccess != 0x0012019f)
+ info.OrigName = GetObjectNameNt(process, handle, objectHandle);
}
}
}
+ else
+ {
+ // Not a file. Query the object normally.
+ info.OrigName = GetObjectNameNt(process, handle, objectHandle);
+ }
- // get a better name for the handle
+ // Get a better name for the handle.
try
{
switch (info.TypeName)
{
case "File":
- // resolves \Device\Harddisk1 into C:, for example
+ // Resolves \Device\Harddisk1 into C:, for example.
info.BestName = FileUtils.DeviceFileNameToDos(info.OrigName);
break;
case "Key":
- string hklmString = "\\registry\\machine";
- string hkcrString = "\\registry\\machine\\software\\classes";
+ const string hklmString = "\\registry\\machine";
+ const string hkcrString = "\\registry\\machine\\software\\classes";
string hkcuString = "\\registry\\user\\" +
System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString().ToLower();
string hkcucrString = "\\registry\\user\\" +
System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString().ToLower() + "_classes";
- string hkuString = "\\registry\\user";
+ const string hkuString = "\\registry\\user";
if (info.OrigName.ToLower().StartsWith(hkcrString))
info.BestName = "HKCR" + info.OrigName.Substring(hkcrString.Length);
diff --git a/trunk/ProcessHacker/Program/NProcessHacker.cs b/trunk/ProcessHacker.Native/NProcessHacker.cs
similarity index 67%
rename from trunk/ProcessHacker/Program/NProcessHacker.cs
rename to trunk/ProcessHacker.Native/NProcessHacker.cs
index 0c8f85e6d..144663b7d 100644
--- a/trunk/ProcessHacker/Program/NProcessHacker.cs
+++ b/trunk/ProcessHacker.Native/NProcessHacker.cs
@@ -24,8 +24,9 @@ using System.Runtime.InteropServices;
using System.Windows.Forms;
using ProcessHacker.Native;
using ProcessHacker.Native.Api;
+using System;
-namespace ProcessHacker
+namespace ProcessHacker.Native
{
public class NProcessHacker
{
@@ -55,22 +56,33 @@ namespace ProcessHacker
[DllImport("nprocesshacker.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern NtStatus PhpQueryProcessWs(
- int ProcessHandle,
- WS_INFORMATION_CLASS WsInformationClass,
- out int WsInformation,
- int WsInformationLength,
- out int ReturnLength
+ [In] IntPtr ProcessHandle,
+ [In] WS_INFORMATION_CLASS WsInformationClass,
+ [Out] out int WsInformation,
+ [In] int WsInformationLength,
+ [Out] out int ReturnLength
);
[DllImport("nprocesshacker.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern NtStatus PhpQueryProcessWs(
- int ProcessHandle,
- WS_INFORMATION_CLASS WsInformationClass,
- out WS_ALL_COUNTS WsInformation,
- int WsInformationLength,
- out int ReturnLength
+ [In] IntPtr ProcessHandle,
+ [In] WS_INFORMATION_CLASS WsInformationClass,
+ [Out] out WS_ALL_COUNTS WsInformation,
+ [In] int WsInformationLength,
+ [Out] out int ReturnLength
);
+ [DllImport("nprocesshacker.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
+ public static extern NtStatus PhQueryNameFileObject(
+ [In] IntPtr FileHandle,
+ [In] IntPtr FileObjectNameInformation,
+ [In] int FileObjectNameInformationLength,
+ [Out] [Optional] out int ReturnLength
+ );
+
+ [DllImport("nprocesshacker.dll", CallingConvention = CallingConvention.Cdecl)]
+ public static extern void PhVoid();
+
[DllImport("nprocesshacker.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern VerifyResult PhvVerifyFile(string FileName);
}
diff --git a/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj b/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj
index 447dc3acb..22056976b 100644
--- a/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj
+++ b/trunk/ProcessHacker.Native/ProcessHacker.Native.csproj
@@ -59,6 +59,7 @@
+
diff --git a/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs b/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs
index f768a82c1..3c3f5ba60 100644
--- a/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs
+++ b/trunk/ProcessHacker/Components/ProcessTree/ProcessNode.cs
@@ -25,6 +25,7 @@ using System.Collections.Generic;
using System.Drawing;
using Aga.Controls.Tree;
using ProcessHacker.Common;
+using ProcessHacker.Native;
using ProcessHacker.Native.Api;
using ProcessHacker.Native.Objects;
using ProcessHacker.Native.Security;
diff --git a/trunk/ProcessHacker/ProcessHacker.csproj b/trunk/ProcessHacker/ProcessHacker.csproj
index 627f7f613..96813b1a1 100644
--- a/trunk/ProcessHacker/ProcessHacker.csproj
+++ b/trunk/ProcessHacker/ProcessHacker.csproj
@@ -705,7 +705,6 @@
-