http://www.lysator.liu.se/~zap/ - Webpage.
Little Drummer Boy is a drum loop creation program. It lacks the fancy features of some of the more expensive commercial products out there, but it gets the job done, and exports your loop to a .wav file. Or it does if you have a registered copy. Wouldn't it be nice to test the quality of its final output before purchasing this program? It is only twelve dollars in beta form, but paying money for a program that turns out to be shit is never nice. So lets improve our trial version.
To start, note that this program is written in Visual Basic. There are several ways you could notice this, among them is seeing the file vbrun300.dll installed in your windows\system directory. You could check it's dll references, or you could look at its assembly code. The important bit is that you notice it, because doing this bugger in assembly would be needlessly hard (or at least someone with my sad assembly knowledge would find it so).
Enter your registration name and e-mail, and a fake reg code. Close the window. Notice that there's no message box informing us that the code is wrong. Re-open the window. You'll notice that we have an unusual boon: an error code and description of it. This makes it much easier to identify any problems with our key. (MESSAGE TO SHAREWARE PROGRAMMERS: Although this makes life easier if your client can't type or you have their name spelt wrong in your keygen, it is technically a REALLY STUPID THING TO DO). I might have gotten pissed off enough to quit if I didn't know exactly what was wrong with the codes I was entering. Still, thanks for the rewarding feeling of progress from seeing error after error disappear). Now, decompile it back to VB code.
Master Zap was not very inventive with his form naming, as it turns out; all the better for us. In the register.bas file, there are only 5 entries. Command1_Click, Command2_Click, Command3_Click, Form_Activate, and Form_Load. Command1_Click contains some code involving our registration information, but it isn't relevant (HOWEVER, knowing which variables hold your serial number, name, and e-mail is nice) as could be predicted from the lack of a messagebox. Rather, look at Form_Activate. Voila! You shall see this beautifully simple bit of code:
. . . Dim l0044 As Integer l0044% = fn0242() If l0044% = 0 Then If gv078A = -1 Then InfoField.Caption = "Registration is active, does not expire." Else InfoField.Caption = "Registration is active, and will expire in " + Format$(gv078A) + " months" End If Else InfoField.Caption = "Registration is invalid, registration code error is " + Format$(l0044%) If l0044% = 1 Then InfoField.Caption = InfoField.Caption + " (code the wrong length)" End If If l0044% = 32 Then InfoField.Caption = InfoField.Caption + " (code has expired!)" End If End If
Now we know what we're looking for. We need fn0242 to return a value of 0, and preferably, have the variable gv078A set to 1 so we can try this program in it's fully functional form for a month. If you were to do something evil, like keep it without paying, you would (in theory) want gv078A to be -1. But that would be wrong. Now, just to find fn0242. It's in module2.bas, and it looks like this:
Dim l0038 As Integer Dim l003A As Variant Dim l0042 As Variant Dim l0046 As Variant l003A = 0 If (Len(fn02B2(gv0756.M0B9B)) <> 16) Then l003A = 1 GoTo L1984 End If For l0042 = 0 To 7 gv0774(l0042) = Val("&H" + Mid$(fn02B2(gv0756.M0B9B), (l0042 * 2) + 1, 2) Next l0042 l0046 = fn0264(gv0774(7), 0) l0038 = fn022B(3) If (l0038 <> gv0774(3)) Then l003A = l003A + 2 End If l0038 = fn022B(6) If (l0038 <> gv0774(6)) Then l003A = l003A + 4 End If If gv0774(4) <> fn0275(fn02B2(gv0756.M0B85)) Then l003A = l003A + 8 End If If gv0774(5) <> fn0275(fn02B2(gv0756.M0B90)) Then l003A = l003A + 16 End If l0038 = fn0258(Date) If gv0774(2) > gv0774(1) And (l0038 < gv0774(1) Or l0038 > gv0774(2)) Then l003A = l003A + 32 End If If gv0774(0) / 16 <> 2 Then l003A = l003A + 64 End If If gv0774(2) <= gv0774(1) Then gv078A = -1 Else gv078A = gv0774(2) - l0038 End If L1984: fn0242 = l003A
A brief glance reveals that there are 6 checks. Each one that fails adds a value to l003A, which is finally the value of the function. The sum of any combination of these numbers is unique, thus the detailed error descriptions in the bottom of the registration window. Right off, the function checks to see if your code is 16 characters long. Fn02B2 simply skims all of the spaces off of the end of the string. If this fails, you are immediately booted out, with an error code 1 (Code Wrong Length, you probably saw this one). Next, the code is broken into 8 sections, each being a hexadecimal number, which is converted to decimal and stored in the array gv0774.
The relevance of l0046 will be covered later, because it is the climax of the crack, and not too much worth worrying about quite yet. The next two checks involve fn022B: these correspond to "Internal Check A" and "Internal Check B" in a completely incorrect, 16 character code. fn022B is not an intimidating function at all. It simply sums all the elements of gv0774 before the number passed to it (after XORing them by AAh) and ANDs the sum by 255.
The third and fourth checks involve your name and e-mail, and here is what fn0275 does:
Function fn0275 (ByVal p006E As String) As Integer Dim l0070 As Integer Dim l0072 As Integer Dim l0074 As Integer l0070% = 139 l0072% = Len(p006E) If l0072% > 45 Then l0072% = 45 For l0074% = 1 To l0072% l0070% = ((l0070% * 147 + Asc(Mid$(p006E, l0074%, 1))) Xor &HB5) And &H7F Next l0074% fn0275 = l0070% And 255 End Function
This would be a pain if it couldn't be circumvented by pasting it into some basic program (like QBasic) and passing it your name and e-mail. The fifth check involves the date (the code can expire). fn0258 converts the time into a number of months after Jan, 1998. From the comparisons, gv0774(1..2) are the registration date and expiration date, also in months since 1998. It makes sure that the registration date wasn't pre-expired, and makes certain that either the date of registration is before the current one, or that the expiration date is after the current one.
The sixth check simply makes sure that the gv0774(0) is equal to 32. Finally, the number gv078A is assigned. This is the number of months until the code expires. Now for the climactic gem: the mysterious l0046. Why assign a variable that is never referred to? Well, lets look at the function.
Function fn0264 (ByVal pv005E As Integer, p0060 As Integer) As Integer Dim l0062 As Long Dim l0064 Dim l0066 As Integer Dim l0068 As Variant l0062 = pv005E For l0064 = 0 To 7 l0066 = fn028E(l0062, 255) If p0060 = 1 Then l0068 = gv0774(l0064) gv0774(l0064) = gv0774(l0064) Xor l0066 If p0060 = 0 Then l0068 = gv0774(l0064) l0062 = l0062 + l0068 Next l0064 fn0264 = pv005E End Function Function fn028E (p007C As Long, p007E As Integer) As Integer fn028E = p007C Mod p007E p007C = (p007C * 147) Mod 1000 End Function
Woohoo! If you tried entering a serial number before this, now you may see why it failed. Here we have a cutely tweaked XOR encryption scheme. It loops through XORing the array of your serial number with an ever changing key. This would actually be a somewhat cool low-end file encryption scheme if the "For l0064 = 0 To 7" was replaced with "While Not EOF(1)"; go ahead and rip it off if you want.
Anyway, since you can work out the values that you want to get out, it should not be too hard to get the values that you need. It is simply a matter of finding the key for the next number, which is but mindless number crunching. In fact, the extremely simple code looks like this, pulled out of my QBasic keygen:
FOR t = 0 TO 6 LET encnum = modvalue MOD 255 LET modvalue = (modvalue * 147) MOD 1000 LET dmed(t) = imed(t) XOR encnum LET modvalue = modvalue + imed(t) NEXT t FOR t = 0 TO 7 LET partz$(t) = HEX$(dmed(t)) IF LEN(partz$(t)) = 1 THEN LET partz$(t) = "0" + partz$(t) NEXT t FOR t = 0 TO 7 LET serial$ = serial$ + partz$(t) NEXT t
imed is the name of the array which contains the final values that you want the program to decrypt your serial number to. serial$ is, obviously, the serial number. You should now be able to improve your trial version of Little Drummer Boy. If not, you're close to getting the rest of the way yourself fairly quickly, because you know where your code went wrong. My useless opinion states that there is something to be learned about the use of logical operations in protection schemes, here. Even if you were already above the level of this reversal you might at least get some code for a adorable little file encryption program and a laugh at how such a thorough and well thought out protection scheme was foiled just because it was written in Micro$oft Visual Basic.