diff --git a/AssemblyData/AssemblyData.csproj b/AssemblyData/AssemblyData.csproj index 847bed52..f34df5c0 100644 --- a/AssemblyData/AssemblyData.csproj +++ b/AssemblyData/AssemblyData.csproj @@ -41,8 +41,14 @@ + + + + + + @@ -58,6 +64,7 @@ + @@ -66,9 +73,9 @@ - + {045B96F2-AF80-4C4C-8D27-E38635AC705E} - blocks + de4dot.blocks {5C93C5E2-196F-4877-BF65-96FEBFCEFCA1} diff --git a/AssemblyData/AssemblyServer.cs b/AssemblyData/AssemblyServer.cs index 44c9ce0b..a9110bd1 100644 --- a/AssemblyData/AssemblyServer.cs +++ b/AssemblyData/AssemblyServer.cs @@ -27,12 +27,13 @@ using AssemblyData; namespace AssemblyServer { public static class Start { public static int Main2(string[] args) { - if (args.Length != 2) + if (args.Length != 3) Environment.Exit(1); - var channelName = args[0]; - var uri = args[1]; + var serviceType = (AssemblyServiceType)int.Parse(args[0]); + var channelName = args[1]; + var uri = args[2]; - var service = new AssemblyService(); + var service = (AssemblyService)AssemblyService.Create(serviceType); StartServer(service, channelName, uri); service.WaitExit(); return 0; diff --git a/AssemblyData/AssemblyService.cs b/AssemblyData/AssemblyService.cs index 7b691efc..74798b51 100644 --- a/AssemblyData/AssemblyService.cs +++ b/AssemblyData/AssemblyService.cs @@ -18,25 +18,51 @@ */ using System; -using System.Collections.Generic; using System.Reflection; using System.Threading; -using dnlib.DotNet; -using de4dot.blocks; -using de4dot.mdecrypt; namespace AssemblyData { - public class AssemblyService : MarshalByRefObject, IAssemblyService { - IStringDecrypter stringDecrypter = null; + public abstract class AssemblyService : MarshalByRefObject, IAssemblyService { ManualResetEvent exitEvent = new ManualResetEvent(false); - Assembly assembly = null; + protected Assembly assembly = null; AssemblyResolver assemblyResolver = new AssemblyResolver(); - bool installCompileMethodCalled = false; + + public static AssemblyService Create(AssemblyServiceType serviceType) { + switch (serviceType) { + case AssemblyServiceType.StringDecrypter: + return new StringDecrypterService(); + + case AssemblyServiceType.MethodDecrypter: + return new MethodDecrypterService(); + + case AssemblyServiceType.Generic: + return new GenericService(); + + default: + throw new ArgumentException("Invalid assembly service type"); + } + } + + public static Type GetType(AssemblyServiceType serviceType) { + switch (serviceType) { + case AssemblyServiceType.StringDecrypter: + return typeof(StringDecrypterService); + + case AssemblyServiceType.MethodDecrypter: + return typeof(MethodDecrypterService); + + case AssemblyServiceType.Generic: + return typeof(GenericService); + + default: + throw new ArgumentException("Invalid assembly service type"); + } + } public void DoNothing() { } - public void Exit() { + public virtual void Exit() { exitEvent.Set(); } @@ -48,17 +74,12 @@ namespace AssemblyData { return null; } - void CheckStringDecrypter() { - if (stringDecrypter == null) - throw new ApplicationException("setStringDecrypterType() hasn't been called yet."); - } - - void CheckAssembly() { + protected void CheckAssembly() { if (assembly == null) - throw new ApplicationException("loadAssembly() hasn't been called yet."); + throw new ApplicationException("LoadAssembly() hasn't been called yet."); } - public void LoadAssembly(string filename) { + protected void LoadAssemblyInternal(string filename) { if (assembly != null) throw new ApplicationException("Only one assembly can be explicitly loaded"); try { @@ -68,86 +89,5 @@ namespace AssemblyData { throw new ApplicationException(string.Format("Could not load assembly {0}. Maybe it's 32-bit or 64-bit only?", filename)); } } - - public void SetStringDecrypterType(StringDecrypterType type) { - if (stringDecrypter != null) - throw new ApplicationException("StringDecrypterType already set"); - - switch (type) { - case StringDecrypterType.Delegate: - stringDecrypter = new DelegateStringDecrypter(); - break; - - case StringDecrypterType.Emulate: - stringDecrypter = new EmuStringDecrypter(); - break; - - default: - throw new ApplicationException(string.Format("Unknown StringDecrypterType {0}", type)); - } - } - - public int DefineStringDecrypter(int methodToken) { - CheckStringDecrypter(); - var methodInfo = FindMethod(methodToken); - if (methodInfo == null) - throw new ApplicationException(string.Format("Could not find method {0:X8}", methodToken)); - if (methodInfo.ReturnType != typeof(string) && methodInfo.ReturnType != typeof(object)) - throw new ApplicationException(string.Format("Method return type must be string or object: {0}", methodInfo)); - return stringDecrypter.DefineStringDecrypter(methodInfo); - } - - public object[] DecryptStrings(int stringDecrypterMethod, object[] args, int callerToken) { - CheckStringDecrypter(); - var caller = GetCaller(callerToken); - foreach (var arg in args) - SimpleData.Unpack((object[])arg); - return SimpleData.Pack(stringDecrypter.DecryptStrings(stringDecrypterMethod, args, caller)); - } - - MethodBase GetCaller(int callerToken) { - try { - return assembly.GetModules()[0].ResolveMethod(callerToken); - } - catch { - return null; - } - } - - MethodInfo FindMethod(int methodToken) { - CheckAssembly(); - - foreach (var module in assembly.GetModules()) { - var method = module.ResolveMethod(methodToken) as MethodInfo; - if (method != null) - return method; - } - - return null; - } - - public void InstallCompileMethod(DecryptMethodsInfo decryptMethodsInfo) { - if (installCompileMethodCalled) - throw new ApplicationException("installCompileMethod() has already been called"); - installCompileMethodCalled = true; - DynamicMethodsDecrypter.Instance.DecryptMethodsInfo = decryptMethodsInfo; - DynamicMethodsDecrypter.Instance.InstallCompileMethod(); - } - - public void LoadObfuscator(string filename) { - LoadAssembly(filename); - DynamicMethodsDecrypter.Instance.Module = assembly.ManifestModule; - DynamicMethodsDecrypter.Instance.LoadObfuscator(); - } - - public bool CanDecryptMethods() { - CheckAssembly(); - return DynamicMethodsDecrypter.Instance.CanDecryptMethods(); - } - - public DumpedMethods DecryptMethods() { - CheckAssembly(); - return DynamicMethodsDecrypter.Instance.DecryptMethods(); - } } } diff --git a/AssemblyData/GenericService.cs b/AssemblyData/GenericService.cs new file mode 100644 index 00000000..a214f024 --- /dev/null +++ b/AssemblyData/GenericService.cs @@ -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 . +*/ + +using System; +using System.Reflection; + +namespace AssemblyData { + class GenericService : AssemblyService, IGenericService { + IUserGenericService userGenericService; + + public override void Exit() { + if (userGenericService != null) + userGenericService.Dispose(); + userGenericService = null; + base.Exit(); + } + + public void LoadUserService(Type createServiceType, object createMethodArgs) { + var createServiceMethod = GetCreateUserServiceMethod(createServiceType); + userGenericService = createServiceMethod.Invoke(null, null) as IUserGenericService; + if (userGenericService == null) + throw new ApplicationException("create-service-method failed to create user service"); + } + + MethodInfo GetCreateUserServiceMethod(Type createServiceType) { + if (createServiceType == null) + throw new ApplicationException(string.Format("Create-service-type is null")); + foreach (var method in createServiceType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) { + if (method.GetCustomAttributes(typeof(CreateUserGenericServiceAttribute), false).Length > 0) + return method; + } + throw new ApplicationException(string.Format("Failed to find create-service-method. Type token: Type: {0}", createServiceType)); + } + + void CheckUserService() { + if (userGenericService == null) + throw new ApplicationException("LoadUserService() hasn't been called yet."); + } + + public void LoadAssembly(string filename) { + CheckUserService(); + LoadAssemblyInternal(filename); + userGenericService.AssemblyLoaded(assembly); + } + + public object SendMessage(int msg, object[] args) { + CheckUserService(); + return userGenericService.HandleMessage(msg, args); + } + } +} diff --git a/AssemblyData/IAssemblyService.cs b/AssemblyData/IAssemblyService.cs index 2867531b..b0e1da67 100644 --- a/AssemblyData/IAssemblyService.cs +++ b/AssemblyData/IAssemblyService.cs @@ -17,29 +17,15 @@ along with de4dot. If not, see . */ -using dnlib.DotNet; -using de4dot.blocks; -using de4dot.mdecrypt; - namespace AssemblyData { - public enum StringDecrypterType { - Delegate, - Emulate, + public enum AssemblyServiceType { + StringDecrypter, + MethodDecrypter, + Generic, } public interface IAssemblyService { void DoNothing(); void Exit(); - - void LoadAssembly(string filename); - - void SetStringDecrypterType(StringDecrypterType type); - int DefineStringDecrypter(int methodToken); - object[] DecryptStrings(int stringDecrypterMethod, object[] args, int callerToken); - - void InstallCompileMethod(DecryptMethodsInfo decryptMethodsInfo); - void LoadObfuscator(string filename); - bool CanDecryptMethods(); - DumpedMethods DecryptMethods(); } } diff --git a/AssemblyData/IGenericService.cs b/AssemblyData/IGenericService.cs new file mode 100644 index 00000000..db4d7a91 --- /dev/null +++ b/AssemblyData/IGenericService.cs @@ -0,0 +1,31 @@ +/* + 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 . +*/ + +using System; + +namespace AssemblyData { + public class CreateUserGenericServiceAttribute : Attribute { + } + + public interface IGenericService : IAssemblyService { + void LoadUserService(Type createServiceType, object createMethodArgs); + void LoadAssembly(string filename); + object SendMessage(int msg, object[] args); + } +} diff --git a/AssemblyData/IMethodDecrypterService.cs b/AssemblyData/IMethodDecrypterService.cs new file mode 100644 index 00000000..5ec2b224 --- /dev/null +++ b/AssemblyData/IMethodDecrypterService.cs @@ -0,0 +1,30 @@ +/* + 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 . +*/ + +using de4dot.blocks; +using de4dot.mdecrypt; + +namespace AssemblyData { + public interface IMethodDecrypterService : IAssemblyService { + void InstallCompileMethod(DecryptMethodsInfo decryptMethodsInfo); + void LoadObfuscator(string filename); + bool CanDecryptMethods(); + DumpedMethods DecryptMethods(); + } +} diff --git a/AssemblyData/IStringDecrypterService.cs b/AssemblyData/IStringDecrypterService.cs new file mode 100644 index 00000000..83ad0c6a --- /dev/null +++ b/AssemblyData/IStringDecrypterService.cs @@ -0,0 +1,32 @@ +/* + 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 . +*/ + +namespace AssemblyData { + public enum StringDecrypterType { + Delegate, + Emulate, + } + + public interface IStringDecrypterService : IAssemblyService { + void LoadAssembly(string filename); + void SetStringDecrypterType(StringDecrypterType type); + int DefineStringDecrypter(int methodToken); + object[] DecryptStrings(int stringDecrypterMethod, object[] args, int callerToken); + } +} diff --git a/AssemblyData/IUserGenericService.cs b/AssemblyData/IUserGenericService.cs new file mode 100644 index 00000000..635ae7f6 --- /dev/null +++ b/AssemblyData/IUserGenericService.cs @@ -0,0 +1,28 @@ +/* + 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 . +*/ + +using System; +using System.Reflection; + +namespace AssemblyData { + public interface IUserGenericService : IDisposable { + void AssemblyLoaded(Assembly assembly); + object HandleMessage(int msg, object[] args); + } +} diff --git a/AssemblyData/MethodDecrypterService.cs b/AssemblyData/MethodDecrypterService.cs new file mode 100644 index 00000000..47c75234 --- /dev/null +++ b/AssemblyData/MethodDecrypterService.cs @@ -0,0 +1,52 @@ +/* + 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 . +*/ + +using System; +using de4dot.blocks; +using de4dot.mdecrypt; + +namespace AssemblyData { + class MethodDecrypterService : AssemblyService, IMethodDecrypterService { + bool installCompileMethodCalled = false; + + public void InstallCompileMethod(DecryptMethodsInfo decryptMethodsInfo) { + if (installCompileMethodCalled) + throw new ApplicationException("installCompileMethod() has already been called"); + installCompileMethodCalled = true; + DynamicMethodsDecrypter.Instance.DecryptMethodsInfo = decryptMethodsInfo; + DynamicMethodsDecrypter.Instance.InstallCompileMethod(); + } + + public void LoadObfuscator(string filename) { + LoadAssemblyInternal(filename); + DynamicMethodsDecrypter.Instance.Module = assembly.ManifestModule; + DynamicMethodsDecrypter.Instance.LoadObfuscator(); + } + + public bool CanDecryptMethods() { + CheckAssembly(); + return DynamicMethodsDecrypter.Instance.CanDecryptMethods(); + } + + public DumpedMethods DecryptMethods() { + CheckAssembly(); + return DynamicMethodsDecrypter.Instance.DecryptMethods(); + } + } +} diff --git a/AssemblyData/StringDecrypterService.cs b/AssemblyData/StringDecrypterService.cs new file mode 100644 index 00000000..08c0be43 --- /dev/null +++ b/AssemblyData/StringDecrypterService.cs @@ -0,0 +1,93 @@ +/* + 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 . +*/ + +using System; +using System.Reflection; + +namespace AssemblyData { + class StringDecrypterService : AssemblyService, IStringDecrypterService { + IStringDecrypter stringDecrypter = null; + + void CheckStringDecrypter() { + if (stringDecrypter == null) + throw new ApplicationException("SetStringDecrypterType() hasn't been called yet."); + } + + public void LoadAssembly(string filename) { + LoadAssemblyInternal(filename); + } + + public void SetStringDecrypterType(StringDecrypterType type) { + if (stringDecrypter != null) + throw new ApplicationException("StringDecrypterType already set"); + + switch (type) { + case StringDecrypterType.Delegate: + stringDecrypter = new DelegateStringDecrypter(); + break; + + case StringDecrypterType.Emulate: + stringDecrypter = new EmuStringDecrypter(); + break; + + default: + throw new ApplicationException(string.Format("Unknown StringDecrypterType {0}", type)); + } + } + + public int DefineStringDecrypter(int methodToken) { + CheckStringDecrypter(); + var methodInfo = FindMethod(methodToken); + if (methodInfo == null) + throw new ApplicationException(string.Format("Could not find method {0:X8}", methodToken)); + if (methodInfo.ReturnType != typeof(string) && methodInfo.ReturnType != typeof(object)) + throw new ApplicationException(string.Format("Method return type must be string or object: {0}", methodInfo)); + return stringDecrypter.DefineStringDecrypter(methodInfo); + } + + public object[] DecryptStrings(int stringDecrypterMethod, object[] args, int callerToken) { + CheckStringDecrypter(); + var caller = GetCaller(callerToken); + foreach (var arg in args) + SimpleData.Unpack((object[])arg); + return SimpleData.Pack(stringDecrypter.DecryptStrings(stringDecrypterMethod, args, caller)); + } + + MethodBase GetCaller(int callerToken) { + try { + return assembly.GetModules()[0].ResolveMethod(callerToken); + } + catch { + return null; + } + } + + MethodInfo FindMethod(int methodToken) { + CheckAssembly(); + + foreach (var module in assembly.GetModules()) { + var method = module.ResolveMethod(methodToken) as MethodInfo; + if (method != null) + return method; + } + + return null; + } + } +} diff --git a/AssemblyData/methodsrewriter/MethodsRewriter.cs b/AssemblyData/methodsrewriter/MethodsRewriter.cs index 117128ff..c37a9ae7 100644 --- a/AssemblyData/methodsrewriter/MethodsRewriter.cs +++ b/AssemblyData/methodsrewriter/MethodsRewriter.cs @@ -338,7 +338,7 @@ namespace AssemblyData.methodsrewriter { return list; } - static FieldInfo GgetStackTraceStackFramesField() { + static FieldInfo GetStackTraceStackFramesField() { var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; return ResolverUtils.GetFieldThrow(typeof(StackTrace), typeof(StackFrame[]), flags, "Could not find StackTrace's frames (StackFrame[]) field"); } @@ -367,7 +367,7 @@ namespace AssemblyData.methodsrewriter { } StackTrace RtFixStackTrace(StackTrace stackTrace) { - var framesField = GgetStackTraceStackFramesField(); + var framesField = GetStackTraceStackFramesField(); var frames = (StackFrame[])framesField.GetValue(stackTrace); var newFrames = new List(frames.Length); diff --git a/blocks/cflow/Real8Value.cs b/blocks/cflow/Real8Value.cs deleted file mode 100644 index 868f2339..00000000 --- a/blocks/cflow/Real8Value.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* - 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 . -*/ - -namespace de4dot.blocks.cflow { - public class Real8Value : Value { - public readonly double value; - - public Real8Value(double value) - : base(ValueType.Real8) { - this.value = value; - } - - public static Real8Value Add(Real8Value a, Real8Value b) { - return new Real8Value(a.value + b.value); - } - - public static Real8Value Sub(Real8Value a, Real8Value b) { - return new Real8Value(a.value - b.value); - } - - public static Real8Value Mul(Real8Value a, Real8Value b) { - return new Real8Value(a.value * b.value); - } - - public static Real8Value Div(Real8Value a, Real8Value b) { - return new Real8Value(a.value / b.value); - } - - public static Real8Value Rem(Real8Value a, Real8Value b) { - return new Real8Value(a.value % b.value); - } - - public static Real8Value Neg(Real8Value a) { - return new Real8Value(-a.value); - } - } -} diff --git a/blocks/BaseBlock.cs b/de4dot.blocks/BaseBlock.cs similarity index 100% rename from blocks/BaseBlock.cs rename to de4dot.blocks/BaseBlock.cs diff --git a/blocks/Block.cs b/de4dot.blocks/Block.cs similarity index 100% rename from blocks/Block.cs rename to de4dot.blocks/Block.cs diff --git a/blocks/Blocks.cs b/de4dot.blocks/Blocks.cs similarity index 100% rename from blocks/Blocks.cs rename to de4dot.blocks/Blocks.cs diff --git a/blocks/BlocksSorter.cs b/de4dot.blocks/BlocksSorter.cs similarity index 81% rename from blocks/BlocksSorter.cs rename to de4dot.blocks/BlocksSorter.cs index 2bf6aec2..484f8f1c 100644 --- a/blocks/BlocksSorter.cs +++ b/de4dot.blocks/BlocksSorter.cs @@ -170,38 +170,60 @@ namespace de4dot.blocks { dest.Add(block); } + struct VisitState { + public BlockInfo Info; + public List Targets; + public int TargetIndex; + public BlockInfo TargetInfo; + public VisitState(BlockInfo info) { + this.Info = info; + this.Targets = null; + this.TargetIndex = 0; + this.TargetInfo = null; + } + } + Stack visitStateStack = new Stack(); void Visit(BlockInfo info) { - if (info.baseBlock == firstBlock) + // This method used to be recursive but to prevent stack overflows, + // it's not recursive anymore. + + VisitState state = new VisitState(info); +recursive_call: + if (state.Info.baseBlock == firstBlock) throw new ApplicationException("Can't visit firstBlock"); - stack.Push(info); - info.onStack = true; - info.dfsNumber = dfsNumber; - info.low = dfsNumber; + stack.Push(state.Info); + state.Info.onStack = true; + state.Info.dfsNumber = dfsNumber; + state.Info.low = dfsNumber; dfsNumber++; - foreach (var tmp in GetTargets(info.baseBlock)) { - var targetInfo = GetInfo(tmp); - if (targetInfo == null) + state.Targets = GetTargets(state.Info.baseBlock); + state.TargetIndex = 0; +return_to_caller: + for (; state.TargetIndex < state.Targets.Count; state.TargetIndex++) { + state.TargetInfo = GetInfo(state.Targets[state.TargetIndex]); + if (state.TargetInfo == null) continue; - if (targetInfo.baseBlock == firstBlock) + if (state.TargetInfo.baseBlock == firstBlock) continue; - if (!targetInfo.Visited()) { - Visit(targetInfo); - info.low = Math.Min(info.low, targetInfo.low); + if (!state.TargetInfo.Visited()) { + visitStateStack.Push(state); + state = new VisitState(state.TargetInfo); + goto recursive_call; } - else if (targetInfo.onStack) - info.low = Math.Min(info.low, targetInfo.dfsNumber); + else if (state.TargetInfo.onStack) + state.Info.low = Math.Min(state.Info.low, state.TargetInfo.dfsNumber); } - if (info.low != info.dfsNumber) - return; + if (state.Info.low != state.Info.dfsNumber) + goto return_from_method; var sccBlocks = new List(); while (true) { var poppedInfo = stack.Pop(); poppedInfo.onStack = false; sccBlocks.Add(poppedInfo.baseBlock); - if (ReferenceEquals(info, poppedInfo)) + if (ReferenceEquals(state.Info, poppedInfo)) break; } if (sccBlocks.Count > 1) { @@ -213,6 +235,14 @@ namespace de4dot.blocks { else { sorted.Insert(0, sccBlocks[0]); } + +return_from_method: + if (visitStateStack.Count == 0) + return; + state = visitStateStack.Pop(); + state.Info.low = Math.Min(state.Info.low, state.TargetInfo.low); + state.TargetIndex++; + goto return_to_caller; } void SortLoopBlock(List list) { diff --git a/blocks/CodeGenerator.cs b/de4dot.blocks/CodeGenerator.cs similarity index 100% rename from blocks/CodeGenerator.cs rename to de4dot.blocks/CodeGenerator.cs diff --git a/blocks/DeadBlocksRemover.cs b/de4dot.blocks/DeadBlocksRemover.cs similarity index 100% rename from blocks/DeadBlocksRemover.cs rename to de4dot.blocks/DeadBlocksRemover.cs diff --git a/blocks/DotNetUtils.cs b/de4dot.blocks/DotNetUtils.cs similarity index 100% rename from blocks/DotNetUtils.cs rename to de4dot.blocks/DotNetUtils.cs diff --git a/blocks/DumpedMethod.cs b/de4dot.blocks/DumpedMethod.cs similarity index 100% rename from blocks/DumpedMethod.cs rename to de4dot.blocks/DumpedMethod.cs diff --git a/blocks/DumpedMethods.cs b/de4dot.blocks/DumpedMethods.cs similarity index 100% rename from blocks/DumpedMethods.cs rename to de4dot.blocks/DumpedMethods.cs diff --git a/blocks/FilterHandlerBlock.cs b/de4dot.blocks/FilterHandlerBlock.cs similarity index 100% rename from blocks/FilterHandlerBlock.cs rename to de4dot.blocks/FilterHandlerBlock.cs diff --git a/blocks/ForwardScanOrder.cs b/de4dot.blocks/ForwardScanOrder.cs similarity index 80% rename from blocks/ForwardScanOrder.cs rename to de4dot.blocks/ForwardScanOrder.cs index a46355e0..a2029955 100644 --- a/blocks/ForwardScanOrder.cs +++ b/de4dot.blocks/ForwardScanOrder.cs @@ -100,25 +100,38 @@ namespace de4dot.blocks { return false; } - void ScanBaseBlock(BaseBlock bb, int stackStart) { - if (blockInfos.ContainsKey(bb) || !scopeBlock.IsOurBaseBlock(bb)) - return; - - var blockInfo = new BlockInfo(bb, stackStart); - blockInfos[bb] = blockInfo; - - var block = bb as Block; - if (block == null) { // i.e., if try, filter, or handler block - // It's not important to know the exact values, so we set them both to 0. - // Compilers must make sure the stack is empty when entering a try block. - blockInfo.stackStart = blockInfo.stackEnd = 0; - return; + struct ScanBaseBlockState { + public BaseBlock bb; + public int stackStart; + public ScanBaseBlockState(BaseBlock bb, int stackStart) { + this.bb = bb; + this.stackStart = stackStart; } + } + Stack scanBaseBlockStack = new Stack(); + void ScanBaseBlock(BaseBlock bb, int stackStart) { + scanBaseBlockStack.Push(new ScanBaseBlockState(bb, stackStart)); + while (scanBaseBlockStack.Count > 0) { + var state = scanBaseBlockStack.Pop(); + if (blockInfos.ContainsKey(state.bb) || !scopeBlock.IsOurBaseBlock(state.bb)) + continue; - blockInfo.CalculateStackUsage(); + var blockInfo = new BlockInfo(state.bb, state.stackStart); + blockInfos[state.bb] = blockInfo; - foreach (var target in block.GetTargets()) - ScanBaseBlock(target, blockInfo.stackEnd); + var block = state.bb as Block; + if (block == null) { // i.e., if try, filter, or handler block + // It's not important to know the exact values, so we set them both to 0. + // Compilers must make sure the stack is empty when entering a try block. + blockInfo.stackStart = blockInfo.stackEnd = 0; + continue; + } + + blockInfo.CalculateStackUsage(); + + foreach (var target in block.GetTargets()) + scanBaseBlockStack.Push(new ScanBaseBlockState(target, blockInfo.stackEnd)); + } } void CreateNewList() { diff --git a/blocks/GenericArgsSubstitutor.cs b/de4dot.blocks/GenericArgsSubstitutor.cs similarity index 100% rename from blocks/GenericArgsSubstitutor.cs rename to de4dot.blocks/GenericArgsSubstitutor.cs diff --git a/blocks/HandlerBlock.cs b/de4dot.blocks/HandlerBlock.cs similarity index 100% rename from blocks/HandlerBlock.cs rename to de4dot.blocks/HandlerBlock.cs diff --git a/blocks/Instr.cs b/de4dot.blocks/Instr.cs similarity index 100% rename from blocks/Instr.cs rename to de4dot.blocks/Instr.cs diff --git a/blocks/InstructionListParser.cs b/de4dot.blocks/InstructionListParser.cs similarity index 100% rename from blocks/InstructionListParser.cs rename to de4dot.blocks/InstructionListParser.cs diff --git a/blocks/MemberDefDict.cs b/de4dot.blocks/MemberDefDict.cs similarity index 100% rename from blocks/MemberDefDict.cs rename to de4dot.blocks/MemberDefDict.cs diff --git a/blocks/MethodBlocks.cs b/de4dot.blocks/MethodBlocks.cs similarity index 100% rename from blocks/MethodBlocks.cs rename to de4dot.blocks/MethodBlocks.cs diff --git a/blocks/Properties/AssemblyInfo.cs b/de4dot.blocks/Properties/AssemblyInfo.cs similarity index 93% rename from blocks/Properties/AssemblyInfo.cs rename to de4dot.blocks/Properties/AssemblyInfo.cs index a0dd84e7..59432fe2 100644 --- a/blocks/Properties/AssemblyInfo.cs +++ b/de4dot.blocks/Properties/AssemblyInfo.cs @@ -20,11 +20,11 @@ using System.Reflection; using System.Runtime.InteropServices; -[assembly: AssemblyTitle("blocks")] +[assembly: AssemblyTitle("de4dot.blocks")] [assembly: AssemblyDescription("Modifies dnlib MethodDef bodies")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("blocks")] +[assembly: AssemblyProduct("de4dot.blocks")] [assembly: AssemblyCopyright("Copyright (C) 2011-2013 de4dot@gmail.com")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/blocks/ScopeBlock.cs b/de4dot.blocks/ScopeBlock.cs similarity index 100% rename from blocks/ScopeBlock.cs rename to de4dot.blocks/ScopeBlock.cs diff --git a/de4dot.blocks/StackTracePatcher.cs b/de4dot.blocks/StackTracePatcher.cs new file mode 100644 index 00000000..1d4334f7 --- /dev/null +++ b/de4dot.blocks/StackTracePatcher.cs @@ -0,0 +1,94 @@ +/* + 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 . +*/ + +using System; +using System.Reflection; +using System.Diagnostics; + +namespace de4dot.blocks { + public class StackTracePatcher { + static readonly FieldInfo methodField; + static readonly FieldInfo framesField; + static readonly FieldInfo methodsToSkipField; + + static StackTracePatcher() { + methodField = GetStackFrameMethodField(); + framesField = GetStackTraceStackFramesField(); + methodsToSkipField = GetMethodsToSkipField(); + } + + static FieldInfo GetStackFrameMethodField() { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + return GetFieldThrow(typeof(StackFrame), typeof(MethodBase), flags, "Could not find StackFrame's method (MethodBase) field"); + } + + static FieldInfo GetStackTraceStackFramesField() { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + return GetFieldThrow(typeof(StackTrace), typeof(StackFrame[]), flags, "Could not find StackTrace's frames (StackFrame[]) field"); + } + + static FieldInfo GetMethodsToSkipField() { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + return GetFieldThrow(typeof(StackTrace), "m_iMethodsToSkip", flags, "Could not find StackTrace's iMethodsToSkip field"); + } + + static FieldInfo GetFieldThrow(Type type, Type fieldType, BindingFlags flags, string msg) { + var info = GetField(type, fieldType, flags); + if (info != null) + return info; + throw new ApplicationException(msg); + } + + static FieldInfo GetField(Type type, Type fieldType, BindingFlags flags) { + foreach (var field in type.GetFields(flags)) { + if (field.FieldType == fieldType) + return field; + } + return null; + } + + static FieldInfo GetFieldThrow(Type type, string fieldName, BindingFlags flags, string msg) { + var info = GetField(type, fieldName, flags); + if (info != null) + return info; + throw new ApplicationException(msg); + } + + static FieldInfo GetField(Type type, string fieldName, BindingFlags flags) { + foreach (var field in type.GetFields(flags)) { + if (field.Name == fieldName) + return field; + } + return null; + } + + public static StackTrace WriteStackFrame(StackTrace stackTrace, int frameNo, MethodBase newMethod) { + var frames = (StackFrame[])framesField.GetValue(stackTrace); + int numFramesToSkip = (int)methodsToSkipField.GetValue(stackTrace); + WriteMethodBase(frames[numFramesToSkip + frameNo], newMethod); + return stackTrace; + } + + static void WriteMethodBase(StackFrame frame, MethodBase method) { + methodField.SetValue(frame, method); + if (frame.GetMethod() != method) + throw new ApplicationException(string.Format("Could not set new method: {0}", method)); + } + } +} diff --git a/blocks/TryBlock.cs b/de4dot.blocks/TryBlock.cs similarity index 100% rename from blocks/TryBlock.cs rename to de4dot.blocks/TryBlock.cs diff --git a/blocks/TryHandlerBlock.cs b/de4dot.blocks/TryHandlerBlock.cs similarity index 100% rename from blocks/TryHandlerBlock.cs rename to de4dot.blocks/TryHandlerBlock.cs diff --git a/blocks/Utils.cs b/de4dot.blocks/Utils.cs similarity index 100% rename from blocks/Utils.cs rename to de4dot.blocks/Utils.cs diff --git a/blocks/cflow/BlockCflowDeobfuscator.cs b/de4dot.blocks/cflow/BlockCflowDeobfuscator.cs similarity index 97% rename from blocks/cflow/BlockCflowDeobfuscator.cs rename to de4dot.blocks/cflow/BlockCflowDeobfuscator.cs index 24975de0..3bc44f1f 100644 --- a/blocks/cflow/BlockCflowDeobfuscator.cs +++ b/de4dot.blocks/cflow/BlockCflowDeobfuscator.cs @@ -35,7 +35,7 @@ namespace de4dot.blocks.cflow { this.block = block; if (!block.LastInstr.IsConditionalBranch() && block.LastInstr.OpCode.Code != Code.Switch) return false; - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == block); var instructions = block.Instructions; if (instructions.Count == 0) diff --git a/blocks/cflow/BlockDeobfuscator.cs b/de4dot.blocks/cflow/BlockDeobfuscator.cs similarity index 100% rename from blocks/cflow/BlockDeobfuscator.cs rename to de4dot.blocks/cflow/BlockDeobfuscator.cs diff --git a/blocks/cflow/BlocksCflowDeobfuscator.cs b/de4dot.blocks/cflow/BlocksCflowDeobfuscator.cs similarity index 100% rename from blocks/cflow/BlocksCflowDeobfuscator.cs rename to de4dot.blocks/cflow/BlocksCflowDeobfuscator.cs diff --git a/blocks/cflow/BranchEmulator.cs b/de4dot.blocks/cflow/BranchEmulator.cs similarity index 100% rename from blocks/cflow/BranchEmulator.cs rename to de4dot.blocks/cflow/BranchEmulator.cs diff --git a/blocks/cflow/CachedCflowDeobfuscator.cs b/de4dot.blocks/cflow/CachedCflowDeobfuscator.cs similarity index 100% rename from blocks/cflow/CachedCflowDeobfuscator.cs rename to de4dot.blocks/cflow/CachedCflowDeobfuscator.cs diff --git a/blocks/cflow/CflowDeobfuscator.cs b/de4dot.blocks/cflow/CflowDeobfuscator.cs similarity index 100% rename from blocks/cflow/CflowDeobfuscator.cs rename to de4dot.blocks/cflow/CflowDeobfuscator.cs diff --git a/blocks/cflow/CflowUtils.cs b/de4dot.blocks/cflow/CflowUtils.cs similarity index 97% rename from blocks/cflow/CflowUtils.cs rename to de4dot.blocks/cflow/CflowUtils.cs index 1f644bf1..401f75d1 100644 --- a/blocks/cflow/CflowUtils.cs +++ b/de4dot.blocks/cflow/CflowUtils.cs @@ -25,7 +25,7 @@ namespace de4dot.blocks.cflow { if (!intValue.AllBitsValid()) return null; - int index = intValue.value; + int index = intValue.Value; if (targets == null || index < 0 || index >= targets.Count) return fallThrough; else diff --git a/blocks/cflow/ConstantsFolder.cs b/de4dot.blocks/cflow/ConstantsFolder.cs similarity index 96% rename from blocks/cflow/ConstantsFolder.cs rename to de4dot.blocks/cflow/ConstantsFolder.cs index 43c291de..9a600dfa 100644 --- a/blocks/cflow/ConstantsFolder.cs +++ b/de4dot.blocks/cflow/ConstantsFolder.cs @@ -37,7 +37,7 @@ namespace de4dot.blocks.cflow { protected override bool Deobfuscate(Block block) { bool modified = false; - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == block); var instrs = block.Instructions; for (int i = 0; i < instrs.Count; i++) { var instr = instrs[i]; @@ -89,14 +89,14 @@ namespace de4dot.blocks.cflow { var intValue = (Int32Value)value; if (!intValue.AllBitsValid()) return false; - block.Instructions[index] = new Instr(Instruction.CreateLdcI4(intValue.value)); + block.Instructions[index] = new Instr(Instruction.CreateLdcI4(intValue.Value)); return true; } else if (value.IsInt64()) { var intValue = (Int64Value)value; if (!intValue.AllBitsValid()) return false; - block.Instructions[index] = new Instr(OpCodes.Ldc_I8.ToInstruction(intValue.value)); + block.Instructions[index] = new Instr(OpCodes.Ldc_I8.ToInstruction(intValue.Value)); return true; } return false; diff --git a/blocks/cflow/DeadCodeRemover.cs b/de4dot.blocks/cflow/DeadCodeRemover.cs similarity index 100% rename from blocks/cflow/DeadCodeRemover.cs rename to de4dot.blocks/cflow/DeadCodeRemover.cs diff --git a/blocks/cflow/DeadStoreRemover.cs b/de4dot.blocks/cflow/DeadStoreRemover.cs similarity index 100% rename from blocks/cflow/DeadStoreRemover.cs rename to de4dot.blocks/cflow/DeadStoreRemover.cs diff --git a/blocks/cflow/DupBlockDeobfuscator.cs b/de4dot.blocks/cflow/DupBlockDeobfuscator.cs similarity index 100% rename from blocks/cflow/DupBlockDeobfuscator.cs rename to de4dot.blocks/cflow/DupBlockDeobfuscator.cs diff --git a/blocks/cflow/IBlocksDeobfuscator.cs b/de4dot.blocks/cflow/IBlocksDeobfuscator.cs similarity index 100% rename from blocks/cflow/IBlocksDeobfuscator.cs rename to de4dot.blocks/cflow/IBlocksDeobfuscator.cs diff --git a/blocks/cflow/ICflowDeobfuscator.cs b/de4dot.blocks/cflow/ICflowDeobfuscator.cs similarity index 100% rename from blocks/cflow/ICflowDeobfuscator.cs rename to de4dot.blocks/cflow/ICflowDeobfuscator.cs diff --git a/blocks/cflow/InstructionEmulator.cs b/de4dot.blocks/cflow/InstructionEmulator.cs similarity index 68% rename from blocks/cflow/InstructionEmulator.cs rename to de4dot.blocks/cflow/InstructionEmulator.cs index 3345cf64..a829d02a 100644 --- a/blocks/cflow/InstructionEmulator.cs +++ b/de4dot.blocks/cflow/InstructionEmulator.cs @@ -34,19 +34,24 @@ namespace de4dot.blocks.cflow { MethodDef prev_method; List cached_args = new List(); List cached_locals = new List(); + List cached_zeroed_locals = new List(); public InstructionEmulator() { } public InstructionEmulator(MethodDef method) { - Initialize(method); + Initialize(method, false); } - public void Initialize(Blocks blocks) { - Initialize(blocks.Method); + public void Initialize(Blocks blocks, bool emulateFromFirstInstruction) { + Initialize(blocks.Method, emulateFromFirstInstruction); } public void Initialize(MethodDef method) { + Initialize(method, false); + } + + public void Initialize(MethodDef method, bool emulateFromFirstInstruction) { this.parameterDefs = method.Parameters; this.localDefs = method.Body.Variables; valueStack.Initialize(); @@ -60,14 +65,17 @@ namespace de4dot.blocks.cflow { cached_args.Add(GetUnknownValue(parameterDefs[i].Type)); cached_locals.Clear(); - for (int i = 0; i < localDefs.Count; i++) + cached_zeroed_locals.Clear(); + for (int i = 0; i < localDefs.Count; i++) { cached_locals.Add(GetUnknownValue(localDefs[i].Type)); + cached_zeroed_locals.Add(GetDefaultValue(localDefs[i].Type)); + } } args.Clear(); args.AddRange(cached_args); locals.Clear(); - locals.AddRange(cached_locals); + locals.AddRange(method.Body.InitLocals && emulateFromFirstInstruction ? cached_zeroed_locals : cached_locals); } public void SetProtected(Value value) { @@ -95,6 +103,25 @@ namespace de4dot.blocks.cflow { return new UnknownValue(); } + static Value GetDefaultValue(TypeSig type) { + if (type == null) + return new UnknownValue(); + switch (type.ElementType) { + case ElementType.Boolean: + case ElementType.I1: + case ElementType.U1: + case ElementType.I2: + case ElementType.U2: + case ElementType.I4: + case ElementType.U4: + return Int32Value.Zero; + case ElementType.I8: + case ElementType.U8: + return Int64Value.Zero; + } + return new UnknownValue(); + } + Value TruncateValue(Value value, TypeSig type) { if (type == null) return value; @@ -141,7 +168,7 @@ namespace de4dot.blocks.cflow { case ElementType.R4: if (value.IsReal8()) - return new Real8Value((float)((Real8Value)value).value); + return ((Real8Value)value).ToSingle(); return new UnknownValue(); case ElementType.R8: @@ -297,8 +324,8 @@ namespace de4dot.blocks.cflow { case Code.Ldc_I8: valueStack.Push(new Int64Value((long)instr.Operand)); break; case Code.Ldc_R4: valueStack.Push(new Real8Value((float)instr.Operand)); break; case Code.Ldc_R8: valueStack.Push(new Real8Value((double)instr.Operand)); break; - case Code.Ldc_I4_0: valueStack.Push(Int32Value.zero); break; - case Code.Ldc_I4_1: valueStack.Push(Int32Value.one); break; + case Code.Ldc_I4_0: valueStack.Push(Int32Value.Zero); break; + case Code.Ldc_I4_1: valueStack.Push(Int32Value.One); break; case Code.Ldc_I4_2: valueStack.Push(new Int32Value(2)); break; case Code.Ldc_I4_3: valueStack.Push(new Int32Value(3)); break; case Code.Ldc_I4_4: valueStack.Push(new Int32Value(4)); break; @@ -347,29 +374,29 @@ namespace de4dot.blocks.cflow { case Code.Castclass: Emulate_Castclass(instr); break; case Code.Isinst: Emulate_Isinst(instr); break; - case Code.Add_Ovf: EmulateIntOps2(); break; - case Code.Add_Ovf_Un: EmulateIntOps2(); break; - case Code.Sub_Ovf: EmulateIntOps2(); break; - case Code.Sub_Ovf_Un: EmulateIntOps2(); break; - case Code.Mul_Ovf: EmulateIntOps2(); break; - case Code.Mul_Ovf_Un: EmulateIntOps2(); break; + case Code.Add_Ovf: Emulate_Add_Ovf(instr); break; + case Code.Add_Ovf_Un: Emulate_Add_Ovf_Un(instr); break; + case Code.Sub_Ovf: Emulate_Sub_Ovf(instr); break; + case Code.Sub_Ovf_Un: Emulate_Sub_Ovf_Un(instr); break; + case Code.Mul_Ovf: Emulate_Mul_Ovf(instr); break; + case Code.Mul_Ovf_Un: Emulate_Mul_Ovf_Un(instr); break; - case Code.Conv_Ovf_I1: - case Code.Conv_Ovf_I1_Un: valueStack.Pop(); valueStack.Push(Int32Value.CreateUnknown()); break; - case Code.Conv_Ovf_I2: - case Code.Conv_Ovf_I2_Un: valueStack.Pop(); valueStack.Push(Int32Value.CreateUnknown()); break; - case Code.Conv_Ovf_I4: - case Code.Conv_Ovf_I4_Un: valueStack.Pop(); valueStack.Push(Int32Value.CreateUnknown()); break; - case Code.Conv_Ovf_I8: - case Code.Conv_Ovf_I8_Un: valueStack.Pop(); valueStack.Push(Int64Value.CreateUnknown()); break; - case Code.Conv_Ovf_U1: - case Code.Conv_Ovf_U1_Un: valueStack.Pop(); valueStack.Push(Int32Value.CreateUnknownUInt8()); break; - case Code.Conv_Ovf_U2: - case Code.Conv_Ovf_U2_Un: valueStack.Pop(); valueStack.Push(Int32Value.CreateUnknownUInt16()); break; - case Code.Conv_Ovf_U4: - case Code.Conv_Ovf_U4_Un: valueStack.Pop(); valueStack.Push(Int32Value.CreateUnknown()); break; - case Code.Conv_Ovf_U8: - case Code.Conv_Ovf_U8_Un: valueStack.Pop(); valueStack.Push(Int64Value.CreateUnknown()); break; + case Code.Conv_Ovf_I1: Emulate_Conv_Ovf_I1(instr); break; + case Code.Conv_Ovf_I1_Un: Emulate_Conv_Ovf_I1_Un(instr); break; + case Code.Conv_Ovf_I2: Emulate_Conv_Ovf_I2(instr); break; + case Code.Conv_Ovf_I2_Un: Emulate_Conv_Ovf_I2_Un(instr); break; + case Code.Conv_Ovf_I4: Emulate_Conv_Ovf_I4(instr); break; + case Code.Conv_Ovf_I4_Un: Emulate_Conv_Ovf_I4_Un(instr); break; + case Code.Conv_Ovf_I8: Emulate_Conv_Ovf_I8(instr); break; + case Code.Conv_Ovf_I8_Un: Emulate_Conv_Ovf_I8_Un(instr); break; + case Code.Conv_Ovf_U1: Emulate_Conv_Ovf_U1(instr); break; + case Code.Conv_Ovf_U1_Un: Emulate_Conv_Ovf_U1_Un(instr); break; + case Code.Conv_Ovf_U2: Emulate_Conv_Ovf_U2(instr); break; + case Code.Conv_Ovf_U2_Un: Emulate_Conv_Ovf_U2_Un(instr); break; + case Code.Conv_Ovf_U4: Emulate_Conv_Ovf_U4(instr); break; + case Code.Conv_Ovf_U4_Un: Emulate_Conv_Ovf_U4_Un(instr); break; + case Code.Conv_Ovf_U8: Emulate_Conv_Ovf_U8(instr); break; + case Code.Conv_Ovf_U8_Un: Emulate_Conv_Ovf_U8_Un(instr); break; case Code.Ldelem_I1: valueStack.Pop(2); valueStack.Push(Int32Value.CreateUnknown()); break; case Code.Ldelem_I2: valueStack.Pop(2); valueStack.Push(Int32Value.CreateUnknown()); break; @@ -595,6 +622,166 @@ namespace de4dot.blocks.cflow { } } + void Emulate_Conv_Ovf_I1(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I1((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I1((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I1((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_I1_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I1_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I1_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I1_Un((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_I2(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I2((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I2((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I2((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_I2_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I2_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I2_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I2_Un((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_I4(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I4((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I4((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I4((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_I4_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I4_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I4_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I4_Un((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_I8(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I8((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I8((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I8((Real8Value)val1)); break; + default: valueStack.Push(Int64Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_I8_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_I8_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_I8_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_I8_Un((Real8Value)val1)); break; + default: valueStack.Push(Int64Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_U1(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U1((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U1((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U1((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknownUInt8()); break; + } + } + + void Emulate_Conv_Ovf_U1_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U1_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U1_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U1_Un((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknownUInt8()); break; + } + } + + void Emulate_Conv_Ovf_U2(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U2((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U2((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U2((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknownUInt16()); break; + } + } + + void Emulate_Conv_Ovf_U2_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U2_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U2_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U2_Un((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknownUInt16()); break; + } + } + + void Emulate_Conv_Ovf_U4(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U4((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U4((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U4((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_U4_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U4_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U4_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U4_Un((Real8Value)val1)); break; + default: valueStack.Push(Int32Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_U8(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U8((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U8((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U8((Real8Value)val1)); break; + default: valueStack.Push(Int64Value.CreateUnknown()); break; + } + } + + void Emulate_Conv_Ovf_U8_Un(Instruction instr) { + var val1 = valueStack.Pop(); + switch (val1.valueType) { + case ValueType.Int32: valueStack.Push(Int32Value.Conv_Ovf_U8_Un((Int32Value)val1)); break; + case ValueType.Int64: valueStack.Push(Int64Value.Conv_Ovf_U8_Un((Int64Value)val1)); break; + case ValueType.Real8: valueStack.Push(Real8Value.Conv_Ovf_U8_Un((Real8Value)val1)); break; + default: valueStack.Push(Int64Value.CreateUnknown()); break; + } + } + void Emulate_Add(Instruction instr) { var val2 = valueStack.Pop(); var val1 = valueStack.Pop(); @@ -702,6 +889,90 @@ namespace de4dot.blocks.cflow { valueStack.PushUnknown(); } + void Emulate_Add_Ovf(Instruction instr) { + var val2 = valueStack.Pop(); + var val1 = valueStack.Pop(); + + if (val1.IsInt32() && val2.IsInt32()) + valueStack.Push(Int32Value.Add_Ovf((Int32Value)val1, (Int32Value)val2)); + else if (val1.IsInt64() && val2.IsInt64()) + valueStack.Push(Int64Value.Add_Ovf((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Add_Ovf((Real8Value)val1, (Real8Value)val2)); + else + valueStack.PushUnknown(); + } + + void Emulate_Add_Ovf_Un(Instruction instr) { + var val2 = valueStack.Pop(); + var val1 = valueStack.Pop(); + + if (val1.IsInt32() && val2.IsInt32()) + valueStack.Push(Int32Value.Add_Ovf_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.IsInt64() && val2.IsInt64()) + valueStack.Push(Int64Value.Add_Ovf_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Add_Ovf_Un((Real8Value)val1, (Real8Value)val2)); + else + valueStack.PushUnknown(); + } + + void Emulate_Sub_Ovf(Instruction instr) { + var val2 = valueStack.Pop(); + var val1 = valueStack.Pop(); + + if (val1.IsInt32() && val2.IsInt32()) + valueStack.Push(Int32Value.Sub_Ovf((Int32Value)val1, (Int32Value)val2)); + else if (val1.IsInt64() && val2.IsInt64()) + valueStack.Push(Int64Value.Sub_Ovf((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Sub_Ovf((Real8Value)val1, (Real8Value)val2)); + else + valueStack.PushUnknown(); + } + + void Emulate_Sub_Ovf_Un(Instruction instr) { + var val2 = valueStack.Pop(); + var val1 = valueStack.Pop(); + + if (val1.IsInt32() && val2.IsInt32()) + valueStack.Push(Int32Value.Sub_Ovf_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.IsInt64() && val2.IsInt64()) + valueStack.Push(Int64Value.Sub_Ovf_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Sub_Ovf_Un((Real8Value)val1, (Real8Value)val2)); + else + valueStack.PushUnknown(); + } + + void Emulate_Mul_Ovf(Instruction instr) { + var val2 = valueStack.Pop(); + var val1 = valueStack.Pop(); + + if (val1.IsInt32() && val2.IsInt32()) + valueStack.Push(Int32Value.Mul_Ovf((Int32Value)val1, (Int32Value)val2)); + else if (val1.IsInt64() && val2.IsInt64()) + valueStack.Push(Int64Value.Mul_Ovf((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Mul_Ovf((Real8Value)val1, (Real8Value)val2)); + else + valueStack.PushUnknown(); + } + + void Emulate_Mul_Ovf_Un(Instruction instr) { + var val2 = valueStack.Pop(); + var val1 = valueStack.Pop(); + + if (val1.IsInt32() && val2.IsInt32()) + valueStack.Push(Int32Value.Mul_Ovf_Un((Int32Value)val1, (Int32Value)val2)); + else if (val1.IsInt64() && val2.IsInt64()) + valueStack.Push(Int64Value.Mul_Ovf_Un((Int64Value)val1, (Int64Value)val2)); + else if (val1.IsReal8() && val2.IsReal8()) + valueStack.Push(Real8Value.Mul_Ovf_Un((Real8Value)val1, (Real8Value)val2)); + else + valueStack.PushUnknown(); + } + void Emulate_And(Instruction instr) { var val2 = valueStack.Pop(); var val1 = valueStack.Pop(); @@ -794,7 +1065,7 @@ namespace de4dot.blocks.cflow { else if (val1.IsInt64() && val2.IsInt64()) valueStack.Push(Int64Value.Ceq((Int64Value)val1, (Int64Value)val2)); else if (val1.IsNull() && val2.IsNull()) - valueStack.Push(Int32Value.one); + valueStack.Push(Int32Value.One); else valueStack.Push(Int32Value.CreateUnknownBool()); } diff --git a/blocks/cflow/Int32Value.cs b/de4dot.blocks/cflow/Int32Value.cs similarity index 52% rename from blocks/cflow/Int32Value.cs rename to de4dot.blocks/cflow/Int32Value.cs index f885bf6a..bb838288 100644 --- a/blocks/cflow/Int32Value.cs +++ b/de4dot.blocks/cflow/Int32Value.cs @@ -21,27 +21,27 @@ using System; namespace de4dot.blocks.cflow { public class Int32Value : Value { - public static readonly Int32Value zero = new Int32Value(0); - public static readonly Int32Value one = new Int32Value(1); + public static readonly Int32Value Zero = new Int32Value(0); + public static readonly Int32Value One = new Int32Value(1); - const uint NO_UNKNOWN_BITS = uint.MaxValue; - public readonly int value; - public readonly uint validMask; + internal const uint NO_UNKNOWN_BITS = uint.MaxValue; + public readonly int Value; + public readonly uint ValidMask; public Int32Value(int value) : base(ValueType.Int32) { - this.value = value; - this.validMask = NO_UNKNOWN_BITS; + this.Value = value; + this.ValidMask = NO_UNKNOWN_BITS; } public Int32Value(int value, uint validMask) : base(ValueType.Int32) { - this.value = value; - this.validMask = validMask; + this.Value = value; + this.ValidMask = validMask; } public bool HasUnknownBits() { - return validMask != NO_UNKNOWN_BITS; + return ValidMask != NO_UNKNOWN_BITS; } public bool AllBitsValid() { @@ -49,13 +49,17 @@ namespace de4dot.blocks.cflow { } bool IsBitValid(int n) { - return IsBitValid(validMask, n); + return IsBitValid(ValidMask, n); } static bool IsBitValid(uint validMask, int n) { return (validMask & (1U << n)) != 0; } + bool AreBitsValid(uint bitsToTest) { + return (ValidMask & bitsToTest) == bitsToTest; + } + public static Int32Value CreateUnknownBool() { return new Int32Value(0, NO_UNKNOWN_BITS << 1); } @@ -77,11 +81,11 @@ namespace de4dot.blocks.cflow { } public bool IsNonZero() { - return (value & validMask) != 0; + return ((uint)Value & ValidMask) != 0; } public bool HasValue(int value) { - return AllBitsValid() && this.value == value; + return AllBitsValid() && this.Value == value; } public bool HasValue(uint value) { @@ -113,11 +117,11 @@ namespace de4dot.blocks.cflow { } public static Int32Value Conv_U1(Int32Value a) { - return Conv_U1(a.value, a.validMask); + return Conv_U1(a.Value, a.ValidMask); } public static Int32Value Conv_U1(Int64Value a) { - return Conv_U1((int)a.value, (uint)a.validMask); + return Conv_U1((int)a.Value, (uint)a.ValidMask); } public static Int32Value Conv_U1(int value, uint validMask) { @@ -127,15 +131,17 @@ namespace de4dot.blocks.cflow { } public static Int32Value Conv_U1(Real8Value a) { - return new Int32Value((int)(byte)a.value); + if (!a.IsValid) + return CreateUnknownUInt8(); + return new Int32Value((int)(byte)a.Value); } public static Int32Value Conv_I1(Int32Value a) { - return Conv_I1(a.value, a.validMask); + return Conv_I1(a.Value, a.ValidMask); } public static Int32Value Conv_I1(Int64Value a) { - return Conv_I1((int)a.value, (uint)a.validMask); + return Conv_I1((int)a.Value, (uint)a.ValidMask); } public static Int32Value Conv_I1(int value, uint validMask) { @@ -148,15 +154,17 @@ namespace de4dot.blocks.cflow { } public static Int32Value Conv_I1(Real8Value a) { - return new Int32Value((int)(sbyte)a.value); + if (!a.IsValid) + return CreateUnknown(); + return new Int32Value((int)(sbyte)a.Value); } public static Int32Value Conv_U2(Int32Value a) { - return Conv_U2(a.value, a.validMask); + return Conv_U2(a.Value, a.ValidMask); } public static Int32Value Conv_U2(Int64Value a) { - return Conv_U2((int)a.value, (uint)a.validMask); + return Conv_U2((int)a.Value, (uint)a.ValidMask); } public static Int32Value Conv_U2(int value, uint validMask) { @@ -166,15 +174,17 @@ namespace de4dot.blocks.cflow { } public static Int32Value Conv_U2(Real8Value a) { - return new Int32Value((int)(ushort)a.value); + if (!a.IsValid) + return CreateUnknownUInt16(); + return new Int32Value((int)(ushort)a.Value); } public static Int32Value Conv_I2(Int32Value a) { - return Conv_I2(a.value, a.validMask); + return Conv_I2(a.Value, a.ValidMask); } public static Int32Value Conv_I2(Int64Value a) { - return Conv_I2((int)a.value, (uint)a.validMask); + return Conv_I2((int)a.Value, (uint)a.ValidMask); } public static Int32Value Conv_I2(int value, uint validMask) { @@ -187,7 +197,9 @@ namespace de4dot.blocks.cflow { } public static Int32Value Conv_I2(Real8Value a) { - return new Int32Value((int)(short)a.value); + if (!a.IsValid) + return CreateUnknown(); + return new Int32Value((int)(short)a.Value); } public static Int32Value Conv_U4(Int32Value a) { @@ -195,11 +207,13 @@ namespace de4dot.blocks.cflow { } public static Int32Value Conv_U4(Int64Value a) { - return new Int32Value((int)(uint)a.value, (uint)a.validMask); + return new Int32Value((int)(uint)a.Value, (uint)a.ValidMask); } public static Int32Value Conv_U4(Real8Value a) { - return new Int32Value((int)(uint)a.value); + if (!a.IsValid) + return CreateUnknown(); + return new Int32Value((int)(uint)a.Value); } public static Int32Value Conv_I4(Int32Value a) { @@ -207,34 +221,137 @@ namespace de4dot.blocks.cflow { } public static Int32Value Conv_I4(Int64Value a) { - return new Int32Value((int)a.value, (uint)a.validMask); + return new Int32Value((int)a.Value, (uint)a.ValidMask); } public static Int32Value Conv_I4(Real8Value a) { - return new Int32Value((int)a.value); + if (!a.IsValid) + return CreateUnknown(); + return new Int32Value((int)a.Value); + } + + bool CheckSign(uint mask) { + return ((uint)Value & mask) == 0 || ((uint)Value & mask) == mask; + } + + public static Int32Value Conv_Ovf_I1(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 7) || + !a.CheckSign(NO_UNKNOWN_BITS << 7)) + return CreateUnknown(); + return Conv_I1(a); + } + + public static Int32Value Conv_Ovf_I1_Un(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 7) || + (uint)a.Value > sbyte.MaxValue) + return CreateUnknown(); + return Conv_I1(a); + } + + public static Int32Value Conv_Ovf_I2(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 15) || + !a.CheckSign(NO_UNKNOWN_BITS << 15)) + return CreateUnknown(); + return Conv_I2(a); + } + + public static Int32Value Conv_Ovf_I2_Un(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 15) || + (uint)a.Value > short.MaxValue) + return CreateUnknown(); + return Conv_I2(a); + } + + public static Int32Value Conv_Ovf_I4(Int32Value a) { + return a; + } + + public static Int32Value Conv_Ovf_I4_Un(Int32Value a) { + if (!IsBitValid(a.ValidMask, 31) || a.Value < 0) + return CreateUnknown(); + return a; + } + + public static Int64Value Conv_Ovf_I8(Int32Value a) { + ulong validMask = a.ValidMask; + if (IsBitValid(a.ValidMask, 31)) + validMask |= Int64Value.NO_UNKNOWN_BITS << 32; + return new Int64Value(a.Value, validMask); + } + + public static Int64Value Conv_Ovf_I8_Un(Int32Value a) { + return new Int64Value((long)(uint)a.Value, a.ValidMask | (Int64Value.NO_UNKNOWN_BITS << 32)); + } + + public static Int32Value Conv_Ovf_U1(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 7) || + a.Value < 0 || a.Value > byte.MaxValue) + return CreateUnknownUInt8(); + return Conv_U1(a); + } + + public static Int32Value Conv_Ovf_U1_Un(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 8) || + (uint)a.Value > byte.MaxValue) + return CreateUnknownUInt8(); + return Conv_U1(a); + } + + public static Int32Value Conv_Ovf_U2(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 15) || + a.Value < 0 || a.Value > ushort.MaxValue) + return CreateUnknownUInt16(); + return Conv_U2(a); + } + + public static Int32Value Conv_Ovf_U2_Un(Int32Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 16) || + (uint)a.Value > ushort.MaxValue) + return CreateUnknownUInt16(); + return Conv_U2(a); + } + + public static Int32Value Conv_Ovf_U4(Int32Value a) { + if (!IsBitValid(a.ValidMask, 31) || a.Value < 0) + return CreateUnknown(); + return a; + } + + public static Int32Value Conv_Ovf_U4_Un(Int32Value a) { + return a; + } + + public static Int64Value Conv_Ovf_U8(Int32Value a) { + if (!IsBitValid(a.ValidMask, 31) || a.Value < 0) + return Int64Value.CreateUnknown(); + return new Int64Value(a.Value, (ulong)a.ValidMask | (Int64Value.NO_UNKNOWN_BITS << 32)); + } + + public static Int64Value Conv_Ovf_U8_Un(Int32Value a) { + return new Int64Value((long)(uint)a.Value, a.ValidMask | (Int64Value.NO_UNKNOWN_BITS << 32)); } public static Int32Value Add(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return new Int32Value(a.value + b.value); + return new Int32Value(a.Value + b.Value); if (ReferenceEquals(a, b)) - return new Int32Value(a.value << 1, (a.validMask << 1) | 1); + return new Int32Value(a.Value << 1, (a.ValidMask << 1) | 1); return CreateUnknown(); } public static Int32Value Sub(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return new Int32Value(a.value - b.value); + return new Int32Value(a.Value - b.Value); if (ReferenceEquals(a, b)) - return zero; + return Zero; return CreateUnknown(); } public static Int32Value Mul(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return new Int32Value(a.value * b.value); + return new Int32Value(a.Value * b.Value); if (a.IsZero() || b.IsZero()) - return zero; + return Zero; if (a.HasValue(1)) return b; if (b.HasValue(1)) @@ -245,14 +362,14 @@ namespace de4dot.blocks.cflow { public static Int32Value Div(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int32Value(a.value / b.value); + return new Int32Value(a.Value / b.Value); } catch (ArithmeticException) { return CreateUnknown(); } } if (ReferenceEquals(a, b) && a.IsNonZero()) - return one; + return One; if (b.HasValue(1)) return a; return CreateUnknown(); @@ -261,14 +378,14 @@ namespace de4dot.blocks.cflow { public static Int32Value Div_Un(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int32Value((int)((uint)a.value / (uint)b.value)); + return new Int32Value((int)((uint)a.Value / (uint)b.Value)); } catch (ArithmeticException) { return CreateUnknown(); } } if (ReferenceEquals(a, b) && a.IsNonZero()) - return one; + return One; if (b.HasValue(1)) return a; return CreateUnknown(); @@ -277,150 +394,219 @@ namespace de4dot.blocks.cflow { public static Int32Value Rem(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int32Value(a.value % b.value); + return new Int32Value(a.Value % b.Value); } catch (ArithmeticException) { return CreateUnknown(); } } if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1)) - return zero; + return Zero; return CreateUnknown(); } public static Int32Value Rem_Un(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int32Value((int)((uint)a.value % (uint)b.value)); + return new Int32Value((int)((uint)a.Value % (uint)b.Value)); } catch (ArithmeticException) { return CreateUnknown(); } } if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1)) - return zero; + return Zero; return CreateUnknown(); } public static Int32Value Neg(Int32Value a) { if (a.AllBitsValid()) - return new Int32Value(-a.value); + return new Int32Value(-a.Value); + return CreateUnknown(); + } + + public static Int32Value Add_Ovf(Int32Value a, Int32Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + try { + return new Int32Value(checked(a.Value + b.Value)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int32Value Add_Ovf_Un(Int32Value a, Int32Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + uint aa = (uint)a.Value, bb = (uint)b.Value; + try { + return new Int32Value((int)checked(aa + bb)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int32Value Sub_Ovf(Int32Value a, Int32Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + try { + return new Int32Value(checked(a.Value - b.Value)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int32Value Sub_Ovf_Un(Int32Value a, Int32Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + uint aa = (uint)a.Value, bb = (uint)b.Value; + try { + return new Int32Value((int)checked(aa - bb)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int32Value Mul_Ovf(Int32Value a, Int32Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + try { + return new Int32Value(checked(a.Value * b.Value)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int32Value Mul_Ovf_Un(Int32Value a, Int32Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + uint aa = (uint)a.Value, bb = (uint)b.Value; + try { + return new Int32Value((int)checked(aa * bb)); + } + catch (OverflowException) { + } + } return CreateUnknown(); } public static Int32Value And(Int32Value a, Int32Value b) { - int av = a.value, bv = b.value; - uint am = a.validMask, bm = b.validMask; - return new Int32Value(av & bv, (uint)((am & bm) | ((av & am) ^ am) | ((bv & bm) ^ bm))); + int av = a.Value, bv = b.Value; + uint am = a.ValidMask, bm = b.ValidMask; + return new Int32Value(av & bv, (am & bm) | (((uint)av & am) ^ am) | (((uint)bv & bm) ^ bm)); } public static Int32Value Or(Int32Value a, Int32Value b) { - int av = a.value, bv = b.value; - uint am = a.validMask, bm = b.validMask; - return new Int32Value(av | bv, (uint)((am & bm) | (av & am) | (bv & bm))); + int av = a.Value, bv = b.Value; + uint am = a.ValidMask, bm = b.ValidMask; + return new Int32Value(av | bv, (am & bm) | ((uint)av & am) | ((uint)bv & bm)); } public static Int32Value Xor(Int32Value a, Int32Value b) { if (ReferenceEquals(a, b)) - return zero; - int av = a.value, bv = b.value; - uint am = a.validMask, bm = b.validMask; - return new Int32Value(av ^ bv, (uint)(am & bm)); + return Zero; + int av = a.Value, bv = b.Value; + uint am = a.ValidMask, bm = b.ValidMask; + return new Int32Value(av ^ bv, am & bm); } public static Int32Value Not(Int32Value a) { - return new Int32Value(~a.value, a.validMask); + return new Int32Value(~a.Value, a.ValidMask); } public static Int32Value Shl(Int32Value a, Int32Value b) { if (b.HasUnknownBits()) return CreateUnknown(); - if (b.value == 0) + if (b.Value == 0) return a; - if (b.value < 0 || b.value >= sizeof(int) * 8) + if (b.Value < 0 || b.Value >= sizeof(int) * 8) return CreateUnknown(); - int shift = b.value; - uint validMask = (a.validMask << shift) | (uint.MaxValue >> (sizeof(int) * 8 - shift)); - return new Int32Value(a.value << shift, validMask); + int shift = b.Value; + uint validMask = (a.ValidMask << shift) | (uint.MaxValue >> (sizeof(int) * 8 - shift)); + return new Int32Value(a.Value << shift, validMask); } public static Int32Value Shr(Int32Value a, Int32Value b) { if (b.HasUnknownBits()) return CreateUnknown(); - if (b.value == 0) + if (b.Value == 0) return a; - if (b.value < 0 || b.value >= sizeof(int) * 8) + if (b.Value < 0 || b.Value >= sizeof(int) * 8) return CreateUnknown(); - int shift = b.value; - uint validMask = a.validMask >> shift; + int shift = b.Value; + uint validMask = a.ValidMask >> shift; if (a.IsBitValid(sizeof(int) * 8 - 1)) validMask |= (uint.MaxValue << (sizeof(int) * 8 - shift)); - return new Int32Value(a.value >> shift, validMask); + return new Int32Value(a.Value >> shift, validMask); } public static Int32Value Shr_Un(Int32Value a, Int32Value b) { if (b.HasUnknownBits()) return CreateUnknown(); - if (b.value == 0) + if (b.Value == 0) return a; - if (b.value < 0 || b.value >= sizeof(int) * 8) + if (b.Value < 0 || b.Value >= sizeof(int) * 8) return CreateUnknown(); - int shift = b.value; - uint validMask = (a.validMask >> shift) | (uint.MaxValue << (sizeof(int) * 8 - shift)); - return new Int32Value((int)((uint)a.value >> shift), validMask); + int shift = b.Value; + uint validMask = (a.ValidMask >> shift) | (uint.MaxValue << (sizeof(int) * 8 - shift)); + return new Int32Value((int)((uint)a.Value >> shift), validMask); } - static Int32Value create(Bool3 b) { + static Int32Value Create(Bool3 b) { switch (b) { - case Bool3.False: return zero; - case Bool3.True: return one; + case Bool3.False: return Zero; + case Bool3.True: return One; default: return CreateUnknownBool(); } } public static Int32Value Ceq(Int32Value a, Int32Value b) { - return create(CompareEq(a, b)); + return Create(CompareEq(a, b)); } public static Int32Value Cgt(Int32Value a, Int32Value b) { - return create(CompareGt(a, b)); + return Create(CompareGt(a, b)); } public static Int32Value Cgt_Un(Int32Value a, Int32Value b) { - return create(CompareGt_Un(a, b)); + return Create(CompareGt_Un(a, b)); } public static Int32Value Clt(Int32Value a, Int32Value b) { - return create(CompareLt(a, b)); + return Create(CompareLt(a, b)); } public static Int32Value Clt_Un(Int32Value a, Int32Value b) { - return create(CompareLt_Un(a, b)); + return Create(CompareLt_Un(a, b)); } public static Bool3 CompareEq(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value == b.value ? Bool3.True : Bool3.False; + return a.Value == b.Value ? Bool3.True : Bool3.False; if (ReferenceEquals(a, b)) return Bool3.True; - if ((a.value & a.validMask & b.validMask) != (b.value & a.validMask & b.validMask)) + if (((uint)a.Value & a.ValidMask & b.ValidMask) != ((uint)b.Value & a.ValidMask & b.ValidMask)) return Bool3.False; return Bool3.Unknown; } public static Bool3 CompareNeq(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value != b.value ? Bool3.True : Bool3.False; + return a.Value != b.Value ? Bool3.True : Bool3.False; if (ReferenceEquals(a, b)) return Bool3.False; - if ((a.value & a.validMask & b.validMask) != (b.value & a.validMask & b.validMask)) + if (((uint)a.Value & a.ValidMask & b.ValidMask) != ((uint)b.Value & a.ValidMask & b.ValidMask)) return Bool3.True; return Bool3.Unknown; } public static Bool3 CompareGt(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value > b.value ? Bool3.True : Bool3.False; + return a.Value > b.Value ? Bool3.True : Bool3.False; if (a.HasValue(int.MinValue)) return Bool3.False; // min > x => false if (b.HasValue(int.MaxValue)) @@ -430,7 +616,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareGt_Un(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (uint)a.value > (uint)b.value ? Bool3.True : Bool3.False; + return (uint)a.Value > (uint)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(uint.MinValue)) return Bool3.False; // min > x => false if (b.HasValue(uint.MaxValue)) @@ -440,7 +626,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareGe(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value >= b.value ? Bool3.True : Bool3.False; + return a.Value >= b.Value ? Bool3.True : Bool3.False; if (a.HasValue(int.MaxValue)) return Bool3.True; // max >= x => true if (b.HasValue(int.MinValue)) @@ -450,7 +636,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareGe_Un(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (uint)a.value >= (uint)b.value ? Bool3.True : Bool3.False; + return (uint)a.Value >= (uint)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(uint.MaxValue)) return Bool3.True; // max >= x => true if (b.HasValue(uint.MinValue)) @@ -460,7 +646,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLe(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value <= b.value ? Bool3.True : Bool3.False; + return a.Value <= b.Value ? Bool3.True : Bool3.False; if (a.HasValue(int.MinValue)) return Bool3.True; // min <= x => true if (b.HasValue(int.MaxValue)) @@ -470,7 +656,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLe_Un(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (uint)a.value <= (uint)b.value ? Bool3.True : Bool3.False; + return (uint)a.Value <= (uint)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(uint.MinValue)) return Bool3.True; // min <= x => true if (b.HasValue(uint.MaxValue)) @@ -480,7 +666,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLt(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value < b.value ? Bool3.True : Bool3.False; + return a.Value < b.Value ? Bool3.True : Bool3.False; if (a.HasValue(int.MaxValue)) return Bool3.False; // max < x => false if (b.HasValue(int.MinValue)) @@ -490,7 +676,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLt_Un(Int32Value a, Int32Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (uint)a.value < (uint)b.value ? Bool3.True : Bool3.False; + return (uint)a.Value < (uint)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(uint.MaxValue)) return Bool3.False; // max < x => false if (b.HasValue(uint.MinValue)) @@ -500,24 +686,24 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareTrue(Int32Value a) { if (a.AllBitsValid()) - return a.value != 0 ? Bool3.True : Bool3.False; - if ((a.value & a.validMask) != 0) + return a.Value != 0 ? Bool3.True : Bool3.False; + if (((uint)a.Value & a.ValidMask) != 0) return Bool3.True; return Bool3.Unknown; } public static Bool3 CompareFalse(Int32Value a) { if (a.AllBitsValid()) - return a.value == 0 ? Bool3.True : Bool3.False; - if ((a.value & a.validMask) != 0) + return a.Value == 0 ? Bool3.True : Bool3.False; + if (((uint)a.Value & a.ValidMask) != 0) return Bool3.False; return Bool3.Unknown; } public override string ToString() { if (AllBitsValid()) - return value.ToString(); - return string.Format("0x{0:X8}({1:X8})", value, validMask); + return Value.ToString(); + return string.Format("0x{0:X8}({1:X8})", Value, ValidMask); } } } diff --git a/blocks/cflow/Int64Value.cs b/de4dot.blocks/cflow/Int64Value.cs similarity index 51% rename from blocks/cflow/Int64Value.cs rename to de4dot.blocks/cflow/Int64Value.cs index 9ec2ff41..48a236e3 100644 --- a/blocks/cflow/Int64Value.cs +++ b/de4dot.blocks/cflow/Int64Value.cs @@ -21,27 +21,27 @@ using System; namespace de4dot.blocks.cflow { public class Int64Value : Value { - public static readonly Int64Value zero = new Int64Value(0); - public static readonly Int64Value one = new Int64Value(1); + public static readonly Int64Value Zero = new Int64Value(0); + public static readonly Int64Value One = new Int64Value(1); - const ulong NO_UNKNOWN_BITS = ulong.MaxValue; - public readonly long value; - public readonly ulong validMask; + internal const ulong NO_UNKNOWN_BITS = ulong.MaxValue; + public readonly long Value; + public readonly ulong ValidMask; public Int64Value(long value) : base(ValueType.Int64) { - this.value = value; - this.validMask = NO_UNKNOWN_BITS; + this.Value = value; + this.ValidMask = NO_UNKNOWN_BITS; } public Int64Value(long value, ulong validMask) : base(ValueType.Int64) { - this.value = value; - this.validMask = validMask; + this.Value = value; + this.ValidMask = validMask; } bool HasUnknownBits() { - return validMask != NO_UNKNOWN_BITS; + return ValidMask != NO_UNKNOWN_BITS; } public bool AllBitsValid() { @@ -49,13 +49,17 @@ namespace de4dot.blocks.cflow { } bool IsBitValid(int n) { - return IsBitValid(validMask, n); + return IsBitValid(ValidMask, n); } static bool IsBitValid(ulong validMask, int n) { return (validMask & (1UL << n)) != 0; } + bool AreBitsValid(ulong bitsToTest) { + return (ValidMask & bitsToTest) == bitsToTest; + } + public static Int64Value CreateUnknown() { return new Int64Value(0, 0UL); } @@ -65,11 +69,11 @@ namespace de4dot.blocks.cflow { } public bool IsNonZero() { - return ((ulong)value & validMask) != 0; + return ((ulong)Value & ValidMask) != 0; } public bool HasValue(long value) { - return AllBitsValid() && this.value == value; + return AllBitsValid() && this.Value == value; } public bool HasValue(ulong value) { @@ -77,8 +81,8 @@ namespace de4dot.blocks.cflow { } public static Int64Value Conv_U8(Int32Value a) { - long value = (long)(ulong)(uint)a.value; - ulong validMask = a.validMask | (NO_UNKNOWN_BITS << 32); + long value = (long)(ulong)(uint)a.Value; + ulong validMask = a.ValidMask | (NO_UNKNOWN_BITS << 32); return new Int64Value(value, validMask); } @@ -87,12 +91,14 @@ namespace de4dot.blocks.cflow { } public static Int64Value Conv_U8(Real8Value a) { - return new Int64Value((long)(ulong)a.value); + if (!a.IsValid) + return CreateUnknown(); + return new Int64Value((long)(ulong)a.Value); } public static Int64Value Conv_I8(Int32Value a) { - long value = a.value; - ulong validMask = a.validMask; + long value = a.Value; + ulong validMask = a.ValidMask; if (IsBitValid(validMask, 31)) validMask |= NO_UNKNOWN_BITS << 32; else @@ -105,30 +111,140 @@ namespace de4dot.blocks.cflow { } public static Int64Value Conv_I8(Real8Value a) { - return new Int64Value((long)a.value); + if (!a.IsValid) + return CreateUnknown(); + return new Int64Value((long)a.Value); + } + + bool CheckSign(ulong mask) { + return ((ulong)Value & mask) == 0 || ((ulong)Value & mask) == mask; + } + + public static Int32Value Conv_Ovf_I1(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 7) || + !a.CheckSign(NO_UNKNOWN_BITS << 7)) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_I1(a); + } + + public static Int32Value Conv_Ovf_I1_Un(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 7) || + (ulong)a.Value > (ulong)sbyte.MaxValue) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_I1(a); + } + + public static Int32Value Conv_Ovf_I2(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 15) || + !a.CheckSign(NO_UNKNOWN_BITS << 15)) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_I2(a); + } + + public static Int32Value Conv_Ovf_I2_Un(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 15) || + (ulong)a.Value > (ulong)short.MaxValue) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_I2(a); + } + + public static Int32Value Conv_Ovf_I4(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 31) || + !a.CheckSign(NO_UNKNOWN_BITS << 31)) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_I4(a); + } + + public static Int32Value Conv_Ovf_I4_Un(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 31) || + (ulong)a.Value > (ulong)int.MaxValue) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_I4(a); + } + + public static Int64Value Conv_Ovf_I8(Int64Value a) { + return a; + } + + public static Int64Value Conv_Ovf_I8_Un(Int64Value a) { + if (!IsBitValid(a.ValidMask, 63) || a.Value < 0) + return CreateUnknown(); + return a; + } + + public static Int32Value Conv_Ovf_U1(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 7) || + a.Value < 0 || a.Value > byte.MaxValue) + return Int32Value.CreateUnknownUInt8(); + return Int32Value.Conv_U1(a); + } + + public static Int32Value Conv_Ovf_U1_Un(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 8) || + (ulong)a.Value > byte.MaxValue) + return Int32Value.CreateUnknownUInt8(); + return Int32Value.Conv_U1(a); + } + + public static Int32Value Conv_Ovf_U2(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 15) || + a.Value < 0 || a.Value > ushort.MaxValue) + return Int32Value.CreateUnknownUInt16(); + return Int32Value.Conv_U2(a); + } + + public static Int32Value Conv_Ovf_U2_Un(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 16) || + (ulong)a.Value > ushort.MaxValue) + return Int32Value.CreateUnknownUInt16(); + return Int32Value.Conv_U2(a); + } + + public static Int32Value Conv_Ovf_U4(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 31) || + a.Value < 0 || a.Value > uint.MaxValue) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_U4(a); + } + + public static Int32Value Conv_Ovf_U4_Un(Int64Value a) { + if (!a.AreBitsValid(NO_UNKNOWN_BITS << 32) || + (ulong)a.Value > uint.MaxValue) + return Int32Value.CreateUnknown(); + return Int32Value.Conv_U4(a); + } + + public static Int64Value Conv_Ovf_U8(Int64Value a) { + if (!IsBitValid(a.ValidMask, 63) || a.Value < 0) + return CreateUnknown(); + return a; + } + + public static Int64Value Conv_Ovf_U8_Un(Int64Value a) { + return a; } public static Int64Value Add(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return new Int64Value(a.value + b.value); + return new Int64Value(a.Value + b.Value); if (ReferenceEquals(a, b)) - return new Int64Value(a.value << 1, (a.validMask << 1) | 1); + return new Int64Value(a.Value << 1, (a.ValidMask << 1) | 1); return CreateUnknown(); } public static Int64Value Sub(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return new Int64Value(a.value - b.value); + return new Int64Value(a.Value - b.Value); if (ReferenceEquals(a, b)) - return zero; + return Zero; return CreateUnknown(); } public static Int64Value Mul(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return new Int64Value(a.value * b.value); + return new Int64Value(a.Value * b.Value); if (a.IsZero() || b.IsZero()) - return zero; + return Zero; if (a.HasValue(1)) return b; if (b.HasValue(1)) @@ -139,14 +255,14 @@ namespace de4dot.blocks.cflow { public static Int64Value Div(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int64Value(a.value / b.value); + return new Int64Value(a.Value / b.Value); } catch (ArithmeticException) { return CreateUnknown(); } } if (ReferenceEquals(a, b) && a.IsNonZero()) - return one; + return One; if (b.HasValue(1)) return a; return CreateUnknown(); @@ -155,14 +271,14 @@ namespace de4dot.blocks.cflow { public static Int64Value Div_Un(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int64Value((long)((ulong)a.value / (ulong)b.value)); + return new Int64Value((long)((ulong)a.Value / (ulong)b.Value)); } catch (ArithmeticException) { return CreateUnknown(); } } if (ReferenceEquals(a, b) && a.IsNonZero()) - return one; + return One; if (b.HasValue(1)) return a; return CreateUnknown(); @@ -171,103 +287,172 @@ namespace de4dot.blocks.cflow { public static Int64Value Rem(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int64Value(a.value % b.value); + return new Int64Value(a.Value % b.Value); } catch (ArithmeticException) { return CreateUnknown(); } } if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1)) - return zero; + return Zero; return CreateUnknown(); } public static Int64Value Rem_Un(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) { try { - return new Int64Value((long)((ulong)a.value % (ulong)b.value)); + return new Int64Value((long)((ulong)a.Value % (ulong)b.Value)); } catch (ArithmeticException) { return CreateUnknown(); } } if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1)) - return zero; + return Zero; return CreateUnknown(); } public static Int64Value Neg(Int64Value a) { if (a.AllBitsValid()) - return new Int64Value(-a.value); + return new Int64Value(-a.Value); + return CreateUnknown(); + } + + public static Int64Value Add_Ovf(Int64Value a, Int64Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + try { + return new Int64Value(checked(a.Value + b.Value)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int64Value Add_Ovf_Un(Int64Value a, Int64Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + ulong aa = (ulong)a.Value, bb = (ulong)b.Value; + try { + return new Int64Value((long)checked(aa + bb)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int64Value Sub_Ovf(Int64Value a, Int64Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + try { + return new Int64Value(checked(a.Value - b.Value)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int64Value Sub_Ovf_Un(Int64Value a, Int64Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + ulong aa = (ulong)a.Value, bb = (ulong)b.Value; + try { + return new Int64Value((long)checked(aa - bb)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int64Value Mul_Ovf(Int64Value a, Int64Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + try { + return new Int64Value(checked(a.Value * b.Value)); + } + catch (OverflowException) { + } + } + return CreateUnknown(); + } + + public static Int64Value Mul_Ovf_Un(Int64Value a, Int64Value b) { + if (a.AllBitsValid() && b.AllBitsValid()) { + ulong aa = (ulong)a.Value, bb = (ulong)b.Value; + try { + return new Int64Value((long)checked(aa * bb)); + } + catch (OverflowException) { + } + } return CreateUnknown(); } public static Int64Value And(Int64Value a, Int64Value b) { - long av = a.value, bv = b.value; - ulong am = a.validMask, bm = b.validMask; + long av = a.Value, bv = b.Value; + ulong am = a.ValidMask, bm = b.ValidMask; return new Int64Value(av & bv, (am & bm) | (((ulong)av & am) ^ am) | (((ulong)bv & bm) ^ bm)); } public static Int64Value Or(Int64Value a, Int64Value b) { - long av = a.value, bv = b.value; - ulong am = a.validMask, bm = b.validMask; + long av = a.Value, bv = b.Value; + ulong am = a.ValidMask, bm = b.ValidMask; return new Int64Value(av | bv, (am & bm) | ((ulong)av & am) | ((ulong)bv & bm)); } public static Int64Value Xor(Int64Value a, Int64Value b) { if (ReferenceEquals(a, b)) - return zero; - long av = a.value, bv = b.value; - ulong am = a.validMask, bm = b.validMask; + return Zero; + long av = a.Value, bv = b.Value; + ulong am = a.ValidMask, bm = b.ValidMask; return new Int64Value(av ^ bv, am & bm); } public static Int64Value Not(Int64Value a) { - return new Int64Value(~a.value, a.validMask); + return new Int64Value(~a.Value, a.ValidMask); } public static Int64Value Shl(Int64Value a, Int32Value b) { if (b.HasUnknownBits()) return CreateUnknown(); - if (b.value == 0) + if (b.Value == 0) return a; - if (b.value < 0 || b.value >= sizeof(long) * 8) + if (b.Value < 0 || b.Value >= sizeof(long) * 8) return CreateUnknown(); - int shift = b.value; - ulong validMask = (a.validMask << shift) | (ulong.MaxValue >> (sizeof(long) * 8 - shift)); - return new Int64Value(a.value << shift, validMask); + int shift = b.Value; + ulong validMask = (a.ValidMask << shift) | (ulong.MaxValue >> (sizeof(long) * 8 - shift)); + return new Int64Value(a.Value << shift, validMask); } public static Int64Value Shr(Int64Value a, Int32Value b) { if (b.HasUnknownBits()) return CreateUnknown(); - if (b.value == 0) + if (b.Value == 0) return a; - if (b.value < 0 || b.value >= sizeof(long) * 8) + if (b.Value < 0 || b.Value >= sizeof(long) * 8) return CreateUnknown(); - int shift = b.value; - ulong validMask = a.validMask >> shift; + int shift = b.Value; + ulong validMask = a.ValidMask >> shift; if (a.IsBitValid(sizeof(long) * 8 - 1)) validMask |= (ulong.MaxValue << (sizeof(long) * 8 - shift)); - return new Int64Value(a.value >> shift, validMask); + return new Int64Value(a.Value >> shift, validMask); } public static Int64Value Shr_Un(Int64Value a, Int32Value b) { if (b.HasUnknownBits()) return CreateUnknown(); - if (b.value == 0) + if (b.Value == 0) return a; - if (b.value < 0 || b.value >= sizeof(long) * 8) + if (b.Value < 0 || b.Value >= sizeof(long) * 8) return CreateUnknown(); - int shift = b.value; - ulong validMask = (a.validMask >> shift) | (ulong.MaxValue << (sizeof(long) * 8 - shift)); - return new Int64Value((long)((ulong)a.value >> shift), validMask); + int shift = b.Value; + ulong validMask = (a.ValidMask >> shift) | (ulong.MaxValue << (sizeof(long) * 8 - shift)); + return new Int64Value((long)((ulong)a.Value >> shift), validMask); } static Int32Value Create(Bool3 b) { switch (b) { - case Bool3.False: return Int32Value.zero; - case Bool3.True: return Int32Value.one; + case Bool3.False: return Int32Value.Zero; + case Bool3.True: return Int32Value.One; default: return Int32Value.CreateUnknownBool(); } } @@ -294,27 +479,27 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareEq(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value == b.value ? Bool3.True : Bool3.False; + return a.Value == b.Value ? Bool3.True : Bool3.False; if (ReferenceEquals(a, b)) return Bool3.True; - if (((ulong)a.value & a.validMask & b.validMask) != ((ulong)b.value & a.validMask & b.validMask)) + if (((ulong)a.Value & a.ValidMask & b.ValidMask) != ((ulong)b.Value & a.ValidMask & b.ValidMask)) return Bool3.False; return Bool3.Unknown; } public static Bool3 CompareNeq(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value != b.value ? Bool3.True : Bool3.False; + return a.Value != b.Value ? Bool3.True : Bool3.False; if (ReferenceEquals(a, b)) return Bool3.False; - if (((ulong)a.value & a.validMask & b.validMask) != ((ulong)b.value & a.validMask & b.validMask)) + if (((ulong)a.Value & a.ValidMask & b.ValidMask) != ((ulong)b.Value & a.ValidMask & b.ValidMask)) return Bool3.True; return Bool3.Unknown; } public static Bool3 CompareGt(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value > b.value ? Bool3.True : Bool3.False; + return a.Value > b.Value ? Bool3.True : Bool3.False; if (a.HasValue(long.MinValue)) return Bool3.False; // min > x => false if (b.HasValue(long.MaxValue)) @@ -324,7 +509,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareGt_Un(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (ulong)a.value > (ulong)b.value ? Bool3.True : Bool3.False; + return (ulong)a.Value > (ulong)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(ulong.MinValue)) return Bool3.False; // min > x => false if (b.HasValue(ulong.MaxValue)) @@ -334,7 +519,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareGe(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value >= b.value ? Bool3.True : Bool3.False; + return a.Value >= b.Value ? Bool3.True : Bool3.False; if (a.HasValue(long.MaxValue)) return Bool3.True; // max >= x => true if (b.HasValue(long.MinValue)) @@ -344,7 +529,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareGe_Un(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (ulong)a.value >= (ulong)b.value ? Bool3.True : Bool3.False; + return (ulong)a.Value >= (ulong)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(ulong.MaxValue)) return Bool3.True; // max >= x => true if (b.HasValue(ulong.MinValue)) @@ -354,7 +539,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLe(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value <= b.value ? Bool3.True : Bool3.False; + return a.Value <= b.Value ? Bool3.True : Bool3.False; if (a.HasValue(long.MinValue)) return Bool3.True; // min <= x => true if (b.HasValue(long.MaxValue)) @@ -364,7 +549,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLe_Un(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (ulong)a.value <= (ulong)b.value ? Bool3.True : Bool3.False; + return (ulong)a.Value <= (ulong)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(ulong.MinValue)) return Bool3.True; // min <= x => true if (b.HasValue(ulong.MaxValue)) @@ -374,7 +559,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLt(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return a.value < b.value ? Bool3.True : Bool3.False; + return a.Value < b.Value ? Bool3.True : Bool3.False; if (a.HasValue(long.MaxValue)) return Bool3.False; // max < x => false if (b.HasValue(long.MinValue)) @@ -384,7 +569,7 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareLt_Un(Int64Value a, Int64Value b) { if (a.AllBitsValid() && b.AllBitsValid()) - return (ulong)a.value < (ulong)b.value ? Bool3.True : Bool3.False; + return (ulong)a.Value < (ulong)b.Value ? Bool3.True : Bool3.False; if (a.HasValue(ulong.MaxValue)) return Bool3.False; // max < x => false if (b.HasValue(ulong.MinValue)) @@ -394,24 +579,24 @@ namespace de4dot.blocks.cflow { public static Bool3 CompareTrue(Int64Value a) { if (a.AllBitsValid()) - return a.value != 0 ? Bool3.True : Bool3.False; - if (((ulong)a.value & a.validMask) != 0) + return a.Value != 0 ? Bool3.True : Bool3.False; + if (((ulong)a.Value & a.ValidMask) != 0) return Bool3.True; return Bool3.Unknown; } public static Bool3 CompareFalse(Int64Value a) { if (a.AllBitsValid()) - return a.value == 0 ? Bool3.True : Bool3.False; - if (((ulong)a.value & a.validMask) != 0) + return a.Value == 0 ? Bool3.True : Bool3.False; + if (((ulong)a.Value & a.ValidMask) != 0) return Bool3.False; return Bool3.Unknown; } public override string ToString() { if (AllBitsValid()) - return value.ToString(); - return string.Format("0x{0:X8}L({1:X8})", value, validMask); + return Value.ToString(); + return string.Format("0x{0:X8}L({1:X8})", Value, ValidMask); } } } diff --git a/blocks/cflow/MethodCallInliner.cs b/de4dot.blocks/cflow/MethodCallInliner.cs similarity index 83% rename from blocks/cflow/MethodCallInliner.cs rename to de4dot.blocks/cflow/MethodCallInliner.cs index a44fc610..4265e706 100644 --- a/blocks/cflow/MethodCallInliner.cs +++ b/de4dot.blocks/cflow/MethodCallInliner.cs @@ -55,6 +55,10 @@ namespace de4dot.blocks.cflow { return inlineInstanceMethods; } + protected virtual Instruction GetFirstInstruction(IList instrs, ref int index) { + return DotNetUtils.GetInstruction(instrs, ref index); + } + bool InlineMethod(Instruction callInstr, int instrIndex) { var methodToInline = callInstr.Operand as MethodDef; if (methodToInline == null) @@ -67,10 +71,11 @@ namespace de4dot.blocks.cflow { return false; int index = 0; - var instr = DotNetUtils.GetInstruction(body.Instructions, ref index); + var instr = GetFirstInstruction(body.Instructions, ref index); if (instr == null) return false; + bool inlinedMethod; switch (instr.OpCode.Code) { case Code.Ldarg: case Code.Ldarg_S: @@ -83,7 +88,8 @@ namespace de4dot.blocks.cflow { case Code.Call: case Code.Callvirt: case Code.Newobj: - return InlineOtherMethod(instrIndex, methodToInline, instr, index); + inlinedMethod = InlineOtherMethod(instrIndex, methodToInline, instr, index); + break; case Code.Ldc_I4: case Code.Ldc_I4_0: @@ -106,11 +112,18 @@ namespace de4dot.blocks.cflow { case Code.Ldtoken: case Code.Ldsfld: case Code.Ldsflda: - return InlineLoadMethod(instrIndex, methodToInline, instr, index); + inlinedMethod = InlineLoadMethod(instrIndex, methodToInline, instr, index); + break; default: - return false; + inlinedMethod = false; + break; } + OnInlinedMethod(methodToInline, inlinedMethod); + return inlinedMethod; + } + + protected virtual void OnInlinedMethod(MethodDef methodToInline, bool inlinedMethod) { } protected override bool IsCompatibleType(int paramIndex, IType origType, IType newType) { diff --git a/blocks/cflow/MethodCallInlinerBase.cs b/de4dot.blocks/cflow/MethodCallInlinerBase.cs similarity index 94% rename from blocks/cflow/MethodCallInlinerBase.cs rename to de4dot.blocks/cflow/MethodCallInlinerBase.cs index 873c4d62..d8ed9247 100644 --- a/blocks/cflow/MethodCallInlinerBase.cs +++ b/de4dot.blocks/cflow/MethodCallInlinerBase.cs @@ -142,7 +142,9 @@ namespace de4dot.blocks.cflow { if (instr == null || loadIndex != methodArgsCount - popLastArgs) return null; - if (instr.OpCode.Code == Code.Call || instr.OpCode.Code == Code.Callvirt) { + switch (instr.OpCode.Code) { + case Code.Call: + case Code.Callvirt: if (foundLdarga) return null; var callInstr = instr; @@ -160,8 +162,8 @@ namespace de4dot.blocks.cflow { return null; return new InstructionPatcher(patchIndex, instrIndex, callInstr); - } - else if (instr.OpCode.Code == Code.Newobj) { + + case Code.Newobj: if (foundLdarga) return null; var newobjInstr = instr; @@ -185,20 +187,30 @@ namespace de4dot.blocks.cflow { return null; return new InstructionPatcher(patchIndex, instrIndex, newobjInstr); - } - else if (instr.OpCode.Code == Code.Ldfld || instr.OpCode.Code == Code.Ldflda || - instr.OpCode.Code == Code.Ldftn || instr.OpCode.Code == Code.Ldvirtftn) { + + case Code.Ldfld: + case Code.Ldflda: + case Code.Ldftn: + case Code.Ldvirtftn: + case Code.Ldlen: + case Code.Initobj: + case Code.Isinst: + case Code.Castclass: + case Code.Newarr: + case Code.Ldtoken: + case Code.Unbox_Any: var ldInstr = instr; if (methodArgsCount != 1) return null; - if (!HasAccessTo(instr.Operand)) + if (instr.OpCode.OperandType != OperandType.InlineNone && !HasAccessTo(instr.Operand)) return null; return new InstructionPatcher(patchIndex, instrIndex, ldInstr); - } - return null; + default: + return null; + } } bool HasAccessTo(object operand) { diff --git a/de4dot.blocks/cflow/Real8Value.cs b/de4dot.blocks/cflow/Real8Value.cs new file mode 100644 index 00000000..5087046d --- /dev/null +++ b/de4dot.blocks/cflow/Real8Value.cs @@ -0,0 +1,177 @@ +/* + 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 . +*/ + +namespace de4dot.blocks.cflow { + public class Real8Value : Value { + public readonly double Value; + public readonly bool IsValid; + + public Real8Value(double value) + : base(ValueType.Real8) { + this.Value = value; + this.IsValid = true; + } + + public Real8Value(double value, bool isValid) + : base(ValueType.Real8) { + this.Value = value; + this.IsValid = isValid; + } + + public static Real8Value CreateUnknown() { + return new Real8Value(0, false); + } + + public Real8Value ToSingle() { + if (!IsValid) + return CreateUnknown(); + return new Real8Value((float)Value); + } + + public static Real8Value Add(Real8Value a, Real8Value b) { + if (!a.IsValid || !b.IsValid) + return CreateUnknown(); + return new Real8Value(a.Value + b.Value); + } + + public static Real8Value Sub(Real8Value a, Real8Value b) { + if (!a.IsValid || !b.IsValid) + return CreateUnknown(); + return new Real8Value(a.Value - b.Value); + } + + public static Real8Value Mul(Real8Value a, Real8Value b) { + if (!a.IsValid || !b.IsValid) + return CreateUnknown(); + return new Real8Value(a.Value * b.Value); + } + + public static Real8Value Div(Real8Value a, Real8Value b) { + if (!a.IsValid || !b.IsValid) + return CreateUnknown(); + return new Real8Value(a.Value / b.Value); + } + + public static Real8Value Rem(Real8Value a, Real8Value b) { + if (!a.IsValid || !b.IsValid) + return CreateUnknown(); + return new Real8Value(a.Value % b.Value); + } + + public static Real8Value Neg(Real8Value a) { + if (!a.IsValid) + return CreateUnknown(); + return new Real8Value(-a.Value); + } + + public static Real8Value Add_Ovf(Real8Value a, Real8Value b) { + return CreateUnknown(); + } + + public static Real8Value Add_Ovf_Un(Real8Value a, Real8Value b) { + return CreateUnknown(); + } + + public static Real8Value Sub_Ovf(Real8Value a, Real8Value b) { + return CreateUnknown(); + } + + public static Real8Value Sub_Ovf_Un(Real8Value a, Real8Value b) { + return CreateUnknown(); + } + + public static Real8Value Mul_Ovf(Real8Value a, Real8Value b) { + return CreateUnknown(); + } + + public static Real8Value Mul_Ovf_Un(Real8Value a, Real8Value b) { + return CreateUnknown(); + } + + public static Int32Value Conv_Ovf_I1(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int32Value Conv_Ovf_I1_Un(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int32Value Conv_Ovf_I2(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int32Value Conv_Ovf_I2_Un(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int32Value Conv_Ovf_I4(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int32Value Conv_Ovf_I4_Un(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int64Value Conv_Ovf_I8(Real8Value a) { + return Int64Value.CreateUnknown(); + } + + public static Int64Value Conv_Ovf_I8_Un(Real8Value a) { + return Int64Value.CreateUnknown(); + } + + public static Int32Value Conv_Ovf_U1(Real8Value a) { + return Int32Value.CreateUnknownUInt8(); + } + + public static Int32Value Conv_Ovf_U1_Un(Real8Value a) { + return Int32Value.CreateUnknownUInt8(); + } + + public static Int32Value Conv_Ovf_U2(Real8Value a) { + return Int32Value.CreateUnknownUInt16(); + } + + public static Int32Value Conv_Ovf_U2_Un(Real8Value a) { + return Int32Value.CreateUnknownUInt16(); + } + + public static Int32Value Conv_Ovf_U4(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int32Value Conv_Ovf_U4_Un(Real8Value a) { + return Int32Value.CreateUnknown(); + } + + public static Int64Value Conv_Ovf_U8(Real8Value a) { + return Int64Value.CreateUnknown(); + } + + public static Int64Value Conv_Ovf_U8_Un(Real8Value a) { + return Int64Value.CreateUnknown(); + } + + public override string ToString() { + if (!IsValid) + return ""; + return Value.ToString(); + } + } +} diff --git a/blocks/cflow/StLdlocFixer.cs b/de4dot.blocks/cflow/StLdlocFixer.cs similarity index 100% rename from blocks/cflow/StLdlocFixer.cs rename to de4dot.blocks/cflow/StLdlocFixer.cs diff --git a/blocks/cflow/SwitchCflowDeobfuscator.cs b/de4dot.blocks/cflow/SwitchCflowDeobfuscator.cs similarity index 97% rename from blocks/cflow/SwitchCflowDeobfuscator.cs rename to de4dot.blocks/cflow/SwitchCflowDeobfuscator.cs index 329843f3..abec82b8 100644 --- a/blocks/cflow/SwitchCflowDeobfuscator.cs +++ b/de4dot.blocks/cflow/SwitchCflowDeobfuscator.cs @@ -158,7 +158,7 @@ namespace de4dot.blocks.cflow { foreach (var source in new List(block.Sources)) { if (!isBranchBlock(source)) continue; - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == source); instructionEmulator.Emulate(source.Instructions); var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.Pop()); @@ -183,7 +183,7 @@ namespace de4dot.blocks.cflow { bool modified = false; foreach (var source in new List(block.Sources)) { if (isBranchBlock(source)) { - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == source); instructionEmulator.Emulate(source.Instructions); var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.GetLocal(switchVariable)); @@ -193,7 +193,7 @@ namespace de4dot.blocks.cflow { modified = true; } else if (IsBccBlock(source)) { - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == source); instructionEmulator.Emulate(source.Instructions); var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.GetLocal(switchVariable)); @@ -223,7 +223,7 @@ namespace de4dot.blocks.cflow { foreach (var source in new List(block.Sources)) { if (!isBranchBlock(source)) continue; - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == source); instructionEmulator.Emulate(source.Instructions); var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.Pop()); @@ -407,7 +407,7 @@ namespace de4dot.blocks.cflow { } bool EmulateGetTarget(Block switchBlock, out Block target) { - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == switchBlock); try { instructionEmulator.Emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1); } @@ -421,7 +421,7 @@ namespace de4dot.blocks.cflow { } bool WillHaveKnownTarget(Block switchBlock, Block source) { - instructionEmulator.Initialize(blocks); + instructionEmulator.Initialize(blocks, allBlocks[0] == source); try { instructionEmulator.Emulate(source.Instructions); instructionEmulator.Emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1); diff --git a/blocks/cflow/Value.cs b/de4dot.blocks/cflow/Value.cs similarity index 100% rename from blocks/cflow/Value.cs rename to de4dot.blocks/cflow/Value.cs diff --git a/blocks/cflow/ValueStack.cs b/de4dot.blocks/cflow/ValueStack.cs similarity index 100% rename from blocks/cflow/ValueStack.cs rename to de4dot.blocks/cflow/ValueStack.cs diff --git a/blocks/blocks.csproj b/de4dot.blocks/de4dot.blocks.csproj similarity index 97% rename from blocks/blocks.csproj rename to de4dot.blocks/de4dot.blocks.csproj index a0bae6cc..677d22f4 100644 --- a/blocks/blocks.csproj +++ b/de4dot.blocks/de4dot.blocks.csproj @@ -9,7 +9,7 @@ Library Properties de4dot.blocks - blocks + de4dot.blocks v2.0 512 true @@ -76,6 +76,7 @@ + diff --git a/de4dot.code/AssemblyClient/AssemblyClient.cs b/de4dot.code/AssemblyClient/AssemblyClient.cs index 68bca68e..b6a01103 100644 --- a/de4dot.code/AssemblyClient/AssemblyClient.cs +++ b/de4dot.code/AssemblyClient/AssemblyClient.cs @@ -35,8 +35,16 @@ namespace de4dot.code.AssemblyClient { get { return service; } } - public AssemblyClient() - : this(new NewProcessAssemblyServerLoader()) { + public IStringDecrypterService StringDecrypterService { + get { return (IStringDecrypterService)service; } + } + + public IMethodDecrypterService MethodDecrypterService { + get { return (IMethodDecrypterService)service; } + } + + public IGenericService GenericService { + get { return (IGenericService)service; } } public AssemblyClient(IAssemblyServerLoader loader) { diff --git a/de4dot.code/AssemblyClient/AssemblyClientFactory.cs b/de4dot.code/AssemblyClient/AssemblyClientFactory.cs index fdcd9a59..83a1967b 100644 --- a/de4dot.code/AssemblyClient/AssemblyClientFactory.cs +++ b/de4dot.code/AssemblyClient/AssemblyClientFactory.cs @@ -18,21 +18,22 @@ */ using dnlib.DotNet; +using AssemblyData; namespace de4dot.code.AssemblyClient { public interface IAssemblyClientFactory { - IAssemblyClient Create(); + IAssemblyClient Create(AssemblyServiceType serviceType); } public class SameAppDomainAssemblyClientFactory : IAssemblyClientFactory { - public IAssemblyClient Create() { - return new AssemblyClient(new SameAppDomainAssemblyServerLoader()); + public IAssemblyClient Create(AssemblyServiceType serviceType) { + return new AssemblyClient(new SameAppDomainAssemblyServerLoader(serviceType)); } } public class NewAppDomainAssemblyClientFactory : IAssemblyClientFactory { - public IAssemblyClient Create() { - return new AssemblyClient(new NewAppDomainAssemblyServerLoader()); + public IAssemblyClient Create(AssemblyServiceType serviceType) { + return new AssemblyClient(new NewAppDomainAssemblyServerLoader(serviceType)); } } @@ -47,12 +48,12 @@ namespace de4dot.code.AssemblyClient { this.serverVersion = serverVersion; } - public IAssemblyClient Create(ModuleDef module) { - return new AssemblyClient(new NewProcessAssemblyServerLoader(GetServerClrVersion(module))); + public IAssemblyClient Create(AssemblyServiceType serviceType, ModuleDef module) { + return new AssemblyClient(new NewProcessAssemblyServerLoader(serviceType, GetServerClrVersion(module))); } - public IAssemblyClient Create() { - return new AssemblyClient(new NewProcessAssemblyServerLoader(serverVersion)); + public IAssemblyClient Create(AssemblyServiceType serviceType) { + return new AssemblyClient(new NewProcessAssemblyServerLoader(serviceType, serverVersion)); } internal static ServerClrVersion GetServerClrVersion(ModuleDef module) { diff --git a/de4dot.code/AssemblyClient/IAssemblyClient.cs b/de4dot.code/AssemblyClient/IAssemblyClient.cs index 0449796b..1b7a2547 100644 --- a/de4dot.code/AssemblyClient/IAssemblyClient.cs +++ b/de4dot.code/AssemblyClient/IAssemblyClient.cs @@ -23,6 +23,9 @@ using AssemblyData; namespace de4dot.code.AssemblyClient { public interface IAssemblyClient : IDisposable { IAssemblyService Service { get; } + IStringDecrypterService StringDecrypterService { get; } + IMethodDecrypterService MethodDecrypterService { get; } + IGenericService GenericService { get; } void Connect(); void WaitConnected(); } diff --git a/de4dot.code/AssemblyClient/IpcAssemblyServerLoader.cs b/de4dot.code/AssemblyClient/IpcAssemblyServerLoader.cs index 423f4eb7..f2af1232 100644 --- a/de4dot.code/AssemblyClient/IpcAssemblyServerLoader.cs +++ b/de4dot.code/AssemblyClient/IpcAssemblyServerLoader.cs @@ -35,13 +35,15 @@ namespace de4dot.code.AssemblyClient { readonly string assemblyServerFilename; protected string ipcName; protected string ipcUri; + protected AssemblyServiceType serviceType; string url; - protected IpcAssemblyServerLoader() - : this(ServerClrVersion.CLR_ANY_ANYCPU) { + protected IpcAssemblyServerLoader(AssemblyServiceType serviceType) + : this(serviceType, ServerClrVersion.CLR_ANY_ANYCPU) { } - protected IpcAssemblyServerLoader(ServerClrVersion serverVersion) { + protected IpcAssemblyServerLoader(AssemblyServiceType serviceType, ServerClrVersion serverVersion) { + this.serviceType = serviceType; assemblyServerFilename = GetServerName(serverVersion); ipcName = Utils.RandomName(15, 20); ipcUri = Utils.RandomName(15, 20); @@ -69,7 +71,7 @@ namespace de4dot.code.AssemblyClient { public abstract void LoadServer(string filename); public IAssemblyService CreateService() { - return (IAssemblyService)Activator.GetObject(typeof(AssemblyService), url); + return (IAssemblyService)Activator.GetObject(AssemblyService.GetType(serviceType), url); } public abstract void Dispose(); diff --git a/de4dot.code/AssemblyClient/NewAppDomainAssemblyServerLoader.cs b/de4dot.code/AssemblyClient/NewAppDomainAssemblyServerLoader.cs index 16b274ce..f7521663 100644 --- a/de4dot.code/AssemblyClient/NewAppDomainAssemblyServerLoader.cs +++ b/de4dot.code/AssemblyClient/NewAppDomainAssemblyServerLoader.cs @@ -19,6 +19,7 @@ using System; using System.Threading; +using AssemblyData; namespace de4dot.code.AssemblyClient { // Starts the server in a new app domain. @@ -26,6 +27,10 @@ namespace de4dot.code.AssemblyClient { AppDomain appDomain; Thread thread; + public NewAppDomainAssemblyServerLoader(AssemblyServiceType serviceType) + : base(serviceType) { + } + public override void LoadServer(string filename) { if (appDomain != null) throw new ApplicationException("Server is already loaded"); @@ -33,7 +38,9 @@ namespace de4dot.code.AssemblyClient { appDomain = AppDomain.CreateDomain(Utils.RandomName(15, 20)); thread = new Thread(new ThreadStart(() => { try { - appDomain.ExecuteAssembly(filename, null, new string[] { ipcName, ipcUri }); + appDomain.ExecuteAssembly(filename, null, new string[] { + ((int)serviceType).ToString(), ipcName, ipcUri + }); } catch (NullReferenceException) { // Here if appDomain was set to null by Dispose() before this thread started diff --git a/de4dot.code/AssemblyClient/NewProcessAssemblyServerLoader.cs b/de4dot.code/AssemblyClient/NewProcessAssemblyServerLoader.cs index 379ed0ce..9aa00537 100644 --- a/de4dot.code/AssemblyClient/NewProcessAssemblyServerLoader.cs +++ b/de4dot.code/AssemblyClient/NewProcessAssemblyServerLoader.cs @@ -19,17 +19,19 @@ using System; using System.Diagnostics; +using AssemblyData; namespace de4dot.code.AssemblyClient { // Starts the server in a new process class NewProcessAssemblyServerLoader : IpcAssemblyServerLoader { Process process; - public NewProcessAssemblyServerLoader() { + public NewProcessAssemblyServerLoader(AssemblyServiceType serviceType) + : base(serviceType) { } - public NewProcessAssemblyServerLoader(ServerClrVersion version) - : base(version) { + public NewProcessAssemblyServerLoader(AssemblyServiceType serviceType, ServerClrVersion version) + : base(serviceType, version) { } public override void LoadServer(string filename) { @@ -37,7 +39,8 @@ namespace de4dot.code.AssemblyClient { throw new ApplicationException("Server is already loaded"); var psi = new ProcessStartInfo { - Arguments = string.Format("{0} {1}", Utils.ShellEscape(ipcName), Utils.ShellEscape(ipcUri)), + Arguments = string.Format("{0} {1} {2}", (int)serviceType, + Utils.ShellEscape(ipcName), Utils.ShellEscape(ipcUri)), CreateNoWindow = true, ErrorDialog = false, FileName = filename, diff --git a/de4dot.code/AssemblyClient/SameAppDomainAssemblyServerLoader.cs b/de4dot.code/AssemblyClient/SameAppDomainAssemblyServerLoader.cs index 978a205c..8cd8096b 100644 --- a/de4dot.code/AssemblyClient/SameAppDomainAssemblyServerLoader.cs +++ b/de4dot.code/AssemblyClient/SameAppDomainAssemblyServerLoader.cs @@ -24,11 +24,16 @@ namespace de4dot.code.AssemblyClient { // Starts the server in the current app domain. class SameAppDomainAssemblyServerLoader : IAssemblyServerLoader { IAssemblyService service; + AssemblyServiceType serviceType; + + public SameAppDomainAssemblyServerLoader(AssemblyServiceType serviceType) { + this.serviceType = serviceType; + } public void LoadServer() { if (service != null) throw new ApplicationException("Server already loaded"); - service = new AssemblyService(); + service = AssemblyService.Create(serviceType); } public IAssemblyService CreateService() { diff --git a/de4dot.code/HandleProcessCorruptedStateExceptionsAttribute.cs b/de4dot.code/HandleProcessCorruptedStateExceptionsAttribute.cs new file mode 100644 index 00000000..35fa3230 --- /dev/null +++ b/de4dot.code/HandleProcessCorruptedStateExceptionsAttribute.cs @@ -0,0 +1,24 @@ +/* + 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 . +*/ + +namespace System.Runtime.ExceptionServices { + [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] + class HandleProcessCorruptedStateExceptionsAttribute : Attribute { + } +} diff --git a/de4dot.code/ObfuscatedFile.cs b/de4dot.code/ObfuscatedFile.cs index c4cfa2bf..35cc83e0 100644 --- a/de4dot.code/ObfuscatedFile.cs +++ b/de4dot.code/ObfuscatedFile.cs @@ -26,6 +26,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using dnlib.DotNet.Writer; using dnlib.PE; +using AssemblyData; using de4dot.code.deobfuscators; using de4dot.blocks; using de4dot.blocks.cflow; @@ -375,9 +376,9 @@ namespace de4dot.code { CheckSupportedStringDecrypter(StringFeatures.AllowDynamicDecryption); var newProcFactory = assemblyClientFactory as NewProcessAssemblyClientFactory; if (newProcFactory != null) - assemblyClient = newProcFactory.Create(module); + assemblyClient = newProcFactory.Create(AssemblyServiceType.StringDecrypter, module); else - assemblyClient = assemblyClientFactory.Create(); + assemblyClient = assemblyClientFactory.Create(AssemblyServiceType.StringDecrypter); assemblyClient.Connect(); break; @@ -432,12 +433,12 @@ namespace de4dot.code { return; assemblyClient.WaitConnected(); - assemblyClient.Service.LoadAssembly(options.Filename); + assemblyClient.StringDecrypterService.LoadAssembly(options.Filename); if (options.StringDecrypterType == DecrypterType.Delegate) - assemblyClient.Service.SetStringDecrypterType(AssemblyData.StringDecrypterType.Delegate); + assemblyClient.StringDecrypterService.SetStringDecrypterType(AssemblyData.StringDecrypterType.Delegate); else if (options.StringDecrypterType == DecrypterType.Emulate) - assemblyClient.Service.SetStringDecrypterType(AssemblyData.StringDecrypterType.Emulate); + assemblyClient.StringDecrypterService.SetStringDecrypterType(AssemblyData.StringDecrypterType.Emulate); else throw new ApplicationException(string.Format("Invalid string decrypter type '{0}'", options.StringDecrypterType)); @@ -752,11 +753,22 @@ namespace de4dot.code { } Dictionary simpleDeobfuscatorFlags = new Dictionary(); bool Check(MethodDef method, SimpleDeobFlags flag) { + if (method == null) + return false; SimpleDeobFlags oldFlags; simpleDeobfuscatorFlags.TryGetValue(method, out oldFlags); simpleDeobfuscatorFlags[method] = oldFlags | flag; return (oldFlags & flag) == flag; } + bool Clear(MethodDef method, SimpleDeobFlags flag) { + if (method == null) + return false; + SimpleDeobFlags oldFlags; + if (!simpleDeobfuscatorFlags.TryGetValue(method, out oldFlags)) + return false; + simpleDeobfuscatorFlags[method] = oldFlags & ~flag; + return true; + } void Deobfuscate(MethodDef method, string msg, Action handler) { if (savedMethodBodies != null) @@ -784,12 +796,16 @@ namespace de4dot.code { Logger.Instance.DeIndent(); } + void ISimpleDeobfuscator.MethodModified(MethodDef method) { + Clear(method, SimpleDeobFlags.HasDeobfuscated); + } + void ISimpleDeobfuscator.Deobfuscate(MethodDef method) { ((ISimpleDeobfuscator)this).Deobfuscate(method, false); } void ISimpleDeobfuscator.Deobfuscate(MethodDef method, bool force) { - if (!force && Check(method, SimpleDeobFlags.HasDeobfuscated)) + if (method == null || (!force && Check(method, SimpleDeobFlags.HasDeobfuscated))) return; Deobfuscate(method, "Deobfuscating control flow", (blocks) => { diff --git a/de4dot.code/StringInliner.cs b/de4dot.code/StringInliner.cs index afbd29ee..ac77dc02 100644 --- a/de4dot.code/StringInliner.cs +++ b/de4dot.code/StringInliner.cs @@ -89,7 +89,7 @@ namespace de4dot.code { foreach (var methodToken in methodTokens) { if (methodTokenToId.ContainsKey(methodToken)) continue; - methodTokenToId[methodToken] = assemblyClient.Service.DefineStringDecrypter(methodToken); + methodTokenToId[methodToken] = assemblyClient.StringDecrypterService.DefineStringDecrypter(methodToken); } } @@ -117,7 +117,7 @@ namespace de4dot.code { AssemblyData.SimpleData.Pack(list[i].args); args[i] = list[i].args; } - var decryptedStrings = assemblyClient.Service.DecryptStrings(methodId, args, Method.MDToken.ToInt32()); + var decryptedStrings = assemblyClient.StringDecrypterService.DecryptStrings(methodId, args, Method.MDToken.ToInt32()); if (decryptedStrings.Length != args.Length) throw new ApplicationException("Invalid decrypted strings array length"); AssemblyData.SimpleData.Unpack(decryptedStrings); diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 31a7bd34..0e189fae 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -24,6 +24,7 @@ DEBUG;TRACE prompt 4 + true AnyCPU @@ -34,6 +35,7 @@ prompt 4 true + true @@ -72,16 +74,36 @@ - - - - - - - - + + + + + + + + + + + + + + True + True + CsvmResources.resx + + + + + + + + + + + + @@ -155,9 +177,12 @@ + + + @@ -183,7 +208,14 @@ - + + + + + + + + @@ -235,7 +267,7 @@ - + @@ -257,6 +289,7 @@ + @@ -297,6 +330,7 @@ + @@ -361,9 +395,9 @@ {FBD84077-9D35-41FE-89DF-8D79EFE0B595} AssemblyData - + {045B96F2-AF80-4C4C-8D27-E38635AC705E} - blocks + de4dot.blocks {5C93C5E2-196F-4877-BF65-96FEBFCEFCA1} @@ -374,6 +408,12 @@ dnlib + + + ResXFileCodeGenerator + CsvmResources.Designer.cs + + mkdir "..\$(OutDir)..\LICENSES" diff --git a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs index 6f6f233e..3eb06f57 100644 --- a/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs +++ b/de4dot.code/deobfuscators/Agile_NET/CliSecureRtType.cs @@ -88,19 +88,6 @@ namespace de4dot.code.deobfuscators.Agile_NET { static readonly string[] requiredFields1 = new string[] { "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() { foreach (var cctor in DeobUtils.GetInitCctors(module, 3)) { foreach (var calledMethod in DotNetUtils.GetCalledMethods(module, cctor)) { @@ -108,8 +95,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { if (type.IsPublic) continue; var fieldTypes = new FieldTypes(type); - if (!fieldTypes.Exactly(requiredFields1) && !fieldTypes.Exactly(requiredFields2) && - !fieldTypes.Exactly(requiredFields3) && !fieldTypes.Exactly(requiredFields4)) + if (!fieldTypes.All(requiredFields1)) continue; if (!HasInitializeMethod(type, "_Initialize") && !HasInitializeMethod(type, "_Initialize64")) continue; @@ -126,11 +112,19 @@ namespace de4dot.code.deobfuscators.Agile_NET { return false; } + static string[] requiredFields6 = new string[] { + "System.Byte[]", + }; + static string[] requiredFields7 = new string[] { + "System.Byte[]", + "System.Collections.Hashtable", + }; bool Find3() { foreach (var type in module.Types) { - if (type.Fields.Count != 1) + if (type.Fields.Count < 1 || type.Fields.Count > 2) continue; - if (type.Fields[0].FieldSig.GetFieldType().GetFullName() != "System.Byte[]") + var fieldTypes = new FieldTypes(type); + if (!fieldTypes.Exactly(requiredFields6) && !fieldTypes.Exactly(requiredFields7)) continue; if (type.Methods.Count != 2) continue; diff --git a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs index 4caf59ad..e0546f3f 100644 --- a/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/Agile_NET/Deobfuscator.cs @@ -85,7 +85,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { ResourceDecrypter resourceDecrypter; StackFrameHelper stackFrameHelper; - vm.Csvm csvm; + vm.v1.Csvm csvmV1; + vm.v2.Csvm csvmV2; internal class Options : OptionsBase { public bool DecryptMethods { get; set; } @@ -166,7 +167,7 @@ namespace de4dot.code.deobfuscators.Agile_NET { ToInt32(stringDecrypter.Detected) + ToInt32(proxyCallFixer.Detected) + ToInt32(resourceDecrypter.Detected) + - ToInt32(csvm.Detected); + ToInt32(csvmV1.Detected || csvmV2.Detected); if (sum > 0) val += 100 + 10 * (sum - 1); if (cliSecureAttributes.Count != 0) @@ -185,8 +186,10 @@ namespace de4dot.code.deobfuscators.Agile_NET { resourceDecrypter.Find(); proxyCallFixer = new ProxyCallFixer(module); proxyCallFixer.FindDelegateCreator(); - csvm = new vm.Csvm(DeobfuscatedFile.DeobfuscatorContext, module); - csvm.Find(); + csvmV1 = new vm.v1.Csvm(DeobfuscatedFile.DeobfuscatorContext, module); + csvmV1.Find(); + csvmV2 = new vm.v2.Csvm(DeobfuscatedFile.DeobfuscatorContext, module); + csvmV2.Find(); } void FindCliSecureAttribute() { @@ -227,7 +230,8 @@ namespace de4dot.code.deobfuscators.Agile_NET { newOne.stringDecrypter = new StringDecrypter(module, stringDecrypter); newOne.resourceDecrypter = new ResourceDecrypter(module, resourceDecrypter); newOne.proxyCallFixer = new ProxyCallFixer(module, proxyCallFixer); - newOne.csvm = new vm.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; } @@ -270,9 +274,11 @@ namespace de4dot.code.deobfuscators.Agile_NET { FindPossibleNamesToRemove(cliSecureRtType.LoadMethod); } - if (options.RestoreVmCode) { - if (csvm.Restore()) - AddResourceToBeRemoved(csvm.Resource, "CSVM data resource"); + if (options.RestoreVmCode && (csvmV1.Detected || csvmV2.Detected)) { + if (csvmV1.Detected && csvmV1.Restore()) + AddResourceToBeRemoved(csvmV1.Resource, "CSVM data resource"); + else if (csvmV2.Detected && csvmV2.Restore()) + AddResourceToBeRemoved(csvmV2.Resource, "CSVM data resource"); else { Logger.e("Couldn't restore VM methods. Use --dont-rename or it will not run"); PreserveTokensAndTypes(); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs index 313243c0..4df28e4c 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CilOperandInstructionRestorer.cs @@ -23,9 +23,7 @@ using de4dot.blocks; namespace de4dot.code.deobfuscators.Agile_NET.vm { // Tries to restore the operands of the following CIL instructions: - // ldelema - // ldobj - // stobj + // ldelema, ldelem.*, stelem.*, ldobj, stobj class CilOperandInstructionRestorer { MethodDef method; @@ -42,10 +40,66 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { if (instr.Operand != null) continue; - TypeSig operandType = null; + TypeSig operandType = null, operandTypeTmp; + OpCode newOpCode = null; + SZArraySig arrayType; 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: - var arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig; + arrayType = MethodStack.GetLoadedType(method, instrs, i, 1) as SZArraySig; if (arrayType == null) break; operandType = arrayType.Next; @@ -64,12 +118,14 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { default: continue; } - if (!IsValidType(operandType)) { + if (newOpCode == null && !IsValidType(operandType)) { atLeastOneFailed = true; continue; } instr.Operand = operandType.ToTypeDefOrRef(); + if (newOpCode != null) + instr.OpCode = newOpCode; } return !atLeastOneFailed; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverter.cs b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs similarity index 75% rename from de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverter.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs index b468ef9e..00757dab 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverter.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/CsvmToCilMethodConverterBase.cs @@ -25,22 +25,33 @@ using dnlib.DotNet.Emit; using de4dot.blocks; namespace de4dot.code.deobfuscators.Agile_NET.vm { - class CsvmToCilMethodConverter { - IDeobfuscatorContext deobfuscatorContext; - ModuleDefMD module; - VmOpCodeHandlerDetector opCodeDetector; - CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer(); + abstract class CsvmToCilMethodConverterBase { + readonly IDeobfuscatorContext deobfuscatorContext; + readonly protected ModuleDefMD module; + readonly CilOperandInstructionRestorer operandRestorer = new CilOperandInstructionRestorer(); + readonly Dictionary cilToVmIndex = new Dictionary(); + readonly Dictionary vmIndexToCil = new Dictionary(); - public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) { + public CsvmToCilMethodConverterBase(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module) { this.deobfuscatorContext = deobfuscatorContext; this.module = module; - this.opCodeDetector = opCodeDetector; + } + + 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, newInstructions); + var newExceptions = ReadExceptions(cilMethod, csvmMethod); FixInstructionOperands(newInstructions); FixLocals(newInstructions, cilMethod.Body.Variables); @@ -59,7 +70,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { if (op == null) continue; - UpdateLocalInstruction(instr, locals[op.local], op.local); + UpdateLocalInstruction(instr, locals[op.Local], op.Local); } } @@ -134,7 +145,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { if (op == null) continue; - UpdateArgInstruction(instr, method.Parameters[op.arg], op.arg); + UpdateArgInstruction(instr, method.Parameters[op.Arg], op.Arg); } } @@ -197,28 +208,16 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { } } - List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) { - var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions)); - var instrs = new List(); - uint offset = 0; - while (reader.BaseStream.Position < reader.BaseStream.Length) { - int vmOpCode = reader.ReadUInt16(); - var instr = opCodeDetector.Handlers[vmOpCode].Read(reader); - instr.Offset = offset; - offset += (uint)GetInstructionSize(instr); - instrs.Add(instr); - } - return instrs; - } + protected abstract List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod); - static int GetInstructionSize(Instruction instr) { + 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; + return instr.OpCode.Size + (op.TargetDisplacements.Length + 1) * 4; } List ReadLocals(MethodDef cilMethod, CsvmMethodData csvmMethod) { @@ -289,7 +288,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { } } - List ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod, List cilInstructions) { + List ReadExceptions(MethodDef cilMethod, CsvmMethodData csvmMethod) { var reader = new BinaryReader(new MemoryStream(csvmMethod.Exceptions)); var ehs = new List(); @@ -302,14 +301,14 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { 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()); + 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(cilInstructions, reader.ReadInt32()); + eh.FilterStart = GetInstruction(reader.ReadInt32()); ehs.Add(eh); } @@ -317,22 +316,20 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return ehs; } - static Instruction GetInstruction(IList instrs, int index) { - return instrs[index]; + Instruction GetInstruction(int vmIndex) { + return vmIndexToCil[vmIndex]; } - static Instruction GetInstructionEnd(IList instrs, int index) { - index++; - if (index == instrs.Count) - return null; - return instrs[index]; + Instruction GetInstructionEnd(int vmIndex) { + vmIndex++; + Instruction instr; + vmIndexToCil.TryGetValue(vmIndex, out instr); + return instr; } - static Instruction GetInstruction(IList 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]; + Instruction GetInstruction(Instruction source, int displ) { + int vmIndex = cilToVmIndex[source]; + return vmIndexToCil[vmIndex + displ]; } void FixInstructionOperands(IList instrs) { @@ -344,41 +341,33 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { } object FixOperand(IList 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); + return GetInstruction(instr, ((TargetDisplOperand)vmOperand).Displacement); if (vmOperand is SwitchTargetDisplOperand) { - var targetDispls = ((SwitchTargetDisplOperand)vmOperand).targetDisplacements; + 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]); + targets[i] = GetInstruction(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); + 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, int token, OpCode staticInstr, OpCode instanceInstr) { - var fieldRef = module.ResolveToken(token) as IField; + 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.", token); + Logger.w("Could not resolve field {0:X8}. Assuming it's not static.", fieldRef == null ? 0 : fieldRef.MDToken.Raw); isStatic = false; } else @@ -387,13 +376,6 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { 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; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/VmOperands.cs b/de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs similarity index 54% rename from de4dot.code/deobfuscators/Agile_NET/vm/VmOperands.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs index bfb143a8..03cde6c4 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/VmOperands.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/VmOperand.cs @@ -17,66 +17,50 @@ along with de4dot. If not, see . */ +using dnlib.DotNet; +using dnlib.DotNet.Emit; + namespace de4dot.code.deobfuscators.Agile_NET.vm { interface IVmOperand { } - class TokenOperand : IVmOperand { - public int token; // any type of token - public TokenOperand(int token) { - this.token = token; - } - } - 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) { - this.displacement = displacement; + this.Displacement = displacement; } } 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) { - this.targetDisplacements = targetDisplacements; + this.TargetDisplacements = targetDisplacements; } } class ArgOperand : IVmOperand { - public ushort arg; + public readonly ushort Arg; public ArgOperand(ushort arg) { - this.arg = arg; + this.Arg = arg; } } class LocalOperand : IVmOperand { - public ushort local; + public readonly ushort Local; public LocalOperand(ushort local) { - this.local = local; + this.Local = local; } } - // OpCode must be changed to ldfld / ldsfld - class LoadFieldOperand : IVmOperand { - public int token; - public LoadFieldOperand(int token) { - this.token = token; - } - } + class FieldInstructionOperand : IVmOperand { + public readonly OpCode StaticOpCode; + public readonly OpCode InstanceOpCode; + public readonly IField Field; - // OpCode must be changed to ldflda / ldsflda - class LoadFieldAddressOperand : IVmOperand { - public int token; - public LoadFieldAddressOperand(int token) { - this.token = token; - } - } - - // OpCode must be changed to stfld / stsfld - class StoreFieldOperand : IVmOperand { - public int token; - public StoreFieldOperand(int token) { - this.token = token; + public FieldInstructionOperand(OpCode staticOpCode, OpCode instanceOpCode, IField field) { + this.StaticOpCode = staticOpCode; + this.InstanceOpCode = instanceOpCode; + this.Field = field; } } } diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs similarity index 97% rename from de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs index e3030940..91339051 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/Csvm.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/Csvm.cs @@ -23,7 +23,7 @@ using System.IO; using dnlib.DotNet; using de4dot.blocks; -namespace de4dot.code.deobfuscators.Agile_NET.vm { +namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { class Csvm { IDeobfuscatorContext deobfuscatorContext; ModuleDefMD module; @@ -140,7 +140,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { var vmModulePath = Path.Combine(Path.GetDirectoryName(module.Location), vmFilename); Logger.v("CSVM filename: {0}", vmFilename); - var dataKey = "cs cached VmOpCodeHandlerDetector"; + var dataKey = "cs cached VmOpCodeHandlerDetector v1"; var dict = (Dictionary)deobfuscatorContext.GetData(dataKey); if (dict == null) deobfuscatorContext.SetData(dataKey, dict = new Dictionary(StringComparer.OrdinalIgnoreCase)); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs new file mode 100644 index 00000000..93516c18 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/CsvmToCilMethodConverter.cs @@ -0,0 +1,52 @@ +/* + 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 . +*/ + +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.v1 { + class CsvmToCilMethodConverter : CsvmToCilMethodConverterBase { + VmOpCodeHandlerDetector opCodeDetector; + + public CsvmToCilMethodConverter(IDeobfuscatorContext deobfuscatorContext, ModuleDefMD module, VmOpCodeHandlerDetector opCodeDetector) + : base(deobfuscatorContext, module) { + this.opCodeDetector = opCodeDetector; + } + + protected override List ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) { + var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions)); + var instrs = new List(); + uint offset = 0; + while (reader.BaseStream.Position < reader.BaseStream.Length) { + int vmOpCode = reader.ReadUInt16(); + var instr = opCodeDetector.Handlers[vmOpCode].Read(reader, module); + instr.Offset = offset; + offset += (uint)GetInstructionSize(instr); + SetCilToVmIndex(instr, instrs.Count); + SetVmIndexToCil(instr, instrs.Count); + instrs.Add(instr); + } + return instrs; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/FieldsInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/FieldsInfo.cs similarity index 97% rename from de4dot.code/deobfuscators/Agile_NET/vm/FieldsInfo.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/FieldsInfo.cs index a6945092..875d349d 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/FieldsInfo.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/FieldsInfo.cs @@ -21,7 +21,7 @@ using System; using System.Collections.Generic; using dnlib.DotNet; -namespace de4dot.code.deobfuscators.Agile_NET.vm { +namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { class FieldsInfo { public static readonly object EnumType = new object(); Dictionary fieldTypes = new Dictionary(StringComparer.Ordinal); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs similarity index 78% rename from de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandler.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs index 1a911df6..9c572780 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandler.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandler.cs @@ -24,12 +24,12 @@ using de4dot.blocks; using dnlib.DotNet; using dnlib.DotNet.Emit; -namespace de4dot.code.deobfuscators.Agile_NET.vm { +namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { partial class OpCodeHandler { public string Name { get; set; } public OpCodeHandlerSigInfo OpCodeHandlerSigInfo { get; set; } public Predicate Check { get; set; } - public Func Read { get; set; } + public Func Read { get; set; } public bool Detect(UnknownHandlerInfo info) { var sigInfo = OpCodeHandlerSigInfo; @@ -68,7 +68,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { } static partial class OpCodeHandlers { - static Instruction arithmetic_read(BinaryReader reader) { + static Instruction arithmetic_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.Add.ToInstruction(); case 1: return OpCodes.Add_Ovf.ToInstruction(); @@ -91,25 +91,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Type System.Reflection.Module::ResolveType(System.Int32)"); } - static Instruction newarr_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Newarr, - Operand = new TokenOperand(reader.ReadInt32()), - }; + static Instruction newarr_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Newarr, resolver.ResolveToken(reader.ReadUInt32())); } - static Instruction box_read(BinaryReader reader) { + static Instruction box_read(BinaryReader reader, IInstructionOperandResolver resolver) { var instr = new Instruction(); switch (reader.ReadByte()) { case 0: instr.OpCode = OpCodes.Box; break; case 1: instr.OpCode = OpCodes.Unbox_Any; break; default: throw new ApplicationException("Invalid opcode"); } - instr.Operand = new TokenOperand(reader.ReadInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); return instr; } - static Instruction call_read(BinaryReader reader) { + static Instruction call_read(BinaryReader reader, IInstructionOperandResolver resolver) { var instr = new Instruction(); switch (reader.ReadByte()) { case 0: instr.OpCode = OpCodes.Newobj; break; @@ -117,22 +114,22 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { case 2: instr.OpCode = OpCodes.Callvirt; break; default: throw new ApplicationException("Invalid opcode"); } - instr.Operand = new TokenOperand(reader.ReadInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); return instr; } - static Instruction cast_read(BinaryReader reader) { + static Instruction cast_read(BinaryReader reader, IInstructionOperandResolver resolver) { var instr = new Instruction(); switch (reader.ReadByte()) { case 0: instr.OpCode = OpCodes.Castclass; break; case 1: instr.OpCode = OpCodes.Isinst; break; default: throw new ApplicationException("Invalid opcode"); } - instr.Operand = new TokenOperand(reader.ReadInt32()); + instr.Operand = resolver.ResolveToken(reader.ReadUInt32()); return instr; } - static Instruction compare_read(BinaryReader reader) { + static Instruction compare_read(BinaryReader reader, IInstructionOperandResolver resolver) { int type = reader.ReadByte(); Instruction instr = new Instruction(); switch (type) { @@ -205,7 +202,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { 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 }, }; - static Instruction convert_read(BinaryReader reader) { + static Instruction convert_read(BinaryReader reader, IInstructionOperandResolver resolver) { byte type = reader.ReadByte(); bool second = reader.ReadBoolean(); bool third = reader.ReadBoolean(); @@ -215,7 +212,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { if (type != info.Type || info.Second != second || info.Third != third) continue; - instr = new Instruction { OpCode = info.OpCode }; + instr = new Instruction(info.OpCode); break; } if (instr == null) @@ -224,7 +221,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return instr; } - static Instruction dup_read(BinaryReader reader) { + static Instruction dup_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.Dup.ToInstruction(); case 1: return OpCodes.Pop.ToInstruction(); @@ -262,7 +259,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { new InstructionInfo2 { First = true, Second = true, Value = 28, OpCode = OpCodes.Ldelem_Ref }, 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; bool first = reader.ReadBoolean(); bool second = reader.ReadBoolean(); @@ -274,9 +271,9 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { continue; if (second) - instr = new Instruction { OpCode = info.OpCode }; + instr = new Instruction(info.OpCode); else - instr = new Instruction { OpCode = info.OpCode, Operand = new TokenOperand(value) }; + instr = new Instruction(info.OpCode, resolver.ResolveToken((uint)value)); break; } if (instr == null) @@ -289,29 +286,26 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { 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(); } - static Instruction ldfld_read(BinaryReader reader) { - var instr = new Instruction(); - switch (reader.ReadByte()) { - case 0: instr.Operand = new LoadFieldOperand(reader.ReadInt32()); break; - case 1: instr.Operand = new LoadFieldAddressOperand(reader.ReadInt32()); break; - case 2: instr.Operand = new StoreFieldOperand(reader.ReadInt32()); break; + static Instruction ldfld_read(BinaryReader reader, IInstructionOperandResolver resolver) { + byte b = reader.ReadByte(); + var field = resolver.ResolveToken(reader.ReadUInt32()) as IField; + switch (b) { + case 0: return new Instruction(null, new FieldInstructionOperand(OpCodes.Ldsfld, OpCodes.Ldfld, field)); + 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"); } - return instr; } - static Instruction initobj_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Initobj, - Operand = new TokenOperand(reader.ReadInt32()), - }; + static Instruction initobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Initobj, resolver.ResolveToken(reader.ReadUInt32())); } - static Instruction ldloc_read(BinaryReader reader) { + static Instruction ldloc_read(BinaryReader reader, IInstructionOperandResolver resolver) { bool isLdarg = reader.ReadBoolean(); ushort index = reader.ReadUInt16(); @@ -328,7 +322,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return instr; } - static Instruction ldloca_read(BinaryReader reader) { + static Instruction ldloca_read(BinaryReader reader, IInstructionOperandResolver resolver) { Instruction instr = new Instruction(); if (reader.ReadBoolean()) { instr.OpCode = OpCodes.Ldarga; @@ -342,25 +336,19 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return instr; } - static Instruction ldelema_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Ldelema, - Operand = null, - }; + static Instruction ldelema_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Ldelema, null); } - static Instruction ldlen_read(BinaryReader reader) { + static Instruction ldlen_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Ldlen.ToInstruction(); } - static Instruction ldobj_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Ldobj, - Operand = null, - }; + static Instruction ldobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Ldobj, null); } - static Instruction ldstr_read(BinaryReader reader) { + static Instruction ldstr_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Ldstr.ToInstruction(reader.ReadString()); } @@ -368,11 +356,8 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return DotNetUtils.CallsMethod(info.ExecuteMethod, "System.Reflection.MemberInfo System.Reflection.Module::ResolveMember(System.Int32)"); } - static Instruction ldtoken_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Ldtoken, - Operand = new TokenOperand(reader.ReadInt32()), - }; + static Instruction ldtoken_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Ldtoken, resolver.ResolveToken(reader.ReadUInt32())); } static bool leave_check(UnknownHandlerInfo info) { @@ -381,15 +366,12 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { !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(); - return new Instruction { - OpCode = OpCodes.Leave, - Operand = new TargetDisplOperand(displacement), - }; + return new Instruction(OpCodes.Leave, new TargetDisplOperand(displacement)); } - static Instruction ldc_read(BinaryReader reader) { + static Instruction ldc_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch ((ElementType)reader.ReadByte()) { case ElementType.I4: return Instruction.CreateLdcI4(reader.ReadInt32()); case ElementType.I8: return OpCodes.Ldc_I8.ToInstruction(reader.ReadInt64()); @@ -400,29 +382,24 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { } } - static Instruction ldftn_read(BinaryReader reader) { + static Instruction ldftn_read(BinaryReader reader, IInstructionOperandResolver resolver) { byte code = reader.ReadByte(); - int token = reader.ReadInt32(); + uint token = reader.ReadUInt32(); - Instruction instr; switch (code) { case 0: - instr = new Instruction { OpCode = OpCodes.Ldftn, Operand = new TokenOperand(token) }; - break; + return new Instruction(OpCodes.Ldftn, resolver.ResolveToken(token)); case 1: reader.ReadInt32(); // token of newobj .ctor - instr = new Instruction { OpCode = OpCodes.Ldvirtftn, Operand = new TokenOperand(token) }; - break; + return new Instruction(OpCodes.Ldvirtftn, resolver.ResolveToken(token)); default: throw new ApplicationException("Invalid opcode"); } - - return instr; } - static Instruction logical_read(BinaryReader reader) { + static Instruction logical_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.And.ToInstruction(); case 1: return OpCodes.Or.ToInstruction(); @@ -448,7 +425,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return false; } - static Instruction nop_read(BinaryReader reader) { + static Instruction nop_read(BinaryReader reader, IInstructionOperandResolver resolver) { return OpCodes.Nop.ToInstruction(); } @@ -456,7 +433,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { 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 return OpCodes.Ret.ToInstruction(); } @@ -465,11 +442,11 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { 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(); } - static Instruction stloc_read(BinaryReader reader) { + static Instruction stloc_read(BinaryReader reader, IInstructionOperandResolver resolver) { bool isStarg = reader.ReadBoolean(); ushort index = reader.ReadUInt16(); @@ -487,33 +464,27 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { return instr; } - static Instruction stobj_read(BinaryReader reader) { - return new Instruction { - OpCode = OpCodes.Stobj, - Operand = null, - }; + static Instruction stobj_read(BinaryReader reader, IInstructionOperandResolver resolver) { + return new Instruction(OpCodes.Stobj, null); } - static Instruction switch_read(BinaryReader reader) { + static Instruction switch_read(BinaryReader reader, IInstructionOperandResolver resolver) { int numTargets = reader.ReadInt32(); int[] targetDispls = new int[numTargets]; for (int i = 0; i < targetDispls.Length; i++) targetDispls[i] = reader.ReadInt32(); - return new Instruction { - OpCode = OpCodes.Switch, - Operand = new SwitchTargetDisplOperand(targetDispls), - }; + return new Instruction(OpCodes.Switch, new SwitchTargetDisplOperand(targetDispls)); } static bool throw_check(UnknownHandlerInfo info) { 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(); } - static Instruction neg_read(BinaryReader reader) { + static Instruction neg_read(BinaryReader reader, IInstructionOperandResolver resolver) { switch (reader.ReadByte()) { case 0: return OpCodes.Neg.ToInstruction(); case 1: return OpCodes.Not.ToInstruction(); diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandlers.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs similarity index 99% rename from de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandlers.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs index 6b9dd252..aa8419a3 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/OpCodeHandlers.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/OpCodeHandlers.cs @@ -17,9 +17,9 @@ along with de4dot. If not, see . */ -namespace de4dot.code.deobfuscators.Agile_NET.vm { +namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { static partial class OpCodeHandlers { - public static readonly OpCodeHandler[][] opcodeHandlers = new OpCodeHandler[][] { + public static readonly OpCodeHandler[][] Handlers = new OpCodeHandler[][] { new OpCodeHandler[] { new OpCodeHandler { Name = "arithmetic", diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/UnknownHandlerInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/UnknownHandlerInfo.cs similarity index 98% rename from de4dot.code/deobfuscators/Agile_NET/vm/UnknownHandlerInfo.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/UnknownHandlerInfo.cs index 05557708..b2a08276 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/UnknownHandlerInfo.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/UnknownHandlerInfo.cs @@ -23,7 +23,7 @@ using dnlib.DotNet; using dnlib.DotNet.Emit; using de4dot.blocks; -namespace de4dot.code.deobfuscators.Agile_NET.vm { +namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { class UnknownHandlerInfo { TypeDef type; CsvmInfo csvmInfo; diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs similarity index 98% rename from de4dot.code/deobfuscators/Agile_NET/vm/VmOpCodeHandlerDetector.cs rename to de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs index 5be219cc..b06ca9c8 100644 --- a/de4dot.code/deobfuscators/Agile_NET/vm/VmOpCodeHandlerDetector.cs +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v1/VmOpCodeHandlerDetector.cs @@ -24,7 +24,7 @@ using dnlib.DotNet.Emit; using de4dot.blocks; using de4dot.blocks.cflow; -namespace de4dot.code.deobfuscators.Agile_NET.vm { +namespace de4dot.code.deobfuscators.Agile_NET.vm.v1 { class OpCodeHandlerSigInfo { public object[] RequiredFieldTypes { get; set; } public string[] ExecuteMethodLocals { get; set; } @@ -214,7 +214,7 @@ namespace de4dot.code.deobfuscators.Agile_NET.vm { opCodeHandlers = new List(); var detected = new List(); - foreach (var handlersList in OpCodeHandlers.opcodeHandlers) { + foreach (var handlersList in OpCodeHandlers.Handlers) { opCodeHandlers.Clear(); foreach (var handlerType in handlerTypes) { diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin new file mode 100644 index 00000000..156f0777 Binary files /dev/null and b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM1_v2.bin differ diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin new file mode 100644 index 00000000..81bd5ae6 Binary files /dev/null and b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM2_v2.bin differ diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin new file mode 100644 index 00000000..a86daab3 Binary files /dev/null and b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CSVM3_v2.bin differ diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs new file mode 100644 index 00000000..9ae1debc --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeHandlerDetector.cs @@ -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 . +*/ + +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 handlers; + + public CompositeHandlerDetector(IList handlers) { + this.handlers = new List(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 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 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 VisitedCompositeBlocks; + public bool Done; + + public FindHandlerState(HandlerState compositeState) { + this.CompositeState = compositeState; + this.VisitedCompositeBlocks = new Dictionary(); + this.Done = false; + } + + public FindHandlerState(HandlerState compositeState, Dictionary visitedCompositeBlocks, bool done) { + this.CompositeState = compositeState; + this.VisitedCompositeBlocks = new Dictionary(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 stack = new Stack(); + 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 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; + var bl = b as IList; + 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; + } + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeOpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeOpCodeHandler.cs new file mode 100644 index 00000000..2508d020 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CompositeOpCodeHandler.cs @@ -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 . +*/ + +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 OpCodeHandlerInfos { get; private set; } + + public CompositeOpCodeHandler(TypeDef handlerType, HandlerMethod execMethod) { + this.HandlerType = handlerType; + this.ExecMethod = execMethod; + this.OpCodeHandlerInfos = new List(); + } + + public override string ToString() { + if (OpCodeHandlerInfos.Count == 0) + return ""; + var sb = new StringBuilder(); + foreach (var handler in OpCodeHandlerInfos) { + if (sb.Length != 0) + sb.Append(", "); + sb.Append(handler.Name); + } + return sb.ToString(); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs new file mode 100644 index 00000000..9b6f0c10 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/Csvm.cs @@ -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 . +*/ + +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)deobfuscatorContext.GetData(dataKey); + if (dict == null) + deobfuscatorContext.SetData(dataKey, dict = new Dictionary(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; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs new file mode 100644 index 00000000..815cc53d --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmInfo.cs @@ -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 . +*/ + +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; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs new file mode 100644 index 00000000..6153040b --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.Designer.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // 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() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [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; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CSVM1_v2 { + get { + object obj = ResourceManager.GetObject("CSVM1_v2", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CSVM2_v2 { + get { + object obj = ResourceManager.GetObject("CSVM2_v2", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] CSVM3_v2 { + get { + object obj = ResourceManager.GetObject("CSVM3_v2", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx new file mode 100644 index 00000000..254aef83 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmResources.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + CSVM1_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CSVM2_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + CSVM3_v2.bin;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs new file mode 100644 index 00000000..3cc2f2f6 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/CsvmToCilMethodConverter.cs @@ -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 . +*/ + +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 ReadInstructions(MethodDef cilMethod, CsvmMethodData csvmMethod) { + var reader = new BinaryReader(new MemoryStream(csvmMethod.Instructions)); + var instrs = new List(); + 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() { 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; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs new file mode 100644 index 00000000..d679c54d --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/HandlerTypeCode.cs @@ -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 . +*/ + +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, + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs new file mode 100644 index 00000000..ca206830 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodFinder.cs @@ -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 . +*/ + +using System.Collections.Generic; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class MethodFinder { + readonly IList handlerInfos; + readonly PrimitiveHandlerMethod handlerMethod; + + class SigState { + public readonly MethodSigInfo SigInfo; + + public SigState(PrimitiveHandlerMethod handlerMethod) { + this.SigInfo = handlerMethod.Sig; + } + } + + public MethodFinder(IList 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 sigIndexToHandlerIndex = new Dictionary(); + Dictionary handlerIndexToSigIndex = new Dictionary(); + Stack stack = new Stack(); + 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; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodSigInfoCreator.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodSigInfoCreator.cs new file mode 100644 index 00000000..c311e0a9 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/MethodSigInfoCreator.cs @@ -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 . +*/ + +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 blockInfos; + + public List BlockInfos { + get { return blockInfos; } + } + + public MethodSigInfo() { + this.blockInfos = new List(); + } + + public MethodSigInfo(IEnumerable blockInfos) { + this.blockInfos = new List(blockInfos); + } + } + + class BlockInfo : IEquatable { + readonly List targets; + + public byte[] Hash { get; set; } + public List Targets { + get { return targets; } + } + + public BlockInfo() { + this.targets = new List(); + } + + public BlockInfo(byte[] hash, IEnumerable targets) { + this.Hash = hash; + this.targets = new List(targets); + } + + public override string ToString() { + if (Hash == null) + return ""; + 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 allBlocks; + Dictionary blockToInfo; + Dictionary methodToId = new Dictionary(); + + 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(); + 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; + } + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs new file mode 100644 index 00000000..606d1f68 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandler.cs @@ -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 . +*/ + +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; + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs new file mode 100644 index 00000000..d7d3d48d --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfo.cs @@ -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 . +*/ + +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"); + } + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs new file mode 100644 index 00000000..96721152 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfoReader.cs @@ -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 . +*/ + +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> readHandlers; + + public OpCodeHandlerInfoReader(IInstructionOperandResolver resolver) { + this.resolver = resolver; + this.readHandlers = new Dictionary> { + { 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 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 instructionInfos1 = new List { + 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(); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs new file mode 100644 index 00000000..13165b97 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/OpCodeHandlerInfos.cs @@ -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 . +*/ + +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 handlerInfos) { + WriteV1(writer, handlerInfos); + } + + public static void WriteV1(BinaryWriter writer, IList 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 Read(BinaryReader reader) { + switch ((OpCodeHandlersFileVersion)reader.ReadInt32()) { + case OpCodeHandlersFileVersion.V1: return ReadV1(reader); + default: throw new ApplicationException("Invalid file version"); + } + } + + static List ReadV1(BinaryReader reader) { + int numHandlers = reader.ReadInt32(); + var list = new List(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[] HandlerInfos = new IList[] { + ReadOpCodeHandlerInfos(CsvmResources.CSVM1_v2), + ReadOpCodeHandlerInfos(CsvmResources.CSVM2_v2), + ReadOpCodeHandlerInfos(CsvmResources.CSVM3_v2), + }; + + static IList ReadOpCodeHandlerInfos(byte[] data) { + return OpCodeHandlerInfos.Read(new BinaryReader(new MemoryStream(data))); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs new file mode 100644 index 00000000..bb1fdd85 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCode.cs @@ -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 . +*/ + +using System.Collections.Generic; +using System.Text; + +namespace de4dot.code.deobfuscators.Agile_NET.vm.v2 { + class VmOpCode { + public List OpCodeHandlerInfos { get; private set; } + + public VmOpCode(List opCodeHandlerInfos) { + this.OpCodeHandlerInfos = new List(opCodeHandlerInfos.Count); + this.OpCodeHandlerInfos.AddRange(opCodeHandlerInfos); + } + + public override string ToString() { + if (OpCodeHandlerInfos.Count == 0) + return ""; + var sb = new StringBuilder(); + foreach (var handler in OpCodeHandlerInfos) { + if (sb.Length != 0) + sb.Append(", "); + sb.Append(handler.Name); + } + return sb.ToString(); + } + } +} diff --git a/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs new file mode 100644 index 00000000..ea234c72 --- /dev/null +++ b/de4dot.code/deobfuscators/Agile_NET/vm/v2/VmOpCodeHandlerDetector.cs @@ -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 . +*/ + +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 allInstructions; + IList 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 vmOpCodes; + MyDeobfuscator deobfuscator; + + public IList 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 CreateVmOpCodes(IList composites) { + var list = new List(composites.Count); + foreach (var composite in composites) + list.Add(new VmOpCode(composite.OpCodeHandlerInfos)); + return list; + } + + bool DetectCompositeHandlers(IEnumerable composites, List 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 CreateCompositeOpCodeHandlers(CsvmInfo csvmInfo, List handlers) { + var list = new List(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 GetVmHandlerTypes(TypeDef baseType) { + foreach (var type in module.Types) { + if (type.BaseType == baseType) + yield return type; + } + } + + List FindBasicVmHandlerTypes(CsvmInfo csvmInfo) { + var list = new List(); + 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 FindVmHandlerTypes() { + var requiredFields = new string[] { + null, + "System.Collections.Generic.Dictionary`2", + "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 FindVmHandlerTypes(MethodDef method) { + var list = new List(); + + 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 CreateOtherHandlers(CsvmInfo csvmInfo, IList handlerInfos) { + var list = new List(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; + } + } +} diff --git a/de4dot.code/deobfuscators/ArrayFinder.cs b/de4dot.code/deobfuscators/ArrayFinder.cs index 821405eb..af7ccc1b 100644 --- a/de4dot.code/deobfuscators/ArrayFinder.cs +++ b/de4dot.code/deobfuscators/ArrayFinder.cs @@ -86,7 +86,7 @@ namespace de4dot.code.deobfuscators { var intValue = resultValueArray[i] as Int32Value; if (intValue == null || !intValue.AllBitsValid()) return null; - resultArray[i] = (byte)intValue.value; + resultArray[i] = (byte)intValue.Value; } return resultArray; } @@ -99,7 +99,7 @@ namespace de4dot.code.deobfuscators { var intValue = resultValueArray[i] as Int32Value; if (intValue == null || !intValue.AllBitsValid()) return null; - resultArray[i] = (short)intValue.value; + resultArray[i] = (short)intValue.Value; } return resultArray; } @@ -112,7 +112,7 @@ namespace de4dot.code.deobfuscators { var intValue = resultValueArray[i] as Int32Value; if (intValue == null || !intValue.AllBitsValid()) return null; - resultArray[i] = (int)intValue.value; + resultArray[i] = (int)intValue.Value; } return resultArray; } @@ -168,8 +168,8 @@ namespace de4dot.code.deobfuscators { var index = emulator.Pop() as Int32Value; var array = emulator.Pop(); if (ReferenceEquals(array, theArray) && index != null && index.AllBitsValid()) { - if (0 <= index.value && index.value < resultValueArray.Length) - resultValueArray[index.value] = value; + if (0 <= index.Value && index.Value < resultValueArray.Length) + resultValueArray[index.Value] = value; } } else diff --git a/de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs b/de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs index eb514e26..88623545 100644 --- a/de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs +++ b/de4dot.code/deobfuscators/Babel_NET/BabelMethodCallInliner.cs @@ -64,8 +64,8 @@ namespace de4dot.code.deobfuscators.Babel_NET { return false; var instr = instructions[emulateIndex]; var targets = (Instruction[])instr.Operand; - if (switchIndex.value >= 0 && switchIndex.value < targets.Length) - emulateIndex = instructions.IndexOf(targets[switchIndex.value]); + if (switchIndex.Value >= 0 && switchIndex.Value < targets.Length) + emulateIndex = instructions.IndexOf(targets[switchIndex.Value]); else emulateIndex++; return true; @@ -221,7 +221,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { var retValue2 = (Int32Value)retValue; if (!retValue2.AllBitsValid()) return false; - newValue = retValue2.value; + newValue = retValue2.Value; return true; default: diff --git a/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs b/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs index bda53b08..77b4f663 100644 --- a/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs +++ b/de4dot.code/deobfuscators/Babel_NET/StringDecrypter.cs @@ -126,7 +126,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { emulator.Push(new Int32Value(offset)); foreach (var instr in OffsetCalcInstructions) emulator.Emulate(instr); - return ((Int32Value)emulator.Pop()).value; + return ((Int32Value)emulator.Pop()).Value; } public string Decrypt(object[] args) { @@ -348,7 +348,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { case Code.Newarr: var arrayType = (ITypeDefOrRef)instr.Operand; - int arrayCount = ((Int32Value)emulator.Pop()).value; + int arrayCount = ((Int32Value)emulator.Pop()).Value; if (arrayType.FullName == "System.Char") emulator.Push(new UserValue(new char[arrayCount])); else @@ -362,7 +362,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { break; case Code.Ldelem_U1: - arrayIndex = ((Int32Value)emulator.Pop()).value; + arrayIndex = ((Int32Value)emulator.Pop()).Value; array = (Value)emulator.Pop(); if (array is UserValue) emulator.Push(new Int32Value(((byte[])((UserValue)array).obj)[arrayIndex])); @@ -372,22 +372,22 @@ namespace de4dot.code.deobfuscators.Babel_NET { case Code.Stelem_I1: value = emulator.Pop(); - arrayIndex = ((Int32Value)emulator.Pop()).value; + arrayIndex = ((Int32Value)emulator.Pop()).Value; array = (Value)emulator.Pop(); if (array is UserValue) - ((byte[])((UserValue)array).obj)[arrayIndex] = (byte)((Int32Value)value).value; + ((byte[])((UserValue)array).obj)[arrayIndex] = (byte)((Int32Value)value).Value; break; case Code.Stelem_I2: value = emulator.Pop(); - arrayIndex = ((Int32Value)emulator.Pop()).value; + arrayIndex = ((Int32Value)emulator.Pop()).Value; array = (Value)emulator.Pop(); if (array is UserValue) - ((char[])((UserValue)array).obj)[arrayIndex] = (char)((Int32Value)value).value; + ((char[])((UserValue)array).obj)[arrayIndex] = (char)((Int32Value)value).Value; break; case Code.Ldelem_Ref: - arrayIndex = ((Int32Value)emulator.Pop()).value; + arrayIndex = ((Int32Value)emulator.Pop()).Value; array = (Value)emulator.Pop(); var userValue = array as UserValue; if (userValue != null && userValue.obj is string[]) @@ -451,7 +451,7 @@ namespace de4dot.code.deobfuscators.Babel_NET { object CreateDNLibOperand(OpCode opcode, Value op) { if (op is Int32Value) - return ((Int32Value)op).value; + return ((Int32Value)op).Value; if (op is StringValue) return ((StringValue)op).value; return null; diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs b/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs index 5b107767..cd13f7f5 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/AssemblyResolver.cs @@ -30,6 +30,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { TypeDef resolverType; MethodDef resolverMethod; List assemblyInfos = new List(); + string asmSeparator; public class AssemblyInfo { public string assemblyName; @@ -66,7 +67,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { this.module = module; } - public void Find() { + public void Find(ISimpleDeobfuscator simpleDeobfuscator) { var cctor = DotNetUtils.GetModuleTypeCctor(module); if (cctor == null) return; @@ -76,16 +77,19 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { continue; if (!method.IsStatic || !DotNetUtils.IsMethod(method, "System.Void", "()")) continue; - if (CheckType(method.DeclaringType, method)) + if (CheckType(method.DeclaringType, method, simpleDeobfuscator)) break; } } - bool CheckType(TypeDef type, MethodDef initMethod) { + bool CheckType(TypeDef type, MethodDef initMethod, ISimpleDeobfuscator simpleDeobfuscator) { if (DotNetUtils.FindFieldType(type, "System.Collections.Hashtable", true) == null) return false; + simpleDeobfuscator.Deobfuscate(initMethod); if (!CheckInitMethod(initMethod)) return false; + if ((asmSeparator = FindAssemblySeparator(initMethod)) == null) + return false; List newAssemblyInfos = null; foreach (var s in DotNetUtils.GetCodeStrings(initMethod)) { @@ -134,7 +138,7 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { var sb = new StringBuilder(s.Length); foreach (var c in s) sb.Append((char)~c); - var tmpAssemblyInfos = sb.ToString().Split(new string[] { "##" }, StringSplitOptions.RemoveEmptyEntries); + var tmpAssemblyInfos = sb.ToString().Split(new string[] { asmSeparator }, StringSplitOptions.RemoveEmptyEntries); if (tmpAssemblyInfos.Length == 0 || (tmpAssemblyInfos.Length & 1) == 1) return null; @@ -151,5 +155,24 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { return newAssemblyInfos; } + + string FindAssemblySeparator(MethodDef initMethod) { + if (!initMethod.HasBody) + return null; + + foreach (var instr in initMethod.Body.Instructions) { + if (instr.OpCode.Code != Code.Newarr) + continue; + var op = module.CorLibTypes.GetCorLibTypeSig(instr.Operand as ITypeDefOrRef); + if (op == null) + continue; + if (op.ElementType == ElementType.String) + return "##"; + if (op.ElementType == ElementType.Char) + return "`"; + } + + return null; + } } } diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/CoMethodCallInliner.cs b/de4dot.code/deobfuscators/CryptoObfuscator/CoMethodCallInliner.cs new file mode 100644 index 00000000..d632ff43 --- /dev/null +++ b/de4dot.code/deobfuscators/CryptoObfuscator/CoMethodCallInliner.cs @@ -0,0 +1,53 @@ +/* + 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 . +*/ + +using dnlib.DotNet; +using de4dot.blocks.cflow; + +namespace de4dot.code.deobfuscators.CryptoObfuscator { + class CoMethodCallInliner : MethodCallInliner { + readonly InlinedMethodTypes inlinedMethodTypes; + + public CoMethodCallInliner(InlinedMethodTypes inlinedMethodTypes) + : base(false) { + this.inlinedMethodTypes = inlinedMethodTypes; + } + + protected override bool CanInline(MethodDef method) { + if (method == null) + return false; + + if (method.Attributes != (MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig)) + return false; + if (method.HasGenericParameters) + return false; + if (!InlinedMethodTypes.IsValidMethodType(method.DeclaringType)) + return false; + + return true; + } + + protected override void OnInlinedMethod(MethodDef methodToInline, bool inlinedMethod) { + if (inlinedMethod) + inlinedMethodTypes.Add(methodToInline.DeclaringType); + else + inlinedMethodTypes.DontRemoveType(methodToInline.DeclaringType); + } + } +} diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/ConstantsDecrypter.cs b/de4dot.code/deobfuscators/CryptoObfuscator/ConstantsDecrypter.cs index 295659f1..18ff0ee0 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/ConstantsDecrypter.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/ConstantsDecrypter.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using System.Text; using dnlib.IO; using dnlib.DotNet; +using dnlib.DotNet.Emit; using de4dot.blocks; namespace de4dot.code.deobfuscators.CryptoObfuscator { @@ -32,6 +33,8 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { MethodDef methodI8; MethodDef methodR4; MethodDef methodR8; + MethodDef methodArray; + InitializedDataCreator initializedDataCreator; EmbeddedResource encryptedResource; byte[] constantsData; @@ -63,8 +66,9 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { get { return decrypterType != null; } } - public ConstantsDecrypter(ModuleDefMD module) { + public ConstantsDecrypter(ModuleDefMD module, InitializedDataCreator initializedDataCreator) { this.module = module; + this.initializedDataCreator = initializedDataCreator; } public void Find() { @@ -98,9 +102,11 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { methodI8 = DotNetUtils.GetMethod(type, "System.Int64", "(System.Int32)"); methodR4 = DotNetUtils.GetMethod(type, "System.Single", "(System.Int32)"); methodR8 = DotNetUtils.GetMethod(type, "System.Double", "(System.Int32)"); + methodArray = DotNetUtils.GetMethod(type, "System.Void", "(System.Array,System.Int32)"); return methodI4 != null && methodI8 != null && - methodR4 != null && methodR8 != null; + methodR4 != null && methodR8 != null && + methodArray != null; } public void Initialize(ResourceDecrypter resourceDecrypter) { @@ -127,5 +133,79 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { public double DecryptDouble(int index) { return BitConverter.ToDouble(constantsData, index); } + + struct ArrayInfo { + public CorLibTypeSig arrayType; + public int start, len; + public int arySize, index; + + public ArrayInfo(int start, int len, CorLibTypeSig arrayType, int arySize, int index) { + this.start = start; + this.len = len; + this.arrayType = arrayType; + this.arySize = arySize; + this.index = index; + } + } + + public void Deobfuscate(Blocks blocks) { + var infos = new List(); + foreach (var block in blocks.MethodBlocks.GetAllBlocks()) { + var instrs = block.Instructions; + infos.Clear(); + + for (int i = 0; i < instrs.Count - 5; i++) { + int index = i; + + var ldci4_arySize = instrs[index++]; + if (!ldci4_arySize.IsLdcI4()) + continue; + + var newarr = instrs[index++]; + if (newarr.OpCode.Code != Code.Newarr) + continue; + var arrayType = module.CorLibTypes.GetCorLibTypeSig(newarr.Operand as ITypeDefOrRef); + if (arrayType == null) + continue; + + if (instrs[index++].OpCode.Code != Code.Dup) + continue; + + var ldci4_index = instrs[index++]; + if (!ldci4_index.IsLdcI4()) + continue; + + var call = instrs[index++]; + if (call.OpCode.Code != Code.Call && call.OpCode.Code != Code.Callvirt) + continue; + if (!MethodEqualityComparer.CompareDeclaringTypes.Equals(call.Operand as IMethod, methodArray)) + continue; + + if (arrayType.ElementType.GetPrimitiveSize() == -1) { + Logger.w("Can't decrypt non-primitive type array in method {0:X8}", blocks.Method.MDToken.ToInt32()); + continue; + } + + infos.Add(new ArrayInfo(i, index - i, arrayType, ldci4_arySize.GetLdcI4Value(), + ldci4_index.GetLdcI4Value())); + } + + infos.Reverse(); + foreach (var info in infos) { + var elemSize = info.arrayType.ElementType.GetPrimitiveSize(); + var decrypted = DecryptArray(info); + initializedDataCreator.AddInitializeArrayCode(block, info.start, info.len, info.arrayType.ToTypeDefOrRef(), decrypted); + Logger.v("Decrypted {0} array: {1} elements", info.arrayType.ToString(), decrypted.Length / elemSize); + } + } + } + + byte[] DecryptArray(ArrayInfo aryInfo) { + var ary = new byte[aryInfo.arySize * aryInfo.arrayType.ElementType.GetPrimitiveSize()]; + int dataIndex = aryInfo.index; + int len = DeobUtils.ReadVariableLengthInt32(constantsData, ref dataIndex); + Buffer.BlockCopy(constantsData, dataIndex, ary, 0, len); + return ary; + } } } diff --git a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs index 1a9b8754..1cb146eb 100644 --- a/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/CryptoObfuscator/Deobfuscator.cs @@ -22,6 +22,7 @@ using System.Collections.Generic; using System.Text.RegularExpressions; using dnlib.DotNet; using de4dot.blocks; +using de4dot.blocks.cflow; namespace de4dot.code.deobfuscators.CryptoObfuscator { public class DeobfuscatorInfo : DeobfuscatorInfoBase { @@ -30,11 +31,15 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { const string DEFAULT_REGEX = @"!^(get_|set_|add_|remove_)?[A-Z]{1,3}(?:`\d+)?$&!^(get_|set_|add_|remove_)?c[0-9a-f]{32}(?:`\d+)?$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX; BoolOption removeTamperProtection; BoolOption decryptConstants; + BoolOption inlineMethods; + BoolOption fixLdnull; public DeobfuscatorInfo() : base(DEFAULT_REGEX) { removeTamperProtection = new BoolOption(null, MakeArgName("tamper"), "Remove tamper protection code", true); decryptConstants = new BoolOption(null, MakeArgName("consts"), "Decrypt constants", true); + inlineMethods = new BoolOption(null, MakeArgName("inline"), "Inline short methods", true); + fixLdnull = new BoolOption(null, MakeArgName("ldnull"), "Restore ldnull instructions", true); } public override string Name { @@ -50,6 +55,8 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { ValidNameRegex = validNameRegex.get(), RemoveTamperProtection = removeTamperProtection.get(), DecryptConstants = decryptConstants.get(), + InlineMethods = inlineMethods.get(), + FixLdnull = fixLdnull.get(), }); } @@ -57,6 +64,8 @@ namespace de4dot.code.deobfuscators.CryptoObfuscator { return new List