NewBies Simple KeyGenMe
From UIC
Simple KeygenMe
Contents |
| NewBies Simple KeyGenMe | |
|---|---|
| Author: | Zyrel |
| Email: | marcolagalla@yahoo.it |
| Website: | Unfair-Gamers |
| Date: | 10/01/2011 (dd/mm/yyyy) |
| Level: |
|
| Language: | English |
| Comments: | Really simple, suitable for newbies. |
Introduction
Directly from UIC's glossary the definition of KeygenMe:
« Programs written specifically to be cracked / reversed. Typically the only feature of crackme is its protection, which may take as a model existing protections (such as commercial programs), or more frequently offer something new and original.».
In this particular case we're not going to crack the program (or we'll do it just for completeness, but is not our goal) but instead we're going to "sniff" the code in order to deduce the algorithm that generates the correct serial.
Notice: there is an italian version of this tutorial.
Tools & Files
URL or FTP program
For the occasion we're going to use a small program created by me: KeygenMe#1
Essay
Well, now I assume that you at least know the basics of assembly and at least one programming language (to create the keygen).
Ok, now start the tutorial itself, start the program just to have an idea of how it works:
Well what we see?
• 1^ Textbox: not editable and shows us a number (integer). • 2^ Textbox: in which we are asked to enter our serial. • Button: used to check the serial.
There is no request for a nick or otherwise, then it seems logical to assume that the algorithm that generates the serial based on precisely that issue, well now we ask:Why just that number?(67 in the screen) , here is if you try to reopen the program you will find another number, always an integer, but each time different.
Perfect load the target (I'll often call in this way the KeygenMe for the rest of the tutorial) in Olly and let's stop to take a look at the code:
00401241 . 89E5 MOV EBP,ESP
00401243 . 83EC 08 SUB ESP,8
00401246 . C70424 0200000>MOV DWORD PTR SS:[ESP],2
0040124D . FF15 3C614000 CALL DWORD PTR DS:[<&msvcrt.__set_app_type>] ; msvcrt.__set_app_type
00401253 . E8 A8FEFFFF CALL Keygen.00401100
00401258 . 90 NOP
00401259 . 8DB426 0000000>LEA ESI,DWORD PTR DS:[ESI]
00401260 $ 55 PUSH EBP
00401261 . 8B0D 54614000 MOV ECX,DWORD PTR DS:[<&msvcrt.atexit>] ; msvcrt.atexit
00401267 . 89E5 MOV EBP,ESP
00401269 . 5D POP EBP ; kernel32.7671D0E9
0040126A . FFE1 JMP ECX
0040126C 8D7426 00 LEA ESI,DWORD PTR DS:[ESI]
00401270 . 55 PUSH EBP
00401271 . 8B0D 48614000 MOV ECX,DWORD PTR DS:[<&msvcrt._onexit>] ; msvcrt._onexit
00401277 . 89E5 MOV EBP,ESP
00401279 . 5D POP EBP ; kernel32.7671D0E9
0040127A . FFE1 JMP ECX
This is the entrypoint of the program, but this part does not concern us, rather shaken down:
• Here we see a call to CreateWindowExA:
0040134C |. 8B45 08 MOV EAX,[ARG.1] ; |
0040134F |. 894424 28 MOV DWORD PTR SS:[ESP+28],EAX ; |kernel32.BaseThreadInitThunk
00401353 |. C74424 24 0000>MOV DWORD PTR SS:[ESP+24],0 ; |
0040135B |. C74424 20 0000>MOV DWORD PTR SS:[ESP+20],0 ; |
00401363 |. C74424 1C 9600>MOV DWORD PTR SS:[ESP+1C],96 ; |
0040136B |. C74424 18 9001>MOV DWORD PTR SS:[ESP+18],190 ; |
00401373 |. B8 00000080 MOV EAX,80000000 ; |
00401378 |. 894424 14 MOV DWORD PTR SS:[ESP+14],EAX ; |kernel32.BaseThreadInitThunk
0040137C |. B8 00000080 MOV EAX,80000000 ; |
00401381 |. 894424 10 MOV DWORD PTR SS:[ESP+10],EAX ; |kernel32.BaseThreadInitThunk
00401385 |. C74424 0C 0000>MOV DWORD PTR SS:[ESP+C],0CF0000 ; |
0040138D |. C74424 08 0040>MOV DWORD PTR SS:[ESP+8],Keygen.00404000 ; |ASCII "Keygen Me #1"
00401395 |. C74424 04 0030>MOV DWORD PTR SS:[ESP+4],Keygen.00403000 ; |ASCII "WindowsApp"
0040139D |. C70424 8801000>MOV DWORD PTR SS:[ESP],188 ; |
004013A4 |. E8 A70B0000 CALL <JMP.&USER32.CreateWindowExA> ; \CreateWindowExA
• Here we find an interesting part, with two calls, at rand() and srand()
004013AF |. C70424 0000000>MOV DWORD PTR SS:[ESP],0 ; |||
004013B6 |. E8 F50A0000 CALL <JMP.&msvcrt.time> ; ||\time
004013BB |. 890424 MOV DWORD PTR SS:[ESP],EAX ; ||kernel32.BaseThreadInitThunk
004013BE |. E8 FD0A0000 CALL <JMP.&msvcrt.srand> ; |\srand
004013C3 |. E8 D80A0000 CALL <JMP.&msvcrt.rand> ; |[rand
Well here we understand how the number that is generated (as I anticipated) the algorithm is based. Then we deduce that uses the srand () and rand () using as seed the first time () function, there is a call to rand () again below but we are not interested.
Run with F9 and try to enter a serial at random, I will use the good old 1234567890 ;) There appears a messagebox that warns us that the serial is not correct, Well I think that the program can be done more or less like this:
unsigned long s_fake = 1234567890; // our serial (wrong)
if (s_fake == generate()) {
MessageBox (NULL, "The serial is correct","Great work", MB_OK);
}
else {
MessageBox (NULL, "The serial is wrong","Try again", MB_OK);
}
Well then do not waste time ;) Topping up your target on Olly pressing CTRL + F2 and Commandline type:
N.B. who did not have the plugin CmdLine can find it here.
In this way we placed a bp (BreakPoint) on all calls to that API (attention to character cases, the cmdline is case-sensitive), now re run pressing F9 and add with our fictitious serial (for me 1234567890), just before the MsgBox appears to tell us that our serial is wrong with the debugger break us here:
If you look just above you will see:
0040178D |. 75 29 JNZ SHORT Keygen.004017B8 ; |conditional jump if the Z flag is set to 0 jumps to 004017B8
0040178F |. C74424 0C 0000>MOV DWORD PTR SS:[ESP+C],0 ; |
00401797 |. C74424 08 E140>MOV DWORD PTR SS:[ESP+8],Keygen.004040E1 ; |ASCII "Ottimo lavoro" Great work
0040179F |. C74424 04 EF40>MOV DWORD PTR SS:[ESP+4],Keygen.004040EF ; |ASCII "Seriale Corretto" The serial is correct
004017A7 |. C70424 0000000>MOV DWORD PTR SS:[ESP],0 ; |
004017AE |. E8 0D080000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
004017B3 |. 83EC 10 SUB ESP,10
004017B6 |. EB 27 JMP SHORT Keygen.004017DF
004017B8 |> C74424 0C 0000>MOV DWORD PTR SS:[ESP+C],0 ; |
004017C0 |. C74424 08 0041>MOV DWORD PTR SS:[ESP+8],Keygen.00404100 ; |ASCII "Tenta ancora ;)" Try again
004017C8 |. C74424 04 1041>MOV DWORD PTR SS:[ESP+4],Keygen.00404110 ; |ASCII "Seriale Sbagliato" The serial is wrong
004017D0 |. C70424 0000000>MOV DWORD PTR SS:[ESP],0 ; |
004017D7 |. E8 E4070000 CALL <JMP.&USER32.MessageBoxA> ; \MessageBoxA
I commented the code in the important points, but the thing that interests us most at the moment is that:
0040178D |. 75 29 JNZ SHORT Keygen.004017B8 ; |conditional jump if the Z flag is set to 0 jumps to 004017B8
Now we open a small bracket:
The Cracking
I believe that you have already guessed, but if we reverse that jump we make that if a serial was the wrong targets would appear to confirm the MsgBox (You want to try? try to set the zero flag to 1, you will see that not make the leap and you will see just MsgBox) I will not dwell much because it is not our purpose to go straight to answer:
•First method: replace jnz (Jump if Not Zero) with a JE (Jump if Equal) •Second method: jnz replaced with a JMP or a jump to incodizionato 0040178F, MsgBox Confirmation •Third method: noppare all doing well up the MsgBox
Frankly, I prefer the first method, then we go for a small patch of just having one byte as follows:
In fact, in the box below:
Now to make the changes permanent you go read the tutorial on how to do manual patching or for the first (if you already know how to do it manually)Right click-> Copy to executables-> All Modifications-> Save.
Identification and study of the algorithm:
Now we enter the real part of our tutorial: analysis of the algorithm. We left here:
If even more shaken up, just below theGetDlgItemInt you will find this CALL:
Hmm...a call in the function that reads the value (number) of the textbox... close to the conditional jump...Well I would say that it may be interesting, so we place a CALL bp with the F2 and run entering the serial, stop us on the CALL, pressing F7 we step into the CALL and we brought up a routine that, I play my slippers is that the generation of the serial:
00401817 |. 89E5 MOV EBP,ESP
00401819 |. 83EC 0C SUB ESP,0C
0040181C |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
0040181F |. 0FAF45 08 IMUL EAX,DWORD PTR SS:[EBP+8]
00401823 |. 8945 F8 MOV DWORD PTR SS:[EBP-8],EAX
00401826 |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
00401829 |. 0FAF45 F8 IMUL EAX,DWORD PTR SS:[EBP-8]
0040182D |. 8945 F4 MOV DWORD PTR SS:[EBP-C],EAX
00401830 |. 8B45 F8 MOV EAX,DWORD PTR SS:[EBP-8]
00401833 |. 0FAF45 F4 IMUL EAX,DWORD PTR SS:[EBP-C]
00401837 |. 0345 08 ADD EAX,DWORD PTR SS:[EBP+8]
0040183A |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX
0040183D |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00401840 |. C9 LEAVE
00401841 \. C3 RETN
As you can see does nothing more than to do some simple calculations with the value in EAX, which is precisely the number random by the program shown in the textbox disabled. In fact, the value returned by an API is always contained in EAX (from GetDlgItemInt). For further proof typedin EAX and you will see that CmdLine contains precisely that number.
I would say that the code is clear, however, Let's see the basic traits:
00401829 |. 0FAF45 F8 IMUL EAX,DWORD PTR SS:[EBP-8]
00401833 |. 0FAF45 F4 IMUL EAX,DWORD PTR SS:[EBP-C]
00401837 |. 0345 08 ADD EAX,DWORD PTR SS:[EBP+8]
That translated into pseudo-code would be:
00401829 multiplies eax * (eax ^ 2) ^ 3 then eax
00401833 multiplies (eax ^ 2) * (eax ^ 3) ^ 5 then eax
00401837 add (eax ^ 5) + eax
So the deliver that generates the serial is:(EAX ^ 5) + Eax where eax is the random number. But here lies the only difficulty with this pseudo crackme, the result of calculations is treated as and when dword eax is greater than 84 (which happens often) then Eax exceeds 0xFFFFFFFF ^ 5, which generates overflow errors that make it invalid in the eyes the program even if the serial potentially correct.
A keygen is precisely follow the calculations made by the program, then: Eax * Eax * Eax *(Eax ^ 2) + Eax and thus avoid an overflow, even more correctly the most appropriate solution would be:serial = ((Eax ^ 5) and & HFFFFFFFF + Eax) and & HFFFFFFFFwith two logical And.
The KeyGen
Finally our beloved keygens ;) I will present it in two languages: C + + and ASM.
using std::cout;
using std::cin;
int main() {
int eax;
unsigned long serial;
cout << "Enter the number returned by the program: ";
cin >> eax;
serial = eax * eax;
serial = serial * eax;
serial = serial * (eax*eax);
serial = serial + eax;
cout << "\nThe correct serial: " << serial << "\n";
system("pause");
return 0;
}
Asm:
.model flat, stdcall
option casemap :none ; case sensitive
include windows.inc
uselib MACRO libname
include libname.inc
includelib libname.lib
ENDM
uselib user32
uselib kernel32
;prototype declaration of the functions of the program
DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD ;window function
MakeSerial PROTO :DWORD ;function generation serial
ErrorProc PROTO ; error function for placing the code
; setting names for the objects of the window
IDC_TXTCODE equ 1001
IDC_TXTSERIAL equ 1002
IDC_CMDGEN equ 1003
IDC_CMDEXIT equ 1004
;dati non inizializzati
.data?
hInstance DWORD ? ;dd can be written as dword
lptranslated DWORD ? ;variable to see if the code is' valid
hWindow DWORD ? ;replicated for the MessageBox window handle
;dati inizializzati
.data
szerrortext db "Value Code incorrect or missing!",0
szerrorcaption db "ERROR",0
.code
ErrorProc proc
;deletes both the code that the serial
invoke SetDlgItemText,hWindow,IDC_TXTCODE,NULL
invoke SetDlgItemText,hWindow,IDC_TXTSERIAL,NULL
;error message
invoke MessageBox,hWindow,ADDR szerrortext,ADDR szerrorcaption,MB_ICONERROR
Ret
ErrorProc EndP
start:
invoke GetModuleHandle, NULL ;exe back to the imagebase (handle) to be passed to create the window
mov hInstance, eax
invoke DialogBoxParam, hInstance, 101, 0, ADDR DlgProc, 0 ;creation window
invoke ExitProcess, eax ;exit the process
; -----------------------------------------------------------------------
DlgProc proc hWin :DWORD,
uMsg :DWORD,
wParam :DWORD,
lParam :DWORD
.if uMsg == WM_COMMAND ;if you press a button ...
.if wParam == IDC_CMDGEN ;if you press the button generates ...
mov eax, hWin
mov hWindow, EAX
invoke GetDlgItemInt,hWin,IDC_TXTCODE,ADDR lptranslated,FALSE ;if it exists, takes the entire code from the text box
.if lptranslated == TRUE
invoke MakeSerial, EAX ;calculates the serial
invoke SetDlgItemInt,hWin,IDC_TXTSERIAL,EAX,FALSE ;writes the value in the textbox
.else
invoke ErrorProc ;calls the error message
.endif
.elseif wParam == IDC_CMDEXIT ;if you press the exit button...
invoke EndDialog,hWin,0
.endif
.elseif uMsg == WM_CLOSE ;if you select X
invoke EndDialog,hWin,0
.endif
xor eax,eax
ret
DlgProc endp
MakeSerial Proc code:DWORD
;starts to calculate the serial only 2 multiplications and an addition, there is nothing to explain
mov eax, code
imul eax, eax
mov edx, eax
mov eax, code
imul eax, edx
mov ecx, eax
mov eax, edx
imul eax, ecx
add eax, code
Ret
MakeSerial EndP
end start
Endnotes
And so we come the end of my first suits up Quequero:) Well I hope to be able to write everything clearly and not too Pallos for any questions or concerns you can contact me via mail, via the bore of the UIC pm on UG, wherever you like:)
A special thanks to Evolution for the source in ASM Keygen (which resolved in 40 seconds flat xd) and for some clarifications. Zyrel.
Disclaimer
I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.
Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.
