diff --git a/de4dot.code/deobfuscators/DeepSea/AssemblyResolver.cs b/de4dot.code/deobfuscators/DeepSea/AssemblyResolver.cs index 0424c2c6..146380d1 100644 --- a/de4dot.code/deobfuscators/DeepSea/AssemblyResolver.cs +++ b/de4dot.code/deobfuscators/DeepSea/AssemblyResolver.cs @@ -35,6 +35,8 @@ namespace de4dot.code.deobfuscators.DeepSea { V3Old, V3, V4, + V404, + V41, } public class AssemblyInfo { @@ -127,13 +129,19 @@ namespace de4dot.code.deobfuscators.DeepSea { simpleDeobfuscator.deobfuscate(handler); List fieldInfosTmp; - if (checkHandlerV4(handler, out fieldInfosTmp) || - checkHandlerV4_0_4(handler, out fieldInfosTmp)) { + if (checkHandlerV4(handler, out fieldInfosTmp)) { version = Version.V4; fieldInfos = fieldInfosTmp; return true; } + Version versionTmp = checkHandlerV404_41(handler, out fieldInfosTmp); + if (fieldInfosTmp.Count != 0) { + version = versionTmp; + fieldInfos = fieldInfosTmp; + return true; + } + return false; } @@ -202,12 +210,13 @@ namespace de4dot.code.deobfuscators.DeepSea { return fieldInfos.Count != 0; } - // 4.0.4+ - bool checkHandlerV4_0_4(MethodDefinition handler, out List fieldInfos) { + // 4.0.4, 4.1+ + Version checkHandlerV404_41(MethodDefinition handler, out List fieldInfos) { + Version version = Version.Unknown; fieldInfos = new List(); var instrs = handler.Body.Instructions; - for (int i = 0; i < instrs.Count - 8; i++) { + for (int i = 0; i < instrs.Count - 6; i++) { int index = i; var ldci4_len = instrs[index++]; @@ -233,26 +242,136 @@ namespace de4dot.code.deobfuscators.DeepSea { if (!DotNetUtils.isMethod(call1.Operand as MethodReference, "System.Void", "(System.Array,System.RuntimeFieldHandle)")) continue; - if (!DotNetUtils.isLdloc(instrs[index++])) - continue; - - var ldci4_magic = instrs[index++]; - if (!DotNetUtils.isLdcI4(ldci4_magic)) - continue; - int magic = DotNetUtils.getLdcI4Value(ldci4_magic); - - var call2 = instrs[index++]; - if (call2.OpCode.Code == Code.Tail) - call2 = instrs[index++]; - if (call2.OpCode.Code != Code.Call) - continue; - if (!DotNetUtils.isMethod(call2.Operand as MethodReference, "System.Reflection.Assembly", "(System.Byte[],System.Int32)")) + int callIndex = getCallDecryptMethodIndex(instrs, index); + if (callIndex < 0) continue; + var args = DsUtils.getArgValues(instrs, callIndex); + if (args == null) + continue; + var decryptMethod = instrs[callIndex].Operand as MethodDefinition; + if (decryptMethod == null) + continue; + int magic; + Version versionTmp; + getMagic(decryptMethod, args, out versionTmp, out magic); + version = versionTmp; fieldInfos.Add(new FieldInfo(field, magic)); } - return fieldInfos.Count != 0; + return version; + } + + static bool getMagic(MethodDefinition method, IList args, out Version version, out int magic) { + magic = 0; + int magicIndex = getMagicIndex(method, out version); + if (magicIndex < 0 || magicIndex >= args.Count) + return false; + var val = args[magicIndex]; + if (!(val is int)) + return false; + + magic = (int)val; + return true; + } + + static int getMagicIndex(MethodDefinition method, out Version version) { + int magicIndex = getMagicIndex404(method); + if (magicIndex >= 0) { + version = Version.V404; + return magicIndex; + } + + magicIndex = getMagicIndex41Trial(method); + if (magicIndex >= 0) { + version = Version.V41; + return magicIndex; + } + + version = Version.Unknown; + return -1; + } + + static int getMagicIndex404(MethodDefinition method) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 4; i++) { + int index = i; + if (!DotNetUtils.isLdloc(instrs[index++])) + continue; + var ldarg = instrs[index++]; + if (!DotNetUtils.isLdarg(ldarg)) + continue; + if (instrs[index++].OpCode.Code != Code.Add) + continue; + var ldci4 = instrs[index++]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + if (DotNetUtils.getLdcI4Value(ldci4) != 0xFF) + continue; + return DotNetUtils.getArgIndex(ldarg); + } + return -1; + } + + static int getMagicIndex41Trial(MethodDefinition method) { + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count - 4; i++) { + int index = i; + if (instrs[index++].OpCode.Code != Code.Div) + continue; + var ldarg = instrs[index++]; + if (!DotNetUtils.isLdarg(ldarg)) + continue; + if (instrs[index++].OpCode.Code != Code.Add) + continue; + var ldci4 = instrs[index++]; + if (!DotNetUtils.isLdcI4(ldci4)) + continue; + if (DotNetUtils.getLdcI4Value(ldci4) != 0xFF) + continue; + return DotNetUtils.getArgIndex(ldarg); + } + return -1; + } + + static int getCallDecryptMethodIndex(IList instrs, int index) { + index = getRetIndex(instrs, index); + if (index < 0) + return -1; + for (int i = index - 1; i >= 0; i--) { + var instr = instrs[i]; + if (!isCallOrNext(instr)) + break; + if (instr.OpCode.Code != Code.Call) + continue; + var calledMethod = instr.Operand as MethodReference; + if (calledMethod == null || calledMethod.Parameters.Count < 2) + continue; + + return i; + } + return -1; + } + + static int getRetIndex(IList instrs, int index) { + for (int i = index; i < instrs.Count; i++) { + var instr = instrs[i]; + if (instr.OpCode.Code == Code.Ret) + return i; + if (!isCallOrNext(instr)) + break; + } + return -1; + } + + static bool isCallOrNext(Instruction instr) { + switch (instr.OpCode.FlowControl) { + case FlowControl.Call: + case FlowControl.Next: + return true; + default: + return false; + } } public IEnumerable getAssemblyInfos() { @@ -264,7 +383,10 @@ namespace de4dot.code.deobfuscators.DeepSea { case Version.V3: return getAssemblyInfosV3(); case Version.V4: + case Version.V404: return getAssemblyInfosV4(); + case Version.V41: + return getAssemblyInfosV41(); default: throw new ApplicationException("Unknown version"); } @@ -306,14 +428,14 @@ namespace de4dot.code.deobfuscators.DeepSea { return new AssemblyInfo(decryptedData, fullName, simpleName, extension, resource); } - IEnumerable getAssemblyInfosV4() { + IEnumerable getAssemblyInfos(Func decrypter) { var infos = new List(); if (fieldInfos == null) return infos; foreach (var fieldInfo in fieldInfos) { - var decrypted = decryptResourceV4(fieldInfo.field.InitialValue, fieldInfo.magic); + var decrypted = decrypter(fieldInfo.field.InitialValue, fieldInfo.magic); infos.Add(getAssemblyInfo(decrypted, null)); fieldInfo.field.InitialValue = new byte[1]; fieldInfo.field.FieldType = module.TypeSystem.Byte; @@ -321,5 +443,19 @@ namespace de4dot.code.deobfuscators.DeepSea { return infos; } + + IEnumerable getAssemblyInfosV4() { + return getAssemblyInfos((data, magic) => decryptResourceV4(data, magic)); + } + + IEnumerable getAssemblyInfosV41() { + return getAssemblyInfos((data, magic) => inflateIfNeeded(decrypt41Trial(data, magic))); + } + + static byte[] decrypt41Trial(byte[] data, int magic) { + for (int i = 0; i < data.Length; i++) + data[i] ^= (byte)(i / 3 + magic); + return data; + } } } diff --git a/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs b/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs index 74b0a2b1..a6890000 100644 --- a/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs +++ b/de4dot.code/deobfuscators/DeepSea/ResolverBase.cs @@ -158,7 +158,14 @@ namespace de4dot.code.deobfuscators.DeepSea { protected static byte[] decryptResource(byte[] data, int start, int len, int magic) { for (int i = start; i < start + len; i++) data[i] ^= (byte)(i - start + magic); + return inflateIfNeeded(data, start, len); + } + protected static byte[] inflateIfNeeded(byte[] data) { + return inflateIfNeeded(data, 0, data.Length); + } + + protected static byte[] inflateIfNeeded(byte[] data, int start, int len) { if (BitConverter.ToInt16(data, start) != 0x5A4D) return DeobUtils.inflate(data, start, len, true);