Adobe's Pagemill Version 2
("Breakpoints on memory access, the 13C680 (15 days) count, registry jongling, stack adjusting")

by Kox
(25 July 1997, slightly edited by Reverser)


Courtesy of Reverser's page of reverse engineering

Well, a pretty interesting essay: Breakpoints on memory access, the 13C680 (15 days) count trick, registry jongling, stack adjusting, RegOpenKey and RegCreateKey and the laziness of today commercial programmers in a very straightforward crack which carries some sound elements of reverse engineering...

PageMill 2 (Late June version) by Kox
www.adobe.com or ftp://ftp.adobe.com/pub/adobe/pagemill/win/2.x/pmwtry5.exe


Tools you will need:

1.W32DASM 8.5 (Thank's Frog's Print)
2.Winice 3 (aka Godot) 	www.numega.com
3.Registry Monitor v3   www.ntinternals.com
4.UltraEdit32 (or your favourite text editor)

First of all ,i would like you to excuse me for any mistakes. As a matter of 
fact i am only a newbie.. (I started learning x86 assmbly 40 days ago).

And i would like to thank +ORC for such a wonderfull attempt to create 
more freedom in this world, and Reverser for hosting many interesting essays... 

Pagemill (in my point of view) is one of the best HTML tools on 
the market. The best thing about it is that it ain't sold by micro$oft.

I heard about it some time ago, and i decided to give it a go.

I downloaded the program (About 5.5 megs... Installed it... and 
run it.
It runs without any nag screens... yet after exiting a dialog pops 
up, informing me that i have only 14 days left for "try out"...

Well, let's check that...

- advance the system clock 3 months.
- run pagemill

A nasty dialog pops up, informing me that my "try out" period has 
expired.

Well, let's get the clock back to normal

- run pagemill again...
- the same dialog pops up again...

So this wanna be protection raised an "expiry" flag somewhere in my system

No problem

Windoze has raised some stop flag for programmers these days... In order 
to carry the infamous crappy windoze 95 logo, you have to go by the rules 
(and whose rules? micro$oft's ofcourse). 
That means you can no longer write to the MBR, nor access hardware directly.

So pagemill HAS to have its expiry flag in either one of two locations.

1. Some file in the ever expanding file list on your hard drive
2. The ever fat piece of slime they call the registry

Well, in order to check the 1st possiblity I used filemon... (An excellent 
file monitor utility, you can find it at www.ntinternal.com, alternatively 
you can use "WineXpose I/O").

But i found nothing ... all the files accessed before the "expired" dialog, 
are non-fishy files.

SO, let's switch over to possiblity no 2 (The registry approach)

run Registry Monitor v3 
(this little baby allows you to monitor registry calls, and version 3 
has filtering capabilities [i.e monitor target task only] ..highly 
recommended)

Well before the nag screen our target accessed 5-6 registry keys. 
The most noticeable one was:
"HKEY_CLASSES_ROOT\CCLSID\{9CFA8CF0-CD93-11d0-8ADE-080009B30D57}"

Ok ..let's delete this key....
1) Delete the key 
 "HKEY_CLASSES_ROOT\CCLSID\{9CFA8CF0-CD93-11d0-8ADE-080009B30D57}"
2) get our clock back to normal
3) run the prog again...

It runs fine...(We are still in the "try out" period)

Ok.. we now know how pagemill expires itself.

Ok .. search for "CLSID\{9CFA8CF0-CD93-11d0-8ADE-080009B30D57}" in pagemill.exe
Maybe it's our lucky day and it isn't encrypted.

And Voila.. it's there.(Those lazy programmers at Adobe did not even bother about 
encrypting the key's name)

Time for some "dead listing" using w32dsm (Thanks Frog's Print, for this 
marvellous tool)

We start disassembling pagemill.exe
(This takes about 25 mins on my P100, and creates a 30 megs "alf" file too....
(time enough for the famous Martini-WODKA, unfortunately i ran out of WODKA,
so i'll just go and give my dog a ride :-)

Back to our cracking session.

Load your dead listing into UltraEdit32 (or your favourite text editor, or 
word processor)

search for "CLSID" 

and you'll find the following:

* StringData Ref from Data Obj ->"CLSID\{9CFA8CF0-CD93-11d0-8ADE-080009B30D57}"
                                  |
:0049411E 68403F5C00              push 005C3F40
:00494123 6800000080              push 80000000

* Reference To: ADVAPI32.RegOpenKeyA, Ord:012Dh
                                  |
:00494128 FF1580445D00            Call dword ptr [005D4480]
:0049412E 85C0                    test eax, eax     
:00494130 7505                    jne 00494137		; if the key exists
:00494132 BE01000000              mov esi, 00000001	; raise a flag


So let's browse a little inside our source code...
50-60 lines above that we find something very interesting...

* Reference To: MSVCRT.time, Ord:02CBh
                                  |
:004940F2 FF15F4525D00     Call dword ptr [005D52F4] ; get system time
:004940F8 83C404           add esp, 00000004	;get stack back to normal
:004940FB A1AC555C00       mov eax, [005C55AC]	; installation date
:00494100 0580C61300       add eax, 0013C680	; add 15 days
:00494105 3945A4           cmp dword ptr [ebp-5C], eax ; compare with currect date
:00494108 7F0B             jg 00494115			;if more than 15 days > trial expired	
:0049410A 8B45A4           mov eax, dword ptr [ebp-5C];smart user set clock before 
                                                        the installation date
:00494113 7E05             jle 0049411A ;jump if good user 

* Referenced by a Jump at Address:00494108(C)
|
:00494115 BE01000000       mov esi, 00000001	;jump here if trial expired

Well, commercial programmers are getting lazier everyday (thanks to expensive 
suits and cars :-)
They are using the famous "0013C680" number...
This number means "15 days" of the trial period, represented in seconds 
(60 secs X 60 mins X 24 hours X 15 days) = 1.296.000 Decimal and 0x13C680

We have the installation date at 005C55AC ..  
copy it to eax	
then we add 15 days to it...
Compare the value with current date (in seconds too) at [ebp-5C]
If the value is more than the current date >>> buzz off: trial finished

And also the prog is checking for smart users who set the clock to pre-install 
dates. And if so >> buzz off you wanna be smart user, we want your money

OK let's check to see where is 005C55AC is getting the install date from...
seach for "[005C55AC],"

nothing ....

Ok never mind, since i am lazy (every body says so these days, to cover up for
shortage of knowledge), lets patch this snippet of code...

:00494100 0580C61300              add eax, 0013C680
:00494105 3945A4                  cmp dword ptr [ebp-5C], eax 
:00494108 7F0B                    jg 00494115	;change to: push eax;pop eax 	
:0049410A 8B45A4                  mov eax, dword ptr [ebp-5C]
:0049410D 3905AC555C00            cmp dword ptr [005C55AC], eax ;
:00494113 7E05                    jle 0049411A	; change to: jmp 0049411A

Ok, lets check our work...

1) Advance the clock
2) run page mill
3) IT WORKS!

4) try out the program... let's open a file...
5) DAMN....The nasty dialog appeared again...

There must be another check at the file open routine!

OK ... 
delete the expiry registry key again.

Browse the code again...
search for "0013C680" ... and voila .. 4 occurances...

and they are using almost the same technique with the same variables

install date at:[005C55AC]
add 0013C680 
compare with current date.

Ok 

i first wanted to know what did those reside for..

So i set back the clock to normal date.

Then fired softice's symbol loader... loaded pagemill into it.

then i traced a couple of times, 
then set a break point on memory access for 005C55AC, the memory 
location where the install date dwells.

:bpmd 005C55AC rw
:crtl d

and soft ice pops up at the above code...
exit softice 
use the program as usual

Open a file with pagemill

soft ice pops again at :4958f9

Ok so :4958f9 is used for file open

exit soft ice.

save a file at pagemill

soft ice pops again at :51ec7f

so: 51ec7f is for file save

exit softice

run most of the function and softice will never pop..

So what is the 4th occurance in our dead listing for.

let's quit pagemill

Aha... soft ice pop up at: 495128

no we can breath tight.
We know every single date check routine in our target, and we gained 
quite a lot of good reverse enngineering information about our target:

FILE OPEN -> 4958F9
FILE SAVE -> 51EC7F
QUIT -> 495128

One very noticable thing is that none of the break point that occured 
were to write a value to 495128, as if the value was hardcoded inside 
the file...
If fact it is, at installtion time: the current time (in seconds) is 
written at that location (hehehehe... at Adobe they are patching their 
own code these days :-)

So lets do our home work "the lazy way" and patch all 4 relevant 
snippets of code...

But watch out! At the exit routine you have to disable both the 
jmp instructions (tracing through the code at that location will 
show you why)

Now run pagemill once more,
and it works! 
We try load, save, most of the functions... and the nasty expiration
dialog will never pop...

so we exit pagemill, back to windows... 
and the "remaining days" dialog pops up, telling us we have -139 days 
left for our try out.

So it works, target cracked...

But that "remainig days" reminder looks bad.
We need to get rid of it..
ok...

1) fire symbol loader again...
2) load pagemill
3) trace(F10) a couple of times, then 

:bpmd 005C55AC rw
the above line sets a break point whenever an instruction tried to read (r) or
write(w) to that location.

:Crtl d

Softice in the background, as usual
then we exit page mill and
soft ice pops up at 495128, the "quit" routine.

:00495123 A1AC555C00      mov eax, [005C55AC]
:00495128 0580C61300      add eax, 0013C680
:0049512D 3B45D4          cmp eax, dword ptr [ebp-2C]
:00495130 50              push eax
:00495131 58              pop eax
:00495132 8B4DD4          mov ecx, dword ptr [ebp-2C]
:00495135 390DAC555C00    cmp dword ptr [005C55AC], ecx
:0049513B 50              push eax			
:0049513C 58              pop eax 	
:0049513D 2BC1            sub eax, ecx
:0049513F B980510100      mov ecx, 00015180


Lets trace (F10) a little, till our nag dialog apears..
and we land here

:00495322 E81DFC0A00       Call 00544F44
:00495327 668B0D302D5700   mov cx, word ptr [00572D30]
:0049532E 6A01             push 00000001
:00495330 51               push ecx
:00495331 8D55DC           lea edx, dword ptr [ebp-24]
:00495334 6A01             push 00000001
:00495336 8D4DB0           lea ecx, dword ptr [ebp-50]
:00495339 52               push edx
:0049533A E81AAF0500       call 004F0259		; remaining days dialog call
:0049533F C645FC04         mov [ebp-04], 04
:00495343 E81A000000       call 00495362
:00495348 C645FC03         mov [ebp-04], 03

Lets patch this ...but wait... we can't use push and pop or nop
cause this way we would ruin our stack!
so lets see
we have 4 "doublewords pushes", that means that our stack pointer is 
decreased by 16 bytes, so we need to make 4 pops, or add 10(hex) to 
the stack

so we change the above code to look like

:00495334 6A01             push 00000001
:00495336 8D4DB0           lea ecx, dword ptr [ebp-50]
:00495339 52               push edx
:0049533A 83C410           add esp,10  ; get back stack as if function was called
:0049533D 50               push eax    ; just filling
:0049533E 58               pop eax     ;  some space
:0049533F C645FC04         mov [ebp-04], 04
:00495343 E81A000000       call 00495362

I think our patch is now finished, but i wanted to make this crack as complete as 
possible.

What if the program has already expired inside some poor lamer's machine??
He/she/it wont be able to delete the CLSID registry key... and our patch would 
be useless for this guy...

So back to our dead listing.
Searching again for the string "CLSID" we find 4 occurences, that look like this:

* StringData Ref from Data Obj ->"CLSID\{9CFA8CF0-CD93-11d0-8ADE-080009B30D57}"
                                  |
:0049411E 68403F5C00       push 005C3F40
:00494123 6800000080       push 80000000

* Reference To: ADVAPI32.RegOpenKeyA, Ord:012Dh
                                  |
:00494128 FF1580445D00     Call dword ptr [005D4480]
:0049412E 85C0             test eax, eax		;test if key is found
:00494130 7505             jne 00494137		    ;not found: go try pagemill
:00494132 BE01000000       mov esi, 00000001    ;found: flag "expiry" routine

Damn, i will not patch 4 more locations... so let's ZEN a little bit...
if the RegOpenKey fails then the...  
YES! That's it! We want the call to fail... and it will fail, of course, if we 
change the ascii value of the key inside our target!

So if we edit pagemill.exe and search for the string "CLSID" we'll find it 
several times, but we need to change only the one that reads

"CLSID\{9CFA8CF0-CD93-11d0-8ADE-080009B30D57}"

to something like

"KOXID\{9CFA8CF0-CD93-11d0-8ADE-080009B30D57}"

(I only changed the CLSID string, 'cause the other value may be randomly 
generated in other machines, whereby "CLSID" is a standard branch of the 
registry)

It's the only occurrence we need to change because this occurrence is used 
only by the RegOpenkeyA function... the others are used by the RegCreateKey 
function (You have to have some API manual to know that and -by the way- 
i don't have one :).

But I, reverser, have. You may want to check:
RegOpenKey and
RegCreateKey.
The most inportant lesson for Adobe to learn is that they should'nt have been 
agressive.
If pagemill didn't create that key... And if that would'nt have been very clear, 
like a black spot on ice, it would have taken me much more efforts to crack 
this...

Well, i guess that's it... CU l8r

Signed.            Kox


Quick patch data

Filesize: 2,126,848 bytes

Comparing files PageMill.bak and pagemill.exe
00093508: 7F 50
00093509: 0B 58
00093513: 7E EB
00094530: 7C 50
00094531: 1A 58
0009453B: 7F 50
0009453C: 0F 58
0009473A: E8 83
0009473B: 1A C4
0009473C: AF 10
0009473D: 05 50
0009473E: 00 58
00094D01: 7C 50
00094D02: 0B 58
00094D0C: 7E EB
0011E087: 7F 50
0011E088: 0A 58
0011E091: 7D EB
001C1D40: 43 4B
001C1D41: 4C 6F
001C1D42: 53 78


(c) Kox 1997, All rights reserved 

Both following examples have been taken from Windoze's API reference inside the COMPLETE version of Borland C/C++ 4.52, wich comes bundled with BRW (!) for less than 4 UK pounds on this month PCplus superCD, N.38 (Issue 130, August 1997, but as usual it appeared in July). See my blackboard
RegOpenKey 
#include shellapi.h LONG RegOpenKey(hkey, lpszSubKey, lphkResult) HKEY hkey; /* handle of an open key */ LPCSTR lpszSubKey; /* address of string for subkey to open */ HKEY FAR* lphkResult; /* address of handle of open key */ The RegOpenKey function opens the specified key. Parameter Description hkey Identifies an open key (which can be HKEY_CLASSES_ROOT). The key opened by the RegOpenKey function is a subkey of the key identified by this parameter. This value should not be NULL. lpszSubKey Points to a null-terminated string specifying the name of the subkey to open. lphkResult Points to the handle of the key that is opened. Returns The return value is ERROR_SUCCESS if the function is successful. Otherwise, it is an error value. Comments Unlike the RegCreateKey function, the RegOpenKey function does not create the specified key if the key does not exist in the database. Example The following example uses the RegOpenKey function to retrieve the handle of the StdFileEditing subkey, calls the RegQueryValue function to retrieve the name of an object handler, and then calls the RegDeleteKey function to delete the key if its value is nwappobj.dll: char szBuff[80]; LONG cb; HKEY hkStdFileEditing; if (RegOpenKey(HKEY_CLASSES_ROOT, "NewAppDocument\\protocol\\StdFileEditing", &hkStdFileEditing) == ERROR_SUCCESS) { cb = sizeof(szBuff); if (RegQueryValue(hkStdFileEditing, "handler", szBuff, &cb) == ERROR_SUCCESS && lstrcmpi("nwappobj.dll", szBuff) == 0) RegDeleteKey(hkStdFileEditing, "handler"); RegCloseKey(hkStdFileEditing); }
RegCreateKey
#include shellapi.h LONG RegCreateKey(hkey, lpszSubKey, lphkResult) HKEY hkey; /* handle of an open key */ LPCSTR lpszSubKey; /* address of string for subkey to open */ HKEY FAR* lphkResult; /* address of handle of open key */ The RegCreateKey function creates the specified key. If the key already exists in the registration database, RegCreateKey opens it. Parameter Description hkey Identifies an open key (which can be HKEY_CLASSES_ROOT). The key opened or created by the RegCreateKey function is a subkey of the key identified by the hkey parameter. This value should not be NULL. lpszSubKey Points to a null-terminated string specifying the subkey to open or create. lphkResult Points to the handle of the key that is opened or created. Returns The return value is ERROR_SUCCESS if the function is successful. Otherwise, it is an error value. Comments An application can create keys that are subordinate to the top level of the database by specifying HKEY_CLASSES_ROOT for the hKey parameter. An application can use the RegCreateKey function to create several keys at once. For example, an application could create a subkey four levels deep and the three preceding subkeys by specifying a string of the following form for the lpszSubKey parameter: subkey1\subkey2\subkey3\subkey4 Example The following example uses the RegCreateKey function to create the handle of a protocol, uses the RegSetValue function to set up the subkeys of the protocol, and then calls RegCloseKey to save the information in the database: HKEY hkProtocol; if (RegCreateKey(HKEY_CLASSES_ROOT, /* root */ "NewAppDocument\\protocol\\StdFileEditing", /* protocol string */ &hkProtocol) != ERROR_SUCCESS) /* protocol key handle */ return FALSE; RegSetValue(hkProtocol, /* handle of protocol key */ "server", /* name of subkey */ REG_SZ, /* required */ "newapp.exe", /* command to activate server */ 10); /* text string size */ RegSetValue(hkProtocol, /* handle of protocol key */ "verb\\0", /* name of subkey */ REG_SZ, /* required */ "Edit", /* server should edit object */ 4); /* text string size */ RegCloseKey(hkProtocol); /* closes protocol key and subkeys */ See Also RegCloseKey, RegOpenKey, RegSetValue

You are deep inside reverser's page of reverse engineering, choose your way out:

homepage links red anonymity +ORC students' essays tools cocktails
antismut search_forms mailreverser
is reverse engineering legal?