mirror of
https://github.com/x64dbg/ScyllaHide
synced 2026-06-08 18:22:18 +00:00
7e648fb29e
Support for the latest Themida is incomplete, see #127
696 lines
20 KiB
C++
696 lines
20 KiB
C++
#include "HookHelper.h"
|
|
|
|
#include <ntdll/ntdll.h>
|
|
|
|
#include "HookedFunctions.h"
|
|
#include "HookMain.h"
|
|
|
|
const WCHAR * BadProcessnameList[] =
|
|
{
|
|
L"ollydbg.exe",
|
|
L"ida.exe",
|
|
L"ida64.exe",
|
|
L"idag.exe",
|
|
L"idag64.exe",
|
|
L"idaw.exe",
|
|
L"idaw64.exe",
|
|
L"idaq.exe",
|
|
L"idaq64.exe",
|
|
L"idau.exe",
|
|
L"idau64.exe",
|
|
L"scylla.exe",
|
|
L"scylla_x64.exe",
|
|
L"scylla_x86.exe",
|
|
L"protection_id.exe",
|
|
L"x64dbg.exe",
|
|
L"x32dbg.exe",
|
|
L"windbg.exe",
|
|
L"reshacker.exe",
|
|
L"ImportREC.exe",
|
|
L"IMMUNITYDEBUGGER.EXE",
|
|
L"devenv.exe"
|
|
};
|
|
|
|
const WCHAR * BadWindowTextList[] =
|
|
{
|
|
L"OLLYDBG",
|
|
L"ida",
|
|
L"disassembly",
|
|
L"scylla",
|
|
L"Debug",
|
|
L"[CPU",
|
|
L"Immunity",
|
|
L"WinDbg",
|
|
L"x32dbg",
|
|
L"x64dbg",
|
|
L"Import reconstructor"
|
|
};
|
|
|
|
const WCHAR * BadWindowClassList[] =
|
|
{
|
|
L"OLLYDBG",
|
|
L"Zeta Debugger",
|
|
L"Rock Debugger",
|
|
L"ObsidianGUI",
|
|
L"ID", //Immunity Debugger
|
|
L"WinDbgFrameClass", //WinDBG
|
|
L"idawindow",
|
|
L"tnavbox",
|
|
L"idaview",
|
|
L"tgrzoom"
|
|
};
|
|
|
|
extern "C" void InstrumentationCallbackAsm();
|
|
|
|
extern HOOK_DLL_DATA HookDllData;
|
|
extern SAVE_DEBUG_REGISTERS ArrayDebugRegister[100];
|
|
|
|
static USHORT DebugObjectTypeIndex = 0;
|
|
static USHORT ProcessTypeIndex = 0;
|
|
static USHORT ThreadTypeIndex = 0;
|
|
|
|
bool IsProcessNameBad(PUNICODE_STRING processName)
|
|
{
|
|
if (processName == nullptr || processName->Length == 0 || processName->Buffer == nullptr)
|
|
return false;
|
|
|
|
UNICODE_STRING badProcessName;
|
|
for (int i = 0; i < _countof(BadProcessnameList); i++)
|
|
{
|
|
RtlInitUnicodeString(&badProcessName, const_cast<PWSTR>(BadProcessnameList[i]));
|
|
if (RtlEqualUnicodeString(processName, &badProcessName, TRUE))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsWindowClassNameBad(PUNICODE_STRING className)
|
|
{
|
|
if (className == nullptr || className->Length == 0 || className->Buffer == nullptr)
|
|
return false;
|
|
|
|
UNICODE_STRING badWindowClassName;
|
|
for (int i = 0; i < _countof(BadWindowClassList); i++)
|
|
{
|
|
RtlInitUnicodeString(&badWindowClassName, const_cast<PWSTR>(BadWindowClassList[i]));
|
|
if (RtlUnicodeStringContains(className, &badWindowClassName, TRUE))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsWindowNameBad(PUNICODE_STRING windowName)
|
|
{
|
|
if (windowName == nullptr || windowName->Length == 0 || windowName->Buffer == nullptr)
|
|
return false;
|
|
|
|
UNICODE_STRING badWindowName;
|
|
for (int i = 0; i < _countof(BadWindowTextList); i++)
|
|
{
|
|
RtlInitUnicodeString(&badWindowName, const_cast<PWSTR>(BadWindowTextList[i]));
|
|
if (RtlUnicodeStringContains(windowName, &badWindowName, TRUE))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool IsWindowBad(HWND hWnd)
|
|
{
|
|
if (HookDllData.EnableProtectProcessId)
|
|
{
|
|
const ULONG Pid = HookDllData.dNtUserQueryWindow != nullptr
|
|
? HandleToULong(HookDllData.dNtUserQueryWindow(hWnd, WindowProcess))
|
|
: HandleToULong(HookDllData.NtUserQueryWindow(hWnd, WindowProcess));
|
|
if (Pid == HookDllData.dwProtectedProcessId)
|
|
return true;
|
|
}
|
|
|
|
DECLARE_UNICODE_STRING_SIZE(ClassName, 256);
|
|
DECLARE_UNICODE_STRING_SIZE(WindowText, 512);
|
|
|
|
ClassName.Length = (USHORT)HookDllData.NtUserGetClassName(hWnd, FALSE, &ClassName) * sizeof(WCHAR);
|
|
ClassName.Buffer[ClassName.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
if (IsWindowClassNameBad(&ClassName))
|
|
return true;
|
|
|
|
WindowText.Length = (USHORT)HookDllData.NtUserInternalGetWindowText(hWnd, WindowText.Buffer, (INT)(WindowText.MaximumLength / sizeof(WCHAR))) * sizeof(WCHAR);
|
|
WindowText.Buffer[WindowText.Length / sizeof(WCHAR)] = UNICODE_NULL;
|
|
return IsWindowNameBad(&WindowText);
|
|
}
|
|
|
|
static void GetBadObjectTypes()
|
|
{
|
|
// If NtQSI is not hooked, this function is N/A
|
|
if (HookDllData.dNtQuerySystemInformation == nullptr)
|
|
return;
|
|
|
|
// Only get the object type indices once
|
|
if (DebugObjectTypeIndex != 0 || ProcessTypeIndex != 0 || ThreadTypeIndex != 0)
|
|
return;
|
|
|
|
// Create handles to three bad object types: an empty debug object and our own process and thread
|
|
HANDLE DebugObjectHandle = nullptr, ProcessHandle = nullptr, ThreadHandle = nullptr;
|
|
OBJECT_ATTRIBUTES ObjectAttributes = { sizeof(OBJECT_ATTRIBUTES) };
|
|
CLIENT_ID ClientId = NtCurrentTeb()->ClientId;
|
|
NtCreateDebugObject(&DebugObjectHandle, DEBUG_ALL_ACCESS, &ObjectAttributes, 0);
|
|
NtOpenProcess(&ProcessHandle, PROCESS_ALL_ACCESS, &ObjectAttributes, &ClientId);
|
|
NtOpenThread(&ThreadHandle, THREAD_ALL_ACCESS, &ObjectAttributes, &ClientId);
|
|
|
|
SYSTEM_HANDLE_INFORMATION_EX Dummy; // Prevent getting STATUS_INFO_LENGTH_MISMATCH twice
|
|
PSYSTEM_HANDLE_INFORMATION_EX HandleInfo = &Dummy;
|
|
ULONG Size;
|
|
NTSTATUS Status;
|
|
if ((Status = HookDllData.dNtQuerySystemInformation(SystemExtendedHandleInformation,
|
|
HandleInfo,
|
|
sizeof(Dummy),
|
|
&Size)) != STATUS_INFO_LENGTH_MISMATCH)
|
|
goto exit;
|
|
|
|
HandleInfo = (PSYSTEM_HANDLE_INFORMATION_EX)RtlAllocateHeap(RtlProcessHeap(), 0, 2 * Size);
|
|
Status = HookDllData.dNtQuerySystemInformation(SystemExtendedHandleInformation,
|
|
HandleInfo,
|
|
2 * Size,
|
|
nullptr);
|
|
if (!NT_SUCCESS(Status))
|
|
goto exit;
|
|
|
|
// Enumerate all handles
|
|
for (ULONG i = 0; i < HandleInfo->NumberOfHandles; ++i)
|
|
{
|
|
SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX Entry = HandleInfo->Handles[i];
|
|
if (Entry.UniqueProcessId != (ULONG_PTR)NtCurrentTeb()->ClientId.UniqueProcess)
|
|
continue; // Not our process
|
|
|
|
if (Entry.HandleValue == (ULONG_PTR)DebugObjectHandle)
|
|
DebugObjectTypeIndex = Entry.ObjectTypeIndex;
|
|
else if (Entry.HandleValue == (ULONG_PTR)ProcessHandle)
|
|
ProcessTypeIndex = Entry.ObjectTypeIndex;
|
|
else if (Entry.HandleValue == (ULONG_PTR)ThreadHandle)
|
|
ThreadTypeIndex = Entry.ObjectTypeIndex;
|
|
}
|
|
|
|
exit:
|
|
if (DebugObjectHandle != nullptr)
|
|
NtClose(DebugObjectHandle);
|
|
if (ProcessHandle != nullptr)
|
|
NtClose(ProcessHandle);
|
|
if (ThreadHandle != nullptr)
|
|
NtClose(ThreadHandle);
|
|
if (HandleInfo != &Dummy)
|
|
RtlFreeHeap(RtlProcessHeap(), 0, HandleInfo);
|
|
}
|
|
|
|
bool IsObjectTypeBad(USHORT objectTypeIndex)
|
|
{
|
|
GetBadObjectTypes();
|
|
return objectTypeIndex == DebugObjectTypeIndex ||
|
|
objectTypeIndex == ProcessTypeIndex ||
|
|
objectTypeIndex == ThreadTypeIndex;
|
|
}
|
|
|
|
static LUID ConvertLongToLuid(LONG value)
|
|
{
|
|
LUID luid;
|
|
LARGE_INTEGER largeInt;
|
|
largeInt.QuadPart = value;
|
|
luid.LowPart = largeInt.LowPart;
|
|
luid.HighPart = largeInt.HighPart;
|
|
return luid;
|
|
}
|
|
|
|
bool HasDebugPrivileges(HANDLE hProcess)
|
|
{
|
|
HANDLE hToken;
|
|
NTSTATUS status = NtOpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
|
|
if (!NT_SUCCESS(status))
|
|
return false;
|
|
|
|
const LUID SeDebugPrivilege = ConvertLongToLuid(SE_DEBUG_PRIVILEGE);
|
|
|
|
PRIVILEGE_SET privilegeSet;
|
|
privilegeSet.PrivilegeCount = 1;
|
|
privilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
|
|
privilegeSet.Privilege[0].Luid = SeDebugPrivilege;
|
|
privilegeSet.Privilege[0].Attributes = 0;
|
|
|
|
BOOLEAN hasDebugPrivileges = FALSE;
|
|
NtPrivilegeCheck(hToken, &privilegeSet, &hasDebugPrivileges);
|
|
|
|
NtClose(hToken);
|
|
return hasDebugPrivileges == TRUE;
|
|
}
|
|
|
|
bool IsWow64Process(HANDLE ProcessHandle)
|
|
{
|
|
PPEB WoW64Peb = nullptr;
|
|
const NTSTATUS Status = NtQueryInformationProcess(ProcessHandle,
|
|
ProcessWow64Information,
|
|
&WoW64Peb,
|
|
sizeof(PPEB),
|
|
nullptr);
|
|
|
|
return NT_SUCCESS(Status) && WoW64Peb != nullptr;
|
|
}
|
|
|
|
NTSTATUS
|
|
InstallInstrumentationCallbackHook(
|
|
_In_ HANDLE ProcessHandle,
|
|
_In_ BOOLEAN Remove
|
|
)
|
|
{
|
|
const PVOID Callback = Remove ? nullptr : (PVOID)InstrumentationCallbackAsm;
|
|
NTSTATUS Status = STATUS_NOT_SUPPORTED;
|
|
|
|
if (RtlNtMajorVersion() > 6)
|
|
{
|
|
// Windows 10
|
|
PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION InstrumentationCallbackInfo;
|
|
#ifdef _WIN64
|
|
InstrumentationCallbackInfo.Version = 0;
|
|
#else
|
|
// Native x86 instrumentation callbacks don't work correctly
|
|
if (!IsWow64Process(ProcessHandle))
|
|
{
|
|
//InstrumentationCallbackInfo.Version = 1; // Value to use if they did
|
|
return Status;
|
|
}
|
|
|
|
// WOW64: set the callback pointer in the version field
|
|
InstrumentationCallbackInfo.Version = (ULONG_PTR)Callback;
|
|
#endif
|
|
InstrumentationCallbackInfo.Reserved = 0;
|
|
InstrumentationCallbackInfo.Callback = Callback;
|
|
|
|
Status = HookDllData.dNtSetInformationProcess != nullptr
|
|
? HookDllData.dNtSetInformationProcess(ProcessHandle,
|
|
ProcessInstrumentationCallback,
|
|
&InstrumentationCallbackInfo,
|
|
sizeof(InstrumentationCallbackInfo))
|
|
: NtSetInformationProcess(ProcessHandle,
|
|
ProcessInstrumentationCallback,
|
|
&InstrumentationCallbackInfo,
|
|
sizeof(InstrumentationCallbackInfo));
|
|
}
|
|
#ifdef _WIN64 // Windows 7-8.1 do not support x86/WOW64 instrumentation callbacks
|
|
else if (RtlNtMajorVersion() == 6 && RtlNtMinorVersion() >= 1)
|
|
{
|
|
// Windows 7-8.1 require SE_DEBUG for this to work, even on the current process
|
|
BOOLEAN SeDebugWasEnabled;
|
|
Status = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, TRUE, FALSE, &SeDebugWasEnabled);
|
|
if (!NT_SUCCESS(Status))
|
|
return Status;
|
|
|
|
Status = HookDllData.dNtSetInformationProcess != nullptr
|
|
? HookDllData.dNtSetInformationProcess(ProcessHandle,
|
|
ProcessInstrumentationCallback,
|
|
(PVOID)&Callback,
|
|
sizeof(Callback))
|
|
: NtSetInformationProcess(ProcessHandle,
|
|
ProcessInstrumentationCallback,
|
|
(PVOID)&Callback,
|
|
sizeof(Callback));
|
|
|
|
RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, SeDebugWasEnabled, FALSE, &SeDebugWasEnabled);
|
|
}
|
|
#endif
|
|
|
|
return Status;
|
|
}
|
|
|
|
void * GetPEBRemote(HANDLE hProcess)
|
|
{
|
|
PROCESS_BASIC_INFORMATION pbi;
|
|
|
|
if (HookDllData.dNtQueryInformationProcess)
|
|
{
|
|
if (HookDllData.dNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), 0) >= 0)
|
|
{
|
|
return pbi.PebBaseAddress;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//maybe not hooked
|
|
if (NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), 0) >= 0)
|
|
{
|
|
return pbi.PebBaseAddress;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
DWORD GetProcessIdByProcessHandle(HANDLE hProcess)
|
|
{
|
|
PROCESS_BASIC_INFORMATION pbi;
|
|
|
|
if (HookDllData.dNtQueryInformationProcess)
|
|
{
|
|
if (HookDllData.dNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), 0) >= 0)
|
|
{
|
|
return HandleToULong(pbi.UniqueProcessId);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//maybe not hooked
|
|
if (NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(PROCESS_BASIC_INFORMATION), 0) >= 0)
|
|
{
|
|
return HandleToULong(pbi.UniqueProcessId);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
DWORD GetProcessIdByThreadHandle(HANDLE hThread)
|
|
{
|
|
THREAD_BASIC_INFORMATION tbi;
|
|
|
|
if (NT_SUCCESS(NtQueryInformationThread(hThread, ThreadBasicInformation, &tbi, sizeof(THREAD_BASIC_INFORMATION), 0)))
|
|
{
|
|
return HandleToULong(tbi.ClientId.UniqueProcess);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void TerminateProcessByProcessId(DWORD dwProcess)
|
|
{
|
|
if (dwProcess == 0)
|
|
return;
|
|
|
|
OBJECT_ATTRIBUTES attributes = { sizeof(OBJECT_ATTRIBUTES) };
|
|
CLIENT_ID clientId = { ULongToHandle(dwProcess) };
|
|
HANDLE hProcess;
|
|
NTSTATUS status = NtOpenProcess(&hProcess, PROCESS_TERMINATE, &attributes, &clientId);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
NtTerminateProcess(hProcess, STATUS_SUCCESS);
|
|
NtClose(hProcess);
|
|
}
|
|
}
|
|
|
|
static DWORD dwExplorerPid = 0;
|
|
|
|
DWORD GetExplorerProcessId()
|
|
{
|
|
if (dwExplorerPid == 0)
|
|
{
|
|
UNICODE_STRING explorerName = RTL_CONSTANT_STRING(L"explorer.exe");
|
|
dwExplorerPid = GetProcessIdByName(&explorerName);
|
|
}
|
|
return dwExplorerPid;
|
|
}
|
|
|
|
DWORD GetProcessIdByName(PUNICODE_STRING processName)
|
|
{
|
|
ULONG size;
|
|
if (NtQuerySystemInformation(SystemProcessInformation, nullptr, 0, &size) != STATUS_INFO_LENGTH_MISMATCH)
|
|
return 0;
|
|
const PSYSTEM_PROCESS_INFORMATION systemProcessInfo =
|
|
static_cast<PSYSTEM_PROCESS_INFORMATION>(RtlAllocateHeap(RtlProcessHeap(), 0, 2 * size));
|
|
NTSTATUS status;
|
|
if (HookDllData.dNtQuerySystemInformation != nullptr)
|
|
{
|
|
status = HookDllData.dNtQuerySystemInformation(SystemProcessInformation,
|
|
systemProcessInfo,
|
|
2 * size,
|
|
nullptr);
|
|
}
|
|
else
|
|
{
|
|
status = NtQuerySystemInformation(SystemProcessInformation,
|
|
systemProcessInfo,
|
|
2 * size,
|
|
nullptr);
|
|
}
|
|
if (!NT_SUCCESS(status))
|
|
return 0;
|
|
|
|
DWORD pid = 0;
|
|
PSYSTEM_PROCESS_INFORMATION process = systemProcessInfo;
|
|
while (true)
|
|
{
|
|
if (RtlEqualUnicodeString(&process->ImageName, processName, TRUE))
|
|
{
|
|
pid = HandleToULong(process->UniqueProcessId);
|
|
break;
|
|
}
|
|
|
|
if (process->NextEntryOffset == 0)
|
|
break;
|
|
process = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)process + process->NextEntryOffset);
|
|
}
|
|
|
|
RtlFreeHeap(RtlProcessHeap(), 0, systemProcessInfo);
|
|
return pid;
|
|
}
|
|
|
|
bool RtlUnicodeStringContains(PUNICODE_STRING Str, PUNICODE_STRING SubStr, BOOLEAN CaseInsensitive)
|
|
{
|
|
if (Str == nullptr || SubStr == nullptr || Str->Length < SubStr->Length)
|
|
return false;
|
|
|
|
const USHORT numCharsDiff = (Str->Length - SubStr->Length) / sizeof(WCHAR);
|
|
UNICODE_STRING slice = *Str;
|
|
slice.Length = SubStr->Length;
|
|
|
|
for (USHORT i = 0; i <= numCharsDiff; ++i, ++slice.Buffer, slice.MaximumLength -= sizeof(WCHAR))
|
|
{
|
|
if (RtlEqualUnicodeString(&slice, SubStr, CaseInsensitive))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ThreadDebugContextRemoveEntry(const int index)
|
|
{
|
|
ArrayDebugRegister[index].dwThreadId = 0;
|
|
}
|
|
|
|
void ThreadDebugContextSaveContext(const int index, const PCONTEXT ThreadContext)
|
|
{
|
|
ArrayDebugRegister[index].dwThreadId = HandleToULong(NtCurrentTeb()->ClientId.UniqueThread);
|
|
ArrayDebugRegister[index].Dr0 = ThreadContext->Dr0;
|
|
ArrayDebugRegister[index].Dr1 = ThreadContext->Dr1;
|
|
ArrayDebugRegister[index].Dr2 = ThreadContext->Dr2;
|
|
ArrayDebugRegister[index].Dr3 = ThreadContext->Dr3;
|
|
ArrayDebugRegister[index].Dr6 = ThreadContext->Dr6;
|
|
ArrayDebugRegister[index].Dr7 = ThreadContext->Dr7;
|
|
}
|
|
|
|
int ThreadDebugContextFindExistingSlotIndex()
|
|
{
|
|
for (int i = 0; i < _countof(ArrayDebugRegister); i++)
|
|
{
|
|
if (ArrayDebugRegister[i].dwThreadId != 0)
|
|
{
|
|
if (ArrayDebugRegister[i].dwThreadId == HandleToULong(NtCurrentTeb()->ClientId.UniqueThread))
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int ThreadDebugContextFindFreeSlotIndex()
|
|
{
|
|
for (int i = 0; i < _countof(ArrayDebugRegister); i++)
|
|
{
|
|
if (ArrayDebugRegister[i].dwThreadId == 0)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// GetSystemTime and GetLocalTime are reimplemented here because the KernelBase functions use
|
|
// RIP-relative addressing which breaks hooking. https://github.com/x64dbg/ScyllaHide/issues/31
|
|
void NTAPI RealGetSystemTime(PSYSTEMTIME lpSystemTime)
|
|
{
|
|
TIME_FIELDS TimeFields;
|
|
RtlTimeToTimeFields((PLARGE_INTEGER)& SharedUserData->SystemTime, &TimeFields);
|
|
|
|
lpSystemTime->wYear = TimeFields.Year;
|
|
lpSystemTime->wMonth = TimeFields.Month;
|
|
lpSystemTime->wDay = TimeFields.Day;
|
|
lpSystemTime->wHour = TimeFields.Hour;
|
|
lpSystemTime->wMinute = TimeFields.Minute;
|
|
lpSystemTime->wSecond = TimeFields.Second;
|
|
lpSystemTime->wMilliseconds = TimeFields.Milliseconds;
|
|
lpSystemTime->wDayOfWeek = TimeFields.Weekday;
|
|
}
|
|
|
|
void NTAPI RealGetLocalTime(LPSYSTEMTIME lpSystemTime)
|
|
{
|
|
TIME_FIELDS TimeFields;
|
|
LARGE_INTEGER SystemTime = *(PLARGE_INTEGER)& SharedUserData->SystemTime;
|
|
LARGE_INTEGER TimeZoneBias = *(PLARGE_INTEGER)& SharedUserData->TimeZoneBias;
|
|
|
|
SystemTime.QuadPart -= TimeZoneBias.QuadPart;
|
|
RtlTimeToTimeFields(&SystemTime, &TimeFields);
|
|
|
|
lpSystemTime->wYear = TimeFields.Year;
|
|
lpSystemTime->wMonth = TimeFields.Month;
|
|
lpSystemTime->wDay = TimeFields.Day;
|
|
lpSystemTime->wHour = TimeFields.Hour;
|
|
lpSystemTime->wMinute = TimeFields.Minute;
|
|
lpSystemTime->wSecond = TimeFields.Second;
|
|
lpSystemTime->wMilliseconds = TimeFields.Milliseconds;
|
|
lpSystemTime->wDayOfWeek = TimeFields.Weekday;
|
|
}
|
|
|
|
void IncreaseSystemTime(LPSYSTEMTIME lpTime)
|
|
{
|
|
lpTime->wMilliseconds++;
|
|
|
|
//The hour. The valid values for this member are 0 through 23.
|
|
//The minute. The valid values for this member are 0 through 59.
|
|
//The second. The valid values for this member are 0 through 59.
|
|
//The millisecond. The valid values for this member are 0 through 999.
|
|
|
|
if (lpTime->wMilliseconds > 999)
|
|
{
|
|
lpTime->wSecond++;
|
|
lpTime->wMilliseconds = 0;
|
|
|
|
if (lpTime->wSecond > 59)
|
|
{
|
|
lpTime->wMinute++;
|
|
lpTime->wSecond = 0;
|
|
|
|
if (lpTime->wMinute > 59)
|
|
{
|
|
lpTime->wHour++;
|
|
lpTime->wMinute = 0;
|
|
|
|
if (lpTime->wHour > 23)
|
|
{
|
|
lpTime->wDay++;
|
|
lpTime->wDayOfWeek++;
|
|
lpTime->wHour = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BYTE memory[sizeof(IMAGE_NT_HEADERS) + 0x100] = {0};
|
|
|
|
void DumpMalware(DWORD dwProcessId)
|
|
{
|
|
OBJECT_ATTRIBUTES attributes = { sizeof(OBJECT_ATTRIBUTES) };
|
|
CLIENT_ID clientId = { ULongToHandle(dwProcessId) };
|
|
HANDLE hProcess;
|
|
NTSTATUS status = NtOpenProcess(&hProcess, PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, &attributes, &clientId);
|
|
if (!NT_SUCCESS(status))
|
|
return;
|
|
|
|
PPEB peb = (PPEB)GetPEBRemote(hProcess);
|
|
if (peb)
|
|
{
|
|
DWORD_PTR imagebase = 0;
|
|
NtReadVirtualMemory(hProcess, &peb->ImageBaseAddress, &imagebase, sizeof(DWORD_PTR), nullptr);
|
|
|
|
NtReadVirtualMemory(hProcess, (PVOID)imagebase, memory, sizeof(memory), nullptr);
|
|
|
|
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)memory;
|
|
if (pDos->e_magic == IMAGE_DOS_SIGNATURE)
|
|
{
|
|
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);
|
|
if (pNt->Signature == IMAGE_NT_SIGNATURE)
|
|
{
|
|
PVOID tempMem = nullptr;
|
|
SIZE_T size = pNt->OptionalHeader.SizeOfImage;
|
|
status = NtAllocateVirtualMemory(NtCurrentProcess, &tempMem, 0, &size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
NtReadVirtualMemory(hProcess, (PVOID)imagebase, tempMem, pNt->OptionalHeader.SizeOfImage, nullptr);
|
|
|
|
WriteMalwareToDisk(tempMem, pNt->OptionalHeader.SizeOfImage, imagebase);
|
|
|
|
size = 0;
|
|
NtFreeVirtualMemory(NtCurrentProcess, &tempMem, &size, MEM_RELEASE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
NtClose(hProcess);
|
|
}
|
|
|
|
WCHAR MalwareFile[MAX_PATH] = {0};
|
|
const WCHAR MalwareFilename[] = L"Unpacked.exe";
|
|
|
|
bool WriteMalwareToDisk(LPCVOID buffer, DWORD bufferSize, DWORD_PTR imagebase)
|
|
{
|
|
if (MalwareFile[0] == 0)
|
|
{
|
|
PUNICODE_STRING imagePath = &NtCurrentPeb()->ProcessParameters->ImagePathName;
|
|
ULONG size = MIN(sizeof(MalwareFile) - 1, imagePath->Length);
|
|
RtlCopyMemory(MalwareFile, imagePath->Buffer, size);
|
|
MalwareFile[size / sizeof(WCHAR)] = L'\0';
|
|
|
|
for (int i = (int)(size / sizeof(WCHAR)) - 1; i >= 0; i--)
|
|
{
|
|
if (MalwareFile[i] == L'\\')
|
|
{
|
|
MalwareFile[i+1] = L'\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
wcscat(MalwareFile, MalwareFilename);
|
|
}
|
|
|
|
return WriteMemoryToFile(MalwareFile, buffer,bufferSize, imagebase);
|
|
}
|
|
|
|
bool WriteMemoryToFile(const WCHAR * filename, LPCVOID buffer, DWORD bufferSize, DWORD_PTR imagebase)
|
|
{
|
|
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
|
|
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew);
|
|
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
|
|
|
|
UNICODE_STRING NtPath;
|
|
if (!RtlDosPathNameToNtPathName_U(filename, &NtPath, nullptr, nullptr))
|
|
return false;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
IO_STATUS_BLOCK ioStatusBlock;
|
|
InitializeObjectAttributes(&objectAttributes, &NtPath, OBJ_CASE_INSENSITIVE, nullptr, nullptr);
|
|
|
|
HANDLE hFile;
|
|
NTSTATUS status = NtCreateFile(&hFile,
|
|
FILE_GENERIC_WRITE,
|
|
&objectAttributes,
|
|
&ioStatusBlock,
|
|
nullptr,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
nullptr,
|
|
0);
|
|
if (!NT_SUCCESS(status))
|
|
return false;
|
|
|
|
status = NtWriteFile(hFile, nullptr, nullptr, nullptr, &ioStatusBlock, (PVOID)buffer,
|
|
pNt->OptionalHeader.SizeOfHeaders, nullptr, nullptr);
|
|
|
|
for (WORD i = 0; i < pNt->FileHeader.NumberOfSections; i++)
|
|
{
|
|
status = NtWriteFile(hFile, nullptr, nullptr, nullptr, &ioStatusBlock, (BYTE *)buffer + pSection->VirtualAddress,
|
|
pSection->SizeOfRawData, nullptr, nullptr);
|
|
pSection++;
|
|
}
|
|
NtClose(hFile);
|
|
|
|
return NT_SUCCESS(status);
|
|
}
|