Add better BL support
This commit is contained in:
parent
4a29eae1c8
commit
fa594c6213
|
@ -57,10 +57,13 @@
|
|||
<Compile Include="DeobfuscatorContext.cs" />
|
||||
<Compile Include="deobfuscators\ArrayFinder.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\AssemblyResolver.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\BabelInflater.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\BabelMethodCallInliner.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\BabelUtils.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\InflaterCreator.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MemberReferenceConverter.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MethodBodyReader.cs" />
|
||||
<Compile Include="deobfuscators\Babel_NET\MethodReferenceReader.cs" />
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Mono.Cecil;
|
||||
using de4dot.blocks;
|
||||
|
@ -24,6 +25,7 @@ using de4dot.blocks;
|
|||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class AssemblyResolver {
|
||||
ModuleDefinition module;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
TypeDefinition resolverType;
|
||||
MethodDefinition registerMethod;
|
||||
EmbeddedResource encryptedResource;
|
||||
|
@ -61,8 +63,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
get { return embeddedAssemblyInfos; }
|
||||
}
|
||||
|
||||
public AssemblyResolver(ModuleDefinition module) {
|
||||
public AssemblyResolver(ModuleDefinition module, ResourceDecrypter resourceDecrypter) {
|
||||
this.module = module;
|
||||
this.resourceDecrypter = resourceDecrypter;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
|
@ -81,12 +84,26 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
if (!BabelUtils.findRegisterMethod(type, out regMethod, out handler))
|
||||
continue;
|
||||
|
||||
var decryptMethod = findDecryptMethod(type);
|
||||
if (decryptMethod == null)
|
||||
throw new ApplicationException("Couldn't find resource type decrypt method");
|
||||
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(decryptMethod);
|
||||
|
||||
resolverType = type;
|
||||
registerMethod = regMethod;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static MethodDefinition findDecryptMethod(TypeDefinition type) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!DotNetUtils.isMethod(method, "System.Void", "(System.IO.Stream)"))
|
||||
continue;
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void initialize(ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob) {
|
||||
if (resolverType == null)
|
||||
return;
|
||||
|
@ -97,7 +114,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
return;
|
||||
}
|
||||
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData());
|
||||
var decrypted = resourceDecrypter.decrypt(encryptedResource.GetResourceData());
|
||||
var reader = new BinaryReader(new MemoryStream(decrypted));
|
||||
int numAssemblies = reader.ReadInt32();
|
||||
embeddedAssemblyInfos = new EmbeddedAssemblyInfo[numAssemblies];
|
||||
|
|
63
de4dot.code/deobfuscators/Babel_NET/BabelInflater.cs
Normal file
63
de4dot.code/deobfuscators/Babel_NET/BabelInflater.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
Copyright (C) 2011-2012 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using ICSharpCode.SharpZipLib;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class BabelInflater : Inflater {
|
||||
int magic;
|
||||
|
||||
public BabelInflater(bool noHeader, int magic)
|
||||
: base(noHeader) {
|
||||
this.magic = magic;
|
||||
}
|
||||
|
||||
protected override bool ReadHeader(ref bool isLastBlock, out int blockType) {
|
||||
const int numBits = 4;
|
||||
|
||||
int type = input.PeekBits(numBits);
|
||||
if (type < 0) {
|
||||
blockType = -1;
|
||||
return false;
|
||||
}
|
||||
input.DropBits(numBits);
|
||||
|
||||
if ((type & 1) != 0)
|
||||
isLastBlock = true;
|
||||
switch (type >> 1) {
|
||||
case 1: blockType = STORED_BLOCK; break;
|
||||
case 5: blockType = STATIC_TREES; break;
|
||||
case 6: blockType = DYN_TREES; break;
|
||||
default: throw new SharpZipBaseException("Unknown block type: " + type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool DecodeStoredLength() {
|
||||
if ((uncomprLen = input.PeekBits(16)) < 0)
|
||||
return false;
|
||||
input.DropBits(16);
|
||||
|
||||
uncomprLen ^= magic;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
244
de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs
Normal file
244
de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
Copyright (C) 2011-2012 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
using de4dot.blocks.cflow;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class BabelMethodCallInliner : MethodCallInlinerBase, IBranchHandler {
|
||||
InstructionEmulator emulator;
|
||||
BranchEmulator branchEmulator;
|
||||
int emulateIndex;
|
||||
IList<Instruction> instructions;
|
||||
|
||||
public BabelMethodCallInliner() {
|
||||
emulator = new InstructionEmulator();
|
||||
branchEmulator = new BranchEmulator(emulator, this);
|
||||
}
|
||||
|
||||
public static List<MethodDefinition> find(ModuleDefinition module, IEnumerable<MethodDefinition> notInlinedMethods) {
|
||||
var notInlinedMethodsDict = new Dictionary<MethodDefinition, bool>();
|
||||
foreach (var method in notInlinedMethods)
|
||||
notInlinedMethodsDict[method] = true;
|
||||
|
||||
var inlinedMethods = new List<MethodDefinition>();
|
||||
|
||||
foreach (var type in module.GetTypes()) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!notInlinedMethodsDict.ContainsKey(method) && canInline(method))
|
||||
inlinedMethods.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
return inlinedMethods;
|
||||
}
|
||||
|
||||
void IBranchHandler.handleNormal(int stackArgs, bool isTaken) {
|
||||
if (!isTaken)
|
||||
emulateIndex++;
|
||||
else
|
||||
emulateIndex = instructions.IndexOf((Instruction)instructions[emulateIndex].Operand);
|
||||
}
|
||||
|
||||
bool IBranchHandler.handleSwitch(Int32Value switchIndex) {
|
||||
if (!switchIndex.allBitsValid())
|
||||
return false;
|
||||
var instr = instructions[emulateIndex];
|
||||
var targets = (Instruction[])instr.Operand;
|
||||
if (switchIndex.value >= 0 && switchIndex.value < targets.Length)
|
||||
emulateIndex = instructions.IndexOf(targets[switchIndex.value]);
|
||||
else
|
||||
emulateIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool deobfuscateInternal() {
|
||||
bool changed = false;
|
||||
var instructions = block.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var instr = instructions[i].Instruction;
|
||||
if (instr.OpCode.Code == Code.Call)
|
||||
changed |= inlineMethod(instr, i);
|
||||
}
|
||||
instructions = null;
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool canInline(MethodDefinition method) {
|
||||
if (!DotNetUtils.isMethod(method, "System.Int32", "(System.Int32)"))
|
||||
return false;
|
||||
if (!method.IsAssembly)
|
||||
return false;
|
||||
if (method.GenericParameters.Count > 0)
|
||||
return false;
|
||||
|
||||
return method.IsStatic;
|
||||
}
|
||||
|
||||
bool canInline2(MethodDefinition method) {
|
||||
return canInline(method) && method != blocks.Method;
|
||||
}
|
||||
|
||||
bool inlineMethod(Instruction callInstr, int instrIndex) {
|
||||
var methodToInline = callInstr.Operand as MethodDefinition;
|
||||
if (methodToInline == null)
|
||||
return false;
|
||||
|
||||
if (!canInline2(methodToInline))
|
||||
return false;
|
||||
var body = methodToInline.Body;
|
||||
if (body == null)
|
||||
return false;
|
||||
|
||||
if (instrIndex == 0)
|
||||
return false;
|
||||
|
||||
var ldci4 = block.Instructions[instrIndex - 1];
|
||||
if (!ldci4.isLdcI4())
|
||||
return false;
|
||||
int newValue;
|
||||
if (!getNewValue(methodToInline, ldci4.getLdcI4Value(), out newValue))
|
||||
return false;
|
||||
|
||||
block.Instructions[instrIndex - 1] = new Instr(Instruction.Create(OpCodes.Nop));
|
||||
block.Instructions[instrIndex] = new Instr(DotNetUtils.createLdci4(newValue));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool getNewValue(MethodDefinition method, int arg, out int newValue) {
|
||||
newValue = 0;
|
||||
emulator.init(method);
|
||||
emulator.setArg(method.Parameters[0], new Int32Value(arg));
|
||||
|
||||
Instruction instr;
|
||||
emulateIndex = 0;
|
||||
instructions = method.Body.Instructions;
|
||||
int counter = 0;
|
||||
while (true) {
|
||||
if (counter++ >= 50)
|
||||
return false;
|
||||
if (emulateIndex < 0 || emulateIndex >= instructions.Count)
|
||||
return false;
|
||||
instr = instructions[emulateIndex];
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldarg:
|
||||
case Code.Ldarg_S:
|
||||
case Code.Ldarg_0:
|
||||
case Code.Ldarg_1:
|
||||
case Code.Ldarg_2:
|
||||
case Code.Ldarg_3:
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
case Code.Ldc_I4:
|
||||
case Code.Ldc_I4_0:
|
||||
case Code.Ldc_I4_1:
|
||||
case Code.Ldc_I4_2:
|
||||
case Code.Ldc_I4_3:
|
||||
case Code.Ldc_I4_4:
|
||||
case Code.Ldc_I4_5:
|
||||
case Code.Ldc_I4_6:
|
||||
case Code.Ldc_I4_7:
|
||||
case Code.Ldc_I4_8:
|
||||
case Code.Ldc_I4_M1:
|
||||
case Code.Ldc_I4_S:
|
||||
case Code.Add:
|
||||
case Code.Sub:
|
||||
case Code.Xor:
|
||||
case Code.Or:
|
||||
case Code.Nop:
|
||||
case Code.Dup:
|
||||
case Code.Mul:
|
||||
case Code.Rem:
|
||||
case Code.Div:
|
||||
emulator.emulate(instr);
|
||||
emulateIndex++;
|
||||
break;
|
||||
|
||||
case Code.Br:
|
||||
case Code.Br_S:
|
||||
case Code.Beq:
|
||||
case Code.Beq_S:
|
||||
case Code.Bge:
|
||||
case Code.Bge_S:
|
||||
case Code.Bge_Un:
|
||||
case Code.Bge_Un_S:
|
||||
case Code.Bgt:
|
||||
case Code.Bgt_S:
|
||||
case Code.Bgt_Un:
|
||||
case Code.Bgt_Un_S:
|
||||
case Code.Ble:
|
||||
case Code.Ble_S:
|
||||
case Code.Ble_Un:
|
||||
case Code.Ble_Un_S:
|
||||
case Code.Blt:
|
||||
case Code.Blt_S:
|
||||
case Code.Blt_Un:
|
||||
case Code.Blt_Un_S:
|
||||
case Code.Bne_Un:
|
||||
case Code.Bne_Un_S:
|
||||
case Code.Brfalse:
|
||||
case Code.Brfalse_S:
|
||||
case Code.Brtrue:
|
||||
case Code.Brtrue_S:
|
||||
case Code.Switch:
|
||||
if (!branchEmulator.emulate(instr))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Code.Ret:
|
||||
var retValue = emulator.pop();
|
||||
if (!retValue.isInt32())
|
||||
return false;
|
||||
var retValue2 = (Int32Value)retValue;
|
||||
if (!retValue2.allBitsValid())
|
||||
return false;
|
||||
newValue = retValue2.value;
|
||||
return true;
|
||||
|
||||
default:
|
||||
if (instr.OpCode.OpCodeType != OpCodeType.Prefix)
|
||||
return false;
|
||||
emulateIndex++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool isCompatibleType(int paramIndex, TypeReference origType, TypeReference newType) {
|
||||
if (MemberReferenceHelper.compareTypes(origType, newType))
|
||||
return true;
|
||||
if (newType.IsValueType || origType.IsValueType)
|
||||
return false;
|
||||
return newType.FullName == "System.Object";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
@ -41,15 +43,60 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
if (!method.IsStatic)
|
||||
continue;
|
||||
fixMethod(method);
|
||||
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
||||
var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
|
||||
if (resource != null)
|
||||
return resource;
|
||||
}
|
||||
var resource = findEmbeddedResource1(module, method) ?? findEmbeddedResource2(module, method);
|
||||
if (resource != null)
|
||||
return resource;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static EmbeddedResource findEmbeddedResource1(ModuleDefinition module, MethodDefinition method) {
|
||||
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
||||
var resource = DotNetUtils.getResource(module, s) as EmbeddedResource;
|
||||
if (resource != null)
|
||||
return resource;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static EmbeddedResource findEmbeddedResource2(ModuleDefinition module, MethodDefinition method) {
|
||||
var strings = new List<string>(DotNetUtils.getCodeStrings(method));
|
||||
if (strings.Count != 1)
|
||||
return null;
|
||||
var encryptedString = strings[0];
|
||||
|
||||
int xorKey;
|
||||
if (!getXorKey2(method, out xorKey))
|
||||
return null;
|
||||
|
||||
var sb = new StringBuilder(encryptedString.Length);
|
||||
foreach (var c in encryptedString)
|
||||
sb.Append((char)(c ^ xorKey));
|
||||
return DotNetUtils.getResource(module, sb.ToString()) as EmbeddedResource;
|
||||
}
|
||||
|
||||
static bool getXorKey2(MethodDefinition method, out int xorKey) {
|
||||
var instrs = method.Body.Instructions;
|
||||
for (int i = 0; i < instrs.Count - 2; i++) {
|
||||
var ldelem = instrs[i];
|
||||
if (ldelem.OpCode.Code != Code.Ldelem_U2)
|
||||
continue;
|
||||
|
||||
var ldci4 = instrs[i + 1];
|
||||
if (!DotNetUtils.isLdcI4(ldci4))
|
||||
continue;
|
||||
|
||||
if (instrs[i + 2].OpCode.Code != Code.Xor)
|
||||
continue;
|
||||
|
||||
xorKey = DotNetUtils.getLdcI4Value(ldci4);
|
||||
return true;
|
||||
}
|
||||
|
||||
xorKey = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool findRegisterMethod(TypeDefinition type, out MethodDefinition regMethod, out MethodDefinition handler) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!method.IsStatic || method.Body == null)
|
||||
|
|
|
@ -28,6 +28,7 @@ using de4dot.blocks;
|
|||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class ConstantsDecrypter {
|
||||
ModuleDefinition module;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
InitializedDataCreator initializedDataCreator;
|
||||
TypeDefinition decrypterType;
|
||||
MethodDefinition int32Decrypter;
|
||||
|
@ -77,8 +78,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
get { return arrayDecrypter; }
|
||||
}
|
||||
|
||||
public ConstantsDecrypter(ModuleDefinition module, InitializedDataCreator initializedDataCreator) {
|
||||
public ConstantsDecrypter(ModuleDefinition module, ResourceDecrypter resourceDecrypter, InitializedDataCreator initializedDataCreator) {
|
||||
this.module = module;
|
||||
this.resourceDecrypter = resourceDecrypter;
|
||||
this.initializedDataCreator = initializedDataCreator;
|
||||
}
|
||||
|
||||
|
@ -107,6 +109,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
if (!checkNestedFields(nested))
|
||||
return false;
|
||||
|
||||
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(DotNetUtils.getMethod(nested, ".ctor"));
|
||||
|
||||
if (DotNetUtils.getMethod(type, "System.Int32", "(System.Int32)") == null)
|
||||
return false;
|
||||
if (DotNetUtils.getMethod(type, "System.Int64", "(System.Int32)") == null)
|
||||
|
@ -147,7 +151,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
return;
|
||||
}
|
||||
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData());
|
||||
var decrypted = resourceDecrypter.decrypt(encryptedResource.GetResourceData());
|
||||
var reader = new BinaryReader(new MemoryStream(decrypted));
|
||||
int count;
|
||||
|
||||
|
@ -270,7 +274,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
}
|
||||
|
||||
byte[] decryptArray(byte[] encryptedData, int elemSize) {
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedData);
|
||||
var decrypted = resourceDecrypter.decrypt(encryptedData);
|
||||
var ary = (Array)new BinaryFormatter().Deserialize(new MemoryStream(decrypted));
|
||||
if (ary is byte[])
|
||||
return (byte[])ary;
|
||||
|
|
|
@ -21,11 +21,14 @@ using System.Collections.Generic;
|
|||
using System.Text.RegularExpressions;
|
||||
using Mono.Cecil;
|
||||
using de4dot.blocks;
|
||||
using de4dot.blocks.cflow;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
public class DeobfuscatorInfo : DeobfuscatorInfoBase {
|
||||
public const string THE_NAME = "Babel .NET";
|
||||
public const string THE_TYPE = "bl";
|
||||
BoolOption inlineMethods;
|
||||
BoolOption removeInlinedMethods;
|
||||
BoolOption decryptMethods;
|
||||
BoolOption decryptResources;
|
||||
BoolOption decryptConstants;
|
||||
|
@ -33,6 +36,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
|
||||
public DeobfuscatorInfo()
|
||||
: base() {
|
||||
inlineMethods = new BoolOption(null, makeArgName("inline"), "Inline short methods", true);
|
||||
removeInlinedMethods = new BoolOption(null, makeArgName("remove-inlined"), "Remove inlined methods", true);
|
||||
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);
|
||||
|
@ -50,6 +55,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
public override IDeobfuscator createDeobfuscator() {
|
||||
return new Deobfuscator(new Deobfuscator.Options {
|
||||
ValidNameRegex = validNameRegex.get(),
|
||||
InlineMethods = inlineMethods.get(),
|
||||
RemoveInlinedMethods = removeInlinedMethods.get(),
|
||||
DecryptMethods = decryptMethods.get(),
|
||||
DecryptResources = decryptResources.get(),
|
||||
DecryptConstants = decryptConstants.get(),
|
||||
|
@ -59,6 +66,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
|
||||
protected override IEnumerable<Option> getOptionsInternal() {
|
||||
return new List<Option>() {
|
||||
inlineMethods,
|
||||
removeInlinedMethods,
|
||||
decryptMethods,
|
||||
decryptResources,
|
||||
decryptConstants,
|
||||
|
@ -71,6 +80,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
Options options;
|
||||
bool foundBabelAttribute = false;
|
||||
string obfuscatorName = DeobfuscatorInfo.THE_NAME;
|
||||
bool startedDeobfuscating = false;
|
||||
|
||||
ResourceResolver resourceResolver;
|
||||
AssemblyResolver assemblyResolver;
|
||||
|
@ -84,6 +94,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
MethodsDecrypter methodsDecrypter;
|
||||
|
||||
internal class Options : OptionsBase {
|
||||
public bool InlineMethods { get; set; }
|
||||
public bool RemoveInlinedMethods { get; set; }
|
||||
public bool DecryptMethods { get; set; }
|
||||
public bool DecryptResources { get; set; }
|
||||
public bool DecryptConstants { get; set; }
|
||||
|
@ -102,6 +114,19 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
get { return obfuscatorName; }
|
||||
}
|
||||
|
||||
protected override bool CanInlineMethods {
|
||||
get { return startedDeobfuscating ? options.InlineMethods : true; }
|
||||
}
|
||||
|
||||
public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
|
||||
get {
|
||||
var list = new List<IBlocksDeobfuscator>();
|
||||
if (CanInlineMethods)
|
||||
list.Add(new BabelMethodCallInliner());
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public Deobfuscator(Options options)
|
||||
: base(options) {
|
||||
this.options = options;
|
||||
|
@ -131,17 +156,18 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
|
||||
protected override void scanForObfuscator() {
|
||||
findBabelAttribute();
|
||||
resourceResolver = new ResourceResolver(module);
|
||||
var resourceDecrypterCreator = new ResourceDecrypterCreator(module, DeobfuscatedFile);
|
||||
resourceResolver = new ResourceResolver(module, resourceDecrypterCreator.create(), DeobfuscatedFile);
|
||||
resourceResolver.find();
|
||||
assemblyResolver = new AssemblyResolver(module);
|
||||
assemblyResolver = new AssemblyResolver(module, resourceDecrypterCreator.create());
|
||||
assemblyResolver.find();
|
||||
stringDecrypter = new StringDecrypter(module);
|
||||
stringDecrypter = new StringDecrypter(module, resourceDecrypterCreator.create());
|
||||
stringDecrypter.find(DeobfuscatedFile);
|
||||
constantsDecrypter = new ConstantsDecrypter(module, initializedDataCreator);
|
||||
constantsDecrypter = new ConstantsDecrypter(module, resourceDecrypterCreator.create(), initializedDataCreator);
|
||||
constantsDecrypter.find();
|
||||
proxyCallFixer = new ProxyCallFixer(module);
|
||||
proxyCallFixer.findDelegateCreator();
|
||||
methodsDecrypter = new MethodsDecrypter(module, DeobfuscatedFile.DeobfuscatorContext);
|
||||
methodsDecrypter = new MethodsDecrypter(module, resourceDecrypterCreator.create(), DeobfuscatedFile.DeobfuscatorContext);
|
||||
methodsDecrypter.find();
|
||||
}
|
||||
|
||||
|
@ -216,6 +242,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
}
|
||||
|
||||
proxyCallFixer.find();
|
||||
startedDeobfuscating = true;
|
||||
}
|
||||
|
||||
void dumpEmbeddedAssemblies() {
|
||||
|
@ -250,6 +277,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
}
|
||||
|
||||
public override void deobfuscateEnd() {
|
||||
removeInlinedMethods();
|
||||
if (CanRemoveStringDecrypterType) {
|
||||
addResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings");
|
||||
addTypeToBeRemoved(stringDecrypter.Type, "String decrypter type");
|
||||
|
@ -259,6 +287,12 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
base.deobfuscateEnd();
|
||||
}
|
||||
|
||||
void removeInlinedMethods() {
|
||||
if (!options.InlineMethods || !options.RemoveInlinedMethods)
|
||||
return;
|
||||
removeInlinedMethods(BabelMethodCallInliner.find(module, staticStringInliner.Methods));
|
||||
}
|
||||
|
||||
public override IEnumerable<int> getStringDecrypterMethods() {
|
||||
var list = new List<int>();
|
||||
if (stringDecrypter.DecryptMethod != null)
|
||||
|
|
117
de4dot.code/deobfuscators/Babel_NET/InflaterCreator.cs
Normal file
117
de4dot.code/deobfuscators/Babel_NET/InflaterCreator.cs
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright (C) 2011-2012 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class InflaterCreator {
|
||||
public static Inflater create(MethodDefinition method, bool noHeader) {
|
||||
return create(findInflaterType(method), noHeader);
|
||||
}
|
||||
|
||||
public static Inflater create(TypeDefinition inflaterType, bool noHeader) {
|
||||
if (inflaterType == null)
|
||||
return createNormal(noHeader);
|
||||
var initHeaderMethod = findInitHeaderMethod(inflaterType);
|
||||
if (initHeaderMethod == null)
|
||||
return createNormal(noHeader, "Could not find inflater init header method");
|
||||
var magic = getMagic(initHeaderMethod);
|
||||
if (!magic.HasValue)
|
||||
return createNormal(noHeader);
|
||||
return new BabelInflater(noHeader, magic.Value);
|
||||
}
|
||||
|
||||
static Inflater createNormal(bool noHeader, string errorMessage = null) {
|
||||
if (errorMessage != null)
|
||||
Log.w("{0}", errorMessage);
|
||||
return new Inflater(noHeader);
|
||||
}
|
||||
|
||||
static TypeDefinition findInflaterType(MethodDefinition method) {
|
||||
if (method == null || method.Body == null)
|
||||
return null;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Call)
|
||||
continue;
|
||||
var calledMethod = instr.Operand as MethodDefinition;
|
||||
if (calledMethod == null || !calledMethod.IsStatic)
|
||||
continue;
|
||||
|
||||
var type = calledMethod.DeclaringType;
|
||||
foreach (var nested in type.NestedTypes) {
|
||||
if (DeobUtils.hasInteger(DotNetUtils.getMethod(nested, ".ctor"), 0x8001))
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static MethodDefinition findInitHeaderMethod(TypeDefinition inflaterType) {
|
||||
foreach (var nested in inflaterType.NestedTypes) {
|
||||
var method = findInitHeaderMethod2(nested);
|
||||
if (method != null)
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static MethodDefinition findInitHeaderMethod2(TypeDefinition nested) {
|
||||
foreach (var method in nested.Methods) {
|
||||
if (method.IsStatic || method.Body == null)
|
||||
continue;
|
||||
if (!DotNetUtils.isMethod(method, "System.Boolean", "()"))
|
||||
continue;
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static int? getMagic(MethodDefinition method) {
|
||||
if (method == null || method.Body == null)
|
||||
return null;
|
||||
var instrs = method.Body.Instructions;
|
||||
for (int i = 0; i < instrs.Count - 3; i++) {
|
||||
var ldci4_1 = instrs[i];
|
||||
if (!DotNetUtils.isLdcI4(ldci4_1) || DotNetUtils.getLdcI4Value(ldci4_1) != 16)
|
||||
continue;
|
||||
|
||||
var callvirt = instrs[i + 1];
|
||||
if (callvirt.OpCode.Code != Code.Callvirt)
|
||||
continue;
|
||||
|
||||
var ldci4_2 = instrs[i + 2];
|
||||
if (!DotNetUtils.isLdcI4(ldci4_2))
|
||||
continue;
|
||||
|
||||
if (instrs[i + 3].OpCode.Code != Code.Xor)
|
||||
continue;
|
||||
|
||||
return DotNetUtils.getLdcI4Value(ldci4_2);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ using de4dot.blocks;
|
|||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class MethodsDecrypter {
|
||||
ModuleDefinition module;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
IDeobfuscatorContext deobfuscatorContext;
|
||||
Dictionary<string, ImageReader> imageReaders = new Dictionary<string, ImageReader>(StringComparer.Ordinal);
|
||||
TypeDefinition methodsDecrypterCreator;
|
||||
|
@ -38,8 +39,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
get { return methodsDecrypterCreator != null; }
|
||||
}
|
||||
|
||||
public MethodsDecrypter(ModuleDefinition module, IDeobfuscatorContext deobfuscatorContext) {
|
||||
public MethodsDecrypter(ModuleDefinition module, ResourceDecrypter resourceDecrypter, IDeobfuscatorContext deobfuscatorContext) {
|
||||
this.module = module;
|
||||
this.resourceDecrypter = resourceDecrypter;
|
||||
this.deobfuscatorContext = deobfuscatorContext;
|
||||
}
|
||||
|
||||
|
@ -62,6 +64,8 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
if (decrypterType == null)
|
||||
continue;
|
||||
|
||||
resourceDecrypter.DecryptMethod = findDecryptMethod(decrypterType);
|
||||
|
||||
methodsDecrypterCreator = type;
|
||||
methodsDecrypter = decrypterType;
|
||||
decryptExecuteMethod = executeMethod;
|
||||
|
@ -69,6 +73,15 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
}
|
||||
}
|
||||
|
||||
static MethodDefinition findDecryptMethod(TypeDefinition type) {
|
||||
foreach (var method in type.Methods) {
|
||||
var decryptMethod = ResourceDecrypter.findDecrypterMethod(method);
|
||||
if (decryptMethod != null)
|
||||
return decryptMethod;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
TypeDefinition findMethodsDecrypterType(TypeDefinition type) {
|
||||
foreach (var field in type.Fields) {
|
||||
var fieldType = DotNetUtils.getType(module, field.FieldType);
|
||||
|
@ -92,29 +105,35 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
return;
|
||||
|
||||
encryptedResource = BabelUtils.findEmbeddedResource(module, methodsDecrypter, simpleDeobfuscator, deob);
|
||||
if (encryptedResource == null) {
|
||||
Log.w("Could not find encrypted methods resource");
|
||||
return;
|
||||
}
|
||||
|
||||
addImageReader("", new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData()));
|
||||
if (encryptedResource != null)
|
||||
addImageReader("", resourceDecrypter.decrypt(encryptedResource.GetResourceData()));
|
||||
}
|
||||
|
||||
void addImageReader(string name, byte[] data) {
|
||||
ImageReader addImageReader(string name, byte[] data) {
|
||||
var imageReader = new ImageReader(deobfuscatorContext, module, data);
|
||||
if (!imageReader.initialize()) {
|
||||
Log.w("Could not read encrypted methods");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
if (imageReaders.ContainsKey(name))
|
||||
throw new ApplicationException(string.Format("ImageReader for name '{0}' already exists", name));
|
||||
imageReaders[name] = imageReader;
|
||||
return imageReader;
|
||||
}
|
||||
|
||||
class EncryptInfo {
|
||||
public string encryptedMethodName;
|
||||
public string feature;
|
||||
public MethodDefinition method;
|
||||
|
||||
public string FullName {
|
||||
get {
|
||||
if (string.IsNullOrEmpty(feature))
|
||||
return encryptedMethodName;
|
||||
return string.Format("{0}:{1}", feature, encryptedMethodName);
|
||||
}
|
||||
}
|
||||
|
||||
public EncryptInfo(string encryptedMethodName, string feature, MethodDefinition method) {
|
||||
this.encryptedMethodName = encryptedMethodName;
|
||||
this.feature = feature;
|
||||
|
@ -134,18 +153,53 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
int totalEncryptedMethods = 0;
|
||||
foreach (var info in getEncryptedMethods()) {
|
||||
totalEncryptedMethods++;
|
||||
ImageReader imageReader;
|
||||
if (!imageReaders.TryGetValue(info.feature, out imageReader)) {
|
||||
var imageReader = getImageReader(info.feature);
|
||||
if (imageReader == null) {
|
||||
numNonDecryptedMethods++;
|
||||
continue;
|
||||
}
|
||||
Log.v("Decrypting method {0:X8}", info.method.MetadataToken.ToInt32());
|
||||
imageReader.restore(info.encryptedMethodName, info.method);
|
||||
imageReader.restore(info.FullName, info.method);
|
||||
}
|
||||
if (numNonDecryptedMethods > 0)
|
||||
Log.w("{0}/{1} methods not decrypted", numNonDecryptedMethods, totalEncryptedMethods);
|
||||
}
|
||||
|
||||
ImageReader getImageReader(string feature) {
|
||||
ImageReader imageReader;
|
||||
if (imageReaders.TryGetValue(feature, out imageReader))
|
||||
return imageReader;
|
||||
|
||||
return createImageReader(feature);
|
||||
}
|
||||
|
||||
ImageReader createImageReader(string feature) {
|
||||
if (string.IsNullOrEmpty(feature))
|
||||
return null;
|
||||
|
||||
try {
|
||||
var encrypted = File.ReadAllBytes(getFile(Path.GetDirectoryName(module.FullyQualifiedName), feature));
|
||||
var decrypted = resourceDecrypter.decrypt(encrypted);
|
||||
return addImageReader(feature, decrypted);
|
||||
}
|
||||
catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static string getFile(string dir, string name) {
|
||||
try {
|
||||
var di = new DirectoryInfo(dir);
|
||||
foreach (var file in di.GetFiles()) {
|
||||
if (Utils.StartsWith(file.Name, name, StringComparison.OrdinalIgnoreCase))
|
||||
return file.FullName;
|
||||
}
|
||||
}
|
||||
catch {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
List<EncryptInfo> getEncryptedMethods() {
|
||||
var infos = new List<EncryptInfo>();
|
||||
|
||||
|
|
|
@ -19,104 +19,243 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using ICSharpCode.SharpZipLib.Zip.Compression;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class ResourceDecrypterCreator {
|
||||
ModuleDefinition module;
|
||||
ISimpleDeobfuscator simpleDeobfuscator;
|
||||
|
||||
public ResourceDecrypterCreator(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator) {
|
||||
this.module = module;
|
||||
this.simpleDeobfuscator = simpleDeobfuscator;
|
||||
}
|
||||
|
||||
public ResourceDecrypter create() {
|
||||
return new ResourceDecrypter(module, simpleDeobfuscator);
|
||||
}
|
||||
}
|
||||
|
||||
class ResourceDecrypter {
|
||||
ModuleDefinition module;
|
||||
ISimpleDeobfuscator simpleDeobfuscator;
|
||||
MethodDefinition decryptMethod;
|
||||
IDecrypter decrypter;
|
||||
|
||||
public ResourceDecrypter(ModuleDefinition module) {
|
||||
public ResourceDecrypter(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator) {
|
||||
this.module = module;
|
||||
this.simpleDeobfuscator = simpleDeobfuscator;
|
||||
}
|
||||
|
||||
interface IDecrypter {
|
||||
byte[] decrypt(byte[] encryptedData);
|
||||
}
|
||||
|
||||
// v3.0
|
||||
class Decrypter1 : IDecrypter {
|
||||
ModuleDefinition module;
|
||||
|
||||
public Decrypter1(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public byte[] decrypt(byte[] encryptedData) {
|
||||
byte[] key, iv;
|
||||
var reader = new BinaryReader(new MemoryStream(encryptedData));
|
||||
bool isCompressed = getHeaderData(reader, out key, out iv);
|
||||
var data = DeobUtils.desDecrypt(encryptedData,
|
||||
(int)reader.BaseStream.Position,
|
||||
(int)(reader.BaseStream.Length - reader.BaseStream.Position),
|
||||
key, iv);
|
||||
if (isCompressed)
|
||||
data = DeobUtils.inflate(data, true);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool getHeaderData(BinaryReader reader, out byte[] key, out byte[] iv) {
|
||||
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, 0, key, 0, key.Length);
|
||||
}
|
||||
|
||||
reader.ReadBytes(reader.ReadInt32()); // hash
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// v3.5+
|
||||
class Decrypter2 : IDecrypter {
|
||||
ModuleDefinition module;
|
||||
|
||||
public Decrypter2(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));
|
||||
|
||||
// 3.0 - 3.5 don't have this field
|
||||
if (headerData[(int)reader.BaseStream.Position] != 8) {
|
||||
var license = reader.ReadString();
|
||||
}
|
||||
|
||||
// 4.2 (and earlier?) always compress the data
|
||||
bool isCompressed = true;
|
||||
if (headerData[(int)reader.BaseStream.Position] != 8)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// v5.0+ retail
|
||||
class Decrypter3 : IDecrypter {
|
||||
ModuleDefinition module;
|
||||
Inflater inflater;
|
||||
|
||||
public Decrypter3(ModuleDefinition module, MethodDefinition decryptMethod) {
|
||||
this.module = module;
|
||||
this.inflater = InflaterCreator.create(decryptMethod, true);
|
||||
}
|
||||
|
||||
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, inflater);
|
||||
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[6];
|
||||
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();
|
||||
|
||||
var unkData = reader.ReadBytes(reader.ReadInt32());
|
||||
|
||||
bool hasEmbeddedKey = reader.ReadBoolean();
|
||||
|
||||
iv = reader.ReadBytes(reader.ReadByte());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
public MethodDefinition DecryptMethod {
|
||||
set {
|
||||
if (value == null)
|
||||
return;
|
||||
if (decryptMethod == null) {
|
||||
decryptMethod = value;
|
||||
simpleDeobfuscator.deobfuscate(decryptMethod);
|
||||
}
|
||||
else if (decryptMethod != value)
|
||||
throw new ApplicationException("Found another decrypter method");
|
||||
}
|
||||
}
|
||||
|
||||
public static MethodDefinition findDecrypterMethod(MethodDefinition method) {
|
||||
if (method == null || method.Body == null)
|
||||
return null;
|
||||
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Call)
|
||||
continue;
|
||||
var calledMethod = instr.Operand as MethodDefinition;
|
||||
if (calledMethod == null || !calledMethod.IsStatic || calledMethod.Body == null)
|
||||
continue;
|
||||
if (!DotNetUtils.isMethod(calledMethod, "System.IO.MemoryStream", "(System.IO.Stream)"))
|
||||
continue;
|
||||
|
||||
return calledMethod;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] decrypt(byte[] encryptedData) {
|
||||
if (decrypter == null)
|
||||
decrypter = createDecrypter(encryptedData);
|
||||
return decrypter.decrypt(encryptedData);
|
||||
}
|
||||
|
||||
IDecrypter createDecrypter(byte[] encryptedData) {
|
||||
if (decryptMethod != null && DeobUtils.hasInteger(decryptMethod, 6))
|
||||
return new Decrypter3(module, decryptMethod);
|
||||
if (isV30(encryptedData))
|
||||
return decryptV30(encryptedData);
|
||||
return decryptV35(encryptedData);
|
||||
return new Decrypter1(module);
|
||||
return new Decrypter2(module);
|
||||
}
|
||||
|
||||
static bool isV30(byte[] data) {
|
||||
return data.Length > 10 && data[0] == 8 && data[9] <= 1 && data[10] == 8;
|
||||
}
|
||||
|
||||
// v3.0
|
||||
byte[] decryptV30(byte[] encryptedData) {
|
||||
byte[] key, iv;
|
||||
var reader = new BinaryReader(new MemoryStream(encryptedData));
|
||||
bool isCompressed = getHeaderData30(reader, out key, out iv);
|
||||
var data = DeobUtils.desDecrypt(encryptedData,
|
||||
(int)reader.BaseStream.Position,
|
||||
(int)(reader.BaseStream.Length - reader.BaseStream.Position),
|
||||
key, iv);
|
||||
if (isCompressed)
|
||||
data = DeobUtils.inflate(data, true);
|
||||
return data;
|
||||
}
|
||||
|
||||
bool getHeaderData30(BinaryReader reader, out byte[] key, out byte[] iv) {
|
||||
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, 0, key, 0, key.Length);
|
||||
}
|
||||
|
||||
reader.ReadBytes(reader.ReadInt32()); // hash
|
||||
return true;
|
||||
}
|
||||
|
||||
// v3.5+
|
||||
byte[] decryptV35(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));
|
||||
|
||||
// 3.0 - 3.5 don't have this field
|
||||
if (headerData[(int)reader.BaseStream.Position] != 8) {
|
||||
var license = reader.ReadString();
|
||||
}
|
||||
|
||||
// 4.2 (and earlier?) always compress the data
|
||||
bool isCompressed = true;
|
||||
if (headerData[(int)reader.BaseStream.Position] != 8)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,16 +17,23 @@
|
|||
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 ResourceResolver {
|
||||
ModuleDefinition module;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
ISimpleDeobfuscator simpleDeobfuscator;
|
||||
TypeDefinition resolverType;
|
||||
MethodDefinition registerMethod;
|
||||
EmbeddedResource encryptedResource;
|
||||
bool hasXorKeys;
|
||||
int xorKey1, xorKey2;
|
||||
|
||||
public bool Detected {
|
||||
get { return resolverType != null; }
|
||||
|
@ -40,8 +47,10 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
get { return registerMethod; }
|
||||
}
|
||||
|
||||
public ResourceResolver(ModuleDefinition module) {
|
||||
public ResourceResolver(ModuleDefinition module, ResourceDecrypter resourceDecrypter, ISimpleDeobfuscator simpleDeobfuscator) {
|
||||
this.module = module;
|
||||
this.resourceDecrypter = resourceDecrypter;
|
||||
this.simpleDeobfuscator = simpleDeobfuscator;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
|
@ -54,7 +63,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
foreach (var type in module.Types) {
|
||||
if (type.HasEvents)
|
||||
continue;
|
||||
if (!new FieldTypes(type).exactly(requiredTypes))
|
||||
if (!new FieldTypes(type).all(requiredTypes))
|
||||
continue;
|
||||
|
||||
MethodDefinition regMethod, handler;
|
||||
|
@ -65,6 +74,12 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
if (resource == null)
|
||||
continue;
|
||||
|
||||
var decryptMethod = findDecryptMethod(type);
|
||||
if (decryptMethod == null)
|
||||
throw new ApplicationException("Couldn't find resource type decrypt method");
|
||||
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(decryptMethod);
|
||||
initXorKeys(decryptMethod);
|
||||
|
||||
resolverType = type;
|
||||
registerMethod = regMethod;
|
||||
encryptedResource = resource;
|
||||
|
@ -72,6 +87,46 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
}
|
||||
}
|
||||
|
||||
static MethodDefinition findDecryptMethod(TypeDefinition type) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (!DotNetUtils.isMethod(method, "System.Reflection.Assembly", "(System.IO.Stream)"))
|
||||
continue;
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void initXorKeys(MethodDefinition method) {
|
||||
simpleDeobfuscator.deobfuscate(method);
|
||||
var ints = new List<int>();
|
||||
var instrs = method.Body.Instructions;
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var callvirt = instrs[i];
|
||||
if (callvirt.OpCode.Code != Code.Callvirt)
|
||||
continue;
|
||||
var calledMethod = callvirt.Operand as MethodReference;
|
||||
if (calledMethod == null)
|
||||
continue;
|
||||
if (calledMethod.FullName != "System.Int32 System.IO.BinaryReader::ReadInt32()")
|
||||
continue;
|
||||
|
||||
var ldci4 = instrs[i + 1];
|
||||
if (!DotNetUtils.isLdcI4(ldci4))
|
||||
continue;
|
||||
|
||||
if (instrs[i + 2].OpCode.Code != Code.Xor)
|
||||
continue;
|
||||
|
||||
ints.Add(DotNetUtils.getLdcI4Value(ldci4));
|
||||
}
|
||||
|
||||
if (ints.Count == 2) {
|
||||
hasXorKeys = true;
|
||||
xorKey1 = ints[0];
|
||||
xorKey2 = ints[1];
|
||||
}
|
||||
}
|
||||
|
||||
public EmbeddedResource mergeResources() {
|
||||
if (encryptedResource == null)
|
||||
return null;
|
||||
|
@ -82,12 +137,19 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
}
|
||||
|
||||
byte[] decryptResourceAssembly() {
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(encryptedResource.GetResourceData());
|
||||
var decrypted = resourceDecrypter.decrypt(encryptedResource.GetResourceData());
|
||||
var reader = new BinaryReader(new MemoryStream(decrypted));
|
||||
int numResources = reader.ReadInt32();
|
||||
|
||||
int numResources = reader.ReadInt32() ^ xorKey1;
|
||||
for (int i = 0; i < numResources; i++)
|
||||
reader.ReadString();
|
||||
return reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
|
||||
|
||||
int len;
|
||||
if (hasXorKeys)
|
||||
len = reader.ReadInt32() ^ xorKey2;
|
||||
else
|
||||
len = (int)(reader.BaseStream.Length - reader.BaseStream.Position);
|
||||
return reader.ReadBytes(len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,10 +24,12 @@ using System.Text;
|
|||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
using de4dot.blocks.cflow;
|
||||
|
||||
namespace de4dot.code.deobfuscators.Babel_NET {
|
||||
class StringDecrypter {
|
||||
ModuleDefinition module;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
ISimpleDeobfuscator simpleDeobfuscator;
|
||||
TypeDefinition decrypterType;
|
||||
EmbeddedResource encryptedResource;
|
||||
|
@ -91,18 +93,39 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
|
||||
class DecrypterInfoV3 : IDecrypterInfo {
|
||||
Dictionary<int, string> offsetToString = new Dictionary<int, string>();
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
InstructionEmulator emulator = new InstructionEmulator();
|
||||
|
||||
public int OffsetMagic { get; set; }
|
||||
public IList<Instruction> OffsetCalcInstructions { get; set; }
|
||||
public MethodDefinition Decrypter { get; set; }
|
||||
public bool NeedsResource {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public DecrypterInfoV3(ResourceDecrypter resourceDecrypter) {
|
||||
this.resourceDecrypter = resourceDecrypter;
|
||||
}
|
||||
|
||||
public void initialize(ModuleDefinition module, EmbeddedResource resource) {
|
||||
var decrypted = new ResourceDecrypter(module).decrypt(resource.GetResourceData());
|
||||
var decrypted = resourceDecrypter.decrypt(resource.GetResourceData());
|
||||
var reader = new BinaryReader(new MemoryStream(decrypted));
|
||||
while (reader.BaseStream.Position < reader.BaseStream.Length)
|
||||
offsetToString[(int)reader.BaseStream.Position] = reader.ReadString();
|
||||
offsetToString[getOffset((int)reader.BaseStream.Position)] = reader.ReadString();
|
||||
}
|
||||
|
||||
MethodDefinition dummyMethod;
|
||||
int getOffset(int offset) {
|
||||
if (OffsetCalcInstructions == null || OffsetCalcInstructions.Count == 0)
|
||||
return offset;
|
||||
if (dummyMethod == null) {
|
||||
dummyMethod = new MethodDefinition("", 0, new TypeReference("", "", null, null));
|
||||
dummyMethod.Body = new MethodBody(dummyMethod);
|
||||
}
|
||||
emulator.init(dummyMethod);
|
||||
emulator.push(new Int32Value(offset));
|
||||
foreach (var instr in OffsetCalcInstructions)
|
||||
emulator.emulate(instr);
|
||||
return ((Int32Value)emulator.pop()).value;
|
||||
}
|
||||
|
||||
public string decrypt(object[] args) {
|
||||
|
@ -110,7 +133,7 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
}
|
||||
|
||||
string decrypt(int offset) {
|
||||
return offsetToString[offset ^ OffsetMagic];
|
||||
return offsetToString[offset];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,8 +153,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
get { return encryptedResource; }
|
||||
}
|
||||
|
||||
public StringDecrypter(ModuleDefinition module) {
|
||||
public StringDecrypter(ModuleDefinition module, ResourceDecrypter resourceDecrypter) {
|
||||
this.module = module;
|
||||
this.resourceDecrypter = resourceDecrypter;
|
||||
}
|
||||
|
||||
public void find(ISimpleDeobfuscator simpleDeobfuscator) {
|
||||
|
@ -225,16 +249,18 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
if (DotNetUtils.getMethod(nested, ".ctor") == null)
|
||||
return null;
|
||||
|
||||
if (nested.Fields.Count == 1) {
|
||||
if (nested.Fields.Count == 1 || nested.Fields.Count == 3) {
|
||||
// 4.0+
|
||||
|
||||
if (!MemberReferenceHelper.compareTypes(nested.Fields[0].FieldType, nested))
|
||||
if (!hasFieldType(nested.Fields, nested))
|
||||
return null;
|
||||
|
||||
var decrypterBuilderMethod = DotNetUtils.getMethod(nested, "System.Reflection.Emit.MethodBuilder", "(System.Reflection.Emit.TypeBuilder)");
|
||||
if (decrypterBuilderMethod == null)
|
||||
return null;
|
||||
|
||||
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(DotNetUtils.getMethod(nested, ".ctor"));
|
||||
|
||||
var nestedDecrypter = DotNetUtils.getMethod(nested, "System.String", "(System.Int32)");
|
||||
if (nestedDecrypter == null || nestedDecrypter.IsStatic)
|
||||
return null;
|
||||
|
@ -243,16 +269,16 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
return null;
|
||||
|
||||
simpleDeobfuscator.deobfuscate(decrypterBuilderMethod);
|
||||
return new DecrypterInfoV3 {
|
||||
return new DecrypterInfoV3(resourceDecrypter) {
|
||||
Decrypter = decrypter,
|
||||
OffsetMagic = getOffsetMagic(decrypterBuilderMethod),
|
||||
OffsetCalcInstructions = getOffsetCalcInstructions(decrypterBuilderMethod),
|
||||
};
|
||||
}
|
||||
else if (nested.Fields.Count == 2) {
|
||||
// 3.0 - 3.5
|
||||
|
||||
if (checkFields(nested, "System.Collections.Hashtable", nested)) {
|
||||
// 3.5
|
||||
// 3.0 - 3.5
|
||||
var nestedDecrypter = DotNetUtils.getMethod(nested, "System.String", "(System.Int32)");
|
||||
if (nestedDecrypter == null || nestedDecrypter.IsStatic)
|
||||
return null;
|
||||
|
@ -260,7 +286,9 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
if (decrypter == null || !decrypter.IsStatic)
|
||||
return null;
|
||||
|
||||
return new DecrypterInfoV3 { Decrypter = decrypter };
|
||||
resourceDecrypter.DecryptMethod = ResourceDecrypter.findDecrypterMethod(DotNetUtils.getMethod(nested, ".ctor"));
|
||||
|
||||
return new DecrypterInfoV3(resourceDecrypter) { Decrypter = decrypter };
|
||||
}
|
||||
else if (checkFields(nested, "System.Byte[]", nested)) {
|
||||
// 3.0
|
||||
|
@ -280,6 +308,205 @@ namespace de4dot.code.deobfuscators.Babel_NET {
|
|||
return null;
|
||||
}
|
||||
|
||||
class ReflectionToCecilMethodCreator {
|
||||
MethodDefinition method;
|
||||
List<Instruction> instructions = new List<Instruction>();
|
||||
InstructionEmulator emulator;
|
||||
int index;
|
||||
|
||||
class UserValue : UnknownValue {
|
||||
public readonly object obj;
|
||||
public UserValue(object obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
public override string ToString() {
|
||||
if (obj == null)
|
||||
return "<null>";
|
||||
return obj.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Instruction> Instructions {
|
||||
get { return instructions; }
|
||||
}
|
||||
|
||||
public ReflectionToCecilMethodCreator(MethodDefinition method) {
|
||||
this.method = method;
|
||||
this.emulator = new InstructionEmulator(method);
|
||||
}
|
||||
|
||||
public bool create() {
|
||||
int arrayIndex;
|
||||
Value array;
|
||||
object value;
|
||||
while (true) {
|
||||
var instr = method.Body.Instructions[index];
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ret:
|
||||
return true;
|
||||
|
||||
case Code.Newarr:
|
||||
var arrayType = (TypeReference)instr.Operand;
|
||||
int arrayCount = ((Int32Value)emulator.pop()).value;
|
||||
if (arrayType.FullName == "System.Char")
|
||||
emulator.push(new UserValue(new char[arrayCount]));
|
||||
else
|
||||
emulator.push(new UnknownValue());
|
||||
break;
|
||||
|
||||
case Code.Call:
|
||||
case Code.Callvirt:
|
||||
if (!doCall(instr))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Code.Ldelem_U1:
|
||||
arrayIndex = ((Int32Value)emulator.pop()).value;
|
||||
array = (Value)emulator.pop();
|
||||
if (array is UserValue)
|
||||
emulator.push(new Int32Value(((byte[])((UserValue)array).obj)[arrayIndex]));
|
||||
else
|
||||
emulator.push(Int32Value.createUnknownUInt8());
|
||||
break;
|
||||
|
||||
case Code.Stelem_I1:
|
||||
value = emulator.pop();
|
||||
arrayIndex = ((Int32Value)emulator.pop()).value;
|
||||
array = (Value)emulator.pop();
|
||||
if (array is UserValue)
|
||||
((byte[])((UserValue)array).obj)[arrayIndex] = (byte)((Int32Value)value).value;
|
||||
break;
|
||||
|
||||
case Code.Stelem_I2:
|
||||
value = emulator.pop();
|
||||
arrayIndex = ((Int32Value)emulator.pop()).value;
|
||||
array = (Value)emulator.pop();
|
||||
if (array is UserValue)
|
||||
((char[])((UserValue)array).obj)[arrayIndex] = (char)((Int32Value)value).value;
|
||||
break;
|
||||
|
||||
case Code.Ldelem_Ref:
|
||||
arrayIndex = ((Int32Value)emulator.pop()).value;
|
||||
array = (Value)emulator.pop();
|
||||
var userValue = array as UserValue;
|
||||
if (userValue != null && userValue.obj is string[])
|
||||
emulator.push(new StringValue(((string[])userValue.obj)[arrayIndex]));
|
||||
else
|
||||
emulator.push(new UnknownValue());
|
||||
break;
|
||||
|
||||
case Code.Ldsfld:
|
||||
emulator.push(new UserValue((FieldReference)instr.Operand));
|
||||
break;
|
||||
|
||||
default:
|
||||
emulator.emulate(instr);
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
bool doCall(Instruction instr) {
|
||||
var calledMethod = (MethodReference)instr.Operand;
|
||||
if (calledMethod.FullName == "System.Byte[] System.Convert::FromBase64String(System.String)") {
|
||||
emulator.push(new UserValue(Convert.FromBase64String(((StringValue)emulator.pop()).value)));
|
||||
return true;
|
||||
}
|
||||
else if (calledMethod.FullName == "System.String System.Text.Encoding::GetString(System.Byte[])") {
|
||||
emulator.push(new StringValue(Encoding.UTF8.GetString((byte[])((UserValue)emulator.pop()).obj)));
|
||||
return true;
|
||||
}
|
||||
else if (calledMethod.FullName == "System.Int32 System.Int32::Parse(System.String)") {
|
||||
emulator.push(new Int32Value(int.Parse(((StringValue)emulator.pop()).value)));
|
||||
return true;
|
||||
}
|
||||
else if (calledMethod.FullName == "System.String[] System.String::Split(System.Char[])") {
|
||||
var ary = (char[])((UserValue)emulator.pop()).obj;
|
||||
var s = ((StringValue)emulator.pop()).value;
|
||||
emulator.push(new UserValue(s.Split(ary)));
|
||||
return true;
|
||||
}
|
||||
else if (calledMethod.HasThis && calledMethod.DeclaringType.FullName == "System.Reflection.Emit.ILGenerator" && calledMethod.Name == "Emit") {
|
||||
Value operand = null;
|
||||
if (calledMethod.Parameters.Count == 2)
|
||||
operand = emulator.pop();
|
||||
var opcode = reflectionToCecilOpCode((FieldReference)((UserValue)emulator.pop()).obj);
|
||||
emulator.pop(); // the this ptr
|
||||
addInstruction(new Instruction {
|
||||
OpCode = opcode,
|
||||
Operand = createCecilOperand(opcode, operand),
|
||||
});
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
emulator.emulate(instr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
object createCecilOperand(OpCode opcode, Value op) {
|
||||
if (op is Int32Value)
|
||||
return ((Int32Value)op).value;
|
||||
if (op is StringValue)
|
||||
return ((StringValue)op).value;
|
||||
return null;
|
||||
}
|
||||
|
||||
void addInstruction(Instruction instr) {
|
||||
instructions.Add(instr);
|
||||
}
|
||||
|
||||
static OpCode reflectionToCecilOpCode(FieldReference reflectionField) {
|
||||
var field = typeof(OpCodes).GetField(reflectionField.Name);
|
||||
if (field == null || field.FieldType != typeof(OpCode))
|
||||
return null;
|
||||
return (OpCode)field.GetValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Instruction> getOffsetCalcInstructions(MethodDefinition method) {
|
||||
var creator = new ReflectionToCecilMethodCreator(method);
|
||||
creator.create();
|
||||
var instrs = creator.Instructions;
|
||||
|
||||
int index = 0;
|
||||
|
||||
index = findInstruction(instrs, index, OpCodes.Conv_I4);
|
||||
if (index < 0)
|
||||
return null;
|
||||
int startInstr = ++index;
|
||||
|
||||
index = findInstruction(instrs, index, OpCodes.Box);
|
||||
if (index < 0)
|
||||
return null;
|
||||
int endInstr = index - 1;
|
||||
|
||||
var transformInstructions = new List<Instruction>();
|
||||
for (int i = startInstr; i <= endInstr; i++)
|
||||
transformInstructions.Add(instrs[i]);
|
||||
return transformInstructions;
|
||||
}
|
||||
|
||||
static int findInstruction(IList<Instruction> instrs, int index, OpCode opcode) {
|
||||
if (index < 0)
|
||||
return -1;
|
||||
for (int i = index; i < instrs.Count; i++) {
|
||||
if (instrs[i].OpCode == opcode)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool hasFieldType(IEnumerable<FieldDefinition> fields, TypeReference fieldType) {
|
||||
foreach (var field in fields) {
|
||||
if (MemberReferenceHelper.compareTypes(field.FieldType, fieldType))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int getOffsetMagic(MethodDefinition method) {
|
||||
var instrs = method.Body.Instructions;
|
||||
for (int i = 0; i < instrs.Count - 4; i++) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user