-
============================
[MSLRH] v0.2 - manually unpacking tutorial
============================
MSLRH 0.2 is quite simple protector with one interesting function. It has option to
emulate Entry Point of SFX code with signature bytes from others protectors/packers
to fool PEiD. This tutorial will try to descrybe this protector.
1. Tools and requirements>
- OllyDbg 1.10
- LordPE
- ImpREC
- Target and packer itself is here in this archive [bin]
- Tutorial was written on windows XP, don't know will it work on other systems.
2. Studing protector
First, uncheck all exceptions in olly debug options except those in kernel32.
Open our target file keygebme1.exe in Olly and take look at next picture. Ups, before
that scan file with PEiD to see what PEiD will sad about packer. PEiD gives "SVKP 1.11 ->
Pavol Cerven". PEiD is wrong. Problem is that PEiD has small signature for SVKP so it can
be very easy emulated by another packer or even you can throw it in some of yours programs.
And this is just what this packer is doing. It can emulate couple well known packers /
protectors to confuse some unexperienced cracker. But it is not hard to find that MSLRH
is what we have under our hands. Lets see how it looks:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
I think that comments on the picture are enough to understand. Tracing and examing is
hard with all this junk and I wrote simple script that will remove most of junk. Run that
script (but when you are asked to use second part of the script, select NO) and then we
will go to examne code. Now is code really easy to read and you can see that this packer
doesn't have much of it's code. First thing that we will meet is RTDSC check. After using
script, there will be lot of NOP's around here so I didn't paste whole code fom olly. Here
is interesting part:
00408081 RDTSC ; Get nuber of cycles.
00408083 PUSH EAX ; Store it to the stack.
00408084 RDTSC ; Get again number of cycles.
...
0040809D SUB EAX,DWORD PTR SS:[ESP] ; Substract last value with one that is stored in stack.
...
004080A9 ADD ESP,4 ; Pop first value from stack to free stack.
...
004080C3 CMP EAX,0FFF ; Now, compare is that difference bigger than FFF.
...
004080D2 JBE SHORT keygenme.004080EF ; If not, jump to normal program work, else go to
... exception that will crush program.
004080DE INT3
004080DF MOV AX,0FE
...
004080EC OUT 64,AX
004080EF NOP ; Here lands good jump.
...
...
RDTSC returns number of clock cyles to EAX (that is some number of instructions that has been
passed until that moment in whole system), then that value is pushed to stack and again is
called RDTSC which gives to EAX new value. Then those values are substracted. If you trace
trough program slowly, that difference will be much bigger and packer will assume that it has
been debugged. But if you just place bp on good spot and run it, that difference will be below
FFF and packer will not notice us. Not something that will slow us. Ok, there is nothing
interesting now for a while so scroll all way down up to this place:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
First packer prepares for exception handling. Ok, you may ask, what the hell I'm talking about now.
This is not tutorial about exceptions so here is a quick and short info:
"The idea of exception handling (often called "Structured Exception Handling") is that your
application installs one or more callback routines called "exception handlers" at run-time and
then, if an exception occurs, the system will call the routine to let the application deal with
the exception. The hope would be that the exception handler may be able to repair the exception
and continue running either from the same area of code where the exception occurred, or from a
"safe place" in the code as if nothing had happened."
Our packer has installed exception handler here
004089BD PUSH DWORD PTR FS:[0]
004089C4 MOV DWORD PTR FS:[0],ESP
and then it will make one exception, INT 3.
If we don't understand how to manually deal with this or any kind of exceptions, we will make
something wrong and crush our program. But we can pass this in a such way that we left program
and windows to deal with exception as they do it without presence of debugger. We can do that by
simoly pressing Shift+F9. But there we have one problem - we don't know from which point after
exception program will continue it's normall work further. Well, I know from which point it will
continue, but that is very hard to find in obfuscated programs and also, that point can be anywhere
in the code. But this packer is after removing junk really easy to follow and we can find our
point after scrolling down:
[ برای مشاهده لینک ، با نام کاربری خود وارد شوید یا ثبت نام کنید ]
This works something like PUSH-POP. Maybe I'm not quite right, but newer mind that. Below this you
will see one JB and lot of some junky code below. Take look at picture and you'll get clear all.
Code below JB is encrypted and this small loop wil just decrypt it. Do it and there you will find
procedure that will get address of GetProcAddress API, then check is it breakpoint placed on it, and
then it will enter in second decryptor loop which will decrypt another part of code below it:
00409DC0 CMP BYTE PTR DS:[EDI],0CC ; Checking for a bp on GetProcAddress.
00409DC3 JNZ SHORT keygenme.00409DCE
00409DC5 XOR ECX,ECX
00409DC7 XOR EDI,EDI
00409DC9 JMP keygenme.004089B1
00409DCE PUSH EDI
00409DCF PUSH EBX
00409DD0 XOR ECX,ECX
00409DD2 CALL keygenme.00409DD7
00409DD7 POP EDI
00409DD8 ADD EDI,1C
00409DDE MOVZX EAX,BYTE PTR DS:[ECX+EDI]
00409DE2 XOR EAX,65
00409DE5 MOV BYTE PTR DS:[ECX+EDI],AL ; Decrypting code.
00409DE8 INC ECX
00409DE9 CMP ECX,134
00409DEF JB SHORT keygenme.00409DDE
00409DF1 POP EBX
00409DF2 POP EDI
00409DF3 MOV SEG?,WORD PTR DS:[ESI+2A] ; Encrypted code which wait for decryption.
00409DF6 ADC BYTE PTR DS:[ECX],DL
After code below is decrypted, trace to there. This code is little obfuscated but you will
see that it's purpose is to get some API addresses using GetProcAddress and that it checks
for bp on every API and then it calls it. Example:
00409E12 CMP BYTE PTR DS:[EDI],0CC ; Bp check before CALL below.
00409E15 JNZ SHORT keygenme.00409E20
00409E17 XOR ECX,ECX
00409E19 XOR EDI,EDI
00409E1B JMP keygenme.004089B1
00409E20 CALL EDI ; kernel32.GetProcAddress
There are two API's that protector uses against debugging:
OutputDebugStringA - that is API that will exploit security bug in Olly and crush it. Armadillo
uses this API too. Just NOP that CALL it in our example:
00409E40 CALL EAX ; kernel32.OutputDebugStringA
IsDebuggerPresent - what to say, you know what this means. Use plugin now to hide olly, that's
the easiest way.
00409ED4 CALL EAX ; kernel32.IsDebuggerPresent
00409ED6 PUSH EAX
00409ED7 MOV EAX,DWORD PTR FS:[30]
00409EDD TEST EAX,EAX
00409EDF JS SHORT keygenme.00409EF0
After that, there is another loop that decrypts another pice of protectors code:
00409F35 MOVZX EAX,BYTE PTR DS:[ECX+EDI]
00409F39 XOR EAX,61
00409F3C MOV BYTE PTR DS:[ECX+EDI],AL
00409F3F INC ECX
00409F40 CMP ECX,1D28
00409F46 JB SHORT keygenme.00409F35
Let it decrypt and scroll waaaaaaaaaaay down to:
0040A8DE OUT 64,AX
0040A8E1 POP EAX
Does that look familiar? Yes, it is again those macro junk code, use script for cleaning
again (and again chose NO for second part). Good, everything is clear now. Again, we have
same RDTSC checks and one INT 3, just ignore all because this decrypted code there is nothing
new or interesting. Scroll down to the this place which was not encrypted from the very beggining
of protectors code:
0040C735 CALL keygenme.0040C73A
That pice of code which is little obfuscated hides procedure that decrypts original code
section of packed program. Now is time to use second part of my de-junk script. After usage
(I removed NOP's to reduce size of text) you will get picture like below. This part calculate
some CRC walue from protectors code and then it decrypts code section of packed program with
that value. It also holds some stolen bytes. Check my comments:
0040C742 POP ECX ; ECX=40C742 - address where this opcode is.
0040C74D SUB ECX,5 ; ECX=ECX-5 - it's upper limit addres for CRC calculation,
0040C75A XOR EBX,EBX it will not take bytes from this procedure for calculation.
0040C766 MOV EAX,4726 ; EAX=4726 is number of bytes of protector code.
0040C775 MOV EDI,ECX ; EDI=that upper address/end of code.
0040C781 SUB EDI,EAX ; Remove size of code because it will exclude emulated EP for PEiD trick.
0040C78D MOVZX EAX,BYTE PTR DS:[EDI] ; Start taking bytes from beggining.
0040C79A ADD EBX,EAX ; EBX will hold CRC summ.
0040C7A6 INC EDI ; Increase address.
0040C7B1 CMP EDI,ECX ; Check did we came to the end.
0040C7BD JB SHORT keygenme.0040C78D ; If not - continue calculating.
That was CRC calculation. Then it's gona start decrypting of code section of original program:
0040C7BF MOV EDI,keygenme.00401000 ; Base address of code section.
0040C7C4 MOV ECX,3000 ; Size of code section.
0040C7D3 MOVZX EAX,BYTE PTR DS:[EDI] ; Take byte from code section.
0040C7E0 ADD BL,BH ; Little caclulating here....
0040C7E2 XOR BL,BH
0040C7E4 XOR AL,BL
0040C7F0 MOV BYTE PTR DS:[EDI],AL ; And place decrypted byte instead encrypted one.
0040C7FC INC EDI
0040C807 DEC ECX
0040C812 JNZ SHORT keygenme.0040C7C9 ; Do all that again until section is decrypted.
0040C814 CALL keygenme.0040C819
0040C819 POP ECX
0040C81A SUB DWORD PTR DS:[ECX+15],EBX ; This will calculate OEP place.
0040C81D POPAD
0040C81E PUSH EBP ; Stolen bytes!
0040C81F MOV EBP,ESP
0040C821 PUSH -1
0040C823 PUSH keygenme.004040B8
0040C828 PUSH keygenme.00401F30
0040C82D PUSH 6262C0 ; This should be OEP value.
0040C832 RETN
I think that you understand everything, but few words more:
Protector has decrypted code section, but with bad CRC value because we have remove junk
code so code section is destroyed and not decrypted. Also, this last PUSH 6262C0 holds value
that subtracted with CRC value gives OEP address of packed program.
This SUB DWORD PTR DS:[ECX+15],EBX will subtract it and we should get OEP to which will last
RETN throw. But again, because we modified code, that value is incorrect and our program will
crush just for that. Also that is not quite correct OEP because we have couple stolen opcodes
from OEP, but that is not problem. How to fix that? Read next chapter.
3. Unpacking
This is really easy job now when we know what we have to do. Restart target in olly and use
IsDebuggerPresent plugin to hide olly. Then set in the command line hardware breakpoint on
execution on OutputDebugStringA API (hardware because normal-software protector can find). It
goes like this "he OutputDebugStringA" and hit ENTER button. In Olly debug options IGNORE ALL
exceptions this time, we don't need them anymore. Run target and you should break in kernel
to our debug string API:
77E9B493 >PUSH 22C
77E9B498 PUSH kernel32.77E9BE60
...
...
77E9B4D9 RETN 4
On your machine this API can look different (maybe). Remove hardware bp and instead first opcode
in API, place ast one, the RETN 4. It should look like this:
77E9B493 >RETN 4
77E9B496 NOP
77E9B497 NOP
77E9B498 PUSH kernel32.77E9BE60
...
...
77E9B4D9 RETN 4
Now press Alt+F9 to return to protector's code and scroll down. Down you will find that CRC loop
and OEP jump that is not encrypted:
0040C80F JMP SHORT keygenme.0040C812
Now just place bp on abowe opcode and run target. You will break here. Trace once with F7:
0040C812 JNZ SHORT keygenme.0040C7C9
0040C814 CALL keygenme.0040C819
0040C819 POP ECX
0040C81A SUB DWORD PTR DS:[ECX+15],EBX
0040C81D POPAD
0040C81E PUSH EBP
Place new bp on abowe place and run target again. Now code section is decrypted and you will jump
to "false" OEP. Write somewere stolen opcodes:
0040C81E PUSH EBP ; Stolen this and all below...
0040C81F MOV EBP,ESP
0040C821 PUSH -1
0040C823 PUSH keygenme.004040B8
0040C828 PUSH keygenme.00401F30
0040C82D PUSH keygenme.0040140A ; End of stolen bytes, this is value of false OEP
0040C832 RETN ; jump to false OEP
Execute last RETN, press Ctrl+A to analyse module and scroll little up:
004013FB POP EAX ; keygenme.00401F30
004013FC POP EAX ; keygenme.00401F30
004013FD POP EAX ; keygenme.00401F30
004013FE POP EAX ; keygenme.00401F30
004013FF POP EAX ; keygenme.00401F30
00401400 POP EAX ; keygenme.00401F30
00401401 POP EAX ; keygenme.00401F30
00401402 POP EAX ; keygenme.00401F30
00401403 POP EAX ; keygenme.00401F30
00401404 POP EAX ; keygenme.00401F30
00401405 POP EAX ; keygenme.00401F30
00401406 POP EAX ; keygenme.00401F30
00401407 POP EAX ; keygenme.00401F30
00401408 POP EAX ; keygenme.00401F30
00401409 POP EAX ; keygenme.00401F30
0040140A MOV EAX,DWORD PTR FS:[0]
00401410 PUSH EAX
00401411 MOV DWORD PTR FS:[0],ESP
00401418 SUB ESP,58
0040141B PUSH EBX
0040141C PUSH ESI
0040141D PUSH EDI
0040141E MOV DWORD PTR SS:[EBP-18],ESP
00401421 CALL DWORD PTR DS:[; kernel32.GetVersion
> Those POP EAX are junk and there you need to place stolen bytes. Then dump file with LordPE.
When you try to dump with LordPE, it will tell you that it can't grab process memory. Right
click in LordPE on our process, select "active dump engine", than "intelliDump" and "select".
Now dump it. You will get message that not all bytes could be dumped and that hey will be
substituted with zeros. Ok, save dump and open ImpREC. Reall OEP is 004013FB so write 13FB and
fix dump. And that's all!
4. Final words
It was not hard to unpack this version. I'm planing to write tutorial about MSLRH v0.32a which is
harder, but all in time. In the archive you will find script for de-junking and one for unpacking
this protector. Also you will find protector itself, packed target and unpacked target
-
Level : beginner
Solution for TDC's crackme #4
Name: Encrypted Password Crackme #4
Coder: TDC
Difficulty: 4/10 (easier, 2-3/10)
Cracker: Knight
Tools used: OllyDbg, PEiD, windows calculator.
First step allways the same, load it in PEiD and it sais 'Nothing found *'.
Look at linker version: '5.12'. It's masm, though it's no use to identity asm compiler. So load it Olly, put bp on GetDlgItemTextA, enter something in crackme and hit "Check". Olly breaks here:
00401258 > 6A 0C PUSH 0C ; /Count = C (12.)
0040125A . 68 5D304000 PUSH OFFSET <serial[0]> ; |Buffer = OFFSET <password.serial[0]>
0040125F . 6A 6B PUSH 6B ; |ControlID = 6B (107.)
00401261 . FF75 08 PUSH DWORD PTR [EBP+8] ; |hWnd
00401264 . E8 EF020000 CALL <JMP.&user32.GetDlgItemTextA> ; GetDlgItemTextA
00401269 . 83F8 0B CMP EAX, 0B
0040126C . 72 10 JB SHORT 0040127E
0040126E . 68 00304000 PUSH 00403000 ; /Text = "ACCESS DENIED!"
00401273 . FF35 80304000 PUSH DWORD PTR [403080] ; |hWnd = 000C022C (class='Edit',parent=000A0236)
00401279 . E8 FE020000 CALL <JMP.&user32.SetWindowTextA> ; SetWindowTextA
(That "OFFSET " is there because i use Labeler plugin, nice plugin).
As we see our serial must be 10 characters long (or less). Then we meet this:
00401292 > 50 PUSH EAX ; length
00401293 . 68 5D304000 PUSH OFFSET <serial[0]> ; serial
00401298 . E8 84010000 CALL <MainCheck>
0040129D . 0BC0 OR EAX, EAX
0040129F . 75 1F JNZ SHORT 004012C0
004012A1 . 68 0F304000 PUSH 0040300F ; /Text = "ACCESS GRANTED!"
004012A6 . FF35 80304000 PUSH DWORD PTR [403080] ; |hWnd = 000C022C (class='Edit',parent=000A0236)
004012AC . E8 CB020000 CALL <JMP.&user32.SetWindowTextA> ; SetWindowTextA
So main check must return 0 if we want to register it. Follow in MainCheck.
0040142A |. 8B45 08 MOV EAX, [ARG.1] ; eax = serial
0040142D |> 813401 674523>/XOR DWORD PTR [ECX+EAX], 1234567
00401434 |. 802401 0E |AND BYTE PTR [ECX+EAX], 0E
00401438 |. 83C1 04 |ADD ECX, 4
0040143B |. 83F9 08 |CMP ECX, 8
0040143E |.^ 75 ED JNZ SHORT 0040142D ; first 2 dwords xored with 1234567h, and first byte of each of them anded with 0Eh
00401440 |. 33C9 XOR ECX, ECX
00401442 |> 8A1401 /MOV DL, BYTE PTR [ECX+EAX]
00401445 |. 0050 08 |ADD BYTE PTR [EAX+8], DL ; counts sum of all chars to serial[8]
00401448 |. 41 |INC ECX
00401449 |. 3B4D 0C |CMP ECX, [ARG.2]
0040144C |.^ 75 F4 JNZ SHORT 00401442
0040144E |. 33C9 XOR ECX, ECX
00401450 |> /813401 DEBC9A>/XOR DWORD PTR [ECX+EAX], 89ABCDE
00401457 |. |802401 0E |AND BYTE PTR [ECX+EAX], 0E
0040145B |. |83C1 04 |ADD ECX, 4
0040145E |. |83F9 08 |CMP ECX, 8
00401461 |.^75 ED JNZ SHORT 00401450 ; same as before, only xor with 89ABCDEh
00401463 |. 33C9 XOR ECX, ECX
00401465 |> 8A1401 /MOV DL, BYTE PTR [ECX+EAX]
00401468 |. 0050 09 |ADD BYTE PTR [EAX+9], DL ; again sum all char, this time in serial[9]
0040146B |. 41 |INC ECX
0040146C |. 3B4D 0C |CMP ECX, [ARG.2]
0040146F |.^ 75 F4 JNZ SHORT 00401465
00401471 |. 8A50 09 MOV DL, BYTE PTR [EAX+9]
00401474 |. 8A70 08 MOV DH, BYTE PTR [EAX+8]
00401477 |. 66:81FA DE42 CMP DX, 42DE
0040147C |. 0F85 8E000000 JNZ <BadBoy>
So we see that first sum (serial[8]; [eax+8]) must be 42h, and second (serial[9]; [eax+9]) must be 0DEh.
Then we meet loop which does nothing and one more check these sums (the same).
004014A3 |. 8A08 MOV CL, BYTE PTR [EAX] ; cl = 08
004014A5 |. 8A68 01 MOV CH, BYTE PTR [EAX+1] ; ch = B0
004014A8 |. 66:81C1 9235 ADD CX, 3592 ; cx = B008
004014AD |. 66:81F9 9AE5 CMP CX, 0E59A
004014B2 |. 75 49 JNZ SHORT <BadBoy>
004014B4 |. 8138 08B0817A CMP DWORD PTR [EAX], 7A81B008
004014BA |. 75 4B JNZ SHORT <BadBoy>
As we see it checks first dword from encrypted serial. And it must be 7A81B008h.
Then follows useless loop and we see last important to us thing:
004014CA |. 8178 04 02BF8>CMP DWORD PTR [EAX+4], 388DBF02
004014D1 |. 75 3D JNZ SHORT <BadBoy>
So second encrypted dword must be 388DBF02h. The rest in crackme is checks already done or useless junk. So lets start reversing it. First is and'ing.
First dword(byte from first dword): x & 0Eh = 08; x = ?8/?9
Second dword(byte from second dword): x & 0Eh = 02; x = ?2/?3
We can't get the exact values. Then is xoring:
First: 7A81B0?8/7A81B0?9 ^ 89ABCDE = 721B0C?6/721B0C?7
Second: 388DBF?2/388DBF?3 ^ 89ABCDE = 301703?C/301703?D
So we passed first loop. If we look now to first we see that here we from above result valid are 721B0C06 and 3017030C, because their least significant byte was and'ed with 0Eh, u can't get 07/0D, nor something like 47. So the first loop:
First: x & 0E = 06; x = ?6/?7
Second: x & 0E = 0C; x = ?C/?D
Xor'ing:
First: 721B0C?6/721B0C?7 ^ 1234567 = 733849?1/733849?0
Second: 301703?C/301703?D ^ 1234567 = 313446?B/313446?A
So convert it to chars we get (don't forget inverse byte order! little endian!):
First: ?I8s
Second: ?F41
In place of "?" we can enter anything what maches our pattern (?1/?0, ?B/?A).
So patern for our serial should be (only alphanumeric chars):
{0, 1, A, a, P, p, Q, q}I8s{J,j K, k, Z, z}F41
Try something lik 0I8sJF41. WTF? It doesn't works. If u haven't forgotten there are checked encrypted serial byte sums. First one is F5h (must be 42h) and second is 39h (must be DEh). Where's the problem? Serial length is not 8, but 10. So last char is (the one which holds second sum):
2*(39h + 42h + x) = DE; x = 74h ('t')
39h is sum of first 8 bytes, 42h is first sum, multiply by 2 because this byte is also added. Then we can count next to last:
2*(F5h + x) + 74h = 42h; x = 72h ('r')
So now we got real pattern:
{0, 1, A, a, P, p, Q, q}I8s{J,j K, k, Z, z}F41rt
Try 0I8sJF41rt. It works.
-
======================================
PolyCrypt PE 2.1.4/2.1.5 - manually unpacking tutorial
======================================
I heard for this packer at PEiD forum where it's author was bragging about his untedectable packer.
Ofcourse, that is not so true and this packer can be recognized like any other. Crypter is not hard
to unpack, altough it has some tricks. It has standard debug check which you can avoid using HideOlly
or IsDebuggerPresent plugins, and it has file locking what can be frustrating to fix, but lucky for us,
we can bypass this.
You need :
- OllyDbg 1.10
- ImpREC
- LordPE
Ok, grab target and load it in olly. Ignore all exceptions under olly options. You are here:
0040700D > 60 PUSHAD
0040700E E8 EDFFFFFF CALL RICHEDIT.00407000
00407013 ^EB F3 JMP SHORT RICHEDIT.00407008
00407015 864D 86 XCHG BYTE PTR SS:[EBP-7A],CL
00407018 D323 SHL DWORD PTR DS:[EBX],CL
0040701A 864B E2 XCHG BYTE PTR DS:[EBX-1E],CL
...
...
Trace with F7 two times and you'll climb up a little to the beggining of section:
00407000 91 XCHG EAX,ECX
00407001 8BF4 MOV ESI,ESP
00407003 AD LODS DWORD PTR DS:[ESI]
00407004 FEC9 DEC CL
00407006 803408 E0 XOR BYTE PTR DS:[EAX+ECX],0E0
0040700A ^E2 FA LOOPD SHORT RICHEDIT.00407006
0040700C C3 RETN
...
...
That is the first decryptor loop. Place bp on RETN and run target. Remove bp and take look below:
00407015 66:AD LODS WORD PTR DS:[ESI]
00407017 66:33C3 XOR AX,BX
0040701A 66:AB STOS WORD PTR ES:[EDI]
0040701C 02DF ADD BL,BH
0040701E 86DF XCHG BH,BL
00407020 66:D1CB ROR BX,1
00407023 66:43 INC BX
00407025 ^E2 EE LOOPD SHORT RICHEDIT.00407015
00407027 C3 RETN
That is another decryptor loop. Place bp on RETN again, press F9 two times to decrypt code and then
remove bp. Now our target is fully decrypted and we can search something that would indicate where
we can find OEP. Take look at this part of code:
00407407 32C0 XOR AL,AL
00407409 B9 33010000 MOV ECX,133
0040740E 8B85 396D4000 MOV EAX,DWORD PTR SS:[EBP+406D39]
00407414 0385 516D4000 ADD EAX,DWORD PTR SS:[EBP+406D51]
0040741A 8985 2B664000 MOV DWORD PTR SS:[EBP+40662B],EAX
00407420 8B0424 MOV EAX,DWORD PTR SS:[ESP] ; RICHEDIT.00407077
00407423 64:67:A3 0000 MOV DWORD PTR FS:[0],EAX
00407428 83C4 08 ADD ESP,8
0040742B 5D POP EBP ; RICHEDIT.00407077
0040742C 9D POPFD
0040742D 61 POPAD
0040742E 68 00000000 PUSH 0
00407433 C3 RETN
This POPFD and POPAD looks very interesting, here packer pops all flags and registeres. Place bp
on that RETN and use olly plugin to hide debugger, then run target. After some time olly will
break on our bp. Notice that PUSH opcode above RETN has changed it's value:
0040742E 68 6E1B4000 PUSH RICHEDIT.00401B6E
00407433 C3 RETN
That value is address where we will return now. Execute RETN with F8 and you'll land in code section
on OEP (press Ctrl+A then):
00401B6E . 6A 00 PUSH 0
00401B70 . E8 BB0E0000 CALL RICHEDIT.00402A30
00401B75 . A3 E8414000 MOV DWORD PTR DS:[4041E8],EAX
00401B7A . E8 9F0E0000 CALL RICHEDIT.00402A1E ; [GetCommandLineA
00401B7F . A3 E0414000 MOV DWORD PTR DS:[4041E0],EAX
00401B84 . E8 DD0E0000 CALL RICHEDIT.00402A66 ; [InitCommonControls
00401B89 . C705 08424000 >MOV DWORD PTR DS:[404208],1
00401B93 . C705 0C424000 >MOV DWORD PTR DS:[40420C],0
Ha, that was easy indeed. Yes but try dump file now with LordPE or OllyDump, or try use ImpREC. LordPE
will sad "Cannot paste original PE header", OllyDump "cannot create file" and ImpREC "Invalid PE header".
So what's the problem with PE header? Open memory window in olly and double click on PE header to see
what we have there. Ups, something weird is happening there, Olly is slowing down like hell and have
problems showing PE header. What's going on? Wait some time for olly to back to normal and don't scroll
else Olly will again go crazy, and then right click and select hex -> 16 bytes. Now you'll see dump without
problem. Take look and will see that, instead of sections info, packer has filled PE header with lot of
FFFFFFF bytes. That makes olly behave weird but that shouldn't prevent LordPE from reading header because
all tools read header from hard disk. So what's the problem? Don't close olly and try to open same target
file with another instance of Olly, you'll fail. Ok, reason is that this packer has locked file to prevent
opening by any tool or proces while first istance of program is running. We could just copy original header
as we open target first time in olly and the just paste it when we reach OEP, but still we cannot dump file
due to this locking feature. There is one more thing that could be problem. Scroll down to the import jumps
and notice that some jumps leads to packer section instead to dll's:
00402A06 $-FF25 0C314000 JMP DWORD PTR DS:[40310C] ; USER32.UpdateWindow
00402A0C $-FF25 64304000 JMP DWORD PTR DS:[403064] ; RICHEDIT.004078F6
However, altough this is not imports redirection in a way that is purpose is to hide imports, it still can
give us problems. I dumped couple files and most of them worked properly jumping to packer section and
returning back, but some files crashed after that. Thing is that in case of locking file, packer provides
support for opening file by hooking some API's like GetModuleHandle, CreateFileA, etc. in case that file
needs access to itself. You can read that in help file of packer ;)
And now unpacking.
Lucky for us, packer provides options for locking and hooking, and in packer code it has simple JNZ jumps
to jump over that features if they are not wanted. So we can just change couple jumps and get non-locked
, non-hooked file that we can dump with ease. Restart target in Olly again and pass that two decryptor loops.
Packer is very anoying to trace because it uses lot of INT 3 exceptions. If you pack file about 4MB is size,
it takes lot of time to reach OEP because of that. Ok, here you will find two debug checks, this is first one:
00407130 33C0 XOR EAX,EAX
00407132 64:8B40 30 MOV EAX,DWORD PTR FS:[EAX+30]
00407136 0FB658 02 MOVZX EBX,BYTE PTR DS:[EAX+2]
0040713A 0ADB OR BL,BL
0040713C 0F85 28030000 JNZ RICHEDIT.0040746A
this one is second:
004070CD 64:67:A1 3000 MOV EAX,DWORD PTR FS:[30]
004070D2 0FB658 02 MOVZX EBX,BYTE PTR DS:[EAX+2]
004070D6 0ADB OR BL,BL
004070D8 0F85 8C030000 JNZ RICHEDIT.0040746A
But interesting part is how we can avoid file locking and API hookis. Scroll here:
0040719F 80BD 316E4000 00 CMP BYTE PTR SS:[EBP+406E31],0
004071A6 74 47 JE SHORT RICHEDIT.004071EF
004071A8 68 04010000 PUSH 104
004071AD 6A 40 PUSH 40
004071AF FF95 056C4000 CALL DWORD PTR SS:[EBP+406C05] ; RICHEDIT.00406CD5
004071B5 8985 336E4000 MOV DWORD PTR SS:[EBP+406E33],EAX
004071BB 68 04010000 PUSH 104
004071C0 FFB5 336E4000 PUSH DWORD PTR SS:[EBP+406E33]
004071C6 6A 00 PUSH 0
004071C8 FF95 F16B4000 CALL DWORD PTR SS:[EBP+406BF1] ; RICHEDIT.00406C8C
004071CE 6A 00 PUSH 0
004071D0 6A 00 PUSH 0
004071D2 6A 04 PUSH 4
004071D4 6A 00 PUSH 0
004071D6 6A 00 PUSH 0
004071D8 68 00000080 PUSH 80000000
004071DD FFB5 336E4000 PUSH DWORD PTR SS:[EBP+406E33]
004071E3 FF95 ED6B4000 CALL DWORD PTR SS:[EBP+406BED] ; RICHEDIT.00406C80
This part is responsable for locking part. Check at the beggining of this procedure just checking does
file needs to be locked. Just follow that address in memory and place 0 byte instea1 and jump will be
executed and file will not be locked. Now you can dump file without problem when you find OEP. With
API hooks is similar thing:
0040737D 80BD 326E4000 01 CMP BYTE PTR SS:[EBP+406E32],1
00407384 75 5E JNZ SHORT RICHEDIT.004073E4
00407386 3B85 0D6C4000 CMP EAX,DWORD PTR SS:[EBP+406C0D] ; RICHEDIT.00406CEC
0040738C 75 08 JNZ SHORT RICHEDIT.00407396
0040738E 8D85 40674000 LEA EAX,DWORD PTR SS:[EBP+406740]
00407394 EB 4E JMP SHORT RICHEDIT.004073E4
00407396 3B85 1D6C4000 CMP EAX,DWORD PTR SS:[EBP+406C1D] ; RICHEDIT.00406D19
0040739C 75 08 JNZ SHORT RICHEDIT.004073A6
0040739E 8D85 026A4000 LEA EAX,DWORD PTR SS:[EBP+406A02]
004073A4 EB 3E JMP SHORT RICHEDIT.004073E4
004073A6 3B85 ED6B4000 CMP EAX,DWORD PTR SS:[EBP+406BED] ; RICHEDIT.00406C80
004073AC 75 08 JNZ SHORT RICHEDIT.004073B6
004073AE 8D85 726A4000 LEA EAX,DWORD PTR SS:[EBP+406A72]
004073B4 EB 2E JMP SHORT RICHEDIT.004073E4
004073B6 3B85 D16B4000 CMP EAX,DWORD PTR SS:[EBP+406BD1] ; RICHEDIT.00406C29
004073BC 75 08 JNZ SHORT RICHEDIT.004073C6
004073BE 8D85 256B4000 LEA EAX,DWORD PTR SS:[EBP+406B25]
004073C4 EB 1E JMP SHORT RICHEDIT.004073E4
004073C6 3B85 F16B4000 CMP EAX,DWORD PTR SS:[EBP+406BF1] ; RICHEDIT.00406C8C
004073CC 75 08 JNZ SHORT RICHEDIT.004073D6
004073CE 8D85 566B4000 LEA EAX,DWORD PTR SS:[EBP+406B56]
004073D4 EB 0E JMP SHORT RICHEDIT.004073E4
004073D6 3B85 F96B4000 CMP EAX,DWORD PTR SS:[EBP+406BF9] ; RICHEDIT.00406CB7
004073DC 75 06 JNZ SHORT RICHEDIT.004073E4
004073DE 8D85 F26A4000 LEA EAX,DWORD PTR SS:[EBP+406AF2]
004073E4 8907 MOV DWORD PTR DS:[EDI],EAX
Just change byte at SS:[EBP+406E32] to 0 and there will be no hooked API's. Now you can use ImpREC to
repair IAT. Do all that and your unpacked target should run just nice :)
In the archive you'll find script for unpacking this version of PolyCrypt along with PEiD signatures
for this version.
-
-/--------------/-
Key Generating Tutorial - TDC #1
-/--------------/-
Written by: TDC - The Dutch Cracker
Tools used: OllyDbg and MASM32
Level: 1/10
Keygenning TDC #1 crackme. (Source code)
Contact info:
----------------
ICQ: 264541651
Introduction:
----------------
Hello all and welcome to this tutorial. I rated this one level 1/10 because I'll explain most things you need, so this would be a good newbie tutorial I hope.
The tutorial:
----------------
Open up OllyDbg and then open "keygenme.exe" or right-click "keygenme.exe" and "Open with OllyDbg". If you don't have the right-click open function then let OllyDbg add it for you. Start-up OllyDbg, go to the Options menu and select "Add to Explorer" there you will find it.
Ok, press F9 (don't set breakpoints yet), and explore the executable. You should see three editboxes, where you will have to fill in two of them. Got that? Good, otherwise pick a cookie :-)
Now, how would the executable get our name and serial? With an API, I'm sure! So... what API then? I'm going to tell you, some basic API's for getting text from editboxes are "GetDlgItemText" and "GetWindowText", they get a value from an editbox and store it somewhere.
How do we find out where it gets our name and serial from the editboxes? Most easy way is setting up some breakpoints on the API's, when the program uses the API to get the text from the editboxes it uses the API, and thus OllyDbg will break. Then you have the right API in front of your eyes.
Right, how do we set up a breakpoint on the API? That's easy, press the lightblue E in OllyDbg or ALT-E on your keyboard. Then right-click our executable from the list and press "View Names" from the menu, or do one left-click on the executable and then CTRL-N on your keyboard. Now you are in a window with the API's that the program uses. You can scroll down to setup breakpoints on GetDlgItemText and GetWindowText, but just typing in the key sequence of the API name is easier, because OllyDbg will then bring you to the API :-)
Found the API GetDlgItemText in the list? Right-click it and press the menu option "Set breakpoint on every reference". Then setup a breakpoint on GetWindowText. (Note: the API's might be called GetDlgItemTextA and GetWindowTextA, the A means 32-bit)
Okay, have you setup the breakpoint? Good. Insert your desired registration name and a fake serial that is easy to remember. Press the Check button. Yay! OllyDbg breaks immediately, the program is now totally under your control, feels powerfull eh :-)
We are now on the point where the call to the API is about to get executed. I have made a dead-listing of the lines with W32Dasm. You don't need W32Dasm for this tutorial.
--- DEAD-LISTING --- BEGIN ---
:00401168 6A5A push 0000005A ; push 90 (5A = 90 in decimal)
:0040116A 68CA644000 push 004064CA ; push address where serial will be stored
:0040116F 6A6C push 0000006C ; push 108, the ID of the editbox serial
:00401171 FF7508 push [ebp+08] ; push window handle from dialogbox of where to get it from
* Reference To: user32.GetDlgItemTextA, Ord:00F4h
|
:00401174 E8D5010000 Call 0040134E ; call the actual API
:00401179 E836010000 call 004012B4 ; call registration procedure
:0040117E 68CA644000 push 004064CA ; push our serial
:00401183 6890634000 push 00406390 ; push right serial
* Reference To: kernel32.lstrcmpiA, Ord:02B9h
|
:00401188 E809020000 Call 00401396 ; call API to compare both serials, our serial and the valid one
--- DEAD-LISTING --- END ---
Do you see the second push? That is where your serial will be stored. Why and how do we know? Because if you look at the API reference you will see it, let me explain:
UINT GetDlgItemText(
HWND hDlg,
int nIDDlgItem,
LPTSTR lpString,
int nMaxCount
);
When assembled, the API from MASM32 like "invoke GetDlgItemText, hWin, 108, offset szSerial, 90" will look like this:
push 5A
push keygenme.4064CA
push 6C
push dword ptr [ebp+08]
call GetDlgItemTextA
That's how it looks in OllyDbg. See? It gets assembled in reverse order, what gets pushed first is the last value that the API will receive, so the ADDR 4064CA is where our serial is stored.
Scroll up a bit (I didn't include this part on the dead-listing). Then, do you see the "cmp eax, 4 " instruction? Well, eax will be the length of your name, the API GetDlgItemText sets eax. So it compares namelength with 3? Yeap! That is correct. What happens next? The "jl" instruction, it means "jump if less". I checked for you where it jumps to. It jumps to a SetWindowText API that sets the text "Please enter more than 3 characters...". So our name must be 4 characters at least, go and change it if it wasn't three characters. To change the name press F9 in OllyDbg and the program will continue. If you look further you also notice a "cmp eax, 9" and a "jge" instruction, so our username may not be 9 characters long, we need a username between 3 - 9 characters, but not 3 and 9 themselves.
Ok, now we are ready to check what is done with our name and how it checks if we entered the correct serial. Hmm, you see that "call" instruction below the "jl" instruction? Press F8 in OllyDbg to step to it, and then press F7 when you are on the call, so it jumps INTO it. I guessed right, here the program generates the valid serial and compares it to ours. How do you know that TDC? Heh Heh! I coded the program that we are keygenning here ;-) Otherwise we would just stepped over the call with F8 and then check what goes on next till we see it gets compared, and then we would reverse it, check where our valid serial came from :-)
We will see a call to the API lstrlen, it will check the length of a value and store it into eax, hmm... the push before the call, isn't that the address to our name? Yes, it is the same as in the GetDlgItemText API. So... why didn't the author (me) store the length after the GetDlgItemText and use that? Dunno, he probably forgot or thought we would change our name after the API's in OllyDbg, or he just wanted to make this keygenme clear for newbies. Yes, I did it to make it clear for newbies :-) Aha, good... let's move on. NO! Wait, turn on your favourite music first if you want and then proceed :-)
I'll make a small dead-listing of the code with W32Dasm so I can point to addresses and I'll comment it here and there.
--- DEAD-LISTING --- START ---
* Referenced by a CALL at Address:
|:00401179
|
:004012B4 53 push ebx ; save ebx, ebx is a register that needs to be saved
:004012B5 57 push edi ; save edi, same story for edi
:004012B6 6870644000 push 00406470 ; push our username string
* Reference To: kernel32.lstrlenA, Ord:02BFh
|
:004012BB E8DC000000 Call 0040139C ; call lstrlen API to get length of name
:004012C0 8BD0 mov edx, eax ; move length to edx
:004012C2 33C9 xor ecx, ecx ; clear ecx
:004012C4 33DB xor ebx, ebx ; clear ebx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004012D5(C)
|
:004012C6 0FB68170644000 movzx eax, byte ptr [ecx+00406470] ; move username character into eax
:004012CD 83C00A add eax, 0000000A ; add 10 decimal
:004012D0 03D8 add ebx, eax ; add eax to ebx ; ebx = the total calculation
:004012D2 41 inc ecx ; increase counter for ecx to get next username character
:004012D3 3BCA cmp ecx, edx ; are we at end of name?
:004012D5 75EF jne 004012C6 ; if not so, go back and pick next character
:004012D7 33C9 xor ecx, ecx ; clear ecx
:004012D9 53 push ebx ; save ebx for later use?
:004012DA 33DB xor ebx, ebx ; clear ebx
:004012DC 81C3596F7500 add ebx, 00756F59 ; add 7696217 decimal to ebx
:004012E2 53 push ebx ; save ebx
:004012E3 33DB xor ebx, ebx ; clear ebx
:004012E5 81C3616C6D00 add ebx, 006D6C61 ; add 7171169 decimal to ebx
:004012EB 53 push ebx ; save ebx
:004012EC 33DB xor ebx, ebx ; clear ebx
:004012EE 81C36F737400 add ebx, 0074736F ; add 7631727 decimal to ebx
:004012F4 53 push ebx ; save ebx
:004012F5 33DB xor ebx, ebx ; clear ebx
:004012F7 81C364696400 add ebx, 00646964 ; add 6580580 decimal to ebx
:004012FD 53 push ebx ; save ebx
:004012FE 33DB xor ebx, ebx ; clear ebx
:00401300 81C369742100 add ebx, 00217469 ; add
:00401306 53 push ebx ; save ebx
:00401307 33DB xor ebx, ebx ; clear ebx
:00401309 81C321212100 add ebx, 00212121 ; add
:0040130F 53 push ebx ; save ebx
:00401310 33DB xor ebx, ebx ; clear ebx
:00401312 5B pop ebx ; get ebx again from the last point where it was saved
:00401313 5B pop ebx ; get next ebx from where it was saved
:00401314 5B pop ebx ; and so on...
:00401315 5B pop ebx ; ...
:00401316 5B pop ebx ; ...
:00401317 5B pop ebx ; ...
:00401318 5B pop ebx ; ... till here ; 7 pops, hmm, 7 pushes also, ebx didn't change from first push
:00401319 69DB697A0000 imul ebx, 00007A69 ; do a last calculation to it, multiply with 31337
:0040131F 53 push ebx ; push ebx for wsprintf API
* Possible StringData Ref from Data Obj ->"%d"
|
:00401320 68D0634000 push 004063D0 ; push string %d
:00401325 6890634000 push 00406390 ; push ADDR of where the transformation will be saved
* Reference To: user32.wsprintfA, Ord:0262h
|
:0040132A E807000000 Call 00401336 ; call the actual API
--- DEAD-LISTING --- END ---
Here it moves the first character in eax, does some calculations with it, and adds the value to ebx, ebx will be the total serial, each loop the calculation based on a character of our name will be added to ebx. After that we will see a wsprintf API, here it turns the calculation into decimal string. Look at the API to understand it.
int wsprintf(
LPTSTR lpOut,
LPCTSTR lpFmt,
...
);
If you check it in OllyDbg you see this:
push ebx
push %d
push output address
call wsprintf
Here ebx is our total serial from the algorithm, %d means it'll turn into a decimal string, for example %X would turn them into HEX. And the output address will be in this case the final serial.
That is ALL we need, no more, no less. We know how the algorithm works so we can code our keygenerator.
First, I will make the design of the keygenerator and then the ASM source. With the design I mean the resource file of course :-)
Look at the zipfile for the resource file called "rsrc.rc". Open it with your favourite text editor. I have commented it.
Have you got that? Nice! Let's code the keygenerator itself.
After the user presses the Generate button, the generating procedure will be initiated. To make it easy I made a procedure called Generate of it in the ASM source, so we can call it when the user presses Generate.
Just extract the whole zipfile into one directory in case you haven't already done that. You should take a look at the ASM source and the resource file to see how it all works if you want that. I commented both files pretty thorough (even the make.bat file) so even people complete new to keygenning could understand it.
Now run "make.bat". Just double-click it and you will see a "keygen.exe" appearing. If not, then you probably don't have MASM32 installed at the same drive as where you extracted the zipfile. Good, test the keygenerator, insert a name and press Generate. Yay! Theres a serial, would it work? I'm sure it does :-)
-
Level : intermediate
PESpin v1.0 , 1.1 & 1.3 - manually unpacking
This is a detailed tutorial about manually unpacking a couple of PESpin versions.(discussing crypted parts, IAT redirection, stolen bytes, ...). Inside archive you'll find tutorial, targets, scripts and some more stuff.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PESpin v1.x -manually unpacking
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Hi and welcome to my new unpacking tutorial.
This tutorial is about manually unpacking PESpin 1.x versions, to be more precisely about 1.0, 1.1 and 1.3 versions. However, this tutorial has some limits:
- PESpin v0.7 will not be described because there is no need for that. If you know how to unpack version 1.3, then you know how to unpack all previous versions.
- Version 1.2 is private version not available to public so I was not able to examne this one.
- PESpin v1.3 have option for advanced code redirection which is not available in public version so this tutorial will not cover that kind of protection (if you find target packed with that option, send me a PM on the forum).
- All unpacked targets in this tutorial runs only on machine on which they where unpacked. More about that read in the first tutorial.
Tutorial is for advanced beginners. You must have basic knowledge about PE file structure and how to use usuall unapcaking tools.
Content:
1. Tools and useful stuff
2. Tutorial 1 - PESpin v1.0
3. Tutorial 2 - PESpin v1.1
4. Tutorial 3 - PESpin v1.3
5. Final words and thanks
1. Tools and useful stuff
- OllyDbg 1.10
- LordPE
- Hex Editor
In this archive you will find two folders: "PESpin scripts" and "PESpin signatures". First folder holds some useful scripts for unpacking PESpin. Those scripts are far away from perfect, but they could hep you a bit. More about them later, or just open them in notepad and read info. In the second folder you will find signatures for PEiD. If you scan with PEiD some file that is packed with some PEspin 1.x version, it will not recognize it properly. Just copy-paste those signatures to userdb.txt that is in PEiD folder, use external scan on packed file and PEiD will recognize real PESpin version. If you notice some bugs, mail or PM me.
There is some good papers that you should read if you want understand this tutorial better:
- Descryption of PE format by Michael J.O'Leary ; I think that this can be found on old BIW site.
- PORTABLE EXECUTABLE FILE FORMAT by Goppit ; .
- unpacking PESpin 0.3 because new versions are improved previous.
2. Tutorial 1 - PESpin v1.0
In this version, PESpin has two new tricks; API bp check and code redirection. Grab first target packed1.0.exe and open it in Olly (set ignore ALL exceptions). Finding OEP and blocking imports redirection is similar as we done it in 0.3 version.
Protector has a check on API breakpoints so open "Executable Modules" window, select kernel32.dll and click "view names".
Find GetTickCount and double click on it. You should be in kernel32 now:
77E7A1EE> BA 0000FE7F MOV EDX,7FFE0000
77E7A1F3 8B02 MOV EAX,DWORD PTR DS:[EDX]
77E7A1F5 F762 04 MUL DWORD PTR DS:[EDX+4]
77E7A1F8 0FACD0 18 SHRD EAX,EDX,18
77E7A1FC C3 RETN
Place bp on RETN opcode and run targed. You will break on bp in kernel.
Remove bp and return to target code:
00409D17 8BD8 MOV EBX,EAX
00409D19 F7D3 NOT EBX
00409D1B 33D8 XOR EBX,EAX
00409D1D 43 INC EBX
...
...
Now scroll waaaaaay up and find this place:
00409109 F9 STC ;Can be CLC (E8hex).
0040910A 72 0D JB SHORT packed1_.00409119 ;Place bp here!!!
0040910C 8D85 0660271E LEA EAX,DWORD PTR SS:[EBP+1E276006]
00409112 2D 8417E71D SUB EAX,1DE71784
00409117 FFD0 CALL EAX ;Call to timer function.
00409119 EB 01 JMP SHORT packed1_.0040911C
This is the last place before jumping to OEP, just like in 0.3 version except here is little different.
If target is packed with timer option, instead of first STC opcode there will be CLC=E8hex.
In that case JB will not be executed and program flow will continue to CAL EAX where timer thread is created.
This place is same for all next PESpin versions. You just place bp on JB opcode (don't run target).
Scroll up again and take a look at this part of code:
0040905F JMP DWORD PTR SS:[ESP-4] ;Important jump, place bp here.
00409063 CALL FAR EB00:000003E8
...
...
...
...
0040909B ???
0040909C CALL packed1_.004089F2
004090A1 CALL packed1_.004090A9
Place bp at that jump and run target. When you break on it, you'll see that jump leads to 409022 address.
That jump is resposible for IAT redirecting.
If there are no IAT redirecting, that jump will jump to 40909C address to that CALL and skip all redirection procedure.
So to avoid IAT redirecting, you just patch all those bytes between jump (you can patch it too) and CALL at 40909C:
0040905F NOP
00409060 NOP
...
...
...
0040909A NOP
0040909B NOP
0040909C CALL packed1_.004089F2
004090A1 CALL packed1_.004090A9
004090A6 JMP SHORT packed1_.004090AC
Now prss F9 again to land on previous bp:
00409109 STC
0040910A JB SHORT packed1_.00409119 ;You're here!!!
0040910C LEA EAX,DWORD PTR SS:[EBP+1E276006]
00409112 SUB EAX,1DE71784
00409117 CALL EAX
00409119 JMP SHORT packed1_.0040911C
Now, if first opcode instead STC is CLC, patch all instructions at 0040910C,00409112 and 00409117 to prevent calling timer fuction.
Acctualy, you can always patch that instructiones, it will change nothing in that case. Then scroll down and search for POPAD opcode.
You won't see it so search for 61hex byte in some instruction. I should be here:
00409278 E8 616A00EB CALL EB40FCDE
Patch first byte, place bp on POPAD and run:
00409278 90 NOP ;Patch this byte.
00409279 61 POPAD ;Place bp here.
0040927A C1E2 F3 SHL EDX,0F3
0040927D 23D0 AND EDX,EAX
0040927F 0FC1D1 XADD ECX,EDX
00409282 BA 69533D94 MOV EDX,943D5369
00409287 69C8 262C4AF2 IMUL ECX,EAX,F24A2C26
0040928D 11C2 ADC EDX,EAX
0040928F 0FBDD0 BSR EDX,EAX
00409292 C7C2 BDCC6DB9 MOV EDX,B96DCCBD
00409298 42 INC EDX
00409299 FFC2 INC EDX
0040929B C1D2 83 RCL EDX,83
0040929E 0FBCC8 BSF ECX,EAX
004092A1 23D0 AND EDX,EAX
004092A3 39C2 CMP EDX,EAX
004092A5 81E9 B576A23F SUB ECX,3FA276B5
004092AB EB 01 JMP SHORT packed1_.004092AE
004092AD E7 0F OUT 0F,EAX
004092AF AF SCAS DWORD PTR ES:[EDI]
004092B0 C8 0FB7D0 ENTER 0B70F,0D0
004092B4 C7C1 0EE985D6 MOV ECX,D685E90E
004092BA F3: PREFIX REP:
004092BB F7C1 1E8ABDAB TEST ECX,ABBD8A1E
004092C1 -E9 3A7DFFFF JMP packed1_.00401000
You're now at the last part of the protectors code. Here you can find the stolen OEP bytes mixed with junk code.
I didn't chose to remove OEP when I packed file so here is only junk to confuse you.
No matter if you have stolen OEP or not, you can always dump file when you reach first opcode after POPAD one and dumped file will normally work.
It doesn't matter that OEP is in the protectors code and that target execution starts from here.
But if you want you can always find stolen bytes, restore it in original place and dump from there.
Now, just trace untill you get to some jump that leads to the code section:
004092C1 -E9 3A7DFFFF JMP packed1_.00401000
In this case jump leads straight to OEP:
00401000 . 6A 00 PUSH 0
00401002 . E8 F1F1FFFF CALL packed1_.004001F8
00401007 . A3 CA204000 MOV DWORD PTR DS:[4020CA],EAX
0040100C . 6A 00 PUSH 0
0040100E .-E9 EBF1FFFF JMP packed1_.004001FE
00401013 . E8 F1F1FFFF CALL packed1_.00400209
00401018 . 0BC0 OR EAX,EAX
0040101A . 74 01 JE SHORT packed1_.0040101D
0040101C . C3 RETN
0040101D > C705 64204000 >MOV DWORD PTR DS:[402064],4003
00401027 . C705 68204000 >MOV DWORD PTR DS:[402068],packed1_.00401>
If you dump file, it will not work. Reason is redirected code. Note where some CALLs and JMPs leads:
00401002 CALL packed1_.004001F8
...
0040100E JMP packed1_.004001FE
They lead to the address below 401000, that means to the PE header. That is not normal. I will explain this briefly:
PESpin will load all sections in memory, decrypt and do all that it needs to be done for unpacking. After that PEheader isn't anymore important for anything so it can be modyfied as you like - it won't affect the program in memory. PESpin will take adwantage of that fact and it will fill one part of PEheader with FFFFF... (and btw destroy part of the header which holds sections info). Then it will search in code section for PUSH xxxxxxxx or CALL xxxxxxxx (maybe JMP too) instructions because they are 5 bytes long. Then it will substitute that opcode with some JMP or CALL to PEheader where it will place original instruction and return to code section (if needed). The reason why instructions must be 5 bytes long is because they are easy to substitute with JMP opcode (it's 5 bytes long). Let's see two examples:
a) CALL example:
00401002 . E8 F1F1FFFF CALL packed1_.004001F8
This CALL points to PEheader. Right click on it and folow it. There you will find original "stolen code":
004001F8 -E9 09130000 JMP packed1_.00401506
So you need just to replace CALL 4001F8 with JMP 401506. As you see it's very simple.
b) JMP example:
0040100E .-E9 EBF1FFFF JMP packed1_.004001FE
Again, follow this jump to PEheader and you'll see stolen original code:
004001FE 68 F4204000 PUSH packed1_.004020F4
You gonna fix this as we fixed it in first example, instead of jump you place PUSH 4020F4 instruction.
Ok, you sow it and it's not hard. First tought would be "Why restoring that code, why just not leaving it there and just dump it?". Reason is that PEheader is destroyed (sections info is erased) and we doesn't have valid PE file anymore. If we paste original header from disk, then program will jump to it and it will not find redirected code, so result is crushing. Second tought is "But there is lot of redirected code, how to fix them all? Manually? I'm not crazy to waste whole night in front of computer!". Acctualy, there can be limited number of redirected code because size of PEheader, but that number is pretty big too. So to fix lot of that code we will just use/write Olly Script for automating job. That is not hard at all. It should be like this:
- First we need to find such JMP/CALL opcodes. Our srcipt must search for JMP/CALL which ends on F because it means that it jumps backword. So search for instructions with E9???????F or E8???????F bytes.
-Then we must check does it jumps to PEheader. Take our a) example. Take address of CALL, 401002 and take CALL value (four bytes after E8 in reversed order) FFFFF1F1. Now add those two values 401002+FFFFF1F1=4001F3, and if result is between 400000<=RESULT<401000 than you know that you are at right place.
- Than, using basic math you must restore original opcode. It can't be copy-pasted, it must be calculated. Basic hex math is needed here, little calculation and some time. After that you will have script that works perfect. If you don't (or don't know how to) do it your self, you can use mine "PESpin - Code Fixer.txt" (included in archive).
Now, if you have done all just like I wrote and then use script to fix redirected code, dump file. If you run it, you'll probably get error message that tels you how this app is not valid Win32 app. Don't worry. Open LordPE, go to options and under rebuilder check only "Validate PE" option. Close options, open rebuilder and open our dumped file in it. After validating of dump, run it and it will work just perfect ;) Good job! You have just unpacked PEspin v1.0.
This aproach will work for ASM and BC++ programs , but not for Delphi and MSVC++. Reason is different IAT redirection which OllyDump plugin isn't able to handle. That can be fixed by building new import section. That we will see in next tutorial.
But there is one reason why dumped file will probably work only on machine on which file was unpacked. If you take look at import jumps, you'll see that most of those jumps are absolute jumps that jump to specified loacation in memory. Because of that, dumped file will probably crush on another computer that has different versions of dll's. This can be fixed , but for bigger file it's impossible to do it manually. This is problem with all PESpin versions.
3. Tutorial 2 - PESpin v1.1
In this version everything is same as in 1.0 except this one brings new option - CRYPT and CLEAR MARKERS. What is that? Simple, read PESpin's help file:
Crypt markers: Code between encryption markers CRYPT_START and CRYPT_END is encrypted during process of file protection and when protected file is executed, code is being decrypted closely before it is used and is again encrypted right after use (runtime protection against dumping).
Clear markers: CLEAR_START and CLEAR_END markers offers ability to remove blocks of code after being executed.These macros should only be placed in regions of code that are executed once in your application.Any attempt to re-execute a block of code inside those macros will cause an exception in the protected application.
This is not a big problem for us. For our second target we will chose keygen.exe which is packed with all CLEAR and CRYPT options (thanks to detten ;). Procedure of finding OEP is exactly the same as in 1.0 version so there is no point to write it all again.
In a short:
exclude all exceptions, place bp to the end of GetTickCount API and run target, remove bp and return to target code.
But... We will meet one problem here that we'll had in all PESpin 1.x versions with Delphi and MSVC++ targets.
Problem is OllyDump cannot rebuild IAT. We will had to do it all manually. Because of that problem we'll had to know which one dll's our packed target is using.
Lets go!
First we will find what dll's keygen.exe needs. Place bp on LoadLibraryA in command line and run target.
You will break in weird place (maybe values are different at you):
77ED6FC4>-E9 1860FB42 JMP BAE8CFE1
77ED6FC9 90 NOP
77ED6FCA 90 NOP
77ED6FCB 90 NOP
77ED6FCC 90 NOP
77ED6FCD 90 NOP
77ED6FCE 90 NOP
77ED6FCF 90 NOP
77ED6FD0 90 NOP
77ED6FD1 90 NOP
77ED6FD2 90 NOP
77ED6FD3>-E9 7D61FB42 JMP BAE8D155
77ED6FD8 90 NOP
77ED6FD9 90 NOP
77ED6FDA 90 NOP
77ED6FDB 90 NOP
77ED6FDC 90 NOP
77ED6FDD 90 NOP
77ED6FDE 90 NOP
77ED6FDF 90 NOP
Remove bp, open memory window and place "memory bp on access" on last section. That is one SFX. Now press Shift+F9 and you'll return to protector code (then remove mem bp):
0040D491 85C0 TEST EAX,EAX
0040D493 0F84 3F090000 JE keygen.0040DDD8
0040D499 E8 01000000 CALL keygen.0040D49F
0040D49E FF59 50 CALL FAR FWORD PTR DS:[ECX+50]
0040D4A1 51 PUSH ECX
0040D4A2 55 PUSH EBP
0040D4A3 810424 12374000 ADD DWORD PTR SS:[ESP],keygen.00403712
0040D4AA 814424 04 220000>ADD DWORD PTR SS:[ESP+4],22
What are we searching here? If you remember from last chapter, PESpin decrypts DLL name, load it in memory and then delete it's name. We are now at place where protector loads dll and we will find place where he erases DLL name. We will patch that erasing procedure so later we can see what DLL's are used. Trace with F7 untill you get here (that's lot of tracing):
0040D4F0 800B 00 OR BYTE PTR DS:[EBX],0
0040D4F3 74 0D JE SHORT keygen.0040D502
0040D4F5 8813 MOV BYTE PTR DS:[EBX],DL
0040D4F7 C1C2 04 ROL EDX,4
0040D4FA 75 01 JNZ SHORT keygen.0040D4FD
0040D4FC E8 43FF6424 CALL 24A5D444
0040D501 FC CLD
0040D502 93 XCHG EAX,EBX
0040D503 8B56 10 MOV EDX,DWORD PTR DS:[ESI+10]
That is place where DLL name is being erased. Get to this line
0040D4F5 8813 MOV BYTE PTR DS:[EBX],DL
and follow it in dump window. There you will see KERNEL32.DLL name. Patch this opcode MOV BYTE PTR DS:[EBX],DL and then just run target.
Our target will start, but that's what we want. Go in dump and there you will find all dll's that target uses:
00402102 4B 45 52 4E 45 4C 33 32 2E 44 4C 4C 00 00 F7 00 KERNEL32.DLL..÷.
00402112 00 00 00 00 00 00 00 00 00 00 C4 00 00 00 00 00 ..........Ä.....
00402122 00 00 00 00 00 00 00 00 00 00 00 00 C5 00 00 00 ............Ĺ...
00402132 00 00 00 00 00 00 00 00 CC 00 00 00 00 00 00 00 ........Ě.......
00402142 00 00 00 00 00 00 CC 00 00 00 00 00 00 00 00 00 ......Ě.........
00402152 00 00 D2 00 00 00 00 00 00 00 00 00 00 00 00 00 ..Ň.............
00402162 00 00 00 00 D3 00 00 00 00 00 00 00 00 00 00 00 ....Ó...........
00402172 00 00 00 00 00 00 00 00 00 00 D3 00 00 00 00 00 ..........Ó.....
00402182 00 00 00 00 00 00 00 00 00 00 D3 00 00 00 00 00 ..........Ó.....
00402192 00 00 00 00 00 00 00 00 00 00 00 00 55 53 45 52 ............USER
004021A2 33 32 2E 44 4C 4C 00 00 C3 00 00 00 00 00 00 00 32.DLL..Ă.......
004021B2 00 00 00 00 00 00 00 00 00 00 00 00 D3 00 00 00 ............Ó...
004021C2 00 00 00 00 00 00 00 00 00 00 D3 00 00 00 00 00 ..........Ó.....
004021D2 00 00 00 00 00 00 D3 00 00 00 00 00 00 00 00 00 ......Ó.........
004021E2 00 00 00 00 00 00 47 44 49 33 32 2E 44 4C 4C 00 ......GDI32.DLL.
004021F2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Our target is small and it uses only kernel32.dll, user32.dll and gdi32.dll. Good!
Now we gonna restart target in Olly and find OEP. Place bp on last RETN in GetTickCount API, run target, remove bp and returne to code:
0040E4D8 8BD8 MOV EBX,EAX
0040E4DA F7D3 NOT EBX
0040E4DC 33D8 XOR EBX,EAX
0040E4DE 43 INC EBX
0040E4DF 68 87000000 PUSH 87
0040E4E4 59 POP ECX
0040E4E5 66:35 4C50 XOR AX,504C
0040E4E9 66:05 8911 ADD AX,1189
0040E4ED AA STOS BYTE PTR ES:[EDI]
0040E4EE EB 01 JMP SHORT keygen.0040E4F1
0040E4F0 68 499CC12C PUSH 2CC19C49
0040E4F5 24 06 AND AL,6
Find the jump that leads to redirection procedure and patch all bytes between it and good call:
0040D5DF FF6424 FC JMP DWORD PTR SS:[ESP-4] <---Jump!!!
...
... <--- Patch all these opcodes!!!
...
0040D643 E8 C4F6FFFF CALL keygen.0040CD0C <---Good call!!!
0040D648 E8 03000000 CALL keygen.0040D650
Then scroll little down to our well known place:
BR>
0040D6D7 B0 F9 MOV AL,0F9
0040D6D9 72 3F JB SHORT keygen.0040D71A
0040D6DB 8D85 F469271E LEA EAX,DWORD PTR SS:[EBP+1E2769F4]
0040D6E1 2D 8417E71D SUB EAX,1DE71784
0040D6E6 FFD0 CALL EAX
0040D6E8 EB 02 JMP SHORT keygen.0040D6EC
This is that place where timer is maybe called. Our target doesn't have that option enabled and we don't need to patch opcodes from 40D6DB to 40D6E6.
Ok, scroll down and find the POPUP opcode. You will find it a little obfuscated
0040D8FA
E8 610FC90F CALL 1009E860
so patch first byte to get clear picture
0040D8FA 90 NOP
0040D8FB 61 POPAD
After that POPAD starts stolen OEP code mixed with junk code. It seams that there is no stolen OEP bytes because jump that leads to code section jumps to 401000 at beginning of section:
0040D8FC 0FC9 BSWAP ECX
0040D8FE 0FACC2 13 SHRD EDX,EAX,13
...
...
...
0040D947 -E9 B436FFFF JMP keygen.00401000 <---Jump toOEP!!!
Execute that jump and you'll land on OEP. Code there has crappy look even if you press Ctrl+A, so right click on it, select analysis and remove analysis from module.
Then you'll have better picture:
00401000 9C PUSHFD
00401001 60 PUSHAD
00401002 B9 B5381023 MOV ECX,231038B5
00401007 BF 7896AA00 MOV EDI,0AA9678
0040100C 81E9 91381023 SUB ECX,23103891
00401012 B8 5F1360C2 MOV EAX,C260135F
...
...
First we gonna check if there is stolen/redirected code to PE header section. Just run "PESpin - Code Fixer.txt" script. Check log window and you'll see that script has fixed 4A opcodes:
[Simple Code Fixing - number of stolen opcodes ] stolen = 0000004A
Ok,that's all we know from previous version of PESpin, but now we gonna met new trick - code decrypting and erasing. This part of code at very beginning is start of first decrypt procedure:
00401000 9C PUSHFD
00401001 60 PUSHAD
00401002 B9 B5381023 MOV ECX,231038B5
00401007 BF 7896AA00 MOV EDI,0AA9678
0040100C 81E9 91381023 SUB ECX,23103891
00401012 B8 5F1360C2 MOV EAX,C260135F
00401017 05 DCE1E03D ADD EAX,3DE0E1DC
0040101C FF0D 22104000 DEC DWORD PTR DS:[401022]
00401022 0011 ADD BYTE PTR DS:[ECX],DL <---Trace to this line!!!
00401024 61 POPAD
00401025 9D POPFD
Trace with F8 to line that I showed abowe and you'll see that it changed to:
00401022 FF10 CALL DWORD PTR DS:[EAX]
That call will lead you to one procedure that will decrypt some code in our target and erase something. You can go ther by F7 if you would like to see what is happening there. It will just decrypt couple opcodes below POPFD opcode and erase all above. Instead F7 press F8 and you'll see changes:
00401000 0000 ADD BYTE PTR DS:[EAX],AL
00401002 0000 ADD BYTE PTR DS:[EAX],AL
00401004 0000 ADD BYTE PTR DS:[EAX],AL
00401006 0000 ADD BYTE PTR DS:[EAX],AL
00401008 0000 ADD BYTE PTR DS:[EAX],AL
0040100A 0000 ADD BYTE PTR DS:[EAX],AL
0040100C 0000 ADD BYTE PTR DS:[EAX],AL
0040100E 0000 ADD BYTE PTR DS:[EAX],AL
00401010 0000 ADD BYTE PTR DS:[EAX],AL
00401012 0000 ADD BYTE PTR DS:[EAX],AL
00401014 0000 ADD BYTE PTR DS:[EAX],AL
00401016 0000 ADD BYTE PTR DS:[EAX],AL
00401018 0000 ADD BYTE PTR DS:[EAX],AL
0040101A 0000 ADD BYTE PTR DS:[EAX],AL
0040101C 0000 ADD BYTE PTR DS:[EAX],AL
0040101E 0000 ADD BYTE PTR DS:[EAX],AL
00401020 0000 ADD BYTE PTR DS:[EAX],AL
00401022 0000 ADD BYTE PTR DS:[EAX],AL
00401024 61 POPAD
00401025 9D POPFD
00401026 6A 00 PUSH 0
00401028 E8 83F1FFFF CALL keygen.004001B0
0040102D A3 E4304000 MOV DWORD PTR DS:[4030E4],EAX
00401032 6A 00 PUSH 0
00401034 -E9 7DF1FFFF JMP keygen.004001B6 <--- to PE header!
00401039 6A 00 PUSH 0
0040103B 6A 01 PUSH 1
0040103D 50 PUSH EAX
0040103E E8 7EF1FFFF CALL keygen.004001C1 <--- to PE header!
00401043
6A 00 PUSH 0
00401045
E8 7DF1FFFF CALL keygen.004001C7 <--- to PE header!
Notice that some of new decrypted jumps and calls lead to PE header, so we must run script for fixing redirections again and then fill all code from 401000 to 401025 with NOP's:
00401000 . 90 NOP
...
...
...
00401024 . 90 NOP
00401025 . 90 NOP
00401026 . 6A 00 PUSH 0
00401028 . E8 1B040000 CALL keygen.00401448
0040102D . A3 E4304000 MOV DWORD PTR DS:[4030E4],EAX
00401032 . 6A 00 PUSH 0
00401034 . 68 7F104000 PUSH keygen.0040107F
00401039 . 6A 00 PUSH 0
0040103B . 6A 01 PUSH 1
0040103D . 50 PUSH EAX
0040103E . E8 11040000 CALL keygen.00401454
00401043 . 6A 00 PUSH 0
00401045 . E8 F8030000 CALL keygen.00401442
0040104A . EB 0B JMP SHORT keygen.00401057
0040104C EB DB EB
We have fixed the first kind of decryption, CLEAR type. Now we gonna find and fix CRYPT one. Since target is small, you can scroll down and find two interesting calls easy:
004013D1 $ FF15 5FF54000 CALL DWORD PTR DS:[40F55F]
004013D7 . 71 27 JNO SHORT keygen.00401400
004013D9 . 0B3E OR EDI,DWORD PTR DS:[ESI]
...
...
...
0040141A . FF15 83F54000 CALL DWORD PTR DS:[40F583]
00401420 . 27 DAA
00401421 . DCE5 FSUBR ST(5),ST
00401423 . 3AFF CMP BH,BH
00401425 .^75 F8 JNZ SHORT keygen.0040141F
You see that these two calls leads to protector section. Place bp on first call and run target. You will get dialog box which asks for name. Enter some name and press generate button. You will break on first bp. Remove bp and enter in call with F7, then press Ctrl+F9 to get to RETN opcode. Execute it and return to target code, press Ctrl+A and scroll up few lines:
004013D1 $ FF15 5FF54000 CALL DWORD PTR DS:[40F55F] <--NOP this!!!
004013D7 . 71 27 JNO SHORT keygen.00401400 <--NOP this!!!
004013D9 . 0B3E OR EDI,DWORD PTR DS:[ESI] <--NOP this!!!
004013DB . 55 PUSH EBP
004013DC . 8BEC MOV EBP,ESP
004013DE . 83C4 F4 ADD ESP,-0C
...
...
...
00401415 . D9E1 FABS
00401417 . DD5D F4 FSTP QWORD PTR SS:[EBP-C]
0040141A . FF15 83F54000 CALL DWORD PTR DS:[40F583]
00401420 . 27 DAA
00401421 . DCE5 FSUBR ST(5),ST
00401423 . 3AFF CMP BH,BH
00401425 .^75 F8 JNZ SHORT keygen.0040141F
00401427 . FF75 F4 PUSH DWORD PTR SS:[EBP-C]
0040142A . 68 BD304000 PUSH keygen.004030BD
Code is decrypted and now wee need to patch deryption calls. Patch first call wich decrypts (abowe picture). Now enter in second call at 40141A and trace to this line
003C004E AA STOS BYTE PTR ES:[EDI]
That line again encrypts decrypted code. So patch it to prevent encrypting. Then return to target code. You will notice that you are not imidiatley after encryptor call, but couple bytes further. That mean some bytes after call are junk, so patch encryptor call and all useless bytes.
Now that decrypted and patched code should look like this:
004013D1 /$ 90 NOP
004013D2 |. 90 NOP
004013D3 |. 90 NOP
004013D4 |. 90 NOP
004013D5 |. 90 NOP
004013D6 |. 90 NOP
004013D7 |. 90 NOP
004013D8 |. 90 NOP
004013D9 |. 90 NOP
004013DA |. 90 NOP
004013DB |. 55 PUSH EBP
004013DC |. 8BEC MOV EBP,ESP
004013DE |. 83C4 F4 ADD ESP,-0C
004013E1 |. 52 PUSH EDX ;
keygen.004030F0
004013E2 |. 51 PUSH ECX
004013E3 |. 33C9 XOR ECX,ECX
004013E5 |. 8B55 08 MOV EDX,DWORD PTR SS:[EBP+8]
004013E8 |. DD05 DB304000 FLD QWORD PTR DS:[4030DB]
004013EE |> 33C0 XOR EAX,EAX
004013F0 |. 8A0411 MOV AL,BYTE PTR DS:[ECX+EDX]
004013F3 |. 24 7F AND AL,7F
004013F5 |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
004013F8 |. DB45 FC FILD DWORD PTR SS:[EBP-4]
004013FB |. DEC1 FADDP ST(1),ST
004013FD |. DC0D C3304000 FMUL QWORD PTR DS:[4030C3]
00401403 |. 41 INC ECX
00401404 |. 3B4D 10 CMP ECX,DWORD PTR SS:[EBP+10]
00401407 |.^72 E5 JB SHORT keygen.004013EE
00401409 |. DC0D CB304000 FMUL QWORD PTR DS:[4030CB]
0040140F |. DC0D D3304000 FMUL QWORD PTR DS:[4030D3]
00401415 |. D9E1 FABS
00401417 |. DD5D F4 FSTP QWORD PTR SS:[EBP-C]
0040141A |. 90 NOP
0040141B |. 90 NOP
0040141C |. 90 NOP
0040141D |. 90 NOP
0040141E |. 90 NOP
0040141F |. 90 NOP
00401420 |. 90 NOP
00401421 |. 90 NOP
00401422 |. 90 NOP
00401423 |. 90 NOP
00401424 |. FF75 F8 PUSH DWORD PTR SS:[EBP-8]
00401427 |. FF75 F4 PUSH DWORD PTR SS:[EBP-C]
0040142A |. 68 BD304000 PUSH keygen.004030BD
0040142F |. 68 54314000 PUSH keygen.00403154
00401434 |. E8 15000000 CALL keygen.0040144E
00401439 |. 83C4 10 ADD ESP,10
0040143C |. 59 POP ECX
0040143D |. 5A POP EDX
0040143E |. C9 LEAVE
0040143F . C2 0C00 RETN 0C
Now open OllyDump plugin, set OEP = 1000 and dump our unpacked target. If you try to run it, it will just crush. Reason is that OllyDump has failed to rebuild IAT (btw, if you get "Not valid PE file message" run LordPE's rebuilder to validate file). Scroll down to imports:
00401442 .-E9 1C84A777 JMP kernel32.ExitProcess
00401447 FF DB FF
00401448 $-E9 8C98A777 JMP kernel32.GetModuleHandleA
0040144D FF DB FF
0040144E $-E9 8B929477 JMP 77D4A6DE
00401453 FF DB FF
00401454 $-E9 87429677 JMP 77D656E0
00401459 FF DB FF
0040145A $-E9 85C59477 JMP 77D4D9E4
0040145F FF DB FF
00401460 $-E9 D3539677 JMP 77D66838
00401465 FF DB FF
00401466 $-E9 B4829477 JMP 77D4971F
0040146B FF DB FF
0040146C $-E9 72699477 JMP 77D47DE3
00401471 FF DB FF
00401472 $-E9 DB509677 JMP 77D66552
00401477 FF DB FF
00401478 $-E9 A04A9477 JMP 77D45F1D
0040147D FF DB FF
0040147E $-E9 636A9477 JMP 77D47EE6
00401483 FF DB FF
00401484 $-E9 5B0CC07E JMP 7F0020E4
00401489 FF DB FF
0040148A $-E9 F308C07E JMP 7F001D82
0040148F FF DB FF
00401490 $-E9 BE09C07E JMP 7F001E53
00401495 FF DB FF
00401496 $-E9 3C08C07E JMP 7F001CD7
What happened?
You see that required dll's like USER32.DLL and GDI32.DLl are not loaded in memory. As you can see, imports jumps jump at properly place where API's should be, but because dll are not there, those jumps leads to nowhere.
OllyDump plugin has added one new section .newIID which should hold IAT but if you take look at the dump of that section you'll see that it is totally empty.
How we gonna fix this?
There is way. Since all import jumps point to correct API's, we need just to load required dll's and that we can do by manually making basic smallest IAT. That IAT will hold every dll with just one API per each DLL and that API even doesn't matter.
It's important only that windows loader loads required dll's.
First you must know how basic import section must look. Import section must have following parts:
1) Thunks - these are groups of 4 bytes. That 4 bytes are relative address (from image base) that point to hints. There is one thunk per each dll. After PE file is loaded in memory, thunks are filled with API addresses. Every thunk must end with 4 zero bytes.
2) IMAGE_IMPORT_DESCRIPTOR - this is group of 5x4 bytes. There is one descriptor per each dll. Every 4 bytes of descriptor holds specific information:
1. First 4 bytes represent RVA to the hint array.
2. Second 4 bytes represent TimeDateStamp for dll. This is not important and it can be filled with zeros.
3. Third 4 bytes represent some ForwarderChain which is not important and it can be filled with zeros too. This ForwarderChain is something obsolete.
4. Forth 4 bytes are address that point to RVA of dll ASCII name in import section.
5. The last 4 bytes points to RVA of thunks.
3) Then we have one more IMAGE_IMPORT_DESCRIPTOR filled with zeros (null terminating one).
4) Hint arrays - these arrays holds RVA's of hints. This is acctually same thing as thunks, but they will not be filled with import addresses like thunks will.
5) Hint - it is a two byte value that is hint for windows loader where it should look first for API. If hint doesn't work (because newer/different version of dll) loader will perform binary search for ASCII API name. Hint is not neccessery and we can just fill it with zeros, just like some compilers do.
6) API name - it is ASCII name of API function. It's purpose is that windows loader can perform binary search for API. It's length isn't determined, but last byte must be 0 (null terminator).
7) DLL name - same thing as API name but for DLL and it must end with two zero bytes.
That is basic import section. We will make our import section in .newIID section which is added by OllyDump. We have three DLL's which need to be loaded, so we'll have three thunks, three decriptors + one zero filled, three hint arrays, and three hint-api-dll names. Open our dumped file with LordPE's PE editor and click on sections. You'll see that RawOffset of .newIID section is 10000. Close LordPE and open dumped file in hex editor. We need to organise place for IAT. Next picture shows how did I reserve place and for what. Note that all values and chars are just symbolic to give you (and me) better view how section should look:
thnk1... are thunks, then with numbers 1,2,3,0 I've fill 1. 2. 3. and null one Image_Import_Descriptors. Hint arrays are that arr1... Hint, API and DLL place cannot be reserved because I don't know length of API and DLL names before I search them.
This is first area that I will fill with data. We said that we need kernel32.dll, user32.dll and gdi32.dll, and one API per each dll. I will chose first API that I know name.
After filling that area, I have this picture:
Now we need to fill all other data; thunks, descryptors and arrays. All that data needs to be filled with RVA's so open this file in Olly and see dump of .newIID section. From that you can easy read all RVA's or even make changes in Olly. Next picture shows how arrays must be filled for first dll , KERNEL32.DLL:
After you fill other arrays, you should have this picture:
That's it, import section is done but you have save these changes to our file if you have done all this in Olly. But here is no option for save to file, so we ganna do that different.
Go in CPU window to 410000 address:
00410000 8000 01 ADD BYTE PTR DS:[EAX],1
00410003 0000 ADD BYTE PTR DS:[EAX],AL
00410005 0000 ADD BYTE PTR DS:[EAX],AL
...
...
...
004100CF 0000 ADD BYTE PTR DS:[EAX],AL
004100D1 0000 ADD BYTE PTR DS:[EAX],AL
004100D3 0000 ADD BYTE PTR DS:[EAX],AL
Select all "opcodes" (these are not opcodes, but our import section) from 410000 to 4100CF, right click, copy to executable -> selection, then in new window select save file.
Save it as our dump.exe
Now we need only to tell windows where is our IAT and how big it is.
What is IAT? IAT isn't whole imports section, but only IMAGE_IMPORT_DESCRIPTORS.
First descriptor starts at RVA 410018==>RawA 10018.
It's size is three descryptors + last one zero filled = 5*4*4 = 50h.
Open dump.exe in LeordPE's PE editor, click on directories and there change ImportTable RVA and Size to 10018 and 50.
Save changes and run dump.exe.
Is it working?
Mine works fine and yours should too, unless you didn't made some mistake.
Thats all for this version of PESpin. It's hard to fix IAT manually because if you have lot of dll's than you have big job to do and you can make mistake on any step.
There are some tools that claim that they can build import section, but non of them didn't work for me.
4. Tutorial 3 - PESpin v1.3
Unpacking PESpin v1.3 is same as unpacking 1.1 version except 1.3 version has option for advanced code redirection.
Unfortunatley for us that option isn't available in the public version so I couldn't use it when packing our target. Target file is Crackme05.exe, which is a Delphi 3 app. Delphi and VC++ programs have different import redirection which make it more difficult to repair.
Grab our target and load it in Olly.
Find place where stolen OEP starts , but also fix import redirection on your way. Let me help you a bit; import jump is at 4137D9, timer check is at 4138D1 and stolen OEP you'll find at:
00413AF6 F7D2 NOT EDX <--------------- Start of stolen OEP!
00413AF8 39C2 CMP EDX,EAX
00413AFA F7C0 74E7F921 TEST EAX,21F9E774
00413B00 0FACC2 48 SHRD EDX,EAX,48
...
...
00413B5C 3C 68 CMP AL,68
00413B5E 67:3B41 00 CMP EAX,DWORD PTR DS:[BX+DI]
00413B62 -E9 4D0AFFFF JMP Crackme0.004045B4 <------- Jump to code section!
Trace to this last jump that leads to code section and enter in it:
004045B4 . 50 PUSH EAX
004045B5 . 6A 00 PUSH 0
004045B7 . E8 F8FEFFFF CALL Crackme0.004044B4 <------- Enter here!
004045BC . BA 98804000 MOV EDX,Crackme0.00408098
004045C1 . 52 PUSH EDX
004045C2 . 8905 B4944000 MOV DWORD PTR DS:[4094B4],EAX
Now trace a little more and enter to first call. You are at the place where one import is:
004044B4 $-FF25 D4564100 JMP DWORD PTR DS:[4156D4] ;kernel32.GetModuleHandleA
But notice how the imports look.
Usually import is one absolute jump that jumps to API, but in this case PESpin didn't change JMP DWORD[xxxxxxxx] to JMP xxxxxxx like he did with ASM and BC++ programs.
Instead he redirected DWORD[xxxxxxxx] which was in .idata import section to it's own section. OllyDump cannot rebuild this kind of redirection, but there is way around it.
As I said, find where the stolen OEP is , fix that redirection jump before and use script for stolen code.
Now just dump it with OllyDump plugin. My dumped file has lost icon and it crashes when I run it.
Thats good ;)!?
Open it in another Olly and check some imports (don't close first Olly).
You will be at stolen OEP beginning so trace with F7 until you enter to crackme code and to the place where first import should be:
As you can see, our import is bad just like most of the restof them because they all point to 00000000 address. There are just couple good ones in whole dump.
But there is one good thing, all required dll's are loaded in memory:
What's good there if we don't have imports? It's good because we can change all import jumps to absolute jumps that jump directly to API. We will done that in our first/original crackme and just copy-paste all code section and dumped file will work. But why didn't I do that the first time? Because OllyDump isn't able to find DLL's then. To change jumps you can use my script which I made for that "PESpin v1.x - Delphi & VC++.txt".
You can do this manually, but there is lot of jumps so this script will do the job for you. But maybe/probably it will not change all jumps and it may *censored* some of them.
For example: if you want that this script finds all import jumps, you'll need to select all code from 401000 to 40104D and NOP it (because it some code can confuse Olly and script).
Then run script and it will change all jumps.
Note that you will have to run script several times for bigger files (~bigger than 500kb). Then after script has fixed all jumps
select all code from 401054 to 407D10 and binary copy it, paste it to our dumped file. Save changes and run dump, it works! If your dump is not working, then open it in Olly and try to find what did went wrong. As usuall, dumped file will only work on machine with same version of operating system because absolute imports.
There it is, that was unpacking of PESpin 1.3. We didn't bothered with stolen OEP bytes because dump will work fine in this way too.
-
Level : intermediate
Patch commercial VCL modules (Delphi/Borland C++) instead of patching your built executables over and over.
Lets take a dive in the Borland package libraries (BPL files)
Target: EasyMap VCL (3w.microolap.com/trials/em.zip)
Tools used:
Delphi 7 Enterprise version
Hiew 6.11 (you can use any hexeditor)
Note: A product version doesn't matter, but it's important that all work. The second thing that you should keep in mind is what all the addresses are represented in hexadimal system, the rest numbers are decimal.
Author : Wizard
Level : 3/10
Origin: I have not failed. I've just found 10,000 ways that won't work. Thomas Jefferson.
Introduction
EasyMap VCL is a Delphi/C++Builder components set for creating your own GIS-related solutions without MapInfo, MapX, MapObjects, WinGIS and so on.
EasyMap VCL allows to add following functionalities in Delphi/C++Builder application:
MIF/MID vector maps displaying in Longitude/Lantitude projection;
Map objects related data in grid or tabular controls displaying;
ZoomIn/ZoomOut/Pan/Selector map tools;
EasyMap Object Model allows to display on the map embedded into your application data from most of known datasources: RDBMS, flat DBs, your own formats etc.
Philosophical story
One day my father asked me to look at some Delphi component that was shareware. I said that I'll take a look at it and crack it like piece of cake. That was an arrogance from my side, because when I made an observation of the components I concluded that a crack task won't be as simple as I thought.
The sense of the task was simple, but realization wasn't. I should patch three files. One BPL file with two NAG screens and two DCU files with one message inside each.
I found the places where the NAGs were. The problem was only in one thing, how to patch these files. One might think, what so hard in that. Nothing, except that DCU files are not just executables.
I spent a lot of hours thinking and analysing of how to do something. I didn't find any information about BPL and DCU file structures over the net. No any information, no tutorials or patchers. I found only one crappy tutorial and lots of links that are just brain-picking of other sites.
During the search I understood that inet is one big scrap-heap and when you need something you won't find it, you will only get it when you don't need it. Well, who need information that he needed some years ago? Hah, of course no one.
When I'm fed up with the shitty plagiarism on the net I decided to kick some ass by myself.
What is what?
First of all, extract em.zip, than run easymapvcl_trial.exe and follow the setup program instructions to install the components.
Now fire up Delphi. Open the demo project EasyMapDemo.dpr in it. Activate Form1 (Shift + F12). You should see two message boxes, one after another, which say: "Evaluation version of EasyMap VCL. For purchasing info visit...". Each message connected to a specified component. Therefore there're two components:
TDataBrowser class, which is in module DataBrowser.dcu
TLayersComboBox class, which is in module LayersComboBox.dcu
How do I know that? Simple, look into EasyMap directory in Delphi. It contains three folders and six files in the its root. Go into DELPHI7 subdirectory of EasyMap folder. In there you're gonna find forenamed DCU files. Besides the messages say for themselves, because each message has a caption with inscription, which tells you the name of a component. As a rule components have the same name as modules, but without first "T" letter.
Note: These modules of Delphi are always included into an exe file during a compilation of the last one. That means those two files are always used in run-time, because Delphi compiles them into the final executable.
I suppose DELPHI7 folder can have another name, which depends on Delphi version.
You ought to see file EasyMapD7.bpl in the same directory. This file is used by Delphi during design-time. Lemme explain why. BPL file is the same DLL file, but renamed. It's called by Delphi, when a programmer edits a form, which contains such components or when he edits the components properties.
In other way there can be few BPL files. To know which of them has the message you better use SoftIce. But there's more artless way to find out where the NAG is. Start any string searcher (e.g. FAR or an other shell). Type "Evaluation version" in the search string and start the search. As you can see only three forenamed files have that stupid message.
Note: In case of some smart crypted/packed modules use the debugger to find out where a message is. But usually DCU and BPL files are naked (ain't packed), which gives you a real power to create a crack art.
Let's find out where to dig
Now we know what files have the message inside. To be exact I'm giving the whole list below with the explanation.
File name Description Comments
DataBrowser.dcu Delphi run-time module It has one message
LayersComboBox.dcu Delphi run-time module It has one message
EasyMapD7.bpl Delphi design-time module It has two messages
First, let's remove the NAGs from BPL file. Close Delphi if it were opened.
Open EasyMapD7.bpl with Hiew and search for the "Evaluation" string. You can do that by pressing F7 in Hiew and type forenamed word in the search string.
I've found one at address 410D2C. Press F4 and select Hex mode (or just press Enter key to do the same). Press Alt + F1 to switch from relative addresses to absolute. Once you've done it you'll get 1012C instead of memory address 410D2C.
Enter the decode mode by pressing enter or F4/Decode. Now hold on up key to scroll up till you see this part of the code:
000100BF : 8D45FC lea eax,[ebp][-0004] ; <= the place you are now
000100C2 : BA03000000 mov edx,000000003
000100C7 : E8C404FFFF call 000000590
000100CC : 8B45FC mov eax,[ebp][-0004]
000100CF : E8D404FFFF call 0000005A8
000100D4 : 50 push eax ; address of title of message box
000100D5 : 682C0D4100 push 000410D2C ; address of text in message box
000100DA : 6A00 push 000 ; handle of owner window
000100DC : E8EB08FFFF call 0000009CC ; MessageBoxA itself
000100E1 : 33C0 xor eax,eax
000100E3 : 5A pop edx
000100E4 : 59 pop ecx
000100E5 : 59 pop ecx
000100E6 : 648910 mov fs:[eax],edx
000100E9 : 68030D4100 push 000410D03
000100EE : 8D45F8 lea eax,[ebp][-0008]
000100F1 : BA02000000 mov edx,000000002
000100F6 : E85504FFFF call 000000550
000100FB : C3 retn
... skipped ...
;................................................. .................
; Attention! Below goes the disassemble of the code the part, where
; 'Evaluation version' strings lays. But here we'll need only
; five bytes of the mentioned string. Therefore you should treat
; below disassemble not as a string, but as a disassemble. That's
; because we're gonna use the space destined for the string as a
; piece of code, which will do what we want and then it will bring
; back the control to the point we need. Keep on reading and you'll
; understand what do I mean.
;................................................. .................
0001012C : 45 inc ebp ; 'E' <= the place you've found
0001012D : 7661 jbe 000010190 ; 'va'
0001012F : 6C insb ; 'l'
00010130 : 7561 jne 000010193 ; 'ua'
00010132 : 7469 je 00001019D ; 'ti'
00010134 : 6F outsd ; 'o'
00010135 : 6E outsb ; 'n'
The main idea is that you ought to avoid the MessageBox call at address 100DC. How to do this? Let's ask Windows API help about that. It says next:
int MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
Ok, now we know that MessageBox has 4 parameters. Now look again at the next piece of the code.
000100CF : E8D404FFFF call 0000005A8
000100D4 : 50 push eax ; address of title of message box
000100D5 : 682C0D4100 push 000410D2C ; address of text in message box
000100DA : 6A00 push 000 ; handle of owner window
000100DC : E8EB08FFFF call 0000009CC ; MessageBoxA itself
000100E1 : 33C0 xor eax,eax
As you can see there're only 3 parameters which will be passed to MessageBox function. According to help there should be another one parameter before those three. But there's no such one, because instead of it there's a call at address 100CF.
Therefore, we can conclude that the last parameter (uType) is in stack already. You're maybe thinking now, let's release the stack from one uType parameter by a command e.g. pop eax and then just "nop" all the rest or jump over the MessageBox.
Not a bad idea at all, but there's one problem here. When I did so Delphi caused an GPF (General protection fault) and suddenly exited. When I've put a breakpoint at address 100D4 in SoftIce and ran Delphi again I saw strange thing. Everything was disassembled correctly in Hiew, but it was wrongly disassembled in Delphi during design-time. That means EasyMapD7.bpl isn't just renamed DLL or maybe I should change something else in it to make Delphi treat the code correctly.
I've been thinking for a long time how to patch it. I tried many times to patch it with different ways, but nothing worked out...
Breaking up the horizons
In one day I suddenly wondered about one simple thing, what if I had changed the part of the code, which was never changed. I mean that all the jmp, call and push commands will be replaced with different addresses appropriate to a situation. That means, for a BPL file Delphi can change commands in memory. E.g. if there were a command like push 410D2C, such command might look in memory as push 470D2C or elsewise. Because it's BPL (DLL) and who knows how Delphi treats it, what procedures/functions uses, in what memory ranges, etc. To answer all these questions we have to know the BPL file structure, but we don't and there's no place to get it yet. That's why we're just wondering to patch it correctly without knowing its structure.
Anyway the conjecture was right. When I patched forenamed push command only the first byte was interpreted correctly, the rest din't. That was because push command (its long equivalent) consists of 5 bytes, 1st byte is mnemonics, the rest 4 are an address, e.g. 68 2C 0D 41 00 (push 410D2C). That's why when I patched another byte than the first I got wrong values in memory during design-time. The same was during run-time, when I compiled an executable the code was wrong in exe file, but only in places where I touched such commands as push (ONLY 5 bytes long), call and jmp.
By knowing that I can conclude only one correct solution. Look at the parts of the code again more attentively.
000100CF : E8D404FFFF call 0000005A8 ; uType is in stack already
000100D4 : 50 push eax ; saving 3rd parameter
000100D5 : 682C0D4100 push 000410D2C ; saving 2nd parameter
000100DA : 6A00 push 000 ; saving 1st parameter
000100DC : E8EB08FFFF call 0000009CC ; MessageBoxA
000100E1 : 33C0 xor eax,eax
... skipped ...
0001012C : 45 inc ebp ; 'E', the first letter of the NAG
0001012D : 7661 jbe 000010190 ; 'va'
0001012F : 6C insb ; 'l'
00010130 : 7561 jne 000010193 ; 'ua'
00010132 : 7469 je 00001019D ; 'ti'
00010134 : 6F outsd ; 'o'
00010135 : 6E outsb ; 'n'
If you're pretty attentive you are obviusly guessed already what we're gonna change. Let's transform the code parts above to another.
000100CF : E8D404FFFF call 0000005A8 ; uType is in stack already
000100D4 : 50 push eax ; saving 3rd parameter
000100D5 : 682C0D4100 push 000410D2C ; saving 2nd parameter
000100DA : EB50 jmps 00001012C ; jump to our patch procedure
000100DC : E8EB08FFFF call 0000009CC ; MessageBoxA
000100E1 : 33C0 xor eax,eax ; <= we will back here
... skipped ... 0001012C : 58 pop eax ; release the 4th parameter
0001012D : 58 pop eax ; release the 3rd parameter
0001012E : 58 pop eax ; release the 2nd parameter
0001012F : EBB0 jmps 0000100E1 ; jump back over the call
00010131 : 61 popad ; 'a'
00010132 : 7469 je 00001019D ; 'ti'
00010134 : 6F outsd ; 'o'
00010135 : 6E outsb ; 'n'
Now the explanation of all we did. First, we changed push 00 to jmps 1012C at address 100DA. That's because Delphi won't change push and jmp commands, which are 2 bytes long.
Why? Because these commands save absolute values, they don't depend upon the memory address or file size, when the last will be compiled. Besides, the code says it pushes zero as the last parameter in stack, which is the first parameter (hWnd) in MessageBox. Well, for you to know - zero is the handle of the screen. It often used by such API functions as GetDC, ReleaseDC, etc. I suppose the components use this handle to draw the message before the form will be shown or even created. That's why you see two NAGs first and only after that the form of an applications appears.
As you can see the jump at address 100DA jumps to the first byte of our string that you've seen already. We're using 5 bytes of it to release the stack from values, which will never be used by MessageBox, because the last won't be called ever too. The last jump at address 1012F brings back the control to the program. At this point the stack is clear from our 3 values that were saved before the jump to our patch procedure. That's how we're preventing the NAG appearing.
Note: I remind you that when we release the stack we use the back order of the elements that were saved. The second thing, we're "popping up" three values because we have 2 of them in stack before the jump at address 100DA and one element was in stack before those 2 had been saved.
Everything should be pretty clear here, except one thing. Why do we use eax as the releasing register? That's because if you look at address 100E1 you'll see the command xor eax,eax. This command lays right after the MessageBox call. Which means that MessageBox function changes eax register. That's why program makes it null after the call. And that's why we're using it as "pop-up" register when releasing our 3 values. If we would use some other register we could *censored* up something in stack or in program, 'cause we don't know what the program does with what registers. That's why we should be very careful when changing something.
If you use some other register to release the stack you better do next steps:
1. before the MessageBox call and after it write down to a paper all values of the registers and the stack from the original (not patched) code;
2. patch the code as best as you can;
3. do the same as in the first step, but in the patched code;
4. compare all the values, which you wrote before the call and after it in the original and the patched code. If there's full match - you did all correctly, but if something wrong - you've made a mistake somewhere. In the last case you have to get back to the first step.
The full patch for the first NAG as a table.
Address Original opcode Original mnemonics Patched opcode Patched mnemonics
000100DA 6A 00 push 000 EB50 jmps 00001012C
0001012C 45 inc ebp 58 pop eax
0001012D 7661 jbe 000010190 58 58 pop eax, pop eax
0001012F 6C insb EB B0 jmps 0000100E1
First step is done. Let's find out where the second NAG lays. To do this call the find dialog again by pressing F7 key. Type "Evaluation" in it and start the search once again.
You have found another and the last one (in this file) at address 1123C. Look a bit up and you will see the same piece of code that you've seen before, when we were patching the first NAG. Nothing different here, but the addresses. Thus, there's no sense to give the same explanation as above. Instead of doing that just look at the table below to check out the addresses, but the algorithm is the same.
Address Original opcode Original mnemonics Patched opcode Patched mnemonics
000111EA 6A 00 push 000 EB 50 jmps 00001123C
0001123C 45 inc ebp 58 pop eax
0001123D 76 61 jbe 0000112A0 58 58 pop eax, pop eax
0001123F 6C insb EB B0 jmps 0000111F1
That's all for EasyMapD7.bpl, because if you will try to search the same string again you won't find it. That means we've done with this file.
Keep on diggin'
I spent a lot of time on the net trying to find any information about DCU files. I found some shitty article about how to write an emulator of Delphi IDE to run programs without Delphi IDE. All the rest I found were the same crap, but in different sites. I couldn't also find DCU structure description. There was only one info that I found about it on some russian forum. There was a public speaking of Borland. The sense of it was very simple: DCU is a private format of Borland and won't be spreaded around the world. What an arrogance! That's why I'm gonna tell you how to patch such files.
Open DataBrowser.dcu with Hiew and search for "Evaluation" string. I've found it at address 348F. Switch to decode mode. Press Ctrl + F1 to change your disassembly settings from 16 bit mode to 32. Do here the same as we do when we were cracking the BPL file. Elevate the cursor till you see almost the same code that we see in BPL.
00003432 : E800000000 call 000003437
00003437 : 50 push eax
00003438 : 68C0000000 push 0000000C0
0000343D : 6A00 push 000
0000343F : E800000000 call 000003444 ; MessageBoxA
00003444 : 33C0 xor eax,eax
Familiar code, ain't it? Sure it is! Thus, you have to patch it exactly like we do before. Check out the table below to compare you patch.
Address Original opcode Original mnemonics Patched opcode Patched mnemonics
000343D 6A 00 push 000 EB 50 jmps 0000348F
000348F 45 inc ebp 58 pop eax
0003490 76 61 jbe 000034F3 58 58 pop eax, pop eax
0003492 6C insb EB B0 jmps 00003444
Try to search the string again. No such one, which means that this file is done and we have to crack the last file.
Now open LayersComboBox.dcu in Hiew and make the same search. You should find the string at address 2695. Scroll a little up and you will see absolutely the same code as in the first dcu, only the addresses are different. At this point I suppose you know what to do with it, but nevertheless compare your patch results with mine.
Address Original opcode Original mnemonics Patched opcode Patched mnemonics
0002643 6A 00 push 000 EB 50 jmps 00002695
0002695 45 inc ebp 58 pop eax
0002696 76 61 jbe 000026F9 58 58 pop eax, pop eax
0002698 6C insb EB B0 jmps 0000264A
Search again the string to make sure there's no more such one. The search is failed, which means we've cracked it. As all of three files have been cracked this issue is over.
Living end
That's was an essay of Delphi cracking. It wasn't very hard and it wasn't too smart, but anyway this is one of the first public steps of cracking Delphi components and related files. I hope this steam of fresh knowledge will give people some more imagination to create their own cracking art.
It isn't too hard when you know what to do and how, but when don't you can waste a lot of time trying to understand how does a thing work. You may ask questions to people, you may search info over the net, etc. But the most important information for you is your own experience. In some cases some people can help if you stuck somewhere, in some cases they can't. That's why first of all, you have to dig by yourself, study yourself and public your work.
When you find a good solution of something real worthy don't put the knowledge away from people. Don't also make brain-picking, because it won't give you more respect. Share it with the mass, because we're all mortal. So don't take your knowledge to tomb.
-
Level : newbie
Manually unpacking EXEstealth 2.74a featuring erased imports and PE header, CRC check, Anti debugging/dumping checks, and API redirection.
~~~~~~~~~~~~~~~~~~~~~~~~~~~
ExeStealth v2.74a - manually unpacking
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Author: haggar
OS: Windows XP
Level: easy
(read this tutor with Courier new font)
Hi! This is simple tutorial about simple protector, ExeStealth v2.74a. If you do some Googling, you'll notice that there is newer version of this packer. So why even bother with this one? That newer version is not much interesting, it packs target .exe first with UPX and then encrypt it with Morphine. I think that anybody can unpack UPX and for Morphine you have nice KaGra's tutorial so there is no need to doing the same thing twice. This version is doing something different.
It is very easy to unpack this version in a few steps, but we will dig a little deeper in the protector and try to understand it better. Then we will try to write Olly script that will automate unpacking job.
1. TOOLS
- OllyDbg 1.10 (and it's OllyDump, OllyScript plugins)
- LordPE
- ImpREC
2. ANALYZING PROTECTOR
I've packed target file with all possible options:
- Erase header
- CRC check
- API redirection
- Imports erasing
- Anti dumping
- Anti SoftICE, SmartCheck, IDA.
2.1. Decryptor loop
So let's begin. Open target in Olly (before that unceheck all exceptions but kernel in options). If you trace with F7 you'll see that this packer is not wasting time with junk code, it starts decripting it's code right at the begining. Note that in this first loop ECX=0C96 will be counter of how many bytes will be decrypted and decryption will start from 40613F address. This is the end of this loop:
...
0040613C AA STOS BYTE PTR ES:[EDI]
0040613D ^E2 C5 LOOPD SHORT IczEdit.00406104
...
Just pass this loop (let the first opcode below LOOPD decrypts and then put bp on it ond press F9, then remove bp). After that, trace with F7 until you land on next interesting part:
2.2. Procedure that calculates CRC
004063C4 MOV EDI,EAX
004063C6 XOR EAX,EAX
004063C8 XOR EBX,EBX
004063CA XOR EDX,EDX
004063CC MOV AL,BYTE PTR DS:[EDI]
004063CE MUL EDX
004063D0 ADD EBX,EAX
004063D2 INC EDX
004063D3 INC EDI
004063D4 LOOPD SHORT IczEdit.004063CC
004063D6 XCHG EAX,EBX
004063D7 RETN
This seams to be general CRC check procedure. Protector will couple times use this procedure to calculate some summ and store it for later comparation. When you exit this loop, you'll get on place where protector is storing calculated value:
00406183 MOV DWORD PTR SS:[EBP+402FF8],EAX
First value 0002A41C will be stored to 4068C3 address/buffer. You will read more about it later.
2.3. Protector API's
Continue with F7 untill you reach this part:
0040620F CALL DWORD PTR SS:[EBP+403444] ; kernel32.LoadLibraryA
00406215 MOV ESI,EAX
00406217 MOV DWORD PTR SS:[EBP+403459],EAX
0040621D LEA EAX,DWORD PTR SS:[EBP+40345D]
00406223 CALL IczEdit.004062BE
...
...
0040629F MOV DWORD PTR SS:[EBP+4034E4],EAX
004062A5 LEA EAX,DWORD PTR SS:[EBP+4034E8]
004062AB CALL IczEdit.004062BE
004062B0 MOV DWORD PTR SS:[EBP+4034F4],EAX
004062B6 LEA EAX,DWORD PTR SS:[EBP+4029FC]
004062BC PUSH EAX
004062BD RETN
004062BE PUSH EAX
004062BF PUSH ESI
004062C0 CALL DWORD PTR SS:[EBP+403448] ; kernel32.GetProcAddress
004062C6 RETN
There, protector will load kernel32.dll in memory and use GetProcAddress to catch addresses of some API functions that it needs. It will read API names from it's own little table and write those addresses at their names. Before (zeroes are reseved for writing):
00406D39 00 00 00 00 56 69 72 74 75 61 6C 50 72 6F 74 65 ....VirtualProte
00406D49 63 74 00 00 00 00 00 47 65 74 4D 6F 64 75 6C 65 ct.....GetModule
00406D59 46 69 6C 65 4E 61 6D 65 41 00 00 00 00 00 43 72 FileNameA.....Cr
00406D69 65 61 74 65 46 69 6C 65 41 00 00 00 00 00 47 6C eateFileA.....Gl
00406D79 6F 62 61 6C 41 6C 6C 6F 63 00 00 00 00 00 47 6C obalAlloc.....Gl
00406D89 6F 62 61 6C 46 72 65 65 00 00 00 00 00 52 65 61 obalFree.....Rea
00406D99 64 46 69 6C 65 00 00 00 00 00 47 65 74 46 69 6C dFile.....GetFil
00406DA9 65 53 69 7A 65 00 00 00 00 00 43 6C 6F 73 65 48 eSize.....CloseH
00406DB9 61 6E 64 6C 65 00 00 00 00 00 49 73 44 65 62 75 andle.....IsDebu
00406DC9 67 67 65 72 50 72 65 73 65 6E 74 00 00 00 00 00 ggerPresent.....
After:
00406D39 D9 AC E7 77 56 69 72 74 75 61 6C 50 72 6F 74 65 ٬çwVirtualProte
00406D49 63 74 00 9E 16 E6 77 47 65 74 4D 6F 64 75 6C 65 ct.žćwGetModule
00406D59 46 69 6C 65 4E 61 6D 65 41 00 FC AC E7 77 43 72 FileNameA.ü¬çwCr
00406D69 65 61 74 65 46 69 6C 65 41 00 C9 B3 E7 77 47 6C eateFileA.ÉłçwGl
00406D79 6F 62 61 6C 41 6C 6C 6F 63 00 C9 56 E7 77 47 6C obalAlloc.ÉVçwGl
00406D89 6F 62 61 6C 46 72 65 65 00 04 58 E7 77 52 65 61 obalFree.XçwRea
00406D99 64 46 69 6C 65 00 A1 AA E7 77 47 65 74 46 69 6C dFile.ˇŞçwGetFil
00406DA9 65 53 69 7A 65 00 B7 15 E8 77 43 6C 6F 73 65 48 eSize.·čwCloseH
00406DB9 61 6E 64 6C 65 00 43 A6 E7 77 49 73 44 65 62 75 andle.C¦çwIsDebu
00406DC9 67 67 65 72 50 72 65 73 65 6E 74 00 00 00 00 00 ggerPresent.....
2.4. CRC of whole file
Than, protector will call VirtualProtect, GetModuleHandle (to get packed file path), CreateFileA (to open file), GetFileSize (to get file size, but note that crackme will the substract 5 from that value, ie. it will not count last 5 bytes), GlobalAlloc, ReadFile and etner again at 4063AF to previous CRC check procedure. In another words, protector will check file for some modifications. In the same CRC procedure, protector will calculate CRC summ of whole_file-last_5_bytes and then, it will store it to some buffer:
004063B4 8985 F42F4000 MOV DWORD PTR SS:[EBP+402FF4],EAX
CRC_summ=001D0B99
buffer=4068BF
2.5. Decrypting sections
Now check something interesting. Open file in hex editor and check last 5 bytes of file. The last 5 bytes are 990B1D0000 what is reverse 00001D0B99, what is our calculated CRC_summ. Now you see why protector didn't take last five bytes of file in calculation, because he wrote there it's checksum. If you continue further with F7, you'll se that GlobalFree and CloseHandle are called. If we continue to trace, we'll find next interesting place:
00406410 CMP DWORD PTR DS:[ESI],63727372
00406416 JNZ SHORT IczEdit.0040641D
00406418 JMP IczEdit.004064E2
0040641D CMP DWORD PTR DS:[ESI],7273722E
00406423 JNZ SHORT IczEdit.0040642A
00406425 JMP IczEdit.004064E2
0040642A CMP DWORD PTR DS:[ESI],7365722E
00406430 JNZ SHORT IczEdit.00406437
00406432 JMP IczEdit.004064E2
00406437 CMP DWORD PTR DS:[ESI],6164652E
0040643D JNZ SHORT IczEdit.00406444
0040643F JMP IczEdit.004064E2
00406444 CMP DWORD PTR DS:[ESI],6F6C6572
0040644A JNZ SHORT IczEdit.00406451
0040644C JMP IczEdit.004064E2
00406451 CMP DWORD PTR DS:[ESI],6C65722E
00406457 JNZ SHORT IczEdit.0040645E
00406459 JMP IczEdit.004064E2
0040645E CMP DWORD PTR DS:[ESI],73656F6E
00406464 JNZ SHORT IczEdit.00406468
00406466 JMP SHORT IczEdit.004064E2
00406468 CMP DWORD PTR DS:[ESI],53657845
0040646E JNZ SHORT IczEdit.00406472
00406470 JMP SHORT IczEdit.004064E2
00406472 CMP DWORD PTR DS:[ESI],6164652E
00406478 JNZ SHORT IczEdit.0040647C
0040647A JMP SHORT IczEdit.004064E2
Here protector takes section names from PE header DS:[ESI], and compare them with some hardcoded:
rsrc , .rsr , .res , .eda , relo , .rel , noes , ExeS, .eda
What's the point? When some section name pass this checks, you'll see that protector is decrypting that section. Conclusion is that protector will not decrypt (and that means that it didn't encrypt them either) one of these sectiones. Why? I don't know what all those sections mean, but I know that there is bug in oleaut32.dll which forbide .rsrc section to change name. Protector will obviously not encrypt resource section. It will also not encrypt it's own ExeS section in this loop. Next little check is checking does section Physical Size == 0 and is it's Physical Offset == 0:
0040647C CMP DWORD PTR DS:[ESI+14],0
00406480 JE SHORT IczEdit.00406488
00406482 CMP DWORD PTR DS:[ESI+10],0
00406486 JNZ SHORT IczEdit.0040648A
00406488 JMP SHORT IczEdit.004064E2
In both cases, decryption is skipped. After decryption is done, program flow continues to place where next section is taken or, if counter of sections EDX reach last number, decryption is done:
004064E1 POPAD
004064E2 ADD ESI,28
004064E5 INC EDX
004064E6 CMP DX,WORD PTR DS:[EDI+6]
004064EA JNZ IczEdit.00406410
004064F0 RETN
2.6. Checking CRC of whole file
After decryption, continue to first CRC check:
00406532 MOV EAX,DWORD PTR SS:[EBP+402FF4]
00406538 OR EAX,EAX
0040653A JE SHORT IczEdit.00406549
0040653C CMP EAX,DWORD PTR SS:[EBP+403522]
00406542 JE SHORT IczEdit.00406549
00406544 JMP IczEdit.004066F8
To EAX is poped CRC_summ=001D0B99 of whole_file-5_bytes and compared with hard written at the end of file. If that values are not equal, JMP at 406544 that lead to ExitThread will be executed.
2.7. Imports redirection
First, protector will count how many there are imports and it will allocate some memory for redirections:
00406575 CMP DWORD PTR DS:[EDX],0
00406578 JNZ SHORT IczEdit.00406571
0040657A ADD ESI,0C
0040657D CMP DWORD PTR DS:[ESI+4],0
00406581 JNZ SHORT IczEdit.00406566
00406583 XOR EDX,EDX
00406585 MOV EAX,5
0040658A MUL ECX
0040658C PUSH EAX
0040658D PUSH 0
0040658F CALL DWORD PTR SS:[EBP+4034B8]
00406595 OR EAX,EAX
00406597 JNZ SHORT IczEdit.0040659E
00406599 ADD ESP,4
0040659C POPAD
0040659D RETN
ECX=49 is number of imports. Then protector will decrypt one DLL name in this call:
...
004065B3 CALL IczEdit.004065C0
...
...
004065C0 PUSH ESI
004065C1 PUSH EDI
004065C2 MOV ESI,EAX
004065C4 MOV EDI,EAX
004065C6 LODS BYTE PTR DS:[ESI]
004065C7 ROR AL,4
004065CA STOS BYTE PTR ES:[EDI]
004065CB CMP BYTE PTR DS:[EDI],0
004065CE JNZ SHORT IczEdit.004065C6
004065D0 POP EDI
004065D1 POP ESI
004065D2 RETN
First decrypted DLL is GDI32.DLL. After decrypting, protector will load this DLL:
004065D4 FF95 44344000 CALL DWORD PTR SS:[EBP+403444] ; kernel32.LoadLibraryA
and then it will delete DLL name from imports section jumping to:
00406845 JMP SHORT IczEdit.0040684B
00406847 MOV BYTE PTR DS:[EAX],0
0040684A INC EAX
0040684B CMP BYTE PTR DS:[EAX],0
0040684E JNZ SHORT IczEdit.00406847
00406850 RETN
After loading DLL, protector will decrypt it's API functions one by one in the same decrypting loop and than, it will get that API address via GetProcAddress and store it:
00406630 CALL IczEdit.004065C0 ; Decrypt API names.
00406635 POP EAX
00406636 MOV EDI,EAX
00406638 PUSH EDX
00406639 PUSH ECX
0040663A PUSH EAX
0040663B PUSH EBX
0040663C CALL DWORD PTR SS:[EBP+403448] ; kernel32.GetProcAddress
00406642 OR EAX,EAX
00406644 JNZ SHORT IczEdit.0040664D
00406646 POP ECX
00406647 POP EDX
00406648 JMP IczEdit.004066F8
0040664D POP ECX
0040664E POP EDX
0040664F PUSHAD
00406650 TEST DWORD PTR SS:[EBP+402FF0],4
0040665A JE SHORT IczEdit.0040666A
0040665C LEA EAX,DWORD PTR SS:[EBP+402D9F]
00406662 PUSH EAX
00406663 MOV EAX,EDI
00406665 JMP IczEdit.00406845 ; Erase API name.
0040666A POPAD
0040666B MOV DWORD PTR DS:[EDX],EAX ; Write API address to IAT.
In this first DLL case, protector will not redirect imports, instead it will write it at it's correct place. Then it will decrypt and load second DLL, comdlg32.dll, write it's imports at the correct place, but it will then jump at:
004066BB PUSH EDI ; IczEdit.0040335C
004066BC PUSH ESI
004066BD LEA EDI,DWORD PTR SS:[EBP+403526]
004066C3 MOV ESI,DWORD PTR DS:[EDI+4]
004066C6 MOV DWORD PTR DS:[EDX],ESI ; Redirect API's!
004066C8 SUB EAX,ESI
004066CA SUB EAX,5
004066CD MOV BYTE PTR DS:[ESI],0E9
004066D0 MOV DWORD PTR DS:[ESI+1],EAX
004066D3 ADD DWORD PTR DS:[EDI+4],5
004066D7 POP ESI
...
...
Here it will redirect imports to allocated place. After that, it will again delete API name. Now you know what hapens with imports. Imports are first written to their correct place, but then, some of them are redirected and all DLL and API names are deleted. This prevents simple dumping but ImpREC can easy find and rebuild this kind of IAT destroing.
2.8. Erasing PE header
This is easy job. After loading all necesery DLL's, redirecting and erasing, protector will delete whole PE header to prevent correct dumping:
004066FF TEST DWORD PTR SS:[EBP+402FF0],2
00406709 JE SHORT IczEdit.00406723
0040670B MOV EDI,DWORD PTR SS:[EBP+402FE8]
00406711 ADD EDI,DWORD PTR DS:[EDI+3C]
00406714 MOV ECX,DWORD PTR DS:[EDI+54]
00406717 MOV ESI,DWORD PTR SS:[EBP+402FE8]
0040671D MOV BYTE PTR DS:[ESI],0 ; Erasing PE header.
00406720 INC ESI
00406721 LOOPD SHORT IczEdit.0040671D
This is easy to fix. We can copy-paste header from unpacked file, or simply patch opcode at 40671D. But, later about this.
2.9. CRC check - again
And now it comes clear what does CRC at the begining do. Go and look at "2.2. Procedure that calculates CRC" at the begining of the tutorial. There was calculated CRC value 0002A41C of protector section that was never used up to now. Here, again is called same CRC calculation procedure and again is calculated same CRC summ. Than this new value will be compared with old one:
00406739 MOV EBX,DWORD PTR SS:[EBP+402FF8]
0040673F XOR EAX,EBX
00406741 JE SHORT IczEdit.0040674B
00406743 JMP SHORT IczEdit.00406746
This check will detect any changes made to ExeStealth code in memory such as our breakpoints or patches. JE at 406741 must be executet. If not, program flow will lead to ExitThread in kernel32.dll. Since I used togle bp at the end of CRC calculation loop, that CC bp was taken in calculation. Simply set EAX=EBX=0 before XOR opcode and you will pass this check.
2.10. Secod decryptor loop and anti debugger stuff
When you pass abowe CRC check, you will land in next decryptor loop what decrypts next pice of ExeStealth code. Do the same as in the first loop to decrypt code. Then IsDebuggerPresent is called:
00406766 LEA EAX,DWORD PTR SS:[EBP+4034F8]
0040676C PUSH EAX
0040676D PUSH DWORD PTR SS:[EBP+403459]
00406773 CALL DWORD PTR SS:[EBP+403448] ; kernel32.GetProcAddress
00406779 OR EAX,EAX
0040677B JE SHORT IczEdit.00406785
0040677D CALL EAX ; kernel32.IsDebuggerPresent
0040677F OR EAX,EAX
00406781 JE SHORT IczEdit.00406785
00406783 POPAD
00406784 RETN
Simply, one of two JE SHORT IczEdit.00406785 must be executed to pass this check. The easiest way is to use IsDebugPresent plugin, or you just set EAX value to zero at 40677F.
After that, if you continue tracing with F7, you'll face on INT 68. I'm not much familiar with interrupts, but it's not so important right now. Scroll down a little and place bp on:
0040681A XOR AL,AL
0040681C LEA EDI,DWORD PTR SS:[EBP+402795]
00406822 MOV ECX,788
00406827 STOS BYTE PTR ES:[EDI]
00406828 LOOPD SHORT IczEdit.00406827
0040682A LEA EDI,DWORD PTR SS:[EBP+402F7A]
00406830 MOV ECX,590
00406835 STOS BYTE PTR ES:[EDI]
00406836 LOOPD SHORT IczEdit.00406835
00406838 POPAD
Press Shift+F9 to break on your bp and then remove it. Place new bp on POPAD instruction and run. You'll see that those two loops deleted all code abowe and below them. Only thing that's left is:
00406838 POPAD
00406839 PUSH EAX
0040683A XOR EAX,EAX
0040683C PUSH DWORD PTR FS:[EAX]
0040683F MOV DWORD PTR FS:[EAX],ESP
00406842 JMP SHORT IczEdit.00406845
00406844 XCHG DWORD PTR DS:[EAX],EAX
00406846 ADD BYTE PTR DS:[EAX],AL
00406848 ADD BYTE PTR DS:[EAX],AL
0040684A ADD BYTE PTR DS:[EAX],AL
0040684C ADD BYTE PTR DS:[EAX],AL
...
...
This is the end ;) Place memory bp on access on whole .code section and run target (Shift+F9 two times). You'll land on OEP of packed target:
00401000 MOV BYTE PTR DS:[40438C],0 ; This should be the OEP!
00401007 MOV BYTE PTR DS:[40448C],0
0040100E PUSH 0 ; /pModule = NULL
00401010 CALL IczEdit.00402E7A ; GetModuleHandleA
...
...
3. QUICK WAY TO UNPACK IT
-To find OEP, uncheck all exceptions, use olly plugin to hide debugger, press Shift+F9 untill you reach last exception, place memory breakpoint to code section and pressShift+F9.
-To restore missing PE header, open in new olly packed file and copy-paste it's PE header and then dump file with olly dump plugin.
-To rebuild IAT, use ImpREC.
That is one way to unpack it. Second way would be to patch all those erasing opcodes that destroys PE header, API/DLL names and redirections. We can write Olly script that will do that.
4. MAKING OLLY SCRIPT
We will make olly script for automaticly unpacking ExeStealth v2.74a. For this you need OllyScript plugin (I don't know does ShAg support it anymore. Last time his site was down :( , but try get it from OllyDbg forum). Making scripts it's very easy, just be sure that you have read readme.txt included with plugin.
Advantage of scripts is to make boring unpacking faster. After you unpack ASpack for 100 times manually, it's no fun anymore. Also, with scripts you can make much simpler some tasks that are manually hard, like redirecting some imports or similar.
As we sow, our packer redirect imports, deletes DLL's and API's names, and it erases PE header. We have fixed that using ImpREC and pasting PE header from unpacked file, but with script, we can patch redirection and erasing procedures so dumped file will not be damaged.
The logic for making script was this:
- First we must hide debugger.
- Then we must get to place where interesting things happens. For that I used GetProcAddress to break on, since that API is called after first decryption loop is done.
- Then we must find places where DLL and API names are erased and patch those instructions.
- Find redirection instruction and patch it.
- Find PE header erasing loop and patch it.
- Patch CRC check.
- Land on OEP.
I have included script for this version of EXEstealth in the archive. Read it in notepad, I've wrote lot of coments so it won't be hard to understand it.
5. FINAL WORDS
Yep, it's easy protector to bypass.
After done with this protector I've grabed Yoda's Crypter 1.3 and done little examing. I was surprised! Yoda Crypter 1.3 is completly indentical with EXEstealth 2.74a! Header erasing, IAT redirection, CRC checks and rest of it's functions that are described in this tutorial are implemented in same way. It means that EXEshield 2.74a is rip of Yoda's Crypter! And guy is seling it and did not even mention Yoda's name in the about box. Yoda is freeware and open source protection/crypter. And new EXEstealth v3.04 is using UPX and Morphine who are again freeware and open source too. Only this time he did mention reall software authors.
I have notice one more interesting thing: if do not use all options while packing target, like you don't wan't to use CRC check or PE header erasing, packer will not exclude it from loader code, it will place them there, but it will jump over them using contitional jumps. So this could be even easier way to prevent erasing header and IAT and redirecting it.
-
Level : beginner
PESpin v0.3 has some nice features; good IAT redirection with some emulation API opcodes and stolen OEP are it's best weapon. It has also some antidebug tricks based on exceptions, but Olly is imune on that, it checks for SoftICE and NTICE, it has couple CRC checks, and it has password locking and time limit.
~~~~~~~~~~~~~~~~~~~~~~~
PESpin v0.3 - manually unpacking
~~~~~~~~~~~~~~~~~~~~~~~
author: haggar
OS: Windows XP
level: beginner-advanced
Greets!
This is older version of PESpin, but I have decide to start with it, tought it will be easier later to try new versions. Tutorial is for beginners who have some experience with unpacking protectors, also you must be familiar with tools (Olly, ImpREc, LordPE).
PESpin v0.3 has some nice features; good IAT redirection with some emulation API opcodes and stolen OEP are it's best weapon. It has also some antidebug tricks based on exceptions, but Olly is imune on that, it checks for SoftICE and NTICE, it has couple CRC checks, and it has password locking and time limit, but that's not problem. Also there is some small diferences between packed files compiled with different compilers (VC++,Borland C++, Delphi, VB, ASM...).
1. ANALYZING PESpin CODE
Grab Olly, exclude all exceptions in options and load target, cruehead's CRACKME3.EXE which I packed with PESpin. Scroll little down and you'll see two decrypting loops that decrypt PESpin code section:
00407127 XOR BYTE PTR DS:[ECX+EDI],BL
0040712A DEC BL
0040712C LOOPD SHORT CRACKME3.00407127 ;First loop.
0040712E PUSH 13C
00407133 POP ECX
00407134 LEA EDI,DWORD PTR SS:[EBP+4036B6]
0040713A ROR BYTE PTR DS:[ECX+EDI],2
0040713E LOOPD SHORT CRACKME3.0040713A ;Second loop.
00407140 CALL CRACKME3.00407147 ;Place bp here to pass them.
Pass those loops and trace to here:
00408138 MOV EDI,DWORD PTR SS:[ESP+20]
0040813C AND EDI,FFFF0000
00408142 CMP WORD PTR DS:[EDI],5A4D
00408147 JNZ SHORT CRACKME3.0040815A
00408149 MOVZX EDX,WORD PTR DS:[EDI+3C]
0040814D TEST DX,0F800
00408152 JNZ SHORT CRACKME3.0040815A
00408154 CMP EDI,DWORD PTR DS:[EDX+EDI+34]
00408158 JE SHORT CRACKME3.00408162
0040815A SUB EDI,10000
00408160 JMP SHORT CRACKME3.00408142
00408162 XCHG EAX,EDI
00408163 PUSH CRACKME3.00402CFC
00408168 PUSH EAX
00408169 XCHG DWORD PTR SS:[EBP+402CED],EAX
0040816F ADD DWORD PTR SS:[ESP+4],EBP
00408173 LEA EAX,DWORD PTR SS:[EBP+EB8382F8]
00408179 LEA EAX,DWORD PTR DS:[EAX+14BCAABD]
0040817F CALL EAX
Here starts procedure that will find all API's in kernel that PESpin needs for it's work. This first part seams that want be sure that it is in kernel32.dll. Then you'll enter in CALL EAX where PESpin search for API's in some weird way. For us is important which are those API's. Trace to here and you'll see that here API addresses are stored to some location:
00408307 MOV DWORD PTR DS:[EDI+1],EAX
API that PESpin want are:
LoadLibraryA
ExitProcess
GetProcAddress
VirtualProtect
CloseHandle
VirtualAlloc
VirtualFree
CreateFileA
ReadFile
VirtualQuery
GetTickCount
GetModuleHandleA
CreateThread
Sleep
GetCurrentProcessId
OpenProcess
TerminateProcess
GetFileSize
GetModuleFileNameA
Then PESpin will make some exceptions which purpose is to detect debugger, but olly doesn't fall on this so you do this: press Shift+F9 4. times and then, place memory bp on PESpin section press Shift+F9 untill you break on mem bp. Code that we skiped doesnt hold anything important for our unpacking, just some exceptions tricks and one decryption loop. You should be here:
00407276 MOV ECX,13
0040727B CALL CRACKME3.00407280
...
...
Trace with F7 and you'll get here:
004072BF JMP DWORD PTR SS:[EBP+402D57]
There is called GetModuleFileNameA API that searchin path for our packed file, then CreateFile opens our file, GetFileSize, VirtualAlloc will reserve enough place in memory for (starting from 390000 on my computer) loading that file, ReadFile will load it to that allocated place, CloseHandle. Than you will enter to CRC calculating routine which calculates CRC of whole file:
00408864 PUSH ECX
00408865 OR DL,4
00408868 INC EDI
00408869 XOR AH,BYTE PTR DS:[EDI]
0040886B SHR EAX,3
0040886E XOR AL,BH
00408870 ADD EAX,7801A018
00408875 XOR EAX,EBX
00408877 MOV CL,BL
00408879 ROR EAX,CL
0040887B XCHG EAX,EBX
0040887C DEC EDX
0040887D JNZ SHORT CRACKME3.0040886E
0040887F POP ECX
00408880 LOOPD SHORT CRACKME3.00408864
00408882 XCHG EAX,EBX
00408883 POP EBX
00408884 POP EDX
00408885 RETN
Then that CRC is substracted from real one which is written in file and stored to it's place:
00407397 SUB DWORD PTR SS:[EBP+403827],EAX
If you open crackme in hex editor or take look a in olly dump last section, you can find where is written reall CRC. Than is caled VirtualFree. After that you will get on one exception, put mem bp on .taz section and press Shift+F9 untill you break here:
00408240 NOP
00408241 NOP
00408242 XOR EBX,EBX
00408244 POP DWORD PTR FS:[EBX]
00408247 POP EBX
00408248 SUB EBX,16
0040824E JMP SHORT CRACKME3.00408251
....
....
Remove bp and trace in to:
00408251 CMP BYTE PTR DS:[EBX],0CC
00408254 JNZ SHORT CRACKME3.00408261
00408256 AND ESP,0FFFF
0040825C CALL CRACKME3.0040827B
00408261 JMP EBX
Here packer is checking is breakpoint placed on [ebx]=4073e0 address. Pass that check and you'll get to that address, then trace and trace untill you get here:
0040745A MOVZX ECX,WORD PTR SS:[EBP+402CCF]
00407461 MOV EDX,DWORD PTR SS:[EBP+402CD5]
00407467 ADD EDX,0F8
0040746D MOV EBX,DWORD PTR SS:[EBP+403817]
00407473 XOR EAX,EAX
00407475 PUSH ECX
00407476 BT EBX,EAX
00407479 JNB SHORT CRACKME3.0040749F
0040747B PUSH EDX
0040747C MOV EDI,DWORD PTR DS:[EDX+C]
0040747F ADD EDI,DWORD PTR SS:[EBP+402CCB]
00407485 MOV ECX,DWORD PTR DS:[EDX+10]
00407488 MOV EDX,DWORD PTR SS:[EBP+403827] ;CRC value is taken.
0040748E SHR EDX,1
00407490 JB SHORT CRACKME3.00407498
00407492 XOR EDX,ED43AF32
00407498 XOR BYTE PTR DS:[EDI],DL ;Decrypting section.
0040749A INC EDI
0040749B DEC ECX
0040749C LOOPD SHORT CRACKME3.0040748E
0040749E POP EDX
0040749F INC EAX
004074A0 ADD EDX,28
004074A3 POP ECX
004074A4 LOOPD SHORT CRACKME3.00407475
004074A6 OR DWORD PTR SS:[EBP+4036B0],0
004074AD JE SHORT CRACKME3.004074BC
004074AF LEA EAX,DWORD PTR SS:[EBP+403524]
004074B5 SUB EAX,3D1
004074BA CALL EAX
OK, we see that it takes that CRC value and then it decrypts .code section with it. Hmm, maybe that value isn't CRC at all, but some key that purpose is to decrypt sections? And maybe is both? I think that it's some key that decrypts sectiones. Pass this check and all sections will be decrypted , but that's only one layer. Trace further and you'll get to SOFTICE check, procedure starts at 408620 and ends at 40867D. It uses CreateFileA to search for SICE and NTICE files and if finds them, program will terminate. Tracing further you'll find one more decryptor loop at 4074dc and one more at 4088B9, not much interesting for us. Let we speed up a little and place bp on VirtualAlloc in command bar, then press Shift+F9 until we break on it. Remove it and keep tracing untill you get here
004078A4 PUSHAD
004078A5 MOV ESI,DWORD PTR SS:[ESP+24]
004078A9 MOV EDI,DWORD PTR SS:[ESP+28]
004078AD CLD
004078AE MOV DL,80
004078B0 XOR EBX,EBX
004078B2 MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
004078B3 MOV BL,2
004078B5 CALL CRACKME3.00407927
004078BA JNB SHORT CRACKME3.004078B2
...
...
0040792D INC ESI
0040792E ADC DL,DL
00407930 RETN
00407931 XOR ECX,ECX
00407933 INC ECX
00407934 CALL CRACKME3.00407927
00407939 ADC ECX,ECX
0040793B CALL CRACKME3.00407927
00407940 JB SHORT CRACKME3.00407934
00407942 RETN
00407943 SUB EDI,DWORD PTR SS:[ESP+28]
00407947 MOV DWORD PTR SS:[ESP+1C],EDI
0040794B POPAD
0040794C RETN ;Place bp here!!!
This is unpacking procedure which will unpack or decrypt sections. It goes like this: first was allocated enough memory, then section is unpacked there, than original section is erased and unpacked is copied from alocated memory to original section, then VirtuallFree is called and VirtualProtect. Place bp on last RETN and run 2 times untill packed section are unpacked, remove bp , put bp on VirtualFree, Shift+F9 untill you break on it, return to user code and you should be here:
00408B60 LEA ESP,DWORD PTR SS:[ESP+4]
00408B64 LEA EAX,DWORD PTR SS:[EBP+12256A5]
00408B6A SUB EAX,0E23546
00408B6F JMP EAX
Enter in that EAX and you'll get to the most interesting place:
00407620 CALL CRACKME3.00407628 ;You're here!!!!
00407625 JMP SHORT CRACKME3.0040762B
00407627 STC
00407628 JMP SHORT CRACKME3.00407625
0040762A SBB EAX,DWORD PTR DS:[EBX+C30C2404]
00407630 CMP BYTE PTR SS:[EBP+40218485],CL
00407636 ADD BYTE PTR DS:[EAX+9D8D7C00],AL
0040763C MOV FS,WORD PTR DS:[EAX+EAX*2]
0040763F ADD BYTE PTR DS:[EBX],CH
00407641 FADD DWORD PTR DS:[EAX-77]
00407644 SBB BYTE PTR SS:[EBP-5],CH
00407647 AAA
00407648 MOV AL,BYTE PTR DS:[EAX]
0040764A CALL CRACKME3.00407669
0040764F PUSHAD ;Here starts import redirection code:
....
.... ;Lot of jumps and stuff here.
....
....
00407708 POPAD ;Here ends import redirection.
00407709 RETN
0040770A ADD DWORD PTR DS:[EDI],ECX
0040770C PUSH EDI
0040770D ADD EDX,ECX
0040770F MOV EDI,ESI
00407711 MOV ESI,EAX
00407713 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[>
00407715 MOV EAX,ESI
00407717 MOV ESI,EDI
00407719 POP EDI
0040771A JMP CRACKME3.00407675
We will not enter in imports redirection procedure yet. I will talk about that later so trace further with F7, you'll get to VirtualProtect again, just trace until you get to this place that looks like dead end street:
004001F8 MOV ESI,CRACKME3.004001FF
004001FD POP DWORD PTR DS:[ESI]
004001FF ADD BYTE PTR DS:[EAX],AL
00400201 ADC BYTE PTR DS:[EAX],AL
00400203 ADD BYTE PTR DS:[EAX],AL
00400205 ADC BYTE PTR DS:[EAX],AL
00400207 ADD BYTE PTR DS:[EAX],AL
00400209 ADD AL,0
0040020B ADD BYTE PTR DS:[EAX],AL
0040020D PUSH ES
0040020E ADD BYTE PTR DS:[EAX],AL
00400210 ADD BYTE PTR DS:[EAX],AL
Place mem bp on .taz section and run untill you break to:
00408553 SUB EBX,EBX
00408555 MOV ESP,DWORD PTR SS:[ESP+8]
00408559 POP DWORD PTR FS:[EBX]
0040855C POP ECX
0040855D POP EBP
0040855E MOV EDI,CRACKME3.00400100
Trace till you get to first GetTickCount API call:
0040857D CALL DWORD PTR SS:[ESP] ; kernel32.GetTickCount
Than till second (right below it):
00408591 JMP DWORD PTR SS:[ESP+EBX-4] ; kernel32.GetTickCount
Packer uses two GetTickCount calls, why I don't know but this will be usefull information for us. Press Shift+F9 once to get to INT3 exception. This is another debugger check, place mem bp on .taz section, run and you're here (right below):
00408A27 JMP SHORT CRACKME3.00408A2A
Now trace and trace alot untill you enter to VirtualAlloc, exit it, trace and trace ..... and you'll get here where packer decrypts DLL names:
004086B3 JE SHORT CRACKME3.004086BA
004086B5 NOT BYTE PTR DS:[EDI] ;This decrypts.
004086B7 INC EDI
004086B8 JMP SHORT CRACKME3.004086B0
004086BA POP EDI
004086BB RETN
Trace and you'll be thrown to LoadLibraryA. Place mem bp on .taz section and press Shift+F9 to get out from there:
77ED6FC4 >JMP BAE8CFE1
77ED6FC9 NOP
77ED6FCA NOP
77ED6FCB NOP
77ED6FCC NOP
77ED6FCD NOP
77ED6FCE NOP
77ED6FCF NOP
77ED6FD0 NOP
77ED6FD1 NOP
77ED6FD2 NOP
77ED6FD3 >JMP BAE8D155
77ED6FD8 NOP
77ED6FD9 NOP
77ED6FDA NOP
77ED6FDB NOP
You should be here:
00407A4D TEST EAX,EAX
You know what to do :), trace again and you will get to DLL name erasing:
00407A8A OR BYTE PTR DS:[EBX],0
00407A8D JE SHORT CRACKME3.00407A9C
00407A8F MOV BYTE PTR DS:[EBX],DL
00407A91 ROL EDX,4
When erasing is over trace to this interesting place
004077D4 CMP BYTE PTR SS:[EBP+4026E5],0CC
004077DB JNZ SHORT CRACKME3.004077E2
004077DD JE SHORT CRACKME3.004077E0
Here it checks is there bp placed on that [EBP+xxxxx] address which is here:
00407BA6 STC
00407BA7 JB SHORT CRACKME3.00407BAE
00407BA9 CALL CRACKME3.0040867E
00407BAE JMP SHORT CRACKME3.00407BB1
That is important place since PESpin is checking it. This place is very close to the end of protector code. That call between two jumps is optional thing; if you chose in PESpin options "Close program after: xxx minutes" that CALL will be executed and there will start thread that will close program after specified time. After this, protector will start with IAT redirection in that big procedure that I mention earlier. Then we must find stolen OEP bytes. That is not far away from here. We must find one POPAD opcode that is last PESpin code before stolen bytes and jump to real OEP. Scroll down:
00407BFA 68 E8030000 PUSH 3E8
00407BFF 00EB ADD BL,CH
00407C01 04 1F ADD AL,1F
00407C03 ^EB FB JMP SHORT CRACKME3.00407C00
00407C05 ^7C 83 JL SHORT CRACKME3.00407B8A
00407C07 04 24 ADD AL,24
00407C09 0C C3 OR AL,0C3
00407C0B E8 616A00EB CALL EB40E671 ;Here it is POPAD opcode!!!!!!!
00407C10 01E4 ADD ESP,ESP
00407C12 E8 6D98FFFF CALL CRACKME3.00401484
00407C17 -E9 EB93FFFF JMP CRACKME3.00401007
00407C1C AE SCAS BYTE PTR ES:[EDI]
Since this place has obfuscated code, Olly doesn't disply it right, but you will see one 61 byte at 407C0B. 61hex=POPAD. Binary edit (NOP one byte) that line of code to make it clear:
00407C07 04 24 ADD AL,24
00407C09 0C C3 OR AL,0C3
00407C0B 90 NOP
00407C0C 61 POPAD
00407C0D 6A 00 PUSH 0
00407C0F EB 01 JMP SHORT CRACKME3.00407C12
Now place bp on POPAD and run crackme. Trace and you'll enter in code section at some 401xxx address, but you will probably be confused by the look of code. Than you will enter to kernel32 and then return to packer code. That is becuse PESpin stole couple first instructions and first instructions of this crackme are some API calls so here is PESpin still interfear with crackme. If you would like, you can trace further and enter to IAT relocations. There you can see how PESpin has mangled imports.
We now know what PESpin is doing and now we gonna unpack our target in a fast way, fix IAT and find stolen OEP.
2. UNPACKING, FIXING IAT AND STOLEN OEP
PESpin , when packing our file, has already changed IAT and OEP of our packed program. But IAT relocation will be done on the fly, in the memory and we can prevent that to happened. GetTickCount is one of last API that are called by protector code and it's very useful since it throw us at most interesting parts, so place bp on it in command bar and run crackme twice (ignore all exceptions in olly before, we don't need them anymore). When you break second time in kernel, remove bp and return to:
0040797F E8 03000000 CALL CRACKME3.00407987
00407984 EB 04 JMP SHORT CRACKME3.0040798A
00407986 DFEB FUCOMIP ST,ST(3)
00407988 FB STI
00407989 DE83 04240CC3 FIADD WORD PTR DS:[EBX+C30C2404]
0040798F D88D 8533734B FMUL DWORD PTR SS:[EBP+4B733385]
Scroll down to our hot spot:
00407BA6 F9 STC ;This opcode is checked for bp.
00407BA7 72 05 JB SHORT CRACKME3.00407BAE
00407BA9 E8 D00A0000 CALL CRACKME3.0040867E ;Timer call, not used here.
00407BAE EB 01 JMP SHORT CRACKME3.00407BB1
If program is using timer feature, it will have this shape since it will enter in timer CALL:
00406BA6 F8 CLC
00406BA7 72 05 JB SHORT abexcrac.00406BAE
00406BA9 E8 D00A0000 CALL abexcrac.0040767E ;Patch this CALL, to kill timer.
00406BAE EB 01 JMP SHORT abexcrac.00406BB1
If you cannot find that opcodes sometimes, think that code is inproperly shown in Olly and search bytes for bytes.
Then scroll down and find POPAD opcode or 61 byte:
00407C0B E8 616A00EB CALL EB40E671 ; It's obfuscated so you will not see it at first.
Patch first byte E8 to 90:
00407C0B 90 NOP
00407C0C 61 POPAD ; Place bp here!!!!!!!!!!!!!
00407C0D 6A 00 PUSH 0
We will return to this place later. We have to fix IAT now.
Scroll waaaaaay up to here:
0040764F 60 PUSHAD
00407650 8DBD D92C4000 LEA EDI,DWORD PTR SS:[EBP+402CD9]
....
....
00407701 C1CA 08 ROR EDX,8
00407704 66:8956 01 MOV WORD PTR DS:[ESI+1],DX
00407708 61 POPAD
00407709 C3 RETN
We sad that is redirection procedure. To prevent IAT redirection, NOP all bytes between PUSHAD and POPAD:
0040764F 60 PUSHAD
00407650 90 NOP
00407651 90 NOP
00407652 90 NOP
....
....
00407705 90 NOP
00407706 90 NOP
00407707 90 NOP
00407708 61 POPAD
00407709 C3 RETN
So this procedure will not do anything now ;).
Maybe this is not the best and the most clever solution, but I couldn't find place to patch or change only few instructions. There is one jump which can be redirected, but I had some errors while testing bigger files (Mozilla ~7 MB,~2000 Imports).
IAT redirection is fixed and now we can go to OEP problem. Run crackme and you will break on our bp:
00407C0C 61 POPAD
00407C0D 6A 00 PUSH 0
00407C0F EB 01 JMP SHORT CRACKME3.00407C12
...
...
Trace to PUSH 0. Below POPAD is first stolen opcode of our OEP. If on that place is some jump to code section, that means that there are no stolen bytes. Our example has them. As I sad, that is the place where stolen OEP is and we can find our bytes now, but guess what? We don't need to do that! If you like, you can dump file now and fix IAT with ImpREC and file is properly unpacked and it is not protected anymore. Fact that it starts from protectors section doesn't change anything. But if you would like to be Mr. Perfect, we will find stolen bytes and restore them. It's easy!
Obfuscated code looks like this in Olly:
00407C0C 61 POPAD
00407C0D 6A 00 PUSH 0 ;First stolen opcode.
00407C0F EB 01 JMP SHORT CRACKME3.00407C12
00407C11 E4 E8 IN AL,0E8 ;Patch first byte.
00407C13 6D INS DWORD PTR ES:[EDI],DX
00407C14 98 CWDE
00407C15 FFFF ???
00407C17 -E9 EB93FFFF JMP CRACKME3.00401007
Patch false bytes to get correct picture (only one byte in this example):
00407C0C 61 POPAD
00407C0D 6A 00 PUSH 0 ;Stolen code.
00407C0F EB 01 JMP SHORT CRACKME3.00407C12 ;Junk jump.
00407C11 90 NOP ;Patched byte, junk too.
00407C12 E8 6D98FFFF CALL CRACKME3.00401484 ;Stolen code.
00407C17 -E9 EB93FFFF JMP CRACKME3.00401007 ;Jump to false OEP, go to it.
Write those stolen bytes and jump to false OEP, scroll up and restore bytes instead zeroes, dump file with ollydump, OEP is 401000=1000. Run file and it works fine! Yeah ;)
3. ISSUES
-Delphi programs dump with OllyDump plugin (do not check repair IAT option) and repair IAT with ImpREC. If ImpREC cannot find imports at real OEP, set OEP to 1000 and then try. When it finds imports, restore real OEP and then fix dump. This common issue with ImpREC and Delphi packed programs with lot of packers.
-ASM and Borland C++ programs can be dumped and fixed with OllyDump plugin. You can use ImpREc if you like or have problems with plugin in-built fixer.
-MSVC++ , dump with OllyDump fix IAT with it or with ImpREC.
-MSVB, use what you want.
LordPE cannot dump files. Why, I don't know.
I have included OllyScript for PESpin 0.3 that fix IAT and finds stolen bytes. Works pretty well ,although some Delphi file can crush after it ;)
Note: If you have some target that PEiD sad it's PESpin 0.3 protected, and you cannot unpack it with this method, that's because that file is packed with different version of PESpin no mater what PEiD sad.
4. FINAL WORDS
Huh, this tutorial was exhausting to write. Sorry for errors, mistakes and bad grammar. Included abexcrackme2.exe is for your homework, it's packed with timer (it will autoclose in 2 minutes) and password. Password is 123456. Unpack it!
Good by, and good night. zZZzzzzZZZZzzZZZZzzZZZzzZZzzZZzzzzzzzz.......
-
Level : beginner
Manually unpacking TElock 0.98
~~~~~~~~~~~~~~~~~~~~~~~~
tElock 0.98 - manually unpacking
~~~~~~~~~~~~~~~~~~~~~~~~
Author: haggar
Objective: tElock 0.98 unpacking
OS: Windows XP
Level: easy
1. TOOLS AND REQUIREMENTS
- OllyDbg v1.10
- LordPE
- ImportREC
2. INTRO
Hi and welcome to my tElock unpacking tutorial :)!
I was thinking to write more detailed tutorial about tElock, but such text would become too large and I didn't have strength to explain all things that I would love to say, so I wrote this shorter version. Consider this tutorial as a simple guideline for unpacking and not as brief explanation of tElock features.
Tutorial is dedicated for beginers who has some experience with simple packers and who are familiar with usuall tools like Olly, LordPE and ImpREC.
tElock 0.98 is simple protector. It's main features are:
- obfuscated code with exceptions, encrypted sections with couple layers
- some antidebug stuff
- first CRC check that checking is file modified
- second CRC check that checks file in memory and discover breakpoints
- imorts splitting and redirection, this is tE best weapon.
Our way of unpacking is:
- find OEP
- restore redirected imports
- dump file and use ImpREC to rebuild IAT.
3. EXAMING tElock CODE
3.1. tElock API's
Uncheck all exceptions in Olly options but those in KERNEL. Press Shift+F9 three times and you'll stop here:
0040608D 90 NOP
0040608E 8BC0 MOV EAX,EAX
00406090 F9 STC
00406091 90 NOP
00406092 8D045D 34120000 LEA EAX,DWORD PTR DS:[EBX*2+1234]
...
...
When you came to this location, tE has already decrypted lot of code that it need for it's work. One part of that code is below this point. Scroll down and place bp on this place:
00406161 2C 04 SUB AL,4
00406163 8885 99000000 MOV BYTE PTR SS:[EBP+99],AL
00406169 8B95 AF1B0000 MOV EDX,DWORD PTR SS:[EBP+1BAF]
0040616F 81E2 0000FFFF AND EDX,FFFF0000
00406175 8BC4 MOV EAX,ESP
00406177 33E4 XOR ESP,ESP
00406179 8BE0 MOV ESP,EAX
0040617B 66:813A 4D5A CMP WORD PTR DS:[EDX],5A4D
00406180 74 08 JE SHORT Packed_I.0040618A
...
...
Now press Shift+F9 untill you break on your bp and then remove bp. In this procedure below, tE will search some API's names in kernel32.dll. Those API's tE needs for unpacking/protecting file. Search goes like this: tE will take name of every single API in kernel32.dll in alphabetic order, starting from the first one "ActivateActCtx" to the last, and compare it's name to the names that it has in it's own list. If some API name coresponds to one from list, it will take that API address and place it to some place for further usage. This is not important for our OEP finding, but it's interesting to see what API's tE uses. Let's see how it does. Trace with F7 untill you reach this line:
004061E4 8138 4C6F6164 CMP DWORD PTR DS:[EAX],64616F4C
Now follow in dump that address DS:[EAX] and youll see that it points to some location in kernel32.dll where API's names starts:
77E85F3F 41 63 74 69 76 61 74 65 41 63 74 43 74 78 00 41 ActivateActCtx.A
77E85F4F 64 64 41 74 6F 6D 41 00 41 64 64 41 74 6F 6D 57 ddAtomA.AddAtomW
77E85F5F 00 41 64 64 43 6F 6E 73 6F 6C 65 41 6C 69 61 73 .AddConsoleAlias
...
...
Now take look what tE is comparing here:
004061E4 8138 4C6F6164 CMP DWORD PTR DS:[EAX],64616F4C
004061EA 75 3A JNZ SHORT Packed_I.00406226
004061EC 8178 04 4C696272 CMP DWORD PTR DS:[EAX+4],7262694C
004061F3 75 31 JNZ SHORT Packed_I.00406226
004061F5 8178 08 61727941 CMP DWORD PTR DS:[EAX+8],41797261
004061FC 75 28 JNZ SHORT Packed_I.00406226
It will check does first 12 bytes/characters of API name corespnds to 64616F4C7262694C41797261 bytes. If you translate those bytes from ACII to characters, you will se that they mean "LoadLibraryA". First API that tE search is LoadLibrary. If you remember, I sad that tE has list of those API's, but as you can see, that list isn't in the form of of some strings written someware. List is made like bunch of comparations. Scroll down and you'll see that there is 12 more this similar comparations. That means that tE will use 13 API functions. Check next check:
00406226 8138 45786974 CMP DWORD PTR DS:[EAX],74697845
0040622C 75 19 JNZ SHORT Packed_I.00406247
0040622E 8178 04 50726F63 CMP DWORD PTR DS:[EAX+4],636F7250
00406235 75 10 JNZ SHORT Packed_I.00406247
00406237 8178 08 65737300 CMP DWORD PTR DS:[EAX+8],737365
0040623E 75 07 JNZ SHORT Packed_I.00406247
00406240 68 C7030000 PUSH 3C7
00406245 ^EB BC JMP SHORT Packed_I.00406203
Rest of 12 this checks all ends with jump to 406203 location. That mean, if API name is found, it will jump to this location so put bp on 406203 and press F9. After you break on bp, trace to this line:
00406218 891C28 MOV DWORD PTR DS:[EAX+EBP],EBX ; kernel32.CloseHandle
Here, tE will store address of founded API to some location. Remove last bp and place new on to this line and press F9. You will see what is next API that tE wants. Do this 13 times and you'll see name of all API's:
kernel32.CloseHandle
kernel32.CreateFileA
kernel32.CreateMutexA
kernel32.ExitProcess
kernel32.GetCurrentProcessId
kernel32.GetModuleFileNameA
kernel32.GetProcAddress
kernel32.LoadLibraryA
kernel32.OpenProcess
kernel32.ReadFile
kernel32.VirtualAlloc
kernel32.VirtualFree
kernel32.VirtualProtectEx
3.2. Decryption layers
That are some interesting API's. But let's go on. Place bp at
00406437 F8 CLC
and press F9, remove bp and trace further with F7. You will stuck on exception at address 004066A8, but nevermind that. Take a look below at:
004066DB 49 DEC ECX
004066DC ^7F ED JG SHORT Packed_I.004066CB
004066DE 3A82 C69E00CE CMP AL,BYTE PTR DS:[EDX+CE009EC6]
This is one decrypting loop that will decript code below that JG SHORT opcode. Place bp on JG SHORT instruction and press F9 untill you break on it. Leave bp and keep pressing F9 untill instruction below JG doesn't change to:
004066DB 49 DEC ECX
004066DC ^7F ED JG SHORT Packed_I.004066CB
004066DE 8D7415 00 LEA ESI,DWORD PTR SS:[EBP+EDX]
Now remove bp and place new bp on 4066DE and press F9. When you break on bp, code below JG is now decrypted. If you are corious how decryption goes, you could just pass it couple times with F7 to better see what it does. It's not much interesting. After this decryption is done, you'll notice another similar below at:
004066FC 49 DEC ECX
004066FD ^7F F0 JG SHORT Packed_I.004066EF
004066FF F9 STC
Do the same as first time. Then, again you will notice couple more loops that decrypts each other and you just do the same. These loops are:
0040671A 86D8 XCHG AL,BL
0040671C ^E2 F2 LOOPD SHORT Packed_I.00406710
0040671E A8 0C TEST AL,0C
0040673B 49 DEC ECX
0040673C ^7F F0 JG SHORT Packed_I.0040672E
0040673E 40 INC EAX
00406765 49 DEC ECX
00406766 ^7F E9 JG SHORT Packed_I.00406751
00406768 B0 58 MOV AL,58
00406788 49 DEC ECX
00406789 ^7F ED JG SHORT Packed_I.00406778
0040678B 05 B560A3C5 ADD EAX,C5A360B5
004067AB 49 DEC ECX
004067AC ^7F EB JG SHORT Packed_I.00406799
004067AE 8E4E 9D MOV CS,WORD PTR DS:[ESI-63]
00406880 49 DEC ECX
00406881 ^7F E0 JG SHORT Packed_I.00406863
00406883 F8 CLC
After you pass these decryption layers, scrol down and you will notice some calls.
3.3. First CRC check
These calls are:
0040694E FF95 EF030000 CALL DWORD PTR SS:[EBP+3EF]
...
00406996 FF95 EB030000 CALL DWORD PTR SS:[EBP+3EB]
...
004069A3 FF95 E7030000 CALL DWORD PTR SS:[EBP+3E7]
004069A9 FF95 E3030000 CALL DWORD PTR SS:[EBP+3E3]
Place bp on each CALL and press F9. You will stop at first call that calls kernel32.GetModuleFileNameA. From API reference: "The GetModuleFileName function retrieves the full path and filename for the executable file containing the specified module." tElock is searching for it's packed file. Press F9 and you'll stop on next bp where kernel32.CreateFileA is called. tE is opening packed file. Next, F9 again and you stop on next bp where kernel32.ReadFile is called - tE is reading our exe file. And next is kernel32.CloseHandle. This is begining of first CRC check. Next you have lots of some code which is not so interesting so scroll down to:
00406C4B 8B85 80D24000 MOV EAX,DWORD PTR SS:[EBP+40D280]
00406C51 50 PUSH EAX
00406C52 35 BB2C7E8E XOR EAX,8E7E2CBB
00406C57 2D 8F6EEDAC SUB EAX,ACED6E8F
00406C5C 5B POP EBX
00406C5D 6A 01 PUSH 1
00406C5F 58 POP EAX
00406C60 6A 08 PUSH 8
00406C62 59 POP ECX
00406C63 0F85 12060000 JNZ Packed_I.0040727B
00406C69 74 19 JE SHORT Packed_I.00406C84
00406C6B 78 50 JS SHORT Packed_I.00406CBD
This is end of first CRC check. Place bp on first above line and press Shift+F9 untill you stop on bp. Remove it. [ebp+40d280] holds calculated checksum that will be XOR-ed and substracted. Finally EAX must be equal to 0 so that JNZ wouldn't be executed. That JNZ at 406C63 leads to CRC error message and exits file.
3.4. Second CRC - checking memory
In a command bar or command line place bp on VirtualAlloc+1. Press Shift+F9 untill you break in kernel, than remove bp and return to user code. We placed bp on second opcode in VirtualAlloc API because tE checks is there bp on that API. We are now in second CRC procedure. This procedure is checking does file is modified in memory so it can find our breakpoints. Scroll little up and you'll see:
0040704D 61 POPAD
0040704E 0F85 27020000 JNZ Packed_I.0040727B
00407054 50 PUSH EAX
00407055 51 PUSH ECX
00407056 E8 1A000000 CALL Packed_I.00407075
0040705B 83F8 FF CMP EAX,-1
0040705E 0F84 17020000 JE Packed_I.0040727B
00407064 83C7 0C ADD EDI,0C
00407067 4E DEC ESI
00407068 7E 06 JLE SHORT Packed_I.00407070
0040706A FFA5 48C34000 JMP DWORD PTR SS:[EBP+40C348]
00407070 E9 02010000 JMP Packed_I.00407177
00407075 60 PUSHAD
00407076 8B5C24 24 MOV EBX,DWORD PTR SS:[ESP+24]
0040707A 83C3 10 ADD EBX,10
0040707D 6A 04 PUSH 4
0040707F 68 00100000 PUSH 1000
00407084 53 PUSH EBX
00407085 6A 00 PUSH 0
00407087 FF95 ECBA4000 CALL DWORD PTR SS:[EBP+40BAEC]
0040708D 85C0 TEST EAX,EAX
0040708F 0F84 D6000000 JE Packed_I.0040716B
That first jump abowe (below POPAD) is end of this CRC check and it leads to error message. Place bp on it and press Shift+F9 to break on it. This CRC check is now passed.
3.5 To the OEP
Next, tE will allocated some blocks of memory for redirecting imports, but about that latter. Press Shift+F9 three times untill you break here:
00407824 ^73 DC JNB SHORT Packed_I.00407802
00407826 CD 20 INT 20
00407828 64:67:8F06 0000 POP DWORD PTR FS:[0]
0040782E 58 POP EAX
0040782F 61 POPAD
00407830 EB 02 JMP SHORT Packed_I.00407834
Place bp on POPAD, Shift+F9, remove bp. This was the last exception. Now, you could trace with F7 for hours and finaly you would get to last RETN at 407902, so you just press Ctrl+F9. That return will throw you to:
0040773F 8B9D 82D34000 MOV EBX,DWORD PTR SS:[EBP+40D382]
00407745 33F6 XOR ESI,ESI
00407747 F7D3 NOT EBX
00407749 0BF3 OR ESI,EBX
...
...
004077A7 66:AB STOS WORD PTR ES:[EDI]
004077A9 EB 02 JMP SHORT Packed_I.004077AD
004077AB CD 20 INT 20
004077AD 61 POPAD
004077AE FF6424 D0 JMP DWORD PTR SS:[ESP-30]
This last jump after POPAD will throw you to the OEP at 401000.
4. QUICK FINDING OEP
So we finaly found OEP. But we spend quite some time on that , are we ;) Isn't that little slow? Don't wory, you shouldn't alway search OEP by this way. Now when you know something more about tE you can use fast way to reach OEP:
Press Shift+F9 20 times and you'll stop at last exception, place bp below on POPAD, again Shift+F9 and remove bp. Press Ctrl+F9 to run to RETN opcode, F8 one time, find jump below popad that leads to OEP. And that's it ;)
5. RESTORING REDIRECTED IMPORTS
Now when we had reach the OEP, we need to restore imports. tElock has allocated (reserved) couple memory blocks in which he splitted and redirected imports. The number of allocated blocks is usually 4~5. When you're at OEP of our target, scroll down to this address:
00402D18 $-FF25 14304000 JMP DWORD PTR DS:[403014]
00402D1E $-FF25 04304000 JMP DWORD PTR DS:[403004]
...
...
00402EC2 $-FF25 20304000 JMP DWORD PTR DS:[403020]
00402EC8 $-FF25 1C304000 JMP DWORD PTR DS:[40301C]
These jumps where originaly jumps to imports. Those [xxxxxxxx] dwords where holding addresses of API's functions, but tE has placed in their place values that leads to allocated blocks of memory. From there, API functions will be called, but before that they are little messed to prevent tools and dumper engines like ImpREC or ProcDump. This is biggest problem when unpacking tElock and this is (or it should be) core of this tutorial. If you dump file and try to use ImpREC, you will fail to repair IAT and fille will crush. We will fix that by this way:
5.1. First redirection
Left click on first jump:
00402D18 $-FF25 14304000 JMP DWORD PTR DS:[403014]
You'll see below that DS:[00403014]=009B0052. In your computer that value will probably be different. Right click on CPU window, select "Go to" and "Expression" and enter 009B0052 in the field. Click OK and you'll land here:
009B0052 EB 01 JMP SHORT 009B0055 ; This is only junk.
009B0054 66:90 NOP
009B0056 2BC5 SUB EAX,EBP
009B0058 B8 09019B00 MOV EAX,9B0109 ; This is only important to us, EAX=9b0109,
009B005D 40 INC EAX ; Add 1 to EAX,
009B005E FF30 PUSH DWORD PTR DS:[EAX] ; Push that address,
009B0060 C3 RETN ; Jump to that address.
This small pice of code above is our cloacked import jump. EAX=9B0109+1=9B010A. Follow that address in dump and you will found that it points to some API address:
009B010A D7 3C 00 7F
Select that four bytes and Binary copy them. Then return to JMP at 402D18 and follow it's walue in dump window:
00403014 52 00 9B 00
Select those four bytes and Binary paste. You have just restored one import to it's original place. Go again to 9B010A in dump and scroll little up. You'll see five more imports:
009B00EA 00 00 00 00 00 00 00 00 00 00 00 00 D7 1C 00 7F
009B00FA E4 20 00 7F CC 39 00 7F 00 1B 00 7F 53 1E 00 7F
Now Binary copy those values and return to that one import that you have restored. Scroll up and Binary paste five new import values to five places above first restored. You have just restored whole one block of redirected imports. This block is small and contains only six imports.
5.2. Second redirection
Now select next unresolved jump:
00402D4E $-FF25 98304000 JMP DWORD PTR DS:[403098]
Do the same as first time and follow this DS:[00403098]=009E00A3 in CPU window to:
009E00A3 F8 CLC
009E00A4 73 02 JNB SHORT 009E00A8
009E00A6 0F21 ??? ; Unknown command
009E00A8 48 DEC EAX
009E00A9 B8 33079E00 MOV EAX,9E0733
009E00AE 40 INC EAX
009E00AF FF30 PUSH DWORD PTR DS:[EAX]
009E00B1 C3 RETN
As you can see, this is very similar as in ou first example. Follow EAX=9E0733+1=9E0734 in dump. There you can see lot of imports:
009E0734 69 5F D4 77 5E 9C D4 77 CB 80 D4 77 88 32 D7 77 i_Ôw^œÔwË€Ôwˆ2×w
009E0744 38 53 D4 77 52 65 D6 77 1D 5F D4 77 0F 6E D5 77 8SÔwReÖw_ÔwnÕw
009E0754 9B 79 D4 77 4E 7A D4 77 E6 7E D4 77 0E 5F D4 77 ›yÔwNzÔwæ~Ôw_Ôw
009E0764 0F 79 D4 77 60 2B D9 77 CC 65 D4 77 E2 3D D4 77 yÔw`+ÙwÌeÔwâ=Ôw
009E0774 84 5B D4 77 E4 D9 D4 77 2C 50 D5 77 BF 57 D6 77 „[ÔwäÙÔw,PÕw¿WÖw
009E0784 69 43 D4 77 E0 56 D6 77 80 A1 D4 77 50 5C D4 77 iCÔwàVÖw€¡ÔwPÔw
009E0794 21 8B D4 77 26 5B D6 77 76 8D D4 77 E4 9F D6 77 !‹Ôw&[ÖwvÔwäŸÖw
009E07A4 46 C2 D4 77 E4 B9 D4 77 8D BD D4 77 42 B7 D4 77 FÂÔwä¹Ôw½ÔwB·Ôw
009E07B4 7C 9C D4 77 64 B0 D6 77 |œÔwd°Öw
Binary copy them, return to unresolved jump JMP DWORD PTR DS:[403098] and follow it in dump and paste copyed bytes. Return again to place fom where you have taken bytes, scroll up and you'll see couple more imports:
009E0704 77 43 D4 77 50 D1 D5 77 wCÔwPÑÕw
009E0714 FE 65 D6 77 C9 48 D5 77 00 53 D4 77 0A 59 D6 77 þeÖwÉHÕw.SÔw.YÖw
009E0724 31 6E D5 77 FF 5E D4 77 29 53 D4 77 1F 97 D4 77 1nÕwÿ^Ôw)SÔw—Ôw
Copy them and paste them above those first fixed, just like in the first example. Just watch that you properly count lines so that you don't mess values because you will get wrong imports. With this you have fixed second redirected imports.
5.3. Last one
Follow next jump in CPU:
00402E56 $-FF25 60304000 JMP DWORD PTR DS:[403060]
You're here:
00A00113 EB 02 JMP SHORT 00A00117
00A00115 FF20 JMP DWORD PTR DS:[EAX]
00A00117 1D DFF2B7DE SBB EAX,DEB7F2DF
00A0011C B8 7703A000 MOV EAX,0A00377
00A00121 40 INC EAX
00A00122 FF30 PUSH DWORD PTR DS:[EAX]
00A00124 C3 RETN
Again, this is mangled jump to some API. Follow EAX=00A00377+1=00A00378 in dump and you'll find couple imports:
00A00378 43 A6 E7 77 AE 98 F7 77 91 4B E7 77 C¦çw®˜÷w‘Kçw
Do again like ine previous examples, binary copy and paste that values, scroll upp and copy rest of values:
00A00328 0C 61 E7 77 .açw
00A00338 35 4E E7 77 8C 5F E7 77 8D F0 E7 77 A1 AA E7 77 5NçwŒ_çwðçw¡ªçw
00A00348 C4 6F ED 77 5F 8C F5 77 6B 15 F5 77 A1 16 F5 77 Äoíw_Œõwkõw¡õw
00A00358 D2 B6 E7 77 C2 28 E7 77 D9 AC E7 77 FC AC E7 77 Ò¶çwÂ(çwÙ¬çwü¬çw
00A00368 85 94 E7 77 DF E5 E7 77 63 98 E7 77 C9 B3 E7 77 …”çwßåçwc˜çwɳçw
and paste them above first ones. You have just restored last redirected import block. Now, all the imports should be on their original places.
Now dump file to hard drive with LordPE.
Open ImpREC, enter OEP, click IAT auto search and get imports. You will see that it found one thunk, but it 3 invalid pointers. Cut that three invalid and fix dumped file. Open dumped file and olay a little with it and you'll see that it works fine.
6. ANTI-DEBUGGER CHECK
tE has old SoftICE check that is no comatibile with XP and that is the reason why I didn't descover it in the first way. If you have Windows 98 or some other that support this check, you will probably have problems following this tutorial because one window will popup teling you that file wont run with active debugger. Now we gona find that check. Restart target and press Shift+F9 about 13 times untill you break here:
00406B67 F7F3 DIV EBX
...
...
00406B93 C3 RETN
00406B94 66:B8 0043 MOV AX,4300
00406B98 EB 02 JMP SHORT Packed_I.00406B9C
00406B9A CD 20 INT 20
00406B9C 81B5 591C0000 AD>XOR DWORD PTR SS:[EBP+1C59],0B4806AD
00406BA6 CD 68 INT 68
00406BA8 66:05 7B0C ADD AX,0C7B
00406BAC 66:48 DEC AX
00406BAE 74 55 JE SHORT Packed_I.00406C05
This last jump must not be executed because it leads to error message. This check will not work on XP machines.
-
Level : newbie
This tutorial contains the following conversions:
DECIMAL BINARY HEX
I separate it in 6 parts:
1. DECIMAL -> BINARY
2. DECIMAL -> HEX
3. HEX -> BINARY
4. BINARY -> HEX
5. BINARY -> DECIMAL
6. HEX -> DECIMAL
Doing them by paper, ofcourse.
First you got to learn this table by heart:
0000 | 0 | 0
0001 | 1 | 1
0010 | 2 | 2
0011 | 3 | 3
0100 | 4 | 4
0101 | 5 | 5
0110 | 6 | 6
0111 | 7 | 7
1000 | 8 | 8
1001 | 9 | 9
1010 | A | 10
1011 | B | 11
1100 | C | 12
1101 | D | 13
1110 | E | 14
1111 | F | 15
Now. Let's get started!
1. ***** DECIMAL -> BINARY *****
Take number 3295 as an example.
3295 | 2 = 1647.5 = remainder 1
1647 | 2 = 823.5 = remainder 1
823 | 2 = 411.5 = remainder 1
411 | 2 = 205.5 = remainder 1
205 | 2 = 102.5 = remainder 1
102 | 2 = 51 = remainder 0
51 | 2 = 25.5 = remainder 1
25 | 2 = 12.5 = remainder 1
12 | 2 = 6 = remainder 0
6 | 2 = 3 = remainder 0
3 | 2 = 1.5 = remainder 1
1 | 2 = 0.5 = remainder 1
Listing the remainders DOWN to UP:
3295d == 110011011111b
I first divide the number by 2 and check for remainder, then divide the results
(without remainder) by 2 again until you reach zero.. and so on.
2. ***** DECIMAL -> HEX *****
Now converting the decimal 3295 to HEX. As we already found it's binary it is easy
to convert to hex if we know the upper table by heart. Here we go!
110011011111b -> 1100 1101 1111 -> C D F
From here we have:
3295d == 110011011111b == 0CDFh
3. ***** HEX -> BINARY *****
Take number DEAD as an example
D = 1101
E = 1110
A = 1010
D = 1101
DEADh = 1101 1110 1010 1101b
Using the upper table we simply convert them.
4. ***** BINARY -> HEX *****
Take number 1010 1101 1110 1111b as an example
From the upper table:
0ADEFh
5. ***** BINARY -> DECIMAL *****
Take number 1101b as an example
1*(2^3) + 1*(2^2) + 0*(2^1) + 1*(2^0) == 13
First number 1
1 * (2^3)
Second number 1
1 * (2^2)
Third number 0
0 * (2^1)
Fourth number 1
1*(2^0)
Equals to 13.
6. ***** HEX -> DECIMAL *****
To convert hex to decimal, we first need to convert hex to binary and then
use the binary to decimal technique. Pretty easy huh :) Let's get going
Take number FEEDh as an example. This number converted to binary
1111 1110 1110 1101b
We will use the previous technique to convert binary to decimal.
This is a bigger number so WATCH what you're doing
1*(2^0)
0*(2^1)
1*(2^2)
1*(2^3)
0*(2^4)
1*(2^5)
1*(2^6)
1*(2^7)
0*(2^8)
1*(2^9)
1*(2^10)
1*(2^11)
1*(2^12)
1*(2^13)
1*(2^14)
1*(2^15)
==
1*(2^0)+0*(2^1)+1*(2^2)+1*(2^3)+0*(2^4)+1*(2^5)+1* (2^6)+1*(2^7)+
+0*(2^8)+1*(2^9)+1*(2^10)+1*(2^11)+1*(2^12)+1*(2^1 3)+1*(2^14)+1*(2^15)=
= 65261d //
FEEDh = 1111111011101101b = 65261d
That's all for now. I feel I should credit kw some, he taught me some parts
of these techniques.