Encryption breaking des PSC Managers

Da ich fast 2 Stunden daran rumgetüftelt habe verdient dieses Programm auch ein Blogeintrag. Also wird es in diesem Post über meine Vorgehensweise beim “cracken” des PSC Managers von dem User Tasty gehen.

In dem Programm wurde eine normale Rijndael-Verschlüsselung beim Speichern und Auslesen der PSCs verwendet. Und das ganze wäre keine Herrausforderung gewesen, wenn nicht der Obfuscator sehr gute Dienste geleistet hätte. Denn es wurden nicht nur die Methodennamen unleserlich gemacht, sondern auch jeder String aus einer Resourcendatei ausgelesen und nach mehreren Bitshifting-Operationen schließlich zurückgegeben. Ein kleiner Ausschnitt der Datei:

            if ( == null)
            {
                if (frame == null)
                {
                     ^= 219315;
                }
                bool flag = type == typeof(RuntimeMethodHandle);
                 ^= 160;
                if (!flag)
                {
                    flag = type == null;
                    if (flag)
                    {
                         ^= 219283;
                    }
                }
                if (flag == (trace != null))
                {
                     ^= 32;
                }
                 ^= 6502 | (skipFrames + 1);
                 = new BinaryReader(manifestResourceStream);
                short num2 = (short) (.ReadInt16() ^ ((short) -~-~~--~~-~21694));
                if (num2 == 0)
                {
                     = (short) (.ReadInt16() ^ ((short) (~--~-~~--~~1534425040 ^ -1534407854)));
                }
// ...
int count = (.ReadInt32() ^ num3) ^ -~-~-~~-~1446402469;
            if (count == -2)
            {
                byte[] buffer2 = .ReadBytes(4);
                 = -1446402468;
                 = (((buffer2[2] | (buffer2[3] << 16)) | (buffer2[0] << 8)) | (buffer2[1] << 24)) ^ -;
                goto Label_000D;
            }
            bool flag2 = (count & -2147483648) != 0;
            bool flag3 = (count & 1073741824) != 0;
            count &= 1073741823;
            byte[] buffer3 =  .(buffer, .ReadBytes(count));
            if (( != null) != ( != 1607814))
            {
                for (int i = 0; i > 5));
                    buffer3[i] = (byte) (buffer3[i] ^ num7);
                }
            }

Diesen Ansatz den Hardcoded String heraus zu bekommen konnte ich also vergessen. Nicht aber, mir den String ausgeben zu lassen wenn er schon lesbar und “entschlüsselt” im Stack ist! :D Aber zuerst einmal ein Bild wie die Funktionen aufgerufen werden:

Im Grunde genommen brauchte man also nur die beiden Parameter auf dem Stack ausgeben lassen und schon sollte man glücklich sein. Doch leider war es nicht sooo einfach:

public string Decrypt(string Text, string Key) // Decrypt Funktionen, Namen der Parameter geändert
{
    byte[] rgbIV = new byte[] { 121, 241, 10, 1, 132, 74, 11, 39, 255, 91, 45, 78, 14, 211, 22, 62 };
    RijndaelManaged managed = new RijndaelManaged();
    byte[] buffer = Convert.FromBase64String(Text); // Der Base64-Codierte Text wird eingelesen
    if (Strings.Len(Key)

Genau diese If-Else hat mich zum Wahnsinn gebracht… Denn wenn die Keylänge unter 32 Zeichen war, so wurde wohl ein weiterer String mit einem Zeichen drangehängt, welcher wieder aus der Ressourcen geladen wird. :/ Aber zurück zu den ersten beiden Parametern Text und Key, welche wir ja herrausbekommen wollten. Also, mit Reflexil war schnell ein Patch geschrieben:

	0	nop	// Funktionseinstieg
	1	ldarg.1	// Wir pushen das zweite Argument auf den Stack
	2	ldarg.2	// Wir pushen das dritte Argument auf den Stack
	3	call	System.Windows.Forms.DialogResult System.Windows.Forms.MessageBox::Show(System.String,System.String) // Wir rufen MessageBox.Show(String,String) auf.
	8	pop	// Das Resultat ist im Stack, also poppen wir dieses
	9	ldc.i4.s	16 // Hier fängt die eignetliche Funktion an.
	11	newarr	System.Byte

Viele werden sich wundern, warum denn der zweite und dritte Parameter genommen wird. Die Antwort ist, das im ersten Parameter (arg0) der This-Pointer übergeben wird. Daher muss man arg+1 rechnen.

Doch sobald ich das gepatchte Programm ausführte, wurde einfach nichts mehr aus der Datei gelesen. Der Grund dafür konnte nur der Try-Catch Block aus der Form1.OnLoad() sein, und diesen galt es jetzt zu entfernen dass ich eine Fehlermeldung erhalten konnte.

private void OnLoad(object Sender, EventArgs e)
{
    try
    {
        this.HelperClass.ReadAndDecrypt(); // Liest das File, decrypted und fügt es in die LVW ein.
    }
    catch (Exception exception1) // Diese galt es zu entfernen.
    {
        ProjectData.SetProjectError(exception1);
        Exception exception = exception1;
        ProjectData.ClearProjectError();
    }
}

Doch mit rauspatchen der IL-Codes war es nicht getan, da die Exceptions nochmal extra vermerkt sind. Nachdem ich den Exception-Tab gefunden hatte war die Exception also auch vollständig enfernt. Doch die Fehlermeldung begeisterte mich noch weniger:

Die Datei oder Assembly “System.Windows.Forms, Version=4.0.0.0″ wurde nicht gefunden

WHAT?? Ich habe doch das .NET Framework 4 drauf, was will das Programm also mehr von mir. Da Google keine Ergebnisse geliefert hat, kam der Facepalm nach wenigen Minuten: Das Programm wurde in 3.5 compiled, daher sind auch keine .NET 4 Dateien geladen. Und das Reflexil-Plugin hat also einfach den MessageBox-Aufruf aus dem 4er Framework verwendet.

Schießlich bekam ich also eine schöne MessageBox:

Doch als ich nun versuchte dieses mit dem Key zu decrypten, hatte ich die schon o.g. if-else vergessen, und somit wurde auch nicht decrypted weil die Keylänge ungültig war. Eine weitere MessageBox schaffte aber Abhilfe:

Ahh, der Rest wird also mit Xs aufgefüllt! Und siehe da, mein Decryptor spuckt nun richtige Sachen aus:

'2134-2134-3324-3433'$next_sub'Not Valid'$next_sub'-'$next_sub'-'$next_sub'-'$next_sub'-'$next_sub;

Fazit: Hardcodet Keys sind nicht sicher, da sie irgendwann verwendbar im Speicher landen. Also immer dem User ein PW abfragen und damit crypten!

Greez

13 people like this post.
    • Hennes
    • 16. Aug. 2011 6:15pm

    Wirklich Super! i like it! Wo ist denn der Facebook-Like-Button?

      • Easysurfer
      • 16. Aug. 2011 7:56pm

      Freut mich zu hören ;-)

      Ich dachte bisher dass der “normale” Like Button genug ist, aber wenn der Wunsch besteht kommt natürlich noch ein FB-Like Button rein!

  1. Noch keine TrackBacks.


1 − = null