Guide to a generic keygen for Soft-Guard 1.10 by sn00pee of BLiZZARD ============================================= First I wanna mention that my english isn't good, sorry for any grammatical or other faults :) What we need ------------ - Our victim: A Simple Task Reminder, download it from http://www.somtel.com/~barrym/download.htm You can also use another Soft-Guard protected app. - Numega SmartCheck - W32DSM(with or without string patch, doesn't matter) - masm32(or tasm if you want, but then you have to apply some changes to the code) - a working brain :) Besides you must have - experience with W32DSM - experience with SmartCheck - experience with the Asm-Language Yes! We really don't need SICE! ;) What is Soft-Guard? ------------------- What the developers say(taken from www.soft-guard.com): Soft-Guard is an ActiveX control designed for use with Visual Basic v5.0/6.0 applications to easily implement characteristics of Shareware Applications into your Windows 95/98/NT programs. Soft-Guard allows you to implement evaluation periods into your applications and also handles the task of generating unique registration codes to unlock your application's evaluation period. Soft-Guard also now allows you to generate registration codes that are system sensitive (i.e. the code only works on a system that matches the same specifications). Soft-Guard keeps track of evaluation period information and registered users in the Windows system registry. Evaluation information is stored three times in the system registry, all of which are encrypted using different methods. Should a user attempt to edit the information, the evaluation period will be voided as the three locations that the information is stored do not match. Soft-Guard's features were designed to be easily implemented with only a few lines of code. Soft-Guard generates a unique registration code based upon the user's name, an encryption key set by the programmer, and other optional choices. Registration codes can be formatted to your own style based upon multiple settings. The registration codes are mathematically calculated combining the user's name and the encryption key, and then formatted to your preferences. Soft-Guard also allows for the registration codes to be date sensitive, which also calculates the date into the code. The registration code will then only be valid on the date on which it was created. What sn00pee says ;): This text is a good overview...it sounds a bit complicated and difficult, but believe me, it really isn't ;) The *.ocx control(SOFTGUARD5.OCX, you can find it in your windows/system dir, assumed you installed the program) is entirelly written in Visual Basic 5, compiled to Native Code and has no anti-debbuging routines. So we can use Numega SmartCheck for cracking. Version compatibility --------------------- As far as i knowm, there is only one version of soft-guard out there (1.10, the one we use), I really never had something to do with other ones. Why a generic keygen? --------------------- Well, commercial protection schemes are very susceptile to generic patches/keygens etc., you can save a lot of time and work and sometimes even money when using them :D. Of course you can reverse the whole thing (btw: reversing the softguard-ocx is quite easy, you just need to invest enough time), but since we wanna expend our horizons, we code a generic keygen. And btw, it sounds much better ;). I tested this keygen with several protected programs, it works with and without system depending serials and should work with date sensitive ones too(don't blame me if it doesn't ;)). Information about our victim ---------------------------- Not much to say, it uses system depending serials, what means that the serial will only work on YOUR PC with YOUR unique System ID(soft-guard uses some weird algo to calculate it). Step 1: Cracking(not very long :)) ---------------------------------- Enough of boring texts, let's rock :) ... Fire up Smart-Check, open the str.exe and start it. Click on yes and you should get a Registration-Dialog with the program name as caption. Enter a name and a dummy serial, click on Ok and you will get an error message saying that the entered serial is wrong. Switch back to SmartCheck and scroll down to the MsgBox-Call. Now we need to find the right serial. Scroll up(be sure you have enabled "Show All Events") until you see the following line(it's at position 96234 but this number can differ on your pc): __vbFreeVar(VARIANT:String:) returns DWORD:40 There we have our right serial. But how do we do a generic keygen now? Hmm, let's do some brain-storming ;)...no matter what name/serial you enter and no matter which soft-guard protected application you wanna crack, it always passes the call above, because it has to free the string with the right serial. So why don't we code a little program that gains control over the softguard5.ocx and "catches" this position(for now, I will call it the "breakpoint") and copies the right serial. You don't know how to do that? Not bad, I will teach you how: Step 2: Coding -------------- We will do it in asm(masm), if you don't know asm then go and search some tutorials, because: no asm -> no cracking ;). Basically, there are 2 different ways to solve our problem: 1) Using Debug-APIs - This is quite complicated and unreliable. 2) The ultimate sn00pee-style :P Guess what method we'll use ;) The idea is to start the program via "CreateProcess", write and endless loop at the breakpoint and then read out the right serial. To write this loop, we use "WriteProcessMemory" and "VirtualProtectEx". You should be familiar to them, if not then refer to an win32-help file. When the program reaches this loop, it won't do anything anymore, it is "freezed". Then our keygen comes into the spotlight ;), it will constantly check if the program is freezed and, if so, read out the right serial by getting the register content(in our case this is ecx, we'll talk about this later). To get the register contents we use "GetThreadContext", here is the function-proto: BOOL GetThreadContext( HANDLE hThread, // handle to thread with context LPCONTEXT lpContext // address of context structure ); We will get hThread after calling CreateProcess, so we only have to declare a CONTEXT structure(I won't list its members here, because it's a very complex structure, don't care about it). Now we need our breakpoint, switch back to SmartCheck and click on the __vbFreeVar line, in the upper-right corner we see an RVA(Relative Virtual Address, means nothing more than a memory-address). Write it down(in our case "19B96"), fire up W32DSM and disassemble the softguard5.ocx(it is in your windows\system dir), now go to code location 11019B96(remember: code address = Imagebase + RVA), you should see the following code: * Reference To: MSVBVM50.__vbaFreeStr, Ord:0000h | :11019B8C FF1550140311 Call dword ptr [11031450] :11019B92 8D4D84 lea ecx, dword ptr [ebp-7C] * Reference To: MSVBVM50.__vbaFreeVar, Ord:0000h | :11019B95 FF1558120311 Call dword ptr [11031258] Do you see the same as me? "lea ecx, dword ptr [ebp-7C]", this is the important instruction, after executing this one, ecx will point to the right serial, right? WRONG! Unfortunately, Visual Basic uses some weird parameter-conventions for its functions. After tracing through the code with SoftICE, I found out that the pointer to the right serial will be 8 bytes after the ECX, what means we have to get the value in ecx, add 8 to it(ecx+8) and copy the pointer at this address(4 bytes long). That's it! Back to the code, now we know that our breakpoint is "11019B95". This is all the information we need! I will now list the complete asm code for our keygen. You have to execute the keygen with the soft-guard protected applications location as command line. (f.e. if you program is located in c:\program files\appname\app.exe then you have to enter "keygen.exe c:\program files\appname\app.exe") ------------------------------ CUT ------------------------------ .386 .model flat, stdcall option casemap:none ;--------------------------[ Includes ]-------------------------- include windows.inc include kernel32.inc include user32.inc ;--------------------------[ Libraries ]------------------------- includelib kernel32.lib includelib user32.lib includelib gdi32.lib ;--------------------[ Initialized Variables ]------------------- .data Breakpoint dd 11019B95h ;Breakpoint SaveBuffer db 2 dup(0) ;SaveBuffer JmpBuffer db 0EBh,0FEh ;opcode for jmp -2 ;->endless loop CommandLine db " ",0 ;error texts err_caption db "Error",0 err_open_text db "Could not execute file!",0 err_notsg_text db "This is not a Soft-Guard 1.10 " db "protected program!",0 err_readmem_text db "Could not read from memory!",0 err_writemem_text db "Could not write to memory!",0 mb_success_capt db "Your Serial",0 Serial db 255 dup(0) tempSerial db 255 dup(0) StartupInfo STARTUPINFO {SIZEOF STARTUPINFO,NULL,NULL, NULL,0,0,0,0,0,0,0,STARTF_USESHOWWINDOW, SW_SHOWDEFAULT,0,NULL,0,0,0} ;--------------------[ Uninitialized Variables ]------------------ .data? lpFilename dd ? temp dd ? hInstance dd ? OldProtected dd ? ProcessInfo PROCESS_INFORMATION {} ProcContext CONTEXT {} ;****************************************************************** ;***************************** CODE ******************************* ;****************************************************************** .code start: invoke GetModuleHandle, NULL mov [hInstance], eax ;Get CommandLine invoke GetCommandLine mov [lpFilename], eax cmp byte ptr[lpFilename], 0 jz sp_done mov esi, [lpFilename] cl_loop: inc esi cmp byte ptr[esi], '"' jnz cl_loop add esi, 2 mov [lpFilename], esi ;Start Program start_prog: invoke CreateProcess, [lpFilename], ADDR [CommandLine], NULL, NULL, FALSE, NULL, NULL, NULL, addr [StartupInfo], addr [ProcessInfo] test eax, eax jz error_open sp_start: ;wait till the softguard5.ocx is loaded invoke Sleep, 200 ;we need to get read/write access invoke VirtualProtectEx,[ProcessInfo.hProcess], [Breakpoint], 2, PAGE_EXECUTE_READWRITE, ADDR OldProtected ;do a little check invoke ReadProcessMemory, [ProcessInfo.hProcess], [Breakpoint], addr [SaveBuffer], 2, NULL test eax, eax jz error_readmem cmp word ptr[SaveBuffer], 15FFh jnz error_notsg ;write our loop invoke WriteProcessMemory, [ProcessInfo.hProcess], [Breakpoint], addr [JmpBuffer], 2, NULL test eax, eax jz error_writemem mov [ProcContext.ContextFlags], (CONTEXT_CONTROL or CONTEXT_INTEGER) ;now we have a loop(the heart of our keygen), which checks if ;the program has reached the breakpoint sp_loop: ;sleep a bit, we don't want to overhead the cpu :P invoke Sleep, 500d invoke GetThreadContext, [ProcessInfo.hThread], ADDR [ProcContext] test eax, eax jz sp_done ;check if program has reached our breakpoint mov eax, dword ptr[ProcContext.regEip] cmp eax, dword ptr[Breakpoint] jnz sp_loop ;ECX+8 points to our serial mov eax, [ProcContext.regEcx] add eax, 8 ;read pointer to the right serial invoke ReadProcessMemory, [ProcessInfo.hProcess], eax, addr [temp], 4, NULL ;now we have the pointer to the right serial invoke ReadProcessMemory, [ProcessInfo.hProcess], [temp], addr [tempSerial], 255d, NULL ;convert unicode string to ansi-string(since VB uses unicode) lea esi, [tempSerial] xor ecx, ecx sp_copyloop: mov al, byte ptr[esi+ecx*2] mov byte ptr[Serial+ecx], al inc ecx cmp byte ptr[esi+ecx*2], 0 jnz sp_copyloop jmp sp_success error_open: invoke MessageBoxA, NULL, addr [err_open_text], addr [err_caption], MB_OK jmp sp_done error_notsg: invoke MessageBoxA, NULL, addr [err_notsg_text], addr [err_caption], MB_OK invoke TerminateProcess, [ProcessInfo.hProcess], NULL jmp sp_done error_readmem: invoke MessageBoxA, NULL, addr [err_readmem_text], addr [err_caption], MB_OK invoke TerminateProcess, [ProcessInfo.hProcess], NULL jmp sp_done error_writemem: invoke MessageBoxA, NULL, addr [err_writemem_text], addr [err_caption], MB_OK invoke TerminateProcess, [ProcessInfo.hProcess], NULL jmp sp_done sp_success: invoke MessageBox, NULL, addr [Serial], addr [mb_success_capt], MB_OK invoke WriteProcessMemory, [ProcessInfo.hProcess], [Breakpoint], ADDR SaveBuffer, 2, NULL invoke Sleep, 100 invoke WriteProcessMemory, [ProcessInfo.hProcess], [Breakpoint], ADDR JmpBuffer, 2, NULL jmp sp_loop sp_done: invoke ExitProcess, NULL ret end start ------------------------------ CUT ------------------------------ The End ------- That's it, I hoped you enjoyed this little tutorial and learned something :), maybe you have to read some passages more than one time(especially the source :)) to understand them. If you have further questions then try to find me on irc(#learn2crack, #kraecker) cya sn00pee Greets to all my 'real' friends "Und die Moral von der Geschicht': Kommerzielle Protections lohnen sich nicht ;)"achCollObj