Acrackme is a program designed to test a programmer's reverse engineering skills.[1] Crackmes are made as a legal way to crack software, since no intellectual property is being infringed upon.
Crackmes, reversemes and keygenmes generally have similar protection schemes and algorithms to those found in proprietary software. However, due to the wide use of packers/protectors in commercial software,[further explanation needed] many crackmes are actually more difficult as the algorithm is harder to find and track than in commercial software.[further explanation needed]
A keygenme is specifically designed for the reverser to not only find the protection algorithm used in the application, but also write a small keygen for it in the programming language of their choice.
Most keygenmes, when properly manipulated, can be self-keygenning. For example, when checking, they might generate the corresponding key and simply compare the expected and entered keys. This makes it easy to copy the key generation algorithm.
This is a simple place where you can download crackmes to improve your reverse engineering skills. If you want to submit a crackme or a solution to one of them, you must register. But before that, I strongly recommend you to read the FAQ. If you have any kind of question regarding the website, a crackme, feel free to join the discord chat.
More interesting is the __DATA segment, where we can see that the binary contains a few constructors. This is code that will be executed before main (technically most of the times there is a start function previous to main).
There are seven mod init function pointers and we will verify the code of each one. Usually in targets such as crackmes, DRM, and malware, the constructors contain interesting code that we usually want to reverse (usually to fool novice reverse engineers).
Code execution before main can also be obtained in the load or init methods of Objective-C classes. Little Snitch for example uses this as you can see in this post.
If you are reverse engineering a target always check all these places for interesting code.
Another point of interest are the initial memory protections of each segment. Any segment with an initial executable and writable memory protection is suspicious (if it is executable it always needs to be readable) since it is usually sign of a packer and/or cryptor. It is really not necessary for packers/cryptors to have this set in the header since they can always call memory related APIs such as mach_vm_protect to modify the current memory protection.
After looking at the headers the next step is to load the target binary in a disassembler. For most targets this is a good idea since interactive disassemblers such as IDA, Hopper, and Binary Ninja allow you to comment the code, which is extremely useful for most targets. This also gives us a good hint about the kind of code that we are against to.
Loading the code in IDA we get an almost clean target, meaning by this, a target that IDA disassembles without any major issues. When IDA fails to disassemble or recognize functions we end up with red areas at the top bar chart (or whatever color your IDA theme uses). This crackme does have a small red area:
The objc_msgSend function is one of the functions from the Objective-C runtime that is responsible for delivering messages to objects. For example an Objective-C code of [object message:argument] can be seen in the disasssembled code as objc_msgSend(object_ID, selector, argument). In i386 binaries we can find the selector with a debugger by breakpointing on objc_msgSend function and displaying the second argument as a string, and in the case of x64-64 binaries we display the RSI register contents as string to display the selector. Usually disassemblers are able to extract the selector but not in this case. We will come back to this topic shortly.
The mach_task_self means the code is accessing its mach task port, which can be used with Mach memory and debugging APIs. That is a good hint of something interesting happening here. The 7 value being moved before the call to function sub_26EE is also interesting if you have some experience reversing certain type of macOS binaries. The function at sub_26EE is very simple and just a stub to call a system library API.
IORegistryEntryCreateCFProperty second argument is a CFStringRef, a reference to a CFString object. This is the object created in InitFunc_5 (certain objects like NSString and CFString are bridgeable between Foundation and CoreFoundation).
In this crackme we can find [MCAppDelegate applicationDidFinishLaunching:]. Usually this is only executed once after the application has finished launching. But we will see later that in this crackme it is called a few times.
This is the prologue of the mk method. If we trace this code we will see that the conditional jump at address 0x3B1B is executed, redirecting the execution flow to the following code:
What happens here is that the mk method is constantly being executed via a scheduled timer, essentially creating a run loop that is waiting for some magic input/flag to execute the remainder of mk code. We know that applicationDidFinishLaunching: will need to return to resume application launching, so this is a simple way to keep mk running in the background. If we insert a breakpoint at the beginning of the mk method we will get constant hits and without any user input the method will just loop.
The nib is compiled but there is an util called NibUnlocker that is able to convert it to a xib. We can open the resulting xib file in Xcode and take a look at the button properties.
If we do this we find out that the activate button sends its action to [MCAppDelegate applicationDidFinishLaunching:]. As I told you this method is used in a different way than usual.
Since we know that mk is looping in the background and applicationDidFinishLaunching: returned before we can try the activate button we can breakpoint applicationDidFinishLaunching:, insert some input, and press the activate button. This time if we trace applicationDidFinishLaunching: we land in a different code path:
What happens here is that some variables are set (variables that control the code flow of this method and others), the serial number is read from the NSTextField as a string and the string object stored at address 0x53AC. The same applicationdidfinishlaunching: selector is scheduled the same way mk was. Because certain variables were set, the code path on next applicationdidfinishlaunching: execution is different. This time the method [MCAppDelegate d] is executed.
The d method will call the function sub_3190. This function is responsible for verifying the length of the serial and making a copy of it. If length is odd then the serial is rejected, otherwise it proceeds to make a copy. There is no clue about the valid length of the serial number. We will need to wait a bit for that.
If we still have a breakpoint on applicationdidfinishlaunching: and used an odd serial number then at address 0x41C7 we can find a call to the fail method that displays the bad serial number message. This is a fast fail path, because if we insert an even number this code path is not triggered, so the serial number check is made somewhere else. With this we found out all the code paths available in applicationdidfinishlaunching:. This method is not responsible for serial number verification.
If we insert a breakpoint on the next instruction at address 0x3B21 (inside mk method) after the conditional jump we will see that the breakpoint is never triggered until we insert an even serial number.
The conditional jump will always execute until the pointer at address 0x52A4 is not NULL. When is that pointer not NULL? After function sub_3190 verifies the input serial and sets that pointer to the converted string we saw above.
If we keep tracing the code we will see that the converted input serial will be used together with the key that was generated in nitFunc_6 constructor. This will generate a new key that will be used to decrypt the contents of address 0x260C. If the key is wrong the decryption fails and the memcmp comparison will fail. We now understand all the program flow necessary to a valid serial.
But this is not the serial that we need to input, since we now know that the machine serial number is being used to generate some kind of key. Because we verified that 1CED36375BA86C4DE17C940BF578ED68 is the right key that we need then the valid serial number needs to be something that will be able to generate this key. It is possible because this RC4 is slightly modified with an extra XOR so that the valid serial number is something that is able to generate this known decryption key. If we XOR each byte of the key generated in InitFunc_6 with each byte of the known secret hash we get the valid serial number.
We saw that the decrypt text is essentially composed by selectors and the valid serial message. The rest of the mk method good path is essentially calling these selectors to build and show the valid message alert. Nothing much to explain there.
Hello and welcome back! I'm excited because this challenge was written by a good friend of mine! It's called "Zed's Crackme" and it is hosted on the website crackmes.one. I really enjoyed solving this challenge and making the video/this blog post. In the description the author mentions our goal is to find the serial and he set 2 restrictions on us: "we cannot patch" and "we cannot bruteforce." These are not terrible restrictions, we haven't patched a binary yet and bruteforcing is beneath us right ?? Fair warning, the author is tricky and he left in an annoying rabbit hole which I fell for when I initially solved this challenge. I will take you down this rabbit hole as I think it is a good exercise to improve our reversing chops. The more disassembly we read the better right? However, I will clearly mark where the rabbit hole is so you can skip that section if you feel so obliged. As always I hope you have a good time reading! I also have a YouTube channel and you are free to check out the corresponding video for this challenge here:
3a8082e126