-
Level : intermediate
~~~~~~~~~~~~~~~~~
Unpacking SecuROM 4.xx
~~~~~~~~~~~~~~~~~
My sister's son brought me yesterday one CD with interactive flash stories for kids. He wanted to watch that on my computer. Since loading of CD material in working memory was slow, I burned image with Nero to my hard drive. I thought that it would be much faster than wait while CD finds right files. But image just wouldn't run. I just cursed it and placed original CD back into drive and I left kid to watch those stories. Today after lunch, I was bored and curious so I decided to throw quick look on original CD. I was pleasantly surprised - main executable was packed by SecuROM.
1. Introduction
I say pleasantly because I never before had in my hands original CD protected with SecuROM. I know that this is old version, but what can I do? But I'm also surprised that Nero 6 couldn't burn working image. I thought that Nero can do that?!? I have some other tools like Alcohol 120%, Dameon tools, but I don't have experience with burning these kind of software. I just don't play these new games that comes on CD-DVD (I like emulators, you know; MAME, WinUAE, ZNES, etc.). Ok, let's start :) I'm writing this tutorial while unpacking so it won't go into depth of SecuROM. Hell I even don't know will I finish it! Maybe I will fail to unpack SecuROM :) I hope not. Let's go!
The CD name is CROTALES and it contains Croatian tales from known writer (in Croatia) Ivana Brlic-Mazuranic. I doubt that readers of this tutorial will find this CD somewhere, but protector was used allot in games from 2-3 years ago (2003-2004). I think that current version of SecuROM is 7. CD contains .swf files and one executable file START.EXE. That file is nothing more than macromedia flash player protected with SecuROM. Cracking this CD is not hard, we can play .swf files in any browser. But we are interested in SecuROM.
First information I got from PEID 0.94:
SecuROM 4.x.x.x - 5.x.x.x -> Sony DADC [Overlay]
If you are interested in exact version, you can use Yates SecuROM/SafeDisc version detector (for older versions):
SecuROM - 4.84.00 0001
Or you could manually search for version information. With some hex editor, search for "AddD" string in file:
0017A000 41 64 64 44 03 00 00 00 34 2E 38 34 2E 30 30 00 AddD...4.84.00.
0017A010 30 30 30 31 A2 20 19 27 C8 27 A3 2C F2 2D 1E 3D 0001˘.'Č'Ł,ň-=
How image looks in memory? Let's see:
Memory map
Address Size Owner Section Contains Type Access Initial Mapped as
00400000 00001000 START PE header Imag R RWE
00401000 00097000 START .text code Imag R RWE
00498000 0000B000 START .rdata code Imag R RWE
004A3000 000D0000 START .data code Imag R RWE
00573000 00001000 START .data1 data Imag R RWE
00574000 00001000 START .CRT Imag R RWE
00575000 00031000 START .cms_t SFX Imag R RWE
005A6000 0007E000 START .cms_d Imag R RWE
00624000 00003000 START .idata imports Imag R RWE
00627000 00016000 START .rsrc resources Imag R RWE
Obviously protector's sections are .data1, .CRT, .cms_t and .idata. EP of protector code looks like common MSVC++ 6.0 OEP:
0059C490 > 55 PUSH EBP
0059C491 8BEC MOV EBP,ESP
0059C493 6A FF PUSH -1
0059C495 68 48A26000 PUSH START.0060A248
0059C49A 68 24C25900 PUSH START.0059C224
0059C49F 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
0059C4A5 50 PUSH EAX
0059C4A6 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
0059C4AD 83EC 58 SUB ESP,58
0059C4B0 53 PUSH EBX
0059C4B1 56 PUSH ESI
0059C4B2 57 PUSH EDI ; ntdll.7C910738
0059C4B3 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
0059C4B6 FF15 EC476200 CALL DWORD PTR DS:[<&kernel32.GetVersion>; kernel32.GetVersion
0059C4BC 33D2 XOR EDX,EDX ; ntdll.KiFastSystemCallRet
0059C4BE 8AD4 MOV DL,AH
0059C4C0 8915 10766100 MOV DWORD PTR DS:[617610],EDX ; ntdll.KiFastSystemCallRet
0059C4C6 8BC8 MOV ECX,EAX
0059C4C8 81E1 FF000000 AND ECX,0FF
0059C4CE 890D 0C766100 MOV DWORD PTR DS:[61760C],ECX
0059C4D4 C1E1 08 SHL ECX,8
0059C4D7 03CA ADD ECX,EDX ; ntdll.KiFastSystemCallRet
0059C4D9 890D 08766100 MOV DWORD PTR DS:[617608],ECX
0059C4DF C1E8 10 SHR EAX,10
0059C4E2 A3 04766100 MOV DWORD PTR DS:[617604],EAX
0059C4E7 33F6 XOR ESI,ESI
0059C4E9 56 PUSH ESI
0059C4EA E8 E5120000 CALL START.0059D7D4
...
...
If we take a look in the first section (where it should be original EP) we can see that section is not encrypted. SecuROM protection consist from CD check, OEP code encryption and import protection. It is not hard to fool SecuROM so it thinks that CD is inside, but that is useless. SecuROM needs some information stored on CD that will decrypt OEP code! And that information cannot be read and copied to another CD, or to hard drive (there my Nero 6 failed). To crack SecuROM we need original CD. So I pushed back CD into drive.
2. Finding OEP and decrypting
This is very easy thing to do if we have original CD. These versions of SecuROM are CD copy protections. They doesn't have anti-debug tricks so decrypting OEP code and breaking there is a joke. To quick find OEP, I placed bp on GetDriveTypeA. After stopping, it resumed in new loaded DLL (DLL extracted in temp folder). I was here:
2000110D 66:3D 0500 CMP AX,5
20001111 75 0F JNZ SHORT sintfnt.20001122
20001113 8B4C24 28 MOV ECX,DWORD PTR SS:[ESP+28] ; START.0061A520
20001117 8B4424 24 MOV EAX,DWORD PTR SS:[ESP+24] ; START.006215D0
2000111B 8919 MOV DWORD PTR DS:[ECX],EBX
2000111D 8B08 MOV ECX,DWORD PTR DS:[EAX]
2000111F 41 INC ECX
20001120 8908 MOV DWORD PTR DS:[EAX],ECX
20001122 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+10]
20001126 D1E9 SHR ECX,1
20001128 4B DEC EBX
20001129 894C24 10 MOV DWORD PTR SS:[ESP+10],ECX
2000112D ^79 8F JNS SHORT sintfnt.200010BE
2000112F 8B5424 24 MOV EDX,DWORD PTR SS:[ESP+24] ; START.006215D0
20001133 33C0 XOR EAX,EAX
20001135 5F POP EDI ; START.00403000
20001136 5E POP ESI ; START.00403000
20001137 8B0A MOV ECX,DWORD PTR DS:[EDX]
20001139 5D POP EBP ; START.00403000
2000113A 85C9 TEST ECX,ECX
2000113C 0F9FC0 SETG AL
2000113F 5B POP EBX ; START.00403000
20001140 83C4 10 ADD ESP,10
20001143 C2 0800 RETN 8
After RETN 8 I was here, back in START.EXE:
00582994 85C0 TEST EAX,EAX
00582996 75 3B JNZ SHORT START.005829D3
00582998 FF15 F4A76100 CALL DWORD PTR DS:[61A7F4] ; sintfnt.FGDM32
0058299E 833D C4C35C00 00 CMP DWORD PTR DS:[5CC3C4],0
005829A5 74 1E JE SHORT START.005829C5
005829A7 68 94216000 PUSH START.00602194 ; ASCII "70015A56C550882901D183BCD7CDD288E72EC775B8161D9AB F56"
005829AC 68 C0F26100 PUSH START.0061F2C0
005829B1 E8 7F800100 CALL START.0059AA35
005829B6 83C4 08 ADD ESP,8
005829B9 68 C0F26100 PUSH START.0061F2C0
005829BE 6A 20 PUSH 20
005829C0 E8 EB3DFFFF CALL START.005767B0
005829C5 6A 01 PUSH 1
005829C7 E8 342DFFFF CALL START.00575700
005829CC 6A 01 PUSH 1
005829CE E8 4B7C0100 CALL START.0059A61E
005829D3 833D C4C35C00 00 CMP DWORD PTR DS:[5CC3C4],0
005829DA 74 2E JE SHORT START.00582A0A
This version of SecuROM has easy to spot OEP jump. Scrolling down couple pages I sow it:
00582EB6 B8 A0704600 MOV EAX,START.004670A0
00582EBB 90 NOP
00582EBC 90 NOP
00582EBD 50 PUSH EAX
00582EBE EB 08 JMP SHORT START.00582EC8
00582EC0 58 POP EAX
00582EC1 A1 38946100 MOV EAX,DWORD PTR DS:[619438]
00582EC6 -FFE0 JMP EAX ; Can it be more obvious !?! :)
00582EC8 A3 38946100 MOV DWORD PTR DS:[619438],EAX
00582ECD A3 40C96100 MOV DWORD PTR DS:[61C940],EAX
00582ED2 833D CC746100 00 CMP DWORD PTR DS:[6174CC],0
00582ED9 74 19 JE SHORT START.00582EF4
00582EDB A1 6EBE6100 MOV EAX,DWORD PTR DS:[61BE6E]
00582EE0 83E0 02 AND EAX,2
00582EE3 85C0 TEST EAX,EAX
00582EE5 74 0D JE SHORT START.00582EF4
OEP of this file looks pretty weird:
004670A0 . 83EC 44 SUB ESP,44
004670A3 . 56 PUSH ESI ; START.00404120
004670A4 . FF15 609E5A00 CALL DWORD PTR DS:[5A9E60] ; START.0057D010
004670AA . 8BF0 MOV ESI,EAX ; START.004670A0
004670AC . 8A06 MOV AL,BYTE PTR DS:[ESI]
004670AE . 3C 22 CMP AL,22
004670B0 . 75 1C JNZ SHORT START.004670CE
004670B2 . 8A46 01 MOV AL,BYTE PTR DS:[ESI+1]
004670B5 . 46 INC ESI ; START.00404120
004670B6 . 3C 22 CMP AL,22
004670B8 . 74 0C JE SHORT START.004670C6
004670BA > 84C0 TEST AL,AL
004670BC . 74 08 JE SHORT START.004670C6
004670BE . 8A46 01 MOV AL,BYTE PTR DS:[ESI+1]
004670C1 . 46 INC ESI ; START.00404120
004670C2 . 3C 22 CMP AL,22
004670C4 .^75 F4 JNZ SHORT START.004670BA
004670C6 > 803E 22 CMP BYTE PTR DS:[ESI],22
004670C9 . 75 0F JNZ SHORT START.004670DA
004670CB . 46 INC ESI ; START.00404120
004670CC . EB 0C JMP SHORT START.004670DA
004670CE > 3C 20 CMP AL,20
004670D0 . 7E 08 JLE SHORT START.004670DA
004670D2 > 8A46 01 MOV AL,BYTE PTR DS:[ESI+1]
004670D5 . 46 INC ESI ; START.00404120
004670D6 . 3C 20 CMP AL,20
004670D8 .^7F F8 JG SHORT START.004670D2
004670DA > 8A06 MOV AL,BYTE PTR DS:[ESI]
004670DC . 84C0 TEST AL,AL
004670DE . 74 0C JE SHORT START.004670EC
004670E0 > 3C 20 CMP AL,20
004670E2 . 7F 08 JG SHORT START.004670EC
004670E4 . 8A46 01 MOV AL,BYTE PTR DS:[ESI+1]
004670E7 . 46 INC ESI ; START.00404120
004670E8 . 84C0 TEST AL,AL
004670EA .^75 F4 JNZ SHORT START.004670E0
004670EC > 8D4424 04 LEA EAX,DWORD PTR SS:[ESP+4]
004670F0 . C74424 30 0000>MOV DWORD PTR SS:[ESP+30],0
004670F8 . 50 PUSH EAX ; START.004670A0
004670F9 . FF15 609E5A00 CALL DWORD PTR DS:[5A9E60] ; START.0057D010
004670FF . F64424 30 01 TEST BYTE PTR SS:[ESP+30],1
00467104 . 74 0B JE SHORT START.00467111
00467106 . 8B4424 34 MOV EAX,DWORD PTR SS:[ESP+34]
0046710A . 25 FFFF0000 AND EAX,0FFFF
0046710F . EB 05 JMP SHORT START.00467116
00467111 > B8 0A000000 MOV EAX,0A
3. Imports
More than half of imports in main executable now leads to same address. For example, first import that will be triggered:
004670A4 . FF15 609E5A00 CALL DWORD PTR DS:[5A9E60] ; START.0057D010
Protection used is just custom function that calculates what import should be triggered from a specified offset. It seams that it doesn't need original CD to be in drive for this. I didn't go in details. After tracing into that call, I scrolled down couple pages again and found one (of two) JMP EAX that jumps to correct import:
0057D010 55 PUSH EBP
0057D011 8BEC MOV EBP,ESP
0057D013 81EC B4000000 SUB ESP,0B4
0057D019 53 PUSH EBX
0057D01A 56 PUSH ESI ; START.00404120
0057D01B 57 PUSH EDI ; START.00403000
0057D01C 8945 D4 MOV DWORD PTR SS:[EBP-2C],EAX ; START.004670A0
0057D01F 895D A4 MOV DWORD PTR SS:[EBP-5C],EBX
0057D022 894D 8C MOV DWORD PTR SS:[EBP-74],ECX
0057D025 8955 EC MOV DWORD PTR SS:[EBP-14],EDX ; ntdll.KiFastSystemCallRet
0057D028 8975 84 MOV DWORD PTR SS:[EBP-7C],ESI ; START.00404120
0057D02B 897D E8 MOV DWORD PTR SS:[EBP-18],EDI ; START.00403000
0057D02E 896D F4 MOV DWORD PTR SS:[EBP-C],EBP
0057D031 8965 C4 MOV DWORD PTR SS:[EBP-3C],ESP
0057D034 8B45 04 MOV EAX,DWORD PTR SS:[EBP+4] ; START.0059C55E
0057D037 8945 A8 MOV DWORD PTR SS:[EBP-58],EAX ; START.004670A0
0057D03A 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8] ; START.00400000
0057D03D 8945 90 MOV DWORD PTR SS:[EBP-70],EAX ; START.004670A0
0057D040 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
0057D043 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX ; START.004670A0
0057D046 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10]
0057D049 8945 DC MOV DWORD PTR SS:[EBP-24],EAX ; START.004670A0
0057D04C 8B45 14 MOV EAX,DWORD PTR SS:[EBP+14]
0057D04F 8945 B4 MOV DWORD PTR SS:[EBP-4C],EAX ; START.004670A0
0057D052 8B45 18 MOV EAX,DWORD PTR SS:[EBP+18] ; ntdll.7C910738
0057D055 8945 D0 MOV DWORD PTR SS:[EBP-30],EAX ; START.004670A0
0057D058 833D 2C736100 00 CMP DWORD PTR DS:[61732C],0
0057D05F 75 09 JNZ SHORT START.0057D06A
0057D061 833D 30736100 00 CMP DWORD PTR DS:[617330],0
0057D068 74 18 JE SHORT START.0057D082
0057D06A A1 28736100 MOV EAX,DWORD PTR DS:[617328]
0057D06F 83E8 01 SUB EAX,1
0057D072 A3 28736100 MOV DWORD PTR DS:[617328],EAX ; START.004670A0
0057D077 833D 28736100 00 CMP DWORD PTR DS:[617328],0
0057D07E 7F 02 JG SHORT START.0057D082
0057D080 -FFE0 JMP EAX ; START.004670A0
0057D082 8B45 04 MOV EAX,DWORD PTR SS:[EBP+4] ; START.0059C55E
0057D085 8945 C8 MOV DWORD PTR SS:[EBP-38],EAX ; START.004670A0
0057D088 60 PUSHAD
0057D089 68 00AD6100 PUSH START.0061AD00
0057D08E FF15 D0D66100 CALL DWORD PTR DS:[61D6D0] ; ntdll.RtlEnterCriticalSection
0057D094 C745 A0 00000000 MOV DWORD PTR SS:[EBP-60],0
0057D09B EB 09 JMP SHORT START.0057D0A6
...
scroooooooolling down
...
0057DD08 50 PUSH EAX ; START.004670A0
0057DD09 8B4D D8 MOV ECX,DWORD PTR SS:[EBP-28] ; ntdll.7C910738
0057DD0C 51 PUSH ECX
0057DD0D E8 7EF2FFFF CALL START.0057CF90
0057DD12 61 POPAD
0057DD13 8B45 CC MOV EAX,DWORD PTR SS:[EBP-34] ; ntdll.7C97E4C0
0057DD16 8BF0 MOV ESI,EAX ; START.004670A0
0057DD18 8B06 MOV EAX,DWORD PTR DS:[ESI]
0057DD1A 5F POP EDI ; 0012FFB0
0057DD1B 5E POP ESI ; 0012FFB0
0057DD1C 5B POP EBX ; 0012FFB0
0057DD1D 8BE5 MOV ESP,EBP
0057DD1F 5D POP EBP ; 0012FFB0
0057DD20 -FFE0 JMP EAX ; Jump to import!!!
0057DD22 6A 00 PUSH 0
...
I'm not sure, but maybe there is some timer check inside. My target crushed on some exception after pausing olly, but if I just run it within Olly (after reaching OEP) it runs fine.
Imports that are not redirected point to original IAT section:
00497DB0 $-FF25 10844900 JMP DWORD PTR DS:[498410] ; comdlg32.GetOpenFileNameA
We can see that IAT is filled with good imports:
00498000 F0 6B DD 77 83 78 DD 77 1B 76 DD 77 49 6F DE 77 .k.w.x.w.v.wIo.w
00498010 BB D5 DF 77 E7 EB DD 77 00 00 00 00 B9 45 F4 77 ...w...w.....E.w
00498020 E4 68 F1 77 D5 5F F1 77 4B 4C F2 77 84 48 F4 77 .h.w._.wKL.w.H.w
00498030 7C F7 F1 77 F7 47 F4 77 CB 4C F2 77 00 65 F2 77 |..w.G.w.L.w.e.w
...
cut
...
00498410 1E 31 3B 76 D8 7C 3C 76 CD 46 3D 76 00 00 00 00 .1;v.|<v.F=v....
00498420 D3 85 52 77 C0 41 4F 77 09 60 52 77 E0 D1 52 77 ..Rw.AOw.`Rw..Rw
00498430 68 20 4F 77 4C 20 4F 77 00 00 00 00 h OwL Ow....
And that ordinals and names are not destroyed:
004A0F30 A4 0F 0A 00 00 00 00 00 57 53 4F 43 4B 33 32 2E ........WSOCK32.
004A0F40 64 6C 6C 00 0F 00 43 6F 43 72 65 61 74 65 49 6E dll...CoCreateIn
004A0F50 73 74 61 6E 63 65 00 00 31 00 43 6F 49 6E 69 74 stance..1.CoInit
004A0F60 69 61 6C 69 7A 65 00 00 59 00 43 6F 55 6E 69 6E ialize..Y.CoUnin
004A0F70 69 74 69 61 6C 69 7A 65 00 00 1B 00 43 6F 46 72 itialize....CoFr
004A0F80 65 65 55 6E 75 73 65 64 4C 69 62 72 61 72 69 65 eeUnusedLibrarie
...
cut
...
004A2030 00 00 9D 00 77 61 76 65 49 6E 43 6C 6F 73 65 00 ....waveInClose.
004A2040 AB 00 77 61 76 65 49 6E 55 6E 70 72 65 70 61 72 ..waveInUnprepar
004A2050 65 48 65 61 64 65 72 00 A8 00 77 61 76 65 49 6E eHeader...waveIn
004A2060 52 65 73 65 74 00 57 49 4E 4D 4D 2E 64 6C 6C 00 Reset.WINMM.dll.
Probably IMAGE_IMPORT_DESCRYPTORS are also untouched. That is very sloppy work by SecuROM because it makes easier IAT rebuilding. I wrote script that finds all calls to import algo , places EIP value there, runs, stops on JMP EAX, grabs import , search for that import in IAT, and then it change CALL destination to that import in IAT. But SecuROM maybe has some trick because target was crushing multiple times so I had to rebuild imports part-by-part. But After I have done with restoring calls, I dumped file to disk, then I used ImpREC to create new IAT. We could even repair IAT without ImpREC, but that would take more time. SecuROM uses WriteProcessMemory to write imports in IAT.
4. Fixing dump
After dumping and using ImpREC I double clicked on dumped file. Macromedia player window opened, but it just stud there without running stories. That is no wonder since macromedia files have overlay. Overlay is some extra data attached at the end of file. That data is not loaded in memory because that data is not registered in PE header as a part of last section. Overlay is usual thing with macromedia files. Since ImpREC created new last section, macromedia player cannot find overlay data (which is now before last section). To fix this problem we need just to attach overlay back at the end of file. How to do it? You calculate size of that overlay, then you use hex editor to copy and paste it at the end of file. Or you can just attach whole original file back to the end of dumped one. After I fixed overlay problem, I double clicked on dump and it worked just fine:) And wow, do you know how faster it runs without SecuROM layer ?!? Much faster because SecuROM must unpack temp files, load couple dlls, pass protection layer, pass that import algo, but searching and reading special data on CD is the slowest part.
5. The End
Btw, SecuROM has created couple DLL's in temp file. They are packed with PEtite except 16-bit file which is packed with PKLITE:
CmdLineExt02.dll
SIntf16.dll
SIntf32.dll
SIntfNT.dll
In some versions this files can be found in windowssystem32 folder. Don't know purpose of these files (I didn't waste time on them).
And that was my first SecuROM unpacking. It was not hard , I guess that newer versions are harder. The most interesting part of SecuROM should be that "unreadable" data on disc, but I needed to return CD so I just find the quickest way to remove protection.
-
Level : intermediate
~~~~~~~~~~~~~~~~~~~
Bustme1 solution by haggar
~~~~~~~~~~~~~~~~~~~
The goal of this crackme is to unpack it and then dig a serial for some name while keygen is optional. Solution consit from two parts - unpacking and reversing serial. Tools that I used are OllyDbg 1.10, ImpREC, LordPE, hex editor.
[1] UNPACKING
Crackme is packed with custom protector. Protector is based on two-process protection (self debugging) which makes it more dificult for unpacking with Ring3 debuggers such as Olly. Protected file has some bad values in PE header (Number Of Rva And Sizes, for example) which Olly cannot dygest. We cannot change those values because crackme becomes corupted. But that doesn't prevent debugging in olly. However, if you would like to have nice analysis, try to find OllyAdvanced plugin that fixes these bugs in Olly.
Protector has few anti-debug tricks. It uses IsDebuggerPresent to detect debugger and BlockInput to block keyboard and mouse. Those are well known tricks (check Yoda Protectors tutorials on BIW reversing for more info about these tricks). But main protection is self-debugging.
Protector debugs it's own process to prevent debugging from Ring3 debuggers. Detaching processes will not give solution because import accessing requires both processes. Unpacking can be separated in two parts; decrypting and import repairing.
- Decrypting and dumping -
Since this is a self-debugging based protection, processes must know which one is debugger and which one is debuggee. When we start process by double-click, that process must check is he a debuggee. There are warious techniques to accomplish this; command line arguments, mutex, etc. But this protector will use very simple solution. It will check does some string exist in it, if it does - that process is debuggee. If not, then it will start a new process and write that string in it. Second process will perform same check. For writing is used WriteProcessMemory that writes next string to 400002 (in PE header) of "son" process:
####INVIUS_L0RD_EXECUTABLE
So to avoid self-debugging, we just have to write this string in our process loaded in Olly. However, altough there will be no double processes, crackme will create debugg loop and try to debug something. Debugging loop is in separate thread while main one enters in infinite jump EB FE. Why it that happening , I didn't tried to find out. All sections of crackme are decrypted except import section at 406000. That is enough for dumping. Dump is OK, it just needs fixing some PE info (sections sizes needs to be rounded on 1000 alignment). OEP is easy to find since we have MingWin compiled executable. OEP is at 401220.
- Import repairing -
This is little harder thing to sove. For that I had to examne protector's debug loop. Secondary process, a debuggee, has redirected all import jumps to allocated block. For example, if we detach processes and follow first import call at 40122D:
0040122D FF15 5C614000 CALL DWORD PTR DS:[40615C]
at the call destination (I removed junk jumps from pasted code):
009B0268 50 PUSH EAX
009B0269 52 PUSH EDX
009B026A B8 860E9EEC MOV EAX,EC9E0E86 ; IMPORT_ID value.
009B026F BA 875A3E00 MOV EDX,3E5A87 ; Address of IMPORT_ALGO
009B0293 C3 RETN
EAX holds IMPORT_ID value which is maybe some hash. Then it jumps to IMPORT_ALGO where some small calculations are performed and then HLT opcode is triggered that couses exception C0000096 (PRIVILEGED INSTRUCTION):
003E5AD5 F4 HLT ; Privileged command
That exception is caugth by debugger, ei. first process which now takes controll. If we examne debug loop of first process, we will see how this exception is handled:
003E4C5D 813D 494D3E00 96>CMP DWORD PTR DS:[3E4D49],C0000096 ; Our exception code.
003E4C67 74 31 JE SHORT 003E4C9A
003E4C69 813D 494D3E00 04>CMP DWORD PTR DS:[3E4D49],80000004
003E4C73 74 58 JE SHORT 003E4CCD
003E4C75 813D 494D3E00 03>CMP DWORD PTR DS:[3E4D49],80000003
003E4C7F 74 05 JE SHORT 003E4C86
003E4C81 ^E9 DAFDFFFF JMP 003E4A60
Protector then retrieves resgisters state of debugged process (via GetThreadContext) and reads some data from debuggee (ReadProcessMemory). With that data it decrypts apropriate strings (API ordinals and DLL names) in 406000 section, loads libraries, retrieves API address and uses SetThreadContext as a jump directly to API. Then it continues debug event.
To retrieve imports, I wrote small script that plays with thread context buffer. After couple minutes and manually work, I placed all pointers in dumped file, then I loaded needed libraries in Olly (kernel32.dll, user32.dll, msvcrt.dll) and used ImpREC to rebuild IAT. Then I noticed that I still has some problems.
- Runtime decryption-encryption -
Protector has runtime decryption-encryption. For example, this part will decrypt 185 bytes starting from 00401C2D address:
00401C08 $ 55 PUSH EBP
00401C09 . 89E5 MOV EBP,ESP
00401C0B . 83EC 78 SUB ESP,78
00401C0E > 50 PUSH EAX
00401C0F . 68 85010000 PUSH 185 ; Number of bytes to decrypt.
00401C14 . 68 2D1C4000 PUSH bustme1.00401C2D ; Starting address.
00401C19 . B8 00009B00 MOV EAX,9B0000 : Block where decryptor loop is placed.
00401C1E . FFD0 CALL EAX
00401C20 . 58 POP EAX
There are about 20~30 this paterns. They doesn't have to be decrypted. We can place those decryptor loops in dump and change MOV EAX,009B0000 to new address , MOV EAX,0040xxxx. And dump will run fine. But it is better to decrypt all those parts and NOP all those opcodes. After decrypting, I removed last two sections and now I have nice and clean dump.
[2] REVERSING KEYCHECK
Keycheck is not hard , but I'm totally rusty. Check is performed in separate thread. Also, crackme goes to Sleep for a 500 miliseconds every little. Don't know why, maybe just to annoy us. Keycheck consist from two main parts.
- First part -
00401A7C |. 83F8 11 |CMP EAX,11 ; Serial must be 11h bytes long.
00401A7F |. 75 25 |JNZ SHORT dump.00401AA6
00401A81 |. C70424 9050400>|MOV DWORD PTR SS:[ESP],dump.00405090
00401A88 |. E8 71F9FFFF |CALL dump.004013FE
00401A8D |. 83F8 03 |CMP EAX,3 ; Name must have min 4 chars.
00401A90 |. 76 14 |JBE SHORT dump.00401AA6
00401A92 |. 803D 14514000 >|CMP BYTE PTR DS:[405114],2D ; Serial format is XXXX-XXX-XXXXXXXX
00401A99 |. 75 0B |JNZ SHORT dump.00401AA6
00401A9B |. 803D 18514000 >|CMP BYTE PTR DS:[405118],2D
00401AA2 |. 75 02 |JNZ SHORT dump.00401AA6
00401AA4 |. EB 14 |JMP SHORT dump.00401ABA
Crackme will then take midlle part of serial (those 3 chars between "-") and INC/DEC them to create new three byte string "S1,S2,S3":
S1 = 6._char + 2
S2 = 7._char + 1
S3 = 8._char - 2
Then it creates 100h byte array and fills it with values 00,01,02,03,04,....,FD,FE,FF. This array is then changed with our new three byte string values. One string is taken:
00BCFF8C 62 75 73 74 20 6D 65 20 23 31 20 2D 20 62 70 78 bust me #1 - bpx
00BCFF9C 20 32 6B 36 FF EE CA 00 2k6....
And it's hashed:
00BCFF8C EC B7 72 DD B9 5A E9 90 B9 10 EA 3E D2 AD 60 28 ..r..Z.....>..`(
00BCFF9C D2 F6 8D 23 D5 B2 CF 00 ...#....
That hash is compared with hardcoded one:
0040407F 94 D8 BE 56 20 7F AA 07 74 B3 EC D4 96 11 35 84 ...V ...t.....5.
0040408F 11 CF 40 25 8F 59 15 00 ..@%.Y..
If they match - first check is passed. This check depends only on 3 bytes from our serial, and it is actually hardcode part same for every name. I tought that it was too big job to rip all that code, and all in order to find out 3 bytes from serial. So I patched algo to loop untill it finds right 3 bytes that give correct hash. Three bytes that gives correct hash are
9G7
which gives new serial shape
XXXX-9G7-XXXXXXXX
- Second part -
Second part of serial is harder to catch and this time I needed some brain usage. I'll start from end. We will see GoodBoy message if this loop gets EAX=3 as incoming parameter. In case of EAX=1, serial is bad, other values are possible bugs in crackme (judging by author comments):
004012C3 |. 837D FC 01 CMP DWORD PTR SS:[EBP-4],1 ; BAD BOY!
004012C7 |. 74 08 JE SHORT dump.004012D1
004012C9 |. 837D FC 03 CMP DWORD PTR SS:[EBP-4],3 ; GOOD BOY!
004012CD |. 74 2C JE SHORT dump.004012FB
004012CF |. EB 54 JMP SHORT dump.00401325
004012D1 |> C74424 0C 1000>MOV DWORD PTR SS:[ESP+C],10 ; |
004012D9 |. C74424 08 0040>MOV DWORD PTR SS:[ESP+8],dump.00404000 ; |ASCII "Error"
004012E1 |. C74424 04 0840>MOV DWORD PTR SS:[ESP+4],dump.00404008 ; |ASCII "Bad serial, you better fix this."
004012E9 |. A1 14504000 MOV EAX,DWORD PTR DS:[405014] ; |
004012EE |. 890424 MOV DWORD PTR SS:[ESP],EAX ; |
004012F1 |. E8 7A170000 CALL <JMP.&user32.MessageBoxA> ; MessageBoxA
004012F6 |. 83EC 10 SUB ESP,10
004012F9 |. EB 5E JMP SHORT dump.00401359
004012FB |> C74424 0C 4000>MOV DWORD PTR SS:[ESP+C],40 ; |
00401303 |. C74424 08 2940>MOV DWORD PTR SS:[ESP+8],dump.00404029 ; |ASCII "Not an Error"
0040130B |. C74424 04 3840>MOV DWORD PTR SS:[ESP+4],dump.00404038 ; |ASCII "Good serial, pat yourself on the back."
00401313 |. A1 14504000 MOV EAX,DWORD PTR DS:[405014] ; |
00401318 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; |
0040131B |. E8 50170000 CALL <JMP.&user32.MessageBoxA> ; MessageBoxA
00401320 |. 83EC 10 SUB ESP,10
00401323 |. EB 34 JMP SHORT dump.00401359
00401325 |> C74424 0C 1000>MOV DWORD PTR SS:[ESP+C],10 ; |
0040132D |. C74424 08 5F40>MOV DWORD PTR SS:[ESP+8],dump.0040405F ; |ASCII "Quitting"
00401335 |. C74424 04 6840>MOV DWORD PTR SS:[ESP+4],dump.00404068 ; |ASCII "Shit, somthin's broken"
0040133D |. A1 14504000 MOV EAX,DWORD PTR DS:[405014] ; |
00401342 |. 890424 MOV DWORD PTR SS:[ESP],EAX ; |
00401345 |. E8 26170000 CALL <JMP.&user32.MessageBoxA> ; MessageBoxA
0040134A |. 83EC 10 SUB ESP,10
0040134D |. C70424 0100000>MOV DWORD PTR SS:[ESP],1 ; |
00401354 |. E8 B7160000 CALL <JMP.&msvcrt.exit> ; exit
Let's see where EAX=3 is given:
00401B8B |. E8 18FBFFFF |CALL dump.004016A8
00401B90 |. 8945 D4 |MOV DWORD PTR SS:[EBP-2C],EAX
00401B93 |. E8 66FDFFFF |CALL dump.004018FE ; This proc must return EAX=0.
00401B98 |. 0105 10504000 |ADD DWORD PTR DS:[405010],EAX
00401B9E |. 833D 10504000 >|CMP DWORD PTR DS:[405010],0 ; [00405010] must be 0 to pass check.
00401BA5 |. 74 14 |JE SHORT dump.00401BBB ; GoodBoy jump is executed in that case.
00401BA7 |. C74424 04 0A00>|MOV DWORD PTR SS:[ESP+4],0A
00401BAF |. C70424 0100000>|MOV DWORD PTR SS:[ESP],1
00401BB6 |. E8 D5F6FFFF |CALL dump.00401290
00401BBB |> 833D 10504000 >|CMP DWORD PTR DS:[405010],0
00401BC2 |. 75 16 |JNZ SHORT dump.00401BDA
00401BC4 |. C74424 04 0000>|MOV DWORD PTR SS:[ESP+4],0
00401BCC |. C70424 0300000>|MOV DWORD PTR SS:[ESP],3 ; EAX=3 will be passed here.
00401BD3 |. E8 B8F6FFFF |CALL dump.00401290
As it can be seen, EAX=3 if [00405010]=0, and that will be if CALL at 00401B93 returns EAX=0. So we must start from the end of that procedure. I will not paste code, but just reversed pseudo code:
EAX = 0 <---- LAST CONDITION !
0 = 69696969 SUB ((reg XOR 37) XOR 37) <-> reg=69696969
70000390 = reg SUB 06969A27 <-> reg=70000390
0000A954 = reg IMUL reg <-> reg=0000A954
0000A955 = reg - 1 <-> reg=0000A955
0000A955 = _lrotr reg , 1 <-> reg=000152AA
000152AA = reg XOR 0B33F <-> reg=0001E195
0001E195 = _lrotl reg , 3 <-> reg=A0003C32
That was reversing from the end. _lrotl is ROL and reg represent DWORD value in register format (in memory bytes are swapped). Before that _lrotr is used on dword given from name hash and last part of serial. Procedure for name hash is at 004016A8. Procedure for serial hash is at 0040195C and it is not actually a hash. It is just characters formated to hex value (egxample "12345678" -> 12345678h). Those two hashes must give A0003C32 value to _lrorl.
[78563412] shash1
[78563412] shash2
[00003B1F] nhash
AND shash1,FFFF0000 -> [00003412]
MOV EDX,shash1 -> 12340000
XOR nhash,EDX -> [00000F0D]
AND shash2,0000FFFF -> [78560000]
mov EDX,shash2 -> 00005678
ADD nhash,EDX -> [78560F0D]
[78560F0D] -> 0D0F5678 -> This value must be A0003C32 to pass last check.
And here is solution: We can take name hash as constant (nhash) and we need to find serial hash that will give needed DWORD. Since serial hash is nothing but last part of serial formated as hex DWORD, we can reverse it easily.
0D0F(5678) -> Last four serial chars from false serial -> "5678"
A000(3C32) -> True last four chars -> "3C32"
XOR 1F3B0000,12340000 = 0D0F0000
but it must be
XOR 1F3B0000,xxxxxxxx = A0000000
which gives
BF3B0000
and serial for my name is
1234-9G7-BF3B3C32
For the end I need to mention that name hash depends on firs four characters from serial too. I didn't botheredd to reverse that part too because keygen is optional, but it is not hard and generic keygen is easy to code.
[3] THE END
-
حالا به سراغ یکی از پروتکتورهای قدرتمند به اسم SDProtector 1.12 می رویم !
-
شما برای کرک این پروتکتور به اسکریپت های زیر احتیاج دارید :
اولین اسکریپت کاملا مشخصه کارش چیه عنوانش را پر رنگتر کردم :
/*
-------------------------------------------------------------
SDProtector 1.12 - generic script for cleaning imports
-------------------------------------------------------------
*/
var x
var y
var addr
var block
var fixed
mov fixed,0
ask "Enter base address of block where IAT is redirected:"
cmp $RESULT,0
je done8
mov block,$RESULT
log "================================================= ======"
log " "
log " SDProtector 1.12 - script for fixing imports (haggar)"
log " "
log "================================================= ======"
log " "
log "Numbers of fixed patherns:"
log " "
mov addr,block
pattern1:
find addr,#5850609C680100000050B8????????50B8????????50 E8????????9D61740E750CFF35#
cmp $RESULT,0
je done1
mov addr,$RESULT
add $RESULT,11
mov x,[$RESULT]
sub x,addr
sub x,5
fill addr,1,E9
add addr,1
mov [addr],x
add addr,4
fill addr,2D,90
inc fixed
jmp pattern1
done1:
log "type 1:"
log fixed
mov addr,block
mov fixed,0
pattern2:
find addr,#5850609C680400000050B8????????50B8????????50 E8????????9D61B8#
cmp $RESULT,0
je done2
mov addr,$RESULT
add $RESULT,1E
mov x,[$RESULT]
add $RESULT,6
mov y,[$RESULT]
xor x,y
sub x,addr
sub x,5
fill addr,1,E9
add addr,1
mov [addr],x
add addr,4
fill addr,25,90
inc fixed
jmp pattern2
done2:
log "type 2:"
log fixed
mov addr,block
mov fixed,0
pattern3:
find addr,#5850609C680200000050B8????????50B8????????50 E8????????9D#
cmp $RESULT,0
je done3
mov addr,$RESULT
add $RESULT,1E
mov x,[$RESULT]
add $RESULT,6
mov y,[$RESULT]
sub x,y
sub x,addr
sub x,5
fill addr,1,E9
add addr,1
mov [addr],x
add addr,4
fill addr,25,90
inc fixed
jmp pattern3
done3:
log "type 3:"
log fixed
mov addr,block
mov fixed,0
pattern4:
find addr,#5850609C680300000050B8????????50B8????????50 E8????????9D#
cmp $RESULT,0
je done4
mov addr,$RESULT
add $RESULT,1E
mov x,[$RESULT]
add $RESULT,6
mov y,[$RESULT]
add x,y
sub x,addr
sub x,5
fill addr,1,E9
add addr,1
mov [addr],x
add addr,4
fill addr,25,90
inc fixed
jmp pattern4
done4:
log "type 4:"
log fixed
mov addr,block
mov fixed,0
pattern5:
find addr,#B8????????9C35????????9D50C3#
cmp $RESULT,0
je done5
mov addr,$RESULT
fill $RESULT,1,0E9
add $RESULT,1
mov x,[$RESULT]
add $RESULT,6
mov y,[$RESULT]
xor x,y
sub $RESULT,6
sub x,$RESULT
sub x,4
mov [$RESULT],x
inc fixed
jmp pattern5
done5:
log "type 5:"
log fixed
mov addr,block
mov fixed,0
pattern6:
find addr,#B8????????9C05????????9DFFE0#
cmp $RESULT,0
je done6
mov addr,$RESULT
fill $RESULT,1,0E9
add $RESULT,1
mov x,[$RESULT]
add $RESULT,6
mov y,[$RESULT]
add x,y
sub $RESULT,6
sub x,$RESULT
sub x,4
mov [$RESULT],x
inc fixed
jmp pattern6
done6:
log "type 6:"
log fixed
mov addr,block
mov fixed,0
pattern7:
find addr,#740E750CFF35????????FF20F6094100B8????????FF E0#
cmp $RESULT,0
je done7
mov addr,$RESULT
fill $RESULT,10,90
add $RESULT,10
fill $RESULT,1,0E9
add $RESULT,1
mov x,[$RESULT]
sub x,$RESULT
sub x,4
mov [$RESULT],x
inc fixed
jmp pattern7
done7:
log "type 7:"
log fixed
mov addr,block
mov fixed,0
pattern8:
find addr,#B8????????9C2D????????9D50C3#
cmp $RESULT,0
je done8
mov addr,$RESULT
fill $RESULT,1,0E9
add $RESULT,1
mov x,[$RESULT]
add $RESULT,6
mov y,[$RESULT]
sub x,y
sub x,addr
sub x,5
add addr,1
mov [addr],x
inc fixed
jmp pattern8
done8:
log "type 8:"
log fixed
log " "
log "Now use ImpREC to repair dumped file."
log " "
log " - THE END -"
ret
-
دومین اسکریپت :
//SDPR 1.12 Pro - redirecting decryption calls
var oep
var addr
var pointer
var type
mov oep,eip
mov addr,401000
LABEL_01:
find addr,#E8??????FFEB11#
cmp $RESULT,0
je END_01
mov addr,$RESULT
add addr,1
mov pointer,addr
mov pointer,[pointer]
add pointer,addr
add pointer,4
mov type,pointer
add type,1
mov type,[type]
add type,pointer
add type,5
cmp type,518738
je FIX_01
cmp type,5185E1
je FIX_01
cmp type,5188D6
je FIX_01
cmp type,5187F4
je FIX_01
jmp LABEL_01
FIX_01:
sub type,addr
sub type,4
mov [addr],type
jmp LABEL_01
END_01:
mov addr,401000
LABEL_02:
find addr,#E8??????FFEB11#
cmp $RESULT,0
je END_02
mov addr,$RESULT
add addr,1
mov pointer,addr
mov pointer,[pointer]
add pointer,addr
add pointer,4
mov type,pointer
add type,1
mov type,[type]
add type,pointer
add type,5
cmp type,14622E
je FIX_02
cmp type,146233
je FIX_02
cmp type,146238
je FIX_02
cmp type,14623D
je FIX_02
cmp type,146242
je FIX_02
cmp type,146247
je FIX_02
jmp LABEL_02
FIX_02:
mov pointer,type
add pointer,1
mov pointer,[pointer]
add pointer,type
add pointer,5
sub pointer,addr
sub pointer,4
mov [addr],pointer
jmp LABEL_02
END_02:
ret
-
سومین اسکریپت :
//SDP 1.12 - runtime decryption
var oep
var addr
var pointer
var type
mov oep,eip
mov addr,401000
LABEL_01: //Decrypt.
find addr,#E8??????FFEB11#
cmp $RESULT,0
je END_01
mov addr,$RESULT
add addr,1
mov pointer,addr
mov pointer,[pointer]
add pointer,addr
add pointer,4
mov type,pointer
add type,1
mov type,[type]
add type,pointer
add type,5
cmp type,518738
jne LABEL_01
sub addr,1
mov eip,addr
add addr,5
bp addr
esto
bc eip
sub addr,5
mov [addr],#909090909090909090909090909090909090909090909090 #
jmp LABEL_01
END_01:
mov addr,401000
LABEL_02: //Patch encryptor.
find addr,#E8??????FFEB11#
cmp $RESULT,0
je END_02
mov addr,$RESULT
add addr,1
mov pointer,addr
mov pointer,[pointer]
add pointer,addr
add pointer,4
mov type,pointer
add type,1
mov type,[type]
add type,pointer
add type,5
cmp type,5185E1
jne LABEL_02
sub addr,1
mov [addr],#909090909090909090909090909090909090909090909090 #
jmp LABEL_02
END_02:
mov eip,oep
ret
-
اسکریپت آخر :
/*
---------------------------------------------------------
SDProtector Pro 1.12 - OEP script for tutorial (haggar)
---------------------------------------------------------
Script just finds stolen code.
---------------------------------------------------------
*/
var SDP_base
var GetVersion
var CreateFile
var IsDebugger
var Snapshot
var SystemInfo
var Unhandled
var GetTick
var ModHandle
mov SDP_base,eip
gpa "GetVersion","kernel32.dll"
mov GetVersion,$RESULT
findop GetVersion,#c3#
mov GetVersion,$RESULT
gpa "CreateFileA","kernel32.dll"
mov CreateFile,$RESULT
findop CreateFile,#C21C00#
mov CreateFile,$RESULT
gpa "IsDebuggerPresent","kernel32.dll"
mov IsDebugger,$RESULT
findop IsDebugger,#c3#
mov IsDebugger,$RESULT
gpa "CreateToolhelp32Snapshot","kernel32.dll"
mov Snapshot,$RESULT
findop Snapshot,#c20800#
mov Snapshot,$RESULT
gpa "GetSystemInfo","kernel32.dll"
mov SystemInfo,$RESULT
findop SystemInfo,#C20400#
mov SystemInfo,$RESULT
gpa "UnhandledExceptionFilter","kernel32.dll"
find $RESULT,#50FF15????????85C00f8C#
mov Unhandled,$RESULT
gpa "GetTickCount","kernel32.dll"
mov GetTick,$RESULT
findop GetTick,#C3#
mov GetTick,$RESULT
gpa "GetModuleHandleA","kernel32.dll"
mov ModHandle,$RESULT
findop ModHandle,#c20400#
mov ModHandle,$RESULT
//-------- To the ZwQueryInformationProcess,custom IsDebuggerPresent -------
bp GetVersion
esto
esto
esto
esto
mov eax,80000001
bc eip
//------------------------- Temporary file check --------------------------
bp CreateFile
esto
bc eip
sti
find eip,#837C241C0C7376E8#
bp $RESULT
esto
bc eip
add eip,7d
mov $RESULT,eip
add $RESULT,32
bp $RESULT
esto
bc eip
mov edi,1234
//--------------------- IsDebuggerPresent check ------------------------
bp IsDebugger
esto
bc eip
mov eax,0
//--------------------- CreateToolhelp32Snapshot ------------------------
bp Snapshot
esto
bc eip
mov eax,0
//---------------------- Kill Monitoring Threads ------------------------
bp SystemInfo
esto
bc eip
sti
mov $RESULT,esp
add $RESULT,24
mov [$RESULT],0
sti
sti
mov [$RESULT],1
//------------------- UnhandledExceptionFilter trick --------------------
bp Unhandled
esto
mov eax,0
bc eip
//--------------------- CreateFileA for drivers check -------------------
bp CreateFile
esto //First time it opens itself.
esto
bc eip
sti
add eip,0cc
//----------------- GetTickCount initialization stuff -------------------
bp GetTick
esto
mov eax,3
mov edx,2
mov esi,1
esto
mov eax,3
mov edx,2
mov esi,1
sti
sti
sti
sti
sti
sti
sti
sti
sti
sti
sti
mov eax,211
esto
mov eax,3
mov edx,2
mov esi,1
bc eip
//----------------- Registration, nag window , reg keys -------------------
bp GetVersion
esto
esto
bc eip
rtr
sti
rtr
//----------------------- Find stolen OEP code -----------------------------
bp GetTick
esto
esto
esto
esto
bc eip
bp ModHandle
esto
esto
bc eip
sti
find SDP_base,#58054BFFFFFF8038E90F85????FFFFC600E89D61 FFE0#
add $RESULT,14
bp $RESULT
esto
bc eip
ret
ret
بعد از این اسکریپت نحوه استفاده از این اسکریپت ها و آموزش کرک را می گذارم .
-
نحوه کرک و آموزش اسکریپت ها :
SDProtector Pro 1.12 - anti debug reference
SDProtector is nice protector with tons of anti-debug tricks. It is very hard to run protected target within debugger. This tutorial will try to show most of those tricks. Target is SDProtector Pro 1.12 itself. Tools that are used in this tutorial are usuall unpacking tools (PEiD, olly, ImpREC, LordPE, hex editor) but also, this tutorial requires that you have Windows XP operating system.
1. EFLAG trick
Imidiatley after you load target in olly, by scrolling down you will see first anti-debug trick. It is EFLAG trick. Protector pushes all flags into stack. There it tries to change T flag in stack:
0050A06A 33C0 XOR EAX,EAX
0050A06C 64:FF30 PUSH DWORD PTR FS:[EAX]
0050A06F 64:8920 MOV DWORD PTR FS:[EAX],ESP <---- Installs local SEH.
0050A072 9C PUSHFD <------------------------ Pushes flags to stack.
0050A073 804C24 01 01 OR BYTE PTR SS:[ESP+1],1 <------ Tries to change flags. That couse exception.
0050A078 9D POPFD
0050A079 90 NOP
0050A07A 90 NOP
0050A07B C3 RETN
It will try to change T flag. Now, that generates exception and protector local SEH (Structured Exception Handler) deals with that exception. Check stack and you will see where is local handler:
0012FFB8 00000246
0012FFBC /0012FFE0 Pointer to next SEH record
0012FFC0 |0050A02F SE handler
0012FFC4 |7C816D4F RETURN to kernel32.7C816D4F
0012FFC8 |7C910738 ntdll.7C910738
0012FFCC |FFFFFFFF
0012FFD0 |7FFD5000
0012FFD4 |8054B038
0012FFD8 |0012FFC8
0012FFDC |82C47BF0
0012FFE0 \FFFFFFFF End of SEH chain
Handler is at 0050A02F. Place bp there and run:
0050A02F E8 01000000 CALL SDProtec.0050A035 <------------ Start of handler.
0050A034 90 NOP <------------------------------- I NOP-ed this junk byte to see better.
0050A035 58 POP EAX <--------------------------- CALL-POP is used to get current address.
0050A036 05 53000000 ADD EAX,53 <------------------------ EAX will point to SEH uninstaller.
0050A03B 51 PUSH ECX
0050A03C 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+10]
0050A040 8981 B8000000 MOV DWORD PTR DS:[ECX+B8],EAX
0050A046 B8 55010000 MOV EAX,155
0050A04B 8941 20 MOV DWORD PTR DS:[ECX+20],EAX
0050A04E 33C0 XOR EAX,EAX
0050A050 8941 04 MOV DWORD PTR DS:[ECX+4],EAX
0050A053 8941 08 MOV DWORD PTR DS:[ECX+8],EAX
0050A056 8941 0C MOV DWORD PTR DS:[ECX+C],EAX
0050A059 8941 10 MOV DWORD PTR DS:[ECX+10],EAX
0050A05C 59 POP ECX
0050A05D C3 RETN <------------------------------ Throws us to ntdll.dll.
SEH will handle exception , then return to ntdll.dll, and ntdll.dll will throw us at return address:
0050A087 64:8F00 POP DWORD PTR FS:[EAX]
0050A08A 58 POP EAX
This trick is to prevent tracing opcode-by-opcode. If somebody don't understand what is happening, he will trace trugh that exception. Since T flag will be changed in stack and then poped back by POPFD, T=1 will couse target to crush. If we change back T=0 after POPFD, it will not help because program flow goes in wrong way and again, target crushes. So we need to let protector local SEH to handle it.
2. SEH chain
I don't know is this a trick, but SDP messes little with SEH's:
0050A5F3 50 PUSH EAX
0050A5F4 83EC 08 SUB ESP,8
0050A5F7 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
0050A5FD 64:FF35 00000000 PUSH DWORD PTR FS:[0]
0050A604 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
0050A60B 83C4 08 ADD ESP,8
0050A60E 50 PUSH EAX
0050A60F 64:FF35 00000000 PUSH DWORD PTR FS:[0]
0050A616 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
0050A61D 64:8F05 00000000 POP DWORD PTR FS:[0]
0050A624 64:A3 00000000 MOV DWORD PTR FS:[0],EAX
0050A62A 83C4 08 ADD ESP,8
0050A62D 58 POP EAX
0050A62E B8 00000000 MOV EAX,0
0050A633 50 PUSH EAX
0050A634 E8 01000000 CALL SDProtec.0050A63A
I sad that I don't know purpose of this because it does not harm to our debugging. Olly has some problems in case of recursive SEH chains. That means, if application installs more than one local (nested inside other) SEH, olly complains. It can desturb our debugging in some cases , as I noticed in some other protectors.
3. INT3 and RDTSC trick
Eventually we will came to next trick. Protector installs local SEH and then couse INT3 exception:
0050F7E1 64:FF30 PUSH DWORD PTR FS:[EAX]
0050F7E4 64:8920 MOV DWORD PTR FS:[EAX],ESP <-------- Installs SEH.
0050F7E7 BD D3F10E30 MOV EBP,300EF1D3
0050F7EC 81C5 78563412 ADD EBP,12345678
0050F7F2 66:B8 1700 MOV AX,17
0050F7F6 66:83E8 13 SUB AX,13
0050F7FA CC INT3 <------------------------------ Make exception.
0050F7FB 90 NOP
0050F7FC 3C 04 CMP AL,4
0050F7FE 74 32 JE SHORT SDProtec.0050F832
0050F800 74 03 JE SHORT SDProtec.0050F805
0050F802 75 01 JNZ SHORT SDProtec.0050F805
In stack we see where handler is and we go there:
0050F894 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
0050F898 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
0050F89C FF81 B8000000 INC DWORD PTR DS:[ECX+B8]
0050F8A2 8B00 MOV EAX,DWORD PTR DS:[EAX]
0050F8A4 2D 03000080 SUB EAX,80000003 <------------------ It checks is it INT3 exception.
0050F8A9 75 0E JNZ SHORT SDProtec.0050F8B9 <------- If not, just crush.
0050F8AB 33C0 XOR EAX,EAX
0050F8AD 8941 04 MOV DWORD PTR DS:[ECX+4],EAX
0050F8B0 8941 08 MOV DWORD PTR DS:[ECX+8],EAX
0050F8B3 8941 0C MOV DWORD PTR DS:[ECX+C],EAX
0050F8B6 8941 10 MOV DWORD PTR DS:[ECX+10],EAX
0050F8B9 C3 RETN <------------------------------ Jump to ntdll.dll.
Ntdll.dll returns us back to code. Here is whole snippet, junk is NOP-ed. Read comments:
0050F7FA CC INT3
0050F7FB 90 NOP <------------------------------- It returns here!
0050F7FC 3C 04 CMP AL,4 <-------------------------- If EAX=4, then OK (exception was handled).
0050F7FE 74 32 JE SHORT SDProtec.0050F832 <-------- Good jump.
0050F800 74 03 JE SHORT SDProtec.0050F805 <-------- If EAX<>4, we are detected.
0050F802 75 01 JNZ SHORT SDProtec.0050F805
0050F804 90 NOP
0050F805 E8 01000000 CALL SDProtec.0050F80B
0050F80A FF58 05 CALL FAR FWORD PTR DS:[EAX+5]
0050F80D 27 DAA
0050F80E 0000 ADD BYTE PTR DS:[EAX],AL
0050F810 008B D083C232 ADD BYTE PTR DS:[EBX+32C283D0],CL
0050F816 E8 9D060000 CALL SDProtec.0050FEB8
0050F81B EB 01 JMP SHORT SDProtec.0050F81E
0050F81D FFE8 JMP FAR EAX
0050F81F 0100 ADD DWORD PTR DS:[EAX],EAX
0050F821 0000 ADD BYTE PTR DS:[EAX],AL
0050F823 FF58 05 CALL FAR FWORD PTR DS:[EAX+5]
0050F826 0E PUSH CS
0050F827 0000 ADD BYTE PTR DS:[EAX],AL
0050F829 0050 E9 ADD BYTE PTR DS:[EAX-17],DL
0050F82C 3F AAS
0050F82D 3F AAS
0050F82E 0000 ADD BYTE PTR DS:[EAX],AL
0050F830 FFE8 JMP FAR EAX
0050F832 64:8F05 00000000 POP DWORD PTR FS:[0] <-------------- Good jump throw us here. Uninstall SEH.
0050F839 83C4 04 ADD ESP,4
0050F83C 0F31 RDTSC <----------------------------- Timer value is given to EAX:EDX.
0050F83E 8BC8 MOV ECX,EAX <----------------------- Then saved to EXC:EBX.
0050F840 8BDA MOV EBX,EDX
0050F842 7E 06 JLE SHORT SDProtec.0050F84A
0050F844 7F 04 JG SHORT SDProtec.0050F84A
0050F846 0010 ADD BYTE PTR DS:[EAX],DL
0050F848 40 INC EAX
0050F849 90 NOP
0050F84A 50 PUSH EAX
0050F84B 9C PUSHFD
0050F84C 51 PUSH ECX
0050F84D 33C9 XOR ECX,ECX
0050F84F 67:E3 05 JCXZ SHORT SDProtec.0050F857
0050F852 0010 ADD BYTE PTR DS:[EAX],DL
0050F854 40 INC EAX
0050F855 00E8 ADD AL,CH
0050F857 59 POP ECX
0050F858 E8 04000000 CALL SDProtec.0050F861
0050F85D 90 NOP
0050F85E 90 NOP
0050F85F 90 NOP
0050F860 90 NOP
0050F861 58 POP EAX
0050F862 EB 02 JMP SHORT SDProtec.0050F866
0050F864 90 NOP
0050F865 90 NOP
0050F866 83C0 02 ADD EAX,2
0050F869 EB 01 JMP SHORT SDProtec.0050F86C
0050F86B 90 NOP
0050F86C 50 PUSH EAX
0050F86D C3 RETN
0050F86E 90 NOP
0050F86F 9D POPFD
0050F870 58 POP EAX
0050F871 0F31 RDTSC <----------------------------- Tracing brings us here! New timer value.
0050F873 2BC1 SUB EAX,ECX <----------------------- Here it substract old timer value and new.
0050F875 1BD3 SBB EDX,EBX
0050F877 83FA 00 CMP EDX,0 <------------------------- Check EDX difference.
0050F87A ^75 84 JNZ SHORT SDProtec.0050F800 <------- If it exist, we are detected.
0050F87C 3D 00000060 CMP EAX,60000000 <------------------ Again, check difference in EAX.
0050F881 ^0F87 79FFFFFF JA SDProtec.0050F800 <-------------- Bad jump again.
0050F887 74 31 JE SHORT SDProtec.0050F8BA <-------- These are good jumps.
0050F889 75 2F JNZ SHORT SDProtec.0050F8BA
And that was first type of INT3-RDTSC check. This check will occure couple times. Comments describe what happens , so we know what to do. Let SEH handles exception and be carefull about RDTSC bad jumps. Here INT3 actually didn't affect RDTSC timing, but there is couple variations of INT3-RDTSC checks.
4. Checking breakpoints on imports
It checks breakpoints on imports:
00519A8C 58 POP EAX
00519A8D 05 A4040000 ADD EAX,4A4
00519A92 8B00 MOV EAX,DWORD PTR DS:[EAX]
00519A94 8038 CC CMP BYTE PTR DS:[EAX],0CC <------- Check first byte on API.
00519A97 74 0A JE SHORT SDProtec.00519AA3 <------ Bad jump!
00519A99 50 PUSH EAX ; kernel32.LoadLibraryA
00519A9A C3 RETN <---------------------------- Jump to import if OK.
Often used and almost all API's are checked like this.
5. Custom GetProcAddress function
SDProtector has custom GetProcAddress function to retrieve API's:
00513788 53 PUSH EBX
00513789 55 PUSH EBP
0051378A 8B6C24 10 MOV EBP,DWORD PTR SS:[ESP+10]
0051378E 56 PUSH ESI
0051378F 57 PUSH EDI ; ntdll.7C910738
00513790 8B7C24 14 MOV EDI,DWORD PTR SS:[ESP+14]
...
...[cut]...
...
00513829 77 07 JA SHORT SDProtec.00513832
0051382B 55 PUSH EBP
0051382C 57 PUSH EDI ; ntdll.7C910738
0051382D E8 C4610000 CALL SDProtec.005199F6
00513832 5F POP EDI ; SDProtec.00516D80
00513833 5E POP ESI ; SDProtec.00516D80
00513834 5D POP EBP ; SDProtec.00516D80
00513835 5B POP EBX ; SDProtec.00516D80
00513836 C2 0800 RETN 8
6. Integrity checks , detecting breakpoints in code
SDProtector has couple integrity checks. These check everything and they can detect breakpoints:
00513AFE > 83EC 68 SUB ESP,68
00513B01 53 PUSH EBX
00513B02 56 PUSH ESI
00513B03 57 PUSH EDI
00513B04 E8 B5B8FFFF CALL SDProtec.0050F3BE
00513B09 6A 00 PUSH 0
00513B0B 8BF8 MOV EDI,EAX
00513B0D E8 C05E0000 CALL <SDProtec.API_BP_CHECK_2>
00513B12 50 PUSH EAX
00513B13 E8 9DC6FFFF CALL SDProtec.005101B5
00513B18 E8 A1B8FFFF CALL SDProtec.0050F3BE
00513B1D 8BF0 MOV ESI,EAX
00513B1F 81EE 00010000 SUB ESI,100
00513B25 E8 B5C0FFFF CALL SDProtec.0050FBDF
00513B2A 8BD8 MOV EBX,EAX
00513B2C 8D4424 1C LEA EAX,DWORD PTR SS:[ESP+1C]
00513B30 50 PUSH EAX
00513B31 E8 B6C7FFFF CALL <SDProtec.INIT_CONST_1>
00513B36 68 00010000 PUSH 100
00513B3B 8D4C24 24 LEA ECX,DWORD PTR SS:[ESP+24]
00513B3F 56 PUSH ESI
00513B40 51 PUSH ECX
00513B41 E8 CEC7FFFF CALL SDProtec.00510314
00513B46 81C3 B8FBFFFF ADD EBX,-448
00513B4C 81C6 48040000 ADD ESI,448
00513B52 53 PUSH EBX
00513B53 8D5424 30 LEA EDX,DWORD PTR SS:[ESP+30]
00513B57 56 PUSH ESI
00513B58 52 PUSH EDX
00513B59 E8 B6C7FFFF CALL SDProtec.00510314
00513B5E 8D4424 38 LEA EAX,DWORD PTR SS:[ESP+38]
00513B62 8D4C24 28 LEA ECX,DWORD PTR SS:[ESP+28]
00513B66 50 PUSH EAX
00513B67 51 PUSH ECX
00513B68 E8 4BC8FFFF CALL SDProtec.005103B8
00513B6D 83C4 24 ADD ESP,24
00513B70 83C7 60 ADD EDI,60
00513B73 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00513B77 6A 10 PUSH 10
00513B79 57 PUSH EDI
00513B7A 52 PUSH EDX
00513B7B E8 62D0FFFF CALL SDProtec.00510BE2
00513B80 5F POP EDI
00513B81 5E POP ESI
00513B82 85C0 TEST EAX,EAX <----------------- EAX must be 0.
00513B84 5B POP EBX
00513B85 74 0A JE SHORT SDProtec.00513B91 <--- Good jump.
00513B87 E8 43B8FFFF CALL SDProtec.0050F3CF
00513B8C E8 DEFBFFFF CALL SDProtec.0051376F
00513B91 B8 01000000 MOV EAX,1
00513B96 83C4 68 ADD ESP,68
00513B99 C3 RETN
If we search for all references on first opcode in snippet, we will see that this part is called couple times at least.
Now, here is very annoying and good trick against Olly. Check what Olly (in Olly help file) says about tracing:
Use hardware breakpoints to step or trace code
If you step or trace executable code, OllyDbg attempts to use single-step trap. In some cases, for example, if you asked to step over the call to subroutine, or execute string command at once, or run your program till selection, OllyDbg must set temporary breakpoint. Normally it uses command INT3. When OS supports hardware breakpoints, this option is active and there is at least one free hardware breakpoint, OllyDbg sets temporary hardware breakpoint. As inter-process memory access is very time-consuming, hardware breakpoints may accelerate run trace by as much as 20% (trace over). Notice that hardware breakpoints are non-destructive, i.e. don't change the contents of the memory. This feature was proposed by kab.
Olly , when tracing over calls, sets temporary breakpoint (INT3=CC) after the call. So SDProtector's checksum algorithm detects our tracing even we tought that we didn't set breakpoint. Simply, that temporary breakpoint will corrupt checksum. Lucky for us, Olly gives good solution for this problem because instead of placing INT3, Olly can use hardware breakpoint.
You can set this option in:
Olly menu --> "Options" --> "Debugging options" --> "Debug" --> "Use hardware breakpoints to step or trace code"
An we will not be detected.
But maybe now we have side afect. Since SDProtector uses exceptions to detect hardware breakpoint, maybe this will be the problem. Another solution is, that you label checksum procedure, then when you see that you are entering in it, you just place hw breakpoint on good jump. Then you remove that hw bp.
7. Three interesting API's
After retrieving couple API's that SDP needs for work, it will then call GetVersion to retrieve some system information. SDP will actually check version of our OS to see if it's supports three API's. If check gives positive result, it will then collect those API's. Those three API's are:
ZwQueryInformationProcess
ZwQuerySystemInformation
ZwSetInformationThread
Those are very powerfull API's. We will see later how SDP will use them. Below is snippet of that code. Read comments:
00517670 8B8424 4C040000 MOV EAX,DWORD PTR SS:[ESP+44C] <-------- Interesting check.
00517677 85C0 TEST EAX,EAX <-------------------------- Some test.
00517679 0F85 8B000000 JNZ SDProtec.0051770A <----------------- Skip these APIs.
0051767F E8 7A210000 CALL <SDProtec.IMPORT_GetVersion> <----- GetVersion.
00517684 3D 00000080 CMP EAX,80000000
00517689 73 7F JNB SHORT SDProtec.0051770A <----------- Skip these APIs.
0051768B 85DB TEST EBX,EBX
0051768D 74 7B JE SHORT SDProtec.0051770A <------------ Skip these APIs.
0051768F 68 0CDD4000 PUSH SDProtec.0040DD0C
00517694 E8 0C7DFFFF CALL <SDProtec.API-DLL_STRING>
00517699 50 PUSH EAX
0051769A 68 6CB34000 PUSH SDProtec.0040B36C
0051769F E8 017DFFFF CALL <SDProtec.API-DLL_STRING>
005176A4 50 PUSH EAX
005176A5 E8 DC230000 CALL <SDProtec.API_BP_CHECK_1>
005176AA 50 PUSH EAX
005176AB E8 D8C0FFFF CALL <SDProtec.GET_PROC_ADDR_1>
005176B0 85C0 TEST EAX,EAX
005176B2 74 05 JE SHORT SDProtec.005176B9
005176B4 8903 MOV DWORD PTR DS:[EBX],EAX
005176B6 83C3 04 ADD EBX,4
005176B9 68 26DD4000 PUSH SDProtec.0040DD26
005176BE E8 E27CFFFF CALL <SDProtec.API-DLL_STRING>
005176C3 50 PUSH EAX
005176C4 68 6CB34000 PUSH SDProtec.0040B36C
005176C9 E8 D77CFFFF CALL <SDProtec.API-DLL_STRING>
005176CE 50 PUSH EAX
005176CF E8 B2230000 CALL <SDProtec.API_BP_CHECK_1>
005176D4 50 PUSH EAX
005176D5 E8 AEC0FFFF CALL <SDProtec.GET_PROC_ADDR_1>
005176DA 85C0 TEST EAX,EAX
005176DC 74 05 JE SHORT SDProtec.005176E3
005176DE 8903 MOV DWORD PTR DS:[EBX],EAX
005176E0 83C3 04 ADD EBX,4
005176E3 68 1BC24000 PUSH SDProtec.0040C21B
005176E8 E8 B87CFFFF CALL <SDProtec.API-DLL_STRING>
005176ED 50 PUSH EAX
005176EE 68 6CB34000 PUSH SDProtec.0040B36C
005176F3 E8 AD7CFFFF CALL <SDProtec.API-DLL_STRING>
005176F8 50 PUSH EAX
005176F9 E8 88230000 CALL <SDProtec.API_BP_CHECK_1>
005176FE 50 PUSH EAX
005176FF E8 84C0FFFF CALL <SDProtec.GET_PROC_ADDR_1>
00517704 85C0 TEST EAX,EAX
00517706 74 02 JE SHORT SDProtec.0051770A
00517708 8903 MOV DWORD PTR DS:[EBX],EAX
0051770A 5F POP EDI ; ntdll.7C910738
0051770B 5E POP ESI ; ntdll.7C910738
0051770C 5D POP EBP ; ntdll.7C910738
0051770D B8 01000000 MOV EAX,1
00517712 5B POP EBX ; ntdll.7C910738
00517713 81C4 38040000 ADD ESP,438
00517719 C2 0400 RETN 4
As you can see, there are three checks and , in case that one fails, these API's will not be retrieved. This means that we can avoid that part of protection by just skipping this whole procedure.
8. SMC trick (trick with Self Modifying Code)
This is very interesting trick against tracing trough code. Check this code:
0050A807 F3:66:A5 REP MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI] <---- I'm here!
0050A80A E8 45000000 CALL SDProtec.0050A854
0050A80F E8 79000000 CALL SDProtec.0050A88D
0050A814 E8 00104000 CALL 0090B819
ECX=00000003 (decimal 3.)
DS:[ESI]=[0050A81D]=01EB
ES:[EDI]=[0050A807]=66F3
Check carefull and you will see that this line of code modyfies itself. When you trace with F7 , it changes opcode to:
0050A807 EB 01 JMP SHORT SDProtec.0050A80A <---------------------- Same line!
0050A809 A5 MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
0050A80A E8 45000000 CALL SDProtec.0050A854 <--------------------------- Then you jump here!
0050A80F E8 79000000 CALL SDProtec.0050A88D
0050A814 E8 00104000 CALL 0090B819
It will change itself to jump. And that new jump leads to "Debugger detected" message! But if we place new origin at 50A809 line, it will again change that line to:
0050A807 EB 01 JMP SHORT SDProtec.0050A80A
0050A809 90 NOP
0050A80A EB 03 JMP SHORT SDProtec.0050A80F <------------------ Here! New code.
0050A80C 90 NOP
0050A80D 0000 ADD BYTE PTR DS:[EAX],AL
0050A80F E8 79000000 CALL SDProtec.0050A88D <----------------------- And jumps here then!
0050A814 E8 00104000 CALL 0090B819
It will change same line to jump. But this jump jumps at second call , and that call is good one! Conclusion is, that if we trace with F7, we will stuck. But if we place bp on second call and run, we will pass this trick.
It is very interesting. I'm wonder how guy discovered it.
This trick will be used couple times more.
9. RDTSC - INT3 - CALL/JMP check
Here is similar trick that is described before. This time protector will first trigger RDTSC to get timer value and save it. Then it will trigger INT3 exception. After exception is handled, it will trigger another RDTSC opcode to retrieve new value. Then it compare difference. This time protector placed INT3 inside RDTSC pairs. So if somebody stuck on exception and then tries to pass it , timer check will detect him. It also has CALL-JMP check. Here is code:
0050A8A0 0F31 RDTSC <---------------------------- 1. Get timer value, then save it in stack.
0050A8A2 50 PUSH EAX
0050A8A3 52 PUSH EDX
...
...
0050A904 CC INT3 <----------------------------- 2. Generate exception.
0050A905 90 NOP <------------------------------ 9. Return after ntdll.dll.
0050A906 3C 04 CMP AL,4
0050A908 74 3A JE SHORT SDProtec.0050A944 <------- 10. Good jump (exception is handled).
0050A90A 74 03 JE SHORT SDProtec.0050A90F <------- 11. Bad jumps.
0050A90C 75 01 JNZ SHORT SDProtec.0050A90F
0050A90E 90 NOP <------------------------------ I NOP-ed byte to clear junk.
0050A90F E8 01000000 CALL SDProtec.0050A915
...
...
0050A944 64:8F05 00000000 POP DWORD PTR FS:[0] <------------- 12. Uninstall local handler.
0050A94B 83C4 04 ADD ESP,4
0050A94E E8 01000000 CALL SDProtec.0050A954
0050A953 90 NOP <------------------------------ I NOP-ed byte to clear junk.
0050A954 58 POP EAX
0050A955 05 C1FEFFFF ADD EAX,-13F
0050A95A 8038 E9 CMP BYTE PTR DS:[EAX],0E9 <-------- 13. Again this CALL-JMP check.
0050A95D ^75 AB JNZ SHORT SDProtec.0050A90A <------ 14. Bad jump.
0050A95F C600 E8 MOV BYTE PTR DS:[EAX],0E8
0050A962 5B POP EBX
0050A963 59 POP ECX
0050A964 0F31 RDTSC <---------------------------- 15. Get timer value.
0050A966 2BC1 SUB EAX,ECX
0050A968 1BD3 SBB EDX,EBX
0050A96A 83FA 00 CMP EDX,0
0050A96D ^75 9B JNZ SHORT SDProtec.0050A90A
0050A96F 3D 00000080 CMP EAX,80000000
0050A974 ^77 94 JA SHORT SDProtec.0050A90A
0050A976 74 49 JE SHORT SDProtec.0050A9C1
0050A978 75 47 JNZ SHORT SDProtec.0050A9C1
0050A97A E8 00104000 CALL 0090B97F
0050A97F B0 89 MOV AL,89
0050A981 9C PUSHFD
0050A982 04 8B ADD AL,8B
0050A984 44 INC ESP
0050A985 24 04 AND AL,4
0050A987 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
0050A98B FF81 B8000000 INC DWORD PTR DS:[ECX+B8]
0050A991 8B00 MOV EAX,DWORD PTR DS:[EAX]
0050A993 2D 03000080 SUB EAX,80000003
0050A998 75 26 JNZ SHORT SDProtec.0050A9C0
0050A99A E8 01000000 CALL SDProtec.0050A9A0
0050A99F FF58 05 CALL FAR FWORD PTR DS:[EAX+5] ; Far call
...
...
0050A983 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] <----- 3. Handle it here.
0050A987 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
0050A98B FF81 B8000000 INC DWORD PTR DS:[ECX+B8]
0050A991 8B00 MOV EAX,DWORD PTR DS:[EAX]
0050A993 2D 03000080 SUB EAX,80000003 <----------------- 4. Exception type check.
0050A998 75 26 JNZ SHORT SDProtec.0050A9C0 <------ 5. Bad jump.
0050A99A E8 01000000 CALL SDProtec.0050A9A0
0050A99F 90 NOP <------------------------------ I NOP-ed byte to clear junk.
0050A9A0 58 POP EAX
0050A9A1 05 75FEFFFF ADD EAX,-18B
0050A9A6 8038 E8 CMP BYTE PTR DS:[EAX],0E8 <-------- 6. CALL-JMP check.
0050A9A9 ^0F85 5BFFFFFF JNZ SDProtec.0050A90A <------------ 7. Bad jump.
0050A9AF C600 E9 MOV BYTE PTR DS:[EAX],0E9
0050A9B2 33C0 XOR EAX,EAX
0050A9B4 8941 04 MOV DWORD PTR DS:[ECX+4],EAX
0050A9B7 8941 08 MOV DWORD PTR DS:[ECX+8],EAX
0050A9BA 8941 0C MOV DWORD PTR DS:[ECX+C],EAX
0050A9BD 8941 10 MOV DWORD PTR DS:[ECX+10],EAX
0050A9C0 C3 RETN <----------------------------- 8. Return to ntdll.dll.
That CALL-JMP check is interesting , but I didn't examne it better. It probably crush target if we somehow pass/nop exception.
10. GetTickCount - INT3 check
SDProtector also uses GetTickCount API to perform similar time check like with RDTSC. GetTickCount returns to EAX and EDX some timer value.
It calls GetTickCount first time, then is saves that EAX value o memory:
0050AB84 8902 MOV DWORD PTR DS:[EDX],EAX ;Let's set EAX=1 and EDX to 2.
Then it calls second GetTickCount and pushes EAX and EDX values to stack:
0050AA19 50 PUSH EAX ;Let's set it to EAX=3.
0050AA1A 8BC2 MOV EAX,EDX ;And EDX=4.
0050AA1C 50 PUSH EAX
Then after little tracing trough code, it calls third time GetTickCount. Let's set EAX=5 and EDX=6. Then it pops previous value (3) to EBX and checks difference:
0050AA4B 5B POP EBX ;EBX=3
0050AA4C 81C3 F4010000 ADD EBX,1F4
0050AA52 2BD8 SUB EBX,EAX ;EBX=(3+1F4)-5
0050AA54 78 42 JS SHORT SDProtec.0050AA98 ;If diffrence is too big, then jump to error message.
That was second GetTickCount check which is nested in first one. Now, protector will generate INT3 exception:
0050AB4E CC INT3 <------------------------------------ 1. Exception.
0050AB4F 90 NOP
0050AB50 33C0 XOR EAX,EAX
0050AB52 C700 521C4000 MOV DWORD PTR DS:[EAX],SDProtec.00401C52
0050AB58 7A 0A JPE SHORT SDProtec.0050AB64
0050AB5A 7B 08 JPO SHORT SDProtec.0050AB64
...
...
0050ABDF 8B6424 08 MOV ESP,DWORD PTR SS:[ESP+8] <------------ 2. SEH.
0050ABE3 64:8F05 00000000 POP DWORD PTR FS:[0]
...
...
0050ABF0 58 POP EAX
0050ABF1 05 0E000000 ADD EAX,0E
0050ABF6 50 PUSH EAX
0050ABF7 E9 42EE0000 JMP <SDProtec.TICK_COUNT_1> <-------------- 3. Then jump to fourth GetTickCount.
0050ABFC FFE8 JMP FAR EAX
...
...
0050AC03 5A POP EDX
0050AC04 81C2 78FBFFFF ADD EDX,-488
0050AC0A 8B0A MOV ECX,DWORD PTR DS:[EDX]
0050AC0C 83F9 00 CMP ECX,0 <---------------------- It checks first EAX=1 is it 0!
0050AC0F ^74 95 JE SHORT SDProtec.0050ABA6 <----- Bad jump!
0050AC11 2BC1 SUB EAX,ECX <-------------------- Then checks difference from first EAX=1 and second EAX=7.
0050AC13 ^78 91 JS SHORT SDProtec.0050ABA6 <----- Bad jump!
0050AC15 2D D0070000 SUB EAX,7D0 <-------------------- Same range check.
0050AC1A ^79 8A JNS SHORT SDProtec.0050ABA6 <---- Bad jump!
0050AC1C B8 7612800E MOV EAX,0E801276
0050AC21 8902 MOV DWORD PTR DS:[EDX],EAX
0050AC23 E8 01000000 CALL SDProtec.0050AC29
So not only that this check will check is difference between first GetTickCount and fourth GetTickCount inside 7D0 range, but it will also check did first GetTickCount returned 0! So if we patch GetTickCount API, or we use some tool or plugin to fool GetTickCount, it will detect that and exit.
The easiest way to kill this check is to set GetTickCount to return EAX=1 and EDX=0. Simply, we can patch first opcodes of that API, but even that isn't needed. We can just place bp at the end of API and run fast 4 times. And all previous checks will be avoided.
11. GetProcessTimes - GetSystemTimeAsFileTime check
This is new time check and it is performed only once. SDProtector first calls GetTickCount. It will nest new check inside GetTickCount one. Then it calls GetVersion to check something. If system doesnt match, this bew check is just skipped. So we can just skip this check by executing one jump. But letc see how it goes:
00517B79 E8 C01E0000 CALL <SDProtec.TICK_COUNT_1> <---------- GetTickCount.
00517B7E 8BF8 MOV EDI,EAX <--------------------------- Store EAX to EDI.
00517B80 E8 791C0000 CALL <SDProtec.IMPORT_GetVersion> <----- GetVersion.
00517B85 3D 00000080 CMP EAX,80000000
00517B8A 0F83 8B000000 JNB SDProtec.00517C1B <----------------- Skip everything if OS doesnt match.
00517B90 81FF 4F5346D2 CMP EDI,D246534F
00517B96 75 05 JNZ SHORT SDProtec.00517B9D
00517B98 BF 505346D2 MOV EDI,D2465350
00517B9D 68 57EB4000 PUSH SDProtec.0040EB57
00517BA2 E8 FE77FFFF CALL <SDProtec.API-DLL_STRING>
00517BA7 50 PUSH EAX
00517BA8 68 67EB4000 PUSH SDProtec.0040EB67
00517BAD E8 F377FFFF CALL <SDProtec.API-DLL_STRING>
00517BB2 50 PUSH EAX
00517BB3 E8 CE1E0000 CALL <SDProtec.API_BP_CHECK_1>
00517BB8 50 PUSH EAX
00517BB9 E8 CABBFFFF CALL <SDProtec.GET_PROC_ADDR_1>
00517BBE 8BF0 MOV ESI,EAX
00517BC0 85F6 TEST ESI,ESI
00517BC2 74 57 JE SHORT SDProtec.00517C1B
00517BC4 8D4424 08 LEA EAX,DWORD PTR SS:[ESP+8]
00517BC8 8D4C24 08 LEA ECX,DWORD PTR SS:[ESP+8]
00517BCC 50 PUSH EAX
00517BCD 8D5424 0C LEA EDX,DWORD PTR SS:[ESP+C]
00517BD1 51 PUSH ECX
00517BD2 8D4424 18 LEA EAX,DWORD PTR SS:[ESP+18]
00517BD6 52 PUSH EDX
00517BD7 50 PUSH EAX
00517BD8 E8 D91B0000 CALL SDProtec.005197B6 <----------------- GetCurrentProcess.
00517BDD 50 PUSH EAX
00517BDE FFD6 CALL ESI <------------------------------- GetProcessTimes.
00517BE0 85C0 TEST EAX,EAX
00517BE2 74 37 JE SHORT SDProtec.00517C1B <------------- If fails, skip check.
00517BE4 8D4C24 08 LEA ECX,DWORD PTR SS:[ESP+8]
00517BE8 51 PUSH ECX
00517BE9 E8 D0190000 CALL SDProtec.005195BE <----------------- GetSystemTimeAsFileTime.
00517BEE 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+C]
00517BF2 8B4C24 14 MOV ECX,DWORD PTR SS:[ESP+14]
00517BF6 2BC1 SUB EAX,ECX
00517BF8 74 2C JE SHORT SDProtec.00517C26
00517BFA 83F8 01 CMP EAX,1
00517BFD 77 39 JA SHORT SDProtec.00517C38 <------------- Bad jump.
00517BFF 8B4424 08 MOV EAX,DWORD PTR SS:[ESP+8]
00517C03 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+10]
00517C07 C1E8 04 SHR EAX,4
00517C0A C1E9 04 SHR ECX,4
00517C0D 05 00000010 ADD EAX,10000000
00517C12 2BC1 SUB EAX,ECX
00517C14 3D 00000001 CMP EAX,1000000
00517C19 77 1D JA SHORT SDProtec.00517C38 <------------- Bad jump.
00517C1B 5F POP EDI
00517C1C B8 4F5346D2 MOV EAX,D246534F
00517C21 5E POP ESI
00517C22 83C4 10 ADD ESP,10
00517C25 C3 RETN
00517C26 8B5424 08 MOV EDX,DWORD PTR SS:[ESP+8]
00517C2A 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+10]
00517C2E 2BD1 SUB EDX,ECX
00517C30 81FA 00000010 CMP EDX,10000000
00517C36 ^76 E3 JBE SHORT SDProtec.00517C1B <------------ Good jump! Must be executed!
00517C38 8BC7 MOV EAX,EDI
00517C3A 5F POP EDI
00517C3B 5E POP ESI
00517C3C 83C4 10 ADD ESP,10
00517C3F C3 RETN
GetProcessTimes will return some timing information about specified process (it called before GetCurrent process and that means it gives information for it's own process) to some 64-bit buffer (two dwords). GetSystemTimeAsFileTime will retrieve system time and store it in file format to some buffer. SDP will just check differences.
Continue to trace and we will meet new anti-debug trick soon. Notice that GetTickCount is still opened and waiting for second pair of that check. SDP author nested couple checks one inside another.
12. ZwQueryInformationProcess , custom IsDebuggerPresent , GetTickCount and RDTSC check
Tracing further after last check, we will meet one SMC trick, EFLAG trick, and finally, we will get here:
005182A3 E8 56150000 CALL <SDProtec.IMPORT_GetVersion> <----- It checks does OS matches request.
005182A8 3D 00000080 CMP EAX,80000000
005182AD 0F83 96000000 JNB SDProtec.00518349 <----------------- If not, we can skip all these checks.
005182B3 53 PUSH EBX
005182B4 55 PUSH EBP
005182B5 56 PUSH ESI
005182B6 57 PUSH EDI
005182B7 E8 82170000 CALL <SDProtec.TICK_COUNT_1> <---------- Second part of GetTickCount check.
005182BC 8BE8 MOV EBP,EAX
005182BE E8 D3E0FFFF CALL SDProtec.00516396 <---------------- RDTSC inside.
005182C3 68 0CDD4000 PUSH SDProtec.0040DD0C
005182C8 8BF8 MOV EDI,EAX
005182CA 8BDA MOV EBX,EDX
005182CC E8 D470FFFF CALL <SDProtec.API-DLL_STRING>
005182D1 50 PUSH EAX
005182D2 68 6CB34000 PUSH SDProtec.0040B36C
005182D7 E8 C970FFFF CALL <SDProtec.API-DLL_STRING>
005182DC 50 PUSH EAX
005182DD E8 A4170000 CALL <SDProtec.API_BP_CHECK_1>
005182E2 50 PUSH EAX
005182E3 E8 A0B4FFFF CALL <SDProtec.GET_PROC_ADDR_1> <------- Get ZwQueryInformationProcess API.
005182E8 8BF0 MOV ESI,EAX
005182EA 85F6 TEST ESI,ESI
005182EC 74 2D JE SHORT SDProtec.0051831B
005182EE 33C0 XOR EAX,EAX
005182F0 803C30 CC CMP BYTE PTR DS:[EAX+ESI],0CC <--------- Check it for breakpoints, first 8 bytes.
005182F4 74 49 JE SHORT SDProtec.0051833F
005182F6 40 INC EAX
005182F7 83F8 08 CMP EAX,8
005182FA ^7C F4 JL SHORT SDProtec.005182F0
005182FC 6A 00 PUSH 0
005182FE 8D4424 14 LEA EAX,DWORD PTR SS:[ESP+14]
00518302 6A 04 PUSH 4
00518304 50 PUSH EAX
00518305 6A 07 PUSH 7 <--------------------------------- Flag for testing debugger.
00518307 E8 AA140000 CALL SDProtec.005197B6 <----------------- GetCurrentProcess import.
0051830C 50 PUSH EAX
0051830D FFD6 CALL ESI <------------------------------- Call ZwQueryInformationProcess.
0051830F 85C0 TEST EAX,EAX
00518311 75 08 JNZ SHORT SDProtec.0051831B <------------ If API failed, skip check.
00518313 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10] <---------- Here it checks buffer for debugg info.
00518317 85C0 TEST EAX,EAX <--------------------------- We are detected if EAX<>0.
00518319 75 24 JNZ SHORT SDProtec.0051833F <------------ Debugger found, we are detected.
0051831B E8 41FAFFFF CALL SDProtec.00517D61 <----------------- Custom IsDebuggerPresent check.
00518320 85C0 TEST EAX,EAX
00518322 75 1B JNZ SHORT SDProtec.0051833F <------------ We are detected if EAX<>0.
00518324 E8 6DE0FFFF CALL SDProtec.00516396 <----------------- Second pair of RDTSC check.
00518329 2BC7 SUB EAX,EDI
0051832B 1BD3 SBB EDX,EBX
0051832D 895424 14 MOV DWORD PTR SS:[ESP+14],EDX
00518331 75 0C JNZ SHORT SDProtec.0051833F <------------- Bad jump.
00518333 3D 000000FF CMP EAX,FF000000
00518338 77 05 JA SHORT SDProtec.0051833F <-------------- Bad jump.
0051833A BD 00000080 MOV EBP,80000000
0051833F 5F POP EDI
00518340 8BC5 MOV EAX,EBP
00518342 5E POP ESI
00518343 5D POP EBP
00518344 5B POP EBX
00518345 83C4 08 ADD ESP,8
00518348 C3 RETN
00518349 B8 00000080 MOV EAX,80000000
0051834E 83C4 08 ADD ESP,8
00518351 C3 RETN
ZwQueryInformationProcess API can check is current process is beeing debugged. Stack:
0012FF90 0051830F /CALL to ZwQueryInformationProcess from SDProtec.0051830D
0012FF94 FFFFFFFF |hProcess = FFFFFFFF
0012FF98 00000007 |InfoClass = 7
0012FF9C 0012FFB8 |Buffer = 0012FFB8
0012FFA0 00000004 |Bufsize = 4
0012FFA4 00000000 \pReqsize = NULL
hProcess = FFFFFFFF points to current process
InfoClass = 7 is code that this API checks for debugger presence
Buffer = 0012FFB8 is buffer where it will store process info.
We check that buffer in dump and execute API:
0012FFB8 FF FF FF FF 46 02 00 00 36 AE 50 00 DF AB 50 00 ....F...6®P...P.
0012FFC8 38 07 91 7C FF FF FF FF 00 C0 FD 7F 38 B0 54 80 8..|........8.T.
0012FFD8 C8 FF 12 00 68 62 C6 82 FF FF FF FF F3 99 83 7C ....hb.........|
0012FFE8 58 6D 81 7C 00 00 00 00 00 00 00 00 00 00 00 00 Xm.|............
0012FFF8 00 A0 50 00 00 00 00 00 ..P.....
First DWORD, FFFFFFFF tells that debugger is found. We must set that to 00000000 and it will think that process is not being debugged.
Custom IsDebuggerPresent check is also inside this code block:
00517D61 55 PUSH EBP
00517D62 8BEC MOV EBP,ESP
00517D64 51 PUSH ECX
00517D65 50 PUSH EAX
00517D66 51 PUSH ECX
00517D67 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
00517D6D 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
00517D70 8B08 MOV ECX,DWORD PTR DS:[EAX]
00517D72 894D FC MOV DWORD PTR SS:[EBP-4],ECX
00517D75 59 POP ECX
00517D76 58 POP EAX
00517D77 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00517D7A C1E8 10 SHR EAX,10
00517D7D 83E0 01 AND EAX,1
00517D80 8BE5 MOV ESP,EBP
00517D82 5D POP EBP
00517D83 C3 RETN
We can notice that all checks and tricks before this ZwQueryInformationProcess can be bypassed by just running fast. All previous tricks where anti-trace tricks. This is first reall anti-debugger check. Also, GetTickCount in this check and previous one is not clear to me. It seams that it doesn't serve the purpose, I didn't noticed that values are used.
13. Temp file and new process
Next check also seams to be based on time. First, one string is prepared:
ASCII "~temp0%d.tmp"
Then with wsprintfA name of temporary file is formated:
ASCII "~temp0-1790399849.tmp"
Protector uses GetTempPathA to get temporary folder path. Then is uses CreateFileA to create that temp file. Then is reads 12 bytes (0C hex) from file using ReadFileA API. Then it will check how many bytes it readed:
00516593 837C24 1C 0C CMP DWORD PTR SS:[ESP+1C],0C
00516598 73 76 JNB SHORT SDProtec.00516610
If 0, file is empty and SDP will generate 8 bytes with RDTSC (EAX:EDX) and write it in buffer. Last 4 bytes that are written in buffer are probably from GetTickCount value. Then it will encrypt that 12 bytes and write them in the file. Then it will start new process from target and exit this one. new process will perform all checks as usuall, and then it will come to this part again.
This time it will see that file has 12 bytes inside, read them, and decrypt them. It will take first DWORD, substract some value in EDI with it and then check is it in given range. If it doesn't fit in that range, it will go wild creating new processes, untill it eventually exits saying "Debugger detected":
0051663E 2B7C24 18 SUB EDI,DWORD PTR SS:[ESP+18]
00516642 81FF 803E0000 CMP EDI,3E80
00516648 897C24 20 MOV DWORD PTR SS:[ESP+20],EDI
0051664C 76 08 JBE SHORT SDProtec.00516656 <------------ Good jump if EDI=<3E80.
0051664E 83FB 05 CMP EBX,5
00516651 74 7E JE SHORT SDProtec.005166D1
00516653 43 INC EBX
00516654 EB 57 JMP SHORT SDProtec.005166AD
00516656 85FF TEST EDI,EDI
00516658 0F85 B0000000 JNZ SDProtec.0051670E <------------------ But it must not be EDI=0.
0051665E E8 33FDFFFF CALL SDProtec.00516396
Avoiding this detection is not hard. By executing three above jumps we avoid new process.
14. ZwQuerySystemInformation
This API is one of those which are not documented so I can only guess what it does. I assume that it can retrieve various information about system, but SDP will use it for detecting hostile drivers. SDP will get names of all drivers loaded in memory. All those drivers will be compared with hardcoded names (like softice drivers, etc...). This doesn't detect Olly so you don't have to worry, but we can avoid this check because it is OS dependent:
0051836E E8 8B140000 CALL <SDProtec.IMPORT_GetVersion>
00518373 3D 00000080 CMP EAX,80000000
00518378 0F83 F6000000 JNB SDProtec.00518474 <---------- Execute this jump and problem is solved.
0051837E 68 26DD4000 PUSH SDProtec.0040DD26
00518383 E8 1D70FFFF CALL SDProtec.0050F3A5
00518388 50 PUSH EAX
...
...
This check is used at least twice (after threads part it is used one time more).
15. IsDebuggerPresent
Well known check:
0051828C E8 F5170000 CALL <SDProtec.API_BP_CHECK_1>
00518291 50 PUSH EAX
00518292 E8 F1B4FFFF CALL <SDProtec.GET_PROC_ADDR_1>
00518297 85C0 TEST EAX,EAX
00518299 74 02 JE SHORT SDProtec.0051829D
0051829B -FFE0 JMP EAX ; kernel32.IsDebuggerPresent
16. CreateToolhelp32Snapshot, Process32First, Process32Next - detecting hostile processes
SDP has black list of some hostile processes:
0012FE74 00517F89 ASCII "OLLYDBG.EXE"
0012FE78 00517F95 ASCII "SOFTICE.EXE"
0012FE7C 00517FA1 ASCII "LORDPE.EXE"
0012FE80 00517FAC ASCII "PEDUMPER.EXE"
0012FE84 00517FB9 ASCII "FILEMON.EXE"
0012FE88 00517FC5 ASCII "REGMON.EXE"
0012FE8C 00517FD0 ASCII "IMPORTREC.EXE"
0012FE90 00517FDE ASCII "SUPERIMPORTREC.EXE"
0012FE94 00517FF1 ASCII "DEBUGAPISPY.EXE"
It uses CreateToolhep32Snapshot to retrieve snapshot. The with Process32First and Process32Next checks does some process is blacklisted. It checks names of processes with blacklisted:
0050FD78 F3:A6 REPE CMPS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
0050FD7A B8 01000000 MOV EAX,1 <------------------------------------ EAX=1.
0050FD7F 75 01 JNZ SHORT SDProtec.0050FD82
0050FD81 48 DEC EAX <-------------------------------------- If it finds hostile process, EAX=0.
0050FD82 5F POP EDI
0050FD83 5E POP ESI
0050FD84 C9 LEAVE
0050FD85 C2 0800 RETN 8
ESI 0012FEBC ASCII "OLLYDBG.EXE"
EDI 00517F89 ASCII "OLLYDBG.EXE"
005181A9 E8 B47BFFFF CALL SDProtec.0050FD62 <--------------- Name check.
005181AE 85C0 TEST EAX,EAX
005181B0 74 08 JE SHORT SDProtec.005181BA <----------- EAX=0 -> jump, debugger detected.
005181B2 8B4424 10 MOV EAX,DWORD PTR SS:[ESP+10]
005181B6 85C0 TEST EAX,EAX
005181B8 74 30 JE SHORT SDProtec.005181EA <----------- EAX=1 -> good jump.
005181BA 8B5424 50 MOV EDX,DWORD PTR SS:[ESP+50]
005181BE 52 PUSH EDX
005181BF 6A 00 PUSH 0
005181C1 6A 01 PUSH 1
005181C3 E8 D61B0000 CALL SDProtec.00519D9E
...
...
We can prevent detection renaming Olly , or to change it in SDP blacklist buffer, but the easiest way is to just set EAX=0 uppon return from CreateToolhep32Snapshot API. This will produce ERROR_INVALID_HANDLE and SDP will just exit from this check.
17. Monitoring threads
SDProtector can create two threads to monitor for file changes and hostile tools. Threads are created suspended:
0012FF6C 00517973 /CALL to CreateThread from SDProtec.0051796E
0012FF70 00000000 |pSecurity = NULL
0012FF74 00010000 |StackSize = 10000 (65536.)
0012FF78 005179E0 |ThreadFunction = SDProtec.005179E0
0012FF7C 0050A1BC |pThreadParm = SDProtec.0050A1BC
0012FF80 00000004 |CreationFlags = CREATE_SUSPENDED
0012FF84 0012FF94 \pThreadId = 0012FF94
Then it will resume threads. We can prevent creating threads or resuming, by just patching API's. Or, since threads are optional, we can just skip them:
00517936 E8 B3220000 CALL SDProtec.00519BEE <--------------- GetSystemInfo
0051793B 837C24 24 01 CMP DWORD PTR SS:[ESP+24],1 <---------- "Do we need threads?" check.
00517940 0F85 94000000 JNZ SDProtec.005179DA <---------------- Jump if not.
00517946 57 PUSH EDI
00517947 E8 727AFFFF CALL SDProtec.0050F3BE
0051794C 8BF0 MOV ESI,EAX
0051794E 8D4C24 0C LEA ECX,DWORD PTR SS:[ESP+C]
00517952 51 PUSH ECX
00517953 6A 04 PUSH 4
00517955 8DBE BC000000 LEA EDI,DWORD PTR DS:[ESI+BC]
0051795B 57 PUSH EDI
0051795C 68 E0E94000 PUSH SDProtec.0040E9E0
00517961 E8 3F7AFFFF CALL SDProtec.0050F3A5
00517966 50 PUSH EAX
00517967 68 00000100 PUSH 10000
0051796C 6A 00 PUSH 0
0051796E E8 B71C0000 CALL <SDProtec.IMPORT_BC_CHECK_4> <---- Create first thread.
00517973 8D5424 10 LEA EDX,DWORD PTR SS:[ESP+10]
00517977 8BD8 MOV EBX,EAX
00517979 52 PUSH EDX
0051797A 6A 04 PUSH 4
0051797C 57 PUSH EDI
0051797D 68 A8E74000 PUSH SDProtec.0040E7A8
00517982 E8 1E7AFFFF CALL SDProtec.0050F3A5
00517987 50 PUSH EAX
00517988 68 00000100 PUSH 10000
0051798D 6A 00 PUSH 0
0051798F E8 961C0000 CALL <SDProtec.IMPORT_BC_CHECK_4> <---- Create second thread.
00517994 85C0 TEST EAX,EAX
00517996 5F POP EDI
00517997 74 41 JE SHORT SDProtec.005179DA
00517999 85DB TEST EBX,EBX
0051799B 74 3D JE SHORT SDProtec.005179DA
0051799D 50 PUSH EAX
0051799E C786 A8000000 00>MOV DWORD PTR DS:[ESI+A8],0
005179A8 C786 AC000000 00>MOV DWORD PTR DS:[ESI+AC],0
005179B2 C786 C0000000 00>MOV DWORD PTR DS:[ESI+C0],0
005179BC 8986 B0000000 MOV DWORD PTR DS:[ESI+B0],EAX
005179C2 899E B4000000 MOV DWORD PTR DS:[ESI+B4],EBX
005179C8 E8 111D0000 CALL <SDProtec.IMPORT_BC_CHECK> <------ ResumeThread.
005179CD 53 PUSH EBX
005179CE E8 0B1D0000 CALL <SDProtec.IMPORT_BC_CHECK> <------ ResumeThread.
005179D3 6A 00 PUSH 0
005179D5 E8 BC1C0000 CALL SDProtec.00519696
005179DA 5E POP ESI
005179DB 5B POP EBX
005179DC 83C4 2C ADD ESP,2C
005179DF C3 RETN
18. Checking installed services in registry for SI
0012FF9C 00515566 /CALL to RegOpenKeyA from SDProtec.00515561
0012FFA0 80000002 |hKey = HKEY_LOCAL_MACHINE
0012FFA4 00515523 |Subkey = "SYSTEM\CurrentControlSet\Services\IceExt"
0012FFA8 0012FFAC \pHandle = 0012FFAC
19. SetUnhandledExceptionFilter
It installs finall SEH and then triggers exception. Within debugger , target crushes. I explained in Obsidium tutorial how to avoid this. Also, you can use some plugin for olly.
20. CreateFileA drivers check
List of drivers and monitoring tools:
0012DBDC 00518E53 ASCII "\\.\SICE"
0012DBE0 00518E5C ASCII "\\.\NTICE"
0012DBE4 00518E66 ASCII "\\.\SIWDEBUG"
0012DBE8 00518E73 ASCII "\\.\SIWVID"
0012DBEC 00518E7E ASCII "\\.\FILEMON"
0012DBF0 00518E8A ASCII "\\.\Global\FILEMON"
0012DBF4 00518E9D ASCII "\\.\REGMON"
0012DBF8 00518EA8 ASCII "\\.\Global\REGMON"
0012DBFC 00518EBA ASCII "\\.\FILEVXD.VXD"
0012DC00 00518ECA ASCII "\\.\REGVXD.VXD"
0012DC04 00518ED9 ASCII "\\.\TRW"
0012DC08 00518EE1 ASCII "\\.\TRWDEBUG"
0012DC0C 00518EEE ASCII "\\.\ICEDUMP"
0012DC10 00518EFA ASCII "\\.\FROGSICE"
0012DC14 00518F07 ASCII "\\.\IceExt"
0012DC18 00518F12 ASCII "\\.\RvtracerDevice0"
0012DC1C 00518F26 ASCII "\\.\BW2K"
0012DC20 00518F2F ASCII "\\.\SUPERBPM"
0012DC24 00518F3C ASCII "\\.\NTICED052"
0012DC28 00518F4A ASCII "\\.\NTICE7871"
0012DC2C 00518F58 ASCII "\\.\TRW2000"
0012DC30 00518F64 ASCII "\\.\VKEYPROD"
It uses CreateFileA to try open such files and RtlGetLastWin32Error to get error information. If ERROR_SUCCSESS is returned, driver is detected. Inside this check is nested IsDebuggerPresent check which is called in every cycle.
21. INT1 trick to detect SoftICE
This is trick for SoftICE, it generates memory access violation or something. I don't have SoftICE so I'm not sure how it works, but I assume that SI handles that exception, return different exception code to program:
0050DBB0 33C0 XOR EAX,EAX
0050DBB2 64:FF30 PUSH DWORD PTR FS:[EAX]
0050DBB5 64:8920 MOV DWORD PTR FS:[EAX],ESP <-------------- 1. Install SEH.
0050DBB8 CD 01 INT 1 <----------------------------------- 2. Generate exception.
0050DBBA C3 RETN
0050DBBB 64:8F05 00000000 POP DWORD PTR FS:[0] <-------------------- 7. Return here.
0050DBC2 83C4 04 ADD ESP,4
0050DBC5 EB 63 JMP SHORT SDProtec.0050DC2A
0050DBC7 74 03 JE SHORT SDProtec.0050DBCC
0050DBC9 75 01 JNZ SHORT SDProtec.0050DBCC
0050DBCB E8 E8010000 CALL SDProtec.0050DDB8
0050DBD0 00FF ADD BH,BH
0050DBD2 58 POP EAX
0050DBD3 05 27000000 ADD EAX,27
0050DBD8 8BD0 MOV EDX,EAX
0050DBDA 83C2 32 ADD EDX,32
0050DBDD E8 D6220000 CALL SDProtec.0050FEB8
0050DBE2 EB 01 JMP SHORT SDProtec.0050DBE5
0050DBE4 FFE8 JMP FAR EAX
0050DBE6 0100 ADD DWORD PTR DS:[EAX],EAX
0050DBE8 0000 ADD BYTE PTR DS:[EAX],AL
0050DBEA FF58 05 CALL FAR FWORD PTR DS:[EAX+5]
0050DBED 0E PUSH CS
0050DBEE 0000 ADD BYTE PTR DS:[EAX],AL
0050DBF0 0050 E9 ADD BYTE PTR DS:[EAX-17],DL
0050DBF3 78 5B JS SHORT SDProtec.0050DC50
0050DBF5 0000 ADD BYTE PTR DS:[EAX],AL
0050DBF7 FF00 INC DWORD PTR DS:[EAX]
0050DBF9 1040 00 ADC BYTE PTR DS:[EAX],AL
0050DBFC 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4] <------------ 3. Handler.
0050DC00 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
0050DC04 8B00 MOV EAX,DWORD PTR DS:[EAX]
0050DC06 3D 04000080 CMP EAX,80000004 <------------------------ 4. Check exception code.
0050DC0B ^74 BA JE SHORT SDProtec.0050DBC7 <-------------- 5. Bad jump.
0050DC0D 3D 050000C0 CMP EAX,C0000005
0050DC12 75 15 JNZ SHORT SDProtec.0050DC29 <------------- 6. Bad jump too.
0050DC14 83A9 B8000000 FD SUB DWORD PTR DS:[ECX+B8],-3
0050DC1B 33C0 XOR EAX,EAX
0050DC1D 8941 04 MOV DWORD PTR DS:[ECX+4],EAX
0050DC20 8941 08 MOV DWORD PTR DS:[ECX+8],EAX
0050DC23 8941 0C MOV DWORD PTR DS:[ECX+C],EAX
0050DC26 8941 10 MOV DWORD PTR DS:[ECX+10],EAX
0050DC29 C3 RETN
22. Import protection
After last INT1 check, we will met one SMC and EFLAG , and then it comes time to decrypt IAT. IAT is first encrypted (thunks, hint arrays, IID, names, ordinals). During runtime, SDP decrypts names, load libraries, decrypts thunks, hints and ordinals. Then it finds import, place it in thunk, or it redirect it to stack memory where it obfuscate it. Then it destroys ordinals, hint arrays, etc. IAT protection will not be discussed here in details because IAT can be repaired after reaching OEP. Also, IAT handling procedure is pretty big, and this tutorial has already become too big. If you are experienced with PE format and rebuilding files, you will easily prevent IAT destruction and retrieve virgin untouched IAT. That gives nice and clean dump, also prevents possible errors while rebuilding IAT with tools such as ImpREC.
Note that IAT algorithm also has couple integrity checks, so it is advisable to patch them.
23. UnhandledExceptionFilter breakpoint check
Altough almost all API's are checked for breakpoints, I'm showing this one separatley because this is a SoftICE check. SoftICE sets INT3 at the beggining of UnhandledExceptionFilter API so it can controll exception handling. SDProtector checks is INT3 placed on this API for that reason. It will not use this API at all.
00518250 85C0 TEST EAX,EAX ; kernel32.UnhandledExceptionFilter
00518252 74 0F JE SHORT SDProtec.00518263
00518254 8038 CC CMP BYTE PTR DS:[EAX],0CC
00518257 75 0A JNZ SHORT SDProtec.00518263
00518259 E8 7171FFFF CALL SDProtec.0050F3CF
0051825E ^E9 0CB5FFFF JMP SDProtec.0051376F
00518263 C3 RETN
24. Stolen code and OEP
After UnhandledExceptionFilter check, it happens a lot interesting things. SDProtector searching some files in windows directory, then in C:\, checks time. It also performs some registry operations, particulary interesting is that it writes key with embedded nulls so "normall" tools like regedit cannot get access.
We can place breakpoint on GetTickCount and run fast 4 times to avoid detection. Then two times we break on GetModuleHandleA, little tracing, and we find jump to stolen OEP code:
0050E79C 58 POP EAX
0050E79D 05 4BFFFFFF ADD EAX,-0B5
0050E7A2 8038 E9 CMP BYTE PTR DS:[EAX],0E9
0050E7A5 ^0F85 73FFFFFF JNZ SDProtec.0050E71E
0050E7AB C600 E8 MOV BYTE PTR DS:[EAX],0E8
0050E7AE 9D POPFD
0050E7AF 61 POPAD
0050E7B0 -FFE0 JMP EAX ; SDProtec.004B5A10
And there we have stolen opcodes:
004B5A10 55 PUSH EBP
004B5A11 8BEC MOV EBP,ESP
004B5A13 6A FF PUSH -1
004B5A15 68 E0214700 PUSH SDProtec.004721E0
004B5A1A 68 64E54300 PUSH SDProtec.0043E564
004B5A1F 60 PUSHAD
004B5A20 9C PUSHFD
004B5A21 -FFE2 JMP EDX
004B5A23 0000 ADD BYTE PTR DS:[EAX],AL
004B5A25 0000 ADD BYTE PTR DS:[EAX],AL
PUSHAD and PUSHFD are not stolen ofcourse. They purpose is to save original registers values. But JMP EDX retuns us to SDProtectors code, right below JMP EAX. Again, same checks are performed and then it point us where is OEP. Here is snippet with NOP-ed bytes to get cleaner picture:
0050ED1B 58 POP EAX
0050ED1C 05 FEB3FFFF ADD EAX,FFFFB3FE
0050ED21 2B08 SUB ECX,DWORD PTR DS:[EAX] <--------- ECX is counter how many bytes to check.
0050ED23 FC CLD
0050ED24 B0 C3 MOV AL,0C3
0050ED26 F2:AE REPNE SCAS BYTE PTR ES:[EDI] <------- EDI=439434, it scans for C3 bytes now.
0050ED28 83F9 00 CMP ECX,0 <-------------------------- Checks if ECX=0, is all code checked.
0050ED2B 7E 13 JLE SHORT SDProtec.0050ED40
0050ED2D EB 01 JMP SHORT SDProtec.0050ED30
0050ED2F 90 NOP
0050ED30 4F DEC EDI
0050ED31 FFD7 CALL EDI <--------------------------- When it finds C3 byte, it jumps on it.
0050ED33 74 03 JE SHORT SDProtec.0050ED38
0050ED35 75 01 JNZ SHORT SDProtec.0050ED38
0050ED37 90 NOP
0050ED38 57 PUSH EDI
0050ED39 47 INC EDI
0050ED3A 49 DEC ECX
0050ED3B ^7F E9 JG SHORT SDProtec.0050ED26
0050ED3D EB 01 JMP SHORT SDProtec.0050ED40
0050ED3F 90 NOP
0050ED40 81EC 00100000 SUB ESP,1000
0050ED46 70 0E JO SHORT SDProtec.0050ED56
0050ED48 71 0C JNO SHORT SDProtec.0050ED56
oep with stolen code:
00439434 0000 ADD BYTE PTR DS:[EAX],AL <----------- This is OEP. Code is stolen.
00439436 0000 ADD BYTE PTR DS:[EAX],AL
00439438 0000 ADD BYTE PTR DS:[EAX],AL
0043943A E8 13CB0D00 CALL SDProtec.00515F52 <------------- Here it points at start.
0043943F 8BE0 MOV ESP,EAX
00439441 9D POPFD
00439442 61 POPAD
00439443 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
00439449 50 PUSH EAX
0043944A 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
00439451 83EC 58 SUB ESP,58
00439454 53 PUSH EBX
00439455 56 PUSH ESI
Now we know where is OEP and what are stolen bytes.
SDProtector pointed us to OEP and it scanned for all 0C3 bytes in code section from our OEP, then it jumped to everyone of them and returned back. Every address of each 0C3 byte is pushed to stack and that it has a reason. Then again, SDP is performing same timer and trace checks, then it decrypt some code couple times. Last decrypted block will erase SDP code:
0050F250 F3:AA REP STOS BYTE PTR ES:[EDI]
0050F252 60 PUSHAD
0050F253 9C PUSHFD
0050F254 58 POP EAX
0050F255 8BC8 MOV ECX,EAX
Then it jumps to OEP:
0050F388 58 POP EAX ; SDProtec.0046970F
0050F389 05 4BFFFFFF ADD EAX,-0B5
0050F38E 8038 E9 CMP BYTE PTR DS:[EAX],0E9
0050F391 ^0F85 73FFFFFF JNZ SDProtec.0050F30A
0050F397 C600 E8 MOV BYTE PTR DS:[EAX],0E8
0050F39A 9D POPFD
0050F39B 61 POPAD
0050F39C C3 RETN <-------------------------- Returns to original code section.
But it will not jump directly to OEP! As I sad before, it pushed addresses of every 0C3 byte in code section after OEP. So it will keep returning, executing all those RETN (0C3) opcodes/bytes untill it reach OEP! To reach OEP, we don't need to execute all these RETN opcodes, we can place breakpoint at it (after stolen code) and just run.
That all looks nice in theory, but SDP has one big trap. Check next part of tutorial.
25. Hashes on everything (imports too) and decrypting
You might fail to reach OEP. SDP performs hashing of everthing ; PE header, Entry Point of it's own layer, sections, memory and... imports! Yep, SDP hashes imports too, so if you placed breakpoint on some import, it is recorded. And those hashes are sometimes checked as checksum, but sometimes they are used as decryption key for decrypting blocks of code. That is case with last decrypted block that throw us at OEP. It is huge task to avoid being detected because it uses hashing many times. But it seams that it uses them sometimes in pairs, for example - for detecting breakpoints on imports.
Last decrypted block is good example of that: I was using my script to find stolen code. That script used breakpoint on GetVersion import, that was detected. But after I found stolen code, I cleared that breakpoint. SDP has hashed again some imports and GetVersion too, and this time it got diffrent value. Those two (at least) hashes are important for decrypting last block. It decrypted last block , but it was all junk. When I tried again, I left breakpoint on GetVersion and this time it decrypted last block correctly!
I assume that hashing with imports and decrypting block could go something like this; hash 1. time, hash 2. time, add or substract hashes, add difference (or that some value) to decryption key, then decrypt. Ofcourse, bad key gives junk code, while good key gives correct decrypted code. Problem is that it has too many these hashing, but, maybe we could find where it holds good keys.
26. Repairing imports
I already sad that imports can be repaired during runtime. But here we will see how they are protected. If we search for all intermodular calls, we will see many calls to stack area:
Found intermodular calls
Address Disassembly Destination
00401193 CALL DWORD PTR DS:[46A62C] DS:[0046A62C]=00145ABE
004011A4 CALL DWORD PTR DS:[46A630] DS:[0046A630]=00145AF6
00401294 CALL DWORD PTR DS:[46A624] DS:[0046A624]=00145A4E
0040130C CALL DWORD PTR DS:[46A620] DS:[0046A620]=00145A16
...
...etc...
If we follow one call , we see how import is protected:
001457E6 58 POP EAX <----------------------- We jump here.
001457E7 50 PUSH EAX
001457E8 60 PUSHAD
001457E9 9C PUSHFD
001457EA 68 03000000 PUSH 3
001457EF 50 PUSH EAX
001457F0 B8 1FDBA78F MOV EAX,8FA7DB1F
001457F5 50 PUSH EAX
001457F6 B8 3B5A2DE8 MOV EAX,E82D5A3B
001457FB 50 PUSH EAX
001457FC E8 6F103D00 CALL SDProtec.00516870 <------- It goes in SDP layer.
00145801 9D POPFD
00145802 61 POPAD
00145803 B8 3B5A2DE8 MOV EAX,E82D5A3B <------------- To eax moves this value.
00145808 9C PUSHFD
00145809 05 1FDBA78F ADD EAX,8FA7DB1F <------------- Then it adds this one.
0014580E 9D POPFD
0014580F -FFE0 JMP EAX <---------------------- And it jumps there.
As you can see, last two value that are given to EAX are destination of jump, and that jump is import. There are couple patherns similar to this one, and we have some even easier. I wrote script to decrypt those imports. Script just patches this code, decrypts EAX value and sets jump to import. Then ImpREC can be used:
001457E6 -E9 6FDDC077 JMP user32.GetSubMenu
001457EB 90 NOP
001457EC 90 NOP
001457ED 90 NOP
001457EE 90 NOP
001457EF 90 NOP
001457F0 90 NOP
001457F1 90 NOP
001457F2 90 NOP
001457F3 90 NOP
001457F4 90 NOP
001457F5 90 NOP
001457F6 90 NOP
001457F7 90 NOP
001457F8 90 NOP
001457F9 90 NOP
001457FA 90 NOP
001457FB 90 NOP
001457FC 90 NOP
001457FD 90 NOP
001457FE 90 NOP
001457FF 90 NOP
00145800 90 NOP
00145801 90 NOP
00145802 90 NOP
00145803 90 NOP
00145804 90 NOP
00145805 90 NOP
00145806 90 NOP
00145807 90 NOP
00145808 90 NOP
00145809 90 NOP
0014580A 90 NOP
0014580B 90 NOP
0014580C 90 NOP
0014580D 90 NOP
0014580E 90 NOP
0014580F 90 NOP
00145810 E0 00 LOOPDNE SHORT 00145812
Now all imports will be found with ImpREC if we use "trace level 1" option, but one will still be unresoplved. That one points to SDP layer at 00515AB3. That one is replaced MessageBoxA or MessageBoxW and this one is part of embeded SDP protection. We can cut that invalid thunk. It will not be erased from image, only new IAT will not have that pointer, but target will still be able to jump there. Before dumping, it would be good idea to decrypt runtime decryption.
27. Runtime decryption-encryption
SDProtector has runtime decryption-encryption code blocks. These blocks can be decrypted before dumping with some small olly script. There are couple types of runtime decryption. We have this call and below him a jump that jumps 11 bytes further. That is marker for decryptor:
0040C52E E8 1B73D3FF CALL 0014384E <------------------------ Call to decryptor.
0040C533 EB 11 JMP SHORT SDProtec.0040C546 <---------- Return here from call.
0040C535 5B POP EBX
0040C536 0000 ADD BYTE PTR DS:[EAX],AL
0040C538 006F 01 ADD BYTE PTR DS:[EDI+1],CH
0040C53B 034C00 00 ADD ECX,DWORD PTR DS:[EAX+EAX]
0040C53F 0000 ADD BYTE PTR DS:[EAX],AL
0040C541 0000 ADD BYTE PTR DS:[EAX],AL
0040C543 0000 ADD BYTE PTR DS:[EAX],AL
0040C545 E8 62F9FE67 CALL 683FBEAC <------------------------ Encrypted block.
0040C54A F71F NEG DWORD PTR DS:[EDI]
0040C54C D9DE FSTP ESI
0040C54E 90 NOP
0040C54F 99 CDQ
0040C550 8D6CB4 5A LEA EBP,DWORD PTR SS:[ESP+ESI*4+5A]
0040C554 B0 E1 MOV AL,0E1
0040C556 4E DEC ESI
0040C557 B6 A3 MOV DH,0A3
0040C559 215C3D A0 AND DWORD PTR SS:[EBP+EDI-60],EBX
0040C55D 4C DEC ESP
If we follow call, we will see a lot of jumps to SDP layer:
0014384E -E9 E54E3D00 JMP SDProtec.00518738
00143853 -E9 894D3D00 JMP SDProtec.005185E1
00143858 -E9 DB4E3D00 JMP SDProtec.00518738
0014385D -E9 7F4D3D00 JMP SDProtec.005185E1
00143862 -E9 D14E3D00 JMP SDProtec.00518738
00143867 -E9 754D3D00 JMP SDProtec.005185E1
0014386C -E9 C74E3D00 JMP SDProtec.00518738
00143871 -E9 6B4D3D00 JMP SDProtec.005185E1
00143876 -E9 BD4E3D00 JMP SDProtec.00518738
0014387B -E9 614D3D00 JMP SDProtec.005185E1
00143880 -E9 B34E3D00 JMP SDProtec.00518738
...
...
We can notice that most of them leads to two addresses - decryptor and encryptor. After decrypting we can patch decryptor call and encryptor, which will be at the bottom of decrypted code:
0040C52C 8BF1 MOV ESI,ECX
0040C52E 90 NOP <-------------------------------- I patched decryptor call.
0040C52F 90 NOP
0040C530 90 NOP
0040C531 90 NOP
0040C532 90 NOP
0040C533 90 NOP
0040C534 90 NOP
0040C535 90 NOP
0040C536 90 NOP
0040C537 90 NOP
0040C538 90 NOP
0040C539 90 NOP
0040C53A 90 NOP
0040C53B 90 NOP
0040C53C 90 NOP
0040C53D 90 NOP
0040C53E 90 NOP
0040C53F 90 NOP
0040C540 90 NOP
0040C541 90 NOP
0040C542 90 NOP
0040C543 90 NOP
0040C544 90 NOP
0040C545 90 NOP
0040C546 6A 00 PUSH 0
0040C548 8D8D DCFDFFFF LEA ECX,DWORD PTR SS:[EBP-224]
0040C54E E8 43910100 CALL SDProtec.00425696
0040C553 8365 FC 00 AND DWORD PTR SS:[EBP-4],0
0040C557 8D7E 74 LEA EDI,DWORD PTR DS:[ESI+74]
0040C55A 57 PUSH EDI
0040C55B 8D8D 38FEFFFF LEA ECX,DWORD PTR SS:[EBP-1C8]
0040C561 E8 A50D0400 CALL SDProtec.0044D30B
0040C566 83C6 78 ADD ESI,78
0040C569 8D8D 3CFEFFFF LEA ECX,DWORD PTR SS:[EBP-1C4]
0040C56F 56 PUSH ESI
0040C570 E8 960D0400 CALL SDProtec.0044D30B
0040C575 8D8D DCFDFFFF LEA ECX,DWORD PTR SS:[EBP-224]
0040C57B E8 4D180400 CALL SDProtec.0044DDCD
0040C580 83F8 01 CMP EAX,1
0040C583 75 1C JNZ SHORT SDProtec.0040C5A1
0040C585 8D85 38FEFFFF LEA EAX,DWORD PTR SS:[EBP-1C8]
0040C58B 8BCF MOV ECX,EDI
0040C58D 50 PUSH EAX
0040C58E E8 780D0400 CALL SDProtec.0044D30B
0040C593 8D85 3CFEFFFF LEA EAX,DWORD PTR SS:[EBP-1C4]
0040C599 8BCE MOV ECX,ESI
0040C59B 50 PUSH EAX
0040C59C E8 6A0D0400 CALL SDProtec.0044D30B
0040C5A1 90 NOP <--------------------------------- And I patched encryptor one.
0040C5A2 90 NOP
0040C5A3 90 NOP
0040C5A4 90 NOP
0040C5A5 90 NOP
0040C5A6 90 NOP
0040C5A7 90 NOP
0040C5A8 90 NOP
0040C5A9 90 NOP
0040C5AA 90 NOP
0040C5AB 90 NOP
0040C5AC 90 NOP
0040C5AD 90 NOP
0040C5AE 90 NOP
0040C5AF 90 NOP
0040C5B0 90 NOP
0040C5B1 90 NOP
0040C5B2 90 NOP
0040C5B3 90 NOP
0040C5B4 90 NOP
0040C5B5 90 NOP
0040C5B6 90 NOP
0040C5B7 90 NOP
0040C5B8 90 NOP
I used script to decrypt all these calls. After that we will still have some unresolved intermodular calls:
Found intermodular calls
Address Disassembly Destination
0041B3F9 CALL SDProtec.00400040 SDProtec.00514E26 <---- DEMO
0041523B CALL SDProtec.00400059 SDProtec.00514F47 <---- Encryptor
0040E715 CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
0041097E CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
00413A5A CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
004151E1 CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
00415F85 CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
00418D8C CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
0041A4B1 CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
00421A77 CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
00424C67 CALL SDProtec.00400054 SDProtec.005150FD <---- Decryptor
00411542 CALL 0014388A SDProtec.005188D6
00411E2C CALL 0014389E SDProtec.005188D6
0041631F CALL 00143966 SDProtec.005188D6
Probably there are more decryptor calls left, because Olly didn't show every one due to not properly analyzed code section. Plus that junk disturbs finding references. If you find some call, fix it manually. We see that some of rest calls points to PE header.
The first one, that points to 00400040, is demo call to function which is not available in demo mode. It shows MessageBox "This function is not....blah blah..." and then it just returns to code. Without valid key we cannot decrypt it. But call points to PE header and in PE header are couple jumps:
00400040 -E9 E961D4FF JMP 0014622E
00400045 -E9 E961D4FF JMP 00146233
0040004A -E9 E961D4FF JMP 00146238
0040004F -E9 E961D4FF JMP 0014623D
00400054 -E9 E961D4FF JMP 00146242
00400059 -E9 E961D4FF JMP 00146247
Those jumps point to stack where are pointers back to SDP layer:
0014622E -E9 F3EB3C00 JMP SDProtec.00514E26
00146233 -E9 40ED3C00 JMP SDProtec.00514F78
00146238 -E9 69EF3C00 JMP SDProtec.005151A6
0014623D -E9 36ED3C00 JMP SDProtec.00514F78
00146242 -E9 B6EE3C00 JMP SDProtec.005150FD
00146247 -E9 FBEC3C00 JMP SDProtec.00514F47
Ok, to finish with first "demo" call, I just replaced CALL 00400040 to CALL 00514E26.
Second one 00400059 is call to encryptor. Above that call (in code section, but Olly didn't show that reference due junk) is decryptor and we just decrypt it like rest.
The rest of them that points to 005150FD are decryptors and we fix them in same way. Please note that this tutorial is not step-by-step guide and you cannot blindly follow it. It just shows and discuss certain types of protections. That mean, you can get defferent results, number of calls (if any) etc.
The last three , that point to 005188D6, are part of embedded protection. Just change that CALL pointers to point where last JMP points (in SDP layer). Check is there more suspitious calls. If not, we can dump.
28. Dumping
SDProtector has protected part of process memory using VirtualProtect API (probably) so some tools cannot dump image. LordPE will complain, OllyDump too, etc... LordPE has option to use Intelli's dump engine which can dump, but it will padd non-accessibile memory with zeros. Maybe that area doesn't have anything important there (maybe it doesn't have anything at all , just zeros), but it is better to dump it properly. It is not some philosophy - in Olly you just set "Full access" on every section and then you can dump it with whatever you like. Then use ImpREC to rebuild new IAT.
My dumped SDrotector now runs fine. But open Olly, run dump, try protect some file and... "Debugger detected" and Olly is closed :) He he, all that I can say to author of SDProtector is "---- you man!". I hope that reader will not take this as insult. It is more compliment to SDP developer. But I am angry. I spent two days on this protector and I'm still on a ice. We are not done yet, no.
29. Embedded protection
As said, some of those calls that points to SDP layer are part of embedded protection. SDP performs again same timer, trace and debugger checks, but it has couple new ones too. If you would now search for all intermodular calls, you would find couple (10~20) calls that point to same place in SDP layer, to this code:
00515AB3 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
00515AB7 85C0 TEST EAX,EAX
00515AB9 7D 1D JGE SHORT dump.00515AD8
00515ABB 83F8 F5 CMP EAX,-0B
00515ABE 7E 18 JLE SHORT dump.00515AD8
00515AC0 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+10]
00515AC4 8B5424 0C MOV EDX,DWORD PTR SS:[ESP+C]
00515AC8 51 PUSH ECX
00515AC9 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
00515ACD 52 PUSH EDX
00515ACE 51 PUSH ECX
00515ACF 50 PUSH EAX
00515AD0 E8 A1F7FFFF CALL dump.00515276
00515AD5 C2 1000 RETN 10
This is starting point of embedded code protection. From this place, program flow is directed to diffrent protection modules. We simply place breakpoint on this addres and run. Then we trace and we meet a first trick.
29.1. ZwSetInformationThread
After little tracing, we get here (I labeled some calls to know what should I expect):
00515232 > 56 PUSH ESI
00515233 E8 C6450000 CALL <SDProtec.IMPORT_GetVersion> <------- OS version.
00515238 3D 00000080 CMP EAX,80000000
0051523D 73 35 JNB SHORT SDProtec.00515274 <------------- Skip this if it doesn't match.
0051523F 68 1BC24000 PUSH SDProtec.0040C21B
00515244 E8 5CA1FFFF CALL <SDProtec.PREPARE_STRING>
00515249 50 PUSH EAX
0051524A 68 6CB34000 PUSH SDProtec.0040B36C
0051524F E8 51A1FFFF CALL <SDProtec.PREPARE_STRING>
00515254 50 PUSH EAX
00515255 E8 2C480000 CALL <SDProtec.API_BP_CHECK_1>
0051525A 50 PUSH EAX
0051525B E8 28E5FFFF CALL <SDProtec.GET_PROC_ADDR_1> <-------- Get proc address ZwSetInformationThread.
00515260 8BF0 MOV ESI,EAX
00515262 85F6 TEST ESI,ESI
00515264 74 0E JE SHORT SDProtec.00515274 <------------- If fails, skip.
00515266 6A 00 PUSH 0
00515268 6A 00 PUSH 0
0051526A 6A 11 PUSH 11 <-------------------------------- 11 -> Debugger cannot read memory.
0051526C E8 9D410000 CALL SDProtec.0051940E <----------------- GetCurrentThread.
00515271 50 PUSH EAX
00515272 FFD6 CALL ESI <------------------------------- ZwSetInformationThread.
00515274 5E POP ESI
00515275 C3 RETN
So here SDP checks does our OS supports ZwSetInformationThread API, then it uses it for making process memory unreadable for debugger. Patching some of those jumps that skips everything solves this issue.
29.2. EnumWindows , GetWindowTextA , FindWindowExA and how to hide Olly
Let's see what more it has in the embedded part. I labeled some procedures:
0051530F 83FF FC CMP EDI,-4 <---------------------------- Here picks what protection is triggered.
00515312 0F84 AE000000 JE SDProtec.005153C6
00515318 83FF FB CMP EDI,-5
0051531B 0F84 A5000000 JE SDProtec.005153C6
00515321 83FF FA CMP EDI,-6
00515324 0F84 9C000000 JE SDProtec.005153C6
0051532A 83FF F9 CMP EDI,-7
0051532D 75 26 JNZ SHORT SDProtec.00515355
0051532F E8 D1A1FFFF CALL <SDProtec.TIME_TRACE_EXCEPTIONS> <- Here are stanard RDTSC, EFLAGS, etc. tricks.
00515334 E8 D52C0000 CALL <SDProtec.SNAPSHOT_CHECK> <-------- Snapshot trick.
00515339 6A 00 PUSH 0
0051533B 68 E2C64000 PUSH SDProtec.0040C6E2
00515340 E8 60A0FFFF CALL <SDProtec.PREPARE_STRING>
00515345 50 PUSH EAX
00515346 E8 43450000 CALL <SDProtec.ENUM_WINDOWS> <---------- All windows tricks.
0051534B 5F POP EDI
0051534C 33C0 XOR EAX,EAX
0051534E 5E POP ESI
0051534F 83C4 40 ADD ESP,40
00515352 C2 1000 RETN 10
00515355 83FF F8 CMP EDI,-8
00515358 75 0F JNZ SHORT SDProtec.00515369
0051535A E8 D3FEFFFF CALL <SDProtec.ZW_SET_THREAD>
0051535F 5F POP EDI
00515360 33C0 XOR EAX,EAX
00515362 5E POP ESI
00515363 83C4 40 ADD ESP,40
00515366 C2 1000 RETN 10
00515369 83FF F7 CMP EDI,-9
0051536C 75 2E JNZ SHORT SDProtec.0051539C
0051536E 8D4C24 10 LEA ECX,DWORD PTR SS:[ESP+10]
00515372 51 PUSH ECX
00515373 6A 05 PUSH 5
00515375 E8 3DEAFFFF CALL <SDProtec.REGISTRATION_CHECK> <---- Checks registration info.
0051537A 85C0 TEST EAX,EAX
0051537C ^74 CD JE SHORT SDProtec.0051534B
We can see that it uses same RDTSC, GetTickCount, EFLAG, INT3 tricks again in one procedure, while in the second one it uses CreateToolhelp32Snapshot. There is no need to explain them again. Since those calls doesn't have any paramethers, we can just patch them.
EnumWindows is new and interesting. When we enter in that part, it will jump to EnumWindows API. This API returns back to some callback function - pice of code that software author coded. We can see where is that function in the stack:
016ED740 0051534B /CALL to EnumWindows from SDProtec.00515346
016ED744 005156E2 |Callback = SDProtec.005156E2
016ED748 00000000 \lParam = 0
Callback procedure , a code that will search for hostile windows etc., is at 005156E2. Procedure is too big to be pasted here and also, this part is possible to examne on dumped file.
SDP will simply enumerate all top windows one by one with EnumWindows which will then pass window handle to callback function. That function uses GetClassName to retrieve class of that window. Then class is compared with blacklisted. If blacklisted is spoted, then it calls GetWindowThreadProcessId to get thread ID and proces PID. Then it opens with OpenProcess and terminates it with TerminateProcess. If class is not suspitious, then it gets window title by using GetWindowTextA. It converts all characters of name to uppercase and then it check on blacklist. It also uses FindWindowExA.
We can see black list above callback routine:
0051557F ASCII "OLLYDBG",0 <---------------------------- Olly class name.
00515587 ASCII "UKILLOD",0 <---------------------------- Couple renamed olly's.
0051558F ASCII "FLYODBG",0
00515597 ASCII "DEBUG_API_SPY_WND_CLASS",0
005155AF ASCII "OLLYDBG -",0 <-------------------------- Olly caption when target is loaded.
005155B9 ASCII "UKILLOD -",0
005155C3 ASCII "IMPORT RECONSTRUCTOR",0
005155D8 ASCII "DFCG SUPERVER IMPORT RECONSTRUCTOR",0
005155FB ASCII "[ LORDPE DELUXE ]",0
0051560D ASCII "DFCG LORDPE",0
00515619 ASCII "FILE MONITOR - SYSINTERNALS",0
00515635 ASCII "REGISTRY MONITOR - SYSINTERNALS",0
00515655 ASCII "FLYODBG -",0
0051565F ASCII "AUTO DEBUG FOR WINDOWS",0
00515676 ASCII "MDIClient",0 <------ This string exist in Olly too , but it doesn't have to be changed(?).
00515680 ASCII "ACPU",0 <------------------------------- And these below are olly classes too.
00515685 ASCII "ACPUASM",0
0051568D ASCII "ACPUDUMP",0
00515696 ASCII "ACPUSTACK",0
005156A0 ASCII "ACPUINFO",0
005156A9 ASCII "ACPUREG",0
005156B1 ASCII "TCPU",0 <------------------------------- These ones are renamed.
005156B6 ASCII "TCPUASM",0
005156BE ASCII "TCPUDUMP",0
005156C7 ASCII "TCPUSTACK",0
005156D1 ASCII "TCPUINFO",0
005156DA ASCII "TCPUREG",0
To hide Olly (if we wish to do sow) we need to rename olly process name , couple classes and window names. We don't need to rename .ini file, or anything in it. Problem is only that plugins won't be loaded, so we need to change ollydbg.exe string in them too. After this, Olly is not detected anymore by embedded protection, neither with monitoring threads.
30. The end
And that would be all.
I just took a brief look at monitoring threads procedures. They seam to do nothing new that wasn't mentioned in this tutorial.
Registry keys , some file searches that are performed in C:\ and %WINDOWS% folders are not examned. And probably I will not make any new update to that.
Btw, here is small trick for passing exceptions checks, because they can be frustrated. When you find SEH handler, you place breakpoint at it. You are now in SEH which returns to ntdll.dll code. Problem could be to track where you should return now back in code. The easiest way is that you place memory breakpoint on access on that section. Then you break there and remove breakpoint. But this can (and probably will) set T flag to 1 , and target will crush. And you might be confused. So always after you handled exception, check T flag. If it's T=1, then set it to 0. And you can continue debugging.
SDProtector Pro 1.12 was the hardes thing that I have unpacked up to this day. Lot of tricks, but main problem is that it uses them constantly over and over. Avoiding being detected in SDProtector is like trying to run betwen rain drops - impossible. But it is very smart protector and you can learn a lot from it.
More info about SDProtector:
SDProtector 1.1 protected WinXP Notepad unpacking - by softworm (chinese tutorial)
SDProtector 1.12 - parts I,II,III,IV - by +NCR/CRC! [ReVeRsEr] (spanish tutorial)
SDProtector 1.16 - by +NCR/CRC! [ReVeRsEr] (spanish tutorial)
(c) haggar 2006
-
Level : newbie
Unwrapping eLicense 4.0
This small tut will describe removing protection layer from eLicense 4.0 protected programs. Since eLicense is not available for evaluation, I was not able to protect some mine file with it and make a target for tutorial. Because of that, I used shareware application for a target. However, I will not provide target name or downlad link in this tutorial. I want to keep it legal (if cracking tutorial can be that at all) as much as possible.
1. Intruduction
After installing protected application, I scanned install folder with PEiD 0.94. PEiD did not give any possible indication that some file is protected with eLicense protector. On the main executable of installed application it returned "Nothing found*". But in the same folder, one file is recognized as ASPack 2.12. That file was elicen40.dll . Name of this file clearly suggest that this dll is part of eLicense protection system.
Next step was loading main executable in OllyDbg. I got 4 warnings that some modules have entry point outside of code section before evaluation window showed. Why 4 modules when only one (elicen40.dll) is packed? Also , evaluation window showed altough I didn't started target.
I found that upon starting target or loading it in olly, it creates file in temporary folder. That file s1cc.2xcp , is a DLL packed with ASPack 2.12 too and it is also part of eLicense system. Second file is ofcourse elicen40.dll. Other two files are lcmmfu.cpl and mmfs.dll , both placed in windows folder, also packed with ASPack. I do not know does this two files are part of protection, but right now that is not important.
After all modules are loaded and evaluation window is showed, I clicked on "Trial" bottun and my main exe file just crushed in Olly at this point:
00453132 A0 C8BF0068 MOV AL,BYTE PTR DS:[6800BFC8]
00453137 5C POP ESP
00453138 25 450064A1 AND EAX,A1640045
0045313D 0000 ADD BYTE PTR DS:[EAX],AL
0045313F 0000 ADD BYTE PTR DS:[EAX],AL
00453141 50 PUSH EAX
00453142 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
00453149 83EC 58 SUB ESP,58
0045314C 53 PUSH EBX
0045314D 56 PUSH ESI
0045314E 57 PUSH EDI
0045314F 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
00453152 FF15 74C24500 CALL DWORD PTR DS:[45C274]
00453158 33D2 XOR EDX,EDX ; WS2_32.71AC4070
0045315A 8AD4 MOV DL,AH
0045315C 8915 8C3B4700 MOV DWORD PTR DS:[473B8C],EDX ; WS2_32.71AC4070
00453162 8BC8 MOV ECX,EAX
Later I sow that this is OEP of main protected executable. Whole file is decrypted, but couple first OEP bytes are destroyed (?) so file crushed. But OEP is found. Solution lies in elicen40.dll. Main executable is encrypted and this dll performs decryption. Protection is so simple that I didn't even bother to go deeper inside. Let's see how we can unwrapp target.
2. Unpacking DLL and finding OEP
To unpack main executable, we need to unpack elicen40.dll. I hope that you know how to unpack ASPack packed DLL. Ok, in case that you don't know, here is how:
- load elicen40.dll in Olly
- place bp on GetProcAddress and run 3 times (then remove bp), return to code
- scroll down and find
024A53AF 61 POPAD
024A53B0 75 08 JNZ SHORT elicen40.024A53BA
024A53B2 B8 01000000 MOV EAX,1
024A53B7 C2 0C00 RETN 0C
024A53BA 68 00000000 PUSH 0
024A53BF C3 RETN
- place bp on last RETN
- run to break on that bp
- press F7 to go on DLL OEP
02485415 55 PUSH EBP
02485416 8BEC MOV EBP,ESP
02485418 53 PUSH EBX ; elicen40.
02485419 8B5D 08 MOV EBX,DWORD PTR SS:[EBP+8] ; elicen40.
0248541C 56 PUSH ESI
0248541D 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C] ; elicen40.02480000
02485420 57 PUSH EDI
02485421 8B7D 10 MOV EDI,DWORD PTR SS:[EBP+10]
02485424 85F6 TEST ESI,ESI
02485426 75 09 JNZ SHORT elicen40.02485431
02485428 833D A8F84902 00 CMP DWORD PTR DS:[249F8A8],0
0248542F EB 26 JMP SHORT elicen40.02485457
02485431 83FE 01 CMP ESI,1
02485434 74 05 JE SHORT elicen40.0248543B
02485436 83FE 02 CMP ESI,2
02485439 75 22 JNZ SHORT elicen40.0248545D
- open LordPE, select loaddll.exe process, in lower pane select our elicen40.dll and make full dump
- open ImpREC, attach to loaddll.exe, click on "Pick DLL" and select our elicen40.dll
- enter your OEP=OEP-ImageBase, in my case it is 00005415=02485415-02480000
- press "IAT auto search", then "Get imports", and then "Fix dump" to rebuild IAT
- our elicen40.dll is now unpacked from ASPack
Change name of that dumped_.dll to elicen40.dll and start main exe to see did you unpacked dll correctly. Mine works good. Btw, ASPack can be unpacked without use of ImpREC and in that case we would ge clener dump. But that is not important.
This dll unpacks our target and then it jumps to OEP via one jump. But if we run target within debugger, OEP bytes will be destroyed. There is quick way around that "bug".
Load unpacked elicen40.dll in Olly. Select first line at the beggining of code section.Right click on code and select "Search for"->"command". Enter JMP DWORD [CONST] and hit "Find" button. First jump that is founded is our OEP jump:
02483CCF . FF25 5CF84902 JMP DWORD PTR DS:[249F85C]
02483CD5 > 5E POP ESI ; ntdll.7C9011A7
02483CD6 . 5D POP EBP ; ntdll.7C9011A7
02483CD7 . 5B POP EBX ; ntdll.7C9011A7
02483CD8 . 8BE5 MOV ESP,EBP
02483CDA . 5D POP EBP ; ntdll.7C9011A7
We binary change first two bytes to EB FE (infinite jump) and then we save changes:
02483CCF -EB FE JMP SHORT elicen40.02483CCF
02483CD1 5C POP ESP ; ntdll.7C9011A7
02483CD2 F8 CLC
02483CD3 49 DEC ECX
02483CD4 025E 5D ADD BL,BYTE PTR DS:[ESI+5D]
02483CD7 . 5B POP EBX ; ntdll.7C9011A7
02483CD8 . 8BE5 MOV ESP,EBP
02483CDA . 5D POP EBP ; ntdll.7C9011A7
Now , after program is unpacked, it will run in memory forever. Start main execuatble, click on "Trial" button and program will eneter in infinite jump. Now , opene Olly and just attached to target. Shift+F9 to run it, then F12 to pause it, and you are on the right place:
02483CCF -EB FE JMP SHORT elicen40.02483CCF
02483CD1 5C POP ESP ; kernel32.7C816D4F
02483CD2 F8 CLC
02483CD3 49 DEC ECX
02483CD4 025E 5D ADD BL,BYTE PTR DS:[ESI+5D]
02483CD7 5B POP EBX ; kernel32.7C816D4F
02483CD8 8BE5 MOV ESP,EBP
02483CDA 5D POP EBP ; kernel32.7C816D4F
Restore original bytes FF 25:
02483CCF -FF25 5CF84902 JMP DWORD PTR DS:[249F85C] ; target.
02483CD5 5E POP ESI ; kernel32.7C816D4F
02483CD6 5D POP EBP ; kernel32.7C816D4F
02483CD7 5B POP EBX ; kernel32.7C816D4F
02483CD8 8BE5 MOV ESP,EBP
02483CDA 5D POP EBP ; kernel32.7C816D4F
F7 to get on OEP:
0045312C > 55 PUSH EBP
0045312D 8BEC MOV EBP,ESP
0045312F 6A FF PUSH -1
00453131 68 A0C84500 PUSH target.0045C8A0
00453136 68 5C254500 PUSH target.0045255C
0045313B 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
00453141 50 PUSH EAX
00453142 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
00453149 83EC 58 SUB ESP,58
0045314C 53 PUSH EBX
0045314D 56 PUSH ESI ; ntdll.ZwQueryValueKey
0045314E 57 PUSH EDI
0045314F 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
00453152 FF15 74C24500 CALL DWORD PTR DS:[45C274]
00453158 33D2 XOR EDX,EDX ; ws2_32.71AC4070
0045315A 8AD4 MOV DL,AH
0045315C 8915 8C3B4700 MOV DWORD PTR DS:[473B8C],EDX ; ws2_32.71AC4070
00453162 8BC8 MOV ECX,EAX
00453164 81E1 FF000000 AND ECX,0FF
0045316A 890D 883B4700 MOV DWORD PTR DS:[473B88],ECX
And that is OEP! With correct bytes. Now we need to dump files and to fix imports.
3. Dumping and rebuilding IAT
First we need to dump main executable. Open LordPE and in options, under "Task viever", select "Full dump:Paste header from disk". That is important since eLicense corrupts PE header in memory. Now make a full dump.
Imports are hidden. If check one call:
00453152 FF15 74C24500 CALL DWORD PTR DS:[45C274] ; DS:[0045C274]=0015C6DD
Then we follow it:
0015C6DD 50 PUSH EAX
0015C6DE A1 38C71500 MOV EAX,DWORD PTR DS:[15C738]
0015C6E3 3305 48C71500 XOR EAX,DWORD PTR DS:[15C748]
0015C6E9 A3 485B1400 MOV DWORD PTR DS:[145B48],EAX
0015C6EE 58 POP EAX ; kernel32.7C816D4F
0015C6EF FF25 485B1400 JMP DWORD PTR DS:[145B48]
We can see that it has encrypted imports. But it is simple:
Import = Encrypted_import XOR some_constant
I just wrote one script for olly , that will restore all imports in my target. It does same thing as eLicense code - it just XOR two values. Check script and try to write one that will work for your target:
//eLicense 4.0 import fixer
var pointer
var constant
var addr
mov addr,45c000 //Start of import table.
label_01:
cmp addr,45c5f4 //End of import table.
ja end_01
mov pointer,[addr]
and pointer,0FFFF0000
cmp pointer,00160000
add addr,4
jne label_01
sub addr,4
mov pointer,[addr]
add pointer,2
mov pointer,[pointer]
mov pointer,[pointer]
mov constant,[addr]
add constant,8
mov constant,[constant]
mov constant,[constant]
xor pointer,constant
mov [addr],pointer
add addr,4
jmp label_01
end_01:
ret
Script scans import table in file and not code section.
After fixing imports, we need to use ImpREC. In ImpREC options, select also "Use PE header from disc". Then attach to target, click "IAT auto search", then "Get imports", and then "Fix dump". Find dumped.exe and fix it. Run dumped_.exe and if it's work, you have unpacked eLicense 4.0.
-
Level : newbie
Serial Fishing Tutorial #1 [by: Khaosgott96]
Hello fellow crackers and newbies. This is my VERY first tutorial I have EVER written so if u have any suggestions on making my future tutorials more informative drop me an email at:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
TOOLS USED
OllyDbg 1.09d or 1.10
Target
------
Power Archiver 9.26.01
================================================== ================================================== ===========================
Ok so first things first. Install PowerArchiver (Thats a given) and run it... a message box should pop up saying this is an evaluation version blah blah blah.....
then u have the options of clicking I agree to use it as an evaluation version or to enter the registeration information ("Enter Registraion Code...") click that and enter any name and serial into it.
I use khaosgott96 for the name and 12345678 for the registration code. Then click OK and you should get an error saying "Incorrect registration information"
So bust out a pen and paper (which, while cracking, you should always have by you) and write that error down. it is not neccessary to write the whole thing down just "Incorrect registration information" will be fine.
Now... Open up Olly and then click file/open and open up POWERARC.EXE
you should have something like the following...
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
next thing your gunna do is right click in the code window (See figure above) and click "Search For-----> All referenced text strings"
Next scroll up to the top and highlight the first line. then right click anywhere in that window and click "Search For Text"
now be sure to UNCHECK the case sensitive box. now type in the search "incorrect registration information" and press OK.
See Figure Below...
[image6]
now... click that line to make sure it is highlighted and press enter. you should now be taken back to the code window.
You now should have landed here... (See figure below)
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Now if you scroll up a bit you'll see:
006519A9 > 55 PUSH EBP
006519AA . 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
006519AD . 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
look at the picture below and compare it to the code listed above...
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
OK so if we take a look at the following code again we see:
006519A9 > 55 PUSH EBP
006519AA . 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
006519AD . 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
so highlight the line:
006519AD . 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
and press F2 to set a breakpoint. now were ready to to catch the program by its tail right before it enters the serial generation routine.
Now press F9 to run the program. (if u get any execeptions press shift+F9 until the program runs). now keep olly open and go to PowerArchiver and click "Enter Registration Code" and enter any name for the username but remember what name u used because you will need it later.
Enter 12345678 as the registration code and click OK. Olly should break and pop up. if it didnt then you didnt set your BreakPoint right, so go back and look how to set it correctly.
Otherwise just continue with the tutorial. Now if you look in the REGISTERS window in olly you will see that EDX contains out user-entered serial "12345678", so what we are going to do is set a breakpoint on it so when the program goes to access it to compare it to the real serial it will break and there in plain text we will see the REAL serial to which it is being compared to.
So then all we have to do is write that number down close olly and reopen the program normally and enter the same name and then the REAL serial that we just fished out.
"well how the f*ck do u do that??" u may ask... well i'll tell you.
First thing you are going to do is higlight the EDX resiter by left clicking it in the REGISTERS window.
Then Right click what you just highlighted and click "Follow In Dump". Now take a look at the Hex Dump Window.
You should see you User-entered serial. in our case "12345678" or in hex "31 32 33 34 35 36 37 38". So what you need to do is highlight the first 4 bytes of our user entered serial in our hex dump window.
These bytes being "31 32 33 34" then right-click the highlighted bytes and click Breakpoint----->Hardware, On Access,------->DWORD. Now what that did was tell Olly to break when it accesses our serial again.
The next time this serial will be accessesd is then it is compared to the REAL serial generated by PowerArchiver. That being said we will see what the REAL serial for our user-entered name will be. (SEE FIGURE BELOW)
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Okay.... do now you have your breakpoint set. now all you have to do is press F9 and olly should then again break. and what do we have in our register window....well we have our user-entered serial in ESI which is "12345678" and whats that right below it???....it looks like its the REAL serial that the program is checking OUR serial against. it is stored in EDI which contains "BC8097CF".... write this number down. yours will probably be different especially if you used a different name than khaosgott96.
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
Have that written down..??? GOOD. now exit Olly and open PowerArchiver by itself..no Olly this time....Sorry olly ur all alone this time.... now click Enter Registration Code and enter the name that you used when u fished out a serial. and use the serial that we fished out for the registration code. and click OK....YES!!! REGISTRATION ACCEPTED....congratulations you now successfully broke through PowerArchiver's protection scheme.