Ein kleiner Artikel für den heutigen Abend. Es geht um das fixxen einer “String-Encryption” (eigentlich ein simples Encoding) mit Hilfe von Mono.Cecil. Dabei werden wir alle Methoden zu dieser String-Encryption-Klasse mit einem ldstr-Opcode ersetzen und dabei natürlich nebenher den String decodieren.
Die String-Encryption-Klasse besteht aus einem großen Byte-Array (XORed). Alle 59 statischen Methoden dieser Klasse beinhalten in etwa folgendes:
public static string smethod_16()
{
return (string_0[15] ?? smethod_0(15, 313, 8));
} |
Alle dieser Methoden liefern also string_0[X] zurück ODER wenn dieses NULL ist smethod_0(X,Y,Y). X ist in diesem Fall die “ID” des Strings, Y wird später besprochen.
Nehmen wir also an wir finden einen Call zu einer solchen Funktion, dann speichern wir die MethodDefinition (quasi die Methoden-Klasse unter Mono.Cecil) ab und übergeben diese unserer Funktion:
if (Globals.lstTypes[i].lstMethods[j].Object.Body.Instructions[k].OpCode.Code == Mono.Cecil.Cil.Code.Call)
{
String sTemp = Globals.lstTypes[i].lstMethods[j].Object.Body.Instructions[k].GetInstructionString();
if (sTemp.Contains("PrivateImplementationDetails"))
{
MethodDefinition MD = (MethodDefinition)Globals.lstTypes[i].lstMethods[j].Object.Body.Instructions[k].Operand;
String sTempDecoded = Utils.GetDecodedStringFromMD(MD, byte_0);
Globals.lstTypes[i].lstMethods[j].Object.Body.Instructions[k] = new Mono.Cecil.Cil.Instruction(Mono.Cecil.Cil.OpCodes.Ldstr, sTempDecoded);
}
} |
Wenig übersichtlich, ich weis
Auf jeden Fall gehen wir alle Methoden durch und prüfen ob diese einen “Call” Opcode enthalten. Wenn ja, so wird geprüft ob eine Funktion aus dem <PrivateImplementationDetails>-Namespace gecalled wird, wenn ja haben wir eine Funktion smethod_X(). Diese erhalten wir über den Operand des Call-OpCodes.
Unsere gefundene MethodDefinition (smethod_X) wird nun an die Utils-Funktion übergeben, zusammen mit dem großen byte-Array mit wohl all den Strings drin. Diese im nächsten Absatz besprochene Funktion liefert uns den decodierten String. Der eigentliche Methodenaufruf (call smethod-X) wird nun durch ein “Ldstr DekodierterString” ersetzt, also wird der dekodierte String direkt geladen, nicht durch die decoding-Funktion.
Zum eigentlichen Decoding. Diese Funktion ist schnell erklärt, es geht mehr um die Mono.Cecil-Funktionsweise:
public static String GetDecodedStringFromMD(MethodDefinition MD, byte[] Array)
{
int[] iParams = new int[3];
for (int i = 0; i < MD.Body.Instructions.Count; i++)
{
if (MD.Body.Instructions[i].GetInstructionString().Contains("call"))
{
iParams[0] = (int)MD.Body.Instructions[i - 3].Operand;
iParams[1] = (int)MD.Body.Instructions[i - 2].Operand;
iParams[2] = (int)MD.Body.Instructions[i - 1].Operand;
}
}
return Encoding.Default.GetString(Array, iParams[1],iParams[2]);
} |
Vor dem eigentlichen Decode-Call werden 3 Argumente der Funktion übergeben (X, Y, Y)
Diese werden ausgelesen, danach folgt die GetString-Funktion mit den ausgelesenen Parametern (Y) als Anfangsoffset und Länge. In Falle von smethod_16() wäre das ein Startoffset von 313 und eine Länge von 8 Zeichen. Und mehr macht die Utils-Funktion auch nicht. Und so leicht ist Mono.Cecil zu bedienen
Greez Easy
2 people like this post.