英文:
SDprotector Pro Edition v1.12 Manually Unpacking Tutorial by KaGra

Download SDPR at www.sdprotector.com,till now is 1.12 pro edition the most recent ver.

  
  Hallo,hallo.Nice talking with U again.Well,this time the victim is SDprotector.
  Well,here I followed a little different approach to unpack this protector.What 
  I mean?HeRe it Comes...


  Tools Used: Olly v1.10 and ImpRec v1.6f,Ollydump Plug,HideOlly Plug (I will not refer to it)


  Well,I have checked all the Options of protection in SDprotector and protected the file.Those
  options are in Options->Left part of the screen.I didn't touch anything else.
  
  Load the exe target in Olly.In debugging options,check them all so that the execution
  of the programm will not be interrupted by any exception (and then ask U to press Shift+F9
  or Shift+F8 or Shift+F7 to continue).Run the exe.A messagebox appearz saying that debugger is
  detected.Well it is hard to find what exactly protection against debugger this may has.
  I found out that it detects a debbuger using CreateToolHelp32Snapshot to find out what windows
  are open and compere them with a default string list (in the list is also Olly).It also uses
  SetUnhandledException filter to find the debugger,but I really tried hard to find out how the
  exception occured after the calling of this API.But I didnt find it.So I thought this:Why just 
  run the exe,not under Olly,and then just attach to the process?

  Well,let's do so.I run the exe,a messagebox appears saying something of a demo version of
  the packer (don't worry,the features we enabled for protecting this file work),and then the
  screenbox of the exe process appears.Now,open Olly.Before U attach,a messagebox appears saying
  that a debugger is detected,and closes the debugger.Run the exe again,and open LordPE to dump it.
  Before U ever try to dump,it closes LordPE and exits.What is going on?

  Well,it can't be using SetUnhandledExceptionFiler or any other kind of exception trick,because
  it is not being debugged.So,the only thing that comes in my mind is that it has hardcoded
  strings refering to processes or Window handles.It also has a loop that checks all the
  running processes and windows handles with those strings,althought it is supposed to be
  in the original exe's code section.Well,it is but the packer has given him some extra code
  and this code still remains and makes this security check loop,althought we have passed the 
  OEP.

  And my quess is true.Rename LordPE.exe to something else.Run the exe.Now run the renamed
  LordPE.It does not closes LordPE.So,do the same with Olly.Damn,it still closes her.Well,this
  happens because Olly has inside her .data and .edata section strings that start with "Olly"
  chars,and all those are hardcoded in the security loop that I mentioned before.So,open Olly,and
  load Olly (yes,U hear right) in the first Olly.Click the "M" button and see the section of the
  loaded Olly,in first Olly:


Memory map
Address    Size       Owner      Section    Contains      Type   Access    Initial   Mapped as

00400000   00001000   OLLYDBG               PE header     Imag   R         RWE
00401000   000AF000   OLLYDBG    .text      code          Imag   R         RWE
004B0000   0005B000   OLLYDBG    .data      data          Imag   R         RWE
0050B000   00001000   OLLYDBG    .tls                     Imag   R         RWE
0050C000   00001000   OLLYDBG    .rdata                   Imag   R         RWE
0050D000   00002000   OLLYDBG    .idata     imports       Imag   R         RWE
0050F000   00002000   OLLYDBG    .edata     exports       Imag   R         RWE
00511000   00036000   OLLYDBG    .rsrc      resources     Imag   R         RWE
00547000   0000C000   OLLYDBG    .reloc     relocations   Imag   R         RWE


  Go to the .idata section and search for the string Olly,withought having the case sensitive 
  checked.U find this:


0050F780  6F 6C 6C 79 64 62 67 2E 65 78 65 00 5F 41 64 64  ollydbg.exe._Add
0050F790  73 6F 72 74 65 64 64 61 74 61 00 5F 41 64 64 74  sorteddata._Addt
0050F7A0  6F 6C 69 73 74 00 5F 41 6E 61 6C 79 73 65 63 6F  olist._Analyseco


  Change ollydbg.exe to something else eg. fffffff.exe.Search again in this section.
  We are lucky,because it is found just once.Go to the .data section and search again
  for the same string.Damn,here the string exists in many places.Well,change all the
  words that have this string inside them.

  When done,dump with OllyDump the process,without checking the Import Rebuild Option on.
  Well,Olly is now patched.Rename the dumped to anything that has not the string Olly in it.Now,
  run the protected exe again till the main window of the crackme appearz.Now,run the dumped
  new patched Olly.Wait a little (some seconds).

  Well,it dooesn't detect Olly!Well,if in this part U have done something wrong,or make
  something wrong in the following steps,next time will detect Olly,and U will need to change
  the patched strings ALL to something else,again.I think that the protected exe,if once find
  an exe string signature that may be a possible debugger or any other "hostile" program for
  it,it put it somewhere (registry,memory,I don't know) and rembers it.So,just do the patch right
  and follow every single instruction of the next linez.But there are pacthes that will patch Olly
  against this protected exe once and for all.Such a patch is putting Fh 's in all strings that will
  be replaced.I don't really know why this string makes this good thing for us,but it works.So,patch Olly
  with Fh 's where U should patch (Hey,Fh ascii not chars!).

  Now,in patched Olly that is running,check all the options of exceptions in Debugger Options,and in 
  option of Ignore also following custom exceptions should be nothing.
  
  Now select the process of the protected exe that runs and attach to it.Do not open any other window
  becuase the protected exe may detect Olly.Just the Debugging options and then the attach window.U are
  HeRe:


77F767CE   C3               RETN
77F767CF > CC               INT3
77F767D0   C3               RETN
77F767D1   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+4]
77F767D5   CC               INT3
77F767D6   C2 0400          RETN 4
77F767D9 > 64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
77F767DF   C3               RETN
77F767E0 > 57               PUSH EDI
77F767E1   8B7C24 0C        MOV EDI,DWORD PTR SS:[ESP+C]
77F767E5   8B5424 08        MOV EDX,DWORD PTR SS:[ESP+8]
77F767E9   C702 00000000    MOV DWORD PTR DS:[EDX],0
77F767EF   897A 04          MOV DWORD PTR DS:[EDX+4],EDI
77F767F2   0BFF             OR EDI,EDI
77F767F4   74 11            JE SHORT ntdll.77F76807
77F767F6   83C9 FF          OR ECX,FFFFFFFF
77F767F9   33C0             XOR EAX,EAX
77F767FB   F2:AE            REPNE SCAS BYTE PTR ES:[EDI]



  Now,press the "M" button and set a memory breakpoint on access in .text section.In this section 
  is the original code of the protected exe.Go to your clock and change the hour.Make it one hour
  less or one hour more.This is done,because changing it we stop the loop of the anti-debugging feature
  that the running protected exe has.It seems that this loop checks periodicall the hour-mins-secs and
  then performs the detection of debugger check.Really nasty,I made 2 hours to understand this!Now press 
  F9 to run the process.The debugger pauses at the breakpoint we have just set,HeRe:



0040111B   C8 000000        ENTER 0,0
0040111F   53               PUSH EBX
00401120   56               PUSH ESI
00401121   57               PUSH EDI
00401122   817D 0C 11010000 CMP DWORD PTR SS:[EBP+C],111
00401129   0F84 AB000000    JE sdprotec.004011DA
0040112F   817D 0C 10010000 CMP DWORD PTR SS:[EBP+C],110
00401136   0F84 86000000    JE sdprotec.004011C2
0040113C   837D 0C 10       CMP DWORD PTR SS:[EBP+C],10
00401140   0F84 B5000000    JE sdprotec.004011FB
00401146   B8 00000000      MOV EAX,0


  
  
  If u hadn't check the time of your clock,an exception that could not be handled would occur,and
  after some tracing U would have made (u cann't stay in a place forever!) the exe would have traced
  Olly and exit.If u fail and try again,change accordinglt the time clock every time U try.  

  
  Yes,we are at the unpacked,original code of the exe.But where is the OEP?As U will know,we have
  passed the OEP becuase we run the exe before patched Olly.But,becuase the exe is just appearing
  a screen that asks for a Name and Registration code,it is in a loop,and not far "after" the OEP,
  because no basic routine has been executed,that will make the flow of the programm to go much away
  from the OEP.Now,we are at 0040111B paused.Just check a few lines up.We see this place:



004010C2   6A 00            PUSH 0
004010C4   E8 E1010000      CALL sdprotec.004012AA                   ; JMP to kernel32.GetModuleHandleA
004010C9   A3 F3204000      MOV DWORD PTR DS:[4020F3],EAX
004010CE   C705 C7204000 03>MOV DWORD PTR DS:[4020C7],4003
004010D8   C705 CB204000 89>MOV DWORD PTR DS:[4020CB],sdprotec.00401>
004010E2   C705 CF204000 00>MOV DWORD PTR DS:[4020CF],0
004010EC   C705 D3204000 00>MOV DWORD PTR DS:[4020D3],0
004010F6   A1 F3204000      MOV EAX,DWORD PTR DS:[4020F3]



  Good place for an entry point,because we see a call at GetModuleHandleA (many progs need the
  return value from such an API call,and call it some opcodes after the OEP) and we don't see any
  other API calls of any kind before the opcode at 004010C2 (and generally,after the OEP API calls
  like GetVersion,GetModuleHandleA,LoadLibrary or GetCommandLineA follow.).U may say,why could it
  be not an earlier?Well,I tried and after the IAT rebuilding (that will follow) I couldn't make it
  work.Eventually the OEP is 004010C2.In some other cases,when an exe is compiled with a 
  language compiler eg. C++,at the OEP arefour to five opcodes that are the same for every 
  produced with the same compiler exe.So,after landing at a place of code after the attach and 
  dump,the OEP cannot be far away,since not many opcode sequences like these exist from the place
  we landed,and near it.Anyway,have in mind that it is NOT ALWAYS necessary to land exactly at 
  OEP,in order for the dump to work.We just say that OEP is the most ideal place,becuase all sections
  are intact,nice and clean (and not changed by self-modifications or of modifications that happen during
  runtime,between the exe in memory and other processes,or by itself).Remove the memory breakpoint.


  Before dumping,right click in all section that u see in tha patched Olly (and PEheader as well) and 
  set access->Full access.This is done becuase during unpacking,the protector has protected the 
  access of those memory locations using probably VirtualProtect API's,and if we try to dump the
  dumper will fail,or may create a false dump.Also,ImpreC will not be able to read the process from
  memory,meaning the data that are contained and to be used,in order to fix the Imports (u can check
  it!)  
  
  Now,dump the exe with OllyDump,without having checked the IMport Rebuilding,and as an OEP the
  value of (OEP as seen in addressing)-Imagebase=004010C2-00400000=10C2.We now have the dump.

  
  For the rebuilding part we will do everything without executing anything in Olly,just seing the code,
  because we cannot put any software or hardware breakpoints,the packer detects them all.But don't
  mind,because code at that time is not self-modified any more and the mem locations that we will
  need contain hard-coded bytes,so no need to run it in Olly actually.Just using Olly as 
  a Dasm.OpenImpRec and put as OEP the value 10C2.Now IAT  autosearch and Get Imports.
  Now press Show invalid.We have many invalid.Select one invalid and right click in
  it->Disassemble.What we see here is that in the place where the code of a valid  API 
  should have been,are instructions that generate the call to that API.So the only thing 
  we have to do for all those invalids,is to follow in debugger this codes and
  see where they finally jamp,at API's.We will know that we are for the first time in 
  API's code,because the address of the first opcode will be (and all those who follow and
  are in the API!) in 7XXXXXXX format.Then,just a search in all module names in Olly will reveal
  which API has as starting address this value,and we can identify this API.Then,we will manually
  put the name of that API at the invalid thunk.

  But how are we going to follow all those invlid thunks?We,check the Disassm of one invalid,eg
   the 00143B98h:


00143B98   58               POP EAX
00143B99   50               PUSH EAX
00143B9A   60               PUSHAD
00143B9B   9C               PUSHFD
00143B9C   68 02000000      PUSH 2
00143BA1   50               PUSH EAX
00143BA2   B8 32DEE44B      MOV EAX,4BE4DE32
00143BA7   50               PUSH EAX
00143BA8   B8 2C1CB9C3      MOV EAX,C3B91C2C
00143BAD   50               PUSH EAX
00143BAE   E8 BD5C3200      CALL sdprotec.00469870
00143BB3   9D               POPFD
00143BB4   61               POPAD
00143BB5   B8 2C1CB9C3      MOV EAX,C3B91C2C
00143BBA   9C               PUSHFD
00143BBB   2D 32DEE44B      SUB EAX,4BE4DE32
00143BC0   9D               POPFD
00143BC1   50               PUSH EAX
00143BC2   C3               RETN




  Well,all code till 00143BB5  is junk code,to confuse the reverser.So at 00143BB5 moves a value at
  EAX,then a PUSHFD (junk opcode also,don't care about this) and at 00143BBB subtract 4BE4DE32 and
  in EAX=77D43DFA.Then,the POPFD of junk,and we jamp at 77D43DFA.What API is there?Look in Olly 
  in Search for all module names and this API is TransLateMessage.So,in ImpRec,invalidate the thunk and
  double click on it,and select from user32.dll the TransLateMessage API.Now again show invalid.Good,we
  reduced invalids to one.As u can see,by this way we can fix all the invalid thunks.Do the same thing
  for every invalid then.But there is one thunk that has not a Push eax-retn that jamps at an API.This
  is thunk 468AB3.Well,in Olly go at 468AB3.U see this:


00468AB3   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+4]
00468AB7   85C0             TEST EAX,EAX
00468AB9   7D 1D            JGE SHORT sdprotec.00468AD8
00468ABB   83F8 F5          CMP EAX,-0B
00468ABE   7E 18            JLE SHORT sdprotec.00468AD8
00468AC0   8B4C24 10        MOV ECX,DWORD PTR SS:[ESP+10]
00468AC4   8B5424 0C        MOV EDX,DWORD PTR SS:[ESP+C]
00468AC8   51               PUSH ECX
00468AC9   8B4C24 0C        MOV ECX,DWORD PTR SS:[ESP+C]
00468ACD   52               PUSH EDX
00468ACE   51               PUSH ECX
00468ACF   50               PUSH EAX
00468AD0   E8 A1F7FFFF      CALL sdprotec.00468276
00468AD5   C2 1000          RETN 10
00468AD8   8B5424 10        MOV EDX,DWORD PTR SS:[ESP+10]
00468ADC   8B4C24 0C        MOV ECX,DWORD PTR SS:[ESP+C]
00468AE0   52               PUSH EDX
00468AE1   8B5424 0C        MOV EDX,DWORD PTR SS:[ESP+C]
00468AE5   51               PUSH ECX
00468AE6   52               PUSH EDX
00468AE7   50               PUSH EAX
00468AE8   E8 BD3F0000      CALL sdprotec.0046CAAA
00468AED   C2 1000          RETN 10


  The call at 00468AE8 goes to the API.All till here is junk code.So in Olly go now at 
  0046CAAA and we are HeRe:

0046CAAA   E8 01000000      CALL sdprotec.0046CAB0
0046CAAF   FF58 05          CALL FAR FWORD PTR DS:[EAX+5]            ; Far call
0046CAB2   C9               LEAVE
0046CAB3   0A00             OR AL,BYTE PTR DS:[EAX]
0046CAB5   008B 008038CC    ADD BYTE PTR DS:[EBX+CC388000],CL
0046CABB   74 0A            JE SHORT sdprotec.0046CAC7
0046CABD   50               PUSH EAX
0046CABE   C3               RETN

  
  It jamps at 0046CAB0.We cannot see the opcode of that,because it is mixed.So just go at 
  0046CAB0 and u see that:


0046CAB0   58               POP EAX
0046CAB1   05 C90A0000      ADD EAX,0AC9
0046CAB6   8B00             MOV EAX,DWORD PTR DS:[EAX]
0046CAB8   8038 CC          CMP BYTE PTR DS:[EAX],0CC
0046CABB   74 0A            JE SHORT sdprotec.0046CAC7
0046CABD   50               PUSH EAX
0046CABE   C3               RETN


  Well at 0046CABE jamps at the good API we are looking for.So,at 0046CAB0, EAX=0046CAAF
  because of the call at 0046CAAA (so stack has return address 0046CAAA ),then adds 
  the value of 0AC9 so eax is now 0046D578.Now at [EAX] is the API address to fix this thunk.A
  small trick that seeks for a software breakpoint there and then a PUSH EAX-RETN and we jamp there.

  What is in [EAX]?Well,go at 0046D578 and U see that:


0046D578   76 64            JBE SHORT sdprotec.0046D5DE
0046D57A   D6               SALC
0046D57B  ^77 E3            JA SHORT sdprotec.0046D560
0046D57D   A6               CMPS BYTE PTR DS:[ESI],BYTE PTR ES:[EDI]
0046D57E   D4 77            AAM 77



  We are interested for the first four bytes in reverse,which give us the address of
  77D66476h.I look in Olly,and this is API MessageBoxA.Then,I give this name to out final
  invalid API thunk.Now press show invalid,no invalids.Fix dump now.

  
  Run the fixed exe and...Yeeeaaahh!!!Last version of the so called SDprotector defeated!!!


  Well,this was a hard protector.Took me at least 6 hours,including writing this tutor.This
  is one contribution to all of U,that are really interested to see how other people think,
  including the makers of this packer...

  I can hear a voice in my mind...time to talk to it...


中文:
Sdprotector 1.12增强版手动脱壳教程(作者:KaGra)

目前最新的1.12增强版SDPR可以在www.sdprotector.com下载。

好!很高兴再次与大家交流,这次我们的实验品是Sdprotector.
这次我用另一种方法脱这个壳。下面是详细过程:

工具准备:Olly v1.1, Imprec v1.6f, Ollydump插件,HideOlly插件(不再赘述)

我钩选了SDprotector所有保护选项给程序加壳。它位于“选项”菜单的屏幕左半部分。其它未变。

用Olly装载加壳程序。在“调试”选项中,全部钩选,这样程序的执行就不会被例外所中止(中止后还需按SHIFT+F9或F8,F7才能继续执行)。加壳程序运行后,对话框提示“发现调试器”。我们很难确定引起反调试的保护究竟是什么。
我发现它使用CreateToolHelp32Snapshot函数侦测调试器,最终找到“什么窗口被打开”并把它们与默认的字符串列表(“Olly”也在此列)进行比较。另外SetUnhandledExceptionfiler也能找到调试器,但调用这个函数以后例外是怎样产生的却令我大惑不解。我产生了一个想法:单纯运行加壳程序,再用Olly附加进程。

说干就干。运行加壳程序,对话框显示demo版壳的一些信息(别担心,它是用来保护文件运行的),接着加壳程序进程出现在屏幕上。现在,打开Olly。还没等你附加进程,对话框提示“发现调试器”并关闭了Olly.再次运行加壳程序,打开LordPE对进程Dump,也是如此。我们该怎么办?
我们可以知道反调试并没有调用SetUnhandledExceptionFiler函数或者别的什么例外陷阱,因为函数或例外并没有被调试。唯一的想法就是代码包含了关于进程或窗口句柄的难解的字符串。同时代码还含有一个循环,用来校验包含那些字符串的运行中的进程和窗口句柄。尽管代码是在原程序的代码段中,但壳却赋予了一些特别的代码,即使我们通过了入口点,这些代码仍然存在并进行循环校验。
我的猜想是正确的。我们重命名LordPE->运行加壳文件->运行重命名后的LordPE。重命名后并未被关闭。可是Olly重命名后却仍然被关闭。正如猜想的一样,是由于加壳程序的.data和.edata段中包含了以“Olly”开头的字符串,并在安全循环中被复杂编码(hardcoded)。我们用Olly装载自身,单击”M“按钮查看装载的Olly:
Memory map(内存镜像)
Address    Size       Owner      Section    Contains      Type   Access    Initial   Mapped as

00400000   00001000   OLLYDBG               PE header     Imag   R         RWE
00401000   000AF000   OLLYDBG    .text      code          Imag   R         RWE
004B0000   0005B000   OLLYDBG    .data      data          Imag   R         RWE
0050B000   00001000   OLLYDBG    .tls                     Imag   R         RWE
0050C000   00001000   OLLYDBG    .rdata                   Imag   R         RWE
0050D000   00002000   OLLYDBG    .idata     imports       Imag   R         RWE
0050F000   00002000   OLLYDBG    .edata     exports       Imag   R         RWE
00511000   00036000   OLLYDBG    .rsrc      resources     Imag   R         RWE
00547000   0000C000   OLLYDBG    .reloc     relocations   Imag   R         RWE

在.idata段中模糊查询字符串“Olly”,发现:
0050F780  6F 6C 6C 79 64 62 67 2E 65 78 65 00 5F 41 64 64  ollydbg.exe._Add
0050F790  73 6F 72 74 65 64 64 61 74 61 00 5F 41 64 64 74  sorteddata._Addt
0050F7A0  6F 6C 69 73 74 00 5F 41 6E 61 6C 79 73 65 63 6F  olist._Analyseco

把“Ollydbg.exe“重命名如fffffff.exe,再次查找。我们会发现只有那一个。在.data段再次查找,会发现很多,修改所有的这些字符串。
不钩选“输入表重建”选项,我们用Ollydump插件对进程Dump. 这时Olly已打上补丁。任意命名Dump文件但不能含有”Olly”字符串。再次运行加壳程序直到主窗口出现。现在运行“新”的Olly。。。。。。
未发现Olly!如果你犯错误或者做错一个步骤,Olly将会被发觉,你将不得不全部重打补丁。当程序发现可能的调试器字串或任何可能构成“威胁“的代码时,它会把这些字串或代码存放起来(寄存器,内存,其它),并做上记号。因此补丁要正确,要遵照说明一步步地去做。还有一种一次性Olly补丁,通过替换字符串中的ASCII”FH“来解决反调试的问题。虽然不知道为什么,但是很有效。你也可以如法炮制。
在“新“的Olly“调试器”选项中钩选所有的例外,“忽略”选项中“下列典型例外”为空。
选择并附加加壳程序进程,由于可能会侦测到Olly,请不要打开其它窗口。完成上述步骤后,你会来到这里:
77F767CE   C3               RETN
77F767CF > CC               INT3
77F767D0   C3               RETN
77F767D1   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+4]
77F767D5   CC               INT3
77F767D6   C2 0400          RETN 4
77F767D9 > 64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
77F767DF   C3               RETN
77F767E0 > 57               PUSH EDI
77F767E1   8B7C24 0C        MOV EDI,DWORD PTR SS:[ESP+C]
77F767E5   8B5424 08        MOV EDX,DWORD PTR SS:[ESP+8]
77F767E9   C702 00000000    MOV DWORD PTR DS:[EDX],0
77F767EF   897A 04          MOV DWORD PTR DS:[EDX+4],EDI
77F767F2   0BFF             OR EDI,EDI
77F767F4   74 11            JE SHORT ntdll.77F76807
77F767F6   83C9 FF          OR ECX,FFFFFFFF
77F767F9   33C0             XOR EAX,EAX
77F767FB   F2:AE            REPNE SCAS BYTE PTR ES:[EDI]
现在,按下“M”按钮,在.text段设置“内存访问断点”。此段包含了加壳程序的原代码。接着调整你的时钟,缩短或加快一个小时。这是因为时钟的调整可以阻止加壳程序反调试循环校验的执行。这个循环通过对时-分-秒周期性的校验执行对调试器的侦测。按F9执行程序。调试器条件中断(译注:内存访问断点)在如下地方:
0040111B   C8 000000        ENTER 0,0
0040111F   53               PUSH EBX
00401120   56               PUSH ESI
00401121   57               PUSH EDI
00401122   817D 0C 11010000 CMP DWORD PTR SS:[EBP+C],111
00401129   0F84 AB000000    JE sdprotec.004011DA
0040112F   817D 0C 10010000 CMP DWORD PTR SS:[EBP+C],110
00401136   0F84 86000000    JE sdprotec.004011C2
0040113C   837D 0C 10       CMP DWORD PTR SS:[EBP+C],10
00401140   0F84 B5000000    JE sdprotec.004011FB
00401146   B8 00000000      MOV EAX,0
如果你没有调整时钟,跟踪时会产生一个无法处理的例外,这将导致Olly被侦测到进而被关闭。一旦失败重试,调整时钟就可解决。
好,我们到达了脱壳后的原代码中。可是入口点在哪里呢?其实我们已经通过了入口点,因为“新”的Olly运行前加壳程序已经运行了。但是,由于加壳程序仅仅显示了注册对话框,因此程序是处于入口点“后”不远的循环之中的,可是循环未被完全执行(译注:时钟调整),这使得程序离入口点又远一些。现在,我们暂停在0040111B处。往上找找。如下地方:
004010C2   6A 00            PUSH 0
004010C4   E8 E1010000      CALL sdprotec.004012AA                   ; JMP to kernel32.GetModuleHandleA
004010C9   A3 F3204000      MOV DWORD PTR DS:[4020F3],EAX
004010CE   C705 C7204000 03>MOV DWORD PTR DS:[4020C7],4003
004010D8   C705 CB204000 89>MOV DWORD PTR DS:[4020CB],sdprotec.00401>
004010E2   C705 CF204000 00>MOV DWORD PTR DS:[4020CF],0
004010EC   C705 D3204000 00>MOV DWORD PTR DS:[4020D3],0
004010F6   A1 F3204000      MOV EAX,DWORD PTR DS:[4020F3]
很像入口的地方,因为我们发现了GetModuleHandleA的CALL(许多程序都利用这一CALL返回数值并把它作为入口点后的操作码),另外我们发现在004010C2之前并没有其它的API CALL(一般情况下,入口处的CALL是GetVersion, GetModuleHandleA, LoadLibrary或GetCommandLineA一类的API)。你可能会说:为什么GetModuleHandleA不能提前?我尝试过,可是在我完成输入表重建后(这是后话),我却无法使程序工作。事实上,入口点就是004010C2。因为在有些情况下,使用如C++一类的语言编译器,编译同样的可执行程序,入口点处的4-5个操作码是相同的。因此,附加和Dump后所停留的代码处,由于本身和周围没有过多的操作码序列(译注:GetVersion, GetModuleHandleA, LoadLibrary或GetCommandLineA一类的API),其实离入口点已经不远了。无论怎样,仅仅为了Dump的方便,我们没有必要确切地停在入口点处。我们称入口点是最理想的地方,那是在所有的段都完整、美好、干净的情况下(还有无自校验的修改、在内存和其它进程或自身的运行中无修改发生)。我们删除内存断点。
在Dump之前,右键单击“新”Olly中的所有段(也可用Peheader),设置访问->完整访问。
这样做的目的是因为:脱壳时,壳通过实际防护API保护内存区域的访问,造成Dump失败或错误的Dump文件。Imprec也一样,你无法从内存中读取进程,表明输入表的修正,占用了数据。
现在以004010C2-400000=10C2作为入口点偏移地址,不钩选输入表重建选项,用OllyDump 插件Dump加壳程序。我们取得了Dump后的程序。
我们不使用Olly重建输入表而只是查看一下代码,这是因为壳可以侦测出所有设置的软硬件断点的缘故。但是别担心,因为Dump后的代码不再自校验且我们需要的内存区域不再包含难解字节,因此Olly的使用是多此一举。Olly只作为DASM来使用。我们打开ImpRec,填入入口点偏移量10C2。选择“输入表自动搜索”(IAT autosearch),“获取输入表”(GET Imports)。选择“显示无效指针”(show invalid)。发现许多。选择其中一个右键单击->反汇编。我们可以在此处查看到指向有效API的代码。因此要使指针有效,我们需要调试这些代码并找出它们实际指向哪里的API。第一次进入API代码中,我们会发现首个操作码地址格式是7XXXXXXX(API皆是如此)。以此为依据,我们在Olly的所有模块中查找对应的API。然后补上有效的API指针。
但是我们怎样补上全部有效指针呢?我们可以查看一个无效指针的反汇编代码,例如:
00143B98H处:
00143B98   58               POP EAX
00143B99   50               PUSH EAX
00143B9A   60               PUSHAD
00143B9B   9C               PUSHFD
00143B9C   68 02000000      PUSH 2
00143BA1   50               PUSH EAX
00143BA2   B8 32DEE44B      MOV EAX,4BE4DE32
00143BA7   50               PUSH EAX
00143BA8   B8 2C1CB9C3      MOV EAX,C3B91C2C
00143BAD   50               PUSH EAX
00143BAE   E8 BD5C3200      CALL sdprotec.00469870
00143BB3   9D               POPFD
00143BB4   61               POPAD
00143BB5   B8 2C1CB9C3      MOV EAX,C3B91C2C
00143BBA   9C               PUSHFD  
00143BBB   2D 32DEE44B      SUB EAX,4BE4DE32
00143BC0   9D               POPFD
00143BC1   50               PUSH EAX
00143BC2   C3               RETN

00143BB5之前的代码是用来混淆视听的垃圾代码。00143BB5处,数值放入EAX,接着全部入栈(也是垃圾操作码,别在意),EAX在00143BBB处减去4BE4DE32,这时的值为77D43DFA。全部出栈来到77D43DFA(译注:PUSH EAX数值)。通过Olly搜索知道API原来是TransLateMessage。在ImpRec中双击此指针,从user32.dll中选择TransLateMessage。再次“显示无效指针”。很好,我们搞定了一个。通过这种方式,我们可以修正所有的无效指针。可是,我们仍然发现了一个指针(没有EAX返回API)无法修复。地址在468AB3处,通过Olly查看如下:
00468AB3   8B4424 04        MOV EAX,DWORD PTR SS:[ESP+4]
00468AB7   85C0             TEST EAX,EAX
00468AB9   7D 1D            JGE SHORT sdprotec.00468AD8
00468ABB   83F8 F5          CMP EAX,-0B
00468ABE   7E 18            JLE SHORT sdprotec.00468AD8
00468AC0   8B4C24 10        MOV ECX,DWORD PTR SS:[ESP+10]
00468AC4   8B5424 0C        MOV EDX,DWORD PTR SS:[ESP+C]
00468AC8   51               PUSH ECX
00468AC9   8B4C24 0C        MOV ECX,DWORD PTR SS:[ESP+C]
00468ACD   52               PUSH EDX
00468ACE   51               PUSH ECX
00468ACF   50               PUSH EAX
00468AD0   E8 A1F7FFFF      CALL sdprotec.00468276
00468AD5   C2 1000          RETN 10
00468AD8   8B5424 10        MOV EDX,DWORD PTR SS:[ESP+10]
00468ADC   8B4C24 0C        MOV ECX,DWORD PTR SS:[ESP+C]
00468AE0   52               PUSH EDX
00468AE1   8B5424 0C        MOV EDX,DWORD PTR SS:[ESP+C]
00468AE5   51               PUSH ECX
00468AE6   52               PUSH EDX
00468AE7   50               PUSH EAX
00468AE8   E8 BD3F0000      CALL sdprotec.0046CAAA
00468AED   C2 1000          RETN 10
我们发现00468AE8处的CALL调用了API。之前都是垃圾代码。因此继续查看0046CAAA(译注: 00468AE8   E8 BD3F0000      CALL sdprotec.0046CAAA),如下:
0046CAAA   E8 01000000      CALL sdprotec.0046CAB0
0046CAAF   FF58 05          CALL FAR FWORD PTR DS:[EAX+5]            ; Far call
0046CAB2   C9               LEAVE
0046CAB3   0A00             OR AL,BYTE PTR DS:[EAX]
0046CAB5   008B 008038CC    ADD BYTE PTR DS:[EBX+CC388000],CL
0046CABB   74 0A            JE SHORT sdprotec.0046CAC7
0046CABD   50               PUSH EAX
0046CABE   C3               RETN
我们发现又调用了0046CAB0,这些层次使我们无法查看操作码(译注:就是7XXXXXXX一类的)。因此继续查看,如下:
0046CAB0   58               POP EAX
0046CAB1   05 C90A0000      ADD EAX,0AC9
0046CAB6   8B00             MOV EAX,DWORD PTR DS:[EAX]
0046CAB8   8038 CC          CMP BYTE PTR DS:[EAX],0CC
0046CABB   74 0A            JE SHORT sdprotec.0046CAC7
0046CABD   50               PUSH EAX
0046CABE   C3               RETN
可以看出,0046CABE有我们需要的API(译注:根据上面的EAX返回数值查找API的方法)。由于0046CAAA处的CALL(这时EAX堆栈返回值为0046CAAA),EAX在0046CAB0处的值等于0046CAAF(译注:0046CAAF  CALL FAR FWORD PTR DS:[EAX+5],所以
0046CAB0  POP EAX后EAX=0046CAAA+5=0046AAF)。增加0AC9后(译注:0046CAB1   ADD EAX,0AC9),EAX=0046D578。现在EAX的数值就是修正指针的API地址。此处设置了一个检验软件断点的陷阱(译注:0046CAB8处的比较),接着EAX入栈-返回。我们不研究这些。 
我们研究的是EAX数值数地址里究竟有什么。查看0046D578处如下:
0046D578   76 64            JBE SHORT sdprotec.0046D5DE
0046D57A   D6               SALC  
0046D57B  ^77 E3            JA SHORT sdprotec.0046D560
0046D57D   A6               CMPS BYTE PTR DS:[ESI],BYTE PTR ES:[EDI]
0046D57E   D4 77            AAM 77
注意前4个字节(译注:从0046D578的“76”到0046D57B的“77”)转换后的结果77D66476H。查看Olly后得知是MessageBoxA.我们修正最后一个无效指针。选择“显示无效指针”,无。修正Dump文件。
运行程序成功!!!我们漂亮地完成了称之为最新SDProtector壳的反击战!!
不过这确实是一个“硬”壳,花了我至少6个小时破解+写教程。这篇教程很有教益。
“是时候与大家分享了。。。”