[WIP] .NET Patcher Library usando DNLIB.
Se você tiver perguntas, sinta -se à vontade para me perguntar via Gitter! Fico feliz em te ajudar! Fazendo solicitações de recursos!
A filial principal fornece a construção estável atual do DNPatch. No entanto, provavelmente não fornecerei mais suporte para ele, pois a versão 1.0 está a caminho na ramificação V1.
O DNPatch é a biblioteca final para todas as suas necessidades de patch .NET. Oferece patches de montagem automatizados, varredura de assinatura e último, mas não menos importante, ignorando os ofuscadores por sua capacidade de encontrar métodos em tipos renomeados/ofuscados. Desde que as estrelas no Github explodiram em alguns dias, o DNPatch foi estendido por alguns projetos. O mais importante é o dnpatch.deobfuscation, que integra o DE4DOT diretamente no dnpatch. Também há dnpatch.script, que oferece a capacidade de escrever patchers com o puro json! A própria biblioteca usa o DNLIB (consulte a próxima parte).
Como o DNPatch usa o DNLIB, é altamente recomendável usar o DNSPY para analisar primeiro seus assemblies, para garantir que você use os nomes corretos, compensações etc., porque ele usa o DNLIB também.
É altamente recomendável que você calcule o índice da instrução em vez de defini -lo, para melhorar a probabilidade de compatibilidade com atualizações futuras.
O construtor leva o nome do arquivo da assembléia.
Patcher patcher = new Patcher ( "Test.exe" ) ;Se você deseja manter o antigo MaxStack (por exemplo, para conjuntos ofuscados), use a sobrecarga:
Patcher patcher = new Patcher ( "Test.exe" , true ) ;Todos os métodos tomam um objeto chamado Target como argumento. O objeto é definido da seguinte forma:
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 targetReturnType e parâmetros são sensíveis ao minúsculo! Exemplo:
PropertyMethod é definido como este:
public enum PropertyMethod
{
Get ,
Set
}Certifique -se de não atribuir valores inconsistentes, por exemplo
var target = new Target
{
Instructions = .. .
Instruction = .. .
}Se você deseja corrigir vários métodos, crie um destino [] e o passa para as funções, ele é aceito pela maioria deles.
Refere -se ao DNLIB e crie uma instrução [] ou instruções com suas instruções (s) e, em seguida, atribua índices onde estão as instruções. Você pode encontrá -las ao engenharia reversa de sua montagem via DNSPY ou qualquer outro decompilador.
Pequeno exemplo:
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 limpar todo o corpo do método e escrever suas instruções, verifique se você não atribui os índices ou a propriedade Index.
Aqui está um exemplo:
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 suas instruções modificadas, você pode chamar o método de 'patch':
patcher . Patch ( Target ) ;ou
patcher . Patch ( Target [ ] ) ;Em alguns casos, pode ser útil encontrar uma instrução dentro de um método, por exemplo, se o método foi atualizado.
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 existem várias instruções idênticas. E agora, Baoss? Bem, é simples. Há uma sobrecarga que leva um INT que é a ocorrência das instruções que você gostaria de 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!Você pode encontrar métodos (Target []) examinando seu corpo em busca de uma assinatura de código OPCOD
OpCode [ ] codes = new OpCode [ ] {
OpCodes . Ldstr ,
OpCodes . Call
} ;
var result = p . FindMethodsByOpCodeSignature ( codes ) ; // holds Target[]Em alguns casos, pode ser mais fácil substituir uma instrução. Nesse ponto de desenvolvimento, não faz muito sentido, mas os recursos chegarão em breve.
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 você queira remover instruções ... bem, é simples assim:
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 .... e se você encontrar a saída de console ofensiva? Você pode modificar o LDSTR sem sequer criar uma instrução :)
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "PrintAlot" ,
Index = 0
} ;
p . PatchOperand ( target , "PatchedOperand" ) ; // pass the Target and a string to replaceou se você precisa modificar um int:
p . PatchOperand ( target , 1337 ) ;Também é capaz de corrigir vários operando no mesmo método usando int [] ou string [].
Se você deseja substituir o corpo do método por uma declaração de retorno true/falsa, você pode fazer isso:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "VerifyMe"
} ;
p . WriteReturnBody ( target , bool ) ; // bool represents the return valueSe você deseja esvaziar um método, use este Amigo:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;Basta fazer isso se você quiser obter instruções do objeto de destino:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
Instruction [ ] instructions = p . GetInstructions ( target ) ;Se você deseja substituir o corpo com um retorno verdadeiro/falso, faça isso:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteReturnBody ( target , bool ) ;
// bool is the return value, e.g. true will return true ;)Se você quiser remover o corpo, basta chamar isso:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;Se você deseja encontrar um método, basta escanear o arquivo inteiro de 2 maneiras:
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 [ ] ) ;Ambas as maneiras retornam um alvo [] que contém todos os alvos apontando para as descobertas.
Se você deseja encontrar as instruções e conhece a classe (e opcionalmente o método), pode deixar esse método retornar um destino [] com os caminhos e í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 ) ;Agora você pode reescrever o getter e o setter de uma propriedade assim:
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 A propriedade chamada 'Propriedade' detém o nome da propriedade de destino.
PropertyMethod pode ser 'PropertyMethod.get' ou 'PropertyMethod.Set'.
As instruções são as novas instruções para o getter ou setter.
Para criar chamadas como "Console.WriteLine (String)", você pode 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
*/Aqui está um exemplo 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" ) ;Se você deseja injetar métodos nas classes, ligue para o InjectMethod. Certifique -se de definir o MethodDef e as instruções. Opcionalmente defina os habitantes locais, 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 enquanto, consulte esta página: https://github.com/0xd4d/dnlib/blob/master/examples/example2.cs
Se você deseja salvar a montagem em um nome diferente, use este:
patcher . Save ( String ) ; // filename hereOu se você deseja substituir o arquivo original:
patcher . Save ( bool ) ; // if true it will create a backup first (filename.bak) Baoss, o que posso fazer se for fortemente ofuscado?! Bem, ouça cuidadoso com seu avô Joe. Use 'dnpatch.deobfuscation'! Tem poderes mágicos! Nah, Joe está apenas infantil, usa as bibliotecas DE4DOT. Faça referência à biblioteca dnpatch.deobfuscation e certifique -se de copiar todos os outros do zip! Então faça isso:
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 Com o dnpatch.script, agora você é capaz de escrever patchers com JSON! Exemplo 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 "
}]
}Nomeie este arquivo script.json e coloque -o na pasta de compilação do TestScript e use -o com test.exe. Para mais informações, consulte o repositório independente.
Eu gostaria de agradecer a essas pessoas: