LockDown 2000
A Tutorial on Time Limit Cracking
The DrunkMan


So, I've been asked to write a tutorial about this target (which is easy enough to understand and to kill).
This tut will just show you how I removed the time limit protection (just 11 days!) from this file. We'll see that the protectione scheme itself is trivial, but the main trouble is patching the executable. You'll learn how a loader works and why it is so useful.

Beginning
ŻŻŻŻŻŻŻŻŻ

After installation, launch the proggie and see what happens: initialization, then the main window displaying that your version is not registered and you have some days left.
What to do? As usually, when you have in front of you a time limit protection, you should activate it... so change your system date consequently. Run again the proggie and it will say "Sorry, your time is over, hope you enjoyed this piece of software, cause it won't run again..." and - BOOM - back to Windoze.
So here it starts our journey!

Removing the Protection
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ

Time limit is not the best way to protect your sw, mainly because you have not many ways to check the system date; as long as I know, the only time API I've ever seen are GetSystemTime and GetLocalTime.
So, the next step is place a breakpoint over these.
If you choose GetSystemTime you break on only one call, in the very beginning of the execution, and nothing more - so I have good reasons to believe this is not the good one.

Then try GetLocalTime; you will break into SoftIce - press F5 - you break again - press F5 - you break again. (Note: when you break, you should take a look at the code nearby to see if the call to GetLocalTime is significant - but I'll just skip this step to keep this tut short enough not to bore the reader :)

Now press F12 and you are in LockDown - you see there is some kind of manipolation using FPU registers:

0157:00409D7C E823CEFFFF CALL KERNEL32!GetLocalTime 
0157:00409D81 668B4C240E MOV CX,[ESP+0E]                <- after F12 you are here
0157:00409D86 668B54240A MOV DX,[ESP+0A] 
0157:00409D8B 668B442408 MOV AX,[ESP+08] 
0157:00409D90 E81FFEFFFF CALL 00409BB4 
0157:00409D95 DD5C2418   FSTP REAL8 PTR [ESP+18]
0157:00409D99 9B         WAIT 
0157:00409D9A 668B442416 MOV AX,[ESP+16] 
0157:00409D9F 50         PUSH EAX 
0157:00409DA0 668B4C2418 MOV CX,[ESP+18] 
0157:00409DA5 668B542416 MOV DX,[ESP+16] 
0157:00409DAA 668B442414 MOV AX,[ESP+14] 
0157:00409DAF E828FCFFFF CALL 004099DC 
0157:00409DB4 DC442418   FADD REAL8 PTR [ESP+18] 
0157:00409DB8 DD1C24     FSTP REAL8 PTR [ESP] 
0157:00409DBB 9B         WAIT 
0157:00409DBC DD0424     FLD REAL8 PTR [ESP] 
0157:00409DBF 83C420     ADD ESP,20 
0157:00409DC2 C3         RET 

Press F12 again and you are straight in the time limit comparison code. Why?
Because you can see there is still FPU manipolation (a subtraction - between two different dates maybe?) and, looking at the result of the following call, you see a positive value in eax meaning "OK, still evaluating" and a negative value meaning "Your time has come" (you may try it yourself by changing the value in eax after the call at 4A31F3).

0157:004A31E8 E8876BF6FF   CALL 00409D74 
0157:004A31ED DCADDCFDFFFF FSUBR REAL8 PTR [EBP-0224]       <- after F12 you are here 
0157:004A31F3 E808F9F5FF   CALL 00402B00                    <- time checking routine 
0157:004A31F8 8BD8         MOV EBX,EAX                      <- eax>=0 : go on evaluating 
0157:004A31FA 33C0         XOR EAX,EAX                         eax<0  : kick his ass! 
0157:004A31FC 5A           POP EDX 
0157:004A31FD 59           POP ECX 
0157:004A31FE 59           POP ECX 
0157:004A31FF 648910       MOV FS:[EAX],EDX 
0157:004A3202 682C324A00   PUSH 004A322C 
0157:004A3207 8D85E4FDFFFF LEA EAX,[EBP-021C] 
0157:004A320D BA02000000   MOV EDX,00000002 
0157:004A3212 E8BD0AF6FF   CALL 00403CD4 
0157:004A3217 8D45F4       LEA EAX,[EBP-0C] 
0157:004A321A BA02000000   MOV EDX,00000002 
0157:004A321F E8B00AF6FF   CALL 00403CD4 
0157:004A3224 C3 RET 

Instead of tracing through the call at 4A31F3, I just want to fool the app saying that I will always have some days to evaluate it (let's say 13 - 0Dh - my lucky number :).

I just need to patch some bytes:

0157:004A31F3 E808F9F5FF CALL 00402B00 -----> 	0157:004A31F3 33C0	XOR	EAX,EAX
						0157:004A31F5 040D 	ADD 	AL,0D
						0157:004A31F7 90 	NOP 

Doing this will lock eax to 0Dh, regardless of time!

Ok, by now you should start your favourite patcher looking for E8,08,F9,F5,FF to replace with a nicer 33,C0,04,0D,90... but as you can see there's no such string in your original LockDown2000.exe !

Some Troubles...
ŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻŻ
No panic, are you a cracker or what ?!?
This means that your executable is packed and/or encrypted (if you are not sure, just try to disassemble the file using WDasm32 and have some fun :). This should prevent casual crackers from completing their jobs. The question is, we are NOT casual crackers :)
The first step I took (it's always the best way) was searching over the net for some tuts about loaders. Well, I find many, but the one that really helped me was R!SC's one; you can find it at csir.cjb.net. There are many interesting docs even on Fravia site, if you manage to find a working mirror...

How does a packer works?
Mainly, a packer compresses your original executable, decreasing its size and writing this compressed version to a new executable; this new executable begins with a decompression algorithm which restores the original program. After the decompression, the original file is in memory and the "real" execution begins.

How can I crack packed programs?
Well, there's more than one way. In this tutorial we'll use a loader, but you may also hard-code the patching bytes in the packed program itself to make your crack (this second method is very elegant indeed).

So how does a loader works?

Loaders
ŻŻŻŻŻŻŻ

You know that the packed file, sooner or later, will be unpacked in memory and when it will be we can patch him as described before. The only thing is, how can we modify a program that is already running?
Windoze offers a couple of process API to help us: they are ReadProcessMemory and WriteProcessMemory. The first one loads in a buffer some bytes directly from a process' memory image, the second one does exactly the opposite - writes a buffer over a process' memory image.

But you can't access those functions unless you have some privileges over the process you want to patch. To gain the required privileges you should CREATE that process - I mean, you have to start that process from INSIDE your loader (by the way, that's the reason why you call it a loader :)

Mainly the step behind a loader are the following:

* Create the process
* Wait for it to decompress
* Read its memory
* Compare with original bytes
* If you find the original bytes, patch them
* Kill the loader, let the patched process run

Obviously, this sequence can be improved (for example, if the bytes in memory are not identical to the original bytes you may try to read again the memory again some times - maybe that part of code hasn't been unpacked yet).

So, here's an example of how a loader should look like (it is taken directly from RISC tutorial - all my credits go to that guy :)
I used Masm32.

;-=-Loader.asm-=-=-cut & paste me :)=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
; based on original loader.exe by Hayras [tNO '98] used in
; Hayras's Neolite v1.22 memory patcher.
; 
; Special thanks to TNO for graciously allowing me to use this
; source on behalf of Hayras, who has now retired from the scene. 
; Yes, Hayras's loader reversed by R!SC, then totally re-written
; & released to the public, so everyone can learn this shit :) 
; (c)1999 R!SC (see what i do instead of cracking...duh) yey Prophecy!

.386P 
.Model Flat ,StdCall 
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\gdi32.inc 
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\gdi32.lib 
;Define the needed external functions and constants here.

;-=-Normal data-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
.Data 
LARGE 		dw 0F000h 
CSiR_Tag 	db ' Lockdown 2000 Loader + Patcher',0
CSiR_Error 	db 'Error!!!',0
CSiR_Error1 	db 'Something fucked up...',0
OpenERR_txt 	db 'CreateProcess Error :(',0
ReadERR_txt 	db 'ReadProcessMemory Error :(',0
WriteERR_txt 	db 'WriteProcessMemory Error :P',0
VersionERR_txt 	db 'Incorrect Version of application :(',0 
CSiR_ProcessInfo 	dd 4 	dup (0) ;process handles
CSiR_StartupInfo 	db 48h 	dup (0) ;startup info for the process were opening
CSiR_RPBuffer 		db 5 	dup (0) ;read buffer, for checking data 
;-=-Patch datas-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
CSiR_AppName 		db 'lockdown2000.EXE',0 
fuck 			dd 04A31F3h 		; address to read data from for version checking
origsize 		dd 5 			; in the new process
checkbytes 		db 0E8h,8h,0F9h,0F5h,0FFh	; original bytes
						  	; if they are not there,
							;we have the wrong version?? 
patch_data_1 		db 033h,0C0h,04h,0Dh,90h ; patch bytes
patch_size_1 		dd 5
patch_addr_1 		dd 04A31F3h 
.Code 
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Main:
	push offset CSiR_Tag
	mov dword ptr [CSiR_StartupInfo],44h ; (the size in bytes of the structure)
	push offset CSiR_ProcessInfo ; Typedef struct _PROCESS_INFORMATION 
	push offset CSiR_StartupInfo ; Pointer to STARTUPINFO structure
	push 0
	push 0
	push 20h ; Creation flags
	push 0
	push 0
	push 0
	push 0
	push offset CSiR_AppName ; Pointer to name of executable mod
	call CreateProcessA	; starts packed executable
	test eax,eax
	jz OpenERR 
Wait4Depack: 
	push LARGE-1 ; Timeout (in milliseconds, -1 = infinite)
	push dword ptr [CSiR_ProcessInfo]
	call WaitForInputIdle	; wait for executable unpacking
Check_Data: 
	push 0 ; BytesRead
	push dword ptr [origsize] ; Length
	push offset CSiR_RPBuffer ; Destination (to read them to)
	push dword ptr [fuck] ; Source
	push dword ptr [CSiR_ProcessInfo] ; Process whose memory we are to read
	call ReadProcessMemory
	test eax,eax
	jz ReadERR
	;...
	;int 03 ;-)
	cld
	lea esi, CSiR_RPBuffer	; compares process' bytes
	lea edi, checkbytes	; and original ones
	mov ecx, dword ptr [origsize]
	repnz cmpsb
	jnz VersionERR
	;...
Patch_the_mother:
	push 0 ; Pointer to byteswritten (i like null though)
	push dword ptr [patch_size_1] ; Length
	push offset patch_data_1 ; Source
	push dword ptr [patch_addr_1] ; Destination
	push dword ptr [CSiR_ProcessInfo] ; Process whose memory we are to patch
	call WriteProcessMemory ; Call Kernel32!WriteProcessMenory
	test eax,eax
	jz WriteERR
Close_This_app:
	push dword ptr [CSiR_ProcessInfo]
	call CloseHandle
	push dword ptr [CSiR_ProcessInfo+4]
	call CloseHandle	; kills the loader
Exit_Proc:
	Push LARGE-1
	Call ExitProcess 
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
VersionERR:
	lea eax, VersionERR_txt
	jmp abort
ReadERR:
	lea eax, ReadERR_txt
	jmp abort
OpenERR:
	lea eax, OpenERR_txt
	jmp abort
WriteERR:
	lea eax, WriteERR_txt
abort:
	push 0
	push offset CSiR_Error ; Title
	push eax ; Message
	push 0
	call MessageBoxA 
jmp Close_This_app
End Main
;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

The code is straightforward: it follows all the steps mentioned above.
To see how does the CreateProcess, WaitForInputIdle, ReadProcessMemory, WriteProcessMemory functions work just refer to a Win32 API Reference... or simply use this piece of code as a template (be careful: it does not always works - when you have really small executables the decryption process may be too fast to allow your patch).
As you can see, the offset where you read from and write to inside de process image is the same you found during the cracking session: 4A31F3.
There should be no more problems - just build the code, copy the executable to the target directory and your little crack is done.

Hope you found this tut somewhat interesting...
Happy cracking,
The DrunkMan

=========================================================================================
Credits: if you still didn't catch that, without RISC this tut would never have been possible.

GREETZ 
All Armageddon members ;)
ZeroC00l
MartiX
dvj

=========================================================================================