Skip to content

Commit

Permalink
Adding FixProxyCall
Browse files Browse the repository at this point in the history
  • Loading branch information
Prab committed Dec 17, 2020
1 parent 2bec63e commit d611452
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 24 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

Logger.cs
10 changes: 9 additions & 1 deletion AgileStringDecryptor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>9</LangVersion>
<Nullable>annotations</Nullable>
<PackageVersion>1.1.0</PackageVersion>
<Title>AgileStringDecryptor</Title>
<TargetFramework>net48</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="dnlib" Version="3.3.2" />
</ItemGroup>

<ItemGroup>
<Compile Remove="Logger.cs" />
</ItemGroup>

</Project>
2 changes: 2 additions & 0 deletions AgileStringDecryptor.sln.DotSettings.user
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<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">
<s:Boolean x:Key="/Default/UserDictionary/Words/=dnlib_0027s/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
12 changes: 12 additions & 0 deletions ModuleDefExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using dnlib.DotNet;

namespace AgileStringDecryptor {
internal static class ModuleDefExtensions {
public static IEnumerable<MethodDef> EnumerateAllMethodDefs(this ModuleDefMD moduleDefMd) {
var methodTableLength = moduleDefMd.TablesStream.MethodTable.Rows;
// Get the length of the Method table.
for (uint rid = 1; rid <= methodTableLength; rid++) yield return moduleDefMd.ResolveMethod(rid);
}
}
}
105 changes: 82 additions & 23 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
Expand All @@ -9,14 +8,14 @@

namespace AgileStringDecryptor {
internal static class Program {
private static Module _module;
private static ModuleDefMD _moduleDefMd;
private static int _amount;
private static Module? _module;
private static ModuleDefMD? _moduleDefMd;
private static int _stringsAmount;
private static int _callsAmount;

private static void Main(string[] args) {
internal static void Main(string[] args) {
Console.Title = "Agile String Decryptor by wwh1004 | Version : 6.x";
try {
// Load the assembly
_module = Assembly.LoadFile(Path.GetFullPath(args[0])).ManifestModule;
}
catch {
Expand All @@ -27,18 +26,84 @@ internal static class Program {
_moduleDefMd = ModuleDefMD.Load(args[0], new ModuleCreationOptions {
TryToLoadPdbFromDisk = false
});
AgileDynamicStringDecryption();
Action decryptString = DecryptString;
decryptString();
Action fixProxyCall = FixProxyCall;
fixProxyCall();
SaveAs(Path.Combine(
Path.GetDirectoryName(args[0]) ?? throw new InvalidOperationException("Failed to save this module !"),
Path.GetFileNameWithoutExtension(args[0]) + "-StrDec" + Path.GetExtension(args[0])));
_moduleDefMd.Dispose();
Console.WriteLine("[?] Decrypted : {0} strings", _amount);
Console.WriteLine($"[?] Fixed : {_callsAmount} calls");
Console.WriteLine($"[?] Decrypted : {_stringsAmount} strings");
Console.WriteLine("[+] Done !");
Console.ReadLine();
}

private static void AgileDynamicStringDecryption() {
// Find namspace empty with class "<AgileDotNetRT>"
internal static void FixProxyCall() {
var globalTypes = _moduleDefMd?.Types.Where(t => t.Namespace == string.Empty).ToArray();
var decryptor = (globalTypes ?? throw new InvalidOperationException()).Single(t => t.Name.StartsWith("{", StringComparison.Ordinal) &&
t.Name.EndsWith("}", StringComparison.Ordinal)).Methods.Single(m => !m.IsInstanceConstructor && m.Parameters.Count == 1);

foreach (var typeDef in globalTypes) {
var cctor = typeDef.FindStaticConstructor();
if(cctor is null || !cctor.Body.Instructions.Any(i => i.OpCode == OpCodes.Call && i.Operand == decryptor))
continue;
switch (_module) {
case not null: {
foreach (var fieldInfo in _module.ResolveType(typeDef.MDToken.ToInt32())
.GetFields(BindingFlags.NonPublic | BindingFlags.Static)!) {
var proxyFieldToken = fieldInfo.MetadataToken;
var proxyFieldDef = _moduleDefMd?.ResolveField((uint) proxyFieldToken - 0x4000000);
var realMethod = ((Delegate) fieldInfo.GetValue(null)!).Method;

if (Utils.IsDynamicMethod(realMethod)) {
var dynamicMethodBodyReader = new DynamicMethodBodyReader(_moduleDefMd, realMethod);
dynamicMethodBodyReader.Read();
var instructionList = dynamicMethodBodyReader.GetMethod().Body.Instructions;
ReplaceAllOperand(proxyFieldDef, instructionList[instructionList.Count - 2].OpCode,
(MemberRef) instructionList[instructionList.Count - 2].Operand);
}
else {
ReplaceAllOperand(proxyFieldDef, realMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call,
(MemberRef) _moduleDefMd.Import(realMethod));
}
}

break;
}
}
}
}

internal static void ReplaceAllOperand(FieldDef? proxyFieldDef, OpCode callOrCallvirt, MemberRef realMethod) {
if (proxyFieldDef is null) throw new ArgumentNullException(nameof(proxyFieldDef));
if (realMethod is null) throw new ArgumentNullException(nameof(realMethod));

if (_moduleDefMd == null) return;
foreach (var method in _moduleDefMd.EnumerateAllMethodDefs()) {
if (!method.HasBody) continue;
var instr = method.Body.Instructions;
for (var i = 0; i < instr.Count; i++) {
if (instr[i].OpCode != OpCodes.Ldsfld || instr[i].Operand != proxyFieldDef) continue;
for (var j = i; j < instr.Count; j++) {
if (instr[j].OpCode.Code != Code.Call || !(instr[j].Operand is MethodDef) ||
((MethodDef) instr[j].Operand).DeclaringType !=
((TypeDefOrRefSig) proxyFieldDef.FieldType).TypeDefOrRef) continue;
instr[i].OpCode = OpCodes.Nop;
instr[i].Operand = null;
instr[j].OpCode = callOrCallvirt;
instr[j].Operand = realMethod;
_callsAmount++;
break;
}
}
}
}


internal static void DecryptString() {
// Find namespace empty with class "<AgileDotNetRT>"
var agileDotNetRt =
_moduleDefMd.Types.First(t => t.Namespace == string.Empty && t.Name == "<AgileDotNetRT>");
// Find a method in the class that has only one parameter with the parameter type String and the return value type String
Expand All @@ -58,28 +123,22 @@ internal static class Program {
instr[i - 1].Operand = decryptor.Invoke(null, new[] {
instr[i - 1].Operand
});
_amount++;
// The instruction corresponding to [i-1] is ldstr,
// we call the string decryptor method and replace the decrypted string back
_stringsAmount++;
}
}

// remove decryption method from the assembly
_moduleDefMd.Types.Remove(decryptionMethod.DeclaringType);
Console.WriteLine("[^] Removed junk : {0} class", decryptionMethod.DeclaringType);
}

private static void SaveAs(string filePath) {
var opts = new ModuleWriterOptions(_moduleDefMd);
opts.MetadataOptions.Flags |= MetadataFlags.PreserveAll | MetadataFlags.KeepOldMaxStack;
opts.Logger = DummyLogger.NoThrowInstance; //this is just to prevent write methods from throwing error
internal static void SaveAs(string filePath) {
var opts = new ModuleWriterOptions(_moduleDefMd) {
MetadataOptions = {Flags = MetadataFlags.PreserveAll}, Logger = DummyLogger.NoThrowInstance
};
_moduleDefMd.Write(filePath, opts);
}
}

internal static class ModuleDefExtensions {
public static IEnumerable<MethodDef> EnumerateAllMethodDefs(this ModuleDefMD moduleDefMd) {
var methodTableLength = moduleDefMd.TablesStream.MethodTable.Rows;
for (uint rid = 1; rid <= methodTableLength; rid++) yield return moduleDefMd.ResolveMethod(rid);
}
}
}
// Ref : https://github.com/wwh1004/blog/
20 changes: 20 additions & 0 deletions Utils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Reflection;

namespace AgileStringDecryptor {
public static class Utils {
internal static bool IsDynamicMethod(MethodBase? methodBase) {
if (methodBase == null)
throw new ArgumentNullException(nameof(methodBase));

try {
var token = methodBase.MetadataToken;
}
catch (InvalidOperationException) {
return true;
}
return false;

}
}
}

0 comments on commit d611452

Please sign in to comment.