The code gets the value argv[1] (the first commandline argument, i.e., the serial) and puts it on top of the stack ([esp+10Ch+var_10C] is in fact [esp]). The serial is the only argument to the rock subroutine called next.

Rock

The graph view of rock, with most of the blocks grouped, looks as follows:

From this picture you can clearly see a loop (backward pointing, bold blue arrow). The local variables at ebp-10h and ebp-0Ch serve as redundant loop counters, I renamed them two i and i2 respectively. The loop iterates over all characters in serial, i.e., until the terminating zero byte is reached. The length of the serial, as the loop counter i, is compared to 19. If the serial length is not 19, the bad boy message is shown with call bomb:

The content inside the loop is assessed most readily with the graph view of IDA. At the bottom of the subroutine we see 3 bad boy nodes:

We clearly need to find a path that avoids those nodes.

First Hurdle

The content of the loop starts with the following node:

If the jump is taken, we get to one of the bomb message. The register edx holds the loop counter i, and eax points to the serial. By adding the two registers and dereferencing the result we get al = serial[i]. In the following I’m using c=serial[i] to refer to the current serial character. The jump is not taken if c > 44.

Second Hurdle

We enter the second stage if c > 44:

The bomb message is shown if both jumps are not taken. So either c <= 45 (we take the first jump), or c > 45 && c > 47 (we take the second jump). So after the second hurdle we have c == 45 || c > 47.

Third Hurdle

We enter the next stage withc == 45 || c > 47:

Again the bomb goes off if both jumps are *not* taken. This can be avoided with c <= 57 or c > 57 && c > 64. Together with what we already know this gives us c = 45 || 47 < c <= 57 || c > 64.

Fourth Hurdle

The next stage is reached with c = 45 || 47 < c <= 57 || c > 64:

The bomb detonates when the jump in the second node is take. So either the jump in the first node is taken with c <= 90 or the jump in the second node is not taken with c > 96 && c > 96. We end up with c = 45 || 47 < c <= 57 || 64 < c <= 90 || c > 96.

Fifth (and Last) Hurdle

The final hurdle is shown in Figure:

The bomb does not go off if the jump is taken, therefore c <= 122. So all in all we have:

c = 45 || 47 < c <= 57 || 64 < c <= 90 || 96 < c <= 122

Or by using the corresponding ASCII characters:

c = '-' || '0' <= c <= '9' || 'a' <= c <= 'z' || 'A' <= c <= 'Z'

Conclusion: If our serial has exactly 19 characters from the following set, the bomb won’t detonate (just yet):

-

0123456789

abcdefghijklmnopqrstuvwxyz

ABCDEFGHIJKLMNOPQRSTUVXWYZ

Paper

After rock follows a call to paper, again serial is the only argument:

The snippet has one strange part starting at offset loc_8048BB9. I added comments on every line to show that the whole sequence does nothing, no matter what the serial is, this sequence will always jump over the bomb message in line 08048BE6. We only need to worry about the code before, which decompiles to:

Why is the code loading a Passed serial is invalid... message even though the bombs did not go off?

Next follow a lot of lines that I won’t discuss in detail. All they do is generate a second string. The code sequence leads to a call to decraycray, which take the Passed serial as one argument, and the generated string as the second argument:

The subroutine implements an XOR cipher. The key length is at least as long as the ciphertext, therefore the decryption can turn the “Passed serial is invalid… ciphertext into any other message by choosing an appropriate key. Since there are no more bombs to defuse in this crackme, we can take an educated guess that the XOR decryption will turn the Passed serial is invalid… into the good boy message.

Keygen

Writing a keygen is trivial. One only needs to enforce the four check rock, paper, scissors and cracker. My keygen first generates a random serial by picking 19 characters from the allowed character set. I then try to fix this serial by setting individual characters. For example, since paper dictates that:

48 <= (serial[8] ^ serial[10]) + 48 <= 57

I set serial[8] to one of the characters that XORed with serial[10] meets this condition. Only about 6.2% of the time fixing the serial does not work because at some point a condition can`t be fixed. The keygen in Python 2.7 just retries with a different starting point in those cases: