diff --git a/de4dot.code/Option.cs b/de4dot.code/Option.cs index ca6de06a..6ccd2a64 100644 --- a/de4dot.code/Option.cs +++ b/de4dot.code/Option.cs @@ -208,21 +208,28 @@ namespace de4dot { class NoArgOption : Option { Action action; + bool triggered; public override bool NeedArgument { get { return false; } } - public NoArgOption(string shortName, string longName, string description, Action action) + public NoArgOption(string shortName, string longName, string description, Action action = null) : base(shortName, longName, description) { this.action = action; } public override bool set(string val, out string error) { - action(); + triggered = true; + if (action != null) + action(); error = ""; return true; } + + public bool get() { + return triggered; + } } class OneArgOption : Option { diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs index 1ec78fe3..7c1b77bc 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/Deobfuscator.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Text.RegularExpressions; using Mono.Cecil; using Mono.Cecil.Cil; @@ -39,6 +40,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { BoolOption decryptResources; BoolOption removeNamespaces; BoolOption removeAntiStrongName; + NoArgOption dumpNativeMethods; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { @@ -51,6 +53,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true); removeNamespaces = new BoolOption(null, makeArgName("ns1"), "Clear namespace if there's only one class in it", true); removeAntiStrongName = new BoolOption(null, makeArgName("sn"), "Remove anti strong name code", true); + dumpNativeMethods = new NoArgOption(null, makeArgName("dump-native"), "Dump native methods to filename.dll.native"); } public override string Name { @@ -73,6 +76,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { DecryptResources = decryptResources.get(), RemoveNamespaces = removeNamespaces.get(), RemoveAntiStrongName = removeAntiStrongName.get(), + DumpNativeMethods = dumpNativeMethods.get(), }); } @@ -87,6 +91,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { decryptResources, removeNamespaces, removeAntiStrongName, + dumpNativeMethods, }; } } @@ -120,6 +125,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { public bool DecryptResources { get; set; } public bool RemoveNamespaces { get; set; } public bool RemoveAntiStrongName { get; set; } + public bool DumpNativeMethods { get; set; } } public override string Type { @@ -365,9 +371,25 @@ namespace de4dot.deobfuscators.dotNET_Reactor { if (!options.DecryptMethods) return false; - if (!methodsDecrypter.decrypt(peImage, DeobfuscatedFile, ref dumpedMethods)) + var tokenToNativeCode = new Dictionary(); + if (!methodsDecrypter.decrypt(peImage, DeobfuscatedFile, ref dumpedMethods, tokenToNativeCode)) return false; + if (options.DumpNativeMethods) { + using (var fileStream = new FileStream(module.FullyQualifiedName + ".native", FileMode.Create, FileAccess.Write, FileShare.Read)) { + var sortedTokens = new List(tokenToNativeCode.Keys); + sortedTokens.Sort(); + var writer = new BinaryWriter(fileStream); + var nops = new byte[] { 0x90, 0x90, 0x90, 0x90 }; + foreach (var token in sortedTokens) { + writer.Write((byte)0xB8); + writer.Write(token); + writer.Write(tokenToNativeCode[token]); + writer.Write(nops); + } + } + } + newFileData = fileData; return true; } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs index 0f82f0c5..12d99f88 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor/MethodsDecrypter.cs @@ -96,7 +96,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { static short[] nativeLdci4 = new short[] { 0x55, 0x8B, 0xEC, 0xB8, -1, -1, -1, -1, 0x5D, 0xC3 }; static short[] nativeLdci4_0 = new short[] { 0x55, 0x8B, 0xEC, 0x33, 0xC0, 0x5D, 0xC3 }; - public bool decrypt(PE.PeImage peImage, ISimpleDeobfuscator simpleDeobfuscator, ref Dictionary dumpedMethods) { + public bool decrypt(PE.PeImage peImage, ISimpleDeobfuscator simpleDeobfuscator, ref Dictionary dumpedMethods, Dictionary tokenToNativeCode) { if (encryptedResource.Method == null) return false; @@ -188,6 +188,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { Log.w("Could not find method having code RVA {0:X8}", rva); continue; } + uint methodToken = 0x06000001 + (uint)methodIndex; if (isNativeCode) { if (!foundNativeCode) { @@ -195,7 +196,10 @@ namespace de4dot.deobfuscators.dotNET_Reactor { Log.w("Found native code. Assembly won't run."); } //TODO: Convert to CIL code - Log.v("Found native code. Ignoring it for now... Assembly won't run. token: {0:X8}", 0x06000001 + methodIndex); + Log.v("Found native code. Ignoring it for now... Assembly won't run. token: {0:X8}", methodToken); + + if (tokenToNativeCode != null) + tokenToNativeCode[methodToken] = methodData; // Convert return true / false methods. The others are converted to // throw 0xDEADCODE. @@ -215,7 +219,7 @@ namespace de4dot.deobfuscators.dotNET_Reactor { } var dm = new DumpedMethod(); - dm.token = (uint)(0x06000001 + methodIndex); + dm.token = methodToken; dm.code = methodData; offset = methodDef.fileOffset + (uint)(methodIndex * methodDef.totalSize);