Add support for another obfuscator
This commit is contained in:
parent
9d08a7fe34
commit
44e58066b3
|
@ -58,6 +58,18 @@
|
|||
<Compile Include="AssemblyClient\SameAppDomainAssemblyServerLoader.cs" />
|
||||
<Compile Include="AssemblyResolver.cs" />
|
||||
<Compile Include="deobfuscators\ArrayFinder.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\ConstantsDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\Deobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\ImageReader.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MemberReferenceConverter.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MethodBodyReader.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MethodReferenceReader.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MethodBodyReaderBase.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MethodsDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\ProxyDelegateFinder.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\ResourceDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\ResourceResolver.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\StringDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\CliSecure\CliSecureRtType.cs" />
|
||||
<Compile Include="deobfuscators\CliSecure\Deobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\CliSecure\MethodsDecrypter.cs" />
|
||||
|
|
296
de4dot.code/deobfuscators/Babel_NET/ConstantsDecrypter.cs
Normal file
296
de4dot.code/deobfuscators/Babel_NET/ConstantsDecrypter.cs
Normal file
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
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 System.Runtime.Serialization.Formatters.Binary;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class ConstantsDecrypter {
|
||||
ModuleDefinition module;
|
||||
InitializedDataCreator initializedDataCreator;
|
||||
TypeDefinition decrypterType;
|
||||
MethodDefinition int32Decrypter;
|
||||
MethodDefinition int64Decrypter;
|
||||
MethodDefinition singleDecrypter;
|
||||
MethodDefinition doubleDecrypter;
|
||||
MethodDefinition arrayDecrypter;
|
||||
EmbeddedResource encryptedResource;
|
||||
int[] decryptedInts;
|
||||
long[] decryptedLongs;
|
||||
float[] decryptedFloats;
|
||||
double[] decryptedDoubles;
|
||||
|
||||
public bool Detected {
|
||||
get { return decrypterType != null; }
|
||||
}
|
||||
|
||||
public bool CanDecrypt {
|
||||
get { return encryptedResource != null; }
|
||||
}
|
||||
|
||||
public Resource Resource {
|
||||
get { return encryptedResource; }
|
||||
}
|
||||
|
||||
public TypeDefinition Type {
|
||||
get { return decrypterType; }
|
||||
}
|
||||
|
||||
public MethodDefinition Int32Decrypter {
|
||||
get { return int32Decrypter; }
|
||||
}
|
||||
|
||||
public MethodDefinition Int64Decrypter {
|
||||
get { return int64Decrypter; }
|
||||
}
|
||||
|
||||
public MethodDefinition SingleDecrypter {
|
||||
get { return singleDecrypter; }
|
||||
}
|
||||
|
||||
public MethodDefinition DoubleDecrypter {
|
||||
get { return doubleDecrypter; }
|
||||
}
|
||||
|
||||
public MethodDefinition ArrayDecrypter {
|
||||
get { return arrayDecrypter; }
|
||||
}
|
||||
|
||||
public ConstantsDecrypter(ModuleDefinition module, InitializedDataCreator initializedDataCreator) {
|
||||
this.module = module;
|
||||
this.initializedDataCreator = initializedDataCreator;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
foreach (var type in module.Types) {
|
||||
if (!isConstantDecrypter(type))
|
||||
continue;
|
||||
|
||||
int32Decrypter = DotNetUtils.getMethod(type, "System.Int32", "(System.Int32)");
|
||||
int64Decrypter = DotNetUtils.getMethod(type, "System.Int64", "(System.Int32)");
|
||||
singleDecrypter = DotNetUtils.getMethod(type, "System.Single", "(System.Int32)");
|
||||
doubleDecrypter = DotNetUtils.getMethod(type, "System.Double", "(System.Int32)");
|
||||
arrayDecrypter = DotNetUtils.getMethod(type, "System.Array", "(System.Byte[])");
|
||||
decrypterType = type;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool isConstantDecrypter(TypeDefinition type) {
|
||||
if (type.HasEvents)
|
||||
return false;
|
||||
if (type.NestedTypes.Count != 1)
|
||||
return false;
|
||||
|
||||
var nested = type.NestedTypes[0];
|
||||
if (!checkNestedFields(nested))
|
||||
return false;
|
||||
|
||||
if (DotNetUtils.getMethod(type, "System.Int32", "(System.Int32)") == null)
|
||||
return false;
|
||||
if (DotNetUtils.getMethod(type, "System.Int64", "(System.Int32)") == null)
|
||||
return false;
|
||||
if (DotNetUtils.getMethod(type, "System.Single", "(System.Int32)") == null)
|
||||
return false;
|
||||
if (DotNetUtils.getMethod(type, "System.Double", "(System.Int32)") == null)
|
||||
return false;
|
||||
if (DotNetUtils.getMethod(type, "System.Array", "(System.Byte[])") == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static string[] requiredTypes = new string[] {
|
||||
"System.Int32[]",
|
||||
"System.Int64[]",
|
||||
"System.Single[]",
|
||||
"System.Double[]",
|
||||
};
|
||||
bool checkNestedFields(TypeDefinition nested) {
|
||||
if (!new FieldTypes(nested).all(requiredTypes))
|
||||
return false;
|
||||
foreach (var field in nested.Fields) {
|
||||
if (MemberReferenceHelper.compareTypes(nested, field.FieldType))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void initialize(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
|
||||
encryptedResource = findEncryptedResource(simpleDeobfuscator, deob);
|
||||
if (encryptedResource == null) {
|
||||
Log.w("Could not find encrypted constants resource");
|
||||
return;
|
||||
}
|
||||
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData());
|
||||
var reader = new BinaryReader(new MemoryStream(decrypted));
|
||||
int count;
|
||||
|
||||
count = reader.ReadInt32();
|
||||
decryptedInts = new int[count];
|
||||
while (count-- > 0)
|
||||
decryptedInts[count] = reader.ReadInt32();
|
||||
|
||||
count = reader.ReadInt32();
|
||||
decryptedLongs = new long[count];
|
||||
while (count-- > 0)
|
||||
decryptedLongs[count] = reader.ReadInt64();
|
||||
|
||||
count = reader.ReadInt32();
|
||||
decryptedFloats = new float[count];
|
||||
while (count-- > 0)
|
||||
decryptedFloats[count] = reader.ReadSingle();
|
||||
|
||||
count = reader.ReadInt32();
|
||||
decryptedDoubles = new double[count];
|
||||
while (count-- > 0)
|
||||
decryptedDoubles[count] = reader.ReadDouble();
|
||||
}
|
||||
|
||||
EmbeddedResource findEncryptedResource(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
|
||||
foreach (var method in decrypterType.Methods) {
|
||||
if (!DotNetUtils.isMethod(method, "System.String", "()"))
|
||||
continue;
|
||||
if (!method.IsStatic)
|
||||
continue;
|
||||
simpleDeobfuscator.deobfuscate(method);
|
||||
simpleDeobfuscator.decryptStrings(method, deob);
|
||||
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
||||
var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
|
||||
if (resource != null)
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int decryptInt32(int index) {
|
||||
return decryptedInts[index];
|
||||
}
|
||||
|
||||
public long decryptInt64(int index) {
|
||||
return decryptedLongs[index];
|
||||
}
|
||||
|
||||
public float decryptSingle(int index) {
|
||||
return decryptedFloats[index];
|
||||
}
|
||||
|
||||
public double decryptDouble(int index) {
|
||||
return decryptedDoubles[index];
|
||||
}
|
||||
|
||||
struct ArrayInfo {
|
||||
public FieldDefinition encryptedField;
|
||||
public ArrayType arrayType;
|
||||
public int start, len;
|
||||
|
||||
public ArrayInfo(int start, int len, FieldDefinition encryptedField, ArrayType arrayType) {
|
||||
this.start = start;
|
||||
this.len = len;
|
||||
this.encryptedField = encryptedField;
|
||||
this.arrayType = arrayType;
|
||||
}
|
||||
}
|
||||
|
||||
public void deobfuscate(Blocks blocks) {
|
||||
if (arrayDecrypter == null)
|
||||
return;
|
||||
|
||||
var infos = new List<ArrayInfo>();
|
||||
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
|
||||
var instrs = block.Instructions;
|
||||
infos.Clear();
|
||||
for (int i = 0; i < instrs.Count - 6; i++) {
|
||||
int index = i;
|
||||
|
||||
var ldci4 = instrs[index++];
|
||||
if (!ldci4.isLdcI4())
|
||||
continue;
|
||||
|
||||
var newarr = instrs[index++];
|
||||
if (newarr.OpCode.Code != Code.Newarr)
|
||||
continue;
|
||||
if (newarr.Operand == null || newarr.Operand.ToString() != "System.Byte")
|
||||
continue;
|
||||
|
||||
if (instrs[index++].OpCode.Code != Code.Dup)
|
||||
continue;
|
||||
|
||||
var ldtoken = instrs[index++];
|
||||
if (ldtoken.OpCode.Code != Code.Ldtoken)
|
||||
continue;
|
||||
var field = ldtoken.Operand as FieldDefinition;
|
||||
if (field == null)
|
||||
continue;
|
||||
|
||||
var call1 = instrs[index++];
|
||||
if (call1.OpCode.Code != Code.Call && call1.OpCode.Code != Code.Callvirt)
|
||||
continue;
|
||||
if (!DotNetUtils.isMethod(call1.Operand as MethodReference, "System.Void", "(System.Array,System.RuntimeFieldHandle)"))
|
||||
continue;
|
||||
|
||||
var call2 = instrs[index++];
|
||||
if (call2.OpCode.Code != Code.Call && call2.OpCode.Code != Code.Callvirt)
|
||||
continue;
|
||||
if (!MemberReferenceHelper.compareMethodReferenceAndDeclaringType(call2.Operand as MethodReference, arrayDecrypter))
|
||||
continue;
|
||||
|
||||
var castclass = instrs[index++];
|
||||
if (castclass.OpCode.Code != Code.Castclass)
|
||||
continue;
|
||||
var arrayType = castclass.Operand as ArrayType;
|
||||
if (arrayType == null)
|
||||
continue;
|
||||
if (arrayType.ElementType.PrimitiveSize == -1) {
|
||||
Log.w("Can't decrypt non-primitive type array in method {0}", blocks.Method.MetadataToken.ToInt32());
|
||||
continue;
|
||||
}
|
||||
|
||||
infos.Add(new ArrayInfo(i, index - i, field, arrayType));
|
||||
}
|
||||
|
||||
infos.Reverse();
|
||||
foreach (var info in infos) {
|
||||
var elemSize = info.arrayType.ElementType.PrimitiveSize;
|
||||
var decrypted = decryptArray(info.encryptedField.InitialValue, elemSize);
|
||||
|
||||
initializedDataCreator.addInitializeArrayCode(block, info.start, info.len, info.arrayType.ElementType, decrypted);
|
||||
Log.v("Decrypted {0} array: {1} elements", info.arrayType.ElementType.ToString(), decrypted.Length / elemSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] decryptArray(byte[] encryptedData, int elemSize) {
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedData);
|
||||
var ary = (Array)new BinaryFormatter().Deserialize(new MemoryStream(decrypted));
|
||||
if (ary is byte[])
|
||||
return (byte[])ary;
|
||||
var newAry = new byte[ary.Length * elemSize];
|
||||
Buffer.BlockCopy(ary, 0, newAry, 0, newAry.Length);
|
||||
return newAry;
|
||||
}
|
||||
}
|
||||
}
|
231
de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs
Normal file
231
de4dot.code/deobfuscators/Babel_NET/Deobfuscator.cs
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
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.Babel_NET {
|
||||
public class DeobfuscatorInfo : DeobfuscatorInfoBase {
|
||||
public const string THE_NAME = "Babel .NET";
|
||||
public const string THE_TYPE = "bl";
|
||||
BoolOption decryptMethods;
|
||||
BoolOption decryptResources;
|
||||
BoolOption decryptConstants;
|
||||
|
||||
public DeobfuscatorInfo()
|
||||
: base() {
|
||||
decryptMethods = new BoolOption(null, makeArgName("methods"), "Decrypt methods", true);
|
||||
decryptResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true);
|
||||
decryptConstants = new BoolOption(null, makeArgName("consts"), "Decrypt constants and arrays", 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 {
|
||||
ValidNameRegex = validNameRegex.get(),
|
||||
DecryptMethods = decryptMethods.get(),
|
||||
DecryptResources = decryptResources.get(),
|
||||
DecryptConstants = decryptConstants.get(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override IEnumerable<Option> getOptionsInternal() {
|
||||
return new List<Option>() {
|
||||
decryptMethods,
|
||||
decryptResources,
|
||||
decryptConstants,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Deobfuscator : DeobfuscatorBase {
|
||||
Options options;
|
||||
bool foundBabelAttribute = false;
|
||||
|
||||
ResourceResolver resourceResolver;
|
||||
StringDecrypter stringDecrypter;
|
||||
ConstantsDecrypter constantsDecrypter;
|
||||
Int32ValueInliner int32ValueInliner;
|
||||
Int64ValueInliner int64ValueInliner;
|
||||
SingleValueInliner singleValueInliner;
|
||||
DoubleValueInliner doubleValueInliner;
|
||||
ProxyDelegateFinder proxyDelegateFinder;
|
||||
MethodsDecrypter methodsDecrypter;
|
||||
|
||||
internal class Options : OptionsBase {
|
||||
public bool DecryptMethods { get; set; }
|
||||
public bool DecryptResources { get; set; }
|
||||
public bool DecryptConstants { 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 DeobfuscatorInfo.THE_NAME; }
|
||||
}
|
||||
|
||||
public Deobfuscator(Options options)
|
||||
: base(options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public override void init(ModuleDefinition module) {
|
||||
base.init(module);
|
||||
}
|
||||
|
||||
protected override int detectInternal() {
|
||||
int val = 0;
|
||||
|
||||
int sum = toInt32(foundBabelAttribute) +
|
||||
toInt32(resourceResolver.Detected) +
|
||||
toInt32(stringDecrypter.Detected) +
|
||||
toInt32(constantsDecrypter.Detected) +
|
||||
toInt32(proxyDelegateFinder.Detected) +
|
||||
toInt32(methodsDecrypter.Detected) +
|
||||
toInt32(hasMetadataStream("Babel"));
|
||||
if (sum > 0)
|
||||
val += 100 + 10 * (sum - 1);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
protected override void scanForObfuscator() {
|
||||
findBabelAttribute();
|
||||
resourceResolver = new ResourceResolver(module);
|
||||
resourceResolver.find();
|
||||
stringDecrypter = new StringDecrypter(module);
|
||||
stringDecrypter.find();
|
||||
constantsDecrypter = new ConstantsDecrypter(module, initializedDataCreator);
|
||||
constantsDecrypter.find();
|
||||
proxyDelegateFinder = new ProxyDelegateFinder(module);
|
||||
proxyDelegateFinder.findDelegateCreator();
|
||||
methodsDecrypter = new MethodsDecrypter(module);
|
||||
methodsDecrypter.find();
|
||||
}
|
||||
|
||||
void findBabelAttribute() {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.FullName == "BabelAttribute" || type.FullName == "BabelObfuscatorAttribute") {
|
||||
foundBabelAttribute = true;
|
||||
addAttributeToBeRemoved(type, "Obfuscator attribute");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void deobfuscateBegin() {
|
||||
base.deobfuscateBegin();
|
||||
|
||||
if (options.DecryptResources) {
|
||||
addCctorInitCallToBeRemoved(resourceResolver.InitMethod);
|
||||
addTypeToBeRemoved(resourceResolver.Type, "Resource resolver type");
|
||||
}
|
||||
|
||||
decryptResources();
|
||||
stringDecrypter.initialize();
|
||||
|
||||
if (Operations.DecryptStrings != OpDecryptString.None) {
|
||||
addResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings");
|
||||
addTypeToBeRemoved(stringDecrypter.Type, "String decrypter type");
|
||||
|
||||
if (stringDecrypter.Resource != null) {
|
||||
Log.v("Adding string decrypter. Resource: {0}", Utils.toCsharpString(stringDecrypter.Resource.Name));
|
||||
staticStringDecrypter.add(stringDecrypter.DecryptMethod, (method, args) => {
|
||||
return stringDecrypter.decrypt((int)args[0]);
|
||||
});
|
||||
DeobfuscatedFile.stringDecryptersAdded();
|
||||
}
|
||||
}
|
||||
|
||||
if (options.DecryptMethods) {
|
||||
methodsDecrypter.initialize(DeobfuscatedFile, this);
|
||||
methodsDecrypter.decrypt();
|
||||
}
|
||||
|
||||
if (options.DecryptConstants) {
|
||||
constantsDecrypter.initialize(DeobfuscatedFile, this);
|
||||
|
||||
addTypeToBeRemoved(constantsDecrypter.Type, "Constants decrypter type");
|
||||
addResourceToBeRemoved(constantsDecrypter.Resource, "Encrypted constants");
|
||||
int32ValueInliner = new Int32ValueInliner();
|
||||
int32ValueInliner.add(constantsDecrypter.Int32Decrypter, (method, args) => constantsDecrypter.decryptInt32((int)args[0]));
|
||||
int64ValueInliner = new Int64ValueInliner();
|
||||
int64ValueInliner.add(constantsDecrypter.Int64Decrypter, (method, args) => constantsDecrypter.decryptInt64((int)args[0]));
|
||||
singleValueInliner = new SingleValueInliner();
|
||||
singleValueInliner.add(constantsDecrypter.SingleDecrypter, (method, args) => constantsDecrypter.decryptSingle((int)args[0]));
|
||||
doubleValueInliner = new DoubleValueInliner();
|
||||
doubleValueInliner.add(constantsDecrypter.DoubleDecrypter, (method, args) => constantsDecrypter.decryptDouble((int)args[0]));
|
||||
}
|
||||
|
||||
proxyDelegateFinder.find();
|
||||
}
|
||||
|
||||
void decryptResources() {
|
||||
if (!options.DecryptResources)
|
||||
return;
|
||||
var rsrc = resourceResolver.mergeResources();
|
||||
if (rsrc == null)
|
||||
return;
|
||||
addResourceToBeRemoved(rsrc, "Encrypted resources");
|
||||
}
|
||||
|
||||
public override void deobfuscateMethodEnd(Blocks blocks) {
|
||||
proxyDelegateFinder.deobfuscate(blocks);
|
||||
if (options.DecryptConstants) {
|
||||
if (int32ValueInliner.HasHandlers)
|
||||
int32ValueInliner.decrypt(blocks);
|
||||
if (int64ValueInliner.HasHandlers)
|
||||
int64ValueInliner.decrypt(blocks);
|
||||
if (singleValueInliner.HasHandlers)
|
||||
singleValueInliner.decrypt(blocks);
|
||||
if (doubleValueInliner.HasHandlers)
|
||||
doubleValueInliner.decrypt(blocks);
|
||||
constantsDecrypter.deobfuscate(blocks);
|
||||
}
|
||||
base.deobfuscateMethodEnd(blocks);
|
||||
}
|
||||
|
||||
public override void deobfuscateEnd() {
|
||||
removeProxyDelegates(proxyDelegateFinder);
|
||||
methodsDecrypter.Dispose();
|
||||
base.deobfuscateEnd();
|
||||
}
|
||||
|
||||
public override IEnumerable<string> getStringDecrypterMethods() {
|
||||
var list = new List<string>();
|
||||
if (stringDecrypter.DecryptMethod != null)
|
||||
list.Add(stringDecrypter.DecryptMethod.MetadataToken.ToInt32().ToString("X8"));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
494
de4dot.code/deobfuscators/Babel_NET/ImageReader.cs
Normal file
494
de4dot.code/deobfuscators/Babel_NET/ImageReader.cs
Normal file
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
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.Runtime.InteropServices;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class ImageReader : IDisposable {
|
||||
static int METHODS_SIG = 0x0000BEBA;
|
||||
static int METADATA_SIG = 0x0100BEBA;
|
||||
static int METHOD_NAMES_SIG = 0x0200BEBA;
|
||||
static int ASSEMBLY_NAMES_SIG = 0x0201BEBA;
|
||||
static int TYPEREFS_SIG = 0x0202BEBA;
|
||||
static int STRINGS_SIG = 0x0203BEBA;
|
||||
|
||||
enum TypeId : byte {
|
||||
TypeRef = 0,
|
||||
GenericInstance = 1,
|
||||
Pointer = 2,
|
||||
Array = 3,
|
||||
ByRef = 4,
|
||||
}
|
||||
|
||||
ModuleDefinition module;
|
||||
BinaryReader reader;
|
||||
string[] strings;
|
||||
AssemblyNameReference[] assemblyNames;
|
||||
Dictionary<string, int> methodOffsets;
|
||||
List<TypeReference> typeReferences;
|
||||
MemberReferenceConverter memberReferenceConverter;
|
||||
ExternalAssemblies externalAssemblies = new ExternalAssemblies();
|
||||
|
||||
public ImageReader(ModuleDefinition module, byte[] data) {
|
||||
this.module = module;
|
||||
this.reader = new BinaryReader(new MemoryStream(data));
|
||||
this.memberReferenceConverter = new MemberReferenceConverter(module);
|
||||
}
|
||||
|
||||
public bool initialize() {
|
||||
if (reader.ReadInt32() != METHODS_SIG)
|
||||
return false;
|
||||
|
||||
int metadataOffset = getMetadataOffset();
|
||||
if (metadataOffset < 0)
|
||||
return false;
|
||||
reader.BaseStream.Position = metadataOffset + 4;
|
||||
int version = reader.ReadInt16(); // major, minor
|
||||
if (version != 0x0001)
|
||||
return false;
|
||||
initializeV10();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
externalAssemblies.unloadAll();
|
||||
}
|
||||
|
||||
void initializeV10() {
|
||||
reader.ReadInt16();
|
||||
int methodNamesOffset = (int)reader.ReadInt64();
|
||||
int typeReferencesOffset = (int)reader.ReadInt64();
|
||||
int assemblyReferencesOffset = (int)reader.ReadInt64();
|
||||
int stringsOffset = (int)reader.ReadInt64();
|
||||
|
||||
initializeStrings(stringsOffset);
|
||||
initializeAssemblyNames(assemblyReferencesOffset);
|
||||
initializeMethodNames(methodNamesOffset);
|
||||
initializeTypeReferences(typeReferencesOffset);
|
||||
}
|
||||
|
||||
public void restore(string name, MethodDefinition method) {
|
||||
var babelMethod = getMethod(name);
|
||||
var body = method.Body;
|
||||
|
||||
body.MaxStackSize = babelMethod.MaxStack;
|
||||
body.InitLocals = babelMethod.InitLocals;
|
||||
|
||||
body.Variables.Clear();
|
||||
foreach (var local in babelMethod.Locals)
|
||||
body.Variables.Add(local);
|
||||
|
||||
var toNewOperand = new Dictionary<object, object>();
|
||||
if (babelMethod.ThisParameter != null)
|
||||
toNewOperand[babelMethod.ThisParameter] = body.ThisParameter;
|
||||
for (int i = 0; i < method.Parameters.Count; i++)
|
||||
toNewOperand[babelMethod.Parameters[i]] = method.Parameters[i];
|
||||
|
||||
body.Instructions.Clear();
|
||||
foreach (var instr in babelMethod.Instructions) {
|
||||
object newOperand;
|
||||
if (instr.Operand != null && toNewOperand.TryGetValue(instr.Operand, out newOperand))
|
||||
instr.Operand = newOperand;
|
||||
body.Instructions.Add(instr);
|
||||
}
|
||||
|
||||
body.ExceptionHandlers.Clear();
|
||||
foreach (var eh in babelMethod.ExceptionHandlers)
|
||||
body.ExceptionHandlers.Add(eh);
|
||||
}
|
||||
|
||||
BabelMethodDefinition getMethod(string name) {
|
||||
int offset = methodOffsets[name];
|
||||
methodOffsets.Remove(name);
|
||||
reader.BaseStream.Position = offset;
|
||||
return new MethodDefinitionReader(this, reader).read();
|
||||
}
|
||||
|
||||
public string readString() {
|
||||
return strings[readVariableLengthInt32()];
|
||||
}
|
||||
|
||||
public TypeReference readTypeReference() {
|
||||
return typeReferences[readVariableLengthInt32()];
|
||||
}
|
||||
|
||||
public TypeReference[] readTypeReferences() {
|
||||
var refs = new TypeReference[readVariableLengthInt32()];
|
||||
for (int i = 0; i < refs.Length; i++)
|
||||
refs[i] = readTypeReference();
|
||||
return refs;
|
||||
}
|
||||
|
||||
public FieldReference readFieldReference() {
|
||||
var name = readString();
|
||||
var declaringType = readTypeReference();
|
||||
|
||||
var fields = getFields(resolve(declaringType), name);
|
||||
if (fields == null || fields.Count != 1) {
|
||||
throw new ApplicationException(string.Format("Couldn't find one field named '{0}' in type {1}",
|
||||
name,
|
||||
Utils.removeNewlines(declaringType)));
|
||||
}
|
||||
|
||||
return memberReferenceConverter.convert(fields[0]);
|
||||
}
|
||||
|
||||
static List<FieldDefinition> getFields(TypeDefinition type, string name) {
|
||||
if (type == null)
|
||||
return null;
|
||||
var fields = new List<FieldDefinition>();
|
||||
foreach (var field in type.Fields) {
|
||||
if (field.Name == name)
|
||||
fields.Add(field);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public MethodReference readMethodReference() {
|
||||
var babelMethodRef = new MethodReferenceReader(this, reader).read();
|
||||
|
||||
var method = getMethodReference(babelMethodRef);
|
||||
if (method == null) {
|
||||
throw new ApplicationException(string.Format("Could not find method '{0}' in type '{1}'",
|
||||
Utils.removeNewlines(babelMethodRef.Name),
|
||||
Utils.removeNewlines(babelMethodRef.DeclaringType)));
|
||||
}
|
||||
|
||||
var git = babelMethodRef.DeclaringType as GenericInstanceType;
|
||||
if (git == null)
|
||||
return method;
|
||||
|
||||
var newMethod = memberReferenceConverter.copy(method);
|
||||
newMethod.DeclaringType = babelMethodRef.DeclaringType;
|
||||
return newMethod;
|
||||
}
|
||||
|
||||
MethodReference getMethodReference(BabelMethodreference babelMethodRef) {
|
||||
var declaringType = resolve(babelMethodRef.DeclaringType);
|
||||
if (declaringType == null)
|
||||
return null;
|
||||
|
||||
var methods = getMethods(declaringType, babelMethodRef);
|
||||
if (methods.Count != 1) {
|
||||
throw new ApplicationException(string.Format("Couldn't find one method named '{0}' in type {1}",
|
||||
babelMethodRef.Name,
|
||||
Utils.removeNewlines(declaringType)));
|
||||
}
|
||||
|
||||
return methods[0];
|
||||
}
|
||||
|
||||
List<MethodReference> getMethods(TypeDefinition declaringType, BabelMethodreference babelMethodRef) {
|
||||
var methods = new List<MethodReference>();
|
||||
|
||||
var git = babelMethodRef.DeclaringType as GenericInstanceType;
|
||||
IGenericInstance gim = babelMethodRef.IsGenericMethod ? babelMethodRef : null;
|
||||
foreach (var method in declaringType.Methods) {
|
||||
if (compareMethod(MethodReferenceInstance.make(method, git, gim), babelMethodRef)) {
|
||||
if (!babelMethodRef.IsGenericMethod)
|
||||
methods.Add(memberReferenceConverter.convert(method));
|
||||
else {
|
||||
var gim2 = new GenericInstanceMethod(memberReferenceConverter.convert(method));
|
||||
foreach (var arg in babelMethodRef.GenericArguments)
|
||||
gim2.GenericArguments.Add(arg);
|
||||
methods.Add(gim2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
bool compareMethod(MethodReference method, BabelMethodreference babelMethodRef) {
|
||||
if (method.Parameters.Count != babelMethodRef.Parameters.Length)
|
||||
return false;
|
||||
if (method.Name != babelMethodRef.Name)
|
||||
return false;
|
||||
if (method.HasThis != babelMethodRef.HasThis)
|
||||
return false;
|
||||
if (method.GenericParameters.Count != babelMethodRef.GenericArguments.Length)
|
||||
return false;
|
||||
|
||||
if (!MemberReferenceHelper.compareTypes(method.MethodReturnType.ReturnType, babelMethodRef.ReturnType))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < babelMethodRef.Parameters.Length; i++) {
|
||||
if (!MemberReferenceHelper.compareTypes(method.Parameters[i].ParameterType, babelMethodRef.Parameters[i].ParameterType))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TypeDefinition resolve(TypeReference type) {
|
||||
if (type is TypeDefinition)
|
||||
return (TypeDefinition)type;
|
||||
|
||||
if (type.IsGenericInstance)
|
||||
type = ((GenericInstanceType)type).ElementType;
|
||||
|
||||
if (type.Scope == module)
|
||||
return DotNetUtils.getType(module, type);
|
||||
|
||||
return externalAssemblies.resolve(type);
|
||||
}
|
||||
|
||||
public CallSite readCallSite() {
|
||||
var returnType = readTypeReference();
|
||||
var paramTypes = readTypeReferences();
|
||||
var callingConvention = (CallingConvention)reader.ReadInt32();
|
||||
|
||||
var cs = new CallSite(returnType);
|
||||
foreach (var paramType in paramTypes)
|
||||
cs.Parameters.Add(new ParameterDefinition(paramType));
|
||||
cs.CallingConvention = convertCallingConvention(callingConvention);
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
static MethodCallingConvention convertCallingConvention(CallingConvention callingConvention) {
|
||||
switch (callingConvention) {
|
||||
case CallingConvention.Winapi: return MethodCallingConvention.Default;
|
||||
case CallingConvention.Cdecl: return MethodCallingConvention.C;
|
||||
case CallingConvention.StdCall: return MethodCallingConvention.StdCall;
|
||||
case CallingConvention.ThisCall: return MethodCallingConvention.ThisCall;
|
||||
case CallingConvention.FastCall: return MethodCallingConvention.FastCall;
|
||||
default: throw new ApplicationException(string.Format("Unknown CallingConvention {0}", callingConvention));
|
||||
}
|
||||
}
|
||||
|
||||
void initializeStrings(int headerOffset) {
|
||||
reader.BaseStream.Position = headerOffset;
|
||||
if (reader.ReadInt32() != STRINGS_SIG)
|
||||
throw new ApplicationException("Invalid strings sig");
|
||||
|
||||
strings = new string[readVariableLengthInt32()];
|
||||
for (int i = 0; i < strings.Length; i++)
|
||||
strings[i] = reader.ReadString();
|
||||
}
|
||||
|
||||
void initializeAssemblyNames(int headerOffset) {
|
||||
reader.BaseStream.Position = headerOffset;
|
||||
if (reader.ReadInt32() != ASSEMBLY_NAMES_SIG)
|
||||
throw new ApplicationException("Invalid assembly names sig");
|
||||
|
||||
assemblyNames = new AssemblyNameReference[readVariableLengthInt32()];
|
||||
for (int i = 0; i < assemblyNames.Length; i++)
|
||||
assemblyNames[i] = getModuleAssemblyReference(AssemblyNameReference.Parse(readString()));
|
||||
}
|
||||
|
||||
bool isModuleAssembly(IMetadataScope scope) {
|
||||
return DotNetUtils.isReferenceToModule(module, scope);
|
||||
}
|
||||
|
||||
AssemblyNameReference getModuleAssemblyReference(AssemblyNameReference asmRef) {
|
||||
if (isModuleAssembly(asmRef))
|
||||
return module.Assembly.Name;
|
||||
return memberReferenceConverter.convert(asmRef);
|
||||
}
|
||||
|
||||
void initializeMethodNames(int headerOffset) {
|
||||
reader.BaseStream.Position = headerOffset;
|
||||
if (reader.ReadInt32() != METHOD_NAMES_SIG)
|
||||
throw new ApplicationException("Invalid methods sig");
|
||||
|
||||
int numMethods = readVariableLengthInt32();
|
||||
methodOffsets = new Dictionary<string, int>(numMethods, StringComparer.Ordinal);
|
||||
for (int i = 0; i < numMethods; i++) {
|
||||
var methodName = readString();
|
||||
methodOffsets[methodName] = readVariableLengthInt32();
|
||||
}
|
||||
}
|
||||
|
||||
void initializeTypeReferences(int headerOffset) {
|
||||
reader.BaseStream.Position = headerOffset;
|
||||
if (reader.ReadInt32() != TYPEREFS_SIG)
|
||||
throw new ApplicationException("Invalid typerefs sig");
|
||||
|
||||
int numTypeRefs = reader.ReadInt32();
|
||||
typeReferences = new List<TypeReference>(numTypeRefs + 1);
|
||||
typeReferences.Add(null);
|
||||
var genericArgFixes = new Dictionary<GenericInstanceType, List<int>>();
|
||||
for (int i = 0; i < numTypeRefs; i++) {
|
||||
TypeId typeId = (TypeId)reader.ReadByte();
|
||||
switch (typeId) {
|
||||
case TypeId.TypeRef:
|
||||
typeReferences.Add(readTypeRef());
|
||||
break;
|
||||
|
||||
case TypeId.GenericInstance:
|
||||
List<int> genericArgs;
|
||||
var git = readGenericInstanceType(out genericArgs);
|
||||
typeReferences.Add(git);
|
||||
genericArgFixes[git] = genericArgs;
|
||||
break;
|
||||
|
||||
case TypeId.Pointer:
|
||||
typeReferences.Add(readPointerType());
|
||||
break;
|
||||
|
||||
case TypeId.Array:
|
||||
typeReferences.Add(readArrayType());
|
||||
break;
|
||||
|
||||
case TypeId.ByRef:
|
||||
typeReferences.Add(readByReferenceType());
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Unknown type id {0}", (int)typeId));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var kv in genericArgFixes) {
|
||||
var git = kv.Key;
|
||||
foreach (var typeNum in kv.Value)
|
||||
git.GenericArguments.Add(typeReferences[typeNum]);
|
||||
}
|
||||
}
|
||||
|
||||
TypeReference readTypeRef() {
|
||||
string ns, name;
|
||||
parseReflectionTypeName(readString(), out ns, out name);
|
||||
var asmRef = assemblyNames[readVariableLengthInt32()];
|
||||
var declaringType = readTypeReference();
|
||||
var typeReference = new TypeReference(ns, name, module, asmRef) {
|
||||
DeclaringType = declaringType,
|
||||
};
|
||||
typeReference.UpdateElementType();
|
||||
|
||||
typeReference = memberReferenceConverter.convert(typeReference);
|
||||
typeReference.IsValueType = isValueType(typeReference);
|
||||
return typeReference;
|
||||
}
|
||||
|
||||
bool isValueType(TypeReference typeRef) {
|
||||
var typeDef = typeRef as TypeDefinition;
|
||||
if (typeDef != null)
|
||||
return typeDef.IsValueType;
|
||||
|
||||
if (typeRef.Module == module && isModuleAssembly(typeRef.Scope))
|
||||
typeDef = DotNetUtils.getType(module, typeRef);
|
||||
else
|
||||
typeDef = resolve(typeRef);
|
||||
if (typeDef != null)
|
||||
return typeDef.IsValueType;
|
||||
|
||||
Log.w("Could not determine whether type '{0}' is a value type", Utils.removeNewlines(typeRef));
|
||||
return false; // Assume it's a reference type
|
||||
}
|
||||
|
||||
static void parseReflectionTypeName(string fullName, out string ns, out string name) {
|
||||
int index = getLastChar(fullName, '.');
|
||||
if (index < 0) {
|
||||
ns = "";
|
||||
name = fullName;
|
||||
}
|
||||
else {
|
||||
ns = unEscape(fullName.Substring(0, index));
|
||||
name = fullName.Substring(index + 1);
|
||||
}
|
||||
|
||||
index = getLastChar(name, '+');
|
||||
if (index < 0)
|
||||
name = unEscape(name);
|
||||
else {
|
||||
ns = "";
|
||||
name = unEscape(name.Substring(index + 1));
|
||||
}
|
||||
}
|
||||
|
||||
static int getLastChar(string name, char c) {
|
||||
if (string.IsNullOrEmpty(name))
|
||||
return -1;
|
||||
int index = name.Length - 1;
|
||||
while (true) {
|
||||
index = name.LastIndexOf(c, index);
|
||||
if (index < 0)
|
||||
return -1;
|
||||
if (index == 0)
|
||||
return index;
|
||||
if (name[index - 1] != '\\')
|
||||
return index;
|
||||
index--;
|
||||
}
|
||||
}
|
||||
|
||||
static string unEscape(string s) {
|
||||
var sb = new StringBuilder(s.Length);
|
||||
for (int i = 0; i < s.Length; i++) {
|
||||
if (s[i] == '\\' && i + 1 < s.Length)
|
||||
i++;
|
||||
sb.Append(s[i]);
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
GenericInstanceType readGenericInstanceType(out List<int> genericArgs) {
|
||||
var git = new GenericInstanceType(readTypeReference());
|
||||
int numArgs = readVariableLengthInt32();
|
||||
genericArgs = new List<int>(numArgs);
|
||||
for (int i = 0; i < numArgs; i++)
|
||||
genericArgs.Add(readVariableLengthInt32());
|
||||
return git;
|
||||
}
|
||||
|
||||
PointerType readPointerType() {
|
||||
return new PointerType(readTypeReference());
|
||||
}
|
||||
|
||||
ArrayType readArrayType() {
|
||||
return new ArrayType(readTypeReference(), readVariableLengthInt32());
|
||||
}
|
||||
|
||||
ByReferenceType readByReferenceType() {
|
||||
return new ByReferenceType(readTypeReference());
|
||||
}
|
||||
|
||||
public int readVariableLengthInt32() {
|
||||
byte b = reader.ReadByte();
|
||||
if ((b & 0x80) == 0)
|
||||
return b;
|
||||
if ((b & 0x40) == 0)
|
||||
return (((int)b & 0x3F) << 8) + reader.ReadByte();
|
||||
return (((int)b & 0x3F) << 24) +
|
||||
((int)reader.ReadByte() << 16) +
|
||||
((int)reader.ReadByte() << 8) +
|
||||
reader.ReadByte();
|
||||
}
|
||||
|
||||
int getMetadataOffset() {
|
||||
reader.BaseStream.Position = reader.BaseStream.Length - 4;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
if (reader.ReadInt32() == METADATA_SIG)
|
||||
return (int)reader.BaseStream.Position - 4;
|
||||
reader.BaseStream.Position -= 8;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
187
de4dot.code/deobfuscators/Babel_NET/MemberReferenceConverter.cs
Normal file
187
de4dot.code/deobfuscators/Babel_NET/MemberReferenceConverter.cs
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
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.Babel_NET {
|
||||
class TypeReferenceConverter : TypeReferenceUpdaterBase {
|
||||
MemberReferenceConverter memberReferenceConverter;
|
||||
|
||||
ModuleDefinition Module {
|
||||
get { return memberReferenceConverter.Module; }
|
||||
}
|
||||
|
||||
public TypeReferenceConverter(MemberReferenceConverter memberReferenceConverter) {
|
||||
this.memberReferenceConverter = memberReferenceConverter;
|
||||
}
|
||||
|
||||
public TypeReference convert(TypeReference a) {
|
||||
var newOne = update(a);
|
||||
if (!(a is GenericParameter) && !MemberReferenceHelper.compareTypes(newOne, a))
|
||||
throw new ApplicationException("Could not convert type reference");
|
||||
return newOne;
|
||||
}
|
||||
|
||||
protected override TypeReference updateTypeReference(TypeReference a) {
|
||||
if (a.Module == Module)
|
||||
return a;
|
||||
|
||||
var newTypeRef = new TypeReference(a.Namespace, a.Name, Module, memberReferenceConverter.convert(a.Scope), a.IsValueType);
|
||||
foreach (var gp in a.GenericParameters)
|
||||
newTypeRef.GenericParameters.Add(new GenericParameter(gp.Name, newTypeRef));
|
||||
newTypeRef.DeclaringType = update(a.DeclaringType);
|
||||
newTypeRef.UpdateElementType();
|
||||
return newTypeRef;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts type references/definitions in one module to this module
|
||||
class MemberReferenceConverter {
|
||||
ModuleDefinition module;
|
||||
|
||||
public ModuleDefinition Module {
|
||||
get { return module; }
|
||||
}
|
||||
|
||||
public MemberReferenceConverter(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
bool isInOurModule(MemberReference memberRef) {
|
||||
return memberRef.Module == module;
|
||||
}
|
||||
|
||||
public TypeReference convert(TypeReference typeRef) {
|
||||
if (typeRef == null)
|
||||
return null;
|
||||
typeRef = new TypeReferenceConverter(this).convert(typeRef);
|
||||
return tryGetTypeDefinition(typeRef);
|
||||
}
|
||||
|
||||
public FieldReference convert(FieldReference fieldRef) {
|
||||
if (isInOurModule(fieldRef))
|
||||
return tryGetFieldDefinition(fieldRef);
|
||||
|
||||
return new FieldReference(fieldRef.Name, convert(fieldRef.FieldType), convert(fieldRef.DeclaringType));
|
||||
}
|
||||
|
||||
public MethodReference convert(MethodReference methodRef) {
|
||||
if (methodRef.GetType() != typeof(MethodReference) && methodRef.GetType() != typeof(MethodDefinition))
|
||||
throw new ApplicationException("Invalid method reference type");
|
||||
if (isInOurModule(methodRef))
|
||||
return tryGetMethodDefinition(methodRef);
|
||||
|
||||
return copy(methodRef);
|
||||
}
|
||||
|
||||
public MethodReference copy(MethodReference methodRef) {
|
||||
if (methodRef.GetType() != typeof(MethodReference) && methodRef.GetType() != typeof(MethodDefinition))
|
||||
throw new ApplicationException("Invalid method reference type");
|
||||
|
||||
var newMethodRef = new MethodReference(methodRef.Name, convert(methodRef.MethodReturnType.ReturnType), convert(methodRef.DeclaringType));
|
||||
newMethodRef.HasThis = methodRef.HasThis;
|
||||
newMethodRef.ExplicitThis = methodRef.ExplicitThis;
|
||||
newMethodRef.CallingConvention = methodRef.CallingConvention;
|
||||
foreach (var param in methodRef.Parameters)
|
||||
newMethodRef.Parameters.Add(new ParameterDefinition(param.Name, param.Attributes, convert(param.ParameterType)));
|
||||
foreach (var gp in methodRef.GenericParameters)
|
||||
newMethodRef.GenericParameters.Add(new GenericParameter(gp.Name, newMethodRef));
|
||||
return newMethodRef;
|
||||
}
|
||||
|
||||
public IMetadataScope convert(IMetadataScope scope) {
|
||||
switch (scope.MetadataScopeType) {
|
||||
case MetadataScopeType.AssemblyNameReference:
|
||||
return convert((AssemblyNameReference)scope);
|
||||
|
||||
case MetadataScopeType.ModuleDefinition:
|
||||
var mod = (ModuleDefinition)scope;
|
||||
if (mod.Assembly != null)
|
||||
return convert((AssemblyNameReference)mod.Assembly.Name);
|
||||
return convert((ModuleReference)scope);
|
||||
|
||||
case MetadataScopeType.ModuleReference:
|
||||
return convert((ModuleReference)scope);
|
||||
|
||||
default:
|
||||
throw new ApplicationException("Unknown MetadataScopeType");
|
||||
}
|
||||
}
|
||||
|
||||
public AssemblyNameReference convert(AssemblyNameReference asmRef) {
|
||||
foreach (var modAsmRef in module.AssemblyReferences) {
|
||||
if (modAsmRef.FullName == asmRef.FullName)
|
||||
return modAsmRef;
|
||||
}
|
||||
|
||||
var newAsmRef = AssemblyNameReference.Parse(asmRef.FullName);
|
||||
module.AssemblyReferences.Add(newAsmRef);
|
||||
return newAsmRef;
|
||||
}
|
||||
|
||||
public ModuleReference convert(ModuleReference modRef) {
|
||||
foreach (var modModRef in module.ModuleReferences) {
|
||||
if (modModRef.Name == modRef.Name)
|
||||
return modModRef;
|
||||
}
|
||||
|
||||
var newModRef = new ModuleReference(modRef.Name);
|
||||
module.ModuleReferences.Add(newModRef);
|
||||
return newModRef;
|
||||
}
|
||||
|
||||
public TypeReference tryGetTypeDefinition(TypeReference typeRef) {
|
||||
return DotNetUtils.getType(module, typeRef) ?? typeRef;
|
||||
}
|
||||
|
||||
public FieldReference tryGetFieldDefinition(FieldReference fieldRef) {
|
||||
var fieldDef = fieldRef as FieldDefinition;
|
||||
if (fieldDef != null)
|
||||
return fieldDef;
|
||||
|
||||
var declaringType = DotNetUtils.getType(module, fieldRef.DeclaringType);
|
||||
if (declaringType == null)
|
||||
return fieldRef;
|
||||
foreach (var field in declaringType.Fields) {
|
||||
if (MemberReferenceHelper.compareFieldReference(field, fieldRef))
|
||||
return field;
|
||||
}
|
||||
|
||||
return fieldRef;
|
||||
}
|
||||
|
||||
public MethodReference tryGetMethodDefinition(MethodReference methodRef) {
|
||||
var methodDef = methodRef as MethodDefinition;
|
||||
if (methodDef != null)
|
||||
return methodDef;
|
||||
|
||||
var declaringType = DotNetUtils.getType(module, methodRef.DeclaringType);
|
||||
if (declaringType == null)
|
||||
return methodRef;
|
||||
foreach (var method in declaringType.Methods) {
|
||||
if (MemberReferenceHelper.compareMethodReference(method, methodRef))
|
||||
return method;
|
||||
}
|
||||
|
||||
return methodRef;
|
||||
}
|
||||
}
|
||||
}
|
94
de4dot.code/deobfuscators/Babel_NET/MethodBodyReader.cs
Normal file
94
de4dot.code/deobfuscators/Babel_NET/MethodBodyReader.cs
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class MethodBodyReader : MethodBodyReaderBase {
|
||||
ImageReader imageReader;
|
||||
public int Flags2 { get; set; }
|
||||
public short MaxStack { get; set; }
|
||||
|
||||
public MethodBodyReader(ImageReader imageReader, BinaryReader reader)
|
||||
: base(reader) {
|
||||
this.imageReader = imageReader;
|
||||
}
|
||||
|
||||
public void read(ParameterDefinition[] parameters) {
|
||||
this.parameters = parameters;
|
||||
Flags2 = reader.ReadInt16();
|
||||
MaxStack = reader.ReadInt16();
|
||||
setLocals(imageReader.readTypeReferences());
|
||||
readInstructions(imageReader.readVariableLengthInt32());
|
||||
readExceptionHandlers(imageReader.readVariableLengthInt32());
|
||||
}
|
||||
|
||||
protected override FieldReference readInlineField(Instruction instr) {
|
||||
return imageReader.readFieldReference();
|
||||
}
|
||||
|
||||
protected override MethodReference readInlineMethod(Instruction instr) {
|
||||
return imageReader.readMethodReference();
|
||||
}
|
||||
|
||||
protected override CallSite readInlineSig(Instruction instr) {
|
||||
return imageReader.readCallSite();
|
||||
}
|
||||
|
||||
protected override string readInlineString(Instruction instr) {
|
||||
return imageReader.readString();
|
||||
}
|
||||
|
||||
protected override MemberReference readInlineTok(Instruction instr) {
|
||||
switch (reader.ReadByte()) {
|
||||
case 0: return imageReader.readTypeReference();
|
||||
case 1: return imageReader.readFieldReference();
|
||||
case 2: return imageReader.readMethodReference();
|
||||
default: throw new ApplicationException("Unknown token type");
|
||||
}
|
||||
}
|
||||
|
||||
protected override TypeReference readInlineType(Instruction instr) {
|
||||
return imageReader.readTypeReference();
|
||||
}
|
||||
|
||||
protected override ExceptionHandler readExceptionHandler() {
|
||||
var ehType = (ExceptionHandlerType)reader.ReadByte();
|
||||
int tryOffset = imageReader.readVariableLengthInt32();
|
||||
int tryLength = imageReader.readVariableLengthInt32();
|
||||
int handlerOffset = imageReader.readVariableLengthInt32();
|
||||
int handlerLength = imageReader.readVariableLengthInt32();
|
||||
var catchType = imageReader.readTypeReference();
|
||||
int filterOffset = imageReader.readVariableLengthInt32();
|
||||
|
||||
var eh = new ExceptionHandler(ehType);
|
||||
eh.TryStart = getInstruction(tryOffset);
|
||||
eh.TryEnd = getInstructionOrNull(tryOffset + tryLength);
|
||||
if (ehType == ExceptionHandlerType.Filter)
|
||||
eh.FilterStart = getInstruction(filterOffset);
|
||||
eh.HandlerStart = getInstruction(handlerOffset);
|
||||
eh.HandlerEnd = getInstructionOrNull(handlerOffset + handlerLength);
|
||||
eh.CatchType = catchType;
|
||||
return eh;
|
||||
}
|
||||
}
|
||||
}
|
240
de4dot.code/deobfuscators/Babel_NET/MethodBodyReaderBase.cs
Normal file
240
de4dot.code/deobfuscators/Babel_NET/MethodBodyReaderBase.cs
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
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.Babel_NET {
|
||||
abstract class MethodBodyReaderBase {
|
||||
protected BinaryReader reader;
|
||||
public List<VariableDefinition> Locals { get; set; }
|
||||
public Instruction[] Instructions { get; set; }
|
||||
public ExceptionHandler[] ExceptionHandlers { get; set; }
|
||||
protected ParameterDefinition[] parameters;
|
||||
int currentOffset;
|
||||
|
||||
public MethodBodyReaderBase(BinaryReader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
protected void setLocals(IList<TypeReference> types) {
|
||||
Locals = new List<VariableDefinition>(types.Count);
|
||||
foreach (var type in types)
|
||||
Locals.Add(new VariableDefinition(type));
|
||||
}
|
||||
|
||||
protected void readInstructions(int numInstrs) {
|
||||
Instructions = new Instruction[numInstrs];
|
||||
currentOffset = 0;
|
||||
for (int i = 0; i < Instructions.Length; i++) {
|
||||
var instr = readInstruction();
|
||||
Instructions[i] = instr;
|
||||
if (instr.OpCode.Code == Code.Switch) {
|
||||
int[] targets = (int[])instr.Operand;
|
||||
currentOffset += instr.OpCode.Size + 4 + 4 * targets.Length;
|
||||
}
|
||||
else
|
||||
currentOffset += Instructions[i].GetSize();
|
||||
}
|
||||
|
||||
foreach (var instr in Instructions) {
|
||||
switch (instr.OpCode.OperandType) {
|
||||
case OperandType.InlineBrTarget:
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
instr.Operand = getInstruction((int)instr.Operand);
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var intTargets = (int[])instr.Operand;
|
||||
var targets = new Instruction[intTargets.Length];
|
||||
for (int i = 0; i < intTargets.Length; i++)
|
||||
targets[i] = getInstruction(intTargets[i]);
|
||||
instr.Operand = targets;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Instruction getInstructionOrNull(int offset) {
|
||||
foreach (var instr in Instructions) {
|
||||
if (instr.Offset == offset)
|
||||
return instr;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Instruction getInstruction(int offset) {
|
||||
var instr = getInstructionOrNull(offset);
|
||||
if (instr != null)
|
||||
return instr;
|
||||
throw new ApplicationException(string.Format("No instruction found at offset {0:X4}", offset));
|
||||
}
|
||||
|
||||
Instruction readInstruction() {
|
||||
int offset = currentOffset;
|
||||
var opcode = readOpCode();
|
||||
var instr = new Instruction {
|
||||
OpCode = opcode,
|
||||
Offset = offset,
|
||||
};
|
||||
instr.Operand = readOperand(instr);
|
||||
return instr;
|
||||
}
|
||||
|
||||
object readOperand(Instruction instr) {
|
||||
switch (instr.OpCode.OperandType) {
|
||||
case OperandType.InlineBrTarget:
|
||||
return readInlineBrTarget(instr);
|
||||
case OperandType.InlineField:
|
||||
return readInlineField(instr);
|
||||
case OperandType.InlineI:
|
||||
return readInlineI(instr);
|
||||
case OperandType.InlineI8:
|
||||
return readInlineI8(instr);
|
||||
case OperandType.InlineMethod:
|
||||
return readInlineMethod(instr);
|
||||
case OperandType.InlineNone:
|
||||
return readInlineNone(instr);
|
||||
case OperandType.InlinePhi:
|
||||
return readInlinePhi(instr);
|
||||
case OperandType.InlineR:
|
||||
return readInlineR(instr);
|
||||
case OperandType.InlineSig:
|
||||
return readInlineSig(instr);
|
||||
case OperandType.InlineString:
|
||||
return readInlineString(instr);
|
||||
case OperandType.InlineSwitch:
|
||||
return readInlineSwitch(instr);
|
||||
case OperandType.InlineTok:
|
||||
return readInlineTok(instr);
|
||||
case OperandType.InlineType:
|
||||
return readInlineType(instr);
|
||||
case OperandType.InlineVar:
|
||||
return readInlineVar(instr);
|
||||
case OperandType.InlineArg:
|
||||
return readInlineArg(instr);
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
return readShortInlineBrTarget(instr);
|
||||
case OperandType.ShortInlineI:
|
||||
return readShortInlineI(instr);
|
||||
case OperandType.ShortInlineR:
|
||||
return readShortInlineR(instr);
|
||||
case OperandType.ShortInlineVar:
|
||||
return readShortInlineVar(instr);
|
||||
case OperandType.ShortInlineArg:
|
||||
return readShortInlineArg(instr);
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Unknown operand type {0}", instr.OpCode.OperandType));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual int readInlineBrTarget(Instruction instr) {
|
||||
return currentOffset + instr.GetSize() + reader.ReadInt32();
|
||||
}
|
||||
|
||||
protected abstract FieldReference readInlineField(Instruction instr);
|
||||
|
||||
protected virtual int readInlineI(Instruction instr) {
|
||||
return reader.ReadInt32();
|
||||
}
|
||||
|
||||
protected virtual long readInlineI8(Instruction instr) {
|
||||
return reader.ReadInt64();
|
||||
}
|
||||
|
||||
protected abstract MethodReference readInlineMethod(Instruction instr);
|
||||
|
||||
protected virtual object readInlineNone(Instruction instr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual object readInlinePhi(Instruction instr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected virtual double readInlineR(Instruction instr) {
|
||||
return reader.ReadDouble();
|
||||
}
|
||||
|
||||
protected abstract CallSite readInlineSig(Instruction instr);
|
||||
|
||||
protected abstract string readInlineString(Instruction instr);
|
||||
|
||||
protected virtual int[] readInlineSwitch(Instruction instr) {
|
||||
var targets = new int[reader.ReadInt32()];
|
||||
int offset = currentOffset + instr.OpCode.Size + 4 + 4 * targets.Length;
|
||||
for (int i = 0; i < targets.Length; i++)
|
||||
targets[i] = offset + reader.ReadInt32();
|
||||
return targets;
|
||||
}
|
||||
|
||||
protected abstract MemberReference readInlineTok(Instruction instr);
|
||||
|
||||
protected abstract TypeReference readInlineType(Instruction instr);
|
||||
|
||||
protected virtual VariableDefinition readInlineVar(Instruction instr) {
|
||||
return Locals[reader.ReadUInt16()];
|
||||
}
|
||||
|
||||
protected virtual ParameterDefinition readInlineArg(Instruction instr) {
|
||||
return parameters[reader.ReadUInt16()];
|
||||
}
|
||||
|
||||
protected virtual int readShortInlineBrTarget(Instruction instr) {
|
||||
return currentOffset + instr.GetSize() + reader.ReadSByte();
|
||||
}
|
||||
|
||||
protected virtual object readShortInlineI(Instruction instr) {
|
||||
if (instr.OpCode.Code == Code.Ldc_I4_S)
|
||||
return reader.ReadSByte();
|
||||
return reader.ReadByte();
|
||||
}
|
||||
|
||||
protected virtual float readShortInlineR(Instruction instr) {
|
||||
return reader.ReadSingle();
|
||||
}
|
||||
|
||||
protected virtual VariableDefinition readShortInlineVar(Instruction instr) {
|
||||
return Locals[reader.ReadByte()];
|
||||
}
|
||||
|
||||
protected virtual ParameterDefinition readShortInlineArg(Instruction instr) {
|
||||
return parameters[reader.ReadByte()];
|
||||
}
|
||||
|
||||
OpCode readOpCode() {
|
||||
var op = reader.ReadByte();
|
||||
if (op != 0xFE)
|
||||
return OpCodes.OneByteOpCode[op];
|
||||
return OpCodes.TwoBytesOpCode[reader.ReadByte()];
|
||||
}
|
||||
|
||||
protected void readExceptionHandlers(int numExceptionHandlers) {
|
||||
ExceptionHandlers = new ExceptionHandler[numExceptionHandlers];
|
||||
for (int i = 0; i < ExceptionHandlers.Length; i++)
|
||||
ExceptionHandlers[i] = readExceptionHandler();
|
||||
}
|
||||
|
||||
protected abstract ExceptionHandler readExceptionHandler();
|
||||
}
|
||||
}
|
166
de4dot.code/deobfuscators/Babel_NET/MethodReferenceReader.cs
Normal file
166
de4dot.code/deobfuscators/Babel_NET/MethodReferenceReader.cs
Normal file
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
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 Mono.Collections.Generic;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class BabelMethodreference : IGenericInstance {
|
||||
public string Name { get; set; }
|
||||
public TypeReference DeclaringType { get; set; }
|
||||
public TypeReference ReturnType { get; set; }
|
||||
public ParameterDefinition[] Parameters { get; set; }
|
||||
public TypeReference[] GenericArguments { get; set; }
|
||||
public int Flags { get; set; }
|
||||
|
||||
public bool HasThis {
|
||||
get { return (Flags & 1) != 0; }
|
||||
}
|
||||
|
||||
public bool IsGenericMethod {
|
||||
get { return (Flags & 2) != 0; }
|
||||
}
|
||||
|
||||
bool IGenericInstance.HasGenericArguments {
|
||||
get { return IsGenericMethod; }
|
||||
}
|
||||
|
||||
Collection<TypeReference> IGenericInstance.GenericArguments {
|
||||
get { return new Collection<TypeReference>(GenericArguments); }
|
||||
}
|
||||
|
||||
MetadataToken IMetadataTokenProvider.MetadataToken {
|
||||
get { throw new NotImplementedException(); }
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
}
|
||||
|
||||
class BabelMethodDefinition : BabelMethodreference {
|
||||
ParameterDefinition thisParameter;
|
||||
|
||||
public int Flags2 { get; set; }
|
||||
public short MaxStack { get; set; }
|
||||
public List<VariableDefinition> Locals { get; set; }
|
||||
public Instruction[] Instructions { get; set; }
|
||||
public ExceptionHandler[] ExceptionHandlers { get; set; }
|
||||
|
||||
public bool IsStatic {
|
||||
get { return (Flags2 & 0x10) != 0; }
|
||||
}
|
||||
|
||||
public bool RequiresFatExceptionHandler {
|
||||
get { return (Flags2 & 0x20) != 0; }
|
||||
}
|
||||
|
||||
public bool InitLocals {
|
||||
get { return (Flags2 & 0x40) != 0; }
|
||||
}
|
||||
|
||||
public bool CacheMethod {
|
||||
get { return (Flags2 & 0x80) != 0; }
|
||||
}
|
||||
|
||||
public ParameterDefinition ThisParameter {
|
||||
get {
|
||||
if (!HasThis)
|
||||
return null;
|
||||
if (thisParameter != null)
|
||||
return thisParameter;
|
||||
return thisParameter = new ParameterDefinition(DeclaringType);
|
||||
}
|
||||
}
|
||||
|
||||
public void setBody(MethodBodyReader mbr) {
|
||||
Flags2 = mbr.Flags2;
|
||||
MaxStack = mbr.MaxStack;
|
||||
Locals = mbr.Locals;
|
||||
Instructions = mbr.Instructions;
|
||||
ExceptionHandlers = mbr.ExceptionHandlers;
|
||||
}
|
||||
|
||||
public ParameterDefinition[] getRealParameters() {
|
||||
if (ThisParameter == null)
|
||||
return Parameters;
|
||||
var parameters = new ParameterDefinition[Parameters.Length + 1];
|
||||
parameters[0] = ThisParameter;
|
||||
Array.Copy(Parameters, 0, parameters, 1, Parameters.Length);
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
class MethodReferenceReader {
|
||||
ImageReader imageReader;
|
||||
BinaryReader reader;
|
||||
BabelMethodreference bmr;
|
||||
|
||||
public MethodReferenceReader(ImageReader imageReader, BinaryReader reader)
|
||||
: this(imageReader, reader, new BabelMethodreference()) {
|
||||
}
|
||||
|
||||
public MethodReferenceReader(ImageReader imageReader, BinaryReader reader, BabelMethodreference bmr) {
|
||||
this.imageReader = imageReader;
|
||||
this.reader = reader;
|
||||
this.bmr = bmr;
|
||||
}
|
||||
|
||||
public BabelMethodreference read() {
|
||||
bmr.Name = imageReader.readString();
|
||||
bmr.DeclaringType = imageReader.readTypeReference();
|
||||
bmr.ReturnType = imageReader.readTypeReference();
|
||||
bmr.Parameters = readParameters();
|
||||
bmr.Flags = reader.ReadByte();
|
||||
if (bmr.IsGenericMethod)
|
||||
bmr.GenericArguments = imageReader.readTypeReferences();
|
||||
else
|
||||
bmr.GenericArguments = new TypeReference[0];
|
||||
return bmr;
|
||||
}
|
||||
|
||||
ParameterDefinition[] readParameters() {
|
||||
var typeReferences = imageReader.readTypeReferences();
|
||||
var parameters = new ParameterDefinition[typeReferences.Length];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
parameters[i] = new ParameterDefinition(typeReferences[i]);
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
|
||||
class MethodDefinitionReader {
|
||||
MethodReferenceReader methodReferenceReader;
|
||||
MethodBodyReader methodBodyReader;
|
||||
BabelMethodDefinition bmd;
|
||||
|
||||
public MethodDefinitionReader(ImageReader imageReader, BinaryReader reader) {
|
||||
this.bmd = new BabelMethodDefinition();
|
||||
this.methodReferenceReader = new MethodReferenceReader(imageReader, reader, bmd);
|
||||
this.methodBodyReader = new MethodBodyReader(imageReader, reader);
|
||||
}
|
||||
|
||||
public BabelMethodDefinition read() {
|
||||
methodReferenceReader.read();
|
||||
methodBodyReader.read(bmd.getRealParameters());
|
||||
bmd.setBody(methodBodyReader);
|
||||
return bmd;
|
||||
}
|
||||
}
|
||||
}
|
216
de4dot.code/deobfuscators/Babel_NET/MethodsDecrypter.cs
Normal file
216
de4dot.code/deobfuscators/Babel_NET/MethodsDecrypter.cs
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
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.Babel_NET {
|
||||
class MethodsDecrypter : IDisposable {
|
||||
ModuleDefinition module;
|
||||
Dictionary<string, ImageReader> imageReaders = new Dictionary<string, ImageReader>(StringComparer.Ordinal);
|
||||
TypeDefinition methodsDecrypterCreator;
|
||||
TypeDefinition methodsDecrypter;
|
||||
MethodDefinition decryptExecuteMethod;
|
||||
EmbeddedResource encryptedResource;
|
||||
|
||||
public bool Detected {
|
||||
get { return methodsDecrypterCreator != null; }
|
||||
}
|
||||
|
||||
public MethodsDecrypter(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
foreach (var imageReader in imageReaders.Values)
|
||||
imageReader.Dispose();
|
||||
}
|
||||
|
||||
public void find() {
|
||||
var requiredFields = new string[] {
|
||||
"System.Threading.ReaderWriterLock",
|
||||
"System.Collections.Hashtable",
|
||||
};
|
||||
foreach (var type in module.GetTypes()) {
|
||||
var fieldTypes = new FieldTypes(type);
|
||||
if (!fieldTypes.all(requiredFields))
|
||||
continue;
|
||||
if (DotNetUtils.getMethod(type, "Finalize") == null)
|
||||
continue;
|
||||
var executeMethod = DotNetUtils.getMethod(type, "System.Object", "(System.String,System.Object[])");
|
||||
if (executeMethod == null || !executeMethod.IsStatic || executeMethod.Body == null)
|
||||
continue;
|
||||
|
||||
var decrypterType = findMethodsDecrypterType(type);
|
||||
if (decrypterType == null)
|
||||
continue;
|
||||
|
||||
methodsDecrypterCreator = type;
|
||||
methodsDecrypter = decrypterType;
|
||||
decryptExecuteMethod = executeMethod;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TypeDefinition findMethodsDecrypterType(TypeDefinition type) {
|
||||
foreach (var field in type.Fields) {
|
||||
var fieldType = DotNetUtils.getType(module, field.FieldType);
|
||||
if (fieldType == null)
|
||||
continue;
|
||||
if (DotNetUtils.getMethod(fieldType, "Finalize") == null)
|
||||
continue;
|
||||
if (!new FieldTypes(fieldType).exists("System.Collections.Hashtable"))
|
||||
continue;
|
||||
if (DotNetUtils.getMethod(fieldType, "System.String", "()") == null)
|
||||
continue;
|
||||
|
||||
return fieldType;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void initialize(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
|
||||
if (methodsDecrypter == null)
|
||||
return;
|
||||
|
||||
encryptedResource = findEncryptedResource(simpleDeobfuscator, deob);
|
||||
if (encryptedResource == null) {
|
||||
Log.w("Could not find encrypted methods resource");
|
||||
return;
|
||||
}
|
||||
|
||||
addImageReader("", new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData()));
|
||||
}
|
||||
|
||||
void addImageReader(string name, byte[] data) {
|
||||
var imageReader = new ImageReader(module, data);
|
||||
if (!imageReader.initialize()) {
|
||||
Log.w("Could not read encrypted methods");
|
||||
return;
|
||||
}
|
||||
if (imageReaders.ContainsKey(name))
|
||||
throw new ApplicationException(string.Format("ImageReader for name '{0}' already exists", name));
|
||||
imageReaders[name] = imageReader;
|
||||
}
|
||||
|
||||
EmbeddedResource findEncryptedResource(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
|
||||
foreach (var method in methodsDecrypter.Methods) {
|
||||
if (!DotNetUtils.isMethod(method, "System.String", "()"))
|
||||
continue;
|
||||
if (!method.IsStatic)
|
||||
continue;
|
||||
simpleDeobfuscator.deobfuscate(method);
|
||||
simpleDeobfuscator.decryptStrings(method, deob);
|
||||
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
||||
var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
|
||||
if (resource != null)
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class EncryptInfo {
|
||||
public string encryptedMethodName;
|
||||
public string feature;
|
||||
public MethodDefinition method;
|
||||
public EncryptInfo(string encryptedMethodName, string feature, MethodDefinition method) {
|
||||
this.encryptedMethodName = encryptedMethodName;
|
||||
this.feature = feature;
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
if (feature != "")
|
||||
return string.Format("{0}:{1} {2:X8}", feature, encryptedMethodName, method.MetadataToken.ToInt32());
|
||||
else
|
||||
return string.Format("{0} {1:X8}", encryptedMethodName, method.MetadataToken.ToInt32());
|
||||
}
|
||||
}
|
||||
|
||||
public void decrypt() {
|
||||
int numNonDecryptedMethods = 0;
|
||||
int totalEncryptedMethods = 0;
|
||||
foreach (var info in getEncryptedMethods()) {
|
||||
totalEncryptedMethods++;
|
||||
ImageReader imageReader;
|
||||
if (!imageReaders.TryGetValue(info.feature, out imageReader)) {
|
||||
numNonDecryptedMethods++;
|
||||
continue;
|
||||
}
|
||||
Log.v("Decrypting method {0:X8}", info.method.MetadataToken.ToInt32());
|
||||
imageReader.restore(info.encryptedMethodName, info.method);
|
||||
}
|
||||
if (numNonDecryptedMethods > 0)
|
||||
Log.w("{0}/{1} methods not decrypted", numNonDecryptedMethods, totalEncryptedMethods);
|
||||
}
|
||||
|
||||
List<EncryptInfo> getEncryptedMethods() {
|
||||
var infos = new List<EncryptInfo>();
|
||||
|
||||
foreach (var type in module.GetTypes()) {
|
||||
foreach (var method in type.Methods) {
|
||||
EncryptInfo info;
|
||||
if (checkEncryptedMethod(method, out info))
|
||||
infos.Add(info);
|
||||
}
|
||||
}
|
||||
|
||||
return infos;
|
||||
}
|
||||
|
||||
bool checkEncryptedMethod(MethodDefinition method, out EncryptInfo info) {
|
||||
info = null;
|
||||
if (method.Body == null)
|
||||
return false;
|
||||
if (!callsExecuteMethod(method))
|
||||
return false;
|
||||
|
||||
var strings = DotNetUtils.getCodeStrings(method);
|
||||
if (strings.Count != 1)
|
||||
throw new ApplicationException(string.Format("Could not find name of encrypted method"));
|
||||
|
||||
string feature = "";
|
||||
string name = strings[0];
|
||||
int index = name.IndexOf(':');
|
||||
if (index >= 0) {
|
||||
feature = name.Substring(0, index);
|
||||
name = name.Substring(index + 1);
|
||||
}
|
||||
|
||||
info = new EncryptInfo(name, feature, method);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool callsExecuteMethod(MethodDefinition method) {
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt)
|
||||
continue;
|
||||
if (MemberReferenceHelper.compareMethodReferenceAndDeclaringType(decryptExecuteMethod, instr.Operand as MethodReference))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
172
de4dot.code/deobfuscators/Babel_NET/ProxyDelegateFinder.cs
Normal file
172
de4dot.code/deobfuscators/Babel_NET/ProxyDelegateFinder.cs
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
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.Babel_NET {
|
||||
class ProxyDelegateFinder : ProxyDelegateFinderBase {
|
||||
MethodDefinitionAndDeclaringTypeDict<ProxyCreatorType> methodToType = new MethodDefinitionAndDeclaringTypeDict<ProxyCreatorType>();
|
||||
|
||||
public ProxyDelegateFinder(ModuleDefinition module)
|
||||
: base(module) {
|
||||
}
|
||||
|
||||
enum ProxyCreatorType {
|
||||
None,
|
||||
CallOrCallvirt,
|
||||
Newobj,
|
||||
}
|
||||
|
||||
class Context {
|
||||
public TypeReference delegateType;
|
||||
public int methodToken;
|
||||
public int declaringTypeToken;
|
||||
public ProxyCreatorType proxyCreatorType;
|
||||
public Context(TypeReference delegateType, int methodToken, int declaringTypeToken, ProxyCreatorType proxyCreatorType) {
|
||||
this.delegateType = delegateType;
|
||||
this.methodToken = methodToken;
|
||||
this.declaringTypeToken = declaringTypeToken;
|
||||
this.proxyCreatorType = proxyCreatorType;
|
||||
}
|
||||
}
|
||||
|
||||
protected override object checkCctor(TypeDefinition type, MethodDefinition cctor) {
|
||||
var instructions = cctor.Body.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
TypeReference delegateType;
|
||||
FieldReference delegateField;
|
||||
MethodReference createMethod;
|
||||
int methodToken, declaringTypeToken;
|
||||
var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Ldtoken, OpCodes.Ldc_I4, OpCodes.Ldc_I4, OpCodes.Ldtoken, OpCodes.Call);
|
||||
if (instrs != null) {
|
||||
delegateType = instrs[0].Operand as TypeReference;
|
||||
methodToken = DotNetUtils.getLdcI4Value(instrs[1]);
|
||||
declaringTypeToken = DotNetUtils.getLdcI4Value(instrs[2]);
|
||||
delegateField = instrs[3].Operand as FieldReference;
|
||||
createMethod = instrs[4].Operand as MethodReference;
|
||||
}
|
||||
else if ((instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Ldtoken, OpCodes.Ldc_I4, OpCodes.Ldtoken, OpCodes.Call)) != null) {
|
||||
delegateType = instrs[0].Operand as TypeReference;
|
||||
methodToken = DotNetUtils.getLdcI4Value(instrs[1]);
|
||||
declaringTypeToken = -1;
|
||||
delegateField = instrs[2].Operand as FieldReference;
|
||||
createMethod = instrs[3].Operand as MethodReference;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
|
||||
if (delegateType == null)
|
||||
continue;
|
||||
if (delegateField == null)
|
||||
continue;
|
||||
if (createMethod == null)
|
||||
continue;
|
||||
var proxyCreatorType = methodToType.find(createMethod);
|
||||
if (proxyCreatorType == ProxyCreatorType.None)
|
||||
continue;
|
||||
|
||||
return new Context(delegateType, methodToken, declaringTypeToken, proxyCreatorType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override void onFoundProxyDelegate(TypeDefinition type) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!method.IsStatic || !method.HasBody || method.Name == ".cctor")
|
||||
continue;
|
||||
|
||||
var instructions = method.Body.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var instr = instructions[i];
|
||||
if (instr.OpCode.Code != Code.Ldsfld)
|
||||
continue;
|
||||
|
||||
add(method, (FieldDefinition)instr.Operand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void getCallInfo(object context, FieldDefinition field, out MethodReference calledMethod, out OpCode callOpcode) {
|
||||
var ctx = (Context)context;
|
||||
|
||||
switch (ctx.proxyCreatorType) {
|
||||
case ProxyCreatorType.CallOrCallvirt:
|
||||
callOpcode = field.IsAssembly ? OpCodes.Callvirt : OpCodes.Call;
|
||||
break;
|
||||
case ProxyCreatorType.Newobj:
|
||||
callOpcode = OpCodes.Newobj;
|
||||
break;
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Invalid proxy creator type: {0}", ctx.proxyCreatorType));
|
||||
}
|
||||
|
||||
calledMethod = module.LookupToken(ctx.methodToken) as MethodReference;
|
||||
}
|
||||
|
||||
public void findDelegateCreator() {
|
||||
var requiredTypes = new string[] {
|
||||
"System.ModuleHandle",
|
||||
};
|
||||
foreach (var type in module.Types) {
|
||||
if (!new FieldTypes(type).exactly(requiredTypes))
|
||||
continue;
|
||||
|
||||
foreach (var method in type.Methods) {
|
||||
if (!method.IsStatic || method.Body == null)
|
||||
continue;
|
||||
if (!DotNetUtils.isMethod(method, "System.Void", "(System.RuntimeTypeHandle,System.Int32,System.RuntimeFieldHandle)") &&
|
||||
!DotNetUtils.isMethod(method, "System.Void", "(System.RuntimeTypeHandle,System.Int32,System.Int32,System.RuntimeFieldHandle)"))
|
||||
continue;
|
||||
var creatorType = getProxyCreatorType(method);
|
||||
if (creatorType == ProxyCreatorType.None)
|
||||
continue;
|
||||
|
||||
methodToType.add(method, creatorType);
|
||||
setDelegateCreatorMethod(method);
|
||||
}
|
||||
|
||||
if (methodToType.Count == 0)
|
||||
continue;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ProxyCreatorType getProxyCreatorType(MethodDefinition methodToCheck) {
|
||||
foreach (var info in DotNetUtils.getCalledMethods(module, methodToCheck)) {
|
||||
var calledMethod = info.Item2;
|
||||
if (!calledMethod.IsStatic || calledMethod.Body == null)
|
||||
continue;
|
||||
if (!MemberReferenceHelper.compareTypes(methodToCheck.DeclaringType, calledMethod.DeclaringType))
|
||||
continue;
|
||||
if (DotNetUtils.isMethod(calledMethod, "System.Void", "(System.Reflection.FieldInfo,System.Type,System.Reflection.MethodInfo)"))
|
||||
return ProxyCreatorType.CallOrCallvirt;
|
||||
if (DotNetUtils.isMethod(calledMethod, "System.Void", "(System.Reflection.FieldInfo,System.Type,System.Reflection.ConstructorInfo)"))
|
||||
return ProxyCreatorType.Newobj;
|
||||
}
|
||||
return ProxyCreatorType.None;
|
||||
}
|
||||
}
|
||||
}
|
75
de4dot.code/deobfuscators/Babel_NET/ResourceDecrypter.cs
Normal file
75
de4dot.code/deobfuscators/Babel_NET/ResourceDecrypter.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
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 Mono.Cecil;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class ResourceDecrypter {
|
||||
ModuleDefinition module;
|
||||
|
||||
public ResourceDecrypter(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public byte[] decrypt(byte[] encryptedData) {
|
||||
int index = 0;
|
||||
byte[] key, iv;
|
||||
bool isCompressed = getKeyIv(getHeaderData(encryptedData, ref index), out key, out iv);
|
||||
var data = DeobUtils.desDecrypt(encryptedData, index, encryptedData.Length - index, key, iv);
|
||||
if (isCompressed)
|
||||
data = DeobUtils.inflate(data, true);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
byte[] getHeaderData(byte[] encryptedData, ref int index) {
|
||||
bool xorDecrypt = encryptedData[index++] != 0;
|
||||
var headerData = new byte[BitConverter.ToUInt16(encryptedData, index)];
|
||||
Array.Copy(encryptedData, index + 2, headerData, 0, headerData.Length);
|
||||
index += headerData.Length + 2;
|
||||
if (!xorDecrypt)
|
||||
return headerData;
|
||||
|
||||
var key = new byte[8];
|
||||
Array.Copy(encryptedData, index, key, 0, key.Length);
|
||||
index += key.Length;
|
||||
for (int i = 0; i < headerData.Length; i++)
|
||||
headerData[i] ^= key[i % key.Length];
|
||||
return headerData;
|
||||
}
|
||||
|
||||
bool getKeyIv(byte[] headerData, out byte[] key, out byte[] iv) {
|
||||
var reader = new BinaryReader(new MemoryStream(headerData));
|
||||
var license = reader.ReadString();
|
||||
bool isCompressed = reader.ReadBoolean();
|
||||
iv = reader.ReadBytes(reader.ReadByte());
|
||||
bool hasEmbeddedKey = reader.ReadBoolean();
|
||||
if (hasEmbeddedKey)
|
||||
key = reader.ReadBytes(reader.ReadByte());
|
||||
else {
|
||||
key = new byte[reader.ReadByte()];
|
||||
Array.Copy(module.Assembly.Name.PublicKey, 12, key, 0, key.Length);
|
||||
key[5] |= 0x80;
|
||||
}
|
||||
return isCompressed;
|
||||
}
|
||||
}
|
||||
}
|
140
de4dot.code/deobfuscators/Babel_NET/ResourceResolver.cs
Normal file
140
de4dot.code/deobfuscators/Babel_NET/ResourceResolver.cs
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
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.IO;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class ResourceResolver {
|
||||
ModuleDefinition module;
|
||||
TypeDefinition resolverType;
|
||||
MethodDefinition registerMethod;
|
||||
EmbeddedResource encryptedResource;
|
||||
|
||||
public bool Detected {
|
||||
get { return resolverType != null; }
|
||||
}
|
||||
|
||||
public TypeDefinition Type {
|
||||
get { return resolverType; }
|
||||
}
|
||||
|
||||
public MethodDefinition InitMethod {
|
||||
get { return registerMethod; }
|
||||
}
|
||||
|
||||
public ResourceResolver(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
var requiredTypes = new string[] {
|
||||
"System.Reflection.Assembly",
|
||||
"System.Object",
|
||||
"System.Int32",
|
||||
"System.String[]",
|
||||
};
|
||||
foreach (var type in module.Types) {
|
||||
if (type.HasEvents)
|
||||
continue;
|
||||
if (!new FieldTypes(type).exactly(requiredTypes))
|
||||
continue;
|
||||
|
||||
MethodDefinition regMethod, handler;
|
||||
if (!findRegisterMethod(type, out regMethod, out handler))
|
||||
continue;
|
||||
|
||||
var resource = findEmbeddedResource(type);
|
||||
if (resource == null)
|
||||
continue;
|
||||
|
||||
resolverType = type;
|
||||
registerMethod = regMethod;
|
||||
encryptedResource = resource;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool findRegisterMethod(TypeDefinition type, out MethodDefinition regMethod, out MethodDefinition handler) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!method.IsStatic || method.Body == null)
|
||||
continue;
|
||||
if (method.Body.ExceptionHandlers.Count != 1)
|
||||
continue;
|
||||
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Ldftn)
|
||||
continue;
|
||||
var handlerRef = instr.Operand as MethodReference;
|
||||
if (handlerRef == null)
|
||||
continue;
|
||||
if (!DotNetUtils.isMethod(handlerRef, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)"))
|
||||
continue;
|
||||
if (!MemberReferenceHelper.compareTypes(type, handlerRef.DeclaringType))
|
||||
continue;
|
||||
handler = DotNetUtils.getMethod(type, handlerRef);
|
||||
if (handler == null)
|
||||
continue;
|
||||
if (handler.Body == null || handler.Body.ExceptionHandlers.Count != 1)
|
||||
continue;
|
||||
|
||||
regMethod = method;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
regMethod = null;
|
||||
handler = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
EmbeddedResource findEmbeddedResource(TypeDefinition type) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!DotNetUtils.isMethod(method, "System.String", "()"))
|
||||
continue;
|
||||
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
||||
var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
|
||||
if (resource != null)
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public EmbeddedResource mergeResources() {
|
||||
if (encryptedResource == null)
|
||||
return null;
|
||||
DeobUtils.decryptAndAddResources(module, encryptedResource.Name, () => decryptResourceAssembly());
|
||||
var result = encryptedResource;
|
||||
encryptedResource = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
byte[] decryptResourceAssembly() {
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData());
|
||||
var reader = new BinaryReader(new MemoryStream(decrypted));
|
||||
int numResources = reader.ReadInt32();
|
||||
for (int i = 0; i < numResources; i++)
|
||||
reader.ReadString();
|
||||
return reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
|
||||
}
|
||||
}
|
||||
}
|
122
de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs
Normal file
122
de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
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.IO;
|
||||
using Mono.Cecil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class StringDecrypter {
|
||||
ModuleDefinition module;
|
||||
Dictionary<int, string> offsetToString = new Dictionary<int, string>();
|
||||
TypeDefinition decrypterType;
|
||||
MethodDefinition decryptMethod;
|
||||
EmbeddedResource encryptedResource;
|
||||
|
||||
public bool Detected {
|
||||
get { return decrypterType != null; }
|
||||
}
|
||||
|
||||
public TypeDefinition Type {
|
||||
get { return decrypterType; }
|
||||
}
|
||||
|
||||
public MethodDefinition DecryptMethod {
|
||||
get { return decryptMethod; }
|
||||
}
|
||||
|
||||
public EmbeddedResource Resource {
|
||||
get { return encryptedResource; }
|
||||
}
|
||||
|
||||
public StringDecrypter(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
foreach (var type in module.Types) {
|
||||
if (!isDecrypterType(type))
|
||||
continue;
|
||||
|
||||
var method = DotNetUtils.getMethod(type, "System.String", "(System.Int32)");
|
||||
if (method == null)
|
||||
continue;
|
||||
|
||||
decrypterType = type;
|
||||
decryptMethod = method;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool isDecrypterType(TypeDefinition type) {
|
||||
if (type.HasEvents)
|
||||
return false;
|
||||
if (type.NestedTypes.Count != 1)
|
||||
return false;
|
||||
if (type.Fields.Count != 0)
|
||||
return false;
|
||||
|
||||
var nested = type.NestedTypes[0];
|
||||
if (nested.HasProperties || nested.HasEvents)
|
||||
return false;
|
||||
if (nested.Fields.Count != 1)
|
||||
return false;
|
||||
if (!MemberReferenceHelper.compareTypes(nested.Fields[0].FieldType, nested))
|
||||
return false;
|
||||
|
||||
if (DotNetUtils.getMethod(nested, "System.Reflection.Emit.MethodBuilder", "(System.Reflection.Emit.TypeBuilder)") == null)
|
||||
return false;
|
||||
if (DotNetUtils.getMethod(nested, "System.String", "(System.Int32)") == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
if (decrypterType == null)
|
||||
return;
|
||||
if (encryptedResource != null)
|
||||
return;
|
||||
encryptedResource = findResource();
|
||||
if (encryptedResource == null)
|
||||
return;
|
||||
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData());
|
||||
var reader = new BinaryReader(new MemoryStream(decrypted));
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
offsetToString[(int)reader.BaseStream.Position] = reader.ReadString();
|
||||
}
|
||||
|
||||
EmbeddedResource findResource() {
|
||||
foreach (var method in decrypterType.Methods) {
|
||||
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
||||
var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
|
||||
if (resource != null)
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public string decrypt(int offset) {
|
||||
return offsetToString[offset];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,6 +30,7 @@ namespace de4dot.cui {
|
|||
static IList<IDeobfuscatorInfo> createDeobfuscatorInfos() {
|
||||
return new List<IDeobfuscatorInfo> {
|
||||
new de4dot.code.deobfuscators.Unknown.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Babel_NET.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CliSecure.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.CryptoObfuscator.DeobfuscatorInfo(),
|
||||
new de4dot.code.deobfuscators.Dotfuscator.DeobfuscatorInfo(),
|
||||
|
|
Loading…
Reference in New Issue
Block a user