【文章标题】: 寻找失踪的代码(ACProtect2.11 Stolen OEP修复 VC6)
【文章作者】: newsoft88
【作者邮箱】: newsoft88@126.com
【作者主页】: http://newsoft88.ys168.com
【作者QQ号】: 
【软件名称】: DiskG.exe
【软件大小】: 5M
【下载地址】: 自己搜索下载
【加壳方式】: ACProtect
【保护方式】: ACProtect2.11 PRO
【编写语言】: VC++6
【使用工具】: OD、ACP2.0脚本、其他脱离壳辅助工具
【操作平台】: WIN98、2000、XP
【软件介绍】: 加上ACProtect2.11的壳,STOLE OEP
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
    这个东西本来加的是ACProtect2.11的壳,用了机机居然他下不来,运行不了,有时真想把他放一边了!后来再次查看时,是OEP的被Stolen出的问题,所以

今天我们不去谈具体脱壳的内容,而是寻找那被丢失的OEP,还软件的原来模样!
  
    废话少说,首先打开PEID,检查是什么壳型,PEID这次报出UltraProtect 1.x -> RISCO Software Inc.;没办法,很多的ACP都这么报,后来用了新的SIG,
报出2.0X,总算提示你,那不是旧版,得上新武器对付他,机机不灵了!
  
    使用ANTI版OD载入软件,几次确定后,停在入口,代码如下:
  
  004BF000 >  60              pushad
  004BF001    23EA            and     ebp, edx
  004BF003    4F              dec     edi
  004BF004    45              inc     ebp
  004BF005    72 01           jb      short 004BF008
  004BF007    F8              clc
  004BF008    66:BB 008E      mov     bx, 8E00
  004BF00C    0BDD            or      ebx, ebp
  004BF00E    87D8            xchg    eax, ebx
  004BF010    C1E8 16         shr     eax, 16
  004BF013    0F8A 02000000   jpe     004BF01B
  004BF019    85C6            test    esi, eax
  
   选择相关的内存异常忽略选项后,直接运行ACP V2的脚本(这个网上很多,可以随处找到);一会脚本停下,提示OEP到,代码停在如下:
  
  0044E879    33C0            xor     eax, eax                         ; 传说中的光明之颠~OEP~
  0044E87B    6A 00           push    0
  0044E87D    394424 08       cmp     dword ptr [esp+8], eax
  0044E881    68 00100000     push    1000
  0044E886    0F94C0          sete    al
  0044E889    50              push    eax
  0044E88A    FF15 A4F44700   call    dword ptr [47F4A4]               ; kernel32.HeapCreate
  0044E890    85C0            test    eax, eax
  0044E892    A3 A8944A00     mov     dword ptr [4A94A8], eax
  0044E897    74 36           je      short 0044E8CF
  0044E899    E8 93FEFFFF     call    0044E731
  
   这个地方知道VC程序的人,就看出,这不是OEP,为什么,因为那是补剪去的OEP下面的一个CALL中的头,为明确VC程序OEP的特征,我们现在来看一个标准的

VC++ OEP头:
  
  Microsoft Visual C++ 6.0 [Debug]
  
  .text
  .rdata
  .data
  .rsrc
  
  005522F3 Baby>/$  55                  push ebp                  //标准OEP   VC6
  005522F4      |.  8BEC                mov ebp,esp
  005522F6      |.  6A FF               push -1
  005522F8      |.  68 58235800         push Babylon.00582358
  005522FD      |.  68 6C5B5500         push Babylon.00555B6C                                    ;  SE handler installation
  00552302      |.  64:A1 00000000      mov eax,dword ptr fs:[0]
  00552308      |.  50                  push eax
  00552309      |.  64:8925 00000000    mov dword ptr fs:[0],esp
  00552310      |.  83EC 58             sub esp,58
  00552313      |.  53                  push ebx
  00552314      |.  56                  push esi
  00552315      |.  57                  push edi                                                 ;  ntdll.7C930738
  00552316      |.  8965 E8             mov dword ptr ss:[ebp-18],esp
  00552319      |.  FF15 00635700       call near dword ptr ds:[<&KERNEL32.GetVersion>]          ;  kernel32.GetVersion
  0055231F      |.  33D2                xor edx,edx                                              ;  ntdll.KiFastSystemCallRet
  00552321      |.  8AD4                mov dl,ah
  00552323      |.  8915 00BF5D00       mov dword ptr ds:[5DBF00],edx                            ;  ntdll.KiFastSystemCallRet
  00552329      |.  8BC8                mov ecx,eax
  0055232B      |.  81E1 FF000000       and ecx,0FF
  00552331      |.  890D FCBE5D00       mov dword ptr ds:[5DBEFC],ecx
  00552337      |.  C1E1 08             shl ecx,8
  0055233A      |.  03CA                add ecx,edx                                              ;  ntdll.KiFastSystemCallRet
  0055233C      |.  890D F8BE5D00       mov dword ptr ds:[5DBEF8],ecx
  00552342      |.  C1E8 10             shr eax,10
  00552345      |.  A3 F4BE5D00         mov dword ptr ds:[5DBEF4],eax
  0055234A      |.  6A 01               push 1
  0055234C      |.  E8 391D0000         call Babylon.0055408A
  00552351      |.  59                  pop ecx                                                  ;  kernel32.7C816FD7
  00552352      |.  85C0                test eax,eax
  00552354      |.  75 08               jnz short Babylon.0055235E
  00552356      |.  6A 1C               push 1C
  
  再回到我们的代码;
  0044E879    33C0            xor     eax, eax                         ; 传说中的光明之颠~OEP~
  0044E87B    6A 00           push    0
  0044E87D    394424 08       cmp     dword ptr [esp+8], eax
  
  看堆栈:
  0012FF44   004CE670  返回到 DiskGeni.004CE670 来自 DiskGeni.0044E879  // 第2句CALL
  0012FF48   00000001                                                   // 第1句PUSH
  0012FF4C   7C930228  ntdll.7C930228
  
  根据我们的分析:现在的位置应该是OEP第一CALL进入的位置;从堆栈处看到,0012FF48是1;所以原来的代码应该是:
  
  push 1
  call 0044E879
  
  这和那个标准VC的OEP代码也对上了如:(用他做参考,很方便)
  0055234A      |.  6A 01               push 1
  0055234C      |.  E8 391D0000         call Babylon.0055408A
  
  从上面上看,还有很长的代码没找到,我们使用ALT + F9返回,
  再在CODE段下内存访问断,shift + F9 ,继续:此时代码停在:
  
  0044ABAD    85C0            test    eax, eax
  0044ABAF    75 08           jnz     short 0044ABB9
  0044ABB1    6A 1C           push    1C
  0044ABB3    E8 C2000000     call    0044AC7A
  0044ABB8    59              pop     ecx
  0044ABB9    E8 D8390000     call    0044E596
  0044ABBE    85C0            test    eax, eax
  0044ABC0    75 08           jnz     short 0044ABCA
  0044ABC2    6A 10           push    10
  0044ABC4    E8 B1000000     call    0044AC7A
  0044ABC9    59              pop     ecx
  0044ABCA    33F6            xor     esi, esi
  0044ABCC    8975 FC         mov     dword ptr [ebp-4], esi
  0044ABCF    E8 06380000     call    0044E3DA
  0044ABD4    E8 60370000     call    0044E339
  0044ABD9    A3 D4954A00     mov     dword ptr [4A95D4], eax
  0044ABDE    E8 E9350000     call    0044E1CC
  0044ABE3    A3 4C7D4A00     mov     dword ptr [4A7D4C], eax
  0044ABE8    E8 B6330000     call    0044DFA3
  0044ABED    E8 F9320000     call    0044DEEB
  0044ABF2    E8 19240000     call    0044D010
  0044ABF7    8975 D0         mov     dword ptr [ebp-30], esi
  0044ABFA    8D45 A4         lea     eax, dword ptr [ebp-5C]
  0044ABFD    50              push    eax
  0044ABFE    FF15 34F54700   call    dword ptr [47F534]               ; kernel32.GetStartupInfoW
  
  此处才是代码真正的OEP一节,因为从0044ABAD向上全是乱码了,所以我们要根据上面分析的来寻找那失去的部分:
  
  根据刚才找到的两句,参照标准OEP头,我们来还原一下代码如下;未猜出的部分先有用XXXXX代替:
  
    / push eax
   |  mov dword ptr fs:[0],esp
   |  sub esp,58
   |  push ebx
   |  push esi
   |  push edi
    \ mov dword ptr ss:[ebp-18],esp        //这几句不变的,直接照抄
  
   call near dword ptr ds:[XXXXXXXX]       //这句要找地址了; <&KERNEL32.GetVersion>
   
   xor edx,edx                             //这两句不变的,直接照抄
   mov dl,ah
  
   mov dword ptr ds:[XXXXXXXX],edx        //地址1                    ;  
   mov ecx,eax
   and ecx,0FF
   mov dword ptr ds:[XXXXXXXX],ecx      //地址2  
   shl ecx,8
   add ecx,edx                                              ;  
   mov dword ptr ds:[XXXXXXXX],ecx      //地址3   
   shr eax,10
   mov dword ptr ds:[XXXXXXXX],eax      //地址4   
  
   push 1                               //这几句上面已经交待了
   call 0044E879
  
     程序停在的代码,接上就行了
   0044ABAD    85C0            test    eax, eax
  0044ABAF    75 08           jnz     short 0044ABB9
  0044ABB1    6A 1C           push    1C
  0044ABB3    E8 C2000000     call    0044AC7A
  0044ABB8    59              pop     ecx
  0044ABB9    E8 D8390000     call    0044E596
  0044ABBE    85C0            test    eax, eax
  0044ABC0    75 08           jnz     short 0044ABCA
  
  根据文件头的猜测,参照标准头,我们写出OEP的代码,但现最头疼的就是那几个[xxxxxxxx]的地址了,我们现在把他挖出来:
  一、call near dword ptr ds:[XXXXXXXX]       //这句要找地址了; <&KERNEL32.GetVersion>
  这个好办,我们把代码向下找,在OD中找到这句:
  0044ABFE    FF15 34F54700   call    dword ptr [47F534]               ; kernel32.GetStartupInfoW
  
  很显然,0047F000节是IAT存放,我们在汇编中找到这节:
  0047F610   .  7B1D807C      dd      kernel32.LoadLibraryA
  0047F614   .  56BE807C      dd      kernel32.lstrlenA
  0047F618   .  989C807C      dd      kernel32.MultiByteToWideChar
  0047F61C   .  6EBC807C      dd      kernel32.FindResourceW
  0047F620   .  55A0807C      dd      kernel32.LoadResource
  0047F624   .  37CD807C      dd      kernel32.SetHandleCount
  0047F628   .  7A12817C      dd      kernel32.GetVersion               //这就是了
  0047F62C   .  D097807C      dd      kernel32.GetCurrentThreadId
  0047F630   .  0C01817C      dd      kernel32.GlobalAddAtomW
  0047F634   .  C74E837C      dd      kernel32.GlobalFindAtomW
  0047F638   .  C30B837C      dd      kernel32.GlobalDeleteAtom
  0047F63C   .  DDE4807C      dd      kernel32.GetModuleHandleW
  
  <&KERNEL32.GetVersion>的地址是 0047F628,那很明显,这个CALL应该是
  
  call    dword ptr [0047F628];  kernel32.GetVersion
  
  二、我们还有四个地址,那就是:
  
  mov dword ptr ds:[XXXXXXXX],edx        //地址1                    ;  
   mov ecx,eax
   and ecx,0FF
   mov dword ptr ds:[XXXXXXXX],ecx      //地址2  
   shl ecx,8
   add ecx,edx                                              ;  
   mov dword ptr ds:[XXXXXXXX],ecx      //地址3   
   shr eax,10
   mov dword ptr ds:[XXXXXXXX],eax      //地址4   
  
  看到这个真晕了。这没有什么参照,我们怎么知道他呢!
  
  我们在OD处看到这行
  
  0044ABD9   .  A3 D4954A00   mov     dword ptr [4A95D4], eax
  0044ABDE   .  E8 E9350000   call    0044E1CC
  0044ABE3   .  A3 4C7D4A00   mov     dword ptr [4A7D4C], eax   //看这行
  0044ABE8   .  E8 B6330000   call    0044DFA3
  0044ABED   .  E8 F9320000   call    0044DEEB
  0044ABF2   .  E8 19240000   call    0044D010
  0044ABF7   .  8975 D0       mov     dword ptr [ebp-30], esi
  0044ABFA   .  8D45 A4       lea     eax, dword ptr [ebp-5C]
  0044ABFD   .  50            push    eax                              ; /pStartupinfo
  0044ABFE   .  FF15 34F54700 call    dword ptr [47F534]               ; \GetStartupInfoW
  
  根据经验,0044ABE3处的地址,和最初值的地址有一差值,这个差值一般比较不变,他也会有变化,但两个地址的差别基本固定在0x78左右,但我们不会根据

这个来决定上面的地址;
  我们首先来看上面的四处赋值,就是四次对连续的地址给值,这从标准代码上也看到,我们直接在堆栈处看看:[4A7D4C]
  
  
  004A7D4C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7D5C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7D6C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7D7C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7D8C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7D9C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7DAC  00 00 00 00 00 00 00 00 00 00 00 00 28 0A 00 00  ............(...
  004A7DBC  01 05 00 00 05 00 00 00 01 00 00 00 00 00 00 00  ............
  004A7DCC  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7DDC  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7DEC  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7DFC  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7E0C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  004A7E1C  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  
  
  整区中只有四个地址有值,而且是连续的,这说明什么,就是前面代码刚赋值的地址,而且地址也符合规律,就是和[4A7D4C]地址差值差不多是0x78左右;
  
  mov dword ptr ds:[004A7DC4],edx        //地址1  最大                 ;  
   mov ecx,eax
   and ecx,0FF
   mov dword ptr ds:[004A7DC0],ecx      //地址2  
   shl ecx,8
   add ecx,edx                                              ;  
   mov dword ptr ds:[004A7DBC],ecx      //地址3   
   shr eax,10
   mov dword ptr ds:[004A7DB8],eax      //地址4  最小。
  
  
  经过上面的过程,我们的OEP丢失的代码已经全部找回来了!
  
  然后保存,用LorePE修正OEP,ImportREC修复IAT!
  
  最后运行了一下,完美修复,没能 任何问题!
  
  
  
--------------------------------------------------------------------------------
【经验总结】
  谢谢大家看完,其实修复这个OEP也并不是很复杂,有时还要看运气,但有一点。针对什么语言,就要熟相关语言的文件头
  ,对比一下,就事半功倍了!写的不好,请不要向我扔石头!欢迎大家交流!
  
--------------------------------------------------------------------------------
【版权声明】: 谢谢观看, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2009年10月04日 10:55:47