This crackme is simple enough to use it for learning purpose. I'm taking it for another round of reversing. This time, it's gdb-fu! In the CTF, this was my first approach, but pin was faster :-)
1/ BasicsWe remember the function name, vm_xor and vm_cmp. Other function looks more like standard operation (JNZ, JMP, call, and so on).
The binary is not stripped, and we see a variable called 'regs'. We guess that's the registers of the VM. While running under gdb, we can print their contents with x/8wx ®s
2/ XOR partIn gdb, we have breakpoints, and we can execute commands at each breakpoints. We'll break at vm_xor, and see what's happening. I don't use any gdbinit scripts because it's a VM, and my gdbscripts are meant to be used in conjunction with known CPUs (ARM/Intel). The goal here is to count how many times the vm_xor function is called, and see if we can gain some insights of what is going on:
Ok, so we understand that vm_xor is called 12 times, whichever the size of the PASS is.
We hope now that vm_xor will works like the XOR: it takes two args from registers. Let's inspect this:
Ok, so we don't see any of our PASS in register. The first one seems to progress one by one, and the 6th one makes progression. It could be relative address, or offset, or anything. Let dive into the vm_xor function. It must have an XOR operation, and it could be interesting to see the operands of the command:
Easy, disable breakpoint 1, and create two more:
Well, only the second XOR is used, but it's not really important at this point. The important point is to see that each of our PASS has been XORed with a constant string (you can repeat to veroify this).
So, we have an XOR key, if we copy it we have: 1337d33dc0deabad1dea1337
(1337d33dc0de?? 1337d34dc0de would have sound better, I think. Another bug? ^_^ )
Now we have to find the expected solution, because PASS^key=solution and with simple math, we can say that: PASS = key ^ solution.
3/ CMP partWell, we use the same technics. Let's break on vm_cmp and see what happens in registers:
Interesting. We see that register 1 changes, and register 2 stays the same.
One more check, because the key is 1337d33dc0deabad1dea1337:
- '1' XOR '0x13' => 0x22
- 'A' XOR '0x13' => 0x52
But, have you spotted something else really weird?
Register 3 changes too!!
And it don't take a lot of time to understand that register 3 holds the "PASS XOR key":
- '2' XOR '0x37' => 0x05
- 'B' XOR '0x37' => 0x75
Once again, we use the gdb commands function to force register to have the good values, and let print this value.
Ok, job done, we just have to copy bytes from register 2 and 4. vm_cmp has two times two bytes to compare, but function is called twice. I really should dig inside this func to understand how this work :)
solution is: 7a60b24eb4bbe6d449837e52
And without any surprise:
Job done, time to drink beer for victory.
I have not failed 700 times, I have not failed once.
I have succeeded in proving those 700 ways will not work.
When I have eliminated the ways that will not work,
I will find the way that will work