[WIP] .NET Patcher Biblioteca usando DNLIB.
Si tiene preguntas, ¡no dude en preguntarme a través de Gitter! ¡Me alegra ayudarte! ¡Tomando solicitudes de funciones!
La rama maestra le proporciona la construcción estable actual de DNPatch. Sin embargo, lo más probable es que ya no brinde soporte ya que la versión 1.0 está en camino en la rama V1.
Dnpatch es la mejor biblioteca para todas sus necesidades de parcheo .NET. Ofrece parches automatizados de ensamblaje, escaneo de firma y por último, pero no menos importante, el omitido de los ofuscadores por su capacidad de encontrar métodos en los tipos renombrados/ofuscados. Desde que las estrellas en Github explotaron en unos días, Dnpatch ha sido extendido por un par de proyectos. El más importante es dnpatch. ¡También hay dnpatch.script, que le brinda la capacidad de escribir parchers con JSON puro! La biblioteca en sí usa dnlib (ver la siguiente parte).
Dado que DNPatch usa DNLIB, se recomienda usar DNSPY para analizar primero sus conjuntos, para asegurarse de usar los nombres, compensaciones, etc. correctos, porque también usa DNLIB.
Se recomienda encarecidamente que calcule el índice de instrucción en lugar de definirlo, para mejorar la probabilidad de compatibilidad con futuras actualizaciones.
El constructor toma el nombre de archivo de la asamblea.
Patcher patcher = new Patcher ( "Test.exe" ) ;Si desea mantener el antiguo maxstack (por ejemplo para ensamblajes ofuscados) use la sobrecarga:
Patcher patcher = new Patcher ( "Test.exe" , true ) ;Todos los métodos toman un objeto llamado objetivo como argumento. El objeto se define de la siguiente manera:
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 target¡ReturnType y los parámetros son sensibles a las minas! Ejemplo:
PropertyMethod se define como este:
public enum PropertyMethod
{
Get ,
Set
}Asegúrese de no asignar valores inconsistentes, por ejemplo,
var target = new Target
{
Instructions = .. .
Instruction = .. .
}Si desea parchar múltiples métodos, cree un objetivo [] y pasarlo a las funciones, la mayoría de ellos lo acepta.
Referencia a DNLib y cree una instrucción [] o instrucción con sus instrucciones, luego asigne a los índices de asignación donde están las instrucciones. Puede encontrarlas mediante ingeniería inversa de su ensamblaje a través de DNSPY o cualquier otro descompilador.
Pequeño ejemplo:
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
} ;Para borrar todo el cuerpo del método y escribir sus instrucciones, asegúrese de no asignar los índices o propiedad del índice.
Aquí hay un ejemplo:
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
} ;Para aplicar sus instrucciones modificadas, puede llamar al método 'parche':
patcher . Patch ( Target ) ;o
patcher . Patch ( Target [ ] ) ;En algunos casos, podría ser útil encontrar una instrucción dentro de un método, por ejemplo, si el método se actualizó.
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 objectDigamos que hay múltiples instrucciones idénticas. ¿Qué ahora, Baoss? Bueno, es simple. Hay una sobrecarga que requiere una int, que es la ocurrencia de la instrucción que le gustaría encontrar.
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!Puede encontrar métodos (Target []) escaneando su cuerpo para una firma de código de operación
OpCode [ ] codes = new OpCode [ ] {
OpCodes . Ldstr ,
OpCodes . Call
} ;
var result = p . FindMethodsByOpCodeSignature ( codes ) ; // holds Target[]En algunos casos, puede ser más fácil reemplazar una instrucción. En este punto de desarrollo, no tiene mucho sentido, pero las características vendrán pronto.
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 ) ;Digamos que desea eliminar las instrucciones ... bueno, es simple como esto:
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 ... ¿Qué pasa si encuentra que la salida de la consola ofende? Puede modificar el ldtr sin siquiera crear una instrucción :)
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "PrintAlot" ,
Index = 0
} ;
p . PatchOperand ( target , "PatchedOperand" ) ; // pass the Target and a string to replaceo en caso de que necesite modificar un INT:
p . PatchOperand ( target , 1337 ) ;También puede parchear múltiples operandos en el mismo método utilizando int [] o string [].
Si desea sobrescribir el Methodbody con una declaración de retorno verdadero/falso, puede hacer esto:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "VerifyMe"
} ;
p . WriteReturnBody ( target , bool ) ; // bool represents the return valueSi solo desea vaciar un Methodbody, use este Amigo:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;Simplemente haga esto si desea obtener instrucciones del objeto objetivo:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
Instruction [ ] instructions = p . GetInstructions ( target ) ;Si desea sobrescribir el cuerpo con un retorno verdadero/falso, haga esto:
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 desea eliminar el cuerpo, simplemente llame a esto:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;Si desea encontrar un método, simplemente puede escanear el archivo completo de 2 maneras:
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 [ ] ) ;En ambos sentidos, devuelve un objetivo [] que contiene todos los objetivos que apuntan a los hallazgos.
Si desea encontrar las instrucciones y conoce la clase (y opcionalmente el método), puede dejar que este método devuelva un objetivo [] con las Patas y los índices.
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 ) ;Ahora puede reescribir el Getter y Setter de una propiedad así:
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 propiedad llamada 'Propiedad' contiene el nombre de la propiedad de destino.
PropertyMethod puede ser 'PropertyMethod.get' o 'PropertyMethod.set'.
Las instrucciones son las nuevas instrucciones para el Getter o Setter.
Para crear llamadas como "Console.WriteLine (String)" puede usar este método:
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
*/Aquí hay un ejemplo de IL para 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 desea inyectar métodos en clases, llame a InjectMethod. Asegúrese de establecer MethodDef e Instrucciones. Opcionalmente, establezca locales, ParameterDefs.
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 ) ;Por ahora, consulte esta página: https://github.com/0xd4d/dnlib/blob/master/examples/example2.cs
Si desea guardar el ensamblaje con un nombre diferente, use esto:
patcher . Save ( String ) ; // filename hereO si desea reemplazar el archivo original:
patcher . Save ( bool ) ; // if true it will create a backup first (filename.bak) Baoss, ¿qué puedo hacer si está muy ofuscado? Bueno, escucha cuidadoso a tu abuelo Joe. ¡Use 'dnpatch.deobfuscation'! ¡Tiene poderes mágicos! No, Joe es solo un niño, usa las bibliotecas DE4DOT. ¡Haga referencia a la biblioteca dnpatch.deobfuscation y asegúrese de que también copie a todos los demás de la zip! Entonces haz esto:
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 ¡Con dnpatch.script ahora puedes scripting Patchers con JSON! Ejemplo 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 "
}]
}Nombra este archivo script.json y colóquelo en la carpeta TestScript Build y úselo con Test.exe. Para obtener más información, consulte el repositorio independiente.
Me gustaría agradecer a estas personas: