Add Goliath.NET obfuscator support

This commit is contained in:
de4dot 2011-12-29 08:26:36 +01:00
parent b52c5f12fe
commit 1fc70d8d9e
13 changed files with 1351 additions and 0 deletions

View File

@ -104,6 +104,17 @@
<Compile Include="deobfuscators\dotNET_Reactor\v3\MemoryPatcher.cs" />
<Compile Include="deobfuscators\Eazfuscator\Deobfuscator.cs" />
<Compile Include="deobfuscators\ExceptionLoggerRemover.cs" />
<Compile Include="deobfuscators\Goliath_NET\ArrayDecrypter.cs" />
<Compile Include="deobfuscators\Goliath_NET\ArrayValueInliner.cs" />
<Compile Include="deobfuscators\Goliath_NET\DecrypterBase.cs" />
<Compile Include="deobfuscators\Goliath_NET\Deobfuscator.cs" />
<Compile Include="deobfuscators\Goliath_NET\IntegerDecrypter.cs" />
<Compile Include="deobfuscators\Goliath_NET\IntegerValueInliner.cs" />
<Compile Include="deobfuscators\Goliath_NET\LocalsRestorer.cs" />
<Compile Include="deobfuscators\Goliath_NET\LogicalExpressionFixer.cs" />
<Compile Include="deobfuscators\Goliath_NET\ProxyDelegateFinder.cs" />
<Compile Include="deobfuscators\Goliath_NET\StringDecrypter.cs" />
<Compile Include="deobfuscators\Goliath_NET\StrongNameChecker.cs" />
<Compile Include="deobfuscators\IDeobfuscatedFile.cs" />
<Compile Include="deobfuscators\IDeobfuscator.cs" />
<Compile Include="deobfuscators\IDeobfuscatorInfo.cs" />

View File

@ -0,0 +1,47 @@
/*
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 Mono.Cecil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class ArrayDecrypter : DecrypterBase {
public ArrayDecrypter(ModuleDefinition module)
: base(module) {
}
protected override string[] getRequiredFieldTypes() {
return new string[] {
"System.Byte[]",
"System.Collections.Generic.Dictionary`2<System.Int32,System.Byte[]>",
};
}
protected override bool checkDelegateInvokeMethod(MethodDefinition invokeMethod) {
return DotNetUtils.isMethod(invokeMethod, "System.Byte[]", "(System.Int32)");
}
public byte[] decrypt(MethodDefinition method) {
var info = getInfo(method);
decryptedReader.BaseStream.Position = info.offset;
return decryptedReader.ReadBytes(decryptedReader.ReadInt32());
}
}
}

View File

@ -0,0 +1,96 @@
/*
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 Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class ArrayValueInliner : MethodReturnValueInliner {
MethodDefinitionAndDeclaringTypeDict<Func<MethodDefinition, object[], byte[]>> intDecrypters = new MethodDefinitionAndDeclaringTypeDict<Func<MethodDefinition, object[], byte[]>>();
InitializedDataCreator initializedDataCreator;
ModuleDefinition module;
MethodReference initializeArrayMethod;
class MyCallResult : CallResult {
public MethodReference methodReference;
public MyCallResult(Block block, int callEndIndex, MethodReference method)
: base(block, callEndIndex) {
this.methodReference = method;
}
}
public bool HasHandlers {
get { return intDecrypters.Count != 0; }
}
public ArrayValueInliner(ModuleDefinition module, InitializedDataCreator initializedDataCreator) {
this.module = module;
this.initializedDataCreator = initializedDataCreator;
var runtimeHelpersType = DotNetUtils.findOrCreateTypeReference(module, module.TypeSystem.Corlib as AssemblyNameReference, "System.Runtime.CompilerServices", "RuntimeHelpers", false);
initializeArrayMethod = new MethodReference("InitializeArray", module.TypeSystem.Void, runtimeHelpersType);
var systemArrayType = DotNetUtils.findOrCreateTypeReference(module, module.TypeSystem.Corlib as AssemblyNameReference, "System", "Array", false);
var runtimeFieldHandleType = DotNetUtils.findOrCreateTypeReference(module, module.TypeSystem.Corlib as AssemblyNameReference, "System", "RuntimeFieldHandle", true);
initializeArrayMethod.Parameters.Add(new ParameterDefinition(systemArrayType));
initializeArrayMethod.Parameters.Add(new ParameterDefinition(runtimeFieldHandleType));
}
public void add(MethodDefinition method, Func<MethodDefinition, object[], byte[]> handler) {
if (method == null)
return;
if (intDecrypters.find(method) != null)
throw new ApplicationException(string.Format("Handler for method {0:X8} has already been added", method.MetadataToken.ToInt32()));
intDecrypters.add(method, handler);
}
protected override void inlineReturnValues(IList<CallResult> callResults) {
foreach (var callResult in callResults) {
var block = callResult.block;
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
var ary = (byte[])callResult.returnValue;
int index = callResult.callStartIndex;
block.replace(index++, num, DotNetUtils.createLdci4(ary.Length));
block.insert(index++, Instruction.Create(OpCodes.Newarr, module.TypeSystem.Byte));
block.insert(index++, Instruction.Create(OpCodes.Dup));
block.insert(index++, Instruction.Create(OpCodes.Ldtoken, initializedDataCreator.create(ary)));
block.insert(index++, Instruction.Create(OpCodes.Call, initializeArrayMethod));
Log.v("Decrypted array: {0} bytes", ary.Length);
}
}
protected override void inlineAllCalls() {
foreach (var tmp in callResults) {
var callResult = (MyCallResult)tmp;
var handler = intDecrypters.find(callResult.methodReference);
callResult.returnValue = handler((MethodDefinition)callResult.methodReference, callResult.args);
}
}
protected override CallResult createCallResult(MethodReference method, Block block, int callInstrIndex) {
if (intDecrypters.find(method) == null)
return null;
return new MyCallResult(block, callInstrIndex, method);
}
}
}

View File

@ -0,0 +1,283 @@
/*
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.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
abstract class DecrypterBase {
ModuleDefinition module;
EmbeddedResource encryptedResource;
TypeDefinition decrypterType;
TypeDefinition delegateType;
TypeDefinition delegateInitType;
protected BinaryReader decryptedReader;
MethodDefinitionAndDeclaringTypeDict<Info> decrypterMethods = new MethodDefinitionAndDeclaringTypeDict<Info>();
protected class Info {
public MethodDefinition method;
public int offset;
public bool referenced = false;
public Info(MethodDefinition method, int offset) {
this.method = method;
this.offset = offset;
}
}
public bool Detected {
get { return encryptedResource != null; }
}
public Resource EncryptedResource {
get { return encryptedResource; }
}
public TypeDefinition Type {
get { return decrypterType; }
}
public TypeDefinition DelegateInitType {
get { return delegateInitType ?? findDelegateInitType();}
}
public TypeDefinition DelegateType {
get { return delegateType; }
}
public IEnumerable<TypeDefinition> DecrypterTypes {
get {
var types = new TypeDefinitionDict<TypeDefinition>();
foreach (var info in decrypterMethods.getAll()) {
if (info.referenced)
types.add(info.method.DeclaringType, info.method.DeclaringType);
}
return types.getAll();
}
}
public DecrypterBase(ModuleDefinition module) {
this.module = module;
}
protected Info getInfo(MethodDefinition method) {
var info = decrypterMethods.find(method);
if (info == null)
return null;
info.referenced = true;
return info;
}
protected abstract string[] getRequiredFieldTypes();
public void find() {
var requiredFields = getRequiredFieldTypes();
foreach (var type in module.Types) {
var resourceName = type.FullName + ".resources";
var resource = DotNetUtils.getResource(module, resourceName) as EmbeddedResource;
if (resource == null)
continue;
if (!new FieldTypes(type).exactly(requiredFields))
continue;
encryptedResource = resource;
decrypterType = type;
break;
}
}
public void initialize() {
if (encryptedResource == null)
return;
decryptedReader = new BinaryReader(new MemoryStream(decrypt(encryptedResource.GetResourceData())));
delegateType = null;
foreach (var type in module.GetTypes()) {
var cctor = DotNetUtils.getMethod(type, ".cctor");
if (cctor == null)
continue;
if (type.Fields.Count != 1)
continue;
var field = type.Fields[0];
var tmpDelegateType = DotNetUtils.getType(module, field.FieldType);
if (tmpDelegateType == null)
continue;
if (!checkDelegateType(tmpDelegateType))
continue;
if (delegateType != null && delegateType != tmpDelegateType)
continue;
if (!checkCctor(cctor))
continue;
delegateType = tmpDelegateType;
foreach (var method in type.Methods) {
if (method.Name == ".cctor")
continue;
if (!method.IsStatic || method.Body == null)
continue;
if (method.Parameters.Count != 0)
continue;
if (method.MethodReturnType.ReturnType.FullName == "System.Void")
continue;
var info = getDecrypterInfo(method, field);
if (info == null)
continue;
decrypterMethods.add(info.method, info);
}
}
}
Info getDecrypterInfo(MethodDefinition method, FieldDefinition delegateField) {
try {
int index = 0;
var instrs = method.Body.Instructions;
if (instrs[index].OpCode.Code != Code.Ldsfld)
return null;
var field = instrs[index++].Operand as FieldDefinition;
if (field != delegateField)
return null;
if (!DotNetUtils.isLdcI4(instrs[index]))
return null;
int offset = DotNetUtils.getLdcI4Value(instrs[index++]);
if (instrs[index].OpCode.Code != Code.Call && instrs[index].OpCode.Code != Code.Callvirt)
return null;
var calledMethod = instrs[index++].Operand as MethodReference;
if (calledMethod.Name != "Invoke")
return null;
if (instrs[index].OpCode.Code == Code.Unbox_Any)
index++;
if (instrs[index++].OpCode.Code != Code.Ret)
return null;
return new Info(method, offset);
}
catch (ArgumentOutOfRangeException) {
return null;
}
}
bool checkCctor(MethodDefinition cctor) {
var ldtokenType = getLdtokenType(cctor);
if (!MemberReferenceHelper.compareTypes(ldtokenType, cctor.DeclaringType))
return false;
MethodDefinition initMethod = null;
foreach (var info in DotNetUtils.getCalledMethods(module, cctor)) {
var method = info.Item2;
if (DotNetUtils.isMethod(method, "System.Void", "(System.Type)")) {
initMethod = method;
break;
}
}
if (initMethod == null || initMethod.Body == null)
return false;
return true;
}
static TypeReference getLdtokenType(MethodDefinition method) {
if (method == null || method.Body == null)
return null;
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code != Code.Ldtoken)
continue;
return instr.Operand as TypeReference;
}
return null;
}
bool checkDelegateType(TypeDefinition type) {
if (!DotNetUtils.derivesFromDelegate(type))
return false;
var invoke = DotNetUtils.getMethod(type, "Invoke");
if (invoke == null)
return false;
return checkDelegateInvokeMethod(invoke);
}
protected abstract bool checkDelegateInvokeMethod(MethodDefinition invokeMethod);
byte[] decrypt(byte[] encryptedData) {
const int KEY_LEN = 0x100;
if (encryptedData.Length < KEY_LEN)
throw new ApplicationException("Invalid encrypted data length");
var decryptedData = new byte[encryptedData.Length - KEY_LEN];
var pkt = module.Assembly.Name.PublicKeyToken;
if (pkt == null || pkt.Length == 0)
pkt = new byte[8];
for (int i = 0, j = 0, ki = 0; i < decryptedData.Length; i++) {
ki = (ki + 1) % (KEY_LEN - 1);
j = (j + encryptedData[ki] + pkt[i % 8]) % (KEY_LEN - 1);
var tmp = encryptedData[j];
encryptedData[j] = encryptedData[ki];
encryptedData[ki] = tmp;
decryptedData[i] = (byte)(encryptedData[KEY_LEN + i] ^ encryptedData[(encryptedData[j] + encryptedData[ki]) % (KEY_LEN - 1)]);
}
return decryptedData;
}
TypeDefinition findDelegateInitType() {
if (delegateType == null)
return null;
foreach (var type in module.Types) {
if (type.HasProperties || type.HasEvents || type.HasFields)
continue;
foreach (var method in type.Methods) {
if (!method.IsStatic || method.IsPrivate || method.Body == null)
continue;
var ldtokenType = getLdtokenType(method);
if (ldtokenType == null)
continue;
if (!MemberReferenceHelper.compareTypes(ldtokenType, delegateType))
continue;
delegateInitType = type;
return delegateInitType;
}
}
return null;
}
public IEnumerable<MethodDefinition> getMethods() {
var list = new List<MethodDefinition>(decrypterMethods.Count);
foreach (var info in decrypterMethods.getAll())
list.Add(info.method);
return list;
}
}
}

View File

@ -0,0 +1,278 @@
/*
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 Mono.Cecil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
public class DeobfuscatorInfo : DeobfuscatorInfoBase {
public const string THE_NAME = "Goliath.NET";
public const string THE_TYPE = "go";
const string DEFAULT_REGEX = @"!^[A-Za-z]{1,2}(?:`\d+)?$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
BoolOption inlineMethods;
BoolOption removeInlinedMethods;
BoolOption restoreLocals;
BoolOption decryptIntegers;
BoolOption decryptArrays;
BoolOption removeAntiStrongName;
public DeobfuscatorInfo()
: base(DEFAULT_REGEX) {
inlineMethods = new BoolOption(null, makeArgName("inline"), "Inline short methods", true);
removeInlinedMethods = new BoolOption(null, makeArgName("remove-inlined"), "Remove inlined methods", true);
restoreLocals = new BoolOption(null, makeArgName("locals"), "Restore locals", true);
decryptIntegers = new BoolOption(null, makeArgName("ints"), "Decrypt integers", true);
decryptArrays = new BoolOption(null, makeArgName("arrays"), "Decrypt arrays", true);
removeAntiStrongName = new BoolOption(null, makeArgName("sn"), "Remove anti strong name code", true);
}
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 {
RenameResourcesInCode = false,
ValidNameRegex = validNameRegex.get(),
InlineMethods = inlineMethods.get(),
RemoveInlinedMethods = removeInlinedMethods.get(),
RestoreLocals = restoreLocals.get(),
DecryptIntegers = decryptIntegers.get(),
DecryptArrays = decryptArrays.get(),
RemoveAntiStrongName = removeAntiStrongName.get(),
});
}
protected override IEnumerable<Option> getOptionsInternal() {
return new List<Option>() {
inlineMethods,
removeInlinedMethods,
restoreLocals,
decryptIntegers,
decryptArrays,
removeAntiStrongName,
};
}
}
class Deobfuscator : DeobfuscatorBase {
Options options;
string obfuscatorName = DeobfuscatorInfo.THE_NAME;
ProxyDelegateFinder proxyDelegateFinder;
LocalsRestorer localsRestorer;
LogicalExpressionFixer logicalExpressionFixer;
StringDecrypter stringDecrypter;
IntegerDecrypter integerDecrypter;
IntegerValueInliner integerValueInliner;
ArrayDecrypter arrayDecrypter;
ArrayValueInliner arrayValueInliner;
StrongNameChecker strongNameChecker;
bool foundGoliathAttribute = false;
bool startedDeobfuscating = false;
internal class Options : OptionsBase {
public bool InlineMethods { get; set; }
public bool RemoveInlinedMethods { get; set; }
public bool RestoreLocals { get; set; }
public bool DecryptIntegers { get; set; }
public bool DecryptArrays { get; set; }
public bool RemoveAntiStrongName { get; set; }
}
public override string Type {
get { return DeobfuscatorInfo.THE_TYPE; }
}
public override string TypeLong {
get { return DeobfuscatorInfo.THE_NAME; }
}
public override string Name {
get { return obfuscatorName; }
}
public override bool CanInlineMethods {
get { return startedDeobfuscating ? options.InlineMethods : true; }
}
internal Deobfuscator(Options options)
: base(options) {
this.options = options;
}
protected override int detectInternal() {
int val = 0;
int sum = convert(foundGoliathAttribute) +
convert(stringDecrypter.Detected) +
convert(integerDecrypter.Detected) +
convert(arrayDecrypter.Detected) +
convert(strongNameChecker.Detected) +
convert(hasMetadataStream("#GOLIATH"));
if (sum > 0)
val += 100 + 10 * (sum - 1);
return val;
}
protected override void scanForObfuscator() {
findGoliathAttribute();
stringDecrypter = new StringDecrypter(module);
stringDecrypter.find();
integerDecrypter = new IntegerDecrypter(module);
integerDecrypter.find();
arrayDecrypter = new ArrayDecrypter(module);
arrayDecrypter.find();
strongNameChecker = new StrongNameChecker(module);
strongNameChecker.find();
}
void findGoliathAttribute() {
foreach (var type in module.Types) {
if (type.FullName.Contains("ObfuscatedByGoliath")) {
foundGoliathAttribute = true;
addAttributeToBeRemoved(type, "Obfuscator attribute");
initializeVersion(type);
break;
}
}
}
void initializeVersion(TypeDefinition attr) {
var s = DotNetUtils.getCustomArgAsString(getAssemblyAttribute(attr), 0);
if (s == null)
return;
var val = System.Text.RegularExpressions.Regex.Match(s, @"^Goliath \.NET Obfuscator rel\. (\d+\.\d+\.\d+)$");
if (val.Groups.Count < 2)
return;
obfuscatorName = DeobfuscatorInfo.THE_NAME + " " + val.Groups[1].ToString();
return;
}
public override void deobfuscateBegin() {
base.deobfuscateBegin();
proxyDelegateFinder = new ProxyDelegateFinder(module);
proxyDelegateFinder.find();
localsRestorer = new LocalsRestorer(module);
if (options.RestoreLocals)
localsRestorer.find();
logicalExpressionFixer = new LogicalExpressionFixer();
stringDecrypter.initialize();
integerDecrypter.initialize();
arrayDecrypter.initialize();
if (options.DecryptIntegers) {
integerValueInliner = new IntegerValueInliner();
foreach (var method in integerDecrypter.getMethods()) {
integerValueInliner.add(method, (method2, args) => {
return integerDecrypter.decrypt(method2);
});
}
}
if (options.DecryptArrays) {
arrayValueInliner = new ArrayValueInliner(module, initializedDataCreator);
foreach (var method in arrayDecrypter.getMethods()) {
arrayValueInliner.add(method, (method2, args) => {
return arrayDecrypter.decrypt(method2);
});
}
}
foreach (var method in stringDecrypter.getMethods()) {
staticStringDecrypter.add(method, (method2, args) => {
return stringDecrypter.decrypt(method2);
});
}
if (options.RemoveAntiStrongName)
addTypeToBeRemoved(strongNameChecker.Type, "Strong name checker type");
startedDeobfuscating = true;
}
public override void deobfuscateMethodBegin(Blocks blocks) {
proxyDelegateFinder.deobfuscate(blocks);
base.deobfuscateMethodBegin(blocks);
}
public override void deobfuscateMethodEnd(Blocks blocks) {
if (integerValueInliner.HasHandlers)
integerValueInliner.decrypt(blocks);
if (arrayValueInliner.HasHandlers)
arrayValueInliner.decrypt(blocks);
if (options.RestoreLocals)
localsRestorer.deobfuscate(blocks);
if (options.RemoveAntiStrongName) {
if (strongNameChecker.deobfuscate(blocks))
Log.v("Removed strong name checker code");
}
logicalExpressionFixer.deobfuscate(blocks);
base.deobfuscateMethodEnd(blocks);
}
public override void deobfuscateEnd() {
removeProxyDelegates(proxyDelegateFinder);
removeInlinedMethods();
addTypesToBeRemoved(localsRestorer.Types, "Method locals obfuscation type");
if (Operations.DecryptStrings != OpDecryptString.None)
removeDecrypterStuff(stringDecrypter, "String", "strings");
if (options.DecryptIntegers)
removeDecrypterStuff(integerDecrypter, "Integer", "integers");
if (options.DecryptArrays)
removeDecrypterStuff(arrayDecrypter, "Array", "arrays");
base.deobfuscateEnd();
}
void removeDecrypterStuff(DecrypterBase decrypter, string name1, string name2) {
addResourceToBeRemoved(decrypter.EncryptedResource, "Encrypted " + name2);
addTypesToBeRemoved(decrypter.DecrypterTypes, name1 + " decrypter type");
addTypeToBeRemoved(decrypter.Type, name1 + " resource decrypter type");
if (decrypter.DelegateInitType != null) {
addTypeToBeRemoved(decrypter.DelegateType, name1 + " resource decrypter delegate type");
addTypeToBeRemoved(decrypter.DelegateInitType, name1 + " delegate initializer type");
}
}
void removeInlinedMethods() {
if (!options.InlineMethods || !options.RemoveInlinedMethods)
return;
findAndRemoveInlinedMethods();
}
public override IEnumerable<string> getStringDecrypterMethods() {
var list = new List<string>();
foreach (var method in stringDecrypter.getMethods())
list.Add(method.MetadataToken.ToInt32().ToString("X8"));
return list;
}
}
}

View File

@ -0,0 +1,48 @@
/*
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 Mono.Cecil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class IntegerDecrypter : DecrypterBase {
public IntegerDecrypter(ModuleDefinition module)
: base(module) {
}
protected override string[] getRequiredFieldTypes() {
return new string[] {
"System.Byte[]",
"System.Collections.Generic.Dictionary`2<System.Int32,System.Object>",
};
}
protected override bool checkDelegateInvokeMethod(MethodDefinition invokeMethod) {
return DotNetUtils.isMethod(invokeMethod, "System.Object", "(System.Int32)");
}
public int decrypt(MethodDefinition method) {
var info = getInfo(method);
decryptedReader.BaseStream.Position = info.offset;
int len = decryptedReader.ReadInt32();
return BitConverter.ToInt32(decryptedReader.ReadBytes(len), 0);
}
}
}

View File

@ -0,0 +1,74 @@
/*
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 Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class IntegerValueInliner : MethodReturnValueInliner {
MethodDefinitionAndDeclaringTypeDict<Func<MethodDefinition, object[], int>> intDecrypters = new MethodDefinitionAndDeclaringTypeDict<Func<MethodDefinition, object[], int>>();
class MyCallResult : CallResult {
public MethodReference methodReference;
public MyCallResult(Block block, int callEndIndex, MethodReference method)
: base(block, callEndIndex) {
this.methodReference = method;
}
}
public bool HasHandlers {
get { return intDecrypters.Count != 0; }
}
public void add(MethodDefinition method, Func<MethodDefinition, object[], int> handler) {
if (method == null)
return;
if (intDecrypters.find(method) != null)
throw new ApplicationException(string.Format("Handler for method {0:X8} has already been added", method.MetadataToken.ToInt32()));
intDecrypters.add(method, handler);
}
protected override void inlineReturnValues(IList<CallResult> callResults) {
foreach (var callResult in callResults) {
var block = callResult.block;
int num = callResult.callEndIndex - callResult.callStartIndex + 1;
block.replace(callResult.callStartIndex, num, DotNetUtils.createLdci4((int)callResult.returnValue));
Log.v("Decrypted integer: {0}", callResult.returnValue);
}
}
protected override void inlineAllCalls() {
foreach (var tmp in callResults) {
var callResult = (MyCallResult)tmp;
var handler = intDecrypters.find(callResult.methodReference);
callResult.returnValue = handler((MethodDefinition)callResult.methodReference, callResult.args);
}
}
protected override CallResult createCallResult(MethodReference method, Block block, int callInstrIndex) {
if (intDecrypters.find(method) == null)
return null;
return new MyCallResult(block, callInstrIndex, method);
}
}
}

View File

@ -0,0 +1,148 @@
/*
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 Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class LocalsRestorer {
ModuleDefinition module;
TypeDefinitionDict<Info> typeToInfo = new TypeDefinitionDict<Info>();
class Info {
public TypeDefinition type;
public TypeReference localType;
public bool referenced = false;
public Info(TypeDefinition type, TypeReference localType) {
this.type = type;
this.localType = localType;
}
}
public List<TypeDefinition> Types {
get {
var list = new List<TypeDefinition>(typeToInfo.Count);
foreach (var info in typeToInfo.getAll()) {
if (info.referenced)
list.Add(info.type);
}
return list;
}
}
public LocalsRestorer(ModuleDefinition module) {
this.module = module;
}
public void find() {
foreach (var type in module.GetTypes())
initialize(type);
}
void initialize(TypeDefinition type) {
if (type.HasEvents || type.HasProperties)
return;
if (!type.IsValueType)
return;
if (type.Methods.Count != 1)
return;
var ctor = type.Methods[0];
if (ctor.Name != ".ctor" || ctor.Body == null || ctor.IsStatic)
return;
if (ctor.Parameters.Count != 1)
return;
var ctorParam = ctor.Parameters[0];
if (type.Fields.Count != 1)
return;
var typeField = type.Fields[0];
if (typeField.IsStatic)
return;
if (!MemberReferenceHelper.compareTypes(ctorParam.ParameterType, typeField.FieldType))
return;
typeToInfo.add(ctor.DeclaringType, new Info(ctor.DeclaringType, typeField.FieldType));
}
public void deobfuscate(Blocks blocks) {
var instrsToRemove = new List<int>();
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
instrsToRemove.Clear();
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count; i++) {
var instr = instrs[i];
int indexToRemove;
TypeReference type;
VariableDefinition local = null;
if (instr.OpCode.Code == Code.Newobj) {
if (i + 1 >= instrs.Count)
continue;
var ctor = instr.Operand as MethodReference;
if (ctor == null || ctor.DeclaringType == null)
continue;
if (ctor.Name != ".ctor")
continue;
var next = instrs[i + 1];
if (!next.isStloc() && !next.isLeave() && next.OpCode.Code != Code.Pop)
continue;
indexToRemove = i;
type = ctor.DeclaringType;
if (next.isStloc())
local = Instr.getLocalVar(blocks.Locals, next);
}
else if (instr.OpCode.Code == Code.Ldfld) {
if (i == 0)
continue;
var ldloc = instrs[i - 1];
if (!ldloc.isLdloc())
continue;
var field = instr.Operand as FieldReference;
if (field == null || field.DeclaringType == null)
continue;
indexToRemove = i;
type = field.DeclaringType;
local = Instr.getLocalVar(blocks.Locals, ldloc);
}
else
continue;
if (type == null)
continue;
var info = typeToInfo.find(type);
if (info == null)
continue;
info.referenced = true;
instrsToRemove.Add(indexToRemove);
if (local != null)
local.VariableType = info.localType;
}
block.remove(instrsToRemove);
}
}
}
}

View File

@ -0,0 +1,45 @@
/*
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.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class LogicalExpressionFixer {
public void deobfuscate(Blocks blocks) {
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 1; i++) {
var first = instrs[i];
var second = instrs[i + 1];
if (first.OpCode.Code == Code.Not && second.OpCode.Code == Code.Neg) {
// It's increment
instrs[i] = new Instr(Instruction.Create(OpCodes.Ldc_I4_1));
instrs[i + 1] = new Instr(Instruction.Create(OpCodes.Add));
}
else if (first.OpCode.Code == Code.Neg && second.OpCode.Code == Code.Not) {
// It's decrement
instrs[i] = new Instr(Instruction.Create(OpCodes.Ldc_I4_1));
instrs[i + 1] = new Instr(Instruction.Create(OpCodes.Sub));
}
}
}
}
}
}

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 Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class ProxyDelegateFinder : ProxyDelegateFinderBase {
public ProxyDelegateFinder(ModuleDefinition module)
: base(module) {
}
class MyInfo {
public MethodDefinition method;
public DelegateInfo delegateInfo;
public MyInfo(MethodDefinition method, DelegateInfo delegateInfo) {
this.method = method;
this.delegateInfo = delegateInfo;
}
}
public new void find() {
Log.v("Finding all proxy delegates");
var infos = new List<MyInfo>();
foreach (var type in module.GetTypes()) {
if (type.BaseType == null || type.BaseType.FullName != "System.MulticastDelegate")
continue;
infos.Clear();
foreach (var method in type.Methods) {
DelegateInfo info;
if (!checkProxyMethod(method, out info))
continue;
infos.Add(new MyInfo(method, info));
}
if (infos.Count == 0)
continue;
Log.v("Found proxy delegate: {0} ({1:X8})", type, type.MetadataToken.ToUInt32());
RemovedDelegateCreatorCalls++;
Log.indent();
foreach (var info in infos) {
var di = info.delegateInfo;
add(info.method, di.field);
addDelegateInfo(di);
Log.v("Field: {0}, Opcode: {1}, Method: {2} ({3:X8})", di.field.Name, di.callOpcode, di.methodRef, di.methodRef.MetadataToken.ToUInt32());
}
Log.deIndent();
delegateTypesDict[type] = true;
}
}
bool checkProxyMethod(MethodDefinition method, out DelegateInfo info) {
info = null;
if (!method.IsStatic || method.Body == null)
return false;
var instrs = method.Body.Instructions;
if (instrs.Count < 7)
return false;
int index = 0;
if (instrs[index].OpCode.Code != Code.Ldsfld)
return false;
var field = instrs[index++].Operand as FieldDefinition;
if (field == null || !field.IsStatic)
return false;
if (!MemberReferenceHelper.compareTypes(method.DeclaringType, field.DeclaringType))
return false;
if (!DotNetUtils.isBrtrue(instrs[index++]))
return false;
if (instrs[index++].OpCode.Code != Code.Ldnull)
return false;
if (instrs[index].OpCode.Code != Code.Ldftn)
return false;
var calledMethod = instrs[index++].Operand as MethodReference;
if (calledMethod == null)
return false;
if (instrs[index++].OpCode.Code != Code.Newobj)
return false;
if (instrs[index].OpCode.Code != Code.Stsfld)
return false;
if (!MemberReferenceHelper.compareFieldReference(field, instrs[index++].Operand as FieldReference))
return false;
if (instrs[index].OpCode.Code != Code.Ldsfld)
return false;
if (!MemberReferenceHelper.compareFieldReference(field, instrs[index++].Operand as FieldReference))
return false;
for (int i = 0; i < method.Parameters.Count; i++) {
if (index >= instrs.Count)
return false;
if (DotNetUtils.getArgIndex(method, instrs[index++]) != i)
return false;
}
if (index + 2 > instrs.Count)
return false;
var call = instrs[index++];
if (call.OpCode.Code != Code.Callvirt)
return false;
if (instrs[index++].OpCode.Code != Code.Ret)
return false;
info = new DelegateInfo(field, calledMethod, OpCodes.Call);
return true;
}
protected override object checkCctor(TypeDefinition type, MethodDefinition cctor) {
throw new System.NotImplementedException();
}
protected override void onFoundProxyDelegate(TypeDefinition type) {
throw new System.NotImplementedException();
}
protected override void getCallInfo(object context, FieldDefinition field, out MethodReference calledMethod, out OpCode callOpcode) {
throw new System.NotImplementedException();
}
}
}

View File

@ -0,0 +1,49 @@
/*
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.Text;
using Mono.Cecil;
using de4dot.blocks;
namespace de4dot.code.deobfuscators.Goliath_NET {
class StringDecrypter : DecrypterBase {
public StringDecrypter(ModuleDefinition module)
: base(module) {
}
protected override string[] getRequiredFieldTypes() {
return new string[] {
"System.Byte[]",
"System.Collections.Generic.Dictionary`2<System.Int32,System.String>",
};
}
protected override bool checkDelegateInvokeMethod(MethodDefinition invokeMethod) {
return DotNetUtils.isMethod(invokeMethod, "System.String", "(System.Int32)");
}
public string decrypt(MethodDefinition method) {
var info = getInfo(method);
decryptedReader.BaseStream.Position = info.offset;
int len = decryptedReader.ReadInt32();
return Encoding.UTF8.GetString(decryptedReader.ReadBytes(len));
}
}
}

View File

@ -0,0 +1,127 @@
/*
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.code.deobfuscators.Goliath_NET {
class StrongNameChecker {
ModuleDefinition module;
TypeDefinition strongNameType;
MethodDefinition strongNameCheckMethod;
public bool Detected {
get { return strongNameType != null;}
}
public TypeDefinition Type {
get { return strongNameType; }
}
public MethodDefinition CheckerMethod {
get { return strongNameCheckMethod; }
}
public StrongNameChecker(ModuleDefinition module) {
this.module = module;
}
public void find() {
foreach (var type in module.Types) {
if (type.HasFields || type.HasEvents || type.HasProperties)
continue;
var checkMethod = getAntiTamperingDetectionMethod(type);
if (checkMethod == null)
continue;
if (DotNetUtils.getMethod(type, "System.Byte[]", "(System.Reflection.Assembly)") == null)
continue;
if (DotNetUtils.getMethod(type, "System.String", "(System.Collections.Generic.Stack`1<System.Int32>)") == null)
continue;
if (DotNetUtils.getMethod(type, "System.Int32", "(System.Int32,System.Byte[])") == null)
continue;
strongNameType = type;
strongNameCheckMethod = checkMethod;
return;
}
}
MethodDefinition getAntiTamperingDetectionMethod(TypeDefinition type) {
var requiredLocals = new string[] {
"System.Reflection.Assembly",
"System.Collections.Generic.Stack`1<System.Int32>",
};
foreach (var method in type.Methods) {
if (!method.IsStatic || method.Body == null)
continue;
if (!DotNetUtils.isMethod(method, "System.Void", "(System.Type)"))
continue;
if (!new LocalTypes(method).all(requiredLocals))
continue;
if (!hasThrow(method))
continue;
return method;
}
return null;
}
static bool hasThrow(MethodDefinition method) {
if (method == null || method.Body == null)
return false;
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code == Code.Throw)
return true;
}
return false;
}
public bool deobfuscate(Blocks blocks) {
if (blocks.Method.Name != ".cctor" && blocks.Method.Name != ".ctor")
return false;
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
var instrs = block.Instructions;
for (int i = 0; i < instrs.Count - 2; i++) {
var ldtoken = instrs[i];
if (ldtoken.OpCode.Code != Code.Ldtoken)
continue;
var call1 = instrs[i + 1];
if (call1.OpCode.Code != Code.Call && call1.OpCode.Code != Code.Callvirt)
continue;
if (!DotNetUtils.isMethod(call1.Operand as MethodReference, "System.Type", "(System.RuntimeTypeHandle)"))
continue;
var call2 = instrs[i + 2];
if (call2.OpCode.Code != Code.Call && call2.OpCode.Code != Code.Callvirt)
continue;
if (!MemberReferenceHelper.compareMethodReferenceAndDeclaringType(call2.Operand as MethodReference, strongNameCheckMethod))
continue;
block.remove(i, 3);
return true;
}
}
return false;
}
}
}

View File

@ -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.Goliath_NET.DeobfuscatorInfo(),
new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(),
new de4dot.code.deobfuscators.Xenocode.DeobfuscatorInfo(),
};