Themida 1.2.0.1 全过程分析
本帖最后由 hkfans007 于 2011-4-17 19:25 编辑【作者】
by hkfans --> 转载请注明作者,谢谢!
【脱后感想】
总共花了3个多月的业余时间来分析themida.. 到现在我大概只剩半条命了。。。 真TMD的累! 脱壳不是人干的活啊。。眼睛快瞎了。精神也有点不大正常。。哎。。累! 再次膜拜写壳的人!
【感谢】
仅以此文感谢我的偶像softworm,要不是他的天书【真的是天书,分析的太透彻了】,我现在估计还是苦苦挣扎在VM的汪洋大海之中..
【思路】
脱壳脱到最后发现其实就是做4件事,anti 解码 IAT OEP到达 (大牛都参透了这个道理了 :) 最后一个算不算一件事情啊?^_^)
所以下面就按照这四个部分来分析themida..
【附件】
本篇文章, NOTEPAD样本 以及简单的IAT还原的脚本
【其他】
代码太多了。我就不全部贴出来了。。只贴出这四部分的内容, 还有很多的准备工作的代码我就不贴出出来了。。
准备工作包括:
1. 一开始外壳分配内存来存储VM的vmContext, oneByteOpcodeTable, twoByteOpcodeTable 以及所有HANDLE的代码.. themida对VirtualAlloc的调用超级多 。。。
2. 在VM用LoadLibrary加载kernel32.dlluser32.dll advapi32.dll 在VM中获取CreateFileA ReadFile CloseFile VirtualAlloc
等API函数,然后利用ReadFile映射kernel32.dlluser32.dll advapi32.dll, 这样以后基本上所有的API函数地址都是镜像地址,非真实的API函数地址..
3. 创建用于解码外壳自身代码的线程,外壳经常是主线程和支线程交替解码,特别特别多的这样的代码。。真正有用的代码其实不多。大多数都是
垃圾代码和这样解码外壳自身代码的代码。。
【最后】
可能有很多东西我漏掉分析。。有些地方自己还没有搞清楚。。。比如调用ZwQuerySystemInformation存储了一些信息之后,但是后面好像没有跟到利用这些信息的代码
还有运行时间的检测,前面有看到保存时间,但后面又没有跟到检测时间的代码。。。<估计是VM跟丢了>
下面进入正题:
=======================================================================================================================================================
【第一部分: anti】
[说明]
themida 1.2.0.1 的anti-debug 的手段有: <好像还有很多我没有看到的样子。:)>
OutputDebugStringA---> 针对OD的BUG, 具体为什么我还没有细细研究..
CreateFileA --> 检查是否使用SoftIce
FindWindowA ---> 查找是否有Filemon RegMon 之类的软件
【详细代码】
1. 使用OutputDebugStringA对付存在BUG的OD
// 先获取OutputDebugStringA在镜像中的地址
011D0555 8D85 7937AA09 lea eax, dword ptr
011D0688 8D85 711DAA09 lea eax, dword ptr
011D068E 0F8E 12000000 jle 011D06A6
011D0696 899D 451C9E09 mov dword ptr , ebx
011D06A6 FFD0 call eax ; call011CED94 调用函数获取 OutputDebugStringA在镜像中的内存地址
// 然后在VM中调用OutputDebugStringA
0106 13 01 011D1A10 push
0107 07 02 011D19F4 mov , context.esp
0108 23 01 011D1A1E Sub , 00000004
0109 30 01 011D19CA pop (4 bytes)
0110 34 02 011D19E6 push
0111 2D 00 011D1A02 pop (4 bytes)
0112 13 00 011D19D8 load context.register, 07C61104
0113 36 00 011D1A2C mov , context.esp
0114 23 01 011D1A9C Sub , 00000004
0115 11 00 011D1A8E push
0116 2D 00 011D1A56 pop (4 bytes)
0117 20 00 011D1A72 push 011D1AB8
0118 39 01 011D1A3A mov , context.esp
0119 0A 02 011D1A80 pop (4 bytes)
0120 1A 01 011D1A64 mov , context.ebp
0121 3B 00 011D1A48 Add , 099E2B09
0122 11 02 011D1AAA call ; call 0098A14C (OutputDebugStringA)
// 堆栈
0006FF94011D1AB8NOTEPAD_.011D1AB8
0006FF98011D0C77ASCII CR,LF,LF,LF,"------------------------------------------------",LF,CR,"--- Themida Professional ---",LF,CR,"--- (c)2005 Oreans Technologies ---",LF,CR,"------------------------------------------------",CR,LF...
================================================================================================================================
2. 使用FindWindowA 查找是否存在指定的软件
011F6349D0 DB F6 F3 2C 6F CE 95 46 69 6C 65 6D 6F 6E 43雄鲶,o螘FilemonC
011F63596C 61 73 73 00 46 69 6C 65 20 4D 6F 6E 69 74 6Flass.File Monito
011F636972 20 2D 20 53 79 73 69 6E 74 65 72 6E 61 6C 73r - Sysinternals
011F63793A 20 77 77 77 2E 73 79 73 69 6E 74 65 72 6E 61: www.sysinterna
011F63896C 73 2E 63 6F 6D 00 C8 61 86 47 74 9D 12 E3 00ls.com.萢咷t??
0037 00 011F70F4 push 00000000
0038 1A 01 011F713A mov , context.esp
0039 17 00 011F7102 Sub , 00000004
0040 30 01 011F712C pop (4 bytes)
0041 1F 01 011F7148 push
0042 0A 02 011F7110 pop (4 bytes) ; push 0
0043 13 00 011F711E load context.register, 07AA8B2C
0044 36 00 011F7156 mov , context.ebp
0045 3B 00 011F7172 Add , 09AC932E
0046 34 02 011F7164 push
0047 07 02 011F718E mov , addr_context.eax
0048 14 02 011F7180 pop (4 bytes) ; lea eax, dword ptr
0049 36 00 011F719C mov , addr_context.eax
0050 20 00 011F71D4 push
0051 39 01 011F71FE mov , context.esp
0052 08 00 011F71F0 Sub , 00000004
0053 30 01 011F71C6 pop (4 bytes)
0054 32 01 011F71B8 push
0055 30 01 011F71E2 pop (4 bytes) ; push eax
0056 1C 02 011F71AA load context.register, 0A831D62
0057 36 00 011F720C mov , context.esp
0058 23 01 011F728A Sub , 00000004
0059 13 01 011F7244 push
0060 2D 00 011F7260 pop (4 bytes)
0061 20 00 011F727C push 011F7298
0062 39 01 011F7298 mov , context.esp
0063 30 01 011F721A pop (4 bytes)
0064 1A 01 011F7252 mov , context.ebp; call 0A676E1 --> FindWindowA
0065 3B 00 011F7236 Add , 099E0055
0066 29 00 011F7228 call ; call dword ptr
// 堆栈 ---> 返回窗口的句柄
// 如果检测到的话就弹出错误
A monitor program has been found running in your system.
Please, unload it from memory and restart your program.
0006FF70011F6351ASCII "Filemon Class" -->窗口的类名
0006FF7400000000
0067 02 011F72A6 mov , addr_context.eax
0068 11 00 011F72B4 push
0069 07 02 011F72DE mov , addr_context.eax
0070 13 01 011F72C2 push
0071 3B 01 011F72D0 Test , (4 bytes) ; test eax, eax
0072 3A 00 011F72EC jnz 011F7500
0007 3C 02 011F72FA mov , context.ebp
0008 3B 00 011F7316 Add , 09AC933B
0009 32 01 011F7340 push
0010 36 00 011F7324 mov , addr_context.eax
0011 30 01 011F7308 pop (4 bytes) ; lea eax, dword ptr
0012 1C 02 011F7332 load context.register, 0AAE8C69
0013 1A 01 011F734E mov , addr_context.eax
0014 11 00 011F7386 push
0015 1A 01 011F735C mov , context.esp
0016 08 00 011F7378 Sub , 00000004
0017 2D 00 011F736A pop (4 bytes)
0018 20 00 011F7394 push
0019 30 01 011F73A2 pop (4 bytes) ; push eax
0020 34 02 011F73B0 push 00000000
0021 07 02 011F73BE mov , context.esp
0022 23 01 011F73DA Sub , 00000004
0023 0A 02 011F73E8 pop (4 bytes)
0024 13 01 011F73CC push
0025 14 02 011F73F6 pop (4 bytes) ; push 0
0026 07 02 011F7404 mov , context.esp
0027 23 01 011F742E Sub , 00000004
0028 34 02 011F7412 push
0029 2D 00 011F744A pop (4 bytes)
0030 11 00 011F7466 push 011F7490
0031 07 02 011F7482 mov , context.esp
0032 30 01 011F7458 pop (4 bytes)
0033 07 02 011F7474 mov , context.ebp
0034 3B 00 011F7420 Add , 099E0055
0035 25 00 011F743C call ; call FindWindowA
// 堆栈
0006FF7000000000
0006FF74011F635EASCII "File Monitor - Sysinternals: www.sysinternals.com"
---------------------------------------------------------------------
0112 00 011F939D push 00000000
0113 36 00 011F93C7 mov , context.esp
0114 23 01 011F93AB Sub , 00000004
0115 30 01 011F93D5 pop (4 bytes)
0116 20 00 011F93B9 push
0117 14 02 011F93E3 pop (4 bytes)
0118 39 01 011F93F1 mov , context.ebp
0119 35 01 011F941B Add , 09ACB5F0
0120 11 00 011F940D push
0121 1A 01 011F9437 mov , addr_context.eax
0122 0A 02 011F9429 pop (4 bytes)
0123 13 00 011F93FF load context.register, 006D7A4E
0124 39 01 011F9445 mov , addr_context.eax
0125 32 01 011F9453 push
0126 39 01 011F946F mov , context.esp
0127 23 01 011F9461 Sub , 00000004
0128 14 02 011F9499 pop (4 bytes)
0129 13 01 011F948B push
0130 2D 00 011F947D pop (4 bytes)
0131 13 00 011F94A7 load context.register, 0BA7DD87
0132 07 02 011F94B5 mov , context.esp
0133 17 00 011F9533 Sub , 00000004
0134 1F 01 011F94C3 push
0135 14 02 011F94ED pop (4 bytes)
0136 11 00 011F94DF push 011F9541
0137 1A 01 011F94D1 mov , context.esp
0138 0A 02 011F9517 pop (4 bytes)
0139 1A 01 011F9509 mov , context.ebp
0140 3B 00 011F9525 Add , 099E0055
0141 29 00 011F94FB call ; call FindWindowA
// 堆栈
0006FF70011F8613ASCII "RegmonClass"
0006FF7400000000
0142 02 011F9541 mov , addr_context.eax
0143 34 02 011F954F push
0144 39 01 011F9579 mov , addr_context.eax
0145 1F 01 011F956B push
0146 3B 01 011F955D Test , (4 bytes)
0147 38 00 011F9587 jnz 011F97B7
0007 3C 02 011F9595 mov , context.ebp
0008 35 01 011F95BF Add , 09ACB5FC
0009 34 02 011F95DB push
0010 36 00 011F95A3 mov , addr_context.eax
0011 0A 02 011F95CD pop (4 bytes)
0012 1C 02 011F95B1 load context.register, 09463DEA
0013 36 00 011F95E9 mov , addr_context.eax
0014 32 01 011F9613 push
0015 39 01 011F963D mov , context.esp
0016 23 01 011F9605 Sub , 00000004
0017 2D 00 011F9621 pop (4 bytes)
0018 32 01 011F95F7 push
0019 2D 00 011F962F pop (4 bytes)
0020 13 00 011F964B load context.register, 0CA35115
0021 34 02 011F9659 push 00000000
0022 07 02 011F9675 mov , context.esp
0023 23 01 011F9691 Sub , 00000004
0024 30 01 011F969F pop (4 bytes)
0025 32 01 011F9683 push
0026 0A 02 011F9667 pop (4 bytes)
0027 1A 01 011F96AD mov , context.esp
0028 08 00 011F96C9 Sub , 00000004
0029 20 00 011F96E5 push
0030 2D 00 011F9701 pop (4 bytes)
0031 11 00 011F96BB push 011F9739
0032 07 02 011F970F mov , context.esp
0033 30 01 011F9739 pop (4 bytes)
0034 36 00 011F96D7 mov , context.ebp
0035 35 01 011F971D Add , 099E0055
0036 3A 00 011F972B call ; call FindWindowA
// 堆栈
0006FF7000000000
0006FF74011F861FASCII "Registry Monitor - Sysinternals: www.sysinternals.com"
================================================================================================================================
3. 使用CreateFileA函数检测SoftICE调试软件
//////////////////////////////////////////////////////////////////////////
// 下面调用 CreateFileA 来检测 SoftICE调试软件
//
011B367C5C 5C 2E 5C 53 49 43 45 00 5C 5C 2E 5C 53 49 57\\.\SICE.\\.\SIW
011B368C56 49 44 00 5C 5C 2E 5C 4E 54 49 43 45 VID.\\.\NTICE
// 堆栈
0006FF80 011B38B3/CALL 到 CreateFileA 来自 NOTEPAD_.011B38B1
0006FF84 011B367C|FileName = "\\.\SICE"
0006FF88 C0000000|Access = GENERIC_READ|GENERIC_WRITE
0006FF8C 00000003|ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
0006FF90 00000000|pSecurity = NULL
0006FF94 00000003|Mode = OPEN_EXISTING
0006FF98 00000080|Attributes = NORMAL
0006FF9C 00000000\hTemplateFile = NULL
011B38A8 891C24 mov dword ptr , ebx ;
011B38AB 899D 4D029E09 mov dword ptr , ebx
011B38B1 FFD0 call eax ; eax = 7C801A28 CreateFileA (这里返回0xFFFFFFFF表示不存在)
// 这里的乱序模式很常见...
011B38B3 6A 00 push 0 ;
011B38B5 56 push esi ;
011B38BE BE BB381B01 mov esi, 011B38BB
011B38BF 897424 04 mov dword ptr , esi
011B38C3 814424 04 18000000 add dword ptr , 18
011B38CB 46 inc esi
011B38CC 56 push esi ;
011B38CD C3 retn
011B38BC 5E pop esi
011B38BD C3 retn
//
011B38D9 8BD0 mov edx, eax
011B38DA 40 inc eax ; eax = -1
011B38DB 0F85 18030000 jnz 011B3BF9 ; 如果这里不等于0就表示检测到SOFTICE软件
// 这里的乱序模式很常见...
011B390D 6A 00 push 0 ; ---> PCode数据地址
011B390F 57 push edi ; ---> PCode数据地址
011B3918 BF 15391B01 mov edi, 011B3915
011B3919 897C24 04 mov dword ptr , edi
011B391D 814424 04 16000000 add dword ptr , 16
011B3925 47 inc edi
011B3926 57 push edi ; ---> PCode数据地址
011B3927 C3 retn
011B3916 5F pop edi
011B3917 C3 retn
011B3A3B 66:81D2 701A adc dx, 1A70
011B3A40 0F014C24 FE sidt fword ptr
011B3A45 5E pop esi
011B3A46 89BD 55059E09 mov dword ptr , edi
011B3A4C 5E pop esi
011B3A4D FFD0 call eax ; eax = 7C801A28 CreateFileA (这里返回0xFFFFFFFF表示不存在)
// 堆栈
0006FF80 011B3A4F/CALL 到 CreateFileA 来自 NOTEPAD_.011B3A4D
0006FF84 011B3685|FileName = "\\.\SIWVID"
0006FF88 C0000000|Access = GENERIC_READ|GENERIC_WRITE
0006FF8C 00000003|ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
0006FF90 00000000|pSecurity = NULL
0006FF94 00000003|Mode = OPEN_EXISTING
0006FF98 00000080|Attributes = NORMAL
0006FF9C 00000000\hTemplateFile = NULL
011B3A6B 8985 A1209E09 mov dword ptr , eax
011B3A71 40 inc eax
011B3A72 0F85 81010000 jnz 011B3BF9 ; 如果这里不等于0就表示检测到SIWVID
011B3BAA 23B5 4D1B9E09 and esi, dword ptr
011B3BB0 FFD0 call eax ; eax = 7C801A28 CreateFileA (这里返回0xFFFFFFFF表示不存在)
// 堆栈
0006FF80 011B3BB2/CALL 到 CreateFileA 来自 NOTEPAD_.011B3BB0
0006FF84 011B3690|FileName = "\\.\NTICE"
0006FF88 C0000000|Access = GENERIC_READ|GENERIC_WRITE
0006FF8C 00000003|ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
0006FF90 00000000|pSecurity = NULL
0006FF94 00000003|Mode = OPEN_EXISTING
0006FF98 00000080|Attributes = NORMAL
0006FF9C 00000000\hTemplateFile = NULL
011B3BB2 0995 7D0D9E09 or dword ptr , edx
011B3BB8 40 inc eax ; 如果这里不等于0就表示检测到NTICES
011B3BB9 0F85 3A000000 jnz 011B3BF9
================================================================================================================================
【第二部分:解码】
1. 先用VirtualProtect修改内存属性..
01203A7D 8B85 C5239E09 mov eax, dword ptr
01203A83 8D8D 536AAD09 lea ecx, dword ptr
01203A89 8941 01 mov dword ptr , eax
01203A8C 8BC3 mov eax, ebx
01203A8E C685 4D229E09 56 mov byte ptr , 56
01203A95 68 DF30C5A9 push A9C530DF
01203A9A FFB5 55209E09 push dword ptr
01203AA0 8D85 711DAA09 lea eax, dword ptr
01203AA6 FFD0 call eax ; 获取API函数VirtualProtecteax = 00930ED4
01203AA8 8D8D 99209E09 lea ecx, dword ptr ; eax = 00930ED4
01203AAE 51 push ecx
01203AAF 6A 40 push 40
01203AB1 FFB5 1D109E09 push dword ptr
01203AB7 FFB5 1D259E09 push dword ptr
01203ABD FFD0 call eax ; 调用VirtualProtect 修改内存访问属性
// 堆栈
0006FF28 01001000|Address = NOTEPAD_.01001000 ; 终于要开始解码被保护程序的代码了... shit
0006FF2C 000034F8|Size = 34F8 (13560.)
0006FF30 00000040|NewProtect = PAGE_EXECUTE_READWRITE; 修改的范围 010044F8 --> 01001000
0006FF34 0110F0BC\pOldProtect = NOTEPAD_.0110F0BC
2. 解压.rsrc 资源段数据
//
// 这里调用UPX解压缩代码的函数01114A45, 注意这里解压的是 .rsrc 资源段的数据的一部分0100B000 --> 0100B568 范围的数据
//
//
01204AB9 8BD8 mov ebx, eax
01204ABB 8B95 E1229E09 mov edx, dword ptr
01204AC1 0395 81269E09 add edx, dword ptr ; edx = 0100B568
01204AC7 8BC2 mov eax, edx ; eax = edx
01204AC9 8B8D 49139E09 mov ecx, dword ptr ; ecx = 0000400D
01204ACF 8330 25 xor dword ptr , 25 ; eax = 0100B568
01204AD2 40 inc eax
01204AD3 49 dec ecx
01204AD4 0F85 F5FFFFFF jnz 01204ACF
01204ADA 53 push ebx ; ebx = 014D0000(UPX解压的目的地址)
01204ADB 52 push edx ; edx = 0100B568(压缩后呆解压的数据)
01204ADC 8D85 227A9E09 lea eax, dword ptr
01204AE2 FFD0 call eax ; UPX解压缩函数
//
01204AE4 8BBD E1229E09 mov edi, dword ptr
01204AEA 03BD 81269E09 add edi, dword ptr ; edi = 0100B568
01204AF0 8BF3 mov esi, ebx
01204AF2 8BC8 mov ecx, eax
01204AF4 F3:A4 rep movs byte ptr es:, byte ptr ; esi = 014D0000(复制解压缩后的数据回去)
01204AF6 C685 4D229E09 56 mov byte ptr , 56
01204AFD 68 396D1FD4 push D41F6D39
01204B02 FFB5 55209E09 push dword ptr
01204B08 8D85 711DAA09 lea eax, dword ptr
01204B0E FFD0 call eax ; 查找API函数 VirtualFree
01204B10 68 00800000 push 8000
01204B15 6A 00 push 0
01204B17 52 push edx
01204B18 FFD0 call eax ; 调用VirtualFree释放内存 --> 这里释放失败,因为那个内存不是用VirtualAlloc分配的内存, 搞什么鬼?
// 堆栈
0006FF28 0100B568|Address = NOTEPAD_.0100B568
0006FF2C 00000000|Size = 0
0006FF30 00008000\FreeType = MEM_RELEASE
01204B1A 8BBD 81269E09 mov edi, dword ptr ; edi = 01000000 模块地址
01204B20 037F 3C add edi, dword ptr ; PE文件头
01204B23 8BBF 88000000 mov edi, dword ptr
01204B29 03BD 81269E09 add edi, dword ptr ; edi = 0100B000 (.rsrc段的RVA)
01204B2F 8BB5 19049E09 mov esi, dword ptr ; esi = 01203D5A
01204B35 B9 68050000 mov ecx, 568
01204B3A F3:A4 rep movs byte ptr es:, byte ptr
01204B3C B8 414B0000 mov eax, 4B41
01204B41 8BC0 mov eax, eax
01204B43 83BD 15009E09 00 cmp dword ptr , 0
01204B4A 75 09 jnz short 01204B55
3. 解码被保护的代码
//
// 获取API函数VirtualAlloc
//
01204BDF 8B9D 81269E09 mov ebx, dword ptr
01204BE5 C685 4D229E09 56 mov byte ptr , 56
01204BEC 68 52B8A89C push 9CA8B852
01204BF1 FFB5 55209E09 push dword ptr
01204BF7 8D85 711DAA09 lea eax, dword ptr
01204BFD FFD0 call eax
01204BFF 6A 04 push 4
01204C01 68 00100000 push 1000
01204C06 FFB5 891D9E09 push dword ptr
01204C0C 6A 00 push 0
01204C0E FFD0 call eax ; 先调用VirtualAlloc分配内存, 存放待解压缩的代码,后面会释放
0006FF24 00000000|Address = NULL
0006FF28 0000A000|Size = A000 (40960.)
0006FF2C 00001000|AllocationType = MEM_COMMIT
0006FF30 00000004\Protect = PAGE_READWRITE
01204C10 85C0 test eax, eax ; eax = 014E0000
01204C12 0F85 0D000000 jnz 01204C25
01204C25 8BC8 mov ecx, eax
01204C27 8BC3 mov eax, ebx
01204C29 0340 3C add eax, dword ptr ; eax = 010000E0 PE文件头
01204C2C 05 F8000000 add eax, 0F8
01204C31 8B50 0C mov edx, dword ptr
01204C34 03D3 add edx, ebx ; edx = 01001000 .code段
01204C36 83BD A5089E09 00 cmp dword ptr , 0
01204C3D 0F84 0E000000 je 01204C51
//
// UPX解压缩到申请的内存中
//
01204C51 51 push ecx ; 前面申请的内存,
01204C52 52 push edx ; 01001000 (这个是压缩后的被保护的代码)
01204C53 8D85 227A9E09 lea eax, dword ptr
01204C59 FFD0 call eax ; 调用UPX解压函数
// 堆栈
0006FF2C 01001000NOTEPAD_.01001000--> 待解压的数据(源数据)
0006FF30 014E0000 --> 解压后存放的内存(目的地址)
//
// 拷贝回去被保护程序的区段,然后释放申请的内存...
//
01204C5B 8BFA mov edi, edx
01204C5D 8BF1 mov esi, ecx
01204C5F 8BD1 mov edx, ecx
01204C61 8BC8 mov ecx, eax
01204C63 F3:A4 rep movs byte ptr es:, byte ptr ; esi = 014E0000edi = 01001000 (赋值回代码)
01204C65 C685 4D229E09 56 mov byte ptr , 56
01204C6C 68 396D1FD4 push D41F6D39
01204C71 FFB5 55209E09 push dword ptr
01204C77 8D85 711DAA09 lea eax, dword ptr
01204C7D FFD0 call eax ; 查找API函数VirtualFree, 释放内存,和前面一样
================================================================================================================================
【第三部分:IAT加载】
IAT的加载被分割成前后两部分:
第一部分是在VM中加载被保护程序需要的动态库,然后将加载后得到的导入模块的基地址保存在一块申请的内存中
第二部分才是获取导入函数的地址,然后加密Kernel32.dll user32.dll advapi32.dll三个模块的导入函数,加密一些特殊的函数如ExitProcess, 最后修复
被保护程序中调用这些API函数的指令... 如FF15 FF25类型的..
1. 获取一些需要特殊处理的API函数的地址..
0120BF5C C785 AD0D9E09 00000000mov dword ptr , 0
0120BF66 C685 4D229E09 45 mov byte ptr , 45
0120BF6D 68 6969728E push 8E726969
0120BF72 FFB5 812E9E09 push dword ptr
0120BF78 8D85 52369E09 lea eax, dword ptr
0120BF7E FFD0 call eax ; 查找API函数ExitProcess = 7C81CB12 (这个是真实的模块地址,非镜像地址)
0120BF80 8985 912E9E09 mov dword ptr , eax ; eax = 7C81CB12 --> ExitProcess
0120BF86 C685 4D229E09 43 mov byte ptr , 43
0120BF8D 68 5E6B679C push 9C676B5E
0120BF92 FFB5 812E9E09 push dword ptr
0120BF98 8D85 52369E09 lea eax, dword ptr
0120BF9E FFD0 call eax ; 查找API函数CreateThread
0120BFA0 8985 EDEDAD09 mov dword ptr , eax ; eax = 7C8106D7 --> CreateThread
0120BFA6 C685 4D229E09 54 mov byte ptr , 54
0120BFAD 68 296862EE push EE626829
0120BFB2 FFB5 812E9E09 push dword ptr
0120BFB8 8D85 52369E09 lea eax, dword ptr
0120BFBE FFD0 call eax
0120BFC0 8985 F1EDAD09 mov dword ptr , eax ; eax = 7C81CB3B--> TerminateThread
0120BFC6 C685 4D229E09 45 mov byte ptr , 45
0120BFCD 68 C23860DA push DA6038C2
0120BFD2 FFB5 812E9E09 push dword ptr
0120BFD8 8D85 52369E09 lea eax, dword ptr
0120BFDE FFD0 call eax
0120BFE0 8985 F5EDAD09 mov dword ptr , eax ; eax = 7C80C0F8 --> ExitThread
0120BFE6 83BD 391A9E09 00 cmp dword ptr , 0
0120BFED 0F85 45000000 jnz 0120C038
0120C038 C685 4D229E09 52 mov byte ptr , 52
0120C03F 68 0DE44ABC push BC4AE40D
0120C044 FFB5 E51F9E09 push dword ptr
0120C04A 8D85 52369E09 lea eax, dword ptr
0120C050 FFD0 call eax
0120C052 8985 F9EDAD09 mov dword ptr , eax ; eax = ntdll.RtlEnterCriticalSection
0120C058 C685 4D229E09 52 mov byte ptr , 52
0120C05F 68 484A4CA6 push A64C4A48
0120C064 FFB5 E51F9E09 push dword ptr
0120C06A 8D85 52369E09 lea eax, dword ptr
0120C070 FFD0 call eax
0120C072 8985 FDEDAD09 mov dword ptr , eax ; eax = 7C9210E0 ntdll.RtlLeaveCriticalSection
0120C078 C685 4D229E09 52 mov byte ptr , 52
0120C07F 68 EAB442FD push FD42B4EA
0120C084 FFB5 812E9E09 push dword ptr
0120C08A 8D85 52369E09 lea eax, dword ptr
0120C090 FFD0 call eax
0120C092 8985 E5199E09 mov dword ptr , eax ; eax = 7C801812 ReadFile
0120C098 C685 4D229E09 77 mov byte ptr , 77
0120C09F 68 A3A897F3 push F397A8A3
0120C0A4 FFB5 19119E09 push dword ptr
0120C0AA 8D85 52369E09 lea eax, dword ptr
0120C0B0 FFD0 call eax
0120C0B2 8985 89139E09 mov dword ptr , eax ; eax = 77D1A8AD wsprintfA
0120C0B8 C685 4D229E09 52 mov byte ptr , 52
0120C0BF 68 1731DD8A push 8ADD3117
0120C0C4 FFB5 812E9E09 push dword ptr
0120C0CA 8D85 52369E09 lea eax, dword ptr
0120C0D0 FFD0 call eax
0120C0D2 8985 BD089E09 mov dword ptr , eax ; eax = 7C812AA9 RaiseException
0120C0D8 C685 4D229E09 56 mov byte ptr , 56
0120C0DF 68 396D1FD4 push D41F6D39
0120C0E4 FFB5 55209E09 push dword ptr
0120C0EA 8D85 711DAA09 lea eax, dword ptr
0120C0F0 FFD0 call eax ; 这里是查找镜像API函数VirtualFree地址
0120C0F2 8985 B91B9E09 mov dword ptr , eax ; eax = 00938F84 VirtualFree
0120C0F8 C685 4D229E09 47 mov byte ptr , 47
0120C0FF 68 7A10DC81 push 81DC107A
0120C104 FFB5 812E9E09 push dword ptr
0120C10A 8D85 52369E09 lea eax, dword ptr
0120C110 FFD0 call eax
0120C112 8985 11059E09 mov dword ptr , eax ; eax =7C80B56F GetModuleFileNameA
0120C118 C685 4D229E09 47 mov byte ptr , 47
0120C11F 68 E2B50F8A push 8A0FB5E2
0120C124 FFB5 812E9E09 push dword ptr
0120C12A 8D85 52369E09 lea eax, dword ptr
0120C130 FFD0 call eax
0120C132 8985 1DEEAD09 mov dword ptr , eax ; EAX = 7C80AE40GetProcAddress
0120C138 83BD 3D1D9E09 00 cmp dword ptr , 0
0120C13F 0F84 40040000 je 0120C585
2.申请两块中间过渡的内存,用来存储API复制代码和经过乱序变形后的API函数代码
0120C585 6A 04 push 4
0120C587 68 00100000 push 1000
0120C58C 68 00100000 push 1000
0120C591 6A 00 push 0
0120C593 FF95 41169E09 call dword ptr ; 申请内存VirtualAlloc
0120C599 8985 B5259E09 mov dword ptr , eax ; eax = 014E0000
0120C59F 8D85 E491AD09 lea eax, dword ptr
0120C5A5 FFD0 call eax
0120C5A7 6A 04 push 4
0120C5A9 68 00100000 push 1000
0120C5AE 68 00200000 push 2000
0120C5B3 6A 00 push 0
0120C5B5 FF95 41169E09 call dword ptr ; 调用VirtualAlloc分配内存 (用于保存临时的API函数的复制代码)
0120C5BB 8985 89119E09 mov dword ptr , eax
0120C5C1 8985 511A9E09 mov dword ptr , eax
0120C5C7 6A 40 push 40
0120C5C9 68 00100000 push 1000
0120C5CE 68 00000100 push 10000
0120C5D3 6A 00 push 0
0120C5D5 FF95 41169E09 call dword ptr ; 调用VirtualAlloc分配内存(用于保存临时的API函数变形代码)
0120C5DB 8985 7D229E09 mov dword ptr , eax
3. IAT 数据的结构大概如下 <不准确>
//
// 数据 -- 所有的IAT数据,结构如下
//
struct stTmdIAT
{
DWORD apiNameHashVal;//导入API函数的名称的HASH值(未解密)
DWORD iatAddress;//该导入API函数的IAT地址 (未解密)===> 如果后面紧跟AAAAAAAA表示FF25 jmp 类型的。。
DWORD callApiInstructions; //被保护程序中调用该API函数的指令地址数组, 以FFFFFFFF结束
};
4. 循环处理每个导入库
0120C5E1 8BB5 E51C9E09 mov esi, dword ptr ; ESI = 01204F0A
0120C5E7 8B9D 11249E09 mov ebx, dword ptr ; ebx = 00FE0000(之前申请的用于保存被保护程序导出库的库基地址)
0120C5ED 89B5 612B9E09 mov dword ptr , esi
0120C5F3 899D 2D239E09 mov dword ptr , ebx
0120C5F9 8B9D 11249E09 mov ebx, dword ptr ; ebx = 00FE0000
0120C5FF 8B0B mov ecx, dword ptr ; ecx = 76320000 (comdlg32模块基地址)
0120C601 83F9 00 cmp ecx, 0
0120C604 0F84 8D090000 je 0120CF97 ; 循环处理每个导入模块 (等于0表示处理完所有的导入库)
// 处理一个导入模块, 先将该模块的所有导出函数名称进行HASH,保存HASH数值到申请的内存中
0120C60A 50 push eax ; eax = 01500000
0120C60B 51 push ecx ; 入栈模块基地址(comdlg32.76320000 )
0120C60C 60 pushad
0120C60D 33C0 xor eax, eax
0120C60F 8985 D1089E09 mov dword ptr , eax
0120C615 BE 3C000000 mov esi, 3C ; esi = 3C
0120C61A 037424 20 add esi, dword ptr ; esi = 7632003C
0120C61E 66:AD lods word ptr
0120C620 034424 20 add eax, dword ptr ; "PE"文件头
0120C624 8B70 78 mov esi, dword ptr
0120C627 037424 20 add esi, dword ptr ; comdlg32导出表的RVA esi = 76322050
0120C62B 8B7E 18 mov edi, dword ptr ; edi = 1A 导出表的名称个数
0120C62E 89BD 8D029E09 mov dword ptr , edi
0120C634 85FF test edi, edi
0120C636 0F85 0A000000 jnz 0120C646 ; 循环该库导出表的每个函数
-------------------
//
// 申请内存,用于存放每个导出函数名的HASH值
//
0120C646 51 push ecx
0120C647 8BD7 mov edx, edi
0120C649 6BD2 04 imul edx, edx, 4
0120C64C 8995 75009E09 mov dword ptr , edx ; edx = 1A*4 = 68
0120C652 6A 04 push 4
0120C654 68 00100000 push 1000
0120C659 52 push edx
0120C65A 6A 00 push 0
0120C65C FF95 41169E09 call dword ptr ; VirtualAlloc申请内存
// 堆栈
0006FEF8 00000000|Address = NULL
0006FEFC 00000068|Size = 68 (104.)
0006FF00 00001000|AllocationType = MEM_COMMIT
0006FF04 00000004\Protect = PAGE_READWRITE
//----------------------------------------------
// 下面先获取导出函数名称字符串的字符个数,然后对每个导出函数进行查表HASH算法, 保存HASH的DWORD值到前面申请的内存中 。。
// 循环处理开始
0120C670 56 push esi
0120C671 AD lods dword ptr
0120C672 034424 24 add eax, dword ptr ; eax = 7632218D "ChooseColorA"---> 每个输出函数名称字符串
0120C676 97 xchg eax, edi
0120C677 8BDF mov ebx, edi
0120C679 57 push edi
0120C67A 32C0 xor al, al
0120C67C AE scas byte ptr es: ; 计算函数名称字符串的个数
0120C67D 0F85 F9FFFFFF jnz 0120C67C
0120C683 5E pop esi
0120C684 2BFB sub edi, ebx ; edi = 0D (14个字符)
0120C686 52 push edx
0120C687 8BD7 mov edx, edi ; edx = 0D
0120C689 8BBD B5259E09 mov edi, dword ptr ; edi = 014E0000 (前面申请的内存,存放解码用的数据表)
0120C68F 83C9 FF or ecx, FFFFFFFF ; ecx = FFFFFFFF
0120C692 33C0 xor eax, eax ; eax = 0
0120C694 8A06 mov al, byte ptr ; 字符'C'
0120C696 32C1 xor al, cl ; xor al, FF
0120C698 46 inc esi
0120C699 8B0487 mov eax, dword ptr ; 查表 014E0000 --> 得到解码KEY
0120C69C C1E9 08 shr ecx, 8
0120C69F 33C8 xor ecx, eax
0120C6A1 4A dec edx ; 字符个数减去1
0120C6A2 0F85 EAFFFFFF jnz 0120C692 ; 继续下一个字符
0120C6A8 8BC1 mov eax, ecx
0120C6AA F7D0 not eax ; eax = 9E59A0D5
0120C6AC 5A pop edx
0120C6AD 8902 mov dword ptr , eax ; edx = 01510000 (前面申请的用于保存一个导出表中所有导出函数名称字符串的HASH数值)
0120C6AF 83C2 04 add edx, 4
0120C6B2 52 push edx
0120C6B3 FF85 D1089E09 inc dword ptr ; 已HASH的函数个数
0120C6B9 8B95 D1089E09 mov edx, dword ptr
0120C6BF 3995 8D029E09 cmp dword ptr , edx ; = 1A (总的导出函数名称个数 = 1A)
0120C6C5 0F84 0A000000 je 0120C6D5
0120C6CB 5A pop edx
0120C6CC 5E pop esi
0120C6CD 83C6 04 add esi, 4
// 循环处理结束 --> HASH完该导出库的所有API函数名称字符串.. 执行到这里
//-----------------------------------------------------
// 这里开始获取并计算要查找的导入函数的HASH值
//
0120C778 B9 D9454449 mov ecx, 494445D9 ; 硬编码的解码KEY1
0120C77D BA 75518541 mov edx, 41855175 ; 硬编码的解码KEY2
0120C782 AD lods dword ptr ; esi = 01204F0A
0120CE4E C746 FC 0000000> mov dword ptr , 0 ; 读取然后清0
0120C783 89B5 612B9E09 mov dword ptr , esi ; eax = 055FEFDE (原始数据,计算后得到导入函数名称HASH值)
;
0120C790 3D EEEEEEEE cmp eax, EEEEEEEE
0120C795 0F85 20000000 jnz 0120C7BB ; 是否等于EEEEEEEE
{-------------------
0120C79B 813E DDDDDDDD cmp dword ptr , DDDDDDDD
0120C7A1 0F85 14000000 jnz 0120C7BB
0120C7A7 C706 00000000 mov dword ptr , 0 ; 如果是EEEEEEEE 后面跟着 DDDDDDDD 的话
0120C7AD 83C6 04 add esi, 4 ; 表示
0120C7B0 89B5 612B9E09 mov dword ptr , esi
0120CF62 89B5 612B9E09 mov dword ptr , esi
0120CF68 52 push edx
0120CF69 68 00800000 push 8000
0120CF6E 6A 00 push 0
0120CF70 FFB5 BD259E09 push dword ptr
0120CF76 FF95 B91B9E09 call dword ptr
}
//
// 解码导入函数的HASH值
//
0120C7BB 8BD8 mov ebx, eax ; ebx = eax = 055FEFDE
0120C7BD 3385 AD0D9E09 xor eax, dword ptr
0120C7C3 C1C8 03 ror eax, 3
0120C7C6 2BC2 sub eax, edx
0120C7C8 C1C0 10 rol eax, 10
0120C7CB 33C1 xor eax, ecx
0120C7CD 899D AD0D9E09 mov dword ptr , ebx
0120C7D3 3D 00000100 cmp eax, 10000
0120C7D8 0F83 45000000 jnb 0120C823 ; eax =E5C23AFF (最终的导入函数名称的HASH值)
-----------------------
// 下面循环查找HASH值相等的库导出函数
0120C823 51 push ecx ; 保存解码KEY1
0120C824 52 push edx ; 保存解码KEY2
0120C825 33C9 xor ecx, ecx
0120C827 8B95 BD259E09 mov edx, dword ptr
0120C82D 3B02 cmp eax, dword ptr ; eax = E5C23AFFedx = 01510000
0120C82F 0F84 38000000 je 0120C86D ; 注意: 这里的EAX就是真正要找的被保护程序的导入函数名的HASH值
0120C835 83C2 04 add edx, 4
0120C838 41 inc ecx
0120C839 3B8D 8D029E09 cmp ecx, dword ptr ; = 1A (comdlg32模块导出函数个数)
0120C83F 0F85 E8FFFFFF jnz 0120C82D
// 跳到这里,表示找到需要的导入函数, 保存名称表的索引值
; 这里是导入函数 PageSetupDlgW
0120C86D 898D D1089E09 mov dword ptr , ecx ; ecx = 0F ( 表示导出库的第15个导出函数是被保护程序需要的),序数是从1开始的,所以这里是
0120C873 5A pop edx ; 弹出解码KEY2
0120C874 59 pop ecx ; 弹出解码KEY1
--------------------------
0120C875 56 push esi
0120C876 8B9D 11249E09 mov ebx, dword ptr
0120C87C 8B0B mov ecx, dword ptr ;ebx = 00FE0000(之前申请的用于保存被保护程序导入库的库基地址)
0120C87E 8B85 D1089E09 mov eax, dword ptr ; eax = 0F (前面比较得到的导入函数索引值)
0120C884 D1E0 shl eax, 1
0120C886 0385 21009E09 add eax, dword ptr ; eax = 7632216A
0120C88C 33F6 xor esi, esi
0120C88E 96 xchg eax, esi ;
//
// 0120C89C 最关键的代码地址,拦截这里可以看到导入函数地址 -->eax = 76344906 comdlg32.PageSetupDlgW
//
0120C891 C1E0 02 shl eax, 2
0120C894 0385 112A9E09 add eax, dword ptr ; eax = 763220B8
0120C89A 96 xchg eax, esi
0120C89B AD lods dword ptr
0120C89C 03C1 add eax, ecx ; 最关键的代码 eax = 76344906 comdlg32.PageSetupDlgW
0120C89E 5E pop esi
0120C89F 83BD 952A9E09 01 cmp dword ptr , 1
0120C8A6 0F84 39000000 je 0120C8E5
//
// 比较当前模块,如果是Kernel32.dll User32.dll Advapi32.dll 3个导入库的话,就进行特殊的处理
//
0120C8AC 3B8D 812E9E09 cmp ecx, dword ptr ; = Kernel32.7C80000
0120C8B2 0F84 2D000000 je 0120C8E5
0120C8B8 3B8D 19119E09 cmp ecx, dword ptr ; = User32.77D10000
0120C8BE 0F84 21000000 je 0120C8E5
0120C8C4 3B8D BD2A9E09 cmp ecx, dword ptr ; = ADVAPI32.77DA0000
0120C8CA 0F84 15000000 je 0120C8E5
// 其他的模块就走到这里 (不是Kernel32.dll User32.dll Advapi32.dll 3个导入库的话)
0120C8D0 8D9D 79FFAD09 lea ebx, dword ptr
0120C8D6 FFD3 call ebx
//
// 下面是获取API函数的IAT地址... (保存在哪里) 取值是接下来的4个字节, 经过简单的计算再加上被保护程序的基地址 01000000
//
// 最关键的代码 0120CE69 8908 mov dword ptr , ecx(拦截这里可以看到API函数地址和该API函数对应的IAT地址)
//
0120C8D8 8BF8 mov edi, eax
0120C8DA 8985 6D109E09 mov dword ptr , eax ; eax = 76344906 comdlg32.PageSetupDlgW
0120CE47 8BB5 612B9E09 mov esi, dword ptr
0120CE4D AD lods dword ptr ; eax = 5DB5DE67
0120CE4E C746 FC 0000000> mov dword ptr , 0 ; 读取然后清0
0120CE55 C1C0 05 rol eax, 5
0120CE58 05 D9454449 add eax, 494445D9
0120CE5D 0385 81269E09 add eax, dword ptr ; eax = 010012C4 (API函数的IAT的地址)
0120CE63 8B8D 6D109E09 mov ecx, dword ptr ; ecx = 76344906 comdlg32.PageSetupDlgW
0120CE69 8908 mov dword ptr , ecx ; 最关键的代码
//------------------------------------------------------------------------------------------------
// 这里循环处理被保护程序调用该API函数的指令,之前都是NOP填充掉的。。现在修改为CALL 调用到API函数
//
// 循环开始
//
0120CE6B AD lods dword ptr
0120CE6C C746 FC 00000000 mov dword ptr , 0
0120CE73 89B5 612B9E09 mov dword ptr , esi
0120CE79 83F8 FF cmp eax, -1
0120CE7C 0F85 20000000 jnz 0120CEA2 ; 比较是否等于 -1 ( 循环被保护程序所有的对该API调用的指令地址)
0120CEA2 C1C0 03 rol eax, 3
0120CEA5 0385 81269E09 add eax, dword ptr ; eax = 01006576
{
// 被保护的该地址是 调用该IAT指向的API函数...
01006576|.FF15 C4120001 call dword ptr [<&comdlg32.PageSetupDlgW>] ;comdlg32.PageSetupDlgW
}
0120CEAB 83BD 71219E09 01 cmp dword ptr , 1 ; 这个比较是干什么用的?
0120CEB2 0F84 9D000000 je 0120CF55
0120CEB8 813E AAAAAAAA cmp dword ptr , AAAAAAAA ; esi = 结构stTmdIAT指针, 如果不是FFFFFFF,看是否为AAAAAAAA
0120CEBE 0F85 12000000 jnz 0120CED6
// 如果是AAAAAAAA --> 表示用jmp修复调用API函数的指令
0120CEC4 83C6 04 add esi, 4
0120CEC7 C746 FC 00000000 mov dword ptr , 0
0120CECE 97 xchg eax, edi
0120CECF B0 E9 mov al, 0E9 ; AL = E9
//------------ 这种类型的修复 FF25类型的
0100737A $- FF25 B8120001 jmp dword ptr [<&WINSPOOL.ClosePrinter>] ;WINSPOOL.ClosePrinter
//--------------
// 如果不是是AAAAAAAA ---> 表示用CALL修复调用API函数的指令--> FF15类型的
0120CED6 97 xchg eax, edi
0120CED7 B0 E8 mov al, 0E8 ; eax最后一个字节变E8 --CALL
0120CED9 50 push eax
0120CEDA 83BD 952A9E09 01 cmp dword ptr , 1
0120CEE1 0F84 3E000000 je 0120CF25
0120CEE7 B8 00010000 mov eax, 100
0120CEEC 83BD 05EEAD09 00 cmp dword ptr , 0
0120CEF3 0F84 08000000 je 0120CF01
0120CEF9 8D9D 6743AD09 lea ebx, dword ptr
0120CEFF FFD3 call ebx ; 这里调用? 返回 8B
0120CF01 803F 90 cmp byte ptr , 90 ; edi = 01006576 (TMD已经将API函数的调用的指令用NOP填充了)
{
//
}
0120CF04 0F84 08000000 je 0120CF12 ;
0120CF12 83F8 50 cmp eax, 50 ; eax = 60 (这里的分支干什么的?)
0120CF15 0F82 0A000000 jb 0120CF25
0120CF1B B0 90 mov al, 90
0120CF1D AA stos byte ptr es: ; 第一个字节还是0x90
0120CF1E 58 pop eax ; eax最后一个字节变E8 --CALL
0120CF1F AA stos byte ptr es: ; EAX = 763449E8 (最后一个字节是E8 CALL)
0120CF49 8B85 6D109E09 mov eax, dword ptr ; EAX = 76344906 (PageSetupDlgW
0120CF4F 2BC7 sub eax, edi ;
0120CF51 83E8 04 sub eax, 4 ; 这里计算CALL到API函数的偏移地址...
0120CF54 AB stos dword ptr es: ; CALL PageSetupDlgW
0120CF55 AD lods dword ptr ; esi = 结构stTmdIAT指针
0120CF56 C746 FC 00000000 mov dword ptr , 0
0120CE73 89B5 612B9E09 mov dword ptr , esi
0120CE79 83F8 FF cmp eax, -1 ; 循环处理每条调用该API函数的指令
0120CE7C 0F85 20000000 jnz 0120CEA2 ; 如果是 FFFFFFFF表示结束< 跳到前面继续处理下一条调用该API函数的指令>
// 循环结束
//
//-----------------------------------------------------------------------
0120CE82 813E DDDDDDDD cmp dword ptr , DDDDDDDD ; 是否为DDDDDDDD (什么意思?结束?)
0120CE88 0F85 14000000 jnz 0120CEA2
0120CE8E C706 00000000 mov dword ptr , 0
0120CE94 83C6 04 add esi, 4
0120CE97 89B5 612B9E09 mov dword ptr , esi
0120C6DA C785 39169E09 00000000mov dword ptr , 0
0120C6E4 C785 2D259E09 00000000mov dword ptr , 0
0120C6EE 83BD 05EEAD09 00 cmp dword ptr , 0
0120C6F5 0F84 08000000 je 0120C703
0120C6FB 8D9D 483AAD09 lea ebx, dword ptr ; 调用函数01200A6B ( 清0)
0120C701 FFD3 call ebx
0120C703 FF85 BD029E09 inc dword ptr ; 这个什么计数器?
0120C709 83BD BD029E09 64 cmp dword ptr , 64
0120C710 0F82 62000000 jb 0120C778
// 跳到前面继续循环
0120C778 B9 D9454449 mov ecx, 494445D9 ; 硬编码的解码KEY1
0120C77D BA 75518541 mov edx, 41855175
0120C782 AD lods dword ptr
0120C783 89B5 612B9E09 mov dword ptr , esi
0120C789 C746 FC 00000000 mov dword ptr , 0
0120C790 3D EEEEEEEE cmp eax, EEEEEEEE ;是否为EEEEEEEE
0120C795 0F85 20000000 jnz 0120C7BB
================================================================================================================================
【第四部分:OEP到达】
说明:OEP到达前有一段变态的 变形+VM的代码。。太恐怖了。。暂时没有去看。。。 有些地方分析到我也是糊里糊涂的。呵呵
1. 进入OEP前的一些工作
//
// 下面修改第一个区段为可读可执行的代码 (修改区段标志)
//
01213149 8BBD 81269E09 mov edi, dword ptr ; edi = 01000000
0121314F 0F82 05000000 jb 0121315A
0121315A 037F 3C add edi, dword ptr ; edi = 010000E0 PE文件头
01213163 81C7 F8000000 add edi, 0F8 ; edi = 010001D8 (第一个区段结构信息)
==> 原本区段是 C0000040 ,即不是可执行的..
01000224 400000C0 DD C0000040 ;Characteristics = INITIALIZED_DATA|READ|WRITE
==> 这里修改为 60000020 , 代码可取可执行
010001FC 20000060 DD 60000020 ;Characteristics = CODE|EXECUTE|READ
01213189 C747 24 20000060 mov dword ptr , 60000020 ; 修改区段的标志信息 C000040 --> 60000020
012131A5 58 pop eax ; eax = 00930ED4
012131A6 81D2 7F7F736B adc edx, 6B737F7F
012131AC 8D8D 3D129E09 lea ecx, dword ptr
012131C9 51 push ecx
012131F0 FC cld
012131F1 0FBFF2 movsx esi, dx
012131F4 FFB5 99209E09 push dword ptr
0121322D 68 D60468A0 push A06804D6
01213232 66:8BF1 mov si, cx
01213235 810424 2A0B985F add dword ptr , 5F980B2A
01213250 0F85 02000000 jnz 01213258
01213258 FFB5 81269E09 push dword ptr
0121325E 89B5 DD139E09 mov dword ptr , esi
01213264 898D AD109E09 mov dword ptr , ecx
0121326A FFD0 call eax ; 调用VirtualProtect 修改PE文件段
// 堆栈
0006FF24 01000000|Address = NOTEPAD_.01000000
0006FF28 00001000|Size = 1000 (4096.)
0006FF2C 00000002|NewProtect = PAGE_READONLY
0006FF30 0110E260\pOldProtect = NOTEPAD_.0110E260
2. 经过一大堆的乱序变形之后就糊里糊涂来到这里了。。最后一个Retn 就进入了VM-OEP
012136E3 8B85 09109E09 mov eax, dword ptr
012136F4 C600 00 mov byte ptr , 0
012139F5 8DB5 9065AE09 lea esi, dword ptr
01213B40 F3:AA rep stos byte ptr es:
01213CBD C3 retn ; 从这里返回到VM (这里进入的VM是被保护程序OEP处的VM)...
3. VM-OEP伪指令代码
0120AEB4 68 4E30CD46 push 46CD304E
0120AEB9^ E9 39CAFFFF jmp 012078F7
0120AEBE 68 C1A26510 push 1065A2C1==> call 01007568
0120AEC3^ E9 2FCAFFFF jmp 012078F7
0120AEC8 68 32EE2A31 push 312AEE32==> call edi
0120AECD^ E9 25CAFFFF jmp 012078F7
//
// 下面是被VM掉的OEP, 对比记事本的入口代码,完全一样。。。:)
//
0634 09 02 0120AA46 push 00000070
0635 3C 02 0120AA8C mov , context.esp
0636 16 01 0120AA70 Sub , 00000004
0637 3A 02 0120AA9A pop (4 bytes)
0638 09 02 0120AA54 push
0639 3A 02 0120AA62 pop (4 bytes) ; push 70
0640 27 02 0120AA7E load context.register, 060AD197
0641 09 02 0120AAA8 push 01001898
0642 3C 02 0120AAD2 mov , context.esp
0643 16 01 0120AAB6 Sub , 00000004
0644 3A 02 0120AAE0 pop (4 bytes)
0645 09 02 0120AAC4 push
0646 3A 02 0120AAEE pop (4 bytes) ; push 01001898
0647 3C 02 0120AAFC mov , context.esp
0648 16 01 0120AB6C Sub , 00000004
0649 09 02 0120AB18 push
0650 3A 02 0120AB50 pop (4 bytes)
0651 09 02 0120AB42 push 0120AB7A
0652 3C 02 0120AB0A mov , context.esp
0653 3A 02 0120AB26 pop (4 bytes)
0654 3C 02 0120AB34 mov , 01007568
0655 02 02 0120AB5E call context.register ; call 01007568
0656 02 0120AB7A mov , addr_context.ebx
0657 09 02 0120ABDC push
0658 3C 02 0120ABB2 mov , addr_context.ebx
0659 09 02 0120ABCE push
0660 21 00 0120ABA4 Xor , (4 bytes)
0661 3C 02 0120ABC0 mov , addr_context.ebx
0662 3A 02 0120AB96 pop (4 bytes) ; xor ebx, ebx
0663 27 02 0120AB88 load context.register, 0C006D96
0664 3C 02 0120ABEA mov , addr_context.ebx
0665 09 02 0120ABF8 push
0666 3C 02 0120AC06 mov , context.esp
0667 16 01 0120AC30 Sub , 00000004
0668 3A 02 0120AC4C pop (4 bytes)
0669 09 02 0120AC3E push
0670 3A 02 0120AC14 pop (4 bytes) ; push ebx
0671 27 02 0120AC22 load context.register, 07BA6731
0672 3C 02 0120AC5A mov , 010010CC
0673 09 02 0120AC84 push
0674 3C 02 0120AC68 mov , addr_context.edi
0675 3A 02 0120AC76 pop (4 bytes) ; mov edi, dword ptr
0676 3C 02 0120AC92 mov , context.esp
0677 16 01 0120ACCA Sub , 00000004
0678 09 02 0120ACE6 push
0679 3A 02 0120ACBC pop (4 bytes)
0680 09 02 0120ACA0 push 0120AD10
0681 3C 02 0120AD02 mov , context.esp
0682 3A 02 0120ACD8 pop (4 bytes)
0683 3C 02 0120ACAE mov , addr_context.edi
0684 02 02 0120ACF4 call ; call edi
0685 02 0120AD10 mov , context.eax
0686 09 02 0120AD48 push
0687 09 02 0120AD2C push 00005A4D
0688 3E 01 0120AD1E Cmp , (2 bytes) ; cmp word ptr , 5A4D
0689 27 02 0120AD3A load context.register, 0E6ADC79
0690 3C 02 0120AD56 mov , 010073DA
0691 02 02 0120AD64 jnz context.register ; jnz 010073DA
0692 3C 02 0120AD72 mov , context.eax
0693 04 00 0120AD9C Add , 0000003C
0694 09 02 0120AD80 push
0695 3C 02 0120AD8E mov , addr_context.ecx
0696 3A 02 0120ADB8 pop (4 bytes) ; mov ecx, dword ptr
0697 27 02 0120ADAA load context.register, 0B1B1124
0698 09 02 0120ADC6 push 010073BE
0699 3C 02 0120AE0C mov , context.esp
0700 16 01 0120ADFE Sub , 00000004
0701 3A 02 0120ADE2 pop (4 bytes)
0702 09 02 0120AE1A push
0703 3A 02 0120ADF0 pop (4 bytes) ; push 010073BE
0704 27 02 0120ADD4 load context.register, 018AABBB
0705 3C 02 0120AE28 mov , context.esp
0706 09 02 0120AE60 push
0707 3C 02 0120AE7C mov , context.esp
0708 04 00 0120AE52 Add , 00000004
0709 09 02 0120AE6E push
0710 3A 02 0120AE36 pop (4 bytes)
0711 3A 02 0120AE44 pop (4 bytes) ; pop Reg
0712 02 02 0120AE8A jmp context.register ; jmp Reg --> jmp 010073BE (跳去VM下的正常代码)
论坛字符截断。。晕。。看附件吧 Discuz对OD数据段里的数据好像识别有问题,会造成中断,hkfans007兄弟保存成word根式上传一份吧,文章里数据段的无用数据可以删掉才能发布完整. 支持一下了,谢谢了 {:301_983:}TMD就这样轻轻被干了???膜拜。。。 佩服LZ的毅力啊,用了三个多月了。 恩,多看多学。 前排膜拜大牛 大牛牛、、、 果断顶了这么牛的都来 高层占位,膜拜各种大牛。