Add CO deobfuscator. Can decrypt embedded assemblies.

This commit is contained in:
de4dot 2011-10-22 14:31:38 +02:00
parent 99bd79e418
commit 3f1b9152bd
14 changed files with 513 additions and 52 deletions

View File

@ -468,6 +468,10 @@ namespace de4dot.blocks {
public static IList<Instruction> getInstructions(IList<Instruction> instructions, int i, params OpCode[] opcodes) {
if (i + opcodes.Length > instructions.Count)
return null;
if (opcodes.Length == 0)
return new List<Instruction>();
if (opcodes[0] != instructions[i].OpCode)
return null;
var list = new List<Instruction>(opcodes.Length);
for (int j = 0; j < opcodes.Length; j++) {

View File

@ -749,9 +749,11 @@ namespace de4dot {
deobfuscate(method, "Static string decryption", (blocks) => theDeob.deobfuscateStrings(blocks));
}
void IDeobfuscatedFile.createAssemblyFile(byte[] data, string assemblyName) {
void IDeobfuscatedFile.createAssemblyFile(byte[] data, string assemblyName, string extension) {
if (extension == null)
extension = ".dll";
var baseDir = Utils.getDirName(options.NewFilename);
var newName = Path.Combine(baseDir, assemblyName + ".dll");
var newName = Path.Combine(baseDir, assemblyName + extension);
Log.n("Creating file {0}", newName);
using (var writer = new BinaryWriter(new FileStream(newName, FileMode.Create))) {
writer.Write(data);

View File

@ -30,6 +30,7 @@ namespace de4dot {
return new List<IDeobfuscatorInfo> {
new de4dot.deobfuscators.Unknown.DeobfuscatorInfo(),
new de4dot.deobfuscators.CliSecure.DeobfuscatorInfo(),
new de4dot.deobfuscators.CryptoObfuscator.DeobfuscatorInfo(),
new de4dot.deobfuscators.Dotfuscator.DeobfuscatorInfo(),
new de4dot.deobfuscators.Eazfuscator.DeobfuscatorInfo(),
new de4dot.deobfuscators.SmartAssembly.DeobfuscatorInfo(),

View File

@ -191,5 +191,12 @@ namespace de4dot {
return false;
return left.Substring(0, right.Length).Equals(right, stringComparison);
}
public static string getAssemblySimpleName(string name) {
int i = name.IndexOf(',');
if (i < 0)
return name;
return name.Substring(0, i);
}
}
}

View File

@ -57,6 +57,10 @@
<Compile Include="CommandLineParser.cs" />
<Compile Include="deobfuscators\CliSecure\Deobfuscator.cs" />
<Compile Include="deobfuscators\CliSecure\ProxyDelegateFinder.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\AssemblyResolver.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\Deobfuscator.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\ResourceDecrypter.cs" />
<Compile Include="deobfuscators\CryptoObfuscator\ResourceResolver.cs" />
<Compile Include="deobfuscators\DeobfuscatorBase.cs" />
<Compile Include="deobfuscators\DeobfuscatorInfoBase.cs" />
<Compile Include="deobfuscators\DeobUtils.cs" />
@ -80,7 +84,6 @@
<Compile Include="deobfuscators\SmartAssembly\ResourceDecrypterInfo.cs" />
<Compile Include="deobfuscators\SmartAssembly\ResourceResolver.cs" />
<Compile Include="deobfuscators\SmartAssembly\ResourceResolverInfo.cs" />
<Compile Include="deobfuscators\SmartAssembly\SA_Utils.cs" />
<Compile Include="deobfuscators\SmartAssembly\SimpleZipInfo.cs" />
<Compile Include="deobfuscators\SmartAssembly\StringDecrypter.cs" />
<Compile Include="deobfuscators\SmartAssembly\StringDecrypterInfo.cs" />

View File

@ -0,0 +1,144 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.deobfuscators.CryptoObfuscator {
class AssemblyResolver {
ModuleDefinition module;
ResourceDecrypter resourceDecrypter;
TypeDefinition resolverType;
List<AssemblyInfo> assemblyInfos;
public class AssemblyInfo {
public string assemblyName;
public EmbeddedResource resource;
public EmbeddedResource symbolsResource;
public AssemblyInfo(string assemblyName, EmbeddedResource resource, EmbeddedResource symbolsResource) {
this.assemblyName = assemblyName;
this.resource = resource;
this.symbolsResource = symbolsResource;
}
public override string ToString() {
return string.Format("{{{0} => {1}}}", assemblyName, resource.Name);
}
}
public List<AssemblyInfo> AssemblyInfos {
get { return assemblyInfos; }
}
public AssemblyResolver(ModuleDefinition module, ResourceDecrypter resourceDecrypter) {
this.module = module;
this.resourceDecrypter = resourceDecrypter;
}
public void find() {
var cctor = DotNetUtils.getMethod(DotNetUtils.getModuleType(module), ".cctor");
if (cctor == null)
return;
foreach (var tuple in DotNetUtils.getCalledMethods(module, cctor)) {
var method = tuple.Item2;
if (method.Name == ".cctor" || method.Name == ".ctor")
continue;
if (!method.IsStatic || !DotNetUtils.isMethod(method, "System.Void", "()"))
continue;
if (checkType(tuple.Item1, method))
break;
}
}
bool checkType(TypeDefinition type, MethodDefinition initMethod) {
if (DotNetUtils.findFieldType(type, "System.Collections.Hashtable", true) == null)
return false;
if (!checkInitMethod(initMethod))
return false;
List<AssemblyInfo> newAssemblyInfos = null;
foreach (var s in DotNetUtils.getCodeStrings(initMethod)) {
newAssemblyInfos = initializeEmbeddedAssemblies(s);
if (newAssemblyInfos != null)
break;
}
if (newAssemblyInfos == null)
return false;
resolverType = type;
assemblyInfos = newAssemblyInfos;
return true;
}
bool checkInitMethod(MethodDefinition initMethod) {
if (!initMethod.HasBody)
return false;
var instructions = initMethod.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Ldnull, OpCodes.Ldftn, OpCodes.Newobj);
if (instrs == null)
continue;
MethodReference methodRef;
var ldftn = instrs[1];
var newobj = instrs[2];
methodRef = ldftn.Operand as MethodReference;
if (methodRef == null || !MemberReferenceHelper.compareTypes(initMethod.DeclaringType, methodRef.DeclaringType))
continue;
methodRef = newobj.Operand as MethodReference;
if (methodRef == null || methodRef.FullName != "System.Void System.ResolveEventHandler::.ctor(System.Object,System.IntPtr)")
continue;
return true;
}
return false;
}
List<AssemblyInfo> initializeEmbeddedAssemblies(string s) {
var sb = new StringBuilder(s.Length);
foreach (var c in s)
sb.Append((char)~c);
var tmpAssemblyInfos = sb.ToString().Split(new string[] { "##" }, StringSplitOptions.RemoveEmptyEntries);
if (tmpAssemblyInfos.Length == 0 || (tmpAssemblyInfos.Length & 1) == 1)
return null;
var newAssemblyInfos = new List<AssemblyInfo>(tmpAssemblyInfos.Length / 2);
for (int i = 0; i < tmpAssemblyInfos.Length; i += 2) {
var assemblyName = tmpAssemblyInfos[i];
var resourceName = tmpAssemblyInfos[i + 1];
var resource = DotNetUtils.getResource(module, resourceName) as EmbeddedResource;
var symbolsResource = DotNetUtils.getResource(module, resourceName + "#") as EmbeddedResource;
if (resource == null)
return null;
newAssemblyInfos.Add(new AssemblyInfo(assemblyName, resource, symbolsResource));
}
return newAssemblyInfos;
}
}
}

View File

@ -0,0 +1,161 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Mono.Cecil;
using de4dot.blocks;
namespace de4dot.deobfuscators.CryptoObfuscator {
class DeobfuscatorInfo : DeobfuscatorInfoBase {
const string DEFAULT_REGEX = @"!^[A-Z]{1,3}(?:`\d+)?$&!^c[0-9a-f]{32}(?:`\d+)?$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
public DeobfuscatorInfo()
: base("co", DEFAULT_REGEX) {
}
internal static string ObfuscatorType {
get { return "CryptoObfuscator"; }
}
public override string Type {
get { return ObfuscatorType; }
}
public override IDeobfuscator createDeobfuscator() {
return new Deobfuscator(new Deobfuscator.Options {
ValidNameRegex = validNameRegex.get(),
});
}
protected override IEnumerable<Option> getOptionsInternal() {
return new List<Option>() {
};
}
}
class Deobfuscator : DeobfuscatorBase {
Options options;
string obfuscatorName = "Crypto Obfuscator";
bool foundCryptoObfuscatorAttribute = false;
bool foundObfuscatedSymbols = false;
ResourceDecrypter resourceDecrypter;
ResourceResolver resourceResolver;
AssemblyResolver assemblyResolver;
internal class Options : OptionsBase {
}
public override string Type {
get { return DeobfuscatorInfo.ObfuscatorType; }
}
public override string Name {
get { return obfuscatorName; }
}
public Deobfuscator(Options options)
: base(options) {
this.options = options;
}
public override void init(ModuleDefinition module) {
base.init(module);
}
public override int detect() {
scanForObfuscator();
int val = 0;
if (foundCryptoObfuscatorAttribute)
val += 100;
else if (foundObfuscatedSymbols)
val += 10;
return val;
}
protected override void scanForObfuscatorInternal() {
foreach (var type in module.Types) {
if (type.FullName == "CryptoObfuscator.ProtectedWithCryptoObfuscatorAttribute") {
foundCryptoObfuscatorAttribute = true;
addAttributeToBeRemoved(type, "Obfuscator attribute");
initializeVersion(type);
}
}
if (checkCryptoObfuscator())
foundObfuscatedSymbols = true;
}
void initializeVersion(TypeDefinition attr) {
var s = DotNetUtils.getCustomArgAsString(getAssemblyAttribute(attr), 0);
if (s == null)
return;
var val = Regex.Match(s, @"^Protected with (Crypto Obfuscator.*)$");
if (val.Groups.Count < 2)
return;
obfuscatorName = val.Groups[1].ToString();
return;
}
bool checkCryptoObfuscator() {
int matched = 0;
foreach (var type in module.Types) {
if (type.Namespace != "A")
continue;
if (Regex.IsMatch(type.Name, "^c[0-9a-f]{32}$"))
return true;
else if (Regex.IsMatch(type.Name, "^A[A-Z]*$")) {
if (++matched >= 10)
return true;
}
}
return false;
}
public override void deobfuscateBegin() {
base.deobfuscateBegin();
resourceDecrypter = new ResourceDecrypter(module);
resourceResolver = new ResourceResolver(module, resourceDecrypter);
assemblyResolver = new AssemblyResolver(module, resourceDecrypter);
resourceResolver.find();
assemblyResolver.find();
dumpEmbeddedAssemblies();
}
void dumpEmbeddedAssemblies() {
foreach (var info in assemblyResolver.AssemblyInfos) {
dumpEmbeddedFile(info.resource, info.assemblyName, true);
if (info.symbolsResource != null)
dumpEmbeddedFile(info.symbolsResource, info.assemblyName, false);
}
}
void dumpEmbeddedFile(EmbeddedResource resource, string assemblyName, bool isAssembly) {
string extension = isAssembly ? ".dll" : ".pdb";
DeobfuscatedFile.createAssemblyFile(resourceDecrypter.decrypt(resource.GetResourceStream()), Utils.getAssemblySimpleName(assemblyName), extension);
addResourceToBeRemoved(resource, string.Format("Embedded assembly{0}: {1}", isAssembly ? "" : " (pdb)", assemblyName));
}
}
}

View File

@ -0,0 +1,103 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using System;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using Mono.Cecil;
namespace de4dot.deobfuscators.CryptoObfuscator {
class ResourceDecrypter {
const int BUFLEN = 0x8000;
ModuleDefinition module;
byte[] buffer1 = new byte[BUFLEN];
byte[] buffer2 = new byte[BUFLEN];
public ResourceDecrypter(ModuleDefinition module) {
this.module = module;
}
public byte[] decrypt(Stream resourceStream) {
byte flags = (byte)resourceStream.ReadByte();
Stream sourceStream = resourceStream;
if ((flags & 1) != 0) {
var memStream = new MemoryStream((int)resourceStream.Length);
using (var provider = new DESCryptoServiceProvider()) {
var iv = new byte[8];
sourceStream.Read(iv, 0, 8);
provider.IV = iv;
provider.Key = getKey(sourceStream);
using (var transform = provider.CreateDecryptor()) {
while (true) {
int count = sourceStream.Read(buffer1, 0, buffer1.Length);
if (count <= 0)
break;
int count2 = transform.TransformBlock(buffer1, 0, count, buffer2, 0);
memStream.Write(buffer2, 0, count2);
}
var finalData = transform.TransformFinalBlock(buffer1, 0, 0);
memStream.Write(finalData, 0, finalData.Length);
}
}
sourceStream = memStream;
}
if ((flags & 2) != 0) {
var memStream = new MemoryStream((int)resourceStream.Length);
sourceStream.Position = 0;
var inflater = new DeflateStream(sourceStream, CompressionMode.Decompress);
while (true) {
int count = inflater.Read(buffer1, 0, buffer1.Length);
if (count <= 0)
break;
memStream.Write(buffer1, 0, count);
}
sourceStream = memStream;
}
if (sourceStream is MemoryStream) {
var memStream = (MemoryStream)sourceStream;
return memStream.ToArray();
}
else {
int len = (int)(sourceStream.Length - sourceStream.Position);
byte[] data = new byte[len];
sourceStream.Read(data, 0, len);
return data;
}
}
byte[] getKey(Stream resourceStream) {
byte[] key = new byte[8];
resourceStream.Read(key, 0, key.Length);
for (int i = 0; i < key.Length; i++) {
if (key[i] != 0)
return key;
}
key = module.Assembly.Name.PublicKeyToken;
if (key == null)
throw new ApplicationException("PublicKeyToken is null, can't decrypt resources");
return key;
}
}
}

View File

@ -0,0 +1,82 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.deobfuscators.CryptoObfuscator {
class ResourceResolver {
ModuleDefinition module;
ResourceDecrypter resourceDecrypter;
TypeDefinition resolverType;
public ResourceResolver(ModuleDefinition module, ResourceDecrypter resourceDecrypter) {
this.module = module;
this.resourceDecrypter = resourceDecrypter;
}
public void find() {
var cctor = DotNetUtils.getMethod(DotNetUtils.getModuleType(module), ".cctor");
if (cctor == null)
return;
foreach (var tuple in DotNetUtils.getCalledMethods(module, cctor)) {
var method = tuple.Item2;
if (method.Name == ".cctor" || method.Name == ".ctor")
continue;
if (!method.IsStatic || !DotNetUtils.isMethod(method, "System.Void", "()"))
continue;
if (checkType(tuple.Item1, method))
break;
}
}
bool checkType(TypeDefinition type, MethodDefinition initMethod) {
if (!initMethod.HasBody)
return false;
if (DotNetUtils.findFieldType(type, "System.Reflection.Assembly", true) == null)
return false;
var instructions = initMethod.Body.Instructions;
for (int i = 0; i < instructions.Count; i++) {
var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Ldnull, OpCodes.Ldftn, OpCodes.Newobj);
if (instrs == null)
continue;
MethodReference methodRef;
var ldftn = instrs[1];
var newobj = instrs[2];
methodRef = ldftn.Operand as MethodReference;
if (methodRef == null || !MemberReferenceHelper.compareTypes(type, methodRef.DeclaringType))
continue;
methodRef = newobj.Operand as MethodReference;
if (methodRef == null || methodRef.FullName != "System.Void System.ResolveEventHandler::.ctor(System.Object,System.IntPtr)")
continue;
resolverType = type;
return true;
}
return false;
}
}
}

View File

@ -19,7 +19,7 @@
namespace de4dot.deobfuscators {
interface IDeobfuscatedFile : ISimpleDeobfuscator {
void createAssemblyFile(byte[] data, string assemblyName);
void createAssemblyFile(byte[] data, string assemblyName, string extension = null);
void stringDecryptersAdded();
}
}

View File

@ -68,7 +68,7 @@ namespace de4dot.deobfuscators.SmartAssembly {
info.assemblyName = Encoding.UTF8.GetString(Convert.FromBase64String(encName));
info.resourceName = rsrcName;
info.resource = DotNetUtils.getResource(module, rsrcName) as EmbeddedResource;
info.simpleName = SA_Utils.getAssemblySimpleName(info.assemblyName);
info.simpleName = Utils.getAssemblySimpleName(info.assemblyName);
return info;
}

View File

@ -53,7 +53,7 @@ namespace de4dot.deobfuscators.SmartAssembly {
if (s == null || calledMethod == null)
continue;
resource = DotNetUtils.getResource(module, SA_Utils.getAssemblySimpleName(s)) as EmbeddedResource;
resource = DotNetUtils.getResource(module, Utils.getAssemblySimpleName(s)) as EmbeddedResource;
if (resource != null)
break;
}

View File

@ -1,29 +0,0 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
namespace de4dot.deobfuscators.SmartAssembly {
static class SA_Utils {
public static string getAssemblySimpleName(string name) {
int i = name.IndexOf(',');
if (i < 0)
return name;
return name.Substring(0, i);
}
}
}

View File

@ -90,8 +90,6 @@ namespace de4dot.deobfuscators.Unknown {
return "CodeFort";
if (type.FullName == "____KILL")
return "DeployLX CodeVeil";
if (type.FullName == "CryptoObfuscator.ProtectedWithCryptoObfuscatorAttribute")
return "Crypto Obfuscator";
if (type.FullName.Contains("ObfuscatedByGoliath"))
return "Goliath .NET Obfuscator";
if (type.FullName == "Xenocode.Client.Attributes.AssemblyAttributes.ProcessedByXenocode")
@ -109,21 +107,6 @@ namespace de4dot.deobfuscators.Unknown {
if (type.FullName == "YanoAttribute")
return "Yano Obfuscator";
}
return checkCryptoObfuscator();
}
string checkCryptoObfuscator() {
int matched = 0;
foreach (var type in module.Types) {
if (type.Namespace != "A")
continue;
if (Regex.IsMatch(type.Name, "^c[0-9a-f]{32}$"))
return "Crypto Obfuscator";
else if (Regex.IsMatch(type.Name, "^A[A-Z]*$")) {
if (++matched >= 10)
return "Crypto Obfuscator";
}
}
return null;
}
}