在 Visual Studio 中开发 .NET Windbg 扩展






4.67/5 (5投票s)
在 Visual Studio 中开发 Windbg 扩展并调用 .NET 库

引言
本文将介绍如何使用 C++/CLI 技术在 Visual Studio 中开发 Windbg
扩展,并利用 .NET 平台。
背景
我曾接到一项任务,评估 .NET 程序集代码混淆对于知识产权保护的适用性,以及它对调试和支持的后果。
混淆并非没有缺点。它几乎不可能进行调试,除非能够逆转混淆。
我评估的混淆器提供了一个外部工具,可以通过映射文件进行反混淆。你需要手动将文本复制粘贴到该工具中。

尽管可以进行反混淆,但这样做仍然非常繁琐。
为了使完全混淆成为一种可行的方法,我们需要通过 Visual Studio 和 Windbg
的插件将其集成到构建环境中。幸运的是,Obfuscator 公司提供了一个公开的 .NET 程序集,他们鼓励我们使用。
我的第一个方法是使用 Windows Driver Kit (WDK) 构建一个普通的 Windbg 扩展,并使其使用在 Visual Studio 中构建的 C++/CLI DLL。这个 C++/CLI DLL 又会使用 .NET 程序集。事实证明,这个解决方案要简单得多。
祝您阅读愉快!
放弃 Windows Driver Kit
阅读 Windbg
附带的手册。
它清楚地说明了您应该使用 Windows Driver Kit。

下面是构建环境的截图。
这是一个基于命令行的构建环境。

在享受了 Visual Studio 的便利之后,回到基于命令行的环境确实不那么有趣了。这不禁让我想起了 UN*X 中简陋的开发工具。
迁移到 Visual Studio
对于我们 Visual Studio 的爱好者来说,还有一线希望。我偶然发现了这篇文章 Developing WinDbg Extension DLLs。这是一篇关于如何使用 Visual Studio 开发 Windbg 扩展的分步指南。感谢主!!
利用 C++/CLI
我的贡献是向您展示如何将 .NET 程序集集成到您的 Windbg
扩展中。成功的道路充满了反复试验的开发、重新编译和大量的崩溃。
部分成功
前面提到的文章只展示了如何使用 C API 在 Visual Studio 中使用原生 C++ 构建 Windbg
扩展。
我实现了那个功能。然后我将项目更改为托管的 C++/CLI 项目。扩展仍然有效。这意味着可以使用 .NET 程序集。
然后,我切换到了 windbg
的 C++ API。这不起作用。加载 DLL 时崩溃了。由于我的关注点是反混淆器而不是故障排除,所以我回退到了 C API。
实现
添加对 .NET 程序集的引用
要添加对 .NET 库的引用,您有两种选择
右键单击项目,然后选择“引用”以调出此窗口。

另一种方法是在您的 C++ 文件中添加以下行。
#using "StackTraceDeobfuscator.dll"
调用托管代码
现在可以从 C++/CLI 调用 .NET 程序集了。
StackTraceDeobfuscator^ decoder = gcnew StackTraceDeobfuscator(filename);
String^ s = decoder->DeobfuscateText(obfuscatedText);
字符串转换
托管对象,如 string
,不能随意传递。托管 string
是引用。垃圾收集器会不时进行内存压缩,这意味着底层内存可能会移动。另一个限制是,非托管代码不能使用托管类型。幸运的是,有一个特殊的数据类型,称为 msclr::auto_gcroot
,可以解决这个问题。有关更多信息,请阅读 Mixing Native and Managed Types in C++。
#include <msclr\auto_gcroot.h>
struct NativeContainer
{
msclr::auto_gcroot<String^> obj;
};
NativeContainer container;
void foo(const char* text)
{
container.obj = gcnew String(text);
}
我使用这种结构来在调用之间保存映射文件的文件名。
为了避免在使用 char* string
时发生分配,请尝试使用 std::string
。该类型将自动为您处理释放和重新分配。
std::string str = "abc";
str += "def";
char* backAgain = str->c_str();
您可能还需要将托管 string
转换为原生 string
并将其传递给原生 C/C++ 函数。
void OutCliString(String^ clistr)
{
IntPtr p = Marshal::StringToHGlobalAnsi(clistr);
const char* linkStr = static_cast<char* />(p.ToPointer());
dprintf(linkStr);
Marshal::FreeHGlobal(p);
}
我必须使用它来调用 C API 函数 dprintf
,以便将文本输出到 Windbg
。
部署
我将 .NET 程序集和Windbg
扩展复制到 winext,这是 Windbg
的标准扩展文件夹。然后我测试了我的扩展
0:000> .loadby sos mscorwks
0:000> .load DeobfuscateExt.dll
0:000> !mapfile C:\SecretApp_1.0.0.0.nrmap
0:000> !dclrstack
令我惊讶的是,它在尝试访问 .NET 程序集时崩溃了。所以我插入了一些异常处理,以获取错误消息。这是我看到的
0:000> !dclrstack
Exception
Could not load file or assembly
'StackTraceDeobfuscator, Version=..., Culture=neutral, PublicKeyToken=...'
or one of its dependencies. The system cannot find the file specified.
这怎么可能?它与另一个 DLL 在同一个目录中。
在多次尝试使其正常工作失败后,我几乎将程序集作为嵌入式资源包含,以便通过 LoadAssembly
函数手动加载。
来自 sysinternals 的 Process Monitor 程序拯救了我。它可以记录注册表访问、库加载、程序集绑定等。在日志中,我可以看到 Windbg
在 GAC 中查找,然后在安装文件夹中查找,但从未在 winext 扩展文件夹中查找。
当我将 .NET 库复制到安装文件夹时,它奏效了。DLL 应该被复制到 winext 文件夹。由于我不喜欢丑陋的解决方法,我继续进行调查。在 Process Monitor 中,我看到程序集加载器还在查找 Windbg.config 文件,但找不到。然后我明白了。 .NET 程序集加载器现在将 Windbg
视为一个 .NET 应用程序。
在安装文件夹中添加了一个 windbg.config 文件,告诉 windbg
在 winext 文件夹中查找 .NET 扩展。它终于奏效了。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding
xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="winext" />
</assemblyBinding>
</runtime>
</configuration>
最终结果
最终结果在 Windbg
中的样子是这样的
0:000> g
// Load in the SOS .Net extension
0:000> .loadby sos mscorwks
// Display the .Net callstack
0:000> !ClrStack
OS Thread Id: 0x748 (0)
ESP EIP
003bed14 770b22a1 [NDirectMethodFrameStandalone:
003bed14] EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.VeInoLCS1()
003bed24 0029390b EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.glsAhkOLS(System.String)
003bed30 002938e9 EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.RqruoQ7Wy(System.String)
003bed38 002938c9 EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.y870B2Qwn(System.String)
003bed40 002938a9 EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.MshWmjm77(System.String)
003bed48 00293889 EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.EQqEmimFN(System.String)
003bed50 00293869 EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.lTK9lM3pf(System.String)
003bed58 00293849 EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.CRBliDoRv(System.String)
003bed60 00293829 EP8d6KKZNOc1stcvCF.rW7Nf5orPqjZvnA6qV.rEXh0q6dg(System.String)
003bed68 0029009d yIRVo677UilAvTI0XH.LoYYaNO29PLFbE32gm.ayXxZy7mO(System.String[])
003bef94 6f0a1b6c [GCFrame: 003bef94]
// Load my deobfuscator extension
0:000> .load DeobfuscateExt.dll
0:000> !mapfile C:\SecretApp_1.0.0.0.nrmap
0:000> !dclrstack
OS Thread Id: 0x748 (0)
ESP EIP
003bed14 770b22a1 [NDirectMethodFrameStandalone: 003bed14]
SecretApp.NestingCalls.DebugBreak()
003bed24 0029390b SecretApp.NestingCalls.Ti(System.String)
003bed30 002938e9 SecretApp.NestingCalls.La(System.String)
003bed38 002938c9 SecretApp.NestingCalls.So(System.String)
003bed40 002938a9 SecretApp.NestingCalls.Fa(System.String)
003bed48 00293889 SecretApp.NestingCalls.Mi(System.String)
003bed50 00293869 SecretApp.NestingCalls.Re(System.String)
003bed58 00293849 SecretApp.NestingCalls.Do(System.String)
003bed60 00293829 SecretApp.NestingCalls.Execute(System.String)
003bed68 0029009d SecretApp.Program.Main(System.String[])
003bef94 6f0a1b6c [GCFrame: 003bef94]
我的扩展充当输入过滤器。它被编写用于反混淆 Sos 扩展的 ClrStack
命令的输出。
// Other possibilities
0:000> !dclrstack -a // Same as !ClrStack -a
0:000> !deobfuscate !ClrStack -a // The general deobfuscate function
// executes "!ClrStack -a"
0:000> !deobfuscate <cmd> <cmd> <cmd>
源代码
实现最初是针对特定的混淆器完成的。由于我不确定将其 DLL 包含在此项目中的适当性,因此我将其删除了。与原始实现唯一的变化是引用 StackTraceDeobfuscator.dll,它已被一个简单的虚拟 .NET DLL 取代,该 DLL 只会将输入转换为大写。windbg
命令 mapfile
接受任何现有文件。命令 dclrstack
和 deobfuscate
只会将输入转换为大写。
关注点
我没有成功让 windbg
的 C++ API 工作。C++ API 是一组宏,它会被展开为调用 C++ 方法的 C 函数。C 函数被正确调用,到 C++ 方法的转换也正确。但是对基类构造函数的调用会导致崩溃。我分析了一些崩溃。对我来说,这似乎是 __cdecl
和 __stdcall
之间的调用约定冲突。我还没有找到一种方法使其正常工作。在该领域任何贡献都将非常感激。
历史
- 2011 年 4 月 26 日:初始帖子