今天下午本来兴奋的以为可以拿那个Themida的壳来实验实验Oreans UnVirtualizer. 结果发现还是掉链子啊, 这个工具还是不行. 绝对的不行.还是老实一点, 自己慢慢倒腾吧..

接着中午的弄, 中午搞到IAT表了. 下面就是分析IAT处理的代码了. 首先是这里.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
0069997D    68 6969728E     push    8E726969
00699982    FFB5 DD1C1B07   push    dword ptr [ebp+71B1CDD]
00699988    8D85 F2361B07   lea     eax, dword ptr [ebp+71B36F2]
0069998E    FFD0            call    eax                                         ; 获取一系列的需要的函数地址.. 外壳使用的
00699990    8985 7D291B07   mov     dword ptr [ebp+71B297D], eax                ; kernel32.ExitProcess
00699996    C685 392E1B07 4>mov     byte ptr [ebp+71B2E39], 43
0069999D    68 5E6B679C     push    9C676B5E
006999A2    FFB5 DD1C1B07   push    dword ptr [ebp+71B1CDD]
006999A8    8D85 F2361B07   lea     eax, dword ptr [ebp+71B36F2]
006999AE    FFD0            call    eax
006999B0    8985 A6D32C07   mov     dword ptr [ebp+72CD3A6], eax                ; kernel32.CreateThread
006999B6    C685 392E1B07 5>mov     byte ptr [ebp+71B2E39], 54
006999BD    68 296862EE     push    EE626829
006999C2    FFB5 DD1C1B07   push    dword ptr [ebp+71B1CDD]
006999C8    8D85 F2361B07   lea     eax, dword ptr [ebp+71B36F2]
006999CE    FFD0            call    eax
006999D0    8985 AAD32C07   mov     dword ptr [ebp+72CD3AA], eax                ; kernel32.TerminateThread

这里是获取外壳所需函数阶段, 大部分都是获取的真实的函数地址. 当然也有一些获取的是重定位内核里面的函数地址, 比如VirtualFree.函数的种类也很丰富, 有打开文件一类的, 查找文件, 复制文件, 还有内存操作的函数.. 函数获取完了就是下面的动作了.

0069A11C    8985 56D42C07   mov     dword ptr [ebp+72CD456], eax                ; 处理IAT开始了感觉好像是加密
0069A122    6A 04           push    4
0069A124    68 00100000     push    1000
0069A129    68 00100000     push    1000
0069A12E    6A 00           push    0
0069A130    FF95 111C1B07   call    dword ptr [ebp+71B1C11]                     ; VirtualAlloc
0069A136    8985 49041B07   mov     dword ptr [ebp+71B0449], eax
0069A13C    8D85 CE9C2C07   lea     eax, dword ptr [ebp+72C9CCE]
0069A142    FFD0            call    eax
0069A144    6A 04           push    4
0069A146    68 00100000     push    1000
0069A14B    68 00200000     push    2000
0069A150    6A 00           push    0
0069A152    FF95 111C1B07   call    dword ptr [ebp+71B1C11]                     ; VirtualAlloc
0069A158    8985 C90D1B07   mov     dword ptr [ebp+71B0DC9], eax
0069A15E    8985 F1151B07   mov     dword ptr [ebp+71B15F1], eax
0069A164    6A 40           push    40
0069A166    68 00100000     push    1000
0069A16B    68 00000100     push    10000
0069A170    6A 00           push    0
0069A172    FF95 111C1B07   call    dword ptr [ebp+71B1C11]                     ; VirtualAlloc
0069A178    8985 41201B07   mov     dword ptr [ebp+71B2041], eax

这里申请了3块内存, 这3块内存的作用下面有讲到

0069A17E    8BB5 952A1B07   mov     esi, dword ptr [ebp+71B2A95]                ; 这句代码非常重要esi-->IAT加密表地址
0069A184    8B9D 411D1B07   mov     ebx, dword ptr [ebp+71B1D41]
0069A18A    89B5 95211B07   mov     dword ptr [ebp+71B2195], esi
0069A190    899D ED0F1B07   mov     dword ptr [ebp+71B0FED], ebx                ; 这里指向所有DLL的模块基址
0069A196    8B9D 411D1B07   mov     ebx, dword ptr [ebp+71B1D41]                ; 这里是获取IAT加密表的地址.. 准备开始

处理IAT了
0069A19C    8B0B            mov     ecx, dword ptr [ebx]                        ; 取某个DLL 模块基址
0069A19E    83F9 00         cmp     ecx, 0
0069A1A1    0F84 690A0000   je      delphi7_.0069AC10                           ; 输入表处理完成等于0后跳

这里有几个关键的地方. esi-->指向的就是上面加密出来的IAT加密表. ebx-->里面是所有DLL的模块基址. 然后mov ecx, [ebx] 我们可以获取到某个DLL的模块基址, 接着一个一个处理, 直到为0. 处理完毕.

0069A1A9    60              pushad
0069A1AA    33C0            xor     eax, eax
0069A1AC    8985 D9091B07   mov     dword ptr [ebp+71B09D9], eax
0069A1B2    BE 3C000000     mov     esi, 3C
0069A1B7    037424 20       add     esi, dword ptr [esp+20]
0069A1BB    66:AD           lods    word ptr [esi]                              ; PE头到了
0069A1BD    034424 20       add     eax, dword ptr [esp+20]
0069A1C1    8B70 78         mov     esi, dword ptr [eax+78]                     ; 定位到PE的导出表RVA
0069A1C4    037424 20       add     esi, dword ptr [esp+20]                     ; 导出表RVA+模块基址
0069A1C8    8B7E 18         mov     edi, dword ptr [esi+18]                     ; NumberOfFunctions导出函数个数
0069A1CB    89BD 691F1B07   mov     dword ptr [ebp+71B1F69], edi                ; edi == 导出函数数量
0069A1D1    85FF            test    edi, edi

这段代码是定位到某个DLL的导出表上面.

0069A1EF    6A 04           push    4
0069A1F1    68 00100000     push    1000
0069A1F6    52              push    edx
0069A1F7    6A 00           push    0
0069A1F9    FF95 111C1B07   call    dword ptr [ebp+71B1C11]                     ; 重定位的VirtualAlloc函数
0069A1FF    8985 511A1B07   mov     dword ptr [ebp+71B1A51], eax                ; 分配到内存
0069A205    8BD0            mov     edx, eax
0069A207    59              pop     ecx
0069A208    E8 10100000     call    delphi7_.0069B21D                           ; 获取到导出表中的名称函数序号偏移
0069A20D    56              push    esi
0069A20E    AD              lods    dword ptr [esi]                             ; 这是函数的字符串名称
0069A20F    034424 24       add     eax, dword ptr [esp+24]                     ; 到这里就已经得到了函数的真实地址
0069A213    97              xchg    eax, edi
0069A214    8BDF            mov     ebx, edi
0069A216    57              push    edi
0069A217    32C0            xor     al, al
0069A219    AE              scas    byte ptr es:[edi]                           ; 求函数字符串的长度
0069A21A  ^ 0F85 F9FFFFFF   jnz     delphi7_.0069A219
0069A220    5E              pop     esi
0069A221    2BFB            sub     edi, ebx
0069A223    52              push    edx
0069A224    8BD7            mov     edx, edi
0069A226    8BBD 49041B07   mov     edi, dword ptr [ebp+71B0449]                ; edi = 前面申请的内存,存放解码用的数据表
0069A22C    83C9 FF         or      ecx, FFFFFFFF
0069A22F    33C0            xor     eax, eax
0069A231    8A06            mov     al, byte ptr [esi]
0069A233    32C1            xor     al, cl
0069A235    46              inc     esi                                         ; 查表, 得到解码Key
0069A236    8B0487          mov     eax, dword ptr [edi+eax\*4]                  ; 求字符串的Hash码
0069A239    C1E9 08         shr     ecx, 8
0069A23C    33C8            xor     ecx, eax
0069A23E    4A              dec     edx
0069A23F  ^ 0F85 EAFFFFFF   jnz     delphi7_.0069A22F
0069A245    8BC1            mov     eax, ecx
0069A247    F7D0            not     eax
0069A249    5A              pop     edx
0069A24A    8902            mov     dword ptr [edx], eax                        ; 上面求出来的的 HASH保存起来
0069A24C    83C2 04         add     edx, 4
0069A24F    52              push    edx
0069A250    FF85 D9091B07   inc     dword ptr [ebp+71B09D9]                     ; 已Hash的函数个数
0069A256    8B95 D9091B07   mov     edx, dword ptr [ebp+71B09D9]
0069A25C    3995 691F1B07   cmp     dword ptr [ebp+71B1F69], edx
0069A262    0F84 0A000000   je      delphi7_.0069A272
0069A268    5A              pop     edx
0069A269    5E              pop     esi
0069A26A    83C6 04         add     esi, 4
0069A26D  ^ E9 9BFFFFFF     jmp     delphi7_.0069A20D
0069A272    5A              pop     edx                                         ; 到这里一个DLL就处理完成了
0069A273    5E              pop     esi                                         ; 感觉上面是加密IAT组建自己的加密IAT表..

上面这段代码是将函数名称的Hash码都保存起来. 反正就是瞎倒腾, 也不知道倒腾出什么东西来.. 下面我们要修改代码, 但是不是这一段.

0069A2BD    60              pushad
0069A2BE    8DB5 82D42C07   lea     esi, dword ptr [ebp+72CD482]
0069A2C4    8DBD 9AEF2C07   lea     edi, dword ptr [ebp+72CEF9A]
0069A2CA    2BFE            sub     edi, esi
0069A2CC    8BD7            mov     edx, edi
0069A2CE    8BBD 49041B07   mov     edi, dword ptr [ebp+71B0449]
0069A2D4    83C9 FF         or      ecx, FFFFFFFF
0069A2D7    33C0            xor     eax, eax
0069A2D9    8A06            mov     al, byte ptr [esi]
0069A2DB    32C1            xor     al, cl
0069A2DD    46              inc     esi
0069A2DE    8B0487          mov     eax, dword ptr [edi+eax\*4]
0069A2E1    C1E9 08         shr     ecx, 8
0069A2E4    33C8            xor     ecx, eax
0069A2E6    4A              dec     edx                                         ; 难道这里是求CRC的代码?
0069A2E7  ^ 0F85 EAFFFFFF   jnz     delphi7_.0069A2D7
0069A2ED    8BC1            mov     eax, ecx
0069A2EF    F7D0            not     eax
0069A2F1    3985 49061B07   cmp     dword ptr [ebp+71B0649], eax
0069A2F7    0F84 17000000   je      delphi7_.0069A314
0069A2FD    83BD 85011B07 0>cmp     dword ptr [ebp+71B0185], 0
0069A304    0F85 0A000000   jnz     delphi7_.0069A314                           ; 自校验path位置1.. 改成jmp
0069A30A    C785 31291B07 0>mov     dword ptr [ebp+71B2931], 1

这一段是关于自效验的判断. 我们当然可以随便就跳过了.. 所以这个是要path的位置1.. 其实看到这个代码我有种说不出的亲切. 这个肯定是用assembly写出来的, 而且是手工的. 看着非常的舒服. 我们可以很轻易的就定位到一小段代码的开始位置, 结束位置. 久违的汇编啊.... 完了之后给其他模块调用的, 我也写过..

0069A315    B9 B6282501     mov     ecx, 12528B6                                ; 硬编码的解码Key1
0069A31A    BA F9628A76     mov     edx, 768A62F9                               ; 硬编码的解码Key2
0069A31F    AD              lods    dword ptr [esi]                             ; 这里是加载IAT加密表的第一个字段
0069A320    89B5 95211B07   mov     dword ptr [ebp+71B2195], esi
0069A326    C746 FC 0000000>mov     dword ptr [esi-4], 0                        ; 取出来就清0, 好猥琐
0069A32D    3D EEEEEEEE     cmp     eax, EEEEEEEE                               ; 结束符标记判断
0069A332    0F85 20000000   jnz     delphi7_.0069A358
0069A338    813E DDDDDDDD   cmp     dword ptr [esi], DDDDDDDD                   ; 结束标记判断
0069A33E    0F85 14000000   jnz     delphi7_.0069A358
0069A344    C706 00000000   mov     dword ptr [esi], 0                          ; 清零
0069A34A    83C6 04         add     esi, 4

0069A358    8BD8            mov     ebx, eax
0069A35A    3385 31291B07   xor     eax, dword ptr [ebp+71B2931]
0069A360    C1C8 03         ror     eax, 3
0069A363    2BC2            sub     eax, edx
0069A365    C1C0 10         rol     eax, 10
0069A368    33C1            xor     eax, ecx
0069A36A    899D 31291B07   mov     dword ptr [ebp+71B2931], ebx                ; 解密出第一个字段值
0069A370    3D 00000100     cmp     eax, 10000
0069A375    0F83 45000000   jnb     delphi7_.0069A3C0
0069A37B    813E BBBBBBBB   cmp     dword ptr [esi], BBBBBBBB                   ; 结束标记判断
0069A381    0F85 39000000   jnz     delphi7_.0069A3C0
0069A387    C706 00000000   mov     dword ptr [esi], 0                          ; 清零
0069A38D    83C6 04         add     esi, 4

到这里开始就是正式的解码阶段了. 这里首先取出来IAT加密结构的第一个字段. 解密出来.. 现在我们手头上已经有了某个API函数的Hash值. 现在我们需要做的就是在加密表中根据HASH进行查找..

0069A3C0    51              push    ecx
0069A3C1    52              push    edx
0069A3C2    33C9            xor     ecx, ecx
0069A3C4    8B95 511A1B07   mov     edx, dword ptr [ebp+71B1A51]
0069A3CA    3B02            cmp     eax, dword ptr [edx]                        ; 比较Hash值
0069A3CC    0F84 38000000   je      delphi7_.0069A40A
0069A3D2    83C2 04         add     edx, 4
0069A3D5    41              inc     ecx
0069A3D6    3B8D 691F1B07   cmp     ecx, dword ptr [ebp+71B1F69]
0069A3DC  ^ 0F85 E8FFFFFF   jnz     delphi7_.0069A3CA                           ; 这里是根据Hash值进行查找, 我们可以使用下
0069A3E2    8DB5 7BD32C07   lea     esi, dword ptr [ebp+72CD37B]
0069A3E8    8DBD 8D221B07   lea     edi, dword ptr [ebp+71B228D]
0069A3EE    AC              lods    byte ptr [esi]
0069A3EF    84C0            test    al, al
0069A3F1    0F84 06000000   je      delphi7_.0069A3FD
0069A3F7    AA              stos    byte ptr es:[edi]
0069A3F8  ^ E9 F1FFFFFF     jmp     delphi7_.0069A3EE
0069A3FD    B8 07000000     mov     eax, 7
0069A402    8D8D D04E1B07   lea     ecx, dword ptr [ebp+71B4ED0]
0069A408    FFE1            jmp     ecx
0069A40A    898D D9091B07   mov     dword ptr [ebp+71B09D9], ecx		 ; 这是IAT加密表中的下标
0069A410    5A              pop     edx
0069A411    59              pop     ecx

这一段就是查找代码.. 查到到以后我们就在IAT加密表中定位到了函数的Hash值. mov [ebp+71B09D9], ecx里面保存的就是下标了.

0069A412    56              push    esi
0069A413    8B9D 411D1B07   mov     ebx, dword ptr [ebp+71B1D41]                ; DLL模块表
0069A419    8B0B            mov     ecx, dword ptr [ebx]                        ; 取得模块基址
0069A41B    8B85 D9091B07   mov     eax, dword ptr [ebp+71B09D9]
0069A421    D1E0            shl     eax, 1                                      ; IAT加密表下标
0069A423    0385 2D201B07   add     eax, dword ptr [ebp+71B202D]
0069A429    33F6            xor     esi, esi
0069A42B    96              xchg    eax, esi
0069A42C    66:AD           lods    word ptr [esi]                              ; 在原始的DLL我们也是可以很容易定位到函数的真实地址.
0069A42E    C1E0 02         shl     eax, 2
0069A431    0385 452A1B07   add     eax, dword ptr [ebp+71B2A45]
0069A437    96              xchg    eax, esi
0069A438    AD              lods    dword ptr [esi]
0069A439    03C1            add     eax, ecx                                    ; 到这里就根据第一个dword解密出API函数的地址了
0069A43B    5E              pop     esi

刚才我们不是加密了么? 又解密出来了, 并且获得了真实的API函数地址.

0069A43C    83BD F91A1B07 0>cmp     dword ptr [ebp+71B1AF9], 1
0069A443    0F84 39000000   je      delphi7_.0069A482                           ; 判断是否是特殊DLL的特殊函数,是则加密.这里改jmp
0069A449    3B8D DD1C1B07   cmp     ecx, dword ptr [ebp+71B1CDD]                ; 判断到底是哪个DLL, 一看就知道了. 有3个DLL
0069A44F    0F84 2D000000   je      delphi7_.0069A482
0069A455    3B8D 491C1B07   cmp     ecx, dword ptr [ebp+71B1C49]
0069A45B    0F84 21000000   je      delphi7_.0069A482
0069A461    3B8D 89291B07   cmp     ecx, dword ptr [ebp+71B2989]
0069A467    0F84 15000000   je      delphi7_.0069A482
0069A46D    8D9D CBE72C07   lea     ebx, dword ptr [ebp+72CE7CB]
0069A473    FFD3            call    ebx                                         ; 这就是传说中的加密函数了
0069A475    8BF8            mov     edi, eax
0069A477    8985 DD261B07   mov     dword ptr [ebp+71B26DD], eax
0069A47D    E9 3E060000     jmp     delphi7_.0069AAC0
0069A482    8D9D CBE72C07   lea     ebx, dword ptr [ebp+72CE7CB]
0069A488    FFD3            call    ebx                                         ; 这是干嘛的??

这里是判断是否是kernel32.dll user32.dll 或者advapi32.dll中的一个.我们当然要跳过. 这样就可以不加密了. 呵呵. 现在我们要跳过的是两个地方..

0069AAC6    AD              lods    dword ptr [esi]                             ; 这里取第IAT加密表的第2个dword
0069AAC7    C746 FC 0000000>mov     dword ptr [esi-4], 0                        ; 清零, 猥琐
0069AACE    C1C0 05         rol     eax, 5                                      ; 第2个dword是解码出API写入IAT表地址
0069AAD1    05 B6282501     add     eax, 12528B6                                ; 解码算法
0069AAD6    0385 F5031B07   add     eax, dword ptr [ebp+71B03F5]
0069AADC    8B8D DD261B07   mov     ecx, dword ptr [ebp+71B26DD]                ; eax == 要写入IAT表的地址.
0069AAE2    8908            mov     dword ptr [eax], ecx                        ; Code断点的第2个阶段. 断在这里
0069AAE4    AD              lods    dword ptr [esi]                             ; 这里取IAT表的第3个dword
0069AAE5    C746 FC 0000000>mov     dword ptr [esi-4], 0                        ; 清零
0069AAEC    89B5 95211B07   mov     dword ptr [ebp+71B2195], esi                ; 第3个dword解码出调用这个API的地址
0069AAF2    83F8 FF         cmp     eax, -1
0069AAF5    0F85 20000000   jnz     delphi7_.0069AB1B
0069AAFB    813E DDDDDDDD   cmp     dword ptr [esi], DDDDDDDD                   ; 是否是结束标记
0069AB01    0F85 14000000   jnz     delphi7_.0069AB1B
0069AB07    C706 00000000   mov     dword ptr [esi], 0
0069AB0D    83C6 04         add     esi, 4
0069AB10    89B5 95211B07   mov     dword ptr [ebp+71B2195], esi
0069AB16  ^ E9 5CF7FFFF     jmp     delphi7_.0069A277
0069AB1B    C1C0 03         rol     eax, 3
0069AB1E    0385 F5031B07   add     eax, dword ptr [ebp+71B03F5]                ; 定位到调用API的地址
0069AB24    83BD 59251B07 0>cmp     dword ptr [ebp+71B2559], 1
0069AB2B    0F84 9D000000   je      delphi7_.0069ABCE
0069AB31    813E AAAAAAAA   cmp     dword ptr [esi], AAAAAAAA                   ; 判断是否是结束标记
0069AB37    0F85 12000000   jnz     delphi7_.0069AB4F
0069AB3D    83C6 04         add     esi, 4
0069AB40    C746 FC 0000000>mov     dword ptr [esi-4], 0                        ; 清零
0069AB47    97              xchg    eax, edi
0069AB48    B0 E9           mov     al, 0E9

这里取出IAT表的第2个字段, 这个字段的作用是定位到调用API的地址..

0069AB94    B0 90           mov     al, 90
0069AB96    AA              stos    byte ptr es:[edi]                           ; 调用的地址首字节写90
0069AB97    58              pop     eax
0069AB98    AA              stos    byte ptr es:[edi]                           ; 写入E9
0069AB99    E9 24000000     jmp     delphi7_.0069ABC2
0069AB9E    58              pop     eax
0069AB9F    AA              stos    byte ptr es:[edi]
0069ABA0    807F FF E9      cmp     byte ptr [edi-1], 0E9
0069ABA4    0F85 18000000   jnz     delphi7_.0069ABC2
0069ABAA    83BD BED32C07 0>cmp     dword ptr [ebp+72CD3BE], 0
0069ABB1    0F84 08000000   je      delphi7_.0069ABBF
0069ABB7    8D9D 7E1B2C07   lea     ebx, dword ptr [ebp+72C1B7E]
0069ABBD    FFD3            call    ebx
0069ABBF    8847 04         mov     byte ptr [edi+4], al
0069ABC2    8B85 DD261B07   mov     eax, dword ptr [ebp+71B26DD]
0069ABC8    2BC7            sub     eax, edi
0069ABCA    83E8 04         sub     eax, 4
0069ABCD    AB              stos    dword ptr es:[edi]                          ; 写入要跳转到的地址
0069ABCE    AD              lods    dword ptr [esi]                             ; 又加载一个字节
0069ABCF    C746 FC 0000000>mov     dword ptr [esi-4], 0                        ; 清零.. 太猥琐了

这里将调用API的地方改成E9 xxxx. 或者E8 xxxx 这个要看程序, 看原来的是ff15或者是ff25一类的..  当然中间还经历了一些偷取代码的过程, 总之是猥琐的很了. 这就不说了. 我们主要关注的是解密这3个dword的过程, 还有后面我们如何脱这个壳, 差不多就是这样了.. 还有其他其他的东西. 我还没有分析到.. 当然明白了作用,肯定不能自己一处一处的去patch代码. 还是用脚本吧, 用人家写的脚本的话, 也没有什么问题, 主要是能够自己知道如何修改脚本, 不然就是死了..差不多了. 刚才我也拿了, 1.9版本以下的壳我脱掉都没有什么问题了.. 现在主要是搞1.9版本往上的.