Writing a keygen: Hacker challenge
I stumbled upon a hacker challenge posed by blogger Andy Sloane last night. He explained how, as a hobby, likes to try to crack or create key generators for commercial programs. Naturally, he never distributes these cracks, but is only doing it for the intellectual challenge. Writing a key generator is usually more useful than cracking, since a good keygen typically allows you to receive future updates. Often, it is also more interesting from an intellectual point of view, as writing a keygen usually requires some knowledge in reverse engineering and number theory. Andy tries to demonstrate what he means by providing a simple key checker of his own for us to break. Since I had a lighter workload last night, I figured I’d try it.
For his challenge to the world, he offers the source code of the key checker and three working keys. The source is available on his blog. (While a normal keygen writer would not have access to the source code of the key checker, it is usually simple, though tedious, to decompile and interpret the compiled binaries that would be released.)
After downloading and skimming the source, I spent a while trying to understand the custom written (and minimally commented) library he uses for large (125 bit, in this case) integers. Rather than try to interpret the code itself, I used gdb to inspect the way the library worked, and had soon figured out the inner workings of the key checker:
Each key that it received, it would convert into an integer. Each key had a unique integer k (with some exceptions: O, Q, and 0; I and 1; and Z and 2 were considered to be interchangeable to lower the change of an misread key) to which it was decoded.
Two magic numbers n = 21967608700894359473408781171922272451 and d=4444901153489723031681183441752517327 were hard-coded into the program. A new number m ≡ kd mod n was created. The key was accepted if m ≡ 12345678 mod c, where c = ⌊n/232⌋ (rounded down to the nearest integer).
This was clearly a problem for number theory! This algorithm looked suspiciously similar to the RSA encryption algorithm, which similarly employs two large numbers with special properties.
A quick summary of the steps behind RSA encryption:
- Pick two large primes p and q.
- Compute n = pq.
- Compute the totient of n, φ(n) = (p-1)(q-1).
- Choose an integer e such that e is relatively prime to and less than φ(n).
- Compute the integer d such that ed ≡ 1 mod φ(n).
- For your message m, encrypt it by generating ciphertext c ≡ me mod n. e is known as the public key and is used to encrypt the message.
- The person who receives the ciphertext by using d and applying the formula m = cd mod n.
This works because of Euler’s theorem, which states that aφ(n) ≡ a mod n for any integers a and n.
From that, cd =(me)d=med= m1+kφ(n) ≡ m mod n. (Check out the explanation of RSA on wikipedia for a more in depth explanation.
I quickly used Mathematica to factor n to see if it was the product of two primes. It was! I checked that d was relatively prime to φ(n) to be sure. It also was. The only part where this differed from RSA was the final step where it checked to see if m ≡ 12345678 mod c before accepting the key.
I was a little thrown off by c being related to n, but I quickly realized that it didn’t matter what c was. It was just there to make sure that more than one m (and therefore more than one key) was accepted. Effectively, what this key checker did was to take RSA generated ciphertext as a key and check to see if it decryped to a message that left a remainder of 12345678 when divided by c.
Therefore, to generate a key, I needed to create a correct message and encrypt it, then convert the ciphertext back to the “key” format. Creating a message is easy. m is an accepted message if m ≡ 12345678 mod c, so m = ac + 12345678, where a is any natural number. To encrypt m, I need to find the private key e. Since n is small enough to factor and ed ≡ 1 mod φ(n), it is easy to find e through modular arithmetic.
Voila! Now I have everything I need to write a keygen for Andy’s example key checker. In fact, here it is. Now, clearly, I should go do my homework, having wasted several hours on this.