r3反反调试插件漏洞利用
本帖内容:ThreadHideFromDebugger也能检测调试器
附件上传为 r3反调试.zip
注意
由于电脑坏了,所以本帖内容包括代码皆为手机编写,可能含有大量失误。
感谢群友oiGzewo与yeyue的测试。
参考链接
alkhaser
ScyllaHIDE
一个反调试笔记
简写
NtSetInfomationTheead set
NtQueryInfomationTheead query
看看反调试是如何实现的
例一
作者只是简单的获取了set的地址并调用set,没有任何的校验,这样的反调试是十分弱小的。
void HideFromDebugger(){
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
NtSetInformationThreadPtr NtSetInformationThread = (NtSetInformationThreadPtr)GetProcAddress(hNtDll,"NtSetInformationThread");
NTSTATUS status = NtSetInformationThread(GetCurrentThread(),ThreadHideFromDebugger,NULL,0);
}
例二
作者使用了set和query,并对一些反反调试可能存在的问题进行了测试。
但是query操作并未对ThreadHideFromDebugger标志位进行校验导致其能被sharp0d与ScyllaH1DE等插件anti。
auto NtSetInformationThread = static_cast<pNtSetInformationThread>(API::GetAPI(API_IDENTIFIER::API_NtSetInformationThread));
auto NtQueryInformationThread = static_cast<pNtQueryInformationThread>(API::GetAPI(API_IDENTIFIER::API_NtQueryInformationThread));
NTSTATUS Status;
bool doQITcheck = false;
if (API::IsAvailable(API_IDENTIFIER::API_NtQueryInformationThread))
{
doQITcheck = IsWindowsVistaOrGreater();
}
AlignedBool isThreadHidden;
isThreadHidden.Value = false;
Status = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &isThreadHidden, 12345);
if (Status == 0)
return TRUE;
Status = NtSetInformationThread((HANDLE)0xFFFF, ThreadHideFromDebugger, NULL, 0);
if (Status == 0)
return TRUE;
Status = NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);
if (Status == 0)
{
if (doQITcheck)
{
Status = NtQueryInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &isThreadHidden.Value, sizeof(bool), NULL);
if (Status == STATUS_INFO_LENGTH_MISMATCH)
{
return TRUE;
}
if (Status == 0)
{
AlignedBool bogusIsThreadHidden;
bogusIsThreadHidden.Value = false;
Status = NtQueryInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &bogusIsThreadHidden.Value, sizeof(BOOL), NULL);
if (Status != STATUS_INFO_LENGTH_MISMATCH)
{
return TRUE;
}
const size_t UnalignedCheckCount = 8;
bool bogusUnalignedValues[UnalignedCheckCount];
int alignmentErrorCount = 0;
#if _WIN64
const size_t MaxAlignmentCheckSuccessCount = 2;
#else
const size_t MaxAlignmentCheckSuccessCount = 4;
#endif
for (size_t i = 0; i < UnalignedCheckCount; i++)
{
Status = NtQueryInformationThread(GetCurrentThread(), ThreadHideFromDebugger, &(bogusUnalignedValues[i]), sizeof(BOOL), NULL);
if (Status == STATUS_DATATYPE_MISALIGNMENT)
{
alignmentErrorCount++;
}
}
if (UnalignedCheckCount - MaxAlignmentCheckSuccessCount > alignmentErrorCount)
{
return TRUE;
}
return isThreadHidden.Value ? FALSE : TRUE;
}
}
}
else
{
return TRUE;
}
return FALSE;
看看反反调试是如何实现的
这里用scyllaHide的源码了解
可以看到,虽然scylla hide对参数进行了校验,但是,scyllaHide并未保存线程状态,并未对query函数进行hook来防止反调试通过set+query的方式检测调试器。
NTSTATUS NTAPI HookedNtSetInformationThread(HANDLE ThreadHandle, THREADINFOCLASS ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength)
{
if (ThreadInformationClass == ThreadHideFromDebugger && ThreadInformationLength == 0)
{
if (ThreadHandle == NtCurrentThread ||
HandleToULong(NtCurrentTeb()->ClientId.UniqueProcess) == GetProcessIdByThreadHandle(ThreadHandle))
{
return STATUS_SUCCESS;
}
}
return HookDllData.dNtSetInformationThread(ThreadHandle, ThreadInformationClass, ThreadInformation, ThreadInformationLength);
}
反 反反调试
我们要做步骤如下:
调用set
调用query进行校验
具体看代码和注释
基于https:
#include <windows.h>
#include <iostream>
typedef DWORD (WINAPI *ZW_SET_INFORMATION_THREAD)(HANDLE, DWORD, PVOID, ULONG);
typedef DWORD (WINAPI *ZW_QUERY_INFORMATION_THREAD)(HANDLE, DWORD, PVOID, ULONG,PULONG);
#define ThreadHideFromDebugger 17
VOID DisableDebugEvent(VOID)
{
HINSTANCE hModule;
ZW_SET_INFORMATION_THREAD ZwSetInformationThread;
ZW_QUERY_INFORMATION_THREAD query;
hModule = GetModuleHandleA("Ntdll");
query = (ZW_QUERY_INFORMATION_THREAD)GetProcAddress(hModule, "ZwQueryInformationThread");
ZwSetInformationThread =(ZW_SET_INFORMATION_THREAD)GetProcAddress(hModule, "ZwSetInformationThread");
bool lRet = NULL;
query(GetCurrentThread(), ThreadHideFromDebugger, &lRet,1, NULL);
std::cout<<lRet<<std::endl;
if(ZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, NULL)==0)
{
query(GetCurrentThread(), ThreadHideFromDebugger, &lRet,1, NULL);
std::cout<<lRet<<std::endl;
if(lRet==false)
{
std::cout<<"检测到调试"<<std::endl;
}
}
else
{
std::cout<<"环境异常"<<std::endl;
}
}
int main()
{
DisableDebugEvent();
getchar();
return 0;
}
总结
这个反调试代码依旧十分弱小。
检测和校验,应该分开,不应该在一起。
校验不应该立即执行。
可以重载dll让反调试更隐秘,同时可以学习下al-khaser的写法,让反调试更完善。
可以通过scyllaHide和al-khaser进行学习。
ps:还有很多函数能这么玩。
另外
国产调试器cpudbg缺少个名字,坛友来取个名?最好有英文和中文。
效果
展示一下朋友发的测试sharp0d与scylla h1de图。
输出01为正常运行,输出00则为异常。