-
============================
[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.
-
Level : newbie
Writing a basic keygen in MASM for Phlux keygenme 1.
Tools used: MASM32 and OllyDBG for debugging if necessary.
Good day to you all who are reading this first, we'll discuss the basic opcodes such as MOV, ADD or other opcodes that are used in the algo I'll explain.
After my last tutorial about the 'Can you CrackMe', I said I'd rate myself 0/10 in keygens, heh, well that's not 0/10 anymore otherwise I couldn't write this tutorial. But because I'm not an expert too, if somebody has ideas to add something or found some serious mistakes contact me.
Okay, I'm hoping you read this thorougly so you can learn something :-)
We'll not go into further details about the full ASM source, or are we? Okay for the ones who really want that too, I'll include a total source at the bottom of this tutorial.
Look at the following algorithm:
--------------------------------------------------------------------------------
generate proc ; indicates a procedure is started
PUSH EBX ; saves the EBX value to the stack because we are going to use EBX, and EBX is a register that needs to be saved (often).
PUSH OFFSET szUser
CALL lstrlen
MOV EDX, EAX
XOR ECX, ECX
XOR EBX, EBX
@@:
movzx eax, byte ptr [szUser+ECX]
xor eax, 31337h
add eax, 0DEADBEEFh
imul eax, 666h
sub eax, 1BADBAB3h
shl eax, 3h
xor eax, 0D34DD00Dh
add ebx, eax
INC ECX
CMP ECX,EDX
JNZ @B
invoke wsprintf, ADDR BUFFER, ADDR PREFIX, ebx ; look at API of wsprintf some lines below
; basically, it turns the hex value into an
; ASCII value, because we use the %X prefix.
;int wsprintf( ; this is the API wsprintf as you might understand :-)
;
; LPTSTR lpOut, ; LPCTSTR lpFmt, ; ... ; other optional arguments ; ); POP EBX
ret
generate endp
--------------------------------------------------------------------------------
I've commented the ASM opcodes of the algorithm, look at them :-). First of all, I also want to say this, EAX and EDX are used for dividing, EAX is the quotient result and EDX is the remainder (you learnt this on school I hope, except for the registers :P). Next, EBX, EBP, ESI, EDI are registers that often need to be saved. If something is in it before you start using them, always use PUSH, and after you are finished use POP to retrieve it again from the stack, but beware: if you use another POP instruction in the thing you are busy on, then that POP gets the value of the PUSH, so you could also POP your PUSHED (the saved register) first and then later on PUSH and POP it again. This can be confusing, therefore I'll comment a little example below:
Look at this piece of code with PUSHES and POPS.
--------------------------------------------------------------------------------
generate proc ; indicates a procedure is started PUSH EBX ; saves the EBX value to the stack
PUSH ESI ; saves the EDI value to the stack
; some code now
XOR ESI,ESI ; clears it, we use the last ESI (XOR destination,source) as 0,
; because that takes less bytes, it's just a fact that it's used
; as 0, nothing more, nothing less
; XOR sets the output bit if the source bit is different from the
; destination bit. So that's why it becomes 0.
XOR EBX,EBX ; clears EBX too, so we can start using it :)
ADD ESI,539h ; this adds the decimal value 1337 to ESI, 1337d is 539h, 539h = 539 HEX :-)
MOV EBX,9865h ; this adds the decimal value 2689 to EBX, it's 9865h and 2689d. You can use
; windows calculator for this, use the SCIENTIFIC view :)
POP EBX ; gets the last value in the stack and saves it to EBX, see?
; that's a mistake that could be taken
; therefore, ignore this opcode
POP ESI ; gets the last value (that was ESI, therefore this is right) and saves it into ESI
POP EBX ; gets the value after the last POP, so that's EBX, see? first in / first out system
ret
generate endp
--------------------------------------------------------------------------------
Okay, so this are some basics :-), MOV puts a value somewhere, ADD adds a value, XOR is explained in the comments above. I strongly recommend that you get 'win32asmtutorial.zip'. It is a HLP file in HTML style, you could also print it to read them somewhere in ease :-), I did put it on my MP3 player because I can view text-files on it :-) saves me some ink.
So, this commands like MOV (is put), DIV (is /, divide), MUL (is *, multiply), there do exist other opcodes of course, that you will often meet in algorithms, therefore I recommend the 'win32asmtutorial.zip' to find out what these things do exactly, if you still don't understand them after reading the Win32 ASM Tutorial, contact me or some experienced cracker at BiW Forum. If you want 'win32asmtutorial.zip' and you REALLY can't find it (would surprise me) you may contact me.
Now I'll include the full ASM, and I'll comment some things too, because that'll make things easier to understand maybe, I'll only comment things that have to do with displaying the serial or getting the username and the algo, I assume you know the other stuff, if not, then please have a break and read the Win32 ASM Tutorial (the 'win32asmtutorial.zip' of course). Or you could just continue and read that tutorial later :-)
Source for the Key Generator (keygen.asm for example):
--------------------------------------------------------------------------------
.686
.mmx
.model flat, stdcall
option casemap:none
include masm32includewindows.inc
include masm32includeuser32.inc
include masm32includekernel32.inc
includelib masm32libuser32.lib
includelib masm32libkernel32.lib
assume fs:flat
WndProc PROTO :DWORD,:DWORD,:DWORD,:DWORD
.data hEdit1 dd 0
hEdit2 dd 0
hInstance dd 0
hIcon dd 0
dlgname db "keygen",0
Format db "%0.8X%0.8X",0
Caption db "About",0
MsgAbout db "Key Generator by TDC",13,10,10
db "..: Cracked by : nick? :.. ",13,10
db "..: Also known as : ur alias? :.. ",13,10
db "..: Protection : Custom :.. ",13,10
db "..: Contact info : mail? :.. ",13,10
db "..: Release date : date? :.. ",13,10
db "..: Date format : dd-mm-yyyy :.. ",13,10
db " ",13,10
db "Greetings to : BiW Reversing members",13,10
db "Special thanks to : usernames here",13,10,0
UserName db "username displayed when start", 0
UserKey db "serial displayed when start", 0
szMsgMinLetter db "Please enter more than 3 characters...",0
BUFFER db 32 dup(0)
PREFIX db "%X", 0 ; important, sets the PREFIX for later use in wsprintf API, %X indicates we are going to use wsprintf for conversion of HEX to ASCII
.data?
szUser db 90 dup (?)
szSerial db 90 dup (?)
.code
start:
invoke GetModuleHandle, NULL
mov hInstance, eax
invoke DialogBoxParam,hInstance,ADDR dlgname,NULL,ADDR WndProc,NULL
invoke ExitProcess,eax
WndProc proc hWin :DWORD, uMsg :DWORD, wParam :DWORD, lParam :DWORD
.if uMsg == WM_INITDIALOG
invoke LoadIcon,hInstance,100
invoke SendMessage,hWin,WM_SETICON,0,eax invoke GetDlgItem,hWin,107
mov hEdit1, eax
invoke GetDlgItem,hWin,108
mov hEdit2, eax
invoke SetDlgItemText,hWin,107,offset UserName
invoke SetDlgItemText,hWin,108,offset UserKey
.elseif uMsg == WM_COMMAND
.if wParam == 109 ; if Generate button is pressed, remember parameter 109 was set in rsrc.rc
invoke GetDlgItemText,hWin,107,offset szUser,90
cmp eax,3 ; checks if username = 3 characters, you can change this to whatever you want the user to require at least
jl error ; if less ( JL = Jump less ) then goto error and display please enter.... blabla..
call generate ; call the actual generating process with the algo
invoke SetWindowText,hEdit2, offset BUFFER ; sets the new serial generated in the hEdit2 box, (the second box)
jmp exit
error: invoke SetDlgItemText,hWin,108, offset szMsgMinLetter ; puts the szMsgMinLetter value in the read-only box, check the box in rsrc.rc (the one with 108 as parameter)
jmp exit
.elseif wParam == 110
invoke MessageBox, hWin,addr MsgAbout, addr Caption,MB_ICONINFORMATION or MB_OK
.elseif wParam == 111
invoke EndDialog,hWin,NULL
.endif
.elseif uMsg == WM_CLOSE
invoke EndDialog,hWin,NULL
.endif exit: xor eax, eax
ret
WndProc endp
generate proc PUSH EBX ; save EBX
PUSH OFFSET szUser ; the push for the call :-)
CALL lstrlen ; checks username length (often used), then it's stored in EAX
MOV EDX, EAX ; in this example we move the EAX value to EDX, EAX was the length.
XOR ECX, ECX ; clear ECX
XOR EBX, EBX ; clear EBX
@@: ; indicates a point in the program, to jump to :-)
movzx eax, byte ptr [szUser+ECX] ; gets next character from szUser
xor eax, 31337h ; XORs that with 31337h
add eax, 0DEADBEEFh ; ADDs DEADBEEFh (we included a 0 before DEADBEEF, that's always necessary when a value begins with a A, B, C, D, E or F, when beginning with a number it's not necessary to put a 0 in front of it.
imul eax, 666h ; we use IMUL because our eax is a signed byte, (more about signed and unsigned bytes in the Win32 ASM Tutorial that I'm pointing much to in this tutorial)
sub eax, 1BADBAB3h ; substracts eax with BADBAB3h
shl eax, 3 ; SHLs the bytes 3, shifts it left by 3
xor eax, 0D34DD00Dh ; XORs that with D34DD00Dh
add ebx, eax ; adds EAX to EBX, ebx is the value that will be the total serial at the end of this loop
INC ECX ; increases the value to pick the next character from szUser in the next jump to @@
CMP ECX,EDX ; checks if we are at the end of the username, if so the jump next is ignored
JNZ @B ; means jump one back, so thats jumping to @@, JMP @F means jump forward
invoke wsprintf, ADDR BUFFER, ADDR PREFIX, ebx ; convert it to an ASCII string for displaying, the PREFIX is set above under the '.data' section.
POP EBX ; get original EBX value back
ret ; return from call (remember the call generate when Generate is pressed?)
generate endp
end start
--------------------------------------------------------------------------------
END OF SOURCE
Source for the resource file (rsrc.rc):
--------------------------------------------------------------------------------
#include "masm32includeresource.h" keygen DIALOGEX 0, 0, 196, 93
STYLE DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU
CAPTION "Brought to you by ?nick-in-rsrc.rc?, 2005"
FONT 8, "Tahoma" BEGIN
CTEXT "Key Generator by ?nick-in-rsrc.rc?",101,6,7,184,11,SS_CENTERIMAGE ,WS_EX_STATICEDGE
CONTROL "",103,"Static",SS_BLACKFRAME | SS_SUNKEN,6,22,184,2
GROUPBOX "",104,6,24,184,45
LTEXT "Name:",105,11,35,22,8
LTEXT "Serial:",106,11,51,21,8
PUSHBUTTON "&Generate",109,140,74,50,14,0,WS_EX_STATICEDG E
PUSHBUTTON "&About",110,86,74,50,14,0,WS_EX_STATICEDGE
PUSHBUTTON "&Close",111,6,74,50,14,0,WS_EX_STATICEDGE
EDITTEXT 107,36,33,147,13,ES_AUTOHSCROLL
EDITTEXT 108,36,49,147,13,ES_AUTOHSCROLL | ES_READONLY
END
--------------------------------------------------------------------------------
END OF SOURCE
Hopefully, you have learned something of the comments, it's not that hard to understand, the parameter for the Generate button is set in the 'rsrc.rc' file, and it's set to 109 in this case. I've done as much as I could to help you understanding it all better, and again I strongly recommend you to take a look at the 'win32asmtutorial.zip', because it gives much information about calculation opcodes and about how memory works in 32-bits.
If you want to compile this all (of course you want :P), then also make a little batch program that automatically makes an .EXE file, it's not hard to do. Heres the MS-DOS batch program I made for the job:
Batch program (let's call it make.bat):
--------------------------------------------------------------------------------
@echo off
REM change the keygen.* to whatever you want to name your keygen.asm, .obj and .exe files.
if exist keygen.exe del keygen.exe
if exist keygen.obj del keygen.obj
if exist rsrc.res del rsrc.res
if exist rsrc.obj del rsrc.obj
masm32binml /c /coff keygen.asm
masm32binrc rsrc.rc
masm32bincvtres /machine:ix86 rsrc.res
masm32binLink /SUBSYSTEM:WINDOWS keygen.obj rsrc.obj
if exist keygen.obj del keygen.obj
if exist rsrc.obj del rsrc.obj
if exist rsrc.res del rsrc.res
-
Level : beginner
How to halt the timer in the solitaire cardgame.
Dedication: My best friend Error_Vir
Food: Ruffles and nectarine juice
Music: Falcon - Cosmic outflow.xm
This tutorial I dedicate especially to my old friend Error_Vir.
And as one of the famous quote follows:
"one time of time on the time at of the time i dont remember it"
For more information on my friend you can find on the following webpage
here; that is dedicated to him.
Enough blabla's. Proceed to tutorial.
Ok. The tool that we will to use is OllyDbg. (Ollydbg)
Alright, sol.exe can be found (obviously, if you didn't need to free
some space up on your HDD by deleting it) in %windir%system32sol.exe.
(on my computer it's C:WINNTsystem32sol.exe)
So let's debug this tiny little app.
Oh, if you're asking why I chose sol.exe I simply find it the best
game for the boring days. Try to combine it with some demoscene music
(scenemusic, ojuice).
Fits perfectly for boring and raining days :-)
BY THE WAY, I won a game in 51 seconds (without any time hacks).
That's my highest score, I also had scores like 71 seconds, 78, 82, ...
Open ollydbg, File->Open and type "%windir%system32sol.exe" there.
Press F9 to run Solitaire.
***TIMER ACTIVATION***
As you might have(n't) noticed, the timer starts when you click anywhere
on the game. Example click on a card. Cool, the timer started!
***TIMER ACTIVATION***
To restart the timer, goto ollydbg Debug->Restart->Yes->Press F9.
We restarted the game.
uh.
Sometimes in cracking,
(as my old friend says,
"in the cracking = idont have time to write all thinges")
I dont have the time to explain all things.
So don't ask me why I picked this way. There are (probably) other ways for
cracking this application, but I prefer the ninja-style.
HOWEVER, back to work. The ninja-style is the following.
We must defeat Solitaire in the easiest way. How do we do that?
Simply. Notice the string "Time: %d" ? :-)
In Ollydbg, View->Memory. Right click anywhere, click Search.
Search for HEX val: 54 00 69 00 6D 00 65 00 3A
(meaning "Time:" with a zero in between every char)
This is very important. Before setting a breakpoint make sure you activate the
timer first, because if you don't activate it Olly will break all the time
and the Timer will be =0 so NO use.
Anyway, we found it. While the text we searched for is highlighted,
right click on it and set a breakpoint on memory access.
Ollydbg IMMEDIATELY breaks, this is a good sign.
Once ollydbg lands us on the physical memory, press ALT+F9 to get back
to the user's memory.
We are here:
0100243B . C2 0800 RETN 8
Now as we step out from this procedure this is what we see:
01005372 |. 8DB445 48FFFFF>LEA ESI,DWORD PTR SS:[EBP+EAX*2-B8]
01005379 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
0100537C |. 8B40 34 MOV EAX,DWORD PTR DS:[EAX+34]
0100537F |. C1F8 02 SAR EAX,2
01005382 |. 50 PUSH EAX ; /Arg2
01005383 |. 56 PUSH ESI ; |Arg1
01005384 |. E8 4ECFFFFF CALL sol.010022D7 ; sol.010022D7
As you can notice (by looking at the code a bit), this procedure sets up the string
like "Text: %d". That means that %d is received in some other procedure (e.g. previous).
You can notice that this works out the text by entering that call at 01005384
and by scrolling down a bit you can find the next API as well:
010053B9 |. FF15 04110001 CALL DWORD PTR DS:[] ; DrawTextW
This procedure starts at 01005349 /$ 55 PUSH EBP.
That means that our previous procedure ends on
01005347 .^EB E5 JMP SHORT sol.0100532E and starts on 010052CF /$ 56 PUSH ESI
You can notice this easily by looking at Olly's arrows.
Good looking procedure is:
010052CF /$ 56 PUSH ESI
010052D0 |. 57 PUSH EDI
.....
010052EF |. 75 3B JNZ SHORT sol.0100532C
010052F1 |. 8B46 34 MOV EAX,DWORD PTR DS:[ESI+34]
010052F4 |. 68 FE7F0000 PUSH 7FFE
010052F9 |. 40 INC EAX
010052FA |. 50 PUSH EAX
010052FB |. E8 CCD0FFFF CALL sol.010023CC
.....
0100533F |. E8 770A0000 CALL sol.01005DBB
01005344 |> 6A 01 PUSH 1
01005346 |. 58 POP EAX
01005347 .^EB E5 JMP SHORT sol.0100532E
Not the whole though, its big enough.
Anyway, about the ninja-style, here we go.
Instead of looking at the code you simply check every opcode and hunt it.
Something like a manual brute-force or so. That's just my way.
So, we set a breakpoint on 010052F1 and press F9 to run. Once we landed there,
in the DUMP Window right click then Goto->Expression and then type ESI+34.
Remove the breakpoint and press F9. Cool, the value in the Dump window changes on every
second, which means that that is our counter :-)
But how is it getting increased? Well, simple enough.
010052F1 |. 8B46 34 MOV EAX,DWORD PTR DS:[ESI+34]
010052F4 |. 68 FE7F0000 PUSH 7FFE
010052F9 |. 40 INC EAX
in EAX, Bill Gates puts the address of the counter and then he increases EAX.
How to stop this? Replace "INC EAX" with "NOP" (40h->90h)
Time hast been stopped! (no, "hast" is not a mistake, its the German way!)
Well, that is all for now my friend, I hope you understood something from this tutorial.
Or maybe not? Who cares anyway, it's already written ;)
-
Level : newbie
Patching Nagscreens in this target here
Gathering Information
---------------------------
Ok as always you should run the target first to see how the program works before diving into the code. We are presented with a nag when the target is run.
Important thing to notice is this is a message box with the text "Oh, do you like this program?" Also, if you press exit you are presented with another message box nag screen stating "Oh, did you forget this one?"
Finding where the nags are called
----------------------------------------
Instead of just scrolling through the code for MessageBoxA calls, we are going to set a breakpoint. Reasoning? A program could have the text encrypted, etc so the mentioned method of scrolling simply won't fly for many programs. MessageBoxA incase you didn't know is located within USER32.dll, so Alt-E to view executable modules. Right click on USER32.dll and choose "View Names". In this new window, locate MessageBoxA, click on it once, and hit F2 (toggle breakpoint). Press F9 to run the program and we end up landing in USER32.dll. If you look a little below you can see the following line:
77D8052A E8 2D000000 CALL USER32.MessageBoxExA
This guy is responsible for the message box, however we are within USER32.dll and want to find how we got here. A nice trick we can do is to look at the call stack (Alt-k) to see how we got here. After pressing Alt-k we see at the top where this was called from and details about this message box including the title, text, etc. It says that MessageBoxA was called from KillNag.00401085. So if we double click that line we will be brought to this location.
Patching the Nags
---------------------
Now that we are in the right location take a look around. If you look above you will notice this line:
00401074 > 8B4C24 04 MOV ECX,DWORD PTR SS:[ESP+4]
The '>' indicates that this location was reached by a jump. If you click/highlight this line, the small pane below will say "Jump from 00401012". You can also view this information by right clicking on the code and selecting "Find references to"->"selected command" which is Ctrl-R. You can reach this mentioned jump location by right clicking on the pane and selecting "Go to JE from..." or double clicking on the line in the window from Ctrl-R. Just to make sure we are all on the same page we should be here:
00401012 74 60 JE SHORT KillNag.00401074
Now, we don't want to jump, otherwise we get that stupid nag. So what we can do is NOP the line of code so that we never have to worry about this. So right click -> "Binary" -> "Fill with Nops". Ok the first nag is taken care of. Now we have to get rid of the message box that is on exit. If you look right below where we are at we can see it:
00401051 . 68 78514000 PUSH KillNag.00405178 ; |Title = "Nag!"
00401056 . 68 58514000 PUSH KillNag.00405158 ; |Text = "Oh, did u forget this one? :P"
0040105B . 56 PUSH ESI ; |hOwner
0040105C . FF15 C8504000 CALL DWORD PTR DS:[>; MessageBoxA
You could have also found this location by just pressing exit and letting Ollydbg break on MessageBoxA again. Anyhoo, if you look above this you will find the line:
0040104A > 56 PUSH ESI
Again, we arrive at this location by a jump. So we have to get rid of the jump to this nag screen. Apply the same methods we used before to reach the line of code with the jump. You should be at this location:
00401026 . 74 22 JE SHORT KillNag.0040104A
So just as we did to the other jump, NOP this line of code. Alright now save your patched program by right-click->"Copy to executable"->"all modifications" then click "copy all" on the messagebox. Then right-click in the new window -> "Backup"->"save data to file". If we run the program it has no first nag, that's good. But click the exit button... well there is no nag but we also don't exit the program!
I showed this because it is likely a mistake that many beginners will make on this program. So let’s reload the program into Olly. If you look right below the 2nd nag in Olly you will notice a "WM_CLOSE". Hmm think this is responsible for closing our application? ;)
If you think for a little bit you will also notice that you can just jump to WM_CLOSE instead of jumping to that stupid nag code. So go back to the jump responsible for the exit nag. Now we want this NOT to jump to 0040104A (nag code), but to 00401093 (WM_CLOSE). So right click-> "assemble" and change 0040104A to 00401093.
Run the program and wallah! It works :)
Final thoughts: A little lengthy, but I put a lot of basic stuff in here to help out those who are new to Ollydbg.
-
Level : newbie
A simple target to keygen, but beware the anti-debugging tricks ;)
Includes explanation of all anti-debugging checks, and sourcecode for keygen in C.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
solution for detten's crackme7 - tutorial by haggar
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Greetings!
Detten has made small and not hard crackme to solve, but still very interesting. Our objective is to avoid any kind of anti-debug staff and to dig-up serial for our name. So let's begin.
Crackme here
1. TOOLS
- OllyDbg 1.10
- paper and pencile, some time and a will to do litlle Googling.
2. ANTI-DEBUG STUFF
2.1 First problem
Ok, first thing that happened to me is that crackme crashed while starting. Is this a bug or intended? Maybe both? That happend on my Windows XP Pro SP1, but I don't know for other systems. So lets open crackme in Olly and see what we have here. Right at the beginning you will see the reason for crushing:
00401000 >/$ B4 43 MOV AH,43
00401002 |. CD 68 INT 68
00401004 |. 66:3D 86F3 CMP AX,0F386
00401008 |. 74 2D JE SHORT crackme7.00401037
So right at the second line we have INT 68 interrupt and that is why crackme crashes. Let we try to analyze what is going on:
- first, to AH register is given 43 value
- then it's executed INT 68
- imagine that crackme doesn't crash at above inerrupt, but continues. Code is checking does AX contains now 0F386 value.
- if AX=0F386, conditional jump at 401008 will be executed and we will jump at 401037 offset where we can see message "Please unload debugger :("
So this is some check for debugger and crackme shouldn't crash but to work properly, or show that message if we run it in Olly. Since I never sow this trick before, I just used Google to find some info about INT 68 interrupt. I didn't find much, just some references that I don't quite understand, but one of listed links was to some Yates tutorial. To not make long story out of this (check yor self that article), this is SoftICE check. Crackme crashes because this trick works only on 9x systems, but in case that we have SI installed on our computer, INT 68 should return 0F386 to EAX, we should get error message and crackme should unload itself. If SI isn't installed, crackme should just procede to GetModuleHandle API and run normally. To solve this issue, just fill with NOP's all opcodes from 401000 to 401009 and save changes to crackme:
00401000 > $ 90 NOP
00401001 . 90 NOP
00401002 . 90 NOP
00401003 . 90 NOP
00401004 . 90 NOP
00401005 . 90 NOP
00401006 . 90 NOP
00401007 . 90 NOP
00401008 . 90 NOP
00401009 . 90 NOP
0040100A . 6A 00 PUSH 0 ; /pModule = NULL
0040100C . E8 A5020000 CALL <JMP.&KERNEL32.GetModuleHandleA> ; GetModuleHandleA
Now run crackme and you'll see that it works fine.
2.2 Junk code
Next thing to notice is that crackme has some small junky opcodes, mostly jumps like this one:
00401016 . EB 02 JMP SHORT crackme7.0040101A
00401018 68 DB 68 ; CHAR 'h'
00401019 A1 DB A1
0040101A > 6A 00 PUSH 0 ; /lParam = NULL
0040101C . 68 50104000 PUSH crackme7.00401050 ; |DlgProc = crackme7.00401050
00401021 . 6A 00 PUSH 0 ; |hOwner = NULL
00401023 . 68 0C304000 PUSH crackme7.0040300C ; |pTemplate = "MyDialog"
00401028 . FF35 C4314000 PUSH DWORD PTR DS:[4031C4] ; |hInst = NULL
0040102E . E8 65020000 CALL <JMP.&USER32.DialogBoxParamA> ; DialogBoxParamA
You see, jump at 401016 jumps above two bytes at 401018 and 401019. Those bytes will NEVER be executed and those jumps+bytes had acctually no purpose to our program. They are there just to confuse reverser and made reading of code a bit dificult. Most today's protectors have such code, only that they have realy harder patterns. What we can do is to patch those junky bytes with NOP's just to make code litlle cleaner, it's not acctually necesery but I like it that way. There is 11 such locations to patch (some jumps+bytes, or just bytes), so do it (if you want) and save changes to crackme.
2.3 Breakpoints directly on API's - check
Next check is common check in today's protectors too. It's checking did we put bp directly on some API function. I don't know how SoftICE is working with breakpoints, I assume that it's identicall as Olly, but I will explain this part on Olly. Also, you must have Windows XP for this kind of breakpoints if using Olly, because only XP is alowing seting bp directly on dll's.
Open "Executable Modules" window in Olly (Alt+E), left click select our crackme, right click on it and "view names". There you have for example this API which our crackme uses to get input from window:
00402014 .rdata Import USER32.GetDlgItemTextA
If you right click on it, you see option "Toggle breakpoint on import". That option will not put bp in our crackme, but in USER32.DLL on address where GetDlgItemTextA API function starts. This is very usefull option so select it. Now run crackme enter name haggar and serial 12345 and click check button. Boom! You get "Please unload debugger :(" message and crackme exits.
Let me explain you what happened, but first set same bp again on same API. Now open again "Executable Modules", but this time click on USER32.DLL and "view names". Find our API:
77D665FE .text Export GetDlgItemTextA
If you placed toggle bp on that import, then 77D665FE address should be marked with red colour now (maybe 77D665FE will be different on your system). Double click on that address and you'll find your self in user32.dll right where GetDlgItemTextA API starts:
77D665FE > 55 PUSH EBP
77D665FF 8BEC MOV EBP,ESP
77D66601 FF75 0C PUSH DWORD PTR SS:[EBP+C]
77D66604 FF75 08 PUSH DWORD PTR SS:[EBP+8]
77D66607 E8 BF1AFEFF CALL USER32.GetDlgItem
77D6660C 85C0 TEST EAX,EAX
77D6660E 0F84 162C0200 JE USER32.77D8922A
77D66614 FF75 14 PUSH DWORD PTR SS:[EBP+14]
77D66617 FF75 10 PUSH DWORD PTR SS:[EBP+10]
77D6661A 50 PUSH EAX
77D6661B E8 DB19FEFF CALL USER32.GetWindowTextA
77D66620 5D POP EBP
77D66621 C2 1000 RETN 10
First line should be red and that means that Olly had put bp on that address.
How Olly sets breakpoints? What is breakpoint at all? Olly uses INT3 = CC (hex) interrupt to put bp (probably others debuggers too), so Olly substitutes byte at 77D665FE (which is original 55) with CC in memory. When program reaches CC byte, it stops and windows give control to debugger. So altough you see 55 in Olly, actualy in memory is on that place CC.
So our crackme somehow checks did we placed bp on some API functions. How? Reastart crackme in Olly and find this place:
00401124 . 68 97314000 PUSH crackme7.00403197 ; /ProcNameOrOrdinal = "GetDlgItemTextA"
00401129 . FF35 30324000 PUSH DWORD PTR DS:[403230] ; |hModule = NULL
0040112F . E8 88010000 CALL <JMP.&KERNEL32.GetProcAddress> ; GetProcAddress
00401134 . 8038 CC CMP BYTE PTR DS:[EAX],0CC
00401137 . 75 05 JNZ SHORT crackme7.0040113E
00401139 .^E9 F9FEFFFF JMP crackme7.00401037
You see, crackme will call GetProcAddress APA. That API will return address of "GetDlgItemTextA" API function to EAX register. Then, at line 401134, it will compare BYTE value at [EAX] address (our API) with CC. If Olly has placed bp on our API, on this place will be then CC byte value (which is INT3=our breakpoint) and crackme will know that so it will procede to JMP crackme7.00401037 that throw us to BadBoy message. Check others usable API's like, GetDlgItemInt and MessageBoxA, and you'll see that crackme checks them too.
There is one more interesting thing here: check where bad jump JMP crackme7.00401037 is throwing us:
00401035 . 68 056A0068 PUSH 68006A05
0040103A . 15 30400068 ADC EAX,68004030 ; |
0040103F . 76 31 JBE SHORT crackme7.00401072 ; |
00401041 . 40 INC EAX ; |
00401042 . 006A 00 ADD BYTE PTR DS:[EDX],CH ; |
00401045 . E8 60020000 CALL <JMP.&USER32.MessageBoxA> ; MessageBoxA
0040104A > 50 PUSH EAX ; /ExitCode
0040104B . E8 60020000 CALL <JMP.&KERNEL32.ExitProcess> ; ExitProcess
As you can see, there are no address 401037 ?!? Ansver is simple and it's called "obfuscation". That is one more trick that modern protectors use, of course in much bigger rate. First two bytes at 401035 are usles and they will never be executed too, so replace them with 9090 so you can get clear picture:
00401035 90 NOP
00401036 90 NOP
00401037 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401039 . 68 15304000 PUSH crackme7.00403015 ; |Title = "Crackme 7"
0040103E . 68 76314000 PUSH crackme7.00403176 ; |Text = "Please unload debugger :("
00401043 . 6A 00 PUSH 0 ; |hOwner = NULL
00401045 . E8 60020000 CALL <JMP.&USER32.MessageBoxA> ; MessageBoxA
0040104A > 50 PUSH EAX ; /ExitCode
0040104B . E8 60020000 CALL <JMP.&KERNEL32.ExitProcess> ; ExitProcess
Is this better ;)?
There is simple way to bypass this checks. You can set in Command Line plugin "bp GetDlgItemTextA+1" breakpoint. That will set bp on 77D665FE+1=77D665FF address in memory, ie. on second instruction in API.
2.4 Breakpoints in crackme - check
If you set bp in crackme, for example, on this line
0040123C > 6A 64 PUSH 64 ; /Count = 64 (100.)
0040123E . 68 CC314000 PUSH crackme7.004031CC ; |Buffer = crackme7.004031CC
00401243 . 68 BB0B0000 PUSH 0BBB ; |ControlID = BBB (3003.)
00401248 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
0040124B . E8 54000000 CALL <JMP.&USER32.GetDlgItemTextA> ; GetDlgItemTextA
you will also get bad message when you enter name/serial and press check button. Reason is here:
004010DD . 60 PUSHAD
004010DE . BF 11114000 MOV EDI,crackme7.00401111
004010E3 . B9 92124000 MOV ECX,<JMP.&USER32.DestroyWindow> ; Entry address
004010E8 . 2BCF SUB ECX,EDI
004010EA . B0 CC MOV AL,0CC
004010EC > F2:AE REPNE SCAS BYTE PTR ES:[EDI]
004010EE . 75 0F JNZ SHORT crackme7.004010FF
004010F0 . FF05 00304000 INC DWORD PTR DS:[403000]
004010F6 . B9 92124000 MOV ECX,<JMP.&USER32.DestroyWindow> ; Entry address
004010FB . 2BCF SUB ECX,EDI
004010FD .^EB ED JMP SHORT crackme7.004010EC
004010FF > 833D 00304000 >CMP DWORD PTR DS:[403000],5
00401106 . 74 05 JE SHORT crackme7.0040110D
00401108 .^E9 2AFFFFFF JMP crackme7.00401037
This algo will count how many there are CC bytes from 401111 to 401292 address and if there are different number of them than 5, it will jump at JMP crackme7.00401037 bad massage. That means, that in code between those addresses there are 5 CC bytes by default. Their purpose is not important to us (3 of them are parts of that breakpoints checks, 2 are used in some addresses), but if we place bp in that range, this algo will find it and count it, so final number will be bigger than 5 and crackme will find that something is wrong. To trick this check, just patch last bad boy jump JMP crackme7.00401037 with NOP's.
There is one more debugger check, but we will meet it while traing to find serial number, so we are going to keygening now.
3. KEYGENING
Crackme has two well known API's which uses to get informatin from user: GetDlgItemTextA and GetDlgItemInt.
GetDlgItemTextA - this one will take our name from window.
GetDlgItemInt - this one will take our serial. This API purpose is to retrieve numeric value from window so our serial must have only decimal digits. That API will return that number to EAX register. Also, if numeric value is greater that 4294967295 (that is in hex FFFFFFFF, one double word - size of one register) , to EAX register will be returned zero value. Conclusion is that serial must be some numeric value betveen 0 and 4294967295.
Address 40123C is place where crackme reads serial and name so place bp on 40123C, run crackme, enter haggar/12345 for name/serial and press check button. Olly will stop on our bp and now we can analyze what crackme is doing:
00401261 MOV DWORD PTR DS:[403008],EAX ; EAX=3039(hex)=12345(dec) - our serial number is placed in memory
00401266 XOR ECX,ECX
00401268 MOV ESI,0DEE1 ; To ESI is given some initial value.
0040126D XOR EAX,EAX
0040126F MOV AL,BYTE PTR DS:[ECX+4031CC] ; AL= char from our name which is placed on 4031CC address.
00401275 OR AL,AL
00401277 JNZ SHORT crackme7.0040128A ; If AL<>0 it will jump to some calculations below.
00401279 OR ECX,ECX
0040127B JNZ SHORT crackme7.0040127F ; In case that we didn't enter name,
0040127D JMP SHORT crackme7.00401215 crackme will show no message box.
0040127F MOV DWORD PTR DS:[403004],ESI ; Placing calculated value to this address.
00401285 JMP crackme7.00401181 ; After algo is over, it jumps to last debug check.
0040128A ADD ESI,EAX ; It will add char value to initial value in ESI (some sum).
0040128C INC CL ; CL is counter of characters.
0040128E XOR ESI,EAX ; summ=summ xor char
00401290 JMP SHORT crackme7.0040126F ; Loop again.
Algorithm is:
1. ESI= 0DEE1
2. ESI= (ESI + character) XOR character
3. Loop to 2. for all name characters.
This is the first part of algo. After all characters are procesed, algo is continuing at 401181:
00401181 PUSH crackme7.00403244 ; /pSystemTime = crackme7.00403244
00401186 CALL <JMP.&KERNEL32.GetSystemTime> ; GetSystemTime
0040118B MOV EAX,crackme7.00403252
00401190 MOV BX,WORD PTR DS:[EAX]
00401193 MOV EAX,crackme7.00403242
00401198 MOV CX,WORD PTR DS:[EAX]
0040119B SUB BX,CX
0040119E CMP BX,5
004011A2 JBE SHORT crackme7.004011BE
004011A4 MOV EBX,DWORD PTR DS:[403008]
004011AA XOR EBX,ESI
004011AC SUB ESI,EBX
004011AE OR ESI,ESI
004011B0 JNZ SHORT crackme7.004011B7
004011B2 MOV EAX,1
004011B7 JMP crackme7.00401037
This is the last anti-debug check and I'll explain it after serial, so for now just pass it, be sure to execute JBE at 4011A2 or you will fail to pass this check. You will jump at second part of algo:
004011BE MOV EAX,DWORD PTR DS:[403008] ; EAX=my serial 12345, only in hex form 3039h
004011C3 SHL EAX,4 ; EAX=EAX*10
004011C6 ADD EAX,0DEAD ; EAX=EAX+DEAD
004011CB ROL EAX,2 ; EAX=EAX ROL 2
004011CE CMP EAX,DWORD PTR DS:[403004] ; compare EAX with that value calculated from name
004011D4 JNZ SHORT crackme7.004011DD ; If values are not equal, jump to bad boy
004011D6 MOV EAX,1
004011DB JMP SHORT crackme7.004011DF ; else jump/procede to good one
004011DD XOR EAX,EAX
004011DF CMP AL,1
004011E1 JNZ SHORT crackme7.004011FE
Crackme calculates some sums from name and serial,then it compares them and if they are equal, we have good serial. How we going to get right serial? We have two choices; to reverse it, or to brute it. Both are very easy, but for some names serial numbers doesn't exist. That includes my nickname "haggar". Now, let we see how keygen algorithm should go:
[1] First we take name algo:
1. x= 0DEE1
2. x= (x + character) XOR character
3. Loop to 2. for all name characters.
[2] Then we reverse serial algo:
4. x= x ROR 2
5. x= x - 0DEAD
6. x= x/10
7. x= serial in hex, just transform it to decimal.
That's it! x is size of one register.
This is C++ source for simple keygen. If serial given by this kegen doesn't work, that's because serial doesn't exist for that name.
#include<iostream>
#include<stdlib.h>
#include<string.h>
using namespace std;
int main()
{
unsigned long int i,l,x,y;
char Name[200];
cout<<" User name: ";
cin.get(Name,200);
l=strlen(Name);
x=57057;
for(i=0;i<l;i++){ x=(x+Name[i])^Name[i]; }
x=(x/4)+(x<<30);
x=x-57005;
x=x/16;
cout<<" User code: "<<x<<"nn ";
system("pause");
return 0;
}
And finally, we have to analyze last anti-debug check, but let this be for your homework. I will just give you a tip: crackme checks time before taking user information from dialog box, it calculate ESI value from our name, then again checks time and calculates how big is difference, and if it's bigger than he expects, it will find out that somebody has been checking it's code.
That's all from me.
-
Level : newbie
Dumping a process and rebuilding the IAT with ImpRec.
Hello all!
I know this isn't the last version of this packer but... I think v0.75 is out.
Anyway, here we go:
Tools needed:
- OllyDbg v1.10 + OllyDump
- ImpRec 1.6 FiNAL
**********************************
UNPACKING
**********************************
After we load packed app in Olly we're here:
00408000 > 60 PUSHAD
00408001 E8 00000000 CALL packed.00408006
00408006 5D POP EBP
00408007 81ED F31D4000 SUB EBP, packed.00401DF3
0040800D B9 7B090000 MOV ECX, 97B
00408012 8DBD 3B1E4000 LEA EDI, DWORD PTR SS:[EBP+401E3B]
00408018 8BF7 MOV ESI, EDI
Now we set a BP on .code section of our process and we press Shift+F9. We are here:
004086CD 8B17 MOV EDX, DWORD PTR DS:[EDI]
004086CF 81F2 13151415 XOR EDX, 15141513
004086D5 8917 MOV DWORD PTR DS:[EDI], EDX
004086D7 83C7 04 ADD EDI, 4
004086DA 83C0 FC ADD EAX, -4
004086DD ^ EB E9 JMP SHORT packed.004086C8
004086DF C9 LEAVE
004086E0 C2 0400 RETN 4
Now we set a BP on RETN and press F9. Execute RETN and we are here:
00408312 E8 01000000 CALL packed.00408318
00408317 6A 8B PUSH -75
00408319 75 68 JNZ SHORT packed.00408383
0040831B 8B5D 3C MOV EBX, DWORD PTR SS:[EBP+3C]
0040831E 03F3 ADD ESI, EBX
00408320 33C0 XOR EAX, EAX
00408322 50 PUSH EAX
Now we again set BP on .code section and press Shift+F9. We should be here:
00401416 55 DB 55 ; CHAR 'U'
00401417 8B DB 8B
00401418 EC DB EC
00401419 6A DB 6A ; CHAR 'j'
0040141A FF DB FF
0040141B 68 DB 68 ; CHAR 'h'
0040141C E0 DB E0
0040141D 50 DB 50 ; CHAR 'P'
0040141E 40 DB 40 ; CHAR '@'
After we press Ctrl+A we get this code:
00401416 . 55 PUSH EBP
00401417 . 8BEC MOV EBP, ESP
00401419 . 6A FF PUSH -1
0040141B . 68 E0504000 PUSH packed.004050E0
00401420 . 68 0C204000 PUSH packed.0040200C ; SE handler installation
00401425 . 64:A1 0000000>MOV EAX, DWORD PTR FS:[0]
0040142B . 50 PUSH EAX
0040142C . 64:8925 00000>MOV DWORD PTR FS:[0], ESP
Yup, looks like standard VC++ progie.
Anyway, we dump the process WITHOUT import rebuilding option checked.
We have to do fix IAT manually.
Now, i think u all know how to work with ImpRec. :)
After we clicked on Show Invalid button, Imprec gives us this info:
Current imports:
0 (decimal:0) valid module(s)
35 (decimal:53) imported function(s). (added: +35 (decimal:+53))
(1A (decimal:26) unresolved pointer(s)) (added: +1A (decimal:+26))
So, we have 26 invalid pointers.
Easy way to fix:
Rightclick on first invalid pointer, select Dissasemble / HEX View:
00173517 68 F294E677 PUSH kernel32.GetEnvironmentStringsA
0017351C ^ E9 CFFFFFFF JMP 001734F0
Heh, so the first import is GetEnvironmentStringsA.
Repeat the above procedure untill u have fixed all bad pointers.
Save the new IAT in dumped file and run it!.
It WORX!
Have phun!
-
Level : newbie
Serial fishing for kito's keygenme2 using Ollydbg. (Breaking on API call)
Well this is about the fastest way that you can get a serial for the name that you input (well, shy of using a keygen). Now, that being said I am only writing the tutorial on getting the key for your name, not writing a keygen as I have never done that... yet.
I used two programs:
PEiD
OllyDbg
Download the KGNME2-KiTo.rar from this site, coded by Kito here
Step One:
Unzip it onto your desktop (in a folder), and make a backup in a new folder. Open PEiD and drag the exe file onto it to see what kind of protection that we are dealing with... Looks like there is none, coded in VC++, so that is good.
Step Two:
Go ahead and right click on the file and open in OllyDbg (if you don't have this option open OllyDbg and goto Option->add to Explorer). Once in OllyDbg hit F9 to run the exe, it asks for a name and a serial, enter anything into those textareas and then hit check... "Bad Boy!" message comes up. Hmmm... what do we do now? Go ahead and close the exe file (not OllyDbg, though) and then hit CTRL+F2 (this will reload the exe file).
Now go down to the Executeable modules window (the one with the blue E in the upper left corner) if you don't see it click on the "E" in the toolbar to open that window. Find the exe file in that list (should have about five things in it for this exe: KGNME2-K, USER32, GDI32, kernel32, and ntdll). You are looking for KGNME2-K. Right click on that name and select view names (or hit Ctrl+n). The names window will pop up and this time (not always this many names, but oh well) now I know from experience that lstrpcmpA will almost always give me the inputed bad key that I put in, and the real one that the computer generates based on the name that I put in. I go through the list and find it about 42 lines down... good. Right click on it and select: Toggle Breakpoint on Import.
Now hit F9 to run the program... it opens up behind OllyDbg, so Alt+Tab to pull it to the front. Enter the username of your choice, I will use warezhog. Now enter a fake Serial: I will use 123456.
Now hit the check button and see where the program breaks: COOL!!! if you look in the CPU main thread window (with the blue C in the upper left corner) down in the lower right side of that window you will see:
0012F974 004010E9 /CALL to lstrcmpA from KGNME2-K.004010E3
0012F978 0012F9C8 |String1 = "1164527"
0012F97C 0012F9A8 String2 = "123456"
Hmmm... as I told you before, was going to use 123456 as my fake serial, what is the other one that is sitting there? I would be willing to bet that it is the serial that the computer generated based on the name that I put in. Go ahead and copy down what it says in the String1 space (1164527 in my case).
Now hit CTRL+F2 to reload the exe file (it should pop-up saying that a Process is still active, that's fine, just click Yes). Let's try to run this without breakpoints to see how it runs. Hit F9 to run it and enter the same username as last time: warezhog (for me) and the serial that you found: 1164527 (in my case) now hit check... HELL YEAH!!! that is cool, it worked!
Well, I have never written a keygen so I am going to look into that now.
I hope that you have learned something from this. Not sure about you but I really just enjoy cracking keygenme's and crackme's I am not interested in the non-legal side of it. So, have fun and learn learn learn.
Warezhog
-
Level : newbie
Serial fishing using Ollydbg. (For absolute newbies only)
Well, this whole crackme should take you about one minute to crack.
I used two programs:
PEiD
OllyDbg
I remeber doing this crackme a while ago when I first started screwing around with debuggers and such. I think it was harder for me then, but who knows.
Well, download the vault.exe here and save it to your desktop (that is where I usually work on files because of its ease of location). Then make a backup in another folder (always make a backup).
Step One:
Drag the exe file into PEiD to see if there is any sort of protection... Nope, it is pure Assembly so we are good to go.
Step Two:
Right click on the vault.exe file and select open with OllyDbg (if you don't have this option, then open OllyDbg and go to Options->Add to Explorer)
This will open the exe in the program. Hit F9 to run the program (you may need to Alt+Tab to pull the program to the front after it is run), it will ask you for the name and the key. Enter anything in there and you will see it says: "No Access!"
Well, that is no good. Go ahead and close the exe and hit Alt+F2, this will reload the program. Now go to the CPU menu in OllyDbg (that is the big one with the Blue C in the upper left hand corner of it) and right click anywhere and go to: Search for-> All referenced text strings.
That should pop up a window that shows about 6 strings total. The first two seem interesting:
Robin Banks
8dS#9d2?@$
Hmmm... that seems odd. The first one looks like a name and the second one looks like a key.
Let's copy those down on a piece of paper or copy and paste them into a notepad file. Then hit F9 to run the program again, put: Robin Banks in the name area and: 8dS#9d2?@$ in the key area. What do you know?!! It Worked!!
I know that this is about as easy as we can get, but hey, you gotta start somewhere. There is an even easier way to get the serial and name, but I will save that one for my next tutorial on an actual generated serial.
Warezhog
Well warezhog beat me to it, but here is a little more detailed way of understanding where that code comes from and how its compared to user input:
You can get to the important code below by either just skimming the code (since the file is so small) or by breaking on GetDlgItemTextA, lstrcmpA, MessageBoxA, etc:
00401069 CALL <JMP.&USER32.GetDlgItemTextA> ; get input from name textbox
0040106E PUSH vault.00403000 ; push your inputted name
00401073 PUSH vault.00403040 ; push "Robin Banks"
00401078 CALL <JMP.&KERNEL32.lstrcmpA> ; compare 2 strings
0040107D OR EAX,EAX ; result = 0 if equal
0040107F JNZ SHORT vault.004010BA ; if not equal (EAX = 1) jump to 004010BA
00401081 PUSH 20 ;
00401083 PUSH vault.00403020 ;
00401088 PUSH 0BB9 ;
0040108D PUSH DWORD PTR SS:[EBP+8] ;
00401090 CALL <JMP.&USER32.GetDlgItemTextA> ; get input from key textbox
00401095 PUSH vault.0040304C ; push "8dS#9d2?@$"
0040109A PUSH vault.00403020 ; push your inputted key
0040109F CALL <JMP.&KERNEL32.lstrcmpA> ; compare 2 strings
004010A4 OR EAX,EAX ; result = 0 if equal
004010A6 JNZ SHORT vault.004010B1 ; if not equal (EAX = 1) jump to 004010B1
If you take the JNZ at 0040107F you land here
------------------------------------------------------
004010BA |> C605 9E304000 >MOV BYTE PTR DS:[40309E],0
004010C1 |> 803D 9E304000 >CMP BYTE PTR DS:[40309E],1
004010C8 |. 75 1D JNZ SHORT vault.004010E7
This verifies 0040107F is a badboy jump because it always moves 0 in and then compares to 1, which will never be equal, hence 004010C8 will always execute the jump to the badboy msg.
The same situation can be seen for the 004010A6 jump:
-------------------------------------------------------
004010B1 |> C605 9E304000 >MOV BYTE PTR DS:[40309E],0
004010B8 |. EB 07 JMP SHORT vault.004010C1
004010C1 |> 803D 9E304000 >CMP BYTE PTR DS:[40309E],1
004010C8 |. 75 1D JNZ SHORT vault.004010E7
This jump leads to another compare which will always never be equal and force 004010C8 to jump to the badboy message.
So we know that our username must be "Robin Banks" and our key "8dS#9d2?@$"
To verify this works (besides just entering it in and looking at the msgbox) you can look at the code:
004010A6 |. 75 09 JNZ SHORT vault.004010B1 ; not taken (look above)
004010A8 |. C605 9E304000 >MOV BYTE PTR DS:[40309E],1
004010AF |. EB 10 JMP SHORT vault.004010C1
004010C1 |> 803D 9E304000 >CMP BYTE PTR DS:[40309E],1
004010C8 |. 75 1D JNZ SHORT vault.004010E7
We see that yes 1 goes in, its compared to 1, and this JNZ will not JUMP! And guess what's below...
A messagebox asking us how we got in ;)
Hope this helps some new people in the cracking world
-thorpe
-
Level : beginner
Removing encrypted unregistered strings, and provide an inline patch for this target.
Multimedia Builder 4.9.0.1
Intro
Tools used:
SoftIce Driver Studio 2.6.0 under Windows 5.1,
WDasm 8.93,
Hiew 6.04 (can be any hexeditor),
UPX v.1.24 (for unpacking only)
Overview
A Windows-based multimedia authoring system that allows you to create autorun CD menus, Multimedia Applications on CD-ROM, Demos, Presentations, MP3 players and much more. The software is great for beginners as well as for advanced users. MB creates small stand-alone Windows exe applications and has all the bells & whistles you will ever need. You will love this easy-to-use, intuitive software.
Program homesite: hxxp://www.mediachance.com
Tutorial author: Wizard
Playing with registration information
Run MMB. Go into its menu Help/About... Push "Enter Reg Code" button. Now you can see three boxes: Name, Code, MP3 unlock code. Each of them must be filled with correct data. So, you can simple do a bpx GetWindowTextA and find out what all these Name & Codes are. You can make sure how simple to find them by yourself. The algorithm is quiet simple, even funny for such kind of program. Here it is:
1) The "Name" field may consist of any symbols, but it must contain "@" symbol anyway. It's for the first look only. But it must be exactly this: "cokebo@thebrabo" and no symbols more or less. Both those strings can be found in main MMB file (MMBuilder.exe) as "obarbeht" and "obekoc", the "@" symbol will be added in memory.
2) The "Code" field consists of next three things: "1-" + "xxxxxx" + "CRC", where first two symbols're constant, the next six're any numbers (you can use random generator), and the rest three're simple checksum, which is the sum of previous eight symbols, including "1-" and xes (six numbers). For example a valid code will be "1-123456-403", 403 is decimal representation of CRC: 31h+2Dh+31h+32h+33h+34h+35h+36h=193h=403.
3) The "MP3 unlock code" field. It's just a field on the form, which gets a string from user and it saves it registry. It doesn't even compare it with anything. That's a trick!
First impression
Having playing a while with these parameters I thought that I found a correct registration information. Yes it is so. But all that registration information I found is a fake. This conjecture can be proved very simple. Just click the icon second from the right of the panel, which's under the main menu. The hint of that icon says: "Compile and Run". As you can see the annoying string below the form, which says: "Created with unregistered version of Multimedia Builder". This's the first problem. The second problem is same, but for the final compilation, when you choose File/Compile... and then press Ok button & run it, we have the same bullshit.
All above said means that program ain't registered yet, maybe author too smart, maybe cracker too stupid, I don't know, anyway it generates executable files with yellow inscription. So, what we gonna do in this case? Let's look at directory, when the main program is situated in. After a little investigation we can see the program (MMBuilder.exe) is written in MS Visual C++ language and not packed. But one interesting we can look on, it's "Player" directory, where situated files "Player.bin", "Player.exe" and "ecard.bin". All those files're packed with UPX v.1.24. There's need to be a prophet to suppose that "Player.bin" file concatenates to each compiled executable. The other two files are also in use. "Player.exe" is just a stand-alone player, which plays .mbd-files from commandline. "ecard.bin" is for E-cards generation. E-Card is a smaller stand-alone executable, which you can send to somebody via e-mail. The most important thing for e-card is the size, because it takes a while when you download files with e-mail client. For more information you can refer to MMB help.
Keep on digging
Make a copy of the original file, e.g. do "copy autorun.exe autorun.old".
Let's find out how to remove this yellow string from a project form. Compile a new empty project (File/Compile.../Ok). Now we have a working executable file with yellow string at the bottom of the form. Unpack the exe file with "upx -d ", e.g. "upx -d autorun.exe". We do it, because disassembler won't work on a packed file. Disassemble it with WDasm. Look at the import functions list. What do you see, nothing interesting, except some functions from GDI32.dll (a cut from WDasm list):
Addr:0010DBCC hint(0000) Name: CreateFontA
Addr:0010DC8A hint(0000) Name: GetTextExtentPoint32A
Addr:0010DD52 hint(0000) Name: BitBlt
And many others. All they are seem so familiar to us, eah? First time I took BitBlt function, but after I traced the code once again, I saw there CreateFontA function. So do a "bpx CreateFontA". Run our unpacked project. SI will pop-up. Trace till this place:
00448F0F: 52 push edx
00448F10: 8BF8 mov edi,eax
00448F12: E87EF30800 call 004D8295
[1] 00448F17: 8D44241C lea eax, dword ptr [esp+1C]
00448F1B: 8BCE mov ecx,esi
00448F1D: 50 push eax
[2] 00448F1E: E89DFCFEFF call 00438BC0
00448F23: 8B00 mov eax,dword ptr [eax]
00448F25: 8D542424 lea edx,dword ptr [esp+24]
00448F29: 52 push edx
[1]: As you can see eax points to the crypted magic string, which looks exactly like this:
ASCII !thhqjwE$fjfhqnunxQ%gq#rtjuui{!fhvjuulkjspx$mukz$i fvdiwD"
HEX 21 74 68 68 71 6A 77 45 24 66 6A 66 68 71 6E 75 6E 78 51 25 67 71 23 72 74 6A 75 75 69 7B 21 66 68 76 6A 75 75 6C 6B 6A 73 70 78 24 6D 75 6B 7A 24 69 66 76 64 69 77 44 22
Trace along till [2], in here the magic string will be totally decrypted. Let's get into the call. Trace till here:
[1] 00438BDA: 33F6 xor esi,esi ; assigns zero to esi
... ... ... skipped
00438C04: 8B442428 mov eax,dword ptr [esp+28]
00438C08: BB01000000 mov ebx,00000001 ; starts from first symbol
[2] 00438C0D: 3970F8 cmp dword ptr [eax-08],esi ; compares a string length with 0, because esi here's ALWAYS zero
00438C10: 7E4F jle 00438C61 ; jumps over the decryption routine
00438C12: 8B0DACA05200 mov ecx,dword ptr [0052A0AC]
00438C18: 894C240C mov dword ptr [esp+0C],ecx
[3] 00438C1C: 8A0406 mov al,byte ptr [esi+eax] ; moves to al next symbol
00438C1F: 8D4C240C lea ecx,dword ptr [esp+0C]
[4] 00438C23: 2AC3 sub al,bl ; subtracts value from symbol code
... ... ... skipped
00438C39: E8FAFC0900 call 004D8938 ; adds symbol to the end of a new string
[5] 00438C3E: 43 inc ebx ; eax contains a pointer to a new string
00438C3F: 83FB05 cmp ebx,00000005 ; for each 5 symbols
[6] 00438C42: 7E05 jle 00438C49
00438C44: BB01000000 mov ebx,00000001 ; drops the value
... ... ... skipped
00438C5C: 3B70F8 cmp esi,dword ptr [eax-08]
[7] 00438C5F: 7CB1 jl 00438C12 ; is all symbols encrypted
00438C61: 8D4C2408 lea ecx,dword ptr [esp+08] ; [esp][8] cell contains a pointer to a new string
[8] 00438C65: E823FE0900 call 004D8A8D ; last decryption routine
The decryption algorithm of the magic string is simple. According to [3]..[7] it can be described as follow:
Original ASCII !thhqjwE$fjf... ...and so on till string ends
HEX 21 74 68 68 71 6A 77 45 24 66 6A 66...
Subtract HEX 01 02 03 04 05 01 02 03 04 05 01 02...
Result HEX 20 72 65 64 69 6C 75 42 20 61 69 64...
Now we have half decrypted string, which looks like this:
ASCII rediluB aidemitluM fo noisrev deretsigernu htiw detaerC
HEX 20 72 65 64 69 6C 75 42 20 61 69 64 65 6D 69 74 6C 75 4D 20 66 6F 20 6E 6F 69 73 72 65 76 20 64 65 72 65 74 73 69 67 65 72 6E 75 20 68 74 69 77 20 64 65 74 61 65 72 43 20
The last step of decryption is [8]. If you'll trace that call you find out what that routine reverses the whole string, e.g. was 'tset', will be 'test'. According to that rule our string now should look like this:
ASCII Created with unregistered version of Multimedia Builder
HEX 20 43 72 65 61 74 65 64 20 77 69 74 68 20 75 6E 72 65 67 69 73 74 65 72 65 64 20 76 65 72 73 69 6F 6E 20 6F 66 20 4D 75 6C 74 69 6D 65 64 69 61 20 42 75 6C 69 64 65 72 20
It's now fully encrypted and gonna be showed on the form. To predict it you can find the magic sequence in file and patch it according to algorithm like this:
Original Replaced
20 43 72 65 61 74 65 64 20 77... 00, 01, 02, 03, 04, 00, 01, 02, 03, 04, and so on for all 57 characters
But the most easiest way to do it is patch of [1]:
The patch table
Original Replaced
00438C0D: 3970F8 cmp dword ptr [eax-08], esi 8970F8 mov dword ptr [eax-08],esi
00438C10: 7E4F jle 00438C61 EB4F jmp 00438C61
This path allow the program to jump over decrypting routine, and also it replaces string length to zero, which's use in next routines after the last decryption call. The main part of cracking is over. All we have to do now is an executable patch. But don't forget about one little thing - the program is packed. This is not so terrible as it looks like, because executable packed with UPX and we can patch it very simple.
Making an inline patch
Overwrite backup copy to original file, e.g. autorun.old to autorun.exe. Now open file in your favourite hexeditor. We're gonna search the instructions, where the packer gives over the control to unpacked routine. But before it our patch will make our business.
Let's search for last unpacker code, it should look like this:
Operation code Mnemonics
61 popad
E9XXXXXXXX jmp XXXXXXXX
0000 add [eax],al
0000 add [eax],al
... ...
So, we're gonna search for two bytes: 61,E9. As a rule those bytes are always somewhere at the end, and you can search till your hexeditor won't say: "Target not found" (it's from Hiew) or something like that. After you find those two instructions you'll see a lot of zeroes below the last instruction. If they're there that means you found correct address, if they aren't there, then keep on searching.
I've found those bytes in autorun.exe at address: 00079C3B. By the way, if you're cracking version 4.9.0.1, then your autorun.exe must be exactly 509,545 bytes long and as follow all addresses will be the same.
The representation of what I've found in executable:
00079C2A: 09C0 or eax,eax
00079C2C: 7407 je 000079C35
00079C2E: 8903 mov [ebx],eax
00079C30: 83C304 add ebx,004
00079C33: EBD8 jmp 000079C0D
00079C35: FF96B8421800 call d,[esi][0001842B8]
00079C3B: 61 popad
00079C3C: E92DEAF3FF jmp 0FFFB866E
00079C41: 0000 add [eax],al
00079C43: 0000 add [eax],al
... ... ... and so on zeroes continue
Below the "popad" instruction you can see "jmp 0FFFB866E", remember or better write it address to a paper.
Now we're gonna add a patch to executable according to the patch table from address that I was marked with blue.
The final crack should look like this:
00079C3C: C6050D8C430089 mov b,[000438C0D],089 mov dword ptr [eax-08],esi
00079C43: C605108C4300EB mov b,[000438C10],0EB jmp 00438C61
00079C4A: E91FEAF3FF jmp 0FFFB866E jump to OEP
Now you can run the application. Behold! It runs without the annoying message. We did crack only for our project. And now we've to crack "Player.bin", because you can always find it in any generated file, e.g. our autorun.exe. So if we'll patch "Player.bin" file, all generated files run without the nag string.
Patching the program files
Let's go to the "Player" directory.
Player.bin.
Make a copy of "Player.bin". Unpack a copy. Find our magic bytes in it: 39,70,F8,7E, but without 4F. Those bytes're marked with red in the "Original" column of the patch table. I've found it at address 00038C0D in file, that means the address we're gonna specify as the memory patch address, it'll be 00038C0D + ImageBase = 00038C0D + 00400000 = 00438C0D. You can kill a copy. Now search in packed file another magic sequence, which is: 61,E9. The address is 00079C3B. Remeber the jump address, it's 0FFFB866E. Believe it or not, but the patch will be ABSOLUTELY THE SAME as the above table. Actually there's nothing amazing in that, because as I said already this file is a part of any compiled project.
Player.exe.
The address to patch in memory is 00038F0D + IB = 00438F0D.
All in patch will be the same, except addresses. Check it out:
00079AEC: C6050D8F430089 mov b,[000438F0D],089
00079AF3: C605108F4300EB mov b,[000438F10],0EB
00079AFA: E9AFEAF3FF jmp 0FFFB85AE
Comment: even if this ain't showing the nag, it contains it, so how do you think what for?
ecard.bin.
The address to patch in memory is 00024597 + IB = 00424597.
The inline patch:
00050DBC: C6059745420089 mov b,[000424597],089
00050DC3: C6059A454200EB mov b,[00042459A],0EB
00050DCA: E97171F8FF jmp 0FFFD7F40
Comment: to get this file type you've got to go to File/Compile.../Choose "E-card" radio button/press Ok.
The final step
Run MMB (if not running). Press "Compile and Run" button. It's still show the yellow string. Close MMB (if it runs). Open its executable (MMBuilder) with a hexeditor. Search 39,70,F8,7E. I've found those bytes at addresses 0002D5FA, 00031C0D and 000D438D.
Merde!!! The magic piece of code is in the three places and it's exactly the same. What does it mean? It means the program author did a smart move. He made three inline copies of the magic decryption procedure. So, patch them all directly to be sure no more thricks will arrive during the user work. As you can see the file is naked (it ain't packed at all), and we can patch it directly.
The patch itself:
0002D5FA: 8970F8
0002D5FD: EB51
00031C0D: 8970F8
00031C10: EB4F
000D438D: 8970F8
000D4390: EB4F
After you patch the program, run it. Press the sneaky button, I guess now it isn't sneaky anymore, because it runs well without any annoying message below the form. Also you can check File/Compile...(choose Full or E-card type), press Ok, run it. Everything runs well without nag.
Are we done? Yes we are! We finally did it. Welldone! That means our essay is over.
-
Level : newbie
Patching the vault crackme that is meant for people just starting out with reversing.
Eiy0w Cr4ck3rs 0ut ThuR!!!
Yup, this tut came a little late..but I do believe that its not yet too late.
I know, by the time that Im typing this tute, there are still hopeful newbies out there who are aspiring to become Crackers,/Reversers.
The reason that I wrote this tute is because I find the solutions for vault.exe crackme thats currently available is quite difficult to understand for a newbie.. (Well.. Im also just a newbie..). Especially to those who are just starting, trying to look for nice crackme's to practice on. Instead of learning something, they find themselves lost not just in the codes but also in the tutorials as well, because the tuts were vaguely written and can only be understood by the experienced. Im not saying that i have the best tut for this cracme..and I have nothing against the authors of the previews tuts of the said crackme either.. Im just hoping to keep the Cracking/Reversing World alive and active by educating the starters in a more, at least, comprehensive way..
I do hope that I made a clear point here...
So I made my own version of the solution, made it more detailed, hoping that the newbies could easily get what I am saying in this tut.
So this tut is really intended for the newbies of the Cracking world. For advanced crackers... well...
You may not have the need to read this tut anymore..
Tools youll need:
* A Computer ---> (Of course... geez...)
* W32Dasm v. 8.xx or higher ---> (You can get this anywhere on the net...)
* Hackers View (Hiew) v. 6.xx or higher ---> (Theres plenty on the net too...)
* Your full attention.. Yup, give your attention to this tut till the end..
Knowledge on how to use W32Dasm and Hiew is a need. So I assume that you, well, at, least, have a little knowledge on how W32Dasm and Hiew work.
The CrackMe:
* vault
* The info.txt file says that we should find the correct Name and Key for this crackme.
But I made a little twist here. We are not goin to find the correct Name and Key, but make any Name and Key to be recognized as a correct Name and Key! & that is called Reversing the code.. Hehie.. confused already?! I hope not... ;-]
Well be dealing with Assembly codes here, so for you newbs, if you dont know nothin about it yet, I suggest you study about it first, so that we will have no difficulty in understanding the terms that Im gonna be using in this tut. Read more files about Assembly and related topics for your dose of infos. Yup, youll really have to read read read read read...
If youre too lazy for that, then cracking/reversing is not for you...
For those who already know, good for you.. youre one step closer to knowledge..
There are still hundreds and thousands of steps though.. ;-]
Okay.. Enough with the talking.. Lets Crack!
* We will be altering the codes of the crackme, so I personally suggest that you backup the original file so that you will still have a spare file just in case you messed with the default codes of the original file.
* Run "vault.exe".
* Enter any Name and Key...then click the Test button.
* An error message should pop up, saying "No Access!".
* Try to memorize that error message (Write it down in a piece of paper if you must... ;-] )
* Now run W32Dasm, and disassemble vault.exe.
* If its the first time using W32Dasm, the disassembled file will have garbage characters at first.
* To get away with this :
* In W32Dasms toolbar, click Disassemble/Font/Select Font
* I suggest Courier New, 10.
* Then save that font to default (You know how.. )
* In the toolbars of W32Dasm, click that button w/ a flashlight icon (Yup, you guessed it right...its the Find Text button.)
* Type the error message that popped up when you entered the invalid Name and Key ("No Access!").
* After pressing enter, you should land here:
Possible StringData Ref from Obj -> "No Access!"
:004010EE 6861304000 push 00403061
:004010F3 FF7508 push [ebp+08]
* Now..pay attention here.. We need to find ConditionalJumps here (Itll be quite long if I explain it here.. So as what Ive said, read more textfiles about Assembly language..).
Scroll up a few lines until you see something like this:
Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004010C8(C)
Saw that right..?
Good...
* Now scroll up a bit more and try to find the location of 004010C8.
You should stop right here:
:004010C8 751D jne 004010E7
An easier alternative for this is to click the GoTo Code Location button in the toolbars and enter the hex value.
* Notice that the clan colored highlight before now became green, that is because we are highlighting a jump.
While highlighting :004010C8 751D jne 004010E7, the status bar on the bottom part of W32Dasm should be:
Line:150 Pg 3 and 4 of 9 Code Data @:004010C8 @Offset 000004C8h in File:vault.exe
* The only thing we need to remember here is the Offset value, which is 000004C8h.
Recall: In hex notation, 000004C8h = 4C8 (I dont have to explain why right...?) ;-)
Copy that hex value on a piece of paper (..or memorize it..)..
It'll be used later..
* Now close W32Dasm (dont just minimize it, exit it! ;-] You will not be able to make changes with vault.exe if it is being used or being run by another program.. ;-] ) and run HackersView (Hiew for short..).
(I suggest you run Hiew on the same folder with the vault.exe, so that it would be easier to browse for vault.exe)
* After Hiew-ing vault.exe, you should of course see a garbage of characters. Press F4, and select Decode as your view mode. You can also press Enter to switch between the view modes. Now that you are in Decode mode, press F5 (GoTo), and type the Offset value that we got earlier. That is...4C8 (or just 4c8...its not case sensitive...)
Now lets go back to this:
:004010C8 751D jne 004010E7
Take note of the value, 751D. In Assembly language, the value 75, as well as jne, stands for jump if not equal. (1D is the number of bytes we ware going to jump down)
That means that if the Name and Key we entered is not equal to the correct one, the message box saying "No Access!" will appear, telling us that the Name and Key we entered was not the correct one. BTW: There is only one correct Name and one corresponding Key for the crackme.
* So what we are going to do is reverse the said code, we will reverse jump if not equal to jump if equal, or je.
In ASM Code:
75 = jne or jump if not equal
74 = je or jump if equal
* Using Hiew, we will replace 751D with 741D.
Doing so will make the vault recognize any Name and Key to be the correct one, and the error message will only appear if the correct Name and Key is entered.
Code Reversing...at its best! Hehie.. ;-]
Okay, back to Hiew and you (O.o? Hmm...)
Where are we again...?
Oh..Okay..
* Now on your Hiew window, press F3 (Edit), and replace 751D with 741D.
(Notice that after changing 75 to 74, jne became je...)
After typing and changing, press F9 (Update), to of course, update your new code for vault.exe.
Then press F10 (Quit), to exit Hiew. Note that you must press F10 after you are done or the changes will not take effect if you just merely clicked the X button to close Hiew.
* Awright... try to test vault.exe if our Reverse Code Engineering worked.. ;-]
Type any Name and Key.. like k5uvt0uk3 or 389utckt3u or b3re529f... etc...
You can even leave both of them blank...
Press Test...And WaLLa!! ;-]
There you go.. I hope I educated someone somewhere.. & I do hope so that you, as a newb, understood not just my instructions but as well as what was happening while we were trying to crack the vault.exe.. So that if there will be another program that uses similar protection scheme as this crackme, you will already know what to do...
And as Ive said, and what will always say, read read read read read read read...
Its one of the fastest way to learn, along with hands-on experience..
-
Level : intermediate
Manually unpacking sdprotector 1.12 using OllyDbg.
%%%%%%%%%% SDprotector Pro Edition v1.12 Manually Unpacking Tutorial by KaGra %%%%%%%
Download SDPR at
کد:
www.sdprotector.com,till
now is 1.12 pro edition the most recent ver.
Hallo,hallo.Nice talking with U again.Well,this time the victim is SDprotector.
Well,here I followed a little different approach to unpack this protector.What
I mean?HeRe it Comes...
Tools Used: Olly v1.10 and ImpRec v1.6f,Ollydump Plug,HideOlly Plug (I will not refer to it)
Well,I have checked all the Options of protection in SDprotector and protected the file.Those
options are in Options->Left part of the screen.I didn't touch anything else.
Load the exe target in Olly.In debugging options,check them all so that the execution
of the programm will not be interrupted by any exception (and then ask U to press Shift+F9
or Shift+F8 or Shift+F7 to continue).Run the exe.A messagebox appearz saying that debugger is
detected.Well it is hard to find what exactly protection against debugger this may has.
I found out that it detects a debbuger using CreateToolHelp32Snapshot to find out what windows
are open and compere them with a default string list (in the list is also Olly).It also uses
SetUnhandledException filter to find the debugger,but I really tried hard to find out how the
exception occured after the calling of this API.But I didnt find it.So I thought this:Why just
run the exe,not under Olly,and then just attach to the process?
Well,let's do so.I run the exe,a messagebox appears saying something of a demo version of
the packer (don't worry,the features we enabled for protecting this file work),and then the
screenbox of the exe process appears.Now,open Olly.Before U attach,a messagebox appears saying
that a debugger is detected,and closes the debugger.Run the exe again,and open LordPE to dump it.
Before U ever try to dump,it closes LordPE and exits.What is going on?
Well,it can't be using SetUnhandledExceptionFiler or any other kind of exception trick,because
it is not being debugged.So,the only thing that comes in my mind is that it has hardcoded
strings refering to processes or Window handles.It also has a loop that checks all the
running processes and windows handles with those strings,althought it is supposed to be
in the original exe's code section.Well,it is but the packer has given him some extra code
and this code still remains and makes this security check loop,althought we have passed the
OEP.
And my quess is true.Rename LordPE.exe to something else.Run the exe.Now run the renamed
LordPE.It does not closes LordPE.So,do the same with Olly.Damn,it still closes her.Well,this
happens because Olly has inside her .data and .edata section strings that start with "Olly"
chars,and all those are hardcoded in the security loop that I mentioned before.So,open Olly,and
load Olly (yes,U hear right) in the first Olly.Click the "M" button and see the section of the
loaded Olly,in first Olly:
Memory map
Address Size Owner Section Contains Type Access Initial Mapped as
00400000 00001000 OLLYDBG PE header Imag R RWE
00401000 000AF000 OLLYDBG .text code Imag R RWE
004B0000 0005B000 OLLYDBG .data data Imag R RWE
0050B000 00001000 OLLYDBG .tls Imag R RWE
0050C000 00001000 OLLYDBG .rdata Imag R RWE
0050D000 00002000 OLLYDBG .idata imports Imag R RWE
0050F000 00002000 OLLYDBG .edata exports Imag R RWE
00511000 00036000 OLLYDBG .rsrc resources Imag R RWE
00547000 0000C000 OLLYDBG .reloc relocations Imag R RWE
Go to the .idata section and search for the string Olly,withought having the case sensitive
checked.U find this:
0050F780 6F 6C 6C 79 64 62 67 2E 65 78 65 00 5F 41 64 64 ollydbg.exe._Add
0050F790 73 6F 72 74 65 64 64 61 74 61 00 5F 41 64 64 74 sorteddata._Addt
0050F7A0 6F 6C 69 73 74 00 5F 41 6E 61 6C 79 73 65 63 6F olist._Analyseco
Change ollydbg.exe to something else eg. fffffff.exe.Search again in this section.
We are lucky,because it is found just once.Go to the .data section and search again
for the same string.Damn,here the string exists in many places.Well,change all the
words that have this string inside them.
When done,dump with OllyDump the process,without checking the Import Rebuild Option on.
Well,Olly is now patched.Rename the dumped to anything that has not the string Olly in it.Now,
run the protected exe again till the main window of the crackme appearz.Now,run the dumped
new patched Olly.Wait a little (some seconds).
Well,it dooesn't detect Olly!Well,if in this part U have done something wrong,or make
something wrong in the following steps,next time will detect Olly,and U will need to change
the patched strings ALL to something else,again.I think that the protected exe,if once find
an exe string signature that may be a possible debugger or any other "hostile" program for
it,it put it somewhere (registry,memory,I don't know) and rembers it.So,just do the patch right
and follow every single instruction of the next linez.But there are pacthes that will patch Olly
against this protected exe once and for all.Such a patch is putting Fh 's in all strings that will
be replaced.I don't really know why this string makes this good thing for us,but it works.So,patch Olly
with Fh 's where U should patch (Hey,Fh ascii not chars!).
Now,in patched Olly that is running,check all the options of exceptions in Debugger Options,and in
option of Ignore also following custom exceptions should be nothing.
Now select the process of the protected exe that runs and attach to it.Do not open any other window
becuase the protected exe may detect Olly.Just the Debugging options and then the attach window.U are
HeRe:
77F767CE C3 RETN
77F767CF > CC INT3
77F767D0 C3 RETN
77F767D1 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
77F767D5 CC INT3
77F767D6 C2 0400 RETN 4
77F767D9 > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
77F767DF C3 RETN
77F767E0 > 57 PUSH EDI
77F767E1 8B7C24 0C MOV EDI,DWORD PTR SS:[ESP+C]
77F767E5 8B5424 08 MOV EDX,DWORD PTR SS:[ESP+8]
77F767E9 C702 00000000 MOV DWORD PTR DS:[EDX],0
77F767EF 897A 04 MOV DWORD PTR DS:[EDX+4],EDI
77F767F2 0BFF OR EDI,EDI
77F767F4 74 11 JE SHORT ntdll.77F76807
77F767F6 83C9 FF OR ECX,FFFFFFFF
77F767F9 33C0 XOR EAX,EAX
77F767FB F2:AE REPNE SCAS BYTE PTR ES:[EDI]
Now,press the "M" button and set a memory breakpoint on access in .text section.In this section
is the original code of the protected exe.Go to your clock and change the hour.Make it one hour
less or one hour more.This is done,because changing it we stop the loop of the anti-debugging feature
that the running protected exe has.It seems that this loop checks periodicall the hour-mins-secs and
then performs the detection of debugger check.Really nasty,I made 2 hours to understand this!Now press
F9 to run the process.The debugger pauses at the breakpoint we have just set,HeRe:
0040111B C8 000000 ENTER 0,0
0040111F 53 PUSH EBX
00401120 56 PUSH ESI
00401121 57 PUSH EDI
00401122 817D 0C 11010000 CMP DWORD PTR SS:[EBP+C],111
00401129 0F84 AB000000 JE sdprotec.004011DA
0040112F 817D 0C 10010000 CMP DWORD PTR SS:[EBP+C],110
00401136 0F84 86000000 JE sdprotec.004011C2
0040113C 837D 0C 10 CMP DWORD PTR SS:[EBP+C],10
00401140 0F84 B5000000 JE sdprotec.004011FB
00401146 B8 00000000 MOV EAX,0
If u hadn't check the time of your clock,an exception that could not be handled would occur,and
after some tracing U would have made (u cann't stay in a place forever!) the exe would have traced
Olly and exit.If u fail and try again,change accordinglt the time clock every time U try.
Yes,we are at the unpacked,original code of the exe.But where is the OEP?As U will know,we have
passed the OEP becuase we run the exe before patched Olly.But,becuase the exe is just appearing
a screen that asks for a Name and Registration code,it is in a loop,and not far "after" the OEP,
because no basic routine has been executed,that will make the flow of the programm to go much away
from the OEP.Now,we are at 0040111B paused.Just check a few lines up.We see this place:
004010C2 6A 00 PUSH 0
004010C4 E8 E1010000 CALL sdprotec.004012AA ; JMP to kernel32.GetModuleHandleA
004010C9 A3 F3204000 MOV DWORD PTR DS:[4020F3],EAX
004010CE C705 C7204000 03>MOV DWORD PTR DS:[4020C7],4003
004010D8 C705 CB204000 89>MOV DWORD PTR DS:[4020CB],sdprotec.00401>
004010E2 C705 CF204000 00>MOV DWORD PTR DS:[4020CF],0
004010EC C705 D3204000 00>MOV DWORD PTR DS:[4020D3],0
004010F6 A1 F3204000 MOV EAX,DWORD PTR DS:[4020F3]
Good place for an entry point,because we see a call at GetModuleHandleA (many progs need the
return value from such an API call,and call it some opcodes after the OEP) and we don't see any
other API calls of any kind before the opcode at 004010C2 (and generally,after the OEP API calls
like GetVersion,GetModuleHandleA,LoadLibrary or GetCommandLineA follow.).U may say,why could it
be not an earlier?Well,I tried and after the IAT rebuilding (that will follow) I couldn't make it
work.Eventually the OEP is 004010C2.In some other cases,when an exe is compiled with a
language compiler eg. C++,at the OEP arefour to five opcodes that are the same for every
produced with the same compiler exe.So,after landing at a place of code after the attach and
dump,the OEP cannot be far away,since not many opcode sequences like these exist from the place
we landed,and near it.Anyway,have in mind that it is NOT ALWAYS necessary to land exactly at
OEP,in order for the dump to work.We just say that OEP is the most ideal place,becuase all sections
are intact,nice and clean (and not changed by self-modifications or of modifications that happen during
runtime,between the exe in memory and other processes,or by itself).Remove the memory breakpoint.
Before dumping,right click in all section that u see in tha patched Olly (and PEheader as well) and
set access->Full access.This is done becuase during unpacking,the protector has protected the
access of those memory locations using probably VirtualProtect API's,and if we try to dump the
dumper will fail,or may create a false dump.Also,ImpreC will not be able to read the process from
memory,meaning the data that are contained and to be used,in order to fix the Imports (u can check
it!)
Now,dump the exe with OllyDump,without having checked the IMport Rebuilding,and as an OEP the
value of (OEP as seen in addressing)-Imagebase=004010C2-00400000=10C2.We now have the dump.
For the rebuilding part we will do everything without executing anything in Olly,just seing the code,
because we cannot put any software or hardware breakpoints,the packer detects them all.But don't
mind,because code at that time is not self-modified any more and the mem locations that we will
need contain hard-coded bytes,so no need to run it in Olly actually.Just using Olly as
a Dasm.OpenImpRec and put as OEP the value 10C2.Now IAT autosearch and Get Imports.
Now press Show invalid.We have many invalid.Select one invalid and right click in
it->Disassemble.What we see here is that in the place where the code of a valid API
should have been,are instructions that generate the call to that API.So the only thing
we have to do for all those invalids,is to follow in debugger this codes and
see where they finally jamp,at API's.We will know that we are for the first time in
API's code,because the address of the first opcode will be (and all those who follow and
are in the API!) in 7XXXXXXX format.Then,just a search in all module names in Olly will reveal
which API has as starting address this value,and we can identify this API.Then,we will manually
put the name of that API at the invalid thunk.
But how are we going to follow all those invlid thunks?We,check the Disassm of one invalid,eg
the 00143B98h:
00143B98 58 POP EAX
00143B99 50 PUSH EAX
00143B9A 60 PUSHAD
00143B9B 9C PUSHFD
00143B9C 68 02000000 PUSH 2
00143BA1 50 PUSH EAX
00143BA2 B8 32DEE44B MOV EAX,4BE4DE32
00143BA7 50 PUSH EAX
00143BA8 B8 2C1CB9C3 MOV EAX,C3B91C2C
00143BAD 50 PUSH EAX
00143BAE E8 BD5C3200 CALL sdprotec.00469870
00143BB3 9D POPFD
00143BB4 61 POPAD
00143BB5 B8 2C1CB9C3 MOV EAX,C3B91C2C
00143BBA 9C PUSHFD
00143BBB 2D 32DEE44B SUB EAX,4BE4DE32
00143BC0 9D POPFD
00143BC1 50 PUSH EAX
00143BC2 C3 RETN
Well,all code till 00143BB5 is junk code,to confuse the reverser.So at 00143BB5 moves a value at
EAX,then a PUSHFD (junk opcode also,don't care about this) and at 00143BBB subtract 4BE4DE32 and
in EAX=77D43DFA.Then,the POPFD of junk,and we jamp at 77D43DFA.What API is there?Look in Olly
in Search for all module names and this API is TransLateMessage.So,in ImpRec,invalidate the thunk and
double click on it,and select from user32.dll the TransLateMessage API.Now again show invalid.Good,we
reduced invalids to one.As u can see,by this way we can fix all the invalid thunks.Do the same thing
for every invalid then.But there is one thunk that has not a Push eax-retn that jamps at an API.This
is thunk 468AB3.Well,in Olly go at 468AB3.U see this:
00468AB3 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+4]
00468AB7 85C0 TEST EAX,EAX
00468AB9 7D 1D JGE SHORT sdprotec.00468AD8
00468ABB 83F8 F5 CMP EAX,-0B
00468ABE 7E 18 JLE SHORT sdprotec.00468AD8
00468AC0 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+10]
00468AC4 8B5424 0C MOV EDX,DWORD PTR SS:[ESP+C]
00468AC8 51 PUSH ECX
00468AC9 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
00468ACD 52 PUSH EDX
00468ACE 51 PUSH ECX
00468ACF 50 PUSH EAX
00468AD0 E8 A1F7FFFF CALL sdprotec.00468276
00468AD5 C2 1000 RETN 10
00468AD8 8B5424 10 MOV EDX,DWORD PTR SS:[ESP+10]
00468ADC 8B4C24 0C MOV ECX,DWORD PTR SS:[ESP+C]
00468AE0 52 PUSH EDX
00468AE1 8B5424 0C MOV EDX,DWORD PTR SS:[ESP+C]
00468AE5 51 PUSH ECX
00468AE6 52 PUSH EDX
00468AE7 50 PUSH EAX
00468AE8 E8 BD3F0000 CALL sdprotec.0046CAAA
00468AED C2 1000 RETN 10
The call at 00468AE8 goes to the API.All till here is junk code.So in Olly go now at
0046CAAA and we are HeRe:
0046CAAA E8 01000000 CALL sdprotec.0046CAB0
0046CAAF FF58 05 CALL FAR FWORD PTR DS:[EAX+5] ; Far call
0046CAB2 C9 LEAVE
0046CAB3 0A00 OR AL,BYTE PTR DS:[EAX]
0046CAB5 008B 008038CC ADD BYTE PTR DS:[EBX+CC388000],CL
0046CABB 74 0A JE SHORT sdprotec.0046CAC7
0046CABD 50 PUSH EAX
0046CABE C3 RETN
It jamps at 0046CAB0.We cannot see the opcode of that,because it is mixed.So just go at
0046CAB0 and u see that:
0046CAB0 58 POP EAX
0046CAB1 05 C90A0000 ADD EAX,0AC9
0046CAB6 8B00 MOV EAX,DWORD PTR DS:[EAX]
0046CAB8 8038 CC CMP BYTE PTR DS:[EAX],0CC
0046CABB 74 0A JE SHORT sdprotec.0046CAC7
0046CABD 50 PUSH EAX
0046CABE C3 RETN
Well at 0046CABE jamps at the good API we are looking for.So,at 0046CAB0, EAX=0046CAAF
because of the call at 0046CAAA (so stack has return address 0046CAAA ),then adds
the value of 0AC9 so eax is now 0046D578.Now at [EAX] is the API address to fix this thunk.A
small trick that seeks for a software breakpoint there and then a PUSH EAX-RETN and we jamp there.
What is in [EAX]?Well,go at 0046D578 and U see that:
0046D578 76 64 JBE SHORT sdprotec.0046D5DE
0046D57A D6 SALC
0046D57B ^77 E3 JA SHORT sdprotec.0046D560
0046D57D A6 CMPS BYTE PTR DS:[ESI],BYTE PTR ES:[EDI]
0046D57E D4 77 AAM 77
We are interested for the first four bytes in reverse,which give us the address of
77D66476h.I look in Olly,and this is API MessageBoxA.Then,I give this name to out final
invalid API thunk.Now press show invalid,no invalids.Fix dump now.
Run the fixed exe and...Yeeeaaahh!!!Last version of the so called SDprotector defeated!!!
Well,this was a hard protector.Took me at least 6 hours,including writing this tutor.This
is one contribution to all of U,that are really interested to see how other people think,
including the makers of this packer...
-
Hands on tutorial for inline patching packed targets.
Target: Zoom Player Professional (inmatrix.com)
Tools used:
OllyDbg 1.09d
PE iDentifier v0.91
Note. A product version (as the product itself) doesn't matter, but it's important that all worked and you understand what you do. The second thing that you have to keep in mind is what all the addresses, values and numbers, which corresponds to a disassembled code are represented in hexadimal system, the rest numbers are decimal.
Author: Wizard
Date: 16.04.2004
Difficulty: beginner
Origin: Don't wish it were easier, wish you were better. Michelangelo
Introduction
Zoom Player Professional is one of the most advanced Media Players and DVD Front-End on the PC today, Designed to be simple at first glance while being remarkably dynamic and flexible when used to its full potential.
Zoom Player works in two modes. A Media mode which can play any file supported by DirectShow (any file which plays in MediaPlayer) and a DVD mode which uses pre-installed DirectShow DVD filters to play DVD content.
Reconnoitre
First of all, I downloaded file zp331pro.exe from the official site and then installed it. Once I've done that, I checked out the main executable (zplayer.exe) file. It was sorta packed. To be sure in that I used PEiD to detect the exact packer. PEid detected some old UPX version, maybe because it was modified a bit, but maybe it was so indeed. Anyway PEid said this: "UPX 0.89.6 - 1.02 / 1.05 - 1.24 (Delphi) stub -> Markus & Laszlo".
Now run the program to see the startup NAG.
You have to wait for 3 seconds and then press the appropriate button. Once you've pressed the button - program starts and runs fine. Check what do we have. Go to the options menu by pressing Ctrl+P or by clicking the appropriate button on the player's panel (at the bottom-right corner).
Click information bar in the left column. We see the strings: "Registered to: This copy is unregistered". You may also notice the register button at the bottom of the form. If you press it, it will bring you to the internet register page. That means, if the program hasn't a password/name dialog it should use some other registration way.
Usually it's a registration file (keyfile). Why? Simple, why does it need inet for? Of course to register you as a legal user for money. After that the registered user will receive some file with key and he'll just put it into the program's folder (usually in the root). It's 99% of cases. Nobody (I mean "any" programs' authors) won't do such tricks as hidden password dialog somewhere by pressing the magic keys combinations or something else even more tricky. That's because usual users don't need a haemorrhoids in their ass, which they couldn't heal. Other words, end-users need simple registration procedure. Of course, it's only my subjective point of view and I can be wrong, but I just confide in my intuition and rely on my experience.
Get a spade and dig
Fire up Olly, press F3 and open file zplayer.exe from your ZoomPlayer directory (e.g. "C:Program FilesZoom Player"). You'll see this dialog, telling that the content (of our executable) could be compressed with some packer.
Press "Yes" button to start analyzing the code. Once it's done you will be directly at UPX's entry point (00683ED0).
00683ED0 : 60 PUSHAD ; <= you're here
00683ED1 : BE00C05C00 MOV ESI,zplayer.005CC000
00683ED6 : 8DBE0050E3FF LEA EDI,DWORD PTR DS:[ESI+FFE35000]
00683EDC : C787C4B01C00 MOV DWORD PTR DS:[EDI+1CB0C4],80067C41
00683EE6 : 57 PUSH EDI
00683EE7 : 83CDFF OR EBP,FFFFFFFF
00683EEA : EB0E JMP SHORT zplayer.00683EFA
... skipped ...
All we need now is to find Real (Original) Entry Point (REP/OEP). You might ask: how to do that? Well, it depends on a cracker, his experience and intuition. Each man will do different. As we know that the program is packed with UPX we can simply scroll down with PageDown key. Personally I pressed it 8 times until I came to the right place. There's also the most simplest method to get there. Press Ctrl+F to start find command dialog and type in it "POPAD", but drop the flag "Entire block", press "Find" button to search the instruction... We're right at the place.
... skipped ...
0068402B : 55 PUSH EBP
0068402C : FF96 7CA62800 CALL DWORD PTR DS:[ESI+28A67C]
00684032 : 09C0 OR EAX,EAX
00684034 : 74 07 JE SHORT zplayer.0068403D
00684036 : 8903 MOV DWORD PTR DS:[EBX],EAX
00684038 : 83C3 04 ADD EBX,4
0068403B : EB D8 JMP SHORT zplayer.00684015
0068403D : FF96 80A62800 CALL DWORD PTR DS:[ESI+28A680]
00684043 : 61 POPAD ; <= the place we've found
00684044 : E9 9774F4FF JMP zplayer.005CB4E0 ; jump to the REP
00684049 : 00 DB 00
0068404A : 00 DB 00
0068404B : 00 DB 00
We searched for "POPAD" (61) instruction, because the program is packed with UPX. I do the same, when unpacking ASPack, but there I need to search a few more times, but for UPX just once (usually), or more times (seldom).
Note. Of course everything could be much harder sometimes, but if it's a standard of UPX (even a little bit modified) you will always find out. Besides, you can try to find the REP (OEP) with automatic tracing, but this way sometimes (very seldom) can be wrong.
Put a breakpoint with F2 at address 00684044. Press F9 to run the proggy. Ok, we're right at the place. Now press F7 or F8 (it doesn't matter now, 'cause it's just a jump) once to get to the REP.
The sneaky arctic fox
We're at the Real Entry Point now.
005CB4E0 : 55 PUSH EBP ; <= the REP itself
005CB4E1 : 8BEC MOV EBP,ESP
005CB4E3 : B9 07000000 MOV ECX,7
005CB4E8 : 6A 00 PUSH 0
005CB4EA : 6A 00 PUSH 0
005CB4EC : 49 DEC ECX
Now, let's remember what inscriptions have we seen at the NAG. We will need only some phrase. Let it be "This is a Trial". We have to search these words in dump. To do this, do a right click at the address we're now, and then select "Follow in Dump/Selection".
Go to the dump window. Its address should be now equal to the REP (005CB4E0). We did it to set the cursor in dump window to one of memory cells of the executable we're cracking. This's very important, 'cause otherwise we won't find our string. That's because such search doesn't cover all the windows memory and you have to select the memory range in which you will search by yourself (just like in SoftIce).
Note. We have set the cursor the that address, because we're now right in the unpacked code. Thus, it has another memory address than UPX entry point. But as we're gonna search for a NAG's string, we must set the cursor to the appropriate memory range. That's because we won't find the string we wanna search in the packed part of the executable. We can only find it in the unpacked memory range.
Press Ctrl+B in dump to start binary search dialog. Type "this is a trial" in ASCII field, set the flag "Entire block" (if you won't set this flag, the search will only go down from the position your cursor has now, but with it the search will cover all the executable related memory). Press "OK" and wait a bit. No luck, mate! Don't worry, just try to search the same string as Unicode. Type the same string, but in UNICODE field. Trying... Success! Personally I found that string at address 00541790.
Note. Never set "Case sensitive" flag, when searching for a string in the memory.
In dump select only the first byte of the string we've found. We select only one byte, because it's always enough to catch a string treatment (if the last is present). Now it should be marked with grey color. Right click on this (selected) byte, then go up through the menu and choose "Breakpoint/Memory, on access".
Now just press F9 to let the program run and debugger will catch the moment, when the string (from NAG) will be read. You will land somewhere in kernel32 module.
77E7AB08 : 0FB702 MOVZX EAX,WORD PTR DS:[EDX] ; <= you're here
77E7AB0B : 8A0418 MOV AL,BYTE PTR DS:[EAX+EBX]
77E7AB0E : 42 INC EDX
... skipped ...
Remove the breakpoint we've set before. To do this, right click at the address you're now in code window (or in dump window) and select "Breakpoint/Remove memory breakpoint" from the appeared pop-up menu.
Once you've done that go to address 77E7AB26 and press F4 to trace right until the command we need. That's because we don't need to run in the cycle below like a squirrel in a wheel.
... skipped ...
77E7AB26 : C2 1800 RETN 18 ; trace out of here
77E7AB29 : 2BF7 SUB ESI,EDI
77E7AB2B : 8975FC MOV DWORD PTR SS:[EBP-4],ESI
77E7AB2E : E901FEFFFF JMP kernel32.77E7A934
After you will out of this call you have to trace with F8 for a long time until you will see the NAG. Yes, that screen we've seen before. We're doing all that stuff, because if the program treats to the trial string (before the dialog), that means the NAG will appear soon.
You maybe wondering, why to do all that tricky stuff, if we can just set a breakpoint to some API call. Well, it's partly correct. First, we don't know on function to set that breakpoint, because it looks like that the program was written in Delphi. We can conclude that because of many unnecessary calls (too deep recursion). Secondly, we just don't have the import table. You may try to Press Ctrl+N to check the names. You will see only kernel names (when you'll be in the range 77E7...) and only the UPX functions (when you'll be in the range 404E..), but there're no real (unpacked) executable names, which can observed. Of course if we were cracking with SoftIce, we would try to set some breakpoint, using our intuition. But for you to say, if this's a Delphi composed proggy then standard API functions won't work, 'cause Delphi uses its own drawing style. You may only try some non-standard functions, like DestroyWindow, CreateWindow or others. All those things you can do with SoftIce, but let's get back to Olly.
Note. I have to warn you. Don't try to make your life easier by pressing F9 to reach the dialog. That won't help, because in such case program just runs. I can give you a hint how to trace "without the brains". Just after you will be out of the call at address 77E7AB26, trace with F8 (yes, just hold it) till you see the NAG. Olly will stop the cursor at the needed address for you.
Olly stopped at address 0057ACA7. Now click "Zoom Player" process in your taskbar to see the NAG. Once you'll see it wait for 3 seconds then press appropriate button to continue tracing.
After the "accident"
Ok, after you pressed the appropriate button Olly took the control again.
0057ACA5 : 8B00 MOV EAX,DWORD PTR DS:[EAX]
0057ACA7 : 8B10 MOV EDX,DWORD PTR DS:[EAX]
0057ACA9 : FF92 EC000000 CALL DWORD PTR DS:[EDX+EC] ; the NAG itself
0057ACAF : 837D DC00 CMP DWORD PTR SS:[EBP-24],0 ; <= you should be here
0057ACB3 : 0F84FA000000 JE zplayer.0057ADB3
0057ACB9 : B201 MOV DL,1
Take a look around. If we're here (at 0057ACAF) then it must be a way to get here. Scroll a bit up to check, if is there something interesting.
0057AC19 : E8 E2460100 CALL zplayer.0058F300
0057AC1E : 803D90315D0000 CMP BYTE PTR DS:[5D3190],0 ; if (@[5D3190]==0) badboy
0057AC25 : 0F8584000000 JNZ zplayer.0057ACAF ; jumps if registered
0057AC2B : 807DC3 01 CMP BYTE PTR SS:[EBP-3D],1
0057AC2F : 7555 JNZ SHORT zplayer.0057AC86
Do you see what I see? At address 0057AC1E we see an interesting compare. It looks like after that compare the conditional jump at address 0057AC25 won't jump, because we're not registered users. Other words, that jump "should" direct the program over the NAG, right at address where we're now (0057ACAF). That also means that the memory cell with address [5D3190] contains the registration status (0 ain't registered, > 0 is registered).
All we need to know now is when the program changes the status to unregistered. Other words, when it will write zero value in that memory cell.
A simple majority
Press Ctrl+F2 to terminate the program. We need to start the program again to catch a moment, when the cell [5D3190] will be written with 0 (unregistered status). But if we had seen the NAG already we won't catch that moment. That's why we have to start it over. So, run the program again. You will break before the REP at our first breakpoint (at 00684044). Press F8 once. Now we're at the start of the real (unpacked code) again at 005CB4E0.
Now go to dump window. Press Ctrl+G, type 5D3190 and press "OK" to go for it. You will be at the right place.
Do the same as we do above (when catching a treatment to the trial string). Other words, first, right click the selected byte (at 5D3190) in dump window. After that select "Breakpoint/Memory, on write". We need only to know when the program will write zero in the memory cell. We already know, where it will read it. Besides, we don't need the last thing at all, because if we patch the memory cell at the right place once (change unregistered status to registered) we'll get registered program without caring about where it will read that memory cell next time to avoid the NAG (maybe also enable disabled/hidden features, whatever).
Let the program run with F9... Bang! Olly broke-up here.
0057A28D : C645C300 MOV BYTE PTR SS:[EBP-3D],0
0057A291 : C60590315D0000 MOV BYTE PTR DS:[5D3190],0 ; <= Olly broke-up here
0057A298 : 8D854CFDFFFF LEA EAX,DWORD PTR SS:[EBP-2B4]
0057A29E : 8B15 48275D00 MOV EDX,DWORD PTR DS:[5D2748] ; points to "zplayer.regkey"
Aha, let's remember this place as the first place we've met unregistered state saving. By the way, if you'll trace a bit down you'll see that the program tries to load file "zplayer.regkey" and then to check it for validity. But we're keep going, 'cause we don't care about that key much, our goal is to register program without finding valid serials/keys, etc., but with reversing the unregistered state to registered. That's why keep on going by pressing F9 to see next place (if the program has it).
Trying... Program ran and NAG has appeared. Ok, that means here's a classic algorithm. First, program moves to a cell unregistered status, and then it tries to load a license file/license ini-file/license key from registry (it doesn't matter, 'cause these are only the methods). Secondly, if it fails to load the license key or it's not valid then it just runs as usual, shows NAG, all pay-features are disabled, etc. If the key is valid (after checking it) the program will definitely move the registered status (in our case something > 0 [1..FF], because of that JNZ, remember) and it will be registered.
Note. Everything can be not so easy sometimes. Register state cell could be initialized with unregistered status already (yes, during the compilation). Or there could be a few such memory cells. Besides, there could be not such a simple construction like ours:
CMP BYTE PTR [RegisteredState],0
JNZ GOOD_BOY
But there could be some other compare tricks like this:
MOV EAX,[RegisteredState]
PUSH EAX
... other commands ...
XCHG DWORD PTR [EBP+A],EDX
CMP EDX,00BAD000
JZ BAD_BOY
Or something else even more perky. Everything depends on an author's fantasy. But it doesn't mean that you can't catch it. It only means that everything ain't so simple.
Now we know that we have standard (classic) scheme, which means registered state memory cell can be written only twice (next situations only as examples):
once during the compilation (unregistered/registered state) and then during the program runs (if the key is invalid/valid);
only during the program runs, first, before the key-check procedure and after that if the key is valid/invalid.
Therefore, we just need to replace one byte in command MOV BYTE PTR DS:[5D3190],0 to MOV BYTE PTR DS:[5D3190],1 at address 0057A291.
We don't know exactly what value it should be, but if there was a JNZ instruction, then we suggest that it should be ANY value bigger than zero in byte interval (01..FF).
By knowing all the above, all we've left is to try will it work or not. It's simple to test. Restart the program again. Press F9. It should break at REP once more. Press F7/F8 to get at REP. We're at address 005CB4E0 again. Press Ctrl+G, type 0057A291 (it's the address we've found earlier) in the field, press "OK". Now, press space and type "MOV BYTE PTR DS:[5D3190],1" instead of "MOV BYTE PTR DS:[5D3190],0", press "Assemble" to confirm the changes.
As you can see only one byte is changed (from 00 to 01) at address 0057A297. Remember that fact for further patching. After that press F9 for a beta-testing. Hooray!!! It runs fine without the NAG. Besides, if you will go to the options again you won't find the "Register" button in the information bar. Nevertheless, that string still remains "This copy is unregistered". I suppose that's because the program didn't find the reg-file with code and name, but it thinks that it's registered anyway.
Ok, restart the program again (with Ctrl+F2) and get ready to patch this baby.
Making an inline patch on the fly
Run the program with F9 to stop at well-known breakpoint (at 00684044). We know what to patch and where. All we need is to find a place where to make our inline patch. To do this we have to find what ImageBase has the executable. That info we need to find some free space (a sequence of zeroes), which we will use as our patch.
To find out what ImageBase has the executable open the memory map with Alt+M shortcut. Find the line "zplayer" in owner column and "PE header" in type column.
Notice the value in the address column. It equals 00400000. That's ImageBase itself. Of course you may also click that line and scroll down in the appeared window to find out the same value (it'll have the appropriate name - Image Base). Actually we need this value only to find a free space from the beginning of the executable. Exactly from "MZ" signature (first two bytes of the program). You will always find a sequence of zeroes in any executable's header. It's a reserved space, which never used. Thus, it can be used.
We've got all the info to make a patch. Close the memory map and go to the dump window. Press Ctrl+G, type 00400000 (the value we've got earlier) and press "OK". We're at the beginning of the executable (right at the header's beginning of the unpacked executable). Scroll a bit down and look for a sequence of zeroes. Scrolling... What are we're seeing now? A little space at address 00400080. Well, you can use this space, but you rather scroll a bit down more. There should be a space from 00400300. Scrolling again... Aha! Just like I told you, there's a lot of free space at the forenamed address. Most of executables have it, and exactly from that address.
Go to the code window. Your cursor should still stand at address 00684044. Press space to change the jump from "JMP 005CB4E0" to "JMP 00400300". Other words, we just changing jump from jump to REP to our patch.
Press enter at the command you just changed, to go to 00400300. You see, it's full of zeroes. Let's create our patch now. Press space and type "MOV BYTE PTR [0057A297],1". Drop the flag "Fill with NOP's" and press "Assemble" (don't close assemble window). That's our patch!
The one thing we have to do is to direct the program to the REP. Simply, type "JMP 005CB4E0", press "Assemble" again and close the assemble window.
We're changing 00 to 01 at address [0057A297], because it's the last byte of the command. Other words, it's only the changes we've made to the command. Thus, there's no need to move to the memory the same commands (bytes), but only those, which need to be changed.
We're done for now. All you have to do is to save all modifications to another or to the same file.
The spirit of the sea
I used Zoom Player for a few weeks, but when I started it one day I saw a strange message, which says something about the integrity check. I didn't even treat it seriously and pressed "OK", but the program just exited to my surprise.
After that, I started the program again and I suddenly understood that it's a hidden protection, which wasn't detected before, because it just couldn't be detected. It seems that this somekind of simple self-check of executable. Other words, it's just a simple CRC check of the module content.
All we need to do is to eliminate this unexpected message, which stops the program from running. To do this restart the program once again (if it runs). Set a breakpoint at address 00684044, and then press F9. Olly breaks at that address. Now press F8 to step to our inline patch at 00400300. Step till you reach the REP of the program (005CB4E0).
Now do the same as we do when we were searching the NAG. Other words, do a right click at the address we're now, and then select "Follow in Dump/Selection". Press Ctrl+B, and try to search the "integrity check" as a Unicode string (don't forget to set the flag "Entire block" to cover all the memory range of the executable)... Uh, no luck! Hm, strange. Try to search as an ASCII string. Yes! I found it at 0054A444. Look a bit above, you will see the beginning of the string ("Zoom Player Professional").
I think that we didn't find it as Unicode, 'cause probably the author made it to save the space. Yes, that's right. He made a simple string ("Zoom Player Professional") to concatenate it with other strings. That's it. That's why we didn't find it as Unicode string. Of course it's only my theory, so don't treat it as a dogma.
Now set a memory breakpoint on access to the first byte of the string we've found. As you know we don't need, just like with the NAG string we just need to catch a treatment to that string. Therefore, select only one byte at address 0054A444, right click on this (selected) byte, then go up through the menu and choose "Breakpoint/Memory, on access". Now press F9 and wait for approximately 10 seconds. When Olly will break at some address (my was 004D1E82) remove the breakpoint. To do it, right click in code or dump window, then select "Breakpoint/Remove memory breakpoint" from the appeared pop-up menu. Trace with F8 a little until this place:
0057DF3C : 803D8C315D0000 CMP BYTE PTR DS:[5D318C],0 ; the reason of jump
0057DF43 : 7544 JNZ SHORT zplayer.0057DF89 ; jump over the NAG
0057DF45 : 833D64245D0000 CMP DWORD PTR DS:[5D2464],0 ; the second reason
0057DF4C : 753B JNZ SHORT zplayer.0057DF89 ; another jump
0057DF4E : 6A00 PUSH 0
0057DF50 : 8D45F4 LEA EAX,DWORD PTR SS:[EBP-C]
0057DF53 : 8B0D58245D00 MOV ECX,DWORD PTR DS:[5D2458] ; @("Zoom Player Professional")
0057DF59 : 8B1554245D00 MOV EDX,DWORD PTR DS:[5D2454] ; @("has failed integrity...")
0057DF5F : E84C71E8FF CALL zplayer.004050B0 ; it concatenates two strings
0057DF64 : 8B55F4 MOV EDX,DWORD PTR SS:[EBP-C] ; <= you will be here
0057DF67 : 8D45F8 LEA EAX,DWORD PTR SS:[EBP-8]
0057DF6A : E8C177E8FF CALL zplayer.00405730 ; converts string to Unicode
0057DF6F : 8B45F8 MOV EAX,DWORD PTR SS:[EBP-8] ; EAX points to Unicode string
0057DF72 : 668B0DC8E25700 MOV CX,WORD PTR DS:[57E2C8]
0057DF79 : 33D2 XOR EDX,EDX
0057DF7B : E884E0F2FF CALL zplayer.004AC004 ; this is that crazy message
0057DF80 : 33D2 XOR EDX,EDX
As you can see, if the content of the memory cell with address [5D318C] or with address [5D2464] is not zero, then you will see no message. That's the way we need. Ok, restart the program once again, press F9. You're at address00684044 again. Trace until the REP. Right click, select "Follow in Dump/Selection". Press Ctrl+G, in the appeared dialog type "005D318C", and press "OK". All we need now is to catch a moment, when this cell will be written with some value (probably zero, because the original [not patched] exe's CRC differs from the patched exe's CRC).
Note. We need only to know, when one of the cells we be written, because both of 'em compares with zero, and after that jump (or don't) over the message. That's why we need only one of 'em. So, choose any. I chose the first, because it's a BYTE (the second is DWORD).
To know, when the cell [5D318C] will be written with a value, right click on address 005D318C, select "Breakpoint/Memory, on write". Now just press F9. Once you've done that, you'll see something like this:
00562DF2 : BA00040000 MOV EDX,400
00562DF7 : B8DB030000 MOV EAX,3DB
00562DFC : E85B500500 CALL zplayer.005B7E5C ; returns 0 (bad CRC) || > 0 (good)
00562E01 : A28C315D00 MOV BYTE PTR DS:[5D318C],AL ; <= Olly broke-up here (EAX = 0)
00562E06 : 33C0 XOR EAX,EAX
Remember this place (00562E01) and press F9 again to see, if is there any other place before that compare at address 0057DF3C... Bams! And you see the the same message now. It means that there's only one place, where our cell [5D318C] is written with zero value. All we've left to do is to write some positive & bigger than zero value (e.g. 01) into that cell.
Improving the inline patch
First of all, you have to decide how to patch it, and then to add that patch you've decided into our inline patch. There're many way of patching it. Your first thought will probably be such. Let's change "MOV BYTE PTR DS:[5D318C],AL" (A2 8C 31 5D 00 => 5 bytes) to "MOV BYTE PTR DS:[5D318C],01" (C6 05 8C 31 5D 00 01 => 7 bytes). Well, not a bad idea, but alas it will increase the command from 5 to 7 bytes. You maybe say, so what. Nothing, but there's a command "XOR EAX,EAX" (33 C0) at address 00562E06, which's exactly two bytes long. If we'll patch in such way we'll just lose the command. As we didn't write this program, then we don't know, how the program will react without that two bytes long command.
You may patch it in other different ways, but leave it. I think the most "correct" way is to patch so. Don't forget, that we're making an inline patch, so this patch differs from a usual patch (in not packed exe). With such kind of patch you can just imagine what to patch, but don't patch it directly in exe. Because you can't patch it directly in unpacked exe, you have to wait when exe is unpacked and then patch it. That's the main sense of the inline patch.
What do we need for the final patch. Not too much, just think a bit. Imagine, that we just don't have the command "MOV BYTE PTR DS:[5D318C],AL" in the unpacked exe. Just imagine it. If so, how will the program act in such case? Right, it will always bring us the message, if of couse the second memory cell ([5D2464]) will be zero too. So, if we'll just fill that 5 byte long command with NOP's, then the program will never write the value with zero. That's what we need, but it's not quit enough. The last thing to do is to write the memory cell [5D318C] with some value bigger than zero (e.g. 01).
The actual patch will be such. Restart the program in the last time, press F9 to get to the well-known breakpoint at 00684044. Press F8 once to get to our patch at address 00400300. We're gonna to add it a bit. Press F8 again (because our first patch works fine, so leave it) to set the cursor to 00400307. Press space now and type "MOV EAX,562E01", press "Assemble". It's the first byte of the address we wanna fill with NOP's. Type "MOV DWORD PTR DS:[EAX],90909090" to fill the first 4 bytes with NOP's. Then type "MOV BYTE PTR DS:[EAX+4],90" to fill the last (the 5th) byte with NOP. After that, type "MOV BYTE PTR DS:[5D318C],1" to set a value we need instead of zero. We set one (01), because so the program will always think that it's CRC is ok. And finally, type the last command of the patch "JMP 005CB4E0", which will return the control to the unpacked program after our patch.
Yes, such patch is much bigger, but it works every time. Save the changes in the last time and run the program. It works fine.
The last thing to say
It's not so hard to find a place or a way to make an inline patch, especially for UPX. I just showed you one of ways of patching. By the way, if you don't familiar too much (or at all) with UPX inline patching, then I recommend you to read Detten's tutorial about how to do it. I recommend you his tutorial, because it's short, simple and pretty clear to start with. Besides, it contains some theory, which I didn't mention in this investigation.
As the final words of this essay, I have to say, that there're as many ways as many people to do the same as we did above. You can even use different methods of cracking, nothing to say about inline patching, because the last thing might vary from cracker's experience, his programming knowledge, intuition, and of course from his logic. That's why don't worry about anything you can't do. Just keep on going forward and you will do it one day.
-
Level : beginner
Basic CD check patching tutorial. (Target included)
Intro
In this tutorial, we are going to patch a CD check. These days, most CDs have a commercial protection. (eg cu-dilla) You can find an unwrapper for (almost) every protection. But when you unwrapped it, you still need to patch the proggy to run without the CD.
The example I'm going to patch, is a little proggy that checks if 'the matrix' CD is inserted. Get it here
TOOLS USED : WDASM, hex-editor or Ollydbg
Let's dig in :)
Where to start?
Disassemble the example program in W32Dasm.
If you search for a CD-protection, the best place to start is the API GetDriveType. So hit the import functions button, and look for it.
Now, what does this API do? Let's check in Win32.hlp :
UINT GetDriveType(
LPCTSTR lpRootPathName // address of root path
);
The return value is an unsigned INT, with following possible return values :
Value Meaning
0 The drive type cannot be determined.
1 The root directory does not exist.
2 DRIVE_REMOVABLE The drive can be removed from the drive.
3 DRIVE_FIXED The disk cannot be removed from the drive.
4 DRIVE_REMOTE The drive is a remote (network) drive.
5 DRIVE_CDROM The drive is a CD-ROM drive.
6 DRIVE_RAMDISK The drive is a RAM disk.
So, the API checks what type of drive LPCTSTR points to. For a CD-ROM the return value will be 5.
Now we are ready to check the deadlisting :)
The code
* Possible StringData Ref from Data Obj ->"A:" 0040100C 6836304000 push 00403036 ;Push pointer with address of root path
* Reference To: KERNEL32.GetDriveTypeA, Ord:00F0h
00401011 E800010000 Call 00401116
00401016 83F805 cmp eax, 00000005 ; Check if drive is CD-ROM
00401019 7411 je 0040102C ; If it is, jump to 'check CD'
0040101B 803D363040005A cmp byte ptr [00403036], 5A ;check if drive = 'Z'
00401022 7440 je 00401064 ; If it is, jump to 'CD not found'
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
00401062(U), :004010B0(U), :004010E0(U)
00401024 FE0536304000 inc byte ptr [00403036] ; Increment first byte of
; the string with root path
; so it points to the next drive
0040102A EBE0 jmp 0040100C ; Jump back to check next drive
What happens in the above code? We start to check if 'a:' is a CD-ROM. If it is no CD-rom, we increment the first byte of the root path-string.
( A: becomes B: ) We check the drive again, and so on till we find a CD-ROM drive.
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401019(C)0040102C 6A00 push 000000000040102E 6A00 push 0000000000401030 6A00 push 0000000000401032 6A00 push 0000000000401034 6A00 push 0000000000401036 6A14 push 00000014 ; Push the max number of bytes to read00401038 68AA314000 push 004031AA ; Push address that will receive Volumename
* Possible StringData Ref from Data Obj ->"A:"
0040103D 6836304000 push 00403036 ; Push pointer to root path
* Reference To: KERNEL32.GetVolumeInformationA, Ord:0162h
00401042 E8DB000000 Call 00401122 ; Get the Volume Name of the drive
00401047 51 push ecx
00401048 33C9 xor ecx, ecx ; Make ecx zero
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401060(U)
0040104A 8A8100304000 mov al, byte ptr [ecx+00403000] ; Move byte of 'THE MATRIX' to eax
00401050 3881AA314000 cmp byte ptr [ecx+004031AA], al ; Cmp volumename with 'THE MATRIX'
00401056 750A jne 00401062 ; If a byte is different, we jump to 'check next drive'
00401058 41 inc ecx ; Add 1 to counter
00401059 83F90A cmp ecx, 0000000A ; First 10 bytes equal?
0040105C 7202 jb 00401060 ; If not 10 bytes yet, go check next byte
0040105E EB1E jmp 0040107E ; jump to 'Next CD check'
The above code is quite simple, the program fetches the VolumeName of the CD-ROM drive, and checks it with a string.
To know the contents of the string at address 00403000, press the 'data hex' button in Wdasm (you will see that it is 'THE MATRIX').
If the VolumeName is 'THE MATRIX' we jump to the next CD check, otherwise we jump to 'Check next drive'.
* Possible StringData Ref from Data Obj ->"A:"
0040107E 6836304000 push 00403036
00401083 68C2314000 push 004031C2
* Reference To: KERNEL32.lstrcpyA, Ord:02DCh
00401088 E8A1000000 Call 0040112E ; Copy root path in new string
* Possible StringData Ref from Data Obj ->"Matrix.DVDivX.*"
0040108D 684A304000 push 0040304A
00401092 68C2314000 push 004031C2
* Reference To: KERNEL32.lstrcatA, Ord:02D3h
00401097 E88C000000 Call 00401128 ; Paste 'MatrixDvDivx.*' behind the root path
0040109C 6860304000 push 00403060 ; Poiter to a WIN32_FIND_DATA structure
004010A1 68C2314000 push 004031C2 ; Pointer to the string just created
* Reference To: KERNEL32.FindFirstFileA, Ord:008Ch
004010A6 E865000000 Call 00401110
004010AB 83F8FF cmp eax, FFFFFFFF ; Is the file there?
004010AE 7505 jne 004010B5 ; then jump to 'Next check'
004010B0 E96FFFFFFF jmp 00401024 ; else jump to 'Check next drive'
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 004010AE(C)
:004010B5 A3BE314000 mov dword ptr [004031BE], eax ; Store File Handle:004010BA FF35BE314000 push dword ptr [004031BE]
* Reference To: KERNEL32.FindClose, Ord:0088h
004010C0 E83B000000 Call 00401100 ; Close the searchHandle
004010C5 A180304000 mov eax, dword ptr [00403080] ; Move size member of WIN32_FIND_DAT to eax
004010CA 3B055A304000 cmp eax, dword ptr [0040305A] ; Check size with dword
004010D0 7504 jne 004010D6 ; If not equal, jump to 'Check next Drive'
004010D2 EB07 jmp 004010DB ; Else, jump to good-guy message
004010D4 EB05 jmp 004010DB
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 004010D0(C)
004010D6 E949FFFFFF jmp 00401024
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: 004010D2(U), :004010D4(U)
004010DB 6A00 push 00000000 ;Good guy Message
* Possible StringData Ref from Data Obj ->"Great!"
004010DD 6843304000 push 00403043
* Possible StringData Ref from Data Obj ->"CD Found"
004010E2 683A304000 push 0040303A
004010E7 6A00 push 00000000
* Reference To: USER32.MessageBoxA, Ord:01BBh
004010E9 E806000000 Call 004010F4
004010EE 50 push eax
* Reference To: KERNEL32.ExitProcess, Ord:0075h
004010EF E806000000 Call 004010FA ; Exit Program
In the above code, the following happens :
First the root path and a file string get pasted together. (eg. 'F:' + 'Matrix.DVDivX.*' = 'F:Matrix.DVDivX.*') Then the program checks if a file which matches the description is there. If not, we jump back to 'check next drive'. If it is there, the program checks its FileSize with the value in 0040305A. (If you press the dataHex button again, you can see that value : 2B6F2000h (mind the reversed order) = 728.702.976 bytes )
If the FileSize matches this value, the program assumes that the right CD is inserted. If not, we go to 'check next drive' again.
Patching the code
If you read all the above code, patching should be easy.
I assume that your computer has a CD-ROM drive. When the program finds your CD-ROM drive, it will check the VolumeLabel. To prevent from jumping back to 'check next drive' we nop that jump out :
00401056 750A jne 00401062 -> nop nop
750A -> 9090
Next, the program searches for the file, let's change the conditional
jump so it always jumps to the next check :
004010AE 7505 jne 004010B5 -> jmp 004010B5
7505 -> EB05
The last thing we have to patch, is the size-check :
004010D0 7504 jne 004010D6 -> nop nop
7504 -> 9090
Get all the FileOffsets of the above codelines in Wdasm (displayed at the bottom of the screen in hex), fire up your hexeditor
and change the appropriate bytes.
Run the program, now it thinks the CD is inserted ! Job done :)
The method to check the CD I used in my little matrix-proggy is very simular to how it's done in real programs. Of course, don't expected that the program you want to patch, will only check one file, or will have such clean code as my proggy :))
Good luck!
-
Level : beginner
Writing a bruter for the Ancient One Crackme.
(MASM source code for the bruter included)
TARGET: Bruteforcing Ancient One crackme by Zephyrous : here
Well, I have to send some credits to bluffer for trying to solve this bruteforcer
and giving me some help actually, hello there bud ;)
Zephy: For giving me an idea of how to bruteforce another (faster) way, thank you ;)
Detten: Hello! :-)
Ok, lets debug the application and quickly set a breakpoint on GetWindowText()
type some serial (e.g. "BoR0")
Ollydbg breaks on our first letter.
---snippet---
0040112E |. 83F8 08 CMP EAX,8
00401131 |. 75 3D JNZ SHORT ch01.00401170
00401133 |. E8 58FFFFFF CALL ch01.00401090
00401138 |. 85C0 TEST EAX,EAX
0040113A |. 0F84 08010000 JE ch01.00401248
00401140 |. E8 7BFFFFFF CALL ch01.004010C0
---snippet---
compare length of chars with 8
if not equal, jump to 00401170 which is NOT where we need to be at.
Ok, let's enter some serial that contains 8 chars (e.g. "BoR0BoR0")
Jump is not taken, COOL! :p
I didn't see anything interesting in the call that is on address 00401133,
so lets see whats in the call that is on address 00401140.
---snippet---
004010C0 /$ 68 90124000 PUSH ch01.00401290 ; /Arg1 = 00401290 ASCII "BOR0BOR0"
004010C5 |. E8 66FFFFFF CALL ch01.00401030 ; ch01.00401030
004010CA |. 35 5587BAA7 XOR EAX,A7BA8755
004010CF |. F7D8 NEG EAX
004010D1 |. 1BC0 SBB EAX,EAX
004010D3 |. 40 INC EAX
004010D4 . C3 RETN
---snippet---
Mmm, looks like this is what we needed. Enter this call for more information :-)
---snippet---
00401030 /$ 55 PUSH EBP
00401031 |. 8BEC MOV EBP,ESP
00401033 |. 56 PUSH ESI
00401034 |. 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8]
00401037 |. B8 C59D1C81 MOV EAX,811C9DC5
0040103C |. 33C9 XOR ECX,ECX
0040103E |> 33D2 /XOR EDX,EDX
00401040 |. 8A1431 |MOV DL,BYTE PTR DS:[ECX+ESI]
00401043 |. 33C2 |XOR EAX,EDX
00401045 |. 41 |INC ECX
00401046 |. 69C0 93010001 |IMUL EAX,EAX,1000193
0040104C |. 83F9 08 |CMP ECX,8
0040104F |.^72 ED JB SHORT ch01.0040103E
00401051 |. 5E POP ESI
00401052 |. 5D POP EBP
00401053 . C2 0400 RETN 4
---snippet---
As you can see by the algo, it xors every char into eax and multiplies the same with 0x1000193.
Don't forget that EAX is initialized as 0x811C9DC5.
We're back to 004010CA. Xors final eax with 0xA7BA8755, NEGs it, substract with borrow
and then increases eax. Looks pretty tough to be solved by pen & paper huh? ;)
Here's our solution:
---snippet---
.386
.model flat, stdcall
option casemap :none
include masm32/include/windows.inc
include masm32/include/user32.inc
include masm32/include/kernel32.inc
includelib masm32/lib/user32.lib
includelib masm32/lib/kernel32.lib
.DATA
MYLOOP dd 0 ;you maybe ask why a variable? our registers are not safe :(
PREFIX db "%8X", 0
BUFFER db 9 dup(0) ;buffer contained of 8 chars and one terminator
.CODE
START:
MOV EAX, 811C9DC5h ;starting code of zephy's algo
XOR ECX, ECX ;...
PUSHAD ;save all registers
INC MYLOOP ;increase loop
;convert our serial into a string
INVOKE wsprintf, ADDR BUFFER, ADDR PREFIX, MYLOOP
POPAD ;bum!
;Main loop that xors our chars with eax and then multiplies the same
LOOPHERE:
XOR EDX, EDX
MOV DL, BYTE PTR [BUFFER+ECX]
XOR EAX, EDX
INC ECX
IMUL EAX, EAX, 1000193h
CMP ECX, 8
JB LOOPHERE
;The final eax modifying
XOR EAX, 0A7BA8755h
NEG EAX
SBB EAX, EAX
INC EAX
TEST EAX, EAX ; if eax == 0
JZ START ; back to start
;if eax != 0, a valid serial has been found
invoke MessageBox, 0, 0, ADDR BUFFER, 0 ;notify the user
invoke ExitProcess, 0 ;thanks ;-)
end START
---snippet---
Should take around 20 mins or so (1.6GHz). That'd be all for now. :)
ah thanks for mention Boro :)
hope i could add a few comments to this tut as you know i was loooking for a sppedier way that eliminates the twenty minutes :)
i removed the wsprintf and coded an inline convertor which reduced the time to about 10 minutes
and i came to #win32asm on efnet to bug some of them to find some more improvements :) parabytes there suggested some improvements in the convertor like stosd instead of stosb which were marginal improvemnts then scali took an interest in the code and coded a c++ and applied pentium optimization switches and it drastically reduced the time to about one minute :) iam pasting below the c++ code that was from scali :)
#include <stdio.h>
int main(void)
{
char* table = "0123456789ABCDEF";
char key[] = "F000FFF0";
for (unsigned int a = 0; a < 16; a++)
{
key[0] = table[a];
for (unsigned int b = 0; b < 16; b++)
{
key[1] = table[b];
for (unsigned int c = 0; c < 16; c++)
{
key[2] = table[c];
for (unsigned int d = 0; d < 16; d++)
{
key[3] = table[d];
for (unsigned int e = 0; e < 16; e++)
{
key[4] = table[e];
for (unsigned int f = 0; f < 16; f++)
{
key[5] = table[f];
for (unsigned int g = 0; g < 16; g++)
{
key[6] = table[g];
for (unsigned int h = 0; h < 16; h++)
{
key[7] = table[h];
unsigned int hash = 0x811C9DC5;
for (unsigned int i = 0; i < 8; i++)
{
hash ^= (unsigned char)key[i];
hash *= 0x1000193;
}
if (hash == 0xA7BA8755)
goto end;
}
}
}
}
}
}
}
}
end:
printf( "%s\n", key );
return 0;
}
commadline for gpp
g++ -Wall -O3 bruter.c -o bruter
[bluffer@]$ ./bruter F000FFF0 Bruting time ->69sec
-
Level : beginner
Not every application uses a simple GetDlgItemText or simular API to get the input from an editbox.
This crackme by basse uses an alternate way to grab the keyboard input.
Intro
The next crackme has some interesting but unusual approaches. Curious? Read on :)
I'm going to explain how this crackme works, but you won't find a valid solution in here, if you want to find one, you'll have to brute it...
TOOLS USED : WDASM
Let's dig in :)
Where to start?
Ok, Disassemble the crackme with WDasm. We see it is not packed. And because it is written using MASM, it's pretty clear code :)
After a quick look in the string table and the import table we can notice the following :
There is no GetDlgItemTextA or GetWindowText
The author uses a hook (SetWindowsHookExA) to get the input...
If we try to enter a combination 3 times, the program crashes...ans possibly your computer too! How is that possible?
The author uses the 'foofbug' here, an opcode combination that crashes Pentium and Pentium MMX procesors. (Find more info on this subject here)
Followed by some garbage code...no wonder we crash :)
Ok, time to check out the code!
The code
; Beginning of DlgProc
0040102C 55 push ebp0040102D 8BEC mov ebp, esp0040102F 8B4508 mov eax, dword ptr [ebp+08]00401032 A30D314000 mov dword ptr [0040310D], eax00401037 817D0C11010000 cmp dword ptr [ebp+0C], 00000111 ;WM_COMMAND ?0040103E 0F85FF000000 jne 0040114300401044 8B4510 mov eax, dword ptr [ebp+10]00401047 663DB90B cmp ax, 0BB9 ; Test-button pressed ?0040104B 0F85D8000000 jne 0040112900401051 FF35FF304000 push dword ptr [004030FF] ; Push hash00401057 C705FF304000ADDED0BA mov dword ptr [004030FF], BAD0DEAD ; Restore init values00401061 C6050831400035 mov byte ptr [00403108], 35 ; Restore init values00401068 6A00 push 00000000
* Possible Reference to Dialog: MYDIALOG, CONTROL_ID:0BB8, ""
0040106A 68B80B0000 push 00000BB8
0040106F FF7508 push [ebp+08]
* Reference To: USER32.SetDlgItemTextA, Ord:0228h
00401072 E827020000 Call 0040129E ; Empty the edit box
00401077 58 pop eax ; pop hash
00401078 3DF700FB02 cmp eax, 02FB00F7 ; check hash
0040107D 7529 jne 004010A8 ; If not equal, inc counter
0040107F 6A40 push 00000040 ; Else good-guy message :)
* Possible StringData Ref from Data Obj ->"Rev"
00401081 68AA304000 push 004030AA
* Possible StringData Ref from Data Obj ->"Good job! You made it!"
00401086 6879304000 push 00403079
0040108B FF7508 push [ebp+08]
* Reference To: USER32.MessageBoxA, Ord:01BBh
0040108E E8FF010000 Call 00401292 ; Display good-guy message
* Possible StringData Ref from Data Obj ->"Success!"
00401093 6870304000 push 00403070
00401098 FF350D314000 push dword ptr [0040310D]
* Reference To: USER32.SetWindowTextA, Ord:0259h
0040109E E807020000 Call 004012AA ; Put "success" in captionbar
Ok, what do we have here? If we press the 'test' button, a hash value get's checked with 02FB00F7h, if we would like to patch, the jnz is the place :)
As said before, we do not find the code in the DlgProc that calculates the hash. There is no WM_CHAR message too... but we get a hint from the import table (SetWindowsHookExA) that this proggy uses a hook to get the input...
This is what the API reference says about SetWindowsHookExA :
The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. An application installs a hook procedure to monitor the system for certain types of events. A hook procedure can monitor events associated either with a specific thread or with all threads in the system. This function supersedes the SetWindowsHook function.
HHOOK SetWindowsHookEx(
int idHook, // type of hook to install
HOOKPROC lpfn, // address of hook procedure
HINSTANCE hMod, // handle of application instance
DWORD dwThreadId // identity of thread to install hook for
);
We find it here :
:00401143 817D0C10010000 cmp dword ptr [ebp+0C], 00000110 ; WM_INITDIALOG:0040114A 7543 jne 0040118F:0040114C C6050831400035 mov byte ptr [00403108], 35 ; init var:00401153 6A00 push 00000000 ; hook all treads :00401155 FF3509314000 push dword ptr [00403109] ; handle:0040115B 68C0114000 push 004011C0 ; HookProcedure:00401160 6A03 push 00000003 ; WH_GETMESSAGE
* Reference To: USER32.SetWindowsHookExA, Ord:025Dh
00401162 E849010000 Call 004012B0
00401167 0BC0 or eax, eax ; if Hook succeeds
00401169 7405 je 00401170
0040116B A303314000 mov dword ptr [00403103], eax ; Save hook handle
Next, the focus is set on the editbox, we are ready for input :)
What happens in the above code? Well, when the DialogBox is created the WM_INITDIALOG message is sent, so this code gets executed once at the beginning.
First the variables for the hash calculation are initialized. (BYTE ptr [403108] = 35 ) and DWORD ptr [4030FF] is already initialized at BAD0DEADh
Next, the hook is set up. It' s a hook for WH_GETMESSAGE and the hook routine starts at address 4011C0h.
Let's check out this hook routine :
004011C0 55 push ebp004011C1 8BEC mov ebp, esp004011C3 837D0800 cmp dword ptr [ebp+08], 00000000 004011C7 731A jnb 004011E3 ; If we have input, go to 'calculate hash'004011C9 FF7510 push [ebp+10] ; Else pass to next004011CC FF750C push [ebp+0C] ; hook in the chain004011CF FF7508 push [ebp+08]004011D2 FF3503314000 push dword ptr [00403103]
* Reference To: USER32.CallNextHookEx, Ord:0014h
004011D8 E89D000000 Call 0040127A
004011DD C9 leave
004011DE C20C00 ret 000C
004011E1 EB73 jmp 00401256
* Referenced by a (U)nconditional or (C)onditional Jump at Address:004011C7(C)
004011E3 837D0800 cmp dword ptr [ebp+08], 00000000 ; Check again
004011E7 756D jne 00401256
004011E9 8B5510 mov edx, dword ptr [ebp+10]
004011EC 817A0402010000 cmp dword ptr [edx+04], 00000102 ;WM_CHAR ?
004011F3 7561 jne 00401256
004011F5 8B4208 mov eax, dword ptr [edx+08] ; Move CHAR to eax
004011F8 8B4A08 mov ecx, dword ptr [edx+08] ; Move CHAR to ecx
004011FB 2A0508314000 sub al, byte ptr [00403108]
00401201 880D08314000 mov byte ptr [00403108], cl
00401207 D315FF304000 rcl dword ptr [004030FF], cl ; Start calculation
0040120D A008314000 mov al, byte ptr [00403108]
00401212 33C8 xor ecx, eax
00401214 C1E007 shl eax, 07
00401217 33C8 xor ecx, eax
00401219 C1E007 shl eax, 07
0040121C 33C8 xor ecx, eax
0040121E C1E007 shl eax, 07
00401221 33C8 xor ecx, eax
00401223 C1E003 shl eax, 03
00401226 33C8 xor ecx, eax
00401228 310DFF304000 xor dword ptr [004030FF], ecx
0040122E FF35FF304000 push dword ptr [004030FF] ; Store hash
...
The next part, displays the hash in the captionbar (I think) Although I didn't see it (in XP) :(...
0040126F C9 leave
00401270 C20C00 ret 000C
Ok, in the hook procedure all the CHARS are intercepted and the hash is further calculated every time you press a key.
If you want to know a valid serial, you have to write a bruter with the code from the hook routine. keep in mind that the hash is initialized BAD0DEADh. The hash result should be 02FB00F7h.
The second byte var, is not needed for the algo, so we can cut it out to speed up the bruting.
This is the hash calculation part out of the hook routine (a little optimized :)
mov al, byte ptr [serial+edi]<br>
rcl dword ptr [hash],al <br>
shl eax, 7<br>
xor ecx, eax<br>
shl eax, 7<br>
xor ecx ,eax<br>
shl eax, 7<br>
xor ecx, eax<br>
shl eax, 3<br>
xor ecx, eax<br>
xor dword ptr [hash], ecx<br>
inc edi </p>
The serial points to an address where you should store the combination currently bruting. edi is the counter.
Let it run till byte ptr [serial+edi] = 0
A little problem is that we can't know the keylength...if this crackme uses 10 chars and only lower cased chars, we have :
i=1
SUM (26^i) = 146.813.779.479.510 possibilities.
i 10
To put that in perspective, if you process 1 milion serials each second, you would still need 4.6 years :(
And then the serial could be longer, and we don't even know the range (1-9, a-z,A-Z,...) So feel free to brute it :)
Although the algorithm probably allows a lot of valid keys :)
=> Note from the author if you want to start bruting
It's only numbers, 0 - 9, and the length is 10.
If you get bored, try *******967 (the * you have to brute :-)
Basse
Now you should be able to brute it in little time...
-
Level : newbie
This is my tutorial about ASProtect. Well, actually it is my attempt to understand what ASProtect doing to packed programs. I wrote this tut maybe couple days ago and yet today I would change some parts if I had a time.
There is an 'interactive' Ollydbg plugin tutorial in the attachment, or you can just read the regular tutorial below.
Enjoy and post questions, commmets, corrections, ideas ..... I need all of them ;-)
__..--~~""~~--.._( Unpacking ASProtect )_..--~~""~~--..__
Lately I had some succses with unpacking ASProtect (half)manually so I have decided to share this experience with others (beginners).
Like many other noobs, I have read dozen of tutorials which describe unpacking ASProtect, but blindly following that tutorials I couldn't unpack anything by myself. Those "...just press Ctrl+F9 now and you will emediatley land on OEP..." or "...enter tc>900000 and hit run..." have take me usually to some exception that Olly couldn't pass. So I started to digging and pasing through ASPr code just to tray figure something myself. Well, I had some sucsess ;-)
1. We gonna need something for this tutor:
- target: Easy Video Capture v1.30 (~600kb in size) , video-capture.info
- OllyDbg 1.10 (try to have it's plugins, esp. CommandBar and Dump)
- LordPE
- ImportREC
2. IsDebuggerPresent check:
First stepp is of course, to install our app and then load it to Olly.
ASProtect have option to detect debugger through IsDebuggerPresent API function. Some protected programs use that option and some doesn't which depends of app autor. I usually just run first time app in Olly just to see will I get that message "Debugger detected...". This option is just pain in the ass because we must spend some time to click on IsDebuggerPresent plugin in Olly if program uses this check and that is quite boring since usually I must restart target couple times. Buah, our target is using that check. Ok, find someware that plugin for Olly which fools this check because that is fastest way to pass it, but I'm gonna also describe manually how to do it in next part. And next part is:
3. Finding OEP:
Finding OEP is not some hard thinking proces, it's acctually well know Shift+F9 + counting exceptions and then watching jumps and lots of tracing through code.
So, go to Olly Debugging Options and uncheck all that exceptions except those memory violations in KERNEL32.DLL.
Also we must fool debugger check. In CommandLine plugin type "bp IsDebuggerPresent" and press ENTER; or if you have not that plugin right click on CPU window in Olly, select "Search for" ... "Names in all modules", find Eport KERNEL32.IsDebuggerPresent, right click and "Toggle brakpoint". Now just press Shift+F9 and after 15 times you should land in kernel:
77E7276B > 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
77E72771 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
77E72774 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2] <---Follow this address in dump
77E72778 C3 RETN
On first line is our breakpoint, remove it and go with F8 to 3-th line and follow that addres in dump:
7FFDF002 01 00 FF FF FF FF 00 00 40 00 A0 1E 24 00 00 00 _.ÿÿÿÿ..@. _$...
Maybe in your case this will look diferent, but first byte must be 01. That byte is confirmation that debuger is present and you need set it on zero. Rigt click on that 01 byte in dump and chose "Bynary - Fill with 00's". It should look like this now:
7FFDF002 00 00 FF FF FF FF 00 00 40 00 A0 1E 24 00 00 00 ..ÿÿÿÿ..@. _$...
You have just fooled debugger check. Probably all this you know from before.
Now we must continue with Shift+F9. I press first until bp 15 times and after 16 and then program has started. So I have pressed it 31 times. Now we need again but this time press it only 30 times. Also, you culd not even to count untill you triger bp in kernel and then count only last 16-1. You should land now here:
00B339EC 3100 XOR DWORD PTR DS:[EAX],EAX <---- Your here on the exception!
00B339EE 64:8F05 00000000 POP DWORD PTR FS:[0]
00B339F5 58 POP EAX <---- Place bp here!
00B339F6 833D B07EB300 00 CMP DWORD PTR DS:[B37EB0],0
00B339FD 74 14 JE SHORT 00B33A13
00B339FF 6A 0C PUSH 0C
00B33A01 B9 B07EB300 MOV ECX,0B37EB0
00B33A06 8D45 F8 LEA EAX,DWORD PTR SS:[EBP-8]
00B33A09 BA 04000000 MOV EDX,4
00B33A0E E8 2DD1FFFF CALL 00B30B40
00B33A13 FF75 FC PUSH DWORD PTR SS:[EBP-4]
00B33A16 FF75 F8 PUSH DWORD PTR SS:[EBP-8]
00B33A19 8B45 F4 MOV EAX,DWORD PTR SS:[EBP-C]
00B33A1C 8338 00 CMP DWORD PTR DS:[EAX],0
00B33A1F 74 02 JE SHORT 00B33A23
00B33A21 FF30 PUSH DWORD PTR DS:[EAX]
00B33A23 FF75 F0 PUSH DWORD PTR SS:[EBP-10]
00B33A26 FF75 EC PUSH DWORD PTR SS:[EBP-14]
00B33A29 C3 RETN <--- Or place bp here!
Now, I'm gonna tell you something from my experience. I have notice mostly two variations of ASProtect: first group have this last pice of code that you see above exactly the same while second have differnt with some calls and jumps. At this first group it is a very easy to find OEP, while for second one it needs to be more patient. Also, there are no anti-debug tricks after this period. To continue with our target, place bp on one of two places that I have marked on abowe code and run app. We have reall boring job now - we must trace with F7 to see what code is doing. It will circle around and around with conditional jumps. If you notice that behaviouring, place bp under couple such jumps and run program to save time, for example:
After you are on that last RETN, continue with F7. Look where jumps are throwing you and you'll see that loot of jumps has no purpose at all - they throw you at the same place where you going to pass when they don't execute. After some time you will find yourself here:
00B45ED4 81EA 314F0F4A SUB EDX,4A0F4F31
00B45EDA 0F81 1F000000 JNO 00B45EFF
00B45EE0 E8 08000000 CALL 00B45EED <------------------------ Place bp here!
00B45EE5 F0:69EE 8F1C25FA LOCK IMUL EBP,ESI,FA251C8F
00B45EEC AB STOS DWORD PTR ES:[EDI]
00B45EED E8 0B000000 CALL 00B45EFD
00B45EF2 87B4DD 522320D9 XCHG DWORD PTR SS:[EBP+EBX*8+D9202352],E>
00B45EF9 9E SAHF
00B45EFA 7F 4C JG SHORT 00B45F48
00B45EFC 95 XCHG EAX,EBP
00B45EFD 59 POP ECX
00B45EFE 5E POP ESI
00B45EFF 81C2 2D4F0F4A ADD EDX,4A0F4F2D
00B45F05 81EB 4D667D59 SUB EBX,597D664D
00B45F0B 81FA 54EBFFFF CMP EDX,-14AC
00B45F11 ^0F85 5BFFFFFF JNZ 00B45E72
00B45F17 E8 0E000000 CALL 00B45F2A <----------------------- Place bp here!
00B45F1C 4E DEC ESI
00B45F1D 6F OUTS DX,DWORD PTR ES:[EDI] 00B45F1E 7C 05 JL SHORT 00B45F25
You just keep on tracing with F7 and you'll see that these two jumps just spining you around. So place bp under them on those calls and run app. You will land on that second call and some code below will change. Continue with F7 and you'll find one more jump that is doing similar thing:
00B44E14 ^75 E9 JNZ SHORT 00B44DFF
00B44E16 5E POP ESI <--------------- Place bp here and run app!
00B44E17 5B POP EBX
00B44E18 5D POP EBP
00B44E19 C2 0800 RETN 8
00B44E1C 0102 ADD DWORD PTR DS:[EDX],EAX
00B44E1E C3 RETN
Place bp on marked place and run target. Continue with F7 and you'll land on POPAD opcode after which one there are lots of JMP's. This pice is standard in this version of ASProtect and this is place where ASProtect is stealing bytes (but later about that). Pass this junk of code with F7 to the last RETN at:
00B45000 68 C3F44000 PUSH 40F4C3
00B45005 68 DB4DB400 PUSH 0B44DDB
00B4500A C3 RETN <--------- Until this RETN
Then execute that RETN:
00B44DDB EB 01 JMP SHORT 00B44DDE
00B44DDD 9A 51579CFC BF1F CALL FAR 1FBF:FC9C5751
00B44DE4 4E DEC ESI
00B44DE5 B4 00 MOV AH,0
00B44DE7 B9 5E140000 MOV ECX,145E
00B44DEC F3:AA REP STOS BYTE PTR ES:[EDI]
00B44DEE 9D POPFD
00B44DEF 5F POP EDI
00B44DF0 59 POP ECX
00B44DF1 C3 RETN <-------------------- Put bp here!
Now you can trace further with F7 (if you want to see what is doing here - nothing special) or put bp on this new RETN and start target. You will see that lot of code has changed at the bottom of the Olly window, but notice where this last RETN is returning you! It returns you to VideoCap.0040F4C3. Execute that RETN with F7 and youl land here:
0040F4C3 FF15 30174100 CALL DWORD PTR DS:[411730] ; msvcrt.__set_app_type
0040F4C9 59 POP ECX
0040F4CA 830D F4AE4100 FF OR DWORD PTR DS:[41AEF4],FFFFFFFF
0040F4D1 830D F8AE4100 FF OR DWORD PTR DS:[41AEF8],FFFFFFFF
0040F4D8 FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode
....
....
....
That oppcode at 0040F4C3 is our false OEP!
Press Ctrl+A to analize code and scroll a litle bit up. Those zeroes are stolen bytes/opcodes/instructions and real OEP starts at first zero byte at 0040F496!
0040F490 $-FF25 50174100 JMP DWORD PTR DS:[411750] ; msvcrt._ftol
0040F496 00 DB 00 <-------------------------This shuld be real OEP.
0040F497 00 DB 00
0040F498 00 DB 00
...
[cut]
...
...
0040F4C0 00 DB 00
0040F4C1 00 DB 00
0040F4C2 . 00FF ADD BH,BH
0040F4C4 . 15 30174100 ADC EAX,VideoCap.00411730
0040F4C9 . 59 POP ECX
0040F4CA . 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
0040F4D1 . 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
0040F4D8 . FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode
Now you have founded false and right OEP! False is at the 0040F4C3 and real is at the 0040F496 address! Remember that two addresses!!! What is false and what purpose of false OEP I will explain in next part:
4. False and right OEP, stolen bytes:
What is this thing with false-real OEP and stolen bytes? From where these bytes are stolen and why? This used to confusing me in the beining reading others tutorials.
The answer is very simple:
You know that every program has it's beginning, first line of code to process. That first line is OEP. You also know that packers unpacking program in memory and then execute it right from that OEP. ASProtect is doing that litle diferent. ASProtect copyes first couple instructiones from packed program to itself code and substitute those in program with zeros. After unpacking program in memory it executes those couple instructions (that should be in original program) within it self code and then continue to run original program from that address after that zeroes. Now we gonna find stolen bytes. These bytes can be little hard to determine because ASProtect mix them with some junk code just to confuse reverser. Only in one version of ASProtect I have fand those stolen bytes in original form.
Also you must know that usualy programs compiled with same compiler have same or similar first instructions. That can help you because if you open some other nonpacked program in Olly which is compiled with same compiler you can see how OEP shuld look like and you'll know when you see stolen bytes somewhere in ASProtect code.
Now we gonna find that bytes. At first look, this program look like it's compiled in some MS Visual C++. Open couple nonpacked programs in Olly and scan them with PEiD and you will easy notice diference between Delphy, Borlland's C++, MS Visual C++ ... In most cases your target will be compiled in some MSV C++.
So now restart target in Olly, pass that exceptions and debugger check again and stop at POPAD opcode after which you can notice lot of jumps:
00B45134 61 POPAD
00B45135 F2: PREFIX REPNE: ; Superfluous prefix
00B45136 EB 01 JMP SHORT 00B45139
00B45138 F0:EB 01 LOCK JMP SHORT 00B4513C ; LOCK prefix is not allowed
00B4513B 698D 6424D683 C4>IMUL ECX,DWORD PTR SS:[EBP+83D62464],EBF>
00B45145 02CD ADD CL,CH
00B45147 2089 442400F2 AND BYTE PTR DS:[ECX+F2002444],CL
00B4514D EB 01 JMP SHORT 00B45150
00B4514F -E9 C1E8E136 JMP 37963A15
00B45154 EB 01 JMP SHORT 00B45157
00B45156 -0F83 E0413EEB JNB EBF2933C
00B4515C 02CD ADD CL,CH
00B4515E 2068 64 AND BYTE PTR DS:[EAX+64],CH
00B45161 51 PUSH ECX
00B45162 B4 00 MOV AH,0
00B45164 58 POP EAX
00B45165 FF50 09 CALL DWORD PTR DS:[EAX+9]
00B45168 0FF39A F29A7751 PSLLQ MM3,QWORD PTR DS:[EDX+51779AF2]
00B4516F B4 00 MOV AH,0
00B45171 F0:E8 F2F269F3 LOCK CALL F41E4469 ; LOCK prefix is not allowed
00B45177 58 POP EAX
00B45178 EB 01 JMP SHORT 00B4517B
This place contains our stolen bytes but also lot of junk code. Trace through with F7 and you will keep finding byte by byte. All that jumps and weird opcodes are just junk and I will show here just real stolen bytes:
00B451B7 55 PUSH EBP This is first stolen byte and it belongs on OEP.
00B451C0 8BEC MOV EBP,ESP This is the next stolen part.
00B451C2 6A FF PUSH -1
00B451C4 68 D8354100 PUSH 4135D8
00B451C9 68 1CF64000 PUSH 40F61C
00B451CE 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
00B463F9 50 PUSH EAX This one too.
00B45206 64:8925 00000000 MOV DWORD PTR FS:[0],ESP Next part.
00B4520D 83EC 68 SUB ESP,68
00B45239 53 PUSH EBX
00B4526B 56 PUSH ESI
00B4529D 57 PUSH EDI
00B452A6 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP This is last stolen pice.
00B452A9 33DB XOR EBX,EBX
00B452AB 895D FC MOV DWORD PTR SS:[EBP-4],EBX
00B452AE 6A 02 PUSH 2
Of course you will need some experience to recognize real bytes but that will come after few unpacked targets. This code above is all that is stolen. Trace further until you reach here:
00B452B8 68 C3F44000 PUSH 40F4C3
00B452BD 68 9350B400 PUSH 0B45093
00B452C2 C3 RETN
Look at that PUSH 40F4C3 opcode. Do you remmber address of the false OEP? If not, than scroll up in this tutorial where I told you to remember OEP's addresses! Yep, that opcode pushes false OEP address! Change that address to 0040F496 which is address of the real OEP by pressing SPACE and changing opcode to PUSH 0040F496. It should look now:
00B452B8 68 96F44000 PUSH 40F496
00B452BD 68 9350B400 PUSH 0B45093
00B452C2 C3 RETN
Now trace further:
00B45093 EB 01 JMP SHORT 00B45096
00B45095 9A 51579CFC BFD7 CALL FAR D7BF:FC9C5751 ; Far call
00B4509C 50 PUSH EAX
00B4509D B4 00 MOV AH,0
00B4509F B9 5E140000 MOV ECX,145E
00B450A4 F3:AA REP STOS BYTE PTR ES:[EDI]
00B450A6 9D POPFD
00B450A7 5F POP EDI
00B450A8 59 POP ECX
00B450A9 C3 RETN <------------- Put bp!
Place bp on this last RETN, run target, remove bp, execute RETN with F7 and you'll land on real OEP address right on that first zeros. Pres Ctrl+A and it suld look like this:
0040F496 00 DB 00
0040F497 00 DB 00
0040F498 00 DB 00
0040F499 00 DB 00
0040F49A 00 DB 00
0040F49B 00 DB 00
...
[cut]
...
0040F4BD 00 DB 00
0040F4BE 00 DB 00
0040F4BF 00 DB 00
0040F4C0 00 DB 00
0040F4C1 00 DB 00
0040F4C2 . 00FF ADD BH,BH
0040F4C4 . 15 30174100 ADC EAX,VideoCap.00411730
0040F4C9 . 59 POP ECX
0040F4CA . 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
0040F4D1 . 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
0040F4D8 . FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode
0040F4DE . 8B0D E8AE4100 MOV ECX,DWORD PTR DS:[41AEE8]
0040F4E4 . 8908 MOV DWORD PTR DS:[EAX],ECX
0040F4E6 . FF15 84174100 CALL DWORD PTR DS:[411784] ; msvcrt.__p__commode
Now you need to put in all that stolen bytes that we have founded before. Do it by asembly or by binary editing. At the end it should look like this (blue was stolen):
0040F496 /. 55 PUSH EBP
0040F497 |. 8BEC MOV EBP,ESP
0040F499 |. 6A FF PUSH -1
0040F49B |. 68 D8354100 PUSH VideoCap.004135D8
0040F4A0 |. 68 1CF64000 PUSH VideoCap.0040F61C ; JMP to msvcrt._except_handler3
0040F4A5 |. 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
0040F4AB |. 50 PUSH EAX
0040F4AC |. 64:8925 000000>MOV DWORD PTR FS:[0],ESP
0040F4B3 |. 83EC 68 SUB ESP,68
0040F4B6 |. 53 PUSH EBX
0040F4B7 |. 56 PUSH ESI
0040F4B8 |. 57 PUSH EDI
0040F4B9 |. 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
0040F4BC |. 33DB XOR EBX,EBX
0040F4BE |. 895D FC MOV DWORD PTR SS:[EBP-4],EBX
0040F4C1 |. 6A 02 PUSH 2
0040F4C3 |. FF15 30174100 CALL DWORD PTR DS:[411730] ; msvcrt.__set_app_type
0040F4C9 |. 59 POP ECX
0040F4CA |. 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
0040F4D1 |. 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
0040F4D8 |. FF15 38174100 CALL DWORD PTR DS:[411738] ; msvcrt.__p__fmode
Now dump target with LordPE. If you scan it with PEiD you will found --> Microsoft Visual C++ 6.0. Our target is unpacked and stolen bytes are returned but we need to repir IAT.
5. Fixing IAT:
We will repair IAT using ImpREC but ASProtect is using one feature which make harder to do that job. I'm not pozitive sure if I'm right (somebody correct me if not) but ASProtect redirects some part of IAT jumps. I think that idea is to redirect jump in IAT table to some other location and there litle encrypt that jump, something like :
XOR EAX,76543210
jmp EAX
Above is just example of code that ImpREC cannot read. When you unpack target, that jump in IAT that leads to this example now leads to nothing and our target is crushing. Tankfuly, there are peoples who know about this more than me and they have made some plugins for ImpREC to fix this. To repair IAT use the plugins in the attachment.
-
Level : newbie
3 ways to remove a nagscreen :
- Kill the DialogBox call
- Skip the Dialogbox procedure
- Reduce the timervalue (to zero)
1. Kill the DialogBox-Call
In this tutorial, we will learn how to defeat nagscreens. We will see that there are different ways to do that.
Our target is editor.exe.
It has a nagscreen that stays there for 10 seconds :(
Let's find out how we can kill it :
The nagscreen, is in fact a DialogBox that is called before displaying the real program.
To do that, the API DialogBoxParam is used. So just search for the API in your import table (eg in W32DASM).
If you find more than one entry, and you don't know which one is the nagscreen, you can always check it in Soft Ice. (bpx DialogBoxParamA)
We find it here :
* Reference To: USER32.SendDlgItemMessageA, Ord:0000h
00401411 E8408D0000 Call 0040A156
00401416 6A00 push 00000000 ;Push init value
00401418 682B124000 push 0040122B ;Push address of dialogbox-procedure
0040141D 53 push ebx ;Push handle owner
* Possible Reference to Dialog: DialogID_0384
0040141E 6884030000 push 00000384 ;Push INT Resource
00401423 6A00 push 00000000 ;Push dialogtemplate
* Reference To: KERNEL32.GetModuleHandleA, Ord:0000h
00401425 E8F28B0000 Call 0040A01C ;Call Modulehandle (to get the handle)
0040142A 50 push eax ;Push handle DialogBox
* Reference To: USER32.DialogBoxParamA, Ord:0000h
0040142B E8C68C0000 Call 0040A0F6 ;Call the nagscreen
00401430 E9A8000000 jmp 004014DD
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 004013A2(C)
00401435 837D1001 cmp dword ptr [ebp+10], 00000001
00401439 0F849E0000 je 004014DD
Now let's grab win32.hlp (you can find it in the Reference part of this site) and search for DialogBoxParam :
int DialogBoxParam
(
HINSTANCE hInstance, // handle to application instance
LPCTSTR lpTemplateName, // identifies dialog box template
HWND hWndParent, // handle to owner window
DLGPROC lpDialogFunc, // pointer to dialog box procedure
LPARAM dwInitParam // initialization value
);
So if we reverse the code above to c++ syntaxis we have the following :
DialogBoxParam (GetModuleHandle(NULL),NULL,MAKEINTRESOURCE(IDD_NA G), hwnd, Nag_Routine);
All we have to do is NOP the entier code for the call out. Fire up your hexeditor, and change the offset A16 till offset A2F in 90's. (If you don't know how to find an offset or how to nop, read some other tutorials on this site (newbie rated))
Run editor.exe, the nag is gone! :)
Now what exactly did we do? We erased the code that calls the dialog. So, the code of the nag is still there, but it's not used anymore :)
2. Skip the dialogbox-procedure
Sometimes, the program checks if the nag is called. That way the above methode will fail. (eg PaintShopPro)
Another easy way to bypass the nag, is let the program call the dialogbox, and then end the diaologbox immediatly.
How? Let's find out :
How can we find the dialogbox procedure?
Easy, the address was used by DialogBoxParam! (address 401418 : push 0040122B)
Ok, jump to 40122B code in W32Dasm.
We are here :
0040122B 55 push ebp
0040122C 8BEC mov ebp, esp
0040122E 8B450C mov eax, dword ptr [ebp+0C]
00401231 2D10010000 sub eax, 00000110
00401236 740A je 00401242 //WM_INITDIALOG ?
00401238 48 dec eax
00401239 7433 je 0040126E //WM_COMMAND ?
0040123B 83E802 sub eax, 00000002
0040123E 741A je 0040125A //WM_TIMER ?
00401240 EB41 jmp 00401283
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401236(C)
00401242 6A00 push 00000000
00401244 FF35E8B04000 push dword ptr [0040B0E8] //Push Timervalue.
0040124A FF35ECB04000 push dword ptr [0040B0EC]
00401250 FF7508 push [ebp+08]
* Reference To: USER32.SetTimer, Ord:0000h
00401253 E80A8F0000 Call 0040A162 //Call SetTimer
00401258 EB2D jmp 00401287 //We can change this jump
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 0040123E(C)
0040125A 6A01 push 00000001
0040125C 6A01 push 00000001
0040125E FF7508 push [ebp+08]
* Reference To: USER32.GetDlgItem, Ord:0000h
00401261 E8AE8E0000 Call 0040A114
00401266 50 push eax
* Reference To: USER32.EnableWindow, Ord:0000h
00401267 E8968E0000 Call 0040A102 //Enable the button
0040126C EB19 jmp 00401287
* Referenced by a (U)nconditional or (C)onditional Jump at Address: 00401239(C)
0040126E 668B5510 mov dx, word ptr [ebp+10]
00401272 66FFCA dec dx
00401275 7510 jne 00401287
00401277 6A01 push 00000001
00401279 FF7508 push [ebp+08]
* Reference To: USER32.EndDialog, Ord:0000h
0040127C E8878E0000 Call 0040A108
00401281 EB04 jmp 00401287
We notice the SetTimer API in the WM_INITDIALOG.
If we want to exit this procedure, we have to call the EndDialog Function (40127c).
We can easily change the jump at address 401258 'jmp 401287' into 'jmp 401277'
That way, we start the dialogbox, and we close it before it is displayed :)
This requires a patch of only one byte. change 'EB 2D' in 'EB 1D' (This changes the jump to jump 10h bytes less far).
Run the program, no nag ! :)
3. Reduce the TimerValue
As you can see in the above code, When the timer is set, we have some variable that are pushed on the stack for it. One of them (00401244) is the time (in millisec.)
How do we know that? Try to figure this one out yourself! Use win32.hlp !
push dword ptr [0040B0E8]
To know what value is in there, we press the 'Data Hex'-button in W32Dasm and search for 40B0E8
0040B080 2D 20 43 6F 70 79 72 69 - Copyri
0040B088 67 68 74 20 31 39 39 36 ght 1996
0040B090 20 42 6F 72 6C 61 6E 64 Borland
0040B098 20 49 6E 74 6C 2E 00 00 Intl...
0040B0A0 00 B0 40 00 48 B0 40 00 ..@.H.@.
0040B0A8 48 B0 40 00 6C B0 40 00 H.@.l.@.
0040B0B0 01 00 00 00 00 00 00 00 ........
0040B0B8 E4 14 40 00 80 6A 40 00 ..@..j@.
0040B0C0 AC 6A 40 00 00 00 00 00 .j@.....
0040B0C8 08 C4 40 00 00 4C D2 40 ..@..L.@
0040B0D0 00 E4 D2 40 00 00 01 00 ...@....
0040B0D8 00 00 00 00 00 00 00 00 ........
0040B0E0 00 00 00 00 00 00 00 90 ........
0040B0E8 10 27 00 00 01 00 00 00 .'......
Since Intel uses the 'Little Endian' way, our dword value is = 00 00 27 10 h = 10000 decimal
10000 millsec = 10 sec This is the value we want to change !
Find the value in your hexeditor, and change it too 00 00 00 00 = 0 sec. (or any other value you want)
Now run the program, we see that the nag appears but the 10 second delay is gone... :)
I showed you three ways to kill a nagscreen, but there are many more ways... To kill a nagscreen the 'right way' you should also remove the DialogBox in the Resource section. This reduces the file size :) But this is beyond the scope of this tutorial. If you are interested in more information about this, or if you have questions about this tutorial, post a comment or ask on the forum.
If you manage to remove the NAG, you have a fast and small texteditor for free! :)
-
Level : newbie
Learn how to keygen using this easy crackme by tsh33p.
Keygen source included.
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*.....
*Author:Tanatos
|Date:28.12.2004
*Target:KeygenMe by terr0r sh33p(tsh33p)
|Dificulty:Easy
*Tools:Olly Debug,C++ Compiler
|Solution:Fishing/Keygen
*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*.....
Introduction:this is another easy keygenme...learn how to keygen it...
The Essay:
Run the keygenme enter any name you like and any serial...the name should be bigger than 3 chars...then press ok...remember the error you get...you don't get one...so we will do the oposite search for the message we should get if the combination would be a good one.
Load the keygenme in olly go to search for->all referenced text strings and look for:"Yeah! You done it." that would be good...double click it...scroll a bit up:
004010DC |. E8 DE000000 CALL <JMP.&USER32.SendMessageA> ; SendMessageA
004010E1 |. 83F8 03 CMP EAX,3
004010E4 |. 0F8C 93000000 JL keygenme.0040117D
004010EA |. 8BD0 MOV EDX,EAX
004010EC |. 33C9 XOR ECX,ECX
ok set a breakpoint on 4010DC and then press F9.Enter the name Tanatos and serial 1234567890 then press ok.It breaks...good to know...after the line we breaked on comes the serial calculation part...so i'll just explain line by line from here:
004010E1 |. 83F8 03 CMP EAX,3 <--Compares the name leght to 3
004010E4 |. 0F8C 93000000 JL keygenme.0040117D <--If its smaller jumps else continues
004010EA |. 8BD0 MOV EDX,EAX <--Moves the leght of the name to EDX
004010EC |. 33C9 XOR ECX,ECX <--Clears ECX(makes it 0)
004010EE |. 33DB XOR EBX,EBX <--Clears EBX
004010F0 |> 0FB681 5020400>/MOVZX EAX,BYTE PTR DS:[ECX+402050] <--takes the value of the curent letter (the hex value)
004010F7 |. 35 37130300 |XOR EAX,31337 <--EAX = EAX ^ 0x31337
004010FC |. 05 EFBEADDE |ADD EAX,DEADBEEF <--EAX+=0xDEADBEEF
00401101 |. 69C0 66060000 |IMUL EAX,EAX,666 <--EAX = EAX *0x666
00401107 |. 2D B3BAAD1B |SUB EAX,1BADBAB3 <--EAX-=0x1BADBAB3
0040110C |. C1E0 03 |SHL EAX,3 <--EAX = EAX << 3
0040110F |. 35 0DD04DD3 |XOR EAX,D34DD00D <--EAX = EAX ^ 0xD34DD00D
00401114 |. 03D8 |ADD EBX,EAX <--This is a variable that will store our serial
00401116 |. 41 |INC ECX <--Rize the counter variable
00401117 |. 3BD1 |CMP EDX,ECX <--Compares the counter to our name leght
00401119 |.^75 D5 JNZ SHORT keygenme.004010F0 <--If its not equal it does the loop again.
That would explain the code now for the keygen code...i coded it in c++ :
#include <stdio.h>
#include <conio.h>
#include <string.h>
char name[32];
int n,i;
long int serial=0,st=0;
void main(void)
{
printf("Name:");scanf("%s",&name);
n=strlen(name);
if(n>2 && n<=32)
{
for(i=0;i<n;i++)
{
st=name[i];
st=st ^ 0x31337;
st+=0xDEADBEEF;
st=st*0x666;
st-=0x1BADBAB3;
st=st << 3;
st=st ^ 0xD34DD00D;
serial=serial+st;
st=0;
}
printf("Serial:%lXn",serial);
}
else
{
printf("Please enter a name with more than 2 chars and less than 32");
}
}
Done explaining hope you understood something out of this...bye
-
Level : beginner
An alternative to writing a keygenerator is making an inline keygenerator. If the target can be serial fished, it can be inline keygenned. An example on a simple crackme.
Intro
In this tutorial we will crack Rith's crackme, and make an internal keygenerator for it.
What is an internal keygenerator? Well, I call it a 'keygen for lazy reversers' The idea is to change the target program in a way that it displays the correct serial for you.
Tools needed :Disassembler & Hexeditor
Why make an internal keygen ?
Well, There are (at least) 2 good reasons :
- You don't have to reverse the algo completely
- You won't make any mistakes decompiling the algo ; )
Reversing the code
004015BC E8E3020000 Call 004018A4
004015C1 8B7E60 mov edi, dword ptr [esi+60] ;Name
004015C4 8B5FF8 mov ebx, dword ptr [edi-08] ;Name length
004015C7 83FB05 cmp ebx, 00000005 ;Name length
004015CA 7C7E jl 0040164A ;Jump to badguy
004015CC 8B4664 mov eax, dword ptr [esi+64] ;Serial
004015CF 89442414 mov dword ptr [esp+14], eax
004015D3 3958F8 cmp dword ptr [eax-08], ebx ;NameLength=Serial length?
(*):004015D6 7572 jne 0040164A ;if not, jump to badguy
004015D8 83FB14 cmp ebx, 00000014 ;Name length>14 ?
004015DB 7F6D jg 0040164A ;if so, jump to badguy
004015DD 33C9 xor ecx, ecx
004015DF 85DB test ebx, ebx
004015E1 7E54 jle 00401637
004015E3 8B742410 mov esi, dword ptr [esp+10] ;Pi (20 digits)
* Referenced by a (U)nconditional or (C)onditional Jump at Address:00401631(C)
004015E7 8A040F mov al, byte ptr [edi+ecx] ;(ecx)th byte of Name
004015EA 0FBE2C31 movsx ebp, byte ptr [ecx+esi] ;(ecx)th digit of Pi
004015EE 0FBEC0 movsx eax, al
004015F1 99 cdq
004015F2 F7FD idiv ebp ;Divide name-byte by Pi-byte
004015F4 8BC2 mov eax, edx ;Move remainder to eax
004015F6 D1E0 shl eax, 1 ;remainder * 2
; The code that follows are just some checks and modification in order to get; a serial between certain ascii values. (So you can enter it in the proggy)
004015F8 83F87B cmp eax, 0000007B
004015FB 7E03 jle 00401600
004015FD 83E81A sub eax, 0000001A
* Referenced by a (U)nconditional or (C)onditional Jump at Address:004015FB(C)
00401600 83F841 cmp eax, 00000041
00401603 7D09 jge 0040160E
00401605 BA82000000 mov edx, 00000082
0040160A 2BD0 sub edx, eax
0040160C 8BC2 mov eax, edx
* Referenced by a (U)nconditional or (C)onditional Jump at Address:00401603(C)
0040160E 83F85B cmp eax, 0000005B
00401611 7E12 jle 00401625
00401613 83F861 cmp eax, 00000061
00401616 7D0D jge 00401625
00401618 99 cdq
00401619 BD0A000000 mov ebp, 0000000A
0040161E F7FD idiv ebp
00401620 83C230 add edx, 00000030
00401623 8BC2 mov eax, edx
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses: 0401611(C), :00401616(C)
00401625 8B542414 mov edx, dword ptr [esp+14]
(**):00401629 38040A cmp byte ptr [edx+ecx], al ;Serial byte=Our Serial byte?
(**):0040162C 751C jne 0040164A ;if not, jump badguy
0040162E 41 inc ecx ;point to next byte/digit
0040162F 3BCB cmp ecx, ebx ;all bytes done?
00401631 7CB4 jl 004015E7 ;if not, jump for next byte
(***)00401633 8B742418 mov esi, dword ptr [esp+18]
00401637 6A00 push 00000000
* Possible StringData Ref from Data Obj ->"Congratulations!"
(****)00401639 6834304000 push 00403034
0040163E 6820304000 push 00403020
00401643 8BCE mov ecx, esi
00401645 E854020000 Call 0040189E ;Call MessageBoxA
Patching the code
To change this crackme in a keygen, we will need to patch the following :
(**) mov byte ptr [edx+ecx], al
HEX : 38 04 0A 75 1C -> 88 04 11 90 90
(***) mov byte ptr [edx+ecx],00h
HEX : 8B 74 24 18 -> C6 04 0A 00
(****) push edx
HEX : 68 20 30 40 00 -> 52 90 90 90 90
(**) Saves each byte of the calculated serial to address in EDX+Counter
(***) Add a NULL string terminator to the Serial
(****) Display serial in the MessageBox
When we change ** and *** and **** we have made a very basic, but working internal keygen.
Now enter your name and a serial with the same length, hit the Register-button ... tada ... There is your valid serial!
Have you noticed that we only had to change 14 bytes to change the proggy to an internal keygen? As I said, this is great for lazy programmers :)
To complete this keygen, let's change the program so that we don't have to enter any serial.
Nop the jump at (*), and we're done!
HEX : 75 72 -> 90 90
Name : Detten
Serial : 6|j46j
Regards,
-
Level : intermediate
Keygenning an application that uses a customized CRC32 algorithm to create a serial for a name + companyname.
TARGET = FireGraphicXP v4.0
TOOLS USED = Softice / IDA
Intro
We start the program, and a registerbox pops up :) The Check button is disabled though, so we can suspect the serial is checked 'on the fly' as we enter it in the editboxes.
Fill the name, company, and serial field with your data, but leave last digit of serial open. Now, put a breakpoint on hmemcpy, or getwindowtext. And enter the final digit. It breaks!
After a bit of tracing in softice, we notice that we land right in the checking routine that keeps looping as long that the registration dialogbox is on.
This is the code we land in :
The code
.text:00414440 checkforenable proc near
.text:00414440 push ebx
.text:00414441 push edi
.text:00414442 push 63h
.text:00414444 push 1
.text:00414446 push 1
.text:00414448 lea edi, [esi+20h] ; Name
.text:0041444B push 64h
.text:0041444D push 43Ch
.text:00414452 mov ebx, edi ; ebx = Name
.text:00414454 mov eax, edi
.text:00414456 call check_inputlength_routine ; Test length
.text:0041445B test eax, eax
.text:0041445D jz skiplengthchecks
.text:00414463 push 63h
.text:00414465 push 1
.text:00414467 push 1
.text:00414469 push 64h
.text:0041446B lea ebx, [esi+84h] ; Company Name
.text:00414471 push 43Dh
.text:00414476 mov eax, edi ; Name
.text:00414478 call check_inputlength_routine
.text:0041447D test eax, eax
.text:0041447F jz skiplengthchecks
.text:00414485 push 4
.text:00414487 push 1
.text:00414489 push 1
.text:0041448B push 5
.text:0041448D lea ebx, [esi+0E8h] ; Serial part 1
.text:00414493 push 43Eh
.text:00414498 mov eax, edi
.text:0041449A call check_inputlength_routine
.text:0041449F test eax, eax
.text:004144A1 jz skiplengthchecks
.text:004144A7 push 4
.text:004144A9 push 1
.text:004144AB push 1
.text:004144AD push 5
.text:004144AF lea ebx, [esi+0EDh] ; serial part 2
.text:004144B5 push 4ADh
.text:004144BA mov eax, edi
.text:004144BC call check_inputlength_routine
.text:004144C1 test eax, eax
.text:004144C3 jz short skiplengthchecks
.text:004144C5 push 4
.text:004144C7 push 1
.text:004144C9 push 1
.text:004144CB push 5
.text:004144CD lea ebx, [esi+0F2h] ; serial part 3
.text:004144D3 push 4AEh
.text:004144D8 mov eax, edi
.text:004144DA call check_inputlength_routine
.text:004144DF test eax, eax
.text:004144E1 jz short skiplengthchecks
.text:004144E3 push 4
.text:004144E5 push 1
.text:004144E7 push 1
.text:004144E9 push 5
.text:004144EB lea ebx, [esi+0F7h] ; serial part 4
.text:004144F1 push 4AFh
.text:004144F6 mov eax, edi
.text:004144F8 call check_inputlength_routine
.text:004144FD test eax, eax
.text:004144FF jz short skiplengthchecks
.text:00414501 push 4
.text:00414503 push 1
.text:00414505 push 1
.text:00414507 push 5
.text:00414509 lea ebx, [esi+0FCh] ; serial part 5
.text:0041450F push 4B0h
.text:00414514 mov eax, edi
.text:00414516 call check_inputlength_routine
.text:0041451B test eax, eax
.text:0041451D jz short skiplengthchecks
.text:0041451F push 4
.text:00414521 push 1
.text:00414523 push 1
.text:00414525 push 5
.text:00414527 lea ebx, [esi+101h] ; serial part 6
.text:0041452D push 4B1h
.text:00414532 mov eax, edi
.text:00414534 call check_inputlength_routine
.text:00414539
.text:00414539 skiplengthchecks: ; CODE XREF: checkforenable+1Dj
.text:00414539 ; checkforenable+3Fj ...
.text:00414539 lea eax, [esi+101h] ; serial part 6
.text:0041453F push eax
.text:00414540 lea ecx, [esi+0FCh] ; serial part 5
.text:00414546 push ecx
.text:00414547 lea edx, [esi+0F7h] ; serial part 4
.text:0041454D push edx
.text:0041454E lea eax, [esi+0F2h] ; serial part 3
.text:00414554 push eax
.text:00414555 lea ecx, [esi+0EDh] ; serial part 2
.text:0041455B push ecx
.text:0041455C lea edx, [esi+0E8h] ; serial part 1
.text:00414562 push edx
.text:00414563 lea ecx, [esi+84h] ; company name
.text:00414569 push offset byte_0_525B08
.text:0041456E mov edx, edi ; edx = Name
.text:00414570 call check_serial ; eax == 1 if serial OK
.text:00414575 neg al
.text:00414577 push 1 ; nIDDlgItem
.text:00414579 sbb eax, eax
.text:0041457B neg eax
.text:0041457D mov edi, eax
.text:0041457F mov eax, [esi+4]
.text:00414582 push eax ; hDlg
.text:00414583 call ds:GetDlgItem
.text:00414589 push edi ; bEnable (depends on check_serial call))
.text:0041458A push eax ; hWnd
.text:0041458B call ds:EnableWindow ; Enable/disable the register button
.text:00414591 pop edi
.text:00414592 xor eax, eax
.text:00414594 pop ebx
.text:00414595 retn 10h
.text:00414595 checkforenable endp
Basically it checks if the serial is complete, and then the call check_serial is executed. If we trace over it, we see that it returns 0 for our fake serial. It is pretty clear that the serial routine is in that routine :)
Ok, let's take a look inside that routine :
.text:00446D70 sub esp, 120h
.text:00446D76 mov eax, dword_0_5232D0
.text:00446D7B xor eax, [esp+120h]
.text:00446D82 push esi
.text:00446D83 push edi
.text:00446D84 mov esi, edx
.text:00446D86 push offset unk_0_5188AB ; lpString2
.text:00446D8B push esi ; lpString1
.text:00446D8C mov [esp+130h+var_4], eax
.text:00446D93 mov edi, ecx
.text:00446D95 call ds:lstrcmpA
.text:00446D9B test eax, eax
.text:00446D9D jnz short go_on
.text:00446D9F
.text:00446D9F serial_not_complete:
.text:00446D9F
.text:00446D9F xor al, al
.text:00446DA1 jmp skipsome
.text:00446DA6 ; ---------------------------------------------------------------------------
.text:00446DA6
.text:00446DA6 go_on: ; CODE XREF: check_serial+2Dj
.text:00446DA6 mov eax, [esp+128h+arg_18] ; serial part 6
.text:00446DAD mov ecx, [esp+128h+arg_14] ; serial part 5
.text:00446DB4 mov edx, [esp+128h+arg_10] ; serial part 4
.text:00446DBB push eax
.text:00446DBC mov eax, [esp+12Ch+arg_C] ; serial part 3
.text:00446DC3 push ecx
.text:00446DC4 mov ecx, [esp+130h+arg_8] ; serial part 2
.text:00446DCB push edx
.text:00446DCC mov edx, [esp+134h+arg_4] ; serial part 1
.text:00446DD3 push eax
.text:00446DD4 push ecx
.text:00446DD5 push edx
.text:00446DD6 lea eax, [esp+140h+sz]
.text:00446DDA push offset aSSSSSS ; "%s%s%s%s%s%s"
.text:00446DDF push eax
.text:00446DE0 call paste_routine ; paste serial together
What happened so far? All the serial parts are pasted together as a string. There is no need to trace that routine, it just pastes together whatever is pushed right before it. (Nobody stops you from tracing in that paste routine though :p)
.text:00446DE5 add esp, 20h
.text:00446DE8 lea ecx, [esp+128h+sz] ; serial
.text:00446DEC push ecx ; lpString (serial)
.text:00446DED call ds:lstrlenA
.text:00446DF3 cmp eax, 18h ; serial length == 24 ?
.text:00446DF6 jnz short serial_not_complete
.text:00446DF8 lea edx, [esp+128h+sz] ; edx = Serial
.text:00446DFC push edx ; lpsz
.text:00446DFD call ds:CharUpperA
.text:00446E03 test edi, edi ; company name
.text:00446E05 mov eax, edi
.text:00446E07 jnz short start_hashing
.text:00446E09 mov eax, offset unk_0_5188AB
the string built from the serialparts is converted to uppercase
.text:00446E0E
.text:00446E0E start_hashing: ; CODE XREF: check_serial+97j
.text:00446E0E push 241D30Bh ; 0037868299
.text:00446E13 push eax ; company name
.text:00446E14 push esi ; Name
.text:00446E15 lea eax, [esp+134h+var_104]
.text:00446E19 push offset aSSD ; "%s%s%d"
.text:00446E1E push eax
.text:00446E1F call paste_routine ; paste name + company name + digits
.text:00446E24 lea eax, [esp+13Ch+var_104] ; eax = paste result userdata
.text:00446E28 call Create_hash_table
Here the paste routine is called again, this time with a fixed number, company name, and name they are pasted all after eachother. From now on I call this string 'userdata'
Then this string is used as parameter in the nex call, in that call a CRC32 hashing table is built, and the userdata is hashed with that table. We will take a look at this call in a minute. ;)
You can also notice the "%s%s%d" string, which is pretty simular to the strings u need for wsprintf API. Check it out in your win32api.hlp if u like ;)
.text:00446E2D push eax ; CRC32 hash 1
.text:00446E2E lea ecx, [esp+140h+var_104]
.text:00446E32 push offset a08x ; "%08X"
.text:00446E37 push ecx ; userdata
.text:00446E38 call paste_routine ; overwrite userdata with CRC32 Hash 1 (in ascii)
.text:00446E3D add esp, 20h
.text:00446E40 mov ecx, 2 ; counter = 2
.text:00446E45 lea edi, [esp+128h+var_104] ; userdata
.text:00446E49 lea esi, [esp+128h+sz] ; serial part 1 & 2
.text:00446E4D xor edx, edx
.text:00446E4F repe cmpsd ; CRC32 hash 1 == serial digit 1 - 8 (2 dwords)
.text:00446E51 jnz serial_not_complete
next the hash created in the create_hash_table routine( which is returned in eax), is pushed and converted to an ascii string. this means we have converted one DWORD to an 8 digits long hex number in ascii, so 8 bytes long.
Next there is a compare done of 2 DWORDS (8bytes) between serialpart1&2 and our hash (created from the userdata).
.text:00446E57 mov eax, [esp+128h+var_118] ; lpSerial part 3
.text:00446E5B mov ecx, [esp+128h+var_114] ; lpSerial part 4
.text:00446E5F mov [esp+128h+var_104], eax ; overwrite dword CRC32 hash 1 with serial part 3
.text:00446E63 lea eax, [esp+128h+var_104]
.text:00446E67 mov [esp+128h+var_100], ecx ; overwrite 2nd dword CRC32 hash 1 with serial part 4
.text:00446E6B call Create_hash_table
.text:00446E70 push eax ; CRC32 Hash 2
.text:00446E71 lea edx, [esp+12Ch+var_104] ; edx = userdata
.text:00446E75 push offset a08x ; "%08X"
.text:00446E7A push edx
.text:00446E7B call paste_routine ; overwrite userdata with CRC32 Hash 2 (in ascii)
.text:00446E80 add esp, 0Ch
.text:00446E83 xor eax, eax
.text:00446E85 mov ecx, 2
.text:00446E8A lea edi, [esp+128h+var_104] ; userdata
.text:00446E8E lea esi, [esp+128h+var_110] ; serial part 6 & 7
.text:00446E92 repe cmpsd ; CRC32 hash 2 == serial digit 16 - 24 (2 dwords)
.text:00446E94 setz al
Same thing happens here, only now the 3rd and 4th part of the serial are used to create a hash. and it is checked with part 5 & 6 of our serial. So it seems part 3,4,5 and 6 don't depend on our data...kinda strange :/ When this check is also correct al is set to 1
.text:00446E97
.text:00446E97 skipsome: ; CODE XREF: check_serial+31j
.text:00446E97 mov ecx, [esp+128h+var_4]
.text:00446E9E xor ecx, [esp+128h]
.text:00446EA5 pop edi ; Name
.text:00446EA6 pop esi
.text:00446EA7 call sub_0_4D6F4A ; internal appcheck
.text:00446EAC add esp, 120h
.text:00446EB2 retn 1Ch
.text:00446EB2 check_serial endp
So this function calculates serial part 1 & 2 out of our userdata, and calculate serial part 5 & 6 out of serial part 3 & 4. It returns 1 if serial was ok, else 0.
Let's have a closer look at the Create_hash_table routine :
.text:004036F0 Create_hash_table proc near
.text:004036F0
.text:004036F0
.text:004036F0 var_400 = dword ptr -400h
.text:004036F0
.text:004036F0 sub esp, 400h
.text:004036F6 push ebx
.text:004036F7 push esi
.text:004036F8 push edi
.text:004036F9 mov edi, eax ; userdata
.text:004036FB xor ebx, ebx
.text:004036FD lea ecx, [ecx+0]
.text:00403700
.text:00403700 hash_table_loop: ; CODE XREF: Create_hash_table+137j
.text:00403700 xor ecx, ecx
.text:00403702 test bl, 1
.text:00403705 jz short loc_0_40370C
.text:00403707 mov ecx, 80h
.text:0040370C
.text:0040370C loc_0_40370C: ; CODE XREF: Create_hash_table+15j
.text:0040370C mov eax, ebx
.text:0040370E shr eax, 1
.text:00403710 test al, 1
.text:00403712 jz short loc_0_403717
.text:00403714 or ecx, 40h
.text:00403717
.text:00403717 loc_0_403717: ; CODE XREF: Create_hash_table+22j
.text:00403717 shr eax, 1
.text:00403719 test al, 1
.text:0040371B jz short loc_0_403720
.text:0040371D or ecx, 20h
.text:00403720
.text:00403720 loc_0_403720: ; CODE XREF: Create_hash_table+2Bj
.text:00403720 shr eax, 1
.text:00403722 test al, 1
.text:00403724 jz short loc_0_403729
.text:00403726 or ecx, 10h
.text:00403729
.text:00403729 loc_0_403729: ; CODE XREF: Create_hash_table+34j
.text:00403729 shr eax, 1
.text:0040372B test al, 1
.text:0040372D jz short loc_0_403732
.text:0040372F or ecx, 8
.text:00403732
.text:00403732 loc_0_403732: ; CODE XREF: Create_hash_table+3Dj
.text:00403732 shr eax, 1
.text:00403734 test al, 1
.text:00403736 jz short loc_0_40373B
.text:00403738 or ecx, 4
.text:0040373B
.text:0040373B loc_0_40373B: ; CODE XREF: Create_hash_table+46j
.text:0040373B shr eax, 1
.text:0040373D test al, 1
.text:0040373F jz short loc_0_403744
.text:00403741 or ecx, 2
.text:00403744
.text:00403744 loc_0_403744: ; CODE XREF: Create_hash_table+4Fj
.text:00403744 test al, 2
.text:00403746 jz short loc_0_40374B
.text:00403748 or ecx, 1
.text:0040374B
.text:0040374B loc_0_40374B: ; CODE XREF: Create_hash_table+56j
.text:0040374B shl ecx, 18h
.text:0040374E mov eax, ecx
.text:00403750 and eax, 80000000h
.text:00403755 neg eax
.text:00403757 sbb eax, eax
.text:00403759 add ecx, ecx
.text:0040375B and eax, 4C11DB7h
.text:00403760 xor eax, ecx
.text:00403762 mov ecx, eax
.text:00403764 and ecx, 80000000h
.text:0040376A neg ecx
.text:0040376C sbb ecx, ecx
.text:0040376E and ecx, 4C11DB7h
.text:00403774 lea edx, [eax+eax]
.text:00403777 xor ecx, edx
.text:00403779 mov eax, ecx
.text:0040377B and eax, 80000000h
.text:00403780 neg eax
.text:00403782 sbb eax, eax
.text:00403784 add ecx, ecx
.text:00403786 and eax, 4C11DB7h
.text:0040378B xor eax, ecx
.text:0040378D mov ecx, eax
.text:0040378F and ecx, 80000000h
.text:00403795 neg ecx
.text:00403797 sbb ecx, ecx
.text:00403799 and ecx, 4C11DB7h
.text:0040379F lea edx, [eax+eax]
.text:004037A2 xor ecx, edx
.text:004037A4 mov eax, ecx
.text:004037A6 and eax, 80000000h
.text:004037AB neg eax
.text:004037AD sbb eax, eax
.text:004037AF add ecx, ecx
.text:004037B1 and eax, 4C11DB7h
.text:004037B6 xor eax, ecx
.text:004037B8 mov ecx, eax
.text:004037BA and ecx, 80000000h
.text:004037C0 neg ecx
.text:004037C2 sbb ecx, ecx
.text:004037C4 and ecx, 4C11DB7h
.text:004037CA lea edx, [eax+eax]
.text:004037CD xor ecx, edx
.text:004037CF mov eax, ecx
.text:004037D1 and eax, 80000000h
.text:004037D6 neg eax
.text:004037D8 sbb eax, eax
.text:004037DA add ecx, ecx
.text:004037DC and eax, 4C11DB7h
.text:004037E1 xor eax, ecx
.text:004037E3 mov ecx, eax
.text:004037E5 and ecx, 80000000h
.text:004037EB neg ecx
.text:004037ED sbb ecx, ecx
.text:004037EF and ecx, 4C11DB7h
.text:004037F5 lea edx, [eax+eax]
.text:004037F8 xor ecx, edx
.text:004037FA mov [esp+ebx*4+40Ch+var_400], ecx
.text:004037FE mov eax, ecx
.text:00403800 xor edx, edx
.text:00403802 mov ecx, 1Fh
.text:00403807
.text:00403807 loc_0_403807: ; CODE XREF: Create_hash_table+12Aj
.text:00403807 test al, 1
.text:00403809 jz short loc_0_403814
.text:0040380B mov esi, 1
.text:00403810 shl esi, cl
.text:00403812 or edx, esi
.text:00403814
.text:00403814 loc_0_403814: ; CODE XREF: Create_hash_table+119j
.text:00403814 shr eax, 1
.text:00403816 dec ecx
.text:00403817 cmp ecx, 0FFFFFFFFh
.text:0040381A jg short loc_0_403807
.text:0040381C mov [esp+ebx*4+40Ch+var_400], edx ; store in hash table
.text:00403820 inc ebx
.text:00403821 cmp ebx, 0FFh ; hash table completed ?
.text:00403827 jle hash_table_loop
All the above code is only to create a hash table. We will need to copy this piece of code for our keygen, or you can just dump the table. Both ways are good, because the table does not depend on variables.
.text:0040382D mov ecx, edi ; userdata
.text:0040382F or eax, 0FFFFFFFFh
.text:00403832 lea esi, [ecx+1] ; p(userdata)+1
.text:00403835
.text:00403835 loop_data: ; CODE XREF: Create_hash_table+14Aj
.text:00403835 mov dl, [ecx]
.text:00403837 inc ecx
.text:00403838 test dl, dl ; char (userdata) == 0 ?
.text:0040383A jnz short loop_data
.text:0040383C sub ecx, esi ; ecx = userdata length
.text:0040383E mov edx, edi ; edx = userdata
.text:00403840 jz short loc_0_40385C ;
.text:00403840
.text:00403840; **userdata = name + company name + 37868299
.text:00403842
.text:00403842 process_more_data: ; CODE XREF: .text:0040385Aj
.text:00403842 movzx edi, byte ptr [edx] ; edi = (char) userdata
.text:00403845 mov esi, eax ; esi = hash
.text:00403847 and esi, 0FFh ; esi = char (hash)
.text:0040384D xor esi, edi ; esi = char (esi) XOR char (userdata)
.text:0040384F mov esi, [esp+esi*4+40Ch+var_400] ; esi = hashtable [esi]
.text:00403853 shr eax, 8 ; eax = eax/256
.text:00403853 Create_hash_table endp
.text:00403853
.text:00403856 xor eax, esi ; hash = hash XOR hashtable dword
.text:00403858 inc edx ; pointer ++
.text:00403859 dec ecx ; counter --
.text:0040385A jnz short process_more_data
.text:0040385C
.text:0040385C loc_0_40385C: ; CODE XREF: Create_hash_table+150j
.text:0040385C pop edi
.text:0040385D pop esi
.text:0040385E not eax ; NOT hash
.text:00403860 pop ebx
.text:00403861 add esp, 400h
.text:00403867 retn
.text:00403867 ; ---------------------------------------------------------------------------
.text:00403868 align 10h
.text:00403870 mov eax, offset unk_0_524F28
.text:00403875 retn
.text:00403875 ; ---------------------------------------------------------------------------
First Conclusion
If you traced the above code with softice, then you are able to construct a valid serial for your data. The first 2 parts of the serial are equal to the first hash(at VA 00446E2Dh in eax) and the 5th and 6th part are equal to the hash of the 3rd and 4th part. (at VA 00446E70h in eax). 3rd and 4th part of serial can be chosen at random... a bit weird... Ok, enter the serials, the buttons gets enabled ;) it works!!! :))) Quit the app completly, and restart it, baammm the registration box is back :/ It seems the 3rd and 4th serial part are probably not so random afterall...
The attack part 2 ;)
There must be a second larger check in the startup of the program...
How can we reach it? we can use the softice symbol loader, and bpx on the pasteroutine or even the hashing table routine. (This won't work if they used a copy of those routines for the startup check).
Another way is to fire up regmon, and check where the serial key is stored in the registry. Even more simple is just to do a search for a part of ur serial in the registry (using regedit). and then bpx that regkey. Anyway u do it, u should land in the following code somewhere :
.text:00446EC0 sub esp, 204h
.text:00446EC6 mov eax, dword_0_5232D0
.text:00446ECB xor eax, [esp+204h]
.text:00446ED2 push esi
.text:00446ED3 push edi
.text:00446ED4 mov esi, edx
.text:00446ED6 push offset unk_0_5188AB ; lpString2
.text:00446EDB push esi ; lpString1
.text:00446EDC mov [esp+214h+var_4], eax
.text:00446EE3 mov edi, ecx
.text:00446EE5 call ds:lstrcmpA
.text:00446EEB test eax, eax
.text:00446EED jnz short loc_0_446EF6
.text:00446EEF
.text:00446EEF loc_0_446EEF: ; CODE XREF: sub_0_446EC0+40j
.text:00446EEF ; sub_0_446EC0+8Ej ...
.text:00446EEF xor al, al
.text:00446EF1 jmp loc_0_446FD0
.text:00446EF6 ; ---------------------------------------------------------------------------
.text:00446EF6
.text:00446EF6 loc_0_446EF6: ; CODE XREF: sub_0_446EC0+2Dj
.text:00446EF6 push ebx ; lpString
.text:00446EF7 call ds:lstrlenA
.text:00446EFD cmp eax, 18h
.text:00446F00 jnz short loc_0_446EEF
.text:00446F02 test edi, edi
.text:00446F04 mov eax, edi
.text:00446F06 jnz short loc_0_446F0D
.text:00446F08 mov eax, offset unk_0_5188AB
.text:00446F0D
.text:00446F0D loc_0_446F0D: ; CODE XREF: sub_0_446EC0+46j
.text:00446F0D push 241D30Bh ; fixed digits
.text:00446F12 push eax
.text:00446F13 push esi
.text:00446F14 lea eax, [esp+218h+var_204]
.text:00446F18 push offset aSSD ; "%s%s%d"
.text:00446F1D push eax
.text:00446F1E call paste_routine
.text:00446F23 lea eax, [esp+220h+var_204]
.text:00446F27 call Create_hash_table ; create hash 1
.text:00446F2C push eax
.text:00446F2D lea ecx, [esp+224h+var_204]
.text:00446F31 push offset a08x ; "%08X"
.text:00446F36 push ecx
.text:00446F37 call paste_routine ; userstring = hash1
.text:00446F3C add esp, 20h
.text:00446F3F mov ecx, 2
.text:00446F44 lea edi, [esp+20Ch+var_204]
.text:00446F48 mov esi, ebx
.text:00446F4A xor edx, edx
.text:00446F4C repe cmpsd ; check serial part 1 & 2 == hash 1
.text:00446F4E jnz short loc_0_446EEF
.text:00446F50 push 3EE24C3h ; second fixed digits
.text:00446F55 lea eax, [esp+210h+var_204]
.text:00446F59 push eax
.text:00446F5A lea ecx, [esp+214h+var_104]
.text:00446F61 push offset aS08x ; "%s%08X"
.text:00446F66 push ecx
.text:00446F67 call paste_routine
.text:00446F6C lea eax, [esp+21Ch+var_104]
.text:00446F73 call Create_hash_table
.text:00446F78 push eax ; hash 2
.text:00446F79 lea edx, [esp+220h+var_204]
.text:00446F7D push offset a08x ; "%08X"
.text:00446F82 push edx
.text:00446F83 call paste_routine
.text:00446F88 add esp, 1Ch
.text:00446F8B mov ecx, 2
.text:00446F90 lea edi, [esp+20Ch+var_204]
.text:00446F94 lea esi, [ebx+8]
.text:00446F97 xor eax, eax
.text:00446F99 repe cmpsd ; check serial part 3 & 4 == hash 2
.text:00446F9B jnz loc_0_446EEF
.text:00446FA1 lea eax, [esp+20Ch+var_204]
.text:00446FA5 call Create_hash_table
.text:00446FAA push eax
.text:00446FAB lea ecx, [esp+210h+var_204]
.text:00446FAF push offset a08x ; "%08X"
.text:00446FB4 push ecx
.text:00446FB5 call paste_routine
.text:00446FBA add esp, 0Ch
.text:00446FBD mov ecx, 2
.text:00446FC2 lea edi, [esp+20Ch+var_204]
.text:00446FC6 lea esi, [ebx+10h]
.text:00446FC9 xor edx, edx
.text:00446FCB repe cmpsd
.text:00446FCD setz al
.text:00446FD0
.text:00446FD0 loc_0_446FD0: ; CODE XREF: sub_0_446EC0+31j
.text:00446FD0 mov ecx, [esp+20Ch+var_4]
.text:00446FD7 xor ecx, [esp+20Ch]
.text:00446FDE pop edi
.text:00446FDF pop esi
.text:00446FE0 call sub_0_4D6F4A
.text:00446FE5 add esp, 204h
.text:00446FEB retn 4
.text:00446FEB sub_0_446EC0 endp
I have commented this routine less than the other one, but you can see it is very simular. The only difference, is that the 3rd and 4th part of the serial are calculated by adding some fixed numbers to the first hash, and rehash that. This gives us a valid 3rd and 4th part of the serial, and the 5th and 6th part remain the same, just a hashing of the 2nd calculated hash.
heh, ok maybe this is a bit confusing, let's try to put this in a scheme :
name+company+37868299
|
|
create hash 1
|
hash1 == serial part 1 & 2
|
|
hash1+03ee24c3
|
|
create hash 2
|
hash2 == serial part 3 & 4
|
|
hash 2
|
|
create hash 3
|
hash3 == serial part 5 & 6
Ok, now we now that, all we have to do is rip the create_hash_table routine (which includes the data hashing) and use it 3 times in our keygen. That's all ;)
Our keygen routine
This routine is ripped and slightly adjusted. It's the Create_hash_table routine.
;-------------------------------------------------------------------+
; key-generation procedure |
;-------------------------------------------------------------------+
GenerateKey PROC aname:DWORD, aserial:DWORD
;--------------------------------------------------------------------
; ALGO RECONSTRUCTION (CRC32 TABLE + HASHING)
;--------------------------------------------------------------------
mov edi, aname
xor ebx, ebx
lea ecx, [ecx+0]
hash_table_loop:
xor ecx, ecx
test bl, 1
jz jump1
mov ecx, 80h
jump1:
mov eax, ebx
shr eax, 1
test al, 1
jz jump2
or ecx, 40h
jump2:
shr eax, 1
test al, 1
jz jump3
or ecx, 20h
jump3:
shr eax, 1
test al, 1
jz jump4
or ecx, 10h
jump4:
shr eax, 1
test al, 1
jz jump5
or ecx, 8
jump5:
shr eax, 1
test al, 1
jz jump6
or ecx, 4
jump6:
shr eax, 1
test al, 1
jz jump7
or ecx, 2
jump7:
test al, 2
jz jump8
or ecx, 1
jump8:
shl ecx, 18h
mov eax, ecx
and eax, 80000000h
neg eax
sbb eax, eax
add ecx, ecx
and eax, 4C11DB7h
xor eax, ecx
mov ecx, eax
and ecx, 80000000h
neg ecx
sbb ecx, ecx
and ecx, 4C11DB7h
lea edx, [eax+eax]
xor ecx, edx
mov eax, ecx
and eax, 80000000h
neg eax
sbb eax, eax
add ecx, ecx
and eax, 4C11DB7h
xor eax, ecx
mov ecx, eax
and ecx, 80000000h
neg ecx
sbb ecx, ecx
and ecx, 4C11DB7h
lea edx, [eax+eax]
xor ecx, edx
mov eax, ecx
and eax, 80000000h
neg eax
sbb eax, eax
add ecx, ecx
and eax, 4C11DB7h
xor eax, ecx
mov ecx, eax
and ecx, 80000000h
neg ecx
sbb ecx, ecx
and ecx, 4C11DB7h
lea edx, [eax+eax]
xor ecx, edx
mov eax, ecx
and eax, 80000000h
neg eax
sbb eax, eax
add ecx, ecx
and eax, 4C11DB7h
xor eax, ecx
mov ecx, eax
and ecx, 80000000h
neg ecx
sbb ecx, ecx
and ecx, 4C11DB7h
lea edx, [eax+eax]
xor ecx, edx
mov [ebx*4+offset hashtable], ecx
mov eax, ecx
xor edx, edx
mov ecx, 1Fh
jump9:
test al, 1
jz jump10
mov esi, 1
shl esi, cl
or edx, esi
jump10:
shr eax, 1
dec ecx
cmp ecx, 0FFFFFFFFh
jg jump9
mov [ebx*4+offset hashtable], edx ; store in hash table
inc ebx
cmp ebx, 0FFh ; hash table completed ?
jle hash_table_loop
mov ecx, edi ; name + company name + digits
or eax, 0FFFFFFFFh
lea esi, [ecx+1] ; lp(name + company name + digits)+1
loop_data:
mov dl, [ecx]
inc ecx
test dl, dl ; char (name + company name + digits) == 0 ?
jnz short loop_data
sub ecx, esi ; ecx = name + company name + digits length
mov edx, edi ; edx = name + company name + digits
jz jump11
process_more_data:
movzx edi, byte ptr [edx] ; edi = (char) userdata
mov esi, eax ; esi = hash
and esi, 0FFh ; esi = char (hash)
xor esi, edi ; esi = char (esi) XOR char (userdata)
mov esi, [esi*4+offset hashtable] ; esi = hashtable [esi]
shr eax, 8 ; eax = eax/256
xor eax, esi ; hash = hash XOR hashtable dword
inc edx ; pointer ++
dec ecx ; counter --
jnz short process_more_data
jump11:
not eax ; NOT hash
;--------------------------------------------------------------------------
; END OF CRC32 HASHING ALGO ---> HASH in EAX
;--------------------------------------------------------------------------
invoke wsprintf, aserial, addr ToHexa, eax
ret
GenerateKey ENDP
When we have made this, all we need is to reconstruct the little scheme above :
pushad
invoke lstrcat, ADDR thename, ADDR thecompany
invoke lstrcat, ADDR thename, ADDR thedigits
invoke GenerateKey, addr thename, addr theserial ; pointer to name, pointer to buffer
mov esi, offset theserial
mov edi, offset buffer
xor ecx, ecx
.WHILE ecx
That's all we need, now write a nice GUI for it, and you have a working keygen ;)
Check out the source, it's pretty straightforward if u understand what happens in
the application.
Outtro
This algo is based on a CRC32 hashing table, and as soon as u r able to reconstruct this table in your keygen, it is pretty easy to keygen it. In this case the same table was used 3 times.
-
Level : newbie
Warcraft 3 seems to use the SetSecurityInfo() api to prevent us to obtain its process handle.
Okay, I debugged and I played around Warcraft 3 for some time.
Trying myself to understand this protection scheme, I bring
the proof here how to defeat this protection.
SetSecurityInfo() blocks us from accessing it's memory.
Let me explain SetSecurityInfo() to you:
------------------------------------------
The SetSecurityInfo function sets specified security information
in the security descriptor of a specified object.
The caller identifies the object by a handle.
DWORD SetSecurityInfo(
HANDLE handle, // handle to the object
SE_OBJECT_TYPE ObjectType, // type of object
SECURITY_INFORMATION SecurityInfo, // type of security information to set
PSID psidOwner, // pointer to the new owner SID
PSID psidGroup, // pointer to the new primary group SID
PACL pDacl, // pointer to the new DACL
PACL pSacl // pointer to the new SACL
);
------------------------------------------
Warcraft3 passes params to this function to block us from getting a valid process handle, or from altering its memory.
We want this if we want to fiddle with it ;)
But where is Warcraft doing this? Simply, debug it and set a breakpoint on SetSecurityInfo.
After debugging as results we get:
------------ game.dll snippet ------------
.6F009962: 53 push ebx ;0
.6F009963: 8D95E0FDFFFF lea edx,[ebp][-00000220]
.6F009969: 52 push edx ; 0012FB4Ch
.6F00996A: 53 push ebx ; 0
.6F00996B: 53 push ebx ; 0
.6F00996C: 6804000080 push 080000004 ; DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION
.6F009971: 6A06 push 006 ; 6
.6F009973: 50 push eax ; FFFFFFFFh
.6F009974: FF55EC call d,[ebp][-14] ; SetSecurityInfo()
------------ game.dll snippet ------------
From here, we have the SetSecurityInfo() API call with params:
SetSecurityInfo(0xFFFFFFFF, 6, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, 0, 0, 0x0012FB4C, 0);
We just have to pass NULL as a third parameter.
Passing NULL as SecurityInfo param, makes sure the api doesn't do anything that can block us. To be exact by making the param NULL, the api doesn't set the specified security information in the discretionary access control list (DACL) of the specified objects security descriptor.
We patch 6804000080 at address 6F00996C to 6800000000.
Just edit game.dll with ANY hex editor and patch it.
I accessed Warcraft 3 memory without any problems :-)
Good luck working on your map hacks, namespoof hacks, etc.
-
Level : beginner
This is a small, and by no means complete, digestion of some crypto techniques.
Featuring :
- Transposition
- Substitution
- Frequency analysis
- Le Chiffre Indéchiffrable
- Charles Babbage and Vigenére
- Playfair
- ADFGVX crypto
Transposition
One method of crypting a text is transposition.
One simple traspose is to write out the message on two rows.
Letters 1,3, 5 and so on, you write on the first row.
Letters 2, 4, 6 etc. you write on the second row.
The message:
Your secret is your prisoner, let it go and you become its prisoner.
Row 1:
Yusceiyupioelyyonyueoeypioi
Row 2:
orertsorrsnreigadobcmisrsnr
Crypted text:
Yusceiyupioelyyonyueoeypioiorertsorrsnreigadobcmis rsnr
Substitution
Another method is substitution where you substitute one letter with another letter.
Code
====
A code substitute a word or group of letter.
Monoalphabetic substitution
=======================
One variant is the Caesar roll. This is based on crypto alphabet that's shifted a number
of steps relative the clear text alphabet. It's common in cryptography to write out the
clear text in lowercase letter and the crypted text in uppercase letter.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet:
DEFGHIJKLMNOPQRSTUVWXYZABC (here with a three letter right rotation)
Clear text:
veni, vidi vici
Crypto text:
YHQL, YLGL, YLFL
To crypt a text, you write out the crypto alphabet under the clear alphabet.
Then you take the letter from the text, here starting with "v", and go to the
column where v is in the clear alphabet.
Look in the same column in the crypto alphabet to get the letter to substitute with.
Here v is substituted with Y. Repeat this for all letters in the text.
As you may see, this is not a very strong protection. With a computer it's easy to
try all possible shifts, where each shift can be regarded as a key to the crypto.
Monoalphabetic substitution with key
====================================
If you choose a more general algorithm where the clear alphabet can be reordered in any possible way,
you get 400 000 000 000 000 000 000 000 000 possible keys to choose from. If the enemy gets hold of
the crypted text and he knows the algorithm, he still have to test all keys to decrypt the message.
If he test one key per second, he approximately would need the time equal the age of
universe multiplied with 10e9.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet:
DJKTUVCWNOLPAEGFHIQRXYMSZB ("random" order)
Clear text:
ettu, brute?
Crypto text:
URRX, JIXRU?
It can be shown with mathematical proof, that if you use a random key as long as the text you want to crypt,
the crypted text CAN NOT be decrypted if you don't know the key. In any crypto a weak spot is repetitions
that occur if you use a to short key. Another thing to remember is that you can't use the same key twice,
and you must use a really random key. If you use the key more then once, there's methods to break the crypto.
A variant to this, but with a less number of keys, is to use a key or key phrase. Instead of a random alphabet
you take a key or a key phrase, for example "Julius Ceasar". Remove all spaces and duplicate letters and use
the rest, JULISCAER, as beginning of the crypto alphabet. The rest of the alphabet is just a shift beginning
where the keyword ends and with the letters in the keyword removed from the alphabet.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet:
JULISCAERTVWXYZBDFGHKMNOPQ
This can also be rotated a number of steps to.
Crypto alphabet:
CAERTVWXYZBDFGHKMNOPQJULIS
A good thing about this is that it is easy to remember the key word or key phrase. The simplicity in combination
with the strength made this the common method to crypt a text the first thousand years a.c.
Anyone interested in cracking a monoalphabetic text can look at the mono crackme in the crackme's.
But there is ways to break this crypto, the Arabs was the first ones out.
Frequency analysis
The oldest known description, by an Arab, to break a monoalphabetic crypto is from 800 a.c.
The trick is that in any given language there's letters that's used more frequently.
If you know the letter frequency for a language, you take the letters frequency in the crypted
text and substitute it with the one that have about the same frequency for that language.
It's relatively easy to make a qualified guess what letter it is.
The same rules can be applied on one, two, three or more, letter words.
As you are a cracker, it's easy to write a small app to analyse some text files on your computer.
What kind of text you analyse of course affects the result of the letter frequency. If you for example
analyze a sorce code file, you get a funny letter and word distribution. If it's a *.asm file you get a
lot of three letter words like: eax,ebx,ecx...:-).
Le Chiffre Indéchiffrable
For many centuries the monoalphabetic substitution crypto was the prevailing method to write a secret text.
But after the Arabs invention of the frequency analyze, this method was no longer a safe one to use.
The original idea for this new crypto came from a man named Leon Battista Alberti, born 1404.
His idea was use two or more crypto alphabet and switch between them.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet 1:
FZBVKIXAYMEPLSDHJORGNQCUTW
Crypto alphabet 2:
GOXBFWTHQILAZPJDESYVCRKUHN
If we crypt the word "hello" we use the first alphabet for h, that become A.
For the second letter e we use the second alphabet, e becomes F, and so on.
With this method hello becomes AFPAD.
A man named Blaise de Vigenére, born 1523, developed this idea further.
Instead of using two or three alphabet, he used 26 (for a-z for English).
Although he based his work on ideas of previous thinkers, this method is known as the Vigenére crypto.
The use of more than one crypto alphabet gives this type of crypto the name polyalphabetic crypto.
The first step to use this is to write out a Vigenére table. This is done by writing down a clear alphabet
followed by 26 crypto alphabets, each one rotated on step in relation to the previous one.
abcdefghijklmnopqrstuvwxyz1 BCDEFGHIJKLMNOPQRSTUVWXYZA2 CDEFGHIJKLMNOPQRSTUVWXYZAB3 DEFGHIJKLMNOPQRSTUVWXYZABC4 EFGHIJKLMNOPQRSTUVWXYZABCD5 FGHIJKLMNOPQRSTUVWXYZABCDE6 GHIJKLMNOPQRSTUVWXYZABCDEF7 HIJKLMNOPQRSTUVWXYZABCDEFG8 IJKLMNOPQRSTUVWXYZABCDEFGH9 JKLMNOPQRSTUVWXYZABCDEFGHI10 KLMNOPQRSTUVWXYZABCDEFGHIJ11 LMNOPQRSTUVWXYZABCDEFGHIJK12 MNOPQRSTUVWXYZABCDEFGHIJKL13 NOPQRSTUVWXYZABCDEFGHIJKLM14 OPQRSTUVWXYZABCDEFGHIJKLMN15 PQRSTUVWXYZABCDEFGHIJKLMNO16 QRSTUVWXYZABCDEFGHIJKLMNOP17 RSTUVWXYZABCDEFGHIJKLMNOPQ18 STUVWXYZABCDEFGHIJKLMNOPQR19 TUVWXYZABCDEFGHIJKLMNOPQRS20 UVWXYZABCDEFGHIJKLMNOPQRST21 VWXYZABCDEFGHIJKLMNOPQRSTU22 WXYZABCDEFGHIJKLMNOPQRSTUV23 XYZABCDEFGHIJKLMNOPQRSTUVW24 YZABCDEFGHIJKLMNOPQRSTUVWX25 ZABCDEFGHIJKLMNOPQRSTUVWXY26 ABCDEFGHIJKLMNOPQRSTUVWXYZ
The first row is a crypto alphabet with a Caesar roll, a rotation of one step.
The second one have a two step rotation and so on.
To use it, you use a new row for each letter you crypt.
To decrypt it the user must know which row to use for which letter.
You maybe use row 5 for the first letter, row 14 for the second, row 21 for the third and so on.
To decrypt the text the receiver must know which row shifts to use.
One way to do this is to use a key word.
To crypt the message "Begin attack at sundown" and the key GREEN we do like this.
Write out the key word above the message over and over, so each letter in the message
is linked to a letter in the key word.
GREENGREENGREENGREENbeginattackatsundown
Then you create the crypto text like this:
Crypt the letter b by taking the letter in the key word above, here it is G.
This letter is the index to a row in the Vigenére table.
The row beginning with G, row 6, is the crypto alphabet to use for
the first letter b. In the row for the clear alphabet, go to the column
for the letter b. To find the substitution letter, follow this column down
to row 6, here you find the letter H to substitute for b.
Repeat this procedure for the letter e. The key letter above e is R which gives
us the row that start with R, row number 17. In the clear alphabet, go to the column
for the letter e in the clear alphabet, follow this column down to row 17 to get the substitution letter V.
Repeat this for each letter in the message.
Each letter in the keyword gives us a different crypto alphabet to use.
The number of letters in the key word gives us the number of rows to use in the
Vigenére table. In this example we use four rows, G, R, E and N (E is doubled here).
The message "beginattackatsundown" crypted with the keyword GREEN comes out as:
HVKMAGKXEPQRXWHTUSAA
With a longer keyword, or a key sentence, you add more rows from the table you make the crypto more
difficult to break. The frequency analysis, described above, has no effect on this kind of crypto.
If you have a word with at double letter, like attack above, the first t becomes K and the second becomes X.
If you do a frequency analysis of this, there's no way to tell that this is the letter t.
Another strength is the enormous number of keys you can use. Any word in the book can be used,
you even can make up you own words. It's just not possible to try all possible combinations.
The work by Vigenéres was published in Tracicté des Chiffres 1586 but did not come to common use
until 200 years later.
Charles Babbage and Vigenére
As mentioned earlier, the strength of the Vigenére crypto is that a letter can be crypted on a number of
different ways. If the keyword is KING is used, there's four different ways to crypt a given letter.
The same apply to words that's crypted. The word "the" can be crypted as, DQR, BUK, GNO or ZRM depending on
the words relation to the keyword KING. You get no clues from the frequency analysis.
This making the decrypting much harder, but not impossible.
A man named Charles Babbage, born 1791, was the first one to break this kind of crypto.
Among many things, he was devoted to statistics. He was one of the first one making statistic on born/death relations,
today commonly used by insurance companies.
He thought that if there's only four ways to crypt the word "the", and the word is used many times in the text,
the possibility that the same crypto variant is repeated on a number of places in the text is rather significant.
The longer the text, the greater the chance. It was this kind of repetition that lead Babbage to the decryption
of the Vigenére crypto.
Babbage stated some fairly simple steps to break the Vigenére crypto.
Look for letter sequences that repeat more that once in the text. There's a possibility that it's the same word
crypted with the same part of the keyword. The distance between the repetitions and the distance from the beginning
of the text, gives you some clues of the length of the keyword. When you know the length of the keyword, you know
how many crypto alphabet that was used to crypt the text. If you use just one alphabet, it's just a
monoalphabetic crypto, and that you know how to decrypt.
If you found out that the keyword is five letters, you use the first alphabet for letter 1, 6 ,11 and so on.
For letter 2, 7, 12... you use the second alphabet and so on. You only have to use as many alphabet as there
are letters in the keyword. But how do we know what alphabet to use?
You already know the answer: Frequency analysis.
When you know the length of the keyword, you just have to use frequency analysis.
Remember that the crypto alphabet is just a clear alphabet rotated one step in relation to the previous one.
Implementing Vigenére in ASM
============================
To implement Vigenére crypto in asm we need a lot of Vigenére tables in memory. To this we need a lot of
index and key char pointers. NOT
This was my first approach, but if we stop and think a bit we can reduce it to this.
Crypting: add al,ah ;al is clear char and ah is key char sub al,"A"+"A" cmp al,25 jng @F sub al,26 ;Overflow, wrap around@@: add al,"A" ;al is now crypted char
Decrypting:
sub al,ah ;al is crypt char and ah is key char
cmp al,0
jge @F
add al,26 ;Underflow, wrap around.
@@:
add al,"A" ;al is now clear char
To understand the code above, remember that the Vigenére table is just 26 alphabets where each one is
rotated one step in relation to the previous. If we simplify it a bit and just use one alphabet,
a-z, we can look at it with a new approach. In the above example with the word "the" and the key
"KING" we get "t" as the first letter to crypt with key char "K". As you remember,
we go to the "t" column in the clear alphabet, and from there down to the line starting with k.
In the point of intersection we find "D".
Another way to look at it is, go to the "K" column of the alphabet starting with "A", column 10 (or "A" - "K",
where the "A"-column is column 0). Add the number of the "T" column, column 19, to the "K" column and we get 29.
As the English alphabet only have 26 letters we get an overflow, or wrap around of 4 (remember "A" column is 0).
If we add 4 to 0 we end up in the "D" column, which is the substitution letter for "T" with the key char "K".
We apply the same rules for the decryption, but here we subtract the key character from the crypted character.
Instead of overflow we look for underflow and if this happen, we shift it up 26 letters.
@Detten add link vigenere.asm
The complete source code can be found here, if Detten is so kind and add a link to it ;-).
And last, an exercise in Vigenére:
BBLM RS VRJ XTYOETOSWP UNTYOJH XBLHCOQ DLVTSQX FHO T PRQMJLJ UJG?
QXJ CD FJDG YK JWTBTKM FHO BB DCXLYCHDS HYW WSBUDTOS NZ IUAA GNNS,
MQE QDMYC BB UUOI NZ VJRTI LLZVNRKOX.
QSTC IU DMY OBOFGBJHNX KEVGJYY XAOVSH UYW TIPUD?
YCHCIE SX ODBWG C PJUEANR....MSSEJ BB UUSSA EAN WJYQY NARCMOS.
Avoiding Vigenére
=================
The strength of the Vigenére crypto also makes this kind of crypto harder to use.
One candidate to a crypto stronger than the monoalphabetic crypto, but easier to use than the
Vigenére crypto is the homophonic substitution crypto.
In this crypto you substitute a letter with many letters, and the number of substitute characters is
proportional to the frequency of the letter. If for example the letter a have a frequency of 9%,
we assign 8 characters to substitute for a. For every occurence of the letter a in the clear text,
we substitute it with any of the 8 characters we assigned to a, not important which one.
After crypting the text, every character substituted for a then have about 1% frequency in the crypted text.
If b, or any letter, have 1% frequency, we only have to substitute it with 2 characters.
When all letters in the clear text is crypted, every character then have about 1% frequency in the crypted text.
An example of homophonic substitution crypto. Here number is used.a b c d e f g h i j k l m n o p q r s t u v w x y z09 48 13 01 14 10 06 23 32 15 04 26 22 18 00 38 94 29 11 17 08 34 60 28 21 0212 81 41 03 16 31 25 39 70 37 27 58 05 95 35 19 20 61 89 5233 62 45 24 50 73 51 59 07 40 36 30 63 47 79 44 56 83 84 66 54 42 76 4353 46 65 88 71 72 77 86 4967 55 68 93 91 90 80 96 6978 57 99 7592 64 85 74 97 82 87 98
With a frequency of 1% for every character, there's no way to use
frequency analysis on the crypted text. Is this unbreakable? NOT
There's a number of discrete clues for a clever decrypter.
Every letter in any language have its own quality and relation to other letters.
This can be distinguished even if homophonic substitution crypto is used.
In english the letter q is always followed by the letter u. No other letter can follow q.
If we want to decrypt an english text crypted with homophonic substitution we now that q is a rare
letter probably only substituted with one character (or number).
We also know that the letter u have e frequency of about 3% of all letters in an english text and therefore
probably substituted with 3 characters.
If we find a character in the crypted text that is always followed by the same three characters,
we have reason to believe that these characters stands for the letter u, and the first stand for q.
Other letter are harder to pick, but their internal relation reveals which one it is.
It's possible to break this kind of crypto, but it's a lot safer than a simple monoalphabetic crypto.
At first glance, the homophobic substitution crypto seems to be some kind of polyalphabetic crypto.
Every clear text letter can be substituted with a number of characters, but there's a significant difference.
In the example above, the letter a is represented by 8 different numbers.
These numbers represent the letter a and only the letter a. In a polyalphabetic crypto a clear text letter
can be substituted with different letters, but here the substitution letters can represent different clear
text letters. Because of this, the homophonic substitution crypto is said to be a monoalphabetic crypto.
By the time the crypto alphabet is made, it's used through out the whole crypto.
It makes no difference that there's a number of substitute for a letter.
If you use a polyalphabetic crypto, you constantly change between different crypto alphabets.
My way of implementing homophonic substitution crypto in asm can be found here.
And now a homophonic exercise:
HNE 0IQWtG OY98CKÂ5u YfTBÅ7| pA vÏÃ2ä] éJ 1W[UZÂjweh3 XÈ i
åÅçgÄvâ ìqmV-sSkboDÁÏI6 }dcaäYz xÉÆÊÇÎË ÍL åét2Wë ãSáÌèDíæT
2.2, 9u ï]HÂ0|Cà X13-5Ã ëZ7gycK. Ulî Ëpx8MEçeikÅÄI ÏtDQw1GB o
äJÁ æA 3éVAObfuch[ jqÇvsz| åWÃ2Â] ÈÆmV-ÎSád}xíïÉ 2.2 Êçg
vÅI2Ïë âãàA-îSHÌèDK0T ]EZì5t9Q GËäUé7u, årWc{ ÂB Å|xy1O3 vÏeÀ
kNäJ Dpën ÄV åéÃ2W].
Playfair
The playfair crypto was created by Lyon Playfair. This crypto substitutes every two letters in the clear
text with another letter pair. To crypt and decrypt a text, the sender and receiver must first agree on a keyword to use.
The crypto is used like this.
Write out the letters in an 5 x 5 square (a-z) and let I and J share the same place. Start with the keyword .
If we use the keyword CHARLES we get:
C H A R LE S B D FG I/J K M NO P Q T UV W X Y Z
Then you divide the message into letter pairs, called bigram. Every bigram must consist of two different letters.
Therefore you put in an x for double letters that otherwise would end up in the same bigram.
Also put an x at the end if the text end with a single letter.
Clear text:
We meet at hammersmith bridge at seven.
Text in bigram:
we-me-et-at-ha-mx-me-rs-mi-th-br-id-ge-at-se-ve-nx
Then the crypting start.
Every bigram fall into one of the following categories.
1 Both letters is on the same line
2 Both letter is in the same column
3 Neither 1 or 2.
1 If both letter is in the same row, substitute them with the letter to the right if each one.
MI becomes NK. If the one letter is the last in the row, substitute it with the first letter in the row.
2 If both letters is in the same column, substitute them with the letter in the column below each one. GE becomes OG. If one letter is in the last row, substitute it with the letter from the first row. YR becomes RD.
3 If neither 1 or 2 apply, we do like this. To crypt the first letter, follow the row it's on until you
reach the column where the second letter is. The letter where the two intersect is used as a substitute for
letter one. When you crypt the second letter, follow the row it's on until you reach the column of the first
letter. The letter where the two intersect is used as a substitute for letter 2. VI becomes WG and SV becomes EW.
If you look at the clear text letters as the corner of a diagonal, the substitute letters is in the opposite corners.
The bigram text:
we me et at ha mx me rs mi th br id ge at se ve nx
The crypted text:
VSDGODQRARKYDGDHNKRPADSMOGQRBSCGKZ
ADFGVX crypto
In ADFGVX crypto, both substitution and transposition is used. The crypto is used like this.
In a 6 x 6 square, 36 positions, write out the letters a-z and the numbers 0-9 in a random order.
Both rows and columns in the square is named with the letters A, D, F, G, V and X.
The order of the letters in the square becomes a part of the key and therefore the receiver must have a
copy of the square.
A D F G V XA 8 p 3 d 1 nD l t 4 o a hF 7 k b c 5 zG j u 6 w g mV x s v i r 2X 9 e y 0 f q
The first step is to look at in which row and column each letter in the text is.
Write down the two letters that refer to this position in the square.
In this example 8 is substituted with AA and p with AD.
Message:
Attack at 2230
Crypt step 1:A t t a c k a t 2 2 3 0DV DD DD DV FG FD DV DD VX VX AF XG
So far it is a simple monoalphabetic substitution crypto, breakable with frequency analysis.
The second step in the crypto is the traspose. The traspose is based on a keyword, and in this example we use MARK.
This keyword must also be know by the receiver.
The transposition is done like this:
Write out the letters of the keyword as the first row of a new square.
Then write out the text from the first step of the crypto below the keyword using the same row length as the keyword.
Then arrange the columns so that the letters in the keyword is sorted from a to z.
The other columns follows the keyword columns.
M A R K -> A K M RD V D D V D D DD D D V D V D DF G F D G D F FD V D D V D D DA V X G V G A XA D G X D X A G
Final crypto text:VDDDDVDDGDFFVDDDVGAXDXAG
The reason for using A, D, F, G, V and X is that they are the letters in the Morse alphabet that
differs the most from each other. This was done to reduce the number of errors when the text was sent.
-
Level : beginner
This is a small, and by no means complete, digestion of some crypto techniques.
Featuring :
- Transposition
- Substitution
- Frequency analysis
- Le Chiffre Indéchiffrable
- Charles Babbage and Vigenére
- Playfair
- ADFGVX crypto
Transposition
One method of crypting a text is transposition.
One simple traspose is to write out the message on two rows.
Letters 1,3, 5 and so on, you write on the first row.
Letters 2, 4, 6 etc. you write on the second row.
The message:
Your secret is your prisoner, let it go and you become its prisoner.
Row 1:
Yusceiyupioelyyonyueoeypioi
Row 2:
orertsorrsnreigadobcmisrsnr
Crypted text:
Yusceiyupioelyyonyueoeypioiorertsorrsnreigadobcmis rsnr
Substitution
Another method is substitution where you substitute one letter with another letter.
Code
====
A code substitute a word or group of letter.
Monoalphabetic substitution
=======================
One variant is the Caesar roll. This is based on crypto alphabet that's shifted a number
of steps relative the clear text alphabet. It's common in cryptography to write out the
clear text in lowercase letter and the crypted text in uppercase letter.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet:
DEFGHIJKLMNOPQRSTUVWXYZABC (here with a three letter right rotation)
Clear text:
veni, vidi vici
Crypto text:
YHQL, YLGL, YLFL
To crypt a text, you write out the crypto alphabet under the clear alphabet.
Then you take the letter from the text, here starting with "v", and go to the
column where v is in the clear alphabet.
Look in the same column in the crypto alphabet to get the letter to substitute with.
Here v is substituted with Y. Repeat this for all letters in the text.
As you may see, this is not a very strong protection. With a computer it's easy to
try all possible shifts, where each shift can be regarded as a key to the crypto.
Monoalphabetic substitution with key
====================================
If you choose a more general algorithm where the clear alphabet can be reordered in any possible way,
you get 400 000 000 000 000 000 000 000 000 possible keys to choose from. If the enemy gets hold of
the crypted text and he knows the algorithm, he still have to test all keys to decrypt the message.
If he test one key per second, he approximately would need the time equal the age of
universe multiplied with 10e9.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet:
DJKTUVCWNOLPAEGFHIQRXYMSZB ("random" order)
Clear text:
ettu, brute?
Crypto text:
URRX, JIXRU?
It can be shown with mathematical proof, that if you use a random key as long as the text you want to crypt,
the crypted text CAN NOT be decrypted if you don't know the key. In any crypto a weak spot is repetitions
that occur if you use a to short key. Another thing to remember is that you can't use the same key twice,
and you must use a really random key. If you use the key more then once, there's methods to break the crypto.
A variant to this, but with a less number of keys, is to use a key or key phrase. Instead of a random alphabet
you take a key or a key phrase, for example "Julius Ceasar". Remove all spaces and duplicate letters and use
the rest, JULISCAER, as beginning of the crypto alphabet. The rest of the alphabet is just a shift beginning
where the keyword ends and with the letters in the keyword removed from the alphabet.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet:
JULISCAERTVWXYZBDFGHKMNOPQ
This can also be rotated a number of steps to.
Crypto alphabet:
CAERTVWXYZBDFGHKMNOPQJULIS
A good thing about this is that it is easy to remember the key word or key phrase. The simplicity in combination
with the strength made this the common method to crypt a text the first thousand years a.c.
Anyone interested in cracking a monoalphabetic text can look at the mono crackme in the crackme's.
But there is ways to break this crypto, the Arabs was the first ones out.
Frequency analysis
The oldest known description, by an Arab, to break a monoalphabetic crypto is from 800 a.c.
The trick is that in any given language there's letters that's used more frequently.
If you know the letter frequency for a language, you take the letters frequency in the crypted
text and substitute it with the one that have about the same frequency for that language.
It's relatively easy to make a qualified guess what letter it is.
The same rules can be applied on one, two, three or more, letter words.
As you are a cracker, it's easy to write a small app to analyse some text files on your computer.
What kind of text you analyse of course affects the result of the letter frequency. If you for example
analyze a sorce code file, you get a funny letter and word distribution. If it's a *.asm file you get a
lot of three letter words like: eax,ebx,ecx...:-).
Le Chiffre Indéchiffrable
For many centuries the monoalphabetic substitution crypto was the prevailing method to write a secret text.
But after the Arabs invention of the frequency analyze, this method was no longer a safe one to use.
The original idea for this new crypto came from a man named Leon Battista Alberti, born 1404.
His idea was use two or more crypto alphabet and switch between them.
Clear alphabet:
abcdefghijklmnopqrstuvwxyz
Crypto alphabet 1:
FZBVKIXAYMEPLSDHJORGNQCUTW
Crypto alphabet 2:
GOXBFWTHQILAZPJDESYVCRKUHN
If we crypt the word "hello" we use the first alphabet for h, that become A.
For the second letter e we use the second alphabet, e becomes F, and so on.
With this method hello becomes AFPAD.
A man named Blaise de Vigenére, born 1523, developed this idea further.
Instead of using two or three alphabet, he used 26 (for a-z for English).
Although he based his work on ideas of previous thinkers, this method is known as the Vigenére crypto.
The use of more than one crypto alphabet gives this type of crypto the name polyalphabetic crypto.
The first step to use this is to write out a Vigenére table. This is done by writing down a clear alphabet
followed by 26 crypto alphabets, each one rotated on step in relation to the previous one.
abcdefghijklmnopqrstuvwxyz1 BCDEFGHIJKLMNOPQRSTUVWXYZA2 CDEFGHIJKLMNOPQRSTUVWXYZAB3 DEFGHIJKLMNOPQRSTUVWXYZABC4 EFGHIJKLMNOPQRSTUVWXYZABCD5 FGHIJKLMNOPQRSTUVWXYZABCDE6 GHIJKLMNOPQRSTUVWXYZABCDEF7 HIJKLMNOPQRSTUVWXYZABCDEFG8 IJKLMNOPQRSTUVWXYZABCDEFGH9 JKLMNOPQRSTUVWXYZABCDEFGHI10 KLMNOPQRSTUVWXYZABCDEFGHIJ11 LMNOPQRSTUVWXYZABCDEFGHIJK12 MNOPQRSTUVWXYZABCDEFGHIJKL13 NOPQRSTUVWXYZABCDEFGHIJKLM14 OPQRSTUVWXYZABCDEFGHIJKLMN15 PQRSTUVWXYZABCDEFGHIJKLMNO16 QRSTUVWXYZABCDEFGHIJKLMNOP17 RSTUVWXYZABCDEFGHIJKLMNOPQ18 STUVWXYZABCDEFGHIJKLMNOPQR19 TUVWXYZABCDEFGHIJKLMNOPQRS20 UVWXYZABCDEFGHIJKLMNOPQRST21 VWXYZABCDEFGHIJKLMNOPQRSTU22 WXYZABCDEFGHIJKLMNOPQRSTUV23 XYZABCDEFGHIJKLMNOPQRSTUVW24 YZABCDEFGHIJKLMNOPQRSTUVWX25 ZABCDEFGHIJKLMNOPQRSTUVWXY26 ABCDEFGHIJKLMNOPQRSTUVWXYZ
The first row is a crypto alphabet with a Caesar roll, a rotation of one step.
The second one have a two step rotation and so on.
To use it, you use a new row for each letter you crypt.
To decrypt it the user must know which row to use for which letter.
You maybe use row 5 for the first letter, row 14 for the second, row 21 for the third and so on.
To decrypt the text the receiver must know which row shifts to use.
One way to do this is to use a key word.
To crypt the message "Begin attack at sundown" and the key GREEN we do like this.
Write out the key word above the message over and over, so each letter in the message
is linked to a letter in the key word.
GREENGREENGREENGREENbeginattackatsundown
Then you create the crypto text like this:
Crypt the letter b by taking the letter in the key word above, here it is G.
This letter is the index to a row in the Vigenére table.
The row beginning with G, row 6, is the crypto alphabet to use for
the first letter b. In the row for the clear alphabet, go to the column
for the letter b. To find the substitution letter, follow this column down
to row 6, here you find the letter H to substitute for b.
Repeat this procedure for the letter e. The key letter above e is R which gives
us the row that start with R, row number 17. In the clear alphabet, go to the column
for the letter e in the clear alphabet, follow this column down to row 17 to get the substitution letter V.
Repeat this for each letter in the message.
Each letter in the keyword gives us a different crypto alphabet to use.
The number of letters in the key word gives us the number of rows to use in the
Vigenére table. In this example we use four rows, G, R, E and N (E is doubled here).
The message "beginattackatsundown" crypted with the keyword GREEN comes out as:
HVKMAGKXEPQRXWHTUSAA
With a longer keyword, or a key sentence, you add more rows from the table you make the crypto more
difficult to break. The frequency analysis, described above, has no effect on this kind of crypto.
If you have a word with at double letter, like attack above, the first t becomes K and the second becomes X.
If you do a frequency analysis of this, there's no way to tell that this is the letter t.
Another strength is the enormous number of keys you can use. Any word in the book can be used,
you even can make up you own words. It's just not possible to try all possible combinations.
The work by Vigenéres was published in Tracicté des Chiffres 1586 but did not come to common use
until 200 years later.
Charles Babbage and Vigenére
As mentioned earlier, the strength of the Vigenére crypto is that a letter can be crypted on a number of
different ways. If the keyword is KING is used, there's four different ways to crypt a given letter.
The same apply to words that's crypted. The word "the" can be crypted as, DQR, BUK, GNO or ZRM depending on
the words relation to the keyword KING. You get no clues from the frequency analysis.
This making the decrypting much harder, but not impossible.
A man named Charles Babbage, born 1791, was the first one to break this kind of crypto.
Among many things, he was devoted to statistics. He was one of the first one making statistic on born/death relations,
today commonly used by insurance companies.
He thought that if there's only four ways to crypt the word "the", and the word is used many times in the text,
the possibility that the same crypto variant is repeated on a number of places in the text is rather significant.
The longer the text, the greater the chance. It was this kind of repetition that lead Babbage to the decryption
of the Vigenére crypto.
Babbage stated some fairly simple steps to break the Vigenére crypto.
Look for letter sequences that repeat more that once in the text. There's a possibility that it's the same word
crypted with the same part of the keyword. The distance between the repetitions and the distance from the beginning
of the text, gives you some clues of the length of the keyword. When you know the length of the keyword, you know
how many crypto alphabet that was used to crypt the text. If you use just one alphabet, it's just a
monoalphabetic crypto, and that you know how to decrypt.
If you found out that the keyword is five letters, you use the first alphabet for letter 1, 6 ,11 and so on.
For letter 2, 7, 12... you use the second alphabet and so on. You only have to use as many alphabet as there
are letters in the keyword. But how do we know what alphabet to use?
You already know the answer: Frequency analysis.
When you know the length of the keyword, you just have to use frequency analysis.
Remember that the crypto alphabet is just a clear alphabet rotated one step in relation to the previous one.
Implementing Vigenére in ASM
============================
To implement Vigenére crypto in asm we need a lot of Vigenére tables in memory. To this we need a lot of
index and key char pointers. NOT
This was my first approach, but if we stop and think a bit we can reduce it to this.
Crypting: add al,ah ;al is clear char and ah is key char sub al,"A"+"A" cmp al,25 jng @F sub al,26 ;Overflow, wrap around@@: add al,"A" ;al is now crypted char
Decrypting:
sub al,ah ;al is crypt char and ah is key char
cmp al,0
jge @F
add al,26 ;Underflow, wrap around.
@@:
add al,"A" ;al is now clear char
To understand the code above, remember that the Vigenére table is just 26 alphabets where each one is
rotated one step in relation to the previous. If we simplify it a bit and just use one alphabet,
a-z, we can look at it with a new approach. In the above example with the word "the" and the key
"KING" we get "t" as the first letter to crypt with key char "K". As you remember,
we go to the "t" column in the clear alphabet, and from there down to the line starting with k.
In the point of intersection we find "D".
Another way to look at it is, go to the "K" column of the alphabet starting with "A", column 10 (or "A" - "K",
where the "A"-column is column 0). Add the number of the "T" column, column 19, to the "K" column and we get 29.
As the English alphabet only have 26 letters we get an overflow, or wrap around of 4 (remember "A" column is 0).
If we add 4 to 0 we end up in the "D" column, which is the substitution letter for "T" with the key char "K".
We apply the same rules for the decryption, but here we subtract the key character from the crypted character.
Instead of overflow we look for underflow and if this happen, we shift it up 26 letters.
@Detten add link vigenere.asm
The complete source code can be found here, if Detten is so kind and add a link to it ;-).
And last, an exercise in Vigenére:
BBLM RS VRJ XTYOETOSWP UNTYOJH XBLHCOQ DLVTSQX FHO T PRQMJLJ UJG?
QXJ CD FJDG YK JWTBTKM FHO BB DCXLYCHDS HYW WSBUDTOS NZ IUAA GNNS,
MQE QDMYC BB UUOI NZ VJRTI LLZVNRKOX.
QSTC IU DMY OBOFGBJHNX KEVGJYY XAOVSH UYW TIPUD?
YCHCIE SX ODBWG C PJUEANR....MSSEJ BB UUSSA EAN WJYQY NARCMOS.
Avoiding Vigenére
=================
The strength of the Vigenére crypto also makes this kind of crypto harder to use.
One candidate to a crypto stronger than the monoalphabetic crypto, but easier to use than the
Vigenére crypto is the homophonic substitution crypto.
In this crypto you substitute a letter with many letters, and the number of substitute characters is
proportional to the frequency of the letter. If for example the letter a have a frequency of 9%,
we assign 8 characters to substitute for a. For every occurence of the letter a in the clear text,
we substitute it with any of the 8 characters we assigned to a, not important which one.
After crypting the text, every character substituted for a then have about 1% frequency in the crypted text.
If b, or any letter, have 1% frequency, we only have to substitute it with 2 characters.
When all letters in the clear text is crypted, every character then have about 1% frequency in the crypted text.
An example of homophonic substitution crypto. Here number is used.a b c d e f g h i j k l m n o p q r s t u v w x y z09 48 13 01 14 10 06 23 32 15 04 26 22 18 00 38 94 29 11 17 08 34 60 28 21 0212 81 41 03 16 31 25 39 70 37 27 58 05 95 35 19 20 61 89 5233 62 45 24 50 73 51 59 07 40 36 30 63 47 79 44 56 83 84 66 54 42 76 4353 46 65 88 71 72 77 86 4967 55 68 93 91 90 80 96 6978 57 99 7592 64 85 74 97 82 87 98
With a frequency of 1% for every character, there's no way to use
frequency analysis on the crypted text. Is this unbreakable? NOT
There's a number of discrete clues for a clever decrypter.
Every letter in any language have its own quality and relation to other letters.
This can be distinguished even if homophonic substitution crypto is used.
In english the letter q is always followed by the letter u. No other letter can follow q.
If we want to decrypt an english text crypted with homophonic substitution we now that q is a rare
letter probably only substituted with one character (or number).
We also know that the letter u have e frequency of about 3% of all letters in an english text and therefore
probably substituted with 3 characters.
If we find a character in the crypted text that is always followed by the same three characters,
we have reason to believe that these characters stands for the letter u, and the first stand for q.
Other letter are harder to pick, but their internal relation reveals which one it is.
It's possible to break this kind of crypto, but it's a lot safer than a simple monoalphabetic crypto.
At first glance, the homophobic substitution crypto seems to be some kind of polyalphabetic crypto.
Every clear text letter can be substituted with a number of characters, but there's a significant difference.
In the example above, the letter a is represented by 8 different numbers.
These numbers represent the letter a and only the letter a. In a polyalphabetic crypto a clear text letter
can be substituted with different letters, but here the substitution letters can represent different clear
text letters. Because of this, the homophonic substitution crypto is said to be a monoalphabetic crypto.
By the time the crypto alphabet is made, it's used through out the whole crypto.
It makes no difference that there's a number of substitute for a letter.
If you use a polyalphabetic crypto, you constantly change between different crypto alphabets.
My way of implementing homophonic substitution crypto in asm can be found here.
And now a homophonic exercise:
HNE 0IQWtG OY98CKÂ5u YfTBÅ7| pA vÏÃ2ä] éJ 1W[UZÂjweh3 XÈ i
åÅçgÄvâ ìqmV-sSkboDÁÏI6 }dcaäYz xÉÆÊÇÎË ÍL åét2Wë ãSáÌèDíæT
2.2, 9u ï]HÂ0|Cà X13-5Ã ëZ7gycK. Ulî Ëpx8MEçeikÅÄI ÏtDQw1GB o
äJÁ æA 3éVAObfuch[ jqÇvsz| åWÃ2Â] ÈÆmV-ÎSád}xíïÉ 2.2 Êçg
vÅI2Ïë âãàA-îSHÌèDK0T ]EZì5t9Q GËäUé7u, årWc{ ÂB Å|xy1O3 vÏeÀ
kNäJ Dpën ÄV åéÃ2W].
Playfair
The playfair crypto was created by Lyon Playfair. This crypto substitutes every two letters in the clear
text with another letter pair. To crypt and decrypt a text, the sender and receiver must first agree on a keyword to use.
The crypto is used like this.
Write out the letters in an 5 x 5 square (a-z) and let I and J share the same place. Start with the keyword .
If we use the keyword CHARLES we get:
C H A R LE S B D FG I/J K M NO P Q T UV W X Y Z
Then you divide the message into letter pairs, called bigram. Every bigram must consist of two different letters.
Therefore you put in an x for double letters that otherwise would end up in the same bigram.
Also put an x at the end if the text end with a single letter.
Clear text:
We meet at hammersmith bridge at seven.
Text in bigram:
we-me-et-at-ha-mx-me-rs-mi-th-br-id-ge-at-se-ve-nx
Then the crypting start.
Every bigram fall into one of the following categories.
1 Both letters is on the same line
2 Both letter is in the same column
3 Neither 1 or 2.
1 If both letter is in the same row, substitute them with the letter to the right if each one.
MI becomes NK. If the one letter is the last in the row, substitute it with the first letter in the row.
2 If both letters is in the same column, substitute them with the letter in the column below each one. GE becomes OG. If one letter is in the last row, substitute it with the letter from the first row. YR becomes RD.
3 If neither 1 or 2 apply, we do like this. To crypt the first letter, follow the row it's on until you
reach the column where the second letter is. The letter where the two intersect is used as a substitute for
letter one. When you crypt the second letter, follow the row it's on until you reach the column of the first
letter. The letter where the two intersect is used as a substitute for letter 2. VI becomes WG and SV becomes EW.
If you look at the clear text letters as the corner of a diagonal, the substitute letters is in the opposite corners.
The bigram text:
we me et at ha mx me rs mi th br id ge at se ve nx
The crypted text:
VSDGODQRARKYDGDHNKRPADSMOGQRBSCGKZ
ADFGVX crypto
In ADFGVX crypto, both substitution and transposition is used. The crypto is used like this.
In a 6 x 6 square, 36 positions, write out the letters a-z and the numbers 0-9 in a random order.
Both rows and columns in the square is named with the letters A, D, F, G, V and X.
The order of the letters in the square becomes a part of the key and therefore the receiver must have a
copy of the square.
A D F G V XA 8 p 3 d 1 nD l t 4 o a hF 7 k b c 5 zG j u 6 w g mV x s v i r 2X 9 e y 0 f q
The first step is to look at in which row and column each letter in the text is.
Write down the two letters that refer to this position in the square.
In this example 8 is substituted with AA and p with AD.
Message:
Attack at 2230
Crypt step 1:A t t a c k a t 2 2 3 0DV DD DD DV FG FD DV DD VX VX AF XG
So far it is a simple monoalphabetic substitution crypto, breakable with frequency analysis.
The second step in the crypto is the traspose. The traspose is based on a keyword, and in this example we use MARK.
This keyword must also be know by the receiver.
The transposition is done like this:
Write out the letters of the keyword as the first row of a new square.
Then write out the text from the first step of the crypto below the keyword using the same row length as the keyword.
Then arrange the columns so that the letters in the keyword is sorted from a to z.
The other columns follows the keyword columns.
M A R K -> A K M RD V D D V D D DD D D V D V D DF G F D G D F FD V D D V D D DA V X G V G A XA D G X D X A G
Final crypto text:VDDDDVDDGDFFVDDDVGAXDXAG
The reason for using A, D, F, G, V and X is that they are the letters in the Morse alphabet that
differs the most from each other. This was done to reduce the number of errors when the text was sent.
-
Level : newbie
Bypassing EXE Protector v2.01a.
The program is supposed to protect the exe file with a password.
I was looking in the other forum and I saw someone asking about this protection:
They were asking about EXEProtector v1.37a
I did a search on the net for it but I came up with this one.
So lets get started:
I put my crackme <int21h.exe> in this protection.
The program is supposed to protect the exe file with a password.
Lets run the program before we do anything.
We run it and find that it has a window title <Password Check> then in the window it has in the box <Enter the password :> It then has a <Cancel> and a <OK> button.
Lets run this through w32dsm89 and look at some strings:
When I look at the strings I see these 2 that catch my interest:
“You have not supplied a password” and “You have supplied a wrong password”
Let’s take a look at the code:
* Reference To: MSVBVM60.__vbaStrCmp, Ord:0000h
:00403A9A E869D9FFFF Call 00401408
:00403A9F 85C0 test eax, eax
:00403AA1 0F85AF000000 jne 00403B56
:00403AA7 B904000280 mov ecx, 80020004
:00403AAC 894D98 mov dword ptr [ebp-68], ecx
:00403AAF 6A0A push 0000000A
:00403AB1 58 pop eax
:00403AB2 894590 mov dword ptr [ebp-70], eax
:00403AB5 894DA8 mov dword ptr [ebp-58], ecx
:00403AB8 8945A0 mov dword ptr [ebp-60], eax
* Possible StringData Ref from Code Obj ->"PPassword"
|
:00403ABB C78578FFFFFFD0224000 mov dword ptr [ebp+FFFFFF78], 004022D0
:00403AC5 89B570FFFFFF mov dword ptr [ebp+FFFFFF70], esi
:00403ACB 8D9570FFFFFF lea edx, dword ptr [ebp+FFFFFF70]
:00403AD1 8D4DB0 lea ecx, dword ptr [ebp-50]
* Reference To: MSVBVM60.__vbaVarDup, Ord:0000h
|
:00403AD4 E83FD8FFFF Call 00401318
* Possible StringData Ref from Code Obj ->"YYou have not supplied a password."
|
:00403AD9 C7458888224000 mov [ebp-78], 00402288
:00403AE0 897580 mov dword ptr [ebp-80], esi
:00403AE3 8D5580 lea edx, dword ptr [ebp-80]
:00403AE6 8D4DC0 lea ecx, dword ptr [ebp-40]
This is the nag you get if you do not enter anything for the password:
The one I am really interested in is this one.
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00403B7F(C)
|
:00403C0A B904000280 mov ecx, 80020004
:00403C0F 894D98 mov dword ptr [ebp-68], ecx
:00403C12 6A0A push 0000000A
:00403C14 58 pop eax
:00403C15 894590 mov dword ptr [ebp-70], eax
:00403C18 894DA8 mov dword ptr [ebp-58], ecx
:00403C1B 8945A0 mov dword ptr [ebp-60], eax
* Possible StringData Ref from Code Obj ->"PPassword"
|
:00403C1E C78578FFFFFFD0224000 mov dword ptr [ebp+FFFFFF78], 004022D0
:00403C28 89B570FFFFFF mov dword ptr [ebp+FFFFFF70], esi
:00403C2E 8D9570FFFFFF lea edx, dword ptr [ebp+FFFFFF70]
:00403C34 8D4DB0 lea ecx, dword ptr [ebp-50]
* Reference To: MSVBVM60.__vbaVarDup, Ord:0000h
|
:00403C37 E8DCD6FFFF Call 00401318
* Possible StringData Ref from Code Obj ->"YYou have supplied a worng password."
|
:00403C3C C74588E8224000 mov [ebp-78], 004022E8
:00403C43 897580 mov dword ptr [ebp-80], esi
:00403C46 8D5580 lea edx, dword ptr [ebp-80]
:00403C49 8D4DC0 lea ecx, dword ptr [ebp-40]
This is the nag you get when you enter a wrong password.
So lets see where is comes from:
I see this a little ways up in the code:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00403B7F(C)
Let’s go check that out…
* Reference To: MSVBVM60.__vbaStrCmp, Ord:0000h
|
:00403B78 E88BD8FFFF Call 00401408
:00403B7D 85C0 test eax, eax
:00403B7F 0F8585000000 jne 00403C0A
:00403B85 390568534000 cmp dword ptr [00405368], eax
:00403B8B 750F jne 00403B9C
:00403B8D 6868534000 push 00405368
:00403B92 681C224000 push 0040221C
Well now I think we can start Olly up and set us a breakpoint on:
:00403B78 E88BD8FFFF Call 00401408
This call is a string compare for VB600 so you can expect for a string to be compared to another one.
Open up the int21h.exe in Olly and set a breakpoint @ address 00403B78.
Run the program <F9>. When the messagebox comes up asking for the password enter this; 12345678 then hit the <Ok> button.
Olly breaks and we see this in the stack window:
0012F404 0014C954 UNICODE "int21h"
0012F408 0014C6D4 UNICODE "12345678"
I would guess that the password would be <int21h>.
Anyway hit <F8> and go through the call.
You should now be here:
:00403B7D 85C0 test eax, eax
Right click the register EAX and then select <Zero>.
Keep hitting <F8> till you are here:
:00403BFE E821D7FFFF Call 00401324
Now hit <F7>
Hit <F7> again.
You should now be here:
6AAA7C33 > 55 PUSH EBP
Now hit <F8> till you reach this:
6AAA7D64 FF15 0C119D6A CALL DWORD PTR DS:[<&KERNEL32.CreateProc>; kernel32.CreateProcessW
I wonder what the stack looks like:
Take a look:
0012F374 00000000 |ModuleFileName = NULL
0012F378 0014CC14 |CommandLine = "C:targetz__70043.exe"
0012F37C 00000000 |pProcessSecurity = NULL
0012F380 00000000 |pThreadSecurity = NULL
0012F384 00000000 |InheritHandles = FALSE
0012F388 00000000 |CreationFlags = 0
0012F38C 00000000 |pEnvironment = NULL
0012F390 00000000 |CurrentDir = NULL
0012F394 0012F3A8 |pStartupInfo = 0012F3A8
0012F398 0012F3EC pProcessInfo = 0012F3EC
I do not remember that file being in my folder.
Go to the folder where you put this target <int21h.exe> You should see a hidden file for me it is: z__70043.exe.
And yes that is the file. Now you can work on it as you would like.
What is interesting to note is that if you run the program and you enter an invalid password it will erase the hidden file.
Have Fun:
int21h
-
Level : beginner
Basic disassembly on linux platform using only Objdump.
Target is the small linux starter crackme.
Reversing A Simple Linux App with OBJDUMP
TARGET = here
DIFF = Tomato ketchup its just Diff
TOOLS USED = OBJDUMP
Requirements
1) I assume that you have some flavour of linux installed in your computer
2) by prompt i mean prompt on your commandline like you@someserver:~>
3) you have the standard binutils avl in this installation
Let's Start
First we will investigate this linux binary with some tools that are included in almost every distribution (binutils package). For easier reversing we will use some customized tools. Find them here.
Ofcourse we could use IDA Pro or another commercial disassembler, but for this tuts we stick with tools that are freely available.
The process of investigating a linux / unix binary is not that different from windows binaries. We start by doing a quick scan to see if the binary is packed/stripped, the we check its header (ELF header, not PE this time ;) ), look for interesting strings, and finally we dig in the disassembly.
Let's start our deadlisting...
Binary details
First off all we use the file command to know the details about the file we are examining
On the prompt > type without quotes
"file small"
And press enter
You will get an output in this format
small: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
for GNU/Linux 2.2.5, dynamically linked (uses shared libs), not stripped
We come to know that this app is not stripped So most of the function calls will look like
call <libc.cout>
Instead of call $0x80485634
If it has been stripped we need to look for symbol table and Our task becomes a little more difficult
Strings
Ok we now look for strings inside that file
On the prompt > type without quotes
"strings small"
And press enter
You see lot of strings along with these strings in side this app
-> Small crackme for stingduk
Disassembling with objdump
Ok we will look at the headers inside this file
On the prompt > type without quotes
" objdump -h small"
And press enter
The output is as follows
small: file format elf32-i386
Sections:
Idx Name Size VMA LMA File off Algn
12 .fini 0000001a 08048a94 08048a94 00000a94 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
13 .rodata 000000d0 08048ac0 08048ac0 00000ac0 2**5
CONTENTS, ALLOC, LOAD, READONLY, DATA
14 .eh_frame_hdr 0000002c 08048b90 08048b90 00000b90 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 .data 0000000c 08049bbc 08049bbc 00000bbc 2**2
CONTENTS, ALLOC, LOAD, DATA
16 .eh_frame 000000a0 08049bc8 08049bc8 00000bc8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
17 .dynamic 000000e0 08049c68 08049c68 00000c68 2**2
CONTENTS, ALLOC, LOAD, DATA
18 .ctors 0000000c 08049d48 08049d48 00000d48 2**2
CONTENTS, ALLOC, LOAD, DATA
19 .dtors 00000008 08049d54 08049d54 00000d54 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .jcr 00000004 08049d5c 08049d5c 00000d5c 2**2
CONTENTS, ALLOC, LOAD, DATA
21 .got 00000038 08049d60 08049d60 00000d60 2**2
CONTENTS, ALLOC, LOAD, DATA
22 .bss 00000128 08049d98 08049d98 00000d98 2**3
ALLOC
23 .comment 000000d9 00000000 00000000 00000d98 2**0
We need to dissemble the file now
On the prompt > type without quotes
" objdump -d small > dump "
And press enter
Open this file with cat dump | more
If you scroll through the dump you will see the main function like I pasted below
Though it is absolutely ok it would be better to look at the disassembly
If we dont have those hex bytes displayed it would also be better to look at
Disassembly if we see0xffffffff(%ebx) as -0x01(%ebx)
Also some xrefs to data section and jump xrefs on the disassembly would also be fine
For analyzing the disassembly easily
804884a: c7 85 34 ff ff ff 00 movl $0x0,0xffffff34(%ebp)
8048851: 00 00 00
8048854: 8d 45 b8 lea 0xffffffb8(%ebp),%eax
8048857: 03 85 34 ff ff ff add 0xffffff34(%ebp),%eax
804885d: 80 38 00 cmpb $0x0,(%eax)
8048860: 75 02 jne 8048864
8048862: eb 42 jmp 80488a6
8048864: 8d 85 38 ff ff ff lea 0xffffff38(%ebp),%eax
804886a: 89 c1 mov %eax,%ecx
804886c: 03 8d 34 ff ff ff add 0xffffff34(%ebp),%ecx
8048872: 8d 45 b8 lea 0xffffffb8(%ebp),%eax
8048875: 03 85 34 ff ff ff add 0xffffff34(%ebp),%eax
804887b: 8a 00 mov (%eax),%al
804887d: 66 0f be d0 movsbw %al,%dx
8048881: 66 c7 85 26 ff ff ff movw $0xa,0xffffff26(%ebp)
8048888: 0a 00
804888a: 89 d0 mov %edx,%eax
804888c: 66 99 cwtd
804888e: 66 f7 bd 26 ff ff ff idivw 0xffffff26(%ebp)
8048895: 88 d0 mov %dl,%al
8048897: 83 c0 30 add $0x30,%eax
804889a: 88 01 mov %al,(%ecx)
804889c: 8d 85 34 ff ff ff lea 0xffffff34(%ebp),%eax
80488a2: ff 00 incl (%eax)
80488a4: eb ae jmp 8048854
80488a6: 8d 85 38 ff ff ff lea 0xffffff38(%ebp),%eax
Tools of the trade
we use an utility called gendump that was coded by Dion Mendel
for his prize winning honeynet reverse-engineering entry it basically uses objdump but gives a little more visually attractive display along with routines
on the prompt > type without quotes
"./gendump small > dump1"
and press enter
you will have a file called dump1 in the directory
you can look at this file by using this command type on prompt
cat dump1 | more
this will output the file page by page
I am showing here only the user code, which is denoted, by main
# 080487bc :
0804884a: movl $0x0,0xffffff34(%ebp)
08048854: lea 0xffffffb8(%ebp),%eax
08048857: add 0xffffff34(%ebp),%eax
0804885d: cmpb $0x0,(%eax)
08048860: jne 0x08048864
08048862: jmp 0x080488a6
08048864: lea 0xffffff38(%ebp),%eax
0804886a: mov %eax,%ecx
0804886c: add 0xffffff34(%ebp),%ecx
08048872: lea 0xffffffb8(%ebp),%eax
08048875: add 0xffffff34(%ebp),%eax
0804887b: mov (%eax),%al
0804887d: movsbw %al,%dx
08048881: movw $0xa,0xffffff26(%ebp)
0804888a: mov %edx,%eax
0804888c: cwtd
0804888e: idivw 0xffffff26(%ebp)
08048895: mov %dl,%al
08048897: add $0x30,%eax
0804889a: mov %al,(%ecx)
0804889c: lea 0xffffff34(%ebp),%eax
080488a2: incl (%eax)
080488a4: jmp 0x08048854
080488a6: lea 0xffffff38(%ebp),%eax
we also use other utility coded by Don Mendel those are
decomp_fixup_signs
decomp_xref_data
decomp_xref_jumps
the first utility will fix unwieldy 0xffffffff(%ebp)
entries to a more readable form like -1(%ebp)
the second utility will point the data that is being used
like push $0x807654 will now look as
possible reference to string "small crackme for stingduk"
the third will show the jump references it looks easier for visualization
on the prompt > type without quotes
"./decomp_fixup_signs dump2"
and press enter
on the prompt > type without quotes
"./decomp_xref_data small dump3 "
and press enter
on the prompt > type without quotes
"./decomp_fixup_jumps dump4"
and press enter
after performing these you will have dump1,dump2,dump3,and dump4 files in your directory
if you want to explore you can use cat "filename" | more to go through the contents
I am pasting here the final content of dump4 main function
# 080487bc :
080487bc: push %ebp
080487bd: mov %esp,%ebp
080487bf: sub $0xe8,%esp
080487c5: and $-0x10,%esp
080487c8: mov $0x0,%eax
080487cd: sub %eax,%esp
080487cf: sub $0x8,%esp
080487d2: push $0x80486d8
080487d7: sub $0xc,%esp
# Possible reference to rodata '-> Small crackme for stingduk
080487e9: add $0x14,%esp
080487ec: push %eax
080487ed: call 0x08048658
080487f2: add $0x10,%esp
080487f5: sub $0x8,%esp
# Possible reference to rodata 'Give me your name (max 50 chars): '
080487f8: push $0x8048b00
# Possible reference to data in bss
080487fd: push $0x8049d98
08048802: call 0x08048698
08048807: add $0x10,%esp
0804880a: sub $0x8,%esp
0804880d: lea -0x48(%ebp),%eax
08048810: push %eax
# Possible reference to data in bss
08048811: push $0x8049e28
08048816: call 0x080486c8
0804881b: add $0x10,%esp
0804881e: sub $0x8,%esp
# Possible reference to rodata 'Pass me the serial (max 50 chars): '
08048821: push $0x8048b40
# Possible reference to data in bss
08048826: push $0x8049d98
0804882b: call 0x08048698
08048830: add $0x10,%esp
08048833: sub $0x8,%esp
08048836: lea -0x88(%ebp),%eax
0804883c: push %eax
# Possible reference to data in bss
0804883d: push $0x8049e28
08048842: call 0x080486c8
08048847: add $0x10,%esp
0804884a: movl $0x0,-0xcc(%ebp)
08048854: lea -0x48(%ebp),%eax *
08048857: add -0xcc(%ebp),%eax |
0804885d: cmpb $0x0,(%eax) |
08048860: jne 0x08048864 * |
08048862: jmp 0x080488a6 |*|
08048864: lea -0xc8(%ebp),%eax *||
0804886a: mov %eax,%ecx ||
0804886c: add -0xcc(%ebp),%ecx ||
08048872: lea -0x48(%ebp),%eax ||
08048875: add -0xcc(%ebp),%eax ||
0804887b: mov (%eax),%al ||
0804887d: movsbw %al,%dx ||
08048881: movw $0xa,-0xda(%ebp) ||
0804888a: mov %edx,%eax ||
0804888c: cwtd ||
0804888e: idivw -0xda(%ebp) ||
08048895: mov %dl,%al ||
08048897: add $0x30,%eax ||
0804889a: mov %al,(%ecx) ||
0804889c: lea -0xcc(%ebp),%eax ||
080488a2: incl (%eax) ||
080488a4: jmp 0x08048854 |*
080488a6: lea -0xc8(%ebp),%eax *
080488ac: add -0xcc(%ebp),%eax
080488b2: movb $0x0,(%eax)
080488b5: lea -0x88(%ebp),%eax
080488bb: lea -0xc8(%ebp),%edx
080488c1: sub $0x8,%esp
080488c4: push %eax
080488c5: push %edx
080488c6: call 0x08048668
080488cb: add $0x10,%esp
080488ce: test %eax,%eax
080488d0: jne 0x080488fa *
080488d2: sub $0x8,%esp |
080488d5: push $0x80486d8 |
080488da: sub $0xc,%esp |
# Possible reference to rodata 'Great work!'
080488dd: push $0x8048b64 |
# Possible reference to data in bss
080488e2: push $0x8049d98 |
080488e7: call 0x08048698 |
080488ec: add $0x14,%esp |
080488ef: push %eax |
080488f0: call 0x08048658 |
080488f5: add $0x10,%esp |
080488f8: jmp 0x08048920 *|
080488fa: sub $0x8,%esp |*
080488fd: push $0x80486d8 |
08048902: sub $0xc,%esp |
# Possible reference to rodata 'No luck here mate :('
08048905: push $0x8048b70 |
# Possible reference to data in bss
0804890a: push $0x8049d98 |
0804890f: call 0x08048698 |
08048914: add $0x14,%esp |
08048917: push %eax |
08048918: call 0x08048658 |
0804891d: add $0x10,%esp |
08048920: mov $0x0,%eax *
08048925: leave
08048926: ret
08048927: nop
Analysis
well we can now analyze the executable and see how it works we notice init is called after every push of a string so we can safely assume this to be a printf or cout function this takes a format parameter and input parameter and prints to stderr
yes we see there are two pushes to each of the call one is a string and other is from bss section which would be the format like %X,%c,%s etc
ok we leave it at it now and go down to a place where it we have entered our serial we see a loop starting from 8048854
0804884a: movl $0x0,-0xcc(%ebp) ;moves 0 in [ebp-0xcc]
08048854: lea -0x48(%ebp),%eax * ;loads address of name string :)
08048857: add -0xcc(%ebp),%eax | ;well effectively adds 0 [ebp-0xcc] to eax
0804885d: cmpb $0x0,(%eax) | ;tests if eax contains 0
08048860: jne 0x08048864 * | ;if not 0 jumps to 64 else go down
08048862: jmp 0x080488a6 |*| ;basically jumps to end of loop
08048864: lea -0xc8(%ebp),%eax *|| ;loads address in [ebp-0xc8] strlength :)
0804886a: mov %eax,%ecx || ;moves the content of eax to ecx
0804886c: add -0xcc(%ebp),%ecx || ;adds 0 to ecx in first round [ebp-0xcc] = 0
08048872: lea -0x48(%ebp),%eax || ;loads address in [ebp-0x48] to eax
08048875: add -0xcc(%ebp),%eax || ;adds 0 to eax in first round [ebp-0xcc] = 0
0804887b: mov (%eax),%al || ;eax will become equal to al
0804887d: movsbw %al,%dx || ;moves the content of al to dx
08048881: movw $0xa,-0xda(%ebp) || ;moves 0xa to [ebp-0xda]
0804888a: mov %edx,%eax || ;moves contents of edx to eax
0804888c: cwtd || ; preparing for division
0804888e: idivw -0xda(%ebp) || ;divides eax by [ebp-0xda] == eax/0xa(10 dec)
08048895: mov %dl,%al || ;moves remainder of the above division to al
08048897: add $0x30,%eax || ;adds 0x30 to remainder of division
0804889a: mov %al,(%ecx) || ;moves the (remainder+0x30) to ecx
0804889c: lea -0xcc(%ebp),%eax || ;well effectively adds 0 [ebp-0xcc] to eax
080488a2: incl (%eax) || ;adds 1 to [eax] == [ebp-0xcc]
080488a4: jmp 0x08048854 |* ;jumps back to start of loop
080488a6: lea -0xc8(%ebp),%eax *
so we can safely assume the serial calculation routine is
== ((chars in name string % 0xa)+0x30)
and when all chars are done it jumps to 080488a6: lea -0xc8(%ebp),%eax *
here is the comparison routine
080488a6: lea -0xc8(%ebp),%eax ;loads eax with [ebp_0xc8]
080488ac: add -0xcc(%ebp),%eax ;adds eax with [ebp_0xc8]
080488b2: movb $0x0,(%eax) ;appends null terminator to calculated hash
080488b5: lea -0x88(%ebp),%eax ;loads eax with [ebp-0x88]
080488bb: lea -0xc8(%ebp),%edx ;loads edx with [ebp-0xc8]
080488c1: sub $0x8,%esp
080488c4: push %eax
080488c5: push %edx
080488c6: call 0x08048668 ;must be strcmp
080488cb: add $0x10,%esp
080488ce: test %eax,%eax ;test
080488d0: jne 0x080488fa ;good guy bad guy jump
Outtro
Till now this was all about deadlisting and analyzing the executable without running it
now we will run the gnu debugger gdb and do a live debugging on the executable in the second part
of this tutorial :) keep looking
-
Level : beginner
Manually unpacking PEid v0.93
It was 10:39 at night,when I decided to see what kind of API's PEid v.093 used
because I was curious about some stuff.Unfortunatelly,this version of PEid is
protected by a protector.So,I opened another version of PEid and identified the packer
as SPLayer 0.08 -> Jibz.
Now only tools needed are: Olly v1.10,OllyDump Plugin,command line plugin,ImpRec v1.6f
Open the PEid (target) using Olly and u are at the Entry Point here:
00425FDA > ? 8D40 00 LEA EAX,DWORD PTR DS:[EAX]
00425FDD . B9 CC5F4200 MOV ECX,PEiD.00425FCC
00425FE2 . 6A 0E PUSH 0E
00425FE4 . 58 POP EAX
00425FE5 > C00C01 04 ROR BYTE PTR DS:[ECX+EAX],4
00425FE9 . 48 DEC EAX
00425FEA .^75 F9 JNZ SHORT PEiD.00425FE5
Now,in exceptions be sure that you have checked only ignore memory access violations in KERNEL32.
Press Shift+F9 one time,and Olly breaks at an exception,here:
00401016 89 DB 89
00401017 08 DB 08
00401018 50 DB 50 ; CHAR 'P'
00401019 45 DB 45 ; CHAR 'E'
0040101A 43 DB 43 ; CHAR 'C'
0040101B 32 DB 32 ; CHAR '2'
0040101C 00 DB 00
0040101D 8E DB 8E
0040101E E0 DB E0
Now,press the "M" buttont in Olly,and you can see the Image of memory.Right click and
set break-on-acess at code section (.text).Now press Shift+F9 once more and the
breakpoint is triggered,Olly pauses at code section,here:
00479599 C602 E9 MOV BYTE PTR DS:[EDX],0E9
0047959C 83C2 05 ADD EDX,5
0047959F 2BCA SUB ECX,EDX
004795A1 894A FC MOV DWORD PTR DS:[EDX-4],ECX
004795A4 33C0 XOR EAX,EAX
004795A6 C3 RETN
Now start tracing using F7 and execute the RETN,and you are at windows API's
memory here:
77F79BA4 64:8B25 00000000 MOV ESP,DWORD PTR FS:[0]
77F79BAB 64:8F05 00000000 POP DWORD PTR FS:[0]
77F79BB2 8BE5 MOV ESP,EBP
77F79BB4 5D POP EBP
77F79BB5 C2 1400 RETN 14
Well,if u continue tracing with F7,the code will lead to an opcode like this:
7FFE0300 8BD4 MOV EDX,ESP
7FFE0302 0F34 SYSENTER ; Jamps at kernel,Ring-3 debuggers cannot proceed debugging
This opcode will jamp at kernel,where Olly as a Ring-3 debugger cannot proceed the
debugging,and will just run the exe,bypassing the OEP where we need to land.So set
again a break-on-access breakpoint when being at 77F79BA4,in code (.text) section and
press Shift+F9 one more time and we land here:
00401016 .-E9 8C850700 JMP PEiD.004795A7
Trace the JMP using F7 and we land Here:
004795A7 B8 6884BAFF MOV EAX,FFBA8468
004795AC 64:8F05 00000000 POP DWORD PTR FS:[0]
004795B3 83C4 04 ADD ESP,4
004795B6 55 PUSH EBP
004795B7 53 PUSH EBX
004795B8 51 PUSH ECX
004795B9 57 PUSH EDI
004795BA 56 PUSH ESI
004795BB 52 PUSH EDX
004795BC 8D98 F8108D00 LEA EBX,DWORD PTR DS:[EAX+8D10F8]
Now trace till 004795BC.Those pushes you see,are the basic register pushes that a packer
saves in stack,then executes its code and at the end pops them back from stack and jamps at OEP.
So,when you are at 004795BC,go to ESP register and right click->Follow in Dump.In Dump window
you see this:
0012FFAC 04 03 FE 7F D5 BB 12 00 F7 03 00 00 00 00 00 00 þÕ».÷......
0012FFBC 00 F0 FD 7F F0 FF 12 00 C7 14 E8 77 F7 03 00 00 .ðýðÿ.Çèw÷..
0012FFCC D4 BB 12 00 00 F0 FD 7F F0 DC B1 F1 C8 FF 12 00 Ô»..ðýðܱñÈÿ.
0012FFDC 8F C8 53 80 FF FF FF FF 09 48 E9 77 10 12 E9 77 ÈSÿÿÿÿ.Héwéw
0012FFEC 00 00 00 00 00 00 00 00 00 00 00 00 DA 5F 42 00 ............Ú_B.
0012FFFC 00 00 00 00 ....
Now highlight the four bytes at 0012FFAC (04 03 FE 7F) and right click->Breakpoint->
Hardware on access->Dword.This will trigger Olly to pause when ESP has the same
value as now (0012FFAC).This will happen when the basic registers will be poped
and just before the Magic jamp at OEP.This trick can be used in many simple
packers,but of cource NOT all packers.
Now press one more time Shift+F9 and the breakpoint is triggered,we land here:
0047964A 5E POP ESI ; PEiD.00455F1E
0047964B 5F POP EDI
0047964C 59 POP ECX
0047964D 5B POP EBX
0047964E 5D POP EBP
0047964F FFE0 JMP EAX
Remove hardware breakpoint.So,you are at 0047964A,trace a little more using F7 and the
magic jamp at 0047964F goes you at OEP,so execute it and we land here:
00455F1E 6A DB 6A ; CHAR 'j'
00455F1F 60 DB 60 ; CHAR '`'
00455F20 68 DB 68 ; CHAR 'h'
00455F21 08 DB 08
00455F22 F5 DB F5
00455F23 42 DB 42 ; CHAR 'B'
00455F24 00 DB 00
00455F25 E8 DB E8
00455F26 B2 DB B2
We cannot see the code,so right click on it->Analysis->Analyze code.You see that:
00455F1E . 6A 60 PUSH 60
00455F20 . 68 08F54200 PUSH PEiD.0042F508
00455F25 . E8 B2180000 CALL PEiD.004577DC
00455F2A . BF 94000000 MOV EDI,94
00455F2F . 8BC7 MOV EAX,EDI
00455F31 . E8 1AFAFFFF CALL PEiD.00455950
00455F36 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
00455F39 . 8BF4 MOV ESI,ESP
00455F3B . 893E MOV DWORD PTR DS:[ESI],EDI
00455F3D . 56 PUSH ESI ; /pVersionInformation
00455F3E . FF15 90114000 CALL DWORD PTR DS:[401190] ; GetVersionExA
00455F44 . 8B4E 10 MOV ECX,DWORD PTR DS:[ESI+10]
So OEP=00455F1E.Now dump the process using OllyDump,without having Import Rebuilding Option
checked.Now open ImpRec,select the process and enter as OEP=OEP as seen in Olly-Imagebase=
00455F1E-00400000=00055F1E.Then IAT Autoseach and Get Imports.Now press Show invalid.Good,no
Invalid thunks,so proceed and Fix Dump.
Now go for it,and run the fixed dump.Wow,it runs just fine.Target Unpacked!
Have in mind that if U ran the fixed dumped exe under Olly,it will check if there is
a Debugger using IsDebuggerPresent API.So you should either have OllyHide Plugin
or just place a breakpoit at IsDebuggerPresent,and when Olly breaks,trace till
user code and make EAX=0.
-
Level : intermediate
How to rebuild an IAT from scratch using ImpRec.
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$ RebuilDing IAT From Scratch Using ImpRec Tutorial by KaGra $$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
Tools used: ImpRec and Olly v1.10
This is a tutorial about rebuilding the IAT in unpacked exe files that their
table has been completelly destroyed but the packer-protector.An example of
such packers are Armadillo and SDprotector.I assume that U know tha basics
so I won't say much of details.
Well,ImpRec has a great feature that is called Load-Save tree.U enter the general
structure of the IAT u want Imprec to create in a txt file,and with Load Tree U
can inject this in the exe.Then Imprec will create this IAt.The save tree dumpes in
a txt file the current IAT of the exe,so U can make modifications and Load it back
again,or do anything U like.
Assume we have a simple exe (in zip,is simple.exe).Just run it,open ImpRec and
select IAT autosearch and the Get imports.Then,Save tree to dump the IAT in a txt.
Now open the txt.I will explain to U the basic format of it,so U can change anything
U may want (I saved it as simple.txt in zip):
Target: C:Documents and SettingsasdDesktopsimple.exe
OEP: 00001000 IATRVA: 00002000 IATSize: 00000058
FThunk: 00002000 NbFunc: 00000003
1 00002000 kernel32.dll 016F GetModuleHandleA
1 00002004 kernel32.dll 03A0 lstrcmp
1 00002008 kernel32.dll 00B0 ExitProcess
FThunk: 00002010 NbFunc: 00000011
1 00002010 user32.dll 0061 CreateWindowExA
1 00002014 user32.dll 008F DefWindowProcA
bla...bla...bla...
Well,OEP is the Original Entry Point,IAT RVA is the virtual address where the start of
the IAT in the exe is and IAT size,what it sais.First number after FThunk string is
the RVA of the first API in IAT,for the API os this same dll and the NbFunc is the
number of all APIs in this dll.After the line of FThunk string,are lines that describe
the API's.The first number (here is 1) says that API is valid (check simple.txt for
more info about it).The second number is the Virtual address that fills with the
Address of the first opcode of the API that a line of this txt is reffering to.The
next is a string that says which dll has this API code,the next number is a number
that makes as to know the relative position of this API,in the IAT and among all
other API's inside IAT and finally the last is the string name of the API.
Let us see the IAT of simple.exe now,then take a line from the simple.txt and
see how those things described in a line of txt can be seen in the exe.So,the IAT
of simple.txt is that (.rdata section in Olly's memory image):
00402000 >86 AD E7 77 EC 5D E7 77 FD 98 E7 77 00 00 00 00 †çwì]çwý˜çw....
00402010 >26 8B D4 77 55 5C D4 77 81 43 D4 77 DC 79 D4 77 &‹ÔwUÔwCÔwÜyÔw
00402020 >0A 1A D6 77 8F 43 D4 77 17 60 D4 77 81 9C D4 77 .ÖwCÔw`ÔwœÔw
00402030 >24 97 D4 77 76 64 D6 77 E9 B9 D4 77 92 BD D4 77 $—ÔwvdÖwé¹Ôw’½Ôw
00402040 >03 6E D5 77 05 79 D4 77 14 79 D4 77 FA 3D D4 77 nÕwyÔwyÔwú=Ôw
00402050 >89 5B D4 77 00 00 00 00 00 00 00 00 00 00 00 00 ‰[Ôw............
00402060 00 00 00 00 3B 27 F9 3C 00 00 00 00 01 00 00 00 ....;'ù<.......
00402070 15 0B 00 00 00 00 00 00 00 1E 00 00 00 00 00 00 .............
00402080 3B 27 F9 3C 00 00 00 00 04 00 00 00 10 01 00 00 ;'ù<.........
00402090 00 00 00 00 18 29 00 00 00 00 00 00 00 00 00 00 ....)..........
004020A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004020B0 00 00 00 00 F0 20 00 00 00 00 00 00 00 00 00 00 ....ð ..........
004020C0 76 21 00 00 00 20 00 00 00 21 00 00 00 00 00 00 v!... ...!......
004020D0 00 00 00 00 A2 22 00 00 10 20 00 00 00 00 00 00 ....¢".. ......
004020E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
004020F0 56 21 00 00 6A 21 00 00 48 21 00 00 00 00 00 00 V!..j!..H!......
00402100 84 21 00 00 96 21 00 00 A8 21 00 00 BC 21 00 00 „!..–!..¨!..¼!..
00402110 CC 21 00 00 DE 21 00 00 EC 21 00 00 00 22 00 00 Ì!..Þ!..ì!..."..
00402120 0E 22 00 00 1A 22 00 00 28 22 00 00 3A 22 00 00 ".."..("..:"..
00402130 4E 22 00 00 60 22 00 00 70 22 00 00 7E 22 00 00 N"..`"..p"..~"..
00402140 92 22 00 00 00 00 00 00 75 00 45 78 69 74 50 72 ’"......u.ExitPr
00402150 6F 63 65 73 73 00 11 01 47 65 74 4D 6F 64 75 6C ocess.GetModul
00402160 65 48 61 6E 64 6C 65 41 00 00 D6 02 6C 73 74 72 eHandleA..Ölstr
00402170 63 6D 70 41 00 00 4B 45 52 4E 45 4C 33 32 2E 64 cmpA..KERNEL32.d
00402180 6C 6C 00 00 58 00 43 72 65 61 74 65 57 69 6E 64 ll..X.CreateWind
00402190 6F 77 45 78 41 00 83 00 44 65 66 57 69 6E 64 6F owExA.ƒ.DefWindo
004021A0 77 50 72 6F 63 41 00 00 94 00 44 69 73 70 61 74 wProcA..”.Dispat
004021B0 63 68 4D 65 73 73 61 67 65 41 00 00 B6 00 45 6E chMessageA..¶.En
004021C0 61 62 6C 65 57 69 6E 64 6F 77 00 00 02 01 47 65 ableWindow..Ge
004021D0 74 44 6C 67 49 74 65 6D 54 65 78 74 41 00 28 01 tDlgItemTextA.(
004021E0 47 65 74 4D 65 73 73 61 67 65 41 00 43 01 47 65 GetMessageA.CGe
004021F0 74 53 79 73 74 65 6D 4D 65 74 72 69 63 73 00 00 tSystemMetrics..
00402200 97 01 4C 6F 61 64 43 75 72 73 6F 72 41 00 9B 01 —LoadCursorA.›
00402210 4C 6F 61 64 49 63 6F 6E 41 00 BB 01 4D 65 73 73 LoadIconA.»Mess
00402220 61 67 65 42 6F 78 41 00 DD 01 50 6F 73 74 51 75 ageBoxA.ÝPostQu
00402230 69 74 4D 65 73 73 61 67 65 00 EF 01 52 65 67 69 itMessage.ïRegi
00402240 73 74 65 72 43 6C 61 73 73 45 78 41 00 00 28 02 sterClassExA..(
00402250 53 65 74 44 6C 67 49 74 65 6D 54 65 78 74 41 00 SetDlgItemTextA.
00402260 56 02 53 65 74 57 69 6E 64 6F 77 50 6F 73 00 00 VSetWindowPos..
00402270 65 02 53 68 6F 77 57 69 6E 64 6F 77 00 00 7D 02 eShowWindow..}
00402280 54 72 61 6E 73 6C 61 74 65 4D 65 73 73 61 67 65 TranslateMessage
00402290 00 00 8B 02 55 70 64 61 74 65 57 69 6E 64 6F 77 ..‹UpdateWindow
004022A0 00 00 55 53 45 52 33 32 2E 64 6C 6C 00 00 00 00 ..USER32.dll....
Let's take a line from the simple.txt file:
1 00002008 kernel32.dll 00B0 ExitProcess
So,this line says thet the address of this API is at 00002008 (+ImageBase=00400000) so
lets look at 00402008,there are those strings:
FD 98 E7 77
Inreverse,this is the start of code of ExitProcess,check it in Olly doing a search
at all modules name (well,your numbers may be different because of the version of
Windows,but still pointing at ExitProcess).ExitProcess belongs to kernel32.dll as U
also can see in Olly.The final value of 00B0 is the same for every IAT txt dump that
U will make in your system,in every exe.Don't change this.It just is an ID in case we
don't want to declare a name for the API.Just let it be the same that is at any exe IAT
dump.
Ok.Assume now that U have the dump of an exe,U have the OEP and manually U have
traced and found what calls are made to APIs.But there is not any valid IAT,and Imprec
doesn't find any imports to validate.If there is not an IAT at all,meaning that all the above
IAT from 00402000 are filled with 00's,then Imprec doesn't give U anyhing to fix manually.So,
just assume U have the following code of the exe (it is target.exe in zip):
00401000 > $ 6A 00 PUSH 0
00401002 . 68 04204000 PUSH dumped.00402004 ; ASCII "Hey man ;)"
00401007 . 68 0F204000 PUSH dumped.0040200F ; ASCII "Try to Run Me under Olly or See APIs using WinDasm"
0040100C . 6A 00 PUSH 0
0040100E . E8 0D000000 CALL dumped.00401020
00401013 . 6A 00 PUSH 0
00401015 . E8 00000000 CALL dumped.0040101A
0040101A FF25 4C304000 JMP DWORD PTR DS:[40304C]
00401020 FF25 54304000 JMP DWORD PTR DS:[403054]
Now,assume U know that the first call goes at MessageBoxA and the second at ExitProcess.U can
also see the section where tha IAT should have been:
00403000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00403010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00403020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00403030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00403040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00403050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00403060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00403070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
So,U have to create a txt file,with the proper format and load it in ImpRec.So,U have
OEP=00001000 and IATRVA=00002000 and u can choose any size (big enough though to fit the
new IAT).Choose also the RVA's of the addresses of API's U like.Generally,a good idea is to
just dump from a known exe and IAT in a txt with Imprec,find those API's U are interested
to fix in new exe,and erase all the others,fixing also the RVA's properelly to your
section where the IAT is to be created.Do,in our case,a good txt file is:
Target: C:Documents and SettingsasdDesktopdumped.exe
OEP: 00001000 IATRVA: 00003000 IATSize: 00000070
FThunk: 0000304C NbFunc: 00000001
1 0000304C kernel32.dll 00B0 ExitProcess
FThunk: 00003054 NbFunc: 00000001
1 00003054 user32.dll 010E MessageBoxA
Now,load it as tree in ImpRec and Fix the dump.Open the new fixed exe and see the new Import
that is created by ImpRec:
00405000 00 00 00 00 00 00 00 00 00 00 00 00 3C 50 00 00 ............<P..
00405010 4C 30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 L0..............
00405020 58 50 00 00 54 30 00 00 00 00 00 00 00 00 00 00 XP..T0..........
00405030 00 00 00 00 00 00 00 00 00 00 00 00 6B 65 72 6E ............kern
00405040 65 6C 33 32 2E 64 6C 6C 00 00 B0 00 45 78 69 74 el32.dll..°.Exit
00405050 50 72 6F 63 65 73 73 00 75 73 65 72 33 32 2E 64 Process.user32.d
00405060 6C 6C 00 00 0E 01 4D 65 73 73 61 67 65 42 6F 78 ll..MessageBox
00405070 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 A...............
Althought IATRVA was 00003000,the fixed has RVA=00005000,because we have checked in
ImpRec to create a new section for IAT,don't mind.It's working just fine.
Have in your mind that the jamps in code section,at 0040101A may also have been
erased from packer,and U may have either to write them as code after IAT rebuilding
or just create a way to emulate them,at any part of your code or other section U may
create.
-
Level : intermediate
How you can block a Ring level 3 debugger.
See this tutor using notepad,Font Fixedsys Regular,size 10
Well,since my last Tutor many packerz got into my handz.And I should say that
the only one I saw having a Ring-3 Debugger Blocker is Armadillo.But why does not
the otherz use this wonderfull feature?The answer is I DON'T KNOW.
Well,creating such a feature is not difficult.The general idea is this:
Olly, (which is Ring-3) when we load an exe in her,locates dlls that the exe
will use (actually,will use the API's in those dll's) from their names in the
Import Address Table.Then,loads those dll's in memory that is also now being
allocated for this purpose.After that,fills the IAT with the API's addresses
that reside in the mentioned loaded dll's.The thing is that if we run this exe
now,and this exe creates a new proccess,Olly will not be able to breakpoint at
any API that is being used from this second process (although those API's are
part of the SAME dll code that has been allocated from the first loaded exe).And
this is because the allocated space that contains the API's (and dll's) is only
a copy of the original,and not the original dll memory space,because it is
a RinG-3 debugger.So,the new process created by the first exe will allocate
its own memory space from windows loader and will call from there the API's and
not from the allocated by Olly (Olly playz actually the windows loader for
the first exe).So,putting any kind of breakpoint in Olly (software or hardware)
will NOT trigger the API's that are called from the second proccess,thus making
Olly useless.
The procedure to follow is simple:
a) The original exe creates a process of itself.
b) Patches itself in memory,because if not then the new process will create another
one,and this another one etc. etc.
c) The process of the original exe (father) exits
d) Now the second proccess (child) continues running and Olly cannot
breakpoint on it.
These API's will be used in order to create the first original exe (the exe anyway):
1) CreateProcessA => To create the new proccess (child) ,which is actually a copy of the father
2) WriteProcessMemory => To patch the first process
3) ResumeThread => After patching child continues to execute and is Free!
4) MessageBoxA => This is actually the usefull code of the prog and cannot be
breakpointed from Olly.
5) ExitProcess => To exit the process
HeRe follows the source code of the exe:
00401000 >/$ 68 80204000 PUSH father.00402080 ; /pProcessInfo = father.00402080
00401005 |. 68 38204000 PUSH father.00402038 ; |pStartupInfo = father.00402038
0040100A |. 6A 00 PUSH 0 ; |CurrentDir = NULL
0040100C |. 6A 00 PUSH 0 ; |pEnvironment = NULL
0040100E |. 6A 04 PUSH 4 ; |CreationFlags = CREATE_SUSPENDED
00401010 |. 6A 00 PUSH 0 ; |InheritHandles = FALSE
00401012 |. 6A 00 PUSH 0 ; |pThreadSecurity = NULL
00401014 |. 6A 00 PUSH 0 ; |pProcessSecurity = NULL
00401016 |. 6A 00 PUSH 0 ; |CommandLine = NULL
00401018 |. 68 2D204000 PUSH father.0040202D ; |ModuleFileName = "father.exe"
0040101D |. E8 46000000 CALL <JMP.&KERNEL32.CreateProcessA> ; CreateProcessA
00401022 |. B8 00104000 MOV EAX,father.<ModuleEntryPoint>
00401027 |. 6A 00 PUSH 0 ; /pBytesWritten = NULL
00401029 |. 6A 05 PUSH 5 ; |BytesToWrite = 5
0040102B |. 68 90204000 PUSH father.00402090 ; |Buffer = father.00402090
00401030 |. 50 PUSH EAX ; |Address => 401000
00401031 |. FF35 80204000 PUSH DWORD PTR DS:[402080] ; |hProcess = NULL
00401037 |. E8 38000000 CALL <JMP.&KERNEL32.WriteProcessMemory> ; WriteProcessMemory
0040103C |. FF35 84204000 PUSH DWORD PTR DS:[402084] ; /hThread = NULL
00401042 |. E8 27000000 CALL <JMP.&KERNEL32.ResumeThread> ; ResumeThread
00401047 |. 6A 00 PUSH 0 ; /ExitCode = 0
00401049 . E8 2C000000 CALL <JMP.&KERNEL32.ExitProcess> ; ExitProcess
0040104E . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401050 . 68 04204000 PUSH father.00402004 ; |Title = "hay :-)"
00401055 . 68 0C204000 PUSH father.0040200C ; |Text = "Did I secapEd frOm YoUr BPx s???"
0040105A . 6A 00 PUSH 0 ; |hOwner = NULL
0040105C . E8 1F000000 CALL <JMP.&USER32.MessageBoxA> ; MessageBoxA
00401061 . 6A 00 PUSH 0 ; /ExitCode = 0
00401063 . E8 12000000 CALL <JMP.&KERNEL32.ExitProcess> ; ExitProcess
At 0040101D it creates a new process which is a copy of the same exe,and pauses (SUSPENDS)
the execution of it.At 00401037 patches the second process at 00401000 with the opcode
JMP 0040104E.So,now the second process has the same code as the first (father) with the
only changed opcode the first at 00401000.At 00401042 the second (child) process continues
to execute (actually,now starts from 00401000).The father then terminates at 00401049,and
the child starting from 00401000,it jamps at 0040104E,and the messagebox pops up.If u place
a hardware of software or any other kind of breakpoint to MessageBoxA API (whenever u like
it doesn't matter) the API will not be triggered,and any other code may follow after 0040104E.
But what if we open a second Olly and attach to the child proccess?Well,do that and child is
not appearing in the tasks list to attach!So,we cannot attach!The child also cannot be
seen from LordPE.
How can this be defeated?Well,the first way is to make somehow the child to appear in the
attach list somehow (or any other attach list with more advanced dumping toolz) and the
second is to place a direct jamp at the "useful" code (here at 0040104E).
Well,u can also defeat those two.The first can be defeated by creating many many childs and
patching that way,that after the xxx child,will run the useful code.This will make attaching
in process (if anyone manages doing it) very irritating,since he has to do this many times.The
second can be defeated by using large garbadge and code of encryption of any kind.U can also
use code to check if the previouly childs have really run (because if someone patches with
jamps,the child(s) will not run).This is done by checking if there are actually modifications
of code,and will be if child(s) have run,due to the use of WriteProcessMemory.
BoNus: Another small trick to block debuggers and Dasm's
Load the bug.exe in Olly.What?Cannot be loaded.Try to see it's contents in WinDasm.What?
Cannot see the API calls.Now check the header using a hex editor,and see those two values:
LoaderFlags: EDABDDCA
NumberOfRvaAndSizes: BBDCBDFA
Well,because of those two values that are false,the tools are being confused.Change them
to LoaderFlags=0 and NumberOfRvaAndSizes=10h (common values for almost all exe's) and the exe
loads just fine in Olly and Dasm now shows the API calls.File was not packed or any other
kind protected as u may have assumed at first.This works by changing BOTH of them.
Well,this is the end of that.U can make the-ThinG routine much more sophisticated
using debugging API's.See MSDN library and U will get what I mean...
This is the end of it.
-
A little guide for wannabe cracker/reverser.
by Zephyrous/BiW-Reversing
1. Introduction
Hello wannabe cracker/reverser.
if you read this text, i assumed you're zero knowledge in reversing/cracking.
but i dont guarantee you'll become a reverser after reading this text.
btw, in reversing, there is no quickstart to be a good reverser.
but, this guide tends to be a quickstart for you if really want to be a reverser.
you must ready to take a lot pains and great pleasure too (after you find the true path).
there are two term in this lore, reversing and cracking. but it's not same.
not all crackers are reversers but all reversers are crackers.
First at all, i want to warn you before started to read this guide. IMHO, to be a
reverser, its not a good way if you going deep directly to the subject without any
programming language knowledge especially the higher level language like C, C++,
Pascal or even BASIC. It is because most of our target are not written in assembly,
thus the perfect programming language that need to be learnt before reversing
is C. The C programming language is slightly higher level than the
computer core language, assembly language (translated machine language).
Fortunately, C is just a small programming language and not too hard to be learnt.
I guess it take just in 21 days :P. Go learn C and then come back again. :)
I am by myself, not an expert in this subject. Be honest, i am just slightly
above from beginner level. So, we could assume this guide is 'from a beginner to beginners'.
Therefore, i could understand what are the problems of reversing in beginning days.
I hope the readers could understand easily this text.
The basic of reversing or some people call it 'reverse code engineering' is
investigation of compiled binary computer codes or machine codes.
in the early of computer era, computer programs are written by 'hand'. no compiler
exist in the days. the programmer must learn & know the internal languague of any computer
they want to program. the process is very pain and very buggy.
so they though about a compiler to translate human language to computer language.
And today, most computer programs are compiled or at least assembled. thus, the
task will be taken by reverser is to study the compiled or assembled (both in binary)
by disassembling the binary code using a disassembler.
the binary is in 0 and 1 digit maybe look like 100100100101010010101010010100001100111001
its very hard to be read so hexadecimal number being used for easier reading.
hexa number base on 16 digit, 0,1,2,3,4,....,9,A,B,C,D,E,F
817D 0C 10010000 <-- 10000001011111010000110000010000000000010000000000 000000
75 2A <-- 111010100101010
68 C8000000 <-- 110100011001000000000000000000000000000
the code above is converted to hexa, because its more readable than binary number
and below is the mnemonix of the intel processor's opcodes.
CMP DWORD PTR SS:[EBP+C],110 ; 81h is opcode for mnemonix cmp (compare)
JNZ 00002A ; 75h is opcode for mnemonix jnz (jump not zero)
PUSH 0C8 ; 68h is opcode for mnemonix push (push)
* opcodes are computer instruction in binary digit(bit).
** xxh - h mean in hexadecimal, sometimes we use 0x prefix for hexa number
example: 0xFF1A
assembly language is computer nature language but the computer still cant understand
because its just mnemonix code and still readable by human. thats why,
codes in assembly lang. must be assembled to binary opcodes or machine codes.
in this guide, i wont tell you how to convert binary to decimal or to hexa or vice versa
manually. you simply can ask your maths teacher or just google to find the tutes.
it is because we have all have calc.exe to do the jobs. but the knowledge is knowledge
and knowledge is power. you should learn how to convert the base number later.
2. The power of Assembly
in cracking or reversing, we dont reverse the machine code but the disassembled machine
codes in assembly language. so, just forget about this course if you dont want to learn
it or too lazy to learn a new programming language.
since machine codes is formed by binary digit (bit), we should take a lesson how the bit is
formed. because bit is special number, it must be aligned because of it natures.
They are bits, nibbles, bytes, word, dword.
example
--------|-----------------------|-----------------------------------|
bit | a digit, 0 or 1 | 0 or 1 |
--------|-----------------------|-----------------------------------|
nibble | four bits, | 1111, 1001 |
--------|-----------------------|-----------------------------------|
byte | eight bits, | 11111111 |
--------|-----------------------|-----------------------------------|
word | sixteen bits | 1111111111111111 |
--------|-----------------------|-----------------------------------|
dword | thirty-two bits | 11111111111111111111111111111111 |
--------|-----------------------|-----------------------------------|
most at all, bits are aligned using bytes or 8 bits. why?
i'll show you something
Nibbles:
hexadecimal | binary | decimal
---------------------------------
0 0000 0
1 0001 1
2 0010 2
3 0011 3
4 0100 4
5 0101 5
6 0110 6
7 0111 7
8 1000 8
9 1001 9
A 1010 10
B 1011 11
C 1100 12
D 1101 13
E 1110 14
F 1111 15
from above you could see a nible can transform up to 16 number from 0h to Fh
or from 0 to 15 in decimal. meditate by yourself why hexadecimal number is
chosen instead of decimal number to REPRESENT the binary number.
by the way, byte is using double of nibble's bits, 11111111 == 0xFF == 255
from 0 to 0xFF ..... 0 to 11111111 ..... 0 to 255.
a 256 combination including 0 of numbers, a byte is enough for replacing
ascii characters in binary.
now run your calc.exe with scientific mode.. try to play with base number.
so, 8 bits (byte) number are 0 -> 0xFF, 16 bits(word) numbers are 0 -> 0xFFFF
and 32 bits (dword) 0 -> 0xFFFFFFFF.
Notes:
(2^32) - 1 == 0xFFFFFFFF
i assumed you use intel compatible processor now. and it is a 32 bits processor.
32 bits technology by today pentium 4 is came from ancient 386 processor.
but, do you know what the hell 32 bits thingy played around with the processor?
and did you ever think how the computer/calculator doing the calculation?
ok, now i'll give little explanation for both question above. and dont worry,
i wont go deeply into the processor architecture, just simple things that
we have to understand to be a reverser or at least, an asm coder.
every processors ever built, they all have REGISTERs to do arithmethic or
bitwise operation (AND, OR, XOR, etc).
for our lovely intel x86 (pentium 4 probably 786 :P), its has several register
to do its operation. these registers will hold any number value according to
its architecture whether 8 bits, 16 bits, or 32 bits.
as we all have known before, intel x86 is a 32 bits processor, its registers will
hold any value from 0 -> 0xFFFFFFFF
80x86 processor has 8 general purpose registers, and these register
mostly used while doing the reversing task.
they are EAX, EBX, ECX, EDX, ESI, EDI, EBP, and ESP.
Thus,
Accumulator [E]AX (AH/AL) Multiply, divide, I/O, fast arithmetic.
Base [E]BX (BH/BL) Pointer to base address (data segment).
Count [E]CX (CH/CL) Count for loops, repeats, and shifts.
Data [E]DX (DH/DL) Multiply, divide, and I/O.
Source Index [E]SI Source string and index pointer.
Destination Index [E]DI Destination string and index pointer.
Base Pointer [E]BP Pointer to stack base address.
Stack Pointer [E]SP Pointer to top of stack.
where [E] mean extended for 32-bit register. Read below carefully.
EAX is a 32bits register (a 'dword' can hold value up to 0xFFFFFFFF)
The lower part of EAX is AX, a 16 bits register
(16 bit register is inherited from 80286, an old 16 bit processor architecture)
AX can be decomposed in his higher and lower part, AH and AL. (that are 8 bits register)
here is the example:
if the value of EAX == 0xCF3922DB2
so AX == 0x2DB2 , AH == 0x2D, and AL == 0xB2.
but there is something weird with higher part of EAX, 0xCF39, it cant be access
directly. no worry, u will learn how to access it soon, after discovering bitwise
manipulation techniques.
* - the example above also could be apply to EBX, ECX and EDX registers
** - the value in eax is dependant for each others, if you modified AX to
0x4F2A so, the new value of EAX == 0xCF3924F2A and vice versa.
The other registers are:
Flags [E]<Flags> Processor flags.
Instruction Pointer [E]IP Memory location of current instruction.
Code Segment CS Segment containing program code.
Data Segment DS Segment containing program data.
Stack Segment SS Segment for stack operations.
Extra Segment ES Extra program data segment.
Extra Segment FS Extra program data segment (386+).
Extra Segment GS Extra program data segment (386+).
Control Registers CR(0-3) Paging, caching, and protection (386+).
Debug Registers DR(0-7) Data and instruction breakpoints (386+).
Test Registers TR(3-7) Testing the TLB and cache (386+).
Global Descriptor GDTR Address and limit of GDT (286+).
Local Descriptor LDTR Address, limit, and selector of LDT (286+).
Interrupt Descriptor IDTR Address and limit of IDT (286+).
Task Register TR Address, limit, selector, and attributes of
current task.
btw, not all of registers above are useful for us a cracker/reverser.
the register that should be care are the flags, EIP, DS, and SS for
pointers thing.
ok newbie reversers, the actual asm short class just started.
Basic ASM opcodes (command).
Before i start to explain these, find by yourself a disassembler like
w32dasm or built-in disassembler in ollydbg. but, i recommend ollydbg..
so, you can feel the code that i'll explain after this.
A. MOV - move instruction
--------
This command prolly most used in a computer program. It simply can move
a value, to a register. since MOV command didnt wipe the source, we
should call it copy tho ;P.
MOV destination, source
btw, the destination could be any register or memory address.
but the source could be immediate value, register, or memory address.
NOTE: the memory address value is stored in a register.
example 1:
before: EAX == 0x000FFF0F, ECX == 0x00000000
MOV EAX, 0x00965695
MOV ECX, EAX
after: EAX == 0x00965695, ECX == 0x00965695
explanation:
firstly, an immediate value = 0x965695 being moved to EAX
after that, the value in EAX will be passed to ECX
thats why, both register are same. its easy to understand,
isnt it?
example 2:
Before:
EAX == 0x0000000, ECX == 0x000011A0, EBP == 0x0012FCDC
value in memory for SS:[EBP] == 0x00FF00FF
MOV EAX,DWORD PTR SS:[EBP]
MOV DWORD PTR SS:[EBP], ECX
explanation:
hmmm... this kind of operation need a proper explanation since it is
very² important. while processing data, a cpu (processor) must have
a memory module to save any value. btw, memory space has it own size and
memory space also must be indexed to keep a lot of different values/datas
by addressing. in win32 platform, it has 32 bit of virtual memory adress.
so, the address or after this we'll call offset could be 0x0 to 0xFFFFFFFF
(4294967295).
MOV EAX,DWORD PTR SS:[EBP]
now EBP == 0x0012FCDC, with this command, we will look 0x0012FCDC as memory
offset. thus, the value of the offset is 0x00FF00FF. so, the value will be moved
to EAX is 0x00FF00FF not 0012FCDC since it just a memory address or pointer to
the value. btw, DWORD mean the value in offset 0x0012FCDC is in DWORD (32 bit).
and DWORD also could be other like BYTE, or WORD.
MOV DWORD PTR SS:[EBP], ECX
this command is the reverse of command above. the value of ECX (0x000011A0) will
be moved to offset 0x0012FCDC. thus, SS:[EBP] == 0x000011A0.
After:
EAX == 0x00FF00FF, ECX == 0x000011A0, EBP == 0x0012FCDC
value in memory for SS:[EBP] == 0x000011A0
note: in softice or ollydbg(with commandbar plugin), u can simply
run 'd ebp' to check the value of for the address. d = dump.
if the command like this
MOV EAX,DWORD PTR SS:[EBP+8]
simply type 'd ebp+8'
B. CMP, JMP, JNZ, JE,... - compare and jump
--------
If you familiar with any high level programming language like c, basic, pascal
you must already familiar with these statement
if (a == b)
do_this
...
...
else
do_that
...
...
could you imagine how the cpu will do the comparison process? simply, it's just
a dirty trick.
* a - b = result. if the result = 0, aren't values the same?
in asm, and of course while doing reversing, it look like these...
-------------------------
CMP EAX, ECX
JE good_boy
bad_boy
..
..
good_boy:
..
..
------------------------
JE is 'jump if equal' sometimes people use JZ (jump if zero), both are same
zero mean equal.. read above *
otherwise, JNZ mean jump if not zero or not equal..
btw, after instruction CMP, theres a lot of jump commands like JGE
(jump if greater or equal), JA (jump if above), JB (jump if below), etc.
Every jump that is combined with a cmp, is a condicional jump. The only
one jump that is not condicional, is the jmp command, which happens
independently of any value or result.
Conclusion.
in this text, i wont explain much about about assembly programming, because
mostly basic book of assembly have about 500 pages or 1000 and even more..
its very long reading/learning. you have to find a book.
if you want to know more deeply of intel 80x86 architecture, i suggest you read
a book, 'The Art of Assembly Language" (AoA) by Randall Hyde. The useful chapters
are :-
Volume One: Chapter 2.5 and 2.6, Chapter 3 and Chapter 4
Volume Two: All Chapters.
After that, Iczelion win32asm tutorials will turn you as win32asm coders.
In case you want to read it :P.
More intresting, in very sooner, AMD will distribute AMD64. i hope its not hard
for us, the reversers/asm coders to migrate on the new platform ;-).
3. The Reversing.
- Win32 API -
In these days, most popular and most used operating system is micro$oft windows
platform including win98, win2k, and lately winxp. all of those os's are typically
same for their programming interface, they all use win32 API (Application programming
interface). in the old DOS days, assembly programmer used to interact directly to
computer interrupt to communicate with computer system hardware. But, after win32 API
was introduced, application programmers no longer have to know much about the hardware,
they just have to know API collection to be used their program. as example, to display
a warning messagebox, theres an API for the purpose, which will be called.
in c programming language, the API function could be called like this.
MessageBox(hwnd, "Your input is wrong!", "Warning!", MB_OK);
as defined in Win32 API documentation.
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
);
the MessageBox function was included in USER32.dll and could be imported by any
win32 applications. beside that function, there are thousands of win32 API
functions collection which implemented in kernel32.dll, GDI32.dll, etc.
DLL (Dynamic Linked Library)
the .dll files is a collection of ready made function which could be use by
programmers. in c, if we made a function for any routine, we can call it
many times so the functions in .dll files. the different is, the function in
c will be included in the .exe itself but not the fuctions in .dll, they in
other file and will be opened after being called. Thats why they called as
Dynamic Linked Library.
i think overview above about win32 api is enough. we will discuss more about
how to manipulate win32 api in reversing later.
- PE Files Format -
if you're from *nix/linux world, elf format for the executable files maybe
familiar to you. but we are in windows now, and it uses PE format.
i will not teach you about it. Just want to give simple explanation of it.
there are many good tutorials about PE by Matt Pietrek, Mammon, LUEVELSMEYER,
etc.
for windows, all executable file names' are in *.exe form. the code in the files
are segmented. if you had read assembly in DOS book, the *.exe also must be
segmented unless it is *.com. the segmentations must be done because of the
computer architecture (x86). segments in easy word is 'memory block'.
in windows, the segmentations are slightly different from dos.
because windows has memory management system all application level programs
will be loaded into their very own virtual memory locations. to make it simple,
the PE files (executables, .DLL, .OCX, .SYS, .CPL and .SCR files) structure
stored on disk is the same after windows load it into memory.
to get the big picture, get yourself a Hex Editor (i prefer Hex Workshop) and
OllyDbg. Open any PE files (*.exe) in both tools.
in ollyDbg, press Alt-M for memory map.
Look at address 0x00400000. OllyDbg will tell us it is address for PE Headers.
the segment size for PE Headers usually 0x1000 bytes or 4096 bytes.
after that, at 0x00401000 is code segment. the size of code segment depends
on the program.
later on, there are several segmentation for imports/exports, data, and resources.
if you're lucky, you'll find strange segments especially if it is
packed/protected. For example: zip self-extract or upx packed files. In windows,
segments are called as sections.
After that, in the hex editor. please check by yourself whether the first 4096 bytes
are PE Headers section. btw, 4096 bytes are too big for PE Headers data, thats why
you'll find many 0x0 value bytes.
for more, please try to open other executables files using OllyDbg. But dont be surprised,
all PE Files stored in virtual memory at 0x00400000 base address.
This is applied to all executables.
in hex editor, you will not find 0x400000. The files begin at address (or could call it offset)
0x0. for the sake of the windows memory management, unfortunately we must learn about
Relative Virtual Address(RVA). fortunately it very easy to understand.
for the example, we want to make a patch at offset 0x401290. as we knew before,
the offset dont exist in hex editor.
so lets do simple maths.
(Virtual address 0x401290)-(base address 0x400000) = RVA 0x1290
to make the patch, we can go to offset 0x1290 in hex editor.
please take note, above example to find physical offset
on disk true if only the size of PE Headers is 0x1000 bytes.
sometimes, optimized executables just took 0x400 bytes for PE Headers on disk.
thus the virtual size in virtual memory still 0x1000.
u can check size of headers using pe editor like procdump, lordPE, etc. Also
OllyDebugger can show you this information.
using pe editor, you find, let say, pe header's size is 0x400. so code section
starts at physical address (raw) 0x400 / virtual address at 0x1000.
lets do the maths again.
0x1000 - 0x400 = 0xC00
base address still 0x400000.
and the virtual offset that we want to patch is 0x404320.
to convert 0x404320 to disk raw offset,
0x4320 - 0xC00 = 0x3720
ok.. thats enough...non standard size of PE Headers make little mess.. just because
0x1000 - 0x_____ != 0.
The reversing itself.
---------------------
For us newbie reversers, the most intresting things to be reversed are protected
sharewares. But, for advanced reverser, there are a lot more advanced things like
packing/protecting and unpacking/unprotecting of PE files(win32 .exe files), adding
and modifying function in an application, stealing codes, reversing tools coding,
etc.
Since the most target for newbie cracker/reverser are sharewares protected by
nag screen, or serial numbers, get rid off the protection then getting registrated
version of the shareware is a great victory for newbies.
before that, all cracker/reverser must use at least a tool to attack the targets.
the must used tool is called debugger. general purpose of any debugger is to pause
or break the processor of any sequence of executions. the problem here is, the
program code is too long, very long enough. its hard to find the exact location
where to break when program being executed. so we need a good disassembler to help
us to find them. an average disassembler i.e W32dasm is good enough for use newbie.
but theres also excellant disassembler called IDA.
btw, in win32 reversing, we're too lucky because we can manipulate win32 api calls
as a breakpoint.
for the example..
when a shareware request input from a user for a name and serial number, it will
calculate it to make sure the serial is correct and turn on the registered version
of the shareware. otherwise, it will display a message (commonly it's a messagebox
or a dialogbox) to tell us the serial we've entered is wrong. from here, we can use
messagebox or dialogbox API function as breakpoints to know from which location
the messagebox was called.
The Cracking Tools.
-------------------
You'll find so many cracking tools coded by crackers/coders/reversers out there
from around the world. But we'll use just a few of them especially in the beginning.
The very basic tools are a debugger and a disassembler. And if necesarry, an unpacker
for packed/protected programs.
1. Debugger
First at all, you have to know there are two kind of debugger, ring-0 level
(system level) and ring-3 level (application level). But now, we wont touch the
low level as we dont need them currently.
Although many ring-0/ring-3 debugger on the black market(you know what i mean), i just
recommended OllyDbg because its very simple, easy to use, yet also powerful.
I think i dont have to teach you to use it because you can learn it by yourself in
minutes. By the way, u can read several tutes about ollydbg from my website by
googling for "zephyrous ollydbg tutorial" or any tutes by other writers.
Biw-reversing site has very good tutorials collection.
2. Disassembler
The most popular are w32dasm and IDA(for advanced reverser). The usage for
w32dasm is pretty straight forward and we can use it for dead listing analysis.
All debugger is a disassembler actually, but they have different function.
It's depend on you the way of your codes analysis job.
After you're becoming better in reversing, you'll find IDA so powerful and very
useful in specific tasks.
3. Unpacker
Generally speaking, all protection(nag screen, serial) are easy to be removed
unless the code itself has been scrambled into wild binary code and cannot be blindly
patched. When the program will be executed, the internal descrambled engine will extract
true binary code for the program to run well. So, the unpacker's job is to extract the
right codes for patching and analysis.
How to Crack?
-------------
This question is too general because there are too many techniques can be used.
Its depend on us cracker to find the best method for every different problems. The most
used technique is by 'guessing', but we won't do it blindly (smart guessing) just like in
any puzzle games. We have to guess because we dont know where is the protection. So, we
have to learn about win32 API as had been discussed before.
For the example (again), a shareware is asking for a serial number. To received the serial
numbers from user, the program will provided an 'Edit Box'. After user has input the some
text in edit box, the program will copy the text to the computer memory for calculation whether
the serial is true or false. Then, some programs will warn if the serial is wrong with
a 'Dialog Box' or 'Message Box'.
The highlighted text (Edit Box,Dialog Box, Message Box) are parts of Win32 API. To read text
from Edit Box, programmer will call GetDlgItemTextA or GetWindowTextA. However, to display a
dialog box or message box, DialogBoxParamA or MessageBoxA will be called. From here, we will
use these API as breakpoints in debugger, so the debugger will pause the program execution
when the breakpoints have been called. After that, we can continue the program execution
line by line using F8 key(in ollydbg) for live tracing. If we're lucky, the codes for serial
calculations will be found after pressing F8 several times. But most times, its not easy
to find the exact location for serial calculations.
The way computer stored data especially name in memory.
For serial calculations, some algos will compute the serial from the user's name.
Example:
"The Reverser"
in memory its become
54 68 65 20 52 65 76 65 72 73 65 72 (in hexadecimal)
where T = 0x54, h = 0x58, e = 0x65, ..., r = 0x72
Computer naturally just could store numbers. So it used number from 0 - 255 or
0x0 - 0xFF to represent any characters and called ASCII codes. An ASCII character
takes a byte. The homework, get yourselves an ASCII codes table for full reference.
Quick reference :-
0-9 => 0x30 - 0x39
A-Z => 0x41 - 0x5A
a-z => 0x61 - 0x7A
To calculate serial, these ASCII values usually will be used. But each character in bytes.
So, sometimes it more efficient to use lower part of the registers, i.e AL, AH, CL, CH.
As usual, maths operation will be used thoroughly like add, substract, multiply, division,
modulus, etc including bitwise in the algorithms.
I think it is enough for now. I wont go further because there are a lot of tutorials
about cracking for specific targets and/or using specific techniques.
a good start is finding the collection for the tutorials at krobar's collection site,
biw-reversing, anticrack.de, and google at it best. maybe i'll make another tutorial
based on this little guide but not in general. i will go specifically like patching,
serial fishing, keygenning, unpacking, etc.
Happy Reversing and Have Fun!
-
Manually unpacking this packer with OllyDbg, and IAT reconstruction with ImpRec.
This one has some catchy anti-debugging tricks aboard ;)
Hallo to all my readerz.Well,since I have see no unpack tutorial in this
protector I decided to make one.The zip containz the packed and the unpacked
files.This is not a hard protector at all,and I don't know why no tutor
exists till now.Let us begin...
Toolz used: Olly v1.10,OllyDump Plugin,HideOlly plugin,ImpRec v1.6f
Ok,I used the Hide Plug to hide Olly from the IsDebuggerPresent API that
is used as an anti-debugging "trick".So,use this plug to avoid setting
breakpoints on that API constantly.
Open the target exe and load it into Olly.If any messages appear press OK
and Yes.Those messages inform U that Olly has quessed that maybe the target
is packed,and asks for the continuation of the analyzation.So U are at EP (Entry Point)
HeRe:
0050F000 > $ 60 PUSHAD
0050F001 . F8 CLC
0050F002 . F8 CLC
0050F003 . 4F DEC EDI
0050F004 . 87CF XCHG EDI,ECX
0050F006 . 66:B9 7F55 MOV CX,557F
0050F00A . 7E 03 JLE SHORT ColorPic.0050F00F
0050F00C . 7F 01 JG SHORT ColorPic.0050F00F
0050F00E . 7B 75 JPO SHORT ColorPic.0050F085
0050F010 01 DB 01
0050F011 F9 DB F9
0050F012 7C DB 7C ; CHAR '|'
0050F013 03 DB 03
Now make sure that U don't have any hardware,memory,software or other breakpoints set
because this packer will find them and will stop the execution of the program,if U
start to executing it.Make also sure that the only exceptions that are checked in
Debbuging Options are Ignore memory access violations in kernel32 and int3 breaks.Well I
figured out this combination of checked exception during trying to unpack it,do not brake your
mind with that.Now,press Shift+F9,an exception occured,continue with Shift+F9.Shit!
The application terminates,and killz Olly also.Well this is an anti-debbuging trick
that Uses three API's:With createtoolhelp32snapshot,creates a list with the current
handles of the running processes.With Process32first it takes the first handle and
takes the rest using process32next.After taking all those handles,the prog has
hardcoded (well actually created during runtime) some names of debuggerz and unprotectorz,
including Olly'z name (OLLYDBG.exe).It searches again using those API's for a proccess
named OLLYDBG (and others),and compares it's handler with it's running father thread.If it is the
same,it terminates and terminates Olly.If one of the running processes is also a
"bad" program,it may terminate it.So what we will do?
We will change the code of createtoolhelp32snapshot API,to return
immediatelly using a ret opcode.Have also in mind that we cannot place any breakpoints
at the first opcode of any API (meaning that U cannot double-click on API's name in
search name in all modules and place a breakpoint at the first opcode,but also do
not put an opcode using commandline plugin like:bp createtoolhelp32snapshot).This is because
the protector,just before calling an API searches for the first opcode.If there finds a CC
instruction (which is a software breakpoint that is set using eg "bp createtoolhelp32snapshot")
it termimanes.For this,it just uses such kind of commands:
MOV eax,byte ptr ds:[XXXXXXXX] <--- XXXXXXXX location contains the address of an API
CMP eax,CC
JNZ YYYYYYYY
So,load again the target.Do not run yet.In code section,right click and search for name in all
modules.Find createtoolhelp32snapshot API,and double click on it.U are at it's code,HeRe:
77E92ED1 > 55 PUSH EBP
77E92ED2 8BEC MOV EBP,ESP
77E92ED4 83EC 0C SUB ESP,0C
77E92ED7 56 PUSH ESI
77E92ED8 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C]
77E92EDB 85F6 TEST ESI,ESI
77E92EDD 0F84 3FE60000 JE kernel32.77EA1522
77E92EE3 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
See a little down,till the retn where the code of the API ends.This is HeRe:
77E92F2A 56 PUSH ESI
77E92F2B FF75 08 PUSH DWORD PTR SS:[EBP+8]
77E92F2E E8 12000000 CALL kernel32.77E92F45
77E92F33 8BF0 MOV ESI,EAX
77E92F35 85F6 TEST ESI,ESI
77E92F37 0F8C F4E50000 JL kernel32.77EA1531
77E92F3D 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
77E92F40 5E POP ESI
77E92F41 C9 LEAVE
77E92F42 C2 0800 RETN 8
Ok,it ends with a RETN 8.So,go at 77E92ED2 and enter the following new commands:
77E92ED1 > 55 PUSH EBP
77E92ED2 5D POP EBP
77E92ED3 33C0 XOR EAX,EAX
77E92ED5 C2 0800 RETN 8
The POP instruction is to make stack as it was before the PUSH EBP,the XOR makes EAX=0
and is used as a flag from the program to continue the execution without crashing (because
it will think that there are not any other processes running at that time,nothing bad actually)
and the retn 8 to return to the code.So,now no proccesses taken,no Olly detection.
Press Shift+F9 (if exceptions occur,again Shift+F9) and the prog runz just fine under Olly.
Now reload it into Olly,make all the anti-tracing things we said and press one time Shift+F9.
Olly breaks for an exception of access violation.Now press the "M" button and see the contents
of memory.Right click on code section (named .text) and set a memory breakpoint on access.Now
one more time Shift+F9,Olly pauses and U see garbage.Right click->Analysis->Analyze code and
we are actually at the OEP,HeRe:
0041A4C9 . 68 28F34300 PUSH ColorPic.0043F328
0041A4CE . 68 58DC4100 PUSH ColorPic.0041DC58 ; SE handler installation
0041A4D3 . 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
0041A4D9 . 50 PUSH EAX
0041A4DA . 64:8925 000000>MOV DWORD PTR FS:[0],ESP
0041A4E1 . 83EC 58 SUB ESP,58
0041A4E4 . 53 PUSH EBX
0041A4E5 . 56 PUSH ESI
0041A4E6 . 57 PUSH EDI
0041A4E7 . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
0041A4EA . FF15 50B24300 CALL DWORD PTR DS:[43B250] ; kernel32.GetVersion
Remove memory breakpoint now and dump the exe using Olly plugin,without having checked
the Import rebuilding option.Do not close Olly.Now open ImpRec and enter as OEP the
value (OEP as appearz)-(ImageBase)=0041A4C9-00400000=1A4C9.Now IAT autosearch and Get
Imports.Now show invalid.We have one invalid thunk.Try tracing levelz,no one validates the
thunk.Well,most of the tutorz out there would now just say "now cut the invalid thunks etc".
But if U cut the thunk as invalid,the exe will not work.Why?Because this pointer that is
unresovled (in me is 5124DB) and should properely have pointed at an API,is just showing
to a memory address that code of the exe is.This code of the exe has been dumped with
OllyDump,and may was part of the protector,but now is part of the exe.And cutting this
thunk we prevent this necessary code to be executed,althought it has nothing to do with
APIs.And where is the API that should have been pointed here,properely?Well,as there is
no other invalid thunk,may be one of the valids and has been just replaced (moved) in
another place (another pointer).Anyway,now fix dump and it will tell U that there are still
unresolved pointers.Well,not really.Run the fixed exe and...yeap,it's unpacked!
-
به نام خدا
سلام فعلا یه چند روزی باید استراحت کنم خیلی خسته ام .... ولی دوباره بر می گردم با دستی پر از آموزش !
از دوستان می خوام اگر می تونند به من کمک کنند و آموزش های این تاپیک را پی دی اف کنند .
اگر هم کسی آموزشی ، سایتی و .... چیزی داشت برام Pm بزنه خوشحال می شم .
تا بعد خدانگهدار عزیزم .