diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index 7c946ab2..f97e575e 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -160,6 +160,11 @@
+
+
+
+
+
@@ -211,7 +216,7 @@
-
+
diff --git a/de4dot.code/deobfuscators/ILProtector/DecryptedMethodInfo.cs b/de4dot.code/deobfuscators/ILProtector/DecryptedMethodInfo.cs
new file mode 100644
index 00000000..23080df2
--- /dev/null
+++ b/de4dot.code/deobfuscators/ILProtector/DecryptedMethodInfo.cs
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2011-2013 de4dot@gmail.com
+
+ This file is part of de4dot.
+
+ de4dot is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ de4dot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with de4dot. If not, see .
+*/
+
+using System;
+
+namespace de4dot.code.deobfuscators.ILProtector {
+ [Serializable]
+ class DecryptedMethodInfo {
+ public int id;
+ public byte[] data;
+
+ public DecryptedMethodInfo(int id, int size) {
+ this.id = id;
+ this.data = new byte[size];
+ }
+
+ public DecryptedMethodInfo(int id, byte[] data) {
+ this.id = id;
+ this.data = data;
+ }
+
+ public override string ToString() {
+ return string.Format("ID: {0}, Size: 0x{1:X}", id, data.Length);
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs b/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs
index a0c4fea2..0594e3da 100644
--- a/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/ILProtector/Deobfuscator.cs
@@ -56,7 +56,8 @@ namespace de4dot.code.deobfuscators.ILProtector {
string obfuscatorName = DeobfuscatorInfo.THE_NAME;
MainType mainType;
- MethodsDecrypter methodsDecrypter;
+ StaticMethodsDecrypter staticMethodsDecrypter;
+ DynamicMethodsRestorer dynamicMethodsRestorer;
internal class Options : OptionsBase {
}
@@ -85,33 +86,45 @@ namespace de4dot.code.deobfuscators.ILProtector {
protected override void ScanForObfuscator() {
mainType = new MainType(module);
mainType.Find();
- methodsDecrypter = new MethodsDecrypter(module, mainType);
- if (mainType.Detected)
- methodsDecrypter.Find();
- if (mainType.Detected && methodsDecrypter.Detected && methodsDecrypter.Version != null)
- obfuscatorName += " " + methodsDecrypter.Version;
+ staticMethodsDecrypter = new StaticMethodsDecrypter(module, mainType);
+ if (mainType.Detected)
+ staticMethodsDecrypter.Find();
+
+ if (mainType.Detected && !staticMethodsDecrypter.Detected)
+ dynamicMethodsRestorer = new DynamicMethodsRestorer(module, mainType);
+
+ if (mainType.Detected && staticMethodsDecrypter.Detected && staticMethodsDecrypter.Version != null)
+ obfuscatorName += " " + staticMethodsDecrypter.Version;
}
public override void DeobfuscateBegin() {
base.DeobfuscateBegin();
if (mainType.Detected) {
- if (methodsDecrypter.Detected) {
- methodsDecrypter.Decrypt();
- AddTypesToBeRemoved(methodsDecrypter.DelegateTypes, "Obfuscator method delegate type");
- AddResourceToBeRemoved(methodsDecrypter.Resource, "Encrypted methods resource");
- AddTypeToBeRemoved(mainType.InvokerDelegate, "Invoker delegate type");
- AddFieldToBeRemoved(mainType.InvokerInstanceField, "Invoker delegate instance field");
- foreach (var pm in mainType.ProtectMethods)
- AddMethodToBeRemoved(pm, "Obfuscator 'Protect' init method");
- mainType.CleanUp();
+ if (staticMethodsDecrypter.Detected) {
+ staticMethodsDecrypter.Decrypt();
+ RemoveObfuscatorJunk(staticMethodsDecrypter);
+ }
+ else if (dynamicMethodsRestorer != null) {
+ dynamicMethodsRestorer.Decrypt();
+ RemoveObfuscatorJunk(dynamicMethodsRestorer);
}
else
Logger.w("New ILProtector version. Can't decrypt methods (yet)");
}
}
+ void RemoveObfuscatorJunk(MethodsDecrypterBase methodsDecrypter) {
+ AddTypesToBeRemoved(methodsDecrypter.DelegateTypes, "Obfuscator method delegate type");
+ AddResourceToBeRemoved(methodsDecrypter.Resource, "Encrypted methods resource");
+ AddTypeToBeRemoved(mainType.InvokerDelegate, "Invoker delegate type");
+ AddFieldToBeRemoved(mainType.InvokerInstanceField, "Invoker delegate instance field");
+ foreach (var pm in mainType.ProtectMethods)
+ AddMethodToBeRemoved(pm, "Obfuscator 'Protect' init method");
+ mainType.CleanUp();
+ }
+
public override IEnumerable GetStringDecrypterMethods() {
return new List();
}
diff --git a/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs
new file mode 100644
index 00000000..dcae0f9f
--- /dev/null
+++ b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs
@@ -0,0 +1,490 @@
+/*
+ Copyright (C) 2011-2013 de4dot@gmail.com
+
+ This file is part of de4dot.
+
+ de4dot is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ de4dot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with de4dot. If not, see .
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.ExceptionServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using dnlib.DotNet;
+using de4dot.blocks;
+
+namespace de4dot.code.deobfuscators.ILProtector {
+ sealed class DynamicMethodsDecrypter : IDisposable {
+ ModuleDefMD module;
+ Module reflectionModule;
+ Module reflectionProtectModule;
+ TypeDef protectMainType;
+ Type reflectionProtectMainType;
+ FieldInfo invokerFieldInfo;
+ ModuleDefMD moduleProtect;
+ IDecrypter decrypter;
+
+ interface IDecrypter {
+ byte[] Decrypt(int methodId, uint rid);
+ }
+
+ abstract class DecrypterBase : IDecrypter {
+ protected readonly DynamicMethodsDecrypter dmd;
+ protected readonly int appDomainId;
+ protected readonly int asmHashCode;
+
+ class PatchData {
+ public int RVA { get; set; }
+ public byte[] Data { get; set; }
+
+ public PatchData() {
+ }
+
+ public PatchData(int rva, byte[] data) {
+ this.RVA = rva;
+ this.Data = data;
+ }
+ }
+
+ class PatchInfo {
+ public int RvaDecryptMethod { get; set; }
+ public List PatchData { get; set; }
+
+ public PatchInfo() {
+ }
+
+ public PatchInfo(int rvaDecryptMethod, PatchData patchData) {
+ this.RvaDecryptMethod = rvaDecryptMethod;
+ this.PatchData = new List { patchData };
+ }
+ }
+
+ static readonly byte[] nops2 = new byte[] { 0x90, 0x90 };
+ static readonly byte[] nops6 = new byte[] { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 };
+ static readonly Dictionary patchInfos32 = new Dictionary {
+ { new Version(2, 0, 8, 0), new PatchInfo(0x00020B20, new PatchData(0x00005733, nops2)) },
+ { new Version(2, 0, 8, 5), new PatchInfo(0x000221A0, new PatchData(0x00005742, nops2)) },
+ { new Version(2, 0, 9, 0), new PatchInfo(0x00023360, new PatchData(0x000056F2, nops6)) },
+ { new Version(2, 0, 10, 0), new PatchInfo(0x00023B30, new PatchData(0x00005B12, nops6)) },
+ { new Version(2, 0, 11, 0), new PatchInfo(0x000207C0, new PatchData(0x00018432, nops6)) },
+ };
+ static readonly Dictionary patchInfos64 = new Dictionary {
+ { new Version(2, 0, 8, 0), new PatchInfo(0x00026090, new PatchData(0x00005E0C, nops6)) },
+ { new Version(2, 0, 8, 5), new PatchInfo(0x000273D0, new PatchData(0x000060CA, nops6)) },
+ { new Version(2, 0, 9, 0), new PatchInfo(0x00028B00, new PatchData(0x00005F70, nops6)) },
+ { new Version(2, 0, 10, 0), new PatchInfo(0x00029630, new PatchData(0x00006510, nops6)) },
+ { new Version(2, 0, 11, 0), new PatchInfo(0x000257C0, new PatchData(0x0001C9A0, nops6)) },
+ };
+
+ [DllImport("kernel32")]
+ static extern bool VirtualProtect(IntPtr addr, int size, uint newProtect, out uint oldProtect);
+ const uint PAGE_EXECUTE_READWRITE = 0x40;
+
+ public DecrypterBase(DynamicMethodsDecrypter dmd) {
+ this.dmd = dmd;
+ this.appDomainId = AppDomain.CurrentDomain.Id;
+ this.asmHashCode = dmd.reflectionModule.Assembly.GetHashCode();
+ }
+
+ protected IntPtr GetDelegateAddress(FieldDef delegateField) {
+ FieldInfo delegateFieldInfo = dmd.reflectionProtectModule.ResolveField(0x04000000 + (int)delegateField.Rid);
+ object mainTypeInst = ((Delegate)dmd.invokerFieldInfo.GetValue(null)).Target;
+ return GetNativeAddressOfDelegate((Delegate)delegateFieldInfo.GetValue(mainTypeInst));
+ }
+
+ static IntPtr GetNativeAddressOfDelegate(Delegate del) {
+ var field = typeof(Delegate).GetField("_methodPtrAux", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+ if (field == null)
+ return IntPtr.Zero;
+
+ return (IntPtr)field.GetValue(del);
+ }
+
+ protected void PatchRuntime(IntPtr decryptAddr) {
+ if (!PatchRuntimeInternal(decryptAddr))
+ throw new ApplicationException("Probably a new version. Could not patch runtime.");
+ }
+
+ bool PatchRuntimeInternal(IntPtr decryptAddr) {
+ var patchInfos = IntPtr.Size == 4 ? patchInfos32 : patchInfos64;
+ var protectVersion = dmd.reflectionProtectModule.Assembly.GetName().Version;
+ PatchInfo info;
+ if (!patchInfos.TryGetValue(protectVersion, out info))
+ return false;
+ return PatchRuntime(decryptAddr, info);
+ }
+
+ [HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0
+ static bool PatchRuntime(IntPtr decryptAddr, PatchInfo info) {
+ try {
+ IntPtr baseAddr = new IntPtr(decryptAddr.ToInt64() - info.RvaDecryptMethod);
+ if ((baseAddr.ToInt64() & 0xFFFF) != 0)
+ return false;
+
+ if (Marshal.ReadInt16(baseAddr) != 0x5A4D)
+ return false;
+
+ foreach (var patchData in info.PatchData) {
+ var patchAddr = new IntPtr(baseAddr.ToInt64() + patchData.RVA);
+ uint oldProtect;
+ if (!VirtualProtect(patchAddr, patchData.Data.Length, PAGE_EXECUTE_READWRITE, out oldProtect))
+ return false;
+ Marshal.Copy(patchData.Data, 0, patchAddr, patchData.Data.Length);
+ VirtualProtect(patchAddr, patchData.Data.Length, oldProtect, out oldProtect);
+ }
+
+ return true;
+ }
+ catch {
+ }
+
+ return false;
+ }
+
+ public abstract byte[] Decrypt(int methodId, uint rid);
+ }
+
+ // 1.0.7.1 - 1.0.8.0
+ class DecrypterV1_0_7_1 : DecrypterBase {
+ DecryptMethod decryptMethod;
+
+ unsafe delegate bool DecryptMethod(int appDomainId, int asmHashCode, int methodId, out byte* pMethodCode, out int methodSize);
+
+ public DecrypterV1_0_7_1(DynamicMethodsDecrypter dmd, FieldDef delegateField)
+ : base(dmd) {
+ IntPtr addr = GetDelegateAddress(delegateField);
+ decryptMethod = (DecryptMethod)Marshal.GetDelegateForFunctionPointer(addr, typeof(DecryptMethod));
+ }
+
+ public unsafe override byte[] Decrypt(int methodId, uint rid) {
+ byte* pMethodCode;
+ int methodSize;
+ if (!decryptMethod(appDomainId, asmHashCode, methodId, out pMethodCode, out methodSize))
+ return null;
+ byte[] methodData = new byte[methodSize];
+ Marshal.Copy(new IntPtr(pMethodCode), methodData, 0, methodData.Length);
+ return methodData;
+ }
+ }
+
+ // 2.0.0.0 - 2.0.7.6
+ class DecrypterV2_0_0_0 : DecrypterBase {
+ DecryptMethod decryptMethod;
+
+ unsafe delegate bool DecryptMethod(int clrMajorVersion, int appDomainId, int asmHashCode, int methodId, out byte* pMethodCode, out int methodSize);
+
+ public DecrypterV2_0_0_0(DynamicMethodsDecrypter dmd, FieldDef delegateField)
+ : base(dmd) {
+ IntPtr addr = GetDelegateAddress(delegateField);
+ decryptMethod = (DecryptMethod)Marshal.GetDelegateForFunctionPointer(addr, typeof(DecryptMethod));
+ }
+
+ public unsafe override byte[] Decrypt(int methodId, uint rid) {
+ byte* pMethodCode;
+ int methodSize;
+ if (!decryptMethod(Environment.Version.Major, appDomainId, asmHashCode, methodId, out pMethodCode, out methodSize))
+ return null;
+ byte[] methodData = new byte[methodSize];
+ Marshal.Copy(new IntPtr(pMethodCode), methodData, 0, methodData.Length);
+ return methodData;
+ }
+ }
+
+ // 2.0.8.0
+ class DecrypterV2_0_8_0 : DecrypterBase {
+ DecryptMethod decryptMethod;
+ byte[] decryptedData;
+
+ delegate bool DecryptMethod(int clrMajorVersion, int appDomainId, int asmHashCode, int methodId, [MarshalAs(UnmanagedType.FunctionPtr)] DecryptCallback decryptCallback, [MarshalAs(UnmanagedType.Interface)] out Delegate createdDelegate);
+ unsafe delegate bool DecryptCallback(byte* pMethodCode, int methodSize, [MarshalAs(UnmanagedType.Interface)] ref Delegate createdDelegate);
+
+ public DecrypterV2_0_8_0(DynamicMethodsDecrypter dmd, FieldDef delegateField)
+ : base(dmd) {
+ IntPtr addr = GetDelegateAddress(delegateField);
+ decryptMethod = (DecryptMethod)Marshal.GetDelegateForFunctionPointer(addr, typeof(DecryptMethod));
+ PatchRuntime(addr);
+ }
+
+ public unsafe override byte[] Decrypt(int methodId, uint rid) {
+ Delegate createdDelegate;
+ if (!decryptMethod(Environment.Version.Major, appDomainId, asmHashCode, methodId, MyDecryptCallback, out createdDelegate))
+ return null;
+ return decryptedData;
+ }
+
+ unsafe bool MyDecryptCallback(byte* pMethodCode, int methodSize, ref Delegate createdDelegate) {
+ decryptedData = new byte[methodSize];
+ Marshal.Copy(new IntPtr(pMethodCode), decryptedData, 0, decryptedData.Length);
+ return true;
+ }
+ }
+
+ // 2.0.8.5
+ class DecrypterV2_0_8_5 : DecrypterBase {
+ DecryptMethod decryptMethod;
+ byte[] decryptedData;
+ bool decryptReturnValue;
+
+ delegate bool DecryptMethod(int clrMajorVersion, int appDomainId, int asmHashCode, int methodId, [MarshalAs(UnmanagedType.Interface)] StackTrace stackTrace, [MarshalAs(UnmanagedType.FunctionPtr)] DecryptCallback decryptCallback, [MarshalAs(UnmanagedType.Interface)] out Delegate createdDelegate);
+ unsafe delegate bool DecryptCallback(byte* pMethodCode, int methodSize, [MarshalAs(UnmanagedType.Interface)] ref Delegate createdDelegate);
+
+ public DecrypterV2_0_8_5(DynamicMethodsDecrypter dmd, FieldDef delegateField)
+ : base(dmd) {
+ IntPtr addr = GetDelegateAddress(delegateField);
+ decryptMethod = (DecryptMethod)Marshal.GetDelegateForFunctionPointer(addr, typeof(DecryptMethod));
+ PatchRuntime(addr);
+ }
+
+ public unsafe override byte[] Decrypt(int methodId, uint rid) {
+ Delegate createdDelegate;
+ decryptReturnValue = false;
+ if (!decryptMethod(Environment.Version.Major, appDomainId, asmHashCode, methodId, new StackTrace(), MyDecryptCallback, out createdDelegate) &&
+ !decryptReturnValue)
+ return null;
+ return decryptedData;
+ }
+
+ unsafe bool MyDecryptCallback(byte* pMethodCode, int methodSize, ref Delegate createdDelegate) {
+ createdDelegate = new DecryptCallback(MyDecryptCallback);
+ decryptedData = new byte[methodSize];
+ Marshal.Copy(new IntPtr(pMethodCode), decryptedData, 0, decryptedData.Length);
+ return decryptReturnValue = true;
+ }
+ }
+
+ // 2.0.9.0 - 2.0.11.0
+ class DecrypterV2_0_9_0 : DecrypterBase {
+ DecryptMethod decryptMethod;
+ byte[] decryptedData;
+
+ delegate bool DecryptMethod(int clrMajorVersion, int appDomainId, int asmHashCode, int methodId, [MarshalAs(UnmanagedType.Interface)] StackTrace stackTrace, [MarshalAs(UnmanagedType.FunctionPtr)] DecryptCallback decryptCallback);
+ unsafe delegate bool DecryptCallback(byte* pMethodCode, int methodSize, int methodId);
+
+ public DecrypterV2_0_9_0(DynamicMethodsDecrypter dmd, FieldDef delegateField)
+ : base(dmd) {
+ IntPtr addr = GetDelegateAddress(delegateField);
+ decryptMethod = (DecryptMethod)Marshal.GetDelegateForFunctionPointer(addr, typeof(DecryptMethod));
+ PatchRuntime(addr);
+ }
+
+ public unsafe override byte[] Decrypt(int methodId, uint rid) {
+ var encMethod = this.dmd.reflectionModule.ResolveMethod(0x06000000 + (int)rid);
+ var stackTrace = StackTracePatcher.WriteStackFrame(new StackTrace(), 1, encMethod);
+ if (!decryptMethod(Environment.Version.Major, appDomainId, asmHashCode, methodId, stackTrace, MyDecryptCallback))
+ return null;
+ return decryptedData;
+ }
+
+ unsafe bool MyDecryptCallback(byte* pMethodCode, int methodSize, int methodId) {
+ decryptedData = new byte[methodSize];
+ Marshal.Copy(new IntPtr(pMethodCode), decryptedData, 0, decryptedData.Length);
+ return true;
+ }
+ }
+
+ public DynamicMethodsDecrypter(ModuleDefMD module, Module reflectionModule) {
+ this.module = module;
+ this.reflectionModule = reflectionModule;
+ }
+
+ public void Initialize() {
+ RuntimeHelpers.RunModuleConstructor(reflectionModule.ModuleHandle);
+ var reflectionProtectAssembly = GetProtectAssembly();
+ if (reflectionProtectAssembly == null)
+ throw new ApplicationException("Could not find 'Protect' assembly");
+ reflectionProtectModule = reflectionProtectAssembly.ManifestModule;
+ moduleProtect = ModuleDefMD.Load(reflectionProtectModule);
+ protectMainType = FindMainType(moduleProtect);
+ if (protectMainType == null)
+ throw new ApplicationException("Could not find Protect.MainType");
+ var invokerField = FindInvokerField(module);
+
+ reflectionProtectMainType = reflectionProtectModule.ResolveType(0x02000000 + (int)protectMainType.Rid);
+ invokerFieldInfo = reflectionModule.ResolveField(0x04000000 + (int)invokerField.Rid);
+
+ decrypter = CreateDecrypter();
+ if (decrypter == null)
+ throw new ApplicationException("Probably a new version. Could not create a decrypter.");
+ }
+
+ public DecryptedMethodInfo Decrypt(int methodId, uint rid) {
+ byte[] methodData = decrypter.Decrypt(methodId, rid);
+ if (methodData == null)
+ throw new ApplicationException(string.Format("Probably a new version. Could not decrypt method. ID:{0}, RID:{1:X4}", methodId, rid));
+ return new DecryptedMethodInfo(methodId, methodData);
+ }
+
+ public void Dispose() {
+ if (moduleProtect != null)
+ moduleProtect.Dispose();
+ moduleProtect = null;
+ }
+
+ IDecrypter CreateDecrypter() {
+ return CreateDecrypterV1_0_7_1() ??
+ CreateDecrypterV2_0_0_0() ??
+ CreateDecrypterV2_0_8_0() ??
+ CreateDecrypterV2_0_8_5() ??
+ CreateDecrypterV2_0_9_0();
+ }
+
+ IDecrypter CreateDecrypterV1_0_7_1() {
+ var delegateField = FindDelegateFieldV1_0_7_1(protectMainType);
+ if (delegateField == null)
+ return null;
+
+ return new DecrypterV1_0_7_1(this, delegateField);
+ }
+
+ IDecrypter CreateDecrypterV2_0_0_0() {
+ var delegateField = FindDelegateFieldV2_0_0_0(protectMainType);
+ if (delegateField == null)
+ return null;
+
+ return new DecrypterV2_0_0_0(this, delegateField);
+ }
+
+ IDecrypter CreateDecrypterV2_0_8_0() {
+ var delegateField = FindDelegateFieldV2_0_8_0(protectMainType, FindDecryptCallbackV2_0_8_0(protectMainType));
+ if (delegateField == null)
+ return null;
+
+ return new DecrypterV2_0_8_0(this, delegateField);
+ }
+
+ IDecrypter CreateDecrypterV2_0_8_5() {
+ var delegateField = FindDelegateFieldV2_0_8_5(protectMainType, FindDecryptCallbackV2_0_8_0(protectMainType));
+ if (delegateField == null)
+ return null;
+
+ return new DecrypterV2_0_8_5(this, delegateField);
+ }
+
+ IDecrypter CreateDecrypterV2_0_9_0() {
+ var delegateField = FindDelegateFieldV2_0_9_0(protectMainType, FindDecryptCallbackV2_0_9_0(protectMainType));
+ if (delegateField == null)
+ return null;
+
+ return new DecrypterV2_0_9_0(this, delegateField);
+ }
+
+ static readonly byte[] ilpPublicKeyToken = new byte[8] { 0x20, 0x12, 0xD3, 0xC0, 0x55, 0x1F, 0xE0, 0x3D };
+ static Assembly GetProtectAssembly() {
+ foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) {
+ if (!string.IsNullOrEmpty(asm.Location))
+ continue;
+ var asmName = asm.GetName();
+ if (asmName.Name != "Protect")
+ continue;
+ if (!Compare(asmName.GetPublicKeyToken(), ilpPublicKeyToken))
+ continue;
+
+ return asm;
+ }
+ return null;
+ }
+
+ static bool Compare(byte[] a, byte[] b) {
+ if (a == null && b == null)
+ return true;
+ if (a == null || b == null)
+ return false;
+ if (a.Length != b.Length)
+ return false;
+ for (int i = 0; i < a.Length; i++) {
+ if (a[i] != b[i])
+ return false;
+ }
+ return true;
+ }
+
+ static TypeDef FindMainType(ModuleDef module) {
+ foreach (var type in module.Types) {
+ if (type.FindMethod("Finalize") != null)
+ return type;
+ }
+ return null;
+ }
+
+ static FieldDef FindInvokerField(ModuleDef module) {
+ return FindDelegateField(module.GlobalType, "System.Delegate", "(System.Int32)");
+ }
+
+ static FieldDef FindDelegateFieldV1_0_7_1(TypeDef mainType) {
+ return FindDelegateField(mainType, "System.Boolean", "(System.Int32,System.Int32,System.Int32,System.Byte*&,System.Int32&)");
+ }
+
+ static FieldDef FindDelegateFieldV2_0_0_0(TypeDef mainType) {
+ return FindDelegateField(mainType, "System.Boolean", "(System.Int32,System.Int32,System.Int32,System.Int32,System.Byte*&,System.Int32&)");
+ }
+
+ static FieldDef FindDecryptCallbackV2_0_8_0(TypeDef mainType) {
+ return FindDelegateField(mainType, "System.Boolean", "(System.Byte*,System.Int32,System.Delegate&)");
+ }
+
+ static FieldDef FindDecryptCallbackV2_0_9_0(TypeDef mainType) {
+ return FindDelegateField(mainType, "System.Boolean", "(System.Byte*,System.Int32,System.Int32)");
+ }
+
+ static FieldDef FindDelegateFieldV2_0_8_0(TypeDef mainType, FieldDef decryptCallbackField) {
+ if (decryptCallbackField == null)
+ return null;
+ var type = decryptCallbackField.FieldSig.GetFieldType().ToTypeDefOrRef() as TypeDef;
+ if (type == null)
+ return null;
+ return FindDelegateField(mainType, "System.Boolean", string.Format("(System.Int32,System.Int32,System.Int32,System.Int32,{0},System.Delegate&)", type.FullName));
+ }
+
+ static FieldDef FindDelegateFieldV2_0_8_5(TypeDef mainType, FieldDef decryptCallbackField) {
+ if (decryptCallbackField == null)
+ return null;
+ var type = decryptCallbackField.FieldSig.GetFieldType().ToTypeDefOrRef() as TypeDef;
+ if (type == null)
+ return null;
+ return FindDelegateField(mainType, "System.Boolean", string.Format("(System.Int32,System.Int32,System.Int32,System.Int32,System.Diagnostics.StackTrace,{0},System.Delegate&)", type.FullName));
+ }
+
+ static FieldDef FindDelegateFieldV2_0_9_0(TypeDef mainType, FieldDef decryptCallbackField) {
+ if (decryptCallbackField == null)
+ return null;
+ var type = decryptCallbackField.FieldSig.GetFieldType().ToTypeDefOrRef() as TypeDef;
+ if (type == null)
+ return null;
+ return FindDelegateField(mainType, "System.Boolean", string.Format("(System.Int32,System.Int32,System.Int32,System.Int32,System.Diagnostics.StackTrace,{0})", type.FullName));
+ }
+
+ static FieldDef FindDelegateField(TypeDef mainType, string returnType, string parameters) {
+ foreach (var field in mainType.Fields) {
+ var fieldSig = field.FieldSig;
+ if (fieldSig == null)
+ continue;
+ var fieldType = fieldSig.Type.ToTypeDefOrRef() as TypeDef;
+ if (fieldType == null)
+ continue;
+ if (fieldType.BaseType != null && fieldType.BaseType.FullName != "System.MulticastDelegate")
+ continue;
+ var invokeMethod = fieldType.FindMethod("Invoke");
+ if (!DotNetUtils.IsMethod(invokeMethod, returnType, parameters))
+ continue;
+
+ return field;
+ }
+ return null;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypterService.cs b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypterService.cs
new file mode 100644
index 00000000..ead8f085
--- /dev/null
+++ b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypterService.cs
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 2011-2013 de4dot@gmail.com
+
+ This file is part of de4dot.
+
+ de4dot is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ de4dot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with de4dot. If not, see .
+*/
+
+using System;
+using System.Reflection;
+using System.Collections.Generic;
+using dnlib.DotNet;
+using AssemblyData;
+
+namespace de4dot.code.deobfuscators.ILProtector {
+ sealed class DynamicMethodsDecrypterService : IUserGenericService {
+ public const int MSG_DECRYPT_METHODS = 0;
+
+ Module reflObfModule;
+ ModuleDefMD obfModule;
+
+ [CreateUserGenericService]
+ public static IUserGenericService Create() {
+ return new DynamicMethodsDecrypterService();
+ }
+
+ public void Dispose() {
+ if (obfModule != null)
+ obfModule.Dispose();
+ obfModule = null;
+ }
+
+ public void AssemblyLoaded(Assembly assembly) {
+ this.reflObfModule = assembly.ManifestModule;
+ this.obfModule = ModuleDefMD.Load(reflObfModule);
+ }
+
+ public object HandleMessage(int msg, object[] args) {
+ switch (msg) {
+ case MSG_DECRYPT_METHODS:
+ return DecryptMethods(args[0] as IList);
+
+ default:
+ throw new ApplicationException(string.Format("Invalid msg: {0:X8}", msg));
+ }
+ }
+
+ IList DecryptMethods(IList methodIds) {
+ using (var decrypter = new DynamicMethodsDecrypter(obfModule, reflObfModule)) {
+ decrypter.Initialize();
+
+ var infos = new List();
+
+ for (int i = 0; i < methodIds.Count; i += 2)
+ infos.Add(decrypter.Decrypt(methodIds[i], (uint)methodIds[i + 1]));
+
+ return infos;
+ }
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/ILProtector/DynamicMethodsRestorer.cs b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsRestorer.cs
new file mode 100644
index 00000000..ecbff882
--- /dev/null
+++ b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsRestorer.cs
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2011-2013 de4dot@gmail.com
+
+ This file is part of de4dot.
+
+ de4dot is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ de4dot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with de4dot. If not, see .
+*/
+
+using System;
+using System.Collections.Generic;
+using dnlib.DotNet;
+using AssemblyData;
+using de4dot.code.AssemblyClient;
+
+namespace de4dot.code.deobfuscators.ILProtector {
+ // Calls class to dynamically decrypt methods, then restores them.
+ class DynamicMethodsRestorer : MethodsDecrypterBase {
+ public DynamicMethodsRestorer(ModuleDefMD module, MainType mainType)
+ : base(module, mainType) {
+ }
+
+ protected override void DecryptInternal() {
+ IList decryptedData;
+ var serverVersion = NewProcessAssemblyClientFactory.GetServerClrVersion(module);
+ using (var client = new NewProcessAssemblyClientFactory(serverVersion).Create(AssemblyServiceType.Generic)) {
+ client.Connect();
+ client.WaitConnected();
+
+ client.GenericService.LoadUserService(typeof(DynamicMethodsDecrypterService), null);
+ client.GenericService.LoadAssembly(module.Location);
+ decryptedData = client.GenericService.SendMessage(DynamicMethodsDecrypterService.MSG_DECRYPT_METHODS, new object[] { GetMethodIds() }) as IList;
+ }
+
+ if (decryptedData == null)
+ throw new ApplicationException("Unknown return value from dynamic methods decrypter service");
+
+ foreach (var info in decryptedData)
+ methodInfos[info.id] = info;
+ }
+
+ IList GetMethodIds() {
+ var ids = new List();
+
+ foreach (var type in module.GetTypes()) {
+ foreach (var method in type.Methods) {
+ int? id = GetMethodId(method);
+ if (id == null)
+ continue;
+
+ ids.Add(id.Value);
+ ids.Add((int)method.Rid);
+ }
+ }
+
+ return ids;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/ILProtector/MainType.cs b/de4dot.code/deobfuscators/ILProtector/MainType.cs
index f853b289..92b8a399 100644
--- a/de4dot.code/deobfuscators/ILProtector/MainType.cs
+++ b/de4dot.code/deobfuscators/ILProtector/MainType.cs
@@ -53,15 +53,20 @@ namespace de4dot.code.deobfuscators.ILProtector {
CheckMethod(DotNetUtils.GetModuleTypeCctor(module));
}
- static string[] ilpLocals = new string[] {
+ static string[] ilpLocalsV1x = new string[] {
"System.Boolean",
"System.IntPtr",
"System.Object[]",
};
+ static string[] ilpLocalsV2x = new string[] {
+ "System.IntPtr",
+ };
bool CheckMethod(MethodDef cctor) {
if (cctor == null || cctor.Body == null)
return false;
- if (!new LocalTypes(cctor).Exactly(ilpLocals))
+ var localTypes = new LocalTypes(cctor);
+ if (!localTypes.Exactly(ilpLocalsV1x) &&
+ !localTypes.Exactly(ilpLocalsV2x))
return false;
var type = cctor.DeclaringType;
@@ -70,20 +75,31 @@ namespace de4dot.code.deobfuscators.ILProtector {
methods = GetPinvokeMethods(type, "P0");
if (methods.Count != 2)
return false;
- if (type.Fields.Count != 1)
+ if (type.Fields.Count < 1 || type.Fields.Count > 2)
return false;
- var theField = type.Fields[0];
- var theDelegate = theField.FieldType.TryGetTypeDef();
- if (theDelegate == null || !DotNetUtils.DerivesFromDelegate(theDelegate))
+ if (!GetDelegate(type, out invokerInstanceField, out invokerDelegate))
return false;
protectMethods = methods;
- invokerDelegate = theDelegate;
- invokerInstanceField = theField;
return true;
}
+ bool GetDelegate(TypeDef type, out FieldDef field, out TypeDef del) {
+ foreach (var fld in type.Fields) {
+ var theDelegate = fld.FieldType.TryGetTypeDef();
+ if (theDelegate != null && DotNetUtils.DerivesFromDelegate(theDelegate)) {
+ field = fld;
+ del = theDelegate;
+ return true;
+ }
+ }
+
+ field = null;
+ del = null;
+ return false;
+ }
+
static List GetPinvokeMethods(TypeDef type, string name) {
var list = new List();
foreach (var method in type.Methods) {
diff --git a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypterBase.cs b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypterBase.cs
new file mode 100644
index 00000000..2ac46930
--- /dev/null
+++ b/de4dot.code/deobfuscators/ILProtector/MethodsDecrypterBase.cs
@@ -0,0 +1,125 @@
+/*
+ Copyright (C) 2011-2013 de4dot@gmail.com
+
+ This file is part of de4dot.
+
+ de4dot is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ de4dot is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with de4dot. If not, see .
+*/
+
+using System.Collections.Generic;
+using dnlib.DotNet;
+using dnlib.DotNet.Emit;
+
+namespace de4dot.code.deobfuscators.ILProtector {
+ abstract class MethodsDecrypterBase {
+ protected ModuleDefMD module;
+ protected MainType mainType;
+ protected EmbeddedResource methodsResource;
+ protected Dictionary methodInfos = new Dictionary();
+ List delegateTypes = new List();
+
+ public EmbeddedResource Resource {
+ get { return methodsResource; }
+ }
+
+ public IEnumerable DelegateTypes {
+ get { return delegateTypes; }
+ }
+
+ public MethodsDecrypterBase(ModuleDefMD module, MainType mainType) {
+ this.module = module;
+ this.mainType = mainType;
+ }
+
+ public void Decrypt() {
+ DecryptInternal();
+ RestoreMethods();
+ }
+
+ protected abstract void DecryptInternal();
+
+ void RestoreMethods() {
+ if (methodInfos.Count == 0)
+ return;
+
+ Logger.v("Restoring {0} methods", methodInfos.Count);
+ Logger.Instance.Indent();
+ foreach (var type in module.GetTypes()) {
+ foreach (var method in type.Methods) {
+ if (method.Body == null)
+ continue;
+
+ if (RestoreMethod(method)) {
+ Logger.v("Restored method {0} ({1:X8}). Instrs:{2}, Locals:{3}, Exceptions:{4}",
+ Utils.RemoveNewlines(method.FullName),
+ method.MDToken.ToInt32(),
+ method.Body.Instructions.Count,
+ method.Body.Variables.Count,
+ method.Body.ExceptionHandlers.Count);
+ }
+ }
+ }
+ Logger.Instance.DeIndent();
+ if (methodInfos.Count != 0)
+ Logger.w("{0} methods weren't restored", methodInfos.Count);
+ }
+
+ bool RestoreMethod(MethodDef method) {
+ int? methodId = GetMethodId(method);
+ if (methodId == null)
+ return false;
+
+ var parameters = method.Parameters;
+ var methodInfo = methodInfos[methodId.Value];
+ methodInfos.Remove(methodId.Value);
+ var methodReader = new MethodReader(module, methodInfo.data, parameters);
+ methodReader.Read();
+
+ RestoreMethod(method, methodReader);
+ delegateTypes.Add(methodReader.DelegateType);
+
+ return true;
+ }
+
+ static void RestoreMethod(MethodDef method, MethodReader methodReader) {
+ // body.MaxStackSize =
+ method.Body.InitLocals = methodReader.InitLocals;
+ methodReader.RestoreMethod(method);
+ }
+
+ protected int? GetMethodId(MethodDef method) {
+ if (method == null || method.Body == null)
+ return null;
+
+ var instrs = method.Body.Instructions;
+ for (int i = 0; i < instrs.Count - 1; i++) {
+ var ldsfld = instrs[i];
+ if (ldsfld.OpCode.Code != Code.Ldsfld)
+ continue;
+
+ var ldci4 = instrs[i + 1];
+ if (!ldci4.IsLdcI4())
+ continue;
+
+ var field = ldsfld.Operand as FieldDef;
+ if (field == null || field != mainType.InvokerInstanceField)
+ continue;
+
+ return ldci4.GetLdcI4Value();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs b/de4dot.code/deobfuscators/ILProtector/StaticMethodsDecrypter.cs
similarity index 71%
rename from de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs
rename to de4dot.code/deobfuscators/ILProtector/StaticMethodsDecrypter.cs
index 47be38aa..c614017b 100644
--- a/de4dot.code/deobfuscators/ILProtector/MethodsDecrypter.cs
+++ b/de4dot.code/deobfuscators/ILProtector/StaticMethodsDecrypter.cs
@@ -22,16 +22,9 @@ using System.Collections.Generic;
using System.IO;
using dnlib.IO;
using dnlib.DotNet;
-using dnlib.DotNet.Emit;
-using de4dot.blocks;
namespace de4dot.code.deobfuscators.ILProtector {
- class MethodsDecrypter {
- ModuleDefMD module;
- MainType mainType;
- EmbeddedResource methodsResource;
- Dictionary methodInfos = new Dictionary();
- List delegateTypes = new List();
+ class StaticMethodsDecrypter : MethodsDecrypterBase {
IDecrypter decrypter;
interface IDecrypter {
@@ -153,13 +146,13 @@ namespace de4dot.code.deobfuscators.ILProtector {
}
}
- // 1.0.6
+ // 1.0.6 - 1.0.7.0
class DecrypterV106 : DecrypterBase {
byte[] decryptionKey6;
byte[] decryptionKey7;
DecrypterV106(byte[] key0, byte[] key6, byte[] key7, int startOffset) {
- this.ilpVersion = "1.0.6";
+ this.ilpVersion = "1.0.6 - 1.0.7.0";
this.startOffset = startOffset;
this.decryptionKey = key0;
this.decryptionKey6 = key6;
@@ -185,7 +178,7 @@ namespace de4dot.code.deobfuscators.ILProtector {
var key0 = DeobUtils.Sha1Sum(sha1Data); // 1.0.6.0
var key6 = GetKey(reader, key0, keyXorOffs6); // 1.0.6.6
- var key7 = GetKey(reader, key0, keyXorOffs7); // 1.0.6.7
+ var key7 = GetKey(reader, key0, keyXorOffs7); // 1.0.6.7 - 1.0.7.0
return new DecrypterV106(key0, key6, key7, encryptedOffs);
}
catch (IOException) {
@@ -232,29 +225,6 @@ namespace de4dot.code.deobfuscators.ILProtector {
}
}
- class MethodInfo2 {
- public int id;
- public int offset;
- public byte[] data;
- public MethodInfo2(int id, int offset, int size) {
- this.id = id;
- this.offset = offset;
- this.data = new byte[size];
- }
-
- public override string ToString() {
- return string.Format("{0} {1:X8} 0x{2:X}", id, offset, data.Length);
- }
- }
-
- public EmbeddedResource Resource {
- get { return methodsResource; }
- }
-
- public IEnumerable DelegateTypes {
- get { return delegateTypes; }
- }
-
public string Version {
get { return decrypter == null ? null : decrypter.Version; }
}
@@ -263,9 +233,8 @@ namespace de4dot.code.deobfuscators.ILProtector {
get { return methodsResource != null; }
}
- public MethodsDecrypter(ModuleDefMD module, MainType mainType) {
- this.module = module;
- this.mainType = mainType;
+ public StaticMethodsDecrypter(ModuleDefMD module, MainType mainType)
+ : base(module, mainType) {
}
public void Find() {
@@ -300,108 +269,35 @@ namespace de4dot.code.deobfuscators.ILProtector {
return decrypter != null;
}
- public void Decrypt() {
+ protected override void DecryptInternal() {
if (methodsResource == null || decrypter == null)
return;
foreach (var info in ReadMethodInfos(decrypter.GetMethodsData(methodsResource)))
methodInfos[info.id] = info;
-
- RestoreMethods();
}
- static MethodInfo2[] ReadMethodInfos(byte[] data) {
+ static DecryptedMethodInfo[] ReadMethodInfos(byte[] data) {
+ var toOffset = new Dictionary();
var reader = MemoryImageStream.Create(data);
int numMethods = (int)reader.Read7BitEncodedUInt32();
int totalCodeSize = (int)reader.Read7BitEncodedUInt32();
- var methodInfos = new MethodInfo2[numMethods];
+ var methodInfos = new DecryptedMethodInfo[numMethods];
int offset = 0;
for (int i = 0; i < numMethods; i++) {
int id = (int)reader.Read7BitEncodedUInt32();
int size = (int)reader.Read7BitEncodedUInt32();
- methodInfos[i] = new MethodInfo2(id, offset, size);
+ var info = new DecryptedMethodInfo(id, size);
+ methodInfos[i] = info;
+ toOffset[info] = offset;
offset += size;
}
long dataOffset = reader.Position;
foreach (var info in methodInfos) {
- reader.Position = dataOffset + info.offset;
+ reader.Position = dataOffset + toOffset[info];
reader.Read(info.data, 0, info.data.Length);
}
return methodInfos;
}
-
- void RestoreMethods() {
- if (methodInfos.Count == 0)
- return;
-
- Logger.v("Restoring {0} methods", methodInfos.Count);
- Logger.Instance.Indent();
- foreach (var type in module.GetTypes()) {
- foreach (var method in type.Methods) {
- if (method.Body == null)
- continue;
-
- if (RestoreMethod(method)) {
- Logger.v("Restored method {0} ({1:X8}). Instrs:{2}, Locals:{3}, Exceptions:{4}",
- Utils.RemoveNewlines(method.FullName),
- method.MDToken.ToInt32(),
- method.Body.Instructions.Count,
- method.Body.Variables.Count,
- method.Body.ExceptionHandlers.Count);
- }
- }
- }
- Logger.Instance.DeIndent();
- if (methodInfos.Count != 0)
- Logger.w("{0} methods weren't restored", methodInfos.Count);
- }
-
- const int INVALID_METHOD_ID = -1;
- bool RestoreMethod(MethodDef method) {
- int methodId = GetMethodId(method);
- if (methodId == INVALID_METHOD_ID)
- return false;
-
- var parameters = method.Parameters;
- var methodInfo = methodInfos[methodId];
- methodInfos.Remove(methodId);
- var methodReader = new MethodReader(module, methodInfo.data, parameters);
- methodReader.Read();
-
- RestoreMethod(method, methodReader);
- delegateTypes.Add(methodReader.DelegateType);
-
- return true;
- }
-
- static void RestoreMethod(MethodDef method, MethodReader methodReader) {
- // body.MaxStackSize =
- method.Body.InitLocals = methodReader.InitLocals;
- methodReader.RestoreMethod(method);
- }
-
- int GetMethodId(MethodDef method) {
- if (method == null || method.Body == null)
- return INVALID_METHOD_ID;
-
- var instrs = method.Body.Instructions;
- for (int i = 0; i < instrs.Count - 1; i++) {
- var ldsfld = instrs[i];
- if (ldsfld.OpCode.Code != Code.Ldsfld)
- continue;
-
- var ldci4 = instrs[i + 1];
- if (!ldci4.IsLdcI4())
- continue;
-
- var field = ldsfld.Operand as FieldDef;
- if (field == null || field != mainType.InvokerInstanceField)
- continue;
-
- return ldci4.GetLdcI4Value();
- }
-
- return INVALID_METHOD_ID;
- }
}
}