Decrypt DS 4.1 strings
This commit is contained in:
parent
a1daee56f8
commit
f307520e62
|
@ -153,8 +153,10 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
if (detectMethodProxyObfuscation())
|
if (detectMethodProxyObfuscation())
|
||||||
return DeobfuscatorInfo.THE_NAME + " 3.5";
|
return DeobfuscatorInfo.THE_NAME + " 3.5";
|
||||||
return DeobfuscatorInfo.THE_NAME + " 1.x-3.x";
|
return DeobfuscatorInfo.THE_NAME + " 1.x-3.x";
|
||||||
case StringDecrypter.DecrypterVersion.V4:
|
case StringDecrypter.DecrypterVersion.V4_0:
|
||||||
return DeobfuscatorInfo.THE_NAME + " 4.x";
|
return DeobfuscatorInfo.THE_NAME + " 4.0";
|
||||||
|
case StringDecrypter.DecrypterVersion.V4_1:
|
||||||
|
return DeobfuscatorInfo.THE_NAME + " 4.1";
|
||||||
}
|
}
|
||||||
|
|
||||||
return DeobfuscatorInfo.THE_NAME;
|
return DeobfuscatorInfo.THE_NAME;
|
||||||
|
|
|
@ -22,6 +22,7 @@ using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Mono.Cecil;
|
using Mono.Cecil;
|
||||||
using Mono.Cecil.Cil;
|
using Mono.Cecil.Cil;
|
||||||
|
using Mono.Cecil.Metadata;
|
||||||
using de4dot.blocks;
|
using de4dot.blocks;
|
||||||
|
|
||||||
namespace de4dot.code.deobfuscators.DeepSea {
|
namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
@ -33,7 +34,8 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
public enum DecrypterVersion {
|
public enum DecrypterVersion {
|
||||||
Unknown,
|
Unknown,
|
||||||
V1_3,
|
V1_3,
|
||||||
V4,
|
V4_0,
|
||||||
|
V4_1,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IDecrypterInfo {
|
interface IDecrypterInfo {
|
||||||
|
@ -94,9 +96,204 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DecrypterInfo4 : IDecrypterInfo {
|
static bool findMagic(MethodDefinition method, out int magic) {
|
||||||
|
int arg1, arg2;
|
||||||
|
return findMagic(method, out arg1, out arg2, out magic);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool findMagic(MethodDefinition method, out int arg1, out int arg2, out int magic) {
|
||||||
|
var instrs = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instrs.Count - 3; i++) {
|
||||||
|
if ((arg1 = DotNetUtils.getArgIndex(instrs[i])) < 0)
|
||||||
|
continue;
|
||||||
|
var ldci4 = instrs[i + 1];
|
||||||
|
if (!DotNetUtils.isLdcI4(ldci4))
|
||||||
|
continue;
|
||||||
|
if (instrs[i + 2].OpCode.Code != Code.Xor)
|
||||||
|
continue;
|
||||||
|
if ((arg2 = DotNetUtils.getArgIndex(instrs[i + 3])) < 0)
|
||||||
|
continue;
|
||||||
|
magic = DotNetUtils.getLdcI4Value(ldci4);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
arg1 = arg2 = 0;
|
||||||
|
magic = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DecrypterInfo41 : IDecrypterInfo {
|
||||||
|
MethodDefinition cctor;
|
||||||
|
int magic;
|
||||||
|
int arg1, arg2;
|
||||||
|
FieldDefinitionAndDeclaringTypeDict<bool> fields;
|
||||||
|
ArrayInfo arrayInfo;
|
||||||
|
ushort[] encryptedData;
|
||||||
|
ushort[] key;
|
||||||
|
|
||||||
|
class ArrayInfo {
|
||||||
|
public int sizeInElems;
|
||||||
|
public TypeReference elementType;
|
||||||
|
public FieldDefinition initField;
|
||||||
|
public FieldDefinition field;
|
||||||
|
|
||||||
|
public ArrayInfo(int sizeInElems, TypeReference elementType, FieldDefinition initField, FieldDefinition field) {
|
||||||
|
this.sizeInElems = sizeInElems;
|
||||||
|
this.elementType = elementType;
|
||||||
|
this.initField = initField;
|
||||||
|
this.field = field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecrypterVersion Version {
|
||||||
|
get { return DecrypterVersion.V4_1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodDefinition Method { get; private set; }
|
||||||
|
|
||||||
|
public DecrypterInfo41(MethodDefinition cctor, MethodDefinition method) {
|
||||||
|
this.cctor = cctor;
|
||||||
|
Method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool isPossibleDecrypterMethod(MethodDefinition method) {
|
||||||
|
if (!checkMethodSignature(method))
|
||||||
|
return false;
|
||||||
|
var fields = getFields(method);
|
||||||
|
if (fields == null || fields.Count != 3)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkMethodSignature(MethodDefinition method) {
|
||||||
|
if (method.MethodReturnType.ReturnType.EType != ElementType.String)
|
||||||
|
return false;
|
||||||
|
int count = 0;
|
||||||
|
foreach (var arg in method.Parameters) {
|
||||||
|
if (arg.ParameterType.EType == ElementType.I4)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count >= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FieldDefinitionAndDeclaringTypeDict<bool> getFields(MethodDefinition method) {
|
||||||
|
var fields = new FieldDefinitionAndDeclaringTypeDict<bool>();
|
||||||
|
foreach (var instr in method.Body.Instructions) {
|
||||||
|
if (instr.OpCode.Code != Code.Ldsfld && instr.OpCode.Code != Code.Stsfld)
|
||||||
|
continue;
|
||||||
|
var field = instr.Operand as FieldDefinition;
|
||||||
|
if (field == null)
|
||||||
|
continue;
|
||||||
|
if (field.DeclaringType != method.DeclaringType)
|
||||||
|
continue;
|
||||||
|
fields.add(field, true);
|
||||||
|
}
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool initialize() {
|
||||||
|
if (!findMagic(Method, out arg1, out arg2, out magic))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
fields = getFields(Method);
|
||||||
|
if (fields == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
arrayInfo = getArrayInfo(cctor);
|
||||||
|
if (arrayInfo == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (arrayInfo.initField.InitialValue.Length % 2 == 1)
|
||||||
|
return false;
|
||||||
|
encryptedData = new ushort[arrayInfo.initField.InitialValue.Length / 2];
|
||||||
|
Buffer.BlockCopy(arrayInfo.initField.InitialValue, 0, encryptedData, 0, arrayInfo.initField.InitialValue.Length);
|
||||||
|
|
||||||
|
key = findKey();
|
||||||
|
if (key == null || key.Length == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayInfo getArrayInfo(MethodDefinition method) {
|
||||||
|
var instructions = method.Body.Instructions;
|
||||||
|
for (int i = 0; i < instructions.Count; i++) {
|
||||||
|
var ldci4_arraySizeInBytes = instructions[i];
|
||||||
|
if (!DotNetUtils.isLdcI4(ldci4_arraySizeInBytes))
|
||||||
|
continue;
|
||||||
|
i++;
|
||||||
|
var instrs = DotNetUtils.getInstructions(instructions, i, OpCodes.Newarr, OpCodes.Dup, OpCodes.Ldtoken, OpCodes.Call, OpCodes.Stsfld);
|
||||||
|
if (instrs == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int sizeInBytes = DotNetUtils.getLdcI4Value(ldci4_arraySizeInBytes);
|
||||||
|
var elementType = instrs[0].Operand as TypeReference;
|
||||||
|
var initField = instrs[2].Operand as FieldDefinition;
|
||||||
|
var field = instrs[4].Operand as FieldDefinition;
|
||||||
|
if (elementType == null)
|
||||||
|
continue;
|
||||||
|
if (initField == null || initField.InitialValue == null || initField.InitialValue.Length == 0)
|
||||||
|
continue;
|
||||||
|
if (!fields.find(field))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return new ArrayInfo(sizeInBytes, elementType, initField, field);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort[] findKey() {
|
||||||
|
if (cctor.Module.Assembly == null)
|
||||||
|
return null;
|
||||||
|
var pkt = cctor.Module.Assembly.Name.PublicKeyToken;
|
||||||
|
if (pkt != null && pkt.Length > 0)
|
||||||
|
return getPublicKeyTokenKey(pkt);
|
||||||
|
return findKey(cctor);
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort[] findKey(MethodDefinition initMethod) {
|
||||||
|
throw new NotImplementedException(); //TODO:
|
||||||
|
}
|
||||||
|
|
||||||
|
static ushort[] getPublicKeyTokenKey(byte[] publicKeyToken) {
|
||||||
|
var key = new ushort[publicKeyToken.Length];
|
||||||
|
for (int i = 0; i < publicKeyToken.Length; i++) {
|
||||||
|
int b = publicKeyToken[i];
|
||||||
|
key[i] = (ushort)((b << 7) ^ b);
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string decrypt(object[] args) {
|
||||||
|
return decrypt((int)args[arg1], (int)args[arg2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
string decrypt(int magic2, int magic3) {
|
||||||
|
int offset = magic ^ magic2 ^ magic3;
|
||||||
|
var keyChar = encryptedData[offset + 2];
|
||||||
|
int cachedIndex = encryptedData[offset + 1] ^ keyChar;
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
int flags = encryptedData[offset] ^ keyChar;
|
||||||
|
int numChars = (flags >> 1) & ~7 | (flags & 7);
|
||||||
|
if ((flags & 8) != 0) {
|
||||||
|
numChars <<= 15;
|
||||||
|
numChars |= encryptedData[offset + 3] ^ keyChar;
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
offset += 3;
|
||||||
|
for (int i = 0; i < numChars; i++)
|
||||||
|
sb.Append((char)(keyChar ^ encryptedData[offset + numChars - i - 1] ^ key[(i + 1 + offset) % key.Length]));
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void cleanup() {
|
||||||
|
arrayInfo.initField.InitialValue = new byte[1];
|
||||||
|
arrayInfo.initField.FieldType = arrayInfo.initField.Module.TypeSystem.Byte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DecrypterInfo40 : IDecrypterInfo {
|
||||||
MethodDefinition cctor;
|
MethodDefinition cctor;
|
||||||
public MethodDefinition Method { get; set; }
|
|
||||||
int magic;
|
int magic;
|
||||||
FieldDefinition cachedStringsField;
|
FieldDefinition cachedStringsField;
|
||||||
FieldDefinition keyField;
|
FieldDefinition keyField;
|
||||||
|
@ -105,15 +302,23 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
short[] key;
|
short[] key;
|
||||||
ushort[] encryptedData;
|
ushort[] encryptedData;
|
||||||
|
|
||||||
|
public MethodDefinition Method { get; private set; }
|
||||||
|
|
||||||
public DecrypterVersion Version {
|
public DecrypterVersion Version {
|
||||||
get { return DecrypterVersion.V4; }
|
get { return DecrypterVersion.V4_0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public DecrypterInfo4(MethodDefinition cctor, MethodDefinition method) {
|
public DecrypterInfo40(MethodDefinition cctor, MethodDefinition method) {
|
||||||
this.cctor = cctor;
|
this.cctor = cctor;
|
||||||
this.Method = method;
|
this.Method = method;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool isPossibleDecrypterMethod(MethodDefinition method) {
|
||||||
|
if (!checkFields(method.DeclaringType.Fields))
|
||||||
|
return false;
|
||||||
|
return DotNetUtils.isMethod(method, "System.String", "(System.Int32,System.Int32)");
|
||||||
|
}
|
||||||
|
|
||||||
public bool initialize() {
|
public bool initialize() {
|
||||||
if (!findMagic(Method, out magic))
|
if (!findMagic(Method, out magic))
|
||||||
return false;
|
return false;
|
||||||
|
@ -140,24 +345,6 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool findMagic(MethodDefinition method, out int magic) {
|
|
||||||
var instrs = method.Body.Instructions;
|
|
||||||
for (int i = 0; i < instrs.Count - 2; i++) {
|
|
||||||
var ldarg = instrs[i];
|
|
||||||
if (DotNetUtils.getArgIndex(ldarg) < 0)
|
|
||||||
continue;
|
|
||||||
var ldci4 = instrs[i + 1];
|
|
||||||
if (!DotNetUtils.isLdcI4(ldci4))
|
|
||||||
continue;
|
|
||||||
if (instrs[i + 2].OpCode.Code != Code.Xor)
|
|
||||||
continue;
|
|
||||||
magic = DotNetUtils.getLdcI4Value(ldci4);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
magic = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<FieldDefinition> findFields() {
|
List<FieldDefinition> findFields() {
|
||||||
var charArrayFields = new List<FieldDefinition>();
|
var charArrayFields = new List<FieldDefinition>();
|
||||||
|
|
||||||
|
@ -263,20 +450,27 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DecrypterInfo3 : IDecrypterInfo {
|
class DecrypterInfo13 : IDecrypterInfo {
|
||||||
MethodDefinition cctor;
|
MethodDefinition cctor;
|
||||||
public MethodDefinition Method { get; set; }
|
|
||||||
FieldDefinition cachedStringsField;
|
FieldDefinition cachedStringsField;
|
||||||
FieldDefinition keyField;
|
FieldDefinition keyField;
|
||||||
int magic;
|
int magic;
|
||||||
string[] encryptedStrings;
|
string[] encryptedStrings;
|
||||||
short[] key;
|
short[] key;
|
||||||
|
|
||||||
|
public MethodDefinition Method { get; private set; }
|
||||||
|
|
||||||
public DecrypterVersion Version {
|
public DecrypterVersion Version {
|
||||||
get { return DecrypterVersion.V1_3; }
|
get { return DecrypterVersion.V1_3; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public DecrypterInfo3(MethodDefinition cctor, MethodDefinition method) {
|
public static bool isPossibleDecrypterMethod(MethodDefinition method) {
|
||||||
|
if (!checkFields(method.DeclaringType.Fields))
|
||||||
|
return false;
|
||||||
|
return DotNetUtils.isMethod(method, "System.String", "(System.Int32)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecrypterInfo13(MethodDefinition cctor, MethodDefinition method) {
|
||||||
this.cctor = cctor;
|
this.cctor = cctor;
|
||||||
this.Method = method;
|
this.Method = method;
|
||||||
}
|
}
|
||||||
|
@ -442,27 +636,31 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
|
|
||||||
bool hasPublicKeyToken = module.Assembly.Name.PublicKeyToken != null && module.Assembly.Name.PublicKeyToken.Length != 0;
|
bool hasPublicKeyToken = module.Assembly.Name.PublicKeyToken != null && module.Assembly.Name.PublicKeyToken.Length != 0;
|
||||||
foreach (var type in module.GetTypes()) {
|
foreach (var type in module.GetTypes()) {
|
||||||
if (!checkFields(type.Fields))
|
|
||||||
continue;
|
|
||||||
var cctor = DotNetUtils.getMethod(type, ".cctor");
|
var cctor = DotNetUtils.getMethod(type, ".cctor");
|
||||||
if (cctor == null)
|
if (cctor == null)
|
||||||
continue;
|
continue;
|
||||||
if (!hasPublicKeyToken)
|
|
||||||
simpleDeobfuscator.deobfuscate(cctor);
|
|
||||||
|
|
||||||
|
bool deobfuscatedCctor = false;
|
||||||
foreach (var method in type.Methods) {
|
foreach (var method in type.Methods) {
|
||||||
if (method.Body == null)
|
if (!method.IsStatic || method.Body == null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
IDecrypterInfo info = null;
|
IDecrypterInfo info = null;
|
||||||
|
|
||||||
if (DotNetUtils.isMethod(method, "System.String", "(System.Int32)")) {
|
if (DecrypterInfo13.isPossibleDecrypterMethod(method)) {
|
||||||
|
deobfuscateCctor(simpleDeobfuscator, cctor, ref deobfuscatedCctor, hasPublicKeyToken);
|
||||||
simpleDeobfuscator.deobfuscate(method);
|
simpleDeobfuscator.deobfuscate(method);
|
||||||
info = getInfoV3(cctor, method);
|
info = getInfoV13(cctor, method);
|
||||||
}
|
}
|
||||||
else if (DotNetUtils.isMethod(method, "System.String", "(System.Int32,System.Int32)")) {
|
else if (DecrypterInfo40.isPossibleDecrypterMethod(method)) {
|
||||||
|
deobfuscateCctor(simpleDeobfuscator, cctor, ref deobfuscatedCctor, hasPublicKeyToken);
|
||||||
simpleDeobfuscator.deobfuscate(method);
|
simpleDeobfuscator.deobfuscate(method);
|
||||||
info = getInfoV4(cctor, method);
|
info = getInfoV40(cctor, method);
|
||||||
|
}
|
||||||
|
else if (DecrypterInfo41.isPossibleDecrypterMethod(method)) {
|
||||||
|
deobfuscateCctor(simpleDeobfuscator, cctor, ref deobfuscatedCctor, hasPublicKeyToken);
|
||||||
|
simpleDeobfuscator.deobfuscate(method);
|
||||||
|
info = getInfoV41(cctor, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (info == null)
|
if (info == null)
|
||||||
|
@ -473,6 +671,13 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void deobfuscateCctor(ISimpleDeobfuscator simpleDeobfuscator, MethodDefinition cctor, ref bool deobfuscatedCctor, bool hasPublicKeyToken) {
|
||||||
|
if (deobfuscatedCctor || hasPublicKeyToken)
|
||||||
|
return;
|
||||||
|
simpleDeobfuscator.deobfuscate(cctor);
|
||||||
|
deobfuscatedCctor = true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool checkFields(IEnumerable<FieldDefinition> fields) {
|
static bool checkFields(IEnumerable<FieldDefinition> fields) {
|
||||||
bool foundCharAry = false, foundStringAry = false;
|
bool foundCharAry = false, foundStringAry = false;
|
||||||
foreach (var field in fields) {
|
foreach (var field in fields) {
|
||||||
|
@ -490,15 +695,22 @@ namespace de4dot.code.deobfuscators.DeepSea {
|
||||||
return foundCharAry && foundStringAry;
|
return foundCharAry && foundStringAry;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecrypterInfo3 getInfoV3(MethodDefinition cctor, MethodDefinition method) {
|
DecrypterInfo13 getInfoV13(MethodDefinition cctor, MethodDefinition method) {
|
||||||
var info = new DecrypterInfo3(cctor, method);
|
var info = new DecrypterInfo13(cctor, method);
|
||||||
if (!info.initialize())
|
if (!info.initialize())
|
||||||
return null;
|
return null;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
DecrypterInfo4 getInfoV4(MethodDefinition cctor, MethodDefinition method) {
|
DecrypterInfo40 getInfoV40(MethodDefinition cctor, MethodDefinition method) {
|
||||||
var info = new DecrypterInfo4(cctor, method);
|
var info = new DecrypterInfo40(cctor, method);
|
||||||
|
if (!info.initialize())
|
||||||
|
return null;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecrypterInfo41 getInfoV41(MethodDefinition cctor, MethodDefinition method) {
|
||||||
|
var info = new DecrypterInfo41(cctor, method);
|
||||||
if (!info.initialize())
|
if (!info.initialize())
|
||||||
return null;
|
return null;
|
||||||
return info;
|
return info;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user