I’m trying to expand my skillset to the point where I can understand one of Nico Waisman’s BlackHat talks, and that means I have work to do (and maybe a brain transplant too). I’ve always had decent assembler skills, I can write simple shellcode, beat Gera’s challenges, but Windows has never been my domain. Let alone bypassing any of the more advanced memory protection techniques (DEP, ASLR, /GS, etc.).
So recently I started doing simple reversing/key generation cracking challenges for getting used to the world of Windows, Immunity Debugger, and memory corruption. First of all, Immunity Debugger is awesome. It’s basically a skinned OllyDbg with Python integration and other cool extensions. I plan on writing visualization scripts to make the mess of hexadecimal a little easier to parse.
Ok, so the first one I did was keygenme3, which was a keygen challenge. The easiest way to break a keygen challenge is to replace the instructions that check the keygen with a NOP sled. The problem with that simple approach is that the author cleverly used WriteProcessMemory() to re-fill the area with code to break that simple patch. So in the end, you just had to NOP sled the WriteProcessMemory() call, too. Easy in theory, but you had to reverse enough of the program to understand what was happening, and that wasn’t easy for me.
The next challenge was LaFarge #2 keygen challenge. This one was also unique, and a good learning experience. The point of these keygens is not to just get a valid key. That’s easy, just find the code that checks the validity of the key, set a breakpoint, and inspect the “valid” value it’s looking for.
Creating a keygen program that spits out a valid key for any input is a little more work, and is part of the “solution”. The LaFarge#2 application uses a custom XOR-algorithm that would take hours to reverse into another language, which is what I tried to do first. Instead, to create a keygen, I took the existing program, and changed it to spit out the valid key it expects rather than an error message when you input an incorrect key.
Here’s the code block that checks the key:
The figure highlights an instruction at the top which shows the stack address of the expected, valid key, which was computed based on the user name provided. The second highlighted instruction shows which string (0x004062E7) is used in the MessageBoxA() call to tell the user their key was invalid. An easy way to turn this program into a key generator rather than a key checker is to switch the stack address pushed onto stack in the second instruction to be the first stack address. That way, the user will be shown the expected key instead of the error message. Here we can see we’ve reassembled the code for the second instruction to do just that:
Now, we run the program after changing that 1 instruction, and it works as we imagined:
Next project: a Python script to visualize stack frames.