Vous êtes sur la page 1sur 21

Guide du débutant en cracking

https://f95zone.to/threads/beginners-guide-to-cracking.59711/

Disclaimer:
Avertissement: Je ne suis en aucun cas un expert sur ce sujet, mais j'ai réussi à résoudre certains problèmes
de la vie réelle et j'ai donc quelques compétences. De plus, ce guide ne traite que des applications
Windows. J'imagine que les principes généraux s'appliquent à d'autres systèmes d'exploitation, mais je n'en
ai aucune expérience. Ce guide s'adresse principalement aux personnes qui aimeraient essayer de cracker
quelque chose et qui ont des connaissances techniques, mais qui ne savent pas par où commencer. J'ai
organisé le guide en sections. J'ai eu du mal à trouver un ordre logique dans lequel tout couvrir, donc ça
pourrait être un peu un vidage d'informations, désolé pour ça. Étant donné que les sections ne suivent pas
un ordre strict, vous pouvez ignorer celles qui ne semblent pas intéressantes.

Tout d'abord, qu'est-ce que j'entends par "craquer" ?


Supprimer principalement les mécanismes DRM pour rendre un jeu plus portable ou supprimer le cryptage
appliqué à un ensemble de fichiers. Les deux vont souvent de pair. Beaucoup de ces trucs pourraient aussi être
appelés « piratage » ou « ingénierie inverse ». Tout est assez lié. Bien qu'avec le "piratage", j'ai tendance à penser
davantage aux attaques de réseau. La plupart des travaux de craquage décrits ici peuvent être effectués sans
connexion Internet active.

Attends... c'est illégal ------------------------


Je n'ai pas regardé les lois spécifiques, mais oui. Oui c'est le cas. Si vous êtes sur ce forum, vous ne vous en souciez
probablement pas. Si les implications morales de la suppression de la protection DRM vous dérangent, gardez à
l'esprit que personne ne vous oblige à publier votre travail. Vous pouvez faire des projets de crack et garder les
résultats entièrement privés, ou contacter les développeurs avec des conseils sur la façon d'améliorer leur système
DRM ou de la merde *. C'est à vous. Si vous envisagez de vous lancer dans la sécurité informatique, apprendre à
pirater/craquer pourrait être très utile car cela vous en apprend beaucoup sur ce qui rend un système sécurisé.
*Remarque : Même si vous n'avez que de bonnes intentions, le crack d'un jeu est illégal, sauf si vous avez une
autorisation préalable. N'admettez pas d'activité criminelle sur un compte lié à votre véritable identité.

Pourquoi craquer ? --------------


Personnellement, je le fais parce que je trouve ça amusant. Essayer de contourner un système de sécurité est
comme ce grand puzzle en monde ouvert. Contrairement à un puzzle construit qui n'a généralement qu'une ou
deux solutions préfabriquées, ce puzzle a des tonnes de solutions potentielles. Certains faciles, certains
ridiculement difficiles. Il y a tellement de liberté et de contrôle créatif dans la façon dont vous allez le résoudre. Je
trouve que c'est un travail vraiment enrichissant. Et si vous réussissez, vous vous sentirez comme un pirate
informatique l33t.

Compétences préalables
1. Connaissance générale du fonctionnement d'un ordinateur au niveau inférieur

2. Connaître au moins un langage de programmation. De préférence un bas niveau comme C++

3. Une volonté d'apprendre

En fin de compte, le point n°3 est le plus important. J'apprends généralement beaucoup sur le
fonctionnement des ordinateurs en essayant de déchiffrer quelque chose. Vous apprenez le plus en faisant,
alors ne vous découragez pas trop si vous ne comprenez pas encore quelque chose. Cela dit, si vous n'avez
pas les points 1 ou 2, la courbe d'apprentissage initiale sera ridiculement raide. Dans ce cas, vous feriez
probablement mieux de faire des projets axés sur l'apprentissage de ces compétences générales avant de
revenir au sujet du cracking. Savoir programmer est important car de nombreuses méthodes vous obligent
à écrire et compiler un programme. Un langage comme C++ est le plus utile pour le travail de crackage
"réel", mais un langage de haut niveau tel que Python est également très utile pour automatiser toutes les
tâches diverses qui se présentent.

Useful tools
---------------
Notepad++ (https://notepad-plus-plus.org/)
(plain-text editor)
Use:
- Taking notes
- General purpose text editing
- Viewing files that are mostly text

Notes:
- There is a button for "show all characters". This will show whitespace characters and line-endings. Use it
whenever you want to be sure that you are seeing the full file.
- Check the "Encoding" menu. Some mostly-text files won't display properly if you use the wrong
encoding.

HxD (https://mh-nexus.de/en/hxd/)
(hex-editor)
Use:
- View binary data of any file
- Check for differences between two files or just check if two files are identical
- Can manually edit files in a pinch

Notes:
- Has side-tab that shows ascii encoding of data. Lets you scan through files for readable strings, which are
more common than you might think.

Dependency walker (https://www.dependencywalker.com/)


(gives information on .dlls and exes)
Use:
- Lets you see which .dlls are statically linked, which files get imported, and most importantly, which
functions a exe/dll exports

Fiddler 4 (https://www.telerik.com/fiddler)
(Gives information on web traffic)
Use:
- Lets you see which web requests are being made on your machine
- Also lets you decode requests or setup automated responses

Notes:
If you really want to get into the network side of things there's also WireShark which does similar things
but is more complex

ILSpy (https://github.com/icsharpcode/ILSpy)
(Decompiler for managed code)
Use:
- Let's you decompile managed code into a (from my experience) quite readable result

Notes:
Unity applications use C# which compiles to managed code.
Other than that most applications use unmanaged code though so you might not get much use out of this
one.

x64dbg (https://x64dbg.com/)
(General purpose debugger)
Use:
- Let's you see which assembly code a program is running and step through the code
- Also lets you see which modules are loaded, what the memory space looks like, and which threads are
running

Notes:
Some applications can only be latched onto if you are running the debugger in admin mode.

Many applications you will encounter (especially dedicated DRM systems) will check for a debugger and
shutdown if one is found. Of course, checking for a debugger still requires code to be executed, so if you
are stepping through the program line-by-line you can probably do something to prevent the debugger
check from triggering. But there is a LOT of assembly code so it most cases you want to run until a
breakpoint or until a function returns and in those situations you often find that a debugger check got
triggered somewhere and shut everything down.

So is the debugger useless for those applications then? Not at all.

1. You can latch onto an application after it is running, so debugger-checks in the start-up phase are
bypassed
2. The process is paused when the debugger latches on, so you get a snapshot of the program at that
time which you can analyze for as long as you wish. (and you can use hooks to pause the program
and latch on during a moment of interest. More on hooks later)
3. Debugger checks don't happen constantly. You might be able to step through quite a bit of code
before encountering one.
4. If the program uses the IsDebuggerPresent windows function, then you may be able to hook it and
disable the checks that way.

Warning:
I only started using a debugger in my workflow fairly recently and found it incredibly helpful. However
my advice regarding it's use and assembly language in general might not be optimal since I'm still learning.

Process Explorer (https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer)


(kinda like taskmanager but with more features)
Use:
- Lets you view which processes are running, what their PID's are, and kill them
- Lets you perform dumps for processes
- Lets you see the settings for how a process was started
- Lets you view the environment variables for a process
- Has a feature for detected strings found in the process memory

Notes:
I've had one instance where a process couldn't be killed with Task Manager but Process Explorer could.
Something to keep in mind
Process Monitor (https://docs.microsoft.com/en-us/sysinternals/downloads/procmon)
Use:
- Let's you view file-reads, file-write, registry access, dll loads, and a few other event types
- For each event, also gives you the stack-trace of the program at that time

Notes:
Together this can tell you a lot about what the program is doing.

Example 1:
You have an archive file of an unknown format and you see the program do something like:
- Read a bit in section A
- Suddenly do a far jump and read a block of data in section B
Then section A is likely an index of some sort and section B holds the raw file data.
Try extracting the data block that got read from section B and see if it's readable.
If not there is probably a second decryption or decompression step applied.

Example 2:
You see the program read through an entire file in equal-sized chunks.
The program is probably performing some sort of integrity check on the file.

Example 3:
You see the program make a failed registry access under a program-specific name
If it's a registry "folder", try creating it and seeing how the program responds.
If you're lucky the program may be trying to read a "IsVerified" value which you can just set to 1 (true)
Admittedly, this DRM setup would be pretty dumb, but there are a lot of dumb DRM setups out there. It's
worth trying the simple stuff.

Warning:
Similar to using the debugger, you have a problem with information overload here.
Programs will trigger a ton of events and most of them are not noteworthy.
The first time I used Process Monitor I saw it making an access to a cryptically named registry key and
thought "Aha! That's where the encryption key is stored!". But no, it was just one of the registries that gets
accessed during normal execution of windows programs.
It might be worthwhile looking at a simple program like say notepad just to get an idea of what the
baseline level of event spam is.

Visual Studio (https://visualstudio.microsoft.com/)


(Programming IDE)
Use:
- Program stuff!
- Let's you open projects others have created. If it's a windows application they usually use Visual Studio

A quick note about managed vs unmanaged code


--------------------------------------------------------------
Most applications compile down to assembly code which can be run directly. This is called "unmanaged
code". However some languages that use the .NET framework like C# compile down to an intermediate
language (aka IL) which gets executed by a different program. .exe and .dll files may contain managed or
unmanaged code and depending on which need to be dealt with differently.
Google it if you want more information.

A quick note about DLLs


-------------------------------
Une note rapide sur les DLL -------------------------------
Si vous n'êtes pas familier avec les DLL, voici une brève introduction : Les DLL et les EXE sont assez similaires. Ils
contiennent tous les deux du code, sauf que les .exe sont conçus comme un point de départ pour l'exécution d'un
programme. Les DLL sont incluses/utilisées par d'autres programmes. Les DLL ont une liste de fonctions
"exportées" qui mappe les noms de chaîne aux adresses de fonction dans la DLL. Les fonctions "exportées"
constituent l'interface par laquelle la DLL est censée être utilisée. Vous pouvez techniquement appeler n'importe
quelle fonction d'une DLL, mais vous devez savoir où elle se trouve en mémoire. Par conséquent, les exportations
sont là pour vous le dire. Pour appeler une fonction, vous devez également connaître la signature de la fonction
(convention d'appel, type de retour et types d'arguments). La table d'exportation ne vous donne PAS cette
information, vous devez vous fier à la documentation externe pour cela. Toute DLL destinée à être utilisée par le
grand public aura une documentation disponible.

Technique de craquage : injection de DLL --------------------------------------------


L'injection de Dll vous permet de forcer une application à charger une DLL de votre choix. Les DLL peuvent exécuter
du code lorsqu'elles sont chargées par un processus, ce qui vous permet d'exécuter du code arbitraire dans le
processus de votre choix. Il existe plusieurs façons d'obtenir qu'une application charge votre DLL, mais les deux
méthodes les plus simples sont : Lancer l'application victime dans un état suspendu, injecter la dll à l'aide d'un
thread distant, reprendre le processus victime Avec un processus victime déjà en cours d'exécution, injectez la dll à
l'aide d'un thread distant Personnellement, je préfère #1 car avec l'application dans un état suspendu, vous pouvez
exécuter votre code avant que quoi que ce soit d'autre ne se produise. Vous donne plus de contrôle.
Alternativement, la méthode n ° 2 est utile s'il y a quelque chose qui vous empêche de lancer l'application via le
code ou si vous souhaitez attendre la fin du code d'initialisation.
Technique générale de craquage : crochetage ------------------------------------------------
L'accrochage vous permet d'écraser le comportement de toute fonction dont vous connaissez l'adresse et la
signature de fonction. Si cela semble vraiment puissant, c'est parce que c'est le cas. Je ne sais pas s'il existe un
moyen pour les applications de vous empêcher de les accrocher, mais si c'est le cas, je ne l'ai pas rencontré. Soyez
la pute que vous avez toujours voulu être ! L'accrochage fonctionne en modifiant les instructions de montage
chargées en mémoire. Je vous conseille de le lire, c'est assez intéressant. L'explication de haut niveau est que
lorsque quelque chose appelle la fonction X, il appelle à la place votre fonction X' qui peut exécuter le code qu'il
veut, y compris l'appel de la fonction d'origine X.
Voici un exemple d'utilisation qui n'est pas trop tiré par les cheveux,
Problème:
Vous avez une application avec un tas de fichiers de données cryptés. L'application utilise une méthode de
cryptage personnalisée et vous ne savez pas comment elle génère la clé. Cependant, il est livré avec une DLL
"crypto.dll" qui contient le code de cryptage. Cette DLL exporte la fonction "déchiffrer". Pour faire simple, disons
que vous savez que la signature de la fonction est : void __stdcall decrypt(void* buffer, int length, char* key)
Solution:
Injectez une dll dans le processus victime qui accroche la fonction "déchiffrer" de crypto.dll et remplacez-la par une
fonction qui imprime la clé dans un fichier. Maintenant que vous avez la clé, vous pouvez appeler la fonction de
décryptage de crypto.dll pour décrypter n'importe quel fichier que vous souhaitez ! Il pourrait utiliser le schéma de
cryptage le plus complexe au monde, peu importe. Vous n'avez pas besoin de savoir comment fonctionne le
décryptage. Cela fonctionne.
Maintenant, dans la pratique, il y a quelques choses qui peuvent rendre cela plus compliqué :
1. Si une bibliothèque externe telle que "cryto" est utilisée, elle n'exportera probablement aucune fonction
critique. Le développeur sait où se trouvent les fonctions et peut les appeler, personne d'autre n'a besoin de savoir
2. Vous ne connaîtrez probablement pas la signature de la fonction. Cependant, c'est quelque chose que vous
pouvez surmonter. Plus à ce sujet plus tard.
3. La DLL peut exécuter du code d'initialisation nécessaire au déchiffrement. Peut-être qu'il a une fonction init
(specialKey) et que les clés entrées plus tard sont XORées avec la clé spéciale avant que le déchiffrement n'ait lieu.
Cependant, dans ce cas, vous pouvez soit : - Hook init (specialKey) aussi ou - Exécutez votre code de décryptage
dans le processus victime à l'aide de la bibliothèque crypto.dll pré-initialisée

Les détails techniques ----------------------------- Maintenant, vous vous demandez peut-être: OK c'est gentil et tout,
mais comment puis-je faire cela? Toutes ces informations sont disponibles en ligne afin que vous puissiez
simplement Google, mais depuis que j'essaie d'être utile ici, je vais également passer sur certaines des méthodes
exactes. Quand j'ai appris pour la première fois à être un talonneur, j'ai utilisé une bibliothèque C # appelée
EasyHook. Cela fonctionne, en quelque sorte, et vous pouvez vous donner un coup si vous aimez vraiment C #, mais
je vous conseillerais d'utiliser C ++ à la place. Raisons d'utiliser C ++ au lieu de C #: 1. C # est du code géré afin que
vous injectionsiez du code géré dans une application non gérée. Cela fonctionne dans la plupart des cas, mais je
pense que cela peut causer un étrange comportement en fonction de ce que vous accrochez. J'ai eu un problème
avec des crochets échouant qui s'est éloigné une fois que je suis passé à C ++ En tant que 2. Avec C ++, il est
beaucoup plus facile de décrire avec précision les signatures de fonction. Par exemple, si vous regardez la fonction
de l'API Windows GetProcAddress, vous verrez la signature est la suivante: FARMROC GETPRODDRESS (HMODULE,
LPCSTR) En tant que Maintenant, vous vous demandez peut-être: WTF est une hmodule, qu'est-ce qu'un LPCSTR?
Qu'est-ce qui est lointain? Si vous continuez à creuser un peu, vous trouvez que: FarProc peut être traité comme
vide * Hmodule peut être traité comme vide * Lpcstr est char * est char * En tant que Maintenant, vous devez
trouver tous les équivalents C # pour construire la définition de la signature et si vous faites des erreurs, cela ne
fonctionnera pas. Avec C ++, vous venez d'importer une fenêtre.h et utilisez directement la merde LPCSTR. Dans
Visual Studio, vous pouvez également survoler le type pour voir ce que le type sous-jacent est. Et si vous utilisez le
type de manière incorrecte, vous obtiendrez probablement une erreur de compilateur. Quoi qu'il en soit, vous
pouvez être sûr que votre signature de fonction est exacte à la documentation. Alors ya, épargnez-vous une
douleur et utilisez c ++ Quoi qu'il en soit, disons que vous souhaitez accrocher une application et votre journal
lorsqu'il écrit dans un fichier. Démarrez Visual Studio et créez un nouveau projet C ++ Console Console, permet de
l'appeler "injecteur". Ajoutez un nouveau projet à votre solution, cette fois une DLL C ++, appelez-la "in3jectiondll".
Si "l'injecteur" n'est pas en gras, cliquez dessus avec le bouton droit de la souris et définissez-le comme "Projet de
démarrage". Aussi cliquez avec le bouton droit de la souris sur le bouton droit de la souris sur les dépendances de
la construction> Dépendances du projet et définissez In3jectionDLL comme une dépendance. Maintenant, lorsque
vous appuyez sur le bouton de démarrage, les deux projets construiront (si nécessaire) et «injecteur» seront
exécutés. Comme j'écris cela, je pense que je vais inclure ce programme de démonstration avec le message au lieu
d'écrire tout ici. Je vais simplement passer sur les points de haut niveau ici. Écrivez le code pour faire l'injection DLL
vous-même. Au début, j'ai cherché une bibliothèque pour cela, mais c'est en fait facile comme merde. (seulement
comme 30 lignes de code) Les étapes sont: - Obtenez une poignée à votre processus de victime avec les
autorisations nécessaires (peut avoir besoin d'exécuter en mode administrateur en fonction de ce que vous ciblez)
- Obtenir l'adresse de LoadLibrarya - allouer la mémoire dans le processus de victime et copier le chemin de votre
dll d'injection - Créez un fil distant qui appelle LoadLibrarya avec le chemin de votre DLL d'injection comme
argument (Étant donné que l'argument est en réalité un pointeur, nous avons dû charger les données de chaîne
dans le processus de victime en premier) - Attendez que le fil distant finisse - Fermez la poignée de processus et la
mémoire allouée librement (ou non, l'injection DLL est effectuée à ce stade) Si vous utilisez la méthode d'injection
n ° 1, vous ajoutez les étapes d'emballage: - lancer EXE dans l'état suspendu (nécessite le chemin de fichier),
renvoyer une poignée - Autres étapes - libérer EXE de l'état suspendu Si vous utilisez la méthode d'injection n ° 2,
vous ajoutez les étapes d'emballage: - Obtenez la poignée pour exécuter le processus (nécessite le PID), retourner
une poignée - Autres étapes Et c'est tout l'injecteur doit faire. Vous pouvez copier-coller le code de l'injecteur pour
chaque projet de craquage que vous effectuez ou configurez quelque chose de fantaisie qui réutilise le même
projet pour tout. Pour l'injectiondll, le point d'entrée est la fonction appelée DLLMAIN. Pour commencer, écrivez
simplement un message dans un fichier et confirmez que le code DLLMain est appelé. Pour effectuer l'accrochage,
j'utilise une bibliothèque appelée Minhook (https://www.codeproject.com/articles/44326/minhook-the-
minimalistic-x-api-hooking-libra) Il y a d'autres bibliothèques d'accrochage, mais je vais juste couvrir Minhook ici.
Installez Minhook dans votre projet DLL et appelez mh_initialize () pour vous assurer que cela fonctionne. Le
processus général de création d'un crochet est: - Créer un TYPEDEF pour la poignée de la fonction. - Créer une
variable globale pour contenir le pointeur de fonction d'origine - Créer une fonction avec la même signature qui
sera appelée à la place - appelez mh_createhook pour faire le crochet - appelez mh_enablehook pour l'activer
(n'oubliez pas cette étape) Dans notre exemple, le code est:

C:
typedef BOOL (__stdcall* WriteFileDef)(HANDLE, LPCVOID,DWORD,LPDWORD,LPOVERLAPPPED);
WriteFileDef WriteFileOriginal = NULL;

BOOL WriteFileDetour(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD


lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped){
// TODO Perform logging code here

// Call original function so host program proceeds unhindered


return WriteFileOriginal(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten,
lpOverlapped);
}

void hookWriteFile(){
if(MH_CreateHook(&WriteFIle, &WriteFileDetour, &WriteFileOriginal) == MH_OK){
// Created successfully
if(MH_EnableHook(&WriteFile) == MH_OK){
// Success!
}
else{
// TODO: Print error message
}
}
else{
// TODO: Print error message
}
}

Ensuite, pour appliquer le hook, vous appelez simplement hookWriteFile() dans DllMain après avoir appelé
MH_Initialize()

C:
void* hookAddr = (void*)GetProcAddress(GetModuleHandleA("external.dll"), "ReadFile");
MH_CreateHook(hookAddr, &WriteFileDetour, &WriteFileOriginal);
MH_EnableHook(hookAddr);

Note that for this to work, "external.dll" needs to be loaded by the process.

Que faire si ma dll cible n'est pas chargée ? ---What if my target dll is not loaded?--
Deux options: Chargez-le vous-même en appelant LoadLibrary() Lorsque le processus hôte essaiera de le
charger à nouveau plus tard, il renverra simplement un descripteur au processus déjà chargé. Accrochez
LoadLibrary et attendez que le processus hôte charge la dll cible. Chargez-le, puis installez vos crochets
avant de revenir.
Piège : caractères larges vs caractères étroits -------------------------------------------------- --------
Comme vous le savez peut-être, en C++, il existe des caractères "normaux" (char) et des caractères larges
(wchar_t) char est de 1 octet (et s'habitue souvent à ne représenter que des données binaires non
textuelles) tandis que wchar_t est de deux octets. Dans le contexte du texte, char est utilisé pour les
caractères ascii tandis que wchar_t est pour l'unicode et d'autres jeux de caractères plus larges. Si vous
avez un fichier avec un caractère japonais dans le nom, vous souhaitez utiliser une chaîne de caractères
large pour représenter le chemin. Quoi qu'il en soit, en gros, tous les appels d'API Windows qui
impliquent du texte ont une version large et "étroite". La version large a un suffixe W tandis que la
version étroite/ascii a un suffixe A. La version par défaut de tous les appels d'API est la version large. Par
exemple, si vous voyez LoadLibrary("x.dll"), il s'agit en fait de LoadLibraryW("x.dll"). LoadLibrary est une
macro qui se développe en LoadLibraryW Un appel à LoadLibraryA et un appel équivalent à LoadLibraryW
se résoudront à une fonction commune sur toute la ligne, mais je préfère ne pas accrocher des trucs de
très bas niveau. Si vous souhaitez surveiller chaque bibliothèque chargée, connectez simplement
LoadLibraryA et LoadLibraryW.
La même idée s'applique aux autres fonctions de l'API.
Piège : convention d'appel incorrecte ----------------------------------------------
Si vous vous trompez sur la convention d'appel d'une fonction, vous obtiendrez un message d'erreur très
utile qui dit quelque chose sur le registre ESP et suggère que vous avez foutu la convention d'appel. Il
existe quelques conventions d'appel, mais les deux que je rencontre constamment sont __stdcall et
__cdecl. Une définition de fonction qui spécifie la convention d'appel ressemble à :

C : void __stdcall funName(...){ ... }

Si vous connectez une API documentée, elle vous indiquera quelle convention d'appel utiliser, souvent
spécifiée via une macro.
Par exemple, l'API Windows a la macro WINAPI qui se développe en __stdcall Utiliser des macros pour
écrire des hooks plus rapidement --------------------------------------------
Vous remarquerez peut-être qu'il y a beaucoup de répétitions lors de l'écriture d'un crochet. Vous devez
écrire deux fois la définition de la fonction et créer plusieurs types et objets portant le même nom. Eh
bien, vous pouvez configurer des macros pour accélérer le processus. J'inclurai les macros que j'utilise
actuellement dans le projet de démonstration. En bref, ils vous permettent d'écrire un crochet comme
celui-ci :

C:
HOOK_DEF(LoadLibraryW, __stdcall, HMODULE, LPCWSTR moduleName) {
// Do something
return fp_LoadLibraryW(moduleName)
}

CREATE_HOOK_SIMPLE(LoadLibraryW)

And then install it somewhere by calling: hookLibraryW()

Getting feedback from your injected DLL


----------------------------------------------------
The injected DLL can't print to the console window (unless your target application has one open), so how
do you get feedback?
A simple solution is writing to files, which is the approach I'd recommend for new people.
A complicated solution is using inter-process-communication (IPC) to send the data to another application
which is also running. This allows you to send data to another application which has a console window
open, giving you feedback in real time. It's quite pretty once setup.
You could also use this to send data to your injected DLL, though I can't think of many applications for
doing that.

Hooking undocumented functions


--------------------------------------------
When hooking a function, the things you want to know, in order of importance, are:
1. The function address
2. The function signature
3. What the function does & what the arguments mean

When hooking a documented API you usually know all three but sometimes you aren't so lucky.
Let's go through them in order:
Case 1: I don't know anything
--------------------------------------
At the very minimum you need to know what your looking for.
Usually this goes something like "I think there is a function that does X, I want to hook it"
Let's say you are trying to remove a DRM check. You speculate that the program has a function like
IsUserAuthorized() which just returns a boolean value. You want to hook this function and have it always
return true.
To make this more challenging, lets also say that the program uses aggressive debugger checking, so
stepping through the assembly code for any extended length of time is not an option.

The first step is to analyze where this function call is taking place. Look at the events triggered by process
monitor and see if you can determine a region where the DRM check is likely happening. Lets say you
determine that while the DRM check is happening a public API function X is being called.
Hook function X and make the thread sleep for, say, 15 seconds, then return the proper value. This way
you are ensuring that the IsUserAuthorized() function is running for at least 15 seconds. Launch the
program (with the hook) and within that 15 second window, latch on with the debugger.
The program is now paused within the DRM check and you can analyze to your hearts content.
(Note: There are usually a lot of threads running. Make sure you are looking at the correct one)

Look at the stack trace and hop around looking at the functions that are currently active. IsUserAuthorized
is probably somewhere near the top (assuming it exists).
This step is obviously easier the more you know about assembly.
I'm not great at assembly myself so I don't want to give too much advice on it here, but two keys things to
note are:
1. Arguments to a function get added by pushing them to the stack. If you see X "push" commands before
a function call, the function probably takes X arguments.
2. Function definitions usually have some "blank" space before them in the form of "INT3" instructions

Once you find a candidate function, note down it's address. (that is, the address of the first instruction in
the function) If the function is within the memory space of a module, write down the base address of the
module.
You can also use the raw address directly, but it is less reliable because modules are not guaranteed to be
loaded in the same place.

Congratulations! You can now move on to the next stage

Note of caution:
As far as my understanding goes, not all assembly level functions can be hooked seamlessly.
"Proper" functions follow some rules like not modifying the registers. At the assembly language level
anything goes. You could have an assembly-level function that modifies registers and external code that
depends on the registers being modified. In that case adding a "proper" function wrapper breaks the code
since the wrapper function will restore registers to their original value preventing the internal function
from having the same effect.

Case 1.5: I have a candidate address, but I'm not sure


-------------------------------------------------------------------
Setup a hook for a function that takes no arguments. In that function make the thread for sleep for 15
seconds.
Run the program and latch on with the debugger once it hangs. (If it doesn't hang at all, your address is
completely incorrect) Take a look at the call stack and the arguments loaded into the stack to ensure
everything looks about as expected.

Side note:
The code for calculating a raw address looks like:
void* address = (DWORD)GetModuleHandleA("baseModule") + (DWORD)0x187a0
There are probably a few ways to format it, but that's the method that's worked for me.

Case 2: I know the address and nothing else


-------------------------------------------------------
The sophisticated method would be to learn enough about calling conventions and assembly that you can
just look at the assembly and go "ah, this takes X arguments and uses calling convention Y, of course". But
for those of us who are too dumb or lazy to do that, the following method also works.

You can experimentally figure out how many arguments a function takes.
This may be wrong, but I think when dealing with 32bit applications, every "type" can just be seen as a 32
bit value.
So incrementally try:
UINT32 __stdcall funName()
UINT32 __stdcall funName(UINT32 arg0)
UINT32 __stdcall funName(UINT32 arg0,UINT32 arg1)
etc.

In each case, just call the original function, return the value, and do nothing else.

From my experience:
- If you get the number of arguments wrong, the program will just crash without any error message or a
very generic one.
- If you have the number of arguments right but the calling convention wrong, you will get an error
message telling you you're using the wrong calling convention
- If you have the function signature right and you're not hooking a weird assembly-level function, then the
program will run like normal!

So, add arguments until you get a calling convention error, then cycle calling conventions until the errors
stop.
If you reach 10+ arguments, check your assumptions. It's very unlikely for a function to have that many
arguments.

Case 3: I know the address and signature but not what it does
------------------------------------------------------------------------------
Start analyzing the function by printing the arguments and the returned value (from the original function)
as hex values.
If the value is relatively low, it's probably a raw value. If it's high it's probably a pointer of some sort.

To figure out pointers, latch on with the debugger and check where in memory the potential pointer is
pointing. If it points to a section of 0 bytes, it's probably not a pointer. Similarly, if it points to the middle
of a section that looks completely random, then it's probably not a raw pointer either.
If you're lucky, you can recognize some data in the area the pointer is pointing to.

You should also try stepping through the function with the debugger if you're able to. Make note of values
that come up and see where they re-appear.

Primer on XOR
-------------------
If you aren't familiar with the XOR operation (also written: ^), the key properties are:
1. It's a bitwise operation, meaning the inputs and outputs are the same length
2. A ^ B = B ^ A
3. A ^ (B ^ C) = (A ^ B) ^ C
4. A ^ A = 0 (ie, a sequence of 0 bits as long as A)
5. A ^ 0 = A

Security systems love xor-ing shit so it's a good operation to be familiar with.

Regarding Encryption Systems


---------------------------------------
Quick primer on terminology:
Plaintext = Unencrypted binary data (doesn't have to represent text)
Ciphertext = Encrypted binary data
Block size = Smallest unit that encryption algorithm can work on
For some algorithms this is 1 byte or even 1 bit making them essentially blockless

Encryption systems can have a few properties.


Let's go over a few:

1. XORing the plaintext to the ciphertext results in a repeating sequence (or one with a clear pattern)
(VERY WEAK)
2. XORing the plaintext to the ciphertext results in a non-repeating random sequence
2a. ...but this sequence is the same for any plain-cipher pair
2b. ...and this sequence is unique for any plain-cipher pair
3. Blocks of plaintext data (often 16 bytes) that are equal get mapped to equal ciphertext blocks
The smaller the blocksize is, the more relevant this property is.
4. Equal plaintext files get equal ciphertext files
5. Plaintext results in a ciphertext of equal size (pretty common)

Assuming you get your hands on some plaintext-ciphertext pairings, most of these properties are pretty
easy to check so I'd recommend doing so.
If your properties are:
XORing results in no usefull data (2b)
Equal blocks don't necessarily map the same (not 3)
Equal files map the same (4) (hard to check though)

Then you are probably dealing with AES in CBC mode.


I'll go over AES in more detail than you probably want in the next section.
The mini-summary is:

 To decrypt a file you need the key.


 To fully decrypt files you also need to know the IV and how it deals with non-block aligned data,
but those two are relatively simple to figure out.
 AES is a very secure standard. You won't figure out any useful information on the key by looking
at plaintext-cipher pairs. The key is also too long to brute force (assuming it got generated in a
random way).

AES, how does it work?


-----------------------------
AES uses a key length of 128, 196, or 256 bits.
It has a block size of 128 bits.

Let examine AES in two levels of detail:

AES Level 1:
----------------
Let's just abstract away the details of how a block gets processed and view it as two functions:
encrypt(key,plain-block) = cipher-block
decrypt(key,cipher-block) = plain-block
(where decrypt(key,encrypt(key,x)) = encrypt(key,decrypt(key,x)) = x)

It's important to remember that encrypt/decrypt only works on binary sequences that are exactly 128 bits
long (the block size)
The main two modes of AES, ECB & CBC, that I will go over take the general approach of breaking the
input up into block-sized chunks and running encrypt/decrypt on each chunk.
But what if the input sequence is not divisible by the block size? Then you have some 1-127 bits leftover
that still need to be dealt with.

Few ways to deal with this:

1. Just don't encrypt it lol


This is a dumb but simple approach. It's bad on a matter of principle since encryption-schemes are
meant to encrypt, not leave plain spots. However in some situations it may not matter. Knowing the
last 127 bits of an exe doesn't get you very far if your goal is to run it. That said, I've never seen this
approach used in the wild.

2. Use a different encryption scheme for the last part that does not have the block-size restriction
There are a lot of encryption schemes out there that don't have the block-size restriction, including
some variants of AES.
Often encryption schemes aren't too complicated when run on very short sequences of data. The
whole system may simplify down to XORing the remaining X bits with the first X bits of a static
sequence.

3. Pad the data so that it fills the block


There are a couple of padding schemes out there, but they all boil down to the general idea of: Pad
with bytes and make the last byte indicate how many bytes of padding there are.
Note that with padding schemes that don't rely on externally stored data, the data will always be
padded even if the plain-text is divisible by the block size. This is because the last byte is reserved
to indicate how much padding there is, it can't be part of the plain-text as well.

Right, so assuming the data has been adjusted so it can be represented as a sequence of blocks, how do we
encrypt it?
(For the following sections plainX means "Xth block of plaintext" and cipherX means "Xth block of
ciphertext")

ECB Mode
--------------
For AES using ECB mode:
cipher1 = encrypt(key,plain1)
cipher2 = encrypt(key,plain2)
...
cipherN = encrypt(key,plainN)

So just encrypt each block separately.

This has the weakness that identical blocks will always get encrypted the same way. If you have access to
some plain-cipher pairs then you may be able to partially decrypt a different file by seeing which cipher
blocks match ones from your known set.
You also know that if two cipher files have equal cipher blocks in a location, then the plain-text files are
identical for those blocks.
That said, if your goal is to decrypt the whole file, ECB mode doesn't really help you. You're still gonna
need the key.

For practical purposes ECB mode is useful since for one-block inputs it is identical to the underlying
encrypt/decrypt functions upon which the other AES modes are built.
CBC Mode
--------------
Unlike ECB, AES in CBC mode requires an additional parameter called an IV (initialization vector).
The IV is always 1 block long regardless of the key size.

CBC works like this:


cipher1 = encrypt(key,plain1 ^ IV)
cipher2 = encrypt(key,plain2 ^ cipher1)
cipher3 = encrypt(key,plain3 ^ cipher2)
...
cipherN = encrypt(key,plainN ^ cipherN-1)

Since every block (expect for the first one) uses the output of the previous block, cipherN depends on
every plain-text block that came before it as well as the IV. This means that two inputs will only get
identical outputs for as long as they are identical, after the first deviation they will look completely
different (doesn't matter if they re-converge later).

However from a cracking perspective, this scheme isn't really harder to deal with than ECB.
The decryption pattern looks like this:
plain1 = decrypt(key,cipher1) ^ IV
plain2 = decrypt(key,cipher2) ^ cipher1
plain3 = decrypt(key,cipher3) ^ cipher2
...
plainN = decrypt(key,cipherN) ^ cipherN-1

Since you have access to all the cipher blocks, the chained nature of the algorithm doesn't really
complicate your computations.
A happy little fact of life is that if you use the wrong IV only the first block will be incorrect. This means
you can hunt for the key before worrying about the IV.

Once you have the key you can determine the IV as long as you know the first plain-text block of any file.
Many file types start with a consistent header that takes up the first block, so in many cases you can deduce
the first block by just looking at the extension.
The result you get from your incorrect decryption of cipher1 is:
decrypt(key,cipher1) ^ wrongIV

with an IV of 0, this is just:


decrypt(key,cipher1) = plain1 ^ IV

So just XOR your value with the known plain-text to get:


decrypt(key,cipher1) ^ plain1 = plain1 ^ plain1 ^ IV = 0 ^ IV = IV

Simple.
It's for this reason that the IV is not considered to be a second "key", just a parameter.

AES Level 2:
---------------
So how does the encrypt/decrypt function actually work?

To encrypt/decrypt blocks AES operates in "rounds".


The number of rounds depends on the key size.
128bit/16byte -> 10 rounds
192bit/24byte -> 12 rounds
256bit/32byte -> 14 rounds
Let's call the number of rounds 'X'
Each round uses a 128bit key, also called the "round-key".
At the start of the algorithm, the key gets expanded into X+1 128bit keys.
These keys are also called the "expanded" key which can be viewed as a 176 byte, 208 byte, and 240 byte
key respectively. However this expanded key only gets accessed in block sized chunks so I think it's more
natural to think of it as a list of X+1 separate keys.
I won't cover the method by which the key gets expanded here.

The pseudo-code for the encrypt function looks like this:

Code:
block = block ^ keys[0]
for i in [1,2,...X-1]:
// Round i
block = subBytes(block)
block = shiftRows(block)
block = mixColumns(block)
block = block ^ keys[i]

// Round X which is slightly different


block = subBytes(block)
block = shiftRows(block)
block = block ^ keys[X]

return block

The decryption function looks like:

Code:
block = block ^ keys[X]
for i in [X-1,...2,1]:
// Round i
block = inv_shiftRows(block)
block = inv_subBytes(block)
block = block ^ keys[i]
block = inv_mixColumns(block)

// Round X which is slightly different


block = inv_shiftRows(block)
block = inv_subBytes(block)
block = block ^ keys[X]

return block
Note on possible confusion:
At first glance it might look like decryption is not symmetric since the looped code does not read:
Code:
block = block ^ keys[i]
block = inv_mixColumns(block)
block = inv_shiftRows(block)
block = inv_subBytes(block)
but that is just because its still formatted as loop + extra. If you read the operations for encryption
backwards you'll see it all matches up.

The sub-operations are stateless and don't depend on the key.


The 16-byte block is viewed as a 4x4 byte matrix and:
subBytes -> Remaps byte values
shiftRows -> Fucks with each row
mixColumns -> Fucks with each column

I won't go into more detail than that on the sub-ops (that would be level 3). Google it if you wish to know
more.
Now you might be wondering: That's great and all, but how does this affect my cracking?
Two ways:
1. If your digging through code, it's good to have a general idea of what the AES algorithm looks like so
you can recognize it when you encounter it
2. When an AES encryption is taking place, you should be able to find the expanded key in memory.
Knowing how to reliably get the base key from the expanded key requires you to understand this.

So, you've found an expanded key in memory, how do you get the base key?
For a normal expanded key, the base-key is just the first X bits where X is the length of the base key.
When decryption is performed, the expanded key may be loaded in reverse block order, so you need to
grab the last blocks, not the first.

This is all super simple UNLESS the algorithm implements the "equivalent inverse cipher" variant.

In that case the decryption function looks like:


Code:
block = block ^ keys[0]
for i in [1,2,...X-1]:
// Round i
block = inv_subBytes(block)
block = inv_shiftRows(block)
block = inv_mixColumns(block)
block = block ^ keys[i]

// Round X
block = inv_subBytes(block)
block = inv_shiftRows(block)
block = block ^ keys[X]
Almost the exact same as the encryption function! Neat!

However to make this work, some adjustments need to be made to the expanded key.
1. The keys are accessed 0->X, so the order of keys needs to be reversed to compensate.
2. In the repeated round, the XOR operation is moved after the mix-columns step.
Luckily this sub-op has the property that:
inv_mixColumns(x ^ y) = inv_mixColumns(x) ^ inv_mixColumns(y)
So if we pre-apply the inv_mixColumns sub-op to all key values other than the first and last we'll get the
same result.

So to obtain the base key from such an expanded key:


1. Reverse the block order
2. Perform mixColumns() on all but the first and last key
(Find an AES implementation that lets you perform the sub-ops manually. If you're feeling adventurous
you could also write your own.)
3. Read the first Y bits (where Y = key length)

Note:
If you're dealing with a 128bit key, it's just the last block in the expanded key. Don't even need a program
for that one.

This process can be further complicated if the algorithm uses a different representation for the 4x4 abstract
matrix.

I think it's most common that for a block with the bytes x0, x1, x2, ... the 4x4 matrix is:
x0 x1 x2 x3
x4 ...
x8 ...
x12 ..

But for some the interpretation is:


x0 x4 x8 x12
x1 ...
x2 ...
x3 ...

This changes the result of the mixColumns operation.


If there's a mismatch between the representation used by the target application and the implementation you
are using you will need to convert the blocks before and after passing them to the mix-columns function
(or modify the mix-column function directly).

Pro tip when dealing with AES


--------------------------------------
There are assembly instructions for doing AES operations.
These instructions start with "660F3". Just ctrl-F that shit and you have a good chance of finding a critical
function.

Determining if a file is encrypted


------------------------------------------
"Proper" encryption system will result in output that looks completely random.
Every byte value will have a 1/256 chance of occurring. The byte probability distribution should be
completely flat.
This is something you can check with a tool or even just eyeball.
If the data looks random, it's probably encrypted (or compressed).

A simple algorithm you could use to assign a "randomness" score to a sequence is:

Code:
// Initialize with 0's
byte histogram[256];

for byte in data:


histogram[byte] += 1

score = 0
for count in histogram:
score += (count/len(data) - 1/256)^2

return score
This will result in a score of 0 if the probability distribution if perfectly flat and higher scores the more it
deviates from the norm.

Note:
You can only get a good estimate of the probability distribution for large files. Determining if a short
sequence of binary data is encrypted is very difficult.

A brief note about brute-forcing


-----------------------------------------
I'd consider brute-forcing to be anything where you systematically try a whole bunch of options to find the
solution.
Brute-forcing a problem can be very effective and let you advance quickly. Just do a bit of math so you
know which problems are manageable. Systematically trying every possible encryption key will never be
feasible if a sane key length is being used. However if you discover that the key has some sort of pattern
(maybe it's only ascii encoded lowercase characters) then maybe brute-forcing it is an option. If you know
that a key is located somewhere in a file, just try every possible offset. The number of possibilities only
grows linearly with the file size.
Tiers of difficulty
----------------------

Tier 0: Games that don't use any form of online verification

It's pretty hard to have a DRM system that does not require an internet connection, so projects under this
category are more likely to deal with extracting encrypted assets than removing DRM.
Assuming you have all the files that were present during the game's installation, this type of project is
always theoretically crackable. All the information is there, you just need to deal with obfuscation.

Tier 0.5: Games that send web-requests, but the web-requests don't contain vital* data

This could be because the DRM system is kind of dumb, or because the game has optional online features
(most games that use the steam API don't really *require* it)
If the remote server does not hold data that you need, then you can cut it out of the equation. Either by
having the game contact a fake server you control, tricking the game into thinking an incorrect server
response is valid, or bypassing the web-request code entirely.

Although on a fundamental level tier 0.5 is very similar to tier 0, unlike tier 0, tier 0.5 apps usually won't
run on their own without valid authorization. So unless you own the app, you have a lot less information to
work with.

Tier 1: Games that use online verification upon the initial installation (and the server sends vital data)

There are roughly 3 ways to approach such a project:

1. Replicate the credentials of an authorized user (account sharing basically)


I wouldn't recommend doing this because it can result it the account being banned (bad for the game
donor), and once the account is banned the "crack" is broken as well.

2. Get the server to accept a set of fake credentials. Preferably ones that can be randomly generated. (the
keygen approach)
This approach only works if the people in charge of the security system fucked up somewhere. Can work,
but it's not reliable.

3. Pass the authentication using valid credentials and then replicate the game's functions such that it can be
run independently.
Depending on how interwoven the DRM system is with the game's code, the "replicate" step could be quite
difficult, but in most cases the DRM system is just a light layer on top.
With this approach you normally need to decrypt the game's files and sometimes replace the .exe used to
launch the project.
For a lot of game engines, the .exe is fairly interchangeable so it's just a case of decrypting the files.

This approach is the most reliable and should always be theoretically possible.
But it does require you to possess valid credentials, which normally means buying the game.

Tier 2: Games that send continuous web-requests that contain vital data.

Multiplayer games fall under this category. If a game uses multiplayer interactions as a primary source of
content it would take an enormous amount of effort to crack since you'd have to replicate all
undocumented server functions and host your own servers for people to use. For most intents and purposes
I'd consider these to be uncrackable.

If a game is single-player but is hosted online, the difficulty depends on how complex the game is.
For example, if it's just a collection of static web pages, then iterating over all of them and storing the data
offline should be feasible. Same deal if the webpages just run client-side scripts. If the game relies on a lot
of server-side scripts then you have a problem. You can only observe the inputs and outputs from the
server and thus can only guess at what the inner mechanics are.

*By "vital data" I mean data that is necessary for the game experience and can't be easily
guessed/replicated
This could be an encryption key or just part of the game's content, like chapters or CG images.

General Approaches: Replication vs Zombie Processes


---------------------------------------------------------------------
There are roughly two approaches to cracking problems:
1. Remove the security system and replicate any necessary behaviors
2. Trick the security system into doing what you want

For most situations I would highly recommend the second approach because it is normally way easier than
reverse engineering the underlying logic. In the case of decryption, you don't need to know what the
encryption method is or what the keys are.

The advantages of replicating behavior is:


- Once setup, you don't have to work around the behaviors of the security system
- It is more simple and elegant solution since you are only performing the necessary operations
- You learn way more about how the system works, which is good because:
- You may identify weaknesses you would have otherwise missed
- Learning can be inherently enjoyable and makes future projects easier

Miscellaneous Stuff
------------------------

Super Hidden Files


------------------------
You're (hopefully) aware of hidden files and how to show those.
What you might not be aware of is "system files" which require a second option to show. To show system
files, go into a folders "View" options and uncheck "Hide protected operating system files". You should
now see "pagefile.sys" on your C drive for example.
These files are hidden for a reason though. If you're not actively working on something that requires you to
see them I'd recommend keeping them hidden so you don't accidentally delete something.

Manually editing .exe & .dll files


----------------------------------------
Under normal circumstances string constants used by .exe's and .dll's will be stored somewhere in memory.
You can often replace these constants with a different string of equal length without breaking the program.

Example 1:
In a real recent example a program was contacting a DRM server to verify ownership. The server URL was
stored in the exe. Using a hex editor I was able to change it to instead contact a localhost server I had
control over. A quick, dirty, and simple way to perform request redirection.

Example 2:
Let's say you found a string in the exe/dll that looks like it could be a decryption key.
You could try changing it and seeing if the encryption breaks.
If the program breaks it could be for a lot of reasons, but if it doesn't immediately break you can rule that
string out as an encryption key. (unless the key is present in multiple locations)
Note:
Programs often perform some sort of integrity check of used files which makes manual edits more
difficult.
Which brings me to the next point

Bypassing file integrity checks


---------------------------------------
In some situations if you know *when* a file integrity check is going to take place, you can hook the file-
open method and redirect it to an unmodified file that passes the check.

Extended debugging tool check


----------------------------------------
I've encountered one situation where a program would not run because it detected a debugging tool active
(process monitor). I was able to get around that by just renaming the debugging program's exe to
something else.

Disassembling Unmanaged Code


-----------------------------------------
There are tools for disassembling unmanaged code back into C but I wouldn't recommend using them. All
the times I tried disassembling unmanaged code the result was too unwieldy to be of any use. You'll
probably get a 20k line file full of functions with 10-20 arguments (everything unnamed of course) and
every variable performs a reinterpret_cast every other line.
You're much better off just looking at the assembly. That way you can also observe the code in action.

Getting started
-------------------

For getting started there are roughly two options:

1. Original Work
---------------------
1a) The illegal way
Look through the cracking forum for a post that interests you and has the uncracked files uploaded. (or buy
the game yourself)
Download the files and start seeing what you can figure out!

1b) The legal way


Ask a developer if you can do penetration testing on their project. Once you get good you may be able to
charge money for this, but to start out with I'd offer to do it for free for the sake of getting experience. Part
of this deal is that once the project is done you WONT release anything publicly and instead just report
your findings to the dev with tips on how they can make it more secure.
The developer may not trust you with their game files and say no, but that is the price of taking the high
road. Just keep at it.

The advantage of doing completely original work is that it's the real deal. You won't find any solutions
online that tell you how to solve this specific case. It's just you and your wits against the program. It's very
satisfying if you're able to make progress knowing that you've broken new ground so to speak.

The disadvantage of doing original work as a beginner is that you have no idea how difficult it is going to
be. You could get lucky and get something simple, or you could stumble across something completely
outside of your skill range.
Even if the project is outside of your skill level, you should still be able to make some progress and at least
learn more about how the system works and learn new skills in the process. But it is more fun when you
can make some major strides it breaking the security.

2. Redoing cracked projects


-----------------------------------
If you just want to learn, I can recommend you some projects which I have already cracked. That way I
have an idea of how difficult it will be and can give you something simple to start out with. I can also give
you some hints on those projects if you get stuck.

The obvious advantage of this is that you'll get manageable tasks to deal with and have some support if you
need it.
The drawback is that it's less satisfying to defeat something when you had outside help and know it doesn't
contribute anything to our lovely community. Also since it's already cracked you might stumble across an
exact solution that spoils the problem.

If you're interested I can dig up some project files from my PC and upload them.
Alternatively, some projects you could try which are currently publicly available:

- White Wings (the uncracked files should still be available)


This is a project I did recently.
It can be cracked without owning or having access to any of the uncracked files.
It has multiple layers of protection which makes it quite interesting to examine.

- The DLSite DRM systems


DLSite actually uses a few different systems. The most common one involves a login prompt when you
first boot up the game and is characterized by a file called startup.exe and a bunch of dll files with a "ct"
prefix.
As one of my first major projects I made a tool that can remove the DRM protection for games you own.
Figure out how I did it or find an alternative way to accomplish the same result.
(Note: This will require you to purchase at least one dlsite game with this protection. The cheapest such
game I am aware of is RE141151 for $3)

For the other DLSite DRM systems, some are easy to crack, some are hard. I think the serial-code one may
even be uncracked at this point? Not sure.

Conclusion
--------------
That's all I have to say on this topic.
Reading this guide may be like cheating in some ways. I had to figure this stuff out the hard way and here I
am just giving you the answers. That said, most of this information is scattered on the web. I just collected
it in one place.

I've found one difficulty with cracking is that while there is a lot of information on security systems out
there, little of it is written for the perspective of an attacker, so hopefully this guide helps.

Let me know what you think in the comments and whether there's an area you'd like extra information on.
 

Attachments

 InjectionExample.zip

178 KB

Reactions: K3NJ1, jamie lannister, strelokhalfer and 4 others


Like Quote Reply
Report

erri120

Newbie

Jul 11, 2020


22
24
Oct 27, 2020

Vous aimerez peut-être aussi