ACProtect 1.41 -- 同益起名大师 v3.36、v3.37、vp3.33(专业版)完美脱壳
by gzgzlxg
使用工具: OllyDBG、PE Explorer、PEditor、IDA
软 件: 同益起名大师 v3.36、v3.37、vp3.33
邮 箱: gzgzlxg@hotmail.com
原创于看雪技术论坛(www.pediy.com) 和 DFCG官方网站,并保持文章的完整性!
请不要发信到我的邮箱去,请我破解同益起名软件。
对文中的技术问题,可来信询问,其余一概不理。 一、 Dump
用OD载入 goodname.exe (这里以 v3.36 做样板分析,其他版本将地址顺序移动即可)
OD 设置:忽略全部异常,忽略硬件中断。
将 OD 改名为按如下方法建立的任意名字:
运算规则:将文件名最后12个字符由前往后按双字相加等于下列任何一个数值
0E3EDEFC2h
0CAF9D7CEh
* 0E8EDDAC5h
0DB02D9C3h
0BAD9D2D2h
例如 GEESPPPP.EXE = 53454547h + 50505050h + 4558452Eh = 0E8EDDAC5h
(文中省略许多解说,fly大侠和股林精怪都有详细介绍,我不便多说,有画蛇添足之嫌。“眼前有景道不得,飞怪说文在天上”)
在输入表区下内存写入断点。
操作: 鼠标点内存操作 M ,在 .idata 行按鼠标右键,选设置内存写入断点
Shift + F9
在如下位置中断:
00690848 8366 0C 00 and dword ptr ds:[esi+C],0
0069084C 03C2 add eax,edx
这段代码是壳用来建立一个变形的用户输入表,下面是 IDA中的详细分析,加有注解,我就不再解释了。省略了代码的前后都是加密解码部分。按代码注解中的位置Nop 某些地方。
(我在 DFCG 官方网站-【基础知识交流】中登载了全部壳代码(html格式)有兴趣研究的朋友可以上那里去下载,另外还有4篇关于壳的详细解释,但没有最后写完,因无人欣赏,所以也就不在续了)
.perplex:00690661 ; ============== S U B R O U T I N E ==================================
.perplex:00690661 ; int __cdecl CreateDistortionImportTable()
.perplex:00690661 CreateDistortionImportTable proc near
.perplex:00690661 pusha
//这里省略了解码过程
......
......
.perplex:0069080C loc_69080C:
.perplex:0069080C call GetRelativeBaseAddr27B000
.perplex:00690811 mov byte ptr ss:(CreateDistortionImportTable+401000h - Start)[ebp], 0C3h
.perplex:00690818 mov ss:(lpDistortionImportTable+401000h - Start)[ebp], 401000h
.perplex:00690822 add ss:(lpDistortionImportTable+401000h - Start)[ebp], ebp ; 401000+27B000=67C000
.perplex:00690828 add ss:(lpDistortionImportTable+401000h - Start)[ebp], 10h ; 67C000+10=67C010
.perplex:00690828 ; 将在原来启动时的代码处建立一个加过密的 API 调用表,
.perplex:00690828 ; 加密方法只是简单的异或:结构如下:
.perplex:00690828 ; push A
.perplex:00690828 ; xor dword ptr [esp],B
.perplex:00690828 ; ret
.perplex:00690828 ; 这里原来 API 调用地址= A xor B
.perplex:0069082F mov edx, ss:(BaseAddr_400000+401000h - Start)[ebp] ; 400000
.perplex:00690835 mov esi, ss:(OriginalImportTableOffset+401000h - Start)[ebp] ; 181000
.perplex:0069083B add esi, edx ; 581000 原输入表入口
.perplex:0069083D LoopCreateDistortionImportTable:
.perplex:0069083D mov eax, [esi+IMAGE_IMPORT_DESCRIPTOR.Name]
.perplex:00690840 or eax, eax
.perplex:00690842 jz loc_690A6D
//中断在这里
.perplex:00690848 这里清除 IMAGE_IMPORT_DESCRIPTOR 结构中的 Name *** Nop 这句
.perplex:00690848 and [esi+IMAGE_IMPORT_DESCRIPTOR.Name], 0
.perplex:0069084C add eax, edx ; 指向 IMAGE_IMPORT_DESCRIPTOR.Name 指向的函数名指针
.perplex:0069084E mov ebx, eax
.perplex:00690850 push esi
.perplex:00690851 push edi
.perplex:00690852 push eax
.perplex:00690853 mov esi, ebx
.perplex:00690855 mov edi, ebx
.perplex:00690857 loc_690857:
.perplex:00690857 lodsb
.perplex:00690858 rol al, 3 ; 这里经过简单的移位,恢复原函数名;
.perplex:0069085B stosb
.perplex:0069085C cmp byte ptr [edi], 0
.perplex:0069085F jnz short loc_690857
.perplex:00690861 pop eax
.perplex:00690862 pop edi
.perplex:00690863 pop esi
.perplex:00690864 push eax ; lpModuleName
.perplex:00690865 call ss:(GetModuleHandleA+401000h - Start)[ebp]
.perplex:0069086B or eax, eax
.perplex:0069086D jnz short loc_6908B2
.perplex:0069086F db 4 dup(90h)
.perplex:00690873 push ebx ; lpLibFileName
.perplex:00690874 call ss:(LoadLibraryA+401000h - Start)[ebp]
.perplex:0069087A or eax, eax
.perplex:0069087C jnz short loc_6908B2
.perplex:0069087E db 4 dup(90h)
.perplex:00690882 loc_690882:
.perplex:00690882 mov edx, ss:(BaseAddr_400000+401000h - Start)[ebp]
.perplex:00690888 add ss:(lpMSGCaption_0+401000h - Start)[ebp], edx
.perplex:0069088E add ss:(lpMSGText_0+401000h - Start)[ebp], edx
.perplex:00690894 push 0 ; uType
.perplex:00690896 push ss:(lpMSGCaption_0+401000h - Start)[ebp] ; lpCaption
.perplex:0069089C push ss:(lpMSGText_0+401000h - Start)[ebp] ; lpText
.perplex:006908A2 push 0 ; hWnd
.perplex:006908A4 call ss:(MessageBoxA+401000h - Start)[ebp]
.perplex:006908AA push 0 ; uExitCode
.perplex:006908AC call ss:(ExitProcess+401000h - Start)[ebp]
.perplex:006908B2 loc_6908B2:
.perplex:006908B2 pusha
.perplex:006908B3 sub eax, eax
.perplex:006908B5 loc_6908B5:
.perplex:006908B5 mov [ebx], al ;*** 这句 Nop 掉,在取得了模块的句柄后,清除模块名(DLL文件名)
.perplex:006908B7 inc ebx
.perplex:006908B8 cmp [ebx], al
.perplex:006908BA jnz short loc_6908B5
.perplex:006908BC popa
.perplex:006908BD mov ss:(hHandle_CreateDistortionImportTable+401000h - Start)[ebp], eax
.perplex:006908C3 mov ss:(LoopStep+401000h - Start)[ebp], 0
.perplex:006908CD loc_6908CD:
.perplex:006908CD mov edx, ss:(BaseAddr_400000+401000h - Start)[ebp]
.perplex:006908D3 如果OriginalFirstThunk不为零,则以OriginalFirstThunk为计算标准,
.perplex:006908D3 否则以FirstThunk为计算实际地址的根据
.perplex:006908D3 mov eax, [esi+IMAGE_IMPORT_DESCRIPTOR.OriginalFirstThunk]
.perplex:006908D5 or eax, eax
.perplex:006908D7 jnz short loc_6908E0
.perplex:006908D9 db 4 dup(90h)
.perplex:006908DD mov eax, [esi+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
.perplex:006908E0
.perplex:006908E0 loc_6908E0:
.perplex:006908E0 add eax, edx ; 计算FirstThunk的实际地址
.perplex:006908E2 add eax, ss:(LoopStep+401000h - Start)[ebp]
.perplex:006908E8 mov ebx, [eax] ; 获取FirstThunk指向的子表中的一个,由NextStep循环获取
.perplex:006908EA mov edi, [esi+IMAGE_IMPORT_DESCRIPTOR.FirstThunk]
.perplex:006908ED add edi, edx
.perplex:006908EF add edi, ss:(LoopStep+401000h - Start)[ebp]
.perplex:006908F5 test ebx, ebx ; 零表示这个模块的所以函数都处理完了。
.perplex:006908F7 jz loc_690A5F
.perplex:006908FD test ebx, 80000000h ; 如果高位置位,表示低31位是DLL的引入编号,如果高位没有置位,
.perplex:006908FD ; 则该数值是指向一个 IMAGE_IMPORT_BY_NAME struc。
.perplex:00690903 jnz short loc_690922
.perplex:00690905 db 4 dup(90h)
.perplex:00690909 处理 IMAGE_IMPORT_BY_NAME struc
.perplex:00690909 add ebx, edx ; 计算 IMAGE_IMPORT_BY_NAME 表的实际地址
.perplex:0069090B add ebx, 2 ; 在偏移量为2的地方是不定长的函数名。
.perplex:0069090E push esi
.perplex:0069090F push edi
.perplex:00690910 push eax
.perplex:00690911 mov esi, ebx
.perplex:00690913 mov edi, ebx
.perplex:00690915 loc_690915:
.perplex:00690915 lodsb
.perplex:00690916 rol al, 3 ; 将函数名解码
.perplex:00690919 stosb
.perplex:0069091A cmp byte ptr [edi], 0
.perplex:0069091D jnz short loc_690915
.perplex:0069091F pop eax
.perplex:00690920 pop edi
.perplex:00690921 pop esi
.perplex:00690922 loc_690922:
.perplex:00690922 cmp ebx, ss:(BaseAddr_400000+401000h - Start)[ebp] ; IMAGE_IMPORT_BY_NAME 如果为空,表示结束。
.perplex:00690928 jl short loc_69093B
.perplex:0069092A db 4 dup(90h)
.perplex:0069092E ThunkFlag为零时表示引入的函数是一个编号,否则引入的函数
.perplex:0069092E 是函数名,这里没有使用这个标志,这段程序可能是从什么地方
.perplex:0069092E 拷贝来加以修改的,所以保留了这个标志,在这里,这个标志
.perplex:0069092E 永远是零。在实际使用中,程序不可能大到在高位有真实的地址
.perplex:0069092E 所以不管是函数名还是引入编号,都可以清除高位标志
.perplex:0069092E and ebx,0FFFFFFFh
.perplex:0069092E cmp ss:(TrunkFlag+401000h - Start)[ebp], 0
.perplex:00690935 jnz short loc_690941
.perplex:00690937 db 4 dup(90h)
.perplex:0069093B loc_69093B:
.perplex:0069093B and ebx, 0FFFFFFFh
.perplex:00690941 loc_690941:
.perplex:00690941 push ebx ; lpProcName
.perplex:00690942 push ss:(hHandle_CreateDistortionImportTable+401000h - Start)[ebp] ; hModule
.perplex:00690948 call ss:(GetProcAddress+401000h - Start)[ebp]
.perplex:0069094E cmp ebx, ss:(BaseAddr_400000+401000h - Start)[ebp]
.perplex:00690954 jl short loc_690965
.perplex:00690956 db 4 dup(90h)
.perplex:0069095A pusha
.perplex:0069095B sub eax, eax
.perplex:0069095D loc_69095D:
.perplex:0069095D mov [ebx], al ; *** 这句 Nop 掉,用完了清除函数名,兔死狗烹,鸟尽弓藏。
.perplex:0069095F inc ebx
.perplex:00690960 cmp [ebx], al
.perplex:00690962 jnz short loc_69095D
.perplex:00690964 popa
.perplex:00690965 loc_690965:
.perplex:00690965 or eax, eax
.perplex:00690967 jz loc_690882
.perplex:0069096D cmp eax, ss:(MessageBoxA+401000h - Start)[ebp]
.perplex:00690973 jz short loc_690995 *** 这句 Nop 掉,不让壳修改 MessageBoxA 的地址
.perplex:00690975 db 4 dup(90h)
.perplex:00690979 cmp eax, ss:(RegisterHotKey+401000h - Start)[ebp]
.perplex:0069097F jz short loc_69098A *** 这句 Nop 掉,不让壳修改 RegisterHotKey 的地址
.perplex:00690981 db 4 dup(90h)
.perplex:00690985 jmp short loc_69099B
.perplex:00690987 db 3 dup(90h)
.perplex:0069098A 这里将用户程序中所有对这两个函数的调用都指向经过特殊处理
.perplex:0069098A 的一个过程。
.perplex:0069098A RegisterHotKey和MessageBoxA这两个函数的处理在一个过程内。
.perplex:0069098A 对于加密来讲,用户可以随便调用两个函数的任何一个,起的作用
.perplex:0069098A 是完全相同的。
.perplex:0069098A 这两个函数是ACPropect的主要功能函数,当入口参数(相当于HWND)
.perplex:0069098A 等于0FFFFFFFFh时,该函数有六个子功能,分别实现四大功能。详细
.perplex:0069098A 见对该模块的分析。
.perplex:0069098A loc_69098A:
.perplex:0069098A lea eax, (RegisterHotKey_Fog+401000h - Start)[ebp]
.perplex:00690990 jmp short loc_69099B
.perplex:00690992 db 3 dup(90h)
.perplex:00690995 loc_690995:
.perplex:00690995 lea eax, (MessageBoxA_Fog+401000h - Start)[ebp]
.perplex:0069099B loc_69099B:
.perplex:0069099B push esi
.perplex:0069099C push ss:(hHandle_CreateDistortionImportTable+401000h - Start)[ebp]
.perplex:006909A2 pop esi
.perplex:006909A3 cmp ss:(hKernel32_DLL+401000h - Start)[ebp], esi
.perplex:006909A9 jz short loc_6909C0
.perplex:006909AB db 4 dup(90h)
.perplex:006909AF cmp ss:(hUser32_DLL+401000h - Start)[ebp], esi
.perplex:006909B5 jz short loc_6909C0
.perplex:006909B7 db 4 dup(90h)
.perplex:006909BB jmp short loc_690A20
.perplex:006909BD db 3 dup(90h)
.perplex:006909C0 loc_6909C0:
.perplex:006909C0 cmp ss:(Flag_CreateDistortionImportTable+401000h - Start)[ebp], 0
.perplex:006909C7 jz short loc_690A20
.perplex:006909C9 db 4 dup(90h)
.perplex:006909CD jmp short loc_6909D6
.perplex:006909CF db 3 dup(90h)
.perplex:006909D2 这个标志应该是用户在选择加密方式的时候设置的,从程序中可以看出
.perplex:006909D2 这个标志为零时,将不建立 Push Xor结构的输入表
.perplex:006909D2 Flag_CreateDistortionImportTable db 1 ; *** 这里改成零
.perplex:006909D3 db 0
.perplex:006909D4 db 0
.perplex:006909D5 db 0
.perplex:006909D6 loc_6909D6:
.perplex:006909D6 mov esi, ss:(lpDistortionImportTable+401000h - Start)[ebp] ; 67C010
.perplex:006909DC add esi, 0Dh ; 每个 push xxxxxxxxh
.perplex:006909DC ; xor dword ptr ss:[esp], xxxxxxxxh
.perplex:006909DC ; ret
.perplex:006909DC ; 长度为0Dh
.perplex:006909DF sub esi, 401BEAh ; 401000h+0BEAh
.perplex:006909DF ; 0BEAh 为整个 NewApiCryptCallAddrTable 的长度
.perplex:006909E5 sub esi, ebp
.perplex:006909E7 cmp esi, 0
.perplex:006909EA jg short loc_690A20 ; 如果是其他函数,直接写入地址,直接调用原函数。
.perplex:006909EC db 4 dup(90h)
.perplex:006909F0 mov esi, ss:(lpDistortionImportTable+401000h - Start)[ebp]
.perplex:006909F6 push ebx
.perplex:006909F7 push eax
.perplex:006909F8 call GetCPUTimeStemp ; 由 CPU 的时钟周期经过计算得到一个数值作为加密码
.perplex:006909FD mov ebx, eax
.perplex:006909FF pop eax
.perplex:00690A00 xor eax, ebx
.perplex:00690A02 mov byte ptr [esi], 68h ; push xxxxxxxxh
.perplex:00690A05 mov [esi+1], eax
.perplex:00690A08 mov dword ptr [esi+5], 243481h ; xor dword ptr[esp], xxxxxxxxh
.perplex:00690A0F mov [esi+8], ebx
.perplex:00690A12 mov byte ptr [esi+0Ch], 0C3h ; ret
.perplex:00690A16 pop ebx
.perplex:00690A17 mov eax, esi
.perplex:00690A19 add ss:(lpDistortionImportTable+401000h - Start)[ebp], 0Dh ; 指向下一个函数表地址
.perplex:00690A20 loc_690A20:
.perplex:00690A20 pop esi ; 581000 ;原来的输入表入口
.perplex:00690A21 pusha
.perplex:00690A22 mov edx, eax ; 调用函数的地址,指向每个 push xxxxxxxxh
.perplex:00690A24 sub edi, ss:(BaseAddr_400000+401000h - Start)[ebp] ; 计算相对偏移
.perplex:00690A2A mov eax, edi
.perplex:00690A2C mov ecx, 101h ; 最大表长度
.perplex:00690A31 lea edi, (ImageOfOriginalImportTable+401000h - Start)[ebp]
.perplex:00690A37 repne scasd ; 查找函数在表中的位置
.perplex:00690A39 or ecx, ecx
.perplex:00690A3B jz short loc_690A50
.perplex:00690A3D db 4 dup(90h)
.perplex:00690A41 sub ecx, 101h
.perplex:00690A47 not ecx
.perplex:00690A49 如果 Flag_CreateDistortionImportTable 为零,CryptImportTable 将不指向
.perplex:00690A49 lpDistortionImportTable 这张异或结构的加密输入表,而直接填入调用 API
.perplex:00690A49 函数的地址,所以如果我们在去壳进行Dump时将这个参数设置为零,为今后修
.perplex:00690A49 复那1000个指向 6886F3 的调用变得较为容易。(参见后文这张表的结构)
.perplex:00690A49 如果 Flag_CreateDistortionImportTable 为 1,将来在修复这个调用时将经过
.perplex:00690A49 异或来得到 API 调用地址,使修复程序变得较为复杂。
.perplex:00690A49 mov ss:(CryptImportTable+401000h - Start)[ebp+ecx*4], edx
.perplex:00690A50 loc_690A50:
.perplex:00690A50 popa
.perplex:00690A51 mov [edi], eax ; *** 这里 Nop掉,修改原程序 IMAGE_IMPORT_BY_NAME
.perplex:00690A51 ; 表的地址,指向新建立的
.perplex:00690A51 ; 带保护的表。
.perplex:00690A53 add ss:(LoopStep+401000h - Start)[ebp], 4 ; 指向下一个函数
.perplex:00690A5A jmp loc_6908CD
.perplex:00690A5F loc_690A5F:
.perplex:00690A5F add esi, 14h ; SizeOf(IMAGE_IMPORT_DESCRIPTOR Struct)
.perplex:00690A5F ; 指向下一个表
.perplex:00690A62 mov edx, ss:(BaseAddr_400000+401000h - Start)[ebp]
.perplex:00690A68 jmp LoopCreateDistortionImportTable
.perplex:00690A6D loc_690A6D:
.perplex:00690A6D lea edi, (ImageOfOriginalImportTable+401000h - Start)[ebp]
.perplex:00690A73 xor eax, eax
.perplex:00690A75 mov ecx, 100h
.perplex:00690A7A rep stosd ;*** 这里 Nop 掉 清除这张从原输入表拷贝过来的函数名指针表。
//这里省略了重新加密的过程
......
......
.perplex:00690AB8 popa
.perplex:00690AB9 call CheckSumGoodname
.perplex:00690ABE retn
.perplex:00690ABE CreateDistortionImportTable endp
同上操作,在代码段下内存访问断点,按 Shift + F9。
中断在这里,这是Delphi 的初始化过程。
004067F4 53 push ebx
004067F5 8BD8 mov ebx,eax
004067F7 33C0 xor eax,eax
004067F9 A3 9CB05700 mov dword ptr ds:[57B09C],eax
004067FE 6A 00 push 0
00406800 E8 25542800 call GoodName.0068BC2A
00406805 A3 68065800 mov dword ptr ds:[580668],eax
0040680A A1 68065800 mov eax,dword ptr ds:[580668]
0040680F A3 A8B05700 mov dword ptr ds:[57B0A8],eax
00406814 33C0 xor eax,eax
00406816 A3 ACB05700 mov dword ptr ds:[57B0AC],eax
0040681B 33C0 xor eax,eax
0040681D A3 B0B05700 mov dword ptr ds:[57B0B0],eax
00406822 E8 C1FFFFFF call GoodName.004067E8
00406827 BA A4B05700 mov edx,GoodName.0057B0A4
0040682C 8BC3 mov eax,ebx
0040682E E8 9DD7FFFF call GoodName.00403FD0
00406833 5B pop ebx
00406834 C3 retn
注意寄存器值:
EAX 0057A00C GoodName.0057A00C
ECX 00010101
EDX FFFFFFFF
EBX 7FFDF000
ESP 0012FFA4 ASCII "jvi"
EBP 0012FFC0
ESI 00000000
EDI 00000000
EIP 004067F4 GoodName.004067F4
其中 EAX 的值指向初始化表头,是一个数值,说明随后有多少相关初始化的过程可以调用。
下面是堆栈列表:
0012FFA4 0069766A 返回到 GoodName.0069766A 来自 GoodName.004067F4
0012FFA8 00000000
0012FFAC 7FFDF000
0012FFB0 FFFFFFFF
0012FFB4 00000000
0012FFB8 00000000
0012FFBC 00000000
0012FFC0 0012FFF0
0012FFC4 77E8893D 返回到 KERNEL32.77E8893D
注意 0012FFA8 和 0012FFAC 中的值和堆栈指针,在恢复原程序被破坏的启动代码时要用到。
继续:将光标移到 00406834 ret
按 Ctrl + * ,也就是跳过初始化过程,按 F7回到壳。(如果有兴趣研究入口前的代码,可以上下浏览)
按 Shift + F9 中断在这里,也就是终点。
0057A4BD E8 72C5E8FF call GoodName.00406A34 //建立互斥对象。
0057A4C2 8BD8 mov ebx,eax
0057A4C4 85DB test ebx,ebx
0057A4C6 74 17 je short GoodName.0057A4DF
......
......
寄存器:
EAX 0057A00C GoodName.0057A00C //上次调用的残余,没有用
ECX 00010101
EDX FFFFFFFF
EBX 7FFDF000
ESP 0012FF9C
EBP 0012FFC0
ESI 00580C0C GoodName.00580C0C // TApplication_Instance 这是结果,实际还有一个指针引用。
EDI 00000000
EIP 0057A4BD GoodName.0057A4BD
堆栈列表:
0012FF9C 00000000 // push 0
0012FFA0 FFFFFFFF // push -1
0012FFA4 0057A530 ASCII "GoodName" // push 57A530
0012FFA8 00000000
0012FFAC 7FFDF000
0012FFB0 FFFFFFFF
0012FFB4 00000000
0012FFB8 00000000
0012FFBC 00000000
0012FFC0 0012FFF0
这是伪入口地址(OEP obfuscation),但离真正的入口已经非常近了。
Dump 文件:
我选用PEditor 选择 Full Dump。 起名UnGDN.exe
修改 Import Table 和 Tls 表地址,我选用 PE Explorer。
PE Explorer 装载 UnGDN.exe (前面Dump的文件)。
点 Section Headers 记录如下数据:
Name Virtual Size Virtual Address
.idata 0000294E 00581000
.rata 00000018 00585000
点 Data Directories
修改用上面的值 .idata 和 Tls 数据。
点 Section Headers ,点计算。然后存盘。
二、 内嵌式和其他保护手段的工作原理:
以我的观点 ACProtect 的最大特点就是内嵌式代码。除了内嵌式代码还有许多其他的保护手段,简述如下。
1. 内嵌式代码(Embedded Protector ):
预留数量是100个,每个大约13500字节(长短是不定的),在这13500字节中,用户程序只占非常少的一部分,并将用户程序割为3到4块,这100个置入用户内部的程序,每个都有自己完整的一套12个保护程序,从 ADP1到ADPC,还有若干个辅助程序,这个ADP*名字不是我起的,是原程序自己带来的,这12个保护程序分别对各种情况进行了防护,在同益中只启用了9个,这些标记是突破防护的最佳助手,当然留下这些标志也是保护程序为自己定位而设置的,这个确实是一大弱点,希望ACProtect在今后的版本中能加以改进。在原程序中,所有的调用 MessageBoxA 都被指向68B224 (不是直接指向的,中间还要绕几个弯,还有另一个调用RegisterHotKey 指向同一个过程,入口在前一点的 68B20A。用户程序调用MessageBoxA也指向这里,根据入口参数来确定,下面是调用格式:
MessageBox (HWND(-1),szRegistrationName,NULL,0);
当 HWND 不等于 -1 时是用户程序调用 MessageBoxA
szRegistrationName 参数一共有 0-6 个,内嵌过程使用 5 --拷贝到内存,4--从内存拷贝回来。功能 2、3 为用户提供RSA 1024 密钥保护,0号功能是获取注册名。1号功能提供软件试运行期限控制。6号功能是获取硬件 ID。同益中只使用了 5、4。
程序在启动时将为每一个内嵌模块根据下面EmbeddedCodeSize表分配一段相同大小的内存,然后将保护程序拷贝到内存中,每次执行时,每个模块首先将自己重新拷贝到内存一次,这是通过调用伪装的 Call MessageBoxA 第5号功能来实现的,
拷贝完成后原程序将自行解密,启动12个保护程序,同时运行用户的那3到4块程序,运行结束后调用MessageBoxA 的第4号功能,从内存中将自己拷贝回来,覆盖那些被解码的部分。在同益起名大师中共有26个模块进行了内嵌处理。
修复这些内嵌代码非常困难,壳中提供了两张表,一张是内嵌代码的在程序中的偏移位置表,一张是内嵌代码的长度,这是破解内嵌代码的重要线索,也是ACProtect的重大缺陷,其实在为内嵌代码分配内存后,应该随手毁灭这两张表(当然就算毁灭了这两张表,还是能修复的,只不过更困难一些)。下面是这两张表:
.perplex:0067CF25 EmbeddedCodeOffset dd 0F52E1h, 17621Fh, 0E00F1h, 10A32Fh, 10D813h
.perplex:0067CF25 dd 110CF7h, 0E33EDh, 0F075Ch, 0E67B3h, 0E9C0Eh
.perplex:0067CF25 dd 1532FFh, 0F896Eh, 161E13h, 144AE6h, 0ED044h
.perplex:0067CF25 dd 13FEA1h, 14864Ah, 15D311h, 103350h, 165B6Bh
.perplex:0067CF25 dd 1568C6h, 0FBC88h, 14BF1Dh, 1069A4h, 1697F6h
.perplex:0067CF25 dd 170123h, 0, 0, 0, 0
.perplex:0067D0B5 EmbeddedCodeSize dd 3272h, 327Ch, 32A0h, 32A6h, 32A6h
.perplex:0067D0B5 dd 32A6h, 32A8h, 32A9h, 32ABh, 32B2h
.perplex:0067D0B5 dd 32B9h, 32C6h, 332Ch, 3360h, 339Bh
.perplex:0067D0B5 dd 3473h, 348Ch, 34D5h, 3555h, 35D1h
.perplex:0067D0B5 dd 35D6h, 3609h, 3623h, 36CFh, 373Ch
.perplex:0067D0B5 dd 3889h, 0, 0, 0, 0
2. 代码替换(Code Replace):
ACProtect 在进行加密用户程序时,在用户程序中2999处随机的移走了长度为5个字节的指令,变为 Call 67D416,然后在移走的几条指令的前后插入几条无用的指令,总长度大约在10个字节以内,经过简单的异或(Key是根据程序入口地址计算得来的,如果修改了入口地址,这2999个被移走的代码将不能被复原,可惜的是ACProtect 这个计算过程只在程序启动时做了一次,随后的复原都是直接使用这个值来计算的,这使得脱壳后仍然背着壳运行的那些方法得以成功,希望ACProtect改进这一点,其实大约只要增加不到十条指令即可完成,即每次都重新通过入口地址计算而得到Key,这样那些修改了入口的程序如果不恢复这2999处被移走的指令,总长度大约是 3000 × 5 = 15000 个字节的代码,程序根本就不能运行,而恢复这 2999 处被移走的代码通过手工修改是不现实的。ACProtect 建立了三张表,第一张是用户原代码处的偏移量表,长度是 3000个双字,最后一个是零,表示结束,所以实际被移走的代码是2999处。第二张是被修改并经过简单异或加密的代码表,每个长度是10个字节,总长度是 3000 × 10 = 30000个字节。第三张是在用户进入工作状态后对这些被移走的代码使用的频率表,如果用户频繁的调用这些代码,达到32次后,为了加快程序的运行效率,ACProtect 将这段代码解码后复制到启动时申请分配的 30000 个字节的内存中,并修改用户程序,将 Call 67D416 指向新的地址,你在程序启动后,简单的将这个 3000 字节长的使用频率表都填为 31(1Fh),这样所有的代码只要运行一次将被解码,搬到内存中,你可以在OllyDBG 的补丁窗口中看到这些代码。前两张表是修复 Code Replace 的关键,下面列出这两张表的部分(太长):
.perplex:0067E269 CodeReplaceOffset dd 180Ah, 153Dh, 1547h, 155Ah, 1741h
.perplex:0067E269 dd 188Fh, 15ADh, 15B2h, 15DCh, 15E1h
.perplex:0067E269 dd 15FAh, 1603h, 1F28h, 1F6Ah, 1F96h
.perplex:0067E269 dd 24FFh, 1D0Ah, 1E91h, 1E96h, 1EB6h
.perplex:0067E269 dd 1E16h, 1E61h, 18FDh, 194Eh, 2037h
.perplex:0067E269 dd 19A6h, 1B99h, 212Ch, 2140h, 21B7h
.perplex:0067E269 dd 25C3h, 2676h, 22C7h, 2359h, 4940h
......
......
.perplex:00681149 CodeTable db 0E4h, 23h, 6Ch, 99h,0EBh,0CCh, 23h,0E4h, 1Ch, 24h
.perplex:00681149 db 6Eh,0F1h,0B3h,0BBh, 6Ch,0B7h,0E3h, 24h,0FBh,0C3h
.perplex:00681149 db 6Ch,0E1h,0B3h, 6Ch,0B4h,0EFh,0BBh, 24h, 1Ch, 24h
.perplex:00681149 db 6Ch,0A4h,0EFh,0D4h, 1Dh, 6Eh,0E1h,0D4h, 1Dh, 24h
.perplex:00681149 db 6Ch, 21h,0A6h,0AEh,0E4h,0A4h,0EBh, 24h,0FAh,0C3h
.perplex:00681149 db 6Ch, 99h,0EBh,0E4h, 2Dh,0E4h, 1Ch,0CCh, 2Dh, 24h
.perplex:00681149 db 6Ch, 29h,0E4h, 0Fh,0CCh, 0Fh,0E4h,0ADh,0E3h, 24h
......
......
下面是计算这个 Key 的代码
.perplex:00691617 GetDecodeKeyStart:
.perplex:00691617 call GetRelativeBaseAddr27B000
.perplex:0069161C 计算偏移,读取PE表程序入口地址
.perplex:0069161C mov eax, ss:(BaseAddr_400000+401000h - Start)[ebp]
.perplex:00691622 mov esi, ds:(DOS_HEADER.e_lfanew - DOS_HEADER.e_magic)[eax] ; 100h
.perplex:00691625 add esi, ss:(BaseAddr_400000+401000h - Start)[ebp] ; 取得 PE 头入口地址
.perplex:0069162B add esi, IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint
.perplex:0069162E lodsd ; 读取入口地址
.perplex:0069162E ; EAX=00 27 C0 00
.perplex:0069162F mov bl, al ; al = 00
.perplex:00691631 add bl, ah ; ah = C0
.perplex:00691633 shr eax, 10h ; ax = 00 27
.perplex:00691636 add bl, al ; al = 27
.perplex:00691638 add bl, ah ; ah = 0
.perplex:0069163A mov ss:(DecodeKey+401000h - Start)[ebp], bl ; bl = E7
至于这些 2+3 或 3+2 替换代码的具体分析,请参考本论坛股林精怪的精彩论述。
3. 变形 API 表(API call into Protection Code)
ACProtect 随机修改了用户程序中1000处 API 调用,将这些调用指向6886F3, ACProtect在启动时将覆盖自己的代码区域,从入口地址偏移10个字节开始,即从(67C010 到 67CBE5) 建立一张经过异或处理的 API 调用表,这些表的结果如下:(由前面CreateDistortionImportTable 过程建立)
.perplex:0067C010 RtlDeleteCriticalSection proc near
.perplex:0067C010 000 68 55 56 F8 77 push 77F85655h
.perplex:0067C015 000 81 34 24 00 00+ xor dword ptr [esp], 0
.perplex:0067C01C 000 C3 retn
.perplex:0067C01C RtlDeleteCriticalSection endp
为了便于理解当壳建立这张表时,修改了原代码,让 xor 的参数为零。这样,push 的参数就是 API的真实地址了。真正程序运行的结果和这个不同。
所有的这 1000处API调用都指向 6886F3过程。下面是6886F3过程的代码,阅读这段代码将有助于理解我后面写的修复程序。
.perplex:0068889E loc_68889E:
.perplex:0068889E call GetRelativeBaseAddr27B000
.perplex:006888A3 mov eax, [esp+20h+ReturnAddr] ; 取调用的返回地址
.perplex:006888A7 sub eax, ss:(BaseAddr_400000+401000h - start)[ebp]
.perplex:006888AD mov ecx, 1001
.perplex:006888B2 lea edi, (CallOffset+401000h - start)[ebp]
.perplex:006888B8 repne scasd ; 在返回地址表中查寻,根据返回地址,确定用户调用
.perplex:006888B8 ; 哪个 API 函数。这张表是加密时建立的。
.perplex:006888BA or ecx, ecx
.perplex:006888BC jnz short loc_6888C2
.perplex:006888BE db 4 dup(90h)
.perplex:006888C2 loc_6888C2:
.perplex:006888C2 sub ecx, 1001
.perplex:006888C8 not ecx ; 根据查找返回地址表中的位置,到第二张API位置表中
.perplex:006888C8 ; 去确定调用哪个 API 函数。
.perplex:006888CA movzx ebx, ss:(APIPosition+401000h - start)[ebp+ecx]
.perplex:006888D2 lea eax, (CryptImportTable+401000h - start)[ebp+ebx*4]
.perplex:006888D9 lea edi, (TempImportTable+401000h - start)[ebp]
.perplex:006888DF mov word ptr [edi], 25FFh ; 建立临时输入表的跳转表,0FF25h ,就是
.perplex:006888DF ; JMP FAR 的指令代码
.perplex:006888E4 mov [edi+2], eax ; 需要跳转的目标地址,即某个 API 函数的入口地址
.perplex:006888E7 mov byte ptr [edi+6], 0C3h ; 调用完成后返回用户地址的代码
.perplex:006888E7 ; JMP FAR (API 函数入口地址)
.perplex:006888E7 ; ret
.perplex:006888EB push [esp+20h+ReturnAddr]
.perplex:006888EF lea edi, (TempImportTable+401000h - start)[ebp]
.perplex:006888F5 xor ecx, ecx
.perplex:006888F7 MoveStack:
.perplex:006888F7 cmp ecx, 8 ; 这里对堆栈从新排位,这样当这个过程结束,
.perplex:006888F7 ; 执行ret指令时将不返回调用程序,而转到
.perplex:006888F7 ; 上面建立的,临时代码表,调用 API 函数
.perplex:006888FA jz short loc_68890A
.perplex:006888FC db 4 dup(90h)
.perplex:00688900 mov eax, [esp+ecx*4+4]
.perplex:00688904 mov [esp+ecx*4], eax
.perplex:00688907 inc ecx
.perplex:00688908 jmp short MoveStack
.perplex:0068890A loc_68890A:
.perplex:0068890A mov [esp+ecx*4], edi
这段代码中使用了三张表,下面列出这些表的部分:
.perplex:0068894B CallOffset dd 142Fh, 149Ch, 165Ch, 1683h, 16AFh, 16D4h, 16FAh, 176Dh
.perplex:0068894B dd 182Eh, 18AFh, 1B2Bh, 1B3Eh, 1B68h, 1BC5h, 1C06h, 1C18h
.perplex:0068894B dd 1C37h, 1C76h, 1C9Fh, 1CA9h, 224Ch, 2377h, 23E5h, 251Dh
.perplex:0068894B dd 2754h, 27BCh, 2A1Ch, 2A48h, 2A52h, 2A6Fh, 2A79h, 2AA2h
......
......
.perplex:006898EB APIPosition db 13h, 7, 5, 4, 5, 5, 4, 4, 5, 4, 3
.perplex:006898EB db 2, 7, 1, 2, 6, 4, 6, 1, 0, 2, 1
.perplex:006898EB db 2, 1, 2, 1, 25h, 25h, 25h, 25h, 25h, 25h, 25h
.perplex:006898EB db 25h, 25h, 16h, 18h, 22h, 22h, 27h, 26h, 28h, 1Eh, 1Eh
.perplex:006898EB db 1Eh, 1Eh, 1Eh, 1Eh, 21h, 21h, 0Dh, 0Eh, 2Bh, 29h, 29h
.perplex:00689CD3 CryptImportTable dd offset sub_67C010
.perplex:00689CD7 dd offset sub_67C01D
.perplex:00689CDB dd offset sub_67C02A
.perplex:00689CDF dd offset sub_67C037
.perplex:00689CE3 dd offset sub_67C044
.perplex:00689CE7 dd offset sub_67C051
.perplex:00689CEB dd offset sub_67C05E
.perplex:00689CEF dd offset sub_67C06B
.perplex:00689CF3 dd offset sub_67C078
第一张表 CallOffset 是用户程序 Call 6886F3 的具体偏移量(位置),第二张表是指明用户需要调用那个API函数,即在第三张表中的偏移量。
第三张表指向那张变了形的 push xor 结构的 API 表。
4. 直接调用壳自己的 api 表。这张表叫 acp_api ,这个名字将来在恢复时要用到。从68BBC9 到 68BC8C。结构如下:
.perplex:0068BC00 Process32first proc near
.perplex:0068BC00 nop
.perplex:0068BC01 jmp ds:Process32first1
.perplex:0068BC01 Process32first endp
.perplex:0068BC07 Process32next proc near
.perplex:0068BC07 nop
.perplex:0068BC08 jmp ds:Process32next1
.perplex:0068BC08 Process32next endp
5. 对部分用户某些重要代码段进行加密:运行前解码,运行结束从新加密,这些过程没有保护措施,只是简单的加密解码。
三、 修复
根据上面这些论述,写了如下的破解程序,分脚本和汇编两类,同时也将Dump部分包括了。
1. 用OD载入 GoodName.exe 忽略全部异常,运行 OllyScript 脚本。
// FindOPEAndDumpFile.OSC By gzgzlxg
// 这脚本用来解码同益起名 V3.36&V3.37&Vp3.33,在OD中关闭所有异常、硬件中断。
// 运行脚本后用 PEditor Dump。
var Addr
//var RunMark
var idata
var VirtualSize
var VirtualAddress
var x
var x1
var x2
var x3
var AddrEP
var DataAddr
//读PE头文件
mov x1, 400000
add x1, 3C
mov x, [x1]
add x, 400000
add x, 28
mov AddrEP, [x]
add AddrEP, 400000
find 400000, #2E6964617461#
mov idata, $RESULT
add idata, 8
mov VirtualSize, [idata]
add idata, 4
mov VirtualAddress, [idata]
add VirtualAddress, 400000
find 400000, #44415441#
mov x, $RESULT
add x, 0C
mov DataAddr, [x]
add DataAddr, 400000
log DataAddr
//可以不要,处理壳自用API表。
//=======================================
dbh
gpa "GetModuleHandleA", "KERNEL32.dll"
bprm $RESULT, 4
esto
bpmc $RESULT
rtu
find eip, #F3AA#
cmp $RESULT, 0
je M
repl $RESULT, #F3AA#, #9090#, 2
//=======================================
//建立用户的变形API表,这里有一个标志需要注意。
//=======================================
bpwm VirtualAddress, VirtualSize
esto
bpmc
find eip, #83660C00#
repl $RESULT, #83660C00#, #90909090#, 4
mov Addr, $RESULT
add Addr, 4
findop Addr, #8803#
repl $RESULT, #8803#, #9090#, 2
mov Addr, $RESULT
add Addr, 4
findop Addr, #8803#
repl $RESULT, #8803#, #9090#, 2
mov Addr, $RESULT
find Addr, #74209090#
repl $RESULT, #7420#, #9090#, 2
mov Addr, $RESULT
add Addr, 4
find Addr, #74099090#
repl $RESULT, #7409#, #9090#, 2
mov Addr, $RESULT
add Addr, 40
//这里是一个秘密标志,为零时将生成正常的 API 表。
//设置这个标志为零,将减少修复输入表的工作(不需要修复)。
find Addr, #90909001#
mov Addr, $RESULT
add Addr, 3
mov [Addr], #00# findop Addr, #8907#
repl $RESULT, #8907#, #9090#, 2
findop Addr, #F3AB#
repl $RESULT, #F3AB#, #9090#, 2
mov Addr, $RESULT
add Addr, 2
go Addr find 400000, #434F44450000#
mov idata, $RESULT
add idata, 8
mov VirtualSize, [idata]
log VirtualSize
//查找 Stolen 代码,这里是入口后初始化调用
bprm 401000, VirtualSize
esto
bpmc 401000
mov x, eip
mov x1, AddrEP
find x1, #52616E64696D697A65#
mov x1, $RESULT
log x1
add x1, 0E
//初始化 Sysinit_InitExe
mov [x1], x //call 4067F4
add x1, 4
//初始化表入口地址,此参数对应有多少初始化过程,
//随后是初始化过程的指针表。注意寄存器的参数,为
//修复Stolen代码做准备。
mov [x1], eax //mov eax, 57A00C
add x1, 4
mov [x1], ebp // mov ebp, esp
add x1, 4
mov x2, esp
add x2, 4
mov [x1],x2 //esp 入口指针 x2 - ebp = add esp, -?? + 8
mov x3, [x2]
add x1, 4
mov [x1], x3 // push esi or push edi
add x1, 4
add x2, 4
mov x3, [x2]
mov [x1], x3 //push ebx
findop x, #C3#
mov eip, $RESULT
sti
//伪入口地址,注意寄存器参数,这里的参数是为了建立互斥对象。
bprm 401000, VirtualSize
esto
bpmc 401000
log eip
add x1, 4
//esi 中是 TAppliction_Instance 的指针
mov [x1], esi // mov esi, [????]
mov x2, esp
add x1, 4
mov x3, [x2]
mov [x1], x3 //push 0
add x1, 4
add x2, 4
mov x3, [x2]
mov [x1], x3 //push -1
add x1, 4
add x2, 4
mov x3, [x2]
//互斥对象名
mov [x1], x3 //push 57a530
jmp Exit
M:
msg "No Find"
Exit:
ret
运行结束Dump文件,修改 ImportTable 和 Tls 表。(如开头所述)
2. 将如下汇编程序编译,将生成的 Exe 文件去掉文件头,贴到启动地址偏移量为 156E0 处。(对于 v3.36 67C000 + 156E0 = 6916E0)
我写了一个贴代码的小程序 PatchCode.exe 可以到 DFCG 去下载(注意下载新的版本),对某些人来说,贴代码可能是件很困难的事。
PatchCode 使用说明。
1.AddressOfEntryPoint 用来改变入口地址,这里不需要。
2.Original Entry Point 也是修改入口地址,不需要。
3.Repair File Image Offset 将补丁贴到何处,这里不需要修改,程序自动计算补丁所贴的位置,按 GetGDNFileName 选择需要贴补丁的程序,这里是 UnGDN.exe。
4.Patch File Image Offset 栏填 401000,即代码段的起始地址。按 GetPatchFileName按键选择要贴的补丁程序,这里是 RemoveEncryptCode.exe。
5.按 WriteToFile,补丁程序将被写到指定的位置。
6.如果选择了Backup,在按 WriteToFile 时会提示输入备份块的名字。按 ComeBack 按钮,选择备份块的名字,备份块会贴回原来的位置,覆盖补丁。即恢复。
; #########################################################################
.386
.model flat, stdcall
Option CaseMap :none ; case sensitive
; RemoveEncryptCode
; Remove ACPropect For GoodName.exe V3.36&V3.37&pV3.33 EmbeddedCode & CryptCode by gzgzlxg
; #########################################################################
include \masm32\include\windows.inc
EmbTable Struct
EmbOffset DWORD 64H DUP (0)
EmbSize DWORD 64H DUP (0)
Resver DB 200H DUP (?)
Typeflag DWORD 64H DUP (0)
FirstPosition DWORD 64H DUP (0)
CodePosition1 DWORD 64H DUP (0)
CodePosition2 DWORD 64H DUP (0)
CodePosition3 DWORD 64H DUP (0)
EmbTable EndS
CallImportTab Struct
aCode Word 0
OAddr DWord 0
SAddr DWord 0
CallImportTab EndS
;=============
; Local macros
;=============
Return Macro arg
Mov Eax, arg
Ret
EndM
;=================
; Local prototypes
;=================
GetCurrAddr Proto
ProgInit Proto :DWORD
FillZero Proto :DWORD, :DWORD
BulidJmp Proto :DWORD, :DWORD
RepairEmbCode Proto
RemoveNop Proto :DWORD
SearchEnd Proto :DWORD, :DWORD
SearchZero Proto :DWORD
SearchChar Proto :DWORD
;-------------------------------------------------
RemoveCodeReplace Proto
RepairOEPCode Proto
RepairCall68Bxxx Proto
RepairCall6886F3 Proto
GetProgAddr Proto
DecodeCryptCode Proto
MoveBadCode45 Proto
MoveBadCode33 Proto
MoveBadCode03 Proto
MoveCodeToOriginalProg Proto :DWord
.Code
start:
Invoke ProgInit, 1
Call RepairEmbCode
Call RepairCall68Bxxx
Call RepairCall6886F3
Call RemoveCodeReplace
Call RepairOEPCode
Int 3
Align 4
GetCurrAddr Proc
Call $ + 5
Pop Edx
Sub Edx, Offset GetCurrAddr + 5
Ret
GetCurrAddr EndP
Align 4
RepairEmbCode Proc
Local Source: DWORD
Local Dest: DWORD
Pusha
Call GetCurrAddr
Mov Ebx, EmbOffset[Edx]
Mov Eax, EmbTable.EmbOffset[Ebx]
.While Eax > 0
Xor Ecx, Ecx
Lea Esi, EndMark1[Edx]
Add Eax, ImageBase[Edx]
.If EmbTable.Typeflag[Ebx] == 0
Sub Eax, 16
.ElseIf
.If EmbTable.Typeflag[Ebx] == 1
Sub Eax, EPDelta1
.ElseIf
.If EmbTable.Typeflag[Ebx] == 2
Sub Eax, EPDelta2
.ElseIf
.If EmbTable.Typeflag[Ebx] == 3
Lea Esi, EndMark5[Edx]
.EndIf
.EndIf
.EndIf
.EndIf
Mov Source, Eax
.While Ecx <= 64H * 4 * 3
Mov Eax, EmbTable.FirstPosition[Ebx + Ecx]
.If (Eax)
Mov Dest, Eax
Invoke BulidJmp, Source, Dest
Invoke FillZero, Source, Dest
.If (Dword Ptr [Esi])
Invoke SearchEnd, Dest, [Esi]
Invoke RemoveNop, Eax
Mov Source, Eax
.EndIf
.EndIf
Add Ecx, 64H * 4
Add Esi, 4
.EndW
Add Ebx, 4
Mov Eax, EmbTable.EmbOffset[Ebx]
.EndW
Popa
Ret
RepairEmbCode EndP
Align 4
;Count 是为调试程序而设置的,本来应该删除,但为了便于大家学习,留在这里
;Count 是用来直接定位某一个内置程序而设定,正常运行为 1,即从头开始
ProgInit Proc Count:DWORD
Local x1
Push Ebx
Push Ecx
Push Edx
Push Esi
Push Edi
Call GetCurrAddr
Mov Ebx, ImageBase[Edx]
Mov Esi, IMAGE_DOS_HEADER.e_lfanew[Ebx]
Mov Eax, IMAGE_NT_HEADERS.OptionalHeader.BaseOfData[Ebx + Esi]
Add Eax, Ebx
Mov BaseOfData[Edx], Eax
Mov Eax, IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint[Ebx + Esi]
Add Eax, Ebx
Mov FakeAddrOfEntryPoint[Edx], Eax
Push Eax
Add Eax, EmbOffsetOffset
Mov EmbOffset[Edx], Eax
Mov Eax, Count
Dec Eax
Shl Eax, 2
Add EmbOffset[Edx], Eax
Pop Eax
Add Eax, PatchOffsetOffset
Mov PatchOffset[Edx], Eax
Mov Ax, Word ptr IMAGE_NT_HEADERS.FileHeader.NumberOfSections[Ebx + Esi]
Mov NumberOfSections[Edx], Ax
Mov Eax, IMAGE_NT_HEADERS.OptionalHeader.SizeOfImage[Ebx + Esi]
Add Eax, Ebx
Mov SizeOfImage[Edx], Eax
Mov Eax, IMAGE_NT_HEADERS.OptionalHeader.BaseOfCode[Ebx + Esi]
Add Eax, ImageBase[Edx]
Mov ProgStartAddr[Edx], Eax
Mov SProgStartAddr[Edx], Eax
Add Esi, SizeOf(IMAGE_NT_HEADERS)
Mov Eax, IMAGE_SECTION_HEADER.Misc.VirtualSize[Ebx + Esi]
Mov CodeLen[Edx], Eax
Mov SCodeLen[Edx], Eax
Xor Ecx, Ecx
PILoop0:
Mov Eax, Dword Ptr IMAGE_SECTION_HEADER.Name1[Ebx + Esi]
Cmp Eax, "adi."
Je L1
Add Esi, SizeOf(IMAGE_SECTION_HEADER)
Inc Cx
Cmp Cx, NumberOfSections[Edx]
Jb PILoop0
Jmp Error
L1:
Mov Eax, IMAGE_SECTION_HEADER.VirtualAddress[Ebx + Esi]
Add Eax, ImageBase[Edx]
Mov Esi, Eax
Mov Eax, IMAGE_IMPORT_DESCRIPTOR.FirstThunk[Esi]
Add Eax, ImageBase[Edx]
Mov ImportTableStart[Edx], Eax
Mov Ecx, IMAGE_IMPORT_DESCRIPTOR.Name1[Esi]
Add Ecx, ImageBase[Edx]
Sub Ecx, 4
Sub Ecx, Eax
Shr Ecx, 2
Mov ImportTabLen[Edx], Ecx
L2:
Add Esi, SizeOf(IMAGE_IMPORT_DESCRIPTOR)
Mov Eax, IMAGE_IMPORT_DESCRIPTOR.Name1[Esi]
Add Eax, ImageBase[Edx]
Cmp Dword Ptr [Eax], "resu"
Jne L2
Cmp Dword Ptr [Eax + 4], "d.23"
Jne L2
Cmp Dword Ptr [Eax + 6], "lld."
Jne L2
Mov Ecx, IMAGE_IMPORT_DESCRIPTOR.FirstThunk[Esi]
Mov Esi, Eax
Mov x1, 0
L3:
Inc x1
Invoke SearchChar, Esi
Add Esi, Eax
Invoke SearchZero, Esi
Add Esi, Eax
Cmp Dword Ptr [Esi], "sseM"
jne L3
Cmp Dword Ptr [Esi + 4], "Bega"
jne L3
Dec x1
Mov Eax, 4
Push Edx
Mul x1
Pop Edx
Add Ecx, Eax
Add Ecx, ImageBase[Edx]
Mov pMessageBoxA[Edx], Ecx
Mov Edi, FakeAddrOfEntryPoint[Edx]
PILoop1:
Inc Edi
Cmp Edi, SizeOfImage[Edx]
Ja Error
Mov Eax, [Edi]
Cmp Eax, "ipxE" ; Expired!
Jne PILoop1
Mov Eax, [Edi + 4]
Cmp Eax, "!der"
Jne PILoop1
Mov Eax, Edi
Sub Eax, 3000 * 10
Mov CodeReplaceCodeTable[Edx], Eax
Sub Eax, 3000 * 4
Mov CodeReplaceOffset[Edx], Eax
Mov Edi, FakeAddrOfEntryPoint[Edx]
PILoop2:
Inc Edi
Cmp Edi, SizeOfImage[Edx]
Ja Error
Mov Eax, [Edi]
Cmp Eax, "MteG" ;GetMachineID
Jne PILoop2
Mov Eax, [Edi + 4]
Cmp Eax, "ihca"
Jne PILoop2
Mov Eax, [Edi + 8]
Cmp Eax, "DIen"
Jne PILoop2
Mov Eax, Edi
Sub Edi, 0A0H
Sub Edi, 256 * 4
Sub Edi, 256 * 4
Mov CryptImportTable[Edx], Edi
Sub Edi, 1000
Mov APIPosition[Edx], Edi
Sub Edi, 1000 * 4
Mov ProcOffAddr[Edx], Edi
Mov Edi, FakeAddrOfEntryPoint[Edx]
PILoop3:
Inc Edi
Cmp Edi, SizeOfImage[Edx]
Ja Error
Mov Eax, [Edi]
Cmp Eax, "_pca"
Jne PILoop3
Mov Eax, [Edi + 3]
Cmp Eax, "ipa_"
Jne PILoop3
Add Edi, 7
Mov ADPAPIStart[Edx], Edi
Add Edi, acp_api_Len
Mov ADPAPIEnd[Edx], Edi
Mov Edi, FakeAddrOfEntryPoint[Edx]
Add Edi, TempCodeTableOffset
Mov TempCodeTable[Edx], Edi
Mov Esi, FakeAddrOfEntryPoint[Edx]
PILoop4:
Inc Esi
Cmp Esi, SizeOfImage[Edx]
Ja Error
Mov Eax, [Esi] ; Randimize
Cmp Eax, "dnaR"
Jne PILoop4
Mov Eax, [Esi + 4]
Cmp Eax, "zimi"
Jne PILoop4
Add Esi, 0AH
Lea Edi, AddressOfEntryPoint[Edx]
Mov Ecx, 11
Rep MovsD
Mov Esi, FakeAddrOfEntryPoint[Edx]
PILoop5:
Inc Esi
Cmp Esi, SizeOfImage[Edx]
Ja Error
Mov Eax, [Esi] ; 替_换_代_码_zcf
Cmp Eax, 0BB5FE6CCH
Jne PILoop5
Mov Eax, [Esi + 4]
Cmp Eax, 0FAB45FBBH
Jne PILoop5
Mov Eax, [Esi + 8]
Cmp Eax, 05FEBC25FH
Jne PILoop5
Mov Al, [Esi - 1]
Mov DecodeKey[Edx], Al
Mov Esi, FakeAddrOfEntryPoint[Edx]
PILoop6:
Inc Esi
Cmp Esi, SizeOfImage[Edx]
Ja Error
Mov Eax, [Esi] ; RETRIVAPIZCF
Cmp Eax, "RTER"
Jne PILoop6
Mov Eax, [Esi + 4]
Cmp Eax, "PAVI"
Jne PILoop6
Mov Eax, [Esi + 8]
Cmp Eax, "FCZI"
Jne PILoop6
Add Esi, 0EH
PILoop7:
LodsD
Test Eax, 40000000H
Jnz PILoop7
Sub Esi, 4
Mov Eax, pMessageBoxA[Edx]
Mov [Esi], Eax
Pop Edi
Pop Esi
Pop Edx
Pop Ecx
Pop Ebx
Ret
Error:
Int 3
ProgInit EndP
Align 4
SearchZero Proc Source:DWORD
Push Ecx
Push Edi
Mov Ecx, 0FFFFFFFFH
Mov Edi, Source
Xor Al, Al
Repe Scasb
Not Ecx
Dec Ecx
Mov Eax, Ecx
Pop Edi
Pop Ecx
Ret
SearchZero EndP
Align 4
SearchChar Proc Source:DWORD
Push Ecx
Push Edi
Mov Ecx, 0FFFFFFFFH
Mov Edi, Source
Xor Al, Al
Repne Scasb
Not Ecx
Dec Ecx
Mov Eax, Ecx
Pop Edi
Pop Ecx
Ret
SearchChar EndP
Align 4
FillZero Proc Source:DWORD, Dest:DWORD
Push Eax
Push Ecx
Push Edi
Mov Eax, Source
Add Eax, 5
Mov Edi, Eax
Mov Ecx, Dest
Sub Ecx, Eax
Xor Eax, Eax
Rep Stosb
Pop Edi
Pop Ecx
Pop Eax
Ret
FillZero EndP
Align 4
BulidJmp Proc Source:DWORD, Dest:DWORD
Push Eax
Push Esi
Mov Esi, Source
Mov Byte Ptr [Esi], JmpCode
Mov Eax, Dest
Sub Eax, Source
Sub Eax, 5
Mov [Esi + 1], Eax
Pop Esi
Pop Eax
Ret
BulidJmp EndP
Align 4
RemoveNop Proc Source:DWORD
Push Ecx
Push Edi
Mov Edi, Source
RNLoop:
Dec Edi
Mov Al, [Edi]
Cmp Al, 90H
Jz RNLoop
Inc Edi
Mov Eax, Edi
Pop Edi
Pop Ecx
Ret
RemoveNop EndP
Align 4
SearchEnd Proc Source:DWORD, Mark:DWORD
Push Ebx
Push Edi
Mov Ebx, Mark
Mov Edi, Source
SELoop:
Inc Edi
Mov Eax, [Edi]
Cmp Eax, Ebx
Jnz SELoop
Mov Eax, Edi
Pop Edi
Pop Ebx
Ret
SearchEnd EndP
;=============================================
RemoveCodeReplace Proc
S1:
Invoke GetCurrAddr
Invoke GetProgAddr
Invoke DecodeCryptCode
Mov Esi, CodeReplaceCodeTable[Edx]
Lodsb
Cmp Al, 0
Je EndProc
Invoke MoveBadCode45
Cmp Eax, 1
Je S1
Invoke MoveBadCode33
Cmp Eax, 1
Je S1
Invoke MoveBadCode03
Cmp Eax, 1
Je S1
EndProc:
Ret
RemoveCodeReplace Endp
RepairOEPCode Proc
Push Ebx
Push Ecx
Push Esi
Push Edi
Call GetCurrAddr
Mov Edi, AddressOfEntryPoint[Edx]
Lea Esi, OEPStolenCode[Edx]
Mov Ecx, 33
Rep Movsb
Mov Esi, AddressOfEntryPoint[Edx]
Mov Eax, SaveEbp[Edx]
Sub Eax, SaveEsp[Edx]
Sub Eax, 9
Not Eax
Mov Byte Ptr [Esi + 5], Al
Mov Eax, InitProgTable[Edx]
Mov [Esi + 9], Eax
Mov Eax, Sysinit_InitExe[Edx]
Mov Ebx, Esi
Add Ebx, 12H
Sub Eax, Ebx
Mov [Esi + 0EH], Eax
Mov Eax, TApplication_instance[Edx]
Mov Edi, BaseOfData[Edx]
Mov Ecx, 0FFFFFFFFH
Repne ScasD
Sub Edi, 4
Mov [Esi + 14H], Edi
Mov Eax, aGoodName[Edx]
Mov [Esi + 19H], Eax
; Mov Ebx, ImageBase[Edx]
; Mov Esi, IMAGE_DOS_HEADER.e_lfanew[Ebx]
; Mov Eax, AddressOfEntryPoint[Edx]
; Sub Eax, Ebx
; Mov IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint[Ebx + Esi], Eax
Pop Edi
Pop Esi
Pop Ecx
Pop Ebx
Ret
RepairOEPCode EndP
Align 4
RepairCall68Bxxx Proc
Call GetCurrAddr
L0:
Mov Edi, ProgStartAddr[Edx]
Mov Ecx, CodeLen[Edx]
L1:
Mov Al, 0E8H
Repne Scasb
Jecxz RepairEnd
Mov Eax, [Edi]
Add Eax, Edi
Add Eax, 4
Cmp Eax, ADPAPIStart[Edx]
Jb L1
Cmp Eax, ADPAPIEnd[Edx]
Ja L1
Cmp Word Ptr [Eax + 1], 25FFH
Jne L1
Mov ProgStartAddr[Edx], Edi
Mov CodeLen[Edx], Ecx
Mov Eax, [Eax + 3]
Mov Eax, [Eax]
Mov Edi, ImportTableStart[Edx]
Mov Ecx, ImportTabLen[Edx]
Repne Scasd
Jecxz L2
Mov Eax, Edi
Sub Eax, 4
L1_1:
Mov TmpCall.OAddr[Edx], Eax
Call SearchJmpImport
Or Eax, Eax
Jz IsWrong
Mov Eax, TmpCall.SAddr[Edx]
Mov Edi, ProgStartAddr[Edx]
Sub Eax, Edi
Sub Eax, 4
Stosd
Sub DWord Ptr CodeLen[Edx], 4
Jmp L0
RepairEnd:
Return 1
L2:
Mov Edi, ProgStartAddr[Edx]
Sub Eax, Edi
Sub Eax, 4
Stosd
Sub DWord Ptr CodeLen[Edx], 4
Jmp L0
IsWrong:
Return 0
RepairCall68Bxxx EndP
Align 4
RepairCall6886F3 Proc
Call GetCurrAddr
Mov Ebp, Edx
Mov Esi, ProcOffAddr[Ebp]
Xor Edx, Edx ;Count
L0:
Lodsd
Add Eax, 400000H
Mov Edi, Eax
Mov Eax, APIPosition[Ebp]
Movzx Ebx, Byte Ptr [Eax + Edx]
Mov Eax, CryptImportTable[Ebp]
Mov Eax, [Eax + Ebx * 4]
Or Eax, Eax
Jz LEnd
Test Eax, 40000000H
Jnz L1
Mov Ebx, Eax
Mov Eax, [Ebx + 1]
Xor Eax, [Ebx + 8]
L1:
Push Edi
Mov Edi, ImportTableStart[Ebp]
Mov Ecx, ImportTabLen[Ebp]
Repne Scasd
Jecxz L3
Mov Eax, Edi
Sub Eax, 4
Pop Edi
Mov TmpCall.OAddr[Ebp], Eax
Call SearchJmpImport
Or Eax, Eax
Jz IsWrong
Mov Eax, TmpCall.SAddr[Ebp]
Sub Eax, Edi
Mov [Edi - 4], Eax
L2:
Inc Edx
Cmp Edx, 1000
Jnz Short L0
LEnd:
Ret
L3:
Pop Edi
Sub Eax, Edi
Mov [Edi - 4], Eax
Jmp L2
IsWrong:
Int 3
RepairCall6886F3 EndP
Align 4
SearchJmpImport Proc
PushA
Call GetCurrAddr
Mov Edi, SProgStartAddr[Edx]
Mov Ecx, SCodeLen[Edx]
Mov Ebx, TmpCall.OAddr[Edx]
L1:
Mov Ax, TmpCall.aCode[Edx]
Repne Scasb
Jecxz SearchNull
Cmp [Edi - 1], Ax
Jnz L1
Cmp [Edi + 1], Ebx
Jnz L1
Dec Edi
Mov TmpCall.SAddr[Edx], Edi
PopA
Return 1
SearchNull:
PopA
Return 0
SearchJmpImport EndP
Align 4
MoveCodeToOriginalProg Proc L:DWORD
Mov Esi, TempCodeTable[Edx]
Mov Edi, OriginalProgAddr[Edx]
Mov Ecx, L
Mov Ebx, 5
Mov Ah, Byte Ptr Mark[Edx]
L0:
Lodsb
L1:
Cmp Al, Ah
Je L2
Stosb
Dec Ebx
Jz pExit
L2:
Loop L0
Or Ebx, Ebx
Jne ExitFalse
pExit:
Add CodeReplaceOffset[Edx], 4
Add CodeReplaceCodeTable[Edx], 0AH
Inc Count[Edx]
Return 1
ExitFalse:
Int 3
Return 0
MoveCodeToOriginalProg endp
Align 4
GetProgAddr proc
Mov Esi, CodeReplaceOffset[Edx]
Lodsd
Add Eax, 400000H
Sub Eax, 5
Mov OriginalProgAddr[Edx], Eax
Ret
GetProgAddr EndP
DecodeCryptCode Proc
Mov Esi, CodeReplaceCodeTable[Edx]
Mov Edi, TempCodeTable[Edx]
Mov Bl, DecodeKey[Edx]
Mov Ecx, 0AH
LoopDecode:
Lodsb
Xor Al, Bl
Stosb
Loop LoopDecode
Sub Edi, 0AH
Mov Al, 90H
Mov Ecx, 0AH
Repne Scasb
Jecxz L90h
Mov DWord Ptr Mark[Edx], 0CCCCCCCCH
Ret
L90h:
Mov DWord Ptr Mark[Edx], 90909090H
Ret
DecodeCryptCode endp
Align 4
MoveBadCode45 Proc
Local Position:DWord
Mov Esi, TempCodeTable[Edx]
Mov Position, 08H
L0:
Dec Position
Jz ExitFalse
Lodsb
Mov Bl, Al
Shr Al, 4
Cmp Al, 4
Jl L0
Cmp Al, 5
Ja L0
Mov Al, Bl
And Al, 0FH
Test Al, 08H
Jnz L0
Mov Al, Bl
Add Al, 08H
Mov Ecx, Position
Mov Edi, Esi
Repne Scasb
Jecxz L0
Mov Al, Byte Ptr Mark[Edx]
Mov [Esi - 1], Al
Mov [Edi - 1], Al
Invoke MoveCodeToOriginalProg, 7
Return 1
ExitFalse:
Return 0
MoveBadCode45 EndP
Align 4
MoveBadCode33 Proc
Local Position:DWord
Mov Esi, TempCodeTable[Edx]
Mov Position, 0AH
L0:
Dec Position
Jz ExitFalse
Lodsb
Cmp Al, 33H
Jnz L0
Mov Bl, Al
Mov Ah, [Esi]
Mov Ecx, Position
Mov Edi, Esi
Inc Edi
Dec Ecx
L1:
Repne Scasb
Jecxz L0
Cmp [Edi - 1], Ax
Jne L1
Mov Ax, Word Ptr Mark[Edx]
Mov [Esi - 1], Ax
Mov [Edi - 1], Ax
Invoke MoveCodeToOriginalProg, 9
Return 1
ExitFalse:
Return 0
MoveBadCode33 EndP
Align 4
MoveBadCode03 Proc
Local Position:DWord
Mov Esi, TempCodeTable[Edx]
Mov Position, 0AH
L0:
Dec Position
Jz ExitFalse
Lodsb
Cmp Al, 03H
Jne L0
Mov Ah, [Esi]
Cmp Ah, 0C0H
Jl L0
Mov Al, 2BH
Mov Ecx, Position
Mov Edi, Esi
Inc Edi
Dec Ecx
L1:
Repne Scasb
Jecxz L0
Cmp [Edi - 1], Ax
Jne L1
Mov Ax, Word Ptr Mark[Edx]
Mov [Esi - 1], Ax
Mov [Edi - 1], Ax
Invoke MoveCodeToOriginalProg, 9
Return 1
ExitFalse:
Return 0
MoveBadCode03 EndP
Align 4
;============================================
EmbOffsetOffset Equ 0F25H
PatchOffsetOffset Equ 156E0H
JmpCode Equ 0E9H
EPDelta1 Equ 1C1H
EPDelta2 Equ 2ABH
EndMark1 DD 6A056A60H
EndMark2 DD 0000E860H
EndMark3 DD 6A046A60H
EndMark4 DD 0
EndMark5 DD 00E86060H
EndMark6 DD 0
EndMark7 DD 0
EndMark8 DD 0
FakeAddrOfEntryPoint DD 0
SizeOfImage DD 0
EmbOffset DD 0
PatchOffset DD 0
ImageBase DD 400000H
;===========================================
TempCodeTableOffset Equ 67CBEFH - 67C000H
acp_api_Len Equ 0C4H
TempCodeTableOffset Equ 0BEFH
CodeReplaceOffset DD 0
CodeReplaceCodeTable DD 0
TempCodeTable DD 0
Count DD 0
OriginalProgAddr DD 0
Mark DD 90909090H
ProgStartAddr DD 0
;ProgEndAddr DD 0
SProgStartAddr DD 0
;SProgEndAddr DD 0
ADPAPIStart DD 0
ADPAPIEnd DD 0 ;acp_api
CodeLen DD 0
SCodeLen DD 0
ImportTableStart DD 0
ImportTabLen DD 0
pMessageBoxA DD 0
FakeMessageBoxA DD 0
ProcOffAddr DD 0
APIPosition DD 0
CryptImportTable DD 0
NumberOfSections DW 0
DecodeKey DB 0
DB 0
;============================================================
BaseOfData DD 0
AddressOfEntryPoint DD 0
Sysinit_InitExe DD 0
InitProgTable DD 0
SaveEbp DD 0
SaveEsp DD 0
PushEsi DD 0
PushEbx DD 0
TApplication_instance DD 0
PushZero DD 0
PushFFFF DD 0
aGoodName DD 0
Align 4
TmpCall CallImportTab < 025FFH, 0, 0 >
OEPStolenCode db 055H,08BH,0ECH,083H,0C4H,000H,053H,056H,0B8H,000H,000H,000H,000H,0E8H,000H,000H, \
000H,000H,08BH,035H,000H,000H,000H,000H,068H,000H,000H,000H,000H,06AH,0FFH,06AH, \
000H
End start
用OD载入贴好补丁的UnGDN.exe,运行下面的脚本:
这个过程大约需要1分钟。
// Remove Embedded Code by gzgzlxg
// 这个过程将同益起名V3.36&V3.37&Vp3.33版的内置的26个保护和其余加密过程解码
var AddressOfEntryPoint
var AddrEP
var EmbOffset
var EmbSize
var EmbOff
var EmbS
var TmpOffset
var TmpSize
var TmpTypeflag
var ESize
var EOff
var tmpEP
var Dflag
var Afterbody
var x
var x1
var x2
var x3
var x4
var y
var z
var Count1
var Count
var FirstC
var RunMark
var Typeflag
var Typef
var FirstPosition
var CodePosition1
var CodePosition2
var CodePosition3
var PatchAddr
//读PE头文件
mov x1, 400000
add x1, 3C
mov x, [x1]
add x, 400000
add x, 28
mov AddrEP, [x]
add AddrEP, 400000
mov PatchAddr, AddrEP
add PatchAddr, 156E0
//置入程序的偏移量表,以零结尾
mov EmbOffset, AddrEP
add EmbOffset, 0F25
mov EmbOff, EmbOffset
//置入程序的大小
mov EmbSize, EmbOffset
add EmbSize, 190
mov EmbS, EmbSize
mov Count, 1
// mov Count, 1A
// RunMark = 1, 运行当前 Count 后直接退出; RunMark = 0,从当前 Count 开始,运行到结尾。
mov RunMark, 0
//==============
mov Typeflag, EmbSize
add Typeflag, 200
add Typeflag, 190
mov Typef, Typeflag
mov FirstPosition, Typeflag
add FirstPosition, 190
mov CodePosition1, FirstPosition
add CodePosition1, 190
mov CodePosition2, CodePosition1
add CodePosition2, 190
mov CodePosition3, CodePosition2
add CodePosition3, 190
fill Typeflag, 7D0, 00
//============== //----------------------
mov x, Count
dec x
EmbLoop:
cmp x, 0
je P1
add EmbOffset, 4
add EmbSize, 4
add Typeflag, 4
add FirstPosition, 4
add CodePosition1, 4
add CodePosition2, 4
add CodePosition3, 4
dec x
jmp EmbLoop
//----------------------
P1:
sub EmbOffset, 4
sub EmbSize, 4
sub Typeflag, 4
sub FirstPosition, 4
sub CodePosition1, 4
sub CodePosition2, 4
sub CodePosition3, 4
dec Count
Start:
//下一个内置代码起始位置
add EmbOffset, 4
add EmbSize, 4
add Typeflag, 4
add FirstPosition, 4
add CodePosition1, 4
add CodePosition2, 4
add CodePosition3, 4
inc Count
//如果为零,结束
cmp [EmbOffset], 0
je SearchCryptCode mov ESize, [EmbSize]
mov EOff, [EmbOffset]
add EOff, 400000 //换算成实际地址
add ESize, EOff
log " "
log "-------------------------------------------"
log Count
log "Embedded Code Start Address"
log EOff
log "Embedded Code End Address"
log ESize
//置入程序分二大种类型,第一类是经过两次加密的,另一类是正常的
//经过两次加密的入口地址和实际地址不同,多了一个加密循环。
mov Dflag, 0
mov tmpEP, [EOff]
and tmpEP, 0FF
cmp tmpEP, 60
je SE
mov Dflag, 1
sub EOff, 1C1
mov tmpEP, [EOff]
and tmpEP, 0FF
cmp tmpEP, 60
je S3
mov Dflag, 2
sub EOff, 0EA
mov tmpEP, [EOff]
and tmpEP, 0FF
cmp tmpEP, 60
je S3
jmp M1
S3:
log EOff
mov eip, EOff
find eip, #0F85??FF#
mov EOff, $RESULT
add EOff, 6
go EOff
log EOff
find eip, #61#
go $RESULT
cmp Dflag, 2
jne S4
log "User Program First Section"
mov Count1, $RESULT
inc Count1
log Count1
mov [FirstPosition], Count1
S4:
//查找入口特征代码:
// pushad
// push 05
// push 00
// push 00
// push -1
// call MessageBoxA
// popad
SS:
find eip, #606A056A#
mov EOff, $RESULT
sti
add EOff, 4
findop EOff, #60#
mov EOff, $RESULT
//从这里开始为正常加密的内置程序
//第一部分将保护程序的前部解码,其中12个ADP保护模块都解码了。
SE:
mov [Typeflag], Dflag
mov y, EOff
add y, 4d7
mov eip, EOff
go y
mov Count1, 0
//第二部分将12个ADP保护程序废除――在入口处加ret(C3)
Loop:
find y, #414450??#
mov y, $RESULT
add y, 5
repl y, #60#, #C3#, 1
inc Count1
cmp Count1, 0C
jbe Loop
//获取入口地址
cmp Count, 1
jne SE1
mov Count1, eip
add Count1, 5
add Count1, 3F0
log Count1
mov AddressOfEntryPoint, [Count1]
add AddressOfEntryPoint, 400000
log AddressOfEntryPoint
mov Count1, AddrEP
find Count1, #52616E64696D697A65#
mov Count1, $RESULT
add Count1, 0A
mov [Count1], AddressOfEntryPoint
//第三部分将一个用来建立自用输入表的部分废除――在入口处加ret(C3)
SE1:
sti
sti
asm eip, "ret"
sti
//第四部分:查找第一段用户代码,这里因为我们是从半路进来,如果执行用户代码将出现问题
//所以必须跳过用户代码。
Loop1:
find eip, #E800000000#
mov z, $RESULT
mov x1, z
find z, #0F85??FF#
mov y, $RESULT
add y, 6
go y
add x1, 0A
mov x2, [x1]
and x2, 0FF
add z, 5
add z, x2
mov x2, [z]
mov x3, [x2]
and x3, 0FF
cmp x3, 0E8
je Loop1
cmp x3, 61
je L1
cmp x3, 90
je L2
cmp x3, 0E9
je L3
jmp M
L1:
go x2
L1_1:
inc x2
mov x3, [x2]
and x3, 0FF
cmp x3, 60
je L1_3
mov x3, [x2]
and x3, 0FFFF
cmp x3, 058F
je L1_2
jmp L1_1
L1_2:
find x2, #60#
mov x3, $RESULT
L1_3:
inc x3
mov eip, x3
jmp Loop1
L2:
log "User Program Code Section 1"
// cmp Count, 2
// je L4
go x2
inc x2
inc x2
log x2
mov [CodePosition1], x2
find x2, #60#
mov y, $RESULT
inc y
mov eip, y
L3:
//第五部分: 查找结尾部分的用户代码,必须跳过这些代码
mov y, EOff
add y, 3222
mov [CodePosition2], y
go y
//查找结尾特征代码
// pushad
// push 04
// push 00
// push 00
// push -1
// call MessageBoxA
find y, #606A046A#
cmp $RESULT, ESize
ja M1
mov y, $RESULT
add y, 2F
mov Count1, [y]
cmp Count1, 00E86060
jne L5
add y, 3E
L5:
log "User Program Code Section 2"
mov z, [CodePosition2]
L7Loop:
inc z
mov Count1, [z]
and Count1, 0FF
cmp Count1, 90
je L7Loop
mov [CodePosition2], z
log z
log "User Program Code Afterbody"
log y
mov [CodePosition3], y
L6:
cmp RunMark, 1
je SearchCryptCode
jmp Start
//查找其他的加密代码(不同于内置代码,除了加密解码,没有其他的保护手段)
SearchCryptCode:
mov FirstC, Count
dec FirstC
cmp RunMark, 1
je Exit
mov x, 401000
pLoop:
find x, #E8000000005D#
mov x, $RESULT
add x, 100
cmp $RESULT, 0
je Exit
mov x1, EmbOff
mov x2, EmbS
mov x3, Typef
mov Count1, 0
NLoop:
cmp Count1, FirstC
jae p0
mov TmpOffset, [x1]
add TmpOffset, 400000
mov TmpSize, [x2]
add TmpSize, TmpOffset
mov TmpTypeflag, [x3]
cmp TmpTypeflag, 0
je N0
cmp TmpTypeflag, 1
je N1
cmp TmpTypeflag, 2
je N2
jmp M1
N0:
sub TmpOffset, 10
jmp N3
N1:
sub TmpOffset, 1C1
jmp N3
N2:
sub TmpOffset, 2AB
N3:
cmp $RESULT, TmpOffset
jb Next
cmp $RESULT, TmpSize
jb pLoop
Next:
add x1, 4
add x2, 4
add x3, 4
inc Count1
jmp NLoop
p0:
mov x, $RESULT
mov x1, x
sub x1, 60
p1:
mov x2, [x1]
and x2, 0FF
cmp x2, 60
je p2
dec x1
jmp p1
p2:
log "-------------------------------"
log "Other EncryptCode Start Address"
log Count
log x1
mov eip, x1
sub x1, 400000
mov [EmbOffset], x1
mov [Typeflag], 3
find eip, #0F85??FF#
mov x, $RESULT
add x, 6
go x
find x, #6060E800#
mov x1, $RESULT
add x1, 0C
mov x, $RESULT
sub x, [x1]
add x, 2
log "User Program First Section"
log x
mov [FirstPosition], x
find x1, #6161#
mov x, $RESULT
add x, 2
log "User Program Code Section 1"
log x
mov [CodePosition1], x
add EmbOffset, 4
add EmbSize, 4
add Typeflag, 4
add FirstPosition, 4
add CodePosition1, 4
add CodePosition2, 4
add CodePosition3, 4
findop x, #C3#
mov x, $RESULT
inc Count
jmp pLoop
M:
log "============================"
log x1
msg "No Find"
M1:
msg "It's wrong"
Exit:
mov eip, PatchAddr
run
ret
运行结束,在OD 的数据窗口选择从401000 到代码段尾部(不能选择整个代码段,代码段的文件长度和加载后在内存的长度不同)可以看到同益起名这几个字的地方。然后用复制到可执行文件的命令将修改过的程序保存。打开记录窗口,可以看到每段内嵌和加密代码的起始位置,每段用户程序的位置等信息,有兴趣可以拷贝下来研究。另外在第一节显示了真正的入口地址,这个需要记录下来。这是记录数据(部分)
-------------------------------------------
Count = 00000001
Embedded Code Start Address
EOff = 004F52E1
Embedded Code End Address
ESize = 004F8553
Count1 = 004F5BAD
AddressOfEntryPoint = 0057A49C ***** 注意这里是真正的 OEP
User Program Code Section 1
x2 = 004F7C82
User Program Code Section 2
z = 004F8506
User Program Code Afterbody
y = 004F8550
-------------------------------------------
Count = 00000002
Embedded Code Start Address
EOff = 0057621F
Embedded Code End Address
ESize = 0057949B
User Program Code Section 2
z = 00579442
User Program Code Afterbody
y = 00579498
......
......
-------------------------------
Other EncryptCode Start Address
Count = 00000023
x1 = 0055CB71
User Program First Section
x = 0055CD22
User Program Code Section 1
x = 0055CE75
-------------------------------
Other EncryptCode Start Address
Count = 00000024
x1 = 00561333
User Program First Section
x = 005614E4
User Program Code Section 1
x = 0056182D
00691700 INT3 命令在 UnGDN.00691700
被调试的程序无法处理异常 用PE Explorer 载入 UnGDN.exe
点 Section Headers,将 PerPlex 段前的小勾去掉,即删除壳,点重新计算。
点 Headers Info,将入口地址改为上面记录的地址,记住,要减去 400000,如0057A49C 改为 0017A49C。存盘。
全文结束。
附件:PatchCode.rar
其中包括:
FindOEPAndDumpFile.OSC
RemoveEncryptCode.asm
RemoveEncryptCode.exe
RemoveEncryptCode.OSC
ParchCode.exe
后记:
这一篇称为完美脱壳,而不是最完美的脱壳,我在DFCG 官方网站的【基础知识交流】发过有ACProtect-同益起名大师最完美的脱壳。那里提供了最完美的脱壳的方法,那是针对v3.36版的。其中差别就是在去除这些垃圾代码后将用户每一片代码移到一处,而这里是使用了 Jmp 指令将它们连接起来,将那段最完美脱壳的代码和上面的汇编程序相加,然后略微修改,就可以达到最完美的脱壳。这是看了股林精怪使用 JMP 连接,觉得还是简单一点好,DFCG 官方网站的【基础知识交流】的版主说我的汇编代码比壳的还要复杂,所以这里决定简单一点。
观点:
脱壳会砸了别人的饭碗吗?
其实脱壳和加壳是对立的统一,谁也离不了谁,试想如果这个世界上没有脱壳的存在,那么加壳的饭碗就真的给砸了。我希望ACProtect的作者能看到这篇东西,并从中获得灵感,写出真正第一流的壳,等有了新的版本,我们再较量。附件:patchcode.rar
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)