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的作者能看到这篇东西,并从中获得灵感,写出真正第一流的壳,等有了新的版本,我们再较量。

附件:http://www.pediy.com/bbs/pediy7/pediy7-690.rar