diff --git a/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs index e6dec7b4..b90f1731 100644 --- a/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/ILProtector/DynamicMethodsDecrypter.cs @@ -40,6 +40,50 @@ namespace de4dot.code.deobfuscators.ILProtector { IDecrypter decrypter; bool methodReaderHasDelegateTypeFlag; + static class CodeAllocator { + [DllImport("kernel32")] + static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect); + + const uint PAGE_EXECUTE_READWRITE = 0x40; + const uint MEM_COMMIT = 0x00001000; + const int ALIGNMENT = 0x10; + + static IntPtr currentPage; + static int nextOffset; + static int pageSize; + + public static IntPtr Allocate(byte[] code) { + if (code == null || code.Length == 0) + return IntPtr.Zero; + + var addr = Allocate(code.Length); + Marshal.Copy(code, 0, addr, code.Length); + return addr; + } + + public static IntPtr Allocate(int size) { + if (size <= 0) + return IntPtr.Zero; + + size = (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); + if (nextOffset + size > pageSize) + AllocNewPage(size); + + var data = new IntPtr(currentPage.ToInt64() + nextOffset); + nextOffset += size; + return data; + } + + static void AllocNewPage(int size) { + size = (size + 0xFFF) & ~0xFFF; + currentPage = VirtualAlloc(IntPtr.Zero, new UIntPtr((uint)size), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (currentPage == IntPtr.Zero) + throw new ApplicationException("VirtualAlloc() failed"); + pageSize = size; + nextOffset = 0; + } + } + interface IDecrypter { byte[] Decrypt(int methodId, uint rid); } @@ -323,7 +367,7 @@ namespace de4dot.code.deobfuscators.ILProtector { pDecryptCallback = new IntPtr(p + IntPtr.Size * 40); } - IntPtr GetStateAddr(object obj) { + public static IntPtr GetStateAddr(object obj) { var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; foreach (var fi in obj.GetType().GetFields(flags)) { if (fi.FieldType == typeof(IntPtr)) @@ -335,7 +379,6 @@ namespace de4dot.code.deobfuscators.ILProtector { public byte[] Decrypt(int methodId, uint rid) { decryptedData = null; currentILBytes = dmd.reflectionModule.ResolveMethod(0x06000000 + (int)rid).GetMethodBody().GetILAsByteArray(); - invoker.DynamicInvoke(new object[1] { methodId }); return decryptedData; } @@ -358,7 +401,7 @@ namespace de4dot.code.deobfuscators.ILProtector { public unsafe DecrypterV2_0_12_0(DynamicMethodsDecrypter dmd) : base(dmd) { getCallerMethodAsILByteArrayDelegate = GetCallerMethodAsILByteArray; - decryptCallbackDelegate = MyDecryptCallback; + decryptCallbackDelegate = DecryptCallback; *(IntPtr*)pGetILBytes = Marshal.GetFunctionPointerForDelegate(getCallerMethodAsILByteArrayDelegate); *(IntPtr*)pDecryptCallback = Marshal.GetFunctionPointerForDelegate(decryptCallbackDelegate); @@ -368,7 +411,7 @@ namespace de4dot.code.deobfuscators.ILProtector { return currentILBytes; } - unsafe bool MyDecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId) { + unsafe bool DecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId) { SaveDecryptedData(pMethodCode, methodSize); return true; } @@ -386,7 +429,7 @@ namespace de4dot.code.deobfuscators.ILProtector { public unsafe DecrypterV2_0_12_3(DynamicMethodsDecrypter dmd) : base(dmd) { getCallerMethodAsILByteArrayDelegate = GetCallerMethodAsILByteArray; - decryptCallbackDelegate = MyDecryptCallback; + decryptCallbackDelegate = DecryptCallback; *(IntPtr*)pGetILBytes = Marshal.GetFunctionPointerForDelegate(getCallerMethodAsILByteArrayDelegate); *(IntPtr*)pDecryptCallback = Marshal.GetFunctionPointerForDelegate(decryptCallbackDelegate); @@ -396,12 +439,176 @@ namespace de4dot.code.deobfuscators.ILProtector { return currentILBytes; } - unsafe bool MyDecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e) { + unsafe bool DecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e) { SaveDecryptedData(pMethodCode, methodSize); return true; } } + abstract class DecrypterV2_0_13_0_Base : IDecrypter { + protected readonly DynamicMethodsDecrypter dmd; + protected byte[] currentILBytes; + byte[] decryptedData; + readonly Delegate invoker; + + readonly GetCallerMethodAsILByteArrayDelegate getCallerMethodAsILByteArrayDelegate; + readonly DecryptCallbackDelegate decryptCallbackDelegate; + readonly IgnoreDelegate ignoreDelegate; + + [DllImport("kernel32")] + static extern bool GetModuleHandleEx(uint dwFlags, IntPtr lpModuleName, out IntPtr phModule); + + [return: MarshalAs(UnmanagedType.SafeArray)] + delegate byte[] GetCallerMethodAsILByteArrayDelegate(IntPtr a, int skipFrames, ref int flags, IntPtr d); + unsafe delegate bool DecryptCallbackDelegate(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e); + delegate IntPtr IgnoreDelegate(IntPtr a, IntPtr b); + + public unsafe DecrypterV2_0_13_0_Base(DynamicMethodsDecrypter dmd) { + this.dmd = dmd; + this.invoker = (Delegate)dmd.invokerFieldInfo.GetValue(null); + + byte* p = (byte*)DecrypterBaseV2_0_12_x.GetStateAddr(invoker.Target); + byte* pis = GetAddr(*(byte**)p); + p = *(byte**)pis; + byte* pam = *(byte**)(p + IntPtr.Size * 2); + p = *(byte**)(p + ((Environment.Version.Major - 2) / 2 * IntPtr.Size)); + p += IntPtr.Size * 8 + 0x18; + p = LookUp(p, AppDomain.CurrentDomain.Id); + p = *(byte**)(p + IntPtr.Size * 16 + 0x18); + byte* pd = p + IntPtr.Size * 2; + p = *(byte**)(p + IntPtr.Size * 13); + + getCallerMethodAsILByteArrayDelegate = GetCallerMethodAsILByteArray; + decryptCallbackDelegate = DecryptCallback; + ignoreDelegate = IgnoreMethod; + + byte* pm = p + 0x28 * IntPtr.Size; + *(IntPtr*)(p + 0x29 * IntPtr.Size) = Marshal.GetFunctionPointerForDelegate(getCallerMethodAsILByteArrayDelegate); + *(IntPtr*)(p + 0x2A * IntPtr.Size) = Marshal.GetFunctionPointerForDelegate(decryptCallbackDelegate); + if (IntPtr.Size == 4) + *(IntPtr*)(p + 0x2B * IntPtr.Size) = Marshal.GetFunctionPointerForDelegate(ignoreDelegate); + InitCode(GetModuleHandle(pis), pam, pd, pm); + } + + static unsafe byte* GetModuleHandle(byte* addr) { + IntPtr hModule; + if (!GetModuleHandleEx(4, new IntPtr(addr), out hModule)) + throw new ApplicationException("GetModuleHandleEx() failed"); + return (byte*)hModule; + } + + protected unsafe abstract void InitCode(byte* ba, byte* pam, byte* pd, byte* pm); + + static unsafe byte* GetAddr(byte* p) { + if (IntPtr.Size == 4) { + for (int i = 0; i < 20; i++, p++) { + if (*p == 0xA1) + return *(byte**)(p + 1); + } + } + else { + for (int i = 0; i < 20; i++, p++) + if (*p == 0x4C && p[1] == 0x8B && p[2] == 0x15) + return p + 7 + *(int*)(p + 3); + } + return null; + } + + static unsafe byte* LookUp(byte* p, int key) { + p = *(byte**)(p + IntPtr.Size); + p = *(byte**)(p + IntPtr.Size); + + int f1 = 0; + int f2 = IntPtr.Size * 2; + int f3 = IntPtr.Size * 3; + int f4 = IntPtr.Size * 4; + int f5 = IntPtr.Size * 5 + 1; + + byte* res = null; + while (true) { + if (*(p + f5) != 0) + break; + int k = *(int*)(p + f3); + if (k < key) + p = *(byte**)(p + f2); + else { + res = p; + p = *(byte**)(p + f1); + } + } + return *(byte**)(res + f4); + } + + byte[] aryDummy = new byte[7]; + IntPtr dummy; + public unsafe byte[] Decrypt(int methodId, uint rid) { + fixed (byte* p = &aryDummy[0]) { + dummy = new IntPtr(p); + decryptedData = null; + currentILBytes = dmd.reflectionModule.ResolveMethod(0x06000000 + (int)rid).GetMethodBody().GetILAsByteArray(); + invoker.DynamicInvoke(new object[1] { methodId }); + } + dummy = IntPtr.Zero; + return decryptedData; + } + + byte[] GetCallerMethodAsILByteArray(IntPtr a, int skipFrames, ref int flags, IntPtr d) { + flags = 2; + return currentILBytes; + } + + unsafe bool DecryptCallback(IntPtr a, byte* pMethodCode, int methodSize, int methodId, IntPtr e) { + decryptedData = new byte[methodSize]; + Marshal.Copy(new IntPtr(pMethodCode), decryptedData, 0, decryptedData.Length); + return true; + } + + IntPtr IgnoreMethod(IntPtr a, IntPtr b) { + return dummy; + } + } + + class DecrypterV2_0_13_0 : DecrypterV2_0_13_0_Base { + public unsafe DecrypterV2_0_13_0(DynamicMethodsDecrypter dmd) + : base(dmd) { + } + + static readonly byte[] initCode_x86 = new byte[] { + 0x8B, 0xCC, 0x8B, 0x41, 0x04, 0xFF, 0x71, 0x10, + 0xFF, 0x71, 0x0C, 0xFF, 0x71, 0x08, 0xFF, 0x51, + 0x14, 0xC2, 0x14, 0x00, + }; + unsafe delegate void InitCode32Delegate(byte* pppam, byte* m, IntPtr s, byte* pd, byte* f); + unsafe delegate void InitCode64Delegate(byte* pppam, byte* m, IntPtr s, byte* pd); + protected unsafe override void InitCode(byte* ba, byte* pam, byte* pd, byte* pm) { + byte* ppam = (byte*)&pam; + byte* pppam = (byte*)&ppam; + if (IntPtr.Size == 4) { + var del = (InitCode32Delegate)Marshal.GetDelegateForFunctionPointer(CodeAllocator.Allocate(initCode_x86), typeof(InitCode32Delegate)); + del(pppam, pm, new IntPtr(IntPtr.Size * 4), pd, ba + 0x00012500); + } + else { + var del = (InitCode64Delegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(ba + 0x00014CF0), typeof(InitCode64Delegate)); + del(pppam, pm, new IntPtr(IntPtr.Size * 4), pd); + } + } + } + + class DecrypterV2_0_13_1 : DecrypterV2_0_13_0_Base { + public unsafe DecrypterV2_0_13_1(DynamicMethodsDecrypter dmd) + : base(dmd) { + } + + unsafe delegate void InitCodeDelegate(byte* pppam, byte* m, IntPtr s, byte* pd); + protected unsafe override void InitCode(byte* ba, byte* pam, byte* pd, byte* pm) { + int rva = IntPtr.Size == 4 ? 0x00013650 : 0x00016B50; + var del = (InitCodeDelegate)Marshal.GetDelegateForFunctionPointer(new IntPtr(ba + rva), typeof(InitCodeDelegate)); + byte* ppam = (byte*)&pam; + byte* pppam = (byte*)&ppam; + del(pppam, pm, new IntPtr(IntPtr.Size * 4), pd); + } + } + public bool MethodReaderHasDelegateTypeFlag { get { return methodReaderHasDelegateTypeFlag; } } @@ -456,9 +663,13 @@ namespace de4dot.code.deobfuscators.ILProtector { methodReaderHasDelegateTypeFlag = true; if (version < new Version(2, 0, 12, 3)) - return CreateDecrypterV2_0_12_0(); - if (version < new Version(2, 0, 12, 4)) - return CreateDecrypterV2_0_12_3(); + return new DecrypterV2_0_12_0(this); + if (version == new Version(2, 0, 12, 3)) + return new DecrypterV2_0_12_3(this); + if (version == new Version(2, 0, 13, 0)) + return new DecrypterV2_0_13_0(this); + if (version == new Version(2, 0, 13, 1)) + return new DecrypterV2_0_13_1(this); return null; } @@ -503,14 +714,6 @@ namespace de4dot.code.deobfuscators.ILProtector { return new DecrypterV2_0_9_0(this, delegateField); } - IDecrypter CreateDecrypterV2_0_12_0() { - return new DecrypterV2_0_12_0(this); - } - - IDecrypter CreateDecrypterV2_0_12_3() { - return new DecrypterV2_0_12_3(this); - } - 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()) { diff --git a/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs b/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs index 2cef7981..4f9eff07 100644 --- a/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs +++ b/de4dot.code/deobfuscators/ILProtector/RuntimeFileInfo.cs @@ -64,6 +64,8 @@ namespace de4dot.code.deobfuscators.ILProtector { new VersionInfo(new Version(2, 0, 12, 0), new byte[] { 0x63, 0x8B, 0x5C, 0xE9, 0x89, 0x83, 0x57, 0x9D, 0xDC, 0xC3, 0xBD, 0xD9, 0xDB, 0x54, 0xBE, 0x66 }), new VersionInfo(new Version(2, 0, 12, 2), new byte[] { 0xD5, 0x46, 0x38, 0xC7, 0x48, 0xF6, 0x3C, 0x1C, 0x1E, 0x7F, 0x3B, 0x7B, 0x5B, 0xE0, 0x49, 0x46 }), new VersionInfo(new Version(2, 0, 12, 3), new byte[] { 0x35, 0xA3, 0x53, 0xE9, 0x9E, 0x30, 0x6E, 0x9C, 0x0F, 0x46, 0x20, 0x9A, 0x91, 0xD2, 0x95, 0x18 }), + new VersionInfo(new Version(2, 0, 13, 0), new byte[] { 0x66, 0x21, 0xA1, 0x1F, 0x8F, 0x4A, 0xD2, 0xF8, 0x68, 0xEE, 0xD5, 0xD9, 0xC8, 0xB8, 0x17, 0xC7 }), + new VersionInfo(new Version(2, 0, 13, 1), new byte[] { 0xDF, 0x7A, 0xBF, 0x8B, 0xAD, 0x2B, 0x94, 0x6F, 0x37, 0xD9, 0x4B, 0xFC, 0x42, 0x7F, 0x0B, 0x37 }), }; static readonly VersionInfo[] versionInfo64 = new VersionInfo[] { @@ -89,6 +91,8 @@ namespace de4dot.code.deobfuscators.ILProtector { new VersionInfo(new Version(2, 0, 12, 0), new byte[] { 0x5F, 0x42, 0xA5, 0x6C, 0x19, 0xC6, 0x73, 0x9E, 0xE6, 0x74, 0x62, 0x3B, 0x8A, 0x51, 0xBB, 0x93 }), new VersionInfo(new Version(2, 0, 12, 2), new byte[] { 0x10, 0x91, 0xED, 0x05, 0x9C, 0x31, 0x0B, 0x63, 0x76, 0xD7, 0x4A, 0xEC, 0xDE, 0x99, 0x6D, 0xD0 }), new VersionInfo(new Version(2, 0, 12, 3), new byte[] { 0x38, 0x86, 0xE0, 0xBF, 0xC6, 0x64, 0xB9, 0xA0, 0x07, 0xED, 0xDB, 0x02, 0x40, 0xD0, 0x57, 0xE8 }), + new VersionInfo(new Version(2, 0, 13, 0), new byte[] { 0xF0, 0x13, 0xC4, 0x6F, 0x31, 0x0F, 0x61, 0xEA, 0x89, 0x1E, 0x8A, 0x95, 0x8C, 0xBE, 0x2E, 0x44 }), + new VersionInfo(new Version(2, 0, 13, 1), new byte[] { 0xD4, 0x71, 0x75, 0xE2, 0xB1, 0xA5, 0xAE, 0xF5, 0x32, 0xD7, 0x72, 0xDE, 0x93, 0xDC, 0x0B, 0x68 }), }; public RuntimeFileInfo(MethodDef protectMethod) {