Ghost CrackMe solution

Once upon a time (about one year ago) I wrote a CrackMe. It was originally published here: (now also available: and here Since till now nobody solved it, and some persons asked me about the solution i decided to reveal the source code with a comment on algorithm used.
(Complete sourcecode of the solution you can get from here:

What the task was about:
The goal was to make the CrackMe to display the “Good boy” message. (No patching allowed.)
Two levels of difficulty were set:

  1. Creating a keyfile which allows to register the app on the particular computer
  2. Creating a DLL, which allows to register the app on any computer.

Additionally it was packed by my small custom packer.

What the CrackMe does:

  1. Creates login, basing on system username and ProfileGuid. From ProfileGuid all characters, which are not letters are removed. Then the result is concatenated with the username.
  2. Creates the key

The key is created using my custom algorithm. It’s inspired by the Game of Life, but the rules are changed. First, the matrix of characters is generated. It is as below:

  a b c d e k l m n o v w x

Each of these fields can take a value from the set [0,1,2].
Fields assigned to letters are initialized basing on Login. Means, if some character is contained in Login, then its field holds a value of 2.

Login = test
1. Uppercase and lowercase characters are not differentiated.
2. All the characters different than letters are converted to ‘x’
3. Only unique characters are used

All the fields with non-zero values are the ‘beings’ in this “Game of Life”… 😛
After initialization of the matrix, the initial population is created, with the help of following rules:

  1. The space between two beings – or between a being and the line ending – is filled by other beings with the value of 1.
  2. Filling takes place first horizontally, then vertically.
Initial filling for login “acet”
Step1: Horizontal filling
Step2: Vertical filling

After creting the initial population, the process of creating further generations starts. In each step, the matrix is modified by the same rules:

  • if the being have 0 neighbors, it dies “out of lonlyness”
  • if it have exactly 1 neighbor – it produces offspring (fills all the free cells in its neighborhood)
  • if it have more than 3 neighbors – it “suffers starvation” (decreases its value by 1 – if it was 2, it’s weakened to 1, if it was 1 – it dies)

After N steps, the matrix is ready to be translated into a key. The key have 13 segments, writen down in hexadecimal.

The key is nothing more but an alternative representation od the matrix. Each of 13 rows is treated as a number with base 3. Then it is displayed hexadecimally.

The matrix representation of above key.

The key verification:

  1. 1. The LilithGhost application creates a matrix M1 for generated login. It modifies it N times by the “Game of Life” algo. (N generations, where N = login length)
  2. 2. Checks the presence of “Lilith.dll”. IF the library exists, then it is loaded and the function _secretspell2  is executed. The function should:
  •  create a file with the key  (Lilith.key) –  containing the state of the matrix for the same login, but in N-1 generation
  •  return the sum of all the fields for the matrix with N generations. If the returned value differs from the M1 sum. the DLL is rejected on the spot.
  1. 3. Lilith Ghost creates a matrix M2 basing on the Lilith.key. It executes on it 1 more step (transforming from N-1 generation to N generation).
  2. 4. If M1 == M2 then key is accepted!

Additional info:
After changing the subsystem to console, program displays hints:
– the initial matrix for a current login
– in case if the file with the key is supplied – the matrix built on its base

Here goes the API

// Mesh.h
#ifndef MeshH
#define MeshH

#include <map>
#include <string>
#include <iostream>

#define COL_MAX 13
#define ROW_MAX 13

using namespace std;
class Mesh
        int mesh[COL_MAX][ROW_MAX];
        map&amp;lt;char, pair&amp;lt;int, int&amp;gt;* &amp;gt; wpolrzedne;
        Mesh() { this-&amp;gt;initialize(); }
        void fillLineFromPoly(int p, int x, int row);

        void initialize();
        Mesh(string s);
        void printMesh();
        void populateAllNbrsOf(int col, int row);
        int countNbrsOf(int col, int row);
        int hurt(int col, int row);
        int kill(int col, int row);
        int sumRow(int row);
        int sumCol(int col);
        long sumMatrix();
        int polynominal(int row);
        string getSerial();
        int operator==(Mesh m2);

        static Mesh* buildMeshFromSerial(string serial, int len);
    friend class MeshAnimator;


#include <Mesh.h>

#ifndef MeshAnimatorH
#define MeshAnimatorH

class MeshAnimator
        static void populate(Mesh *m, bool isRow);
        static void produceGeneration(Mesh *m);



#ifndef UserGenH
#define UserGenH

#include <string>
#include <windows.h>

namespace UserGen
    string getCharsOnly(std::string s);
    string getUser();


Full source code:

About hasherezade

Programmer and researcher, interested in InfoSec.
This entry was posted in CONfidence, CrackMe and tagged , . Bookmark the permalink.

3 Responses to Ghost CrackMe solution

  1. bartek says:

    Sup 🙂

  2. Darkwarrior86 says:

    its awsome algorithm looks like pgp nice anyways…claps!!!!!

  3. pierluigi says:

    great work! i have just two doubts:
    1. why is the character ‘q’ missing?
    2. why in the first horizontal filling of ‘acet’ login do not you fill with 1 the ‘d’ slot? it should be a slot between two beings
    many thanks

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s