mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-07-10 09:39:36 +00:00
911 lines
25 KiB
C
911 lines
25 KiB
C
/**
|
|
* @file UserAccess.c
|
|
* @author Sina Karvandi (sina@hyperdbg.org)
|
|
* @brief Access and parse user-mode components of binaries
|
|
* @details Access to Portable Executables
|
|
*
|
|
* @version 0.1
|
|
* @date 2021-12-24
|
|
*
|
|
* @copyright This project is released under the GNU Public License v3.
|
|
*
|
|
*/
|
|
#include "pch.h"
|
|
|
|
/**
|
|
* @brief Get the image path from process Id
|
|
* @details This function should be called in vmx non-root
|
|
* for size 512 is enough, if the size is not enough it
|
|
* returns FALSE
|
|
* it's up to the user to deallocate ProcessImageName.Buffer
|
|
*
|
|
* @param ProcessId
|
|
* @param ProcessImageName
|
|
* @param SizeOfImageNameToBeAllocated
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessAllocateAndGetImagePathFromProcessId(HANDLE ProcessId,
|
|
PUNICODE_STRING ProcessImageName,
|
|
UINT32 SizeOfImageNameToBeAllocated)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ReturnedLength;
|
|
ULONG BufferLength;
|
|
HANDLE ProcessHandle;
|
|
PVOID Buffer;
|
|
PEPROCESS EProcess;
|
|
PUNICODE_STRING ImageName;
|
|
|
|
//
|
|
// This eliminates the possibility of the IDLE Thread/Process
|
|
//
|
|
PAGED_CODE();
|
|
|
|
Status = PsLookupProcessByProcessId(ProcessId, &EProcess);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = ObOpenObjectByPointer(EProcess, 0, NULL, 0, 0, KernelMode, &ProcessHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
LogError("Err, cannot get the process object (%08x)", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
ObDereferenceObject(EProcess);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Probably, the process id is wrong!
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_ZwQueryInformationProcess == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Query the actual size of the process path
|
|
//
|
|
Status = g_ZwQueryInformationProcess(ProcessHandle,
|
|
ProcessImageFileName,
|
|
NULL, // Buffer
|
|
0, // Buffer size
|
|
&ReturnedLength);
|
|
|
|
if (Status != STATUS_INFO_LENGTH_MISMATCH)
|
|
{
|
|
//
|
|
// ZwQueryInformationProcess failed
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check there is enough space to store the actual process path when it is found
|
|
// If not return FALSE
|
|
//
|
|
BufferLength = ReturnedLength - sizeof(UNICODE_STRING);
|
|
|
|
if (SizeOfImageNameToBeAllocated < BufferLength)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Allocate a temporary buffer to store the path name
|
|
//
|
|
Buffer = PlatformMemAllocateZeroedNonPagedPool(ReturnedLength);
|
|
|
|
if (Buffer == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Retrieve the process path from the handle to the process
|
|
//
|
|
Status = g_ZwQueryInformationProcess(ProcessHandle,
|
|
ProcessImageFileName,
|
|
Buffer,
|
|
ReturnedLength,
|
|
&ReturnedLength);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
//
|
|
// Copy the path name
|
|
//
|
|
ImageName = (PUNICODE_STRING)Buffer;
|
|
|
|
//
|
|
// Allocate UNICODE_STRING
|
|
//
|
|
ProcessImageName->Length = 0;
|
|
ProcessImageName->MaximumLength = (USHORT)SizeOfImageNameToBeAllocated;
|
|
ProcessImageName->Buffer = (PWSTR)PlatformMemAllocateZeroedNonPagedPool(SizeOfImageNameToBeAllocated);
|
|
|
|
if (ProcessImageName->Buffer == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(ProcessImageName->Buffer, SizeOfImageNameToBeAllocated);
|
|
|
|
//
|
|
// Copy path to the buffer
|
|
//
|
|
RtlCopyUnicodeString(ProcessImageName, ImageName);
|
|
|
|
//
|
|
// Free the temp buffer which stored the path
|
|
//
|
|
PlatformMemFreePool(Buffer);
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There was an error in ZwQueryInformationProcess
|
|
// Free the temp buffer which stored the path
|
|
//
|
|
PlatformMemFreePool(Buffer);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get the process's PEB from process Id
|
|
* @details This function should be called in vmx non-root
|
|
*
|
|
* @param ProcessId
|
|
* @param Peb
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessGetPebFromProcessId(HANDLE ProcessId, PUINT64 Peb)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ReturnedLength;
|
|
HANDLE ProcessHandle;
|
|
PEPROCESS EProcess;
|
|
PPEB ProcessPeb;
|
|
PROCESS_BASIC_INFORMATION ProcessBasicInfo = {0};
|
|
|
|
//
|
|
// This eliminates the possibility of the IDLE Thread/Process
|
|
//
|
|
PAGED_CODE();
|
|
|
|
Status = PsLookupProcessByProcessId(ProcessId, &EProcess);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = ObOpenObjectByPointer(EProcess, 0, NULL, 0, 0, KernelMode, &ProcessHandle);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
LogError("Err, cannot get the process object (%08x)", Status);
|
|
return FALSE;
|
|
}
|
|
|
|
ObDereferenceObject(EProcess);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Probably, the process id is wrong!
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_ZwQueryInformationProcess == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Retrieve the process path from the handle to the process
|
|
//
|
|
Status = g_ZwQueryInformationProcess(ProcessHandle,
|
|
ProcessBasicInformation,
|
|
&ProcessBasicInfo,
|
|
sizeof(PROCESS_BASIC_INFORMATION),
|
|
&ReturnedLength);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
ProcessPeb = ProcessBasicInfo.PebBaseAddress;
|
|
|
|
*Peb = (UINT64)ProcessPeb;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief If the target process's main module is loaded, it fills
|
|
* the Entrypoint and the BaseAddress
|
|
* @details This function is safe to be called in vmx non-root
|
|
*
|
|
* @param PebAddress
|
|
* @param Is32Bit
|
|
* @param BaseAddress
|
|
* @param Entrypoint
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessGetBaseAndEntrypointOfMainModuleIfLoadedInVmxRoot(PPEB PebAddress,
|
|
BOOLEAN Is32Bit,
|
|
PUINT64 BaseAddress,
|
|
PUINT64 Entrypoint)
|
|
{
|
|
if (Is32Bit)
|
|
{
|
|
PEB_LDR_DATA32 Ldr32 = {0};
|
|
PEB32 Peb32 = {0};
|
|
PPEB_LDR_DATA32 LdrAddress32 = NULL;
|
|
|
|
MemoryMapperReadMemorySafeOnTargetProcess((UINT64)PebAddress, &Peb32, sizeof(PEB32));
|
|
|
|
LdrAddress32 = (PPEB_LDR_DATA32)Peb32.Ldr;
|
|
|
|
if (!LdrAddress32)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
MemoryMapperReadMemorySafeOnTargetProcess((UINT64)LdrAddress32, &Ldr32, sizeof(PEB_LDR_DATA32));
|
|
|
|
PLIST_ENTRY32 List = (PLIST_ENTRY32)Ldr32.InLoadOrderModuleList.Flink;
|
|
|
|
PLDR_DATA_TABLE_ENTRY32 EntryAddress = CONTAINING_RECORD(List, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
|
|
LDR_DATA_TABLE_ENTRY32 Entry = {0};
|
|
|
|
MemoryMapperReadMemorySafeOnTargetProcess((UINT64)EntryAddress, &Entry, sizeof(LDR_DATA_TABLE_ENTRY32));
|
|
|
|
if (Entry.DllBase == NULL_ZERO || Entry.EntryPoint == NULL_ZERO)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
*BaseAddress = Entry.DllBase;
|
|
*Entrypoint = Entry.EntryPoint;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PPEB_LDR_DATA LdrAddress = NULL;
|
|
PEB_LDR_DATA Ldr = {0};
|
|
|
|
PEB Peb = {0};
|
|
|
|
MemoryMapperReadMemorySafeOnTargetProcess((UINT64)PebAddress, &Peb, sizeof(PEB));
|
|
|
|
LdrAddress = (PPEB_LDR_DATA)Peb.Ldr;
|
|
|
|
if (!LdrAddress)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
MemoryMapperReadMemorySafeOnTargetProcess((UINT64)LdrAddress, &Ldr, sizeof(PEB_LDR_DATA));
|
|
|
|
PLIST_ENTRY List = (PLIST_ENTRY)Ldr.ModuleListLoadOrder.Flink;
|
|
|
|
PLDR_DATA_TABLE_ENTRY EntryAddress = CONTAINING_RECORD(List, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
|
|
LDR_DATA_TABLE_ENTRY Entry = {0};
|
|
|
|
MemoryMapperReadMemorySafeOnTargetProcess((UINT64)EntryAddress, &Entry, sizeof(LDR_DATA_TABLE_ENTRY));
|
|
|
|
// LogInfo("base: %llx | entry: %llx", Entry.DllBase, Entry.EntryPoint);
|
|
|
|
if (Entry.DllBase == NULL || Entry.EntryPoint == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
*BaseAddress = (UINT64)Entry.DllBase;
|
|
*Entrypoint = (UINT64)Entry.EntryPoint;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the loaded modules details from PEB
|
|
* @details This function should be called in vmx non-root
|
|
*
|
|
* @param Proc
|
|
* @param OnlyCountModules
|
|
* @param ModulesCount
|
|
* @param ModulesList
|
|
* @param SizeOfBufferForModulesList
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessPrintLoadedModulesX64(PEPROCESS Proc,
|
|
BOOLEAN OnlyCountModules,
|
|
PUINT32 ModulesCount,
|
|
PUSERMODE_LOADED_MODULE_SYMBOLS ModulesList,
|
|
UINT32 SizeOfBufferForModulesList)
|
|
{
|
|
KAPC_STATE State;
|
|
PPEB Peb = NULL;
|
|
PPEB_LDR_DATA Ldr = NULL;
|
|
UINT32 CountOfModules = 0;
|
|
UINT32 CurrentSavedModules = 0;
|
|
UINT32 TempSize = 0;
|
|
|
|
if (g_PsGetProcessPeb == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Process PEB, function is unexported and undocumented
|
|
//
|
|
Peb = (PPEB)g_PsGetProcessPeb(Proc);
|
|
|
|
if (!Peb)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
KeStackAttachProcess(Proc, &State);
|
|
|
|
Ldr = (PPEB_LDR_DATA)Peb->Ldr;
|
|
|
|
if (!Ldr)
|
|
{
|
|
KeUnstackDetachProcess(&State);
|
|
return FALSE;
|
|
}
|
|
|
|
if (OnlyCountModules)
|
|
{
|
|
//
|
|
// loop the linked list (Computer the size)
|
|
//
|
|
for (PLIST_ENTRY List = (PLIST_ENTRY)Ldr->ModuleListLoadOrder.Flink;
|
|
List != &Ldr->ModuleListLoadOrder;
|
|
List = (PLIST_ENTRY)List->Flink)
|
|
{
|
|
/*
|
|
PLDR_DATA_TABLE_ENTRY Entry =
|
|
CONTAINING_RECORD(List, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
|
|
|
|
Log("Base: %016llx\tEntryPoint: %016llx\tModule: %ws\tPath: %ws\n",
|
|
Entry->DllBase,
|
|
Entry->EntryPoint,
|
|
Entry->BaseDllName.Buffer,
|
|
Entry->FullDllName.Buffer);
|
|
*/
|
|
|
|
//
|
|
// Calculate count of modules
|
|
//
|
|
CountOfModules++;
|
|
}
|
|
|
|
*ModulesCount = CountOfModules;
|
|
|
|
KeUnstackDetachProcess(&State);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's not counting the modules, so we compute the number of modules
|
|
// that can be stored in the buffer by using the size of the buffer
|
|
//
|
|
CountOfModules = SizeOfBufferForModulesList / sizeof(USERMODE_LOADED_MODULE_SYMBOLS);
|
|
}
|
|
|
|
//
|
|
// Walk again to save the buffer
|
|
//
|
|
Ldr = (PPEB_LDR_DATA)Peb->Ldr;
|
|
|
|
if (!Ldr)
|
|
{
|
|
KeUnstackDetachProcess(&State);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// loop the linked list
|
|
//
|
|
for (PLIST_ENTRY List = (PLIST_ENTRY)Ldr->ModuleListLoadOrder.Flink;
|
|
List != &Ldr->ModuleListLoadOrder;
|
|
List = (PLIST_ENTRY)List->Flink)
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY Entry =
|
|
CONTAINING_RECORD(List, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
|
|
|
|
if (CountOfModules == CurrentSavedModules)
|
|
{
|
|
//
|
|
// Won't continue as the buffer is now full
|
|
// Generally, we shouldn't be at this stage, only when
|
|
// a module is just loaded and we didn't allocate enough
|
|
// memory for it, so it's better to continue
|
|
//
|
|
KeUnstackDetachProcess(&State);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Save the details into the storage
|
|
//
|
|
ModulesList[CurrentSavedModules].Entrypoint = (UINT64)Entry->EntryPoint;
|
|
ModulesList[CurrentSavedModules].BaseAddress = (UINT64)Entry->DllBase;
|
|
|
|
//
|
|
// Copy the path
|
|
//
|
|
TempSize = Entry->FullDllName.Length;
|
|
if (TempSize >= MAX_PATH)
|
|
{
|
|
TempSize = MAX_PATH;
|
|
}
|
|
|
|
TempSize = TempSize * 2;
|
|
memcpy(&ModulesList[CurrentSavedModules].FilePath, Entry->FullDllName.Buffer, TempSize);
|
|
|
|
CurrentSavedModules++;
|
|
}
|
|
|
|
KeUnstackDetachProcess(&State);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Gets the loaded modules details from PEB (x86)
|
|
* @details This function should be called in vmx non-root
|
|
*
|
|
* @param Proc
|
|
* @param OnlyCountModules
|
|
* @param ModulesCount
|
|
* @param ModulesList
|
|
* @param SizeOfBufferForModulesList
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessPrintLoadedModulesX86(PEPROCESS Proc,
|
|
BOOLEAN OnlyCountModules,
|
|
PUINT32 ModulesCount,
|
|
PUSERMODE_LOADED_MODULE_SYMBOLS ModulesList,
|
|
UINT32 SizeOfBufferForModulesList)
|
|
{
|
|
KAPC_STATE State;
|
|
PPEB32 Peb = NULL;
|
|
PPEB_LDR_DATA32 Ldr = NULL;
|
|
UINT32 CountOfModules = 0;
|
|
UINT32 CurrentSavedModules = 0;
|
|
UINT32 TempSize = 0;
|
|
|
|
if (g_PsGetProcessWow64Process == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Process PEB, function is unexported and undocumented
|
|
//
|
|
Peb = (PPEB32)g_PsGetProcessWow64Process(Proc);
|
|
|
|
if (!Peb)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
KeStackAttachProcess(Proc, &State);
|
|
|
|
Ldr = (PPEB_LDR_DATA32)Peb->Ldr;
|
|
|
|
if (!Ldr)
|
|
{
|
|
KeUnstackDetachProcess(&State);
|
|
return FALSE;
|
|
}
|
|
|
|
if (OnlyCountModules)
|
|
{
|
|
//
|
|
// loop the linked list (Computer the size)
|
|
//
|
|
for (PLIST_ENTRY32 List = (PLIST_ENTRY32)Ldr->InLoadOrderModuleList.Flink;
|
|
List != &Ldr->InLoadOrderModuleList;
|
|
List = (PLIST_ENTRY32)List->Flink)
|
|
{
|
|
/*
|
|
PLDR_DATA_TABLE_ENTRY32 Entry =
|
|
CONTAINING_RECORD(List, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
|
|
*/
|
|
|
|
//
|
|
// Calculate count of modules
|
|
//
|
|
CountOfModules++;
|
|
}
|
|
|
|
*ModulesCount = CountOfModules;
|
|
|
|
KeUnstackDetachProcess(&State);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's not counting the modules, so we compute the number of modules
|
|
// that can be stored in the buffer by using the size of the buffer
|
|
//
|
|
CountOfModules = SizeOfBufferForModulesList / sizeof(USERMODE_LOADED_MODULE_SYMBOLS);
|
|
}
|
|
|
|
//
|
|
// Walk again to save the buffer
|
|
//
|
|
Ldr = (PPEB_LDR_DATA32)Peb->Ldr;
|
|
|
|
if (!Ldr)
|
|
{
|
|
KeUnstackDetachProcess(&State);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// loop the linked list
|
|
//
|
|
for (PLIST_ENTRY32 List = (PLIST_ENTRY32)Ldr->InLoadOrderModuleList.Flink;
|
|
List != &Ldr->InLoadOrderModuleList;
|
|
List = (PLIST_ENTRY32)List->Flink)
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY32 Entry =
|
|
CONTAINING_RECORD(List, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
|
|
|
|
if (CountOfModules == CurrentSavedModules)
|
|
{
|
|
//
|
|
// Won't continue as the buffer is now full
|
|
// Generally, we shouldn't be at this stage, only when
|
|
// a module is just loaded and we didn't allocate enough
|
|
// memory for it, so it's better to continue
|
|
//
|
|
KeUnstackDetachProcess(&State);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Save the details into the storage
|
|
//
|
|
ModulesList[CurrentSavedModules].Entrypoint = Entry->EntryPoint;
|
|
ModulesList[CurrentSavedModules].BaseAddress = Entry->DllBase;
|
|
|
|
//
|
|
// Copy the path
|
|
//
|
|
TempSize = Entry->FullDllName.Length;
|
|
if (TempSize >= MAX_PATH)
|
|
{
|
|
TempSize = MAX_PATH;
|
|
}
|
|
|
|
TempSize = TempSize * 2;
|
|
memcpy(&ModulesList[CurrentSavedModules].FilePath, (const PVOID)Entry->FullDllName.Buffer, TempSize);
|
|
|
|
CurrentSavedModules++;
|
|
}
|
|
|
|
KeUnstackDetachProcess(&State);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Print loaded modules details from PEB
|
|
* @details This function should be called in vmx non-root
|
|
*
|
|
* @param Proc
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessPrintLoadedModulesX86_2(PEPROCESS Proc)
|
|
{
|
|
KAPC_STATE State;
|
|
PPEB32 Peb = NULL;
|
|
PPEB_LDR_DATA32 Ldr = NULL;
|
|
|
|
if (g_PsGetProcessWow64Process == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// get process PEB for the x86 part, function is unexported and undocumented
|
|
//
|
|
Peb = (PPEB32)g_PsGetProcessWow64Process(Proc);
|
|
|
|
if (!Peb)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
KeStackAttachProcess(Proc, &State);
|
|
|
|
Ldr = (PPEB_LDR_DATA32)Peb->Ldr;
|
|
|
|
if (!Ldr)
|
|
{
|
|
KeUnstackDetachProcess(&State);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// loop the linked list
|
|
//
|
|
for (PLIST_ENTRY32 List = (PLIST_ENTRY32)Ldr->InLoadOrderModuleList.Flink;
|
|
List != &Ldr->InLoadOrderModuleList;
|
|
List = (PLIST_ENTRY32)List->Flink)
|
|
{
|
|
PLDR_DATA_TABLE_ENTRY32 Entry =
|
|
CONTAINING_RECORD(List, LDR_DATA_TABLE_ENTRY32, InLoadOrderLinks);
|
|
|
|
//
|
|
// since the PEB is x86, the DLL is x86, and so the base address is in x86 (4 byte as compared to 8 byte)
|
|
// and the UNICODE STRING is in 32 bit(UNICODE_STRING32), and because there is no viable conversion
|
|
// we are just going to force everything in
|
|
//
|
|
UNICODE_STRING ModuleName;
|
|
UNICODE_STRING ModulePath;
|
|
UINT64 BaseAddr = (UINT64)NULL;
|
|
UINT64 EntrypointAddress = (UINT64)NULL;
|
|
|
|
BaseAddr = Entry->DllBase;
|
|
EntrypointAddress = Entry->EntryPoint;
|
|
|
|
ModuleName.Length = Entry->BaseDllName.Length;
|
|
ModuleName.MaximumLength = Entry->BaseDllName.MaximumLength;
|
|
ModuleName.Buffer = (PWCH)Entry->BaseDllName.Buffer;
|
|
|
|
ModulePath.Length = Entry->FullDllName.Length;
|
|
ModulePath.MaximumLength = Entry->FullDllName.MaximumLength;
|
|
ModulePath.Buffer = (PWCH)Entry->FullDllName.Buffer;
|
|
|
|
Log("Base: %016llx\tEntryPoint: %016llx\tModule: %ws\tPath: %ws\n",
|
|
BaseAddr,
|
|
EntrypointAddress,
|
|
ModuleName.Buffer,
|
|
ModulePath.Buffer);
|
|
}
|
|
|
|
KeUnstackDetachProcess(&State);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Detects whether process is 32-bit or 64-bit by using EPROCESS pointer
|
|
* @details This function should be called in vmx non-root
|
|
*
|
|
* @param SourceProcess
|
|
* @param Is32Bit
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessIsWow64ProcessByEprocess(PEPROCESS SourceProcess, PBOOLEAN Is32Bit)
|
|
{
|
|
if (g_PsGetProcessWow64Process == NULL || g_PsGetProcessPeb == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (g_PsGetProcessWow64Process(SourceProcess))
|
|
{
|
|
//
|
|
// x86 process, walk x86 module list
|
|
//
|
|
|
|
*Is32Bit = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
else if (g_PsGetProcessPeb(SourceProcess))
|
|
{
|
|
//
|
|
// x64 process, walk x64 module list
|
|
//
|
|
*Is32Bit = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Detects whether process is 32-bit or 64-bit
|
|
* @details This function should be called in vmx non-root
|
|
*
|
|
* @param ProcessId
|
|
* @param Is32Bit
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessIsWow64Process(HANDLE ProcessId, PBOOLEAN Is32Bit)
|
|
{
|
|
PEPROCESS SourceProcess;
|
|
|
|
if (PsLookupProcessByProcessId(ProcessId, &SourceProcess) != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// if the process not found
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
ObDereferenceObject(SourceProcess);
|
|
|
|
return UserAccessIsWow64ProcessByEprocess(SourceProcess, Is32Bit);
|
|
}
|
|
|
|
/**
|
|
* @brief Get details about loaded modules
|
|
* @details This function should be called in vmx non-root
|
|
*
|
|
* @param ProcessLoadedModuleRequest
|
|
* @param BufferSize
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessGetLoadedModules(PUSERMODE_LOADED_MODULE_DETAILS ProcessLoadedModuleRequest, UINT32 BufferSize)
|
|
{
|
|
PEPROCESS SourceProcess;
|
|
BOOLEAN Is32Bit;
|
|
|
|
if (PsLookupProcessByProcessId((HANDLE)ProcessLoadedModuleRequest->ProcessId, &SourceProcess) != STATUS_SUCCESS)
|
|
{
|
|
//
|
|
// if the process not found
|
|
//
|
|
ProcessLoadedModuleRequest->Result = DEBUGGER_ERROR_INVALID_PROCESS_ID;
|
|
return FALSE;
|
|
}
|
|
|
|
ObDereferenceObject(SourceProcess);
|
|
|
|
//
|
|
// check whether the target process is 32-bit or 64-bit
|
|
//
|
|
if (!UserAccessIsWow64Process((HANDLE)ProcessLoadedModuleRequest->ProcessId, &Is32Bit))
|
|
{
|
|
//
|
|
// Unable to detect whether it's 32-bit or 64-bit
|
|
//
|
|
ProcessLoadedModuleRequest->Result = DEBUGGER_ERROR_UNABLE_TO_GET_MODULES_OF_THE_PROCESS;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Indicate that the process is x86
|
|
//
|
|
ProcessLoadedModuleRequest->Is32Bit = Is32Bit;
|
|
|
|
if (Is32Bit)
|
|
{
|
|
//
|
|
// x86 process, walk x86 module list
|
|
//
|
|
if (UserAccessPrintLoadedModulesX86(SourceProcess,
|
|
ProcessLoadedModuleRequest->OnlyCountModules,
|
|
&ProcessLoadedModuleRequest->ModulesCount,
|
|
(USERMODE_LOADED_MODULE_SYMBOLS *)((UINT64)ProcessLoadedModuleRequest + sizeof(USERMODE_LOADED_MODULE_DETAILS)),
|
|
BufferSize - sizeof(USERMODE_LOADED_MODULE_DETAILS)))
|
|
{
|
|
ProcessLoadedModuleRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// x64 process, walk x64 module list
|
|
//
|
|
if (UserAccessPrintLoadedModulesX64(SourceProcess,
|
|
ProcessLoadedModuleRequest->OnlyCountModules,
|
|
&ProcessLoadedModuleRequest->ModulesCount,
|
|
(USERMODE_LOADED_MODULE_SYMBOLS *)((UINT64)ProcessLoadedModuleRequest + sizeof(USERMODE_LOADED_MODULE_DETAILS)),
|
|
BufferSize - sizeof(USERMODE_LOADED_MODULE_DETAILS)))
|
|
{
|
|
ProcessLoadedModuleRequest->Result = DEBUGGER_OPERATION_WAS_SUCCESSFUL;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
ProcessLoadedModuleRequest->Result = DEBUGGER_ERROR_UNABLE_TO_GET_MODULES_OF_THE_PROCESS;
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* @brief Checks whether the loaded module is available or not
|
|
* @param CoreId
|
|
*
|
|
* @return BOOLEAN
|
|
*/
|
|
BOOLEAN
|
|
UserAccessCheckForLoadedModuleDetails(UINT32 CoreId)
|
|
{
|
|
PUSERMODE_DEBUGGING_PROCESS_DETAILS ProcessDebuggingDetail;
|
|
UINT64 BaseAddress = (UINT64)NULL;
|
|
UINT64 Entrypoint = (UINT64)NULL;
|
|
PROCESSOR_DEBUGGING_STATE * DbgState = &g_DbgState[CoreId];
|
|
|
|
//
|
|
// Check if the callback needs to be handled or not
|
|
//
|
|
if (!g_IsWaitingForUserModeProcessEntryToBeCalled)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Find the thread debugging detail structure
|
|
//
|
|
ProcessDebuggingDetail =
|
|
AttachingFindProcessDebuggingDetailsByProcessId(HANDLE_TO_UINT32(PsGetCurrentProcessId()));
|
|
|
|
//
|
|
// Check if we find the debugging detail of the thread or not
|
|
//
|
|
if (ProcessDebuggingDetail == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (ProcessDebuggingDetail->EntrypointOfMainModule == (UINT64)NULL &&
|
|
ProcessDebuggingDetail->PebAddressToMonitor != (PVOID)NULL &&
|
|
CheckAccessValidityAndSafety((UINT64)ProcessDebuggingDetail->PebAddressToMonitor, sizeof(CHAR)) &&
|
|
UserAccessGetBaseAndEntrypointOfMainModuleIfLoadedInVmxRoot(ProcessDebuggingDetail->PebAddressToMonitor,
|
|
ProcessDebuggingDetail->Is32Bit,
|
|
&BaseAddress,
|
|
&Entrypoint))
|
|
{
|
|
if (Entrypoint != (UINT64)NULL)
|
|
{
|
|
ProcessDebuggingDetail->BaseAddressOfMainModule = BaseAddress;
|
|
ProcessDebuggingDetail->EntrypointOfMainModule = Entrypoint;
|
|
|
|
// LogInfo("Base: %016llx \t EntryPoint: %016llx", BaseAddress, Entrypoint);
|
|
|
|
//
|
|
// Handle entrypoint interception
|
|
//
|
|
AttachingHandleEntrypointInterception(DbgState);
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Not available
|
|
//
|
|
return FALSE;
|
|
}
|