OSX Cracking (Dash)

This is intended to be an educational exploration of some cracking concepts. If you enjoy the software target, please support the original authors by purchasing it.

Dash is an offline documentation browser. It has no copy protection which makes it an ideal target for learning cracking. It has an offline license authentication scheme that uses a license file.

You can use any disassembler you please, but this tutorial will be using Hopper.

Right click on Dash and choose "Show Package Contents" to see the directory. On OSX, executables are stored in Contents/MacOS/. Other than that, there is not much interesting the in the package, just graphic resources and some external libraries.

If you start Dash, click purchase and choose "Activate using license file," you will see that you cannot select any file. This is because Dash has passed a flag to the openfile dialog to only allow selecting specific file types.

In Hopper, search for openfile and browse the code related to opening files. You will see all the possible flags, but the one you are interested in is the dash license file, which is referenced here:

0000000100008ef7         lea        rdx, qword [cfstring_dash_license] ;@dash-license

It is looking for a file with a .dash-license suffix, so you can create one in your terminal:

touch license.dash-license

You can now select the file. Doing it on our newly selected file will produce an "Invalid License" prompt. Look for that string in the executable:

00000001001ea6e8         db         "Invalid License", 0

If you look where it is referenced from, you will see it is loaded in the [DHInApp processLicenseFile:] procedure, specifically at:

00000001000c86c3         lea        rdx, qword [cfstring_Used_invalid_li

If you break at the top of [DHInApp processLicenseFile:] and step through the function, you see that it is jumped to at:

00000001000c85da         je         loc_1000c86c3

However, even if this jump is eliminated, you can see it will continue with another invalid code branch:

00000001000c85e0         lea        rdx, qword [cfstring_Dash_2_license_

Scrolling up, you should see that 00000001000c858c is where this block of logic begins. If you follow it up to its appropriate jump, you should see:

00000001000c8225         je         loc_1000c858c

Most of the code under the above jump looks responsible for setting variables too, so it appears slightly suspicious. Place a breakpoint on the code and run the application again. When the breakpoint pops, you can set the zero flag and see the code fall through and validate the license.

If I had to guess at the original code of the application, it looks something like this:

    processLicenseFile() {
        if( some_condition ) { //judging by the code, probably a keygen check
        else {
            if( old_license ) {}
            else if( invalid_license ) {}

If you have written code like this, you should now see how easy it is to reverse. The condition doesn't even need to be known or reversed, as you saw in this example.

It's important to remember that it is impossible to write a "crack-proof" program. When implementing anti-crack features, the idea is to make the process annoying enough or out of the reach of beginners.

Obfuscating strings stops a lot of low-level reversers

This is easily defeated, but adds time and will stop a lot of people who can only search for strings and nop. A simple implementation:

    char *invalid_license = "SMDQ Q !<<! JD!N12";

    unobfuscate_string(string) {
        return string | 0xSECRETKEY

    display_message( unobfuscate_string(invalid_license) );

You can use code-automation tools to change the strings in your code in one batch. You can also keep a private mapping file so that local copies are easy to debug. To defeat this, all the reverser needs to do is look for a function call he knows (e.g., display_message_box), put a breakpoint on it, and see how the string is generated. Once the method of obfuscating strings is known, a lua or python script can be written to deobfuscate all strings in a program.

If possible, don't use static conditionals

For example:

    keygen_check(key) {
        //some validation logic
        return key | 0xDEADBEEF

    processLicenseFile() {
        char *key = //key
        keygen_result = keygen_check(key)
        call keygen_result

    catch( InvalidAccessException )

A pretty common scheme is to use the key as part of the calculation to determine the location to jump to. An incorrect keygen will cause the application to jump to garbage code and crash. To avoid that on paying customers, catch the exception. This can be defeated by the cracker buying one legit license and seeing where the code is jumped to or simply reversing the keygen procedure. One of these requires money and the other requires skills higher than most beginners will have.