Support latest Agile.NET
This commit is contained in:
parent
f9dde3317c
commit
b65e9a59df
|
@ -66,17 +66,37 @@
|
||||||
<Compile Include="deobfuscators\Agile_NET\ResourceDecrypter.cs" />
|
<Compile Include="deobfuscators\Agile_NET\ResourceDecrypter.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\StackFrameHelper.cs" />
|
<Compile Include="deobfuscators\Agile_NET\StackFrameHelper.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\StringDecrypter.cs" />
|
<Compile Include="deobfuscators\Agile_NET\StringDecrypter.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\CilOperandInstructionRestorer.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\CilOperandInstructionRestorer.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\CsvmDataReader.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\CsvmMethodData.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\CsvmToCilMethodConverterBase.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\Csvm.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v1\Csvm.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\CsvmDataReader.cs" />
|
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\CsvmMethodData.cs" />
|
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\CsvmToCilMethodConverter.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v1\CsvmToCilMethodConverter.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\FieldsInfo.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v1\FieldsInfo.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\OpCodeHandler.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v1\OpCodeHandler.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\OpCodeHandlers.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v1\OpCodeHandlers.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\UnknownHandlerInfo.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v1\UnknownHandlerInfo.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\VmOpCodeHandlerDetector.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v1\VmOpCodeHandlerDetector.cs" />
|
||||||
<Compile Include="deobfuscators\Agile_NET\vm\v1\VmOperands.cs" />
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\CompositeHandlerDetector.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\CompositeOpCodeHandler.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\Csvm.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\CsvmInfo.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\CsvmResources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>CsvmResources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\CsvmToCilMethodConverter.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\HandlerTypeCode.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\MethodFinder.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\MethodSigInfoCreator.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandler.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandlerInfo.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandlerInfoReader.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\OpCodeHandlerInfos.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\VmOpCode.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\v2\VmOpCodeHandlerDetector.cs" />
|
||||||
|
<Compile Include="deobfuscators\Agile_NET\vm\VmOperand.cs" />
|
||||||
<Compile Include="deobfuscators\ArrayFinder.cs" />
|
<Compile Include="deobfuscators\ArrayFinder.cs" />
|
||||||
<Compile Include="deobfuscators\Babel_NET\AssemblyResolver.cs" />
|
<Compile Include="deobfuscators\Babel_NET\AssemblyResolver.cs" />
|
||||||
<Compile Include="deobfuscators\Babel_NET\BabelInflater.cs" />
|
<Compile Include="deobfuscators\Babel_NET\BabelInflater.cs" />
|
||||||
|
@ -233,6 +253,7 @@
|
||||||
<Compile Include="deobfuscators\MethodStack.cs" />
|
<Compile Include="deobfuscators\MethodStack.cs" />
|
||||||
<Compile Include="deobfuscators\MPRESS\Deobfuscator.cs" />
|
<Compile Include="deobfuscators\MPRESS\Deobfuscator.cs" />
|
||||||
<Compile Include="deobfuscators\MPRESS\Lzmat.cs" />
|
<Compile Include="deobfuscators\MPRESS\Lzmat.cs" />
|
||||||
|
<Compile Include="deobfuscators\NullStream.cs" />
|
||||||
<Compile Include="deobfuscators\Operations.cs" />
|
<Compile Include="deobfuscators\Operations.cs" />
|
||||||
<Compile Include="deobfuscators\ProxyCallFixerBase.cs" />
|
<Compile Include="deobfuscators\ProxyCallFixerBase.cs" />
|
||||||
<Compile Include="deobfuscators\QuickLZ.cs" />
|
<Compile Include="deobfuscators\QuickLZ.cs" />
|
||||||
|
@ -351,6 +372,12 @@
|
||||||
<Name>dnlib</Name>
|
<Name>dnlib</Name>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="deobfuscators\Agile_NET\vm\v2\CsvmResources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>CsvmResources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PostBuildEvent>mkdir "..\$(OutDir)..\LICENSES"
|
<PostBuildEvent>mkdir "..\$(OutDir)..\LICENSES"
|
||||||
|
|
|
@ -88,19 +88,6 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
static readonly string[] requiredFields1 = new string[] {
|
static readonly string[] requiredFields1 = new string[] {
|
||||||
"System.Boolean",
|
"System.Boolean",
|
||||||
};
|
};
|
||||||
static readonly string[] requiredFields2 = new string[] {
|
|
||||||
"System.Boolean",
|
|
||||||
"System.Reflection.Assembly",
|
|
||||||
};
|
|
||||||
static readonly string[] requiredFields3 = new string[] {
|
|
||||||
"System.Boolean",
|
|
||||||
"System.Byte[]",
|
|
||||||
};
|
|
||||||
static readonly string[] requiredFields4 = new string[] {
|
|
||||||
"System.Boolean",
|
|
||||||
"System.Reflection.Assembly",
|
|
||||||
"System.Byte[]",
|
|
||||||
};
|
|
||||||
bool Find2() {
|
bool Find2() {
|
||||||
foreach (var cctor in DeobUtils.GetInitCctors(module, 3)) {
|
foreach (var cctor in DeobUtils.GetInitCctors(module, 3)) {
|
||||||
foreach (var calledMethod in DotNetUtils.GetCalledMethods(module, cctor)) {
|
foreach (var calledMethod in DotNetUtils.GetCalledMethods(module, cctor)) {
|
||||||
|
@ -108,8 +95,7 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
if (type.IsPublic)
|
if (type.IsPublic)
|
||||||
continue;
|
continue;
|
||||||
var fieldTypes = new FieldTypes(type);
|
var fieldTypes = new FieldTypes(type);
|
||||||
if (!fieldTypes.Exactly(requiredFields1) && !fieldTypes.Exactly(requiredFields2) &&
|
if (!fieldTypes.All(requiredFields1))
|
||||||
!fieldTypes.Exactly(requiredFields3) && !fieldTypes.Exactly(requiredFields4))
|
|
||||||
continue;
|
continue;
|
||||||
if (!HasInitializeMethod(type, "_Initialize") && !HasInitializeMethod(type, "_Initialize64"))
|
if (!HasInitializeMethod(type, "_Initialize") && !HasInitializeMethod(type, "_Initialize64"))
|
||||||
continue;
|
continue;
|
||||||
|
@ -126,11 +112,19 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string[] requiredFields6 = new string[] {
|
||||||
|
"System.Byte[]",
|
||||||
|
};
|
||||||
|
static string[] requiredFields7 = new string[] {
|
||||||
|
"System.Byte[]",
|
||||||
|
"System.Collections.Hashtable",
|
||||||
|
};
|
||||||
bool Find3() {
|
bool Find3() {
|
||||||
foreach (var type in module.Types) {
|
foreach (var type in module.Types) {
|
||||||
if (type.Fields.Count != 1)
|
if (type.Fields.Count < 1 || type.Fields.Count > 2)
|
||||||
continue;
|
continue;
|
||||||
if (type.Fields[0].FieldSig.GetFieldType().GetFullName() != "System.Byte[]")
|
var fieldTypes = new FieldTypes(type);
|
||||||
|
if (!fieldTypes.Exactly(requiredFields6) && !fieldTypes.Exactly(requiredFields7))
|
||||||
continue;
|
continue;
|
||||||
if (type.Methods.Count != 2)
|
if (type.Methods.Count != 2)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -85,7 +85,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
ResourceDecrypter resourceDecrypter;
|
ResourceDecrypter resourceDecrypter;
|
||||||
|
|
||||||
StackFrameHelper stackFrameHelper;
|
StackFrameHelper stackFrameHelper;
|
||||||
vm.v1.Csvm csvm;
|
vm.v1.Csvm csvmV1;
|
||||||
|
vm.v2.Csvm csvmV2;
|
||||||
|
|
||||||
internal class Options : OptionsBase {
|
internal class Options : OptionsBase {
|
||||||
public bool DecryptMethods { get; set; }
|
public bool DecryptMethods { get; set; }
|
||||||
|
@ -166,7 +167,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
ToInt32(stringDecrypter.Detected) +
|
ToInt32(stringDecrypter.Detected) +
|
||||||
ToInt32(proxyCallFixer.Detected) +
|
ToInt32(proxyCallFixer.Detected) +
|
||||||
ToInt32(resourceDecrypter.Detected) +
|
ToInt32(resourceDecrypter.Detected) +
|
||||||
ToInt32(csvm.Detected);
|
ToInt32(csvmV1.Detected) +
|
||||||
|
ToInt32(csvmV2.Detected);
|
||||||
if (sum > 0)
|
if (sum > 0)
|
||||||
val += 100 + 10 * (sum - 1);
|
val += 100 + 10 * (sum - 1);
|
||||||
if (cliSecureAttributes.Count != 0)
|
if (cliSecureAttributes.Count != 0)
|
||||||
|
@ -185,8 +187,10 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
resourceDecrypter.Find();
|
resourceDecrypter.Find();
|
||||||
proxyCallFixer = new ProxyCallFixer(module);
|
proxyCallFixer = new ProxyCallFixer(module);
|
||||||
proxyCallFixer.FindDelegateCreator();
|
proxyCallFixer.FindDelegateCreator();
|
||||||
csvm = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module);
|
csvmV1 = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module);
|
||||||
csvm.Find();
|
csvmV1.Find();
|
||||||
|
csvmV2 = new vm.v2.Csvm(DeobfuscatedFile.DeobfuscatorContext, module);
|
||||||
|
csvmV2.Find();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FindCliSecureAttribute() {
|
void FindCliSecureAttribute() {
|
||||||
|
@ -227,7 +231,8 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
newOne.stringDecrypter = new StringDecrypter(module, stringDecrypter);
|
newOne.stringDecrypter = new StringDecrypter(module, stringDecrypter);
|
||||||
newOne.resourceDecrypter = new ResourceDecrypter(module, resourceDecrypter);
|
newOne.resourceDecrypter = new ResourceDecrypter(module, resourceDecrypter);
|
||||||
newOne.proxyCallFixer = new ProxyCallFixer(module, proxyCallFixer);
|
newOne.proxyCallFixer = new ProxyCallFixer(module, proxyCallFixer);
|
||||||
newOne.csvm = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvm);
|
newOne.csvmV1 = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvmV1);
|
||||||
|
newOne.csvmV2 = new vm.v2.Csvm(DeobfuscatedFile.DeobfuscatorContext, module, csvmV2);
|
||||||
return newOne;
|
return newOne;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,9 +275,11 @@ namespace de4dot.code.deobfuscators.Agile_NET {
|
||||||
FindPossibleNamesToRemove(cliSecureRtType.LoadMethod);
|
FindPossibleNamesToRemove(cliSecureRtType.LoadMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.RestoreVmCode) {
|
if (options.RestoreVmCode && (csvmV1.Detected || csvmV2.Detected)) {
|
||||||
if (csvm.Restore())
|
if (csvmV1.Detected && csvmV1.Restore())
|
||||||
AddResourceToBeRemoved(csvm.Resource, "CSVM data resource");
|
AddResourceToBeRemoved(csvmV1.Resource, "CSVM data resource");
|
||||||
|
else if (csvmV2.Detected && csvmV2.Restore())
|
||||||
|
AddResourceToBeRemoved(csvmV2.Resource, "CSVM data resource");
|
||||||
else {
|
else {
|
||||||
Logger.e("Couldn't restore VM methods. Use --dont-rename or it will not run");
|
Logger.e("Couldn't restore VM methods. Use --dont-rename or it will not run");
|
||||||
PreserveTokensAndTypes();
|
PreserveTokensAndTypes();
|
||||||
|
|
|
@ -21,11 +21,9 @@ using dnlib.DotNet;
|
||||||
using dnlib.DotNet.Emit;
|
using dnlib.DotNet.Emit;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
namespace de4dot.code.deobfuscators.Agile_NET.vm {
|
||||||
// Tries to restore the operands of the following CIL instructions:
|
// Tries to restore the operands of the following CIL instructions:
|
||||||
// ldelema
|
// ldelema, ldelem.*, stelem.*, ldobj, stobj
|
||||||
// ldobj
|
|
||||||
// stobj
|
|
||||||
class CilOperandInstructionRestorer {
|
class CilOperandInstructionRestorer {
|
||||||
MethodDef method;
|
MethodDef method;
|
||||||
|
|
||||||
|
@ -42,10 +40,66 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
if (instr.Operand != null)
|
if (instr.Operand != null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
TypeSig operandType = null;
|
TypeSig operandType = null, operandTypeTmp;
|
||||||
|
OpCode newOpCode = null;
|
||||||
|
SZArraySig arrayType;
|
||||||
switch (instr.OpCode.Code) {
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Ldelem:
|
||||||
|
arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig;
|
||||||
|
if (arrayType == null)
|
||||||
|
break;
|
||||||
|
operandTypeTmp = arrayType.Next;
|
||||||
|
if (operandTypeTmp == null)
|
||||||
|
newOpCode = OpCodes.Ldelem_Ref;
|
||||||
|
else {
|
||||||
|
switch (operandTypeTmp.ElementType) {
|
||||||
|
case ElementType.I: newOpCode = OpCodes.Ldelem_I; break;
|
||||||
|
case ElementType.I1: newOpCode = OpCodes.Ldelem_I1; break;
|
||||||
|
case ElementType.I2: newOpCode = OpCodes.Ldelem_I2; break;
|
||||||
|
case ElementType.I4: newOpCode = OpCodes.Ldelem_I4; break;
|
||||||
|
case ElementType.I8: newOpCode = OpCodes.Ldelem_I8; break;
|
||||||
|
case ElementType.U: newOpCode = OpCodes.Ldelem_I; break;
|
||||||
|
case ElementType.U1: newOpCode = OpCodes.Ldelem_U1; break;
|
||||||
|
case ElementType.U2: newOpCode = OpCodes.Ldelem_U2; break;
|
||||||
|
case ElementType.U4: newOpCode = OpCodes.Ldelem_U4; break;
|
||||||
|
case ElementType.U8: newOpCode = OpCodes.Ldelem_I8; break;
|
||||||
|
case ElementType.R4: newOpCode = OpCodes.Ldelem_R4; break;
|
||||||
|
case ElementType.R8: newOpCode = OpCodes.Ldelem_R8; break;
|
||||||
|
default: newOpCode = OpCodes.Ldelem_Ref; break;
|
||||||
|
//TODO: Ldelem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stelem:
|
||||||
|
arrayType = MethodStack.GetLoadedType(method, instrs, i, 2) as SZArraySig;
|
||||||
|
if (arrayType == null)
|
||||||
|
break;
|
||||||
|
operandTypeTmp = arrayType.Next;
|
||||||
|
if (operandTypeTmp == null)
|
||||||
|
newOpCode = OpCodes.Stelem_Ref;
|
||||||
|
else {
|
||||||
|
switch (operandTypeTmp.ElementType) {
|
||||||
|
case ElementType.U:
|
||||||
|
case ElementType.I: newOpCode = OpCodes.Stelem_I; break;
|
||||||
|
case ElementType.U1:
|
||||||
|
case ElementType.I1: newOpCode = OpCodes.Stelem_I1; break;
|
||||||
|
case ElementType.U2:
|
||||||
|
case ElementType.I2: newOpCode = OpCodes.Stelem_I2; break;
|
||||||
|
case ElementType.U4:
|
||||||
|
case ElementType.I4: newOpCode = OpCodes.Stelem_I4; break;
|
||||||
|
case ElementType.U8:
|
||||||
|
case ElementType.I8: newOpCode = OpCodes.Stelem_I8; break;
|
||||||
|
case ElementType.R4: newOpCode = OpCodes.Stelem_R4; break;
|
||||||
|
case ElementType.R8: newOpCode = OpCodes.Stelem_R8; break;
|
||||||
|
default: newOpCode = OpCodes.Stelem_Ref; break;
|
||||||
|
//TODO: Stelem
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case Code.Ldelema:
|
case Code.Ldelema:
|
||||||
var arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig;
|
arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig;
|
||||||
if (arrayType == null)
|
if (arrayType == null)
|
||||||
break;
|
break;
|
||||||
operandType = arrayType.Next;
|
operandType = arrayType.Next;
|
||||||
|
@ -64,12 +118,14 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!IsValidType(operandType)) {
|
if (newOpCode == null && !IsValidType(operandType)) {
|
||||||
atLeastOneFailed = true;
|
atLeastOneFailed = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
instr.Operand = operandType.ToTypeDefOrRef();
|
instr.Operand = operandType.ToTypeDefOrRef();
|
||||||
|
if (newOpCode != null)
|
||||||
|
instr.OpCode = newOpCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !atLeastOneFailed;
|
return !atLeastOneFailed;
|
|
@ -24,7 +24,7 @@ using dnlib.IO;
|
||||||
using dnlib.DotNet;
|
using dnlib.DotNet;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
namespace de4dot.code.deobfuscators.Agile_NET.vm {
|
||||||
class CsvmDataReader {
|
class CsvmDataReader {
|
||||||
IBinaryReader reader;
|
IBinaryReader reader;
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
namespace de4dot.code.deobfuscators.Agile_NET.vm {
|
||||||
class CsvmMethodData {
|
class CsvmMethodData {
|
||||||
public Guid Guid { get; set; }
|
public Guid Guid { get; set; }
|
||||||
public int Token { get; set; }
|
public int Token { get; set; }
|
|
@ -0,0 +1,417 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm {
|
||||||
|
abstract class CsvmToCilMethodConverterBase {
|
||||||
|
readonly IDeobfuscatorContext deobfuscatorContext;
|
||||||
|
readonly protected ModuleDefMD module;
|
||||||
|
readonly CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer();
|
||||||
|
readonly Dictionary<Instruction, int> cilToVmIndex = new Dictionary<Instruction, int>();
|
||||||
|
readonly Dictionary<int, Instruction> vmIndexToCil = new Dictionary<int, Instruction>();
|
||||||
|
|
||||||
|
public CsvmToCilMethodConverterBase(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module) {
|
||||||
|
this.deobfuscatorContext = deobfuscatorContext;
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetCilToVmIndex(Instruction instr, int vmIndex) {
|
||||||
|
cilToVmIndex[instr] = vmIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void SetVmIndexToCil(Instruction instr, int vmIndex) {
|
||||||
|
vmIndexToCil[vmIndex] = instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Convert(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
||||||
|
cilToVmIndex.Clear();
|
||||||
|
vmIndexToCil.Clear();
|
||||||
|
|
||||||
|
var newInstructions = ReadInstructions(cilMethod, csvmMethod);
|
||||||
|
var newLocals = ReadLocals(cilMethod, csvmMethod);
|
||||||
|
var newExceptions = ReadExceptions(cilMethod, csvmMethod);
|
||||||
|
|
||||||
|
FixInstructionOperands(newInstructions);
|
||||||
|
FixLocals(newInstructions, cilMethod.Body.Variables);
|
||||||
|
FixArgs(newInstructions, cilMethod);
|
||||||
|
|
||||||
|
DotNetUtils.RestoreBody(cilMethod, newInstructions, newExceptions);
|
||||||
|
|
||||||
|
if (!operandRestorer.Restore(cilMethod))
|
||||||
|
Logger.w("Failed to restore one or more instruction operands in CSVM method {0:X8}", cilMethod.MDToken.ToInt32());
|
||||||
|
RestoreConstrainedPrefix(cilMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixLocals(IList<Instruction> instrs, IList<Local> locals) {
|
||||||
|
foreach (var instr in instrs) {
|
||||||
|
var op = instr.Operand as LocalOperand;
|
||||||
|
if (op == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UpdateLocalInstruction(instr, locals[op.Local], op.Local);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdateLocalInstruction(Instruction instr, Local local, int index) {
|
||||||
|
object operand = null;
|
||||||
|
OpCode opcode;
|
||||||
|
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Ldloc_S:
|
||||||
|
case Code.Ldloc:
|
||||||
|
if (index == 0)
|
||||||
|
opcode = OpCodes.Ldloc_0;
|
||||||
|
else if (index == 1)
|
||||||
|
opcode = OpCodes.Ldloc_1;
|
||||||
|
else if (index == 2)
|
||||||
|
opcode = OpCodes.Ldloc_2;
|
||||||
|
else if (index == 3)
|
||||||
|
opcode = OpCodes.Ldloc_3;
|
||||||
|
else if (byte.MinValue <= index && index <= byte.MaxValue) {
|
||||||
|
opcode = OpCodes.Ldloc_S;
|
||||||
|
operand = local;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
opcode = OpCodes.Ldloc;
|
||||||
|
operand = local;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc:
|
||||||
|
case Code.Stloc_S:
|
||||||
|
if (index == 0)
|
||||||
|
opcode = OpCodes.Stloc_0;
|
||||||
|
else if (index == 1)
|
||||||
|
opcode = OpCodes.Stloc_1;
|
||||||
|
else if (index == 2)
|
||||||
|
opcode = OpCodes.Stloc_2;
|
||||||
|
else if (index == 3)
|
||||||
|
opcode = OpCodes.Stloc_3;
|
||||||
|
else if (byte.MinValue <= index && index <= byte.MaxValue) {
|
||||||
|
opcode = OpCodes.Stloc_S;
|
||||||
|
operand = local;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
opcode = OpCodes.Stloc;
|
||||||
|
operand = local;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloca:
|
||||||
|
case Code.Ldloca_S:
|
||||||
|
if (byte.MinValue <= index && index <= byte.MaxValue) {
|
||||||
|
opcode = OpCodes.Ldloca_S;
|
||||||
|
operand = local;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
opcode = OpCodes.Ldloca;
|
||||||
|
operand = local;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ApplicationException("Invalid opcode");
|
||||||
|
}
|
||||||
|
|
||||||
|
instr.OpCode = opcode;
|
||||||
|
instr.Operand = operand;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixArgs(IList<Instruction> instrs, MethodDef method) {
|
||||||
|
foreach (var instr in instrs) {
|
||||||
|
var op = instr.Operand as ArgOperand;
|
||||||
|
if (op == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UpdateArgInstruction(instr, method.Parameters[op.Arg], op.Arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UpdateArgInstruction(Instruction instr, Parameter arg, int index) {
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Ldarg:
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
if (index == 0) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_0;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (index == 1) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_1;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (index == 2) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_2;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (index == 3) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_3;
|
||||||
|
instr.Operand = null;
|
||||||
|
}
|
||||||
|
else if (byte.MinValue <= index && index <= byte.MaxValue) {
|
||||||
|
instr.OpCode = OpCodes.Ldarg_S;
|
||||||
|
instr.Operand = arg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
instr.OpCode = OpCodes.Ldarg;
|
||||||
|
instr.Operand = arg;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Starg:
|
||||||
|
case Code.Starg_S:
|
||||||
|
if (byte.MinValue <= index && index <= byte.MaxValue) {
|
||||||
|
instr.OpCode = OpCodes.Starg_S;
|
||||||
|
instr.Operand = arg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
instr.OpCode = OpCodes.Starg;
|
||||||
|
instr.Operand = arg;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarga:
|
||||||
|
case Code.Ldarga_S:
|
||||||
|
if (byte.MinValue <= index && index <= byte.MaxValue) {
|
||||||
|
instr.OpCode = OpCodes.Ldarga_S;
|
||||||
|
instr.Operand = arg;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
instr.OpCode = OpCodes.Ldarga;
|
||||||
|
instr.Operand = arg;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new ApplicationException("Invalid opcode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract List<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod);
|
||||||
|
|
||||||
|
protected static int GetInstructionSize(Instruction instr) {
|
||||||
|
var opcode = instr.OpCode;
|
||||||
|
if (opcode == null)
|
||||||
|
return 5; // Load store/field
|
||||||
|
var op = instr.Operand as SwitchTargetDisplOperand;
|
||||||
|
if (op == null)
|
||||||
|
return instr.GetSize();
|
||||||
|
return instr.OpCode.Size + (op.TargetDisplacements.Length + 1) * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Local> ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
||||||
|
var locals = new List<Local>();
|
||||||
|
var reader = new BinaryReader(new MemoryStream(csvmMethod.Locals));
|
||||||
|
|
||||||
|
if (csvmMethod.Locals.Length == 0)
|
||||||
|
return locals;
|
||||||
|
|
||||||
|
// v6.0.0.5 sometimes duplicates the last two locals so only check for a negative value.
|
||||||
|
int numLocals = reader.ReadInt32();
|
||||||
|
if (numLocals < 0)
|
||||||
|
throw new ApplicationException("Invalid number of locals");
|
||||||
|
|
||||||
|
for (int i = 0; i < numLocals; i++)
|
||||||
|
locals.Add(new Local(ReadTypeRef(reader)));
|
||||||
|
|
||||||
|
return locals;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSig ReadTypeRef(BinaryReader reader) {
|
||||||
|
var etype = (ElementType)reader.ReadInt32();
|
||||||
|
switch (etype) {
|
||||||
|
case ElementType.Void: return module.CorLibTypes.Void;
|
||||||
|
case ElementType.Boolean: return module.CorLibTypes.Boolean;
|
||||||
|
case ElementType.Char: return module.CorLibTypes.Char;
|
||||||
|
case ElementType.I1: return module.CorLibTypes.SByte;
|
||||||
|
case ElementType.U1: return module.CorLibTypes.Byte;
|
||||||
|
case ElementType.I2: return module.CorLibTypes.Int16;
|
||||||
|
case ElementType.U2: return module.CorLibTypes.UInt16;
|
||||||
|
case ElementType.I4: return module.CorLibTypes.Int32;
|
||||||
|
case ElementType.U4: return module.CorLibTypes.UInt32;
|
||||||
|
case ElementType.I8: return module.CorLibTypes.Int64;
|
||||||
|
case ElementType.U8: return module.CorLibTypes.UInt64;
|
||||||
|
case ElementType.R4: return module.CorLibTypes.Single;
|
||||||
|
case ElementType.R8: return module.CorLibTypes.Double;
|
||||||
|
case ElementType.String: return module.CorLibTypes.String;
|
||||||
|
case ElementType.TypedByRef: return module.CorLibTypes.TypedReference;
|
||||||
|
case ElementType.I: return module.CorLibTypes.IntPtr;
|
||||||
|
case ElementType.U: return module.CorLibTypes.UIntPtr;
|
||||||
|
case ElementType.Object: return module.CorLibTypes.Object;
|
||||||
|
|
||||||
|
case ElementType.ValueType:
|
||||||
|
case ElementType.Var:
|
||||||
|
case ElementType.MVar:
|
||||||
|
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
|
||||||
|
|
||||||
|
case ElementType.GenericInst:
|
||||||
|
etype = (ElementType)reader.ReadInt32();
|
||||||
|
if (etype == ElementType.ValueType)
|
||||||
|
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
|
||||||
|
// ElementType.Class
|
||||||
|
return module.CorLibTypes.Object;
|
||||||
|
|
||||||
|
case ElementType.Ptr:
|
||||||
|
case ElementType.Class:
|
||||||
|
case ElementType.Array:
|
||||||
|
case ElementType.FnPtr:
|
||||||
|
case ElementType.SZArray:
|
||||||
|
case ElementType.ByRef:
|
||||||
|
case ElementType.CModReqd:
|
||||||
|
case ElementType.CModOpt:
|
||||||
|
case ElementType.Internal:
|
||||||
|
case ElementType.Sentinel:
|
||||||
|
case ElementType.Pinned:
|
||||||
|
default:
|
||||||
|
return module.CorLibTypes.Object;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ExceptionHandler> ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
||||||
|
var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions));
|
||||||
|
var ehs = new List<ExceptionHandler>();
|
||||||
|
|
||||||
|
if (reader.BaseStream.Length == 0)
|
||||||
|
return ehs;
|
||||||
|
|
||||||
|
int numExceptions = reader.ReadInt32();
|
||||||
|
if (numExceptions < 0)
|
||||||
|
throw new ApplicationException("Invalid number of exception handlers");
|
||||||
|
|
||||||
|
for (int i = 0; i < numExceptions; i++) {
|
||||||
|
var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32());
|
||||||
|
eh.TryStart = GetInstruction(reader.ReadInt32());
|
||||||
|
eh.TryEnd = GetInstructionEnd(reader.ReadInt32());
|
||||||
|
eh.HandlerStart = GetInstruction(reader.ReadInt32());
|
||||||
|
eh.HandlerEnd = GetInstructionEnd(reader.ReadInt32());
|
||||||
|
if (eh.HandlerType == ExceptionHandlerType.Catch)
|
||||||
|
eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
else if (eh.HandlerType == ExceptionHandlerType.Filter)
|
||||||
|
eh.FilterStart = GetInstruction(reader.ReadInt32());
|
||||||
|
|
||||||
|
ehs.Add(eh);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ehs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction GetInstruction(int vmIndex) {
|
||||||
|
return vmIndexToCil[vmIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction GetInstructionEnd(int vmIndex) {
|
||||||
|
vmIndex++;
|
||||||
|
Instruction instr;
|
||||||
|
vmIndexToCil.TryGetValue(vmIndex, out instr);
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction GetInstruction(Instruction source, int displ) {
|
||||||
|
int vmIndex = cilToVmIndex[source];
|
||||||
|
return vmIndexToCil[vmIndex + displ];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FixInstructionOperands(IList<Instruction> instrs) {
|
||||||
|
foreach (var instr in instrs) {
|
||||||
|
var op = instr.Operand as IVmOperand;
|
||||||
|
if (op != null)
|
||||||
|
instr.Operand = FixOperand(instrs, instr, op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object FixOperand(IList<Instruction> instrs, Instruction instr, IVmOperand vmOperand) {
|
||||||
|
if (vmOperand is TargetDisplOperand)
|
||||||
|
return GetInstruction(instr, ((TargetDisplOperand)vmOperand).Displacement);
|
||||||
|
|
||||||
|
if (vmOperand is SwitchTargetDisplOperand) {
|
||||||
|
var targetDispls = ((SwitchTargetDisplOperand)vmOperand).TargetDisplacements;
|
||||||
|
Instruction[] targets = new Instruction[targetDispls.Length];
|
||||||
|
for (int i = 0; i < targets.Length; i++)
|
||||||
|
targets[i] = GetInstruction(instr, targetDispls[i]);
|
||||||
|
return targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vmOperand is ArgOperand || vmOperand is LocalOperand)
|
||||||
|
return vmOperand;
|
||||||
|
|
||||||
|
if (vmOperand is FieldInstructionOperand) {
|
||||||
|
var fieldInfo = (FieldInstructionOperand)vmOperand;
|
||||||
|
return FixLoadStoreFieldInstruction(instr, fieldInfo.Field, fieldInfo.StaticOpCode, fieldInfo.InstanceOpCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ApplicationException(string.Format("Unknown operand type: {0}", vmOperand.GetType()));
|
||||||
|
}
|
||||||
|
|
||||||
|
IField FixLoadStoreFieldInstruction(Instruction instr, IField fieldRef, OpCode staticInstr, OpCode instanceInstr) {
|
||||||
|
var field = deobfuscatorContext.ResolveField(fieldRef);
|
||||||
|
bool isStatic;
|
||||||
|
if (field == null) {
|
||||||
|
Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", fieldRef == null ? 0 : fieldRef.MDToken.Raw);
|
||||||
|
isStatic = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
isStatic = field.IsStatic;
|
||||||
|
instr.OpCode = isStatic ? staticInstr : instanceInstr;
|
||||||
|
return fieldRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RestoreConstrainedPrefix(MethodDef method) {
|
||||||
|
if (method == null || method.Body == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count; i++) {
|
||||||
|
var instr = instrs[i];
|
||||||
|
if (instr.OpCode.Code != Code.Callvirt)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var calledMethod = instr.Operand as IMethod;
|
||||||
|
if (calledMethod == null)
|
||||||
|
continue;
|
||||||
|
var sig = calledMethod.MethodSig;
|
||||||
|
if (sig == null || !sig.HasThis)
|
||||||
|
continue;
|
||||||
|
var thisType = MethodStack.GetLoadedType(method, instrs, i, sig.Params.Count) as ByRefSig;
|
||||||
|
if (thisType == null)
|
||||||
|
continue;
|
||||||
|
if (HasPrefix(instrs, i, Code.Constrained))
|
||||||
|
continue;
|
||||||
|
instrs.Insert(i, OpCodes.Constrained.ToInstruction(thisType.Next.ToTypeDefOrRef()));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasPrefix(IList<Instruction> instrs, int index, Code prefix) {
|
||||||
|
index--;
|
||||||
|
for (; index >= 0; index--) {
|
||||||
|
var instr = instrs[index];
|
||||||
|
if (instr.OpCode.OpCodeType != OpCodeType.Prefix)
|
||||||
|
break;
|
||||||
|
if (instr.OpCode.Code == prefix)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,66 +17,50 @@
|
||||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm {
|
||||||
interface IVmOperand {
|
interface IVmOperand {
|
||||||
}
|
}
|
||||||
|
|
||||||
class TokenOperand : IVmOperand {
|
|
||||||
public int token; // any type of token
|
|
||||||
public TokenOperand(int token) {
|
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TargetDisplOperand : IVmOperand {
|
class TargetDisplOperand : IVmOperand {
|
||||||
public int displacement; // number of instructions from current instr
|
public readonly int Displacement; // number of VM instructions from current VM instr
|
||||||
public TargetDisplOperand(int displacement) {
|
public TargetDisplOperand(int displacement) {
|
||||||
this.displacement = displacement;
|
this.Displacement = displacement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SwitchTargetDisplOperand : IVmOperand {
|
class SwitchTargetDisplOperand : IVmOperand {
|
||||||
public int[] targetDisplacements; // number of instructions from current instr
|
public readonly int[] TargetDisplacements; // number of VM instructions from current VM instr
|
||||||
public SwitchTargetDisplOperand(int[] targetDisplacements) {
|
public SwitchTargetDisplOperand(int[] targetDisplacements) {
|
||||||
this.targetDisplacements = targetDisplacements;
|
this.TargetDisplacements = targetDisplacements;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArgOperand : IVmOperand {
|
class ArgOperand : IVmOperand {
|
||||||
public ushort arg;
|
public readonly ushort Arg;
|
||||||
public ArgOperand(ushort arg) {
|
public ArgOperand(ushort arg) {
|
||||||
this.arg = arg;
|
this.Arg = arg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LocalOperand : IVmOperand {
|
class LocalOperand : IVmOperand {
|
||||||
public ushort local;
|
public readonly ushort Local;
|
||||||
public LocalOperand(ushort local) {
|
public LocalOperand(ushort local) {
|
||||||
this.local = local;
|
this.Local = local;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpCode must be changed to ldfld / ldsfld
|
class FieldInstructionOperand : IVmOperand {
|
||||||
class LoadFieldOperand : IVmOperand {
|
public readonly OpCode StaticOpCode;
|
||||||
public int token;
|
public readonly OpCode InstanceOpCode;
|
||||||
public LoadFieldOperand(int token) {
|
public readonly IField Field;
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpCode must be changed to ldflda / ldsflda
|
public FieldInstructionOperand(OpCode staticOpCode, OpCode instanceOpCode, IField field) {
|
||||||
class LoadFieldAddressOperand : IVmOperand {
|
this.StaticOpCode = staticOpCode;
|
||||||
public int token;
|
this.InstanceOpCode = instanceOpCode;
|
||||||
public LoadFieldAddressOperand(int token) {
|
this.Field = field;
|
||||||
this.token = token;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpCode must be changed to stfld / stsfld
|
|
||||||
class StoreFieldOperand : IVmOperand {
|
|
||||||
public int token;
|
|
||||||
public StoreFieldOperand(int token) {
|
|
||||||
this.token = token;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -140,7 +140,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
var vmModulePath = Path.Combine(Path.GetDirectoryName(module.Location), vmFilename);
|
var vmModulePath = Path.Combine(Path.GetDirectoryName(module.Location), vmFilename);
|
||||||
Logger.v("CSVM filename: {0}", vmFilename);
|
Logger.v("CSVM filename: {0}", vmFilename);
|
||||||
|
|
||||||
var dataKey = "cs cached VmOpCodeHandlerDetector";
|
var dataKey = "cs cached VmOpCodeHandlerDetector v1";
|
||||||
var dict = (Dictionary<string, VmOpCodeHandlerDetector>)deobfuscatorContext.GetData(dataKey);
|
var dict = (Dictionary<string, VmOpCodeHandlerDetector>)deobfuscatorContext.GetData(dataKey);
|
||||||
if (dict == null)
|
if (dict == null)
|
||||||
deobfuscatorContext.SetData(dataKey, dict = new Dictionary<string, VmOpCodeHandlerDetector>(StringComparer.OrdinalIgnoreCase));
|
deobfuscatorContext.SetData(dataKey, dict = new Dictionary<string, VmOpCodeHandlerDetector>(StringComparer.OrdinalIgnoreCase));
|
||||||
|
|
|
@ -25,411 +25,28 @@ using dnlib.DotNet.Emit;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
class CsvmToCilMethodConverter {
|
class CsvmToCilMethodConverter : CsvmToCilMethodConverterBase {
|
||||||
IDeobfuscatorContext deobfuscatorContext;
|
|
||||||
ModuleDefMD module;
|
|
||||||
VmOpCodeHandlerDetector opCodeDetector;
|
VmOpCodeHandlerDetector opCodeDetector;
|
||||||
CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer();
|
|
||||||
|
|
||||||
public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) {
|
public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector)
|
||||||
this.deobfuscatorContext = deobfuscatorContext;
|
: base(deobfuscatorContext, module) {
|
||||||
this.module = module;
|
|
||||||
this.opCodeDetector = opCodeDetector;
|
this.opCodeDetector = opCodeDetector;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Convert(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
protected override List<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
||||||
var newInstructions = ReadInstructions(cilMethod, csvmMethod);
|
|
||||||
var newLocals = ReadLocals(cilMethod, csvmMethod);
|
|
||||||
var newExceptions = ReadExceptions(cilMethod, csvmMethod, newInstructions);
|
|
||||||
|
|
||||||
FixInstructionOperands(newInstructions);
|
|
||||||
FixLocals(newInstructions, cilMethod.Body.Variables);
|
|
||||||
FixArgs(newInstructions, cilMethod);
|
|
||||||
|
|
||||||
DotNetUtils.RestoreBody(cilMethod, newInstructions, newExceptions);
|
|
||||||
|
|
||||||
if (!operandRestorer.Restore(cilMethod))
|
|
||||||
Logger.w("Failed to restore one or more instruction operands in CSVM method {0:X8}", cilMethod.MDToken.ToInt32());
|
|
||||||
RestoreConstrainedPrefix(cilMethod);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FixLocals(IList<Instruction> instrs, IList<Local> locals) {
|
|
||||||
foreach (var instr in instrs) {
|
|
||||||
var op = instr.Operand as LocalOperand;
|
|
||||||
if (op == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
UpdateLocalInstruction(instr, locals[op.local], op.local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UpdateLocalInstruction(Instruction instr, Local local, int index) {
|
|
||||||
object operand = null;
|
|
||||||
OpCode opcode;
|
|
||||||
|
|
||||||
switch (instr.OpCode.Code) {
|
|
||||||
case Code.Ldloc_S:
|
|
||||||
case Code.Ldloc:
|
|
||||||
if (index == 0)
|
|
||||||
opcode = OpCodes.Ldloc_0;
|
|
||||||
else if (index == 1)
|
|
||||||
opcode = OpCodes.Ldloc_1;
|
|
||||||
else if (index == 2)
|
|
||||||
opcode = OpCodes.Ldloc_2;
|
|
||||||
else if (index == 3)
|
|
||||||
opcode = OpCodes.Ldloc_3;
|
|
||||||
else if (byte.MinValue <= index && index <= byte.MaxValue) {
|
|
||||||
opcode = OpCodes.Ldloc_S;
|
|
||||||
operand = local;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
opcode = OpCodes.Ldloc;
|
|
||||||
operand = local;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Code.Stloc:
|
|
||||||
case Code.Stloc_S:
|
|
||||||
if (index == 0)
|
|
||||||
opcode = OpCodes.Stloc_0;
|
|
||||||
else if (index == 1)
|
|
||||||
opcode = OpCodes.Stloc_1;
|
|
||||||
else if (index == 2)
|
|
||||||
opcode = OpCodes.Stloc_2;
|
|
||||||
else if (index == 3)
|
|
||||||
opcode = OpCodes.Stloc_3;
|
|
||||||
else if (byte.MinValue <= index && index <= byte.MaxValue) {
|
|
||||||
opcode = OpCodes.Stloc_S;
|
|
||||||
operand = local;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
opcode = OpCodes.Stloc;
|
|
||||||
operand = local;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Code.Ldloca:
|
|
||||||
case Code.Ldloca_S:
|
|
||||||
if (byte.MinValue <= index && index <= byte.MaxValue) {
|
|
||||||
opcode = OpCodes.Ldloca_S;
|
|
||||||
operand = local;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
opcode = OpCodes.Ldloca;
|
|
||||||
operand = local;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new ApplicationException("Invalid opcode");
|
|
||||||
}
|
|
||||||
|
|
||||||
instr.OpCode = opcode;
|
|
||||||
instr.Operand = operand;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FixArgs(IList<Instruction> instrs, MethodDef method) {
|
|
||||||
foreach (var instr in instrs) {
|
|
||||||
var op = instr.Operand as ArgOperand;
|
|
||||||
if (op == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
UpdateArgInstruction(instr, method.Parameters[op.arg], op.arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UpdateArgInstruction(Instruction instr, Parameter arg, int index) {
|
|
||||||
switch (instr.OpCode.Code) {
|
|
||||||
case Code.Ldarg:
|
|
||||||
case Code.Ldarg_S:
|
|
||||||
if (index == 0) {
|
|
||||||
instr.OpCode = OpCodes.Ldarg_0;
|
|
||||||
instr.Operand = null;
|
|
||||||
}
|
|
||||||
else if (index == 1) {
|
|
||||||
instr.OpCode = OpCodes.Ldarg_1;
|
|
||||||
instr.Operand = null;
|
|
||||||
}
|
|
||||||
else if (index == 2) {
|
|
||||||
instr.OpCode = OpCodes.Ldarg_2;
|
|
||||||
instr.Operand = null;
|
|
||||||
}
|
|
||||||
else if (index == 3) {
|
|
||||||
instr.OpCode = OpCodes.Ldarg_3;
|
|
||||||
instr.Operand = null;
|
|
||||||
}
|
|
||||||
else if (byte.MinValue <= index && index <= byte.MaxValue) {
|
|
||||||
instr.OpCode = OpCodes.Ldarg_S;
|
|
||||||
instr.Operand = arg;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
instr.OpCode = OpCodes.Ldarg;
|
|
||||||
instr.Operand = arg;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Code.Starg:
|
|
||||||
case Code.Starg_S:
|
|
||||||
if (byte.MinValue <= index && index <= byte.MaxValue) {
|
|
||||||
instr.OpCode = OpCodes.Starg_S;
|
|
||||||
instr.Operand = arg;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
instr.OpCode = OpCodes.Starg;
|
|
||||||
instr.Operand = arg;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case Code.Ldarga:
|
|
||||||
case Code.Ldarga_S:
|
|
||||||
if (byte.MinValue <= index && index <= byte.MaxValue) {
|
|
||||||
instr.OpCode = OpCodes.Ldarga_S;
|
|
||||||
instr.Operand = arg;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
instr.OpCode = OpCodes.Ldarga;
|
|
||||||
instr.Operand = arg;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new ApplicationException("Invalid opcode");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
|
||||||
var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions));
|
var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions));
|
||||||
var instrs = new List<Instruction>();
|
var instrs = new List<Instruction>();
|
||||||
uint offset = 0;
|
uint offset = 0;
|
||||||
while (reader.BaseStream.Position < reader.BaseStream.Length) {
|
while (reader.BaseStream.Position < reader.BaseStream.Length) {
|
||||||
int vmOpCode = reader.ReadUInt16();
|
int vmOpCode = reader.ReadUInt16();
|
||||||
var instr = opCodeDetector.Handlers[vmOpCode].Read(reader);
|
var instr = opCodeDetector.Handlers[vmOpCode].Read(reader, module);
|
||||||
instr.Offset = offset;
|
instr.Offset = offset;
|
||||||
offset += (uint)GetInstructionSize(instr);
|
offset += (uint)GetInstructionSize(instr);
|
||||||
|
SetCilToVmIndex(instr, instrs.Count);
|
||||||
|
SetVmIndexToCil(instr, instrs.Count);
|
||||||
instrs.Add(instr);
|
instrs.Add(instr);
|
||||||
}
|
}
|
||||||
return instrs;
|
return instrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int GetInstructionSize(Instruction instr) {
|
|
||||||
var opcode = instr.OpCode;
|
|
||||||
if (opcode == null)
|
|
||||||
return 5; // Load store/field
|
|
||||||
var op = instr.Operand as SwitchTargetDisplOperand;
|
|
||||||
if (op == null)
|
|
||||||
return instr.GetSize();
|
|
||||||
return instr.OpCode.Size + (op.targetDisplacements.Length + 1) * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Local> ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
|
||||||
var locals = new List<Local>();
|
|
||||||
var reader = new BinaryReader(new MemoryStream(csvmMethod.Locals));
|
|
||||||
|
|
||||||
if (csvmMethod.Locals.Length == 0)
|
|
||||||
return locals;
|
|
||||||
|
|
||||||
// v6.0.0.5 sometimes duplicates the last two locals so only check for a negative value.
|
|
||||||
int numLocals = reader.ReadInt32();
|
|
||||||
if (numLocals < 0)
|
|
||||||
throw new ApplicationException("Invalid number of locals");
|
|
||||||
|
|
||||||
for (int i = 0; i < numLocals; i++)
|
|
||||||
locals.Add(new Local(ReadTypeRef(reader)));
|
|
||||||
|
|
||||||
return locals;
|
|
||||||
}
|
|
||||||
|
|
||||||
TypeSig ReadTypeRef(BinaryReader reader) {
|
|
||||||
var etype = (ElementType)reader.ReadInt32();
|
|
||||||
switch (etype) {
|
|
||||||
case ElementType.Void: return module.CorLibTypes.Void;
|
|
||||||
case ElementType.Boolean: return module.CorLibTypes.Boolean;
|
|
||||||
case ElementType.Char: return module.CorLibTypes.Char;
|
|
||||||
case ElementType.I1: return module.CorLibTypes.SByte;
|
|
||||||
case ElementType.U1: return module.CorLibTypes.Byte;
|
|
||||||
case ElementType.I2: return module.CorLibTypes.Int16;
|
|
||||||
case ElementType.U2: return module.CorLibTypes.UInt16;
|
|
||||||
case ElementType.I4: return module.CorLibTypes.Int32;
|
|
||||||
case ElementType.U4: return module.CorLibTypes.UInt32;
|
|
||||||
case ElementType.I8: return module.CorLibTypes.Int64;
|
|
||||||
case ElementType.U8: return module.CorLibTypes.UInt64;
|
|
||||||
case ElementType.R4: return module.CorLibTypes.Single;
|
|
||||||
case ElementType.R8: return module.CorLibTypes.Double;
|
|
||||||
case ElementType.String: return module.CorLibTypes.String;
|
|
||||||
case ElementType.TypedByRef: return module.CorLibTypes.TypedReference;
|
|
||||||
case ElementType.I: return module.CorLibTypes.IntPtr;
|
|
||||||
case ElementType.U: return module.CorLibTypes.UIntPtr;
|
|
||||||
case ElementType.Object: return module.CorLibTypes.Object;
|
|
||||||
|
|
||||||
case ElementType.ValueType:
|
|
||||||
case ElementType.Var:
|
|
||||||
case ElementType.MVar:
|
|
||||||
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
|
|
||||||
|
|
||||||
case ElementType.GenericInst:
|
|
||||||
etype = (ElementType)reader.ReadInt32();
|
|
||||||
if (etype == ElementType.ValueType)
|
|
||||||
return (module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef).ToTypeSig();
|
|
||||||
// ElementType.Class
|
|
||||||
return module.CorLibTypes.Object;
|
|
||||||
|
|
||||||
case ElementType.Ptr:
|
|
||||||
case ElementType.Class:
|
|
||||||
case ElementType.Array:
|
|
||||||
case ElementType.FnPtr:
|
|
||||||
case ElementType.SZArray:
|
|
||||||
case ElementType.ByRef:
|
|
||||||
case ElementType.CModReqd:
|
|
||||||
case ElementType.CModOpt:
|
|
||||||
case ElementType.Internal:
|
|
||||||
case ElementType.Sentinel:
|
|
||||||
case ElementType.Pinned:
|
|
||||||
default:
|
|
||||||
return module.CorLibTypes.Object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<ExceptionHandler> ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod, List<Instruction> cilInstructions) {
|
|
||||||
var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions));
|
|
||||||
var ehs = new List<ExceptionHandler>();
|
|
||||||
|
|
||||||
if (reader.BaseStream.Length == 0)
|
|
||||||
return ehs;
|
|
||||||
|
|
||||||
int numExceptions = reader.ReadInt32();
|
|
||||||
if (numExceptions < 0)
|
|
||||||
throw new ApplicationException("Invalid number of exception handlers");
|
|
||||||
|
|
||||||
for (int i = 0; i < numExceptions; i++) {
|
|
||||||
var eh = new ExceptionHandler((ExceptionHandlerType)reader.ReadInt32());
|
|
||||||
eh.TryStart = GetInstruction(cilInstructions, reader.ReadInt32());
|
|
||||||
eh.TryEnd = GetInstructionEnd(cilInstructions, reader.ReadInt32());
|
|
||||||
eh.HandlerStart = GetInstruction(cilInstructions, reader.ReadInt32());
|
|
||||||
eh.HandlerEnd = GetInstructionEnd(cilInstructions, reader.ReadInt32());
|
|
||||||
if (eh.HandlerType == ExceptionHandlerType.Catch)
|
|
||||||
eh.CatchType = module.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
|
||||||
else if (eh.HandlerType == ExceptionHandlerType.Filter)
|
|
||||||
eh.FilterStart = GetInstruction(cilInstructions, reader.ReadInt32());
|
|
||||||
|
|
||||||
ehs.Add(eh);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ehs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Instruction GetInstruction(IList<Instruction> instrs, int index) {
|
|
||||||
return instrs[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
static Instruction GetInstructionEnd(IList<Instruction> instrs, int index) {
|
|
||||||
index++;
|
|
||||||
if (index == instrs.Count)
|
|
||||||
return null;
|
|
||||||
return instrs[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
static Instruction GetInstruction(IList<Instruction> instrs, Instruction source, int displ) {
|
|
||||||
int sourceIndex = instrs.IndexOf(source);
|
|
||||||
if (sourceIndex < 0)
|
|
||||||
throw new ApplicationException("Could not find source instruction");
|
|
||||||
return instrs[sourceIndex + displ];
|
|
||||||
}
|
|
||||||
|
|
||||||
void FixInstructionOperands(IList<Instruction> instrs) {
|
|
||||||
foreach (var instr in instrs) {
|
|
||||||
var op = instr.Operand as IVmOperand;
|
|
||||||
if (op != null)
|
|
||||||
instr.Operand = FixOperand(instrs, instr, op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object FixOperand(IList<Instruction> instrs, Instruction instr, IVmOperand vmOperand) {
|
|
||||||
if (vmOperand is TokenOperand)
|
|
||||||
return GetMemberRef(((TokenOperand)vmOperand).token);
|
|
||||||
|
|
||||||
if (vmOperand is TargetDisplOperand)
|
|
||||||
return GetInstruction(instrs, instr, ((TargetDisplOperand)vmOperand).displacement);
|
|
||||||
|
|
||||||
if (vmOperand is SwitchTargetDisplOperand) {
|
|
||||||
var targetDispls = ((SwitchTargetDisplOperand)vmOperand).targetDisplacements;
|
|
||||||
Instruction[] targets = new Instruction[targetDispls.Length];
|
|
||||||
for (int i = 0; i < targets.Length; i++)
|
|
||||||
targets[i] = GetInstruction(instrs, instr, targetDispls[i]);
|
|
||||||
return targets;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vmOperand is ArgOperand || vmOperand is LocalOperand)
|
|
||||||
return vmOperand;
|
|
||||||
|
|
||||||
if (vmOperand is LoadFieldOperand)
|
|
||||||
return FixLoadStoreFieldInstruction(instr, ((LoadFieldOperand)vmOperand).token, OpCodes.Ldsfld, OpCodes.Ldfld);
|
|
||||||
|
|
||||||
if (vmOperand is LoadFieldAddressOperand)
|
|
||||||
return FixLoadStoreFieldInstruction(instr, ((LoadFieldAddressOperand)vmOperand).token, OpCodes.Ldsflda, OpCodes.Ldflda);
|
|
||||||
|
|
||||||
if (vmOperand is StoreFieldOperand)
|
|
||||||
return FixLoadStoreFieldInstruction(instr, ((StoreFieldOperand)vmOperand).token, OpCodes.Stsfld, OpCodes.Stfld);
|
|
||||||
|
|
||||||
throw new ApplicationException(string.Format("Unknown operand type: {0}", vmOperand.GetType()));
|
|
||||||
}
|
|
||||||
|
|
||||||
IField FixLoadStoreFieldInstruction(Instruction instr, int token, OpCode staticInstr, OpCode instanceInstr) {
|
|
||||||
var fieldRef = module.ResolveToken(token) as IField;
|
|
||||||
var field = deobfuscatorContext.ResolveField(fieldRef);
|
|
||||||
bool isStatic;
|
|
||||||
if (field == null) {
|
|
||||||
Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", token);
|
|
||||||
isStatic = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
isStatic = field.IsStatic;
|
|
||||||
instr.OpCode = isStatic ? staticInstr : instanceInstr;
|
|
||||||
return fieldRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
ITokenOperand GetMemberRef(int token) {
|
|
||||||
var memberRef = module.ResolveToken(token) as ITokenOperand;
|
|
||||||
if (memberRef == null)
|
|
||||||
throw new ApplicationException(string.Format("Could not find member ref: {0:X8}", token));
|
|
||||||
return memberRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void RestoreConstrainedPrefix(MethodDef method) {
|
|
||||||
if (method == null || method.Body == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var instrs = method.Body.Instructions;
|
|
||||||
for (int i = 0; i < instrs.Count; i++) {
|
|
||||||
var instr = instrs[i];
|
|
||||||
if (instr.OpCode.Code != Code.Callvirt)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var calledMethod = instr.Operand as IMethod;
|
|
||||||
if (calledMethod == null)
|
|
||||||
continue;
|
|
||||||
var sig = calledMethod.MethodSig;
|
|
||||||
if (sig == null || !sig.HasThis)
|
|
||||||
continue;
|
|
||||||
var thisType = MethodStack.GetLoadedType(method, instrs, i, sig.Params.Count) as ByRefSig;
|
|
||||||
if (thisType == null)
|
|
||||||
continue;
|
|
||||||
if (HasPrefix(instrs, i, Code.Constrained))
|
|
||||||
continue;
|
|
||||||
instrs.Insert(i, OpCodes.Constrained.ToInstruction(thisType.Next.ToTypeDefOrRef()));
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool HasPrefix(IList<Instruction> instrs, int index, Code prefix) {
|
|
||||||
index--;
|
|
||||||
for (; index >= 0; index--) {
|
|
||||||
var instr = instrs[index];
|
|
||||||
if (instr.OpCode.OpCodeType != OpCodeType.Prefix)
|
|
||||||
break;
|
|
||||||
if (instr.OpCode.Code == prefix)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public OpCodeHandlerSigInfo OpCodeHandlerSigInfo { get; set; }
|
public OpCodeHandlerSigInfo OpCodeHandlerSigInfo { get; set; }
|
||||||
public Predicate<UnknownHandlerInfo> Check { get; set; }
|
public Predicate<UnknownHandlerInfo> Check { get; set; }
|
||||||
public Func<BinaryReader, Instruction> Read { get; set; }
|
public Func<BinaryReader, IInstructionOperandResolver, Instruction> Read { get; set; }
|
||||||
|
|
||||||
public bool Detect(UnknownHandlerInfo info) {
|
public bool Detect(UnknownHandlerInfo info) {
|
||||||
var sigInfo = OpCodeHandlerSigInfo;
|
var sigInfo = OpCodeHandlerSigInfo;
|
||||||
|
@ -68,7 +68,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
static partial class OpCodeHandlers {
|
static partial class OpCodeHandlers {
|
||||||
static Instruction arithmetic_read(BinaryReader reader) {
|
static Instruction arithmetic_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
switch (reader.ReadByte()) {
|
switch (reader.ReadByte()) {
|
||||||
case 0: return OpCodes.Add.ToInstruction();
|
case 0: return OpCodes.Add.ToInstruction();
|
||||||
case 1: return OpCodes.Add_Ovf.ToInstruction();
|
case 1: return OpCodes.Add_Ovf.ToInstruction();
|
||||||
|
@ -91,25 +91,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Type System.Reflection.Module::ResolveType(System.Int32)");
|
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Type System.Reflection.Module::ResolveType(System.Int32)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction newarr_read(BinaryReader reader) {
|
static Instruction newarr_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Newarr, resolver.ResolveToken(reader.ReadUInt32()));
|
||||||
OpCode = OpCodes.Newarr,
|
|
||||||
Operand = new TokenOperand(reader.ReadInt32()),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction box_read(BinaryReader reader) {
|
static Instruction box_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
var instr = new Instruction();
|
var instr = new Instruction();
|
||||||
switch (reader.ReadByte()) {
|
switch (reader.ReadByte()) {
|
||||||
case 0: instr.OpCode = OpCodes.Box; break;
|
case 0: instr.OpCode = OpCodes.Box; break;
|
||||||
case 1: instr.OpCode = OpCodes.Unbox_Any; break;
|
case 1: instr.OpCode = OpCodes.Unbox_Any; break;
|
||||||
default: throw new ApplicationException("Invalid opcode");
|
default: throw new ApplicationException("Invalid opcode");
|
||||||
}
|
}
|
||||||
instr.Operand = new TokenOperand(reader.ReadInt32());
|
instr.Operand = resolver.ResolveToken(reader.ReadUInt32());
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction call_read(BinaryReader reader) {
|
static Instruction call_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
var instr = new Instruction();
|
var instr = new Instruction();
|
||||||
switch (reader.ReadByte()) {
|
switch (reader.ReadByte()) {
|
||||||
case 0: instr.OpCode = OpCodes.Newobj; break;
|
case 0: instr.OpCode = OpCodes.Newobj; break;
|
||||||
|
@ -117,22 +114,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
case 2: instr.OpCode = OpCodes.Callvirt; break;
|
case 2: instr.OpCode = OpCodes.Callvirt; break;
|
||||||
default: throw new ApplicationException("Invalid opcode");
|
default: throw new ApplicationException("Invalid opcode");
|
||||||
}
|
}
|
||||||
instr.Operand = new TokenOperand(reader.ReadInt32());
|
instr.Operand = resolver.ResolveToken(reader.ReadUInt32());
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction cast_read(BinaryReader reader) {
|
static Instruction cast_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
var instr = new Instruction();
|
var instr = new Instruction();
|
||||||
switch (reader.ReadByte()) {
|
switch (reader.ReadByte()) {
|
||||||
case 0: instr.OpCode = OpCodes.Castclass; break;
|
case 0: instr.OpCode = OpCodes.Castclass; break;
|
||||||
case 1: instr.OpCode = OpCodes.Isinst; break;
|
case 1: instr.OpCode = OpCodes.Isinst; break;
|
||||||
default: throw new ApplicationException("Invalid opcode");
|
default: throw new ApplicationException("Invalid opcode");
|
||||||
}
|
}
|
||||||
instr.Operand = new TokenOperand(reader.ReadInt32());
|
instr.Operand = resolver.ResolveToken(reader.ReadUInt32());
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction compare_read(BinaryReader reader) {
|
static Instruction compare_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
int type = reader.ReadByte();
|
int type = reader.ReadByte();
|
||||||
Instruction instr = new Instruction();
|
Instruction instr = new Instruction();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
@ -205,7 +202,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
new InstructionInfo1 { Type = 11, Second = true, Third = true, OpCode = OpCodes.Conv_Ovf_U_Un },
|
new InstructionInfo1 { Type = 11, Second = true, Third = true, OpCode = OpCodes.Conv_Ovf_U_Un },
|
||||||
new InstructionInfo1 { Type = 12, Second = true, Third = true, OpCode = OpCodes.Conv_R_Un },
|
new InstructionInfo1 { Type = 12, Second = true, Third = true, OpCode = OpCodes.Conv_R_Un },
|
||||||
};
|
};
|
||||||
static Instruction convert_read(BinaryReader reader) {
|
static Instruction convert_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
byte type = reader.ReadByte();
|
byte type = reader.ReadByte();
|
||||||
bool second = reader.ReadBoolean();
|
bool second = reader.ReadBoolean();
|
||||||
bool third = reader.ReadBoolean();
|
bool third = reader.ReadBoolean();
|
||||||
|
@ -215,7 +212,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
if (type != info.Type || info.Second != second || info.Third != third)
|
if (type != info.Type || info.Second != second || info.Third != third)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
instr = new Instruction { OpCode = info.OpCode };
|
instr = new Instruction(info.OpCode);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (instr == null)
|
if (instr == null)
|
||||||
|
@ -224,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction dup_read(BinaryReader reader) {
|
static Instruction dup_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
switch (reader.ReadByte()) {
|
switch (reader.ReadByte()) {
|
||||||
case 0: return OpCodes.Dup.ToInstruction();
|
case 0: return OpCodes.Dup.ToInstruction();
|
||||||
case 1: return OpCodes.Pop.ToInstruction();
|
case 1: return OpCodes.Pop.ToInstruction();
|
||||||
|
@ -262,7 +259,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
new InstructionInfo2 { First = true, Second = true, Value = 28, OpCode = OpCodes.Ldelem_Ref },
|
new InstructionInfo2 { First = true, Second = true, Value = 28, OpCode = OpCodes.Ldelem_Ref },
|
||||||
new InstructionInfo2 { First = true, Second = false, Value = 0, OpCode = OpCodes.Ldelem },
|
new InstructionInfo2 { First = true, Second = false, Value = 0, OpCode = OpCodes.Ldelem },
|
||||||
};
|
};
|
||||||
static Instruction ldelem_read(BinaryReader reader) {
|
static Instruction ldelem_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
Instruction instr = null;
|
Instruction instr = null;
|
||||||
bool first = reader.ReadBoolean();
|
bool first = reader.ReadBoolean();
|
||||||
bool second = reader.ReadBoolean();
|
bool second = reader.ReadBoolean();
|
||||||
|
@ -274,9 +271,9 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (second)
|
if (second)
|
||||||
instr = new Instruction { OpCode = info.OpCode };
|
instr = new Instruction(info.OpCode);
|
||||||
else
|
else
|
||||||
instr = new Instruction { OpCode = info.OpCode, Operand = new TokenOperand(value) };
|
instr = new Instruction(info.OpCode, resolver.ResolveToken((uint)value));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (instr == null)
|
if (instr == null)
|
||||||
|
@ -289,29 +286,26 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)");
|
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction endfinally_read(BinaryReader reader) {
|
static Instruction endfinally_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return OpCodes.Endfinally.ToInstruction();
|
return OpCodes.Endfinally.ToInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldfld_read(BinaryReader reader) {
|
static Instruction ldfld_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
var instr = new Instruction();
|
byte b = reader.ReadByte();
|
||||||
switch (reader.ReadByte()) {
|
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
|
||||||
case 0: instr.Operand = new LoadFieldOperand(reader.ReadInt32()); break;
|
switch (b) {
|
||||||
case 1: instr.Operand = new LoadFieldAddressOperand(reader.ReadInt32()); break;
|
case 0: return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsfld, OpCodes.Ldfld, field));
|
||||||
case 2: instr.Operand = new StoreFieldOperand(reader.ReadInt32()); break;
|
case 1: return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsflda, OpCodes.Ldflda, field));
|
||||||
|
case 2: return new Instruction(null, new FieldInstructionOperand(OpCodes.Stsfld, OpCodes.Stfld, field));
|
||||||
default: throw new ApplicationException("Invalid opcode");
|
default: throw new ApplicationException("Invalid opcode");
|
||||||
}
|
}
|
||||||
return instr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction initobj_read(BinaryReader reader) {
|
static Instruction initobj_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Initobj, resolver.ResolveToken(reader.ReadUInt32()));
|
||||||
OpCode = OpCodes.Initobj,
|
|
||||||
Operand = new TokenOperand(reader.ReadInt32()),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldloc_read(BinaryReader reader) {
|
static Instruction ldloc_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
bool isLdarg = reader.ReadBoolean();
|
bool isLdarg = reader.ReadBoolean();
|
||||||
ushort index = reader.ReadUInt16();
|
ushort index = reader.ReadUInt16();
|
||||||
|
|
||||||
|
@ -328,7 +322,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldloca_read(BinaryReader reader) {
|
static Instruction ldloca_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
Instruction instr = new Instruction();
|
Instruction instr = new Instruction();
|
||||||
if (reader.ReadBoolean()) {
|
if (reader.ReadBoolean()) {
|
||||||
instr.OpCode = OpCodes.Ldarga;
|
instr.OpCode = OpCodes.Ldarga;
|
||||||
|
@ -342,25 +336,19 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldelema_read(BinaryReader reader) {
|
static Instruction ldelema_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Ldelema, null);
|
||||||
OpCode = OpCodes.Ldelema,
|
|
||||||
Operand = null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldlen_read(BinaryReader reader) {
|
static Instruction ldlen_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return OpCodes.Ldlen.ToInstruction();
|
return OpCodes.Ldlen.ToInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldobj_read(BinaryReader reader) {
|
static Instruction ldobj_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Ldobj, null);
|
||||||
OpCode = OpCodes.Ldobj,
|
|
||||||
Operand = null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldstr_read(BinaryReader reader) {
|
static Instruction ldstr_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return OpCodes.Ldstr.ToInstruction(reader.ReadString());
|
return OpCodes.Ldstr.ToInstruction(reader.ReadString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,11 +356,8 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)");
|
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldtoken_read(BinaryReader reader) {
|
static Instruction ldtoken_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Ldtoken, resolver.ResolveToken(reader.ReadUInt32()));
|
||||||
OpCode = OpCodes.Ldtoken,
|
|
||||||
Operand = new TokenOperand(reader.ReadInt32()),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool leave_check(UnknownHandlerInfo info) {
|
static bool leave_check(UnknownHandlerInfo info) {
|
||||||
|
@ -381,15 +366,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
!DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)");
|
!DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction leave_read(BinaryReader reader) {
|
static Instruction leave_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
int displacement = reader.ReadInt32();
|
int displacement = reader.ReadInt32();
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Leave, new TargetDisplOperand(displacement));
|
||||||
OpCode = OpCodes.Leave,
|
|
||||||
Operand = new TargetDisplOperand(displacement),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldc_read(BinaryReader reader) {
|
static Instruction ldc_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
switch ((ElementType)reader.ReadByte()) {
|
switch ((ElementType)reader.ReadByte()) {
|
||||||
case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32());
|
case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32());
|
||||||
case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64());
|
case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64());
|
||||||
|
@ -400,29 +382,24 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ldftn_read(BinaryReader reader) {
|
static Instruction ldftn_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
byte code = reader.ReadByte();
|
byte code = reader.ReadByte();
|
||||||
int token = reader.ReadInt32();
|
uint token = reader.ReadUInt32();
|
||||||
|
|
||||||
Instruction instr;
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 0:
|
case 0:
|
||||||
instr = new Instruction { OpCode = OpCodes.Ldftn, Operand = new TokenOperand(token) };
|
return new Instruction(OpCodes.Ldftn, resolver.ResolveToken(token));
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
reader.ReadInt32(); // token of newobj .ctor
|
reader.ReadInt32(); // token of newobj .ctor
|
||||||
instr = new Instruction { OpCode = OpCodes.Ldvirtftn, Operand = new TokenOperand(token) };
|
return new Instruction(OpCodes.Ldvirtftn, resolver.ResolveToken(token));
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ApplicationException("Invalid opcode");
|
throw new ApplicationException("Invalid opcode");
|
||||||
}
|
}
|
||||||
|
|
||||||
return instr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction logical_read(BinaryReader reader) {
|
static Instruction logical_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
switch (reader.ReadByte()) {
|
switch (reader.ReadByte()) {
|
||||||
case 0: return OpCodes.And.ToInstruction();
|
case 0: return OpCodes.And.ToInstruction();
|
||||||
case 1: return OpCodes.Or.ToInstruction();
|
case 1: return OpCodes.Or.ToInstruction();
|
||||||
|
@ -448,7 +425,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction nop_read(BinaryReader reader) {
|
static Instruction nop_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return OpCodes.Nop.ToInstruction();
|
return OpCodes.Nop.ToInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +433,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodBase System.Reflection.Module::ResolveMethod(System.Int32)");
|
return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodBase System.Reflection.Module::ResolveMethod(System.Int32)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction ret_read(BinaryReader reader) {
|
static Instruction ret_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
reader.ReadInt32(); // token of current method
|
reader.ReadInt32(); // token of current method
|
||||||
return OpCodes.Ret.ToInstruction();
|
return OpCodes.Ret.ToInstruction();
|
||||||
}
|
}
|
||||||
|
@ -465,11 +442,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return info.ExecuteMethod.Body.Variables.Count == 0;
|
return info.ExecuteMethod.Body.Variables.Count == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction rethrow_read(BinaryReader reader) {
|
static Instruction rethrow_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return OpCodes.Rethrow.ToInstruction();
|
return OpCodes.Rethrow.ToInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction stloc_read(BinaryReader reader) {
|
static Instruction stloc_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
bool isStarg = reader.ReadBoolean();
|
bool isStarg = reader.ReadBoolean();
|
||||||
ushort index = reader.ReadUInt16();
|
ushort index = reader.ReadUInt16();
|
||||||
|
|
||||||
|
@ -487,33 +464,27 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
return instr;
|
return instr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction stobj_read(BinaryReader reader) {
|
static Instruction stobj_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Stobj, null);
|
||||||
OpCode = OpCodes.Stobj,
|
|
||||||
Operand = null,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction switch_read(BinaryReader reader) {
|
static Instruction switch_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
int numTargets = reader.ReadInt32();
|
int numTargets = reader.ReadInt32();
|
||||||
int[] targetDispls = new int[numTargets];
|
int[] targetDispls = new int[numTargets];
|
||||||
for (int i = 0; i < targetDispls.Length; i++)
|
for (int i = 0; i < targetDispls.Length; i++)
|
||||||
targetDispls[i] = reader.ReadInt32();
|
targetDispls[i] = reader.ReadInt32();
|
||||||
return new Instruction {
|
return new Instruction(OpCodes.Switch, new SwitchTargetDisplOperand(targetDispls));
|
||||||
OpCode = OpCodes.Switch,
|
|
||||||
Operand = new SwitchTargetDisplOperand(targetDispls),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool throw_check(UnknownHandlerInfo info) {
|
static bool throw_check(UnknownHandlerInfo info) {
|
||||||
return !DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)");
|
return !DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MethodInfo System.Type::GetMethod(System.String,System.Reflection.BindingFlags)");
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction throw_read(BinaryReader reader) {
|
static Instruction throw_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
return OpCodes.Throw.ToInstruction();
|
return OpCodes.Throw.ToInstruction();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Instruction neg_read(BinaryReader reader) {
|
static Instruction neg_read(BinaryReader reader, IInstructionOperandResolver resolver) {
|
||||||
switch (reader.ReadByte()) {
|
switch (reader.ReadByte()) {
|
||||||
case 0: return OpCodes.Neg.ToInstruction();
|
case 0: return OpCodes.Neg.ToInstruction();
|
||||||
case 1: return OpCodes.Not.ToInstruction();
|
case 1: return OpCodes.Not.ToInstruction();
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
static partial class OpCodeHandlers {
|
static partial class OpCodeHandlers {
|
||||||
public static readonly OpCodeHandler[][] opcodeHandlers = new OpCodeHandler[][] {
|
public static readonly OpCodeHandler[][] Handlers = new OpCodeHandler[][] {
|
||||||
new OpCodeHandler[] {
|
new OpCodeHandler[] {
|
||||||
new OpCodeHandler {
|
new OpCodeHandler {
|
||||||
Name = "arithmetic",
|
Name = "arithmetic",
|
||||||
|
|
|
@ -214,7 +214,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 {
|
||||||
opCodeHandlers = new List<OpCodeHandler>();
|
opCodeHandlers = new List<OpCodeHandler>();
|
||||||
var detected = new List<OpCodeHandler>();
|
var detected = new List<OpCodeHandler>();
|
||||||
|
|
||||||
foreach (var handlersList in OpCodeHandlers.opcodeHandlers) {
|
foreach (var handlersList in OpCodeHandlers.Handlers) {
|
||||||
opCodeHandlers.Clear();
|
opCodeHandlers.Clear();
|
||||||
|
|
||||||
foreach (var handlerType in handlerTypes) {
|
foreach (var handlerType in handlerTypes) {
|
||||||
|
|
BIN
de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin
Normal file
BIN
de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin
Normal file
Binary file not shown.
BIN
de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin
Normal file
BIN
de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin
Normal file
Binary file not shown.
BIN
de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin
Normal file
BIN
de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin
Normal file
Binary file not shown.
|
@ -0,0 +1,335 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class CompositeHandlerDetector {
|
||||||
|
readonly List<OpCodeHandler> handlers;
|
||||||
|
|
||||||
|
public CompositeHandlerDetector(IList<OpCodeHandler> handlers) {
|
||||||
|
this.handlers = new List<OpCodeHandler>(handlers.Count);
|
||||||
|
OpCodeHandler nop = null;
|
||||||
|
foreach (var handler in handlers) {
|
||||||
|
if (nop == null && handler.OpCodeHandlerInfo.TypeCode == HandlerTypeCode.Nop)
|
||||||
|
nop = handler;
|
||||||
|
else
|
||||||
|
this.handlers.Add(handler);
|
||||||
|
}
|
||||||
|
if (nop != null)
|
||||||
|
this.handlers.Add(nop);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MatchState {
|
||||||
|
public HandlerState OtherState;
|
||||||
|
public HandlerState CompositeState;
|
||||||
|
|
||||||
|
public MatchState(HandlerState OtherState, HandlerState CompositeState) {
|
||||||
|
this.OtherState = OtherState;
|
||||||
|
this.CompositeState = CompositeState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HandlerState {
|
||||||
|
public readonly HandlerMethod HandlerMethod;
|
||||||
|
public readonly IList<Block> Blocks;
|
||||||
|
public readonly int BlockIndex;
|
||||||
|
public int InstructionIndex;
|
||||||
|
|
||||||
|
public HandlerState(HandlerMethod handlerMethod, int blockIndex, int instructionIndex) {
|
||||||
|
this.HandlerMethod = handlerMethod;
|
||||||
|
this.Blocks = handlerMethod.Blocks.MethodBlocks.GetAllBlocks();
|
||||||
|
this.BlockIndex = blockIndex;
|
||||||
|
this.InstructionIndex = instructionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandlerState(HandlerMethod handlerMethod, IList<Block> blocks, int blockIndex, int instructionIndex) {
|
||||||
|
this.HandlerMethod = handlerMethod;
|
||||||
|
this.Blocks = blocks;
|
||||||
|
this.BlockIndex = blockIndex;
|
||||||
|
this.InstructionIndex = instructionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandlerState Clone() {
|
||||||
|
return new HandlerState(HandlerMethod, Blocks, BlockIndex, InstructionIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FindHandlerState {
|
||||||
|
public HandlerState CompositeState;
|
||||||
|
public readonly Dictionary<int, bool> VisitedCompositeBlocks;
|
||||||
|
public bool Done;
|
||||||
|
|
||||||
|
public FindHandlerState(HandlerState compositeState) {
|
||||||
|
this.CompositeState = compositeState;
|
||||||
|
this.VisitedCompositeBlocks = new Dictionary<int, bool>();
|
||||||
|
this.Done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FindHandlerState(HandlerState compositeState, Dictionary<int, bool> visitedCompositeBlocks, bool done) {
|
||||||
|
this.CompositeState = compositeState;
|
||||||
|
this.VisitedCompositeBlocks = new Dictionary<int, bool>(visitedCompositeBlocks);
|
||||||
|
this.Done = done;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FindHandlerState Clone() {
|
||||||
|
return new FindHandlerState(CompositeState.Clone(), VisitedCompositeBlocks, Done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FindHandlers(CompositeOpCodeHandler composite) {
|
||||||
|
composite.OpCodeHandlerInfos.Clear();
|
||||||
|
var compositeExecState = new FindHandlerState(new HandlerState(composite.ExecMethod, 0, 0));
|
||||||
|
while (!compositeExecState.Done) {
|
||||||
|
var handler = FindHandlerMethod(ref compositeExecState);
|
||||||
|
if (handler == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
composite.OpCodeHandlerInfos.Add(handler.OpCodeHandlerInfo);
|
||||||
|
}
|
||||||
|
return composite.OpCodeHandlerInfos.Count != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCodeHandler FindHandlerMethod(ref FindHandlerState findExecState) {
|
||||||
|
foreach (var handler in handlers) {
|
||||||
|
FindHandlerState findExecStateNew = findExecState.Clone();
|
||||||
|
if (!Matches(handler.ExecMethod, ref findExecStateNew))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
findExecState = findExecStateNew;
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stack<MatchState> stack = new Stack<MatchState>();
|
||||||
|
bool Matches(HandlerMethod handler, ref FindHandlerState findState) {
|
||||||
|
HandlerState? nextState = null;
|
||||||
|
stack.Clear();
|
||||||
|
stack.Push(new MatchState(new HandlerState(handler, 0, 0), findState.CompositeState));
|
||||||
|
while (stack.Count > 0) {
|
||||||
|
var matchState = stack.Pop();
|
||||||
|
|
||||||
|
if (matchState.CompositeState.InstructionIndex == 0) {
|
||||||
|
if (findState.VisitedCompositeBlocks.ContainsKey(matchState.CompositeState.BlockIndex))
|
||||||
|
continue;
|
||||||
|
findState.VisitedCompositeBlocks[matchState.CompositeState.BlockIndex] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!findState.VisitedCompositeBlocks.ContainsKey(matchState.CompositeState.BlockIndex))
|
||||||
|
throw new ApplicationException("Block hasn't been visited");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Compare(ref matchState.OtherState, ref matchState.CompositeState))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var hblock = matchState.OtherState.Blocks[matchState.OtherState.BlockIndex];
|
||||||
|
var hinstrs = hblock.Instructions;
|
||||||
|
int hi = matchState.OtherState.InstructionIndex;
|
||||||
|
var cblock = matchState.CompositeState.Blocks[matchState.CompositeState.BlockIndex];
|
||||||
|
var cinstrs = cblock.Instructions;
|
||||||
|
int ci = matchState.CompositeState.InstructionIndex;
|
||||||
|
if (hi < hinstrs.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ci < cinstrs.Count) {
|
||||||
|
if (hblock.CountTargets() != 0)
|
||||||
|
return false;
|
||||||
|
if (hblock.LastInstr.OpCode.Code == Code.Ret) {
|
||||||
|
if (nextState != null)
|
||||||
|
return false;
|
||||||
|
nextState = matchState.CompositeState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (cblock.CountTargets() != hblock.CountTargets())
|
||||||
|
return false;
|
||||||
|
if (cblock.FallThrough != null || hblock.FallThrough != null) {
|
||||||
|
if (cblock.FallThrough == null || hblock.FallThrough == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var hs = CreateHandlerState(handler, matchState.OtherState.Blocks, hblock.FallThrough);
|
||||||
|
var cs = CreateHandlerState(findState.CompositeState.HandlerMethod, findState.CompositeState.Blocks, cblock.FallThrough);
|
||||||
|
stack.Push(new MatchState(hs, cs));
|
||||||
|
}
|
||||||
|
if (cblock.Targets != null || hblock.Targets != null) {
|
||||||
|
if (cblock.Targets == null || hblock.Targets == null ||
|
||||||
|
cblock.Targets.Count != hblock.Targets.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < cblock.Targets.Count; i++) {
|
||||||
|
var hs = CreateHandlerState(handler, matchState.OtherState.Blocks, hblock.Targets[i]);
|
||||||
|
var cs = CreateHandlerState(findState.CompositeState.HandlerMethod, findState.CompositeState.Blocks, cblock.Targets[i]);
|
||||||
|
stack.Push(new MatchState(hs, cs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextState == null) {
|
||||||
|
findState.Done = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (findState.CompositeState.BlockIndex == nextState.Value.BlockIndex &&
|
||||||
|
findState.CompositeState.InstructionIndex == nextState.Value.InstructionIndex)
|
||||||
|
return false;
|
||||||
|
findState.CompositeState = nextState.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HandlerState CreateHandlerState(HandlerMethod handler, IList<Block> blocks, Block target) {
|
||||||
|
return new HandlerState(handler, blocks.IndexOf(target), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Compare(ref HandlerState handler, ref HandlerState composite) {
|
||||||
|
var hinstrs = handler.Blocks[handler.BlockIndex].Instructions;
|
||||||
|
int hi = handler.InstructionIndex;
|
||||||
|
var cinstrs = composite.Blocks[composite.BlockIndex].Instructions;
|
||||||
|
int ci = composite.InstructionIndex;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (hi >= hinstrs.Count && ci >= cinstrs.Count)
|
||||||
|
break;
|
||||||
|
if (hi >= hinstrs.Count || ci >= cinstrs.Count)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var hinstr = hinstrs[hi++];
|
||||||
|
var cinstr = cinstrs[ci++];
|
||||||
|
if (hinstr.OpCode.Code == Code.Nop ||
|
||||||
|
cinstr.OpCode.Code == Code.Nop) {
|
||||||
|
if (hinstr.OpCode.Code != Code.Nop)
|
||||||
|
hi--;
|
||||||
|
if (cinstr.OpCode.Code != Code.Nop)
|
||||||
|
ci--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hi == hinstrs.Count && hinstr.OpCode.Code == Code.Ret) {
|
||||||
|
if (cinstr.OpCode.Code != Code.Br && cinstr.OpCode.Code != Code.Ret)
|
||||||
|
ci--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hinstr.OpCode.Code != cinstr.OpCode.Code)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (hinstr.OpCode.Code == Code.Ldfld &&
|
||||||
|
hi + 1 < hinstrs.Count && ci + 1 < cinstrs.Count) {
|
||||||
|
var hfield = hinstr.Operand as FieldDef;
|
||||||
|
var cfield = cinstr.Operand as FieldDef;
|
||||||
|
if (hfield != null && cfield != null &&
|
||||||
|
!hfield.IsStatic && !cfield.IsStatic &&
|
||||||
|
hfield.DeclaringType == handler.HandlerMethod.Method.DeclaringType &&
|
||||||
|
cfield.DeclaringType == composite.HandlerMethod.Method.DeclaringType &&
|
||||||
|
SignatureEqualityComparer.Instance.Equals(hfield.Signature, cfield.Signature)) {
|
||||||
|
cinstr = cinstrs[ci++];
|
||||||
|
hinstr = hinstrs[hi++];
|
||||||
|
if (cinstr.OpCode.Code != Code.Ldc_I4 ||
|
||||||
|
hinstr.OpCode.Code != Code.Ldc_I4)
|
||||||
|
return false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CompareOperand(hinstr.OpCode.OperandType, cinstr.Operand, hinstr.Operand))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.InstructionIndex = hi;
|
||||||
|
composite.InstructionIndex = ci;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CompareOperand(OperandType opType, object a, object b) {
|
||||||
|
switch (opType) {
|
||||||
|
case OperandType.ShortInlineI:
|
||||||
|
return (a is byte && b is byte && (byte)a == (byte)b) ||
|
||||||
|
(a is sbyte && b is sbyte && (sbyte)a == (sbyte)b);
|
||||||
|
|
||||||
|
case OperandType.InlineI:
|
||||||
|
return a is int && b is int && (int)a == (int)b;
|
||||||
|
|
||||||
|
case OperandType.InlineI8:
|
||||||
|
return a is long && b is long && (long)a == (long)b;
|
||||||
|
|
||||||
|
case OperandType.ShortInlineR:
|
||||||
|
return a is float && b is float && (float)a == (float)b;
|
||||||
|
|
||||||
|
case OperandType.InlineR:
|
||||||
|
return a is double && b is double && (double)a == (double)b;
|
||||||
|
|
||||||
|
case OperandType.InlineField:
|
||||||
|
return FieldEqualityComparer.CompareDeclaringTypes.Equals(a as IField, b as IField);
|
||||||
|
|
||||||
|
case OperandType.InlineMethod:
|
||||||
|
return MethodEqualityComparer.CompareDeclaringTypes.Equals(a as IMethod, b as IMethod);
|
||||||
|
|
||||||
|
case OperandType.InlineSig:
|
||||||
|
return SignatureEqualityComparer.Instance.Equals(a as MethodSig, b as MethodSig);
|
||||||
|
|
||||||
|
case OperandType.InlineString:
|
||||||
|
return string.Equals(a as string, b as string);
|
||||||
|
|
||||||
|
case OperandType.InlineSwitch:
|
||||||
|
var al = a as IList<Instruction>;
|
||||||
|
var bl = b as IList<Instruction>;
|
||||||
|
return al != null && bl != null && al.Count == bl.Count;
|
||||||
|
|
||||||
|
case OperandType.InlineTok:
|
||||||
|
var fa = a as IField;
|
||||||
|
var fb = b as IField;
|
||||||
|
if (fa != null && fb != null)
|
||||||
|
return FieldEqualityComparer.CompareDeclaringTypes.Equals(fa, fb);
|
||||||
|
var ma = a as IMethod;
|
||||||
|
var mb = b as IMethod;
|
||||||
|
if (ma != null && mb != null)
|
||||||
|
return MethodEqualityComparer.CompareDeclaringTypes.Equals(ma, mb);
|
||||||
|
return TypeEqualityComparer.Instance.Equals(a as ITypeDefOrRef, b as ITypeDefOrRef);
|
||||||
|
|
||||||
|
case OperandType.InlineType:
|
||||||
|
return TypeEqualityComparer.Instance.Equals(a as ITypeDefOrRef, b as ITypeDefOrRef);
|
||||||
|
|
||||||
|
case OperandType.InlineVar:
|
||||||
|
case OperandType.ShortInlineVar:
|
||||||
|
var la = a as Local;
|
||||||
|
var lb = b as Local;
|
||||||
|
if (la != null && lb != null)
|
||||||
|
return true;
|
||||||
|
var pa = a as Parameter;
|
||||||
|
var pb = b as Parameter;
|
||||||
|
return pa != null && pb != null && pa.Index == pb.Index;
|
||||||
|
|
||||||
|
case OperandType.InlineBrTarget:
|
||||||
|
case OperandType.ShortInlineBrTarget:
|
||||||
|
case OperandType.InlineNone:
|
||||||
|
case OperandType.InlinePhi:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 de4dot@gmail.com
|
||||||
|
|
||||||
|
This file is part of de4dot.
|
||||||
|
|
||||||
|
de4dot is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
de4dot is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class HandlerMethod {
|
||||||
|
public MethodDef Method { get; private set; }
|
||||||
|
public Blocks Blocks { get; private set; }
|
||||||
|
|
||||||
|
public HandlerMethod(MethodDef method) {
|
||||||
|
this.Method = method;
|
||||||
|
this.Blocks = new Blocks(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PrimitiveHandlerMethod : HandlerMethod {
|
||||||
|
public MethodSigInfo Sig { get; set; }
|
||||||
|
|
||||||
|
public PrimitiveHandlerMethod(MethodDef method)
|
||||||
|
: base(method) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompositeOpCodeHandler {
|
||||||
|
public TypeDef HandlerType { get; private set; }
|
||||||
|
public HandlerMethod ExecMethod { get; private set; }
|
||||||
|
public List<OpCodeHandlerInfo> OpCodeHandlerInfos { get; private set; }
|
||||||
|
|
||||||
|
public CompositeOpCodeHandler(TypeDef handlerType, HandlerMethod execMethod) {
|
||||||
|
this.HandlerType = handlerType;
|
||||||
|
this.ExecMethod = execMethod;
|
||||||
|
this.OpCodeHandlerInfos = new List<OpCodeHandlerInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
if (OpCodeHandlerInfos.Count == 0)
|
||||||
|
return "<nothing>";
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var handler in OpCodeHandlerInfos) {
|
||||||
|
if (sb.Length != 0)
|
||||||
|
sb.Append(", ");
|
||||||
|
sb.Append(handler.Name);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
164
de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs
Normal file
164
de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet;
|
||||||
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class Csvm {
|
||||||
|
IDeobfuscatorContext deobfuscatorContext;
|
||||||
|
ModuleDefMD module;
|
||||||
|
EmbeddedResource resource;
|
||||||
|
AssemblyRef vmAssemblyRef;
|
||||||
|
|
||||||
|
public bool Detected {
|
||||||
|
get { return resource != null && vmAssemblyRef != null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmbeddedResource Resource {
|
||||||
|
get { return Detected ? resource : null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public Csvm(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module) {
|
||||||
|
this.deobfuscatorContext = deobfuscatorContext;
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Csvm(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, Csvm oldOne) {
|
||||||
|
this.deobfuscatorContext = deobfuscatorContext;
|
||||||
|
this.module = module;
|
||||||
|
if (oldOne.resource != null)
|
||||||
|
this.resource = (EmbeddedResource)module.Resources[oldOne.module.Resources.IndexOf(oldOne.resource)];
|
||||||
|
if (oldOne.vmAssemblyRef != null)
|
||||||
|
this.vmAssemblyRef = module.ResolveAssemblyRef(oldOne.vmAssemblyRef.Rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Find() {
|
||||||
|
resource = FindCsvmResource();
|
||||||
|
vmAssemblyRef = FindVmAssemblyRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
AssemblyRef FindVmAssemblyRef() {
|
||||||
|
foreach (var memberRef in module.GetMemberRefs()) {
|
||||||
|
var sig = memberRef.MethodSig;
|
||||||
|
if (sig == null)
|
||||||
|
continue;
|
||||||
|
if (sig.RetType.GetElementType() != ElementType.Object)
|
||||||
|
continue;
|
||||||
|
if (sig.Params.Count != 2)
|
||||||
|
continue;
|
||||||
|
if (memberRef.Name != "RunMethod")
|
||||||
|
continue;
|
||||||
|
if (memberRef.FullName == "System.Object VMRuntime.Libraries.CSVMRuntime::RunMethod(System.String,System.Object[])")
|
||||||
|
return memberRef.DeclaringType.Scope as AssemblyRef;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
EmbeddedResource FindCsvmResource() {
|
||||||
|
return DotNetUtils.GetResource(module, "_CSVM") as EmbeddedResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Restore() {
|
||||||
|
if (!Detected)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
int oldIndent = Logger.Instance.IndentLevel;
|
||||||
|
try {
|
||||||
|
Restore2();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
Logger.Instance.IndentLevel = oldIndent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Restore2() {
|
||||||
|
Logger.n("Restoring CSVM methods");
|
||||||
|
Logger.Instance.Indent();
|
||||||
|
|
||||||
|
var opcodeDetector = GetVmOpCodeHandlerDetector();
|
||||||
|
var csvmMethods = new CsvmDataReader(resource.Data).Read();
|
||||||
|
|
||||||
|
var converter = new CsvmToCilMethodConverter(deobfuscatorContext, module, opcodeDetector);
|
||||||
|
var methodPrinter = new MethodPrinter();
|
||||||
|
foreach (var csvmMethod in csvmMethods) {
|
||||||
|
var cilMethod = module.ResolveToken(csvmMethod.Token) as MethodDef;
|
||||||
|
if (cilMethod == null)
|
||||||
|
throw new ApplicationException(string.Format("Could not find method {0:X8}", csvmMethod.Token));
|
||||||
|
converter.Convert(cilMethod, csvmMethod);
|
||||||
|
Logger.v("Restored method {0:X8}", cilMethod.MDToken.ToInt32());
|
||||||
|
PrintMethod(methodPrinter, cilMethod);
|
||||||
|
}
|
||||||
|
Logger.Instance.DeIndent();
|
||||||
|
Logger.n("Restored {0} CSVM methods", csvmMethods.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void PrintMethod(MethodPrinter methodPrinter, MethodDef method) {
|
||||||
|
const LoggerEvent dumpLogLevel = LoggerEvent.Verbose;
|
||||||
|
if (Logger.Instance.IgnoresEvent(dumpLogLevel))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Logger.Instance.Indent();
|
||||||
|
|
||||||
|
Logger.v("Locals:");
|
||||||
|
Logger.Instance.Indent();
|
||||||
|
for (int i = 0; i < method.Body.Variables.Count; i++)
|
||||||
|
Logger.v("#{0}: {1}", i, method.Body.Variables[i].Type);
|
||||||
|
Logger.Instance.DeIndent();
|
||||||
|
|
||||||
|
Logger.v("Code:");
|
||||||
|
Logger.Instance.Indent();
|
||||||
|
methodPrinter.Print(dumpLogLevel, method.Body.Instructions, method.Body.ExceptionHandlers);
|
||||||
|
Logger.Instance.DeIndent();
|
||||||
|
|
||||||
|
Logger.Instance.DeIndent();
|
||||||
|
}
|
||||||
|
|
||||||
|
VmOpCodeHandlerDetector GetVmOpCodeHandlerDetector() {
|
||||||
|
var vmFilename = vmAssemblyRef.Name + ".dll";
|
||||||
|
var vmModulePath = Path.Combine(Path.GetDirectoryName(module.Location), vmFilename);
|
||||||
|
Logger.v("CSVM filename: {0}", vmFilename);
|
||||||
|
|
||||||
|
var dataKey = "cs cached VmOpCodeHandlerDetector v2";
|
||||||
|
var dict = (Dictionary<string, VmOpCodeHandlerDetector>)deobfuscatorContext.GetData(dataKey);
|
||||||
|
if (dict == null)
|
||||||
|
deobfuscatorContext.SetData(dataKey, dict = new Dictionary<string, VmOpCodeHandlerDetector>(StringComparer.OrdinalIgnoreCase));
|
||||||
|
VmOpCodeHandlerDetector detector;
|
||||||
|
if (dict.TryGetValue(vmModulePath, out detector))
|
||||||
|
return detector;
|
||||||
|
dict[vmModulePath] = detector = new VmOpCodeHandlerDetector(ModuleDefMD.Load(vmModulePath));
|
||||||
|
|
||||||
|
detector.FindHandlers();
|
||||||
|
Logger.v("CSVM opcodes: {0}", detector.Handlers.Count);
|
||||||
|
Logger.Instance.Indent();
|
||||||
|
for (int i = 0; i < detector.Handlers.Count; i++)
|
||||||
|
Logger.v("{0:X4}: {1}", i, detector.Handlers[i]);
|
||||||
|
Logger.Instance.DeIndent();
|
||||||
|
|
||||||
|
return detector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
779
de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs
Normal file
779
de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs
Normal file
|
@ -0,0 +1,779 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class CsvmInfo {
|
||||||
|
ModuleDef module;
|
||||||
|
|
||||||
|
public TypeDef VmHandlerBaseType;
|
||||||
|
|
||||||
|
public MethodDef LogicalOpShrUn;
|
||||||
|
public MethodDef LogicalOpShl;
|
||||||
|
public MethodDef LogicalOpShr;
|
||||||
|
public MethodDef LogicalOpAnd;
|
||||||
|
public MethodDef LogicalOpXor;
|
||||||
|
public MethodDef LogicalOpOr;
|
||||||
|
|
||||||
|
public MethodDef CompareLt;
|
||||||
|
public MethodDef CompareLte;
|
||||||
|
public MethodDef CompareGt;
|
||||||
|
public MethodDef CompareGte;
|
||||||
|
public MethodDef CompareEq;
|
||||||
|
public MethodDef CompareEqz;
|
||||||
|
|
||||||
|
public MethodDef ArithmeticSubOvfUn;
|
||||||
|
public MethodDef ArithmeticMulOvfUn;
|
||||||
|
public MethodDef ArithmeticRemUn;
|
||||||
|
public MethodDef ArithmeticRem;
|
||||||
|
public MethodDef ArithmeticDivUn;
|
||||||
|
public MethodDef ArithmeticDiv;
|
||||||
|
public MethodDef ArithmeticMul;
|
||||||
|
public MethodDef ArithmeticMulOvf;
|
||||||
|
public MethodDef ArithmeticSub;
|
||||||
|
public MethodDef ArithmeticSubOvf;
|
||||||
|
public MethodDef ArithmeticAddOvfUn;
|
||||||
|
public MethodDef ArithmeticAddOvf;
|
||||||
|
public MethodDef ArithmeticAdd;
|
||||||
|
|
||||||
|
public MethodDef UnaryNot;
|
||||||
|
public MethodDef UnaryNeg;
|
||||||
|
|
||||||
|
public MethodDef ArgsGet;
|
||||||
|
public MethodDef ArgsSet;
|
||||||
|
public MethodDef LocalsGet;
|
||||||
|
public MethodDef LocalsSet;
|
||||||
|
|
||||||
|
public CsvmInfo(ModuleDef module) {
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Initialize() {
|
||||||
|
return FindVmHandlerBase() &&
|
||||||
|
FindLocalOpsMethods() &&
|
||||||
|
FindComparerMethods() &&
|
||||||
|
FindArithmeticMethods() &&
|
||||||
|
FindUnaryOpsMethods() &&
|
||||||
|
FindArgsLocals();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FindVmHandlerBase() {
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
if (!type.IsPublic || !type.IsAbstract)
|
||||||
|
continue;
|
||||||
|
if (type.HasFields || type.HasProperties || type.HasEvents)
|
||||||
|
continue;
|
||||||
|
if (type.BaseType == null || type.BaseType.FullName != "System.Object")
|
||||||
|
continue;
|
||||||
|
if (CountVirtual(type) != 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
VmHandlerBaseType = type;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FindLocalOpsMethods() {
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
if (type.BaseType == null || type.BaseType.FullName != "System.Object")
|
||||||
|
continue;
|
||||||
|
if (type.Methods.Count != 6 && type.Methods.Count != 7)
|
||||||
|
continue;
|
||||||
|
LogicalOpShrUn = FindLogicalOpMethodShrUn(type);
|
||||||
|
if (LogicalOpShrUn == null)
|
||||||
|
continue;
|
||||||
|
LogicalOpShl = FindLogicalOpMethodShl(type);
|
||||||
|
LogicalOpShr = FindLogicalOpMethodShr(type);
|
||||||
|
LogicalOpAnd = FindLogicalOpMethodAnd(type);
|
||||||
|
LogicalOpXor = FindLogicalOpMethodXor(type);
|
||||||
|
LogicalOpOr = FindLogicalOpMethodOr(type);
|
||||||
|
if (LogicalOpShrUn != null && LogicalOpShl != null &&
|
||||||
|
LogicalOpShr != null && LogicalOpAnd != null &&
|
||||||
|
LogicalOpXor != null && LogicalOpOr != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethodShrUn(TypeDef type) {
|
||||||
|
return FindLogicalOpMethod(type, ElementType.U4, ElementType.I4, ElementType.U4, Code.Shr_Un);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethodShl(TypeDef type) {
|
||||||
|
return FindLogicalOpMethod(type, ElementType.I4, ElementType.I4, ElementType.I4, Code.Shl);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethodShr(TypeDef type) {
|
||||||
|
return FindLogicalOpMethod(type, ElementType.I4, ElementType.I4, ElementType.I4, Code.Shr);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethod(TypeDef type, ElementType e1, ElementType e2, ElementType e3, Code code) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckLogicalMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 7; i++) {
|
||||||
|
var ldarg0 = instrs[i];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 1], e1))
|
||||||
|
continue;
|
||||||
|
var ldarg1 = instrs[i + 2];
|
||||||
|
if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 3], e2))
|
||||||
|
continue;
|
||||||
|
var ldci4 = instrs[i + 4];
|
||||||
|
if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 0x1F)
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 5].OpCode.Code != Code.And)
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 6].OpCode.Code != code)
|
||||||
|
continue;
|
||||||
|
if (!CheckBox(instrs[i + 7], e3))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethodAnd(TypeDef type) {
|
||||||
|
return FindLogicalOpMethod(type, Code.And);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethodXor(TypeDef type) {
|
||||||
|
return FindLogicalOpMethod(type, Code.Xor);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethodOr(TypeDef type) {
|
||||||
|
return FindLogicalOpMethod(type, Code.Or);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindLogicalOpMethod(TypeDef type, Code code) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckLogicalMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 5; i++) {
|
||||||
|
var ldarg0 = instrs[i];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 1], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
var ldarg1 = instrs[i + 2];
|
||||||
|
if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 3], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 4].OpCode.Code != code)
|
||||||
|
continue;
|
||||||
|
if (!CheckBox(instrs[i + 5], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckLogicalMethodSig(MethodDef method) {
|
||||||
|
return method != null &&
|
||||||
|
method.IsStatic &&
|
||||||
|
method.MethodSig.GetParamCount() == 2 &&
|
||||||
|
method.MethodSig.RetType.GetElementType() == ElementType.Object &&
|
||||||
|
method.MethodSig.Params[0].GetElementType() == ElementType.Object &&
|
||||||
|
method.MethodSig.Params[1].GetElementType() == ElementType.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FindComparerMethods() {
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
if (type.BaseType == null || type.BaseType.FullName != "System.Object")
|
||||||
|
continue;
|
||||||
|
if (type.Methods.Count != 9)
|
||||||
|
continue;
|
||||||
|
CompareLt = FindCompareLt(type);
|
||||||
|
if (CompareLt == null)
|
||||||
|
continue;
|
||||||
|
CompareLte = FindCompareLte(type);
|
||||||
|
CompareGt = FindCompareGt(type);
|
||||||
|
CompareGte = FindCompareGte(type);
|
||||||
|
CompareEq = FindCompareEq(type);
|
||||||
|
CompareEqz = FindCompareEqz(type);
|
||||||
|
if (CompareLt != null && CompareLte != null &&
|
||||||
|
CompareGt != null && CompareGte != null &&
|
||||||
|
CompareEq != null && CompareEqz != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindCompareLt(TypeDef type) {
|
||||||
|
return FindCompareMethod(type, Code.Clt, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindCompareLte(TypeDef type) {
|
||||||
|
return FindCompareMethod(type, Code.Cgt, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindCompareGt(TypeDef type) {
|
||||||
|
return FindCompareMethod(type, Code.Cgt, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindCompareGte(TypeDef type) {
|
||||||
|
return FindCompareMethod(type, Code.Clt, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindCompareMethod(TypeDef type, Code code, bool invert) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckCompareMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
int end = instrs.Count - 6;
|
||||||
|
if (invert)
|
||||||
|
end -= 2;
|
||||||
|
for (int i = 0; i < end; i++) {
|
||||||
|
int index = i;
|
||||||
|
var ldarg0 = instrs[index++];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[index++], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
var ldarg1 = instrs[index++];
|
||||||
|
if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[index++], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
if (instrs[index++].OpCode.Code != code)
|
||||||
|
continue;
|
||||||
|
if (invert) {
|
||||||
|
var ldci4 = instrs[index++];
|
||||||
|
if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 0)
|
||||||
|
continue;
|
||||||
|
if (instrs[index++].OpCode.Code != Code.Ceq)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!instrs[index++].IsStloc())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckCompareMethodSig(MethodDef method) {
|
||||||
|
if (method == null || !method.IsStatic)
|
||||||
|
return false;
|
||||||
|
var sig = method.MethodSig;
|
||||||
|
if (sig == null || sig.GetParamCount() != 3)
|
||||||
|
return false;
|
||||||
|
if (sig.RetType.GetElementType() != ElementType.Boolean)
|
||||||
|
return false;
|
||||||
|
if (sig.Params[0].GetElementType() != ElementType.Object)
|
||||||
|
return false;
|
||||||
|
if (sig.Params[1].GetElementType() != ElementType.Object)
|
||||||
|
return false;
|
||||||
|
var arg2 = sig.Params[2] as ValueTypeSig;
|
||||||
|
if (arg2 == null || arg2.TypeDef == null || !arg2.TypeDef.IsEnum)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindCompareEq(TypeDef type) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckCompareEqMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 5; i++) {
|
||||||
|
var ldarg0 = instrs[i];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 1], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
var ldarg1 = instrs[i + 2];
|
||||||
|
if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 3], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 4].OpCode.Code != Code.Ceq)
|
||||||
|
continue;
|
||||||
|
if (!instrs[i + 5].IsStloc())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckCompareEqMethodSig(MethodDef method) {
|
||||||
|
return method != null &&
|
||||||
|
method.IsStatic &&
|
||||||
|
method.MethodSig.GetParamCount() == 2 &&
|
||||||
|
method.MethodSig.RetType.GetElementType() == ElementType.Boolean &&
|
||||||
|
method.MethodSig.Params[0].GetElementType() == ElementType.Object &&
|
||||||
|
method.MethodSig.Params[1].GetElementType() == ElementType.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindCompareEqz(TypeDef type) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckCompareEqzMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 4; i++) {
|
||||||
|
var ldarg0 = instrs[i];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 1], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
var ldci4 = instrs[i + 2];
|
||||||
|
if (!ldci4.IsLdcI4() || ldci4.GetLdcI4Value() != 0)
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 3].OpCode.Code != Code.Ceq)
|
||||||
|
continue;
|
||||||
|
if (!instrs[i + 4].IsStloc())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckCompareEqzMethodSig(MethodDef method) {
|
||||||
|
return method != null &&
|
||||||
|
method.IsStatic &&
|
||||||
|
method.MethodSig.GetParamCount() == 1 &&
|
||||||
|
method.MethodSig.RetType.GetElementType() == ElementType.Boolean &&
|
||||||
|
method.MethodSig.Params[0].GetElementType() == ElementType.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FindArithmeticMethods() {
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
if (type.BaseType == null || type.BaseType.FullName != "System.Object")
|
||||||
|
continue;
|
||||||
|
if (type.Methods.Count != 15)
|
||||||
|
continue;
|
||||||
|
ArithmeticSubOvfUn = FindArithmeticSubOvfUn(type);
|
||||||
|
if (ArithmeticSubOvfUn == null)
|
||||||
|
continue;
|
||||||
|
ArithmeticMulOvfUn = FindArithmeticMulOvfUn(type);
|
||||||
|
ArithmeticRemUn = FindArithmeticRemUn(type);
|
||||||
|
ArithmeticRem = FindArithmeticRem(type);
|
||||||
|
ArithmeticDivUn = FindArithmeticDivUn(type);
|
||||||
|
ArithmeticDiv = FindArithmeticDiv(type);
|
||||||
|
ArithmeticMul = FindArithmeticMul(type);
|
||||||
|
ArithmeticMulOvf = FindArithmeticMulOvf(type);
|
||||||
|
ArithmeticSub = FindArithmeticSub(type);
|
||||||
|
ArithmeticSubOvf = FindArithmeticSubOvf(type);
|
||||||
|
ArithmeticAddOvfUn = FindArithmeticAddOvfUn(type);
|
||||||
|
ArithmeticAddOvf = FindArithmeticAddOvf(type);
|
||||||
|
ArithmeticAdd = FindArithmeticAdd(type);
|
||||||
|
|
||||||
|
if (ArithmeticSubOvfUn != null && ArithmeticMulOvfUn != null &&
|
||||||
|
ArithmeticRemUn != null && ArithmeticRem != null &&
|
||||||
|
ArithmeticDivUn != null && ArithmeticDiv != null &&
|
||||||
|
ArithmeticMul != null && ArithmeticMulOvf != null &&
|
||||||
|
ArithmeticSub != null && ArithmeticSubOvf != null &&
|
||||||
|
ArithmeticAddOvfUn != null && ArithmeticAddOvf != null &&
|
||||||
|
ArithmeticAdd != null)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticSubOvfUn(TypeDef type) {
|
||||||
|
return FindArithmeticOpUn(type, Code.Sub_Ovf_Un);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticMulOvfUn(TypeDef type) {
|
||||||
|
return FindArithmeticOpUn(type, Code.Mul_Ovf_Un);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticAddOvfUn(TypeDef type) {
|
||||||
|
return FindArithmeticOpUn(type, Code.Add_Ovf_Un);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticOpUn(TypeDef type, Code code) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckArithmeticUnMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 8; i++) {
|
||||||
|
var ldarg0 = instrs[i];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckCallvirt(instrs[i + 1], "System.Int32", "()"))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 2].OpCode.Code != Code.Conv_Ovf_U4)
|
||||||
|
continue;
|
||||||
|
var ldarg1 = instrs[i + 3];
|
||||||
|
if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1)
|
||||||
|
continue;
|
||||||
|
if (!CheckCallvirt(instrs[i + 4], "System.Int32", "()"))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 5].OpCode.Code != Code.Conv_Ovf_U4)
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 6].OpCode.Code != code)
|
||||||
|
continue;
|
||||||
|
if (!CheckBox(instrs[i + 7], ElementType.U4))
|
||||||
|
continue;
|
||||||
|
if (!instrs[i + 8].IsStloc())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckArithmeticUnMethodSig(MethodDef method) {
|
||||||
|
return method != null &&
|
||||||
|
method.IsStatic &&
|
||||||
|
method.MethodSig.GetParamCount() == 2 &&
|
||||||
|
method.MethodSig.RetType.GetElementType() == ElementType.Object &&
|
||||||
|
method.MethodSig.Params[0].GetElementType() == ElementType.Class &&
|
||||||
|
method.MethodSig.Params[1].GetElementType() == ElementType.Class;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticRemUn(TypeDef type) {
|
||||||
|
return FindArithmeticDivOrRemUn(type, Code.Rem_Un);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticDivUn(TypeDef type) {
|
||||||
|
return FindArithmeticDivOrRemUn(type, Code.Div_Un);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticDivOrRemUn(TypeDef type, Code code) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckArithmeticUnMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 7; i++) {
|
||||||
|
var ldarg0 = instrs[i];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckCallvirt(instrs[i + 1], "System.Int32", "()"))
|
||||||
|
continue;
|
||||||
|
var ldarg1 = instrs[i + 2];
|
||||||
|
if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1)
|
||||||
|
continue;
|
||||||
|
if (!CheckCallvirt(instrs[i + 3], "System.Int32", "()"))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 4].OpCode.Code != code)
|
||||||
|
continue;
|
||||||
|
if (!CheckBox(instrs[i + 5], ElementType.U4))
|
||||||
|
continue;
|
||||||
|
if (!instrs[i + 6].IsStloc())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticRem(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticDiv(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Div);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticMul(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Mul);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticMulOvf(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Mul_Ovf);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticSub(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticSubOvf(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Sub_Ovf);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticAdd(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Add);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticAddOvf(TypeDef type) {
|
||||||
|
return FindArithmeticOther(type, Code.Add_Ovf);
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindArithmeticOther(TypeDef type, Code code) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!CheckArithmeticOtherMethodSig(method))
|
||||||
|
continue;
|
||||||
|
if (method.Body == null)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 6; i++) {
|
||||||
|
var ldarg0 = instrs[i];
|
||||||
|
if (!ldarg0.IsLdarg() || ldarg0.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 1], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
var ldarg1 = instrs[i + 2];
|
||||||
|
if (!ldarg1.IsLdarg() || ldarg1.GetParameterIndex() != 1)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 3], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 4].OpCode.Code != code)
|
||||||
|
continue;
|
||||||
|
if (!CheckBox(instrs[i + 5], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckArithmeticOtherMethodSig(MethodDef method) {
|
||||||
|
return method != null &&
|
||||||
|
method.IsStatic &&
|
||||||
|
method.MethodSig.GetParamCount() == 2 &&
|
||||||
|
method.MethodSig.RetType.GetElementType() == ElementType.Object &&
|
||||||
|
method.MethodSig.Params[0].GetElementType() == ElementType.Object &&
|
||||||
|
method.MethodSig.Params[1].GetElementType() == ElementType.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FindUnaryOpsMethods() {
|
||||||
|
UnaryNot = FindUnaryOpMethod(Code.Not);
|
||||||
|
UnaryNeg = FindUnaryOpMethod(Code.Neg);
|
||||||
|
return UnaryNot != null && UnaryNeg != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef FindUnaryOpMethod(Code code) {
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
if (type.BaseType != VmHandlerBaseType)
|
||||||
|
continue;
|
||||||
|
if (type.Methods.Count != 4)
|
||||||
|
continue;
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (!method.HasBody || !method.IsStatic)
|
||||||
|
continue;
|
||||||
|
if (!DotNetUtils.IsMethod(method, "System.Object", "(System.Object)"))
|
||||||
|
continue;
|
||||||
|
if (CountThrows(method) != 1)
|
||||||
|
continue;
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 4; i++) {
|
||||||
|
var ldarg = instrs[i];
|
||||||
|
if (!ldarg.IsLdarg() || ldarg.GetParameterIndex() != 0)
|
||||||
|
continue;
|
||||||
|
if (!CheckUnboxAny(instrs[i + 1], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 2].OpCode.Code != code)
|
||||||
|
continue;
|
||||||
|
if (!CheckBox(instrs[i + 3], ElementType.I4))
|
||||||
|
continue;
|
||||||
|
if (!instrs[i + 4].IsStloc())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CountThrows(MethodDef method) {
|
||||||
|
if (method == null || method.Body == null)
|
||||||
|
return 0;
|
||||||
|
int count = 0;
|
||||||
|
foreach (var instr in method.Body.Instructions) {
|
||||||
|
if (instr.OpCode.Code == Code.Throw)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool FindArgsLocals() {
|
||||||
|
var vmState = FindVmState();
|
||||||
|
if (vmState == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var ctor = vmState.FindMethod(".ctor");
|
||||||
|
return FindArgsLocals(ctor, 1, out ArgsGet, out ArgsSet) &&
|
||||||
|
FindArgsLocals(ctor, 2, out LocalsGet, out LocalsSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeDef FindVmState() {
|
||||||
|
if (VmHandlerBaseType == null)
|
||||||
|
return null;
|
||||||
|
foreach (var method in VmHandlerBaseType.Methods) {
|
||||||
|
if (method.IsStatic || !method.IsAbstract)
|
||||||
|
continue;
|
||||||
|
if (method.Parameters.Count != 2)
|
||||||
|
continue;
|
||||||
|
var arg1 = method.Parameters[1].Type.TryGetTypeDef();
|
||||||
|
if (arg1 == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return arg1;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool FindArgsLocals(MethodDef ctor, int arg, out MethodDef getter, out MethodDef setter) {
|
||||||
|
getter = null;
|
||||||
|
setter = null;
|
||||||
|
if (ctor == null || !ctor.HasBody)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
setter = FindSetter(ctor, arg);
|
||||||
|
if (setter == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var propField = GetPropField(setter);
|
||||||
|
if (propField == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
getter = FindGetter(ctor.DeclaringType, propField);
|
||||||
|
return getter != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodDef FindSetter(MethodDef ctor, int arg) {
|
||||||
|
if (ctor == null || !ctor.HasBody)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var instrs = ctor.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 1; i++) {
|
||||||
|
var ldarg = instrs[i];
|
||||||
|
if (!ldarg.IsLdarg() || ldarg.GetParameterIndex() != arg)
|
||||||
|
continue;
|
||||||
|
var call = instrs[i + 1];
|
||||||
|
if (call.OpCode.Code != Code.Call)
|
||||||
|
continue;
|
||||||
|
var method = call.Operand as MethodDef;
|
||||||
|
if (method == null)
|
||||||
|
continue;
|
||||||
|
if (method.DeclaringType != ctor.DeclaringType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FieldDef GetPropField(MethodDef method) {
|
||||||
|
if (method == null || !method.HasBody)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foreach (var instr in method.Body.Instructions) {
|
||||||
|
if (instr.OpCode.Code != Code.Stfld)
|
||||||
|
continue;
|
||||||
|
var field = instr.Operand as FieldDef;
|
||||||
|
if (field == null || field.DeclaringType != method.DeclaringType)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodDef FindGetter(TypeDef type, FieldDef propField) {
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (method.IsStatic || !method.HasBody)
|
||||||
|
continue;
|
||||||
|
foreach (var instr in method.Body.Instructions) {
|
||||||
|
if (instr.OpCode.Code != Code.Ldfld)
|
||||||
|
continue;
|
||||||
|
if (instr.Operand != propField)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CheckCallvirt(Instruction instr, string returnType, string parameters) {
|
||||||
|
if (instr.OpCode.Code != Code.Callvirt)
|
||||||
|
return false;
|
||||||
|
return DotNetUtils.IsMethod(instr.Operand as IMethod, returnType, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckUnboxAny(Instruction instr, ElementType expectedType) {
|
||||||
|
if (instr == null || instr.OpCode.Code != Code.Unbox_Any)
|
||||||
|
return false;
|
||||||
|
var typeSig = module.CorLibTypes.GetCorLibTypeSig(instr.Operand as ITypeDefOrRef);
|
||||||
|
return typeSig.GetElementType() == expectedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckBox(Instruction instr, ElementType expectedType) {
|
||||||
|
if (instr == null || instr.OpCode.Code != Code.Box)
|
||||||
|
return false;
|
||||||
|
var typeSig = module.CorLibTypes.GetCorLibTypeSig(instr.Operand as ITypeDefOrRef);
|
||||||
|
return typeSig.GetElementType() == expectedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CountVirtual(TypeDef type) {
|
||||||
|
int count = 0;
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
if (method.IsVirtual)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
93
de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs
generated
Normal file
93
de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs
generated
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.18052
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
|
/// </summary>
|
||||||
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
|
// class via a tool like ResGen or Visual Studio.
|
||||||
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
|
// with the /str option, or rebuild your VS project.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class CsvmResources {
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal CsvmResources() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
|
get {
|
||||||
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("de4dot.code.deobfuscators.Agile_NET.vm.v2.CsvmResources", typeof(CsvmResources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
|
/// resource lookups using this strongly typed resource class.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
|
get {
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Byte[].
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] CSVM1_v2 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("CSVM1_v2", resourceCulture);
|
||||||
|
return ((byte[])(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Byte[].
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] CSVM2_v2 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("CSVM2_v2", resourceCulture);
|
||||||
|
return ((byte[])(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized resource of type System.Byte[].
|
||||||
|
/// </summary>
|
||||||
|
internal static byte[] CSVM3_v2 {
|
||||||
|
get {
|
||||||
|
object obj = ResourceManager.GetObject("CSVM3_v2", resourceCulture);
|
||||||
|
return ((byte[])(obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
130
de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx
Normal file
130
de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
<xsd:attribute ref="xml:space" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||||
|
<data name="CSVM1_v2" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>CSVM1_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</data>
|
||||||
|
<data name="CSVM2_v2" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>CSVM2_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</data>
|
||||||
|
<data name="CSVM3_v2" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||||
|
<value>CSVM3_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</data>
|
||||||
|
</root>
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class CsvmToCilMethodConverter : CsvmToCilMethodConverterBase {
|
||||||
|
VmOpCodeHandlerDetector opCodeDetector;
|
||||||
|
|
||||||
|
public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector)
|
||||||
|
: base(deobfuscatorContext, module) {
|
||||||
|
this.opCodeDetector = opCodeDetector;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override List<Instruction> ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) {
|
||||||
|
var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions));
|
||||||
|
var instrs = new List<Instruction>();
|
||||||
|
var handlerInfoReader = new OpCodeHandlerInfoReader(module);
|
||||||
|
|
||||||
|
int numVmInstrs = reader.ReadInt32();
|
||||||
|
var vmInstrs = new ushort[numVmInstrs];
|
||||||
|
for (int i = 0; i < numVmInstrs; i++)
|
||||||
|
vmInstrs[i] = reader.ReadUInt16();
|
||||||
|
|
||||||
|
uint offset = 0;
|
||||||
|
for (int vmInstrIndex = 0; vmInstrIndex < numVmInstrs; vmInstrIndex++) {
|
||||||
|
var composite = opCodeDetector.Handlers[vmInstrs[vmInstrIndex]];
|
||||||
|
var handlerInfos = composite.OpCodeHandlerInfos;
|
||||||
|
if (handlerInfos.Count == 0)
|
||||||
|
handlerInfos = new List<OpCodeHandlerInfo>() { new OpCodeHandlerInfo(HandlerTypeCode.Nop, null) };
|
||||||
|
for (int hi = 0; hi < handlerInfos.Count; hi++) {
|
||||||
|
var instr = handlerInfoReader.Read(handlerInfos[hi].TypeCode, reader);
|
||||||
|
instr.Offset = offset;
|
||||||
|
offset += (uint)GetInstructionSize(instr);
|
||||||
|
SetCilToVmIndex(instr, vmInstrIndex);
|
||||||
|
if (hi == 0)
|
||||||
|
SetVmIndexToCil(instr, vmInstrIndex);
|
||||||
|
instrs.Add(instr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instrs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs
Normal file
102
de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 de4dot@gmail.com
|
||||||
|
|
||||||
|
This file is part of de4dot.
|
||||||
|
|
||||||
|
de4dot is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
de4dot is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
// These constants are hard coded. Don't change the values (i.e., only append if more are needed)
|
||||||
|
enum HandlerTypeCode {
|
||||||
|
Add,
|
||||||
|
Add_Ovf,
|
||||||
|
Add_Ovf_Un,
|
||||||
|
And,
|
||||||
|
Beq,
|
||||||
|
Bge,
|
||||||
|
Bge_Un,
|
||||||
|
Bgt,
|
||||||
|
Bgt_Un,
|
||||||
|
Ble,
|
||||||
|
Ble_Un,
|
||||||
|
Blt,
|
||||||
|
Blt_Un,
|
||||||
|
Bne_Un,
|
||||||
|
Box,
|
||||||
|
Br,
|
||||||
|
Brfalse,
|
||||||
|
Brtrue,
|
||||||
|
Call,
|
||||||
|
Callvirt,
|
||||||
|
Castclass,
|
||||||
|
Ceq,
|
||||||
|
Cgt,
|
||||||
|
Cgt_Un,
|
||||||
|
Clt,
|
||||||
|
Clt_Un,
|
||||||
|
Conv,
|
||||||
|
Div,
|
||||||
|
Div_Un,
|
||||||
|
Dup,
|
||||||
|
Endfinally,
|
||||||
|
Initobj,
|
||||||
|
Isinst,
|
||||||
|
Ldarg,
|
||||||
|
Ldarga,
|
||||||
|
Ldc,
|
||||||
|
Ldelem,
|
||||||
|
Ldelema,
|
||||||
|
Ldfld_Ldsfld,
|
||||||
|
Ldflda_Ldsflda,
|
||||||
|
Ldftn,
|
||||||
|
Ldlen,
|
||||||
|
Ldloc,
|
||||||
|
Ldloca,
|
||||||
|
Ldobj,
|
||||||
|
Ldstr,
|
||||||
|
Ldtoken,
|
||||||
|
Ldvirtftn,
|
||||||
|
Leave,
|
||||||
|
Mul,
|
||||||
|
Mul_Ovf,
|
||||||
|
Mul_Ovf_Un,
|
||||||
|
Neg,
|
||||||
|
Newarr,
|
||||||
|
Newobj,
|
||||||
|
Nop,
|
||||||
|
Not,
|
||||||
|
Or,
|
||||||
|
Pop,
|
||||||
|
Rem,
|
||||||
|
Rem_Un,
|
||||||
|
Ret,
|
||||||
|
Rethrow,
|
||||||
|
Shl,
|
||||||
|
Shr,
|
||||||
|
Shr_Un,
|
||||||
|
Starg,
|
||||||
|
Stelem,
|
||||||
|
Stfld_Stsfld,
|
||||||
|
Stloc,
|
||||||
|
Stobj,
|
||||||
|
Sub,
|
||||||
|
Sub_Ovf,
|
||||||
|
Sub_Ovf_Un,
|
||||||
|
Switch,
|
||||||
|
Throw,
|
||||||
|
Unbox_Any,
|
||||||
|
Xor,
|
||||||
|
}
|
||||||
|
}
|
109
de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs
Normal file
109
de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class MethodFinder {
|
||||||
|
readonly IList<OpCodeHandlerInfo> handlerInfos;
|
||||||
|
readonly PrimitiveHandlerMethod handlerMethod;
|
||||||
|
|
||||||
|
class SigState {
|
||||||
|
public readonly MethodSigInfo SigInfo;
|
||||||
|
|
||||||
|
public SigState(PrimitiveHandlerMethod handlerMethod) {
|
||||||
|
this.SigInfo = handlerMethod.Sig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodFinder(IList<OpCodeHandlerInfo> handlerInfos, PrimitiveHandlerMethod handlerMethod) {
|
||||||
|
this.handlerInfos = handlerInfos;
|
||||||
|
this.handlerMethod = handlerMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpCodeHandler FindHandler() {
|
||||||
|
var handler = FindHandler(new SigState(handlerMethod));
|
||||||
|
if (handler == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new OpCodeHandler(handler, handlerMethod.Method.DeclaringType, handlerMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
OpCodeHandlerInfo FindHandler(SigState execSigState) {
|
||||||
|
foreach (var handler in handlerInfos) {
|
||||||
|
if (Matches(handler.ExecSig, execSigState))
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MatchInfo {
|
||||||
|
public int HandlerIndex;
|
||||||
|
public int SigIndex;
|
||||||
|
|
||||||
|
public MatchInfo(int handlerIndex, int sigIndex) {
|
||||||
|
this.HandlerIndex = handlerIndex;
|
||||||
|
this.SigIndex = sigIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<int, int> sigIndexToHandlerIndex = new Dictionary<int, int>();
|
||||||
|
Dictionary<int, int> handlerIndexToSigIndex = new Dictionary<int, int>();
|
||||||
|
Stack<MatchInfo> stack = new Stack<MatchInfo>();
|
||||||
|
bool Matches(MethodSigInfo handlerSig, SigState sigState) {
|
||||||
|
stack.Clear();
|
||||||
|
sigIndexToHandlerIndex.Clear();
|
||||||
|
handlerIndexToSigIndex.Clear();
|
||||||
|
var handlerInfos = handlerSig.BlockInfos;
|
||||||
|
var sigInfos = sigState.SigInfo.BlockInfos;
|
||||||
|
|
||||||
|
stack.Push(new MatchInfo(0, 0));
|
||||||
|
while (stack.Count > 0) {
|
||||||
|
var info = stack.Pop();
|
||||||
|
|
||||||
|
int handlerIndex, sigIndex;
|
||||||
|
bool hasVisitedHandler = handlerIndexToSigIndex.TryGetValue(info.HandlerIndex, out sigIndex);
|
||||||
|
bool hasVisitedSig = sigIndexToHandlerIndex.TryGetValue(info.SigIndex, out handlerIndex);
|
||||||
|
if (hasVisitedHandler != hasVisitedSig)
|
||||||
|
return false;
|
||||||
|
if (hasVisitedHandler) {
|
||||||
|
if (handlerIndex != info.HandlerIndex || sigIndex != info.SigIndex)
|
||||||
|
return false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
handlerIndexToSigIndex[info.HandlerIndex] = info.SigIndex;
|
||||||
|
sigIndexToHandlerIndex[info.SigIndex] = info.HandlerIndex;
|
||||||
|
|
||||||
|
var handlerBlock = handlerInfos[info.HandlerIndex];
|
||||||
|
var sigBlock = sigInfos[info.SigIndex];
|
||||||
|
|
||||||
|
if (!handlerBlock.Equals(sigBlock))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < handlerBlock.Targets.Count; i++) {
|
||||||
|
int handlerTargetIndex = handlerBlock.Targets[i];
|
||||||
|
int sigTargetIndex = sigBlock.Targets[i];
|
||||||
|
stack.Push(new MatchInfo(handlerTargetIndex, sigTargetIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,737 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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.Security.Cryptography;
|
||||||
|
using dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using dnlib.DotNet.MD;
|
||||||
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class MethodSigInfo {
|
||||||
|
readonly List<BlockInfo> blockInfos;
|
||||||
|
|
||||||
|
public List<BlockInfo> BlockInfos {
|
||||||
|
get { return blockInfos; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodSigInfo() {
|
||||||
|
this.blockInfos = new List<BlockInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodSigInfo(IEnumerable<BlockInfo> blockInfos) {
|
||||||
|
this.blockInfos = new List<BlockInfo>(blockInfos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlockInfo : IEquatable<BlockInfo> {
|
||||||
|
readonly List<int> targets;
|
||||||
|
|
||||||
|
public byte[] Hash { get; set; }
|
||||||
|
public List<int> Targets {
|
||||||
|
get { return targets; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockInfo() {
|
||||||
|
this.targets = new List<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockInfo(byte[] hash, IEnumerable<int> targets) {
|
||||||
|
this.Hash = hash;
|
||||||
|
this.targets = new List<int>(targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
if (Hash == null)
|
||||||
|
return "<null>";
|
||||||
|
return BitConverter.ToString(Hash).Replace("-", string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(BlockInfo other) {
|
||||||
|
return Equals(Hash, other.Hash) &&
|
||||||
|
Targets.Count == other.Targets.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Equals(byte[] a, byte[] b) {
|
||||||
|
if (a == b)
|
||||||
|
return true;
|
||||||
|
if (a == null || b == null)
|
||||||
|
return false;
|
||||||
|
if (a.Length != b.Length)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < a.Length; i++) {
|
||||||
|
if (a[i] != b[i])
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MethodSigInfoCreator {
|
||||||
|
MethodSigInfo methodSigInfo;
|
||||||
|
Blocks blocks;
|
||||||
|
IList<Block> allBlocks;
|
||||||
|
Dictionary<Block, BlockInfo> blockToInfo;
|
||||||
|
Dictionary<object, int> methodToId = new Dictionary<object, int>();
|
||||||
|
|
||||||
|
public void AddId(object key, int id) {
|
||||||
|
if (key != null)
|
||||||
|
methodToId[key] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetId(object key) {
|
||||||
|
if (key == null)
|
||||||
|
return int.MinValue;
|
||||||
|
|
||||||
|
int id;
|
||||||
|
if (methodToId.TryGetValue(key, out id))
|
||||||
|
return id;
|
||||||
|
return int.MinValue + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodSigInfo Create(Blocks blocks) {
|
||||||
|
methodSigInfo = new MethodSigInfo();
|
||||||
|
|
||||||
|
this.blocks = blocks;
|
||||||
|
allBlocks = blocks.MethodBlocks.GetAllBlocks();
|
||||||
|
|
||||||
|
blockToInfo = new Dictionary<Block, BlockInfo>();
|
||||||
|
foreach (var block in allBlocks) {
|
||||||
|
var blockInfo = new BlockInfo();
|
||||||
|
blockToInfo[block] = blockInfo;
|
||||||
|
methodSigInfo.BlockInfos.Add(blockInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var block in allBlocks) {
|
||||||
|
var blockInfo = blockToInfo[block];
|
||||||
|
Update(blockInfo, block);
|
||||||
|
if (block.FallThrough != null)
|
||||||
|
blockInfo.Targets.Add(allBlocks.IndexOf(block.FallThrough));
|
||||||
|
if (block.Targets != null) {
|
||||||
|
foreach (var target in block.Targets)
|
||||||
|
blockInfo.Targets.Add(allBlocks.IndexOf(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodSigInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(BlockInfo blockInfo, Block block) {
|
||||||
|
using (var hasher = MD5.Create()) {
|
||||||
|
bool emptyHash;
|
||||||
|
using (var outStream = new NullStream()) {
|
||||||
|
using (var csStream = new CryptoStream(outStream, hasher, CryptoStreamMode.Write)) {
|
||||||
|
var writer = new BinaryWriter(csStream);
|
||||||
|
Update(writer, blockInfo, block);
|
||||||
|
}
|
||||||
|
emptyHash = outStream.Length == 0;
|
||||||
|
}
|
||||||
|
if (!emptyHash)
|
||||||
|
blockInfo.Hash = hasher.Hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update(BinaryWriter writer, BlockInfo blockInfo, Block block) {
|
||||||
|
var instrs = block.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count; i++) {
|
||||||
|
var instr = instrs[i];
|
||||||
|
switch (instr.OpCode.Code) {
|
||||||
|
case Code.Beq_S:
|
||||||
|
case Code.Bge_S:
|
||||||
|
case Code.Bgt_S:
|
||||||
|
case Code.Ble_S:
|
||||||
|
case Code.Blt_S:
|
||||||
|
case Code.Bne_Un_S:
|
||||||
|
case Code.Bge_Un_S:
|
||||||
|
case Code.Bgt_Un_S:
|
||||||
|
case Code.Ble_Un_S:
|
||||||
|
case Code.Blt_Un_S:
|
||||||
|
case Code.Brfalse_S:
|
||||||
|
case Code.Brtrue_S:
|
||||||
|
case Code.Leave_S:
|
||||||
|
case Code.Beq:
|
||||||
|
case Code.Bge:
|
||||||
|
case Code.Bgt:
|
||||||
|
case Code.Ble:
|
||||||
|
case Code.Blt:
|
||||||
|
case Code.Bne_Un:
|
||||||
|
case Code.Bge_Un:
|
||||||
|
case Code.Bgt_Un:
|
||||||
|
case Code.Ble_Un:
|
||||||
|
case Code.Blt_Un:
|
||||||
|
case Code.Brfalse:
|
||||||
|
case Code.Brtrue:
|
||||||
|
case Code.Leave:
|
||||||
|
writer.Write((ushort)SimplifyBranch(instr.OpCode.Code));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Switch:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
writer.Write(blockInfo.Targets.Count);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Br_S:
|
||||||
|
case Code.Br:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ret:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I4_M1:
|
||||||
|
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:
|
||||||
|
case Code.Ldc_I4_S:
|
||||||
|
writer.Write((ushort)Code.Ldc_I4);
|
||||||
|
writer.Write(instr.GetLdcI4Value());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_I8:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
writer.Write((long)instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_R4:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
writer.Write((float)instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldc_R8:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
writer.Write((double)instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldfld:
|
||||||
|
var typeField = instr.Operand as FieldDef;
|
||||||
|
bool isField = IsTypeField(typeField);
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
writer.Write(isField);
|
||||||
|
if (isField) {
|
||||||
|
if (i + 1 < instrs.Count && instrs[i + 1].IsLdcI4())
|
||||||
|
i++;
|
||||||
|
writer.Write(GetFieldId(typeField));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Write(writer, instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Call:
|
||||||
|
case Code.Callvirt:
|
||||||
|
case Code.Newobj:
|
||||||
|
case Code.Jmp:
|
||||||
|
case Code.Ldftn:
|
||||||
|
case Code.Ldvirtftn:
|
||||||
|
case Code.Ldtoken:
|
||||||
|
case Code.Stfld:
|
||||||
|
case Code.Ldsfld:
|
||||||
|
case Code.Stsfld:
|
||||||
|
case Code.Ldflda:
|
||||||
|
case Code.Ldsflda:
|
||||||
|
case Code.Cpobj:
|
||||||
|
case Code.Ldobj:
|
||||||
|
case Code.Castclass:
|
||||||
|
case Code.Isinst:
|
||||||
|
case Code.Unbox:
|
||||||
|
case Code.Stobj:
|
||||||
|
case Code.Box:
|
||||||
|
case Code.Newarr:
|
||||||
|
case Code.Ldelema:
|
||||||
|
case Code.Ldelem:
|
||||||
|
case Code.Stelem:
|
||||||
|
case Code.Unbox_Any:
|
||||||
|
case Code.Refanyval:
|
||||||
|
case Code.Mkrefany:
|
||||||
|
case Code.Initobj:
|
||||||
|
case Code.Constrained:
|
||||||
|
case Code.Sizeof:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
Write(writer, instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldstr:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarg:
|
||||||
|
case Code.Ldarg_S:
|
||||||
|
case Code.Ldarg_0:
|
||||||
|
case Code.Ldarg_1:
|
||||||
|
case Code.Ldarg_2:
|
||||||
|
case Code.Ldarg_3:
|
||||||
|
writer.Write((ushort)Code.Ldarg);
|
||||||
|
writer.Write(instr.Instruction.GetParameterIndex());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldarga:
|
||||||
|
case Code.Ldarga_S:
|
||||||
|
writer.Write((ushort)Code.Ldarga);
|
||||||
|
writer.Write(instr.Instruction.GetParameterIndex());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Starg:
|
||||||
|
case Code.Starg_S:
|
||||||
|
writer.Write((ushort)Code.Starg);
|
||||||
|
writer.Write(instr.Instruction.GetParameterIndex());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloc:
|
||||||
|
case Code.Ldloc_S:
|
||||||
|
case Code.Ldloc_0:
|
||||||
|
case Code.Ldloc_1:
|
||||||
|
case Code.Ldloc_2:
|
||||||
|
case Code.Ldloc_3:
|
||||||
|
writer.Write((ushort)Code.Ldloc);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldloca:
|
||||||
|
case Code.Ldloca_S:
|
||||||
|
writer.Write((ushort)Code.Ldloca);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Stloc:
|
||||||
|
case Code.Stloc_S:
|
||||||
|
case Code.Stloc_0:
|
||||||
|
case Code.Stloc_1:
|
||||||
|
case Code.Stloc_2:
|
||||||
|
case Code.Stloc_3:
|
||||||
|
writer.Write((ushort)Code.Stloc);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Ldnull:
|
||||||
|
case Code.Throw:
|
||||||
|
case Code.Rethrow:
|
||||||
|
case Code.Ldlen:
|
||||||
|
case Code.Ckfinite:
|
||||||
|
case Code.Arglist:
|
||||||
|
case Code.Localloc:
|
||||||
|
case Code.Volatile:
|
||||||
|
case Code.Tailcall:
|
||||||
|
case Code.Cpblk:
|
||||||
|
case Code.Initblk:
|
||||||
|
case Code.Refanytype:
|
||||||
|
case Code.Readonly:
|
||||||
|
case Code.Break:
|
||||||
|
case Code.Endfinally:
|
||||||
|
case Code.Endfilter:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Calli:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
Write(writer, instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Code.Unaligned:
|
||||||
|
writer.Write((ushort)instr.OpCode.Code);
|
||||||
|
writer.Write((byte)instr.Operand);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, object op) {
|
||||||
|
var fd = op as FieldDef;
|
||||||
|
if (fd != null) {
|
||||||
|
Write(writer, fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mr = op as MemberRef;
|
||||||
|
if (mr != null) {
|
||||||
|
Write(writer, mr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var md = op as MethodDef;
|
||||||
|
if (md != null) {
|
||||||
|
Write(writer, md);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ms = op as MethodSpec;
|
||||||
|
if (ms != null) {
|
||||||
|
Write(writer, ms);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var td = op as TypeDef;
|
||||||
|
if (td != null) {
|
||||||
|
Write(writer, td);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tr = op as TypeRef;
|
||||||
|
if (tr != null) {
|
||||||
|
Write(writer, tr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ts = op as TypeSpec;
|
||||||
|
if (ts != null) {
|
||||||
|
Write(writer, ts);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var fsig = op as FieldSig;
|
||||||
|
if (fsig != null) {
|
||||||
|
Write(writer, fsig);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var msig = op as MethodSig;
|
||||||
|
if (msig != null) {
|
||||||
|
Write(writer, msig);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gsig = op as GenericInstMethodSig;
|
||||||
|
if (gsig != null) {
|
||||||
|
Write(writer, gsig);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var asmRef = op as AssemblyRef;
|
||||||
|
if (asmRef != null) {
|
||||||
|
Write(writer, asmRef);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write((byte)ObjectType.Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ObjectType : byte {
|
||||||
|
// 00..3F = Table.XXX values.
|
||||||
|
Unknown = 0x40,
|
||||||
|
TypeSig = 0x41,
|
||||||
|
FieldSig = 0x42,
|
||||||
|
MethodSig = 0x43,
|
||||||
|
GenericInstMethodSig = 0x44,
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, TypeSig sig) {
|
||||||
|
Write(writer, sig, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, TypeSig sig, int level) {
|
||||||
|
if (level++ > 20)
|
||||||
|
return;
|
||||||
|
|
||||||
|
writer.Write((byte)ObjectType.TypeSig);
|
||||||
|
var etype = sig.GetElementType();
|
||||||
|
writer.Write((byte)etype);
|
||||||
|
switch (etype) {
|
||||||
|
case ElementType.Ptr:
|
||||||
|
case ElementType.ByRef:
|
||||||
|
case ElementType.SZArray:
|
||||||
|
case ElementType.Pinned:
|
||||||
|
Write(writer, sig.Next, level);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.Array:
|
||||||
|
var arySig = (ArraySig)sig;
|
||||||
|
writer.Write(arySig.Rank);
|
||||||
|
writer.Write(arySig.Sizes.Count);
|
||||||
|
writer.Write(arySig.LowerBounds.Count);
|
||||||
|
Write(writer, sig.Next, level);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.CModReqd:
|
||||||
|
case ElementType.CModOpt:
|
||||||
|
Write(writer, ((ModifierSig)sig).Modifier);
|
||||||
|
Write(writer, sig.Next, level);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.ValueArray:
|
||||||
|
writer.Write(((ValueArraySig)sig).Size);
|
||||||
|
Write(writer, sig.Next, level);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.Module:
|
||||||
|
writer.Write(((ModuleSig)sig).Index);
|
||||||
|
Write(writer, sig.Next, level);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.GenericInst:
|
||||||
|
var gis = (GenericInstSig)sig;
|
||||||
|
Write(writer, gis.GenericType, level);
|
||||||
|
foreach (var ga in gis.GenericArguments)
|
||||||
|
Write(writer, ga, level);
|
||||||
|
Write(writer, sig.Next, level);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.FnPtr:
|
||||||
|
Write(writer, ((FnPtrSig)sig).Signature);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.Var:
|
||||||
|
case ElementType.MVar:
|
||||||
|
writer.Write(((GenericSig)sig).Number);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.ValueType:
|
||||||
|
case ElementType.Class:
|
||||||
|
Write(writer, ((TypeDefOrRefSig)sig).TypeDefOrRef);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ElementType.End:
|
||||||
|
case ElementType.Void:
|
||||||
|
case ElementType.Boolean:
|
||||||
|
case ElementType.Char:
|
||||||
|
case ElementType.I1:
|
||||||
|
case ElementType.U1:
|
||||||
|
case ElementType.I2:
|
||||||
|
case ElementType.U2:
|
||||||
|
case ElementType.I4:
|
||||||
|
case ElementType.U4:
|
||||||
|
case ElementType.I8:
|
||||||
|
case ElementType.U8:
|
||||||
|
case ElementType.R4:
|
||||||
|
case ElementType.R8:
|
||||||
|
case ElementType.String:
|
||||||
|
case ElementType.TypedByRef:
|
||||||
|
case ElementType.I:
|
||||||
|
case ElementType.U:
|
||||||
|
case ElementType.R:
|
||||||
|
case ElementType.Object:
|
||||||
|
case ElementType.Internal:
|
||||||
|
case ElementType.Sentinel:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, FieldSig sig) {
|
||||||
|
writer.Write((byte)ObjectType.FieldSig);
|
||||||
|
writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention()));
|
||||||
|
Write(writer, sig.GetFieldType());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, MethodSig sig) {
|
||||||
|
writer.Write((byte)ObjectType.MethodSig);
|
||||||
|
writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention()));
|
||||||
|
Write(writer, sig.GetRetType());
|
||||||
|
foreach (var p in sig.GetParams())
|
||||||
|
Write(writer, p);
|
||||||
|
writer.Write(sig.GetParamCount());
|
||||||
|
bool hasParamsAfterSentinel = sig.GetParamsAfterSentinel() != null;
|
||||||
|
writer.Write(hasParamsAfterSentinel);
|
||||||
|
if (hasParamsAfterSentinel) {
|
||||||
|
foreach (var p in sig.GetParamsAfterSentinel())
|
||||||
|
Write(writer, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, GenericInstMethodSig sig) {
|
||||||
|
writer.Write((byte)ObjectType.GenericInstMethodSig);
|
||||||
|
writer.Write((byte)(sig == null ? 0 : sig.GetCallingConvention()));
|
||||||
|
foreach (var ga in sig.GetGenericArguments())
|
||||||
|
Write(writer, ga);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, FieldDef fd) {
|
||||||
|
writer.Write((byte)Table.Field);
|
||||||
|
Write(writer, fd.DeclaringType);
|
||||||
|
var attrMask = FieldAttributes.Static | FieldAttributes.InitOnly |
|
||||||
|
FieldAttributes.Literal | FieldAttributes.SpecialName |
|
||||||
|
FieldAttributes.PinvokeImpl | FieldAttributes.RTSpecialName;
|
||||||
|
writer.Write((ushort)(fd == null ? 0 : fd.Attributes & attrMask));
|
||||||
|
Write(writer, fd == null ? null : fd.Signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, MemberRef mr) {
|
||||||
|
writer.Write((byte)Table.MemberRef);
|
||||||
|
var parent = mr == null ? null : mr.Class;
|
||||||
|
Write(writer, parent);
|
||||||
|
bool canWriteName = IsFromNonObfuscatedAssembly(parent);
|
||||||
|
writer.Write(canWriteName);
|
||||||
|
if (canWriteName)
|
||||||
|
writer.Write(mr.Name);
|
||||||
|
Write(writer, mr == null ? null : mr.Signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, MethodDef md) {
|
||||||
|
writer.Write((byte)Table.Method);
|
||||||
|
Write(writer, md.DeclaringType);
|
||||||
|
var attrMask1 = MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask |
|
||||||
|
MethodImplAttributes.ForwardRef | MethodImplAttributes.PreserveSig |
|
||||||
|
MethodImplAttributes.InternalCall;
|
||||||
|
writer.Write((ushort)(md == null ? 0 : md.ImplAttributes & attrMask1));
|
||||||
|
var attrMask2 = MethodAttributes.Static | MethodAttributes.Virtual |
|
||||||
|
MethodAttributes.HideBySig | MethodAttributes.VtableLayoutMask |
|
||||||
|
MethodAttributes.CheckAccessOnOverride | MethodAttributes.Abstract |
|
||||||
|
MethodAttributes.SpecialName | MethodAttributes.PinvokeImpl |
|
||||||
|
MethodAttributes.UnmanagedExport | MethodAttributes.RTSpecialName;
|
||||||
|
writer.Write((ushort)(md == null ? 0 : md.Attributes & attrMask2));
|
||||||
|
Write(writer, md == null ? null : md.Signature);
|
||||||
|
writer.Write(md == null ? 0 : md.ParamDefs.Count);
|
||||||
|
writer.Write(md == null ? 0 : md.GenericParameters.Count);
|
||||||
|
writer.Write(md == null ? false : md.HasImplMap);
|
||||||
|
writer.Write(GetId(md));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, MethodSpec ms) {
|
||||||
|
writer.Write((byte)Table.MethodSpec);
|
||||||
|
Write(writer, ms == null ? null : ms.Method);
|
||||||
|
Write(writer, ms == null ? null : ms.Instantiation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, TypeDef td) {
|
||||||
|
writer.Write((byte)Table.TypeDef);
|
||||||
|
Write(writer, td == null ? null : td.BaseType);
|
||||||
|
var attrMask = TypeAttributes.LayoutMask | TypeAttributes.ClassSemanticsMask |
|
||||||
|
TypeAttributes.Abstract | TypeAttributes.SpecialName |
|
||||||
|
TypeAttributes.Import | TypeAttributes.WindowsRuntime |
|
||||||
|
TypeAttributes.StringFormatMask | TypeAttributes.RTSpecialName;
|
||||||
|
writer.Write((uint)(td == null ? 0 : td.Attributes & attrMask));
|
||||||
|
Write(writer, td == null ? null : td.BaseType);
|
||||||
|
writer.Write(td == null ? 0 : td.GenericParameters.Count);
|
||||||
|
writer.Write(td == null ? 0 : td.Interfaces.Count);
|
||||||
|
if (td != null) {
|
||||||
|
foreach (var iface in td.Interfaces)
|
||||||
|
Write(writer, iface);
|
||||||
|
}
|
||||||
|
writer.Write(GetId(td));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, TypeRef tr) {
|
||||||
|
writer.Write((byte)Table.TypeRef);
|
||||||
|
Write(writer, tr == null ? null : tr.ResolutionScope);
|
||||||
|
bool canWriteName = IsFromNonObfuscatedAssembly(tr);
|
||||||
|
writer.Write(canWriteName);
|
||||||
|
if (canWriteName) {
|
||||||
|
writer.Write(tr.Namespace);
|
||||||
|
writer.Write(tr.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, TypeSpec ts) {
|
||||||
|
writer.Write((byte)Table.TypeSpec);
|
||||||
|
Write(writer, ts == null ? null : ts.TypeSig);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(BinaryWriter writer, AssemblyRef asmRef) {
|
||||||
|
writer.Write((byte)Table.AssemblyRef);
|
||||||
|
|
||||||
|
bool canWriteAsm = IsNonObfuscatedAssembly(asmRef);
|
||||||
|
writer.Write(canWriteAsm);
|
||||||
|
if (canWriteAsm) {
|
||||||
|
bool hasPk = !PublicKeyBase.IsNullOrEmpty2(asmRef.PublicKeyOrToken);
|
||||||
|
writer.Write(hasPk);
|
||||||
|
if (hasPk)
|
||||||
|
writer.Write(PublicKeyBase.ToPublicKeyToken(asmRef.PublicKeyOrToken).Data);
|
||||||
|
writer.Write(asmRef.Name);
|
||||||
|
writer.Write(asmRef.Culture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsFromNonObfuscatedAssembly(IMemberRefParent mrp) {
|
||||||
|
return IsFromNonObfuscatedAssembly(mrp as TypeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsFromNonObfuscatedAssembly(TypeRef tr) {
|
||||||
|
if (tr == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
var asmRef = tr.ResolutionScope as AssemblyRef;
|
||||||
|
if (asmRef != null)
|
||||||
|
return IsNonObfuscatedAssembly(asmRef);
|
||||||
|
|
||||||
|
var tr2 = tr.ResolutionScope as TypeRef;
|
||||||
|
if (tr2 != null) {
|
||||||
|
tr = tr2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsNonObfuscatedAssembly(AssemblyRef asmRef) {
|
||||||
|
if (asmRef == null)
|
||||||
|
return false;
|
||||||
|
// The only external asm refs it uses...
|
||||||
|
if (asmRef.Name != "mscorlib" && asmRef.Name != "System")
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsTypeField(FieldDef fd) {
|
||||||
|
return fd != null && fd.DeclaringType == blocks.Method.DeclaringType;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetFieldId(FieldDef fd) {
|
||||||
|
if (fd == null)
|
||||||
|
return int.MinValue;
|
||||||
|
var fieldType = fd.FieldSig.GetFieldType();
|
||||||
|
if (fieldType == null)
|
||||||
|
return int.MinValue + 1;
|
||||||
|
|
||||||
|
int result = 0;
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
result += (int)fieldType.ElementType;
|
||||||
|
if (fieldType.Next == null)
|
||||||
|
break;
|
||||||
|
result += 0x100;
|
||||||
|
fieldType = fieldType.Next;
|
||||||
|
}
|
||||||
|
|
||||||
|
var td = fieldType.TryGetTypeDef();
|
||||||
|
if (td != null && td.IsEnum)
|
||||||
|
return result + 0x10000000;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Code SimplifyBranch(Code code) {
|
||||||
|
switch (code) {
|
||||||
|
case Code.Beq_S: return Code.Beq;
|
||||||
|
case Code.Bge_S: return Code.Bge;
|
||||||
|
case Code.Bgt_S: return Code.Bgt;
|
||||||
|
case Code.Ble_S: return Code.Ble;
|
||||||
|
case Code.Blt_S: return Code.Blt;
|
||||||
|
case Code.Bne_Un_S: return Code.Bne_Un;
|
||||||
|
case Code.Bge_Un_S: return Code.Bge_Un;
|
||||||
|
case Code.Bgt_Un_S: return Code.Bgt_Un;
|
||||||
|
case Code.Ble_Un_S: return Code.Ble_Un;
|
||||||
|
case Code.Blt_Un_S: return Code.Blt_Un;
|
||||||
|
case Code.Br_S: return Code.Br;
|
||||||
|
case Code.Brfalse_S: return Code.Brfalse;
|
||||||
|
case Code.Brtrue_S: return Code.Brtrue;
|
||||||
|
case Code.Leave_S: return Code.Leave;
|
||||||
|
default: return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs
Normal file
38
de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class OpCodeHandler {
|
||||||
|
public OpCodeHandlerInfo OpCodeHandlerInfo { get; private set; }
|
||||||
|
public TypeDef HandlerType { get; private set; }
|
||||||
|
public HandlerMethod ExecMethod { get; private set; }
|
||||||
|
|
||||||
|
public OpCodeHandler(OpCodeHandlerInfo opCodeHandlerInfo, TypeDef handlerType, HandlerMethod execMethod) {
|
||||||
|
this.OpCodeHandlerInfo = opCodeHandlerInfo;
|
||||||
|
this.HandlerType = handlerType;
|
||||||
|
this.ExecMethod = execMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
return OpCodeHandlerInfo.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
123
de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs
Normal file
123
de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class OpCodeHandlerInfo {
|
||||||
|
public HandlerTypeCode TypeCode { get; private set; }
|
||||||
|
public string Name { get; private set; }
|
||||||
|
public MethodSigInfo ExecSig { get; private set; }
|
||||||
|
|
||||||
|
public OpCodeHandlerInfo(HandlerTypeCode typeCode, MethodSigInfo execSig) {
|
||||||
|
this.TypeCode = typeCode;
|
||||||
|
this.Name = GetHandlerName(typeCode);
|
||||||
|
this.ExecSig = execSig;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetHandlerName(HandlerTypeCode code) {
|
||||||
|
switch (code) {
|
||||||
|
case HandlerTypeCode.Add: return "add";
|
||||||
|
case HandlerTypeCode.Add_Ovf: return "add.ovf";
|
||||||
|
case HandlerTypeCode.Add_Ovf_Un: return "add.ovf.un";
|
||||||
|
case HandlerTypeCode.And: return "and";
|
||||||
|
case HandlerTypeCode.Beq: return "beq";
|
||||||
|
case HandlerTypeCode.Bge: return "bge";
|
||||||
|
case HandlerTypeCode.Bge_Un: return "bge.un";
|
||||||
|
case HandlerTypeCode.Bgt: return "bgt";
|
||||||
|
case HandlerTypeCode.Bgt_Un: return "bgt.un";
|
||||||
|
case HandlerTypeCode.Ble: return "ble";
|
||||||
|
case HandlerTypeCode.Ble_Un: return "ble.un";
|
||||||
|
case HandlerTypeCode.Blt: return "blt";
|
||||||
|
case HandlerTypeCode.Blt_Un: return "blt.un";
|
||||||
|
case HandlerTypeCode.Bne_Un: return "bne.un";
|
||||||
|
case HandlerTypeCode.Box: return "box";
|
||||||
|
case HandlerTypeCode.Br: return "br";
|
||||||
|
case HandlerTypeCode.Brfalse: return "brfalse";
|
||||||
|
case HandlerTypeCode.Brtrue: return "brtrue";
|
||||||
|
case HandlerTypeCode.Call: return "call";
|
||||||
|
case HandlerTypeCode.Callvirt: return "callvirt";
|
||||||
|
case HandlerTypeCode.Castclass: return "castclass";
|
||||||
|
case HandlerTypeCode.Ceq: return "ceq";
|
||||||
|
case HandlerTypeCode.Cgt: return "cgt";
|
||||||
|
case HandlerTypeCode.Cgt_Un: return "cgt.un";
|
||||||
|
case HandlerTypeCode.Clt: return "clt";
|
||||||
|
case HandlerTypeCode.Clt_Un: return "clt.un";
|
||||||
|
case HandlerTypeCode.Conv: return "conv";
|
||||||
|
case HandlerTypeCode.Div: return "div";
|
||||||
|
case HandlerTypeCode.Div_Un: return "div.un";
|
||||||
|
case HandlerTypeCode.Dup: return "dup";
|
||||||
|
case HandlerTypeCode.Endfinally: return "endfinally";
|
||||||
|
case HandlerTypeCode.Initobj: return "initobj";
|
||||||
|
case HandlerTypeCode.Isinst: return "isinst";
|
||||||
|
case HandlerTypeCode.Ldarg: return "ldarg";
|
||||||
|
case HandlerTypeCode.Ldarga: return "ldarga";
|
||||||
|
case HandlerTypeCode.Ldc: return "ldc";
|
||||||
|
case HandlerTypeCode.Ldelem: return "ldelem";
|
||||||
|
case HandlerTypeCode.Ldelema: return "ldelema";
|
||||||
|
case HandlerTypeCode.Ldfld_Ldsfld: return "ldfld/ldsfld";
|
||||||
|
case HandlerTypeCode.Ldflda_Ldsflda:return "ldflda/ldsflda";
|
||||||
|
case HandlerTypeCode.Ldftn: return "ldftn";
|
||||||
|
case HandlerTypeCode.Ldlen: return "ldlen";
|
||||||
|
case HandlerTypeCode.Ldloc: return "ldloc";
|
||||||
|
case HandlerTypeCode.Ldloca: return "ldloca";
|
||||||
|
case HandlerTypeCode.Ldobj: return "ldobj";
|
||||||
|
case HandlerTypeCode.Ldstr: return "ldstr";
|
||||||
|
case HandlerTypeCode.Ldtoken: return "ldtoken";
|
||||||
|
case HandlerTypeCode.Ldvirtftn: return "ldvirtftn";
|
||||||
|
case HandlerTypeCode.Leave: return "leave";
|
||||||
|
case HandlerTypeCode.Mul: return "mul";
|
||||||
|
case HandlerTypeCode.Mul_Ovf: return "mul.ovf";
|
||||||
|
case HandlerTypeCode.Mul_Ovf_Un: return "mul.ovf.un";
|
||||||
|
case HandlerTypeCode.Neg: return "neg";
|
||||||
|
case HandlerTypeCode.Newarr: return "newarr";
|
||||||
|
case HandlerTypeCode.Newobj: return "newobj";
|
||||||
|
case HandlerTypeCode.Nop: return "nop";
|
||||||
|
case HandlerTypeCode.Not: return "not";
|
||||||
|
case HandlerTypeCode.Or: return "or";
|
||||||
|
case HandlerTypeCode.Pop: return "pop";
|
||||||
|
case HandlerTypeCode.Rem: return "rem";
|
||||||
|
case HandlerTypeCode.Rem_Un: return "rem.un";
|
||||||
|
case HandlerTypeCode.Ret: return "ret";
|
||||||
|
case HandlerTypeCode.Rethrow: return "rethrow";
|
||||||
|
case HandlerTypeCode.Shl: return "shl";
|
||||||
|
case HandlerTypeCode.Shr: return "shr";
|
||||||
|
case HandlerTypeCode.Shr_Un: return "shr.un";
|
||||||
|
case HandlerTypeCode.Starg: return "starg";
|
||||||
|
case HandlerTypeCode.Stelem: return "stelem";
|
||||||
|
case HandlerTypeCode.Stfld_Stsfld: return "stfld/stsfld";
|
||||||
|
case HandlerTypeCode.Stloc: return "stloc";
|
||||||
|
case HandlerTypeCode.Stobj: return "stobj";
|
||||||
|
case HandlerTypeCode.Sub: return "sub";
|
||||||
|
case HandlerTypeCode.Sub_Ovf: return "sub.ovf";
|
||||||
|
case HandlerTypeCode.Sub_Ovf_Un: return "sub.ovf.un";
|
||||||
|
case HandlerTypeCode.Switch: return "switch";
|
||||||
|
case HandlerTypeCode.Throw: return "throw";
|
||||||
|
case HandlerTypeCode.Unbox_Any: return "unbox.any";
|
||||||
|
case HandlerTypeCode.Xor: return "xor";
|
||||||
|
default: throw new ApplicationException("Invalid handler type code");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,528 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet.Emit;
|
||||||
|
using dnlib.DotNet;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class OpCodeHandlerInfoReader {
|
||||||
|
IInstructionOperandResolver resolver;
|
||||||
|
Dictionary<HandlerTypeCode, Func<BinaryReader, Instruction>> readHandlers;
|
||||||
|
|
||||||
|
public OpCodeHandlerInfoReader(IInstructionOperandResolver resolver) {
|
||||||
|
this.resolver = resolver;
|
||||||
|
this.readHandlers = new Dictionary<HandlerTypeCode, Func<BinaryReader, Instruction>> {
|
||||||
|
{ HandlerTypeCode.Add, Handler_Add },
|
||||||
|
{ HandlerTypeCode.Add_Ovf, Handler_Add_Ovf },
|
||||||
|
{ HandlerTypeCode.Add_Ovf_Un, Handler_Add_Ovf_Un },
|
||||||
|
{ HandlerTypeCode.And, Handler_And },
|
||||||
|
{ HandlerTypeCode.Beq, Handler_Beq },
|
||||||
|
{ HandlerTypeCode.Bge, Handler_Bge },
|
||||||
|
{ HandlerTypeCode.Bge_Un, Handler_Bge_Un },
|
||||||
|
{ HandlerTypeCode.Bgt, Handler_Bgt },
|
||||||
|
{ HandlerTypeCode.Bgt_Un, Handler_Bgt_Un },
|
||||||
|
{ HandlerTypeCode.Ble, Handler_Ble },
|
||||||
|
{ HandlerTypeCode.Ble_Un, Handler_Ble_Un },
|
||||||
|
{ HandlerTypeCode.Blt, Handler_Blt },
|
||||||
|
{ HandlerTypeCode.Blt_Un, Handler_Blt_Un },
|
||||||
|
{ HandlerTypeCode.Bne_Un, Handler_Bne_Un },
|
||||||
|
{ HandlerTypeCode.Box, Handler_Box },
|
||||||
|
{ HandlerTypeCode.Br, Handler_Br },
|
||||||
|
{ HandlerTypeCode.Brfalse, Handler_Brfalse },
|
||||||
|
{ HandlerTypeCode.Brtrue, Handler_Brtrue },
|
||||||
|
{ HandlerTypeCode.Call, Handler_Call },
|
||||||
|
{ HandlerTypeCode.Callvirt, Handler_Callvirt },
|
||||||
|
{ HandlerTypeCode.Castclass, Handler_Castclass },
|
||||||
|
{ HandlerTypeCode.Ceq, Handler_Ceq },
|
||||||
|
{ HandlerTypeCode.Cgt, Handler_Cgt },
|
||||||
|
{ HandlerTypeCode.Cgt_Un, Handler_Cgt_Un },
|
||||||
|
{ HandlerTypeCode.Clt, Handler_Clt },
|
||||||
|
{ HandlerTypeCode.Clt_Un, Handler_Clt_Un },
|
||||||
|
{ HandlerTypeCode.Conv, Handler_Conv },
|
||||||
|
{ HandlerTypeCode.Div, Handler_Div },
|
||||||
|
{ HandlerTypeCode.Div_Un, Handler_Div_Un },
|
||||||
|
{ HandlerTypeCode.Dup, Handler_Dup },
|
||||||
|
{ HandlerTypeCode.Endfinally, Handler_Endfinally },
|
||||||
|
{ HandlerTypeCode.Initobj, Handler_Initobj },
|
||||||
|
{ HandlerTypeCode.Isinst, Handler_Isinst },
|
||||||
|
{ HandlerTypeCode.Ldarg, Handler_Ldarg },
|
||||||
|
{ HandlerTypeCode.Ldarga, Handler_Ldarga },
|
||||||
|
{ HandlerTypeCode.Ldc, Handler_Ldc },
|
||||||
|
{ HandlerTypeCode.Ldelem, Handler_Ldelem },
|
||||||
|
{ HandlerTypeCode.Ldelema, Handler_Ldelema },
|
||||||
|
{ HandlerTypeCode.Ldfld_Ldsfld, Handler_Ldfld_Ldsfld },
|
||||||
|
{ HandlerTypeCode.Ldflda_Ldsflda, Handler_Ldflda_Ldsflda },
|
||||||
|
{ HandlerTypeCode.Ldftn, Handler_Ldftn },
|
||||||
|
{ HandlerTypeCode.Ldlen, Handler_Ldlen },
|
||||||
|
{ HandlerTypeCode.Ldloc, Handler_Ldloc },
|
||||||
|
{ HandlerTypeCode.Ldloca, Handler_Ldloca },
|
||||||
|
{ HandlerTypeCode.Ldobj, Handler_Ldobj },
|
||||||
|
{ HandlerTypeCode.Ldstr, Handler_Ldstr },
|
||||||
|
{ HandlerTypeCode.Ldtoken, Handler_Ldtoken },
|
||||||
|
{ HandlerTypeCode.Ldvirtftn, Handler_Ldvirtftn },
|
||||||
|
{ HandlerTypeCode.Leave, Handler_Leave },
|
||||||
|
{ HandlerTypeCode.Mul, Handler_Mul },
|
||||||
|
{ HandlerTypeCode.Mul_Ovf, Handler_Mul_Ovf },
|
||||||
|
{ HandlerTypeCode.Mul_Ovf_Un, Handler_Mul_Ovf_Un },
|
||||||
|
{ HandlerTypeCode.Neg, Handler_Neg },
|
||||||
|
{ HandlerTypeCode.Newarr, Handler_Newarr },
|
||||||
|
{ HandlerTypeCode.Newobj, Handler_Newobj },
|
||||||
|
{ HandlerTypeCode.Nop, Handler_Nop },
|
||||||
|
{ HandlerTypeCode.Not, Handler_Not },
|
||||||
|
{ HandlerTypeCode.Or, Handler_Or },
|
||||||
|
{ HandlerTypeCode.Pop, Handler_Pop },
|
||||||
|
{ HandlerTypeCode.Rem, Handler_Rem },
|
||||||
|
{ HandlerTypeCode.Rem_Un, Handler_Rem_Un },
|
||||||
|
{ HandlerTypeCode.Ret, Handler_Ret },
|
||||||
|
{ HandlerTypeCode.Rethrow, Handler_Rethrow },
|
||||||
|
{ HandlerTypeCode.Shl, Handler_Shl },
|
||||||
|
{ HandlerTypeCode.Shr, Handler_Shr },
|
||||||
|
{ HandlerTypeCode.Shr_Un, Handler_Shr_Un },
|
||||||
|
{ HandlerTypeCode.Starg, Handler_Starg },
|
||||||
|
{ HandlerTypeCode.Stelem, Handler_Stelem },
|
||||||
|
{ HandlerTypeCode.Stfld_Stsfld, Handler_Stfld_Stsfld },
|
||||||
|
{ HandlerTypeCode.Stloc, Handler_Stloc },
|
||||||
|
{ HandlerTypeCode.Stobj, Handler_Stobj },
|
||||||
|
{ HandlerTypeCode.Sub, Handler_Sub },
|
||||||
|
{ HandlerTypeCode.Sub_Ovf, Handler_Sub_Ovf },
|
||||||
|
{ HandlerTypeCode.Sub_Ovf_Un, Handler_Sub_Ovf_Un },
|
||||||
|
{ HandlerTypeCode.Switch, Handler_Switch },
|
||||||
|
{ HandlerTypeCode.Throw, Handler_Throw },
|
||||||
|
{ HandlerTypeCode.Unbox_Any, Handler_Unbox_Any },
|
||||||
|
{ HandlerTypeCode.Xor, Handler_Xor },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction Read(HandlerTypeCode typeCode, BinaryReader reader) {
|
||||||
|
Func<BinaryReader, Instruction> readHandler;
|
||||||
|
if (!readHandlers.TryGetValue(typeCode, out readHandler))
|
||||||
|
throw new ApplicationException("Invalid handler type");
|
||||||
|
return readHandler(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Add(BinaryReader reader) {
|
||||||
|
return OpCodes.Add.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Add_Ovf(BinaryReader reader) {
|
||||||
|
return OpCodes.Add_Ovf.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Add_Ovf_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Add_Ovf_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_And(BinaryReader reader) {
|
||||||
|
return OpCodes.And.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Beq(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Beq, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Bge(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Bge, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Bge_Un(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Bge_Un, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Bgt(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Bgt, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Bgt_Un(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Bgt_Un, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ble(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ble, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ble_Un(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ble_Un, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Blt(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Blt, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Blt_Un(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Blt_Un, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Bne_Un(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Bne_Un, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Box(BinaryReader reader) {
|
||||||
|
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
return OpCodes.Box.ToInstruction(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Br(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Br, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Brfalse(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Brfalse, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Brtrue(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Brtrue, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Call(BinaryReader reader) {
|
||||||
|
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
|
||||||
|
return OpCodes.Call.ToInstruction(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Callvirt(BinaryReader reader) {
|
||||||
|
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
|
||||||
|
return OpCodes.Callvirt.ToInstruction(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Castclass(BinaryReader reader) {
|
||||||
|
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
return OpCodes.Castclass.ToInstruction(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ceq(BinaryReader reader) {
|
||||||
|
return OpCodes.Ceq.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Cgt(BinaryReader reader) {
|
||||||
|
return OpCodes.Cgt.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Cgt_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Cgt_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Clt(BinaryReader reader) {
|
||||||
|
return OpCodes.Clt.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Clt_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Clt_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConvInfo {
|
||||||
|
public byte Type { get; private set; }
|
||||||
|
public bool Second { get; private set; }
|
||||||
|
public bool Third { get; private set; }
|
||||||
|
public OpCode OpCode { get; private set; }
|
||||||
|
public ConvInfo(byte type, bool second, bool third, OpCode opCode) {
|
||||||
|
this.Type = type;
|
||||||
|
this.Second = second;
|
||||||
|
this.Third = third;
|
||||||
|
this.OpCode = opCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
readonly static List<ConvInfo> instructionInfos1 = new List<ConvInfo> {
|
||||||
|
new ConvInfo(0, false, false, OpCodes.Conv_I1),
|
||||||
|
new ConvInfo(1, false, false, OpCodes.Conv_I2),
|
||||||
|
new ConvInfo(2, false, false, OpCodes.Conv_I4),
|
||||||
|
new ConvInfo(3, false, false, OpCodes.Conv_I8),
|
||||||
|
new ConvInfo(4, false, false, OpCodes.Conv_R4),
|
||||||
|
new ConvInfo(5, false, false, OpCodes.Conv_R8),
|
||||||
|
new ConvInfo(6, false, false, OpCodes.Conv_U1),
|
||||||
|
new ConvInfo(7, false, false, OpCodes.Conv_U2),
|
||||||
|
new ConvInfo(8, false, false, OpCodes.Conv_U4),
|
||||||
|
new ConvInfo(9, false, false, OpCodes.Conv_U8),
|
||||||
|
new ConvInfo(10, false, false, OpCodes.Conv_I),
|
||||||
|
new ConvInfo(11, false, false, OpCodes.Conv_U),
|
||||||
|
|
||||||
|
new ConvInfo(0, true, false, OpCodes.Conv_Ovf_I1),
|
||||||
|
new ConvInfo(1, true, false, OpCodes.Conv_Ovf_I2),
|
||||||
|
new ConvInfo(2, true, false, OpCodes.Conv_Ovf_I4),
|
||||||
|
new ConvInfo(3, true, false, OpCodes.Conv_Ovf_I8),
|
||||||
|
new ConvInfo(6, true, false, OpCodes.Conv_Ovf_U1),
|
||||||
|
new ConvInfo(7, true, false, OpCodes.Conv_Ovf_U2),
|
||||||
|
new ConvInfo(8, true, false, OpCodes.Conv_Ovf_U4),
|
||||||
|
new ConvInfo(9, true, false, OpCodes.Conv_Ovf_U8),
|
||||||
|
new ConvInfo(10, true, false, OpCodes.Conv_Ovf_I),
|
||||||
|
new ConvInfo(11, true, false, OpCodes.Conv_Ovf_U),
|
||||||
|
|
||||||
|
new ConvInfo(0, true, true, OpCodes.Conv_Ovf_I1_Un),
|
||||||
|
new ConvInfo(1, true, true, OpCodes.Conv_Ovf_I2_Un),
|
||||||
|
new ConvInfo(2, true, true, OpCodes.Conv_Ovf_I4_Un),
|
||||||
|
new ConvInfo(3, true, true, OpCodes.Conv_Ovf_I8_Un),
|
||||||
|
new ConvInfo(6, true, true, OpCodes.Conv_Ovf_U1_Un),
|
||||||
|
new ConvInfo(7, true, true, OpCodes.Conv_Ovf_U2_Un),
|
||||||
|
new ConvInfo(8, true, true, OpCodes.Conv_Ovf_U4_Un),
|
||||||
|
new ConvInfo(9, true, true, OpCodes.Conv_Ovf_U8_Un),
|
||||||
|
new ConvInfo(10, true, true, OpCodes.Conv_Ovf_I_Un),
|
||||||
|
new ConvInfo(11, true, true, OpCodes.Conv_Ovf_U_Un),
|
||||||
|
new ConvInfo(12, true, true, OpCodes.Conv_R_Un),
|
||||||
|
};
|
||||||
|
Instruction Handler_Conv(BinaryReader reader) {
|
||||||
|
byte type = reader.ReadByte();
|
||||||
|
bool second = reader.ReadBoolean();
|
||||||
|
bool third = reader.ReadBoolean();
|
||||||
|
|
||||||
|
Instruction instr = null;
|
||||||
|
foreach (var info in instructionInfos1) {
|
||||||
|
if (type != info.Type || info.Second != second || info.Third != third)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
instr = new Instruction { OpCode = info.OpCode };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (instr == null)
|
||||||
|
throw new ApplicationException("Invalid opcode");
|
||||||
|
|
||||||
|
return instr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Div(BinaryReader reader) {
|
||||||
|
return OpCodes.Div.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Div_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Div_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Dup(BinaryReader reader) {
|
||||||
|
return OpCodes.Dup.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Endfinally(BinaryReader reader) {
|
||||||
|
return OpCodes.Endfinally.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Initobj(BinaryReader reader) {
|
||||||
|
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
return OpCodes.Initobj.ToInstruction(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Isinst(BinaryReader reader) {
|
||||||
|
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
return OpCodes.Isinst.ToInstruction(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldarg(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ldarg, new ArgOperand(reader.ReadUInt16()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldarga(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ldarga, new ArgOperand(reader.ReadUInt16()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldc(BinaryReader reader) {
|
||||||
|
switch ((ElementType)reader.ReadByte()) {
|
||||||
|
case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32());
|
||||||
|
case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64());
|
||||||
|
case ElementType.R4: return OpCodes.Ldc_R4.ToInstruction(reader.ReadSingle());
|
||||||
|
case ElementType.R8: return OpCodes.Ldc_R8.ToInstruction(reader.ReadDouble());
|
||||||
|
case ElementType.Object: return OpCodes.Ldnull.ToInstruction();
|
||||||
|
default: throw new ApplicationException("Invalid instruction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldelem(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ldelem, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldelema(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ldelema, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldfld_Ldsfld(BinaryReader reader) {
|
||||||
|
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
|
||||||
|
return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsfld, OpCodes.Ldfld, field));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldflda_Ldsflda(BinaryReader reader) {
|
||||||
|
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
|
||||||
|
return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsflda, OpCodes.Ldflda, field));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldftn(BinaryReader reader) {
|
||||||
|
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
|
||||||
|
return OpCodes.Ldftn.ToInstruction(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldlen(BinaryReader reader) {
|
||||||
|
return OpCodes.Ldlen.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldloc(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ldloc, new LocalOperand(reader.ReadUInt16()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldloca(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ldloca, new LocalOperand(reader.ReadUInt16()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldobj(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Ldobj, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldstr(BinaryReader reader) {
|
||||||
|
return OpCodes.Ldstr.ToInstruction(reader.ReadString());
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldtoken(BinaryReader reader) {
|
||||||
|
var member = resolver.ResolveToken(reader.ReadUInt32()) as ITokenOperand;
|
||||||
|
return OpCodes.Ldtoken.ToInstruction(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ldvirtftn(BinaryReader reader) {
|
||||||
|
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
|
||||||
|
reader.ReadUInt32();
|
||||||
|
return OpCodes.Ldvirtftn.ToInstruction(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Leave(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Leave, new TargetDisplOperand(reader.ReadInt32()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Mul(BinaryReader reader) {
|
||||||
|
return OpCodes.Mul.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Mul_Ovf(BinaryReader reader) {
|
||||||
|
return OpCodes.Mul_Ovf.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Mul_Ovf_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Mul_Ovf_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Neg(BinaryReader reader) {
|
||||||
|
return OpCodes.Neg.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Newarr(BinaryReader reader) {
|
||||||
|
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
return OpCodes.Newarr.ToInstruction(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Newobj(BinaryReader reader) {
|
||||||
|
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
|
||||||
|
return OpCodes.Newobj.ToInstruction(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Nop(BinaryReader reader) {
|
||||||
|
return OpCodes.Nop.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Not(BinaryReader reader) {
|
||||||
|
return OpCodes.Not.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Or(BinaryReader reader) {
|
||||||
|
return OpCodes.Or.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Pop(BinaryReader reader) {
|
||||||
|
return OpCodes.Pop.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Rem(BinaryReader reader) {
|
||||||
|
return OpCodes.Rem.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Rem_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Rem_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Ret(BinaryReader reader) {
|
||||||
|
var method = resolver.ResolveToken(reader.ReadUInt32()) as IMethod;
|
||||||
|
return OpCodes.Ret.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Rethrow(BinaryReader reader) {
|
||||||
|
return OpCodes.Rethrow.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Shl(BinaryReader reader) {
|
||||||
|
return OpCodes.Shl.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Shr(BinaryReader reader) {
|
||||||
|
return OpCodes.Shr.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Shr_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Shr_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Starg(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Starg, new ArgOperand(reader.ReadUInt16()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Stelem(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Stelem, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Stfld_Stsfld(BinaryReader reader) {
|
||||||
|
var field = resolver.ResolveToken(reader.ReadUInt32()) as IField;
|
||||||
|
return new Instruction(null, new FieldInstructionOperand(OpCodes.Stsfld, OpCodes.Stfld, field));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Stloc(BinaryReader reader) {
|
||||||
|
ushort loc = reader.ReadUInt16();
|
||||||
|
var etype = (ElementType)reader.ReadInt32();
|
||||||
|
return new Instruction(OpCodes.Stloc, new LocalOperand(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Stobj(BinaryReader reader) {
|
||||||
|
return new Instruction(OpCodes.Stobj, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Sub(BinaryReader reader) {
|
||||||
|
return OpCodes.Sub.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Sub_Ovf(BinaryReader reader) {
|
||||||
|
return OpCodes.Sub_Ovf.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Sub_Ovf_Un(BinaryReader reader) {
|
||||||
|
return OpCodes.Sub_Ovf_Un.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Switch(BinaryReader reader) {
|
||||||
|
int size = reader.ReadInt32();
|
||||||
|
var offsets = new int[size];
|
||||||
|
for (int i = 0; i < size; i++)
|
||||||
|
offsets[i] = reader.ReadInt32();
|
||||||
|
return new Instruction(OpCodes.Switch, new SwitchTargetDisplOperand(offsets));
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Throw(BinaryReader reader) {
|
||||||
|
return OpCodes.Throw.ToInstruction();
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Unbox_Any(BinaryReader reader) {
|
||||||
|
var type = resolver.ResolveToken(reader.ReadUInt32()) as ITypeDefOrRef;
|
||||||
|
return OpCodes.Unbox_Any.ToInstruction(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction Handler_Xor(BinaryReader reader) {
|
||||||
|
return OpCodes.Xor.ToInstruction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
static class OpCodeHandlerInfos {
|
||||||
|
enum OpCodeHandlersFileVersion : int {
|
||||||
|
V1 = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Write(BinaryWriter writer, IList<OpCodeHandlerInfo> handlerInfos) {
|
||||||
|
WriteV1(writer, handlerInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void WriteV1(BinaryWriter writer, IList<OpCodeHandlerInfo> handlerInfos) {
|
||||||
|
writer.Write((int)OpCodeHandlersFileVersion.V1);
|
||||||
|
writer.Write(handlerInfos.Count);
|
||||||
|
foreach (var handler in handlerInfos) {
|
||||||
|
writer.Write((int)handler.TypeCode);
|
||||||
|
var infos = handler.ExecSig.BlockInfos;
|
||||||
|
writer.Write(infos.Count);
|
||||||
|
foreach (var info in infos) {
|
||||||
|
if (info.Hash == null)
|
||||||
|
writer.Write(0);
|
||||||
|
else {
|
||||||
|
writer.Write(info.Hash.Length);
|
||||||
|
writer.Write(info.Hash);
|
||||||
|
}
|
||||||
|
writer.Write(info.Targets.Count);
|
||||||
|
foreach (var target in info.Targets)
|
||||||
|
writer.Write(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<OpCodeHandlerInfo> Read(BinaryReader reader) {
|
||||||
|
switch ((OpCodeHandlersFileVersion)reader.ReadInt32()) {
|
||||||
|
case OpCodeHandlersFileVersion.V1: return ReadV1(reader);
|
||||||
|
default: throw new ApplicationException("Invalid file version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<OpCodeHandlerInfo> ReadV1(BinaryReader reader) {
|
||||||
|
int numHandlers = reader.ReadInt32();
|
||||||
|
var list = new List<OpCodeHandlerInfo>(numHandlers);
|
||||||
|
for (int i = 0; i < numHandlers; i++) {
|
||||||
|
var typeCode = (HandlerTypeCode)reader.ReadInt32();
|
||||||
|
int numInfos = reader.ReadInt32();
|
||||||
|
var sigInfo = new MethodSigInfo();
|
||||||
|
for (int j = 0; j < numInfos; j++) {
|
||||||
|
var info = new BlockInfo();
|
||||||
|
|
||||||
|
info.Hash = reader.ReadBytes(reader.ReadInt32());
|
||||||
|
if (info.Hash.Length == 0)
|
||||||
|
info.Hash = null;
|
||||||
|
|
||||||
|
int numTargets = reader.ReadInt32();
|
||||||
|
for (int k = 0; k < numTargets; k++)
|
||||||
|
info.Targets.Add(reader.ReadInt32());
|
||||||
|
|
||||||
|
sigInfo.BlockInfos.Add(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(new OpCodeHandlerInfo(typeCode, sigInfo));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static readonly IList<OpCodeHandlerInfo>[] HandlerInfos = new IList<OpCodeHandlerInfo>[] {
|
||||||
|
ReadOpCodeHandlerInfos(CsvmResources.CSVM1_v2),
|
||||||
|
ReadOpCodeHandlerInfos(CsvmResources.CSVM2_v2),
|
||||||
|
ReadOpCodeHandlerInfos(CsvmResources.CSVM3_v2),
|
||||||
|
};
|
||||||
|
|
||||||
|
static IList<OpCodeHandlerInfo> ReadOpCodeHandlerInfos(byte[] data) {
|
||||||
|
return OpCodeHandlerInfos.Read(new BinaryReader(new MemoryStream(data)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
44
de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs
Normal file
44
de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 de4dot@gmail.com
|
||||||
|
|
||||||
|
This file is part of de4dot.
|
||||||
|
|
||||||
|
de4dot is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
de4dot is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class VmOpCode {
|
||||||
|
public List<OpCodeHandlerInfo> OpCodeHandlerInfos { get; private set; }
|
||||||
|
|
||||||
|
public VmOpCode(List<OpCodeHandlerInfo> opCodeHandlerInfos) {
|
||||||
|
this.OpCodeHandlerInfos = new List<OpCodeHandlerInfo>(opCodeHandlerInfos.Count);
|
||||||
|
this.OpCodeHandlerInfos.AddRange(opCodeHandlerInfos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString() {
|
||||||
|
if (OpCodeHandlerInfos.Count == 0)
|
||||||
|
return "<nothing>";
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var handler in OpCodeHandlerInfos) {
|
||||||
|
if (sb.Length != 0)
|
||||||
|
sb.Append(", ");
|
||||||
|
sb.Append(handler.Name);
|
||||||
|
}
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,297 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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 dnlib.DotNet;
|
||||||
|
using dnlib.DotNet.Emit;
|
||||||
|
using de4dot.blocks;
|
||||||
|
using de4dot.blocks.cflow;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 {
|
||||||
|
class MyDeobfuscator {
|
||||||
|
CliSecureRtType cliSecureRtType;
|
||||||
|
StringDecrypter stringDecrypter;
|
||||||
|
StaticStringInliner staticStringInliner = new StaticStringInliner();
|
||||||
|
|
||||||
|
public MyDeobfuscator(ModuleDefMD module) {
|
||||||
|
cliSecureRtType = new CliSecureRtType(module);
|
||||||
|
cliSecureRtType.Find(null);
|
||||||
|
stringDecrypter = new StringDecrypter(module, cliSecureRtType.StringDecrypterMethod);
|
||||||
|
stringDecrypter.Find();
|
||||||
|
cliSecureRtType.FindStringDecrypterMethod();
|
||||||
|
stringDecrypter.Method = cliSecureRtType.StringDecrypterMethod;
|
||||||
|
staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt((string)args[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RestoreMethod(Blocks blocks) {
|
||||||
|
IList<Instruction> allInstructions;
|
||||||
|
IList<ExceptionHandler> allExceptionHandlers;
|
||||||
|
blocks.GetCode(out allInstructions, out allExceptionHandlers);
|
||||||
|
DotNetUtils.RestoreBody(blocks.Method, allInstructions, allExceptionHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecryptStrings(MethodDef method) {
|
||||||
|
var blocks = new Blocks(method);
|
||||||
|
DecryptStrings(blocks);
|
||||||
|
RestoreMethod(blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecryptStrings(Blocks blocks) {
|
||||||
|
staticStringInliner.Decrypt(blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deobfuscate(MethodDef method) {
|
||||||
|
DecryptStrings(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class VmOpCodeHandlerDetector {
|
||||||
|
const int NUM_HANDLERS = 78;
|
||||||
|
ModuleDefMD module;
|
||||||
|
List<VmOpCode> vmOpCodes;
|
||||||
|
MyDeobfuscator deobfuscator;
|
||||||
|
|
||||||
|
public IList<VmOpCode> Handlers {
|
||||||
|
get { return vmOpCodes; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public VmOpCodeHandlerDetector(ModuleDefMD module) {
|
||||||
|
this.module = module;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FindHandlers() {
|
||||||
|
if (vmOpCodes != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
deobfuscator = new MyDeobfuscator(module);
|
||||||
|
|
||||||
|
var csvmInfo = new CsvmInfo(module);
|
||||||
|
csvmInfo.Initialize();
|
||||||
|
var vmHandlerTypes = FindVmHandlerTypes();
|
||||||
|
if (vmHandlerTypes == null)
|
||||||
|
throw new ApplicationException("Could not find CSVM opcode handler types");
|
||||||
|
|
||||||
|
var composites = CreateCompositeOpCodeHandlers(csvmInfo, vmHandlerTypes);
|
||||||
|
foreach (var handlerInfos in OpCodeHandlerInfos.HandlerInfos) {
|
||||||
|
var otherHandlers = CreateOtherHandlers(csvmInfo, handlerInfos);
|
||||||
|
|
||||||
|
if (!DetectCompositeHandlers(composites, otherHandlers))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vmOpCodes = CreateVmOpCodes(composites);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (vmOpCodes == null)
|
||||||
|
throw new ApplicationException("Could not find any/all CSVM handlers");
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<VmOpCode> CreateVmOpCodes(IList<CompositeOpCodeHandler> composites) {
|
||||||
|
var list = new List<VmOpCode>(composites.Count);
|
||||||
|
foreach (var composite in composites)
|
||||||
|
list.Add(new VmOpCode(composite.OpCodeHandlerInfos));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetectCompositeHandlers(IEnumerable<CompositeOpCodeHandler> composites, List<OpCodeHandler> otherHandlers) {
|
||||||
|
var detector = new CompositeHandlerDetector(otherHandlers);
|
||||||
|
foreach (var composite in composites) {
|
||||||
|
if (!detector.FindHandlers(composite))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodDef SimplifyInstructions(MethodDef method) {
|
||||||
|
if (method.Body == null)
|
||||||
|
return method;
|
||||||
|
method.Body.SimplifyMacros(method.Parameters);
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CompositeOpCodeHandler> CreateCompositeOpCodeHandlers(CsvmInfo csvmInfo, List<TypeDef> handlers) {
|
||||||
|
var list = new List<CompositeOpCodeHandler>(handlers.Count);
|
||||||
|
|
||||||
|
foreach (var handler in handlers) {
|
||||||
|
var execHandler = new HandlerMethod(GetExecMethod(handler));
|
||||||
|
list.Add(new CompositeOpCodeHandler(handler, execHandler));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodDef GetExecMethod(TypeDef type) {
|
||||||
|
MethodDef readMethod, execMethod;
|
||||||
|
GetReadAndExecMethods(type, out readMethod, out execMethod);
|
||||||
|
deobfuscator.Deobfuscate(execMethod);
|
||||||
|
SimplifyInstructions(execMethod);
|
||||||
|
return execMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MethodSigInfoCreator CreateMethodSigInfoCreator(CsvmInfo csvmInfo) {
|
||||||
|
var creator = new MethodSigInfoCreator();
|
||||||
|
|
||||||
|
creator.AddId(csvmInfo.LogicalOpShrUn, 1);
|
||||||
|
creator.AddId(csvmInfo.LogicalOpShl, 2);
|
||||||
|
creator.AddId(csvmInfo.LogicalOpShr, 3);
|
||||||
|
creator.AddId(csvmInfo.LogicalOpAnd, 4);
|
||||||
|
creator.AddId(csvmInfo.LogicalOpXor, 5);
|
||||||
|
creator.AddId(csvmInfo.LogicalOpOr, 6);
|
||||||
|
|
||||||
|
creator.AddId(csvmInfo.CompareLt, 7);
|
||||||
|
creator.AddId(csvmInfo.CompareLte, 8);
|
||||||
|
creator.AddId(csvmInfo.CompareGt, 9);
|
||||||
|
creator.AddId(csvmInfo.CompareGte, 10);
|
||||||
|
creator.AddId(csvmInfo.CompareEq, 11);
|
||||||
|
creator.AddId(csvmInfo.CompareEqz, 12);
|
||||||
|
|
||||||
|
creator.AddId(csvmInfo.ArithmeticSubOvfUn, 13);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticMulOvfUn, 14);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticRemUn, 15);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticRem, 16);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticDivUn, 17);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticDiv, 18);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticMul, 19);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticMulOvf, 20);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticSub, 21);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticSubOvf, 22);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticAddOvfUn, 23);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticAddOvf, 24);
|
||||||
|
creator.AddId(csvmInfo.ArithmeticAdd, 25);
|
||||||
|
|
||||||
|
creator.AddId(csvmInfo.UnaryNot, 26);
|
||||||
|
creator.AddId(csvmInfo.UnaryNeg, 27);
|
||||||
|
|
||||||
|
creator.AddId(csvmInfo.ArgsGet, 28);
|
||||||
|
creator.AddId(csvmInfo.ArgsSet, 29);
|
||||||
|
creator.AddId(csvmInfo.LocalsGet, 30);
|
||||||
|
creator.AddId(csvmInfo.LocalsSet, 31);
|
||||||
|
|
||||||
|
return creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetReadAndExecMethods(TypeDef handler, out MethodDef readMethod, out MethodDef execMethod) {
|
||||||
|
readMethod = execMethod = null;
|
||||||
|
foreach (var method in handler.Methods) {
|
||||||
|
if (!method.IsVirtual)
|
||||||
|
continue;
|
||||||
|
if (DotNetUtils.IsMethod(method, "System.Void", "(System.IO.BinaryReader)")) {
|
||||||
|
if (readMethod != null)
|
||||||
|
throw new ApplicationException("Found another read method");
|
||||||
|
readMethod = method;
|
||||||
|
}
|
||||||
|
else if (!DotNetUtils.HasReturnValue(method) && method.MethodSig.GetParamCount() == 1) {
|
||||||
|
if (execMethod != null)
|
||||||
|
throw new ApplicationException("Found another execute method");
|
||||||
|
execMethod = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readMethod == null)
|
||||||
|
throw new ApplicationException("Could not find read method");
|
||||||
|
if (execMethod == null)
|
||||||
|
throw new ApplicationException("Could not find execute method");
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerable<TypeDef> GetVmHandlerTypes(TypeDef baseType) {
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
if (type.BaseType == baseType)
|
||||||
|
yield return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TypeDef> FindBasicVmHandlerTypes(CsvmInfo csvmInfo) {
|
||||||
|
var list = new List<TypeDef>();
|
||||||
|
if (csvmInfo.VmHandlerBaseType == null)
|
||||||
|
return list;
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
if (list.Count == NUM_HANDLERS)
|
||||||
|
break;
|
||||||
|
if (type.BaseType == csvmInfo.VmHandlerBaseType)
|
||||||
|
list.Add(type);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TypeDef> FindVmHandlerTypes() {
|
||||||
|
var requiredFields = new string[] {
|
||||||
|
null,
|
||||||
|
"System.Collections.Generic.Dictionary`2<System.UInt16,System.Type>",
|
||||||
|
"System.UInt16",
|
||||||
|
};
|
||||||
|
var cflowDeobfuscator = new CflowDeobfuscator();
|
||||||
|
foreach (var type in module.Types) {
|
||||||
|
var cctor = type.FindStaticConstructor();
|
||||||
|
if (cctor == null)
|
||||||
|
continue;
|
||||||
|
requiredFields[0] = type.FullName;
|
||||||
|
if (!new FieldTypes(type).Exactly(requiredFields))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cflowDeobfuscator.Deobfuscate(cctor);
|
||||||
|
var handlers = FindVmHandlerTypes(cctor);
|
||||||
|
if (handlers.Count < NUM_HANDLERS)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return handlers;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<TypeDef> FindVmHandlerTypes(MethodDef method) {
|
||||||
|
var list = new List<TypeDef>();
|
||||||
|
|
||||||
|
foreach (var instr in method.Body.Instructions) {
|
||||||
|
if (instr.OpCode.Code != Code.Ldtoken)
|
||||||
|
continue;
|
||||||
|
var type = instr.Operand as TypeDef;
|
||||||
|
if (type == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
list.Add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OpCodeHandler> CreateOtherHandlers(CsvmInfo csvmInfo, IList<OpCodeHandlerInfo> handlerInfos) {
|
||||||
|
var list = new List<OpCodeHandler>(NUM_HANDLERS);
|
||||||
|
|
||||||
|
foreach (var type in GetVmHandlerTypes(csvmInfo.VmHandlerBaseType)) {
|
||||||
|
if (list.Count == NUM_HANDLERS)
|
||||||
|
break;
|
||||||
|
|
||||||
|
var execHandler = new PrimitiveHandlerMethod(GetExecMethod(type));
|
||||||
|
execHandler.Sig = CreateMethodSigInfoCreator(csvmInfo).Create(execHandler.Blocks);
|
||||||
|
|
||||||
|
var finder = new MethodFinder(handlerInfos, execHandler);
|
||||||
|
var handler = finder.FindHandler();
|
||||||
|
if (handler == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
list.Add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Sort((a, b) => a.OpCodeHandlerInfo.Name.ToUpperInvariant().CompareTo(b.OpCodeHandlerInfo.Name.ToUpperInvariant()));
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
de4dot.code/deobfuscators/NullStream.cs
Normal file
87
de4dot.code/deobfuscators/NullStream.cs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2011-2013 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;
|
||||||
|
|
||||||
|
namespace de4dot.code.deobfuscators {
|
||||||
|
class NullStream : Stream {
|
||||||
|
long offset = 0;
|
||||||
|
long length = 0;
|
||||||
|
|
||||||
|
public override bool CanRead {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanSeek {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanWrite {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Length {
|
||||||
|
get { return length; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Position {
|
||||||
|
get { return offset; }
|
||||||
|
set { offset = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Read(byte[] buffer, int offset, int count) {
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin) {
|
||||||
|
switch (origin) {
|
||||||
|
case SeekOrigin.Begin:
|
||||||
|
this.offset = offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SeekOrigin.Current:
|
||||||
|
this.offset += offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SeekOrigin.End:
|
||||||
|
this.offset = length + offset;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value) {
|
||||||
|
this.length = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(byte[] buffer, int offset, int count) {
|
||||||
|
this.offset += count;
|
||||||
|
if (this.offset > this.length)
|
||||||
|
this.length = this.offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,12 +20,21 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Security;
|
||||||
using dnlib.DotNet;
|
using dnlib.DotNet;
|
||||||
using dnlib.DotNet.MD;
|
using dnlib.DotNet.MD;
|
||||||
|
using dnlib.PE;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
|
namespace System.Runtime.ExceptionServices {
|
||||||
|
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
|
||||||
|
class HandleProcessCorruptedStateExceptionsAttribute : Attribute {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace de4dot.mdecrypt {
|
namespace de4dot.mdecrypt {
|
||||||
public class DynamicMethodsDecrypter {
|
public class DynamicMethodsDecrypter {
|
||||||
static DynamicMethodsDecrypter instance;
|
static DynamicMethodsDecrypter instance;
|
||||||
|
@ -132,6 +141,9 @@ namespace de4dot.mdecrypt {
|
||||||
[DllImport("kernel32")]
|
[DllImport("kernel32")]
|
||||||
static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect);
|
static extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, uint flAllocationType, uint flProtect);
|
||||||
|
|
||||||
|
[DllImport("kernel32")]
|
||||||
|
static extern bool GetModuleHandleEx(uint dwFlags, IntPtr lpModuleName, out IntPtr phModule);
|
||||||
|
|
||||||
delegate IntPtr GetJit();
|
delegate IntPtr GetJit();
|
||||||
delegate int CompileMethod(IntPtr jitter, IntPtr comp, IntPtr info, uint flags, IntPtr nativeEntry, IntPtr nativeSizeOfCode, out bool handled);
|
delegate int CompileMethod(IntPtr jitter, IntPtr comp, IntPtr info, uint flags, IntPtr nativeEntry, IntPtr nativeSizeOfCode, out bool handled);
|
||||||
delegate int ReturnMethodToken();
|
delegate int ReturnMethodToken();
|
||||||
|
@ -364,13 +376,15 @@ namespace de4dot.mdecrypt {
|
||||||
// We're not decrypting methods
|
// We're not decrypting methods
|
||||||
|
|
||||||
var info2 = (CORINFO_METHOD_INFO*)info;
|
var info2 = (CORINFO_METHOD_INFO*)info;
|
||||||
if (info2->scope != moduleToDecryptScope) {
|
if (info2->scope != moduleToDecryptScope ||
|
||||||
|
decryptMethodsInfo.moduleCctorBytes == null ||
|
||||||
|
moduleCctorCodeRva == 0) {
|
||||||
handled = false;
|
handled = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint codeRva = (uint)((byte*)info2->ILCode - (byte*)hInstModule);
|
uint codeRva = (uint)((byte*)info2->ILCode - (byte*)hInstModule);
|
||||||
if (decryptMethodsInfo.moduleCctorBytes != null && moduleCctorCodeRva != 0 && moduleCctorCodeRva == codeRva) {
|
if (moduleCctorCodeRva == codeRva) {
|
||||||
fixed (byte* newIlCodeBytes = &decryptMethodsInfo.moduleCctorBytes[0]) {
|
fixed (byte* newIlCodeBytes = &decryptMethodsInfo.moduleCctorBytes[0]) {
|
||||||
WriteCompileMethod(origCompileMethod);
|
WriteCompileMethod(origCompileMethod);
|
||||||
info2->ILCode = new IntPtr(newIlCodeBytes);
|
info2->ILCode = new IntPtr(newIlCodeBytes);
|
||||||
|
@ -553,30 +567,78 @@ namespace de4dot.mdecrypt {
|
||||||
if (hasInstalledCompileMethod2)
|
if (hasInstalledCompileMethod2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!PatchDword(*(IntPtr*)jitterVtbl, 0x30000, origCompileMethod, ourCompileMethodInfo.ptrInDll))
|
if (!PatchCM(*(IntPtr*)jitterVtbl, origCompileMethod, ourCompileMethodInfo.ptrInDll))
|
||||||
throw new ApplicationException("Couldn't patch compileMethod");
|
throw new ApplicationException("Couldn't patch compileMethod");
|
||||||
|
|
||||||
hasInstalledCompileMethod2 = true;
|
hasInstalledCompileMethod2 = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe bool PatchDword(IntPtr addr, int size, IntPtr origValue, IntPtr newValue) {
|
static IntPtr GetModuleHandle(IntPtr addr) {
|
||||||
addr = new IntPtr(addr.ToInt64() & ~0xFFF);
|
IntPtr hModule;
|
||||||
var endAddr = new IntPtr(addr.ToInt64() + size);
|
if (!GetModuleHandleEx(4, addr, out hModule))
|
||||||
for (; addr.ToPointer() < endAddr.ToPointer(); addr = new IntPtr(addr.ToInt64() + 0x1000)) {
|
throw new ApplicationException("GetModuleHandleEx() failed");
|
||||||
try {
|
return hModule;
|
||||||
for (int i = 0; i < 0x1000; i += IntPtr.Size) {
|
}
|
||||||
var addr2 = (IntPtr*)((byte*)addr + i);
|
|
||||||
if (*addr2 == origValue) {
|
static unsafe bool PatchCM(IntPtr addr, IntPtr origValue, IntPtr newValue) {
|
||||||
*addr2 = newValue;
|
var baseAddr = GetModuleHandle(addr);
|
||||||
return true;
|
IntPtr patchAddr;
|
||||||
|
using (var peImage = new PEImage(baseAddr))
|
||||||
|
patchAddr = FindCMAddress(peImage, baseAddr, origValue);
|
||||||
|
if (patchAddr == IntPtr.Zero)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*(IntPtr*)patchAddr = newValue;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HandleProcessCorruptedStateExceptions, SecurityCritical] // Req'd on .NET 4.0
|
||||||
|
static unsafe IntPtr FindCMAddress(PEImage peImage, IntPtr baseAddr, IntPtr origValue) {
|
||||||
|
const int offset1_CLR2 = 0x78;
|
||||||
|
const int offset1_CLR4 = 0x74;
|
||||||
|
int offset1 = Environment.Version.Major == 2 ? offset1_CLR2 : offset1_CLR4;
|
||||||
|
|
||||||
|
const int offset2_CLR2 = 0x10;
|
||||||
|
const int offset2_CLR4 = 0x28;
|
||||||
|
int offset2 = Environment.Version.Major == 2 ? offset2_CLR2 : offset2_CLR4;
|
||||||
|
|
||||||
|
foreach (var section in peImage.ImageSectionHeaders) {
|
||||||
|
const uint RW = 0x80000000 | 0x40000000;
|
||||||
|
if ((section.Characteristics & RW) != RW)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
byte* p = (byte*)baseAddr + (uint)section.VirtualAddress + ((section.VirtualSize + IntPtr.Size - 1) & ~(IntPtr.Size - 1)) - IntPtr.Size;
|
||||||
|
for (; p >= (byte*)baseAddr; p -= IntPtr.Size) {
|
||||||
|
try {
|
||||||
|
byte* p2 = (byte*)*(IntPtr**)p;
|
||||||
|
if ((ulong)p2 >= 0x10000) {
|
||||||
|
p2 += offset1;
|
||||||
|
if (*(IntPtr*)p2 == origValue)
|
||||||
|
return new IntPtr(p2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
catch {
|
||||||
catch {
|
}
|
||||||
|
try {
|
||||||
|
byte* p2 = (byte*)*(IntPtr**)p;
|
||||||
|
if ((ulong)p2 >= 0x10000) {
|
||||||
|
p2 += offset2;
|
||||||
|
if (*(IntPtr*)p2 == origValue)
|
||||||
|
return new IntPtr(p2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (*(IntPtr*)p == origValue)
|
||||||
|
return new IntPtr(p);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return IntPtr.Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user