GemBox Keygen - Modulo ist das Schlüsselwort

Lange ist hier nichts geschehen, ich hoffe trotzdem dass der eine oder andere treue Leser mitbekommt dass ein neuer Artikel online ist. Ich verspreche dass spätestens ab dem 15. Januar wieder mehr Posts kommen werden. 15. Januar aus dem Grund, da dort die Cyber Security Challenge Germany endet. 13 Aufgaben sind bei der Challenge online gestellt und zehn davon wurden bereits von mir gelöst. Diese Lösungen erforderten zum Teil viel Denk/Programmierarbeit, daher werden auch ein paar als Lösungen in diesem Blog verewigt. Gerade in den Gebieten Crypto und Forensik musste ich mich durchbeißen, daher ist ein Blogpost zum wiederholen der Materie ziemlich gut. Und um generell CTF Writeups zu üben ist heute ein kleiner Serial-Keygen dran.

Auf der Suche nach einem C# Wrapper für ein Word Dokument, welcher nicht Office.Interops verwendet, stiess ich auf GemBox.Document. Diese Library hat alles geboten was ich brauchte. Wäre es denn nur nutzbar gewesen. Denn nach 20 Dokument-Elementen sagt die Software:

Free version limitation has been exceeded (Free version is limited to 20 paragraphs)

Das Interesse war geweckt und mit der Zeit stellte sich auch herraus, dass dieser leichte, aber nicht triviale Serial-Algorithmus perfekt für den Blog als Writeup geeignet ist. An dieser Stelle ein Danke an die GemBox-Entwickler ;) Beim Laden der Library in ein .NET Obfuscator unserer Wahl stellt sich schnell herraus, dass die internen Methoden obfuscated sind. De4Dot schafft wie immer Abhilfe und macht zuverlässig alle Verschleierungen des “Eazfuscator.NET 3.3.149 - 3.4″ rückgängig. Die Methoden- und Typnamen können zwar nicht wiederhergestellt werden, aber dieser Luxus ist eh die Ausnahme.

Um die Library verwenden zu können muss die Komponente über ein “ComponentInfo.SetLicense(“0000-0000-0000-0000″)” registriert werden. Wenn der Serialkey dem Format, welches später besprochen wird, entspricht, so ist die Komponente für die Verwendung freigegeben. Andernfalls wird eine Exception geworfen. Unsere Suche fängt demnach bei der SetLicense-Funktion an.

public static void SetLicense(string serialKey)
{
	ComponentInfo.class919_0.method_0(serialKey);
}

Nicht sehr aufschlussreich, es wird auf eine interne Klasse verwiesen. Diese wird später noch interessant, merk euch also den Namen Class919. Die Funktion method_0 macht nichts anderes, als den Lizenzkey in den Klassenmember “string_0″ zu kopieren (merken!) und danach die Funktion wieder zu verlassen. Über ILSpy und das Analyse-Fenster finden wir Methoden, welche den Lizenz-Key wieder lesen. Und dieses sind zum Glück nur zwei, von denen eine die gerade schon gefundene Methode zum setzen des Lizenzkeys ist. Bleibt also die andere Methode, welche ziemlich direkt unser Lizenzcheck ist.

serial1

Die Ausgabevariablen haben uns erst später zu interessieren, sie sind trotzdem schonmal vorgehoben. Ansonsten können wir nur entnehmen, dass wenn der Lizenzkey mit einem “D” beginnt, der Key an die Methode Class132.smethod_1 übergeben wird, sonst an die Methode Class132.smethod_3 . Da der letztere Fall symapatischer Aussieht, wurde erst dieser Weg genommen.

public static char smethod_3(string string_0)
{
	int num = 0;
	for (int i = 0; i < string_0.Length; i++)
	{
		int num2 = Class132.smethod_6(string_0[i]); // ASCII Char to Index
		if (i % 2 == 1)
		{
			int num3 = num2 * 2;
			num += num3 / 36 + num3 % 36;
		}
		else
		{
			num += num2;
		}
	}
	return Class132.smethod_5(num % 36);
}

Dieser Sourcecode ist nicht weiter Wild. Und bei genauem Hinschauen findet man bereits die Modulo-36 Operationen die im Titel erwähnt sind ;) Der ganze String wird durchlaufen und daraus wohl eine Checksumme gebildet (int num). Bei jedem zweiten Durchlauf ( = wenn die Indexvariable i durch 2 Teilbar ist), so wird einfach der konvertierte ASCII-Character zu der Checksum addiert. Die Konvertierung ist hierbei, dass eine ASCII Zahl (z.b. 0 = 0×30) zu einer echten Zahl als Integer gemacht wird. Ein späteres Beispiel wird das verständlicher machen. Bei jedem zweiten Durchlauf wird die aktuelle Zahl (also num2) erst mit 2 multipliziert und dann der eine Division durch 36 mit und ohne Rest durchgeführt. Da wir uns hier auf Zahlen von 0 bis 9 beschränken, wird also nur der Rest auf die Checksum aufaddiert. Nach durchlaufen des Lizenzschlüssels wird der durch 36 geteilte Rest dieser Checksum einer weiteren Funktion übergeben:

private static char smethod_5(int int_0)
{
	if (int_0 > 9)
	{
		return (char)(65 + int_0 - 10);
	}
	return (char)(48 + int_0);
}

Auch hier ist nichts wildes dabei. Wenn der Rest der Checksum > 9 ist, so wird ein ASCII Character (welche ab 65 beginnen) zurückgeliefert, sonst eine Zahl als ASCII Character (welche bei 48 = 0×30 beginnen). Schauen wir nochmal auf die Anfangsbedingung: Diese Funktion muss ein ‘R’ Character zurückgeben, was laut ASCII-Tabelle einem 0×52 = 82d entspricht. Über Grundschulmathe wird schnell klar, dass der Parameter int_0 in diesem Fall 27d entsprechen muss. Damit muss also gelten: Checksum % 36 = 27!

Jetzt kommt der Part an dem man etwas überlegen muss: Wie Konstruiert man sich so eine Checksum? Nunja, es wird abwechselnd der Wert des aktuell durchlaufenden Buchstabens mit 1 oder mit 2 multipliziert. Der einfachste Fall ist also, dass wir eine Checksum erreichen die genau 27 ist, denn 27 % 36 = 27! Für den ersten von 4 Blöcken können wir zum Beispiel nehmen:

[0] 0 * 1 = 0
[1] 5 * 2 = 10
[2] 0 * 1 = 0
[3] 5 * 2 = 10
-----------------
            20

Damit haben wir schon 20, brauchen wir noch die 7 obendrauf:

[0] 1 * 1 = 1
[1] 0 * 2 = 0
[2] 1 * 1 = 1
[3] 0 * 2 = 0
[0] 1 * 1 = 1
[1] 0 * 2 = 0
[2] 1 * 1 = 1
[3] 0 * 2 = 0
[0] 1 * 1 = 1
[1] 1 * 2 = 2
[2] 0 * 1 = 0
[3] 0 * 2 = 0
-------------------
            7

Und damit als Serial: 0505-1010-1010-1100! In der Annehme der Serial-Mechanismus geknackt zu haben wurde die Serial eingetragen. Und sogar akzeptiert. Aber leider haben wir nur eine Probeversion erreicht. Deswegen sah also der andere Weg des Lizenzchecks so kompliziert aus :D

Guter Dinge schauen wir uns also den anderen Lizenzcheck an, denn die bisherige Vorarbeit war keineswegs umsonst! Interessant ist zunächst nur folgender Funktionsabschnitt in einer Unterfunktion. string_0 ist dabei wieder unserer Lizenzkey.

string string_ = string_0.Substring(0, string_0.Length - 1);
if (Class132.smethod_3(string_) != string_0[string_0.Length - 1])
{
	throw new Exception4("Long key has wrong checksum.");
}
return string_0.Substring(1, string_0.Length - 2);

Es wird der Lizenzkey ausgenommen dem letzten Buchstaben kopiert und dann die gerade eben beschriebene Berechnung eines Buchstabens durchgeführt. Dieser berechnete Buchstaben muss nun dem letzten Buchstaben des Lizenzkeys entsprechen. Diese Checksum können wir also nun easy schonmal abhaken. Ab jetzt wurde es etwas wilder, daher wird der restliche Algorithmus nur noch oberflächlich besprochen. Der nochmal gekürzte Lizenzkey wird einer weiteren Funktion übergeben, welche wie im folgenden Source gezeigt nochmal alle Buchstaben durchläuft und miteinander verrechnet. Auch hier treffen wir den alten Bekannten Modulo 36 wieder.

char[] array = new char[string_0.Length - 1];
for (int i = 0; i < array.Length; i++)
{
	int num2 = Class132.smethod_6(string_0[i + 1]);
	int num3 = @class.vmethod_3(0, 36);
	array[i] = Class132.smethod_5((36 + num2 - num3) % 36);
}
string string_3 = new string(array, 0, 1);
enum34_0 = (Class132.Enum34)Class132.smethod_4(string_3);
string string_4 = new string(array, 1, 2);
int_0 = Class132.smethod_4(string_4); // WICHTIG!

Ein Array wird also durch wildes rumrechnen und verrechnen gefüllt. Für uns ist nur von Bedeutung, dass am Ende der Ausgabe-Parameter int_0 geschrieben wird. Und zwar mit dem Wert an Stelle 1 des Arrays und dort die nächsten beiden Zeichen. Dieser Wert wird später mit der Zahl 150 vergleichen und muss ihr entsprechen. Genug Informationen um zu Brute-Forcen!

Für jeden per Brute-Force erstellten Key brauchen wir auch eine passende Checksum, aber die wissen wir ja inzwischen zu berechnen. Daher war die Vorarbeit von dem Trial-Key nicht umsonst. Gebrutet werden nur die ersten 4 Stellen, der Rest des Keys beeinflusst die Rückgabe-Zahl ja nicht. Soweit zur Theorie, doch in der Praxis müssen wir zum Bruten der Serial auch einen Lizenzcheck durchführen. Natürlich könnte man den gesammten Sourcecode der Library exportieren und in das eigene Projekt einfügen, aber so richtig elegant ist das nicht. Von möglichen Fehlern die beim decompilen entstehen mal abgesehen. Nutzen wir doch einfach die internen Klassen der Library. Wie der Name allerdings schon sagt sind diese Intern und eigentlich nicht zu erreichen. Aber wo ein Wille, da ist auch ein Weg, in diesem Fall nennt sich der Weg System.Reflection. Über Reflection haben wir Zugriff auf private Unterklassen und deren private Klassenmember und Methoden. Ihr erinnert euch, dass man sich Class919 und string_0 merken sollte? Nunja, hier wird es gebraucht:

var gemBoxAssembly = Assembly.Load("GemBox.Document");
Type class919 = gemBoxAssembly.GetType("Class919");
Object serialClassInstance = Activator.CreateInstance(class919);
FieldInfo serialKeyField = serialClassInstance.GetType().GetField("string_0", BindingFlags.NonPublic | BindingFlags.Instance);
serialKeyField.SetValue(serialClassInstance, "0505-1010-1010-1100");
 
// Serial Check Methode
MethodInfo checkMethod = serialClassInstance.GetType().GetMethod("method_1", BindingFlags.Public | BindingFlags.Instance);

Die Magie läuft folgendermaßen ab: Wir holen uns den Type der Klasse “Class919″ und erstellen über diesen Typ ein Objekt dieses Types. Die Binding-Flags sagen dabei, dass es sich um einen privaten Typen handelt welcher nicht statisch ist. Da dieser interne Typ C# nicht bekannt ist, haben wir auch keinen Zugriff auf die Member beziehungsweise können das erstellte Object nicht auf diesen Typen casten. Also müssen wir das Feld “string_0″ via Reflecion abrufen und setzen und zu guter letzt auch noch die Methode “method_1″ abrufen. Nun sind wir in der Lage die Methode aufzurufen mit den gesetzten Lizenzwerten.

Zum Brute-Forcen wurde eine schöne und schnelle Klasse gefunden, welche ein Charset, sowie eine Stringlänge entgegen nimmt. Bei jedem erstellten String wird eine Callback-Methode aufgerufen die die Serial prüft. Sollte die Serial gültig sein, aber keine richtige Lizenzserial sein, so wird die Library wieder nur als Testversion aktiviert. In diesem Fall (siehe Grafik oben) ist die Differenz der beiden Rückgabewerte exakt 150. In jedem anderen Fall haben wir eine gültige Serial gefunden.

var dataToTest = new string(test);
var serialToTest = "D" + new string(test) + "0009999000";
var additionalChar = smethod_3(serialToTest);
serialToTest += additionalChar;
 
 
serialKeyField.SetValue(serialClassInstance, serialToTest);
var result = checkMethod.Invoke(serialClassInstance, parameters);
if (Convert.ToInt32(parameters[1]) - Convert.ToInt32(parameters[2]) != 150)
    return true;

Bingo, die erste Serial ist nach 0,03 Sekunden gefunden, ganze Listen können ganz einfach erstellt werden. Es gibt hier keinen vollen Sourcecode des Programms, aber wer sich damit ernsthaft beschäftigt kann selbst ein Keygen erstellen.

Hoffe ihr habt wiedermal etwas über Serials und .NET Internals gelernt und wisst nun, wie ihr eigene Programme NICHT zu schützen habt ;)

Greez

5 people like this post.
  1. Hey, da ich weiß das du an Reserve Eng. spaß hast hab ich mal ne Frage.
    Kannst du uns und denn Leuten Europaweit helfen und dieses Problem zu lösen?
    https://gsmfreeboard.com/showthread.php?t=225640
    Falls irgendwelche Fragen da sind meld dich :)
    Der Easysurf Logo ersteller :D

      • Easysurfer
      • 25. Dez. 2014 1:39pm

      Hatte schon deinen Thread auf Coderz gesehn. Hab bei Firmware RE keinerlei Erfahrung und auch kein TomTom Gerät um solche Analysen zu machen, daher kann ich euch leider nicht helfen. Aber trotzdem viel Erfolg.

  1. Noch keine TrackBacks.