I'm enumerating all DLL modules in a process, but there are two issues:
#1 Duplicate records
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume4\AC\Debug\AC.exe
\Device\HarddiskVolume2\Windows\SysWOW64\KernelBase.dll
\Device\HarddiskVolume2\Windows\SysWOW64\KernelBase.dll
\Device\HarddiskVolume2\Windows\SysWOW64\KernelBase.dll
\Device\HarddiskVolume2\Windows\SysWOW64\KernelBase.dll
\Device\HarddiskVolume2\Windows\SysWOW64\KernelBase.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\SysWOW64\kernel32.dll
\Device\HarddiskVolume2\Windows\System32\wow64cpu.dll
\Device\HarddiskVolume2\Windows\System32\wow64cpu.dll
\Device\HarddiskVolume2\Windows\System32\wow64cpu.dll
\Device\HarddiskVolume2\Windows\System32\wow64cpu.dll
\Device\HarddiskVolume2\Windows\System32\wow64cpu.dll
\Device\HarddiskVolume2\Windows\System32\wow64cpu.dll
\Device\HarddiskVolume2\Windows\System32\wow64cpu.dll
\Device\HarddiskVolume2\Windows\SysWOW64\ntdll.dll
\Device\HarddiskVolume2\Windows\SysWOW64\ntdll.dll
\Device\HarddiskVolume2\Windows\SysWOW64\ntdll.dll
\Device\HarddiskVolume2\Windows\SysWOW64\ntdll.dll
#2 NtFreeVirtualMemory doesn't deallocate the whole space it allocated.
Before NtFreeVirtualMemory call
After it. You can see how it left all those regions.
Code
bool enumerate_modules()
{
MEMORY_BASIC_INFORMATION mbi{};
const std::size_t buffer_size = 512;
void* base_address = 0;
std::size_t region_size = buffer_size;
NTSTATUS status = NtAllocateVirtualMemory(NtCurrentProcess(), &base_address, 0, ®ion_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
if (!NT_SUCCESS(status))
return false;
const auto& section_name = reinterpret_cast<PMEMORY_SECTION_NAME>(base_address);
SYSTEM_INFO si{};
GetSystemInfo(&si);
auto* min_address = static_cast<std::uint8_t*>(si.lpMinimumApplicationAddress);
auto* max_address = static_cast<std::uint8_t*>(si.lpMaximumApplicationAddress);
while (min_address < max_address)
{
status = NtQueryVirtualMemory(NtCurrentProcess(), min_address, MemoryBasicInformation, &mbi, sizeof mbi, nullptr);
if (!NT_SUCCESS(status))
break;
if (mbi.Type == MEM_IMAGE)
{
status = NtQueryVirtualMemory(NtCurrentProcess(), min_address, MemoryMappedFilenameInformation, section_name, buffer_size, nullptr);
if (!NT_SUCCESS(status))
break;
const auto& dll = section_name->SectionFileName.Buffer;
printf("%S\n", dll);
}
min_address += mbi.RegionSize;
}
region_size = 0;
status = NtFreeVirtualMemory(NtCurrentProcess(), &base_address, ®ion_size, MEM_RELEASE);
if (!NT_SUCCESS(status))
return false;
return true;
}
"I'm enumerating all DLL modules in a process"
Well, you're not. That's what EnumProcessModules does. You enumerate memory regions.
As for the remaining pages; you didn't DECOMMIT them.
Note that these Nt functions are very low-level and assume that you know exactly what you are doing.
Related
The driver I wrote is taken from the Windows Kernel Programming book but for some reason it just doesn't work.
Here is my driver entry:
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
auto status = STATUS_SUCCESS;
InitializeListHead(&g_Globals.ItemsHead);
g_Globals.Mutex.Init();
PDEVICE_OBJECT DeviceObject = nullptr;
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\sysmon");
bool symLinkCreated = false;
do {
UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\sysmon");
status = IoCreateDevice(DriverObject, 0, &devName,
FILE_DEVICE_UNKNOWN, 0, TRUE, &DeviceObject);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to create device (0x%08X)\n",
status));
break;
}
DeviceObject->Flags |= DO_DIRECT_IO;
status = IoCreateSymbolicLink(&symLink, &devName);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to create sym link (0x%08X)\n",
status));
break;
}
symLinkCreated = true;
// register for process notifications
status = PsSetCreateProcessNotifyRoutineEx(OnProcessNotify, FALSE);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to register process callback (0x%08X)\n",
status));
break;
}
status = PsSetCreateThreadNotifyRoutine(OnThreadNotify);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to set thread callbacks (status=%08X)\n", status)\
);
break;
}
} while (false);
if (!NT_SUCCESS(status)) {
if (symLinkCreated)
IoDeleteSymbolicLink(&symLink);
if (DeviceObject)
IoDeleteDevice(DeviceObject);
}
DriverObject->DriverUnload = SysMonUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SysMonCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = SysMonRead;
return status;
}
now this function works well but i'm pretty sure the problem lies around here.
here is the SysMonCrearteClose function which also fails (I must say that I don't fully understand what I need to code in this function however even without calling it the driver failes, I'm sure there is a problem in here as well):
NTSTATUS SysMonCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
now here is the SysMonUnload function:
void SysMonUnload(PDRIVER_OBJECT DriverObject) {
// unregister process notifications
PsSetCreateProcessNotifyRoutineEx(OnProcessNotify, TRUE);
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\sysmon");
IoDeleteSymbolicLink(&symLink);
IoDeleteDevice(DriverObject->DeviceObject);
// free remaining items
while (!IsListEmpty(&g_Globals.ItemsHead)) {
auto entry = RemoveHeadList(&g_Globals.ItemsHead);
ExFreePool(CONTAINING_RECORD(entry, FullItem<ItemHeader>, Entry));
}
}
Here is the last function that matters, the SysMonRead function:
NTSTATUS SysMonRead(PDEVICE_OBJECT, PIRP Irp) {
UNREFERENCED_PARAMETER(Irp);
auto stack = IoGetCurrentIrpStackLocation(Irp);
auto len = stack->Parameters.Read.Length;
auto status = STATUS_SUCCESS;
auto count = 0;
NT_ASSERT(Irp->MdlAddress); // we're using Direct I/O
auto buffer = (UCHAR*)MmGetSystemAddressForMdlSafe(Irp->MdlAddress,
NormalPagePriority);
if (!buffer) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
else {
AutoLock lock(g_Globals.Mutex);
while (true) {
if (IsListEmpty(&g_Globals.ItemsHead)) // can also check g_Globals.ItemCount
break;
auto entry = RemoveHeadList(&g_Globals.ItemsHead);
auto info = CONTAINING_RECORD(entry, FullItem<ItemHeader>, Entry);
auto size = info->Data.Size;
if (len < size) {
// user's buffer is full, insert item back
InsertHeadList(&g_Globals.ItemsHead, entry);
break;
}
g_Globals.ItemCount--;
::memcpy(buffer, &info->Data, size);
len -= size;
buffer += size;
count += size;
// free data after copy
ExFreePool(info);
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = count;
IoCompleteRequest(Irp, 0);
return status;
}
throughout the program I allocate memory that is freed in the unload routine and open handles to files and mutexes which I also close. Whenever I load this driver the functionality intended is working properly (when I write to a file it works, the CreateClose function does not work) however when I unload the driver the system crashes. I used windbg to try and see what the issue is to no avail, here is the error message:
*** Fatal System Error: 0x000000ce
(0xFFFFF80260A71390,0x0000000000000010,0xFFFFF80260A71390,0x0000000000000000)
Driver at fault: ProcessThreadMonitorDriver.sys.
Break instruction exception - code 80000003 (first chance)
nt_exe!DbgBreakPointWithStatus:
I noticed sometimes it said something about an invalid memory access but it was inconsistent and unclear.
So I'm attempting to make file accesible only for certain process, firstly by finding it via this function:
bool GetProcessSid(PSID* pSID)
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE)
{
while(Process32Next(snapshot, &entry) == TRUE)
{
const char* process_name = "testfileaccess2.exe";
std::string t(process_name);
std::wstring w_process_name(t.begin(), t.end());
if (w_process_name.compare(entry.szExeFile)== 0)
{
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);
//...
GetSecurityInfo(hProcess,SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION,pSID,NULL,NULL,NULL,NULL);
//getsecurityinfo(hprocess,SE_SERVICE) for service
CloseHandle(hProcess);
return TRUE;
}
}
}
return FALSE;
}
Works fine, always finds process if its alive and returns pSID.
Then I create file like this:
PACL pNewDACL = NULL;
PSID process_with_access = NULL;
PSID current_user = NULL;
DWORD sid_size = SECURITY_MAX_SID_SIZE;
SID everyone_sid;
DWORD dwRes;
if (CreateWellKnownSid(WinWorldSid, NULL, &everyone_sid, &sid_size) ==
FALSE) {
throw std::runtime_error("CreateWellKnownSid() failed: " +
std::to_string(GetLastError()));
}
GetProcessSid(&process_with_access);
GetCurrentUserSid(¤t_user);
EXPLICIT_ACCESSA ea[2];
ZeroMemory(&ea, 2 * sizeof(EXPLICIT_ACCESSA));
ea[0].grfAccessPermissions = ACCESS_SYSTEM_SECURITY | READ_CONTROL | WRITE_DAC | GENERIC_ALL;
ea[0].grfAccessMode = DENY_ACCESS;
ea[0].grfInheritance = NO_INHERITANCE;
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[0].Trustee.ptstrName = reinterpret_cast<char*>(process_with_access);
ea[1].grfAccessPermissions = ACCESS_SYSTEM_SECURITY | READ_CONTROL | WRITE_DAC | GENERIC_ALL;
ea[1].grfAccessMode = GRANT_ACCESS;
ea[1].grfInheritance = NO_INHERITANCE;
ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID;
ea[1].Trustee.ptstrName = reinterpret_cast<char*>(current_user);
dwRes = SetEntriesInAclA(2, ea, NULL, &pNewDACL);
if (ERROR_SUCCESS != dwRes) {
printf("SetEntriesInAcl Error %u\n", dwRes);
//TODO: goto Cleanup;
}
PSECURITY_DESCRIPTOR pSD = NULL;
// Initialize a security descriptor.
pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,
SECURITY_DESCRIPTOR_MIN_LENGTH);
if (NULL == pSD)
{
printf("error");
}
if (!InitializeSecurityDescriptor(pSD,
SECURITY_DESCRIPTOR_REVISION))
{
printf("error");
}
// Add the ACL to the security descriptor.
if (!SetSecurityDescriptorDacl(pSD,
TRUE, // bDaclPresent flag
pNewDACL,
FALSE)) // not a default DACL
{
printf("error");
}
SECURITY_ATTRIBUTES sa;
// Initialize a security attributes structure.
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = pSD;
sa.bInheritHandle = FALSE;
HANDLE hFile = CreateFileA(filename, GENERIC_ALL, 0, &sa, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
CloseHandle(hFile);
And for now it only sets access for parent of a process, not the process itself. So is it even possible what I am trying to achieve?
No, Windows' security is based around user/group access lists. It would be possible to set up a process so that it was running as some particular user and then restrict access to that user, but any program running as Administrator or Local System would be able to bypass that protection.
The best you can do against such programs is to protect against accidental access rather than malicious.
I used the example from https://github.com/hdeldar/QtCrashDumper and made us of the same in my application. Lately for actual crashes in the application the stack trace has been a little useless.
Another question is the dmp files generated module version does not match. I have double checked the module version and it is the same however the dump file has different versions. File version is 2.8.0.4 and the dmp file has 2.08.0.4
I have looked into a few answers on stackoverflow to not understand what functions I am missing.
CrashDump Info
The KB link does not exist anymore - KB313109
My implementation
#include "CrashDumpDef.h"
#include "version.h"
#ifdef USE_MINI_DUMP
#include "CrashDump.h"
#include "FileSystem.h"
LPCWSTR CrashDump::m_szAppName;
LPWSTR CrashDump::m_szAppVersion;
LPWSTR CrashDump::m_szAppBuildNumber;
WCHAR CrashDump::m_szMessageText[MAX_WARNING_MESSAGE_PATH];
LPWSTR CrashDump::m_szDumpFilePath;
#define DEFAULT_MESSAGE_TEXT L"%s Designer has experienced an issue. \nCrash dump has been saved in %s."
#define MAX_DUMP_FILE_NUMBER 9999
CrashDump::CrashDump(LPCWSTR szAppName, LPCWSTR szVersion, LPCWSTR szBuildNumber)
{
// if this assert fires then you have two instances of CrashDump
// which is not allowed
Q_ASSERT(m_szAppName == NULL);
const char* sz = VER_PRODUCTVERSION_STR;
std::vector<wchar_t> vec;
size_t len = strlen(sz);
vec.resize(len + 1);
mbstowcs(&vec[0], sz, len);
const wchar_t* productVersion = &vec[0];
std::string version = VER_PRODUCTVERSION_STR;
char build = version.back();
const char* buildNum = new char(build);
std::vector<wchar_t> vec1;
size_t lenA = strlen(buildNum);
vec1.resize(lenA + 1);
mbstowcs(&vec1[0], buildNum, lenA);
const wchar_t* buildNumber = &vec1[0];
m_szAppName = szAppName ? wcsdup(szAppName) : wcsdup(L"Designer");
m_szAppVersion = productVersion ? wcsdup(productVersion) : wcsdup(productVersion);
m_szAppBuildNumber = buildNumber ? wcsdup(buildNumber) : wcsdup(buildNumber);
wcscpy(m_szMessageText, DEFAULT_MESSAGE_TEXT);
m_szDumpFilePath = NULL;
::SetUnhandledExceptionFilter(TopLevelFilter);
}
CrashDump::~CrashDump()
{
}
void CrashDump::SetVersion(LPCWSTR szVersion)
{
if (szVersion)
{
free(m_szAppVersion);
m_szAppVersion = wcsdup(szVersion);
}
}
void CrashDump::SetBuildNumber(LPCWSTR szBuildNumber)
{
if (szBuildNumber)
{
free(m_szAppBuildNumber);
m_szAppBuildNumber = wcsdup(szBuildNumber);
}
}
void CrashDump::SetDumpFilePath(LPCWSTR szFilePath)
{
free(m_szDumpFilePath);
{
m_szDumpFilePath = wcsdup(szFilePath);
}
}
LONG CrashDump::TopLevelFilter(struct _EXCEPTION_POINTERS *pExceptionInfo)
{
//::MessageBoxW(NULL, L"debugging", m_szAppName, MB_OK);
LONG retval = EXCEPTION_CONTINUE_SEARCH;
HWND hParent = NULL; // find a better value for your app
// firstly see if dbghelp.dll is around and has the function we need
// look next to the EXE first, as the one in System32 might be old
// (e.g. Windows 2000)
HMODULE hDll = NULL;
WCHAR szDbgHelpPath[_MAX_PATH];
if (GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH))
{
WCHAR *pSlash = wcsrchr(szDbgHelpPath, L'\\');
if (pSlash)
{
wcscpy(pSlash + 1, L"DBGHELP.DLL");
hDll = ::LoadLibrary(szDbgHelpPath);
}
}
if (hDll == NULL)
{
// load any version we can
hDll = ::LoadLibrary(L"DBGHELP.DLL");
}
LPCWSTR szResult = NULL;
if (hDll)
{
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
if (pDump)
{
WCHAR szDumpPath[_MAX_PATH];
WCHAR szDumpRootPath[_MAX_PATH];
WCHAR szScratch[_MAX_PATH];
// work out a good place for the dump file - add the path here
if (m_szDumpFilePath == NULL)
{
if (GetModuleFileName(NULL, szDbgHelpPath, _MAX_PATH))
{
WCHAR *pSlash = wcsrchr(szDbgHelpPath, L'\\');
if (pSlash)
{
wcscpy(pSlash + 1, L"");
wcscpy(szDumpPath, szDbgHelpPath);
}
}
//else if (!GetTempPath(_MAX_PATH, szDumpPath))
std::string dmpFile = filesystem::buildFilename(QStringList()
<< QDir::toNativeSeparators(QDir::homePath()) + "\\AppData\\Roaming\\ABC\\logs\\"
).toStdString();
std::wstring wideDmpFile;
for (int i = 0; i < dmpFile.length(); ++i)
wideDmpFile += wchar_t(dmpFile[i]);
const wchar_t* szName = wideDmpFile.c_str();
wcscpy(szDumpPath, szName);
}
else
{
wcscpy(szDumpPath, m_szDumpFilePath);
}
wcscpy(szDumpRootPath, szDumpPath);
//PrintDebug(L"[CrashDump] Mini Dump file:[%s]",szDumpPath);
// ask the user if they want to save a dump file
//if (::MessageBox( NULL, _T("Crash, would you like to save a diagnostic file?"), m_szAppName, MB_YESNO )==IDYES)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
int i = 1;
WCHAR szFileNumber[_MAX_PATH];
while (hFile == INVALID_HANDLE_VALUE)
{
swprintf(szFileNumber, sizeof(szFileNumber), L"_%04d", i);
wcscpy(szDumpPath, szDumpRootPath);
wcscat(szDumpPath, m_szAppName);
wcscat(szDumpPath, L"_");
wcscat(szDumpPath, m_szAppVersion);
wcscat(szDumpPath, L"_");
wcscat(szDumpPath, m_szAppBuildNumber);
wcscat(szDumpPath, szFileNumber);
wcscat(szDumpPath, L".dmp");
hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
i++;
if (i > MAX_DUMP_FILE_NUMBER)
{
hFile = ::CreateFile(szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
break;
}
}
// create the file
if (hFile != INVALID_HANDLE_VALUE)
{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = NULL;
// write the dump
BOOL bOK = pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL);
if (bOK)
{
swprintf(szScratch, sizeof(szScratch), L"Saved dump file to '%s'", szDumpPath);
szResult = szScratch;
retval = EXCEPTION_EXECUTE_HANDLER;
}
else
{
swprintf(szScratch, sizeof(szScratch), L"Failed to save dump file to '%s' (error %d)", szDumpPath, GetLastError());
szResult = szScratch;
}
::CloseHandle(hFile);
WCHAR csOutMessage[MAX_WARNING_MESSAGE_PATH];
swprintf(csOutMessage, sizeof(csOutMessage), m_szMessageText, m_szAppName, szDumpPath);
//PrintError(_T("%s"), csOutMessage);
::MessageBoxW(NULL, csOutMessage, m_szAppName, MB_OK);
}
else
{
swprintf(szScratch, sizeof(szScratch), L"Failed to create dump file '%s' (error %d)", szDumpPath, GetLastError());
szResult = szScratch;
}
}
}
else
{
szResult = L"DBGHELP.DLL too old";
}
}
else
{
szResult = L"DBGHELP.DLL not found";
}
if (szResult)
{
//PrintDebug(_T("[CrashDump] Mini Dump result:[%s]"),szResult);
}
return retval;
}
#endif
In filter driver I can call IoGetCurrentProcess to get an PEPROCESS structure, and than call PsGetProcessImageFileName to get file name.
My questions is how I can get full name of the process image file?
You can use ZwQueryInformationProcess with the information class of 27. THe following code uses this routine to obtain the full image file name from process' handle.
NTSTATUS GetProcessNameByHandle(_In_ HANDLE ProcessHandle, _Out_ PUNICODE_STRING *Name)
{
ULONG retLength = 0;
ULONG pniSize = 512;
PUNICODE_STRING pni = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
do {
pni = (PUNICODE_STRING)ExAllocatePoolWithTag(PagedPool, pniSize, POOL_TAG);
if (pni != NULL) {
status = ZwQueryInformationProcess(ProcessHandle, 27, pni, pniSize, &retLength);
if (!NT_SUCCESS(status)) {
ExFreePoolWithTag(pni, POOL_TAG);
pniSize *= 2;
}
} else status = STATUS_INSUFFICIENT_RESOURCES;
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (NT_SUCCESS(status))
*Name = pni;
return status;
}
You can obtain the process handle by the following ways:
ObOpenObjectByPointer, you need process' EPROCESS address (PsLookupProcessByProcessId may help).
ZwOpenProcess – youn need to know PID of the target process.
However, using this code in every invocation of your minifilter's pre/post callback can be quite time-consuming. I solve this problem by caching process names in a hash table that uses PID as a key. Notify routines (PsSetXXXNotifyRoutine(Ex)) may prove very useful when building and managing such a table.
here I found full code like #Martin Drab code
EDIT: new fixed code
NTSTATUS
GetProcessImageName(
PEPROCESS eProcess,
PUNICODE_STRING* ProcessImageName
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG returnedLength;
HANDLE hProcess = NULL;
PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process
if (eProcess == NULL)
{
return STATUS_INVALID_PARAMETER_1;
}
status = ObOpenObjectByPointer(eProcess,
0, NULL, 0, 0, KernelMode, &hProcess);
if (!NT_SUCCESS(status))
{
DbgPrint("ObOpenObjectByPointer Failed: %08x\n", status);
return status;
}
if (ZwQueryInformationProcess == NULL)
{
UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess");
ZwQueryInformationProcess =
(QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName);
if (ZwQueryInformationProcess == NULL)
{
DbgPrint("Cannot resolve ZwQueryInformationProcess\n");
status = STATUS_UNSUCCESSFUL;
goto cleanUp;
}
}
/* Query the actual size of the process path */
status = ZwQueryInformationProcess(hProcess,
ProcessImageFileName,
NULL, // buffer
0, // buffer size
&returnedLength);
if (STATUS_INFO_LENGTH_MISMATCH != status) {
DbgPrint("ZwQueryInformationProcess status = %x\n", status);
goto cleanUp;
}
*ProcessImageName = kmalloc(returnedLength);
if (ProcessImageName == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanUp;
}
/* Retrieve the process path from the handle to the process */
status = ZwQueryInformationProcess(hProcess,
ProcessImageFileName,
*ProcessImageName,
returnedLength,
&returnedLength);
if (!NT_SUCCESS(status)) kfree(*ProcessImageName);
cleanUp:
ZwClose(hProcess);
return status;
}
FLT_POSTOP_CALLBACK_STATUS
PostCreate(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
PUNICODE_STRING pni = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = GetProcessImageName(IoThreadToProcess(Data->Thread), &pni);
if (NT_SUCCESS(status))
{
DbgPrint("ProcessName = %ws\n", pni->Buffer);
kfree(pni);
}
else
{
DbgPrint("GetProcessImageName status = %x\n", status);
}
// ...
}
I scan for opened handles to my process and print them in the console.
I start my process
I attach cheat engine
I run the enumeration of opened handles
I see which process has a handle to my process
The weird issue at this point is as follows, check the code:
array<Accessor^>^ AntiCheat::ScanHandles()
{
List<Accessor^>^ accessorList = gcnew List<Accessor^>();
if (!EnableDebugPrivilege(true))
printf("EnableDebugPrivilege failed: %d\n", GetLastError());
tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
PSYSTEM_HANDLE_INFORMATION handleInfo = new SYSTEM_HANDLE_INFORMATION;
SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS)16; // SystemHandleInformation
DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
DWORD needed = 0;
NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
while (!NT_SUCCESS(status))
{
if (needed == 0)
return nullptr;
// The previously supplied buffer wasn't enough.
delete handleInfo;
size = needed + 1024;
handleInfo = (PSYSTEM_HANDLE_INFORMATION)new BYTE[size];
status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
}
HANDLE currentProcess = GetCurrentProcess();
DWORD currentProcessId = GetProcessId(currentProcess);
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
//printf(".");
SYSTEM_HANDLE handle = handleInfo->Handles[i];
HANDLE procHandle = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
if (GetLastError() == ERROR_ACCESS_DENIED)
continue;
HANDLE dupl = 0;
if (!DuplicateHandle(procHandle, (HANDLE)handle.wValue, currentProcess, &dupl, 0, false, DUPLICATE_SAME_ACCESS))
continue;
DWORD procId = GetProcessId(dupl);
if (procId == currentProcessId)
{
printf("accessing us\n");
char processName[MAX_PATH];
GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
}
CloseHandle(dupl);
}
return accessorList->ToArray();
}
If I uncomment the line with printf(".");, I see 3 opened handles to my process (cheatengine). If it's commented (runs way faster), there is no opened handle. However I don't know why this affects my code. Im surprised, does anyone know why this happens? Or how to find out how to find the handles without my printf("."); line?
Another issue is: each time I call the function, the number of allocated bytes duplicates. And I don't know why.
I see logic problems with your code.
You are not ignoring array items where handle.dwProcessId equals currentProcessId, so you end up opening handles to your own process. Since you are only interested in looking for other processes, you should be ignoring items where handle.dwProcessId is equal to currentProcessId.
You are not checking if OpenProcess() fails for any reason other than ERROR_ACCESS_DENIED. Do not call GetLastError() unless OpenProcess() actually returns NULL first.
You are not closing an opened handle if DuplicateHandle() fails. And why are you duplicating each source handle just to call GetProcessId() on it? You already have their process IDs from the array, so the whole DuplicateHandle()+GetProcessId() is completely unnecessary.
You are taking the wrong approach anyway. Have a look at this discussion:
Enumerating the processes referencing an object
Use NtQuerySystemInformation with SystemInformationClass set to SystemHandleInformation. This fills in an array of SYSTEM_HANDLE_INFORMATION structures, which are defined as:
typedef struct _SYSTEM_HANDLE_INFORMATION {
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION;
Search for the entry corresponding to the handle you opened with ProcessID equal to GetCurrentProcessId(), then find all entries with the same Object pointer.
Although the discussion shows the wrong declaration for SYSTEM_HANDLE_INFORMATION. The following article shows the correct one:
HOWTO: Enumerate handles
#define SystemHandleInformation 16
typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
ULONG SystemInformationClass,
PVOID SystemInformation,
ULONG SystemInformationLength,
PULONG ReturnLength
);
/* The following structure is actually called SYSTEM_HANDLE_TABLE_ENTRY_INFO, but SYSTEM_HANDLE is shorter. */
typedef struct _SYSTEM_HANDLE
{
ULONG ProcessId;
BYTE ObjectTypeNumber;
BYTE Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE, *PSYSTEM_HANDLE;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG HandleCount; /* Or NumberOfHandles if you prefer. */
SYSTEM_HANDLE Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
With that said, try something more like this:
array<Accessor^>^ AntiCheat::ScanHandles()
{
List<Accessor^>^ accessorList = gcnew List<Accessor^>();
if (!EnableDebugPrivilege(true))
printf("EnableDebugPrivilege failed: %d\n", GetLastError());
tNtQuerySystemInformation oNtQuerySystemInformation = (tNtQuerySystemInformation) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
DWORD currentProcessId = GetCurrentProcessId();
HANDLE currentProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, currentProcessId);
PVOID currentProcessAddr = nullptr;
DWORD size = sizeof(SYSTEM_HANDLE_INFORMATION);
DWORD needed = 0;
PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
SYSTEM_INFORMATION_CLASS infoClass = (SYSTEM_INFORMATION_CLASS) 16; // SystemHandleInformation
NTSTATUS status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
while (status == STATUS_INFO_LENGTH_MISMATCH)
{
// The previously supplied buffer wasn't enough.
delete[] handleInfo;
size += 1024;
handleInfo = (PSYSTEM_HANDLE_INFORMATION) new BYTE[size];
status = oNtQuerySystemInformation(infoClass, handleInfo, size, &needed);
}
if (status != 0)
{
delete[] handleInfo;
return nullptr;
}
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
SYSTEM_HANDLE &handle = handleInfo->Handles[i];
if ((handle.dwProcessId == currentProcessId) &&
(currentProcess == (HANDLE)handle.wValue))
{
currentProcessAddr = handle.pAddress;
break;
}
}
for (DWORD i = 0; i < handleInfo->dwCount; i++)
{
SYSTEM_HANDLE &handle = handleInfo->Handles[i];
if ((handle.dwProcessId != currentProcessId) &&
(handle.pAddress == currentProcessAddr))
{
printf("accessing us\n");
HANDLE procHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, handle.dwProcessId);
if (procHandle != 0)
{
char processName[MAX_PATH+1];
DWORD len = GetModuleFileNameEx((HMODULE)procHandle, NULL, processName, MAX_PATH);
CloseHandle(procHandle);
processName[len] = '\0';
accessorList->Add(gcnew Accessor(gcnew String(processName), handle.GrantedAccess));
}
else
accessorList->Add(gcnew Accessor(gcnew String("unknown"), handle.GrantedAccess));
}
}
CloseHandle(currentProcess);
delete[] handleInfo;
return accessorList->ToArray();
}