diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index b3c2e119..21dd27fa 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -79,7 +79,9 @@ + + diff --git a/de4dot.code/deobfuscators/dotNET_Reactor3/DecrypterType.cs b/de4dot.code/deobfuscators/dotNET_Reactor3/DecrypterType.cs new file mode 100644 index 00000000..c20cb3b9 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor3/DecrypterType.cs @@ -0,0 +1,117 @@ +/* + Copyright (C) 2011 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.Text; +using Mono.Cecil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.dotNET_Reactor3 { + // Find the type that decrypts strings and calls the native lib + class DecrypterType { + ModuleDefinition module; + TypeDefinition decrypterType; + MethodDefinition stringDecrypter1; + MethodDefinition stringDecrypter2; + List initMethods = new List(); + + public bool Detected { + get { return decrypterType != null; } + } + + public TypeDefinition Type { + get { return decrypterType; } + } + + public MethodDefinition StringDecrypter1 { + get { return stringDecrypter1; } + } + + public MethodDefinition StringDecrypter2 { + get { return stringDecrypter2; } + } + + public IEnumerable InitMethods { + get { return initMethods; } + } + + public IEnumerable StringDecrypters { + get { + return new List { + stringDecrypter1, + stringDecrypter2, + }; + } + } + + public DecrypterType(ModuleDefinition module) { + this.module = module; + } + + public DecrypterType(ModuleDefinition module, DecrypterType oldOne) { + this.module = module; + this.decrypterType = lookup(oldOne.decrypterType, "Could not find decrypterType"); + this.stringDecrypter1 = lookup(oldOne.stringDecrypter1, "Could not find stringDecrypter1"); + this.stringDecrypter2 = lookup(oldOne.stringDecrypter2, "Could not find stringDecrypter2"); + foreach (var method in oldOne.initMethods) + initMethods.Add(lookup(method, "Could not find initMethod")); + } + + T lookup(T def, string errorMessage) where T : MemberReference { + return DeobUtils.lookup(module, def, errorMessage); + } + + public void find() { + foreach (var type in module.Types) { + if (type.FullName != "{B4838DC1-AC79-43d1-949F-41B518B904A8}") + continue; + + decrypterType = type; + stringDecrypter1 = addStringDecrypter(type, "CS$0$0004"); + stringDecrypter2 = addStringDecrypter(type, "CS$0$0005"); + foreach (var method in type.Methods) { + if (DotNetUtils.isMethod(method, "System.Void", "()")) + initMethods.Add(method); + } + return; + } + } + + MethodDefinition addStringDecrypter(TypeDefinition type, string name) { + var method = DotNetUtils.getMethod(type, name); + if (method == null) + return null; + if (!DotNetUtils.isMethod(method, "System.String", "(System.String)")) + return null; + return method; + } + + public string decrypt1(string s) { + var sb = new StringBuilder(s.Length); + foreach (var c in s) + sb.Append((char)(0xFF - (byte)c)); + return sb.ToString(); + } + + public string decrypt2(string s) { + return Encoding.Unicode.GetString(Convert.FromBase64String(s)); + } + } +} diff --git a/de4dot.code/deobfuscators/dotNET_Reactor3/Deobfuscator.cs b/de4dot.code/deobfuscators/dotNET_Reactor3/Deobfuscator.cs index dd5d0a45..10ff75ad 100644 --- a/de4dot.code/deobfuscators/dotNET_Reactor3/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/dotNET_Reactor3/Deobfuscator.cs @@ -81,9 +81,12 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor3 { Options options; string obfuscatorName = DeobfuscatorInfo.THE_NAME; + DecrypterType decrypterType; + NativeLibSaver nativeLibSaver; List unpackedFiles = new List(); bool unpackedNativeFile = false; + bool canRemoveDecrypterType = true; bool startedDeobfuscating = false; internal class Options : OptionsBase { @@ -132,6 +135,27 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor3 { return data; } + public override bool getDecryptedModule(ref byte[] newFileData, ref Dictionary dumpedMethods) { + if (!nativeLibSaver.Detected) + return false; + + var fileData = ModuleBytes ?? DeobUtils.readModule(module); + var peImage = new PeImage(fileData); + if (!nativeLibSaver.patch(peImage)) + return false; + + newFileData = fileData; + return true; + } + + public override IDeobfuscator moduleReloaded(ModuleDefinition module) { + var newOne = new Deobfuscator(options); + newOne.setModule(module); + newOne.decrypterType = new DecrypterType(module, decrypterType); + newOne.nativeLibSaver = new NativeLibSaver(module, nativeLibSaver); + return newOne; + } + public override void init(ModuleDefinition module) { base.init(module); } @@ -194,6 +218,12 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor3 { if (unpackedNativeFile) val += 100; + int sum = convert(unpackedNativeFile) + + convert(decrypterType.Detected) + + convert(nativeLibSaver.Detected); + if (sum > 0) + val += 100 + 10 * (sum - 1); + return val; } @@ -202,6 +232,10 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor3 { } protected override void scanForObfuscator() { + decrypterType = new DecrypterType(module); + decrypterType.find(); + nativeLibSaver = new NativeLibSaver(module); + nativeLibSaver.find(); obfuscatorName = detectVersion(); if (unpackedNativeFile) obfuscatorName += " (native)"; @@ -214,6 +248,23 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor3 { public override void deobfuscateBegin() { base.deobfuscateBegin(); + staticStringDecrypter.add(decrypterType.StringDecrypter1, (method2, args) => { + return decrypterType.decrypt1((string)args[0]); + }); + staticStringDecrypter.add(decrypterType.StringDecrypter2, (method2, args) => { + return decrypterType.decrypt2((string)args[0]); + }); + + if (Operations.DecryptStrings == OpDecryptString.None) + canRemoveDecrypterType = false; + + addCctorInitCallToBeRemoved(nativeLibSaver.InitMethod); + addResourceToBeRemoved(nativeLibSaver.Resource, "Native lib resource"); + addTypeToBeRemoved(nativeLibSaver.Type, "Native lib saver type"); + + foreach (var initMethod in decrypterType.InitMethods) + addCctorInitCallToBeRemoved(initMethod); + dumpUnpackedFiles(); startedDeobfuscating = true; @@ -229,6 +280,9 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor3 { if (options.RestoreTypes) new TypesRestorer(module).deobfuscate(); + if (canRemoveDecrypterType && !isTypeCalled(decrypterType.Type)) + addTypeToBeRemoved(decrypterType.Type, "Decrypter type"); + base.deobfuscateEnd(); } @@ -240,6 +294,8 @@ namespace de4dot.code.deobfuscators.dotNET_Reactor3 { public override IEnumerable getStringDecrypterMethods() { var list = new List(); + foreach (var method in decrypterType.StringDecrypters) + list.Add(method.MetadataToken.ToInt32().ToString("X8")); return list; } } diff --git a/de4dot.code/deobfuscators/dotNET_Reactor3/NativeLibSaver.cs b/de4dot.code/deobfuscators/dotNET_Reactor3/NativeLibSaver.cs new file mode 100644 index 00000000..30f1e269 --- /dev/null +++ b/de4dot.code/deobfuscators/dotNET_Reactor3/NativeLibSaver.cs @@ -0,0 +1,120 @@ +/* + Copyright (C) 2011 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 Mono.Cecil; +using de4dot.blocks; +using de4dot.code.PE; + +namespace de4dot.code.deobfuscators.dotNET_Reactor3 { + // Finds the type that saves the native lib (if in resources) to disk + class NativeLibSaver { + ModuleDefinition module; + TypeDefinition nativeLibCallerType; + MethodDefinition initMethod; + Resource nativeFileResource; + + public TypeDefinition Type { + get { return nativeLibCallerType; } + } + + public MethodDefinition InitMethod { + get { return initMethod; } + } + + public Resource Resource { + get { return nativeFileResource; } + } + + public bool Detected { + get { return nativeLibCallerType != null; } + } + + public NativeLibSaver(ModuleDefinition module) { + this.module = module; + } + + public NativeLibSaver(ModuleDefinition module, NativeLibSaver oldOne) { + this.module = module; + this.nativeLibCallerType = lookup(oldOne.nativeLibCallerType, "Could not find nativeLibCallerType"); + this.initMethod = lookup(oldOne.initMethod, "Could not find initMethod"); + if (oldOne.nativeFileResource != null) { + this.nativeFileResource = DotNetUtils.getResource(module, oldOne.nativeFileResource.Name); + if (this.nativeFileResource == null) + throw new ApplicationException("Could not find nativeFileResource"); + } + } + + T lookup(T def, string errorMessage) where T : MemberReference { + return DeobUtils.lookup(module, def, errorMessage); + } + + public void find() { + foreach (var info in DotNetUtils.getCalledMethods(module, DotNetUtils.getMethod(DotNetUtils.getModuleType(module), ".cctor"))) { + if (!DotNetUtils.isMethod(info.Item2, "System.Void", "()")) + continue; + if (info.Item1.FullName != "{F1C5056B-0AFC-4423-9B83-D13A26B48869}") + continue; + + nativeLibCallerType = info.Item1; + initMethod = info.Item2; + foreach (var s in DotNetUtils.getCodeStrings(initMethod)) { + nativeFileResource = DotNetUtils.getResource(module, s); + if (nativeFileResource != null) + break; + } + return; + } + } + + public bool patch(PeImage peImage) { + try { + return patch2(peImage); + } + catch { + Log.w("Could not patch the file"); + return false; + } + } + + bool patch2(PeImage peImage) { + uint numPatches = peImage.offsetReadUInt32(peImage.ImageLength - 4); + uint offset = checked(peImage.ImageLength - 4 - numPatches * 8); + + const uint magic = 2749; + for (uint i = 0; i < numPatches; i++, offset += 8) { + uint rva = checked((peImage.offsetReadUInt32(offset) - magic) / 3); + var value = peImage.offsetReadUInt32(offset + 4); + + if (value == 4) { + i++; + offset += 8; + rva = checked((peImage.offsetReadUInt32(offset) - magic) / 3); + value = peImage.offsetReadUInt32(offset); + } + else + value = checked((value - magic) / 3); + + peImage.dotNetSafeWrite(rva, BitConverter.GetBytes(value)); + } + + return true; + } + } +}