[WIP] Bibliothèque Patcher .NET à l'aide de DNLIB.
Si vous avez des questions, n'hésitez pas à me poser via Gitter! Je suis content de vous aider! Prendre des demandes de fonctionnalités!
La branche principale vous fournit la construction stable actuelle de DNPatch. Cependant, je ne le soutiendrai plus probablement plus, car la version 1.0 est en route dans la branche V1.
DNPatch est la bibliothèque ultime pour tous vos besoins de correctifs .NET. Il propose des correctifs d'assemblage automatisés, un balayage de signature et le dernier mais non le moindre contournement des obscurcisseurs par sa capacité à trouver des méthodes dans des types renommés / obscurcis. Depuis que les stars de Github ont explosé en quelques jours, DNPatch a été étendu par quelques projets. Le plus important est dnpatch.deobfuscation qui intègre De4DOT directement dans DNPatch. Il y a aussi dnpatch.script, ce qui vous donne la possibilité d'écrire des patchs avec du JSON pur! La bibliothèque elle-même utilise DNLIB (voir partie suivante).
Étant donné que DNPatch utilise DNLIB, il est fortement recommandé d'utiliser DNSPY pour analyser d'abord vos assemblages, pour vous assurer d'utiliser les noms corrects, les décalages, etc., car il utilise également DNLIB.
Il est fortement recommandé de calculer l'indice de l'instruction au lieu de le définir, pour améliorer la probabilité de compatibilité avec les futures mises à jour.
Le constructeur prend le nom de fichier de l'assemblée.
Patcher patcher = new Patcher ( "Test.exe" ) ;Si vous souhaitez garder l'ancien maxstack (par exemple pour les assemblages obscurcis), utilisez la surcharge:
Patcher patcher = new Patcher ( "Test.exe" , true ) ;Toutes les méthodes prennent un objet appelé Target comme argument. L'objet est défini comme suit:
public string Namespace { get ; set ; } // needed
public string Class { get ; set ; } // needed
public string Method { get ; set ; } // needed
/* If you want to patch multiple indexes in the method */
public int [ ] Indexes { get ; set ; }
public Instruction [ ] Instructions { get ; set ; }
/* If you want to patch 1 index in the method */
public int Index { get ; set ; } = - 1 ;
public Instruction Instruction { get ; set ; }
/* If the path to the method has more than 1 nested class use this */
public string [ ] NestedClasses { get ; set ; }
/* If the path to the method has 1 nested class use this */
public string NestedClass { get ; set ; }
/* If you want to set the parameters for the method (if it's overloaded) use this */
public string [ ] Parameters { get ; set ; }
/* If you want to set the return type for the method use this */
public string ReturnType { get ; set ; }
/* If you want to rewrite the getters or setters of a property use this */
public string Property { get ; set ; } // The name
public PropertyMethod PropertyMethod { get ; set ; } // See below, determines patch targetRetourType et les paramètres sont sensibles à la casse! Exemple:
PropertyMethod est défini comme ceci:
public enum PropertyMethod
{
Get ,
Set
}Veuillez vous assurer que vous n'attribuez pas de valeurs incohérentes, par exemple
var target = new Target
{
Instructions = .. .
Instruction = .. .
}Si vous souhaitez corriger plusieurs méthodes, créez une cible [] et passez-la aux fonctions, elle est acceptée par la plupart d'entre elles.
Référence à DNLIB et créer une instruction [] ou des instructions avec vos instructions, puis attribuer des index en cas d'indices.
Petit exemple:
Instruction [ ] opCodes = {
Instruction . Create ( OpCodes . Ldstr , "Hello Sir 1" ) ,
Instruction . Create ( OpCodes . Ldstr , "Hello Sir 2" )
} ;
int [ ] indexes = {
0 , // index of Instruction
2
} ;
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "Print" ,
Instructions = opCodes ,
Indexes = indexes
} ;Pour effacer l'intégralité de la méthode et rédiger vos instructions, assurez-vous de ne pas affecter les index ou la propriété d'index.
Voici un exemple:
Instruction [ ] opCodes = {
Instruction . Create ( OpCodes . Ldstr , "Hello Sir" ) ,
Instruction . Create ( OpCodes . Call , p . BuildCall ( typeof ( Console ) , "WriteLine" , typeof ( void ) , new [ ] { typeof ( string ) } ) ) ,
Instruction . Create ( OpCodes . Ret )
} ;
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "Print" ,
Instructions = opCodes
} ;Pour appliquer vos instructions modifiées, vous pouvez appeler la méthode «patch»:
patcher . Patch ( Target ) ;ou
patcher . Patch ( Target [ ] ) ;Dans certains cas, il peut être utile de trouver une instruction dans une méthode, par exemple si la méthode a été mise à jour.
Instruction opCode = Instruction . Create ( OpCodes . Ldstr , "TheTrain" ) ;
Instruction toFind = Instruction . Create ( OpCodes . Ldstr , "TheWord" ) ;
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "FindMe" ,
Instruction = opCode // you can also set it later
} ;
target . Index = p . FindInstruction ( target , toFind ) ;
// now you have the full Target objectDisons qu'il existe plusieurs instructions identiques. Quoi maintenant, Baoss? Eh bien, c'est simple. Il y a une surcharge qui prend un INT qui est l'occurrence de l'instruction que vous souhaitez trouver.
Instruction opCode = Instruction . Create ( OpCodes . Ldstr , "TheTrain" ) ;
Instruction toFind = Instruction . Create ( OpCodes . Ldstr , "TheWord" ) ;
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "FindMe" ,
Instruction = opCode // you can also set it later
} ;
target . Index = p . FindInstruction ( target , toFind , 2 ) ; // Sir, find the second occurence!Vous pouvez trouver des méthodes (cible []) en scannant leur corps pour une signature d'opcode
OpCode [ ] codes = new OpCode [ ] {
OpCodes . Ldstr ,
OpCodes . Call
} ;
var result = p . FindMethodsByOpCodeSignature ( codes ) ; // holds Target[]Dans certains cas, il pourrait être plus facile de simplement remplacer une instruction. À ce stade de développement, cela n'a pas beaucoup de sens, mais les fonctionnalités viendront bientôt.
Instruction opCode = Instruction . Create ( OpCodes . Ldstr , "I love kittens" ) ;
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "ReplaceMe" ,
Instruction = opCode ,
Index = 0
} ;
p . ReplaceInstruction ( target ) ;Disons que vous voulez supprimer les instructions ... eh bien c'est simple comme ceci:
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "RemoveMe" ,
Indexes = new [ ] { 0 , 1 } // the indexes, you can also just use 'Index'
} ;
p . RemoveInstruction ( target ) ;Hmmm .... et si vous trouvez la sortie de sortie de la console? Vous pouvez modifier le LDSTR sans même créer une instruction :)
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "PrintAlot" ,
Index = 0
} ;
p . PatchOperand ( target , "PatchedOperand" ) ; // pass the Target and a string to replaceou au cas où vous devez modifier un int:
p . PatchOperand ( target , 1337 ) ;Il est également capable de corriger plusieurs opérandes dans la même méthode en utilisant int [] ou String [].
Si vous souhaitez écraser la méthode avec une instruction Return True / Faux, vous pouvez le faire:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "VerifyMe"
} ;
p . WriteReturnBody ( target , bool ) ; // bool represents the return valueSi vous voulez simplement vider une méthode, utilisez cette Amigo:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;Faites-le simplement si vous souhaitez obtenir des instructions de l'objet cible:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
Instruction [ ] instructions = p . GetInstructions ( target ) ;Si vous souhaitez écraser le corps avec un retour true / false, faites ceci:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteReturnBody ( target , bool ) ;
// bool is the return value, e.g. true will return true ;)Si vous souhaitez supprimer le corps, appelez ceci:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;Si vous souhaitez trouver une méthode, vous pouvez simplement numériser le fichier entier de 2 manières:
p . FindInstructionsByOperand ( string [ ] ) ;
// or p.FindInstructionsByOperand(int[]);
// string[] with all operands in the method, if there are multiple identical operands, make sure to have the same amount as in the method.
// or do this via opcodes:
p . FindInstructionsByOpcode ( OpCode [ ] ) ;Les deux voies renvoient une cible [] qui contient toutes les cibles pointant vers les résultats.
Si vous souhaitez trouver les instructions et que vous connaissez la classe (et éventuellement la méthode), vous pouvez laisser cette méthode renvoyer une cible [] avec les paths et les index.
p . FindInstructionsByOperand ( Target , int [ ] , bool ) ;
// int[]: the operands
// bool: if true it will search for the operands once, it will delete the index if the index was found
// for opcodes:
p . FindInstructionsByOpcode ( Target , int [ ] , bool ) ;Vous pouvez maintenant réécrire un getteur et un setter d'une propriété comme ceci:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Property = "IsPremium" , // Property name
PropertyMethod = PropertyMethod . Get , // Getter or Setter
Instructions = new [ ]
{
Instruction . Create ( OpCodes . Ldc_I4_1 ) ,
Instruction . Create ( OpCodes . Ret )
} // the new instructions
} ;
p . RewriteProperty ( target ) ; // Will overwrite it with return true in getter La propriété appelée «propriété» détient le nom de la propriété cible.
PropertyMethod peut être «PropertyMethod.get» ou «PropertyMethod.set».
Les instructions sont les nouvelles instructions pour Getter ou Setter.
Pour créer des appels comme "Console.WriteLine (String)" Vous pouvez utiliser cette méthode:
p . BuildCall ( typeof ( Console ) , "WriteLine" , typeof ( void ) , new [ ] { typeof ( string ) } )
/*
* Type -> type, a Type instance
* string -> method, the name of the method
* Type -> returnType, a Type instance of the return value
* Type[] -> parameters, an array with the parameter's Types
*/Voici un exemple IL pour Console.WriteLine:
Patcher p = new Patcher ( "Test.exe" ) ;
Instruction [ ] opcodesConsoleWriteLine = {
Instruction . Create ( OpCodes . Ldstr , "Hello Sir" ) , // String to print
Instruction . Create ( OpCodes . Call , p . BuildCall ( typeof ( Console ) , "WriteLine" , typeof ( void ) , new [ ] { typeof ( string ) } ) ) , // Console.WriteLine call
Instruction . Create ( OpCodes . Ret ) // Always return smth
} ;
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "Print" ,
Instructions = opcodesConsoleWriteLine
} ;
p . Patch ( target ) ;
p . Save ( "Test1.exe" ) ;Si vous souhaitez injecter des méthodes dans les classes, appelez InjectMethod. Assurez-vous de définir MethodDef et des instructions. Définir éventuellement les locaux, paramètre.
Target target = new Target ( ) ;
MethodImplAttributes methImplFlags = MethodImplAttributes . IL | MethodImplAttributes . Managed ;
MethodAttributes methFlags = MethodAttributes . Public | MethodAttributes . Static | MethodAttributes . HideBySig | MethodAttributes . ReuseSlot ;
MethodDef meth1 = new MethodDefUser ( "MyMethod" ,
MethodSig . CreateStatic ( mod . CorLibTypes . Int32 , mod . CorLibTypes . Int32 , mod . CorLibTypes . Int32 ) ,
methImplFlags , methFlags ) ;
target . ParameterDefs = new [ ] { new ParamDefUser ( "a" , 1 ) } ;
target . Locals = new [ ] { new Local ( mod . CorLibTypes . Int32 ) } ;
target . MethodDef = meth1 ;
target . Class = "" ;
// ... target as always...
patcher . InjectMethod ( target ) ;Pour l'instant, reportez-vous à cette page: https://github.com/0xd4d/dnlib/blob/master/examples/example2.cs
Si vous souhaitez enregistrer l'assemblage sous un autre nom, utilisez ceci:
patcher . Save ( String ) ; // filename hereOu si vous souhaitez remplacer le fichier d'origine:
patcher . Save ( bool ) ; // if true it will create a backup first (filename.bak) Baoss, que puis-je faire s'il est fortement obscurci ?! Eh bien, écoutez attention à votre grand-père Joe. Utilisez «dnpatch.deobfuscation»! Il a des pouvoirs magiques! Non, Joe est juste Kiddin ', il utilise les bibliothèques De4DOT. Référez la bibliothèque dnpatch.deobfuscation et assurez-vous de copier également toutes les autres à partir du zip! Alors faites ceci:
Deobfuscation d = new Deobfuscation ( string , string ) ;
// string 1 -> file to deobfuscate
// string 2 -> new filename for the deobfuscated file
d . Deobfuscate ( ) ; // Deobfuscates the file and writes it to the disk Avec dnpatch.script, vous êtes maintenant en mesure de scripter les patchs avec JSON! Exemple JSON:
{
"target" : " Test.exe " ,
"targets" :[{
"ns" : " Test " ,
"cl" : " Program " ,
"me" : " ReplaceMe " ,
"ac" : " replace " ,
"index" : 0 ,
"instructions" :[{
"opcode" : " ldstr " ,
"operand" : " script working "
}]
},{
"ns" : " Test " ,
"cl" : " Program " ,
"me" : " RemoveMe " ,
"ac" : " empty "
}]
}Nommez ce fichier script.json et placez-le dans le dossier de construction TestScript et utilisez-le avec test.exe. Pour plus d'informations, veuillez vous référer au dépôt autonome.
Je voudrais remercier ces gens: