Skip to content

Commit d611452

Browse files
author
Prab
committed
Adding FixProxyCall
1 parent 2bec63e commit d611452

File tree

6 files changed

+127
-24
lines changed

6 files changed

+127
-24
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
Logger.cs

AgileStringDecryptor.csproj

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
<LangVersion>9</LangVersion>
6+
<Nullable>annotations</Nullable>
7+
<PackageVersion>1.1.0</PackageVersion>
8+
<Title>AgileStringDecryptor</Title>
9+
<TargetFramework>net48</TargetFramework>
610
</PropertyGroup>
711

812
<ItemGroup>
913
<PackageReference Include="dnlib" Version="3.3.2" />
1014
</ItemGroup>
1115

16+
<ItemGroup>
17+
<Compile Remove="Logger.cs" />
18+
</ItemGroup>
19+
1220
</Project>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/UserDictionary/Words/=dnlib_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

ModuleDefExtensions.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Collections.Generic;
2+
using dnlib.DotNet;
3+
4+
namespace AgileStringDecryptor {
5+
internal static class ModuleDefExtensions {
6+
public static IEnumerable<MethodDef> EnumerateAllMethodDefs(this ModuleDefMD moduleDefMd) {
7+
var methodTableLength = moduleDefMd.TablesStream.MethodTable.Rows;
8+
// Get the length of the Method table.
9+
for (uint rid = 1; rid <= methodTableLength; rid++) yield return moduleDefMd.ResolveMethod(rid);
10+
}
11+
}
12+
}

Program.cs

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
32
using System.IO;
43
using System.Linq;
54
using System.Reflection;
@@ -9,14 +8,14 @@
98

109
namespace 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/

Utils.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System;
2+
using System.Reflection;
3+
4+
namespace AgileStringDecryptor {
5+
public static class Utils {
6+
internal static bool IsDynamicMethod(MethodBase? methodBase) {
7+
if (methodBase == null)
8+
throw new ArgumentNullException(nameof(methodBase));
9+
10+
try {
11+
var token = methodBase.MetadataToken;
12+
}
13+
catch (InvalidOperationException) {
14+
return true;
15+
}
16+
return false;
17+
18+
}
19+
}
20+
}

0 commit comments

Comments
 (0)