这取决于你想要做多长时间。它本质上是一场猫捉老鼠的游戏 - 不良行为者会试图通过跳过一些晦涩难懂的圈子来寻找新的方法来规避您的检测,您将为这些技巧添加更复杂的检测方法,他们会想到新的技巧,等等。
此外,这取决于您是要静态还是动态确定,以及您是否真的想知道是否GetDesktopWindow
被调用或“程序是否获得桌面窗口的句柄”(这也可以通过其他方式实现)。
这是一个非详尽的想法列表:
- 您可以通过查看导入目录来静态确定函数是否被导入。研究 PE 文件结构以了解更多信息。这篇文章可能会有所帮助。
LoadLibrary
通过使用和动态导入函数,可以轻松绕过这种检测方法GetProcAddress
。
- 您可以扫描文件中的字符串
GetDesktopWindow
以检测动态导入的可能用法。
- 通过打包、加密或以其他方式混淆动态导入函数的名称,可以轻松绕过这种检测方法。
GetDesktopWindow
您可以通过注册一个或一个全局挂钩来动态观察该函数是否被调用,AppInit_DLL
该挂钩被注入到每个新进程中,并GetDesktopWindow
通过跳转到您自己的代码覆盖其第一个字节来从进程内部挂钩该函数,以某种方式通知您的检测组件,执行原始字节并跳回。(Microsoft Detours可以提供帮助。)
- 如果目标注意到钩子并在调用之前将其删除,则可以绕过这种检测方法,因为它在自己的进程空间中。(您还可以像调试器一样做一些技巧,并在 的第一条指令上设置硬件断点
GetDesktopWindow
,但由于目标也可以修改调试寄存器,因此仍有办法检测或规避这一点。)
- 您可以从内核模式构建一个驱动程序来执行此操作,但现在我们已经变得非常深入了。
请注意,到目前为止,我们专注于GetDesktopWindow
从user32.dll
. 但是,如果目标只是使用不同的方式来实现获取桌面窗口句柄的目标呢?
- 当前线程的桌面窗口句柄存储在 TIB(线程信息块)中,可通过
fs:[18]
用户模式访问。您可以在GetDesktopWindow
ReactOS 的源代码中看到这一点,这与 Microsoft 的实际实现相比非常准确(您可以通过在调试器中查看它来验证)。因此,目标可以直接访问 TIB 并提取此值,甚至根本不需要调用GetDesktopWindow
。
- 目标可以只获取一个已知的顶级窗口,例如您将通过的 shell 的隐藏兼容性窗口,
GetShellWindow()
或者 - 为了避免GetShellWindow
也检测到 - 例如FindWindow(NULL, "Program Manager")
(甚至是新创建的窗口!)并调用GetAncestor(hWnd, GA_PARENT)
它来获取桌面窗口句柄。
- 我敢肯定,只要有一些创造力,你的对手会想出比这些更聪明的想法。
此外,如果我们更进一步,看看截屏的最终目标,还有其他方法可以实现这一目标。想到的第一个例子:他们可以keybd_event
用来模拟按下 PrnSc 键,然后从剪贴板数据中读取屏幕截图。
所以这完全取决于你想走多远。
顺便说一句,您可能会发现该drltrace
项目很有趣——它是一个库调用跟踪器。