[WIP] .NET Patcher 라이브러리 DNLIB를 사용한 라이브러리.
궁금한 점이 있으면 Gitter를 통해 저에게 물어보십시오! 도와 드리게되어 기쁩니다! 기능 요청을받습니다!
마스터 브랜치는 현재 안정적인 DNPATCH 빌드를 제공합니다. 그러나 버전 1.0이 V1 지점에 있기 때문에 더 이상 지원하지 않을 것입니다.
DNPATCH는 모든 .NET 패치 요구를위한 최고의 라이브러리입니다. 자동 조립 패치, 시그니처 스캐닝 및 마지막으로 이름이 변경/난독 화 된 유형의 방법을 찾는 능력으로 Obfuscator를 우회하는 것을 제공합니다. Github의 별들이 며칠 만에 폭발 한 이후 Dnpatch는 몇 가지 프로젝트에 의해 연장되었습니다. 가장 중요한 것은 dnpatch.deobfuscation입니다. DE4DOT를 dnpatch에 직접 통합합니다. 또한 dnpatch.script가있어 순수한 JSON으로 패치를 쓸 수 있습니다! 라이브러리 자체는 dnlib을 사용합니다 (다음 부분 참조).
dnpatch는 dnlib을 사용하기 때문에 dnspy를 사용하여 먼저 어셈블리를 분석하여 DNLIB를 사용하기 때문에 올바른 이름, 오프셋 등을 사용하는 것이 좋습니다.
향후 업데이트와의 호환성 가능성을 향상시키기 위해 명령어를 정의하는 대신 지침의 색인을 계산하는 것이 좋습니다.
생성자는 어셈블리의 파일 이름을 가져옵니다.
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 targetreturnType 및 매개 변수는 사례에 민감합니다! 예:
PropertyMethod는 다음과 같이 정의됩니다.
public enum PropertyMethod
{
Get ,
Set
}일관되지 않은 값을 할당하지 않도록하십시오.
var target = new Target
{
Instructions = .. .
Instruction = .. .
}여러 메소드를 패치하려면 대상을 생성하고 함수로 전달하려면 대부분의 기능에 의해 받아 들여집니다.
DNLIB를 참조하고 명령어를 사용하여 명령을 작성한 다음 명령어를 지침 할당 할당 지침 위치에 할당 할당합니다.
Small Example:
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 [] 또는 String []를 사용하여 동일한 방법으로 여러 피연산자를 패치 할 수 있습니다.
반환 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 ) ;반품으로 신체를 덮어 쓰고 싶다면 true/false를 수행하십시오.
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 및 setter를 다시 작성할 수 있습니다.
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" ) ;메소드를 클래스에 주입하려면 InjectMethod를 호출하십시오. MethodDef 및 지침을 설정하십시오. 선택적으로 현지인, 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 ) ;지금은이 페이지를 참조하십시오 : https://github.com/0xd4d/dnlib/blob/master/examples/exampl.cs
어셈블리를 다른 이름으로 저장하려면 다음을 사용하십시오.
patcher . Save ( String ) ; // filename here또는 원본 파일을 교체하려는 경우 :
patcher . Save ( bool ) ; // if true it will create a backup first (filename.bak) Baoss, 심하게 난독 화 된 경우 어떻게해야합니까?! 글쎄, 할아버지 조를주의 깊게 들어보세요. 'dnpatch.deobfuscation'을 사용하십시오! 마법의 힘이 있습니다! Nah, Joe는 단지 Kiddin '이며 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으로 Patchers를 스크립트 할 수 있습니다! 예제 예제 :
{
"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 "
}]
}이 파일 Script.json의 이름을 지정하여 TestScript 빌드 폴더에 배치하여 Test.Exe와 함께 사용하십시오. 자세한 내용은 독립형 Repo를 참조하십시오.
이 사람들에게 감사하고 싶습니다.