============================================================================================== Cracking PlasmaVis (Making a Keygen in C) ----------------------------------------- This tutorial is NOT meant for beginners in x86 assembly or C. ============================================================================================== Please note that readers should have some background knowledge of x86 assembly as well as C. I decided to make this tutorial specifically on PlasmaVis because it is fairly trivial to reverse engineer, but great for learning. This tutorial is meant solely for learning purposes. The techniques described in this tutorial should not be used to steal software. If you enjoy using software, respect and support the author by purchasing it! ---------------------------------------------------------------------------------------------- Product..............: PlasmaVis Music Visualization Version..............: PlasmaVis version 1.1.0.3 Vendor...............: Olsen Software (http://www.plasmavis.com/]http://www.plasmavis.com/) Product Link.........: http://www.plasmavis.com/PlasmaVis.msi Written by...........: SlimTim10 Required tools.......: C Compiler, Debugger, ASPack 1.12 Unpacker, PE Identifier ---------------------------------------------------------------------------------------------- The tools I will use are, respectively: Dev-C++, OllyDbg, AspackDie 1.2, and PE iDentifier v0.94. The tutorial is broken up into steps that may be followed for most basic reverse engineering attempts on products. => Run the target and attempt to register using false information. Keep note of the text in the error message that is displayed when the registration fails. In PlasmaVis, the message is "Invalid license key. Please verify all inputs and try again." In some cases, it may be necessary to use the message box's title (i.e. "PlasmaVis Configuration") if the message string cannot be found. => Find out if the target is packed using a PE identifier. If it is, unpack it with a reliable unpacker. Using PEiD, you can find out that PlasmaVis is packed using ASPack 2.12. AspackDie 1.2 will work as an unpacker for PlasmaVis. => Now that the target is unpacked, begin debugging the unpacked executable with a debugger. Using OllyDbg, find the string reference to the registration error message. It should be located at address 0044F82A. In OllyDbg, find references to the address (CTRL+R). The only reference should be the operation code JNZ SHORT 0044F82A at address 0044F803. It is evident that if the condition above the jump does not set the zero flag, the bad message will be produced. Looking above that address, it should be clear that the registration algorithm begins at 0044F708, so set a breakpoint at that address and run the application. (Press SHIFT+F9 twice in OllyDbg after the application is ran to pass the exceptions). When the target has started up, attempt to register using false information again. As soon as the "Register" button is clicked, OllyDbg should stop PlasmaVis due to the breakpoint. The CALL at address 0044F732 will call the function that produces the registration input window in PlasmaVis. Like at the beginning, enter false information again and attempt to register the target. In PlasmaVis, it is best to use a different value for each input box. For this tutorial, I will use "email", "first", "last", and "test", respectively. => Step over each line in OllyDbg (F8) while analyzing the procedure of each line and the target's progression. At address 0044F7EA in PlasmaVis, you should notice that each of the first three strings are stored in separate registers and then a function is called. If you stepover the CALL opcode, the next two values stored in registers are the valid license key and the value you input as the license key, respectively. This means that the CALL you jumped over called the function that made the valid license key! Set a breakpoint at the CALL, which should be address 0044F7F3. Restart PlasmaVis in OllyDbg and run it until it reaches the new breakpoint. This time, step into (F7) the opcode instead of stepping over it. Now all that is left is to analyze the function and create the key generator in C. => First, each input value is stored in a separate register. The EAX register holds "email", ECX holds "last", and EDX holds "first". Then the first two characters of each string are extracted and joined together to form a single string (i.e. "emfila"). You may also notice that a strange-looking string is going to be used, "qazwsx123*()*()&". Beginning at address 0044E46B is the main algorithm in PlasmaVis which produces a valid license key. For those that understand x86 assembly at a reasonably high level, you should be able to understand the block of code on your own, so skip the next step. Others, read on. => This is a representation of what you should see in OllyDbg: 0044E46B |> 8B45 F0 /MOV EAX,DWORD PTR SS:[EBP-10] 0044E46E |. 8A4418 FF |MOV AL,BYTE PTR DS:[EAX+EBX-1] 0044E472 |. 8B55 EC |MOV EDX,DWORD PTR SS:[EBP-14] ; plasmavis.0044E520 0044E475 |. 8A541A FF |MOV DL,BYTE PTR DS:[EDX+EBX-1] 0044E479 |. 32C2 |XOR AL,DL 0044E47B |. 25 FF000000 |AND EAX,0FF 0044E480 |. 8D4D C8 |LEA ECX,DWORD PTR SS:[EBP-38] 0044E483 |. BA 02000000 |MOV EDX,2 0044E488 |. E8 2799FBFF |CALL plasmavis.00407DB4 0044E48D |. 8B55 C8 |MOV EDX,DWORD PTR SS:[EBP-38] 0044E490 |. 8BC6 |MOV EAX,ESI 0044E492 |. E8 B957FBFF |CALL plasmavis.00403C50 0044E497 |. 8BC3 |MOV EAX,EBX 0044E499 |. 25 01000080 |AND EAX,80000001 0044E49E |. 79 05 |JNS SHORT plasmavis.0044E4A5 0044E4A0 |. 48 |DEC EAX 0044E4A1 |. 83C8 FE |OR EAX,FFFFFFFE 0044E4A4 |. 40 |INC EAX 0044E4A5 |> 85C0 |TEST EAX,EAX 0044E4A7 |. 75 18 |JNZ SHORT plasmavis.0044E4C1 0044E4A9 |. 8B45 F0 |MOV EAX,DWORD PTR SS:[EBP-10] 0044E4AC |. E8 9757FBFF |CALL plasmavis.00403C48 0044E4B1 |. 3BD8 |CMP EBX,EAX 0044E4B3 |. 74 0C |JE SHORT plasmavis.0044E4C1 0044E4B5 |. 8BC6 |MOV EAX,ESI 0044E4B7 |. BA 60E54400 |MOV EDX,plasmavis.0044E560 0044E4BC |. E8 8F57FBFF |CALL plasmavis.00403C50 0044E4C1 |> 43 |INC EBX 0044E4C2 |. 4F |DEC EDI 0044E4C3 |.^75 A6 \JNZ SHORT plasmavis.0044E46B The first opcode simply stores the string "emfila" in the EAX register. Then the first letter, "e", is stored in AL (since it is a single character, only one byte is needed). The string "qazwsx123*()*()&" is then stored in the EDX register. Then the first character of that string, "q", is stored in DL. AL is then computed with DL using the XOR operation (read more here: http://en.wikipedia.org/wiki/Bitwise_operation#XOR). The EAX register is then computed with the hexadecimal 0FF value using the AND operation. The result of the previous expression is then converted into a string of characters and stored in the EDX register. During this loop function, the EDI register is used as a counter. It is compared at the end of each loop to determine whether it is still greater than 0. EDI begins the loop as the number of characters in the "emfila" string (i.e. 6) and it is decreased by 1 each time the loop reaches the "bottom". The loop continues until the EDI register is no longer greater than 0. => In pseudocode, the function would look something like this: for (EDI = length of EAX; EDI > 0; EDI--) { EAX = XOR #(EDI) character of "emfila" with #(EDI) character of "qazwsx123*()*()&" //first time: XOR 1st char of "emfila" with 1st char of "qazwsx123*()*()&" (XOR e,q) EAX = EAX AND 0FF EDI-- } The CALL at address 0044E4DB calls a simple function that replaces any "0" characters in the string with "Z" characters. Finally, the CALL right before the conditional jump (good message/bad message) compares each corresponding character of the user input license key with the valid license key. => The following C code is a snippet that I used in my own PlasmaVis key generator. It may be a spoiler to those of you who wish to make an attempt at creating the keygen alone. Note that I created a GUI keygen, so it is not as basic as if it were meant to be run via command-line. int len_e = GetWindowTextLength(GetDlgItem(hwnd, IDC_EMAIL)); int len_f = GetWindowTextLength(GetDlgItem(hwnd, IDC_FIRST)); int len_l = GetWindowTextLength(GetDlgItem(hwnd, IDC_LAST)); if(len_e > 0 && len_f > 0 && len_l > 0) { if(len_e > 2 && len_f > 2 && len_l > 2) { char *buf_e; buf_e = (char*)GlobalAlloc(GPTR, len_e + 1); GetDlgItemText(hwnd, IDC_EMAIL, buf_e, len_e + 1); char *buf_f; buf_f = (char*)GlobalAlloc(GPTR, len_f + 1); GetDlgItemText(hwnd, IDC_FIRST, buf_f, len_f + 1); char *buf_l; buf_l = (char*)GlobalAlloc(GPTR, len_l + 1); GetDlgItemText(hwnd, IDC_LAST, buf_l, len_l + 1); char tmp[] = { buf_e[0], buf_e[1], buf_f[0], buf_f[1], buf_l[0], buf_l[1] }; char enc[] = "qazwsx123*()*()&"; int len_k = strlen(tmp); char key[0]; int i; for (i = 0; i < len_k; i++) { if (i == 2 || i == 4) SendMessage(key_hWnd, EM_REPLACESEL, 0, "-"); tmp[i] = tmp[i] ^ enc[i]; if (tmp[i] < 0x10) SendMessage(key_hWnd, EM_REPLACESEL, 0, "Z"); itoa((int)tmp[i], key, 16); strupr(key); SendMessage(key_hWnd, EM_REPLACESEL, 0, key); } GlobalFree((HANDLE)buf_e); GlobalFree((HANDLE)buf_f); GlobalFree((HANDLE)buf_l); } else { MessageBox(hwnd, "All fields must be more than 2 characters long!", "Warning", MB_OK); } } Thank you for taking the time to read this tutorial on cracking PlasmaVis. This tutorial should be used for educational purposes only. I am not responsible for any illegal actions performed by readers of this tutorial. Download Resources: Dev-C++: http://www.bloodshed.net/dev/devcpp.html OllyDbg: http://www.ollydbg.de/odbg110.zip AspackDie 1.2: http://www.exetools.com/files/unpackers/win/aspackdie12.zip PE iDentifier v0.94: http://www.softpedia.com/progDownload/PEiD-Download-4102.html # milw0rm.com [2007-01-03]