【问题标题】:Anti-debugger technique: How to hide a thread from the debugger using VB.NET?反调试器技术:如何使用 VB.NET 对调试器隐藏线程?
【发布时间】:2019-11-28 19:29:20
【问题描述】:

几天来,我一直在尝试记录自己有关 Anti-Debugger 技术的内容。

所以我找到了许多不同的方法来实现这一点。在这些技术中,我发现了在调试器中隐藏线程的可能性,这要归功于 NtSetInformationThread 方法。 我的项目是在我用VB.NET编写的代码中使用这种方法。

以下是我在研究中发现的技术的描述,我觉得它解释得很好:

在 Windows 2000 中,将一类新的线程信息传输到 NtSetInformationThread 函数出现——ThreadHideFromDebugger。 这是由提供的第一个反调试技术之一 Windows 在微软搜索如何防止逆向工程, 它非常强大。如果为线程设置了此标志,则 线程停止发送有关调试事件的通知

From this website

所以我找到了来自site 的来源来实现这一目标。 这是他在 C++ 中使用的方法:

typedef enum _THREADINFOCLASS {

    ThreadHideFromDebugger=17

} THREADINFOCLASS;

extern "C" ULONG __stdcall NtSetInformationThread(
    __in HANDLE ThreadHandle,
    __in THREADINFOCLASS ThreadInformationClass,
    __in_bcount(ThreadInformationLength) PVOID ThreadInformation,
    __in ULONG ThreadInformationLength
);

ULONG main()
{
    ULONG Status;

    Status=NtSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, NULL, 0);

    if(Status)
        printf("Error with NtSetInformationThread : 0x%xn", Status);

    __asm {int 3}
    return 0; 
}

所以我尝试用我的 C++ 初学者知识来翻译这段代码(老实说,我对这门语言了解不多)。 这是它给出的:

Private Declare Function NtSetInformationThread Lib "Ntdll.dll" (ByVal hThread As Long, ByVal ThreadInformationClass As Long, ByVal ThreadInformation As Long, ByVal ThreadInformationLength As Long) As Long
<DllImport("kernel32.dll")>

Public Shared Function GetCurrentThreadId() As UInteger
End Function

Shared Function HideFromDebugger() As UInteger
    Dim Status As UInteger

    Status = NtSetInformationThread(GetCurrentThreadId(), 17, Nothing, 0)

    If Status <> 0 Then

        Console.Write("Error with NtSetInformationThread : 0x{0:x}n", Status)
        Debugger.Break()
        Return 0
    End If
End Function

但是,我觉得我在某个地方错了。例如,我不明白参数“17”的用途。你能告诉我我是否走在正确的轨道上吗?

每个答案对我来说都很有价值:)

【问题讨论】:

  • 您可能想通过调用IsDebuggerPresent 来检查您的代码是否正在调试器下运行,如果是,则安静地退出。
  • 反调试器技术不止一种。 IsDebuggerPresent 是最著名的,但它的作用领域不同。我提到的网站apriorit.com/dev-blog/… 列出了很多。
  • 17ThreadHideFromDebugger 的值,并没有任何特殊的来源或意义。它只是告诉NtSetInformationThread()什么信息要更改有关线程的信息。
  • 请注意,NtSetInformationThread 函数的 P/Invoke 声明并不完全正确。它实际上应该是(ByVal hThread As IntPtr, ByVal ThreadInformationClass As Integer, ByVal ThreadInformation As IntPtr, ByVal ThreadInformationLength As UInteger) As Integer。也支持DllImport 而不是Declare Function。后者是VB6的遗物。 :)
  • 应该注意的是,您显示的枚举声明几乎可以肯定是完整枚举的缩减版本,仅包含您需要的特定成员。枚举的全部意义在于赋予 17 意义,使其不仅仅是一个幻数。

标签: c++ vb.net debugging reverse-engineering translate


【解决方案1】:

您快到了,但您当前的代码有两个问题:

首先,您对 NtSetInformationThread 函数的 P/Invoke 声明并不完全正确,我建议您坚持使用 DllImport,因为您在 Internet 上找到的大多数 Declare Function 声明都是为 VB6 编写的并且不兼容使用 VB.NET。

这里是更正的版本:

<DllImport("Ntdll.dll")>
Public Shared Function NtSetInformationThread(ByVal hThread As IntPtr, ByVal ThreadInformationClass As Integer, ByVal ThreadInformation As IntPtr, ByVal ThreadInformationLength As UInteger) As UInteger
End Function

其次,注意 C++ 代码如何使用函数 GetCurrentThread()not GetCurrentThreadId()。这两者的不同之处在于前者为您提供了一个更像是指向线程的指针的句柄,而后者只是为您获取分配给线程的数字 ID。

您需要改用GetCurrentThread 函数:

<DllImport("Kernel32.dll")>
Public Shared Function GetCurrentThread() As IntPtr
End Function

我不明白“17”的说法是什么。

17ThreadHideFromDebugger 的值,并没有任何特殊的来源或意义。它只是告诉NtSetInformationThread() 要更改有关线程的哪些信息。

【讨论】:

  • 非常感谢您花时间查看我的代码。我现在更好地理解了我的错误,我将不得不使用更多NtSetInformationThread 的文档 :) 你解释得很好。仍然希望我的代码真正实现它的功能
  • @MarshallCop:很高兴我能帮上忙!我已经在调用NtSetInformationThread 后测试了最后的代码和断点等停止工作,所以它似乎正在做它应该做的事情。
  • 是的,非常完美! :) 感谢您的帮助!
猜你喜欢
  • 2020-09-02
  • 2014-08-11
  • 2016-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多