July 1998
"JPEG Optimizer V2.02"
( 'Searching for clues'  )
Win '95 PROGRAM
Win Code Reversing
 
 
by The Sandman 
 
 
Code Reversing For Beginners 
 
 
 
Program Details
Program Name: jpomin.zip
Program Type: JPEG Compressor
Program Location: Here
Program Size: 326K 
 
   
Tools Used:
W32Dasm V8.9 - Disassembler
 Softice V3.2
Hex Workshop32 or any other Hex Editor
Rating
Easy ( X )  Medium (  )  Hard (    )  Pro (    ) 
There is a crack, a crack in everything. That's how the light gets in.
 
  
 
JPEG Optimizer V2.02
( 'Searching for clues'  )
Written by The Sandman
 
 
 
Introduction
 
The author says about JPEG Optimizer :

"JPEG Optimizer is designed to create the smallest possible JPEG image files. Savings of up to 50% in file size are possible  which can considerably decrease Web Page download times or save on disc space.
 
JPEG Optimizer achieves this by:

· Giving the user complete interactive control of the JPEG file compression setting and
· The ability to compress some areas of the image more than others.

JPEG Optimizer is easy to use. Users familiar with Web graphics should find it intuitive."
 
About this protection system
 
The protection system within this program comprises of a single registration number, which once accepted will be stored at:
 
HKEY_LOCAL_MACHINE\SOFTWARE\XA Tech\
With the following entry:-

CODE="XXXXXX"
 
The X's represent a six digit number. To enter your registration code you simply select the 'Help' menu option then the 'Registration' sub option.

For those who are interested, each digit of our registration code seems to be XOR with the value of 93h which then gives the true registration code. My knowledge of Assembler is very rusty but that's what it looks like to me..
 
The Essay 
     
When you visit the homepage of JPEG Optimizer your given two choices, one to download a 362K zip file of their latest version containing only the program files or, download the self installing version which is around 950K in size. I choose the first option since I didn't need to have the program installed for me, I could this myself and save around 600K of excess baggage in the process..

Once you've unzipped/installed the program run it a few times, make notes on what messages are shown when you try and register the program.  These notes will prove very helpful when we come to dead listing the program.

OK, we'll start of with a 'Dead Listing' of this target program, as always I choose W32Dasm to do this job, although some *experienced* crackers seem to prefer a DOS based Disassembler called IDA Pro, each to their own...

Open up the program's String Data Resources and locate the message:-

"Thank you for registering JPEG Optimizer"

Once you've done this you will also see the 'Incorrect Registration Code' message, which some crackers refer to as  "The beggar off cracker" message.

Now scroll up your dead listing until you come across one of the following conditions:-

1. A Conditional jump instruction such as jnz XXXXXXX or JZ XXXXXXXX
2. A RET instruction, signifying the end of one routine and the start of our routine.

If you understand the above two lines then you will see this code snippet:-

:00427F95 BA02000000       mov edx, 00000002
:00427F9A E891D10100       call 00445130 ;Check User's serial no
:00427F9F 59               pop ecx       ;fetch results and put into ecx
:00427FA0 84C9             test cl, cl   ;perform AND on low byte of ecx
:00427FA2 0F84B7000000     je 0042805F   ;Show 'Beggar off Cracker' message
                                         ;if serial number is invalid.

:00427FA8 66C746102C00     mov [esi+10], 002C ;else 'Good Cracker'
:00427FAE BA6D9B4700       mov edx, 00479B6D
:00427FB3 8D45F4           lea eax, dword ptr [ebp-0C]

We're on the right track, it's here the program calls memory location 00445130 where it then saves the results of checking our serial number onto the STACK. When the program returns from calling memory location 00445130 it then fetches this result and places it into the ecx register.  The program then checks this register to see if it contains a certain value and if this value is not the right one it knows the serial number check failed.

Some of you will immediately see that we could simply nop (90h) out this je 0042805F instruction so that the program will always accept whatever serial number we give it.
 
This is a good start and one which I have chosen to do.. When we patch this routine using Nops (90h) it will look like this:-

:00427F95 BA02000000       mov edx, 00000002
:00427F9A E891D10100       call 00445130 ;Check User's serial no
:00427F9F 59               pop ecx       ;fetch results and put into ecx
:00427FA0 84C9             test cl, cl   ;perform AND on low byte of ecx
:00427FA2 90               nop           ;Proceed to register program.
:00427FA3 90               nop
:00427FA4 90               nop
:00427FA5 90               nop
:00427FA6 90               nop
:00427FA7 90               nop
:00427FA8 66C746102C00     mov [esi+10], 002C
:00427FAE BA6D9B4700       mov edx, 00479B6D
:00427FB3 8D45F4           lea eax, dword ptr [ebp-0C]

We could now test this patch out and the program would indeed accept whatever serial number we enter into it.. The program would also create a new entry at: HKEY_LOCAL_MACHINE\SOFTWARE\XA Tech\  with Code=XXXXXXX the X's represent the serial number you might have used to test this patch out.
 

If you Lets see if this is permanent... Exit the program and re-run it. If we now select 'Help' then the 'About Registered Version' the program now tells us that it's disabled functions (Batch mode etc) are now all enabled, so far so good..  However, if you now select the 'About' screen you'll see the program is still unregistered..  We've obviously still have some more work to do...
 
What's happening is this.. While we've been able to 'fool' the program into accepting any serial number we give it, the problem comes when we re-run the program, it's here that the program reads our fake serial number from the System Registry File then checks to see if it's valid, and as we know, it isn't so the program assumes it's still unregistered.. A bug in this program means that although the program has found our serial number invalid, it does not handle this situation too well, that's why when we check the 'About Registered version' screen it still shows that all the disabled functions are in fact, all enabled, even though the program found our serial number invalid..
 
Right, we have a 'dead listing' of this program, but where do we start looking and more importantly, what do we look for!.

Just think for a moment, try and start thinking like a *cracker*, we must understand the basics of how most programs work when it comes to Shareware programs verifying serial numbers in order that it can start running as a fully registered program.
 
1. While loading, the program will either open up the System Registry File or it's own .ini file and attempt to read your User details including a serial number.
 
2. If no serial number found then the program will assume it's still unregistered. It will then place in one or more memory locations a value (usually '0' ) which the rest of program will then refer to during the course of you using it.

In the majority of programs, a value of '0' is usually taken as meaning 'Unregistered', while a value of '1' is usually taken as meaning 'Registered'.

3. If it was able to find a serial number it then creates a  real serial number based on your User details (usually based on your name/handle) then check to see if your serial number is the same as the one the program has just created from your name/handle. Again, if the program finds your serial number invalid it will place in one or more memory locations a value (usually '0' ) which the rest of program will then refer to during the course of you using it.

4. If the program gets this far then it's found your serial number as being valid and will again place in one or memory locations a value of '1' which the rest of the program will refer to as during the course of you using it.  A value of '1' in certain memory locations chosen by the programmers will mean that the program has been registered.

Can you see which stage we should 'patch' the program yet?. Do you now know 'what' we should be looking for?.

For those who are still a little unsure, we want to attack the program at the 3rd stage, where it has loaded in our User details and checks to see if the serial number is valid or not. By patching the program at this stage we can fool the program into going onto the 4th stage..

Let's go back to our Dead listing, in particular lets have another look at the 'String Data Resources'.. We are now looking at any Shareware type messages, those that get displayed if the program is still unregistered.. Now the help file that comes with this program mentions the following items:-
 
Registered Version Differences

The Evaluation Version writes a small advertisement  into the JPEG file.
The Registered Version allows batch processing  ie compressing multiple files at one time.

The Registered Version enables Zoom, Undo and Redo.

Can you see a 'clue'?, the program writes a small 'advertisement' into any JPEG files the User may save.. Lets go fishing..

I came across these String References in our Dead Listing... Nag256 and Nag16 which smells like 'advertisements' you would use on 256 colour & 16 colour saved JPEG image files!. so lets follow these up a little more closely.. Now there are two sets of memory locations where this text string is used, we will examine the first occurrence we come across because that's what W32Dasm found first..
 

* Referenced by a (C)onditional Jump at Address: :0040361C(C)
 
* Possible StringData Ref from Data Obj ->"Nag256"
                                  |
:00403675 BAC5414700              mov edx, 004741C5
:0040367A 8D4584                  lea eax, dword ptr [ebp-7C]
:0040367D E84E180400              call 00444ED0

Ok, so far so good, now W32Dasm tells us that this routine is executed by a conditional jump statement at memory location :004036C  so lets take a look at this jump statement and see what trigger's it..

:004035F9 803D00F4470000     cmp byte ptr [0047F400], 00
:00403600 7505               jne 00403607
 

:00403602 E8E9020000         call 004038F0
:00403607 66C7855CFFFFFF1400 mov word ptr [ebp+FFFFFF5C], 0014
:00403610 E9AE010000         jmp 004037C3
:00403615 83BD44FFFFFF00     cmp dword ptr [ebp+FFFFFF44], 00000000
:0040361C 754E               jne 0040366C ;Display Nag256 advertisement
:0040361E 66C7855CFFFFFF1C01 mov word ptr [ebp+FFFFFF5C], 011C

Take a look at that cmp dword ptr [ebp+FFFFFF44], 00000000 that precedes our conditional Nag256 jump, it's checking for a value of '0' pointed to by the register ebp+FFFFFF44, it could be checking to see if the program is registered or not!.  Earlier I said that most programs uses a value of '0' to signify that the program is still unregistered but I also said that these memory locations were specially chosen by the programers themselves. However, the program may use other memory locations for different purposes that may or may not have anything to with the protection system but which ALSO uses either a value of '0' or '1' to signify different things!.. Confused yet?.

Well before making up your mind at this point just take a look a few more lines above this cmp instruction:-

:004035F9 803D00F4470000     cmp byte ptr [0047F400], 00
:00403600 7505               jne 00403607

This also is checking for  a memory location that may, or may not have a value of '0'. The reasons why I'm more interested in these two lines than the ones below is because it has the potential to jump over the first sets of Nag256 advertisements if the program finds a value of '1' or more in memory location 0047F400.

At this stage we are still not sure about the importance of memory location 0047F400 other than the program checks to see if it has a value of '0' in it and if it does then it will execute the lines of code that deal with the first of two sets of Nag256 & Nag16 string references. Again, you might ask yourself how can we find out more about this memory location, well, we can do a search of our 'Dead Listing' and try and find out where it gets changed, i.e does it ever get initialized with a value of '1' .

While in our Dead Listing with the cursor at the top of the dead listing do:

search for "0047F400"

The first occurrence found using this memory location is here:

:004035F4 83FB64                  cmp ebx, 00000064
:004035F7 7E0E                    jle 00403607
:004035F9 803D00F4470000          cmp byte ptr [0047F400], 00
:00403600 7505                    jne 00403607
:00403602 E8E9020000              call 004038F0

Which is where we are already at, so search again for this memory location:-

* Referenced by a CALL at Address: :004045A0

:004043EC 53                      push ebx
:004043ED 56                      push esi
:004043EE 57                      push edi
:004043EF 83C4E8                  add esp, FFFFFFE8
:004043F2 C744240801000000        mov [esp+08], 00000001
:004043FA C744241400F44700        mov [esp+14], 0047F400
:00404402 E940010000              jmp 00404547

Here the program is 'storing' the memory address [0047F400]  into another memory address pointed to by the esp+14 instruction!. So from our point of view our target program CAN access memory location [47F400] from two different memory address.. Don't worry if this all sounds confusing, lets keep things nice and easy..

Lets search again for any references to memory location 0047F400:-

* Referenced by a (C)onditional Jump at Address: 0042A4BD(C)
 
:0042A4B5 803093           xor byte ptr [eax], 93 ;xor contents of eax
                                                  ;with 93h
:0042A4B8 42             inc edx                  ;Increase count by 1
:0042A4B9 40             inc eax                  ;get next memory location
:0042A4BA 83FA06         cmp edx, 00000006        ;count =6?
:0042A4BD 7CF6           jl 0042A4B5              ;no? then loop back

;The next 18 lines check all six memory locations (there are 6 numbers to our serial number ) one by one pointed to by the ebx register. After some testing I found out that the *real* serial number is NOT created in memory but instead the program checks our serial number one number at a time.
 
:0042A4BF 0FBE0B         movsx ecx, byte ptr [ebx];Get 1st number of serial
:0042A4C2 83F9D8         cmp ecx, FFFFFFD8
:0042A4C5 7545           jne 0042A50C ;1st number wrong? then 'beggar off'
:0042A4C7 0FBE4301       movsx eax, byte ptr [ebx+01]
:0042A4CB 83F8D9         cmp eax, FFFFFFD9
:0042A4CE 753C           jne 0042A50C ;2nd number wrong? then 'beggar off'
:0042A4D0 0FBE5302       movsx edx, byte ptr [ebx+02]
:0042A4D4 83FAA4         cmp edx, FFFFFFA4
:0042A4D7 7533           jne 0042A50C ;3rd number wrong? then 'beggar off'
:0042A4D9 0FBE4B03       movsx ecx, byte ptr [ebx+03]
:0042A4DD 83F9A1         cmp ecx, FFFFFFA1
:0042A4E0 752A           jne 0042A50C ;4th number wrong? then 'beggar off'
:0042A4E2 0FBE4304       movsx eax, byte ptr [ebx+04]
:0042A4E6 83F8A7         cmp eax, FFFFFFA7
:0042A4E9 7521           jne 0042A50C ;5th number wrong? then 'beggar off'
:0042A4EB 0FBE5305       movsx edx, byte ptr [ebx+05]
:0042A4EF 83FAA5         cmp edx, FFFFFFA5
:0042A4F2 7518           jne 0042A50C ;6th number wrong? then 'beggar off'
 
:0042A4F4 C605F0EC470001 mov byte ptr [0047ECF0], 01 ;Serial OK Reg Flag1=1
:0042A4FB C60500F4470001 mov byte ptr [0047F400], 01 ;Serial OK Reg Flag2=1
:0042A502 E88993FDFF     call 00403890
:0042A507 B001           mov al, 01
:0042A509 5B             pop ebx
:0042A50A 5D             pop ebp
:0042A50B C3             ret

LOOK!  This is the last reference to memory location 0047F400, it's also here that it gets initialized with a value of '1'.    That's a good sign..  If you look at the whole routine you'll see many checks on consecutive memory locations pointed to by the ebx register.

In fact, if you do your own tests on this section of code (get softice to break on any bpx messageboxa calls the program might use then type u 0042A4C5 followed by bpx 0042A4C5)  you'll see that it involves our six digit serial number, so if you enter more than six numbers these will be ignored.  If all six numbers of our serial number pass their checks then, and only then will the program place a value of '1' in the two memory locations shown above. If you do deiced to test this routine out then once you break at 0042A4C5 follow this with r eip= 42A4F4 which is where the program places a value of '1' in memory locations [47ECF0] and [0047F400] then watch what the program does afterwards.

I apologize once again if all this might seem very confusing but it WILL start to make more sense as you get better at *cracking*.  Unlike some tutorials on the web that are supposedly written for the newbie *cracker*,  I try and explain the 'thinking' that goes behind the decisions I make, rather than say to you do this, do that then do this where all I will achieve is that you can follow simple instructions.  It's much better I think, that the quicker you can question my chosen methods in cracking programs the better you'll become *real* crackers.

It's here that I have deiced to create my second and final 'Patch' to this program.

Here's how my second patch works..

THE ORIGINAL CODE (BEFORE)

:0042A4BF 0FBE0B         movsx ecx, byte ptr [ebx];Get 1st number of serial
:0042A4C2 83F9D8         cmp ecx, FFFFFFD8
:0042A4C5 7545           jne 0042A50C ;1st number wrong? then 'beggar off'

THE PATCHED CODE (AFTER)

:0042A4BF 0FBE0B         movsx ecx, byte ptr [ebx];Get 1st number of serial
:0042A4C2 83F9D8         cmp ecx, FFFFFFD8
:0042A4C5 EB2D           jmp 0042A4F4 ;Jump to: 'Register Program Code' if
                                      ;checking 1st number of our serial #

Program now jumps here... 'Register Program Code'
 

:0042A4F4 C605F0EC470001  mov byte ptr [0047ECF0], 01
:0042A4FB C60500F4470001  mov byte ptr [0047F400], 01
:0042A502 E88993FDFF      call 00403890
:0042A507 B001            mov al, 01
:0042A509 5B              pop ebx
:0042A50A 5D              pop ebp
:0042A50B C3              ret
 
Notice I've changed a conditional jump on our 1st number check into a full jump instruction which in effect, tells the program to ignore any further checks on our serial number and to automatically pass our serial number as valid without any further checks on it.
 
Job Done.
 
The 'Crack' 
 
Here's how to 'patch' this target program so that it will be fully registered..:)

Load up jpegopt..exe into your favorite Hex-Editor ( I prefer hexWorkshop-32) but just about any will do..
 
SEARCH FOR THE FOLLOWING BYTES : C90F84B700000066
REPLACE WITH HIGHLIGHTED BYTES : 9090909090909066
 
SEARCH FOR THE FOLLOWING BYTES : 0FBE0B83F9D87545
REPLACE WITH HIGHLIGHTED BYTES : 0FBE0B83F9D8EB2D
 
 
Final Notes 
 
Although this essay 'looks' hard to follow, the actual 'patches' are relatively dead easy to find and implement.  All it takes is a little thought and care and of course, some practice and you'll quickly see that this program is as easy to *crack* as any other you may come across on the web. Remember, take your time and be patient, follow the clues shown in your dead listing and make plenty of notes for future references.
 
My thanks and gratitude goes to:-
 
Fravia+ for providing possibly the greatest source of Reverse Engineering
knowledge on the Web.
 
+ORC for showing me the light at the end of the tunnel.
 
Ob Duh 
 
Do I really have to remind you all that by buying and NOT stealing the software you use will ensure that these software houses will continue to  produce even *better* software for us to use and more importantly, to continue offering even more challenges to breaking their often weak protection systems.
 
If your looking for cracks or serial numbers from these pages then your wasting your time, try searching elsewhere on the Web under Warze, Cracks etc.
 


 
 
 Next   Return to Essay Index   Previous 
 


Essay by:          The Sandman
Page Created: 11th July 1998