mirror of
https://github.com/mrexodia/TitanHide
synced 2026-08-13 04:23:04 -04:00
Close additional anti-debug side channels
This commit is contained in:
parent
7b7b3a00f5
commit
b13a71ca7a
3 changed files with 337 additions and 22 deletions
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
|
|
@ -22,10 +22,10 @@ jobs:
|
|||
msbuild.exe ${{ github.event.repository.name }}.sln /m /verbosity:minimal "/t:TitanHideGUI:Rebuild;TitanHideTest:Rebuild;TitanHide_x64dbg:Rebuild;TitanHide_TitanEngine:Rebuild" /p:Configuration=Release /p:Platform=x64
|
||||
msbuild.exe ${{ github.event.repository.name }}.sln /m /verbosity:minimal "/t:TitanHideGUI:Rebuild;TitanHideTest:Rebuild;TitanHide_x64dbg:Rebuild;TitanHide_OllyDbg:Rebuild;TitanHide_TitanEngine:Rebuild" /p:Configuration=Release /p:Platform=Win32
|
||||
|
||||
- name: Verify native ProcessDebugObjectHandle contract
|
||||
- name: Verify native anti-debug contracts
|
||||
run: |
|
||||
.\Release\TitanHideTest.exe --process-debug-object-contract
|
||||
.\x64\Release\TitanHideTest.exe --process-debug-object-contract
|
||||
.\Release\TitanHideTest.exe --native-contracts
|
||||
.\x64\Release\TitanHideTest.exe --native-contracts
|
||||
|
||||
- name: Install Visual Studio 2019 Build Tools
|
||||
shell: pwsh
|
||||
|
|
|
|||
|
|
@ -18,6 +18,19 @@ static HOOK hNtSystemDebugControl = 0;
|
|||
static HOOK hNtCreateThreadEx = 0;
|
||||
static KMUTEX gDebugPortMutex;
|
||||
|
||||
struct VIRTUAL_THREAD_HIDE_ENTRY
|
||||
{
|
||||
HANDLE ProcessId;
|
||||
HANDLE ThreadId;
|
||||
PETHREAD Thread;
|
||||
};
|
||||
|
||||
#define MAX_VIRTUAL_THREAD_HIDE_ENTRIES 4096
|
||||
static VIRTUAL_THREAD_HIDE_ENTRY gVirtualThreadHideEntries[MAX_VIRTUAL_THREAD_HIDE_ENTRIES];
|
||||
static ULONG gVirtualThreadHideEntryCount = 0;
|
||||
static KSPIN_LOCK gVirtualThreadHideLock;
|
||||
static bool gThreadNotifyRegistered = false;
|
||||
|
||||
//https://forum.tuts4you.com/topic/40011-debugme-vmprotect-312-build-886-anti-debug-method-improved/#comment-192824
|
||||
//https://github.com/x64dbg/ScyllaHide/issues/47
|
||||
//https://github.com/mrexodia/TitanHide/issues/27
|
||||
|
|
@ -55,6 +68,85 @@ static bool IsDebugObjectTypeInformation(
|
|||
return RtlCompareMemory(InlineTypeName, DebugObject->Buffer, DebugObject->Length) == DebugObject->Length;
|
||||
}
|
||||
|
||||
static void RemoveVirtualThreadHide(HANDLE ProcessId, HANDLE ThreadId)
|
||||
{
|
||||
PETHREAD Thread = nullptr;
|
||||
KIRQL Irql;
|
||||
KeAcquireSpinLock(&gVirtualThreadHideLock, &Irql);
|
||||
for(ULONG i = 0; i < gVirtualThreadHideEntryCount; i++)
|
||||
{
|
||||
if(gVirtualThreadHideEntries[i].ProcessId == ProcessId &&
|
||||
gVirtualThreadHideEntries[i].ThreadId == ThreadId)
|
||||
{
|
||||
Thread = gVirtualThreadHideEntries[i].Thread;
|
||||
gVirtualThreadHideEntries[i] =
|
||||
gVirtualThreadHideEntries[--gVirtualThreadHideEntryCount];
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinLock(&gVirtualThreadHideLock, Irql);
|
||||
if(Thread != nullptr)
|
||||
ObDereferenceObject(Thread);
|
||||
}
|
||||
|
||||
static bool RegisterVirtualThreadHide(PETHREAD Thread)
|
||||
{
|
||||
VIRTUAL_THREAD_HIDE_ENTRY Entry;
|
||||
Entry.ProcessId = PsGetProcessId(PsGetThreadProcess(Thread));
|
||||
Entry.ThreadId = PsGetThreadId(Thread);
|
||||
Entry.Thread = Thread;
|
||||
|
||||
KIRQL Irql;
|
||||
KeAcquireSpinLock(&gVirtualThreadHideLock, &Irql);
|
||||
for(ULONG i = 0; i < gVirtualThreadHideEntryCount; i++)
|
||||
{
|
||||
if(gVirtualThreadHideEntries[i].Thread == Thread)
|
||||
{
|
||||
KeReleaseSpinLock(&gVirtualThreadHideLock, Irql);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const bool Registered = gVirtualThreadHideEntryCount < MAX_VIRTUAL_THREAD_HIDE_ENTRIES;
|
||||
if(Registered)
|
||||
{
|
||||
ObReferenceObject(Thread);
|
||||
gVirtualThreadHideEntries[gVirtualThreadHideEntryCount++] = Entry;
|
||||
}
|
||||
KeReleaseSpinLock(&gVirtualThreadHideLock, Irql);
|
||||
|
||||
// If exit notification raced ahead of registration, remove the entry now.
|
||||
if(Registered && PsIsThreadTerminating(Thread))
|
||||
{
|
||||
RemoveVirtualThreadHide(Entry.ProcessId, Entry.ThreadId);
|
||||
return false;
|
||||
}
|
||||
return Registered;
|
||||
}
|
||||
|
||||
static bool HasVirtualThreadHide(PETHREAD Thread)
|
||||
{
|
||||
bool Found = false;
|
||||
KIRQL Irql;
|
||||
KeAcquireSpinLock(&gVirtualThreadHideLock, &Irql);
|
||||
for(ULONG i = 0; i < gVirtualThreadHideEntryCount; i++)
|
||||
{
|
||||
if(gVirtualThreadHideEntries[i].Thread == Thread)
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
KeReleaseSpinLock(&gVirtualThreadHideLock, Irql);
|
||||
return Found;
|
||||
}
|
||||
|
||||
static void ThreadNotifyRoutine(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create)
|
||||
{
|
||||
if(!Create)
|
||||
RemoveVirtualThreadHide(ProcessId, ThreadId);
|
||||
}
|
||||
|
||||
static NTSTATUS NTAPI HookNtQueryInformationThread(
|
||||
IN HANDLE ThreadHandle,
|
||||
IN THREADINFOCLASS ThreadInformationClass,
|
||||
|
|
@ -98,8 +190,8 @@ static NTSTATUS NTAPI HookNtQueryInformationThread(
|
|||
ProbeForWrite(&Wow64Context->ContextFlags, sizeof(ULONG), 1);
|
||||
Wow64Context->ContextFlags = OriginalContextFlags;
|
||||
|
||||
// If debug registers were requested, zero user input
|
||||
if(DebugRegistersRequested)
|
||||
// If debug registers were requested successfully, zero user input.
|
||||
if(NT_SUCCESS(Status) && DebugRegistersRequested)
|
||||
{
|
||||
Wow64Context->Dr0 = 0;
|
||||
Wow64Context->Dr1 = 0;
|
||||
|
|
@ -123,7 +215,8 @@ static NTSTATUS NTAPI HookNtQueryInformationThread(
|
|||
|
||||
if(NT_SUCCESS(Status) && ThreadInformationClass == ThreadHideFromDebugger)
|
||||
{
|
||||
if(Hider::IsHidden(pid, HideThreadHideFromDebugger) &&
|
||||
if(gThreadNotifyRegistered &&
|
||||
Hider::IsHidden(pid, HideThreadHideFromDebugger) &&
|
||||
Hider::IsHidden(targetPid, HideThreadHideFromDebugger))
|
||||
{
|
||||
Log("[TITANHIDE] NtQueryInformationThread(ThreadHideFromDebugger) by %d\r\n", pid);
|
||||
|
|
@ -132,8 +225,20 @@ static NTSTATUS NTAPI HookNtQueryInformationThread(
|
|||
{
|
||||
BACKUP_RETURNLENGTH();
|
||||
|
||||
// Since they're asking, assume they're expecting "yes"
|
||||
*(BOOLEAN*)ThreadInformation = TRUE;
|
||||
PETHREAD Thread = nullptr;
|
||||
NTSTATUS ReferenceStatus = ObReferenceObjectByHandle(
|
||||
ThreadHandle,
|
||||
0,
|
||||
*PsThreadType,
|
||||
ExGetPreviousMode(),
|
||||
(PVOID*)&Thread,
|
||||
nullptr);
|
||||
if(NT_SUCCESS(ReferenceStatus))
|
||||
{
|
||||
if(HasVirtualThreadHide(Thread))
|
||||
*(BOOLEAN*)ThreadInformation = TRUE;
|
||||
ObDereferenceObject(Thread);
|
||||
}
|
||||
|
||||
RESTORE_RETURNLENGTH();
|
||||
}
|
||||
|
|
@ -158,7 +263,7 @@ static NTSTATUS NTAPI HookNtSetInformationThread(
|
|||
//Bug found by Aguila, thanks!
|
||||
if(ThreadInformationClass == ThreadHideFromDebugger && !ThreadInformationLength)
|
||||
{
|
||||
if(Hider::IsHidden(pid, HideThreadHideFromDebugger))
|
||||
if(gThreadNotifyRegistered && Hider::IsHidden(pid, HideThreadHideFromDebugger))
|
||||
{
|
||||
Log("[TITANHIDE] NtSetInformationThread(ThreadHideFromDebugger) by %d\r\n", pid);
|
||||
PETHREAD Thread;
|
||||
|
|
@ -169,7 +274,16 @@ static NTSTATUS NTAPI HookNtSetInformationThread(
|
|||
(PVOID*)&Thread,
|
||||
NULL);
|
||||
if(NT_SUCCESS(status))
|
||||
{
|
||||
const bool Registered = RegisterVirtualThreadHide(Thread);
|
||||
ObDereferenceObject(Thread);
|
||||
if(!Registered)
|
||||
return Undocumented::NtSetInformationThread(
|
||||
ThreadHandle,
|
||||
ThreadInformationClass,
|
||||
ThreadInformation,
|
||||
ThreadInformationLength);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
|
@ -500,25 +614,49 @@ static NTSTATUS NTAPI HookNtQueryObject(
|
|||
|
||||
OBJECT_ALL_INFORMATION* pObjectAllInfo = (OBJECT_ALL_INFORMATION*)ObjectInformation;
|
||||
unsigned char* pObjInfoLocation = (unsigned char*)pObjectAllInfo->ObjectTypeInformation;
|
||||
unsigned char* BufferEnd = (unsigned char*)ObjectInformation + ObjectInformationLength;
|
||||
if(BufferEnd < (unsigned char*)ObjectInformation)
|
||||
{
|
||||
RESTORE_RETURNLENGTH();
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned int TotalObjects = pObjectAllInfo->NumberOfObjects;
|
||||
for(unsigned int i = 0; i < TotalObjects; i++)
|
||||
{
|
||||
if(pObjInfoLocation > BufferEnd ||
|
||||
(SIZE_T)(BufferEnd - pObjInfoLocation) < sizeof(OBJECT_TYPE_INFORMATION))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
OBJECT_TYPE_INFORMATION* pObjectTypeInfo = (OBJECT_TYPE_INFORMATION*)pObjInfoLocation;
|
||||
ProbeForRead(pObjectTypeInfo, 1, 1);
|
||||
ProbeForRead(pObjectTypeInfo->TypeName.Buffer, 1, 1);
|
||||
if(RtlEqualUnicodeString(&pObjectTypeInfo->TypeName, &DebugObject, FALSE)) //DebugObject
|
||||
ProbeForRead(pObjectTypeInfo, sizeof(OBJECT_TYPE_INFORMATION), 1);
|
||||
|
||||
// The name is inline after the fixed structure. TypeName.Buffer
|
||||
// may have been overwritten by an overlapping ReturnLength.
|
||||
unsigned char* InlineTypeName = (unsigned char*)(pObjectTypeInfo + 1);
|
||||
if(InlineTypeName > BufferEnd ||
|
||||
pObjectTypeInfo->TypeName.MaximumLength > (SIZE_T)(BufferEnd - InlineTypeName))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if(pObjectTypeInfo->TypeName.Length == DebugObject.Length &&
|
||||
pObjectTypeInfo->TypeName.Length <= pObjectTypeInfo->TypeName.MaximumLength &&
|
||||
RtlCompareMemory(InlineTypeName, DebugObject.Buffer, DebugObject.Length) == DebugObject.Length)
|
||||
{
|
||||
Log("[TITANHIDE] DebugObject by %d\r\n", pid);
|
||||
DEBUG_OBJECT_CONTRIBUTION Contribution;
|
||||
if(QueryDebugObjectContribution(&Contribution))
|
||||
RemoveDebugObjectContribution(pObjectTypeInfo, &Contribution);
|
||||
}
|
||||
pObjInfoLocation = (unsigned char*)pObjectTypeInfo->TypeName.Buffer;
|
||||
pObjInfoLocation += pObjectTypeInfo->TypeName.MaximumLength;
|
||||
ULONG_PTR tmp = ((ULONG_PTR)pObjInfoLocation) & -(LONG_PTR)sizeof(void*);
|
||||
if((ULONG_PTR)tmp != (ULONG_PTR)pObjInfoLocation)
|
||||
tmp += sizeof(void*);
|
||||
pObjInfoLocation = ((unsigned char*)tmp);
|
||||
|
||||
ULONG_PTR Next = (ULONG_PTR)InlineTypeName + pObjectTypeInfo->TypeName.MaximumLength;
|
||||
Next = (Next + sizeof(void*) - 1) & -(LONG_PTR)sizeof(void*);
|
||||
if(Next <= (ULONG_PTR)pObjInfoLocation || Next > (ULONG_PTR)BufferEnd)
|
||||
break;
|
||||
pObjInfoLocation = (unsigned char*)Next;
|
||||
}
|
||||
|
||||
RESTORE_RETURNLENGTH();
|
||||
|
|
@ -675,8 +813,8 @@ static NTSTATUS NTAPI HookNtGetContextThread(
|
|||
ProbeForWrite(&Context->ContextFlags, sizeof(ULONG), 1);
|
||||
Context->ContextFlags = OriginalContextFlags;
|
||||
|
||||
// If debug registers were requested, zero user input
|
||||
if(DebugRegistersRequested)
|
||||
// Failed queries must leave the caller's output untouched.
|
||||
if(NT_SUCCESS(ret) && DebugRegistersRequested)
|
||||
{
|
||||
Context->Dr0 = 0;
|
||||
Context->Dr1 = 0;
|
||||
|
|
@ -784,6 +922,10 @@ static NTSTATUS NTAPI HookNtCreateThreadEx(
|
|||
int Hooks::Initialize()
|
||||
{
|
||||
KeInitializeMutex(&gDebugPortMutex, 0);
|
||||
KeInitializeSpinLock(&gVirtualThreadHideLock);
|
||||
gVirtualThreadHideEntryCount = 0;
|
||||
gThreadNotifyRegistered = NT_SUCCESS(PsSetCreateThreadNotifyRoutine(ThreadNotifyRoutine));
|
||||
|
||||
int hook_count = 0;
|
||||
hNtQueryInformationProcess = SSDT::Hook("NtQueryInformationProcess", (void*)HookNtQueryInformationProcess);
|
||||
if(hNtQueryInformationProcess)
|
||||
|
|
@ -840,4 +982,16 @@ void Hooks::Deinitialize()
|
|||
{
|
||||
SSDT::Unhook(hNtCreateThreadEx, true);
|
||||
}
|
||||
|
||||
if(gThreadNotifyRegistered)
|
||||
{
|
||||
PsRemoveCreateThreadNotifyRoutine(ThreadNotifyRoutine);
|
||||
gThreadNotifyRegistered = false;
|
||||
}
|
||||
|
||||
while(gVirtualThreadHideEntryCount != 0)
|
||||
{
|
||||
PETHREAD Thread = gVirtualThreadHideEntries[--gVirtualThreadHideEntryCount].Thread;
|
||||
ObDereferenceObject(Thread);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,6 +247,94 @@ bool HideFromDebugger()
|
|||
0));
|
||||
}
|
||||
|
||||
static DWORD WINAPI ContractThreadProc(PVOID)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CheckThreadHideFromDebuggerContract()
|
||||
{
|
||||
typedef NTSTATUS(NTAPI * NT_QUERY_INFORMATION_THREAD)(
|
||||
HANDLE, ULONG, PVOID, ULONG, PULONG);
|
||||
typedef NTSTATUS(NTAPI * NT_SET_INFORMATION_THREAD)(
|
||||
HANDLE, ULONG, PVOID, ULONG);
|
||||
|
||||
NT_QUERY_INFORMATION_THREAD NtQIT = (NT_QUERY_INFORMATION_THREAD)
|
||||
GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtQueryInformationThread");
|
||||
NT_SET_INFORMATION_THREAD NtSIT = (NT_SET_INFORMATION_THREAD)
|
||||
GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtSetInformationThread");
|
||||
if(NtQIT == nullptr || NtSIT == nullptr)
|
||||
return false;
|
||||
|
||||
HANDLE Thread = CreateThread(nullptr, 0, ContractThreadProc, nullptr, CREATE_SUSPENDED, nullptr);
|
||||
if(Thread == nullptr)
|
||||
return false;
|
||||
|
||||
bool Detected = false;
|
||||
BOOLEAN Hidden = TRUE;
|
||||
ULONG ReturnLength = 0;
|
||||
NTSTATUS Status = NtQIT(Thread, 0x11, &Hidden, sizeof(Hidden), &ReturnLength);
|
||||
if(!NT_SUCCESS(Status) || Hidden != FALSE || ReturnLength != sizeof(Hidden))
|
||||
{
|
||||
printf("ThreadHideFromDebugger initial-state mismatch: %08X, %u, %u\n",
|
||||
Status, Hidden, ReturnLength);
|
||||
Detected = true;
|
||||
}
|
||||
|
||||
Status = NtSIT(Thread, 0x11, nullptr, 0);
|
||||
if(!NT_SUCCESS(Status))
|
||||
{
|
||||
printf("ThreadHideFromDebugger set failed: %08X\n", Status);
|
||||
Detected = true;
|
||||
}
|
||||
|
||||
Hidden = FALSE;
|
||||
ReturnLength = 0;
|
||||
Status = NtQIT(Thread, 0x11, &Hidden, sizeof(Hidden), &ReturnLength);
|
||||
if(!NT_SUCCESS(Status) || Hidden != TRUE || ReturnLength != sizeof(Hidden))
|
||||
{
|
||||
printf("ThreadHideFromDebugger virtual-state mismatch: %08X, %u, %u\n",
|
||||
Status, Hidden, ReturnLength);
|
||||
Detected = true;
|
||||
}
|
||||
|
||||
TerminateThread(Thread, 0);
|
||||
CloseHandle(Thread);
|
||||
return Detected;
|
||||
}
|
||||
|
||||
bool CheckGetContextFailureContract()
|
||||
{
|
||||
typedef NTSTATUS(NTAPI * NT_GET_CONTEXT_THREAD)(HANDLE, PCONTEXT);
|
||||
NT_GET_CONTEXT_THREAD NtGCT = (NT_GET_CONTEXT_THREAD)
|
||||
GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtGetContextThread");
|
||||
if(NtGCT == nullptr)
|
||||
return false;
|
||||
|
||||
HANDLE LimitedThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION,
|
||||
FALSE,
|
||||
GetCurrentThreadId());
|
||||
if(LimitedThread == nullptr)
|
||||
return false;
|
||||
|
||||
__declspec(align(16)) CONTEXT Context;
|
||||
memset(&Context, 0xA5, sizeof(Context));
|
||||
Context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
CONTEXT OriginalContext;
|
||||
memcpy(&OriginalContext, &Context, sizeof(Context));
|
||||
|
||||
NTSTATUS Status = NtGCT(LimitedThread, &Context);
|
||||
CloseHandle(LimitedThread);
|
||||
|
||||
const NTSTATUS StatusAccessDenied = (NTSTATUS)0xC0000022L;
|
||||
if(Status != StatusAccessDenied || memcmp(&Context, &OriginalContext, sizeof(Context)) != 0)
|
||||
{
|
||||
printf("NtGetContextThread failure contract mismatch: %08X\n", Status);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
typedef struct _OBJECT_TYPE_INFORMATION
|
||||
{
|
||||
UNICODE_STRING TypeName;
|
||||
|
|
@ -469,6 +557,71 @@ bool CheckObjectList()
|
|||
}
|
||||
}
|
||||
|
||||
bool CheckObjectTypesInformationOverlapContract()
|
||||
{
|
||||
typedef NTSTATUS(NTAPI * pNtQueryObject)(
|
||||
HANDLE, OBJECT_INFORMATION_CLASS, PVOID, ULONG, PULONG);
|
||||
pNtQueryObject NtQO = (pNtQueryObject)GetProcAddress(
|
||||
GetModuleHandle(TEXT("ntdll.dll")),
|
||||
"NtQueryObject");
|
||||
if(NtQO == nullptr)
|
||||
return false;
|
||||
|
||||
ULONG Size = 0;
|
||||
NTSTATUS Status = NtQO(nullptr, ObjectTypesInformation, nullptr, 0, &Size);
|
||||
if(Status != (NTSTATUS)0xC0000004L || Size == 0)
|
||||
return false;
|
||||
|
||||
const ULONG BufferSize = Size + 0x10000;
|
||||
unsigned char* Buffer = (unsigned char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, BufferSize);
|
||||
if(Buffer == nullptr)
|
||||
return false;
|
||||
|
||||
ULONG ActualLength = 0;
|
||||
Status = NtQO(nullptr, ObjectTypesInformation, Buffer, BufferSize, &ActualLength);
|
||||
if(!NT_SUCCESS(Status))
|
||||
{
|
||||
printf("ObjectTypesInformation baseline query failed: %08X\n", Status);
|
||||
HeapFree(GetProcessHeap(), 0, Buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
OBJECT_ALL_INFORMATION* All = (OBJECT_ALL_INFORMATION*)Buffer;
|
||||
unsigned char* Location = (unsigned char*)All->ObjectTypeInformation;
|
||||
PULONG Overlap = nullptr;
|
||||
const wchar_t DebugObject[] = L"DebugObject";
|
||||
const USHORT DebugObjectLength = sizeof(DebugObject) - sizeof(wchar_t);
|
||||
for(ULONG i = 0; i < All->NumberOfObjects; i++)
|
||||
{
|
||||
OBJECT_TYPE_INFORMATION* Type = (OBJECT_TYPE_INFORMATION*)Location;
|
||||
if(Type->TypeName.Length == DebugObjectLength &&
|
||||
memcmp(Type->TypeName.Buffer, DebugObject, DebugObjectLength) == 0)
|
||||
{
|
||||
Overlap = (PULONG)&Type->TypeName.Buffer;
|
||||
break;
|
||||
}
|
||||
Location = (unsigned char*)Type->TypeName.Buffer + Type->TypeName.MaximumLength;
|
||||
Location = (unsigned char*)(((ULONG_PTR)Location + sizeof(void*) - 1) &
|
||||
-(LONG_PTR)sizeof(void*));
|
||||
}
|
||||
|
||||
bool Detected = Overlap == nullptr;
|
||||
if(Overlap == nullptr)
|
||||
puts("ObjectTypesInformation did not contain DebugObject");
|
||||
if(Overlap != nullptr)
|
||||
{
|
||||
Status = NtQO(nullptr, ObjectTypesInformation, Buffer, BufferSize, Overlap);
|
||||
if(!NT_SUCCESS(Status) || *Overlap != ActualLength)
|
||||
{
|
||||
printf("ObjectTypesInformation overlap contract mismatch: %08X\n", Status);
|
||||
Detected = true;
|
||||
}
|
||||
}
|
||||
|
||||
HeapFree(GetProcessHeap(), 0, Buffer);
|
||||
return Detected;
|
||||
}
|
||||
|
||||
enum PROCESSINFOCLASS
|
||||
{
|
||||
ProcessBasicInformation = 0, // 0, q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
|
||||
|
|
@ -605,8 +758,16 @@ bool CheckNtClose()
|
|||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if(argc == 2 && strcmp(argv[1], "--process-debug-object-contract") == 0)
|
||||
return CheckProcessDebugObjectHandle() ? 1 : 0;
|
||||
if(argc == 2 && strcmp(argv[1], "--native-contracts") == 0)
|
||||
{
|
||||
const bool ProcessDebugObject = CheckProcessDebugObjectHandle();
|
||||
const bool ThreadHide = CheckThreadHideFromDebuggerContract();
|
||||
const bool GetContextFailure = CheckGetContextFailureContract();
|
||||
const bool ObjectTypesOverlap = CheckObjectTypesInformationOverlapContract();
|
||||
printf("Native contracts: ProcessDebugObject=%d ThreadHide=%d GetContextFailure=%d ObjectTypesOverlap=%d\n",
|
||||
ProcessDebugObject, ThreadHide, GetContextFailure, ObjectTypesOverlap);
|
||||
return ProcessDebugObject || ThreadHide || GetContextFailure || ObjectTypesOverlap ? 1 : 0;
|
||||
}
|
||||
|
||||
char title[256] = "";
|
||||
sprintf_s(title, "pid: %d", (int)GetCurrentProcessId());
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue