ScllaTest 若干检测调试手段

从ScyllaTest 学习若干64位 反调试手法

AntiDbg是一个繁多且深入的领域,前人们的奇思构想,深入研究 诞生了许多经典的反调试方法,常见的已有20几种,若加上各种针对插件,调试器的主动攻击防御,恐有百种. 不过许多方法在最新win10 x64位已经失效,其中思想仍值得学习,目前开源的反反调试工具中十分出色的是ScyllaHide,这里仅针对ScyllaTest用到的反调试手段进行总结

Process Environment Block(PEB)

最重要的反调试检测手段,几乎所有的反调试器都会检测PEB的若干字段

  • BeingDebugged: IsDebuggerPresent() check this value
TestResult Check_PEB_BeingDebugged()
{
	PEB* peb = GetPebAddress(g_proc_handle);
	TEST_FAIL_IF(!peb);
	return TEST_CHECK(peb->BeingDebugged == 0);
}
  • NtGlobalFlag
TestResult Check_PEB_NtGlobalFlag()
{
	DWORD bad_flags = FLG_HEAP_ENABLE_FREE | FLG_HEAP_ENABLE_TAIL | FLG_HEAP_VALIDATE_PARAMETERS;
	PEB* peb = GetPebAddress(g_proc_handle);
	TEST_FAIL_IF(!peb);
	return TEST_CHECK((peb->NtGlobalFlag & bad_flags) == 0);
}
  • HeapFlags
TestResult Check_PEB_HeapFlags()
{
	DWORD bad_flags = HEAP_TAIL_CHECKING_ENABLED | HEAP_FREE_CHECKING_ENABLED | HEAP_SKIP_VALIDATION_CHECKS | HEAP_VALIDATE_PARAMETERS_ENABLED;
	PEB* peb = GetPebAddress(g_proc_handle);
	TEST_FAIL_IF(!peb);
	void** heaps = (void**)peb->ProcessHeaps;
	for (DWORD i = 0; i < peb->NumberOfHeaps; i++)
	{
		DWORD flags = *(DWORD*)((BYTE*)heaps[i] + 0x70);
		DWORD force_flags = *(DWORD*)((BYTE*)heaps[i] + 0x74);

		if ((flags & bad_flags) || (force_flags & bad_flags))
			return TestDetected;
	}
	return TestOk;
}
  • StartupInfo: 原先调试器启动的process并不会清空startupinfo结构体内的值,ollydbg还会设置其中一个成员变量值,导致可以检测出差异,经过测试该方法对目前主流x64 dbg失效

  • ProcessParameters

TestResult Check_PEB_ProcessParameters()
{
	PEB* peb = GetPebAddress(g_proc_handle);
	TEST_FAIL_IF(!peb);
	
	return TEST_CHECK((peb->ProcessParameters->Flags & 0x4000) != 0);
}

Exception

  • OutputDebugString: 当没有调试器的时候,OutputDebugString会抛出异常,从而GetLastError会返回一个不同于预先设置的值
TestResult Check_OutputDebugStringA_LastError()
{
	DWORD last_error = 0xDEAD;
	SetLastError(last_error);
	OutputDebugStringA("I don't wish debugger recieve this message!");
	//printf("%d", GetLastError());
	return TEST_CHECK(GetLastError() != last_error);
}
  • RaiseException: 通过在一个try_catch块中抛出一个异常, 如果没有调试器,则异常会被except块捕获,从而实现检测,当存在调试器的时候,抛出的异常会被调试器捕获(2次机会),而如果调试器直接处理了异常,没有pass到程序,except块将不会被执行,从而实现检测, 应对方法是设置调试器收到异常交由被调试程序处理
TestResult Check_RaiseException()
{
	__try
	{
		RaiseException(0xdeadbeaf, 0, 0, 0);
		return TestDetected;
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return TestOk;
	}
}
  • NtClose: 向NtClose传入一个错误的句柄, 当没有调试器的时候NtClose会返回false,而有调试器时,该函数会抛出异常EXCEPTION_INVALID_HANDLE
TestResult Check_NtClose()
{
	__try
	{
		NtClose((HANDLE)(ULONG_PTR)0x1234);
		return TestOk;
	}
	__except (EXCEPTION_EXECUTE_HANDLER)
	{
		return GetExceptionCode() == EXCEPTION_INVALID_HANDLE
			? TestDetected
			: TestFail;
	}
}

NtQuery***

  • CheckRemoteDebuggerPresent: 函数内部调用NtQueryInformationProcess 传入(ProcessDebugPort)检测返回值
TestResult Check_CheckRemoteDebuggerPresent()
{
	BOOL present;
	CheckRemoteDebuggerPresent(g_proc_handle, &present);
	return TEST_CHECK(!present);
}
  • NtQueryInformationProcess
  1. ProcessDebugPort:
TestResult Check_NtQueryInformationProcess_ProcessDebugPort()
{
	HANDLE handle = nullptr;
	TEST_FAIL_IF(!NT_SUCCESS(NtQueryInformationProcess(g_proc_handle, ProcessDebugPort, &handle, sizeof(handle), nullptr)));
	return TEST_CHECK(handle == nullptr);
}
  1. KernelDebugger:
TestResult Check_NtQuerySystemInformation_KernelDebugger()
{
	SYSTEM_KERNEL_DEBUGGER_INFORMATION SysKernDebInfo;

	TEST_FAIL_IF(!NT_SUCCESS(NtQuerySystemInformation(SystemKernelDebuggerInformation, &SysKernDebInfo, sizeof(SysKernDebInfo), NULL)));

	if (SysKernDebInfo.KernelDebuggerEnabled || !SysKernDebInfo.KernelDebuggerNotPresent)
	{
		return TestDetected;
	}
	return TestOk;
}

Misc

  • OtherOperationCount: 当进程被调试的时候,如果调用了NtMapViewOfSection 调试器会把Section 映射进空间 从而使IO count中OtherOperationCount计数器增加数值
ULONGLONG GetOtherOperationCount()
{
	DWORD size;
	NtQuerySystemInformation(SystemProcessInformation, nullptr, 0, &size);
	PSYSTEM_PROCESS_INFORMATION SystemProcessInfo = (PSYSTEM_PROCESS_INFORMATION)RtlAllocateHeap(RtlProcessHeap(), HEAP_ZERO_MEMORY, size * 2);
	NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, SystemProcessInfo, size * 2, nullptr);
	PSYSTEM_PROCESS_INFORMATION entry = SystemProcessInfo;

	ULONGLONG otherOperationCount = 0;

	while (1)
	{
		//wprintf(L"%s\n", entry->ImageName.Buffer);
		if (entry->UniqueProcessId == NtCurrentTeb()->ClientId.UniqueProcess)
		{
			otherOperationCount = entry->OtherOperationCount.QuadPart;
			break;
		}
		if (entry->NextEntryOffset == 0)
			break;
		entry = (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)entry + entry->NextEntryOffset);
	}

	return otherOperationCount;
}

TestResult Check_OtherOperationCount()
{
	//Just open a file
	IO_STATUS_BLOCK ioStatusBlock;
	UNICODE_STRING ntdllPath = RTL_CONSTANT_STRING(L"\\SystemRoot\\System32\\ntdll.dll");
	OBJECT_ATTRIBUTES objectAttributes = RTL_CONSTANT_OBJECT_ATTRIBUTES((PUNICODE_STRING)&ntdllPath, OBJ_CASE_INSENSITIVE);
	HANDLE fileHandle;
	NTSTATUS status = NtCreateFile(&fileHandle,
		SYNCHRONIZE | FILE_EXECUTE,
		&objectAttributes,
		&ioStatusBlock,
		nullptr,
		FILE_ATTRIBUTE_NORMAL,
		FILE_SHARE_READ,
		FILE_OPEN,
		FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
		nullptr,
		0);
	
	//Create section. SEC_IMAGE
	HANDLE SectionHandle;
	status = NtCreateSection(&SectionHandle,
		SECTION_MAP_EXECUTE,
		nullptr,
		nullptr,
		PAGE_EXECUTE,
		SEC_IMAGE,
		fileHandle);

	NtClose(fileHandle);

	//Query other operation count (before map)
	ULONGLONG otherOperationCountBefore = GetOtherOperationCount();
	
	//Map a view of section
	PVOID baseAddress = nullptr;
	SIZE_T viewSize = 0;
	NtMapViewOfSection(SectionHandle,
		g_proc_handle,
		&baseAddress,
		0,
		0,
		nullptr,
		&viewSize,
		ViewUnmap,
		0,
		PAGE_EXECUTE);

	NtClose(SectionHandle);

	//Query other operation count (after map)
	ULONGLONG otherOperationCountAfter = GetOtherOperationCount();

	NtUnmapViewOfSection(g_proc_handle, baseAddress);

	//if the other operation count was incremented, the image was mapped into a debugger.
	if (otherOperationCountAfter > otherOperationCountBefore)
		return TestDetected;

	return TestOk;
}
## Features ### Anti-debugging attacks - IsDebuggerPresent - CheckRemoteDebuggerPresent - Process Environement Block (BeingDebugged) - Process Environement Block (NtGlobalFlag) - ProcessHeap (Flags) - ProcessHeap (ForceFlags) - NtQueryInformationProcess (ProcessDebugPort) - NtQueryInformationProcess (ProcessDebugFlags) - NtQueryInformationProcess (ProcessDebugObject) - NtSetInformationThread (HideThreadFromDebugger) - NtQueryObject (ObjectTypeInformation) - NtQueryObject (ObjectAllTypesInformation) - CloseHanlde (NtClose) Invalide Handle - SetHandleInformation (Protected Handle) - UnhandledExceptionFilter - OutputDebugString (GetLastError()) - Hardware Breakpoints (SEH / GetThreadContext) - Software Breakpoints (INT3 / 0xCC) - Memory Breakpoints (PAGE_GUARD) - Interrupt 0x2d - Interrupt 1 - Parent Process (Explorer.exe) - SeDebugPrivilege (Csrss.exe) - NtYieldExecution / SwitchToThread - TLS callbacks ### Anti-Dumping - Erase PE header from memory - SizeOfImage ### Timing Attacks [Anti-Sandbox] - RDTSC (with CPUID to force a VM Exit) - RDTSC (Locky version with GetProcessHeap & CloseHandle) - Sleep -> SleepEx -> NtDelayExecution - Sleep (in a loop a small delay) - Sleep and check if time was accelerated (GetTickCount) - SetTimer (Standard Windows Timers) - timeSetEvent (Multimedia Timers) - WaitForSingleObject -> WaitForSingleObjectEx -> NtWaitForSingleObject - WaitForMultipleObjects -> WaitForMultipleObjectsEx -> NtWaitForMultipleObjects (todo) - IcmpSendEcho (CCleaner Malware) - CreateWaitableTimer (todo) - CreateTimerQueueTimer (todo) - Big crypto loops (todo) ### Human Interaction / Generic [Anti-Sandbox] - Mouse movement - Total Physical memory (GlobalMemoryStatusEx) - Disk size using DeviceIoControl (IOCTL_DISK_GET_LENGTH_INFO) - Disk size using GetDiskFreeSpaceEx (TotalNumberOfBytes) - Mouse (Single click / Double click) (todo) - DialogBox (todo) - Scrolling (todo) - Execution after reboot (todo) - Count of processors (Win32/Tinba - Win32/Dyre) - Sandbox k
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值