diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index f1f330ca..5659af29 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -194,6 +194,8 @@ + + diff --git a/de4dot.code/deobfuscators/Rummage/Deobfuscator.cs b/de4dot.code/deobfuscators/Rummage/Deobfuscator.cs new file mode 100644 index 00000000..f99473bb --- /dev/null +++ b/de4dot.code/deobfuscators/Rummage/Deobfuscator.cs @@ -0,0 +1,110 @@ +/* + Copyright (C) 2011-2012 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 Mono.Cecil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Rummage { + public class DeobfuscatorInfo : DeobfuscatorInfoBase { + public const string THE_NAME = "Rummage"; + public const string THE_TYPE = "rm"; + const string DEFAULT_REGEX = @"!."; + public DeobfuscatorInfo() + : base(DEFAULT_REGEX) { + } + + public override string Name { + get { return THE_NAME; } + } + + public override string Type { + get { return THE_TYPE; } + } + + public override IDeobfuscator createDeobfuscator() { + return new Deobfuscator(new Deobfuscator.Options { + ValidNameRegex = validNameRegex.get(), + }); + } + } + + class Deobfuscator : DeobfuscatorBase { + StringDecrypter stringDecrypter; + + internal class Options : OptionsBase { + } + + public override string Type { + get { return DeobfuscatorInfo.THE_TYPE; } + } + + public override string TypeLong { + get { return DeobfuscatorInfo.THE_NAME; } + } + + public override string Name { + get { return TypeLong; } + } + + public Deobfuscator(Options options) + : base(options) { + } + + protected override int detectInternal() { + int val = 0; + + int sum = toInt32(stringDecrypter.Detected); + if (sum > 0) + val += 100 + 10 * (sum - 1); + + return val; + } + + protected override void scanForObfuscator() { + stringDecrypter = new StringDecrypter(module); + stringDecrypter.find(); + } + + public override void deobfuscateBegin() { + base.deobfuscateBegin(); + + stringDecrypter.initialize(); + } + + + public override void deobfuscateMethodEnd(Blocks blocks) { + if (CanRemoveStringDecrypterType) + stringDecrypter.deobfuscate(blocks); + base.deobfuscateMethodEnd(blocks); + } + + public override void deobfuscateEnd() { + if (CanRemoveStringDecrypterType) { + addTypeToBeRemoved(stringDecrypter.Type, "String decrypter type"); + addTypesToBeRemoved(stringDecrypter.OtherTypes, "Decrypted string type"); + } + base.deobfuscateEnd(); + } + + public override IEnumerable getStringDecrypterMethods() { + return new List(); + } + } +} diff --git a/de4dot.code/deobfuscators/Rummage/StringDecrypter.cs b/de4dot.code/deobfuscators/Rummage/StringDecrypter.cs new file mode 100644 index 00000000..fc390be8 --- /dev/null +++ b/de4dot.code/deobfuscators/Rummage/StringDecrypter.cs @@ -0,0 +1,256 @@ +/* + Copyright (C) 2011-2012 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.IO; +using System.Text; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Rummage { + class StringDecrypter { + ModuleDefinition module; + MethodDefinition stringDecrypterMethod; + FieldDefinitionAndDeclaringTypeDict stringInfos = new FieldDefinitionAndDeclaringTypeDict(); + int fileDispl; + uint[] key; + BinaryReader reader; + + class StringInfo { + public readonly FieldDefinition field; + public readonly int stringId; + public string decrypted; + + public StringInfo(FieldDefinition field, int stringId) { + this.field = field; + this.stringId = stringId; + } + + public override string ToString() { + if (decrypted != null) + return string.Format("{0:X8} - {1}", stringId, Utils.toCsharpString(decrypted)); + return string.Format("{0:X8}", stringId); + } + } + + public TypeDefinition Type { + get { return stringDecrypterMethod != null ? stringDecrypterMethod.DeclaringType : null; } + } + + public IEnumerable OtherTypes { + get { + var list = new List(stringInfos.Count); + foreach (var info in stringInfos.getValues()) + list.Add(info.field.DeclaringType); + return list; + } + } + + public bool Detected { + get { return stringDecrypterMethod != null; } + } + + public StringDecrypter(ModuleDefinition module) { + this.module = module; + } + + public void find() { + foreach (var type in module.Types) { + var method = checkType(type); + if (method == null) + continue; + if (!getDispl(method, ref fileDispl)) + continue; + + stringDecrypterMethod = method; + break; + } + } + + static readonly string[] requiredFields = new string[] { + "System.UInt32[]", + }; + static readonly string[] requiredLocals = new string[] { + "System.Byte[]", + "System.Int32", + "System.IO.FileStream", + }; + static MethodDefinition checkType(TypeDefinition type) { + if (!new FieldTypes(type).exactly(requiredFields)) + return null; + var cctor = DotNetUtils.getMethod(type, ".cctor"); + if (cctor == null) + return null; + if (!new LocalTypes(cctor).all(requiredLocals)) + return null; + + return checkMethods(type); + } + + static MethodDefinition checkMethods(TypeDefinition type) { + MethodDefinition cctor = null, decrypterMethod = null; + foreach (var method in type.Methods) { + if (!method.IsStatic || method.Body == null) + return null; + if (method.Name == ".cctor") + cctor = method; + else if (DotNetUtils.isMethod(method, "System.String", "(System.Int32)")) + decrypterMethod = method; + else + return null; + } + if (cctor == null || decrypterMethod == null) + return null; + + return decrypterMethod; + } + + static bool getDispl(MethodDefinition method, ref int displ) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 2; i++) { + var mul = instrs[i]; + if (mul.OpCode.Code != Code.Mul) + continue; + + var ldci4 = instrs[i + 1]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + + var sub = instrs[i + 2]; + if (sub.OpCode.Code != Code.Sub) + continue; + + displ = DotNetUtils.getLdcI4Value(ldci4); + return true; + } + + return false; + } + + public void initialize() { + reader = new BinaryReader(new FileStream(module.FullyQualifiedName, FileMode.Open, FileAccess.Read, FileShare.Read)); + initKey(); + + foreach (var type in module.Types) + initType(type); + } + + void initKey() { + reader.BaseStream.Position = reader.BaseStream.Length - 48; + key = new uint[4]; + for (int i = 0; i < key.Length; i++) + key[i] = reader.ReadUInt32(); + } + + void initType(TypeDefinition type) { + var cctor = DotNetUtils.getMethod(type, ".cctor"); + if (cctor == null) + return; + var info = getStringInfo(cctor); + if (info == null) + return; + + stringInfos.add(info.field, info); + } + + StringInfo getStringInfo(MethodDefinition method) { + if (method == null || method.Body == null) + return null; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 2; i++) { + var ldci4 = instrs[i]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + int stringId = DotNetUtils.getLdcI4Value(ldci4); + + var call = instrs[i + 1]; + if (call.OpCode.Code != Code.Call) + continue; + var calledMethod = call.Operand as MethodReference; + if (!MemberReferenceHelper.compareMethodReferenceAndDeclaringType(stringDecrypterMethod, calledMethod)) + continue; + + var stsfld = instrs[i + 2]; + if (stsfld.OpCode.Code != Code.Stsfld) + continue; + var field = stsfld.Operand as FieldDefinition; + if (field == null) + continue; + + return new StringInfo(field, stringId); + } + + return null; + } + + public void deobfuscate(Blocks blocks) { + foreach (var block in blocks.MethodBlocks.getAllBlocks()) { + var instrs = block.Instructions; + for (int i = 0; i < instrs.Count; i++) { + var instr = instrs[i]; + + if (instr.OpCode.Code != Code.Ldsfld) + continue; + + var field = instr.Operand as FieldReference; + if (field == null) + continue; + var info = stringInfos.find(field); + if (info == null) + continue; + var decrypted = decrypt(info); + + instrs[i] = new Instr(Instruction.Create(OpCodes.Ldstr, decrypted)); + Log.v("Decrypted string: {0}", Utils.toCsharpString(decrypted)); + } + } + } + + string decrypt(StringInfo info) { + if (info.decrypted == null) + info.decrypted = decrypt(info.stringId); + + return info.decrypted; + } + + string decrypt(int stringId) { + reader.BaseStream.Position = reader.BaseStream.Length + (stringId * 4 - fileDispl); + + uint v0 = reader.ReadUInt32(); + uint v1 = reader.ReadUInt32(); + DeobUtils.xteaDecrypt(ref v0, ref v1, key, 32); + int utf8Length = (int)v0; + var decrypted = new uint[(utf8Length + 11) / 8 * 2 - 1]; + decrypted[0] = v1; + for (int i = 1; i + 1 < decrypted.Length; i += 2) { + v0 = reader.ReadUInt32(); + v1 = reader.ReadUInt32(); + DeobUtils.xteaDecrypt(ref v0, ref v1, key, 32); + decrypted[i] = v0; + decrypted[i + 1] = v1; + } + + var utf8 = new byte[utf8Length]; + Buffer.BlockCopy(decrypted, 0, utf8, 0, utf8.Length); + return Encoding.UTF8.GetString(utf8); + } + } +} diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs index a3a5f5ac..f8e2472b 100644 --- a/de4dot.cui/Program.cs +++ b/de4dot.cui/Program.cs @@ -48,6 +48,7 @@ namespace de4dot.cui { new de4dot.code.deobfuscators.Eazfuscator_NET.DeobfuscatorInfo(), new de4dot.code.deobfuscators.Goliath_NET.DeobfuscatorInfo(), new de4dot.code.deobfuscators.MaxtoCode.DeobfuscatorInfo(), + new de4dot.code.deobfuscators.Rummage.DeobfuscatorInfo(), new de4dot.code.deobfuscators.Skater_NET.DeobfuscatorInfo(), new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(), new de4dot.code.deobfuscators.Spices_Net.DeobfuscatorInfo(),