From 8df65610610f35d668765d7fef52af7ff86d2dd8 Mon Sep 17 00:00:00 2001 From: de4dot Date: Sat, 31 Dec 2011 13:14:02 +0100 Subject: [PATCH] Add Skater .NET support --- de4dot.code/de4dot.code.csproj | 3 + .../deobfuscators/Skater_NET/Deobfuscator.cs | 125 ++++++++++ .../Skater_NET/EnumClassFinder.cs | 81 ++++++ .../Skater_NET/StringDecrypter.cs | 233 ++++++++++++++++++ de4dot.cui/Program.cs | 1 + 5 files changed, 443 insertions(+) create mode 100644 de4dot.code/deobfuscators/Skater_NET/Deobfuscator.cs create mode 100644 de4dot.code/deobfuscators/Skater_NET/EnumClassFinder.cs create mode 100644 de4dot.code/deobfuscators/Skater_NET/StringDecrypter.cs diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 187942ec..3ef05933 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -112,6 +112,9 @@ + + + diff --git a/de4dot.code/deobfuscators/Skater_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Skater_NET/Deobfuscator.cs new file mode 100644 index 00000000..f515734c --- /dev/null +++ b/de4dot.code/deobfuscators/Skater_NET/Deobfuscator.cs @@ -0,0 +1,125 @@ +/* + 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.Collections.Generic; +using Mono.Cecil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Skater_NET { + public class DeobfuscatorInfo : DeobfuscatorInfoBase { + public const string THE_NAME = "Skater .NET"; + public const string THE_TYPE = "sk"; + public DeobfuscatorInfo() + : base() { + } + + 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 { + Options options; + + StringDecrypter stringDecrypter; + EnumClassFinder enumClassFinder; + + 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 DeobfuscatorInfo.THE_NAME; } + } + + public Deobfuscator(Options options) + : base(options) { + this.options = options; + StringFeatures = StringFeatures.AllowNoDecryption | StringFeatures.AllowStaticDecryption; + } + + protected override int detectInternal() { + int val = 0; + + if (stringDecrypter.Detected) + val += 100; + + return val; + } + + protected override void scanForObfuscator() { + stringDecrypter = new StringDecrypter(module); + + if (hasAssemblyReference("Microsoft.VisualBasic")) + stringDecrypter.find(); + } + + bool hasAssemblyReference(string name) { + foreach (var asmRef in module.AssemblyReferences) { + if (asmRef.Name == name) + return true; + } + return false; + } + + public override void deobfuscateBegin() { + base.deobfuscateBegin(); + + enumClassFinder = new EnumClassFinder(module); + + stringDecrypter.initialize(); + } + + public override void deobfuscateMethodEnd(Blocks blocks) { + if (Operations.DecryptStrings != OpDecryptString.None) + stringDecrypter.deobfuscate(blocks); + enumClassFinder.deobfuscate(blocks); + base.deobfuscateMethodEnd(blocks); + } + + public override void deobfuscateEnd() { + if (Operations.DecryptStrings != OpDecryptString.None) + addTypeToBeRemoved(stringDecrypter.Type, "String decrypter type"); + + base.deobfuscateEnd(); + } + + public override IEnumerable getStringDecrypterMethods() { + var list = new List(); + return list; + } + } +} diff --git a/de4dot.code/deobfuscators/Skater_NET/EnumClassFinder.cs b/de4dot.code/deobfuscators/Skater_NET/EnumClassFinder.cs new file mode 100644 index 00000000..1e4fee68 --- /dev/null +++ b/de4dot.code/deobfuscators/Skater_NET/EnumClassFinder.cs @@ -0,0 +1,81 @@ +/* + 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 Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Skater_NET { + class EnumClassFinder { + ModuleDefinition module; + FieldDefinition enumField; + + public EnumClassFinder(ModuleDefinition module) { + this.module = module; + find(); + } + + void find() { + foreach (var type in module.Types) { + if (type.HasEvents || type.HasProperties) + continue; + if (type.Methods.Count != 1) + continue; + if (type.Fields.Count != 1) + continue; + var method = type.Methods[0]; + if (method.Name != ".ctor") + continue; + var field = type.Fields[0]; + var fieldType = DotNetUtils.getType(module, field.FieldType); + if (fieldType == null) + continue; + if (!fieldType.IsEnum) + continue; + enumField = field; + return; + } + } + + public void deobfuscate(Blocks blocks) { + foreach (var block in blocks.MethodBlocks.getAllBlocks()) { + var instrs = block.Instructions; + for (int i = 0; i < instrs.Count - 2; i++) { + var ldsfld = instrs[i]; + if (ldsfld.OpCode.Code != Code.Ldsfld) + continue; + + var ldci4 = instrs[i + 1]; + if (!ldci4.isLdcI4()) + continue; + + var stfld = instrs[i + 2]; + if (stfld.OpCode.Code != Code.Stfld) + continue; + + var field = stfld.Operand as FieldReference; + if (!MemberReferenceHelper.compareFieldReferenceAndDeclaringType(enumField, field)) + continue; + block.remove(i, 3); + i--; + } + } + } + } +} diff --git a/de4dot.code/deobfuscators/Skater_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Skater_NET/StringDecrypter.cs new file mode 100644 index 00000000..28471637 --- /dev/null +++ b/de4dot.code/deobfuscators/Skater_NET/StringDecrypter.cs @@ -0,0 +1,233 @@ +/* + 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.Globalization; +using System.Security.Cryptography; +using System.Text; +using System.Text.RegularExpressions; +using Mono.Cecil; +using Mono.Cecil.Cil; +using de4dot.blocks; + +namespace de4dot.code.deobfuscators.Skater_NET { + class StringDecrypter { + ModuleDefinition module; + TypeDefinition decrypterType; + MethodDefinition decrypterCctor; + byte[] key; + byte[] iv; + FieldDefinitionAndDeclaringTypeDict fieldToDecryptedString = new FieldDefinitionAndDeclaringTypeDict(); + + public bool Detected { + get { return decrypterType != null; } + } + + public TypeDefinition Type { + get { return decrypterType; } + } + + public StringDecrypter(ModuleDefinition module) { + this.module = module; + } + + public void find() { + foreach (var type in module.Types) { + if (type.HasProperties || type.HasEvents) + continue; + + var cctor = DotNetUtils.getMethod(type, ".cctor"); + if (cctor == null) + continue; + + if (checkType(type)) { + decrypterType = type; + decrypterCctor = cctor; + return; + } + } + } + + public void initialize() { + if (decrypterCctor == null) + return; + + var instrs = decrypterCctor.Body.Instructions; + for (int i = 0; i < instrs.Count - 4; i++) { + var ldstr = instrs[i]; + if (ldstr.OpCode.Code != Code.Ldstr) + continue; + var encryptedString = ldstr.Operand as string; + if (encryptedString == null) + continue; + if (instrs[i + 1].OpCode.Code != Code.Stsfld) + continue; + if (instrs[i + 2].OpCode.Code != Code.Ldsfld) + continue; + if (instrs[i + 3].OpCode.Code != Code.Call) + continue; + if (instrs[i + 4].OpCode.Code != Code.Stsfld) + continue; + var field = instrs[i + 4].Operand as FieldDefinition; + if (field == null) + continue; + if (!MemberReferenceHelper.compareTypes(field.DeclaringType, decrypterType)) + continue; + + var decryptedString = Encoding.Unicode.GetString(DeobUtils.des3Decrypt(Convert.FromBase64String(encryptedString), key, iv)); + fieldToDecryptedString.add(field, decryptedString); + } + } + + bool checkType(TypeDefinition type) { + foreach (var method in type.Methods) { + if (!method.IsStatic || method.Body == null) + continue; + if (!DotNetUtils.isMethod(method, "System.String", "(System.String)")) + continue; + + var salt = getSalt(method); + if (salt == null) + continue; + + var password = getPassword(method); + if (string.IsNullOrEmpty(password)) + continue; + + var passwordBytes = new PasswordDeriveBytes(password, salt); + key = passwordBytes.GetBytes(16); + iv = passwordBytes.GetBytes(8); + return true; + } + + return false; + } + + static byte[] getSalt(MethodDefinition method) { + foreach (var s in DotNetUtils.getCodeStrings(method)) { + var saltAry = fixSalt(s); + if (saltAry != null) + return saltAry; + } + + return null; + } + + static byte[] fixSalt(string s) { + if (s.Length < 10 || s.Length > 30 || s.Length / 2 * 2 != s.Length) + return null; + + var ary = s.ToCharArray(); + Array.Reverse(ary); + for (int i = 0; i < ary.Length; i++) + ary[i]--; + var s2 = new string(ary); + + var saltAry = new byte[(int)Math.Round((double)s2.Length / 2 - 1) + 1]; + for (int i = 0; i < saltAry.Length; i++) { + int result; + if (!int.TryParse(s2.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out result)) + return null; + saltAry[i] = (byte)result; + } + + return saltAry; + } + + string getPassword(MethodDefinition decryptMethod) { + foreach (var info in DotNetUtils.getCalledMethods(module, decryptMethod)) { + var method = info.Item2; + if (!method.IsStatic || method.Body == null) + continue; + if (!MemberReferenceHelper.compareTypes(method.DeclaringType, decryptMethod.DeclaringType)) + continue; + if (!DotNetUtils.isMethod(method, "System.String", "()")) + continue; + + var hexChars = getPassword2(method); + if (string.IsNullOrEmpty(hexChars)) + continue; + + var password = fixPassword(hexChars); + if (string.IsNullOrEmpty(password)) + continue; + + return password; + } + return null; + } + + string fixPassword(string hexChars) { + var ary = hexChars.Trim().Split(' '); + string password = ""; + for (int i = 0; i < ary.Length; i++) { + int result; + if (!int.TryParse(ary[i], NumberStyles.AllowHexSpecifier, null, out result)) + return null; + password += (char)result; + } + return password; + } + + string getPassword2(MethodDefinition method) { + string password = ""; + foreach (var info in DotNetUtils.getCalledMethods(module, method)) { + var s = getPassword3(info.Item2); + if (string.IsNullOrEmpty(s)) + return null; + + password += s; + } + return password; + } + + string getPassword3(MethodDefinition method) { + var strings = new List(DotNetUtils.getCodeStrings(method)); + if (strings.Count != 1) + return null; + + var s = strings[0]; + if (!Regex.IsMatch(s, @"^[a-fA-F0-9]{2} $")) + return null; + + return s; + } + + 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 decrypted = fieldToDecryptedString.find(field); + if (decrypted == null) + continue; + + instrs[i] = new Instr(Instruction.Create(OpCodes.Ldstr, decrypted)); + Log.v("Decrypted string: {0}", Utils.toCsharpString(decrypted)); + } + } + } + } +} diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs index 98a8ec6d..a334e734 100644 --- a/de4dot.cui/Program.cs +++ b/de4dot.cui/Program.cs @@ -36,6 +36,7 @@ namespace de4dot.cui { new de4dot.code.deobfuscators.dotNET_Reactor.v3.DeobfuscatorInfo(), new de4dot.code.deobfuscators.dotNET_Reactor.v4.DeobfuscatorInfo(), new de4dot.code.deobfuscators.Eazfuscator.DeobfuscatorInfo(), + new de4dot.code.deobfuscators.Skater_NET.DeobfuscatorInfo(), new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(), new de4dot.code.deobfuscators.Xenocode.DeobfuscatorInfo(), };