Managed C++/C# CrackMe 的逆向工程

逆向工程 。网 快手 C#
2021-06-14 23:05:58

我正在对用托管 C++/C# 编写的 CrackMe 进行逆向工程。参数是“字符串”(在这种情况下是电子邮件)和“串行”。串行在某种程度上取决于字符串,我需要了解如何。

我使用 dnSpy 反汇编程序,发现 onClick 函数开始检查序列是否正确:button2_onClick

在这个函数的末尾,我们可以看到一个布尔函数 Check(),true如果序列是正确的,它会返回Check()

因为我是逆向工程的新手(尤其是 .NET 应用程序的 RE),所以我需要一些关于我们可以在这两个函数中看到的反编译代码的解释:

1)具体做什么

md = <Module>.std.basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>.{ctor}(ref basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>4, ref basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>2);

是指第 25 行button2_onClick吗?我知道这是某种类型的任务,但仅此而已。

2) 这些行(第 14、15 行等Check())是做什么的?

*(ref basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>2 + 16) = 0;
*(ref basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>2 + 20) = 0;

3) 数字(行尾附近的 5、6、7)是什么意思?

<Module>.std.basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>._Tidy(ref basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>5, true, 0u);

basic_string<char,std::char_traits<char>,std::allocator<char>\u0020> basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>6;

basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>* right2 = <Module>.md5(&basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>7, str);

4)它只是变相的两个字符串的比较吗?

result =(<Module>.std.basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>.compare(ref basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>, 0u, (uint)(*(ref basic_string<char,std::char_traits<char>,std::allocator<char>\u0020> + 16)), ptr, count) == 0);

5)也许有一种我不知道的更简单的方法来解决这个问题?我首先尝试像往常一样使用 IDA,但这在这种情况下并没有真正的帮助。

1个回答
  1. 看起来像副本ctor,所以我的判断和你的一样 - 作业。
  2. 我会假设在指定的偏移处设置值 0。

编辑:这是部分正确的,经过进一步调查,它看起来像在索引 16 处,存储了字符串的长度,而在索引 20 处,它是某种类型。如果值@20 > 16,则字符串实际上是指向存储字符串的内存区域的指针。对于较短的字符串,它存储在内部。因此,将这两个设置为 0 是初始化/重置值。

  1. 难以置信,但这是自动生成名称的一部分。如果你不声明变量并且有这样的代码

    std::string("");
    std::string(""); 
    

    那么下面它会是这样的

    basic_string<char,std::char_traits<char>,std::allocator<char>\u0020> basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>;
    basic_string<char,std::char_traits<char>,std::allocator<char>\u0020> basic_string<char,std::char_traits<char>,std::allocator<char>\u0020>2; 
    
  2. 这是比较方法,签名是: System::String::Compare(strA, indexA, strB, indexB, len)在您的情况下,它将string从索引 0 开始的变量内容与同一变量的另一部分进行比较 - 高于索引 16。

  3. 您需要知道的一切都在 check 方法中。为什么不调试呢?dnSpy 可以做到这一点。通过快速查看,它从电子邮件中计算出 MD5,但有些部分需要进一步调查,您没有提供完整的二进制文件。一些问题<Module>.GetString()返回什么?- 看起来它对计算很重要。<Module>.??_C@_00CNPNBAHC@?$AA@也可能是一个常量字符串。

更多的信息,虽然它比普通的 .NET 应用程序要困难一些,但仍然可以获取正确解决此问题所需的数据。如果我们检查我们比较的方法

在此处输入图片说明

我们可以在那里检查变量和地址,这些变量和地址实际上是包含这些值的内存位置。也许您无法从 Locals 中检查它们,但是如果您按 CTRL+G 并键入内存位置,您将被带到您需要的位置。

在此处输入图片说明