我遇到了一个名为《终极反反向参考》的文档,该文档描述了各种反调试技术。在第4点中。线程本地存储有提到


每当创建线程或销毁线程时都会调用线程本地存储回调(除非进程调用kernel32
DisableThreadLibraryCalls()或ntdll
LdrDisableThreadCalloutsForDll()函数)。其中包括调试器附加到进程时Windows创建的线程

调试器线程是特殊的,因为它的入口点不指向映像内部。相反,它指向kernel32.dll内部。因此,
一种简单的调试器检测方法是使用线程本地存储
回调查询所创建的每个线程的起始地址。
可以使用此32位代码进行检查。在32位或64位版本的Windows上检查32位Windows环境



 push eax
 mov eax, esp
 push 0
 push 4
 push eax
 ;ThreadQuerySetWin32StartAddress
 push 9
 push -2 ;GetCurrentThread()
 call NtQueryInformationThread
 pop eax
 cmp eax, offset l1
 jnb being_debugged
 ...


我编写了如下的c ++代码

bool fooBar()
{
    uintptr_t dwStartAddress;
    TFNNtQueryInformationThread ntQueryInformationThread = (TFNNtQueryInformationThread)GetProcAddress(
    GetModuleHandle(TEXT("ntdll.dll")), "NtQueryInformationThread");

    if (ntQueryInformationThread != 0) {

        NTSTATUS status = ntQueryInformationThread(
            (HANDLE)-2,
            (_THREADINFOCLASS)9,
            &dwStartAddress,
            sizeof(dwStartAddress),
            nullptr);
       cout << hex << "dwStartAddress: 0x" << dwStartAddress << dec << endl;
    }


,我正在TLS回调中运行此代码
/> dwStartAddress的值指向.exe模块,而不是文本中指出的kernel32.dll。无论我是运行exe还是在调试器中运行,还是将调试器附加到进程中(我对附加的经验都不太丰富,所以也许我在这里做错了)。

我是在做错什么,还是文本错误/不再有效?

评论

您是否在可执行文件的主要功能内运行无限循环?应该为每个创建的线程调用TLS回调。在main之前执行一次,在运行时附加到该进程执行一次。

我使用了system(“ pause”),但现在更改为无限循环,仍然没有输出表明TLS回调被称为第二次

我认为我已经尝试了所有方法,但无法获得调试器来生成本文档中所述的线程。我正在使用Windows10。如果有人可以在Windows 10和其他操作系统上进行检查,我将非常感激

今天,我再次遇到了这种情况,但是在我的情况下,在调试附加上调用了TLS回调。我明天带你看看

我调试了一些,似乎调用了LdrDisableThreadCalloutsForDll,并将kernel32.dll的地址作为参数。现在的问题是如何禁用此调用。此调用在main之前,crtMain之前,TLS回调之前进行。什么时候可以在dll载入中?

#1 楼

当调试器想要附加到进程时,它将执行以下操作(请参见ReactOS上的DebugActiveProcess实现):


使用DbgUiConnectToDbg连接到调试对象告诉内核使用NtDebugActiveProcess开始调试过程

使用DbgBreakPoint在附加进程中发出DbgUiIssueRemoteBreakin


DbgUiIssueRemoteBreakin函数在调试对象中创建线程指向DbgUiRemoteBreakin,依次称为DbgBreakPoint。此反调试技巧不再适用于Windows 7及更高版本,因为DbgUiIssueRemoteBreakin使用DbgUiRemoteBreakin标志创建了SkipThreadAttach线程(相关博客文章)。这将导致新创建的线程无法以DllMainDLL_THREAD_ATTACH原因调用DLL_THREAD_DETACH或TLS回调。

评论


确实,这似乎是一个问题。您能否也请确认我对此是否正确:不仅由于SkipThreadAttach标志未调用回调,而且即使被调用,线程入口点也将指向ntdll.dll而不是kernel32.dll,对吗?

– Marcin K.
18年7月23日在18:24



是。线程的起始地址在DbgUiRemoteBreakin中,它是ntdll.dll的导出

– mrexodia
18年7月23日在18:25