混淆后的逆向工程应用(第2部分)

引言


该出版物旨在研究一些逆向工程技术。 所有材料仅供参考,不得用于任何个人利益。

第一部分推荐读物
如果教给外科医生一个人如何工作并给他一把手术刀,那并不意味着他会利用这个知识来损害某人的利益,而且知识渊博的汇编者也没有梦想写出超级病毒。
因此,在这些课程中,您不应该寻找破解和破解的提示。

研究课题


我们将继续研究Visual Studio Atomineer Pro文档(以下称为APD)的插件代码。 让我们熟悉该工具及其功能。 因此,假设我们在C ++中有一个类。

class ClassForReadFile { public: ClassForReadFile(); }; 

配置APD,以便注释采用Doxygen样式。 我们将光标放在班级上 ,然后按CTRL + SHIFT +D。 我们得到以下内容:

 /** The class for read file. */ class ClassForReadFile { public: ClassForReadFile(); }; 

该插件添加了一个很好的类描述。 一切都很棒! 我们继续前进。 假设一个类属于一个库,我们必须将其导出。 添加宏并更改类定义

 #ifdef DLL_EXPORTS #define DATA_READER_DLL_EXPORTS __declspec(dllexport) #else #define DATA_READER_DLL_EXPORTS __declspec(dllimport) #endif class DATA_READER_DLL_EXPORTS ClassForReadFile { public: ClassForReadFile(); }; 

对于C ++(Windows OS),情况是标准的。 查看我们的插件。 按CTRL + SHIFT + D并没有得到我们所期望的

 /** A data reader DLL exports. */ class DATA_READER_DLL_EXPORTS ClassForReadFile { }; 

DATA_READER_DLL_EXPORTS 定义的名称定义为类的名称,而不是ClassForReadFile ,并且从该名称生成了类描述。 也就是说,在插件代码中,这种情况(即类的导出)未处理或正在处理错误。 这是我们将尝试纠正的问题。

第一步


我们将寻找线索。 首先,由于从C / C ++导出函数和类是一种标准情况,因此我们仍然尝试正确地“强制”插件。 代替DATA_READER_DLL_EXPORTS 定义,插入__declspec指令本身并生成文档

 /** The class for read file. */ class __declspec(dllexport) ClassForReadFile { }; 

而且,瞧,他们得到了正确的类说明! 因此,我们得出结论,在APD中,有一些代码检查类描述中字符串“ __declspec”的存在,并忽略其进一步的用于构建文档的算法。

我们使用来自Microsoft SDK的标准ildasm.exe反编译该库。 找到“ __declspec”行。 在2种方法CmdDocComment :: a和CmdDocComment :: b中找到它。 第一课 我们将对其进行进一步研究。

第二步


我必须马上说,我们正在寻找的是CmdDocComment ::一个方法

这是__declspec相遇的地方。 仅显示最有趣的行。

字符串a(CmdDocComment.GeneratorInfo A_0)
 List<string> e = A_0.e; //....... List<string> list = A_0.e; int num3 = 0; while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (list[num3] == "__declspec") { if (num3 + 1 < list.Count) { num = list[num3 + 1].IndexOf(')'); if (num >= 0) { list[num3 + 1] = list[num3 + 1].Substring(num + 1).Trim(); } } list.RemoveAt(num3); num3--; } num3++; } if (list.Count > 0 && (list[0] == "struct" || list[0] == "union")) { if (list.Count == 1) { //...... 


我们根据验证后得出的结论
列表[num3] ==“ __declspec”
删除方法被称为
list.RemoveAt(num3);
推理(大声思考):

  1. CmdDocComment ::方法具有包含字符串数组的局部变量

     List<string> list = A_0.e; 
  2. 该数组的第一个元素存储函数,结构,类等的描述的开始,即关键字“类”,“结构”,“联合”
     list[0] == "struct" 
  3. 数组的每个元素都包含一个单独的单词。 在我们的情况下,它将是{“ class”,“ DATA_READER_DLL_EXPORTS”,“ ClassForReadFile”}
  4. 有一个循环遍历数组“ e”的所有元素,查找元素“ __declspec”,然后删除它以及括号中的所有内容
  5. 退出循环还有其他条件。 这是单词“ where”或“:”的位置。 老实说,不熟悉官方词“ where”,但是在继承类时使用“:”

定义新算法和更改目的:

1.更改不应影响其余功能

2.我们将根据算法删除“列表”数组的元素
-跳过第一个元素;
-如果下一个元素不是“:”而不是“ where”而不是数组的末尾,则删除。

写出所需的周期

 //     ,          num2 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { // ,      .     if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } //  if (list[num3] == "__declspec"),  if (num3 != 0 && num3 < (list.Count - 1) && list[num3 + 1] != ":" && list[num3 + 1] != "where") { e.RemoveAt(index); --index; } num3++; } 

仍然需要对其进行编程。

第三步


大声编程。 一般情况下,编程是编写源代码,进行编译,链接。 但是混淆器剥夺了我们这样的机会。 我们将使用推荐的dnSpy工具 。 我们将直接在库中更改CIL命令的十六进制代码,事实证明,这是非常令人兴奋和有益的! 让我们开始吧。 打开dnSpy,加载库。

找到我们的方法
图片

选择一会儿,然后将视图更改为IL
我们的周期
图片

我也会列出一个清单,尽管它很庞大

我们的周期
 /* 0x00016710 07 */ IL_018C: ldloc.1 //         1. /* 0x00016711 1119 */ IL_018D: ldloc.s V_25 //          ( ). /* 0x00016713 6FF900000A */ IL_018F: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016718 72925E0070 */ IL_0194: ldstr "where" //       ,   ,   . /* 0x0001671D 287000000A */ IL_0199: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016722 3AAB000000 */ IL_019E: brtrue IL_024E //    ,   value  true,    null   . /* 0x00016727 07 */ IL_01A3: ldloc.1 //         1. /* 0x00016728 1119 */ IL_01A4: ldloc.s V_25 //          ( ). /* 0x0001672A 6FF900000A */ IL_01A6: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001672F 72A31D0070 */ IL_01AB: ldstr ":" //       ,   ,   . /* 0x00016734 287000000A */ IL_01B0: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016739 3A94000000 */ IL_01B5: brtrue IL_024E //    ,   value  true,    null   . /* 0x0001673E 07 */ IL_01BA: ldloc.1 //         1. /* 0x0001673F 1119 */ IL_01BB: ldloc.s V_25 //          ( ). /* 0x00016741 6FF900000A */ IL_01BD: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016746 03 */ IL_01C2: ldarg.1 //     1   . /* 0x00016747 7B12010004 */ IL_01C3: ldfld string Atomineer.Utils.CmdDocComment/GeneratorInfo::b //      ,       . /* 0x0001674C 287000000A */ IL_01C8: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x00016751 2C07 */ IL_01CD: brfalse.s IL_01D6 //    ,   value  false,    . /* 0x00016753 09 */ IL_01CF: ldloc.3 //         3. /* 0x00016754 16 */ IL_01D0: ldc.i4.0 //    0     int32. /* 0x00016755 2F03 */ IL_01D1: bge.s IL_01D6 //     ( ),        . /* 0x00016757 1119 */ IL_01D3: ldloc.s V_25 //          ( ). /* 0x00016759 0D */ IL_01D5: stloc.3 //                3. /* 0x0001675A 07 */ IL_01D6: ldloc.1 //         1. /* 0x0001675B 1119 */ IL_01D7: ldloc.s V_25 //          ( ). /* 0x0001675D 6FF900000A */ IL_01D9: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016762 729E5E0070 */ IL_01DE: ldstr "__declspec" //       ,   ,   . /* 0x00016767 287000000A */ IL_01E3: call bool [mscorlib]System.String::op_Equality(string, string) //  ,      . /* 0x0001676C 2C51 */ IL_01E8: brfalse.s IL_023B //    ,   value  false,    . /* 0x0001676E 1119 */ IL_01EA: ldloc.s V_25 //          ( ). /* 0x00016770 17 */ IL_01EC: ldc.i4.1 //    1     int32. /* 0x00016771 58 */ IL_01ED: add //         . /* 0x00016772 07 */ IL_01EE: ldloc.1 //         1. /* 0x00016773 6FF700000A */ IL_01EF: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x00016778 2F37 */ IL_01F4: bge.s IL_022D //     ( ),        . /* 0x0001677A 07 */ IL_01F6: ldloc.1 //         1. /* 0x0001677B 1119 */ IL_01F7: ldloc.s V_25 //          ( ). /* 0x0001677D 17 */ IL_01F9: ldc.i4.1 //    1     int32. /* 0x0001677E 58 */ IL_01FA: add //         . /* 0x0001677F 6FF900000A */ IL_01FB: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x00016784 1F29 */ IL_0200: ldc.i4.s 41 //      int8     int32 ( ). /* 0x00016786 6FC800000A */ IL_0202: callvirt instance int32 [mscorlib]System.String::IndexOf(char) //             . /* 0x0001678B 0C */ IL_0207: stloc.2 //                2. /* 0x0001678C 08 */ IL_0208: ldloc.2 //         2. /* 0x0001678D 16 */ IL_0209: ldc.i4.0 //    0     int32. /* 0x0001678E 3221 */ IL_020A: blt.s IL_022D //     ( ),      . /* 0x00016790 07 */ IL_020C: ldloc.1 //         1. /* 0x00016791 1119 */ IL_020D: ldloc.s V_25 //          ( ). /* 0x00016793 17 */ IL_020F: ldc.i4.1 //    1     int32. /* 0x00016794 58 */ IL_0210: add //         . /* 0x00016795 07 */ IL_0211: ldloc.1 //         1. /* 0x00016796 1119 */ IL_0212: ldloc.s V_25 //          ( ). /* 0x00016798 17 */ IL_0214: ldc.i4.1 //    1     int32. /* 0x00016799 58 */ IL_0215: add //         . /* 0x0001679A 6FF900000A */ IL_0216: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<string>::get_Item(int32) //             . /* 0x0001679F 08 */ IL_021B: ldloc.2 //         2. /* 0x000167A0 17 */ IL_021C: ldc.i4.1 //    1     int32. /* 0x000167A1 58 */ IL_021D: add //         . /* 0x000167A2 6FCB00000A */ IL_021E: callvirt instance string [mscorlib]System.String::Substring(int32) //             . /* 0x000167A7 6F8600000A */ IL_0223: callvirt instance string [mscorlib]System.String::Trim() //             . /* 0x000167AC 6FFF00000A */ IL_0228: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::set_Item(int32, !0) //             . /* 0x000167B1 07 */ IL_022D: ldloc.1 //         1. /* 0x000167B2 1119 */ IL_022E: ldloc.s V_25 //          ( ). /* 0x000167B4 6F6701000A */ IL_0230: callvirt instance void class [mscorlib]System.Collections.Generic.List`1<string>::RemoveAt(int32) //             . /* 0x000167B9 1119 */ IL_0235: ldloc.s V_25 //          ( ). /* 0x000167BB 17 */ IL_0237: ldc.i4.1 //    1     int32. /* 0x000167BC 59 */ IL_0238: sub //           . /* 0x000167BD 1319 */ IL_0239: stloc.s V_25 //                index ( ). /* 0x000167BF 1119 */ IL_023B: ldloc.s V_25 //          ( ). /* 0x000167C1 17 */ IL_023D: ldc.i4.1 //    1     int32. /* 0x000167C2 58 */ IL_023E: add //         . /* 0x000167C3 1319 */ IL_023F: stloc.s V_25 //                index ( ). /* 0x000167C5 1119 */ IL_0241: ldloc.s V_25 //          ( ). /* 0x000167C7 07 */ IL_0243: ldloc.1 //         1. /* 0x000167C8 6FF700000A */ IL_0244: callvirt instance int32 class [mscorlib]System.Collections.Generic.List`1<string>::get_Count() //             . /* 0x000167CD 3F3EFFFFFF */ IL_0249: blt IL_018C //    ,     . 


现在,我们在CIL命令中有了一个窗口,它们的十六进制表示形式,文件偏移量和描述。 全部集中在一处。 非常方便(感谢CrazyAlex25 )。
让我们注意提到“ __declspec”的代码块。 块偏移量0x0001675A。 这将是我们编辑的开始。 接下来,找到RemoveAt方法。 它将对我们保持不变。 该块的最后一个字节是0x000167BF。 转到HEX编辑器Ctrl + X并在此范围内写入0x00。 我们将保存并检查更改导致的结果。
空循环
 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } list.RemoveAt(num3); num3--; num3++; } 


现在,我们将实现新的逻辑。 首先,添加条件

  if (num3 != 0 && num3 < list.Count - 1) 

下表显示了新命令及其说明。
1119ldloc.s将具有指定索引的局部变量加载到计算堆栈(缩写形式)上。
2C61brfalse.s如果value为false,空引用或零,则将控制权传递给最终语句。 注意 :如果num3 == 0,则转到增加循环迭代器的步骤。 值0x64是指令0x000167BF的地址偏移量(请参见清单)
1119ldloc.s将具有指定索引的局部变量加载到计算堆栈中(简写形式)
07ldloc.1将索引为1的局部变量加载到计算堆栈中
6FF700000A呼叫病毒get_Count()-调用后绑定对象方法并将返回值压入计算堆栈
17ldc.i4.1将整数值1推入计算堆栈为int32
59从另一个值中减去一个值,并将结果压入计算堆栈。
2F55博客如果第一个值大于或等于第二个值,它将控制权转移到最终指令(短格式)。 注意 :如果num3> list.Count-1,则转到增加循环迭代器的步骤。 值0x55是指令0x000167BF之前的地址偏移量

我们从偏移量0x0001675A开始写入这些字节。 再次保存并反编译

第一个条件
 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } //     if (num3 != 0 && num3 < list.Count - 1) { list.RemoveAt(num3); num3--; } num3++; } 


现在,我们添加字符串检查“ where”和“:”。 我引用了以下十六进制代码,没有其他注释:

 07 11 19 17 58 6F F9 00 00 0A 72 A3 1D 00 70 28 70 00 00 0A 2D 3F 07 11 19 17 58 6F F9 00 00 0A 72 92 5E 00 70 28 70 00 00 0A 2D 29 

反编译并获得您的计划

新周期
 while (num3 < list.Count && !(list[num3] == "where") && !(list[num3] == ":")) { if (list[num3] == A_0.b && num2 < 0) { num2 = num3; } if (num3 != 0 && num3 < list.Count - 1 && !(list[num3 + 1] == ":") && !(list[num3 + 1] == "where")) { list.RemoveAt(num3); num3--; } num3++; } 


通过这些更改,插件将生成以下代码文档:

 /** The class for read file. */ class DATA_READER_DLL_EXPORTS ClassForReadFile { }; 

结论


在本课程中,我们学习了如何应用我们的知识来修复错误。 当然,该示例并未反映出错误的全部种类及其处理方式,但这不是“平庸的裂缝”。 我们修复了明显的错误,没有源代码,也没有重建应用程序。

Source: https://habr.com/ru/post/zh-CN445126/


All Articles