Lesson 9 (3): How to crack Windows, Hands on
Nagscreens galore (B): The 'Dead listing' approach


  (Hic sunt tabulae: Best viewed with good old Courier 10)

     In this lesson I'll teach you another cracking technique
known as "dead listing" approach, in opposition to the "live"
cracking (through Softice/Winice) that we have used (and we'll
use) most of the time.
     Since this approach requires a good Hexeditor and a good
disassembler (and a good Wordprocessor), it suits us well to
begin the 'hands on'  part cracking what we need: a good
     I'll crack here the nagscreens out of Hexworkshop, Version
2.10 (32 bit version). A good and relatively quick (for Windoze's
standards) application by Breakpoint software. Many among you
will already have this hexeditor inside their Software
collection, if not find it on the Web through an Archie search
and download it through ftpmail... searching through the archies
will give you something like this: 

Host    (
Last updated 11:25 23 Oct 1996
Location: /pub1/pc/win95/programr
FILE -r--r--r-- 707906 bytes  02:00 13 Aug 1995

The exact NAME of this hexediting program is HWORKS32.EXE, it is
524288 bytes LONG, and its DATED 2 February 1996. This program
has many little nag annoyances:
-    shows a nagscreen reminding you how many days you have used
the program;
-    keeps a nag_string "unregistered version" on the main screen
of the program;
-    has, inside help, a "registration serial number form"...
this is the number you could get registering... and should digit
inside the HELP/ABOUT HEX WORKSHOP window form.

     Elsewhere in this tutorial we have already learned how to
defeat this type of protection: through Winice breakpoints.
Basically we would type a fake registration number (say
'121212121212') and then look for the occurrences of our string
in memory, and then search and investigate the manipulations
undertaken by the protection scheme. In this way we would finally
be able to crack the nagscreen procedure. 
     Fine, fine... this would indeed work, but I want to teach
you through this lesson ANOTHER, completely different approach
to cracking, which is and will be especially useful for
'nagscreen' and 'time-limit' cracking... i.e. the "dead listing"
approach. This approach is a much more "relaxed" sort of crack,
which  will work flawlessly most of the time. 
     This approach is particularly easy with Windows programs,
since -as I have already told you a hundred times- the modularity
of this overbloated atrocity makes it particularly easy for us
little crackers to track back the -mostly primitive- protection
methods utilized by the commercial programmers.
     I remember older times, when programming was still an art
for intelligent (not mercantile) beings, cracking the old (and
sometime beautifully crafted) Z-80, CP/M and DOS programs. In
those time 'a byte was a byte'! And good programmers found funny
tricks in order to write 'tight' code. We old crackers used the
'dead listing' approach sitting (or snoozing) in pleasant (albeit
a little battered) armchairs, inside a huge university library,
drinking very good Martini Wodka (you would be well advised to
use only Moskowskaja, coz non Russian Wodkas are repugnant). Four
of us at a time, each one just looking at his paper listings. You
cannot imagine how many little (unrelated) tricks we found inside
the code cracking in that way! A hacker, a cracker (me), a 'real'
programmer and an encryption specialist, all four sitting in the
same room, exchanging pretty clever findings and sipping (much
too many) good cocktails... it's long ago, those times (and
societies) are gone for ever! Microsoft's abomination has
unfortunately created this overbloated world of huge 'programs'
which perform more slowly (and much more awkwardly) what the old
programs did quickly and flawlessly.
     You don't believe me? Why don't you just install Linux on
your harddisk and see for yourself the difference between a good
OS and Windoze? Just try it... you'll be amazed and you'll never
go back... well, you'll actually do... only in order to crack the
hell out of Windows: We'll never damage enough Microsoft's
interests to compensate for this moronic situation: millions of
stupid users have to wait hours (and this with microprocessors
that are 1000 times quicker than the old 8086) to perform with
MS_Word -slowly- what they could have done immediately with an
old copy of Wordperfect for Dos. 
     Anyway, the huge dimension of all Windows' programs forces
us to abandon the printed "dead listing" approach... printing in
extenso the listing of a Windows program would consume a couple
of ink cartridges and could easily take ages... therefore we can
indeed still crack with the "dead listing" method, and we can
indeed still drink our Martinis (and we do, Oh Yessir), but we
must keep the overbloated listings off page, on our PC, printing
only the small part of them that are relevant to our crack.

     As I said at the beginning, for this art of cracking you'll
need basically only two programs (and you will NOT need

1)        A disassembler like W32DASM (or another good one) in
order to find the protection scheme.
You will probably already possess it (else how did you crack
until now?)... if not find the last version on the Web -through
an Archie search- and download it -through ftpmail-, Searching
the archies will give you something like this:

Host    (
Last updated 07:57  1 Jan 1997
Location: /pub/mirrors/
FILE    -rwxrwxr-x  650327 bytes  23:25 28 Oct 1996

2)        An hexeditor (Use Hexworkshop version 2.10 itself,
since we are already cracking it :=) in order to defeat the
protection scheme as soon as you have found it. OK, enough, let's

Here is how the "dead listing" approach works:
1)   Run the program you want to crack, in this case Hexworkshop,
look at all the nag_strings and write them down (I mean snap them
down -automatically- from screen... see elsewhere in my tutorial
how to get and crack snap32);
2)   Wdasm the file (that means: 'open' it inside the
disassembler in order to get the listing).
3)   Transfer the listing to your favorite wordprocessor (we will
not need Wdasm any more, bye)
4)   Search (Find) inside your wordprocessor the nag_references,
say, in our case, the string "unregistered version"

You'll immediately land inside following piece of code:
:00415732 CC              int 03
:00415733 CC              int 03
:00415734 CC              int 03
:00415735 CC              int 03
:00415736 CC              int 03
:00415737 CC              int 03
:00415738 CC              int 03
:00415739 CC              int 03
:0041573A CC              int 03
:0041573B CC              int 03
:0041573C CC              int 03
:0041573D CC              int 03
:0041573E CC              int 03
:0041573F CC              int 03
:00415740 55              push ebp
:00415741 8BEC            mov ebp, esp
:00415743 6AFF            push FFFFFFFF
:00415745 6873584100      push 00415873
:0041574A 64A100000000    mov eax, fs:[00000000]
:00415750 50              push eax
:00415751 64892500000000  mov fs:[00000000], esp
:00415758 81EC0C020000    sub esp, 0000020C
:0041575E 53              push ebx
:0041575F 56              push esi
:00415760 57              push edi
:00415761 898DE8FDFFFF    mov [ebp-00000218], ecx
:00415767 8B450C          mov eax, [ebp+0C]
:0041576A 50              push eax
:0041576B 6A73            push 00000073
:0041576D 8B8DE8FDFFFF    mov ecx, [ebp-00000218]
:00415773 E87EFC0100      call 004353F6
:00415778 C745FC00000000  mov [ebp-04], 00000000
:0041577F 8B8DE8FDFFFF    mov ecx, [ebp-00000218]
:00415785 83C144          add ecx, 00000044
:00415788 E84EF40100      call 00434BDB
:0041578D C645FC01        mov [ebp-04], 01
:00415791 8B85E8FDFFFF    mov eax, [ebp-00000218]
:00415797 C70028AE4500    mov dword ptr [eax], 0045AE28

*StringData Ref from Data Obj->"An unregistered version of Hex"
                             ->"Workshop has been on"
:0041579D 68085B4600      push 00465B08
:004157A2 8D85F4FDFFFF    lea eax, [ebp-0000020C]

Good, we immediately see that the above routine starts at
:00415740 55              push ebp
Therefore now we'll search our listing for following string:
call 00415740                 (that is: who calls here?).
The reason we must search for the caller is very simple: there
is no conditional jump inside the piece of code above... and
therefore it'll very unlikely hide a protection and/or a check
time or nag routine. If we do perform our search for the caller
we'll immediately land inside following routine: 

:00415DAC 55              push ebp          ;pusha lotta values
:00415DAD 8BEC            mov ebp, esp
:00415DAF 6AFF            push FFFFFFFF
:00415DB1 68045E4100      push 00415E04
:00415DB6 64A100000000    mov eax, fs:[00000000]
:00415DBC 50              push eax
:00415DBD 64892500000000  mov fs:[00000000], esp
:00415DC4 83EC54          sub esp, 00000054
:00415DC7 53              push ebx         
:00415DC8 56              push esi
:00415DC9 57              push edi
:00415DCA 894DA0          mov [ebp-60], ecx
:00415DCD 6A00            push 00000000
:00415DCF 8B4508          mov eax, [ebp+08]
:00415DD2 50              push eax
:00415DD3 8D4DA4          lea ecx, [ebp-5C]  ;for the call
:00415DD6 E865F9FFFF      call 00415740    ;*** HERE !!! ***
:00415DDB C745FC00000000  mov [ebp-04], 00000000
:00415DE2 8D4DA4          lea ecx, [ebp-5C]
:00415DE5 E804F70100      call 004354EE
:00415DEA C745FCFFFFFFFF  mov [ebp-04], FFFFFFFF
:00415DF1 E805000000      call 00415DFB
:00415DF6 E913000000      jmp 00415E0E
:00415DFB 8D4DA4          lea ecx, [ebp-5C]
:00415DFE E8FD000000      call 00415F00
:00415E03 C3              ret

Good, OK. Now -once more- who calls this function? As -here too-
we don't have any conditional jumps, we are compelled to look
further inside the green branches of our code_tree.
Let's go on: searching for
call 00415DAC       (the beginning of the above function)
we will land inside following code:

:0040254C 6808414500      push 00454108           
:00402551 8D8568FEFFFF    lea eax, [ebp-198]
:00402557 50              push eax
:00402558 E893460200      call 00426BF0
:0040255D 83C408          add esp, 8
:00402560 0FBF0508414500  movsx word ptr eax, [00454108]     
:00402567 85C0            test eax, eax
:00402569 0F8586000000    jne 004025F5  ;jump 1 over CALL_NAG
:0040256F 8B8D40FEFFFF    mov ecx, [ebp-1C0]
:00402575 E8D3350100      call 00415B4D
:0040257A 85C0            test eax, eax
:0040257C 0F841B000000    je 0040259D
:00402582 8B8D40FEFFFF    mov ecx, [ebp-1C0]
:00402588 E866360100      call 00415BF3
:0040258D 8B8D40FEFFFF    mov ecx, [ebp-1C0]
:00402593 E8DF360100      call 00415C77
:00402598 E953000000      jmp 004025F0  ;jump 2 over CALL_NAG
:0040259D 8B8D40FEFFFF    mov ecx, [ebp-1C0]
:004025A3 E83D370100      call 00415CE5 ;(month year routine)
:004025A8 898570FFFFFF    mov [ebp-90], eax
:004025AE 83BD70FFFFFF00  cmp dword ptr [ebp-90], 0
:004025B5 0F8417000000    je 004025D2    ;jump 3 over CALL_NAG
:004025BB 8B8570FFFFFF    mov eax, [ebp-90]
:004025C1 50              push eax
:004025C2 8B8D40FEFFFF    mov ecx, [ebp-1C0]
:004025C8 E8DF370100      call 00415DAC ;HERE! CALL_NAG! *
:004025CD E91E000000      jmp 004025F0
:004025D2 8D8D7CFFFFFF    lea ecx, [ebp-84];jump 3 lands here
:004025D8 E88C420200      call 00426869
:004025DD 85C0            test eax, eax
:004025DF 0F840B000000    je 004025F0
:004025E5 8D8D7CFFFFFF    lea ecx, [ebp-84]
:004025EB E8FE2E0300      call 004354EE
:004025F0 E91E000000      jmp 00402613     ;jump 2 lands here
:004025F5 8D8D7CFFFFFF    lea ecx, [ebp-84];jump 1 lands here
:004025FB E869420200      call 00426869

Now have a good look at the code above: As you can see there are
only three possible jumps which will NOT call the CALL_NAG
routine. The one at
:00402598 E953000000          jmp 004025F0 (Jump 2)         
is not conditional, and therefore very rarely used for nagscreens
protections. Besides, it links to another unconditional jump and
it's most probably a "Quick_out" way... let's eliminate it, at
least for now.
We remain with only two jumps over the NAG_SCREEN call routine:
00402569 0F8586000000         jne 004025F5 (jump 1)
004025B5 0F8417000000         je 004025D2      (jump 3)

     You may investigate both of them, but, hey, you could
eliminate one more jump, again, just using a little Zen code
feeling): see how the jump condition for jump_3 is a base pointer
value [ebp-90], while the one for jump_1 is a memory fixed
location [00454108]... this sort of condition is usually a green
light, for compiler reasons I'll not delve inside here. Therefore
let's have a closer look at it and let's forget jump 3, at least
for now.

:0040254C 6808414500      push 00454108 ;values for         
:00402551 8D8568FEFFFF    lea eax, [ebp-198]
:00402557 50              push eax      ;the following
:00402558 E893460200      call 00426BF0 ;call
:0040255D 83C408          add esp, 8 ;modify stack
:00402560 0FBF0508414500  movsx word ptr eax, [00454108] ;HERE 
:00402567 85C0            test eax, eax ;conditional test
:00402569 0F8586000000    jne 004025F5  ;jump over CALL_NAG

What will then this mysterious [00454108] location be? Don't you
feel it? It's the ACTIVATOR! The location with the flag, which
sets the whole nag screen galore for Hexworkshop! Our cracking
job is already finished! 
     We don't need anything more: we don't need any fiddling with
breakpoints, nor to examine hundreds of irrelevant calls... with
Windows this kind of "dead listing" cracks works so smooth I
could shriek!
     But, hey, OK, we will continue our snooping, for the sake
of it and just in order to be completely sure... it's not
necessary, but let's do it anyway... check a little more
around... search, inside your huge listing, all the other
occurrences of the same [00454108] location... you'll get no more
than 5 hits. The most striking one from our cracking point of
view being the following occurrence:

:00402770 898574FFFFFF   mov [ebp-8C], eax ;save old eax
:00402776 0FBF0508414500 movsx word ptr eax, [00454108];FLAG*
:0040277D 85C0           test eax, eax     ;flag is zero?
:0040277F 0F8528000000   jne 004027AD    ;nope, so we won't
:00402785 8B4DEC         mov ecx, [ebp-14]             
:00402788 E883280000     call 00405010   ;call this, nor
:0040278D 898574FFFFFF   mov [ebp-8C], eax ;write

* Possible StringData Ref from Data Obj ->"Unregistered
:00402793 6854494600    push 00464954   ;on the screen
:00402798 685C800000    push 0000805C   ;& we'll jump over
:0040279D 6800400000    push 00004000   ;these pushes
:004027A2 8B8D74FFFFFF  mov ecx, [ebp-8C]
:004027A8 E823280000    call 00404FD0   ;and this call

* Possible StringData Ref from Data Obj ->"ControlBars"
:004027AD 686C494600   push 0046496C     ;directly here

Well, yes, now it's more than enough, thanks... location
[00454108] seems indeed to be the flag we are searching.
Confirmed. Struck and sunk! Now let's quickly crack Hexworkshop:

*** CRACK FOR HEXWORKSHOP VERSION 2.10 by +ORC (January 1997) **
Use hexworkshop itself (no more debug/symdeb, coz we are working
with the overbloated Microsoft monstrosity) and search inside the
code of the copy on your harddisk for the hex sequence:
that's the code for our flag_location, duh?

****(a short digression): BE CAREFUL SEARCHING FOR BYTES ***** 
Be careful with instructions you try to search for. You should
only search for instructions that don't change the bytes they
assemble to, depending on their location in memory. For example,
searching for the following instructions presents no problem:
PUSH      DX
POP       [DI+4]
ADD       AX, 100
but searching for the following instructions CAN cause
unpredictable results:
JE        123
LOOP      100
**** (end) ********************************* **** **** ****

Searching for the byte sequence 08 41 45 00 you will obviously
find the several occurrences of it we have seen before... you
have to modify only one location though, the one followed by the
two bytes
0F85...  (jne after CALL_NAG)
at the FIFTH (and sixth) position after the search string,
because that's the location we are looking for.
Once you found it, write over the byte
the byte
and now you'll have modified the jne in a (je after CALL_NAG).
     We'll now jump if equal (as all men should be, by the
way)... Look!... no more nagscreens to annoy our proven aesthetic
perception, no more silly protections to blemish our suave future
hexediting around :=)
I almost forgot... that's obviously not enough... you must as
well modify the location at :0040277F

:0040277F 0F8528000000   jne 004027AD   ;jump over 'unregistered'
:0040277F 0F8428000000   je 004027AD    ;jump equal!

in order to have a well cracked copy of our target.

[PSP 32]
Can we apply this 'dead listing' approach to other programs? Can
we use the same strategies for other protection schemes?
Sure! Let's remain a little more inside the nagscreens
     A logical step, after the invention of the nagscreen itself,
has been the "mingling" of the nagscreen calling routine. In this
kind of protection the nagscreen routines are 'amalgamated' with
other routines, which cannot be skipped as they are essential for
the working of the program. The main disadvantage of this
approach, for the mercantile protectionists, is that they have
therefore to prepare TWO different versions of their programs:
a 'nagscreened' one and a 'clean' one, since else we would
immediately be able to transform the crippled program in a fully
functional one using the same approach they would use to
'uncripple' it.
     Such mingling is therefore only used by major programs which
are well established on the market and can afford the 'doubled'
approach. This is the case of the last versions of PaintShopPro
     Even in this case, though, we can crack nice enough, as I
will now demonstrate you with PaintShopPro version 3.2 (a 32 bit
program for Windows 95).
     You will probably already possess it, if not find the last
version on the Web -through an Archie search- and download it
-through ftpmail-, Searching the archies will give you something
like this:

Host    (
Last updated 08:10  4 Dec 1996
Location: /pub/windows/win95/graphic_utils/paintshop
FILE  -rw-r--r--  311542 bytes  15:11 27 Jul 1996

     We have seen in the preceding lesson (-> lesson 9.2) how to
disrupt, through the usual Winice 'live' approach, the
PaintShopPro nagscreens for Windows 3.1., cracking the older
versions of this program. Instead we will now try our 'dead
listing' approach for PSP 32 and PSP Version 4.1 (both 32 bit
     Let's fire PSP32 (I am using here the shareware version with
a PSP.EXE of 1.042.944 bytes, dated 27 December 1995). Let's have
a good look at the nagscreen (snapping it), OK, that's enough.
     Now load the target inside Wdasm32 (I am using here version
5 of Wdasm, you'll find cracked versions of it everywhere on the
     As soon as you have the complete (huge) listing of PSP.EXE
use the option "Save disassembly to text file"... you'll get a
huge text file (with 12.590.025 bytes, gosh). 
     Load it inside a (good and quick, i.e. not Microsoft's)
Wordprocessor and let's first of all have a look at the code
preceding and following our nagscreen (at this point -if you are
not completely imbecile- you should already have grasped the
foundation techniques of my "dead listing" cracking approach).
     You will see that in the code of PSP we have a lot of USER
calls. Therefore we cannot use the same easy 'find the caller'
approach used before (more about code mingling later).
     Have a look at the following piece of code (the Stringdata
for the Shareware notice has landed us there) there is a
GetDlgItem(), which is User32.EB and a EnableWindow(), which is
     GetDlgItem(), as you should know, returns the handle of the
specified ID control (or zero if error). The ID number is the
parameter passed, the returned value is the hdlg (handle of the
dialog box of ID).

:0041DB69 891D38A04B00    mov [004BA038], ebx     ;ebx in here
:0041DB6F 6A6C             push 6C
:0041DB71 881DF4134C00    mov [004C13F4], bl      ;bl in here **
:0041DB77 56               push esi

* Reference To: GetDlgItem, Ord:00EBh in USER32.dll
:0041DB78 FF154C5A4C00    call dword ptr [004C5A4C]   ;callnag-1
:0041DB7E 50               push eax

* Reference To: EnableWindow, Ord:00ABh in USER32.dll ;callnag-2
:0041DB7F FF15DC594C00    call dword ptr [004C59DC]

*StringData Ref from Data Obj->"Paint Shop Pro Shareware Notice"
:0041DB85 6820174C00      push 004C1720
:0041DB8A 56              push esi

Now follow me closely:
1)   Since the following code pushes two locations (one with bx
and the other with bl) just before calling the  GetDlgItem and
EnableWindow routines for the "PaintShopPro Shareware Notice"
2)   ...we just need to search these locations ELSEWHERE,
"around" the above section of the code. Let's do it... Searching
the STRING "[004BA038]" we'll fetch inside our wordprocessed
listing two more occurrences: Let's look at the first one: this
piece of code compares to 1 our location just after enabling a

* Reference To: EnableWindow, Ord:00ABh in USER32.dll
:0041DA41 FF15DC594C00   call dword ptr [004C59DC]
:0041DA47 C605F4134C0002 mov byte ptr [004C13F4],2
:0041DA4E 833D38A04B0001 cmp dword ptr [004BA038],1 ;HERE!! **
:0041DA55 7510           jne 0041DA67 ;and the conditional jump!
:0041DA57 6A00           push 00000000 ;If equal (Zero flag), 
:0041DA59 6A6C           push 0000006C ;push these parameters 
:0041DA5B 6811010000     push 00000111 ;for the PostMessage
:0041DA60 56             push esi     ;function...

* Reference To: PostMessageA, Ord:01A3h in USER32.dll
:0041DA61 FF155C594C00   call dword ptr [004C595C] ;
:0041DA67 B801000000     mov eax, 1        ;our jump here: flag=1
:0041DA6C E9EE030000     jmp 0041DE5F  ;end of code snippet, this

jumps to the "popping away" part of the code...
:0041DE5F 5D                  pop ebp
:0041DE60 5F                  pop edi
:0041DE61 5E                  pop esi
:0041DE62 5B                  pop ebx
:0041DE63 81C434050000        add esp, 00000534
:0041DE69 C21000              ret 0010

     AhHa! The value 111! (at :0041DA5B). You know what that
means, don't you? For the newbyes among you that don't, learn it
here (the others can skip): 
************ THE 111 WM_COMMAND RELEVANCE, by +ORC ********
The function PostMessage() has following structure:
PostMessage(HWND hWnd, UINT uMsg, WPARAM wMsgParam1, LPARAM
lMsgParam2)Where hWnd is the receiving Window, UINT is TRUE or
FALSE WPARAM is a 16 bit value and LPARAM a 32 bit one! 
Windows' applications use PostMessage() to deliver WM_Message
requests... and parameter 111 is WM_COMMAND!
Write it on your cracking notes and underline it! 1 is IDOK, 2
is IDCANCEL and 111 is WM_COMMAND.
     WM_COMMAND is extremely important for understanding the
behaviour of the application you want to crack, because the
handler for WM_COMMAND is where that application deals with user
commands, such as menu selctions, dialog push button clicks,
etcetera. In other words all what makes the 'guts' of an
     An application can tell wich command a user gives through
the wParam parameter to the WM_COMMAND message.
     These values are (almost) always part of the application's
menu resources, and it is easy to get the menu ID values through
any utility for resources dumping.
************ (end) **************
     OK, so the above listed piece of code has a jump over the
PostMessage routine which (probably) disables the nag screen (6C
is the same ID, for both pieces of code we have seen)...let's try
a "weak" crack on it:

***** WEAK CRACK FOR PSP32, by +ORC, January 1997 ********

1)   Use Hexworks32
2)   Load PSP.EXE
3)   Search for the bytes sequences of the instructions
:0041DA4E 833D38A04B0001      cmp dword ptr [004BA038],1    
:0041DA55 7510                jne 0041DA67   
which are followed by the pushes:
:0041DA57 6A00                push 00000000 ;zero
:0041DA59 6A6C                push 0000006C ;ID
:0041DA5B 6811010000          push 00000111 ;WM_COMMAND

And as a quick crack (but there are more elegant ways) we can
4) substitute the byte '75' at :0041DA55 with a 74, transforming
the jne in a je, as usual, inverting the jumps. Jump if equal!

Since I have been (blandly) critized by fellow scholars for
speaking all the time of more elegant ways and yet presenting
only simple ways, here IS a more elegant crack for this code:

4)   Substitute with a new ONE the first TWO instructions:
:0041DA4E 66C70538A04B000100   mov word ptr [004BA038],1
and leave all the rest unchanged:
:0041DA57 6A00                push 00000000 ;zero
:0041DA59 6A6C                push 0000006C ;ID
:0041DA5B 6811010000          push 00000111 ;WM_COMMAND
As you can see, we have just moved the flag '1' inside our
location, instead of comparing and jumping away as in the
original code and in the quickly cracked one. Estilo muchissimo

Since this lesson was thought as +HCU material, here is the
result of the work on it made by (some of) my students:


 Let's finish our cracking of the nagscreens of the
whole PaintShopPro family with version 4.1, which is the most
recentwe know of. 
I am using here PSP.EXE 1.151.488 bytes from 1 Sep 1996, fetch
it through the archies.
We will crack this shareware program BETTER than the registered
version, since we will completely eliminate any nagscreen and
we'll not ever have the welcome screen that REMAINS inside the
registered version. We'll do this 'in a hurry', since it is not
+ORC speaking, but some of his students... you already know
enough about the 'dead listing' method to be able to follow. No
Winice in our hands, Ladies and Gentleman, We never used it on
this program. (Well... we actually did, before reading 9.3, but
it brought us nowhere, for this crack we (almost) DID NOT,
following +his instructions)
1) Get the 'dead listing' of psp.exe (it's huge)
2) Load it inside a fast wordprocessor
3) Find the string 'Purchasing' at :00406710
4) short after 'Purchasing' the only location compared and loaded
is location [004BC218]. Let's call it 'BINGO' and let's hope
it'll work.
5) this location is handled inside a small part of the code
:00406290 to :004067B5, this reduces the more than one million
bytes to 1317 bytes we'll have to examine, not bad.
6) Let's print this nagging part of the code and let's feel it
a little
7) Let's see the possible cracks we could think of (we repeat,
here is not +ORC's but his students at work, we are not perfect).
You'll see this piece of code
:004065D0 50                  push eax
:004065D1 64892500000000      mov fs:[00000000], esp
:004065D8 81ECB4000000        sub esp, 000000B4
:004065DE 833D18C24B0000      cmp dword ptr [004BC218], 0 ;Is
BINGO zero?
:004065E5 56                  push esi
:004065E6 57                  push edi
:004065E7 0F85A0010000        jne 0040678D ;not really
this is 'pentium optimised' code, with a couple of pushes
'inside' the two logically consecutive instructions following the
double 80586 fetching... on a 80486 you would have had:
push esi; push edi; cmp dword ptr [004BC218], 0; jne 0040678D
We may just try substituting a 0F84 at :004065E7 (that is a jump
equal instead of the jne) and BOUM! We get an 'inamovible'
nagscreen, No way to click it away, it covers the main program
PaintShopPro for the eternity. This confirms that we are on the
right track (it's a green light). Uncrack the code (or reload the
original nagged program). Now look here:
:00406495 E95AEA0900         jmp 004A4EF4
:0040649A 33F6               xor esi, esi
:0040649C C745FCFFFFFFFF     mov [ebp-04], FFFFFFFF
:004064A3 893518C24B00       mov [004BC218], esi ;load esi in
Since :00406495 jumps away, it would be interesting to know who
the cuckoos lands at :0040649A. You'll quickly discover that
there are two jumps in this area. One 'xores' the esi before
loading it in our location, the other one does not:
:0040641C 747C       je 0040649A        ;xores esi
:0040646F EB2B       jmp 0040649C  ;does not xore esi
So you may be tempted to try 'a passe ou a casse' following
:0040641C 747E       je 0040649C  ;no xoring even if it should
:0040646F EB19       jmp 0040649A ; xoring even if it should not
Bad score: a frozen nagscreen, a nothing and a crash. Three to
zero for our enemies. Let's look a little more around our 1317
bytes listing.

Well, what else?
The following code refers THREE TIMES to our location, that's a
lot, let's hope it does not suck:
:00406547 33C0            xor eax, eax  ;xor
:00406549 85C0            test eax, eax ;test ax
:0040654B 7513            jne 00406560 ;don't move in cx
:0040654D 8B0D18C24B00 1! mov ecx, [004BC218] ;move in cx
:00406553 85C9            test ecx, ecx ;test cx
:00406555 7418            je 0040656F  ;don't call Updatewin
:00406557 6A01            push 1
:00406559 8B01            mov eax, [ecx] ;move cx in ax
:0040655B FF5004          call [eax+04]  ;and call here
:0040655E EB0F            jmp 0040656F   ;don't call Updatewin
:00406560 A118C24B00  2!  mov eax, [004BC218]  ;move in ax soon
:00406565 8B4820          mov ecx, [eax+20]
:00406568 51              push ecx
:00406569 FF15C4014C00 call dword ptr [004C01C4] ;call
:0040656F 6A00            push 0
:00406571 A118C24B00  3!  mov eax, [004BC218]  ;move in ax later

Well, something fishy here, don't you feel it? Who comes in here?
From where? Let's have a GOOD look at the 'preamble', i.e. the
part of the code that 'switches' to our THREE_SISTER_BINGOS block
:004064DD FF15BCF24B00    call dword ptr [004BF2BC] ;check this
:004064E3 85C0            test eax, eax       ;test
:004064E5 744E            je 00406535              ;zero, go 6535
:004064E7 B896000000      mov eax, 96              ;load par 96
:004064EC 3945E0          cmp [ebp-20], eax     ;compare this
:004064EF 7C44            jl 00406535              ;lower, go
:004064F1 3945E4          cmp [ebp-1C], eax     ;compare that
:004064F4 7C3F            jl 00406535             ;lower, go 6535
:004064F6 8B4508          mov eax, [ebp+08]     ;load this
:004064F9 85C0            test eax, eax         ;test it
:004064FB 7504            jne 00406501          ;do not xor
:004064FD 33C0            xor eax, eax          ;xor and
:004064FF EB03            jmp 00406504          ;do not
:00406501 8B4020          mov eax, [eax+20]     ;...load this
:00406504 6A00            push 0                ;push
:00406506 8B4DE0          mov ecx, [ebp-20]     ;load ecx
:00406509 6A00            push 0                  ;push

Let's have a look at the 6535 routine of the switch PREAMBLE:
:00406535 E836560100       call 0041BB70
:41BB70 A118C34B00         mov eax, [004BC318]
:41BB75 85C0               test eax, eax
:41BB77 7407               je 0041BB80
:41BB79 50                 push eax
:41BB7A FF1530F54B00       call dword ptr [004BF530] ;globalFree
:41BB80 C70518C34B00       mov dword ptr [004BC318], 0
:41BB8A C3                 ret

Well, let's try along, what happen if we substitute a ret at
:00406535 C390909090     ret and nops
Nothing at all.
And what if we substitute here
:004064F6 8B4508                  mov eax, [ebp+08]
:004064F9 85C0                    test eax, eax
:004064FB 7504                    jne 00406501
:004064FD 33C0                    xor eax, eax
:004064FF EB03                    jmp 00406504
:00406501 8B4020                  mov eax, [eax+20]
:00406504 6A00                    push 00000000
... the instruction
:004064FB 7504                    jne 00406501
with the instruction 7500 (or 90 90)?
Now we XOR ANYWAY THIS AX without caring for ax+20. The nagscreen
is defeated? Not really... the nagscreen has been passed ON THE
BACKGROUND, where it still remains when you shut the 'main'
window of the program... the nag protection is somewhere there,
looking at us square in the faces... but where?
+gthorne here: since i had already seen the production version
of paint shop pro, i realized that it also had a nag screen - not
per se, but a splash screen (the very same screen without the
buttons to push and the 'number of days elapsed' warning). what
this means is that it is very possible that the screen
(regardless of what version we are running) could be intermingled
with the rest of the code, and not fully existing within a 
call. This does not proved it, just allowed the possibility.

Poking around, and being playful, i fired up snap32 and grabbed
the nagscreen window and saved it for future reference.

The next thing i did was check into the help screen area of Paint
Shop Pro. Often in shareware programs there is a HELP ABOUT, or
a HELP REGISTER section. Sometimes those places give away good
info needed (or just plain useful) in the crack routine.

Something that stood out in the help area was the paint shop pro
version statement. It seemed to be identical to the printed
version as seen on the nag screen. That means one of two things:
either the version number string was just typed in twice, or more
likely, just another call to a function: PSP_VERSION() or

What that means for the crackist should be pretty clearly a way
to locate the protection routine. Since the word SHAREWARE shows
up in both version calls, it can be scanned for pretty easily
with either that or the word VERSION itself.
Then, once i felt i had done enough scouting the territory, I ran
WDASM and grabbed myself a dead listing. Scanning for SHAREWARE,
I found a couple easy references to it... one being a data string
that I promptly blanked out and the other being in the text that
comes up on the nagscreen itself. WELL WELL... 

  This is the nagscreen result here:

* StringData Ref from Data Obj ->"This is a shareware version 
                                of "Paint Shop Pro."
:00406B64 6810A34B00              push 004BA310
:00406B69 8D4DEC                  lea ecx, [ebp-14]

Okay, running the program again, and firing softice (old friend)
i immediately saw that the version checking routine no longer
tacked on the word SHAREWARE in either the nagscreen or in the
help box.. thus proving the version call to be just that.. a call
(as suspected).

Immediately, something else came up that I was not expecting, the
location of most of the program in memory was exactly the same
as seen with softice that it was hardcoded into the disassembly.
What i mean by that is that where softice was reporting
0137:0043FAD1 for a location in memory, identical data was
reported at location :0043FAD1 on the disassembly.

Now, talk about convenient!

Simply enough, i breakpointed in softice with a few locations i
had located in the disassembly, and voila... landed smack in the
middle of the nagscreen data.

It was do-able with soft-ice alone, but tracing into the maze of
nested calls in PSP really wasn't worth my time.

Line-by-line'ing it, it was simple to watch the nagscreen build
itself call by call, and NOP or alter JZ's & JNZ's to JMP's (74's
and 75's become EB's) so that the nagscreen lost more and more

Then came a little work - not too much, but some things were a
little funny. Notice the BITBLT in the nagscreen creation code.
I had tried to scan for some of the standard SHOWWINDOW calls
with softice and in the disassembly, but not one was to be found
where it needed to be. Apparently PSP was using some other method
of showing the screen... and a simple graphics memory copy (known
to those who follow Micro$oft as a BitBlt) was apparently the
modus operandi.

Some of the text was already in place before the BitBlt, so here
is our reason that some of the nagscreen was not being done as
we watched with the debugger... it was being written to a
backbuffer before being copied as a whole window before being
blitted to the screen. This, for those of you new to graphics
programming, is how people make smooth animation that does not
flicker... all of the animating is done in the background and a
whole screen is blitted at one time rather than by bits and
pieces (you can tell which programs do not do this very easily
since they have images that seem to disappear or flicker wrong
in odd places on the screen)

There were not many calls in that area, and only the ones that 
referenced string data or the bitblt itself could be effectively
nop'ed out.

Here is where i basically stopped the work, since i was having
trouble locating what CALLED the nagscreen area.

The reason we are getting the ghost window in the back (notice
it's size is EXACTLY the same as the nagscreen, is that windows
is being instructed by some window create command to block off
the rectangular space for the screen even though the graphic
interior does not get written.

The backgrounding code is invaluable here since the ghost window 
does not have a way to be removed without paint shop pro closing 
(which cleans up nicely for itself on exit).

Now we need to locate the call that creates the window in the
first place and nop it or whatever... there is probably a nicer
way to do all this (if you work at it, you can probably jmp past
all the functions i nop'd - taking care not to leave unmatched
pushes or pops which ruin the flow of the program - at some point
in the nagscreen area

The funny part of all this is that our cracked version will be
better than the registered version since the registered version
has a splash screen anyway - and ours does not :)

Using all of this knowledge later can actually benefit us in a
different way... we can actually use all of the techniques (since
we already know where the nagscreen is) on the executable of the
full version as well. 
Since it has no nag functions, it is a smaller executable and
cracking it will give is a nagless, splashless (eg. screenless)
version of paint shop pro that is smaller (i.e. better and more
functional) than by just cracking the shareware version.

I find that funny somehow :)

So here is all the rest you need: 

Let's have a look at the VERSION subroutine that is called both
in the nagscreen and the HELP screen:

* StringData Ref from Data Obj ->"4" ;FIRST DIGIT OF VERSION 4.01
:004350E4 6874AE4B00              push 004BAE74
:004350E9 8B3D90004C00            mov edi, [004C0090]
:004350EF C645FC07                mov [ebp-04], 07
:004350F3 C706F0A54A00            mov dword ptr [esi], 004AA5F0
:004350F9 FFD7                    call edi
:004350FB 83C404                  add esp, 00000004
:004350FE 8BD8                    mov ebx, eax
:00435100 C1E310                  shl ebx, 10

* StringData Ref from Data Obj ->"0"    ;SECOND DIGIT OF 4.01
:00435103 6860A64B00              push 004BA660
:00435108 FFD7                    call edi
:0043510A 83C404                  add esp, 00000004
:0043510D 0BD8                    or ebx, eax
:0043510F C1E308                  shl ebx, 08

* StringData Ref from Data Obj ->"1"   ;THIRD DIGIT OF 4.01
:00435112 685CA64B00              push 004BA65C
:00435117 FFD7                    call edi
:00435119 C1E010                  shl eax, 10
:0043511C 83C404                  add esp, 00000004
:0043511F 0BD8                    or ebx, eax

* Possible StringData Ref from Data Obj ->"2"
:00435121 68C4A14B00              push 004BA1C4
:00435126 FFD7                    call edi
:00435128 83C404                  add esp, 00000004
:0043512B 0BD8                    or ebx, eax
:0043512D 899ED4000000            mov [esi-000000D4], ebx

* Possible StringData Ref from Data Obj ->"0"
:00435133 6860A64B00              push 004BA660
:00435138 FFD7                    call edi
:0043513A 83C404                  add esp, 00000004
:0043513D 50                      push eax

* Possible StringData Ref from Data Obj ->"1"
:0043513E 685CA64B00              push 004BA65C
:00435143 FFD7                    call edi
:00435145 83C404                  add esp, 00000004
:00435148 50                      push eax

* Possible StringData Ref from Data Obj ->"4"
:00435149 6874AE4B00              push 004BAE74
:0043514E FFD7                    call edi
:00435150 83C404                  add esp, 00000004
:00435153 50                      push eax

* Ref from Data Obj ->"%i.%i%i"  ;PRINT 3 INTEGERS: N.NN (4.01)
:00435154 686CAE4B00              push 004BAE6C
:00435159 8D86CC000000            lea eax, [esi-000000CC]
:0043515F 50                      push eax

* Reference To: n/a, Ord:09A7h in MFC40.DLL
:00435160 E815FF0600              call 004A507A
:00435165 83C414                  add esp, 00000014
:00435168 8D8ECC000000            lea ecx, [esi-000000CC]

* StringData Ref from Data Obj ->" Shareware"  ;PRINT SHAREWARE
:0043516E 6860AE4B00              push 004BAE60

The first BitBlt routine of the program puts the screen on!

it is at:

* Reference To: BitBlt, Ord:000Ah in GDI32.dll
:00406917 FF15B4F24B00            call dword ptr [004BF2B4]
:0040691D B800000000              mov eax, 00000000
:00406922 85FF                    test edi, edi
:00406924 7403                    je 00406929
:00406926 8B4704                  mov eax, [edi+04]
:00406929 50                      push eax
:0040692A 8B45B4                  mov eax, [ebp-4C]
:0040692D 50                      push eax

and here some other annoying pieces of code: The red bar as soon
as you use the program more than 30 days...

* Reference To: n/a, Ord:0970h in MFC40.DLL
:00406BE3 E836E60900    call 004A521E ;DRAW RED BAR
:00406BE8 6801080000    push 00000801
:00406BED 8D45A0        lea eax, [ebp-60]
:00406BF0 50            push eax
:00406BF1 8B4DEC        mov ecx, [ebp-14]
:00406BF4 8B853CFFFFFF  mov eax, [ebp-000000C4]
:00406BFA 8B51F8        mov edx, [ecx-08]
:00406BFD 52            push edx
:00406BFE 51            push ecx
:00406BFF 8D8D3CFFFFFF  lea ecx, [ebp-000000C4]
:00406C05 FF5070        call [eax+70] ;BOTTOM WINDOW TXT+RED BAR
:00406C08 53            push ebx
:00406C09 8D8D3CFFFFFF  lea ecx, [ebp-000000C4]

Once found all the above, the rest is pretty obvious... here all
the necessary crackcodes: bytes to find and change. This is a
weird, non-elegant crack, but kinda funny, so i had to write it
down until i discover a better one (+gthorne speaking) note:
i gave good search strings so lamers don't get confused with
similar patterns in the software:

8B450885C07504 to 8B450885C07500 -  screen backgrounding
3CFFFFFFFF5070 to 3CFFFFFF750090 - do not draw lower text in box
3CFFFFFFFF5064 to 3CFFFFFFEB0090 - do not report version number
83FF1E7E1E to 83FF1EEB1E         - do not draw red bar
000083FFC07403 to 000083FFC0EB03 - turn nagscreen invisible
53686172657761726500 to 20202020202020202000 - disable shareware
notice writing phisycally some spaces over it

final note: since psp closes the nagscreen when the program
exits, all is now cleaned up.

 FF15B4F24B00 to 750090750090 (bitblt) seems to not do anything
the above cracks dont do.

 Time to explain BitBlt... isn 't it? BitBlt copies
a bitmap from the device context sopecified by hdcSource to the
device context specified by hdcTarget performing a block copy.
On success the function returns not zero. 
It is called with the handle hdcTarget, int nTargetX and nTArgetY
(upper left coordinates of the point at which the bitmap will be
copied... a 'pixel point' locator may be very useful in our
trade) int nWidth and nHeight of the region, the handle hdcSource
and then, finally the upper left coordinates IN the bitmap, the
two int nSourceX and nSourcey. Is it all? NO! The final
parameter, DWORD dwRaster determines HOW the bit-by-bit contents
of the bitmap will actually be copied (black, inverted, ANDed,
ORed, XOred...etc.).

Lotta things to learn if you want to crack a lot...
THE STUDENTS OF UNIT 4 (+HCU, January 1997)

As a matter of fact both cracks here, mine for PSPS32 and my
student's VERY smart one for PSP41 are 'weak', though: they will
not eliminate the nagscreen (you should search for the remaining
occurrences of our locations and work from them inwards, if you
really want to crack completely this scheme, but in this case it
would be better to work more with Winice95, in my opinion. But
I hope I have made the point about "dead listing" cracking: It
works! Quick and placid! In the PSP32 case the protection will
still show you the nagscreen, which will "automatically"
disappear, though, leaving you with no annoyance (in nuce: you
cracked the return button), in the other case you have physically
eliminated all possible annoyances. The nagscreen is still there,
but it does not 'harm' you any more.
Anyway I wanted only to show you the POWER and the CHILD'S PLAY
working of this "dead listing" approach (that you may combine
with some quick winice probes if you really think you need it
     You'll agree with me, though, that this quick "weak" crack,
made with the "dead listing" method is far less tiresome than a
'live' crack with our beloved SoftIce/WinIce.
     Now, in life, I believe, you should always search to obtain
the maximum giving the minimum. There is no point in being
altruistic or excessively honest in a society where the tiny
minority that profits most keeps getting richer and richer and
the overwhelming majority that lives with meager earnings keeps
getting poorer and poorer (and -moronized by TV-watching and
other Pavlovian propagandistic sources of misinformation- keeps
voting the same rich bastards that keep it enslaved under the
whips of publicity, as if the slaves of ancient Egypt would
happily vote for their Pharaohs). I abhor and despise the society
I am compelled to live in... but this does not mean that I
renounce to anything. I am (pretty) rich, (yet do not exploit
anybody), eat very well, have a big nice house with all the
useless objects and cars and garages and terraces and futile
gadgets you are supposed to enjoy in this moronic society (I
enjoy foremost my spacious library) and I drink my regular bottle
of (Moskowskaja) Wodka every week. My liver (and my nice family)
do not seem to complain :=)

Well, that's it for this lesson, reader. Not all lessons of my
tutorial are or will be on the Web.
     You'll obtain the missing lessons IF AND ONLY IF you mail
me back (via with some tricks of the trade I may
not know that YOU discovered. Mostly I'll actually know them
already, but if they are really new you'll be given full credit,
and even if they are not, should I judge that you "rediscovered"
them with your work, or that you actually did good work on them,
I'll send you the remaining lessons nevertheless. Your
suggestions and critics on the whole crap I wrote are also
welcomed. Do not annoy me with requests for warez, everything is
on the Web, learn how to search, for goddess sake.

     "If you give a man a crack he'll be hungry again
     tomorrow, but if you teach him how to crack, he'll
     never be hungry again"

E-mail +ORC