11using System ;
2- using System . Collections . Generic ;
32using System . IO ;
43using System . Linq ;
54using System . Reflection ;
98
109namespace AgileStringDecryptor {
1110 internal static class Program {
12- private static Module _module ;
13- private static ModuleDefMD _moduleDefMd ;
14- private static int _amount ;
11+ private static Module ? _module ;
12+ private static ModuleDefMD ? _moduleDefMd ;
13+ private static int _stringsAmount ;
14+ private static int _callsAmount ;
1515
16- private static void Main ( string [ ] args ) {
16+ internal static void Main ( string [ ] args ) {
1717 Console . Title = "Agile String Decryptor by wwh1004 | Version : 6.x" ;
1818 try {
19- // Load the assembly
2019 _module = Assembly . LoadFile ( Path . GetFullPath ( args [ 0 ] ) ) . ManifestModule ;
2120 }
2221 catch {
@@ -27,18 +26,84 @@ private static void Main(string[] args) {
2726 _moduleDefMd = ModuleDefMD . Load ( args [ 0 ] , new ModuleCreationOptions {
2827 TryToLoadPdbFromDisk = false
2928 } ) ;
30- AgileDynamicStringDecryption ( ) ;
29+ Action decryptString = DecryptString ;
30+ decryptString ( ) ;
31+ Action fixProxyCall = FixProxyCall ;
32+ fixProxyCall ( ) ;
3133 SaveAs ( Path . Combine (
3234 Path . GetDirectoryName ( args [ 0 ] ) ?? throw new InvalidOperationException ( "Failed to save this module !" ) ,
3335 Path . GetFileNameWithoutExtension ( args [ 0 ] ) + "-StrDec" + Path . GetExtension ( args [ 0 ] ) ) ) ;
3436 _moduleDefMd . Dispose ( ) ;
35- Console . WriteLine ( "[?] Decrypted : {0} strings" , _amount ) ;
37+ Console . WriteLine ( $ "[?] Fixed : { _callsAmount } calls") ;
38+ Console . WriteLine ( $ "[?] Decrypted : { _stringsAmount } strings") ;
3639 Console . WriteLine ( "[+] Done !" ) ;
3740 Console . ReadLine ( ) ;
3841 }
3942
40- private static void AgileDynamicStringDecryption ( ) {
41- // Find namspace empty with class "<AgileDotNetRT>"
43+ internal static void FixProxyCall ( ) {
44+ var globalTypes = _moduleDefMd ? . Types . Where ( t => t . Namespace == string . Empty ) . ToArray ( ) ;
45+ var decryptor = ( globalTypes ?? throw new InvalidOperationException ( ) ) . Single ( t => t . Name . StartsWith ( "{" , StringComparison . Ordinal ) &&
46+ t . Name . EndsWith ( "}" , StringComparison . Ordinal ) ) . Methods . Single ( m => ! m . IsInstanceConstructor && m . Parameters . Count == 1 ) ;
47+
48+ foreach ( var typeDef in globalTypes ) {
49+ var cctor = typeDef . FindStaticConstructor ( ) ;
50+ if ( cctor is null || ! cctor . Body . Instructions . Any ( i => i . OpCode == OpCodes . Call && i . Operand == decryptor ) )
51+ continue ;
52+ switch ( _module ) {
53+ case not null : {
54+ foreach ( var fieldInfo in _module . ResolveType ( typeDef . MDToken . ToInt32 ( ) )
55+ . GetFields ( BindingFlags . NonPublic | BindingFlags . Static ) ! ) {
56+ var proxyFieldToken = fieldInfo . MetadataToken ;
57+ var proxyFieldDef = _moduleDefMd ? . ResolveField ( ( uint ) proxyFieldToken - 0x4000000 ) ;
58+ var realMethod = ( ( Delegate ) fieldInfo . GetValue ( null ) ! ) . Method ;
59+
60+ if ( Utils . IsDynamicMethod ( realMethod ) ) {
61+ var dynamicMethodBodyReader = new DynamicMethodBodyReader ( _moduleDefMd , realMethod ) ;
62+ dynamicMethodBodyReader . Read ( ) ;
63+ var instructionList = dynamicMethodBodyReader . GetMethod ( ) . Body . Instructions ;
64+ ReplaceAllOperand ( proxyFieldDef , instructionList [ instructionList . Count - 2 ] . OpCode ,
65+ ( MemberRef ) instructionList [ instructionList . Count - 2 ] . Operand ) ;
66+ }
67+ else {
68+ ReplaceAllOperand ( proxyFieldDef , realMethod . IsVirtual ? OpCodes . Callvirt : OpCodes . Call ,
69+ ( MemberRef ) _moduleDefMd . Import ( realMethod ) ) ;
70+ }
71+ }
72+
73+ break ;
74+ }
75+ }
76+ }
77+ }
78+
79+ internal static void ReplaceAllOperand ( FieldDef ? proxyFieldDef , OpCode callOrCallvirt , MemberRef realMethod ) {
80+ if ( proxyFieldDef is null ) throw new ArgumentNullException ( nameof ( proxyFieldDef ) ) ;
81+ if ( realMethod is null ) throw new ArgumentNullException ( nameof ( realMethod ) ) ;
82+
83+ if ( _moduleDefMd == null ) return ;
84+ foreach ( var method in _moduleDefMd . EnumerateAllMethodDefs ( ) ) {
85+ if ( ! method . HasBody ) continue ;
86+ var instr = method . Body . Instructions ;
87+ for ( var i = 0 ; i < instr . Count ; i ++ ) {
88+ if ( instr [ i ] . OpCode != OpCodes . Ldsfld || instr [ i ] . Operand != proxyFieldDef ) continue ;
89+ for ( var j = i ; j < instr . Count ; j ++ ) {
90+ if ( instr [ j ] . OpCode . Code != Code . Call || ! ( instr [ j ] . Operand is MethodDef ) ||
91+ ( ( MethodDef ) instr [ j ] . Operand ) . DeclaringType !=
92+ ( ( TypeDefOrRefSig ) proxyFieldDef . FieldType ) . TypeDefOrRef ) continue ;
93+ instr [ i ] . OpCode = OpCodes . Nop ;
94+ instr [ i ] . Operand = null ;
95+ instr [ j ] . OpCode = callOrCallvirt ;
96+ instr [ j ] . Operand = realMethod ;
97+ _callsAmount ++ ;
98+ break ;
99+ }
100+ }
101+ }
102+ }
103+
104+
105+ internal static void DecryptString ( ) {
106+ // Find namespace empty with class "<AgileDotNetRT>"
42107 var agileDotNetRt =
43108 _moduleDefMd . Types . First ( t => t . Namespace == string . Empty && t . Name == "<AgileDotNetRT>" ) ;
44109 // Find a method in the class that has only one parameter with the parameter type String and the return value type String
@@ -58,28 +123,22 @@ private static void AgileDynamicStringDecryption() {
58123 instr [ i - 1 ] . Operand = decryptor . Invoke ( null , new [ ] {
59124 instr [ i - 1 ] . Operand
60125 } ) ;
61- _amount ++ ;
126+ // The instruction corresponding to [i-1] is ldstr,
127+ // we call the string decryptor method and replace the decrypted string back
128+ _stringsAmount ++ ;
62129 }
63130 }
64131
65132 // remove decryption method from the assembly
66133 _moduleDefMd . Types . Remove ( decryptionMethod . DeclaringType ) ;
67- Console . WriteLine ( "[^] Removed junk : {0} class" , decryptionMethod . DeclaringType ) ;
68134 }
69135
70- private static void SaveAs ( string filePath ) {
71- var opts = new ModuleWriterOptions ( _moduleDefMd ) ;
72- opts . MetadataOptions . Flags | = MetadataFlags . PreserveAll | MetadataFlags . KeepOldMaxStack ;
73- opts . Logger = DummyLogger . NoThrowInstance ; //this is just to prevent write methods from throwing error
136+ internal static void SaveAs ( string filePath ) {
137+ var opts = new ModuleWriterOptions ( _moduleDefMd ) {
138+ MetadataOptions = { Flags = MetadataFlags . PreserveAll } , Logger = DummyLogger . NoThrowInstance
139+ } ;
74140 _moduleDefMd . Write ( filePath , opts ) ;
75141 }
76142 }
77-
78- internal static class ModuleDefExtensions {
79- public static IEnumerable < MethodDef > EnumerateAllMethodDefs ( this ModuleDefMD moduleDefMd ) {
80- var methodTableLength = moduleDefMd . TablesStream . MethodTable . Rows ;
81- for ( uint rid = 1 ; rid <= methodTableLength ; rid ++ ) yield return moduleDefMd . ResolveMethod ( rid ) ;
82- }
83- }
84143}
85144// Ref : https://github.com/wwh1004/blog/
0 commit comments