每当创建线程或销毁线程时都会调用线程本地存储回调(除非进程调用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还是在调试器中运行,还是将调试器附加到进程中(我对附加的经验都不太丰富,所以也许我在这里做错了)。
我是在做错什么,还是文本错误/不再有效?
#1 楼
当调试器想要附加到进程时,它将执行以下操作(请参见ReactOS上的DebugActiveProcess实现):使用
DbgUiConnectToDbg
连接到调试对象告诉内核使用NtDebugActiveProcess
开始调试过程使用
DbgBreakPoint
在附加进程中发出DbgUiIssueRemoteBreakin
DbgUiIssueRemoteBreakin
函数在调试对象中创建线程指向DbgUiRemoteBreakin
,依次称为DbgBreakPoint
。此反调试技巧不再适用于Windows 7及更高版本,因为DbgUiIssueRemoteBreakin
使用DbgUiRemoteBreakin
标志创建了SkipThreadAttach
线程(相关博客文章)。这将导致新创建的线程无法以DllMain
或DLL_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
评论
您是否在可执行文件的主要功能内运行无限循环?应该为每个创建的线程调用TLS回调。在main之前执行一次,在运行时附加到该进程执行一次。我使用了system(“ pause”),但现在更改为无限循环,仍然没有输出表明TLS回调被称为第二次
我认为我已经尝试了所有方法,但无法获得调试器来生成本文档中所述的线程。我正在使用Windows10。如果有人可以在Windows 10和其他操作系统上进行检查,我将非常感激
今天,我再次遇到了这种情况,但是在我的情况下,在调试附加上调用了TLS回调。我明天带你看看
我调试了一些,似乎调用了LdrDisableThreadCalloutsForDll,并将kernel32.dll的地址作为参数。现在的问题是如何禁用此调用。此调用在main之前,crtMain之前,TLS回调之前进行。什么时候可以在dll载入中?