Patchme: genass3's Patch protect

Download: https://crackmes.one/crackme/68b2c2768fac2855fe6fb9be
MD5: 12b79adcbcc2b6aac0e6d84adbf0f6e7
SHA1: 716dbe22f9b6c55102e72e2dfef220819b0eff97
SHA256: 4f8cb7e39950c2571b1151d5da5a64b9ed4f91e425bc6d5b29a5d8513ac6bdce

This is listed as a C/C++ x64 executable with a difficulty rating of 2.0. The author is genass3 and the executable is written for Windows.

The authors description:

Try to patch, should be not so hard, also you can find password very easily, if you have eyes :) Ur main goal is patch. Also feel free to give a feed back about the protection, i would like to read and improve. The source code of the protection - https://github.com/generin0/PatchGuard The source code of the crackme - https://github.com/generin0/crackme1

I think we should normalize including the source code to crackme challenges. For one, it would make it easier to know whether they're malicious or not. Two, it makes for a great learning aid. The old adage to learning reverse engineering was "write some code, compile it, and exam it." This would essentially be that but where you aren't limited by your own coding ability. You get to learn from the code of others. You learn neat little tricks. Just all around a good thing if you ask me. Less I digress anymore, let's get it.

Taking a high level look at the Detect it Easy output, nothing really jumps out to me. It was compiled using Visual Studio 2022 and compiled to run as a console application.


Taking a gander at the strings, we see what can presumably be described as the console output. Good, so we know strings aren't encoded/encrypted. I also see function names like CreateThread, fgets, memcpy which I think will be relevant later.


In my previous blog post, I mentioned that I'm accustomed to seeing the main function called by the CRT code about 2/3 of the way through the crt function. I identify it by looking for 3 arguments being passed to a function call. This is exactly what I am referring to. Note that this is an x64 application and function arguments are passed via the RCX, RDX, R8, R9 and then the stack.


I spent a few minutes cleaning up the code and this is the result. A call to InitializeCriticalSection which allows threads of a single process to make use of mutual-exclusion synchronization. This makes sense because shortly after, a call to CreateThread is being called with PatchMonitor as it's argument. Before that thread is created though, a snapshot is taken of the virtual memory space with K32GetModuleInformation. This will be used to detect any tampering. After that, MessageBox and Beep are dynamically loaded. Finally, we get to the section of the code that actually determines if we entered in the correct password. Seeing as that is irrelevant for this challenge, I didn't bother spending too much time on it.

This is the PatchMonitor function. This function checks to see that the executable isn't resized or relocated and ensures that the code is never tampered with. With this knowledge in mind, it seems kind of trivial to make the patches at this point. I think the author intended for the challenge to be to patch the executable after the thread has been created.

I loaded the executable inside of x64 and when the entry breakpoint was hit, I went into the code and nopped out everything between the Print letting me know that the application had started and the Print letting me know that CRC monitoring thread has started. Then I scrolled down and looked at the jumps that were taken to the bad boy message and paused execution on them. I also just nopped one of them out as it kept triggering. My guess is that the code tests the input against the correct passcode one at a time. If it reaches an incorrect character, it jumps. So yeah, I just got rid of that. The jumps prior probably just check my input length and jump to the badboy if isn't the same length as the correct password. If any of the jumps tried going to the bad boy, I modified the flag register to force the good boy message.

 
To stay true to the authors intent, I allowed the executable to execute normally all the way until it prompted me for input (which is after the CRC Monitoring Thread was created). At this point, I suspended all the threads and patched the PatchMonitor function to avoid jumping to the bad boy sections of code. I resumed the threads and the code continues to run. I could now make all the same patches/breakpoints I made above to get the code to jump to the good boy message.

 
Now the only thing left to do is find the password. I still had all my breakpoints in place from when I coerced the application to jump to the good boy. When I entered the password "1234", I hit a breakpoint where RDX is being compared to RAX. RAX contained the value of 4 and RDX contained the value of 7. Okay. The password is likely 7 bytes long. Next I entered the password "1234567" and I made it past the length check but still triggered the bad boy message. I need to re-enable the breakpoint that I had removed earlier to see the character comparisons. Once that breakpoint was hit, I saw a comparison between CL and [rsp+rax+50]. Cl contained the value of "1" and the jump would be taken to the bad boy message. [rsp+rax+50] must be where the password is stored so I followed it in the dump and I see "tftcftc."

 
If we enter that as our password, we get the good boy message. I of course did all of this while the CRC monitoring thread was still eating up CPU cycles.

 
As for feedback for the author, I would try and avoid having a single point of failure. Try to integrate your anti-tamper code with the rest of the logic so it can't just be avoided altogether. I would also suggest hiding your strings. Entering the PatchMonitor function and having strings conveniently tell me exactly what it was doing saved me a lot of trouble.

Comments

Popular posts from this blog

Crackme: atherusti's First C program

Crackme: antilagvip's medium crackme

Crackme: Meist's Passfind Crackme