[WIP]使用dnlib的.NET PATCHER库。
如果您有问题,请随时通过吉特问我!我很高兴为您提供帮助!接受功能请求!
主分支为您提供当前稳定的dnpatch构建。但是,由于1.0版在V1分支中,我很可能不再为此提供支持。
DNPATCH是您所有.NET修补需求的终极库。它提供了自动化的装配补丁,签名扫描和最后但并非最不重要的,但它可以通过在重命名/混淆类型中查找方法来绕开混淆器。由于Github上的星星在几天后爆炸了,因此DNPATCH已被几个项目扩展。最重要的是DNPATCH.DEOBFUSCATION,它将DE4DOT直接集成到DNPATCH中。还有dnpatch.script,它使您能够使用纯JSON编写修补程序!库本身使用dnlib(请参阅下一部分)。
由于DNPATCH使用DNLIB,因此强烈建议使用DNSpy首先分析您的组件,以确保使用正确的名称,偏移等,因为它使用DNLIB ASWELL。
强烈建议您计算说明的索引而不是定义指令,以提高与未来更新的兼容性。
构造函数以组装的文件名为单位。
Patcher patcher = new Patcher ( "Test.exe" ) ;如果要保留旧的Maxstack(例如,用于混淆的组件)使用过载:
Patcher patcher = new Patcher ( "Test.exe" , true ) ;所有方法都以一个称为目标的对象作为参数。该对象定义如下:
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返回类型和参数对案例敏感!例子:
propertyMethod定义为:
public enum PropertyMethod
{
Get ,
Set
}请确保您不会分配不一致的值,例如
var target = new Target
{
Instructions = .. .
Instruction = .. .
}如果要修补多种方法创建一个目标[]并将其传递给功能,则大多数方法都接受了它。
引用DNLIB并使用您的指令创建指令[]或指令,然后分配指令所在的索引。您可以通过DNSpy或任何其他分解器来反向工程来找到它们。
小例子:
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
} ;要清除整个方法机构并编写您的说明,请确保您不分配索引或索引属性。
这是一个示例:
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
} ;要应用您的修改后的说明,您可以将方法称为“补丁”:
patcher . Patch ( Target ) ;或者
patcher . Patch ( Target [ ] ) ;在某些情况下,在方法中找到指令可能很有用,例如,如果该方法已更新。
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 object假设有多个相同的说明。现在,鲍斯(Baoss)?好吧,这很简单。有一个超负荷的int,这是您想找到的指令的情况。
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!您可以通过扫描其身体以找到opcode签名来找到方法(target [])
OpCode [ ] codes = new OpCode [ ] {
OpCodes . Ldstr ,
OpCodes . Call
} ;
var result = p . FindMethodsByOpCodeSignature ( codes ) ; // holds Target[]在某些情况下,仅替换指令可能会更容易。在这一开发时刻,这没有多大意义,但是功能很快就会出现。
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 ) ;假设您想删除说明...好吧,这很简单:
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 ) ;嗯。...如果您发现控制台输出违规该怎么办?您可以修改LDSTR而不创建指令:)
Target target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "PrintAlot" ,
Index = 0
} ;
p . PatchOperand ( target , "PatchedOperand" ) ; // pass the Target and a string to replace或您需要修改INT:
p . PatchOperand ( target , 1337 ) ;它还可以使用int []或字符串[]以相同方法修补多个操作数。
如果您想用返回true/false语句覆盖方法机构,则可以执行此操作:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "VerifyMe"
} ;
p . WriteReturnBody ( target , bool ) ; // bool represents the return value如果您只想清空方法机构,请使用此Amigo:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;如果您想获取目标对象的说明,只需执行此操作:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
Instruction [ ] instructions = p . GetInstructions ( target ) ;如果您想用返回覆盖身体,请做到这一点:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteReturnBody ( target , bool ) ;
// bool is the return value, e.g. true will return true ;)如果要删除身体,只需致电:
target = new Target ( )
{
Namespace = "Test" ,
Class = "Program" ,
Method = "WriteLog"
} ;
p . WriteEmptyBody ( target ) ;如果要找到一种方法,则可以通过两种方式扫描整个文件:
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 [ ] ) ;两种方式都返回一个目标[],其中包含指向发现的所有目标。
如果您想找到指令,并且知道类(以及可选的方法),则可以让此方法带有路径和索引的目标[]。
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 ) ;现在,您可以重写像这样的属性的Getter和设定器:
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称为“属性”的属性保留目标属性的名称。
propertyMethod可以是“ propertymethod.get”或“ propertymethod.set”。
说明是Getter或Setter的新说明。
要构建诸如“ console.writeline(string)”之类的呼叫,您可以使用此方法:
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
*/这是Console.Writeline的IL示例:
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" ) ;如果要将方法注入类,请致电注射。确保设置MethodDEF和说明。可选设置当地人,参数defs。
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 ) ;现在请参阅此页面:https://github.com/0xd4d/dnlib/blob/master/master/examples/example2.cs
如果要以其他名称保存组件,请使用以下方式:
patcher . Save ( String ) ; // filename here或者如果要替换原始文件:
patcher . Save ( bool ) ; // if true it will create a backup first (filename.bak) Baoss,如果它沉重混淆,该怎么办?好吧,请谨慎对待您的乔(Joe)。使用“ dnpatch.deobfuscation”!它具有魔力!不,乔只是孩子,它使用了de4dot库。引用库dnpatch.deobfuscation,并确保您还从zip中复制所有其他!然后这样做:
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 使用dnpatch.script,您现在可以使用JSON脚本修补程序!示例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 "
}]
}命名此文件脚本。JSON并将其放入testscript构建文件夹中,然后将其与test.exe一起使用。有关更多信息,请参阅独立仓库。
我要感谢这些人: