Stop nagging me will ya!
(Evaluate This)
November 2000
by Reed
Courtesy of Fravia's page of reverse engineering
slightly edited
by +Tsehp
A good newbie's essay, good enough to start your anti-nag skills.
There is a crack, a crack in everything That's how the light gets in
(x)Beginner ()Intermediate ( )Advanced ( )Expert

Kind of playing around with nags (splash screens and so on), with a tiny bit M$ VC++ reversing.
Stop nagging me will ya!
(Evaluate This)
Written by Reed


This is not cracking, it's just simple reversing or modifying an application, you know those programs where you wait five minutes for it to kill it's "splash" screen (or "company logo"), or when you have to click it to get rid of it. The one in this essay is a nag, it's Paint Shop Pro's.

When your confronted with a nag, you don't really have to crack it, sometimes it can be too much trouble to crack the nag out of an application, it could be hard work, but I'm sure if your really capable--you'll do it!

You see if we make the nag, stop nagging, as long as it doesn't bother you, you'll be able to "evaluate" the application properly. Oh yeah, a nag that doesn't nag isn't a nag, right? :-). Also I think it's stupid to nag your potential buyers, you'll put 'em off, so 'stop nagging me will ya!' and protect you apps or disable some stuff (that's fun to crack ;-).

I'm using Win98, version 6.01 of PSP, psp.exe with a huge size of 7782400 bytes!

Tools required
SoftICE 4.05
IDA (I used 4.04, but you might not need to disassemble anything anyway)
Hex Editor (or HIEW, or something)

Target's URL/FTP

Program History
PSP was cracked in +ORC's tutorial lessons 9.2 and 9.3, which you have read (I hope).


If you use the "Evaluation Version" of PSP then you have certainly noticed the nag screen that presents itself at startup. This nag is comprised of a huge bitmap (or a couple, who cares?), a little text (including the "Evaluation" text and notice), and two buttons. You can't immediately kill the nag by clicking the 'Ok' button, OH! NO! to piss you off even more the coders decided to rub a little salt on the wounds that the nag created and add a timer that lets you click the nag away after a specified amount of time. If you examine the window procedure of the nag you'll notice that, if you click the nag, it first checks where you clicked by obtaining the co-ords of the mouse (uses GetCursorPos, then PtInRect if you clicked in the correct places -a button- it then continues), then it sees whether the click is allowed, if so it kills the nag for you. Oh yeah, it's written in M$ Vi$ual C++, you'll land in the libraries quite a lot, which is to be expected for this type of application (given the size and speed 'n' all).

I could now go on explaining what I found, taking this approach -the window procedure one- but after a while I decided to take a different approach, because removing the nag would be nice but it isn't really necessary.

So, the approach, well when d'you think would be the right.time and place to fix this nag, of course, your thinking when it's created and your right. Firstly we'll have to find where the nag is created and then we can worry about what to do.

When you launch PSP -after a while of loading- you see the nag screen, you see it but you don't see anything else around do you. What this tells us is that the nag is created before the main GUI is loaded (or updated). Where can we go from here? We certainly should think how this nag is shown to us. Well the program obviously uses the API UpdateWindow to "update" the nag to the screen, so our next port of call is to investigate the area of code that shows us the nag, so 'bpx updatewindow do "p ret;rs"' in SoftICE until you see the nag on the screen and, you'll be here:

loc_76B84A:				; CODE XREF: .text:0076B82Fj
		mov	eax, dword_930B2C
		mov	ecx, [eax+20h]
		push	ecx		; hNag
		call	ds:UpdateWindow	; UpdateWindow:	<--- HERE YOU GO!

loc_76B859:				; CODE XREF: .text:0076B848j
		push	edi
		push	edi
		push	20000h		; Style (WS_MINIMIZEBOX)
		mov	ecx, esi
		call	j_MFC42_4284	; ?ModifyStyle@CWnd@@QAEHKKI@Z:	
		push	offset str5000	; "5000"
		call	ebx		; atoi -> ASCII to INT
		mov	ecx, dword_930B2C
		add	esp, 4
		mov	edx, [ecx+20h]
		push	edi
		push	eax
		push	7
		push	edx		; hNag
		call	ds:SetTimer	; SetTimer, set the button timer
		mov	ecx, dword_930B2C
		mov	[ecx+74h], eax
		call	ds:GetTickCount	; GetTickCount
		mov	ecx, [esp+10h]
		pop	esi
		mov	dword_930B20, eax
		pop	ebx
		mov	eax, 1
		pop	edi
		mov	large fs:0, ecx
		add	esp, 10h
This is from IDA of course. IDA helps us (a lot!) because if you look at the 'call j_MFC42_4284' you'll see it's a call to 'ModifyStyle'. (By the way, I didn't disassemble PSP straight away, with IDA or anything, but I noticed that WS_MINIMIZEBOX was being pushed (20000h, from coding and stuff), I changed it to null and it worked so I had to have a look, I couldn't help myself :)

ModifyStyle, of course, modifies the style of a window, it takes a few args but PSP is using it here to remove the WS_MINIMIZEBOX style (20000h) from the main window of PSP (I don't know anything about M$ VC++ so please excuse me if I get stuff wrong). Now to make our first fix, let's change the line 'push 20000h' (before the call to ModifyStyle).

So as mentioned above (before I disassembled psp.exe remember), this

0076B859  57                  PUSH      EDI
0076B85A  57                  PUSH      EDI
0076B85B  6800000200          PUSH      00020000	; WS_MINIMIZEBOX
0076B860  8BCE                MOV       ECX,ESI
0076B862  E835870C00          CALL      00833F9C	; ModifyStyle
switches to this

0076B859  57                  PUSH      EDI
0076B85A  57                  PUSH      EDI
0076B85B  6800000000          PUSH      00000000	; NOTHING!
0076B860  8BCE                MOV       ECX,ESI
0076B862  E835870C00          CALL      00833F9C	; ModifyStyle
You could also change it to 6A00909090 (push 0, nop, nop, nop--because the stack is aligned to a dword boundary in 32 bits).

Now restart PSP, after applying the fix, and oh look, the main window is nice now with it's minimize box, no more grey disabled bits, isn't it? :-) I'm not going to go into the timer and everything, we'll now make sure the nag stops nagging. What to do then?, well we're going to extend this area of code (the nagging code, called from 0064F11F, by the way) to make it kill the nag after it's creation. We'll send a WM_CLOSE message to the nag forcing it to close and clean itself up. Nice, let's go!

We're extending this sub, but where's our code going, take a look around, all those fillers (int 3 (CCh)), that's right we can use them to our hearts content.

So this

0076B8A5  64890D00000000      MOV       FS:[00000000],ECX
0076B8AC  83C410              ADD       ESP,10
0076B8AF  C3                  RET

0076B8A5  64890D00000000      MOV       FS:[00000000],ECX
0076B8AC  A12C0B9300          MOV       EAX,[00930B2C]
0076B8B1  8B4820              MOV       ECX,[EAX+20]		; Get hNag
0076B8B4  6A00                PUSH      00
0076B8B6  6A00                PUSH      00
0076B8B8  6A10                PUSH      10			; WM_CLOSE
0076B8BA  51                  PUSH      ECX			; hNag
0076B8BB  FF1590B59800        CALL      [USER32!SendMessageA]	; Kill the nag
0076B8C1  83C410              ADD       ESP,10
0076B8C4  B801000000          MOV       EAX,00000001		; Just to be safe
0076B8C9  C3                  RET
You'll need to set a breakpoint on SendMessageA to check how it's called inside PSP, because if you assemble this in SoftICE you'll be calling straight to User32 and not through the import table, it'll be Ok on your PC if you don't but may not work on others, which probably won't bother you. The WM_CLOSE message is used because that's the one that didn't make PSP scream like a baby.

If you now run PSP you'll see the nag popup... then disappear! Yep, nice isn't it. But your still seeing the damn thing, straight in your face every time you run PSP.

So one last little fix, modifying the size of the nag. How's the nag itself created. After the ret at 0076B8C9, or 0076B8AF if you haven't fixed it yet, you'll return to 0064F11F. The call here creates and displays the nag (that's where we were a minute ago). So set a breakpoint on 0064F11F ('bpx 0064F11F'), step into the call, and 'bpx createwindowexa', p ret ('F12') out of the API, your now in M$ VC++ class library (MFC42.DLL) so p ret out of that (p ret twice really), and looky here then:

loc_76BF17:				; CODE XREF: .text:0076BF12j
		push	0
		push	0
		push	eax
		push	edx		; height
		push	ecx		; width
		push	0
		push	0
		push	90000000h
		push	0
		push	0
		push	0
		push	0
		push	0
		call	j_MFC42_1233	; ?AfxRegisterWndClass@@YGPBDIPAUHICON__@@PAUHBRUSH__@@0@Z: 
		push	eax
		push	0
		mov	ecx, ebx
		call	j_MFC42_2152	; ?CreateEx@CWnd@@QAEHKPBD0KHHHHPAUHWND__@@PAUHMENU__@@PAX@Z: 
		pop	edi
		pop	esi
		pop	ebx
		pop	ecx
		retn	4
Here we go then, this is where the nag is created. The only two args I've outlined are the height and width (the rest are hInst, Style and mostly nulls) which is all we need, we'll change the height and width to 1x1 so you'll only see 1 pixel in the center of the screen.

'PUSH REG' is only one byte, so we only have two bytes. A 'PUSH BYTE' is two bytes and we have two pushes (height and width) so we're going to have to put a jump there. A short jump is two bytes which is good even though this really doesn't matter because if we'd crushed more instructions we could have made them up with all those fillers, let's fix it then.

We change this

0076BF17  6A00                PUSH      00
0076BF19  6A00                PUSH      00
0076BF1B  50                  PUSH      EAX
0076BF1C  52                  PUSH      EDX	; height
0076BF1D  51                  PUSH      ECX	; width
to this

0076BF17  6A00                PUSH      00
0076BF19  6A00                PUSH      00
0076BF1B  50                  PUSH      EAX
0076BF1C  EB42                JMP       0076BF60; jump to fix
and don't forget to put this little piece in place

0076BF5C  C20400              RET       0004
0076BF5F  90                  NOP
0076BF60  6A01                PUSH      01	; height (1 pixel)
0076BF62  6A01                PUSH      01	; width (1 pixel)
0076BF64  EBB8                JMP       0076BF1E; jump back
0076BF66  CC                  INT       3	; <-Filler
Now, run PSP and see what happens. Yeah, the nag's hardly visible. I think we can finish now, but I know that some of you may be wondering why we kill the nag even though it's only 1 pixel in size now, well it's taking up memory and it could get in the way of things, that's it really. I think the WM_CLOSE message worked here because the nag didn't process the WM_CLOSE message, this results in it sending the message to DefWindowProc which in turn uses the DestroyWindow API to kill the nag. So this message is pretty useful if your app doesn't process it.

You can also try sending WM_DESTROY, WM_QUIT to your nag or the old "simulate click" trick (send a WM_LBUTTONDOWN, WM_LBUTTONUP message) or even a WM_COMMAND message with the ID of your button, try anything! This approach saves you from getting into the messy message loop, but every situation is different and you just try things until you 'think' you found a solution.

Final Notes

There's a problem (big problem) with nags that are totally integrated with the 'real' code, it's a bitch getting rid of them. Just examining the surrounding code isn't enough when your in this situation, but remember kids "a nag that doesn't nag isn't a nag" and you can do this with any sort of window--modify it to suit your own needs!

So your all going to modify all those "startup", "splash", "nag" or whatever screens that you were once forced to see everytime you started an application now, aren't you?


Ob Duh
I wont even bother explaining you that you should BUY this target program if you intend to use it for a longer period than the allowed one. Should you want to STEAL this software instead, you don't need to crack its protection scheme at all: you'll find it on most Warez sites, complete and already regged, farewell, don't come back.

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

redhomepage redlinks redsearch_forms red+ORC redhow to protect redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_Fravia
redIs reverse engineering legal?