mirror of
https://github.com/sp-tarkov/assembly-tool.git
synced 2025-02-12 16:50:44 -05:00
Merge pull request #14 from ArchangelWTF/update-de4dot
Update de4dot to use dnlib 4.4.0 and x64
This commit is contained in:
commit
790543bed8
Binary file not shown.
BIN
Assets/de4dot/AssemblyServer-x64.exe
Normal file
BIN
Assets/de4dot/AssemblyServer-x64.exe
Normal file
Binary file not shown.
9
Assets/de4dot/AssemblyServer-x64.exe.config
Normal file
9
Assets/de4dot/AssemblyServer-x64.exe.config
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<loadFromRemoteSources enabled="true" />
|
||||
</runtime>
|
||||
</configuration>
|
BIN
Assets/de4dot/de4dot-x64.exe
Normal file
BIN
Assets/de4dot/de4dot-x64.exe
Normal file
Binary file not shown.
13
Assets/de4dot/de4dot-x64.exe.config
Normal file
13
Assets/de4dot/de4dot-x64.exe.config
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" />
|
||||
<supportedRuntime version="v2.0.50727" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<probing privatePath="bin" />
|
||||
</assemblyBinding>
|
||||
<loadFromRemoteSources enabled="true" />
|
||||
</runtime>
|
||||
</configuration>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -10,7 +10,7 @@ public static class Deobfuscator
|
||||
{
|
||||
public static void Deobfuscate(string assemblyPath, bool isLauncher = false)
|
||||
{
|
||||
var executablePath = Path.Combine(DataProvider.DataPath, "De4dot", "de4dot.exe");
|
||||
var executablePath = Path.Combine(DataProvider.DataPath, "De4dot", "de4dot-x64.exe");
|
||||
|
||||
string token;
|
||||
|
||||
|
112
de4dot/.editorconfig
Normal file
112
de4dot/.editorconfig
Normal file
@ -0,0 +1,112 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
#end_of_line =
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
tab_width = 4
|
||||
|
||||
[*.json]
|
||||
|
||||
[app.config]
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.{proj,csproj,vbproj,props,targets,resx,vsixmanifest}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[app.manifest]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[*.xml]
|
||||
|
||||
[*.xaml]
|
||||
indent_style = space
|
||||
|
||||
[*.{cs,vb}]
|
||||
insert_final_newline = true
|
||||
|
||||
dotnet_separate_import_directive_groups = false
|
||||
dotnet_sort_system_directives_first = true
|
||||
dotnet_style_coalesce_expression = true:suggestion
|
||||
dotnet_style_collection_initializer = true:suggestion
|
||||
dotnet_style_explicit_tuple_names = true:suggestion
|
||||
dotnet_style_null_propagation = true:suggestion
|
||||
dotnet_style_object_initializer = true:suggestion
|
||||
dotnet_style_predefined_type_for_locals_parameters_members = true:none
|
||||
dotnet_style_predefined_type_for_member_access = true:none
|
||||
dotnet_style_prefer_auto_properties = true:suggestion
|
||||
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = false:suggestion
|
||||
dotnet_style_qualification_for_event = false:suggestion
|
||||
dotnet_style_qualification_for_field = false:suggestion
|
||||
dotnet_style_qualification_for_method = false:suggestion
|
||||
dotnet_style_qualification_for_property = false:suggestion
|
||||
dotnet_style_require_accessibility_modifiers = never:info
|
||||
|
||||
[*.cs]
|
||||
csharp_indent_block_contents = true
|
||||
csharp_indent_braces = false
|
||||
csharp_indent_case_contents = true
|
||||
csharp_indent_case_contents_when_block = false
|
||||
csharp_indent_labels = flush_left
|
||||
csharp_indent_switch_labels = false
|
||||
csharp_new_line_before_catch = true
|
||||
csharp_new_line_before_else = true
|
||||
csharp_new_line_before_finally = true
|
||||
csharp_new_line_before_members_in_anonymous_types = true
|
||||
csharp_new_line_before_members_in_object_initializers = true
|
||||
csharp_new_line_before_open_brace = none
|
||||
csharp_new_line_between_query_expression_clauses = true
|
||||
csharp_prefer_braces = false
|
||||
csharp_prefer_simple_default_expression = true:suggestion
|
||||
#csharp_preferred_modifier_order =
|
||||
csharp_preserve_single_line_blocks = true
|
||||
csharp_preserve_single_line_statements = true
|
||||
csharp_space_after_cast = false
|
||||
csharp_space_after_colon_in_inheritance_clause = true
|
||||
csharp_space_after_comma = true
|
||||
csharp_space_after_dot = false
|
||||
csharp_space_after_keywords_in_control_flow_statements = true
|
||||
csharp_space_after_semicolon_in_for_statement = true
|
||||
csharp_space_around_binary_operators = before_and_after
|
||||
csharp_space_around_declaration_statements = false
|
||||
csharp_space_before_colon_in_inheritance_clause = true
|
||||
csharp_space_before_comma = false
|
||||
csharp_space_before_dot = false
|
||||
csharp_space_before_open_square_brackets = false
|
||||
csharp_space_before_semicolon_in_for_statement = false
|
||||
csharp_space_between_empty_square_brackets = false
|
||||
csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_call_name_and_opening_parenthesis = false
|
||||
csharp_space_between_method_call_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
|
||||
csharp_space_between_method_declaration_name_and_open_parenthesis = false
|
||||
csharp_space_between_method_declaration_parameter_list_parentheses = false
|
||||
csharp_space_between_parentheses =
|
||||
csharp_space_between_square_brackets = false
|
||||
csharp_style_conditional_delegate_call = true:suggestion
|
||||
csharp_style_deconstructed_variable_declaration = false:none
|
||||
csharp_style_expression_bodied_accessors = true:suggestion
|
||||
csharp_style_expression_bodied_constructors = true:suggestion
|
||||
csharp_style_expression_bodied_indexers = true:suggestion
|
||||
csharp_style_expression_bodied_methods = true:suggestion
|
||||
csharp_style_expression_bodied_operators = true:suggestion
|
||||
csharp_style_expression_bodied_properties = true:suggestion
|
||||
csharp_style_inlined_variable_declaration = true:suggestion
|
||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
||||
csharp_style_throw_expression = true:suggestion
|
||||
csharp_style_var_elsewhere = true:suggestion
|
||||
csharp_style_var_for_built_in_types = false:none
|
||||
csharp_style_var_when_type_is_apparent = true:suggestion
|
||||
|
||||
[*.vb]
|
||||
#visual_basic_preferred_modifier_order =
|
10
de4dot/.gitignore
vendored
Normal file
10
de4dot/.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
*~
|
||||
.vs/
|
||||
bin/
|
||||
obj/
|
||||
Release/
|
||||
Debug/
|
||||
publish-netcoreapp*/
|
||||
launchSettings.json
|
||||
*.user
|
||||
.nuget/
|
12
de4dot/AssemblyData/AssemblyData.csproj
Normal file
12
de4dot/AssemblyData/AssemblyData.csproj
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Runtime.Remoting" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\de4dot.blocks\de4dot.blocks.csproj" />
|
||||
<ProjectReference Include="..\de4dot.mdecrypt\de4dot.mdecrypt.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
134
de4dot/AssemblyData/AssemblyResolver.cs
Normal file
134
de4dot/AssemblyData/AssemblyResolver.cs
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Xml;
|
||||
|
||||
namespace AssemblyData {
|
||||
class AssemblyResolver {
|
||||
Dictionary<string, Assembly> assemblies = new Dictionary<string, Assembly>(StringComparer.Ordinal);
|
||||
Dictionary<string, bool> assemblySearchPathsDict = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
List<string> assemblySearchPaths = new List<string>();
|
||||
|
||||
public AssemblyResolver() => AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
|
||||
|
||||
void AddAssemblySearchPath(string path) {
|
||||
if (assemblySearchPathsDict.ContainsKey(path))
|
||||
return;
|
||||
assemblySearchPathsDict[path] = true;
|
||||
assemblySearchPaths.Add(path);
|
||||
}
|
||||
|
||||
Assembly Get(string assemblyFullName) {
|
||||
var asmName = new AssemblyName(assemblyFullName);
|
||||
|
||||
if (assemblies.TryGetValue(asmName.FullName, out var assembly))
|
||||
return assembly;
|
||||
if (assemblies.TryGetValue(asmName.Name, out assembly))
|
||||
return assembly;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static string[] assemblyExtensions = new string[] { ".dll", ".exe" };
|
||||
Assembly AssemblyResolve(object sender, ResolveEventArgs args) {
|
||||
var assembly = Get(args.Name);
|
||||
if (assembly != null)
|
||||
return assembly;
|
||||
|
||||
var asmName = new AssemblyName(args.Name);
|
||||
foreach (var path in assemblySearchPaths) {
|
||||
foreach (var ext in assemblyExtensions) {
|
||||
try {
|
||||
var filename = Path.Combine(path, asmName.Name + ext);
|
||||
if (!new FileInfo(filename).Exists)
|
||||
continue;
|
||||
AddConfigFile(filename + ".config");
|
||||
return AddAssembly(Assembly.LoadFile(filename));
|
||||
}
|
||||
catch (IOException) {
|
||||
}
|
||||
catch (BadImageFormatException) {
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
}
|
||||
catch (NotSupportedException) {
|
||||
}
|
||||
catch (UnauthorizedAccessException) {
|
||||
}
|
||||
catch (System.Security.SecurityException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Assembly Load(string filename) {
|
||||
AddConfigFile(filename + ".config");
|
||||
return AddAssembly(LoadFile(filename));
|
||||
}
|
||||
|
||||
Assembly LoadFile(string filename) {
|
||||
try {
|
||||
return Assembly.LoadFrom(filename);
|
||||
}
|
||||
catch (FileLoadException) {
|
||||
// Here if eg. strong name signature validation failed and possibly other errors
|
||||
return Assembly.Load(File.ReadAllBytes(filename));
|
||||
}
|
||||
}
|
||||
|
||||
Assembly AddAssembly(Assembly assembly) {
|
||||
var asmName = assembly.GetName();
|
||||
assemblies[asmName.FullName] = assembly;
|
||||
assemblies[asmName.Name] = assembly;
|
||||
return assembly;
|
||||
}
|
||||
|
||||
void AddConfigFile(string configFilename) {
|
||||
var dirName = Utils.GetDirName(Utils.GetFullPath(configFilename));
|
||||
AddAssemblySearchPath(dirName);
|
||||
|
||||
try {
|
||||
using (var xmlStream = new FileStream(configFilename, FileMode.Open, FileAccess.Read, FileShare.Read)) {
|
||||
var doc = new XmlDocument();
|
||||
doc.Load(XmlReader.Create(xmlStream));
|
||||
foreach (var tmp in doc.GetElementsByTagName("probing")) {
|
||||
var probingElem = tmp as XmlElement;
|
||||
if (probingElem == null)
|
||||
continue;
|
||||
var privatePath = probingElem.GetAttribute("privatePath");
|
||||
if (string.IsNullOrEmpty(privatePath))
|
||||
continue;
|
||||
foreach (var path in privatePath.Split(';'))
|
||||
AddAssemblySearchPath(Path.Combine(dirName, path));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException) {
|
||||
}
|
||||
catch (XmlException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
de4dot/AssemblyData/AssemblyServer.cs
Normal file
63
de4dot/AssemblyData/AssemblyServer.cs
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if NETFRAMEWORK
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Runtime.Remoting;
|
||||
using System.Runtime.Remoting.Channels;
|
||||
using System.Runtime.Remoting.Channels.Ipc;
|
||||
using AssemblyData;
|
||||
|
||||
namespace AssemblyServer {
|
||||
public static class Start {
|
||||
public static int Main(string[] args) {
|
||||
if (args.Length != 3)
|
||||
Environment.Exit(1);
|
||||
var serviceType = (AssemblyServiceType)int.Parse(args[0]);
|
||||
var channelName = args[1];
|
||||
var uri = args[2];
|
||||
|
||||
var service = (AssemblyService)AssemblyService.Create(serviceType);
|
||||
StartServer(service, channelName, uri);
|
||||
service.WaitExit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void StartServer(AssemblyService service, string name, string uri) {
|
||||
var props = new Hashtable();
|
||||
props["portName"] = name;
|
||||
var provider = new BinaryServerFormatterSinkProvider();
|
||||
provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
|
||||
var channel = new IpcServerChannel(props, provider);
|
||||
ChannelServices.RegisterChannel(channel, false);
|
||||
RemotingServices.Marshal(service, uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
namespace AssemblyServer {
|
||||
public static class Start {
|
||||
public static int Main(string[] args) {
|
||||
System.Console.WriteLine("Not supported");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
83
de4dot/AssemblyData/AssemblyService.cs
Normal file
83
de4dot/AssemblyData/AssemblyService.cs
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
|
||||
namespace AssemblyData {
|
||||
public abstract class AssemblyService : MarshalByRefObject, IAssemblyService {
|
||||
ManualResetEvent exitEvent = new ManualResetEvent(false);
|
||||
protected Assembly assembly = null;
|
||||
AssemblyResolver assemblyResolver = new AssemblyResolver();
|
||||
|
||||
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 virtual void Exit() => exitEvent.Set();
|
||||
public void WaitExit() => exitEvent.WaitOne();
|
||||
public override object InitializeLifetimeService() => null;
|
||||
|
||||
protected void CheckAssembly() {
|
||||
if (assembly == null)
|
||||
throw new ApplicationException("LoadAssembly() hasn't been called yet.");
|
||||
}
|
||||
|
||||
protected void LoadAssemblyInternal(string filename) {
|
||||
if (assembly != null)
|
||||
throw new ApplicationException("Only one assembly can be explicitly loaded");
|
||||
try {
|
||||
assembly = assemblyResolver.Load(filename);
|
||||
}
|
||||
catch (BadImageFormatException ex) {
|
||||
throw new ApplicationException($"Could not load assembly {filename}. Maybe it's 32-bit or 64-bit only?", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
de4dot/AssemblyData/DelegateStringDecrypter.cs
Normal file
52
de4dot/AssemblyData/DelegateStringDecrypter.cs
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssemblyData {
|
||||
class DelegateStringDecrypter : IStringDecrypter {
|
||||
delegate string DecryptString(object[] args);
|
||||
List<DecryptString> stringDecryptMethods = new List<DecryptString>();
|
||||
|
||||
public int DefineStringDecrypter(MethodInfo method) {
|
||||
stringDecryptMethods.Add(BuildDynamicMethod(method));
|
||||
return stringDecryptMethods.Count - 1;
|
||||
}
|
||||
|
||||
public object[] DecryptStrings(int stringDecrypterMethod, object[] args, MethodBase caller) {
|
||||
if (stringDecrypterMethod > stringDecryptMethods.Count)
|
||||
throw new ApplicationException("Invalid string decrypter method");
|
||||
|
||||
var rv = new object[args.Length];
|
||||
var stringDecrypter = stringDecryptMethods[stringDecrypterMethod];
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
rv[i] = stringDecrypter((object[])args[i]);
|
||||
return rv;
|
||||
}
|
||||
|
||||
DecryptString BuildDynamicMethod(MethodInfo method) {
|
||||
var dm = new DynamicMethod("", typeof(string), new Type[] { typeof(object[]) }, typeof(DelegateStringDecrypter), true);
|
||||
Utils.AddCallStringDecrypterMethodInstructions(method, dm.GetILGenerator());
|
||||
return (DecryptString)dm.CreateDelegate(typeof(DecryptString));
|
||||
}
|
||||
}
|
||||
}
|
58
de4dot/AssemblyData/EmuStringDecrypter.cs
Normal file
58
de4dot/AssemblyData/EmuStringDecrypter.cs
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using AssemblyData.methodsrewriter;
|
||||
|
||||
namespace AssemblyData {
|
||||
class EmuStringDecrypter : IStringDecrypter {
|
||||
List<DecryptInfo> decryptInfos = new List<DecryptInfo>();
|
||||
MethodsRewriter methodsRewriter = new MethodsRewriter();
|
||||
|
||||
class DecryptInfo {
|
||||
public MethodInfo method;
|
||||
public RewrittenMethod decryptString;
|
||||
|
||||
public DecryptInfo(MethodInfo method) => this.method = method;
|
||||
}
|
||||
|
||||
public int DefineStringDecrypter(MethodInfo method) {
|
||||
decryptInfos.Add(new DecryptInfo(method));
|
||||
return decryptInfos.Count - 1;
|
||||
}
|
||||
|
||||
public object[] DecryptStrings(int stringDecrypterMethod, object[] args, MethodBase caller) {
|
||||
var decryptInfo = decryptInfos[stringDecrypterMethod];
|
||||
if (decryptInfo.decryptString == null)
|
||||
decryptInfo.decryptString = CreateDecryptString(decryptInfo.method);
|
||||
|
||||
methodsRewriter.SetCaller(decryptInfo.decryptString, caller);
|
||||
var result = new object[args.Length];
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
result[i] = decryptInfo.decryptString((object[])args[i]);
|
||||
return result;
|
||||
}
|
||||
|
||||
RewrittenMethod CreateDecryptString(MethodInfo method) {
|
||||
methodsRewriter.CreateMethod(method);
|
||||
return methodsRewriter.CreateDelegate(method);
|
||||
}
|
||||
}
|
||||
}
|
67
de4dot/AssemblyData/GenericService.cs
Normal file
67
de4dot/AssemblyData/GenericService.cs
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.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("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($"Failed to find create-service-method. Type token: Type: {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);
|
||||
}
|
||||
}
|
||||
}
|
31
de4dot/AssemblyData/IAssemblyService.cs
Normal file
31
de4dot/AssemblyData/IAssemblyService.cs
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace AssemblyData {
|
||||
public enum AssemblyServiceType {
|
||||
StringDecrypter,
|
||||
MethodDecrypter,
|
||||
Generic,
|
||||
}
|
||||
|
||||
public interface IAssemblyService {
|
||||
void DoNothing();
|
||||
void Exit();
|
||||
}
|
||||
}
|
31
de4dot/AssemblyData/IGenericService.cs
Normal file
31
de4dot/AssemblyData/IGenericService.cs
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
30
de4dot/AssemblyData/IMethodDecrypterService.cs
Normal file
30
de4dot/AssemblyData/IMethodDecrypterService.cs
Normal file
@ -0,0 +1,30 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using de4dot.blocks;
|
||||
using de4dot.mdecrypt;
|
||||
|
||||
namespace AssemblyData {
|
||||
public interface IMethodDecrypterService : IAssemblyService {
|
||||
void InstallCompileMethod(DecryptMethodsInfo decryptMethodsInfo);
|
||||
void LoadObfuscator(string filename);
|
||||
bool CanDecryptMethods();
|
||||
DumpedMethods DecryptMethods();
|
||||
}
|
||||
}
|
27
de4dot/AssemblyData/IStringDecrypter.cs
Normal file
27
de4dot/AssemblyData/IStringDecrypter.cs
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
namespace AssemblyData {
|
||||
interface IStringDecrypter {
|
||||
int DefineStringDecrypter(MethodInfo method);
|
||||
object[] DecryptStrings(int stringDecrypterMethod, object[] args, MethodBase caller);
|
||||
}
|
||||
}
|
32
de4dot/AssemblyData/IStringDecrypterService.cs
Normal file
32
de4dot/AssemblyData/IStringDecrypterService.cs
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace 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);
|
||||
}
|
||||
}
|
28
de4dot/AssemblyData/IUserGenericService.cs
Normal file
28
de4dot/AssemblyData/IUserGenericService.cs
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace AssemblyData {
|
||||
public interface IUserGenericService : IDisposable {
|
||||
void AssemblyLoaded(Assembly assembly);
|
||||
object HandleMessage(int msg, object[] args);
|
||||
}
|
||||
}
|
52
de4dot/AssemblyData/MethodDecrypterService.cs
Normal file
52
de4dot/AssemblyData/MethodDecrypterService.cs
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using 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();
|
||||
}
|
||||
}
|
||||
}
|
70
de4dot/AssemblyData/SimpleData.cs
Normal file
70
de4dot/AssemblyData/SimpleData.cs
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace AssemblyData {
|
||||
// This class will make sure no data in the string is destroyed by serialization
|
||||
[Serializable]
|
||||
class MyString {
|
||||
short[] data;
|
||||
|
||||
public MyString() {
|
||||
}
|
||||
|
||||
public MyString(string s) {
|
||||
if (s == null)
|
||||
data = null;
|
||||
else {
|
||||
data = new short[s.Length];
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
data[i] = (short)s[i];
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
var sb = new StringBuilder(data.Length);
|
||||
foreach (var c in data)
|
||||
sb.Append((char)c);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SimpleData {
|
||||
public static object[] Pack(object[] args) {
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
if (args[i] is string s)
|
||||
args[i] = new MyString(s);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
public static object[] Unpack(object[] args) {
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
if (args[i] is MyString s)
|
||||
args[i] = s.ToString();
|
||||
}
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
90
de4dot/AssemblyData/StringDecrypterService.cs
Normal file
90
de4dot/AssemblyData/StringDecrypterService.cs
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.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($"Unknown StringDecrypterType {type}");
|
||||
}
|
||||
}
|
||||
|
||||
public int DefineStringDecrypter(int methodToken) {
|
||||
CheckStringDecrypter();
|
||||
var methodInfo = FindMethod(methodToken);
|
||||
if (methodInfo == null)
|
||||
throw new ApplicationException($"Could not find method {methodToken:X8}");
|
||||
if (methodInfo.ReturnType != typeof(string) && methodInfo.ReturnType != typeof(object))
|
||||
throw new ApplicationException($"Method return type must be string or object: {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()) {
|
||||
if (module.ResolveMethod(methodToken) is MethodInfo method)
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
174
de4dot/AssemblyData/Utils.cs
Normal file
174
de4dot/AssemblyData/Utils.cs
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
|
||||
namespace AssemblyData {
|
||||
internal delegate void Action<T1, T2, T3, T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18);
|
||||
internal delegate void Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18);
|
||||
internal delegate TResult Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16, T17 arg17, T18 arg18, T19 arg19);
|
||||
|
||||
static class Utils {
|
||||
static Random random = new Random();
|
||||
|
||||
public static uint GetRandomUint() => (uint)(random.NextDouble() * uint.MaxValue);
|
||||
|
||||
public static Type GetDelegateType(Type returnType, Type[] args) {
|
||||
Type[] types;
|
||||
if (returnType == typeof(void)) {
|
||||
types = args;
|
||||
switch (types.Length) {
|
||||
case 0: return typeof(Action).MakeGenericType(types);
|
||||
case 1: return typeof(Action<>).MakeGenericType(types);
|
||||
case 2: return typeof(Action<,>).MakeGenericType(types);
|
||||
case 3: return typeof(Action<,,>).MakeGenericType(types);
|
||||
case 4: return typeof(Action<,,,>).MakeGenericType(types);
|
||||
case 5: return typeof(Action<,,,,>).MakeGenericType(types);
|
||||
case 6: return typeof(Action<,,,,,>).MakeGenericType(types);
|
||||
case 7: return typeof(Action<,,,,,,>).MakeGenericType(types);
|
||||
case 8: return typeof(Action<,,,,,,,>).MakeGenericType(types);
|
||||
case 9: return typeof(Action<,,,,,,,,>).MakeGenericType(types);
|
||||
case 10: return typeof(Action<,,,,,,,,,>).MakeGenericType(types);
|
||||
case 11: return typeof(Action<,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 12: return typeof(Action<,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 13: return typeof(Action<,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 14: return typeof(Action<,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 15: return typeof(Action<,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 16: return typeof(Action<,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 17: return typeof(Action<,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 18: return typeof(Action<,,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 19: return typeof(Action<,,,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
default:
|
||||
throw new ApplicationException($"Too many delegate type arguments: {types.Length}");
|
||||
}
|
||||
}
|
||||
else {
|
||||
types = new Type[args.Length + 1];
|
||||
Array.Copy(args, types, args.Length);
|
||||
types[types.Length - 1] = returnType;
|
||||
|
||||
switch (types.Length) {
|
||||
case 1: return typeof(Func<>).MakeGenericType(types);
|
||||
case 2: return typeof(Func<,>).MakeGenericType(types);
|
||||
case 3: return typeof(Func<,,>).MakeGenericType(types);
|
||||
case 4: return typeof(Func<,,,>).MakeGenericType(types);
|
||||
case 5: return typeof(Func<,,,,>).MakeGenericType(types);
|
||||
case 6: return typeof(Func<,,,,,>).MakeGenericType(types);
|
||||
case 7: return typeof(Func<,,,,,,>).MakeGenericType(types);
|
||||
case 8: return typeof(Func<,,,,,,,>).MakeGenericType(types);
|
||||
case 9: return typeof(Func<,,,,,,,,>).MakeGenericType(types);
|
||||
case 10: return typeof(Func<,,,,,,,,,>).MakeGenericType(types);
|
||||
case 11: return typeof(Func<,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 12: return typeof(Func<,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 13: return typeof(Func<,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 14: return typeof(Func<,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 15: return typeof(Func<,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 16: return typeof(Func<,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 17: return typeof(Func<,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 18: return typeof(Func<,,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 19: return typeof(Func<,,,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
case 20: return typeof(Func<,,,,,,,,,,,,,,,,,,,>).MakeGenericType(types);
|
||||
default:
|
||||
throw new ApplicationException($"Too many delegate type arguments: {types.Length}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string RandomName(int min, int max) {
|
||||
int numChars = random.Next(min, max + 1);
|
||||
var sb = new StringBuilder(numChars);
|
||||
int numLower = 0;
|
||||
for (int i = 0; i < numChars; i++) {
|
||||
if (numLower == 0)
|
||||
sb.Append((char)((int)'A' + random.Next(26)));
|
||||
else
|
||||
sb.Append((char)((int)'a' + random.Next(26)));
|
||||
|
||||
if (numLower == 0) {
|
||||
numLower = random.Next(1, 5);
|
||||
}
|
||||
else {
|
||||
numLower--;
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static void AddCallStringDecrypterMethodInstructions(MethodInfo method, ILGenerator ilg) {
|
||||
var args = method.GetParameters();
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
var arg = args[i].ParameterType;
|
||||
|
||||
ilg.Emit(OpCodes.Ldarg_0);
|
||||
ilg.Emit(OpCodes.Ldc_I4, i);
|
||||
ilg.Emit(OpCodes.Ldelem_Ref);
|
||||
|
||||
if (arg.IsValueType)
|
||||
ilg.Emit(OpCodes.Unbox_Any, arg);
|
||||
else
|
||||
ilg.Emit(OpCodes.Castclass, arg);
|
||||
}
|
||||
ilg.Emit(OpCodes.Call, method);
|
||||
ilg.Emit(OpCodes.Ret);
|
||||
}
|
||||
|
||||
public static string GetFullPath(string path) {
|
||||
try {
|
||||
return Path.GetFullPath(path);
|
||||
}
|
||||
catch (Exception) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetDirName(string name) => Path.GetDirectoryName(name);
|
||||
}
|
||||
}
|
141
de4dot/AssemblyData/methodsrewriter/AssemblyResolver.cs
Normal file
141
de4dot/AssemblyData/methodsrewriter/AssemblyResolver.cs
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class MGenericParameter {
|
||||
}
|
||||
|
||||
class AssemblyResolver {
|
||||
Dictionary<string, List<TypeResolver>> types = new Dictionary<string, List<TypeResolver>>(StringComparer.Ordinal);
|
||||
List<MethodBase> globalMethods;
|
||||
List<FieldInfo> globalFields;
|
||||
Assembly assembly;
|
||||
|
||||
public AssemblyResolver(string asmName) {
|
||||
assembly = Assembly.Load(new AssemblyName(asmName));
|
||||
InitTypes();
|
||||
}
|
||||
|
||||
void InitTypes() {
|
||||
foreach (var type in assembly.GetTypes()) {
|
||||
string key = (type.Namespace ?? "") + "." + type.Name;
|
||||
if (!types.TryGetValue(key, out var list))
|
||||
types[key] = list = new List<TypeResolver>();
|
||||
list.Add(new TypeResolver(type));
|
||||
}
|
||||
}
|
||||
|
||||
TypeResolver GetTypeResolver(ITypeDefOrRef typeRef) {
|
||||
if (typeRef == null)
|
||||
return null;
|
||||
var scopeType = typeRef.ScopeType;
|
||||
var key = scopeType.Namespace + "." + scopeType.TypeName;
|
||||
if (!types.TryGetValue(key, out var list))
|
||||
return null;
|
||||
|
||||
if (scopeType is TypeDef) {
|
||||
foreach (var resolver in list) {
|
||||
if (resolver.type.MetadataToken == scopeType.MDToken.Raw)
|
||||
return resolver;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var resolver in list) {
|
||||
if (ResolverUtils.CompareTypes(resolver.type, scopeType))
|
||||
return resolver;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public FieldInfo Resolve(IField fieldRef) {
|
||||
var resolver = GetTypeResolver(fieldRef.DeclaringType);
|
||||
if (resolver != null)
|
||||
return resolver.Resolve(fieldRef);
|
||||
return ResolveGlobalField(fieldRef);
|
||||
}
|
||||
|
||||
FieldInfo ResolveGlobalField(IField fieldRef) {
|
||||
InitGlobalFields();
|
||||
foreach (var globalField in globalFields) {
|
||||
if (ResolverUtils.CompareFields(globalField, fieldRef))
|
||||
return globalField;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void InitGlobalFields() {
|
||||
if (globalFields != null)
|
||||
return;
|
||||
globalFields = new List<FieldInfo>();
|
||||
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
|
||||
foreach (var module in assembly.GetModules(true)) {
|
||||
foreach (var method in module.GetFields(flags))
|
||||
globalFields.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
public MethodBase Resolve(IMethod methodRef) {
|
||||
var resolver = GetTypeResolver(methodRef.DeclaringType);
|
||||
if (resolver != null)
|
||||
return resolver.Resolve(methodRef);
|
||||
return ResolveGlobalMethod(methodRef);
|
||||
}
|
||||
|
||||
MethodBase ResolveGlobalMethod(IMethod methodRef) {
|
||||
InitGlobalMethods();
|
||||
foreach (var globalMethod in globalMethods) {
|
||||
if (ResolverUtils.CompareMethods(globalMethod, methodRef))
|
||||
return globalMethod;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void InitGlobalMethods() {
|
||||
if (globalMethods != null)
|
||||
return;
|
||||
globalMethods = new List<MethodBase>();
|
||||
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
|
||||
foreach (var module in assembly.GetModules(true)) {
|
||||
foreach (var method in module.GetMethods(flags))
|
||||
globalMethods.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
public Type Resolve(ITypeDefOrRef typeRef) {
|
||||
var resolver = GetTypeResolver(typeRef);
|
||||
if (resolver != null)
|
||||
return resolver.type;
|
||||
|
||||
if (typeRef is TypeSpec ts && ts.TypeSig is GenericSig)
|
||||
return typeof(MGenericParameter);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string ToString() => assembly.ToString();
|
||||
}
|
||||
}
|
328
de4dot/AssemblyData/methodsrewriter/CodeGenerator.cs
Normal file
328
de4dot/AssemblyData/methodsrewriter/CodeGenerator.cs
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
|
||||
using OpCode = dnlib.DotNet.Emit.OpCode;
|
||||
using OpCodes = dnlib.DotNet.Emit.OpCodes;
|
||||
using OperandType = dnlib.DotNet.Emit.OperandType;
|
||||
using ROpCode = System.Reflection.Emit.OpCode;
|
||||
using ROpCodes = System.Reflection.Emit.OpCodes;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class CodeGenerator {
|
||||
static Dictionary<OpCode, ROpCode> dnlibToReflection = new Dictionary<OpCode, ROpCode>();
|
||||
static CodeGenerator() {
|
||||
var refDict = new Dictionary<short, ROpCode>(0x100);
|
||||
foreach (var f in typeof(ROpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)) {
|
||||
if (f.FieldType != typeof(ROpCode))
|
||||
continue;
|
||||
var ropcode = (ROpCode)f.GetValue(null);
|
||||
refDict[ropcode.Value] = ropcode;
|
||||
}
|
||||
|
||||
foreach (var f in typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public)) {
|
||||
if (f.FieldType != typeof(OpCode))
|
||||
continue;
|
||||
var opcode = (OpCode)f.GetValue(null);
|
||||
if (!refDict.TryGetValue(opcode.Value, out var ropcode))
|
||||
continue;
|
||||
dnlibToReflection[opcode] = ropcode;
|
||||
}
|
||||
}
|
||||
|
||||
IMethodsRewriter methodsRewriter;
|
||||
string methodName;
|
||||
IList<Instruction> allInstructions;
|
||||
IList<dnlib.DotNet.Emit.ExceptionHandler> allExceptionHandlers;
|
||||
ILGenerator ilg;
|
||||
Type methodReturnType;
|
||||
Type[] methodParameters;
|
||||
Type delegateType;
|
||||
MMethod methodInfo;
|
||||
LocalBuilder tempObjLocal;
|
||||
LocalBuilder tempObjArrayLocal;
|
||||
int thisArgIndex;
|
||||
List<LocalBuilder> locals;
|
||||
List<Label> labels;
|
||||
Dictionary<Instruction, int> instrToIndex;
|
||||
Stack<dnlib.DotNet.Emit.ExceptionHandler> exceptionHandlersStack;
|
||||
|
||||
public Type DelegateType => delegateType;
|
||||
|
||||
public CodeGenerator(IMethodsRewriter methodsRewriter, string methodName) {
|
||||
this.methodsRewriter = methodsRewriter;
|
||||
this.methodName = methodName;
|
||||
}
|
||||
|
||||
public void SetMethodInfo(MMethod methodInfo) {
|
||||
this.methodInfo = methodInfo;
|
||||
methodReturnType = ResolverUtils.GetReturnType(methodInfo.methodBase);
|
||||
methodParameters = GetMethodParameterTypes(methodInfo.methodBase);
|
||||
delegateType = Utils.GetDelegateType(methodReturnType, methodParameters);
|
||||
}
|
||||
|
||||
public Delegate Generate(IList<Instruction> allInstructions, IList<dnlib.DotNet.Emit.ExceptionHandler> allExceptionHandlers) {
|
||||
this.allInstructions = allInstructions;
|
||||
this.allExceptionHandlers = allExceptionHandlers;
|
||||
|
||||
var dm = new DynamicMethod(methodName, methodReturnType, methodParameters, methodInfo.methodBase.Module, true);
|
||||
var lastInstr = allInstructions[allInstructions.Count - 1];
|
||||
ilg = dm.GetILGenerator((int)lastInstr.Offset + lastInstr.GetSize());
|
||||
|
||||
InitInstrToIndex();
|
||||
InitLocals();
|
||||
InitLabels();
|
||||
|
||||
exceptionHandlersStack = new Stack<dnlib.DotNet.Emit.ExceptionHandler>();
|
||||
for (int i = 0; i < allInstructions.Count; i++) {
|
||||
UpdateExceptionHandlers(i);
|
||||
var instr = allInstructions[i];
|
||||
ilg.MarkLabel(labels[i]);
|
||||
if (instr.Operand is Operand)
|
||||
WriteSpecialInstr(instr, (Operand)instr.Operand);
|
||||
else
|
||||
WriteInstr(instr);
|
||||
}
|
||||
UpdateExceptionHandlers(-1);
|
||||
|
||||
return dm.CreateDelegate(delegateType);
|
||||
}
|
||||
|
||||
Instruction GetExceptionInstruction(int instructionIndex) => instructionIndex < 0 ? null : allInstructions[instructionIndex];
|
||||
|
||||
void UpdateExceptionHandlers(int instructionIndex) {
|
||||
var instr = GetExceptionInstruction(instructionIndex);
|
||||
UpdateExceptionHandlers(instr);
|
||||
if (AddTryStart(instr))
|
||||
UpdateExceptionHandlers(instr);
|
||||
}
|
||||
|
||||
void UpdateExceptionHandlers(Instruction instr) {
|
||||
while (exceptionHandlersStack.Count > 0) {
|
||||
var ex = exceptionHandlersStack.Peek();
|
||||
if (ex.TryEnd == instr) {
|
||||
}
|
||||
if (ex.FilterStart == instr) {
|
||||
}
|
||||
if (ex.HandlerStart == instr) {
|
||||
if (ex.HandlerType == ExceptionHandlerType.Finally)
|
||||
ilg.BeginFinallyBlock();
|
||||
else
|
||||
ilg.BeginCatchBlock(Resolver.GetRtType(ex.CatchType));
|
||||
}
|
||||
if (ex.HandlerEnd == instr) {
|
||||
exceptionHandlersStack.Pop();
|
||||
if (exceptionHandlersStack.Count == 0 || !IsSameTryBlock(ex, exceptionHandlersStack.Peek()))
|
||||
ilg.EndExceptionBlock();
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool AddTryStart(Instruction instr) {
|
||||
var list = new List<dnlib.DotNet.Emit.ExceptionHandler>();
|
||||
foreach (var ex in allExceptionHandlers) {
|
||||
if (ex.TryStart == instr)
|
||||
list.Add(ex);
|
||||
}
|
||||
list.Reverse();
|
||||
|
||||
foreach (var ex in list) {
|
||||
if (exceptionHandlersStack.Count == 0 || !IsSameTryBlock(ex, exceptionHandlersStack.Peek()))
|
||||
ilg.BeginExceptionBlock();
|
||||
exceptionHandlersStack.Push(ex);
|
||||
}
|
||||
|
||||
return list.Count > 0;
|
||||
}
|
||||
|
||||
static bool IsSameTryBlock(dnlib.DotNet.Emit.ExceptionHandler ex1, dnlib.DotNet.Emit.ExceptionHandler ex2) => ex1.TryStart == ex2.TryStart && ex1.TryEnd == ex2.TryEnd;
|
||||
|
||||
void InitInstrToIndex() {
|
||||
instrToIndex = new Dictionary<Instruction, int>(allInstructions.Count);
|
||||
for (int i = 0; i < allInstructions.Count; i++)
|
||||
instrToIndex[allInstructions[i]] = i;
|
||||
}
|
||||
|
||||
void InitLocals() {
|
||||
locals = new List<LocalBuilder>();
|
||||
foreach (var local in methodInfo.methodDef.Body.Variables)
|
||||
locals.Add(ilg.DeclareLocal(Resolver.GetRtType(local.Type), local.Type.RemoveModifiers().IsPinned));
|
||||
tempObjLocal = ilg.DeclareLocal(typeof(object));
|
||||
tempObjArrayLocal = ilg.DeclareLocal(typeof(object[]));
|
||||
}
|
||||
|
||||
void InitLabels() {
|
||||
labels = new List<Label>(allInstructions.Count);
|
||||
for (int i = 0; i < allInstructions.Count; i++)
|
||||
labels.Add(ilg.DefineLabel());
|
||||
}
|
||||
|
||||
Type[] GetMethodParameterTypes(MethodBase method) {
|
||||
var list = new List<Type>();
|
||||
if (ResolverUtils.HasThis(method))
|
||||
list.Add(method.DeclaringType);
|
||||
|
||||
foreach (var param in method.GetParameters())
|
||||
list.Add(param.ParameterType);
|
||||
|
||||
thisArgIndex = list.Count;
|
||||
list.Add(methodsRewriter.GetType());
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
void WriteSpecialInstr(Instruction instr, Operand operand) {
|
||||
BindingFlags flags;
|
||||
switch (operand.type) {
|
||||
case Operand.Type.ThisArg:
|
||||
ilg.Emit(ConvertOpCode(instr.OpCode), (short)thisArgIndex);
|
||||
break;
|
||||
|
||||
case Operand.Type.TempObj:
|
||||
ilg.Emit(ConvertOpCode(instr.OpCode), tempObjLocal);
|
||||
break;
|
||||
|
||||
case Operand.Type.TempObjArray:
|
||||
ilg.Emit(ConvertOpCode(instr.OpCode), tempObjArrayLocal);
|
||||
break;
|
||||
|
||||
case Operand.Type.OurMethod:
|
||||
flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public;
|
||||
var methodName = (string)operand.data;
|
||||
var ourMethod = methodsRewriter.GetType().GetMethod(methodName, flags);
|
||||
if (ourMethod == null)
|
||||
throw new ApplicationException($"Could not find method {methodName}");
|
||||
ilg.Emit(ConvertOpCode(instr.OpCode), ourMethod);
|
||||
break;
|
||||
|
||||
case Operand.Type.NewMethod:
|
||||
var methodBase = (MethodBase)operand.data;
|
||||
var delegateType = methodsRewriter.GetDelegateType(methodBase);
|
||||
flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
|
||||
var invokeMethod = delegateType.GetMethod("Invoke", flags);
|
||||
ilg.Emit(ConvertOpCode(instr.OpCode), invokeMethod);
|
||||
break;
|
||||
|
||||
case Operand.Type.ReflectionType:
|
||||
ilg.Emit(ConvertOpCode(instr.OpCode), (Type)operand.data);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException($"Unknown operand type: {operand.type}");
|
||||
}
|
||||
}
|
||||
|
||||
Label GetLabel(Instruction target) => labels[instrToIndex[target]];
|
||||
|
||||
Label[] GetLabels(Instruction[] targets) {
|
||||
var labels = new Label[targets.Length];
|
||||
for (int i = 0; i < labels.Length; i++)
|
||||
labels[i] = GetLabel(targets[i]);
|
||||
return labels;
|
||||
}
|
||||
|
||||
void WriteInstr(Instruction instr) {
|
||||
var opcode = ConvertOpCode(instr.OpCode);
|
||||
switch (instr.OpCode.OperandType) {
|
||||
case OperandType.InlineNone:
|
||||
ilg.Emit(opcode);
|
||||
break;
|
||||
|
||||
case OperandType.InlineBrTarget:
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
ilg.Emit(opcode, GetLabel((Instruction)instr.Operand));
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
ilg.Emit(opcode, GetLabels((Instruction[])instr.Operand));
|
||||
break;
|
||||
|
||||
case OperandType.ShortInlineI:
|
||||
if (instr.OpCode.Code == Code.Ldc_I4_S)
|
||||
ilg.Emit(opcode, (sbyte)instr.Operand);
|
||||
else
|
||||
ilg.Emit(opcode, (byte)instr.Operand);
|
||||
break;
|
||||
|
||||
case OperandType.InlineI:
|
||||
ilg.Emit(opcode, (int)instr.Operand);
|
||||
break;
|
||||
|
||||
case OperandType.InlineI8:
|
||||
ilg.Emit(opcode, (long)instr.Operand);
|
||||
break;
|
||||
|
||||
case OperandType.InlineR:
|
||||
ilg.Emit(opcode, (double)instr.Operand);
|
||||
break;
|
||||
|
||||
case OperandType.ShortInlineR:
|
||||
ilg.Emit(opcode, (float)instr.Operand);
|
||||
break;
|
||||
|
||||
case OperandType.InlineString:
|
||||
ilg.Emit(opcode, (string)instr.Operand);
|
||||
break;
|
||||
|
||||
case OperandType.InlineTok:
|
||||
case OperandType.InlineType:
|
||||
case OperandType.InlineMethod:
|
||||
case OperandType.InlineField:
|
||||
var obj = Resolver.GetRtObject((ITokenOperand)instr.Operand);
|
||||
if (obj is ConstructorInfo)
|
||||
ilg.Emit(opcode, (ConstructorInfo)obj);
|
||||
else if (obj is MethodInfo)
|
||||
ilg.Emit(opcode, (MethodInfo)obj);
|
||||
else if (obj is FieldInfo)
|
||||
ilg.Emit(opcode, (FieldInfo)obj);
|
||||
else if (obj is Type)
|
||||
ilg.Emit(opcode, (Type)obj);
|
||||
else
|
||||
throw new ApplicationException($"Unknown type: {(obj == null ? obj : obj.GetType())}");
|
||||
break;
|
||||
|
||||
case OperandType.InlineVar:
|
||||
ilg.Emit(opcode, checked((short)((IVariable)instr.Operand).Index));
|
||||
break;
|
||||
|
||||
case OperandType.ShortInlineVar:
|
||||
ilg.Emit(opcode, checked((byte)((IVariable)instr.Operand).Index));
|
||||
break;
|
||||
|
||||
case OperandType.InlineSig: //TODO:
|
||||
default:
|
||||
throw new ApplicationException($"Unknown OperandType {instr.OpCode.OperandType}");
|
||||
}
|
||||
}
|
||||
|
||||
ROpCode ConvertOpCode(OpCode opcode) {
|
||||
if (dnlibToReflection.TryGetValue(opcode, out var ropcode))
|
||||
return ropcode;
|
||||
return ROpCodes.Nop;
|
||||
}
|
||||
}
|
||||
}
|
27
de4dot/AssemblyData/methodsrewriter/IMethodsRewriter.cs
Normal file
27
de4dot/AssemblyData/methodsrewriter/IMethodsRewriter.cs
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
interface IMethodsRewriter {
|
||||
Type GetDelegateType(MethodBase methodBase);
|
||||
}
|
||||
}
|
35
de4dot/AssemblyData/methodsrewriter/MField.cs
Normal file
35
de4dot/AssemblyData/methodsrewriter/MField.cs
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class MField {
|
||||
public FieldInfo fieldInfo;
|
||||
public FieldDef fieldDef;
|
||||
|
||||
public MField(FieldInfo fieldInfo, FieldDef fieldDef) {
|
||||
this.fieldInfo = fieldInfo;
|
||||
this.fieldDef = fieldDef;
|
||||
}
|
||||
|
||||
public override string ToString() => fieldDef.ToString();
|
||||
}
|
||||
}
|
35
de4dot/AssemblyData/methodsrewriter/MMethod.cs
Normal file
35
de4dot/AssemblyData/methodsrewriter/MMethod.cs
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class MMethod {
|
||||
public MethodBase methodBase;
|
||||
public MethodDef methodDef;
|
||||
public MMethod(MethodBase methodBase, MethodDef methodDef) {
|
||||
this.methodBase = methodBase;
|
||||
this.methodDef = methodDef;
|
||||
}
|
||||
|
||||
public bool HasInstructions() => methodDef.Body != null && methodDef.Body.Instructions.Count != 0;
|
||||
public override string ToString() => methodDef.ToString();
|
||||
}
|
||||
}
|
143
de4dot/AssemblyData/methodsrewriter/MModule.cs
Normal file
143
de4dot/AssemblyData/methodsrewriter/MModule.cs
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class MModule {
|
||||
public Module module;
|
||||
public ModuleDefMD moduleDef;
|
||||
TypeDefDict<MType> typeRefToType = new TypeDefDict<MType>();
|
||||
Dictionary<int, MType> tokenToType = new Dictionary<int, MType>();
|
||||
Dictionary<int, MMethod> tokenToGlobalMethod;
|
||||
Dictionary<int, MField> tokenToGlobalField;
|
||||
TypeDef moduleType;
|
||||
|
||||
public MModule(Module module, ModuleDefMD moduleDef) {
|
||||
this.module = module;
|
||||
this.moduleDef = moduleDef;
|
||||
InitTokenToType();
|
||||
}
|
||||
|
||||
void InitTokenToType() {
|
||||
moduleType = moduleDef.Types[0];
|
||||
foreach (var typeDef in moduleDef.GetTypes()) {
|
||||
int token = (int)typeDef.MDToken.Raw;
|
||||
Type type;
|
||||
try {
|
||||
type = module.ResolveType(token);
|
||||
}
|
||||
catch {
|
||||
tokenToType[token] = null;
|
||||
typeRefToType.Add(typeDef, null);
|
||||
continue;
|
||||
}
|
||||
var mtype = new MType(type, typeDef);
|
||||
tokenToType[token] = mtype;
|
||||
typeRefToType.Add(typeDef, mtype);
|
||||
}
|
||||
}
|
||||
|
||||
public MType GetType(IType typeRef) => typeRefToType.Find(typeRef);
|
||||
|
||||
public MMethod GetMethod(IMethod methodRef) {
|
||||
var type = GetType(methodRef.DeclaringType);
|
||||
if (type != null)
|
||||
return type.GetMethod(methodRef);
|
||||
if (!new SigComparer().Equals(moduleType, methodRef.DeclaringType))
|
||||
return null;
|
||||
|
||||
InitGlobalMethods();
|
||||
foreach (var method in tokenToGlobalMethod.Values) {
|
||||
if (new SigComparer().Equals(methodRef, method.methodDef))
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MField GetField(IField fieldRef) {
|
||||
var type = GetType(fieldRef.DeclaringType);
|
||||
if (type != null)
|
||||
return type.GetField(fieldRef);
|
||||
if (!new SigComparer().Equals(moduleType, fieldRef.DeclaringType))
|
||||
return null;
|
||||
|
||||
InitGlobalFields();
|
||||
foreach (var field in tokenToGlobalField.Values) {
|
||||
if (new SigComparer().Equals(fieldRef, field.fieldDef))
|
||||
return field;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MMethod GetMethod(MethodBase method) {
|
||||
if (method.Module != module)
|
||||
throw new ApplicationException("Not our module");
|
||||
if (method.DeclaringType == null)
|
||||
return GetGlobalMethod(method);
|
||||
var type = tokenToType[method.DeclaringType.MetadataToken];
|
||||
return type.GetMethod(method.MetadataToken);
|
||||
}
|
||||
|
||||
public MMethod GetGlobalMethod(MethodBase method) {
|
||||
InitGlobalMethods();
|
||||
return tokenToGlobalMethod[method.MetadataToken];
|
||||
}
|
||||
|
||||
void InitGlobalMethods() {
|
||||
if (tokenToGlobalMethod != null)
|
||||
return;
|
||||
tokenToGlobalMethod = new Dictionary<int, MMethod>();
|
||||
|
||||
var tmpTokenToGlobalMethod = new Dictionary<int, MethodInfo>();
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
foreach (var m in module.GetMethods(flags))
|
||||
tmpTokenToGlobalMethod[m.MetadataToken] = m;
|
||||
foreach (var m in moduleType.Methods) {
|
||||
if (m.Name == ".cctor") //TODO: Use module.GetMethod(token) to get .cctor method
|
||||
continue;
|
||||
var token = (int)m.MDToken.Raw;
|
||||
tokenToGlobalMethod[token] = new MMethod(tmpTokenToGlobalMethod[token], m);
|
||||
}
|
||||
}
|
||||
|
||||
void InitGlobalFields() {
|
||||
if (tokenToGlobalField != null)
|
||||
return;
|
||||
tokenToGlobalField = new Dictionary<int, MField>();
|
||||
|
||||
var tmpTokenToGlobalField = new Dictionary<int, FieldInfo>();
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
foreach (var f in module.GetFields(flags))
|
||||
tmpTokenToGlobalField[f.MetadataToken] = f;
|
||||
foreach (var f in moduleType.Fields) {
|
||||
var token = (int)f.MDToken.Raw;
|
||||
tokenToGlobalField[token] = new MField(tmpTokenToGlobalField[token], f);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => moduleDef.Location;
|
||||
}
|
||||
}
|
98
de4dot/AssemblyData/methodsrewriter/MType.cs
Normal file
98
de4dot/AssemblyData/methodsrewriter/MType.cs
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class MType {
|
||||
public Type type;
|
||||
public TypeDef typeDef;
|
||||
Dictionary<int, MMethod> tokenToMethod;
|
||||
MethodDefDict<MMethod> methodRefToMethod;
|
||||
Dictionary<int, MField> tokenToField;
|
||||
FieldDefDict<MField> fieldRefToField;
|
||||
|
||||
public MType(Type type, TypeDef typeDef) {
|
||||
this.type = type;
|
||||
this.typeDef = typeDef;
|
||||
}
|
||||
|
||||
public MMethod GetMethod(IMethod methodRef) {
|
||||
InitMethods();
|
||||
return methodRefToMethod.Find(methodRef);
|
||||
}
|
||||
|
||||
public MField GetField(IField fieldRef) {
|
||||
InitFields();
|
||||
return fieldRefToField.Find(fieldRef);
|
||||
}
|
||||
|
||||
public MMethod GetMethod(int token) {
|
||||
InitMethods();
|
||||
return tokenToMethod[token];
|
||||
}
|
||||
|
||||
public MField GetField(int token) {
|
||||
InitFields();
|
||||
return tokenToField[token];
|
||||
}
|
||||
|
||||
void InitMethods() {
|
||||
if (tokenToMethod != null)
|
||||
return;
|
||||
tokenToMethod = new Dictionary<int, MMethod>(typeDef.Methods.Count);
|
||||
methodRefToMethod = new MethodDefDict<MMethod>();
|
||||
|
||||
var tmpTokenToMethod = new Dictionary<int, MethodBase>();
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
foreach (var m in ResolverUtils.GetMethodBases(type, flags))
|
||||
tmpTokenToMethod[m.MetadataToken] = m;
|
||||
foreach (var m in typeDef.Methods) {
|
||||
var token = (int)m.MDToken.Raw;
|
||||
var method = new MMethod(tmpTokenToMethod[token], m);
|
||||
tokenToMethod[token] = method;
|
||||
methodRefToMethod.Add(method.methodDef, method);
|
||||
}
|
||||
}
|
||||
|
||||
void InitFields() {
|
||||
if (tokenToField != null)
|
||||
return;
|
||||
tokenToField = new Dictionary<int, MField>(typeDef.Fields.Count);
|
||||
fieldRefToField = new FieldDefDict<MField>();
|
||||
|
||||
var tmpTokenToField = new Dictionary<int, FieldInfo>();
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
foreach (var f in type.GetFields(flags))
|
||||
tmpTokenToField[f.MetadataToken] = f;
|
||||
foreach (var f in typeDef.Fields) {
|
||||
var token = (int)f.MDToken.Raw;
|
||||
var field = new MField(tmpTokenToField[token], f);
|
||||
tokenToField[token] = field;
|
||||
fieldRefToField.Add(field.fieldDef, field);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => $"{typeDef.MDToken.Raw:X8} - {typeDef.FullName}";
|
||||
}
|
||||
}
|
402
de4dot/AssemblyData/methodsrewriter/MethodsRewriter.cs
Normal file
402
de4dot/AssemblyData/methodsrewriter/MethodsRewriter.cs
Normal file
@ -0,0 +1,402 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using dnlib.DotNet.Emit;
|
||||
using dnlib.DotNet;
|
||||
using de4dot.blocks;
|
||||
|
||||
using OpCode = dnlib.DotNet.Emit.OpCode;
|
||||
using OpCodes = dnlib.DotNet.Emit.OpCodes;
|
||||
using ROpCodes = System.Reflection.Emit.OpCodes;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
delegate object RewrittenMethod(object[] args);
|
||||
|
||||
class MethodsFinder {
|
||||
Dictionary<Module, MethodsModule> moduleToMethods = new Dictionary<Module, MethodsModule>();
|
||||
|
||||
class MethodsModule {
|
||||
const int MAX_METHODS = 30;
|
||||
List<MethodBase> methods = new List<MethodBase>(MAX_METHODS);
|
||||
int next;
|
||||
|
||||
public MethodsModule(Module module) {
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
|
||||
foreach (var type in module.GetTypes()) {
|
||||
if (methods.Count >= MAX_METHODS)
|
||||
break;
|
||||
foreach (var method in type.GetMethods(flags)) {
|
||||
if (methods.Count >= MAX_METHODS)
|
||||
break;
|
||||
methods.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var method in module.GetMethods(flags)) {
|
||||
if (methods.Count >= MAX_METHODS)
|
||||
break;
|
||||
methods.Add(method);
|
||||
}
|
||||
}
|
||||
|
||||
public MethodBase GetNext() => methods[next++ % methods.Count];
|
||||
}
|
||||
|
||||
public MethodBase GetMethod(Module module) {
|
||||
if (!moduleToMethods.TryGetValue(module, out var methodsModule))
|
||||
moduleToMethods[module] = methodsModule = new MethodsModule(module);
|
||||
return methodsModule.GetNext();
|
||||
}
|
||||
}
|
||||
|
||||
class MethodsRewriter : IMethodsRewriter {
|
||||
MethodsFinder methodsFinder = new MethodsFinder();
|
||||
Dictionary<MethodBase, NewMethodInfo> realMethodToNewMethod = new Dictionary<MethodBase, NewMethodInfo>();
|
||||
Dictionary<NewMethodInfo, MethodBase> newStackMethodDict = new Dictionary<NewMethodInfo, MethodBase>();
|
||||
List<NewMethodInfo> newMethodInfos = new List<NewMethodInfo>();
|
||||
|
||||
// There's no documented way to get a dynamic method's MethodInfo. If we name the
|
||||
// method and it's a unique random name, we can still find the emulated method.
|
||||
Dictionary<string, NewMethodInfo> delegateNameToNewMethodInfo = new Dictionary<string, NewMethodInfo>(StringComparer.Ordinal);
|
||||
|
||||
class NewMethodInfo {
|
||||
// Original method
|
||||
public MethodBase oldMethod;
|
||||
|
||||
public Type delegateType;
|
||||
|
||||
// The modified code is here
|
||||
public Delegate delegateInstance;
|
||||
|
||||
// newMethodInfos index
|
||||
public int delegateIndex;
|
||||
|
||||
public RewrittenMethod rewrittenMethod;
|
||||
|
||||
// Name of method used by delegateInstance
|
||||
public string delegateMethodName;
|
||||
|
||||
// Name of method used by rewrittenMethod
|
||||
public string rewrittenMethodName;
|
||||
|
||||
public NewMethodInfo(MethodBase oldMethod, int delegateIndex, string delegateMethodName, string rewrittenMethodName) {
|
||||
this.oldMethod = oldMethod;
|
||||
this.delegateIndex = delegateIndex;
|
||||
this.delegateMethodName = delegateMethodName;
|
||||
this.rewrittenMethodName = rewrittenMethodName;
|
||||
}
|
||||
|
||||
public bool IsRewrittenMethod(string name) => name == rewrittenMethodName;
|
||||
public bool IsDelegateMethod(string name) => name == delegateMethodName;
|
||||
}
|
||||
|
||||
public Type GetDelegateType(MethodBase methodBase) => realMethodToNewMethod[methodBase].delegateType;
|
||||
|
||||
public RewrittenMethod CreateDelegate(MethodBase realMethod) {
|
||||
var newMethodInfo = realMethodToNewMethod[realMethod];
|
||||
if (newMethodInfo.rewrittenMethod != null)
|
||||
return newMethodInfo.rewrittenMethod;
|
||||
|
||||
var dm = new DynamicMethod(newMethodInfo.rewrittenMethodName, typeof(object), new Type[] { GetType(), typeof(object[]) }, newMethodInfo.oldMethod.Module, true);
|
||||
var ilg = dm.GetILGenerator();
|
||||
|
||||
ilg.Emit(ROpCodes.Ldarg_0);
|
||||
ilg.Emit(ROpCodes.Ldc_I4, newMethodInfo.delegateIndex);
|
||||
ilg.Emit(ROpCodes.Call, GetType().GetMethod("RtGetDelegateInstance", BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
ilg.Emit(ROpCodes.Castclass, newMethodInfo.delegateType);
|
||||
|
||||
var args = newMethodInfo.oldMethod.GetParameters();
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
var arg = args[i].ParameterType;
|
||||
|
||||
ilg.Emit(ROpCodes.Ldarg_1);
|
||||
ilg.Emit(ROpCodes.Ldc_I4, i);
|
||||
ilg.Emit(ROpCodes.Ldelem_Ref);
|
||||
|
||||
if (arg.IsValueType)
|
||||
ilg.Emit(ROpCodes.Unbox_Any, arg);
|
||||
else
|
||||
ilg.Emit(ROpCodes.Castclass, arg);
|
||||
}
|
||||
ilg.Emit(ROpCodes.Ldarg_0);
|
||||
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance;
|
||||
var invokeMethod = newMethodInfo.delegateType.GetMethod("Invoke", flags);
|
||||
ilg.Emit(ROpCodes.Call, invokeMethod);
|
||||
if (ResolverUtils.GetReturnType(newMethodInfo.oldMethod) == typeof(void))
|
||||
ilg.Emit(ROpCodes.Ldnull);
|
||||
ilg.Emit(ROpCodes.Ret);
|
||||
|
||||
newMethodInfo.rewrittenMethod = (RewrittenMethod)dm.CreateDelegate(typeof(RewrittenMethod), this);
|
||||
return newMethodInfo.rewrittenMethod;
|
||||
}
|
||||
|
||||
public void SetCaller(RewrittenMethod rewrittenMethod, MethodBase caller) {
|
||||
if (caller == null)
|
||||
return;
|
||||
var newMethodInfo = GetNewMethodInfo(rewrittenMethod.Method.Name);
|
||||
newStackMethodDict[newMethodInfo] = caller;
|
||||
}
|
||||
|
||||
string GetDelegateMethodName(MethodBase method) {
|
||||
string name = null;
|
||||
do {
|
||||
name = $" {method.Name} {method.MetadataToken:X8} DMN {Utils.GetRandomUint():X8} ";
|
||||
} while (delegateNameToNewMethodInfo.ContainsKey(name));
|
||||
return name;
|
||||
}
|
||||
|
||||
public void CreateMethod(MethodBase realMethod) {
|
||||
if (realMethodToNewMethod.ContainsKey(realMethod))
|
||||
return;
|
||||
var newMethodInfo = new NewMethodInfo(realMethod, newMethodInfos.Count, GetDelegateMethodName(realMethod), GetDelegateMethodName(realMethod));
|
||||
newMethodInfos.Add(newMethodInfo);
|
||||
delegateNameToNewMethodInfo[newMethodInfo.delegateMethodName] = newMethodInfo;
|
||||
delegateNameToNewMethodInfo[newMethodInfo.rewrittenMethodName] = newMethodInfo;
|
||||
realMethodToNewMethod[realMethod] = newMethodInfo;
|
||||
|
||||
var moduleInfo = Resolver.LoadAssembly(realMethod.Module);
|
||||
var methodInfo = moduleInfo.GetMethod(realMethod);
|
||||
if (!methodInfo.HasInstructions())
|
||||
throw new ApplicationException($"Method {methodInfo.methodDef} ({methodInfo.methodDef.MDToken.Raw:X8}) has no body");
|
||||
|
||||
var codeGenerator = new CodeGenerator(this, newMethodInfo.delegateMethodName);
|
||||
codeGenerator.SetMethodInfo(methodInfo);
|
||||
newMethodInfo.delegateType = codeGenerator.DelegateType;
|
||||
|
||||
var blocks = new Blocks(methodInfo.methodDef);
|
||||
foreach (var block in blocks.MethodBlocks.GetAllBlocks())
|
||||
Update(block, newMethodInfo);
|
||||
|
||||
blocks.GetCode(out var allInstructions, out var allExceptionHandlers);
|
||||
newMethodInfo.delegateInstance = codeGenerator.Generate(allInstructions, allExceptionHandlers);
|
||||
}
|
||||
|
||||
static Instruction Create(OpCode opcode, object operand) =>
|
||||
new Instruction {
|
||||
OpCode = opcode,
|
||||
Operand = operand,
|
||||
};
|
||||
|
||||
// Inserts ldarg THIS, and returns number of instructions inserted at 'i'
|
||||
int InsertLoadThis(Block block, int i) {
|
||||
block.Insert(i, Create(OpCodes.Ldarg, new Operand(Operand.Type.ThisArg)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int InsertCallOurMethod(Block block, int i, string methodName) {
|
||||
block.Insert(i, Create(OpCodes.Call, new Operand(Operand.Type.OurMethod, methodName)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void Update(Block block, NewMethodInfo currentMethodInfo) {
|
||||
var instrs = block.Instructions;
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var instr = instrs[i];
|
||||
if (instr.OpCode == OpCodes.Newobj) {
|
||||
var ctor = (IMethod)instr.Operand;
|
||||
var ctorTypeFullName = ctor.DeclaringType.FullName;
|
||||
if (ctorTypeFullName == "System.Diagnostics.StackTrace") {
|
||||
InsertLoadThis(block, i + 1);
|
||||
InsertCallOurMethod(block, i + 2, "static_RtFixStackTrace");
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
else if (ctorTypeFullName == "System.Diagnostics.StackFrame") {
|
||||
InsertLoadThis(block, i + 1);
|
||||
InsertCallOurMethod(block, i + 2, "static_RtFixStackFrame");
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (instr.OpCode == OpCodes.Call || instr.OpCode == OpCodes.Callvirt) {
|
||||
var calledMethod = (IMethod)instr.Operand;
|
||||
if (calledMethod.DeclaringType.DefinitionAssembly.IsCorLib()) {
|
||||
var calledMethodFullName = calledMethod.FullName;
|
||||
if (calledMethodFullName == "System.Reflection.Assembly System.Reflection.Assembly::GetAssembly(System.Type)") {
|
||||
block.Replace(i, 1, OpCodes.Nop.ToInstruction());
|
||||
InsertLoadThis(block, i + 1);
|
||||
InsertCallOurMethod(block, i + 2, "static_RtGetAssembly_TypeArg");
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
else if (calledMethodFullName == "System.Reflection.Assembly System.Reflection.Assembly::GetCallingAssembly()" ||
|
||||
calledMethodFullName == "System.Reflection.Assembly System.Reflection.Assembly::GetEntryAssembly()" ||
|
||||
calledMethodFullName == "System.Reflection.Assembly System.Reflection.Assembly::GetExecutingAssembly()") {
|
||||
block.Replace(i, 1, OpCodes.Nop.ToInstruction());
|
||||
InsertLoadThis(block, i + 1);
|
||||
block.Insert(i + 2, OpCodes.Ldc_I4.ToInstruction(currentMethodInfo.delegateIndex));
|
||||
InsertCallOurMethod(block, i + 3, "RtGetAssembly");
|
||||
i += 3;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
var method = Resolver.GetMethod((IMethod)instr.Operand);
|
||||
if (method != null) {
|
||||
CreateMethod(method.methodBase);
|
||||
var newMethodInfo = realMethodToNewMethod[method.methodBase];
|
||||
|
||||
block.Replace(i, 1, OpCodes.Nop.ToInstruction());
|
||||
int n = i + 1;
|
||||
|
||||
// Pop all pushed args to a temp array
|
||||
var mparams = GetParameters(method.methodDef);
|
||||
if (mparams.Count > 0) {
|
||||
block.Insert(n++, OpCodes.Ldc_I4.ToInstruction(mparams.Count));
|
||||
var objectType = method.methodDef.DeclaringType.Module.CorLibTypes.Object;
|
||||
block.Insert(n++, OpCodes.Newarr.ToInstruction(objectType));
|
||||
block.Insert(n++, Create(OpCodes.Stloc, new Operand(Operand.Type.TempObjArray)));
|
||||
|
||||
for (int j = mparams.Count - 1; j >= 0; j--) {
|
||||
var argType = mparams[j];
|
||||
if (argType.RemovePinnedAndModifiers().IsValueType)
|
||||
block.Insert(n++, OpCodes.Box.ToInstruction(((TypeDefOrRefSig)argType).TypeDefOrRef));
|
||||
block.Insert(n++, Create(OpCodes.Stloc, new Operand(Operand.Type.TempObj)));
|
||||
block.Insert(n++, Create(OpCodes.Ldloc, new Operand(Operand.Type.TempObjArray)));
|
||||
block.Insert(n++, OpCodes.Ldc_I4.ToInstruction(j));
|
||||
block.Insert(n++, Create(OpCodes.Ldloc, new Operand(Operand.Type.TempObj)));
|
||||
block.Insert(n++, OpCodes.Stelem_Ref.ToInstruction());
|
||||
}
|
||||
}
|
||||
|
||||
// Push delegate instance
|
||||
InsertLoadThis(block, n++);
|
||||
block.Insert(n++, OpCodes.Ldc_I4.ToInstruction(newMethodInfo.delegateIndex));
|
||||
InsertCallOurMethod(block, n++, "RtGetDelegateInstance");
|
||||
block.Insert(n++, Create(OpCodes.Castclass, new Operand(Operand.Type.ReflectionType, newMethodInfo.delegateType)));
|
||||
|
||||
// Push all popped args
|
||||
if (mparams.Count > 0) {
|
||||
for (int j = 0; j < mparams.Count; j++) {
|
||||
block.Insert(n++, Create(OpCodes.Ldloc, new Operand(Operand.Type.TempObjArray)));
|
||||
block.Insert(n++, OpCodes.Ldc_I4.ToInstruction(j));
|
||||
block.Insert(n++, OpCodes.Ldelem_Ref.ToInstruction());
|
||||
var argType = mparams[j];
|
||||
if (argType.RemovePinnedAndModifiers().IsValueType)
|
||||
block.Insert(n++, OpCodes.Unbox_Any.ToInstruction(((TypeDefOrRefSig)argType).TypeDefOrRef));
|
||||
else {
|
||||
// Don't cast it to its correct type. This will sometimes cause
|
||||
// an exception in some EF obfuscated assembly since we'll be
|
||||
// trying to cast a System.Reflection.AssemblyName type to some
|
||||
// other type.
|
||||
// block.insert(n++, Instruction.Create(OpCodes.Castclass, argType.ToTypeDefOrRef()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InsertLoadThis(block, n++);
|
||||
block.Insert(n++, Create(OpCodes.Call, new Operand(Operand.Type.NewMethod, method.methodBase)));
|
||||
i = n - 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static IList<TypeSig> GetParameters(MethodDef method) {
|
||||
var list = new List<TypeSig>(method.Parameters.Count);
|
||||
for (int i = 0; i < method.Parameters.Count; i++)
|
||||
list.Add(method.Parameters[i].Type);
|
||||
return list;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
static FieldInfo GetStackFrameMethodField() {
|
||||
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
|
||||
return ResolverUtils.GetFieldThrow(typeof(StackFrame), typeof(MethodBase), flags, "Could not find StackFrame's method (MethodBase) field");
|
||||
}
|
||||
|
||||
static void WriteMethodBase(StackFrame frame, MethodBase method) {
|
||||
var methodField = GetStackFrameMethodField();
|
||||
methodField.SetValue(frame, method);
|
||||
if (frame.GetMethod() != method)
|
||||
throw new ApplicationException($"Could not set new method: {method}");
|
||||
}
|
||||
|
||||
NewMethodInfo GetNewMethodInfo(string name) {
|
||||
delegateNameToNewMethodInfo.TryGetValue(name, out var info);
|
||||
return info;
|
||||
}
|
||||
|
||||
// Called after the StackTrace ctor has been called.
|
||||
static StackTrace static_RtFixStackTrace(StackTrace stackTrace, MethodsRewriter self) => self.RtFixStackTrace(stackTrace);
|
||||
|
||||
StackTrace RtFixStackTrace(StackTrace stackTrace) {
|
||||
var framesField = GetStackTraceStackFramesField();
|
||||
var frames = (StackFrame[])framesField.GetValue(stackTrace);
|
||||
|
||||
var newFrames = new List<StackFrame>(frames.Length);
|
||||
foreach (var frame in frames) {
|
||||
FixStackFrame(frame);
|
||||
newFrames.Add(frame);
|
||||
}
|
||||
|
||||
framesField.SetValue(stackTrace, newFrames.ToArray());
|
||||
return stackTrace;
|
||||
}
|
||||
|
||||
static StackFrame static_RtFixStackFrame(StackFrame stackFrame, MethodsRewriter self) => self.RtFixStackFrame(stackFrame);
|
||||
|
||||
StackFrame RtFixStackFrame(StackFrame frame) {
|
||||
FixStackFrame(frame);
|
||||
return frame;
|
||||
}
|
||||
|
||||
void FixStackFrame(StackFrame frame) {
|
||||
var method = frame.GetMethod();
|
||||
var info = GetNewMethodInfo(method.Name);
|
||||
if (info == null)
|
||||
return;
|
||||
|
||||
if (newStackMethodDict.TryGetValue(info, out var stackMethod)) {
|
||||
WriteMethodBase(frame, stackMethod);
|
||||
}
|
||||
else if (info.IsRewrittenMethod(method.Name)) {
|
||||
// Write random method from the same module
|
||||
WriteMethodBase(frame, methodsFinder.GetMethod(info.oldMethod.Module));
|
||||
}
|
||||
else if (info.IsDelegateMethod(method.Name)) {
|
||||
// Write original method
|
||||
WriteMethodBase(frame, info.oldMethod);
|
||||
}
|
||||
else
|
||||
throw new ApplicationException("BUG: Shouldn't be here");
|
||||
}
|
||||
|
||||
// Called when the code calls GetCallingAssembly(), GetEntryAssembly(), or GetExecutingAssembly()
|
||||
Assembly RtGetAssembly(int delegateIndex) => newMethodInfos[delegateIndex].oldMethod.Module.Assembly;
|
||||
|
||||
// Called when the code calls GetAssembly(Type)
|
||||
static Assembly static_RtGetAssembly_TypeArg(Type type, MethodsRewriter self) => self.RtGetAssembly_TypeArg(type);
|
||||
Assembly RtGetAssembly_TypeArg(Type type) => Assembly.GetAssembly(type);
|
||||
Delegate RtGetDelegateInstance(int delegateIndex) => newMethodInfos[delegateIndex].delegateInstance;
|
||||
}
|
||||
}
|
46
de4dot/AssemblyData/methodsrewriter/Operand.cs
Normal file
46
de4dot/AssemblyData/methodsrewriter/Operand.cs
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class Operand {
|
||||
public enum Type {
|
||||
ThisArg, // Replace operand with the 'this' arg
|
||||
TempObj, // Replace operand with temp object local variable
|
||||
TempObjArray, // Replace operand with temp object[] local variable
|
||||
OurMethod, // Replace operand with a call to our method. methodName must be unique.
|
||||
NewMethod, // Replace operand with a call to new method. data is realMethod
|
||||
ReflectionType, // Replace operand with a .NET type
|
||||
}
|
||||
|
||||
public Type type;
|
||||
public object data;
|
||||
|
||||
public Operand(Type type) {
|
||||
this.type = type;
|
||||
data = null;
|
||||
}
|
||||
|
||||
public Operand(Type type, object data) {
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public override string ToString() => "{" + type + " => " + data + "}";
|
||||
}
|
||||
}
|
214
de4dot/AssemblyData/methodsrewriter/Resolver.cs
Normal file
214
de4dot/AssemblyData/methodsrewriter/Resolver.cs
Normal file
@ -0,0 +1,214 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
static class Resolver {
|
||||
static Dictionary<string, AssemblyResolver> assemblyResolvers = new Dictionary<string, AssemblyResolver>(StringComparer.Ordinal);
|
||||
static Dictionary<Module, MModule> modules = new Dictionary<Module, MModule>();
|
||||
|
||||
public static MModule LoadAssembly(Module module) {
|
||||
if (modules.TryGetValue(module, out var info))
|
||||
return info;
|
||||
|
||||
info = new MModule(module, ModuleDefMD.Load(module.FullyQualifiedName));
|
||||
modules[module] = info;
|
||||
return info;
|
||||
}
|
||||
|
||||
static MModule GetModule(ModuleDef moduleDef) {
|
||||
foreach (var mm in modules.Values) {
|
||||
if (mm.moduleDef == moduleDef)
|
||||
return mm;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static MModule GetModule(AssemblyRef asmRef) {
|
||||
foreach (var mm in modules.Values) {
|
||||
var asm = mm.moduleDef.Assembly;
|
||||
if (asm != null && asm.FullName == asmRef.FullName)
|
||||
return mm;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MModule GetModule(IScope scope) {
|
||||
if (scope.ScopeType == ScopeType.ModuleDef)
|
||||
return GetModule((ModuleDef)scope);
|
||||
else if (scope.ScopeType == ScopeType.AssemblyRef)
|
||||
return GetModule((AssemblyRef)scope);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MType GetType(IType typeRef) {
|
||||
if (typeRef == null)
|
||||
return null;
|
||||
var module = GetModule(typeRef.Scope);
|
||||
if (module != null)
|
||||
return module.GetType(typeRef);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MMethod GetMethod(IMethod methodRef) {
|
||||
if (methodRef == null)
|
||||
return null;
|
||||
var module = GetModule(methodRef.DeclaringType.Scope);
|
||||
if (module != null)
|
||||
return module.GetMethod(methodRef);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MField GetField(IField fieldRef) {
|
||||
if (fieldRef == null)
|
||||
return null;
|
||||
var module = GetModule(fieldRef.DeclaringType.Scope);
|
||||
if (module != null)
|
||||
return module.GetField(fieldRef);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static object GetRtObject(ITokenOperand memberRef) {
|
||||
if (memberRef == null)
|
||||
return null;
|
||||
if (memberRef is ITypeDefOrRef tdr)
|
||||
return GetRtType(tdr);
|
||||
if (memberRef is IField field && field.FieldSig != null)
|
||||
return GetRtField(field);
|
||||
if (memberRef is IMethod method && method.MethodSig != null)
|
||||
return GetRtMethod(method);
|
||||
|
||||
throw new ApplicationException($"Unknown MemberRef: {memberRef}");
|
||||
}
|
||||
|
||||
public static Type GetRtType(IType typeRef) {
|
||||
var mtype = GetType(typeRef);
|
||||
if (mtype != null)
|
||||
return mtype.type;
|
||||
|
||||
return Resolver.Resolve(typeRef);
|
||||
}
|
||||
|
||||
public static FieldInfo GetRtField(IField fieldRef) {
|
||||
var mfield = GetField(fieldRef);
|
||||
if (mfield != null)
|
||||
return mfield.fieldInfo;
|
||||
|
||||
return Resolver.Resolve(fieldRef);
|
||||
}
|
||||
|
||||
public static MethodBase GetRtMethod(IMethod methodRef) {
|
||||
var mmethod = GetMethod(methodRef);
|
||||
if (mmethod != null)
|
||||
return mmethod.methodBase;
|
||||
|
||||
return Resolver.Resolve(methodRef);
|
||||
}
|
||||
|
||||
static AssemblyResolver GetAssemblyResolver(ITypeDefOrRef type) {
|
||||
var asmName = type.DefinitionAssembly.FullName;
|
||||
if (!assemblyResolvers.TryGetValue(asmName, out var resolver))
|
||||
assemblyResolvers[asmName] = resolver = new AssemblyResolver(asmName);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
static Type Resolve(IType typeRef) {
|
||||
if (typeRef == null)
|
||||
return null;
|
||||
var scopeType = typeRef.ScopeType;
|
||||
var resolver = GetAssemblyResolver(scopeType);
|
||||
var resolvedType = resolver.Resolve(scopeType);
|
||||
if (resolvedType != null)
|
||||
return FixType(typeRef, resolvedType);
|
||||
throw new ApplicationException($"Could not resolve type {typeRef} ({typeRef.MDToken.Raw:X8}) in assembly {resolver}");
|
||||
}
|
||||
|
||||
static FieldInfo Resolve(IField fieldRef) {
|
||||
if (fieldRef == null)
|
||||
return null;
|
||||
var resolver = GetAssemblyResolver(fieldRef.DeclaringType);
|
||||
var fieldInfo = resolver.Resolve(fieldRef);
|
||||
if (fieldInfo != null)
|
||||
return fieldInfo;
|
||||
throw new ApplicationException($"Could not resolve field {fieldRef} ({fieldRef.MDToken.Raw:X8}) in assembly {resolver}");
|
||||
}
|
||||
|
||||
static MethodBase Resolve(IMethod methodRef) {
|
||||
if (methodRef == null)
|
||||
return null;
|
||||
var resolver = GetAssemblyResolver(methodRef.DeclaringType);
|
||||
var methodBase = resolver.Resolve(methodRef);
|
||||
if (methodBase != null)
|
||||
return methodBase;
|
||||
throw new ApplicationException($"Could not resolve method {methodRef} ({methodRef.MDToken.Raw:X8}) in assembly {resolver}");
|
||||
}
|
||||
|
||||
static Type FixType(IType typeRef, Type type) {
|
||||
var sig = typeRef as TypeSig;
|
||||
if (sig == null) {
|
||||
if (typeRef is TypeSpec ts)
|
||||
sig = ts.TypeSig;
|
||||
}
|
||||
while (sig != null) {
|
||||
switch (sig.ElementType) {
|
||||
case ElementType.SZArray:
|
||||
type = type.MakeArrayType();
|
||||
break;
|
||||
|
||||
case ElementType.Array:
|
||||
type = type.MakeArrayType((int)((ArraySig)sig).Rank);
|
||||
break;
|
||||
|
||||
case ElementType.ByRef:
|
||||
type = type.MakeByRefType();
|
||||
break;
|
||||
|
||||
case ElementType.Ptr:
|
||||
type = type.MakePointerType();
|
||||
break;
|
||||
|
||||
case ElementType.GenericInst:
|
||||
var git = (GenericInstSig)sig;
|
||||
var args = new Type[git.GenericArguments.Count];
|
||||
bool isGenericTypeDef = true;
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
var arg = git.GenericArguments[i];
|
||||
if (!(arg is GenericSig))
|
||||
isGenericTypeDef = false;
|
||||
args[i] = Resolver.Resolve(arg);
|
||||
}
|
||||
if (!isGenericTypeDef)
|
||||
type = type.MakeGenericType(args);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sig = sig.Next;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
130
de4dot/AssemblyData/methodsrewriter/ResolverUtils.cs
Normal file
130
de4dot/AssemblyData/methodsrewriter/ResolverUtils.cs
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
static class ResolverUtils {
|
||||
public static bool CompareTypes(Type a, IType b) => new SigComparer().Equals(a, b);
|
||||
public static bool CompareFields(FieldInfo a, IField b) => new SigComparer().Equals(a, b);
|
||||
public static bool HasThis(MethodBase method) => (method.CallingConvention & CallingConventions.HasThis) != 0;
|
||||
public static bool ExplicitThis(MethodBase method) => (method.CallingConvention & CallingConventions.ExplicitThis) != 0;
|
||||
public static bool CompareMethods(MethodBase a, IMethod b) => new SigComparer().Equals(a, b);
|
||||
|
||||
public static Type GetReturnType(MethodBase methodBase) {
|
||||
if (methodBase is MethodInfo methodInfo)
|
||||
return methodInfo.ReturnType;
|
||||
|
||||
if (methodBase is ConstructorInfo ctorInfo)
|
||||
return typeof(void);
|
||||
|
||||
throw new ApplicationException($"Could not figure out return type: {methodBase} ({methodBase.MetadataToken:X8})");
|
||||
}
|
||||
|
||||
public static Type[] GetGenericArguments(MethodBase methodBase) {
|
||||
try {
|
||||
return methodBase.GetGenericArguments();
|
||||
}
|
||||
catch (NotSupportedException) {
|
||||
return new Type[0];
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodBase> GetMethodBases(Type type, BindingFlags flags) {
|
||||
if (type.TypeInitializer != null)
|
||||
yield return type.TypeInitializer;
|
||||
foreach (var ctor in type.GetConstructors(flags))
|
||||
yield return ctor;
|
||||
foreach (var m in type.GetMethods(flags))
|
||||
yield return m;
|
||||
}
|
||||
|
||||
class CachedMemberInfo {
|
||||
Type type;
|
||||
Type memberType;
|
||||
public CachedMemberInfo(Type type, Type memberType) {
|
||||
this.type = type;
|
||||
this.memberType = memberType;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => type.GetHashCode() ^ memberType.GetHashCode();
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as CachedMemberInfo;
|
||||
if (other == null)
|
||||
return false;
|
||||
return type == other.type && memberType == other.memberType;
|
||||
}
|
||||
}
|
||||
|
||||
static Dictionary<CachedMemberInfo, FieldInfo> cachedFieldInfos = new Dictionary<CachedMemberInfo, FieldInfo>();
|
||||
public static FieldInfo GetField(Type type, Type fieldType, BindingFlags flags) {
|
||||
var key = new CachedMemberInfo(type, fieldType);
|
||||
if (cachedFieldInfos.TryGetValue(key, out var fieldInfo))
|
||||
return fieldInfo;
|
||||
|
||||
foreach (var field in type.GetFields(flags)) {
|
||||
if (field.FieldType == fieldType) {
|
||||
cachedFieldInfos[key] = field;
|
||||
return field;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
|
||||
public static List<FieldInfo> GetFields(Type type, Type fieldType, BindingFlags flags) {
|
||||
var list = new List<FieldInfo>();
|
||||
foreach (var field in type.GetFields(flags)) {
|
||||
if (field.FieldType == fieldType)
|
||||
list.Add(field);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Type MakeInstanceType(Type type, ITypeDefOrRef typeRef) {
|
||||
var ts = typeRef as TypeSpec;
|
||||
if (ts == null)
|
||||
return type;
|
||||
var git = ts.TypeSig as GenericInstSig;
|
||||
if (git == null)
|
||||
return type;
|
||||
var types = new Type[git.GenericArguments.Count];
|
||||
bool isTypeDef = true;
|
||||
for (int i = 0; i < git.GenericArguments.Count; i++) {
|
||||
var arg = git.GenericArguments[i];
|
||||
if (!(arg is GenericSig))
|
||||
isTypeDef = false;
|
||||
types[i] = Resolver.GetRtType(arg);
|
||||
}
|
||||
if (isTypeDef)
|
||||
return type;
|
||||
return type.MakeGenericType(types);
|
||||
}
|
||||
}
|
||||
}
|
92
de4dot/AssemblyData/methodsrewriter/TypeInstanceResolver.cs
Normal file
92
de4dot/AssemblyData/methodsrewriter/TypeInstanceResolver.cs
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class TypeInstanceResolver {
|
||||
Type type;
|
||||
Dictionary<string, List<MethodBase>> methods;
|
||||
Dictionary<string, List<FieldInfo>> fields;
|
||||
|
||||
public TypeInstanceResolver(Type type, ITypeDefOrRef typeRef) => this.type = ResolverUtils.MakeInstanceType(type, typeRef);
|
||||
|
||||
public FieldInfo Resolve(IField fieldRef) {
|
||||
InitFields();
|
||||
|
||||
if (!fields.TryGetValue(fieldRef.Name.String, out var list))
|
||||
return null;
|
||||
|
||||
fieldRef = GenericArgsSubstitutor.Create(fieldRef, fieldRef.DeclaringType.TryGetGenericInstSig());
|
||||
|
||||
foreach (var field in list) {
|
||||
if (ResolverUtils.CompareFields(field, fieldRef))
|
||||
return field;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void InitFields() {
|
||||
if (fields != null)
|
||||
return;
|
||||
fields = new Dictionary<string, List<FieldInfo>>(StringComparer.Ordinal);
|
||||
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
|
||||
foreach (var field in type.GetFields(flags)) {
|
||||
if (!fields.TryGetValue(field.Name, out var list))
|
||||
fields[field.Name] = list = new List<FieldInfo>();
|
||||
list.Add(field);
|
||||
}
|
||||
}
|
||||
|
||||
public MethodBase Resolve(IMethod methodRef) {
|
||||
InitMethods();
|
||||
|
||||
if (!methods.TryGetValue(methodRef.Name.String, out var list))
|
||||
return null;
|
||||
|
||||
methodRef = GenericArgsSubstitutor.Create(methodRef, methodRef.DeclaringType.TryGetGenericInstSig());
|
||||
|
||||
foreach (var method in list) {
|
||||
if (ResolverUtils.CompareMethods(method, methodRef))
|
||||
return method;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
void InitMethods() {
|
||||
if (methods != null)
|
||||
return;
|
||||
methods = new Dictionary<string, List<MethodBase>>(StringComparer.Ordinal);
|
||||
|
||||
var flags = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;
|
||||
foreach (var method in ResolverUtils.GetMethodBases(type, flags)) {
|
||||
if (!methods.TryGetValue(method.Name, out var list))
|
||||
methods[method.Name] = list = new List<MethodBase>();
|
||||
list.Add(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
41
de4dot/AssemblyData/methodsrewriter/TypeResolver.cs
Normal file
41
de4dot/AssemblyData/methodsrewriter/TypeResolver.cs
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace AssemblyData.methodsrewriter {
|
||||
class TypeResolver {
|
||||
public Type type;
|
||||
Dictionary<ITypeDefOrRef, TypeInstanceResolver> typeRefToInstance = new Dictionary<ITypeDefOrRef, TypeInstanceResolver>(TypeEqualityComparer.Instance);
|
||||
|
||||
public TypeResolver(Type type) => this.type = type;
|
||||
|
||||
TypeInstanceResolver GetTypeInstance(ITypeDefOrRef typeRef) {
|
||||
if (!typeRefToInstance.TryGetValue(typeRef, out var instance))
|
||||
typeRefToInstance[typeRef] = instance = new TypeInstanceResolver(type, typeRef);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public FieldInfo Resolve(IField fieldRef) => GetTypeInstance(fieldRef.DeclaringType).Resolve(fieldRef);
|
||||
public MethodBase Resolve(IMethod methodRef) => GetTypeInstance(methodRef.DeclaringType).Resolve(methodRef);
|
||||
}
|
||||
}
|
9
de4dot/AssemblyServer-x64/App.config
Normal file
9
de4dot/AssemblyServer-x64/App.config
Normal file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0"/>
|
||||
</startup>
|
||||
<runtime>
|
||||
<loadFromRemoteSources enabled="true"/>
|
||||
</runtime>
|
||||
</configuration>
|
12
de4dot/AssemblyServer-x64/AssemblyServer-x64.csproj
Normal file
12
de4dot/AssemblyServer-x64/AssemblyServer-x64.csproj
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssemblyData\AssemblyData.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
24
de4dot/AssemblyServer-x64/Program.cs
Normal file
24
de4dot/AssemblyServer-x64/Program.cs
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace AssemblyServer_x64 {
|
||||
class Program {
|
||||
static int Main(string[] args) => AssemblyServer.Start.Main(args);
|
||||
}
|
||||
}
|
674
de4dot/COPYING
Normal file
674
de4dot/COPYING
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program 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.
|
||||
|
||||
This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
14
de4dot/Directory.Build.props
Normal file
14
de4dot/Directory.Build.props
Normal file
@ -0,0 +1,14 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<Features>strict</Features>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Version>3.2.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
22
de4dot/LICENSE.de4dot.txt
Normal file
22
de4dot/LICENSE.de4dot.txt
Normal file
@ -0,0 +1,22 @@
|
||||
<LICENSE>
|
||||
Copyright (C) 2011-2014 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
</LICENSE>
|
||||
|
||||
Official site: https://github.com/0xd4d/de4dot
|
||||
|
||||
See the file COPYING for more details.
|
12
de4dot/Nuget.config
Normal file
12
de4dot/Nuget.config
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
|
||||
<config>
|
||||
<add key="globalPackagesFolder" value=".nuget/packages"/>
|
||||
</config>
|
||||
|
||||
<packageSources>
|
||||
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
|
||||
</packageSources>
|
||||
|
||||
</configuration>
|
4
de4dot/README.md
Normal file
4
de4dot/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# de4dot
|
||||
|
||||
Modified to only handle cases for Escape From Tarkov (Arena).
|
||||
Uses updated dnlib (4.4.0) and targets `net48`.
|
13
de4dot/de4dot-x64/App.config
Normal file
13
de4dot/de4dot-x64/App.config
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<probing privatePath="bin"/>
|
||||
</assemblyBinding>
|
||||
<loadFromRemoteSources enabled="true"/>
|
||||
</runtime>
|
||||
</configuration>
|
24
de4dot/de4dot-x64/Program.cs
Normal file
24
de4dot/de4dot-x64/Program.cs
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace de4dot_x64 {
|
||||
class Program {
|
||||
static int Main(string[] args) => de4dot.cui.Program.Main(args);
|
||||
}
|
||||
}
|
12
de4dot/de4dot-x64/de4dot-x64.csproj
Normal file
12
de4dot/de4dot-x64/de4dot-x64.csproj
Normal file
@ -0,0 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<OutputType>Exe</OutputType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\de4dot.cui\de4dot.cui.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
29
de4dot/de4dot.blocks/BaseBlock.cs
Normal file
29
de4dot/de4dot.blocks/BaseBlock.cs
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public abstract class BaseBlock {
|
||||
ScopeBlock parent = null;
|
||||
|
||||
public ScopeBlock Parent {
|
||||
get => parent;
|
||||
set => parent = value;
|
||||
}
|
||||
}
|
||||
}
|
298
de4dot/de4dot.blocks/Block.cs
Normal file
298
de4dot/de4dot.blocks/Block.cs
Normal file
@ -0,0 +1,298 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public class Block : BaseBlock {
|
||||
List<Instr> instructions = new List<Instr>();
|
||||
|
||||
// List of all explicit (non-fall-through) targets. It's just one if it's a normal
|
||||
// branch, but if it's a switch, it could be many targets.
|
||||
List<Block> targets;
|
||||
|
||||
// This is the fall through Block (non branch instructions)
|
||||
Block fallThrough;
|
||||
|
||||
// All blocks that fall through or branches to this block
|
||||
List<Block> sources = new List<Block>();
|
||||
|
||||
public Block FallThrough {
|
||||
get => fallThrough;
|
||||
set => fallThrough = value;
|
||||
}
|
||||
|
||||
public List<Block> Targets {
|
||||
get => targets;
|
||||
set => targets = value;
|
||||
}
|
||||
|
||||
public List<Block> Sources => sources;
|
||||
|
||||
public Instr FirstInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
Add(new Instr(OpCodes.Nop.ToInstruction()));
|
||||
return instructions[0];
|
||||
}
|
||||
}
|
||||
|
||||
public Instr LastInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
Add(new Instr(OpCodes.Nop.ToInstruction()));
|
||||
return instructions[instructions.Count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public void Add(Instr instr) => instructions.Add(instr);
|
||||
public void Insert(int index, Instruction instr) => instructions.Insert(index, new Instr(instr));
|
||||
|
||||
public List<Instr> Instructions => instructions;
|
||||
|
||||
// If last instr is a br/br.s, removes it and replaces it with a fall through
|
||||
public void RemoveLastBr() {
|
||||
if (!LastInstr.IsBr())
|
||||
return;
|
||||
|
||||
if (fallThrough != null || (LastInstr.Operand != null && (targets == null || targets.Count != 1)))
|
||||
throw new ApplicationException("Invalid block state when last instr is a br/br.s");
|
||||
fallThrough = LastInstr.Operand != null ? targets[0] : null;
|
||||
targets = null;
|
||||
instructions.RemoveAt(instructions.Count - 1);
|
||||
}
|
||||
|
||||
public void Replace(int index, int num, Instruction instruction) {
|
||||
if (num <= 0)
|
||||
throw new ArgumentOutOfRangeException("num");
|
||||
Remove(index, num);
|
||||
instructions.Insert(index, new Instr(instruction));
|
||||
}
|
||||
|
||||
public void Remove(int index, int num) {
|
||||
if (index + num > instructions.Count)
|
||||
throw new ApplicationException("Overflow");
|
||||
if (num > 0 && index + num == instructions.Count && LastInstr.IsConditionalBranch())
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
instructions.RemoveRange(index, num);
|
||||
}
|
||||
|
||||
public void Remove(IEnumerable<int> indexes) {
|
||||
var instrsToDelete = new List<int>(Utils.Unique(indexes));
|
||||
instrsToDelete.Sort();
|
||||
instrsToDelete.Reverse();
|
||||
foreach (var index in instrsToDelete)
|
||||
Remove(index, 1);
|
||||
}
|
||||
|
||||
// Replace the last instructions with a branch to target
|
||||
public void ReplaceLastInstrsWithBranch(int numInstrs, Block target) {
|
||||
if (numInstrs < 0 || numInstrs > instructions.Count)
|
||||
throw new ApplicationException("Invalid numInstrs to replace with branch");
|
||||
if (target == null)
|
||||
throw new ApplicationException("Invalid new target, it's null");
|
||||
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
if (numInstrs > 0)
|
||||
instructions.RemoveRange(instructions.Count - numInstrs, numInstrs);
|
||||
fallThrough = target;
|
||||
target.sources.Add(this);
|
||||
}
|
||||
|
||||
public void ReplaceLastNonBranchWithBranch(int numInstrs, Block target) {
|
||||
if (LastInstr.IsBr())
|
||||
numInstrs++;
|
||||
ReplaceLastInstrsWithBranch(numInstrs, target);
|
||||
}
|
||||
|
||||
public void ReplaceBccWithBranch(bool isTaken) => ReplaceLastInstrsWithBranch(1, isTaken ? targets[0] : fallThrough);
|
||||
|
||||
public void ReplaceSwitchWithBranch(Block target) {
|
||||
if (LastInstr.OpCode.Code != Code.Switch)
|
||||
throw new ApplicationException("Last instruction is not a switch");
|
||||
ReplaceLastInstrsWithBranch(1, target);
|
||||
}
|
||||
|
||||
public void SetNewFallThrough(Block newFallThrough) {
|
||||
DisconnectFromFallThrough();
|
||||
fallThrough = newFallThrough;
|
||||
newFallThrough.sources.Add(this);
|
||||
}
|
||||
|
||||
public void SetNewTarget(int index, Block newTarget) {
|
||||
DisconnectFromBlock(targets[index]);
|
||||
targets[index] = newTarget;
|
||||
newTarget.sources.Add(this);
|
||||
}
|
||||
|
||||
public void RemoveDeadBlock() {
|
||||
if (sources.Count != 0)
|
||||
throw new ApplicationException("Trying to remove a non-dead block");
|
||||
RemoveGuaranteedDeadBlock();
|
||||
}
|
||||
|
||||
// Removes a block that has been guaranteed to be dead. This method won't verify
|
||||
// that it really is dead.
|
||||
public void RemoveGuaranteedDeadBlock() {
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
Parent = null;
|
||||
}
|
||||
|
||||
void DisconnectFromFallThroughAndTargets() {
|
||||
DisconnectFromFallThrough();
|
||||
DisconnectFromTargets();
|
||||
}
|
||||
|
||||
void DisconnectFromFallThrough() {
|
||||
if (fallThrough != null) {
|
||||
DisconnectFromBlock(fallThrough);
|
||||
fallThrough = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectFromTargets() {
|
||||
if (targets != null) {
|
||||
foreach (var target in targets)
|
||||
DisconnectFromBlock(target);
|
||||
targets = null;
|
||||
}
|
||||
}
|
||||
|
||||
void DisconnectFromBlock(Block target) {
|
||||
if (!target.sources.Remove(this))
|
||||
throw new ApplicationException("Could not remove the block from its target block");
|
||||
}
|
||||
|
||||
public int CountTargets() {
|
||||
int count = fallThrough != null ? 1 : 0;
|
||||
if (targets != null)
|
||||
count += targets.Count;
|
||||
return count;
|
||||
}
|
||||
|
||||
// Returns the target iff it has only ONE target. Else it returns null.
|
||||
public Block GetOnlyTarget() {
|
||||
if (CountTargets() != 1)
|
||||
return null;
|
||||
if (fallThrough != null)
|
||||
return fallThrough;
|
||||
return targets[0];
|
||||
}
|
||||
|
||||
// Returns all targets. FallThrough (if not null) is always returned first!
|
||||
public IEnumerable<Block> GetTargets() {
|
||||
if (fallThrough != null)
|
||||
yield return fallThrough;
|
||||
if (targets != null) {
|
||||
foreach (var block in targets)
|
||||
yield return block;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true iff other is the only block in Sources
|
||||
public bool IsOnlySource(Block other) => sources.Count == 1 && sources[0] == other;
|
||||
|
||||
// Returns true if we can merge other with this
|
||||
public bool CanMerge(Block other) => CanAppend(other) && other.IsOnlySource(this);
|
||||
|
||||
// Merge two blocks into one
|
||||
public void Merge(Block other) {
|
||||
if (!CanMerge(other))
|
||||
throw new ApplicationException("Can't merge the two blocks!");
|
||||
Append(other);
|
||||
other.DisconnectFromFallThroughAndTargets();
|
||||
other.Parent = null;
|
||||
}
|
||||
|
||||
public bool CanAppend(Block other) {
|
||||
if (other == null || other == this || GetOnlyTarget() != other)
|
||||
return false;
|
||||
// If it's eg. a leave, then don't merge them since it clears the stack.
|
||||
return LastInstr.IsBr() || Instr.IsFallThrough(LastInstr.OpCode);
|
||||
}
|
||||
|
||||
public void Append(Block other) {
|
||||
if (!CanAppend(other))
|
||||
throw new ApplicationException("Can't append the block!");
|
||||
|
||||
RemoveLastBr(); // Get rid of last br/br.s if present
|
||||
|
||||
var newInstructions = new List<Instr>(instructions.Count + other.instructions.Count);
|
||||
AddInstructions(newInstructions, instructions, false);
|
||||
AddInstructions(newInstructions, other.instructions, true);
|
||||
instructions = newInstructions;
|
||||
|
||||
DisconnectFromFallThroughAndTargets();
|
||||
if (other.targets != null)
|
||||
targets = new List<Block>(other.targets);
|
||||
else
|
||||
targets = null;
|
||||
fallThrough = other.fallThrough;
|
||||
UpdateSources();
|
||||
}
|
||||
|
||||
void AddInstructions(IList<Instr> dest, IList<Instr> instrs, bool clone) {
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var instr = instrs[i];
|
||||
if (instr.OpCode != OpCodes.Nop)
|
||||
dest.Add(clone ? new Instr(instr.Instruction.Clone()) : instr);
|
||||
}
|
||||
}
|
||||
|
||||
// Update each target's Sources property. Must only be called if this isn't in the
|
||||
// Sources list!
|
||||
public void UpdateSources() {
|
||||
if (fallThrough != null)
|
||||
fallThrough.sources.Add(this);
|
||||
if (targets != null) {
|
||||
foreach (var target in targets)
|
||||
target.sources.Add(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it falls through
|
||||
public bool IsFallThrough() => targets == null && fallThrough != null;
|
||||
|
||||
public bool CanFlipConditionalBranch() => LastInstr.CanFlipConditionalBranch();
|
||||
|
||||
public void FlipConditionalBranch() {
|
||||
if (fallThrough == null || targets == null || targets.Count != 1)
|
||||
throw new ApplicationException("Invalid bcc block state");
|
||||
LastInstr.FlipConditonalBranch();
|
||||
var oldFallThrough = fallThrough;
|
||||
fallThrough = targets[0];
|
||||
targets[0] = oldFallThrough;
|
||||
}
|
||||
|
||||
// Returns true if it's a conditional branch
|
||||
public bool IsConditionalBranch() => LastInstr.IsConditionalBranch();
|
||||
|
||||
public bool IsNopBlock() {
|
||||
if (!IsFallThrough())
|
||||
return false;
|
||||
foreach (var instr in instructions) {
|
||||
if (instr.OpCode.Code != Code.Nop)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
270
de4dot/de4dot.blocks/Blocks.cs
Normal file
270
de4dot/de4dot.blocks/Blocks.cs
Normal file
@ -0,0 +1,270 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public class Blocks {
|
||||
MethodDef method;
|
||||
IList<Local> locals;
|
||||
MethodBlocks methodBlocks;
|
||||
|
||||
public MethodBlocks MethodBlocks => methodBlocks;
|
||||
public IList<Local> Locals => locals;
|
||||
public MethodDef Method => method;
|
||||
|
||||
public Blocks(MethodDef method) {
|
||||
this.method = method;
|
||||
UpdateBlocks();
|
||||
}
|
||||
|
||||
public void UpdateBlocks() {
|
||||
var body = method.Body;
|
||||
locals = body.Variables;
|
||||
methodBlocks = new InstructionListParser(body.Instructions, body.ExceptionHandlers).Parse();
|
||||
}
|
||||
|
||||
IEnumerable<ScopeBlock> GetAllScopeBlocks(ScopeBlock scopeBlock) {
|
||||
var list = new List<ScopeBlock>();
|
||||
list.Add(scopeBlock);
|
||||
list.AddRange(scopeBlock.GetAllScopeBlocks());
|
||||
return list;
|
||||
}
|
||||
|
||||
public int RemoveDeadBlocks() => new DeadBlocksRemover(methodBlocks).Remove();
|
||||
|
||||
public void GetCode(out IList<Instruction> allInstructions, out IList<ExceptionHandler> allExceptionHandlers) =>
|
||||
new CodeGenerator(methodBlocks).GetCode(out allInstructions, out allExceptionHandlers);
|
||||
|
||||
struct LocalVariableInfo {
|
||||
public Block block;
|
||||
public int index;
|
||||
public LocalVariableInfo(Block block, int index) {
|
||||
this.block = block;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
|
||||
public int OptimizeLocals() {
|
||||
if (locals.Count == 0)
|
||||
return 0;
|
||||
|
||||
var usedLocals = new Dictionary<Local, List<LocalVariableInfo>>();
|
||||
foreach (var block in methodBlocks.GetAllBlocks()) {
|
||||
for (int i = 0; i < block.Instructions.Count; i++) {
|
||||
var instr = block.Instructions[i];
|
||||
Local local;
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
local = Instr.GetLocalVar(locals, instr);
|
||||
break;
|
||||
|
||||
case Code.Ldloca_S:
|
||||
case Code.Ldloca:
|
||||
local = (Local)instr.Operand;
|
||||
break;
|
||||
|
||||
default:
|
||||
local = null;
|
||||
break;
|
||||
}
|
||||
if (local == null)
|
||||
continue;
|
||||
|
||||
if (!usedLocals.TryGetValue(local, out var list))
|
||||
usedLocals[local] = list = new List<LocalVariableInfo>();
|
||||
list.Add(new LocalVariableInfo(block, i));
|
||||
if (usedLocals.Count == locals.Count)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int newIndex = -1;
|
||||
var newLocals = new List<Local>(usedLocals.Count);
|
||||
var newLocalsDict = new Dictionary<Local, bool>(usedLocals.Count);
|
||||
foreach (var local in usedLocals.Keys) {
|
||||
newIndex++;
|
||||
newLocals.Add(local);
|
||||
newLocalsDict[local] = true;
|
||||
foreach (var info in usedLocals[local])
|
||||
info.block.Instructions[info.index] = new Instr(OptimizeLocalInstr(info.block.Instructions[info.index], local, (uint)newIndex));
|
||||
}
|
||||
|
||||
// We can't remove all locals. Locals that reference another assembly will
|
||||
// cause the CLR to load that assembly before the method is executed if it
|
||||
// hasn't been loaded yet. This is a side effect the program may depend on.
|
||||
// At least one program has this dependency and will crash if we remove the
|
||||
// unused local. This took a while to figure out...
|
||||
var keptAssemblies = new Dictionary<string, bool>(StringComparer.Ordinal);
|
||||
foreach (var local in locals) {
|
||||
if (newLocalsDict.ContainsKey(local))
|
||||
continue;
|
||||
var defAsm = local.Type.DefinitionAssembly;
|
||||
if (defAsm == null)
|
||||
continue; // eg. fnptr
|
||||
if (defAsm == method.DeclaringType.Module.Assembly)
|
||||
continue; // this assembly is always loaded
|
||||
if (defAsm.IsCorLib())
|
||||
continue; // mscorlib is always loaded
|
||||
var asmName = defAsm.FullName;
|
||||
if (keptAssemblies.ContainsKey(asmName))
|
||||
continue;
|
||||
keptAssemblies[asmName] = true;
|
||||
newLocals.Add(local);
|
||||
}
|
||||
|
||||
int numRemoved = locals.Count - newLocals.Count;
|
||||
locals.Clear();
|
||||
foreach (var local in newLocals)
|
||||
locals.Add(local);
|
||||
return numRemoved;
|
||||
}
|
||||
|
||||
static Instruction OptimizeLocalInstr(Instr instr, Local local, uint newIndex) {
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
if (newIndex == 0)
|
||||
return OpCodes.Ldloc_0.ToInstruction();
|
||||
if (newIndex == 1)
|
||||
return OpCodes.Ldloc_1.ToInstruction();
|
||||
if (newIndex == 2)
|
||||
return OpCodes.Ldloc_2.ToInstruction();
|
||||
if (newIndex == 3)
|
||||
return OpCodes.Ldloc_3.ToInstruction();
|
||||
if (newIndex <= 0xFF)
|
||||
return OpCodes.Ldloc_S.ToInstruction(local);
|
||||
return OpCodes.Ldloc.ToInstruction(local);
|
||||
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
if (newIndex == 0)
|
||||
return OpCodes.Stloc_0.ToInstruction();
|
||||
if (newIndex == 1)
|
||||
return OpCodes.Stloc_1.ToInstruction();
|
||||
if (newIndex == 2)
|
||||
return OpCodes.Stloc_2.ToInstruction();
|
||||
if (newIndex == 3)
|
||||
return OpCodes.Stloc_3.ToInstruction();
|
||||
if (newIndex <= 0xFF)
|
||||
return OpCodes.Stloc_S.ToInstruction(local);
|
||||
return OpCodes.Stloc.ToInstruction(local);
|
||||
|
||||
case Code.Ldloca_S:
|
||||
case Code.Ldloca:
|
||||
if (newIndex <= 0xFF)
|
||||
return OpCodes.Ldloca_S.ToInstruction(local);
|
||||
return OpCodes.Ldloca.ToInstruction(local);
|
||||
|
||||
default:
|
||||
throw new ApplicationException("Invalid ld/st local instruction");
|
||||
}
|
||||
}
|
||||
|
||||
public void RepartitionBlocks() {
|
||||
MergeNopBlocks();
|
||||
foreach (var scopeBlock in GetAllScopeBlocks(methodBlocks)) {
|
||||
try {
|
||||
scopeBlock.RepartitionBlocks();
|
||||
}
|
||||
catch (NullReferenceException) {
|
||||
//TODO: Send this message to the log
|
||||
Console.WriteLine("Null ref exception! Invalid metadata token in code? Method: {0:X8}: {1}", method.MDToken.Raw, method.FullName);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MergeNopBlocks() {
|
||||
var allBlocks = methodBlocks.GetAllBlocks();
|
||||
|
||||
var nopBlocks = new Dictionary<Block, bool>();
|
||||
foreach (var nopBlock in allBlocks) {
|
||||
if (nopBlock.IsNopBlock())
|
||||
nopBlocks[nopBlock] = true;
|
||||
}
|
||||
|
||||
if (nopBlocks.Count == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
bool modified = false;
|
||||
|
||||
foreach (var block in allBlocks) {
|
||||
Block nopBlockTarget;
|
||||
|
||||
nopBlockTarget = GetNopBlockTarget(nopBlocks, block, block.FallThrough);
|
||||
if (nopBlockTarget != null) {
|
||||
block.SetNewFallThrough(nopBlockTarget);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
if (block.Targets != null) {
|
||||
for (int targetIndex = 0; targetIndex < block.Targets.Count; targetIndex++) {
|
||||
nopBlockTarget = GetNopBlockTarget(nopBlocks, block, block.Targets[targetIndex]);
|
||||
if (nopBlockTarget == null)
|
||||
continue;
|
||||
block.SetNewTarget(targetIndex, nopBlockTarget);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!modified)
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (var nopBlock in nopBlocks.Keys)
|
||||
nopBlock.Parent.RemoveDeadBlock(nopBlock);
|
||||
}
|
||||
|
||||
static Block GetNopBlockTarget(Dictionary<Block, bool> nopBlocks, Block source, Block nopBlock) {
|
||||
if (nopBlock == null || !nopBlocks.ContainsKey(nopBlock) || source == nopBlock.FallThrough)
|
||||
return null;
|
||||
if (nopBlock.Parent.BaseBlocks[0] == nopBlock)
|
||||
return null;
|
||||
var target = nopBlock.FallThrough;
|
||||
if (nopBlock.Parent != target.Parent)
|
||||
return null;
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
291
de4dot/de4dot.blocks/BlocksSorter.cs
Normal file
291
de4dot/de4dot.blocks/BlocksSorter.cs
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
class BlocksSorter {
|
||||
ScopeBlock scopeBlock;
|
||||
|
||||
class BlockInfo {
|
||||
public int dfsNumber = -1;
|
||||
public int low;
|
||||
public BaseBlock baseBlock;
|
||||
public bool onStack;
|
||||
|
||||
public BlockInfo(BaseBlock baseBlock) => this.baseBlock = baseBlock;
|
||||
public bool Visited() => dfsNumber >= 0;
|
||||
public override string ToString() => $"L:{low}, D:{dfsNumber}, S:{onStack}";
|
||||
}
|
||||
|
||||
// It uses Tarjan's strongly connected components algorithm to find all SCCs.
|
||||
// See http://www.ics.uci.edu/~eppstein/161/960220.html or wikipedia for a good explanation.
|
||||
// The non-Tarjan code is still pretty simple and can (should) be improved.
|
||||
class Sorter {
|
||||
ScopeBlock scopeBlock;
|
||||
IList<BaseBlock> validBlocks;
|
||||
Dictionary<BaseBlock, BlockInfo> blockToInfo = new Dictionary<BaseBlock, BlockInfo>();
|
||||
Stack<BlockInfo> stack = new Stack<BlockInfo>();
|
||||
List<BaseBlock> sorted;
|
||||
int dfsNumber = 0;
|
||||
bool skipFirstBlock;
|
||||
BaseBlock firstBlock;
|
||||
|
||||
public Sorter(ScopeBlock scopeBlock, IList<BaseBlock> validBlocks, bool skipFirstBlock) {
|
||||
this.scopeBlock = scopeBlock;
|
||||
this.validBlocks = validBlocks;
|
||||
this.skipFirstBlock = skipFirstBlock;
|
||||
}
|
||||
|
||||
public List<BaseBlock> Sort() {
|
||||
if (validBlocks.Count == 0)
|
||||
return new List<BaseBlock>();
|
||||
if (skipFirstBlock)
|
||||
firstBlock = validBlocks[0];
|
||||
|
||||
foreach (var block in validBlocks) {
|
||||
if (block != firstBlock)
|
||||
blockToInfo[block] = new BlockInfo(block);
|
||||
}
|
||||
|
||||
sorted = new List<BaseBlock>(validBlocks.Count);
|
||||
var finalList = new List<BaseBlock>(validBlocks.Count);
|
||||
|
||||
if (firstBlock is Block) {
|
||||
foreach (var target in GetTargets(firstBlock)) {
|
||||
Visit(target);
|
||||
finalList.AddRange(sorted);
|
||||
sorted.Clear();
|
||||
}
|
||||
}
|
||||
foreach (var bb in validBlocks) {
|
||||
Visit(bb);
|
||||
finalList.AddRange(sorted);
|
||||
sorted.Clear();
|
||||
}
|
||||
|
||||
if (stack.Count > 0)
|
||||
throw new ApplicationException("Stack isn't empty");
|
||||
|
||||
if (firstBlock != null)
|
||||
finalList.Insert(0, firstBlock);
|
||||
else if (validBlocks[0] != finalList[0]) {
|
||||
// Make sure the original first block is first
|
||||
int index = finalList.IndexOf(validBlocks[0]);
|
||||
finalList.RemoveAt(index);
|
||||
finalList.Insert(0, validBlocks[0]);
|
||||
}
|
||||
return finalList;
|
||||
}
|
||||
|
||||
void Visit(BaseBlock bb) {
|
||||
var info = GetInfo(bb);
|
||||
if (info == null)
|
||||
return;
|
||||
if (info.baseBlock == firstBlock)
|
||||
return;
|
||||
if (info.Visited())
|
||||
return;
|
||||
Visit(info);
|
||||
}
|
||||
|
||||
BlockInfo GetInfo(BaseBlock baseBlock) {
|
||||
baseBlock = scopeBlock.ToChild(baseBlock);
|
||||
if (baseBlock == null)
|
||||
return null;
|
||||
blockToInfo.TryGetValue(baseBlock, out var info);
|
||||
return info;
|
||||
}
|
||||
|
||||
List<BaseBlock> GetTargets(BaseBlock baseBlock) {
|
||||
var list = new List<BaseBlock>();
|
||||
|
||||
if (baseBlock is Block block)
|
||||
AddTargets(list, block.GetTargets());
|
||||
else if (baseBlock is TryBlock)
|
||||
AddTargets(list, (TryBlock)baseBlock);
|
||||
else if (baseBlock is TryHandlerBlock)
|
||||
AddTargets(list, (TryHandlerBlock)baseBlock);
|
||||
else
|
||||
AddTargets(list, (ScopeBlock)baseBlock);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void AddTargets(List<BaseBlock> dest, TryBlock tryBlock) {
|
||||
AddTargets(dest, (ScopeBlock)tryBlock);
|
||||
foreach (var tryHandlerBlock in tryBlock.TryHandlerBlocks) {
|
||||
dest.Add(tryHandlerBlock);
|
||||
AddTargets(dest, tryHandlerBlock);
|
||||
}
|
||||
}
|
||||
|
||||
void AddTargets(List<BaseBlock> dest, TryHandlerBlock tryHandlerBlock) {
|
||||
AddTargets(dest, (ScopeBlock)tryHandlerBlock);
|
||||
|
||||
dest.Add(tryHandlerBlock.FilterHandlerBlock);
|
||||
AddTargets(dest, tryHandlerBlock.FilterHandlerBlock);
|
||||
|
||||
dest.Add(tryHandlerBlock.HandlerBlock);
|
||||
AddTargets(dest, tryHandlerBlock.HandlerBlock);
|
||||
}
|
||||
|
||||
void AddTargets(List<BaseBlock> dest, ScopeBlock scopeBlock) {
|
||||
foreach (var block in scopeBlock.GetAllBlocks())
|
||||
AddTargets(dest, block.GetTargets());
|
||||
}
|
||||
|
||||
void AddTargets(List<BaseBlock> dest, IEnumerable<Block> source) {
|
||||
var list = new List<Block>(source);
|
||||
list.Reverse();
|
||||
foreach (var block in list)
|
||||
dest.Add(block);
|
||||
}
|
||||
|
||||
struct VisitState {
|
||||
public BlockInfo Info;
|
||||
public List<BaseBlock> Targets;
|
||||
public int TargetIndex;
|
||||
public BlockInfo TargetInfo;
|
||||
public VisitState(BlockInfo info) {
|
||||
Info = info;
|
||||
Targets = null;
|
||||
TargetIndex = 0;
|
||||
TargetInfo = null;
|
||||
}
|
||||
}
|
||||
Stack<VisitState> visitStateStack = new Stack<VisitState>();
|
||||
void Visit(BlockInfo info) {
|
||||
// This method used to be recursive but to prevent stack overflows,
|
||||
// it's not recursive anymore.
|
||||
|
||||
var state = new VisitState(info);
|
||||
recursive_call:
|
||||
if (state.Info.baseBlock == firstBlock)
|
||||
throw new ApplicationException("Can't visit firstBlock");
|
||||
stack.Push(state.Info);
|
||||
state.Info.onStack = true;
|
||||
state.Info.dfsNumber = dfsNumber;
|
||||
state.Info.low = dfsNumber;
|
||||
dfsNumber++;
|
||||
|
||||
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 (state.TargetInfo.baseBlock == firstBlock)
|
||||
continue;
|
||||
|
||||
if (!state.TargetInfo.Visited()) {
|
||||
visitStateStack.Push(state);
|
||||
state = new VisitState(state.TargetInfo);
|
||||
goto recursive_call;
|
||||
}
|
||||
else if (state.TargetInfo.onStack)
|
||||
state.Info.low = Math.Min(state.Info.low, state.TargetInfo.dfsNumber);
|
||||
}
|
||||
|
||||
if (state.Info.low != state.Info.dfsNumber)
|
||||
goto return_from_method;
|
||||
var sccBlocks = new List<BaseBlock>();
|
||||
while (true) {
|
||||
var poppedInfo = stack.Pop();
|
||||
poppedInfo.onStack = false;
|
||||
sccBlocks.Add(poppedInfo.baseBlock);
|
||||
if (ReferenceEquals(state.Info, poppedInfo))
|
||||
break;
|
||||
}
|
||||
if (sccBlocks.Count > 1) {
|
||||
sccBlocks.Reverse();
|
||||
var result = new Sorter(scopeBlock, sccBlocks, true).Sort();
|
||||
SortLoopBlock(result);
|
||||
sorted.InsertRange(0, result);
|
||||
}
|
||||
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<BaseBlock> list) {
|
||||
// Some popular decompilers sometimes produce bad output unless the loop condition
|
||||
// checker block is at the end of the loop. Eg., they may use a while loop when
|
||||
// it's really a for/foreach loop.
|
||||
|
||||
var loopStart = GetLoopStartBlock(list);
|
||||
if (loopStart == null)
|
||||
return;
|
||||
|
||||
if (!list.Remove(loopStart))
|
||||
throw new ApplicationException("Could not remove block");
|
||||
list.Add(loopStart);
|
||||
}
|
||||
|
||||
Block GetLoopStartBlock(List<BaseBlock> list) {
|
||||
var loopBlocks = new Dictionary<Block, bool>(list.Count);
|
||||
foreach (var bb in list) {
|
||||
if (bb is Block block)
|
||||
loopBlocks[block] = true;
|
||||
}
|
||||
|
||||
var targetBlocks = new Dictionary<Block, int>();
|
||||
foreach (var bb in list) {
|
||||
var block = bb as Block;
|
||||
if (block == null)
|
||||
continue;
|
||||
foreach (var source in block.Sources) {
|
||||
if (loopBlocks.ContainsKey(source))
|
||||
continue;
|
||||
targetBlocks.TryGetValue(block, out int count);
|
||||
targetBlocks[block] = count + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int max = -1;
|
||||
Block loopStart = null;
|
||||
foreach (var kv in targetBlocks) {
|
||||
if (kv.Value <= max)
|
||||
continue;
|
||||
max = kv.Value;
|
||||
loopStart = kv.Key;
|
||||
}
|
||||
|
||||
return loopStart;
|
||||
}
|
||||
}
|
||||
|
||||
public BlocksSorter(ScopeBlock scopeBlock) => this.scopeBlock = scopeBlock;
|
||||
|
||||
public List<BaseBlock> Sort() {
|
||||
var sorted = new Sorter(scopeBlock, scopeBlock.BaseBlocks, false).Sort();
|
||||
return new ForwardScanOrder(scopeBlock, sorted).Fix();
|
||||
}
|
||||
}
|
||||
}
|
320
de4dot/de4dot.blocks/CodeGenerator.cs
Normal file
320
de4dot/de4dot.blocks/CodeGenerator.cs
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
class CodeGenerator {
|
||||
MethodBlocks methodBlocks;
|
||||
List<Block> blocks = new List<Block>();
|
||||
Stack<BlockState> stateStack = new Stack<BlockState>();
|
||||
List<ExceptionInfo> exceptions = new List<ExceptionInfo>();
|
||||
Dictionary<BaseBlock, bool> visited = new Dictionary<BaseBlock, bool>();
|
||||
List<BaseBlock> notProcessedYet = new List<BaseBlock>();
|
||||
|
||||
class BlockState {
|
||||
public ScopeBlock scopeBlock;
|
||||
public BlockState(ScopeBlock scopeBlock) => this.scopeBlock = scopeBlock;
|
||||
}
|
||||
|
||||
class ExceptionInfo {
|
||||
public int tryStart;
|
||||
public int tryEnd;
|
||||
public int filterStart;
|
||||
public int handlerStart;
|
||||
public int handlerEnd;
|
||||
public ITypeDefOrRef catchType;
|
||||
public ExceptionHandlerType handlerType;
|
||||
public ExceptionInfo(int tryStart, int tryEnd, int filterStart,
|
||||
int handlerStart, int handlerEnd, ITypeDefOrRef catchType,
|
||||
ExceptionHandlerType handlerType) {
|
||||
if (tryStart > tryEnd || filterStart > handlerStart ||
|
||||
tryStart < 0 || tryEnd < 0 || filterStart < 0 || handlerStart < 0 || handlerEnd < 0)
|
||||
throw new ApplicationException("Invalid start/end/filter/handler indexes");
|
||||
this.tryStart = tryStart;
|
||||
this.tryEnd = tryEnd;
|
||||
this.filterStart = filterStart == handlerStart ? -1 : filterStart;
|
||||
this.handlerStart = handlerStart;
|
||||
this.handlerEnd = handlerEnd;
|
||||
this.catchType = catchType;
|
||||
this.handlerType = handlerType;
|
||||
}
|
||||
}
|
||||
|
||||
public CodeGenerator(MethodBlocks methodBlocks) => this.methodBlocks = methodBlocks;
|
||||
|
||||
public void GetCode(out IList<Instruction> allInstructions, out IList<ExceptionHandler> allExceptionHandlers) {
|
||||
FixEmptyBlocks();
|
||||
LayOutBlocks();
|
||||
SortExceptions();
|
||||
LayOutInstructions(out allInstructions, out allExceptionHandlers);
|
||||
|
||||
allInstructions.SimplifyBranches();
|
||||
allInstructions.OptimizeBranches();
|
||||
allInstructions.UpdateInstructionOffsets();
|
||||
}
|
||||
|
||||
class BlockInfo {
|
||||
public int start;
|
||||
public int end;
|
||||
public BlockInfo(int start, int end) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
}
|
||||
}
|
||||
|
||||
void LayOutInstructions(out IList<Instruction> allInstructions, out IList<ExceptionHandler> allExceptionHandlers) {
|
||||
allInstructions = new List<Instruction>();
|
||||
allExceptionHandlers = new List<ExceptionHandler>();
|
||||
|
||||
var blockInfos = new List<BlockInfo>();
|
||||
for (int i = 0; i < blocks.Count; i++) {
|
||||
var block = blocks[i];
|
||||
|
||||
int startIndex = allInstructions.Count;
|
||||
for (int j = 0; j < block.Instructions.Count - 1; j++)
|
||||
allInstructions.Add(block.Instructions[j].Instruction);
|
||||
|
||||
if (block.Targets != null) {
|
||||
var targets = new List<Instr>();
|
||||
foreach (var target in block.Targets)
|
||||
targets.Add(target.FirstInstr);
|
||||
block.LastInstr.UpdateTargets(targets);
|
||||
}
|
||||
allInstructions.Add(block.LastInstr.Instruction);
|
||||
|
||||
var next = i + 1 < blocks.Count ? blocks[i + 1] : null;
|
||||
|
||||
// If eg. ble next, then change it to bgt XYZ and fall through to next.
|
||||
if (block.Targets != null && block.CanFlipConditionalBranch() && block.Targets[0] == next) {
|
||||
block.FlipConditionalBranch();
|
||||
block.LastInstr.UpdateTargets(new List<Instr> { block.Targets[0].FirstInstr });
|
||||
}
|
||||
else if (block.FallThrough != null && block.FallThrough != next) {
|
||||
var instr = new Instr(OpCodes.Br.ToInstruction(block.FallThrough.FirstInstr.Instruction));
|
||||
instr.UpdateTargets(new List<Instr> { block.FallThrough.FirstInstr });
|
||||
allInstructions.Add(instr.Instruction);
|
||||
}
|
||||
|
||||
int endIndex = allInstructions.Count - 1;
|
||||
|
||||
blockInfos.Add(new BlockInfo(startIndex, endIndex));
|
||||
}
|
||||
|
||||
foreach (var ex in exceptions) {
|
||||
var tryStart = GetBlockInfo(blockInfos, ex.tryStart).start;
|
||||
var tryEnd = GetBlockInfo(blockInfos, ex.tryEnd).end;
|
||||
var filterStart = ex.filterStart == -1 ? -1 : GetBlockInfo(blockInfos, ex.filterStart).start;
|
||||
var handlerStart = GetBlockInfo(blockInfos, ex.handlerStart).start;
|
||||
var handlerEnd = GetBlockInfo(blockInfos, ex.handlerEnd).end;
|
||||
|
||||
var eh = new ExceptionHandler(ex.handlerType);
|
||||
eh.CatchType = ex.catchType;
|
||||
eh.TryStart = GetInstruction(allInstructions, tryStart);
|
||||
eh.TryEnd = GetInstruction(allInstructions, tryEnd + 1);
|
||||
eh.FilterStart = filterStart == -1 ? null : GetInstruction(allInstructions, filterStart);
|
||||
eh.HandlerStart = GetInstruction(allInstructions, handlerStart);
|
||||
eh.HandlerEnd = GetInstruction(allInstructions, handlerEnd + 1);
|
||||
|
||||
allExceptionHandlers.Add(eh);
|
||||
}
|
||||
}
|
||||
|
||||
static BlockInfo GetBlockInfo(List<BlockInfo> blockInfos, int index) {
|
||||
if (index >= blockInfos.Count)
|
||||
index = blockInfos.Count - 1;
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
return blockInfos[index];
|
||||
}
|
||||
|
||||
static Instruction GetInstruction(IList<Instruction> allInstructions, int i) {
|
||||
if (i < allInstructions.Count)
|
||||
return allInstructions[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
void SortExceptions() =>
|
||||
exceptions.Sort((a, b) => {
|
||||
// Make sure nested try blocks are sorted before the outer try block.
|
||||
if (a.tryStart > b.tryStart) return -1; // a could be nested, but b is not
|
||||
if (a.tryStart < b.tryStart) return 1; // b could be nested, but a is not
|
||||
// same tryStart
|
||||
if (a.tryEnd < b.tryEnd) return -1; // a is nested
|
||||
if (a.tryEnd > b.tryEnd) return 1; // b is nested
|
||||
// same tryEnd (they share try block)
|
||||
|
||||
int ai = a.filterStart == -1 ? a.handlerStart : a.filterStart;
|
||||
int bi = b.filterStart == -1 ? b.handlerStart : b.filterStart;
|
||||
if (ai < bi) return -1;
|
||||
if (ai > bi) return 1;
|
||||
// same start
|
||||
|
||||
// if we're here, they should be identical since handlers can't overlap
|
||||
// when they share the try block!
|
||||
if (a.handlerEnd < b.handlerEnd) return -1;
|
||||
if (a.handlerEnd > b.handlerEnd) return 1;
|
||||
// same handler end
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
void FixEmptyBlocks() {
|
||||
foreach (var block in methodBlocks.GetAllBlocks()) {
|
||||
if (block.Instructions.Count == 0) {
|
||||
block.Instructions.Add(new Instr(OpCodes.Nop.ToInstruction()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write all blocks to the blocks list
|
||||
void LayOutBlocks() {
|
||||
if (methodBlocks.BaseBlocks.Count == 0)
|
||||
return;
|
||||
|
||||
stateStack.Push(new BlockState(methodBlocks));
|
||||
ProcessBaseBlocks(methodBlocks.BaseBlocks, (block) => {
|
||||
return block.LastInstr.OpCode == OpCodes.Ret;
|
||||
});
|
||||
|
||||
stateStack.Pop();
|
||||
|
||||
foreach (var bb in notProcessedYet) {
|
||||
visited.TryGetValue(bb, out bool wasVisited);
|
||||
if (!wasVisited)
|
||||
throw new ApplicationException("A block wasn't processed");
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessBaseBlocks(List<BaseBlock> lb, Func<Block, bool> placeLast) {
|
||||
var bbs = new List<BaseBlock>();
|
||||
int lastIndex = -1;
|
||||
for (int i = 0; i < lb.Count; i++) {
|
||||
var bb = lb[i];
|
||||
if (bb is Block block && placeLast(block))
|
||||
lastIndex = i;
|
||||
bbs.Add(bb);
|
||||
}
|
||||
if (lastIndex != -1) {
|
||||
var block = (Block)bbs[lastIndex];
|
||||
bbs.RemoveAt(lastIndex);
|
||||
bbs.Add(block);
|
||||
}
|
||||
foreach (var bb in bbs)
|
||||
DoBaseBlock(bb);
|
||||
}
|
||||
|
||||
// Returns the BaseBlock's ScopeBlock. The return value is either current ScopeBlock,
|
||||
// the ScopeBlock one step below current (current one's child), or null.
|
||||
ScopeBlock GetScopeBlock(BaseBlock bb) {
|
||||
var current = stateStack.Peek();
|
||||
|
||||
if (current.scopeBlock.IsOurBaseBlock(bb))
|
||||
return current.scopeBlock;
|
||||
return (ScopeBlock)current.scopeBlock.ToChild(bb);
|
||||
}
|
||||
|
||||
void DoBaseBlock(BaseBlock bb) {
|
||||
var current = stateStack.Peek();
|
||||
var newOne = GetScopeBlock(bb);
|
||||
if (newOne == null)
|
||||
return; // Not a BaseBlock somewhere inside this ScopeBlock
|
||||
if (newOne != current.scopeBlock)
|
||||
bb = newOne;
|
||||
|
||||
if (!visited.TryGetValue(bb, out bool hasVisited))
|
||||
visited[bb] = hasVisited = false;
|
||||
if (hasVisited)
|
||||
return;
|
||||
visited[bb] = true;
|
||||
|
||||
if (bb is Block)
|
||||
DoBlock(bb as Block);
|
||||
else if (bb is TryBlock)
|
||||
DoTryBlock(bb as TryBlock);
|
||||
else if (bb is FilterHandlerBlock)
|
||||
DoFilterHandlerBlock(bb as FilterHandlerBlock);
|
||||
else if (bb is HandlerBlock)
|
||||
DoHandlerBlock(bb as HandlerBlock);
|
||||
else if (bb is TryHandlerBlock) {
|
||||
// The try handler block is usually after the try block, but sometimes it isn't...
|
||||
// Handle that case here.
|
||||
visited.Remove(bb);
|
||||
notProcessedYet.Add(bb);
|
||||
}
|
||||
else
|
||||
throw new ApplicationException("Invalid block found");
|
||||
}
|
||||
|
||||
void DoBlock(Block block) => blocks.Add(block);
|
||||
|
||||
void DoTryBlock(TryBlock tryBlock) {
|
||||
var tryStart = blocks.Count;
|
||||
stateStack.Push(new BlockState(tryBlock));
|
||||
ProcessBaseBlocks(tryBlock.BaseBlocks, (block) => {
|
||||
return block.LastInstr.OpCode == OpCodes.Leave ||
|
||||
block.LastInstr.OpCode == OpCodes.Leave_S;
|
||||
});
|
||||
stateStack.Pop();
|
||||
var tryEnd = blocks.Count - 1;
|
||||
|
||||
if (tryBlock.TryHandlerBlocks.Count == 0)
|
||||
throw new ApplicationException("No handler blocks");
|
||||
|
||||
foreach (var handlerBlock in tryBlock.TryHandlerBlocks) {
|
||||
visited[handlerBlock] = true;
|
||||
|
||||
stateStack.Push(new BlockState(handlerBlock));
|
||||
|
||||
var filterStart = blocks.Count;
|
||||
if (handlerBlock.FilterHandlerBlock.BaseBlocks != null)
|
||||
DoBaseBlock(handlerBlock.FilterHandlerBlock);
|
||||
|
||||
var handlerStart = blocks.Count;
|
||||
DoBaseBlock(handlerBlock.HandlerBlock);
|
||||
var handlerEnd = blocks.Count - 1;
|
||||
|
||||
exceptions.Add(new ExceptionInfo(tryStart, tryEnd, filterStart, handlerStart, handlerEnd, handlerBlock.CatchType, handlerBlock.HandlerType));
|
||||
|
||||
stateStack.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
void DoFilterHandlerBlock(FilterHandlerBlock filterHandlerBlock) {
|
||||
stateStack.Push(new BlockState(filterHandlerBlock));
|
||||
ProcessBaseBlocks(filterHandlerBlock.BaseBlocks, (block) => {
|
||||
return block.LastInstr.OpCode == OpCodes.Endfilter; // MUST end with endfilter!
|
||||
});
|
||||
stateStack.Pop();
|
||||
}
|
||||
|
||||
void DoHandlerBlock(HandlerBlock handlerBlock) {
|
||||
stateStack.Push(new BlockState(handlerBlock));
|
||||
ProcessBaseBlocks(handlerBlock.BaseBlocks, (block) => {
|
||||
return block.LastInstr.OpCode == OpCodes.Endfinally ||
|
||||
block.LastInstr.OpCode == OpCodes.Leave ||
|
||||
block.LastInstr.OpCode == OpCodes.Leave_S;
|
||||
});
|
||||
stateStack.Pop();
|
||||
}
|
||||
}
|
||||
}
|
133
de4dot/de4dot.blocks/DeadBlocksRemover.cs
Normal file
133
de4dot/de4dot.blocks/DeadBlocksRemover.cs
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
class DeadBlocksRemover {
|
||||
MethodBlocks methodBlocks;
|
||||
Dictionary<BaseBlock, bool> checkedBaseBlocks = new Dictionary<BaseBlock, bool>();
|
||||
Dictionary<ScopeBlock, bool> checkedScopeBlocks = new Dictionary<ScopeBlock, bool>();
|
||||
Stack<BaseBlock> baseBlocksToCheck = new Stack<BaseBlock>();
|
||||
Stack<ScopeBlock> scopeBlocksToCheck = new Stack<ScopeBlock>();
|
||||
|
||||
public DeadBlocksRemover(MethodBlocks methodBlocks) => this.methodBlocks = methodBlocks;
|
||||
|
||||
public int Remove() {
|
||||
AddScopeBlock(methodBlocks);
|
||||
ProcessAll();
|
||||
return RemoveDeadBlocks();
|
||||
}
|
||||
|
||||
class ScopeBlockInfo {
|
||||
public ScopeBlock scopeBlock;
|
||||
public IList<BaseBlock> deadBlocks = new List<BaseBlock>();
|
||||
public ScopeBlockInfo(ScopeBlock scopeBlock) => this.scopeBlock = scopeBlock;
|
||||
}
|
||||
|
||||
int RemoveDeadBlocks() {
|
||||
int numDeadBlocks = 0;
|
||||
|
||||
var infos = new Dictionary<ScopeBlock, ScopeBlockInfo>();
|
||||
var deadBlocksDict = new Dictionary<BaseBlock, bool>();
|
||||
foreach (var baseBlock in FindDeadBlocks()) {
|
||||
deadBlocksDict[baseBlock] = true;
|
||||
var parent = baseBlock.Parent;
|
||||
if (!infos.TryGetValue(parent, out var info))
|
||||
infos[parent] = info = new ScopeBlockInfo(parent);
|
||||
info.deadBlocks.Add(baseBlock);
|
||||
numDeadBlocks++;
|
||||
}
|
||||
|
||||
foreach (var info in infos.Values)
|
||||
info.scopeBlock.RemoveAllDeadBlocks(info.deadBlocks, deadBlocksDict);
|
||||
|
||||
return numDeadBlocks;
|
||||
}
|
||||
|
||||
IList<BaseBlock> FindDeadBlocks() {
|
||||
var deadBlocks = new List<BaseBlock>();
|
||||
|
||||
foreach (var bb in methodBlocks.GetAllBaseBlocks()) {
|
||||
if (!checkedBaseBlocks.ContainsKey(bb))
|
||||
deadBlocks.Add(bb);
|
||||
}
|
||||
|
||||
return deadBlocks;
|
||||
}
|
||||
|
||||
void AddScopeBlock(ScopeBlock scopeBlock) => scopeBlocksToCheck.Push(scopeBlock);
|
||||
|
||||
void ProcessAll() {
|
||||
bool didSomething;
|
||||
do {
|
||||
didSomething = false;
|
||||
while (baseBlocksToCheck.Count > 0) {
|
||||
ProcessBaseBlock(baseBlocksToCheck.Pop());
|
||||
didSomething = true;
|
||||
}
|
||||
while (scopeBlocksToCheck.Count > 0) {
|
||||
ProcessScopeBlock(scopeBlocksToCheck.Pop());
|
||||
didSomething = true;
|
||||
}
|
||||
} while (didSomething);
|
||||
}
|
||||
|
||||
void ProcessBaseBlock(BaseBlock baseBlock) {
|
||||
if (baseBlock == null || checkedBaseBlocks.ContainsKey(baseBlock))
|
||||
return;
|
||||
checkedBaseBlocks[baseBlock] = true;
|
||||
|
||||
if (baseBlock is Block block) {
|
||||
foreach (var block2 in block.GetTargets())
|
||||
AddBaseBlock(block2);
|
||||
}
|
||||
else if (baseBlock is ScopeBlock scopeBlock) {
|
||||
AddScopeBlock(scopeBlock);
|
||||
if (scopeBlock.BaseBlocks != null && scopeBlock.BaseBlocks.Count > 0)
|
||||
AddBaseBlock(scopeBlock.BaseBlocks[0]);
|
||||
}
|
||||
else
|
||||
throw new ApplicationException($"Unknown BaseBlock type {baseBlock.GetType()}");
|
||||
}
|
||||
|
||||
// Add a block to be processed later, including all its enclosing ScopeBlocks.
|
||||
void AddBaseBlock(BaseBlock baseBlock) {
|
||||
for (var bb = baseBlock; bb != null; bb = bb.Parent)
|
||||
baseBlocksToCheck.Push(bb);
|
||||
}
|
||||
|
||||
void ProcessScopeBlock(ScopeBlock scopeBlock) {
|
||||
if (scopeBlock == null || checkedScopeBlocks.ContainsKey(scopeBlock))
|
||||
return;
|
||||
checkedScopeBlocks[scopeBlock] = true;
|
||||
AddBaseBlock(scopeBlock);
|
||||
|
||||
if (scopeBlock is TryBlock tryBlock) {
|
||||
foreach (var handler in tryBlock.TryHandlerBlocks)
|
||||
AddScopeBlock(handler);
|
||||
}
|
||||
else if (scopeBlock is TryHandlerBlock tryHandlerBlock) {
|
||||
AddScopeBlock(tryHandlerBlock.FilterHandlerBlock);
|
||||
AddScopeBlock(tryHandlerBlock.HandlerBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
701
de4dot/de4dot.blocks/DotNetUtils.cs
Normal file
701
de4dot/de4dot.blocks/DotNetUtils.cs
Normal file
@ -0,0 +1,701 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public enum FrameworkType {
|
||||
Unknown,
|
||||
Desktop,
|
||||
Silverlight, // and WindowsPhone, XNA Xbox360
|
||||
CompactFramework,
|
||||
XNA,
|
||||
Zune,
|
||||
}
|
||||
|
||||
public class CallCounter {
|
||||
Dictionary<IMethod, int> calls = new Dictionary<IMethod, int>(MethodEqualityComparer.CompareDeclaringTypes);
|
||||
|
||||
public void Add(IMethod calledMethod) {
|
||||
calls.TryGetValue(calledMethod, out int count);
|
||||
calls[calledMethod] = count + 1;
|
||||
}
|
||||
|
||||
public IMethod Most() => Most(out int numCalls);
|
||||
|
||||
public IMethod Most(out int numCalls) {
|
||||
IMethod method = null;
|
||||
int callCount = 0;
|
||||
foreach (var key in calls.Keys) {
|
||||
if (calls[key] > callCount) {
|
||||
callCount = calls[key];
|
||||
method = key;
|
||||
}
|
||||
}
|
||||
numCalls = callCount;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DotNetUtils {
|
||||
public static TypeDef GetModuleType(ModuleDef module) => module.GlobalType;
|
||||
public static MethodDef GetModuleTypeCctor(ModuleDef module) => module.GlobalType.FindStaticConstructor();
|
||||
|
||||
public static bool IsEmpty(MethodDef method) {
|
||||
if (method.Body == null)
|
||||
return false;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
var code = instr.OpCode.Code;
|
||||
if (code != Code.Nop && code != Code.Ret)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool IsEmptyObfuscated(MethodDef method) {
|
||||
if (method.Body == null)
|
||||
return false;
|
||||
int index = 0;
|
||||
var instr = GetInstruction(method.Body.Instructions, ref index);
|
||||
if (instr == null || instr.OpCode.Code != Code.Ret)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static FieldDef FindFieldType(TypeDef typeDef, string typeName, bool isStatic) {
|
||||
if (typeDef == null)
|
||||
return null;
|
||||
foreach (var field in typeDef.Fields) {
|
||||
if (field.IsStatic == isStatic && field.FieldSig.GetFieldType().GetFullName() == typeName)
|
||||
return field;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodDef> FindMethods(IEnumerable<MethodDef> methods, string returnType, string[] argsTypes) =>
|
||||
FindMethods(methods, returnType, argsTypes, true);
|
||||
|
||||
public static IEnumerable<MethodDef> FindMethods(IEnumerable<MethodDef> methods, string returnType, string[] argsTypes, bool isStatic) {
|
||||
foreach (var method in methods) {
|
||||
var sig = method.MethodSig;
|
||||
if (sig == null || !method.HasBody || !sig.IsDefault)
|
||||
continue;
|
||||
if (method.IsStatic != isStatic || sig.Params.Count != argsTypes.Length)
|
||||
continue;
|
||||
if (sig.GenParamCount > 0)
|
||||
continue;
|
||||
if (sig.RetType.GetFullName() != returnType)
|
||||
continue;
|
||||
for (int i = 0; i < argsTypes.Length; i++) {
|
||||
if (sig.Params[i].GetFullName() != argsTypes[i])
|
||||
goto next;
|
||||
}
|
||||
yield return method;
|
||||
next: ;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDelegate(IType type) {
|
||||
if (type == null)
|
||||
return false;
|
||||
var fn = type.FullName;
|
||||
return fn == "System.Delegate" || fn == "System.MulticastDelegate";
|
||||
}
|
||||
|
||||
public static bool DerivesFromDelegate(TypeDef type) => type != null && IsDelegate(type.BaseType);
|
||||
|
||||
public static bool IsMethod(IMethod method, string returnType, string parameters) =>
|
||||
method != null && method.FullName == returnType + " " + method.DeclaringType.FullName + "::" + method.Name + parameters;
|
||||
|
||||
public static string GetDllName(string dll) {
|
||||
if (dll.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
||||
return dll.Substring(0, dll.Length - 4);
|
||||
return dll;
|
||||
}
|
||||
|
||||
public static bool HasPinvokeMethod(TypeDef type, string methodName) =>
|
||||
GetPInvokeMethod(type, methodName) != null;
|
||||
|
||||
public static MethodDef GetPInvokeMethod(TypeDef type, string methodName) {
|
||||
if (type == null)
|
||||
return null;
|
||||
UTF8String mname = methodName;
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.ImplMap == null)
|
||||
continue;
|
||||
if (UTF8String.Equals(method.ImplMap.Name, mname))
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MethodDef GetPInvokeMethod(TypeDef type, string dll, string funcName) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (IsPinvokeMethod(method, dll, funcName))
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool IsPinvokeMethod(MethodDef method, string dll, string funcName) {
|
||||
if (method == null)
|
||||
return false;
|
||||
if (method.ImplMap == null)
|
||||
return false;
|
||||
return method.ImplMap.IsPinvokeMethod(dll, funcName);
|
||||
}
|
||||
|
||||
public static MethodDef GetMethod(ModuleDefMD module, IMethod method) {
|
||||
if (method == null)
|
||||
return null;
|
||||
return GetMethod(module, method, method.DeclaringType);
|
||||
}
|
||||
|
||||
public static MethodDef GetMethod2(ModuleDefMD module, IMethod method) {
|
||||
if (method == null)
|
||||
return null;
|
||||
if (method is MethodDef)
|
||||
return (MethodDef)method;
|
||||
var git = method.DeclaringType.TryGetGenericInstSig();
|
||||
var dt = git == null ? method.DeclaringType : git.GenericType.TypeDefOrRef;
|
||||
return GetMethod(module, method, dt);
|
||||
}
|
||||
|
||||
static MethodDef GetMethod(ModuleDefMD module, IMethod method, ITypeDefOrRef declaringType) {
|
||||
if (method == null)
|
||||
return null;
|
||||
if (method is MethodDef)
|
||||
return (MethodDef)method;
|
||||
return GetMethod(GetType(module, declaringType), method);
|
||||
}
|
||||
|
||||
public static MethodDef GetMethod(TypeDef type, string returnType, string parameters) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (IsMethod(method, returnType, parameters))
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MethodDef GetMethod2(ModuleDef module, IMethod method) {
|
||||
if (method == null)
|
||||
return null;
|
||||
return GetMethod(module, method, method.DeclaringType.ScopeType);
|
||||
}
|
||||
|
||||
public static TypeDef GetType(ModuleDef module, TypeSig type) {
|
||||
type = type.RemovePinnedAndModifiers();
|
||||
var tdr = type as TypeDefOrRefSig;
|
||||
if (tdr == null)
|
||||
return null;
|
||||
return GetType(module, tdr.TypeDefOrRef);
|
||||
}
|
||||
|
||||
public static TypeDef GetType(ModuleDef module, ITypeDefOrRef type) {
|
||||
var td = type as TypeDef;
|
||||
if (td == null) {
|
||||
if (type is TypeRef tr) {
|
||||
var trAsm = tr.DefinitionAssembly;
|
||||
var modAsm = module.Assembly;
|
||||
if (trAsm != null && modAsm != null && trAsm.Name == modAsm.Name)
|
||||
td = tr.Resolve();
|
||||
}
|
||||
}
|
||||
return td != null && td.Module == module ? td : null;
|
||||
}
|
||||
|
||||
static MethodDef GetMethod(ModuleDef module, IMethod method, ITypeDefOrRef declaringType) {
|
||||
if (method == null)
|
||||
return null;
|
||||
if (method is MethodDef)
|
||||
return (MethodDef)method;
|
||||
return GetMethod(GetType(module, declaringType), method);
|
||||
}
|
||||
|
||||
public static MethodDef GetMethod(TypeDef type, IMethod methodRef) {
|
||||
if (type == null || methodRef == null)
|
||||
return null;
|
||||
if (methodRef is MethodDef)
|
||||
return (MethodDef)methodRef;
|
||||
return type.FindMethod(methodRef.Name, methodRef.MethodSig);
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodDef> GetNormalMethods(TypeDef type) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.HasImplMap)
|
||||
continue;
|
||||
if (method.IsConstructor)
|
||||
continue;
|
||||
|
||||
yield return method;
|
||||
}
|
||||
}
|
||||
|
||||
public static FieldDef GetField(ModuleDef module, IField field) {
|
||||
if (field == null)
|
||||
return null;
|
||||
if (field is FieldDef)
|
||||
return (FieldDef)field;
|
||||
return GetField(GetType(module, field.DeclaringType), field);
|
||||
}
|
||||
|
||||
public static FieldDef GetField(TypeDef type, IField fieldRef) {
|
||||
if (type == null || fieldRef == null)
|
||||
return null;
|
||||
if (fieldRef is FieldDef)
|
||||
return (FieldDef)fieldRef;
|
||||
return type.FindField(fieldRef.Name, fieldRef.FieldSig);
|
||||
}
|
||||
|
||||
public static FieldDef GetField(TypeDef type, string typeFullName) {
|
||||
if (type == null)
|
||||
return null;
|
||||
foreach (var field in type.Fields) {
|
||||
if (field.FieldSig.GetFieldType().GetFullName() == typeFullName)
|
||||
return field;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<IMethod> GetMethodCalls(MethodDef method) {
|
||||
var list = new List<IMethod>();
|
||||
if (method.HasBody) {
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.Operand is IMethod calledMethod)
|
||||
list.Add(calledMethod);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static bool HasString(MethodDef method, string s) {
|
||||
if (method == null || method.Body == null)
|
||||
return false;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code == Code.Ldstr && (string)instr.Operand == s)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IList<string> GetCodeStrings(MethodDef method) {
|
||||
var strings = new List<string>();
|
||||
if (method != null && method.Body != null) {
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code == Code.Ldstr)
|
||||
strings.Add((string)instr.Operand);
|
||||
}
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
public static Resource GetResource(ModuleDef module, string name) =>
|
||||
GetResource(module, new List<string> { name });
|
||||
|
||||
public static Resource GetResource(ModuleDef module, IEnumerable<string> strings) {
|
||||
if (!module.HasResources)
|
||||
return null;
|
||||
|
||||
var resources = module.Resources;
|
||||
foreach (var tmp in strings) {
|
||||
var resourceName = RemoveFromNullChar(tmp);
|
||||
if (resourceName == null)
|
||||
continue;
|
||||
UTF8String name = resourceName;
|
||||
foreach (var resource in resources) {
|
||||
if (UTF8String.Equals(resource.Name, name))
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static string RemoveFromNullChar(string s) {
|
||||
int index = s.IndexOf((char)0);
|
||||
if (index < 0)
|
||||
return s;
|
||||
return s.Substring(0, index);
|
||||
}
|
||||
|
||||
// Copies most things but not everything
|
||||
public static MethodDef Clone(MethodDef method) {
|
||||
var newMethod = new MethodDefUser(method.Name, method.MethodSig, method.ImplAttributes, method.Attributes);
|
||||
newMethod.Rid = method.Rid;
|
||||
newMethod.DeclaringType2 = method.DeclaringType;
|
||||
foreach (var pd in method.ParamDefs)
|
||||
newMethod.ParamDefs.Add(new ParamDefUser(pd.Name, pd.Sequence, pd.Attributes));
|
||||
foreach (var gp in method.GenericParameters) {
|
||||
var newGp = new GenericParamUser(gp.Number, gp.Flags, gp.Name);
|
||||
foreach (var gpc in gp.GenericParamConstraints)
|
||||
newGp.GenericParamConstraints.Add(new GenericParamConstraintUser(gpc.Constraint));
|
||||
newMethod.GenericParameters.Add(newGp);
|
||||
}
|
||||
newMethod.Body = new CilBody();
|
||||
CopyBodyFromTo(method, newMethod);
|
||||
return newMethod;
|
||||
}
|
||||
|
||||
public static void CopyBody(MethodDef method, out IList<Instruction> instructions, out IList<ExceptionHandler> exceptionHandlers) {
|
||||
if (method == null || !method.HasBody) {
|
||||
instructions = new List<Instruction>();
|
||||
exceptionHandlers = new List<ExceptionHandler>();
|
||||
return;
|
||||
}
|
||||
|
||||
var oldInstrs = method.Body.Instructions;
|
||||
var oldExHandlers = method.Body.ExceptionHandlers;
|
||||
instructions = new List<Instruction>(oldInstrs.Count);
|
||||
exceptionHandlers = new List<ExceptionHandler>(oldExHandlers.Count);
|
||||
var oldToIndex = Utils.CreateObjectToIndexDictionary(oldInstrs);
|
||||
|
||||
foreach (var oldInstr in oldInstrs)
|
||||
instructions.Add(oldInstr.Clone());
|
||||
|
||||
foreach (var newInstr in instructions) {
|
||||
var operand = newInstr.Operand;
|
||||
if (operand is Instruction)
|
||||
newInstr.Operand = instructions[oldToIndex[(Instruction)operand]];
|
||||
else if (operand is IList<Instruction> oldArray) {
|
||||
var newArray = new Instruction[oldArray.Count];
|
||||
for (int i = 0; i < oldArray.Count; i++)
|
||||
newArray[i] = instructions[oldToIndex[oldArray[i]]];
|
||||
newInstr.Operand = newArray;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var oldEx in oldExHandlers) {
|
||||
var newEx = new ExceptionHandler(oldEx.HandlerType) {
|
||||
TryStart = GetInstruction(instructions, oldToIndex, oldEx.TryStart),
|
||||
TryEnd = GetInstruction(instructions, oldToIndex, oldEx.TryEnd),
|
||||
FilterStart = GetInstruction(instructions, oldToIndex, oldEx.FilterStart),
|
||||
HandlerStart = GetInstruction(instructions, oldToIndex, oldEx.HandlerStart),
|
||||
HandlerEnd = GetInstruction(instructions, oldToIndex, oldEx.HandlerEnd),
|
||||
CatchType = oldEx.CatchType,
|
||||
};
|
||||
exceptionHandlers.Add(newEx);
|
||||
}
|
||||
}
|
||||
|
||||
static Instruction GetInstruction(IList<Instruction> instructions, IDictionary<Instruction, int> instructionToIndex, Instruction instruction) {
|
||||
if (instruction == null)
|
||||
return null;
|
||||
return instructions[instructionToIndex[instruction]];
|
||||
}
|
||||
|
||||
public static void RestoreBody(MethodDef method, IEnumerable<Instruction> instructions, IEnumerable<ExceptionHandler> exceptionHandlers) {
|
||||
if (method == null || method.Body == null)
|
||||
return;
|
||||
|
||||
var bodyInstrs = method.Body.Instructions;
|
||||
bodyInstrs.Clear();
|
||||
foreach (var instr in instructions)
|
||||
bodyInstrs.Add(instr);
|
||||
|
||||
var bodyExceptionHandlers = method.Body.ExceptionHandlers;
|
||||
bodyExceptionHandlers.Clear();
|
||||
foreach (var eh in exceptionHandlers)
|
||||
bodyExceptionHandlers.Add(eh);
|
||||
}
|
||||
|
||||
public static void CopyBodyFromTo(MethodDef fromMethod, MethodDef toMethod) {
|
||||
if (fromMethod == toMethod)
|
||||
return;
|
||||
|
||||
CopyBody(fromMethod, out var instructions, out var exceptionHandlers);
|
||||
RestoreBody(toMethod, instructions, exceptionHandlers);
|
||||
CopyLocalsFromTo(fromMethod, toMethod);
|
||||
UpdateInstructionOperands(fromMethod, toMethod);
|
||||
}
|
||||
|
||||
static void CopyLocalsFromTo(MethodDef fromMethod, MethodDef toMethod) {
|
||||
var fromBody = fromMethod.Body;
|
||||
var toBody = toMethod.Body;
|
||||
|
||||
toBody.Variables.Clear();
|
||||
foreach (var local in fromBody.Variables)
|
||||
toBody.Variables.Add(new Local(local.Type));
|
||||
}
|
||||
|
||||
static void UpdateInstructionOperands(MethodDef fromMethod, MethodDef toMethod) {
|
||||
var fromBody = fromMethod.Body;
|
||||
var toBody = toMethod.Body;
|
||||
|
||||
toBody.InitLocals = fromBody.InitLocals;
|
||||
toBody.MaxStack = fromBody.MaxStack;
|
||||
|
||||
var newOperands = new Dictionary<object, object>();
|
||||
var fromParams = fromMethod.Parameters;
|
||||
var toParams = toMethod.Parameters;
|
||||
for (int i = 0; i < fromParams.Count; i++)
|
||||
newOperands[fromParams[i]] = toParams[i];
|
||||
for (int i = 0; i < fromBody.Variables.Count; i++)
|
||||
newOperands[fromBody.Variables[i]] = toBody.Variables[i];
|
||||
|
||||
foreach (var instr in toBody.Instructions) {
|
||||
if (instr.Operand == null)
|
||||
continue;
|
||||
if (newOperands.TryGetValue(instr.Operand, out object newOperand))
|
||||
instr.Operand = newOperand;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetCustomArgAsString(CustomAttribute cattr, int arg) {
|
||||
if (cattr == null || arg >= cattr.ConstructorArguments.Count)
|
||||
return null;
|
||||
var carg = cattr.ConstructorArguments[arg];
|
||||
if (carg.Type.GetElementType() != ElementType.String)
|
||||
return null;
|
||||
return UTF8String.ToSystemStringOrEmpty((UTF8String)carg.Value);
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodDef> GetCalledMethods(ModuleDef module, MethodDef method) {
|
||||
if (method != null && method.HasBody) {
|
||||
foreach (var call in method.Body.Instructions) {
|
||||
if (call.OpCode.Code != Code.Call && call.OpCode.Code != Code.Callvirt)
|
||||
continue;
|
||||
var methodRef = call.Operand as IMethod;
|
||||
if (methodRef == null)
|
||||
continue;
|
||||
var type = GetType(module, methodRef.DeclaringType);
|
||||
var methodDef = GetMethod(type, methodRef);
|
||||
if (methodDef != null)
|
||||
yield return methodDef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IList<Instruction> GetInstructions(IList<Instruction> instructions, int i, params OpCode[] opcodes) {
|
||||
if (i + opcodes.Length > instructions.Count)
|
||||
return null;
|
||||
if (opcodes.Length == 0)
|
||||
return new List<Instruction>();
|
||||
if (opcodes[0] != instructions[i].OpCode)
|
||||
return null;
|
||||
|
||||
var list = new List<Instruction>(opcodes.Length);
|
||||
for (int j = 0; j < opcodes.Length; j++) {
|
||||
var instr = instructions[i + j];
|
||||
if (instr.OpCode != opcodes[j])
|
||||
return null;
|
||||
list.Add(instr);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static bool HasReturnValue(IMethod method) {
|
||||
if (method == null || method.MethodSig == null || method.MethodSig.RetType == null)
|
||||
return false;
|
||||
return method.MethodSig.RetType.RemovePinnedAndModifiers().ElementType != ElementType.Void;
|
||||
}
|
||||
|
||||
public static Parameter GetParameter(IList<Parameter> parameters, int index) {
|
||||
if (0 <= index && index < parameters.Count)
|
||||
return parameters[index];
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TypeSig GetArg(IList<TypeSig> args, int index) {
|
||||
if (0 <= index && index < args.Count)
|
||||
return args[index];
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<TypeSig> GetArgs(IMethod method) {
|
||||
var sig = method.MethodSig;
|
||||
var args = new List<TypeSig>(sig.Params.Count + 1);
|
||||
if (sig.ImplicitThis)
|
||||
args.Add(method.DeclaringType.ToTypeSig());
|
||||
foreach (var arg in sig.Params)
|
||||
args.Add(arg);
|
||||
return args;
|
||||
}
|
||||
|
||||
public static int GetArgsCount(IMethod method) {
|
||||
var sig = method.MethodSig;
|
||||
if (sig == null)
|
||||
return 0;
|
||||
int count = sig.Params.Count;
|
||||
if (sig.ImplicitThis)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
public static IList<TypeSig> ReplaceGenericParameters(GenericInstSig typeOwner, MethodSpec methodOwner, IList<TypeSig> types) {
|
||||
if (typeOwner == null && methodOwner == null)
|
||||
return types;
|
||||
for (int i = 0; i < types.Count; i++)
|
||||
types[i] = GetGenericArgument(typeOwner, methodOwner, types[i]);
|
||||
return types;
|
||||
}
|
||||
|
||||
public static TypeSig GetGenericArgument(GenericInstSig typeOwner, MethodSpec methodOwner, TypeSig type) {
|
||||
var typeArgs = typeOwner?.GenericArguments;
|
||||
var genMethodArgs = methodOwner == null || methodOwner.GenericInstMethodSig == null ?
|
||||
null : methodOwner.GenericInstMethodSig.GenericArguments;
|
||||
return GenericArgsSubstitutor.Create(type, typeArgs, genMethodArgs);
|
||||
}
|
||||
|
||||
public static Instruction GetInstruction(IList<Instruction> instructions, ref int index) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (index < 0 || index >= instructions.Count)
|
||||
return null;
|
||||
var instr = instructions[index++];
|
||||
if (instr.OpCode.Code == Code.Nop)
|
||||
continue;
|
||||
if (instr.OpCode.OpCodeType == OpCodeType.Prefix)
|
||||
continue;
|
||||
if (instr == null || (instr.OpCode.Code != Code.Br && instr.OpCode.Code != Code.Br_S))
|
||||
return instr;
|
||||
instr = instr.Operand as Instruction;
|
||||
if (instr == null)
|
||||
return null;
|
||||
index = instructions.IndexOf(instr);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static TypeDefOrRefSig FindOrCreateTypeRef(ModuleDef module, AssemblyRef asmRef, string ns, string name, bool isValueType) {
|
||||
var typeRef = module.UpdateRowId(new TypeRefUser(module, ns, name, asmRef));
|
||||
if (isValueType)
|
||||
return new ValueTypeSig(typeRef);
|
||||
else
|
||||
return new ClassSig(typeRef);
|
||||
}
|
||||
|
||||
public static FrameworkType GetFrameworkType(ModuleDefMD module) {
|
||||
foreach (var modRef in module.GetAssemblyRefs()) {
|
||||
if (modRef.Name != "mscorlib")
|
||||
continue;
|
||||
if (PublicKeyBase.IsNullOrEmpty2(modRef.PublicKeyOrToken))
|
||||
continue;
|
||||
switch (BitConverter.ToString(modRef.PublicKeyOrToken.Data).Replace("-", "").ToLowerInvariant()) {
|
||||
case "b77a5c561934e089":
|
||||
return FrameworkType.Desktop;
|
||||
case "7cec85d7bea7798e":
|
||||
return FrameworkType.Silverlight;
|
||||
case "969db8053d3322ac":
|
||||
return FrameworkType.CompactFramework;
|
||||
case "1c9e259686f921e0":
|
||||
return FrameworkType.XNA;
|
||||
case "e92a8b81eba7ceb7":
|
||||
return FrameworkType.Zune;
|
||||
}
|
||||
}
|
||||
|
||||
return FrameworkType.Unknown;
|
||||
}
|
||||
|
||||
public static int GetMethodCalls(MethodDef method, string methodFullName) {
|
||||
if (method == null || method.Body == null)
|
||||
return 0;
|
||||
|
||||
int count = 0;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt && instr.OpCode.Code != Code.Newobj)
|
||||
continue;
|
||||
var calledMethod = instr.Operand as IMethod;
|
||||
if (calledMethod == null)
|
||||
continue;
|
||||
if (calledMethod.FullName == methodFullName)
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public static bool CallsMethod(MethodDef method, string methodFullName) {
|
||||
if (method == null || method.Body == null)
|
||||
return false;
|
||||
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt && instr.OpCode.Code != Code.Newobj)
|
||||
continue;
|
||||
var calledMethod = instr.Operand as IMethod;
|
||||
if (calledMethod == null)
|
||||
continue;
|
||||
if (calledMethod.FullName == methodFullName)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool CallsMethod(MethodDef method, string returnType, string parameters) {
|
||||
if (method == null || method.Body == null)
|
||||
return false;
|
||||
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Call && instr.OpCode.Code != Code.Callvirt && instr.OpCode.Code != Code.Newobj)
|
||||
continue;
|
||||
if (IsMethod(instr.Operand as IMethod, returnType, parameters))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IList<Instruction> GetArgPushes(IList<Instruction> instrs, int index) =>
|
||||
GetArgPushes(instrs, ref index);
|
||||
|
||||
public static IList<Instruction> GetArgPushes(IList<Instruction> instrs, ref int index) {
|
||||
if (index < 0 || index >= instrs.Count)
|
||||
return null;
|
||||
var startInstr = instrs[index];
|
||||
startInstr.CalculateStackUsage(false, out int pushes, out int pops);
|
||||
|
||||
index--;
|
||||
int numArgs = pops;
|
||||
var args = new List<Instruction>(numArgs);
|
||||
int stackSize = numArgs;
|
||||
while (index >= 0 && args.Count != numArgs) {
|
||||
var instr = instrs[index--];
|
||||
instr.CalculateStackUsage(false, out pushes, out pops);
|
||||
if (instr.OpCode.Code == Code.Dup) {
|
||||
args.Add(instr);
|
||||
stackSize--;
|
||||
}
|
||||
else {
|
||||
if (pushes == 1)
|
||||
args.Add(instr);
|
||||
else if (pushes > 1)
|
||||
throw new NotImplementedException();
|
||||
stackSize -= pushes;
|
||||
|
||||
if (pops != 0) {
|
||||
index++;
|
||||
if (GetArgPushes(instrs, ref index) == null)
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (stackSize < 0)
|
||||
return null;
|
||||
}
|
||||
if (args.Count != numArgs)
|
||||
return null;
|
||||
args.Reverse();
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
42
de4dot/de4dot.blocks/DumpedMethod.cs
Normal file
42
de4dot/de4dot.blocks/DumpedMethod.cs
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
[Serializable]
|
||||
public class DumpedMethod {
|
||||
public ushort mhFlags; // method header Flags
|
||||
public ushort mhMaxStack; // method header MaxStack
|
||||
public uint mhCodeSize; // method header CodeSize
|
||||
public uint mhLocalVarSigTok; // method header LocalVarSigTok
|
||||
|
||||
public uint mdRVA; // methodDef RVA
|
||||
public ushort mdImplFlags; // methodDef ImplFlags
|
||||
public ushort mdFlags; // methodDef Flags
|
||||
public uint mdName; // methodDef Name (index into #String)
|
||||
public uint mdSignature; // methodDef Signature (index into #Blob)
|
||||
public uint mdParamList; // methodDef ParamList (index into Param table)
|
||||
|
||||
public uint token; // metadata token
|
||||
|
||||
public byte[] code;
|
||||
public byte[] extraSections;
|
||||
}
|
||||
}
|
45
de4dot/de4dot.blocks/DumpedMethods.cs
Normal file
45
de4dot/de4dot.blocks/DumpedMethods.cs
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.MD;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
[Serializable]
|
||||
public class DumpedMethods {
|
||||
Dictionary<uint, DumpedMethod> methods = new Dictionary<uint, DumpedMethod>();
|
||||
|
||||
public int Count => methods.Count;
|
||||
public void Add(uint token, DumpedMethod info) => methods[token] = info;
|
||||
public DumpedMethod Get(MethodDef method) => Get(method.MDToken.ToUInt32());
|
||||
|
||||
public DumpedMethod Get(uint token) {
|
||||
methods.TryGetValue(token, out var dm);
|
||||
return dm;
|
||||
}
|
||||
|
||||
public void Add(DumpedMethod dm) {
|
||||
if (MDToken.ToTable(dm.token) != Table.Method || MDToken.ToRID(dm.token) == 0)
|
||||
throw new ArgumentException("Invalid token");
|
||||
methods[dm.token] = dm;
|
||||
}
|
||||
}
|
||||
}
|
23
de4dot/de4dot.blocks/FilterHandlerBlock.cs
Normal file
23
de4dot/de4dot.blocks/FilterHandlerBlock.cs
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public class FilterHandlerBlock : ScopeBlock {
|
||||
}
|
||||
}
|
186
de4dot/de4dot.blocks/ForwardScanOrder.cs
Normal file
186
de4dot/de4dot.blocks/ForwardScanOrder.cs
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// This class makes sure that each block that is entered with a non-empty stack has at
|
||||
// least one of its source blocks sorted before itself. This is to make sure peverify
|
||||
// doesn't complain AND also to make sure dnlib sets the correct maxstack.
|
||||
class ForwardScanOrder {
|
||||
ScopeBlock scopeBlock;
|
||||
IList<BaseBlock> sorted;
|
||||
Dictionary<BaseBlock, BlockInfo> blockInfos = new Dictionary<BaseBlock, BlockInfo>();
|
||||
Dictionary<BaseBlock, bool> inNewList = new Dictionary<BaseBlock, bool>();
|
||||
List<BaseBlock> newList;
|
||||
|
||||
class BlockInfo {
|
||||
BaseBlock baseBlock;
|
||||
public int stackStart = 0;
|
||||
public int stackEnd = 0;
|
||||
|
||||
public BlockInfo(BaseBlock baseBlock, int stackStart) {
|
||||
this.baseBlock = baseBlock;
|
||||
this.stackStart = stackStart;
|
||||
}
|
||||
|
||||
public void CalculateStackUsage() {
|
||||
var block = baseBlock as Block;
|
||||
if (block == null) {
|
||||
stackEnd = stackStart;
|
||||
return;
|
||||
}
|
||||
|
||||
int stack = stackStart;
|
||||
foreach (var instr in block.Instructions)
|
||||
instr.Instruction.UpdateStack(ref stack, false);
|
||||
stackEnd = stack;
|
||||
}
|
||||
}
|
||||
|
||||
public ForwardScanOrder(ScopeBlock scopeBlock, IList<BaseBlock> sorted) {
|
||||
this.scopeBlock = scopeBlock;
|
||||
this.sorted = sorted;
|
||||
}
|
||||
|
||||
public List<BaseBlock> Fix() {
|
||||
CreateBlockInfos();
|
||||
CreateNewList();
|
||||
return newList;
|
||||
}
|
||||
|
||||
void CreateBlockInfos() {
|
||||
int firstBlockStackStart = 0;
|
||||
if ((scopeBlock is HandlerBlock || scopeBlock is FilterHandlerBlock) &&
|
||||
scopeBlock.Parent is TryHandlerBlock tryHandlerBlock &&
|
||||
(tryHandlerBlock.HandlerType == ExceptionHandlerType.Catch || tryHandlerBlock.HandlerType == ExceptionHandlerType.Filter)) {
|
||||
firstBlockStackStart = 1;
|
||||
}
|
||||
foreach (var bb in GetStartBlocks()) {
|
||||
int stackStart = ReferenceEquals(bb, sorted[0]) ? firstBlockStackStart : 0;
|
||||
ScanBaseBlock(bb, stackStart);
|
||||
}
|
||||
|
||||
// One reason for this to fail is if there are still dead blocks left. Could also
|
||||
// be a bug in the code.
|
||||
if (blockInfos.Count != sorted.Count)
|
||||
throw new ApplicationException($"Didn't add all blocks: {blockInfos.Count} vs {sorted.Count}");
|
||||
}
|
||||
|
||||
IEnumerable<BaseBlock> GetStartBlocks() {
|
||||
if (sorted.Count > 0) {
|
||||
yield return sorted[0];
|
||||
foreach (var bb in sorted) {
|
||||
if (ReferenceEquals(bb, sorted[0]))
|
||||
continue;
|
||||
var block = bb as Block;
|
||||
if (block == null || block.Sources == null || IsOneSourceInAnotherScopeBlock(block))
|
||||
yield return bb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IsOneSourceInAnotherScopeBlock(Block block) {
|
||||
foreach (var source in block.Sources) {
|
||||
if (!scopeBlock.IsOurBaseBlock(source))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
struct ScanBaseBlockState {
|
||||
public BaseBlock bb;
|
||||
public int stackStart;
|
||||
public ScanBaseBlockState(BaseBlock bb, int stackStart) {
|
||||
this.bb = bb;
|
||||
this.stackStart = stackStart;
|
||||
}
|
||||
}
|
||||
Stack<ScanBaseBlockState> scanBaseBlockStack = new Stack<ScanBaseBlockState>();
|
||||
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;
|
||||
|
||||
var blockInfo = new BlockInfo(state.bb, state.stackStart);
|
||||
blockInfos[state.bb] = blockInfo;
|
||||
|
||||
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() {
|
||||
newList = new List<BaseBlock>(sorted.Count);
|
||||
foreach (var bb in sorted)
|
||||
AddToNewList(bb);
|
||||
if (newList.Count != sorted.Count)
|
||||
throw new ApplicationException($"Too many/few blocks after sorting: {newList.Count} vs {sorted.Count}");
|
||||
if (newList.Count > 0 && !ReferenceEquals(newList[0], sorted[0]))
|
||||
throw new ApplicationException("Start block is not first block after sorting");
|
||||
}
|
||||
|
||||
void AddToNewList(BaseBlock bb) {
|
||||
if (inNewList.ContainsKey(bb) || !scopeBlock.IsOurBaseBlock(bb))
|
||||
return;
|
||||
inNewList[bb] = false;
|
||||
|
||||
var blockInfo = blockInfos[bb];
|
||||
var block = bb as Block;
|
||||
if (blockInfo.stackStart == 0 || ReferenceEquals(bb, sorted[0]) ||
|
||||
block == null || block.Sources == null || IsInNewList(block.Sources)) {
|
||||
}
|
||||
else {
|
||||
foreach (var source in block.Sources) {
|
||||
if (!scopeBlock.IsOurBaseBlock(source))
|
||||
continue;
|
||||
int oldCount = newList.Count;
|
||||
AddToNewList(source); // Make sure it's before this block
|
||||
if (oldCount != newList.Count)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inNewList[bb] = true;
|
||||
newList.Add(bb);
|
||||
}
|
||||
|
||||
bool IsInNewList(IEnumerable<Block> blocks) {
|
||||
foreach (var block in blocks) {
|
||||
if (inNewList.ContainsKey(block) && inNewList[block])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
309
de4dot/de4dot.blocks/GenericArgsSubstitutor.cs
Normal file
309
de4dot/de4dot.blocks/GenericArgsSubstitutor.cs
Normal file
@ -0,0 +1,309 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public struct GenericArgsSubstitutor {
|
||||
IList<TypeSig> genericArgs;
|
||||
IList<TypeSig> genericMethodArgs;
|
||||
bool updated;
|
||||
|
||||
public static ITypeDefOrRef Create(ITypeDefOrRef type, GenericInstSig git) {
|
||||
if (git == null)
|
||||
return type;
|
||||
return Create(type, git.GenericArguments);
|
||||
}
|
||||
|
||||
public static ITypeDefOrRef Create(ITypeDefOrRef type, IList<TypeSig> genericArgs) {
|
||||
if (genericArgs == null || genericArgs.Count == 0)
|
||||
return type;
|
||||
var ts = type as TypeSpec;
|
||||
if (ts == null)
|
||||
return type;
|
||||
var newSig = Create(ts.TypeSig, genericArgs);
|
||||
return newSig == ts.TypeSig ? type : new TypeSpecUser(newSig);
|
||||
}
|
||||
|
||||
public static TypeSig Create(TypeSig type, IList<TypeSig> genericArgs) {
|
||||
if (type == null || genericArgs == null || genericArgs.Count == 0)
|
||||
return type;
|
||||
return new GenericArgsSubstitutor(genericArgs).Create(type);
|
||||
}
|
||||
|
||||
public static TypeSig Create(TypeSig type, IList<TypeSig> genericArgs, IList<TypeSig> genericMethodArgs) {
|
||||
if (type == null || ((genericArgs == null || genericArgs.Count == 0) &&
|
||||
(genericMethodArgs == null || genericMethodArgs.Count == 0)))
|
||||
return type;
|
||||
return new GenericArgsSubstitutor(genericArgs, genericMethodArgs).Create(type);
|
||||
}
|
||||
|
||||
public static IField Create(IField field, GenericInstSig git) {
|
||||
if (git == null)
|
||||
return field;
|
||||
return Create(field, git.GenericArguments);
|
||||
}
|
||||
|
||||
public static IField Create(IField field, IList<TypeSig> genericArgs) {
|
||||
if (field == null || genericArgs == null || genericArgs.Count == 0)
|
||||
return field;
|
||||
var newSig = Create(field.FieldSig, genericArgs);
|
||||
if (newSig == field.FieldSig)
|
||||
return field;
|
||||
var module = field.DeclaringType?.Module;
|
||||
return new MemberRefUser(module, field.Name, newSig, field.DeclaringType);
|
||||
}
|
||||
|
||||
public static FieldSig Create(FieldSig sig, GenericInstSig git) {
|
||||
if (git == null)
|
||||
return sig;
|
||||
return Create(sig, git.GenericArguments);
|
||||
}
|
||||
|
||||
public static FieldSig Create(FieldSig sig, IList<TypeSig> genericArgs) {
|
||||
if (sig == null || genericArgs == null || genericArgs.Count == 0)
|
||||
return sig;
|
||||
return new GenericArgsSubstitutor(genericArgs).Create(sig);
|
||||
}
|
||||
|
||||
public static IMethod Create(IMethod method, GenericInstSig git) {
|
||||
if (git == null)
|
||||
return method;
|
||||
|
||||
if (method is IMethodDefOrRef mdr)
|
||||
return Create(mdr, git);
|
||||
|
||||
if (method is MethodSpec ms)
|
||||
return Create(ms, git);
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
public static MethodSpec Create(MethodSpec method, GenericInstSig git) {
|
||||
if (method == null || git == null)
|
||||
return method;
|
||||
var newMethod = Create(method.Method, git);
|
||||
var newInst = Create(method.GenericInstMethodSig, git);
|
||||
bool updated = newMethod != method.Method || newInst != method.GenericInstMethodSig;
|
||||
return updated ? new MethodSpecUser(newMethod, newInst) : method;
|
||||
}
|
||||
|
||||
public static GenericInstMethodSig Create(GenericInstMethodSig sig, GenericInstSig git) {
|
||||
if (git == null)
|
||||
return sig;
|
||||
return Create(sig, git.GenericArguments);
|
||||
}
|
||||
|
||||
public static GenericInstMethodSig Create(GenericInstMethodSig sig, IList<TypeSig> genericArgs) {
|
||||
if (sig == null || genericArgs == null || genericArgs.Count == 0)
|
||||
return sig;
|
||||
return new GenericArgsSubstitutor(genericArgs).Create(sig);
|
||||
}
|
||||
|
||||
public static IMethodDefOrRef Create(IMethodDefOrRef method, GenericInstSig git) {
|
||||
if (git == null)
|
||||
return method;
|
||||
return Create(method, git.GenericArguments);
|
||||
}
|
||||
|
||||
public static IMethodDefOrRef Create(IMethodDefOrRef method, IList<TypeSig> genericArgs) =>
|
||||
Create(method, genericArgs, null);
|
||||
|
||||
public static IMethodDefOrRef Create(IMethodDefOrRef method, GenericInstSig git, IList<TypeSig> genericMethodArgs) =>
|
||||
Create(method, git?.GenericArguments, genericMethodArgs);
|
||||
|
||||
// Creates a new method but keeps declaring type as is
|
||||
public static IMethodDefOrRef Create(IMethodDefOrRef method, IList<TypeSig> genericArgs, IList<TypeSig> genericMethodArgs) {
|
||||
if (method == null)
|
||||
return method;
|
||||
if ((genericArgs == null || genericArgs.Count == 0) && (genericMethodArgs == null || genericMethodArgs.Count == 0))
|
||||
return method;
|
||||
|
||||
var sig = method.MethodSig;
|
||||
if (sig == null)
|
||||
return method;
|
||||
|
||||
var newSig = new GenericArgsSubstitutor(genericArgs, genericMethodArgs).Create(sig);
|
||||
if (newSig == sig)
|
||||
return method;
|
||||
|
||||
return new MemberRefUser(method.DeclaringType.Module, method.Name, newSig, method.DeclaringType);
|
||||
}
|
||||
|
||||
GenericArgsSubstitutor(IList<TypeSig> genericArgs) {
|
||||
this.genericArgs = genericArgs;
|
||||
genericMethodArgs = null;
|
||||
updated = false;
|
||||
}
|
||||
|
||||
GenericArgsSubstitutor(IList<TypeSig> genericArgs, IList<TypeSig> genericMethodArgs) {
|
||||
this.genericArgs = genericArgs;
|
||||
this.genericMethodArgs = genericMethodArgs;
|
||||
updated = false;
|
||||
}
|
||||
|
||||
TypeSig Create(TypeSig type) {
|
||||
var newType = Create2(type);
|
||||
return updated ? newType : type;
|
||||
}
|
||||
|
||||
TypeSig Create2(TypeSig type) {
|
||||
if (type == null)
|
||||
return type;
|
||||
TypeSig result;
|
||||
|
||||
GenericSig varSig;
|
||||
switch (type.ElementType) {
|
||||
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.Object:
|
||||
result = type;
|
||||
break;
|
||||
|
||||
case ElementType.Ptr:
|
||||
result = new PtrSig(Create2(type.Next));
|
||||
break;
|
||||
|
||||
case ElementType.ByRef:
|
||||
result = new ByRefSig(Create2(type.Next));
|
||||
break;
|
||||
|
||||
case ElementType.Array:
|
||||
var ary = (ArraySig)type;
|
||||
result = new ArraySig(ary.Next, ary.Rank, ary.Sizes, ary.LowerBounds);
|
||||
break;
|
||||
|
||||
case ElementType.SZArray:
|
||||
result = new SZArraySig(Create2(type.Next));
|
||||
break;
|
||||
|
||||
case ElementType.Pinned:
|
||||
result = new PinnedSig(Create2(type.Next));
|
||||
break;
|
||||
|
||||
case ElementType.ValueType:
|
||||
case ElementType.Class:
|
||||
result = type;
|
||||
break;
|
||||
|
||||
case ElementType.Var:
|
||||
varSig = (GenericSig)type;
|
||||
if (genericArgs != null && varSig.Number < (uint)genericArgs.Count) {
|
||||
result = genericArgs[(int)varSig.Number];
|
||||
updated = true;
|
||||
}
|
||||
else
|
||||
result = type;
|
||||
break;
|
||||
|
||||
case ElementType.MVar:
|
||||
varSig = (GenericSig)type;
|
||||
if (genericMethodArgs != null && varSig.Number < (uint)genericMethodArgs.Count) {
|
||||
result = genericMethodArgs[(int)varSig.Number];
|
||||
updated = true;
|
||||
}
|
||||
else
|
||||
result = type;
|
||||
break;
|
||||
|
||||
case ElementType.GenericInst:
|
||||
var gis = (GenericInstSig)type;
|
||||
var newGis = new GenericInstSig(Create2(gis.GenericType) as ClassOrValueTypeSig, gis.GenericArguments.Count);
|
||||
for (int i = 0; i < gis.GenericArguments.Count; i++)
|
||||
newGis.GenericArguments.Add(Create2(gis.GenericArguments[i]));
|
||||
result = newGis;
|
||||
break;
|
||||
|
||||
case ElementType.ValueArray:
|
||||
result = new ValueArraySig(type.Next, ((ValueArraySig)type).Size);
|
||||
break;
|
||||
|
||||
case ElementType.Module:
|
||||
result = new ModuleSig(((ModuleSig)type).Index, type.Next);
|
||||
break;
|
||||
|
||||
case ElementType.CModReqd:
|
||||
result = new CModReqdSig(((ModifierSig)type).Modifier, type.Next);
|
||||
break;
|
||||
|
||||
case ElementType.CModOpt:
|
||||
result = new CModOptSig(((ModifierSig)type).Modifier, type.Next);
|
||||
break;
|
||||
|
||||
case ElementType.FnPtr:
|
||||
result = new FnPtrSig(Create(((FnPtrSig)type).MethodSig));
|
||||
break;
|
||||
|
||||
case ElementType.End:
|
||||
case ElementType.R:
|
||||
case ElementType.Sentinel:
|
||||
case ElementType.Internal:
|
||||
default:
|
||||
result = type;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
MethodSig Create(MethodSig sig) {
|
||||
if (sig == null)
|
||||
return sig;
|
||||
var newSig = new MethodSig(sig.GetCallingConvention());
|
||||
newSig.RetType = Create2(sig.RetType);
|
||||
for (int i = 0; i < sig.Params.Count; i++)
|
||||
newSig.Params.Add(Create2(sig.Params[i]));
|
||||
newSig.GenParamCount = sig.GenParamCount;
|
||||
if (sig.ParamsAfterSentinel != null) {
|
||||
newSig.ParamsAfterSentinel = new List<TypeSig>();
|
||||
for (int i = 0; i < sig.ParamsAfterSentinel.Count; i++)
|
||||
newSig.ParamsAfterSentinel.Add(Create2(sig.ParamsAfterSentinel[i]));
|
||||
}
|
||||
return updated ? newSig : sig;
|
||||
}
|
||||
|
||||
GenericInstMethodSig Create(GenericInstMethodSig sig) {
|
||||
var newSig = new GenericInstMethodSig();
|
||||
for (int i = 0; i < sig.GenericArguments.Count; i++)
|
||||
newSig.GenericArguments.Add(Create2(sig.GenericArguments[i]));
|
||||
return updated ? newSig : sig;
|
||||
}
|
||||
|
||||
FieldSig Create(FieldSig sig) {
|
||||
var newSig = new FieldSig(Create2(sig.Type));
|
||||
return updated ? newSig : sig;
|
||||
}
|
||||
}
|
||||
}
|
24
de4dot/de4dot.blocks/HandlerBlock.cs
Normal file
24
de4dot/de4dot.blocks/HandlerBlock.cs
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// This is the block inside catch(xxx) { }.
|
||||
public class HandlerBlock : ScopeBlock {
|
||||
}
|
||||
}
|
188
de4dot/de4dot.blocks/Instr.cs
Normal file
188
de4dot/de4dot.blocks/Instr.cs
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public class Instr {
|
||||
Instruction instruction;
|
||||
|
||||
public OpCode OpCode => instruction.OpCode;
|
||||
|
||||
public object Operand {
|
||||
get => instruction.Operand;
|
||||
set => instruction.Operand = value;
|
||||
}
|
||||
|
||||
public Instr(Instruction instruction) => this.instruction = instruction;
|
||||
|
||||
public Instruction Instruction => instruction;
|
||||
|
||||
// Returns the variable or null if it's not a ldloc/stloc instruction. It does not return
|
||||
// a local variable if it's a ldloca/ldloca.s instruction.
|
||||
public static Local GetLocalVar(IList<Local> locals, Instr instr) {
|
||||
if (instr.Instruction.IsLdloc() || instr.Instruction.IsStloc())
|
||||
return instr.Instruction.GetLocal(locals);
|
||||
return null;
|
||||
}
|
||||
|
||||
static public bool IsFallThrough(OpCode opCode) {
|
||||
switch (opCode.FlowControl) {
|
||||
case FlowControl.Call:
|
||||
return opCode != OpCodes.Jmp;
|
||||
case FlowControl.Cond_Branch:
|
||||
case FlowControl.Next:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if the instruction only pushes one value onto the stack and pops nothing
|
||||
public bool IsSimpleLoad() {
|
||||
switch (OpCode.Code) {
|
||||
case Code.Ldarg:
|
||||
case Code.Ldarg_S:
|
||||
case Code.Ldarg_0:
|
||||
case Code.Ldarg_1:
|
||||
case Code.Ldarg_2:
|
||||
case Code.Ldarg_3:
|
||||
case Code.Ldarga:
|
||||
case Code.Ldarga_S:
|
||||
case Code.Ldc_I4:
|
||||
case Code.Ldc_I4_S:
|
||||
case Code.Ldc_I4_0:
|
||||
case Code.Ldc_I4_1:
|
||||
case Code.Ldc_I4_2:
|
||||
case Code.Ldc_I4_3:
|
||||
case Code.Ldc_I4_4:
|
||||
case Code.Ldc_I4_5:
|
||||
case Code.Ldc_I4_6:
|
||||
case Code.Ldc_I4_7:
|
||||
case Code.Ldc_I4_8:
|
||||
case Code.Ldc_I4_M1:
|
||||
case Code.Ldc_I8:
|
||||
case Code.Ldc_R4:
|
||||
case Code.Ldc_R8:
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
case Code.Ldloca:
|
||||
case Code.Ldloca_S:
|
||||
case Code.Ldnull:
|
||||
case Code.Ldstr:
|
||||
case Code.Ldtoken:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLdcI4() => instruction.IsLdcI4();
|
||||
public int GetLdcI4Value() => instruction.GetLdcI4Value();
|
||||
public bool IsLdarg() => instruction.IsLdarg();
|
||||
public bool IsStloc() => instruction.IsStloc();
|
||||
public bool IsLdloc() => instruction.IsLdloc();
|
||||
public bool IsNop() => OpCode == OpCodes.Nop;
|
||||
public bool IsPop() => OpCode == OpCodes.Pop;
|
||||
public bool IsLeave() => instruction.IsLeave();
|
||||
public bool IsBr() => instruction.IsBr();
|
||||
public bool IsBrfalse() => instruction.IsBrfalse();
|
||||
public bool IsBrtrue() => instruction.IsBrtrue();
|
||||
public bool IsConditionalBranch() => instruction.IsConditionalBranch();
|
||||
|
||||
public bool GetFlippedBranchOpCode(out OpCode opcode) {
|
||||
switch (OpCode.Code) {
|
||||
case Code.Bge: opcode = OpCodes.Blt; return true;
|
||||
case Code.Bge_S: opcode = OpCodes.Blt_S; return true;
|
||||
case Code.Bge_Un: opcode = OpCodes.Blt_Un; return true;
|
||||
case Code.Bge_Un_S: opcode = OpCodes.Blt_Un_S; return true;
|
||||
|
||||
case Code.Blt: opcode = OpCodes.Bge; return true;
|
||||
case Code.Blt_S: opcode = OpCodes.Bge_S; return true;
|
||||
case Code.Blt_Un: opcode = OpCodes.Bge_Un; return true;
|
||||
case Code.Blt_Un_S: opcode = OpCodes.Bge_Un_S; return true;
|
||||
|
||||
case Code.Bgt: opcode = OpCodes.Ble; return true;
|
||||
case Code.Bgt_S: opcode = OpCodes.Ble_S; return true;
|
||||
case Code.Bgt_Un: opcode = OpCodes.Ble_Un; return true;
|
||||
case Code.Bgt_Un_S: opcode = OpCodes.Ble_Un_S; return true;
|
||||
|
||||
case Code.Ble: opcode = OpCodes.Bgt; return true;
|
||||
case Code.Ble_S: opcode = OpCodes.Bgt_S; return true;
|
||||
case Code.Ble_Un: opcode = OpCodes.Bgt_Un; return true;
|
||||
case Code.Ble_Un_S: opcode = OpCodes.Bgt_Un_S; return true;
|
||||
|
||||
case Code.Brfalse: opcode = OpCodes.Brtrue; return true;
|
||||
case Code.Brfalse_S:opcode = OpCodes.Brtrue_S; return true;
|
||||
|
||||
case Code.Brtrue: opcode = OpCodes.Brfalse; return true;
|
||||
case Code.Brtrue_S: opcode = OpCodes.Brfalse_S; return true;
|
||||
|
||||
// Can't flip beq and bne.un since it's object vs uint/float
|
||||
case Code.Beq:
|
||||
case Code.Beq_S:
|
||||
case Code.Bne_Un:
|
||||
case Code.Bne_Un_S:
|
||||
default:
|
||||
opcode = OpCodes.Nop; // Whatever...
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void FlipConditonalBranch() {
|
||||
if (!GetFlippedBranchOpCode(out var opcode))
|
||||
throw new ApplicationException("Can't flip conditional since it's not a supported conditional instruction");
|
||||
instruction.OpCode = opcode;
|
||||
}
|
||||
|
||||
// Returns true if we can flip a conditional branch
|
||||
public bool CanFlipConditionalBranch() => GetFlippedBranchOpCode(out var opcode);
|
||||
|
||||
public void UpdateTargets(List<Instr> targets) {
|
||||
switch (OpCode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
if (targets.Count != 1)
|
||||
throw new ApplicationException("More than one target!");
|
||||
instruction.Operand = targets[0].Instruction;
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var switchTargets = new Instruction[targets.Count];
|
||||
for (var i = 0; i < targets.Count; i++)
|
||||
switchTargets[i] = targets[i].Instruction;
|
||||
instruction.Operand = switchTargets;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (targets.Count != 0)
|
||||
throw new ApplicationException("This instruction doesn't have any targets!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString() => instruction.ToString();
|
||||
}
|
||||
}
|
374
de4dot/de4dot.blocks/InstructionListParser.cs
Normal file
374
de4dot/de4dot.blocks/InstructionListParser.cs
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
class InstructionListParser {
|
||||
IList<Instruction> instructions;
|
||||
IList<ExceptionHandler> exceptionHandlers;
|
||||
Dictionary<Instruction, int> instrToIndex;
|
||||
Dictionary<int, List<int>> branches; // key = dest index, value = instrs branching to dest
|
||||
|
||||
public InstructionListParser(IList<Instruction> instructions, IList<ExceptionHandler> exceptionHandlers) {
|
||||
this.instructions = instructions;
|
||||
this.exceptionHandlers = exceptionHandlers;
|
||||
branches = new Dictionary<int, List<int>>();
|
||||
|
||||
CreateInstrToIndex();
|
||||
CreateBranches();
|
||||
CreateExceptionBranches();
|
||||
}
|
||||
|
||||
void CreateInstrToIndex() {
|
||||
instrToIndex = new Dictionary<Instruction, int>();
|
||||
|
||||
for (int i = 0; i < instructions.Count; i++)
|
||||
instrToIndex[instructions[i]] = i;
|
||||
}
|
||||
|
||||
List<int> GetBranchTargetList(int index) {
|
||||
if (!branches.TryGetValue(index, out var targetsList))
|
||||
branches[index] = targetsList = new List<int>();
|
||||
return targetsList;
|
||||
}
|
||||
|
||||
void MarkAsBranchTarget(Instruction instr) {
|
||||
if (instr == null)
|
||||
return;
|
||||
|
||||
int index = instrToIndex[instr];
|
||||
GetBranchTargetList(index); // Just create the list
|
||||
}
|
||||
|
||||
void CreateExceptionBranches() {
|
||||
foreach (var eh in exceptionHandlers) {
|
||||
MarkAsBranchTarget(eh.TryStart);
|
||||
MarkAsBranchTarget(eh.TryEnd);
|
||||
MarkAsBranchTarget(eh.FilterStart);
|
||||
MarkAsBranchTarget(eh.HandlerStart);
|
||||
MarkAsBranchTarget(eh.HandlerEnd);
|
||||
}
|
||||
}
|
||||
|
||||
void CreateBranches() {
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var instr = instructions[i];
|
||||
|
||||
List<int> targets = null;
|
||||
switch (instr.OpCode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
var targetInstr = instr.Operand as Instruction;
|
||||
if (targetInstr != null)
|
||||
targets = new List<int> { instrToIndex[targetInstr] };
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var switchTargets = (Instruction[])instr.Operand;
|
||||
targets = new List<int>(switchTargets.Length);
|
||||
for (int j = 0; j < switchTargets.Length; j++) {
|
||||
var target = switchTargets[j];
|
||||
if (target == null)
|
||||
continue;
|
||||
targets.Add(instrToIndex[target]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Endfilter:
|
||||
case Code.Endfinally:
|
||||
case Code.Jmp:
|
||||
case Code.Ret:
|
||||
case Code.Rethrow:
|
||||
case Code.Throw:
|
||||
targets = new List<int>();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (targets != null) {
|
||||
if (i + 1 < instructions.Count)
|
||||
targets.Add(i + 1);
|
||||
for (int j = 0; j < targets.Count; j++) {
|
||||
int targetIndex = targets[j];
|
||||
GetBranchTargetList(targetIndex).Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindBlocks(List<Block> instrToBlock, List<Block> allBlocks) {
|
||||
Block block = null;
|
||||
for (var i = 0; i < instructions.Count; i++) {
|
||||
if (branches.TryGetValue(i, out var branchSources) || block == null) {
|
||||
block = new Block();
|
||||
allBlocks.Add(block);
|
||||
}
|
||||
|
||||
block.Add(new Instr(instructions[i]));
|
||||
instrToBlock.Add(block);
|
||||
}
|
||||
}
|
||||
|
||||
// Fix all branches so they now point to a Block, and not an Instruction. The
|
||||
// block's Targets field is updated, not the Instruction's Operand field.
|
||||
// Also update Block.FallThrough with next Block if last instr falls through.
|
||||
void FixBranchTargets(List<Block> instrToBlock, List<Block> allBlocks) {
|
||||
for (var i = 0; i < allBlocks.Count; i++) {
|
||||
var block = allBlocks[i];
|
||||
var lastInstr = block.LastInstr;
|
||||
|
||||
switch (lastInstr.OpCode.OperandType) {
|
||||
case OperandType.ShortInlineBrTarget:
|
||||
case OperandType.InlineBrTarget:
|
||||
var targetInstr = lastInstr.Operand as Instruction;
|
||||
if (targetInstr != null)
|
||||
block.Targets = new List<Block> { instrToBlock[instrToIndex[targetInstr]] };
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var switchTargets = (Instruction[])lastInstr.Operand;
|
||||
var newSwitchTargets = new List<Block>();
|
||||
block.Targets = newSwitchTargets;
|
||||
foreach (var target in switchTargets) {
|
||||
if (target != null)
|
||||
newSwitchTargets.Add(instrToBlock[instrToIndex[target]]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (i + 1 < allBlocks.Count && Instr.IsFallThrough(lastInstr.OpCode))
|
||||
block.FallThrough = allBlocks[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Updates the sources field of each block
|
||||
void FixBlockSources(List<Block> allBlocks) {
|
||||
foreach (var block in allBlocks) {
|
||||
block.UpdateSources();
|
||||
}
|
||||
}
|
||||
|
||||
class EHInfo {
|
||||
public ExceptionHandler eh;
|
||||
|
||||
public EHInfo(ExceptionHandler eh) => this.eh = eh;
|
||||
|
||||
public override int GetHashCode() {
|
||||
int res = eh.TryStart.GetHashCode();
|
||||
if (eh.TryEnd != null)
|
||||
res += eh.TryEnd.GetHashCode();
|
||||
return res;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as EHInfo;
|
||||
if (other == null)
|
||||
return false;
|
||||
return ReferenceEquals(eh.TryStart, other.eh.TryStart) &&
|
||||
ReferenceEquals(eh.TryEnd, other.eh.TryEnd);
|
||||
}
|
||||
}
|
||||
|
||||
List<List<ExceptionHandler>> GetSortedExceptionInfos() {
|
||||
var exInfos = new Dictionary<EHInfo, List<ExceptionHandler>>();
|
||||
foreach (var eh in exceptionHandlers) {
|
||||
if (!exInfos.TryGetValue(new EHInfo(eh), out var handlers))
|
||||
exInfos[new EHInfo(eh)] = handlers = new List<ExceptionHandler>();
|
||||
|
||||
handlers.Add(eh);
|
||||
if (!ReferenceEquals(handlers[0].TryEnd, eh.TryEnd))
|
||||
throw new ApplicationException("Exception handler's try block does not start and end at the same place as the other one.");
|
||||
}
|
||||
|
||||
var exSorted = new List<List<ExceptionHandler>>(exInfos.Values);
|
||||
exSorted.Sort((a, b) => {
|
||||
int ai, bi;
|
||||
|
||||
// Sort in reverse order of TryStart. This is to make sure that nested
|
||||
// try handlers are before the outer try handler.
|
||||
ai = instrToIndex[a[0].TryStart];
|
||||
bi = instrToIndex[b[0].TryStart];
|
||||
if (ai > bi) return -1;
|
||||
if (ai < bi) return 1;
|
||||
|
||||
// Same start instruction. The nested one is the one that ends earliest,
|
||||
// so it should be sorted before the outer one.
|
||||
ai = GetInstrIndex(a[0].TryEnd);
|
||||
bi = GetInstrIndex(b[0].TryEnd);
|
||||
if (ai < bi) return -1;
|
||||
if (ai > bi) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return exSorted;
|
||||
}
|
||||
|
||||
class BaseBlocksList {
|
||||
class BaseBlockInfo {
|
||||
public int startInstr, endInstr;
|
||||
public BaseBlock baseBlock;
|
||||
|
||||
public BaseBlockInfo(int start, int end, BaseBlock bb) {
|
||||
startInstr = start;
|
||||
endInstr = end;
|
||||
baseBlock = bb;
|
||||
}
|
||||
}
|
||||
|
||||
List<BaseBlockInfo> blocksLeft = new List<BaseBlockInfo>();
|
||||
|
||||
public void Add(BaseBlock bb, int start, int end) {
|
||||
if (start < 0 || end < 0 || end < start)
|
||||
throw new ApplicationException("Invalid start and/or end index");
|
||||
if (blocksLeft.Count != 0) {
|
||||
var bbi = blocksLeft[blocksLeft.Count - 1];
|
||||
if (bbi.endInstr + 1 != start)
|
||||
throw new ApplicationException("Previous BaseBlock does not end where this new one starts");
|
||||
}
|
||||
blocksLeft.Add(new BaseBlockInfo(start, end, bb));
|
||||
}
|
||||
|
||||
int FindStart(int instrIndex) {
|
||||
for (int i = 0; i < blocksLeft.Count; i++) {
|
||||
if (blocksLeft[i].startInstr == instrIndex)
|
||||
return i;
|
||||
}
|
||||
throw new ApplicationException("Could not find start BaseBlockInfo");
|
||||
}
|
||||
|
||||
int FindEnd(int instrIndex) {
|
||||
for (int i = 0; i < blocksLeft.Count; i++) {
|
||||
if (blocksLeft[i].endInstr == instrIndex)
|
||||
return i;
|
||||
}
|
||||
throw new ApplicationException("Could not find end BaseBlockInfo");
|
||||
}
|
||||
|
||||
List<BaseBlock> GetBlocks(int startInstr, int endInstr, out int startIndex, out int endIndex) {
|
||||
if (endInstr < startInstr || startInstr < 0 || endInstr < 0)
|
||||
throw new ApplicationException("Invalid startInstr and/or endInstr");
|
||||
|
||||
var rv = new List<BaseBlock>();
|
||||
|
||||
startIndex = FindStart(startInstr);
|
||||
endIndex = FindEnd(endInstr);
|
||||
|
||||
for (int i = startIndex; i <= endIndex; i++)
|
||||
rv.Add(blocksLeft[i].baseBlock);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Replace the BaseBlocks with a new BaseBlock, returning the old ones.
|
||||
public List<BaseBlock> Replace(int startInstr, int endInstr, ScopeBlock bb) {
|
||||
if (endInstr < startInstr)
|
||||
return new List<BaseBlock>();
|
||||
|
||||
var rv = GetBlocks(startInstr, endInstr, out int startIndex, out int endIndex);
|
||||
UpdateParent(rv, bb);
|
||||
|
||||
var bbi = new BaseBlockInfo(blocksLeft[startIndex].startInstr, blocksLeft[endIndex].endInstr, bb);
|
||||
blocksLeft.RemoveRange(startIndex, endIndex - startIndex + 1);
|
||||
blocksLeft.Insert(startIndex, bbi);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
public List<BaseBlock> GetBlocks(ScopeBlock parent) {
|
||||
if (blocksLeft.Count == 0)
|
||||
return new List<BaseBlock>();
|
||||
var lb = GetBlocks(0, blocksLeft[blocksLeft.Count - 1].endInstr, out int startIndex, out int endIndex);
|
||||
return UpdateParent(lb, parent);
|
||||
}
|
||||
|
||||
List<BaseBlock> UpdateParent(List<BaseBlock> lb, ScopeBlock parent) {
|
||||
foreach (var bb in lb)
|
||||
bb.Parent = parent;
|
||||
return lb;
|
||||
}
|
||||
}
|
||||
|
||||
BaseBlocksList CreateBaseBlockList(List<Block> allBlocks, List<List<ExceptionHandler>> exSorted) {
|
||||
var bbl = new BaseBlocksList();
|
||||
foreach (var block in allBlocks) {
|
||||
int start = instrToIndex[block.FirstInstr.Instruction];
|
||||
int end = instrToIndex[block.LastInstr.Instruction];
|
||||
bbl.Add(block, start, end);
|
||||
}
|
||||
|
||||
foreach (var exHandlers in exSorted) {
|
||||
var tryBlock = new TryBlock();
|
||||
var tryStart = instrToIndex[exHandlers[0].TryStart];
|
||||
var tryEnd = GetInstrIndex(exHandlers[0].TryEnd) - 1;
|
||||
tryBlock.BaseBlocks = bbl.Replace(tryStart, tryEnd, tryBlock);
|
||||
|
||||
foreach (var exHandler in exHandlers) {
|
||||
var tryHandlerBlock = new TryHandlerBlock(exHandler);
|
||||
tryBlock.AddTryHandler(tryHandlerBlock);
|
||||
|
||||
int filterStart = -1, handlerStart = -1, handlerEnd = -1;
|
||||
|
||||
if (exHandler.FilterStart != null) {
|
||||
filterStart = instrToIndex[exHandler.FilterStart];
|
||||
var end = instrToIndex[exHandler.HandlerStart] - 1;
|
||||
tryHandlerBlock.FilterHandlerBlock.BaseBlocks = bbl.Replace(filterStart, end, tryHandlerBlock.FilterHandlerBlock);
|
||||
}
|
||||
|
||||
handlerStart = instrToIndex[exHandler.HandlerStart];
|
||||
handlerEnd = GetInstrIndex(exHandler.HandlerEnd) - 1;
|
||||
tryHandlerBlock.HandlerBlock.BaseBlocks = bbl.Replace(handlerStart, handlerEnd, tryHandlerBlock.HandlerBlock);
|
||||
|
||||
tryHandlerBlock.BaseBlocks = bbl.Replace(filterStart == -1 ? handlerStart : filterStart, handlerEnd, tryHandlerBlock);
|
||||
}
|
||||
}
|
||||
|
||||
return bbl;
|
||||
}
|
||||
|
||||
int GetInstrIndex(Instruction instruction) {
|
||||
if (instruction == null)
|
||||
return instructions.Count;
|
||||
return instrToIndex[instruction];
|
||||
}
|
||||
|
||||
public MethodBlocks Parse() {
|
||||
var instrToBlock = new List<Block>(instructions.Count);
|
||||
var allBlocks = new List<Block>();
|
||||
FindBlocks(instrToBlock, allBlocks);
|
||||
FixBranchTargets(instrToBlock, allBlocks);
|
||||
FixBlockSources(allBlocks);
|
||||
var exSorted = GetSortedExceptionInfos();
|
||||
var bbl = CreateBaseBlockList(allBlocks, exSorted);
|
||||
|
||||
foreach (var block in allBlocks)
|
||||
block.RemoveLastBr();
|
||||
|
||||
var mb = new MethodBlocks();
|
||||
mb.BaseBlocks = bbl.GetBlocks(mb);
|
||||
return mb;
|
||||
}
|
||||
}
|
||||
}
|
577
de4dot/de4dot.blocks/MemberDefDict.cs
Normal file
577
de4dot/de4dot.blocks/MemberDefDict.cs
Normal file
@ -0,0 +1,577 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public class TypeDefDict<TValue> {
|
||||
Dictionary<ScopeAndTokenKey, TValue> tokenToValue = new Dictionary<ScopeAndTokenKey, TValue>();
|
||||
Dictionary<ScopeAndTokenKey, TypeDef> tokenToKey = new Dictionary<ScopeAndTokenKey, TypeDef>();
|
||||
Dictionary<IType, TValue> refToValue = new Dictionary<IType, TValue>(TypeEqualityComparer.Instance);
|
||||
Dictionary<IType, TypeDef> refToKey = new Dictionary<IType, TypeDef>(TypeEqualityComparer.Instance);
|
||||
|
||||
public int Count => tokenToValue.Count;
|
||||
public IEnumerable<TypeDef> GetKeys() => tokenToKey.Values;
|
||||
public IEnumerable<TValue> GetValues() => tokenToValue.Values;
|
||||
ScopeAndTokenKey GetTokenKey(TypeDef typeDef) => new ScopeAndTokenKey(typeDef);
|
||||
|
||||
public TValue Find(IType typeRef) {
|
||||
TValue value;
|
||||
if (typeRef is TypeDef typeDef)
|
||||
tokenToValue.TryGetValue(GetTokenKey(typeDef), out value);
|
||||
else if (typeRef != null)
|
||||
refToValue.TryGetValue(typeRef, out value);
|
||||
else
|
||||
value = default;
|
||||
return value;
|
||||
}
|
||||
|
||||
public TValue FindAny(IType type) {
|
||||
if (type is TypeDef typeDef && tokenToValue.TryGetValue(GetTokenKey(typeDef), out var value))
|
||||
return value;
|
||||
|
||||
refToValue.TryGetValue(type, out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Add(TypeDef typeDef, TValue value) {
|
||||
var tokenKey = GetTokenKey(typeDef);
|
||||
tokenToValue[tokenKey] = value;
|
||||
tokenToKey[tokenKey] = typeDef;
|
||||
|
||||
if (!refToValue.ContainsKey(typeDef) ||
|
||||
GetAccessibilityOrder(typeDef) < GetAccessibilityOrder(refToKey[typeDef])) {
|
||||
refToKey[typeDef] = typeDef;
|
||||
refToValue[typeDef] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Order: public, family, assembly, private
|
||||
static int[] accessibilityOrder = new int[8] {
|
||||
40, // NotPublic
|
||||
0, // Public
|
||||
10, // NestedPublic
|
||||
70, // NestedPrivate
|
||||
20, // NestedFamily
|
||||
50, // NestedAssembly
|
||||
60, // NestedFamANDAssem
|
||||
30, // NestedFamORAssem
|
||||
};
|
||||
static int GetAccessibilityOrder(TypeDef typeDef) => accessibilityOrder[(int)typeDef.Attributes & 7];
|
||||
|
||||
public void OnTypesRenamed() {
|
||||
var newTypeRefToValue = new Dictionary<IType, TValue>(refToValue.Count);
|
||||
foreach (var kvp in refToValue)
|
||||
newTypeRefToValue[kvp.Key] = kvp.Value;
|
||||
refToValue = newTypeRefToValue;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class FieldDefDictBase<TValue> {
|
||||
Dictionary<ScopeAndTokenKey, TValue> tokenToValue = new Dictionary<ScopeAndTokenKey, TValue>();
|
||||
Dictionary<ScopeAndTokenKey, FieldDef> tokenToKey = new Dictionary<ScopeAndTokenKey, FieldDef>();
|
||||
Dictionary<IFieldRefKey, TValue> refToValue = new Dictionary<IFieldRefKey, TValue>();
|
||||
Dictionary<IFieldRefKey, FieldDef> refToKey = new Dictionary<IFieldRefKey, FieldDef>();
|
||||
|
||||
public int Count => tokenToValue.Count;
|
||||
public IEnumerable<FieldDef> GetKeys() => tokenToKey.Values;
|
||||
public IEnumerable<TValue> GetValues() => tokenToValue.Values;
|
||||
ScopeAndTokenKey GetTokenKey(FieldDef fieldDef) => new ScopeAndTokenKey(fieldDef);
|
||||
internal abstract IFieldRefKey GetRefKey(IField fieldRef);
|
||||
|
||||
public TValue Find(IField fieldRef) {
|
||||
TValue value;
|
||||
if (fieldRef is FieldDef fieldDef)
|
||||
tokenToValue.TryGetValue(GetTokenKey(fieldDef), out value);
|
||||
else
|
||||
refToValue.TryGetValue(GetRefKey(fieldRef), out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public TValue FindAny(IField fieldRef) {
|
||||
if (fieldRef is FieldDef fieldDef && tokenToValue.TryGetValue(GetTokenKey(fieldDef), out var value))
|
||||
return value;
|
||||
|
||||
refToValue.TryGetValue(GetRefKey(fieldRef), out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Add(FieldDef fieldDef, TValue value) {
|
||||
var tokenKey = GetTokenKey(fieldDef);
|
||||
tokenToValue[tokenKey] = value;
|
||||
tokenToKey[tokenKey] = fieldDef;
|
||||
|
||||
var refKey = GetRefKey(fieldDef);
|
||||
if (!refToValue.ContainsKey(refKey) ||
|
||||
GetAccessibilityOrder(fieldDef) < GetAccessibilityOrder(refToKey[refKey])) {
|
||||
refToKey[refKey] = fieldDef;
|
||||
refToValue[refKey] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Order: public, family, assembly, private
|
||||
static int[] accessibilityOrder = new int[8] {
|
||||
60, // PrivateScope
|
||||
50, // Private
|
||||
40, // FamANDAssem
|
||||
30, // Assembly
|
||||
10, // Family
|
||||
20, // FamORAssem
|
||||
0, // Public
|
||||
70, // <reserved>
|
||||
};
|
||||
static int GetAccessibilityOrder(FieldDef fieldDef) => accessibilityOrder[(int)fieldDef.Attributes & 7];
|
||||
|
||||
public void OnTypesRenamed() {
|
||||
var newFieldRefToDef = new Dictionary<IFieldRefKey, TValue>(refToValue.Count);
|
||||
foreach (var kvp in refToValue)
|
||||
newFieldRefToDef[GetRefKey((FieldDef)kvp.Key.FieldRef)] = kvp.Value;
|
||||
refToValue = newFieldRefToDef;
|
||||
}
|
||||
}
|
||||
|
||||
public class FieldDefDict<TValue> : FieldDefDictBase<TValue> {
|
||||
internal override IFieldRefKey GetRefKey(IField fieldRef) => new FieldRefKey(fieldRef);
|
||||
}
|
||||
|
||||
public class FieldDefAndDeclaringTypeDict<TValue> : FieldDefDictBase<TValue> {
|
||||
internal override IFieldRefKey GetRefKey(IField fieldRef) => new FieldRefAndDeclaringTypeKey(fieldRef);
|
||||
}
|
||||
|
||||
public abstract class MethodDefDictBase<TValue> {
|
||||
Dictionary<ScopeAndTokenKey, TValue> tokenToValue = new Dictionary<ScopeAndTokenKey, TValue>();
|
||||
Dictionary<ScopeAndTokenKey, MethodDef> tokenToKey = new Dictionary<ScopeAndTokenKey, MethodDef>();
|
||||
Dictionary<IMethodRefKey, TValue> refToValue = new Dictionary<IMethodRefKey, TValue>();
|
||||
Dictionary<IMethodRefKey, MethodDef> refToKey = new Dictionary<IMethodRefKey, MethodDef>();
|
||||
|
||||
public int Count => tokenToValue.Count;
|
||||
public IEnumerable<MethodDef> GetKeys() => tokenToKey.Values;
|
||||
public IEnumerable<TValue> GetValues() => tokenToValue.Values;
|
||||
ScopeAndTokenKey GetTokenKey(MethodDef methodDef) => new ScopeAndTokenKey(methodDef);
|
||||
internal abstract IMethodRefKey GetRefKey(IMethod methodRef);
|
||||
|
||||
public TValue Find(IMethod methodRef) {
|
||||
TValue value;
|
||||
if (methodRef is MethodDef methodDef)
|
||||
tokenToValue.TryGetValue(GetTokenKey(methodDef), out value);
|
||||
else
|
||||
refToValue.TryGetValue(GetRefKey(methodRef), out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public TValue FindAny(IMethod methodRef) {
|
||||
if (methodRef is MethodDef methodDef && tokenToValue.TryGetValue(GetTokenKey(methodDef), out var value))
|
||||
return value;
|
||||
|
||||
refToValue.TryGetValue(GetRefKey(methodRef), out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Add(MethodDef methodDef, TValue value) {
|
||||
var tokenKey = GetTokenKey(methodDef);
|
||||
tokenToValue[tokenKey] = value;
|
||||
tokenToKey[tokenKey] = methodDef;
|
||||
|
||||
var refKey = GetRefKey(methodDef);
|
||||
if (!refToValue.ContainsKey(refKey) ||
|
||||
GetAccessibilityOrder(methodDef) < GetAccessibilityOrder(refToKey[refKey])) {
|
||||
refToKey[refKey] = methodDef;
|
||||
refToValue[refKey] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Order: public, family, assembly, private
|
||||
static int[] accessibilityOrder = new int[8] {
|
||||
60, // PrivateScope
|
||||
50, // Private
|
||||
40, // FamANDAssem
|
||||
30, // Assembly
|
||||
10, // Family
|
||||
20, // FamORAssem
|
||||
0, // Public
|
||||
70, // <reserved>
|
||||
};
|
||||
static int GetAccessibilityOrder(MethodDef methodDef) => accessibilityOrder[(int)methodDef.Attributes & 7];
|
||||
|
||||
public void OnTypesRenamed() {
|
||||
var newFieldRefToDef = new Dictionary<IMethodRefKey, TValue>(refToValue.Count);
|
||||
foreach (var kvp in refToValue)
|
||||
newFieldRefToDef[GetRefKey((MethodDef)kvp.Key.MethodRef)] = kvp.Value;
|
||||
refToValue = newFieldRefToDef;
|
||||
}
|
||||
}
|
||||
|
||||
public class MethodDefDict<TValue> : MethodDefDictBase<TValue> {
|
||||
internal override IMethodRefKey GetRefKey(IMethod methodRef) => new MethodRefKey(methodRef);
|
||||
}
|
||||
|
||||
public class MethodDefAndDeclaringTypeDict<TValue> : MethodDefDictBase<TValue> {
|
||||
internal override IMethodRefKey GetRefKey(IMethod methodRef) => new MethodRefAndDeclaringTypeKey(methodRef);
|
||||
}
|
||||
|
||||
public abstract class EventDefDictBase<TValue> {
|
||||
Dictionary<ScopeAndTokenKey, TValue> tokenToValue = new Dictionary<ScopeAndTokenKey, TValue>();
|
||||
Dictionary<ScopeAndTokenKey, EventDef> tokenToKey = new Dictionary<ScopeAndTokenKey, EventDef>();
|
||||
Dictionary<IEventRefKey, TValue> refToValue = new Dictionary<IEventRefKey, TValue>();
|
||||
|
||||
public int Count => tokenToValue.Count;
|
||||
public IEnumerable<EventDef> GetKeys() => tokenToKey.Values;
|
||||
public IEnumerable<TValue> GetValues() => tokenToValue.Values;
|
||||
ScopeAndTokenKey GetTokenKey(EventDef eventRef) => new ScopeAndTokenKey(eventRef);
|
||||
internal abstract IEventRefKey GetRefKey(EventDef eventRef);
|
||||
|
||||
public TValue Find(EventDef eventRef) {
|
||||
tokenToValue.TryGetValue(GetTokenKey(eventRef), out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public TValue FindAny(EventDef eventRef) {
|
||||
if (tokenToValue.TryGetValue(GetTokenKey(eventRef), out var value))
|
||||
return value;
|
||||
|
||||
refToValue.TryGetValue(GetRefKey(eventRef), out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Add(EventDef eventDef, TValue value) {
|
||||
var tokenKey = GetTokenKey(eventDef);
|
||||
tokenToValue[tokenKey] = value;
|
||||
tokenToKey[tokenKey] = eventDef;
|
||||
|
||||
refToValue[GetRefKey(eventDef)] = value;
|
||||
}
|
||||
|
||||
public void OnTypesRenamed() {
|
||||
var newFieldRefToDef = new Dictionary<IEventRefKey, TValue>(refToValue.Count);
|
||||
foreach (var kvp in refToValue)
|
||||
newFieldRefToDef[GetRefKey((EventDef)kvp.Key.EventDef)] = kvp.Value;
|
||||
refToValue = newFieldRefToDef;
|
||||
}
|
||||
}
|
||||
|
||||
public class EventDefDict<TValue> : EventDefDictBase<TValue> {
|
||||
internal override IEventRefKey GetRefKey(EventDef eventRef) => new EventRefKey(eventRef);
|
||||
}
|
||||
|
||||
public class EventDefAndDeclaringTypeDict<TValue> : EventDefDictBase<TValue> {
|
||||
internal override IEventRefKey GetRefKey(EventDef eventRef) => new EventRefAndDeclaringTypeKey(eventRef);
|
||||
}
|
||||
|
||||
public abstract class PropertyDefDictBase<TValue> {
|
||||
Dictionary<ScopeAndTokenKey, TValue> tokenToValue = new Dictionary<ScopeAndTokenKey, TValue>();
|
||||
Dictionary<ScopeAndTokenKey, PropertyDef> tokenToKey = new Dictionary<ScopeAndTokenKey, PropertyDef>();
|
||||
Dictionary<IPropertyRefKey, TValue> refToValue = new Dictionary<IPropertyRefKey, TValue>();
|
||||
|
||||
public int Count => tokenToValue.Count;
|
||||
public IEnumerable<PropertyDef> GetKeys() => tokenToKey.Values;
|
||||
public IEnumerable<TValue> GetValues() => tokenToValue.Values;
|
||||
ScopeAndTokenKey GetTokenKey(PropertyDef propertyRef) => new ScopeAndTokenKey(propertyRef);
|
||||
internal abstract IPropertyRefKey GetRefKey(PropertyDef propertyRef);
|
||||
|
||||
public TValue Find(PropertyDef propRef) {
|
||||
tokenToValue.TryGetValue(GetTokenKey(propRef), out var value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public TValue FindAny(PropertyDef propRef) {
|
||||
if (tokenToValue.TryGetValue(GetTokenKey(propRef), out var value))
|
||||
return value;
|
||||
|
||||
refToValue.TryGetValue(GetRefKey(propRef), out value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Add(PropertyDef propDef, TValue value) {
|
||||
var tokenKey = GetTokenKey(propDef);
|
||||
tokenToValue[tokenKey] = value;
|
||||
tokenToKey[tokenKey] = propDef;
|
||||
|
||||
refToValue[GetRefKey(propDef)] = value;
|
||||
}
|
||||
|
||||
public void OnTypesRenamed() {
|
||||
var newFieldRefToDef = new Dictionary<IPropertyRefKey, TValue>(refToValue.Count);
|
||||
foreach (var kvp in refToValue)
|
||||
newFieldRefToDef[GetRefKey((PropertyDef)kvp.Key.PropertyDef)] = kvp.Value;
|
||||
refToValue = newFieldRefToDef;
|
||||
}
|
||||
}
|
||||
|
||||
public class PropertyDefDict<TValue> : PropertyDefDictBase<TValue> {
|
||||
internal override IPropertyRefKey GetRefKey(PropertyDef propRef) => new PropertyRefKey(propRef);
|
||||
}
|
||||
|
||||
public class PropertyDefAndDeclaringTypeDict<TValue> : PropertyDefDictBase<TValue> {
|
||||
internal override IPropertyRefKey GetRefKey(PropertyDef propRef) => new PropertyRefAndDeclaringTypeKey(propRef);
|
||||
}
|
||||
|
||||
sealed class ScopeAndTokenKey {
|
||||
readonly IScope scope;
|
||||
readonly uint token;
|
||||
|
||||
public ScopeAndTokenKey(TypeDef type)
|
||||
: this(type.Module, type.MDToken.Raw) {
|
||||
}
|
||||
|
||||
public ScopeAndTokenKey(FieldDef field)
|
||||
: this(field.DeclaringType?.Module, field.MDToken.Raw) {
|
||||
}
|
||||
|
||||
public ScopeAndTokenKey(MethodDef method)
|
||||
: this(method.DeclaringType?.Module, method.MDToken.Raw) {
|
||||
}
|
||||
|
||||
public ScopeAndTokenKey(PropertyDef prop)
|
||||
: this(prop.DeclaringType?.Module, prop.MDToken.Raw) {
|
||||
}
|
||||
|
||||
public ScopeAndTokenKey(EventDef evt)
|
||||
: this(evt.DeclaringType?.Module, evt.MDToken.Raw) {
|
||||
}
|
||||
|
||||
public ScopeAndTokenKey(IScope scope, uint token) {
|
||||
this.scope = scope;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public override int GetHashCode() => (int)token + GetHashCode(scope);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as ScopeAndTokenKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return token == other.token &&
|
||||
Equals(scope, other.scope);
|
||||
}
|
||||
|
||||
public override string ToString() => $"{token:X8} {scope}";
|
||||
|
||||
static bool Equals(IScope a, IScope b) {
|
||||
if (a == b)
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
return GetCanonicalizedScopeName(a) == GetCanonicalizedScopeName(b);
|
||||
}
|
||||
|
||||
static int GetHashCode(IScope a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return GetCanonicalizedScopeName(a).GetHashCode();
|
||||
}
|
||||
|
||||
static string GetAssemblyName(IScope a) {
|
||||
switch (a.ScopeType) {
|
||||
case ScopeType.AssemblyRef:
|
||||
return ((AssemblyRef)a).Name.String;
|
||||
case ScopeType.ModuleDef:
|
||||
var asm = ((ModuleDef)a).Assembly;
|
||||
if (asm != null)
|
||||
return asm.Name.String;
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static string GetCanonicalizedScopeName(IScope a) {
|
||||
if (a == null)
|
||||
return string.Empty;
|
||||
var asmName = GetAssemblyName(a);
|
||||
if (asmName != null) {
|
||||
// The version number should be ignored. Older code may reference an old version of
|
||||
// the assembly, but if the newer one has been loaded, that one is used.
|
||||
return asmName.ToUpperInvariant();
|
||||
}
|
||||
return a.ScopeName.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
interface IFieldRefKey {
|
||||
IField FieldRef { get; }
|
||||
}
|
||||
|
||||
interface IMethodRefKey {
|
||||
IMethod MethodRef { get; }
|
||||
}
|
||||
|
||||
interface IEventRefKey {
|
||||
EventDef EventDef { get; }
|
||||
}
|
||||
|
||||
interface IPropertyRefKey {
|
||||
PropertyDef PropertyDef { get; }
|
||||
}
|
||||
|
||||
sealed class FieldRefKey : IFieldRefKey {
|
||||
static SigComparerOptions SIG_COMPARER_FLAGS = SigComparerOptions.PrivateScopeFieldIsComparable;
|
||||
readonly IField fieldRef;
|
||||
|
||||
public IField FieldRef => fieldRef;
|
||||
|
||||
public FieldRefKey(IField fieldRef) => this.fieldRef = fieldRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer(SIG_COMPARER_FLAGS).GetHashCode(fieldRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as FieldRefKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer(SIG_COMPARER_FLAGS).Equals(fieldRef, other.fieldRef);
|
||||
}
|
||||
|
||||
public override string ToString() => fieldRef.ToString();
|
||||
}
|
||||
|
||||
sealed class MethodRefKey : IMethodRefKey {
|
||||
static SigComparerOptions SIG_COMPARER_FLAGS = SigComparerOptions.PrivateScopeMethodIsComparable;
|
||||
readonly IMethod methodRef;
|
||||
|
||||
public IMethod MethodRef => methodRef;
|
||||
|
||||
public MethodRefKey(IMethod methodRef) => this.methodRef = methodRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer(SIG_COMPARER_FLAGS).GetHashCode(methodRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as MethodRefKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer(SIG_COMPARER_FLAGS).Equals(methodRef, other.methodRef);
|
||||
}
|
||||
|
||||
public override string ToString() => methodRef.ToString();
|
||||
}
|
||||
|
||||
sealed class FieldRefAndDeclaringTypeKey : IFieldRefKey {
|
||||
static SigComparerOptions SIG_COMPARER_FLAGS = SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.PrivateScopeFieldIsComparable;
|
||||
readonly IField fieldRef;
|
||||
|
||||
public IField FieldRef => fieldRef;
|
||||
|
||||
public FieldRefAndDeclaringTypeKey(IField fieldRef) => this.fieldRef = fieldRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer(SIG_COMPARER_FLAGS).GetHashCode(fieldRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as FieldRefAndDeclaringTypeKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer(SIG_COMPARER_FLAGS).Equals(fieldRef, other.fieldRef);
|
||||
}
|
||||
|
||||
public override string ToString() => fieldRef.ToString();
|
||||
}
|
||||
|
||||
sealed class MethodRefAndDeclaringTypeKey : IMethodRefKey {
|
||||
static SigComparerOptions SIG_COMPARER_FLAGS = SigComparerOptions.CompareMethodFieldDeclaringType | SigComparerOptions.PrivateScopeMethodIsComparable;
|
||||
readonly IMethod methodRef;
|
||||
|
||||
public IMethod MethodRef => methodRef;
|
||||
|
||||
public MethodRefAndDeclaringTypeKey(IMethod methodRef) => this.methodRef = methodRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer(SIG_COMPARER_FLAGS).GetHashCode(methodRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as MethodRefAndDeclaringTypeKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer(SIG_COMPARER_FLAGS).Equals(methodRef, other.methodRef);
|
||||
}
|
||||
|
||||
public override string ToString() => methodRef.ToString();
|
||||
}
|
||||
|
||||
sealed class EventRefKey : IEventRefKey {
|
||||
readonly EventDef eventRef;
|
||||
|
||||
public EventDef EventDef => eventRef;
|
||||
|
||||
public EventRefKey(EventDef eventRef) => this.eventRef = eventRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer().GetHashCode(eventRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as EventRefKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer().Equals(eventRef, other.eventRef);
|
||||
}
|
||||
|
||||
public override string ToString() => eventRef.ToString();
|
||||
}
|
||||
|
||||
sealed class EventRefAndDeclaringTypeKey : IEventRefKey {
|
||||
readonly EventDef eventRef;
|
||||
|
||||
public EventDef EventDef => eventRef;
|
||||
|
||||
public EventRefAndDeclaringTypeKey(EventDef eventRef) => this.eventRef = eventRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer(SigComparerOptions.CompareEventDeclaringType).GetHashCode(eventRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as EventRefAndDeclaringTypeKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer(SigComparerOptions.CompareEventDeclaringType).Equals(eventRef, other.eventRef);
|
||||
}
|
||||
|
||||
public override string ToString() => eventRef.ToString();
|
||||
}
|
||||
|
||||
sealed class PropertyRefKey : IPropertyRefKey {
|
||||
readonly PropertyDef propRef;
|
||||
|
||||
public PropertyDef PropertyDef => propRef;
|
||||
|
||||
public PropertyRefKey(PropertyDef propRef) => this.propRef = propRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer().GetHashCode(propRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as PropertyRefKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer().Equals(propRef, other.propRef);
|
||||
}
|
||||
|
||||
public override string ToString() => propRef.ToString();
|
||||
}
|
||||
|
||||
sealed class PropertyRefAndDeclaringTypeKey : IPropertyRefKey {
|
||||
readonly PropertyDef propRef;
|
||||
|
||||
public PropertyDef PropertyDef => propRef;
|
||||
|
||||
public PropertyRefAndDeclaringTypeKey(PropertyDef propRef) => this.propRef = propRef;
|
||||
|
||||
public override int GetHashCode() => new SigComparer(SigComparerOptions.ComparePropertyDeclaringType).GetHashCode(propRef);
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as PropertyRefAndDeclaringTypeKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return new SigComparer(SigComparerOptions.ComparePropertyDeclaringType).Equals(propRef, other.propRef);
|
||||
}
|
||||
|
||||
public override string ToString() => propRef.ToString();
|
||||
}
|
||||
}
|
24
de4dot/de4dot.blocks/MethodBlocks.cs
Normal file
24
de4dot/de4dot.blocks/MethodBlocks.cs
Normal file
@ -0,0 +1,24 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// Start of a method
|
||||
public class MethodBlocks : ScopeBlock {
|
||||
}
|
||||
}
|
279
de4dot/de4dot.blocks/ScopeBlock.cs
Normal file
279
de4dot/de4dot.blocks/ScopeBlock.cs
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// A normal branch may not transfer out of a protected block (try block), filter handler,
|
||||
// an exception handler block, or a method.
|
||||
public abstract class ScopeBlock : BaseBlock {
|
||||
protected List<BaseBlock> baseBlocks;
|
||||
|
||||
public List<BaseBlock> BaseBlocks {
|
||||
get => baseBlocks;
|
||||
set => baseBlocks = value;
|
||||
}
|
||||
|
||||
public IEnumerable<BaseBlock> GetBaseBlocks() {
|
||||
if (baseBlocks != null) {
|
||||
foreach (var bb in baseBlocks)
|
||||
yield return bb;
|
||||
}
|
||||
}
|
||||
|
||||
public List<BaseBlock> GetAllBaseBlocks() => GetTheBlocks(new List<BaseBlock>());
|
||||
public List<Block> GetAllBlocks() => GetTheBlocks(new List<Block>());
|
||||
|
||||
public List<Block> GetAllBlocks(List<Block> allBlocks) {
|
||||
allBlocks.Clear();
|
||||
return GetTheBlocks(allBlocks);
|
||||
}
|
||||
|
||||
public List<ScopeBlock> GetAllScopeBlocks() => GetTheBlocks(new List<ScopeBlock>());
|
||||
|
||||
public List<T> GetTheBlocks<T>(List<T> list) where T : BaseBlock {
|
||||
AddBlocks(list, this);
|
||||
return list;
|
||||
}
|
||||
|
||||
void AddBlocks<T>(IList<T> list, ScopeBlock scopeBlock) where T : BaseBlock {
|
||||
foreach (var bb in scopeBlock.GetBaseBlocks()) {
|
||||
if (bb is T t)
|
||||
list.Add(t);
|
||||
if (bb is ScopeBlock)
|
||||
AddBlocks(list, (ScopeBlock)bb);
|
||||
}
|
||||
}
|
||||
|
||||
List<Block> FindBlocks() => FindBlocks(null);
|
||||
|
||||
List<Block> FindBlocks(Func<Block, bool> blockChecker) {
|
||||
var blocks = new List<Block>();
|
||||
foreach (var bb in GetBaseBlocks()) {
|
||||
if (bb is Block block && (blockChecker == null || blockChecker(block)))
|
||||
blocks.Add(block);
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
internal bool GetLdcValue(Instr instr, out int value) {
|
||||
if (Code.Ldc_I4_0 <= instr.OpCode.Code && instr.OpCode.Code <= Code.Ldc_I4_8)
|
||||
value = instr.OpCode.Code - Code.Ldc_I4_0;
|
||||
else if (instr.OpCode.Code == Code.Ldc_I4)
|
||||
value = (int)instr.Operand;
|
||||
else if (instr.OpCode.Code == Code.Ldc_I4_S)
|
||||
value = (sbyte)instr.Operand;
|
||||
else if (instr.OpCode.Code == Code.Ldc_I4_M1)
|
||||
value = -1;
|
||||
else {
|
||||
value = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove the block if it's a dead block. If it has refs to other dead blocks, those
|
||||
// are also removed.
|
||||
public void RemoveDeadBlock(Block block) => RemoveDeadBlocks(new List<Block> { block });
|
||||
|
||||
// Remove all dead blocks we can find
|
||||
public void RemoveDeadBlocks() => RemoveDeadBlocks(FindBlocks());
|
||||
|
||||
// Remove the blocks if they're dead blocks. If they have refs to other dead blocks,
|
||||
// those are also removed.
|
||||
public void RemoveDeadBlocks(List<Block> blocks) {
|
||||
while (blocks.Count != 0) {
|
||||
var block = blocks[blocks.Count - 1];
|
||||
blocks.RemoveAt(blocks.Count - 1);
|
||||
if (block.Sources.Count != 0)
|
||||
continue; // Not dead
|
||||
if (block == baseBlocks[0])
|
||||
continue; // It's the start of this block fence so must be present
|
||||
if (!IsOurBaseBlock(block))
|
||||
continue; // Some other ScopeBlock owns it, eg. first instr of an exception handler
|
||||
|
||||
// It's a dead block we can delete!
|
||||
|
||||
if (block.FallThrough != null)
|
||||
blocks.Add(block.FallThrough);
|
||||
if (block.Targets != null)
|
||||
blocks.AddRange(block.Targets);
|
||||
block.RemoveDeadBlock();
|
||||
if (!baseBlocks.Remove(block))
|
||||
throw new ApplicationException("Could not remove dead block from baseBlocks");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsOurBaseBlock(BaseBlock bb) => bb != null && bb.Parent == this;
|
||||
|
||||
// For each block, if it has only one target, and the target has only one source, then
|
||||
// merge them into one block.
|
||||
public int MergeBlocks() {
|
||||
int mergedBlocks = 0;
|
||||
var blocks = FindBlocks();
|
||||
for (int i = 0; i < blocks.Count; i++) {
|
||||
var block = blocks[i];
|
||||
var target = block.GetOnlyTarget();
|
||||
if (!IsOurBaseBlock(target))
|
||||
continue; // Only merge blocks we own!
|
||||
if (!block.CanMerge(target))
|
||||
continue; // Can't merge them!
|
||||
if (target == baseBlocks[0])
|
||||
continue; // The first one has an implicit source (eg. start of method or exception handler)
|
||||
|
||||
var targetIndex = blocks.IndexOf(target);
|
||||
if (targetIndex < 0)
|
||||
throw new ApplicationException("Could not remove target block from blocks");
|
||||
blocks.RemoveAt(targetIndex);
|
||||
block.Merge(target);
|
||||
if (!baseBlocks.Remove(target))
|
||||
throw new ApplicationException("Could not remove merged block from baseBlocks");
|
||||
if (targetIndex < i)
|
||||
i--;
|
||||
i--; // Redo since there may be more blocks we can merge
|
||||
mergedBlocks++;
|
||||
}
|
||||
|
||||
return mergedBlocks;
|
||||
}
|
||||
|
||||
// If bb is in baseBlocks (a direct child), return bb. If bb is a BaseBlock in a
|
||||
// ScopeBlock that is a direct child, then return that ScopeBlock. Else return null.
|
||||
public BaseBlock ToChild(BaseBlock bb) {
|
||||
if (IsOurBaseBlock(bb))
|
||||
return bb;
|
||||
|
||||
for (var sb = bb.Parent; sb != null; sb = sb.Parent) {
|
||||
if (IsOurBaseBlock(sb))
|
||||
return sb;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void RepartitionBlocks() {
|
||||
var newBaseBlocks = new BlocksSorter(this).Sort();
|
||||
|
||||
const bool insane = true;
|
||||
if (insane) {
|
||||
if (newBaseBlocks.Count != baseBlocks.Count)
|
||||
throw new ApplicationException("BlocksSorter included too many/few BaseBlocks");
|
||||
if (baseBlocks.Count > 0 && baseBlocks[0] != newBaseBlocks[0])
|
||||
throw new ApplicationException("BlocksSorter removed the start block");
|
||||
foreach (var bb in baseBlocks) {
|
||||
if (!newBaseBlocks.Contains(bb))
|
||||
throw new ApplicationException("BlocksSorter forgot a child");
|
||||
}
|
||||
}
|
||||
|
||||
baseBlocks = newBaseBlocks;
|
||||
}
|
||||
|
||||
// Removes the TryBlock and all its TryHandlerBlocks. The code inside the try block
|
||||
// is not removed.
|
||||
public void RemoveTryBlock(TryBlock tryBlock) {
|
||||
int tryBlockIndex = baseBlocks.IndexOf(tryBlock);
|
||||
if (tryBlockIndex < 0)
|
||||
throw new ApplicationException("Can't remove the TryBlock since it's not this ScopeBlock's TryBlock");
|
||||
|
||||
foreach (var bb in tryBlock.BaseBlocks)
|
||||
bb.Parent = this;
|
||||
baseBlocks.RemoveAt(tryBlockIndex);
|
||||
baseBlocks.InsertRange(tryBlockIndex, tryBlock.BaseBlocks);
|
||||
tryBlock.BaseBlocks.Clear();
|
||||
|
||||
// Get removed blocks and make sure they're not referenced by remaining code
|
||||
var removedBlocks = new List<Block>();
|
||||
foreach (var handler in tryBlock.TryHandlerBlocks)
|
||||
handler.GetTheBlocks(removedBlocks);
|
||||
if (!VerifyNoExternalRefs(removedBlocks))
|
||||
throw new ApplicationException("Removed blocks are referenced by remaining code");
|
||||
|
||||
RemoveAllDeadBlocks(Utils.Convert<TryHandlerBlock, BaseBlock>(tryBlock.TryHandlerBlocks));
|
||||
}
|
||||
|
||||
// Returns true if no external blocks references the blocks
|
||||
static bool VerifyNoExternalRefs(IList<Block> removedBlocks) {
|
||||
var removedDict = new Dictionary<Block, bool>();
|
||||
foreach (var removedBlock in removedBlocks)
|
||||
removedDict[removedBlock] = true;
|
||||
foreach (var removedBlock in removedBlocks) {
|
||||
foreach (var source in removedBlock.Sources) {
|
||||
if (!removedDict.TryGetValue(source, out bool val))
|
||||
return false; // external code references a removed block
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Remove all blocks in deadBlocks. They're guaranteed to be dead.
|
||||
void RemoveAllDeadBlocks(IEnumerable<BaseBlock> deadBlocks) => RemoveAllDeadBlocks(deadBlocks, null);
|
||||
|
||||
// Remove all blocks in deadBlocks. They're guaranteed to be dead. deadBlocksDict is
|
||||
// a dictionary of all dead blocks (even those not in this ScopeBlock).
|
||||
internal void RemoveAllDeadBlocks(IEnumerable<BaseBlock> deadBlocks, Dictionary<BaseBlock, bool> deadBlocksDict) {
|
||||
|
||||
// Verify that all the blocks really are dead. If all their source blocks are
|
||||
// dead, then they are dead.
|
||||
|
||||
var allDeadBlocks = new List<Block>();
|
||||
foreach (var bb in deadBlocks) {
|
||||
if (bb is Block)
|
||||
allDeadBlocks.Add(bb as Block);
|
||||
else if (bb is ScopeBlock sb)
|
||||
allDeadBlocks.AddRange(sb.GetAllBlocks());
|
||||
else
|
||||
throw new ApplicationException($"Unknown BaseBlock type {bb.GetType()}");
|
||||
}
|
||||
|
||||
if (deadBlocksDict != null) {
|
||||
foreach (var block in allDeadBlocks) {
|
||||
if (block.Sources == null)
|
||||
continue;
|
||||
foreach (var source in block.Sources) {
|
||||
if (!deadBlocksDict.ContainsKey(source))
|
||||
throw new ApplicationException("Trying to remove a block that is not dead!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var block in allDeadBlocks)
|
||||
block.RemoveGuaranteedDeadBlock();
|
||||
foreach (var bb in deadBlocks) {
|
||||
if (!baseBlocks.Remove(bb))
|
||||
throw new ApplicationException("Could not remove dead base block from baseBlocks");
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveGuaranteedDeadBlock(Block block) {
|
||||
if (!baseBlocks.Remove(block))
|
||||
throw new ApplicationException("Could not remove dead block");
|
||||
block.RemoveGuaranteedDeadBlock();
|
||||
}
|
||||
|
||||
public void Add(Block block) {
|
||||
if (block.Parent != null)
|
||||
throw new ApplicationException("Block already has a parent");
|
||||
baseBlocks.Add(block);
|
||||
block.Parent = this;
|
||||
}
|
||||
}
|
||||
}
|
94
de4dot/de4dot.blocks/StackTracePatcher.cs
Normal file
94
de4dot/de4dot.blocks/StackTracePatcher.cs
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.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($"Could not set new method: {method}");
|
||||
}
|
||||
}
|
||||
}
|
33
de4dot/de4dot.blocks/TryBlock.cs
Normal file
33
de4dot/de4dot.blocks/TryBlock.cs
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// This is the block inside try { }.
|
||||
public class TryBlock : ScopeBlock {
|
||||
// The first one is the most nested one and the last one is the
|
||||
// outer most handler. I.e., the exceptions are written to the
|
||||
// image in the same order they're saved here.
|
||||
List<TryHandlerBlock> handlerBlocks = new List<TryHandlerBlock>();
|
||||
|
||||
public List<TryHandlerBlock> TryHandlerBlocks => handlerBlocks;
|
||||
public void AddTryHandler(TryHandlerBlock tryHandlerBlock) => handlerBlocks.Add(tryHandlerBlock);
|
||||
}
|
||||
}
|
43
de4dot/de4dot.blocks/TryHandlerBlock.cs
Normal file
43
de4dot/de4dot.blocks/TryHandlerBlock.cs
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// Contains the filter handler block and the catch handler block.
|
||||
public class TryHandlerBlock : ScopeBlock {
|
||||
FilterHandlerBlock filterHandlerBlock = new FilterHandlerBlock();
|
||||
HandlerBlock handlerBlock = new HandlerBlock();
|
||||
|
||||
// State for an ExceptionHandler instance
|
||||
ITypeDefOrRef catchType;
|
||||
ExceptionHandlerType handlerType;
|
||||
|
||||
public ITypeDefOrRef CatchType => catchType;
|
||||
public ExceptionHandlerType HandlerType => handlerType;
|
||||
public FilterHandlerBlock FilterHandlerBlock => filterHandlerBlock;
|
||||
public HandlerBlock HandlerBlock => handlerBlock;
|
||||
|
||||
public TryHandlerBlock(ExceptionHandler handler) {
|
||||
catchType = handler.CatchType;
|
||||
handlerType = handler.HandlerType;
|
||||
}
|
||||
}
|
||||
}
|
59
de4dot/de4dot.blocks/Utils.cs
Normal file
59
de4dot/de4dot.blocks/Utils.cs
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
public class Tuple<T1, T2> {
|
||||
public T1 Item1 { get; set; }
|
||||
public T2 Item2 { get; set; }
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as Tuple<T1, T2>;
|
||||
if (other == null)
|
||||
return false;
|
||||
return Item1.Equals(other.Item1) && Item2.Equals(other.Item2);
|
||||
}
|
||||
public override int GetHashCode() => Item1.GetHashCode() + Item2.GetHashCode();
|
||||
public override string ToString() => "<" + Item1.ToString() + "," + Item2.ToString() + ">";
|
||||
}
|
||||
|
||||
static class Utils {
|
||||
public static IDictionary<T, int> CreateObjectToIndexDictionary<T>(IList<T> objs) {
|
||||
var dict = new Dictionary<T, int>();
|
||||
for (int i = 0; i < objs.Count; i++)
|
||||
dict[objs[i]] = i;
|
||||
return dict;
|
||||
}
|
||||
|
||||
public static List<TOut> Convert<TIn, TOut>(IEnumerable<TIn> list) where TIn : TOut {
|
||||
var olist = new List<TOut>();
|
||||
foreach (var l in list)
|
||||
olist.Add(l);
|
||||
return olist;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> Unique<T>(IEnumerable<T> values) {
|
||||
// HashSet is only available in .NET 3.5 and later.
|
||||
var dict = new Dictionary<T, bool>();
|
||||
foreach (var val in values)
|
||||
dict[val] = true;
|
||||
return dict.Keys;
|
||||
}
|
||||
}
|
||||
}
|
604
de4dot/de4dot.blocks/cflow/AccessChecker.cs
Normal file
604
de4dot/de4dot.blocks/cflow/AccessChecker.cs
Normal file
@ -0,0 +1,604 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
/// <summary>
|
||||
/// Checks whether a type has access to some other target type, method or field
|
||||
/// according to the target's visibility.
|
||||
/// </summary>
|
||||
public struct AccessChecker {
|
||||
TypeDef userType;
|
||||
List<TypeDef> userTypeEnclosingTypes;
|
||||
bool enclosingTypesInitialized;
|
||||
Dictionary<IType, bool> baseTypes;
|
||||
bool baseTypesInitialized;
|
||||
|
||||
[Flags]
|
||||
enum CheckTypeAccess {
|
||||
/// <summary>
|
||||
/// Can't access the type
|
||||
/// </summary>
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Normal access to the type and its members. Type + member must be public, internal
|
||||
/// or protected (for sub classes) to access the member.
|
||||
/// </summary>
|
||||
Normal = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Full access to the type, even if the type is private. If clear, the type
|
||||
/// must be public, internal or protected (for sub classes).
|
||||
/// </summary>
|
||||
FullTypeAccess = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Full access to the type's members (types, fields, methods), even if the
|
||||
/// members are private. If clear, the members must be public, internal
|
||||
/// or protected (for sub classes)
|
||||
/// </summary>
|
||||
FullMemberAccess = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Full access to the type and its members
|
||||
/// </summary>
|
||||
Full = Normal | FullTypeAccess | FullMemberAccess,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the user type which is accessing the target type, field or method
|
||||
/// </summary>
|
||||
public TypeDef UserType {
|
||||
get => userType;
|
||||
set {
|
||||
if (userType == value)
|
||||
return;
|
||||
userType = value;
|
||||
enclosingTypesInitialized = false;
|
||||
baseTypesInitialized = false;
|
||||
if (userTypeEnclosingTypes != null)
|
||||
userTypeEnclosingTypes.Clear();
|
||||
if (baseTypes != null)
|
||||
baseTypes.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="userType">The type accessing the target type, field or method</param>
|
||||
public AccessChecker(TypeDef userType) {
|
||||
this.userType = userType;
|
||||
userTypeEnclosingTypes = null;
|
||||
baseTypes = null;
|
||||
enclosingTypesInitialized = false;
|
||||
baseTypesInitialized = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a method or a field
|
||||
/// </summary>
|
||||
/// <param name="op">Operand</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(object op) {
|
||||
if (op is MethodDef md)
|
||||
return CanAccess(md);
|
||||
|
||||
if (op is MemberRef mr)
|
||||
return CanAccess(mr);
|
||||
|
||||
if (op is FieldDef fd)
|
||||
return CanAccess(fd);
|
||||
|
||||
if (op is MethodSpec ms)
|
||||
return CanAccess(ms);
|
||||
|
||||
if (op is TypeRef tr)
|
||||
return CanAccess(tr.Resolve());
|
||||
|
||||
if (op is TypeDef td)
|
||||
return CanAccess(td);
|
||||
|
||||
if (op is TypeSpec ts)
|
||||
return CanAccess(ts);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a <see cref="TypeRef"/>
|
||||
/// </summary>
|
||||
/// <param name="tr">The type</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(TypeRef tr) {
|
||||
if (tr == null)
|
||||
return null;
|
||||
return CanAccess(tr.Resolve());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a <see cref="TypeDef"/>
|
||||
/// </summary>
|
||||
/// <param name="td">The type</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(TypeDef td) {
|
||||
var access = GetTypeAccess(td, null);
|
||||
if (access == null)
|
||||
return null;
|
||||
return (access.Value & CheckTypeAccess.Normal) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the access we have to <paramref name="td"/>. If <paramref name="td"/> is
|
||||
/// enclosing this type, we have private access to it and all its members. If its
|
||||
/// declaring type encloses us, we have private access to it, but only normal access
|
||||
/// to its members. Else, we only have normal access to it and its members. If we inherit
|
||||
/// it, we have protected access to it and its members.
|
||||
/// </summary>
|
||||
/// <param name="td">The type</param>
|
||||
/// <param name="git">Generic instance of <paramref name="td"/> or <c>null</c> if none</param>
|
||||
CheckTypeAccess? GetTypeAccess(TypeDef td, GenericInstSig git) {
|
||||
if (td == null)
|
||||
return null;
|
||||
if (userType == td)
|
||||
return CheckTypeAccess.Full;
|
||||
|
||||
// If this is our nested type, we have private access to it itself, but normal
|
||||
// access to its members.
|
||||
if (td.DeclaringType == userType)
|
||||
return CheckTypeAccess.Normal | CheckTypeAccess.FullTypeAccess;
|
||||
|
||||
// If we're not a nested type, td can't be our enclosing type
|
||||
if (userType.DeclaringType == null)
|
||||
return GetTypeAccess2(td, git);
|
||||
|
||||
// Can't be an enclosing type if they're not in the same module
|
||||
if (userType.Module != td.Module)
|
||||
return GetTypeAccess2(td, git);
|
||||
|
||||
var tdEncTypes = GetEnclosingTypes(td, true);
|
||||
var ourEncTypes = InitializeOurEnclosingTypes();
|
||||
int maxChecks = Math.Min(tdEncTypes.Count, ourEncTypes.Count);
|
||||
int commonIndex;
|
||||
for (commonIndex = 0; commonIndex < maxChecks; commonIndex++) {
|
||||
if (tdEncTypes[commonIndex] != ourEncTypes[commonIndex])
|
||||
break;
|
||||
}
|
||||
|
||||
// If td encloses us, then we have access to td and all its members even if
|
||||
// they're private.
|
||||
if (commonIndex == tdEncTypes.Count)
|
||||
return CheckTypeAccess.Full;
|
||||
|
||||
// If there are no common enclosing types, only check the visibility.
|
||||
if (commonIndex == 0)
|
||||
return GetTypeAccess2(td, git);
|
||||
|
||||
// If td's declaring type encloses this, then we have full access to td even if
|
||||
// it's private, but only normal access to its members.
|
||||
if (commonIndex + 1 == tdEncTypes.Count)
|
||||
return CheckTypeAccess.Normal | CheckTypeAccess.FullTypeAccess;
|
||||
|
||||
// Normal visibility checks starting from type after common enclosing type.
|
||||
// Note that we have full access to it so we don't need to check its access,
|
||||
// so start from the next one.
|
||||
for (int i = commonIndex + 1; i < tdEncTypes.Count; i++) {
|
||||
if (!IsVisible(tdEncTypes[i], null))
|
||||
return CheckTypeAccess.None;
|
||||
}
|
||||
return CheckTypeAccess.Normal;
|
||||
}
|
||||
|
||||
CheckTypeAccess GetTypeAccess2(TypeDef td, GenericInstSig git) {
|
||||
while (td != null) {
|
||||
var declType = td.DeclaringType;
|
||||
if (userType != declType && !IsVisible(td, git))
|
||||
return CheckTypeAccess.None;
|
||||
td = declType;
|
||||
git = null;
|
||||
}
|
||||
return CheckTypeAccess.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether <paramref name="td"/> is visible to us without checking whether they
|
||||
/// have any common enclosing types.
|
||||
/// </summary>
|
||||
/// <param name="td">Type</param>
|
||||
/// <param name="git">Generic instance of <paramref name="td"/> or <c>null</c> if none</param>
|
||||
bool IsVisible(TypeDef td, GenericInstSig git) {
|
||||
if (td == null)
|
||||
return false;
|
||||
if (td == userType)
|
||||
return true;
|
||||
|
||||
switch (td.Visibility) {
|
||||
case TypeAttributes.NotPublic:
|
||||
return IsSameAssemblyOrFriendAssembly(td.Module);
|
||||
|
||||
case TypeAttributes.Public:
|
||||
return true;
|
||||
|
||||
case TypeAttributes.NestedPublic:
|
||||
return true;
|
||||
|
||||
case TypeAttributes.NestedPrivate:
|
||||
return false;
|
||||
|
||||
case TypeAttributes.NestedFamily:
|
||||
return CheckFamily(td, git);
|
||||
|
||||
case TypeAttributes.NestedAssembly:
|
||||
return IsSameAssemblyOrFriendAssembly(td.Module);
|
||||
|
||||
case TypeAttributes.NestedFamANDAssem:
|
||||
return IsSameAssemblyOrFriendAssembly(td.Module) &&
|
||||
CheckFamily(td, git);
|
||||
|
||||
case TypeAttributes.NestedFamORAssem:
|
||||
return IsSameAssemblyOrFriendAssembly(td.Module) ||
|
||||
CheckFamily(td, git);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsSameAssemblyOrFriendAssembly(ModuleDef module) {
|
||||
if (module == null)
|
||||
return false;
|
||||
var userModule = userType.Module;
|
||||
if (userModule == null)
|
||||
return false;
|
||||
if (userModule == module)
|
||||
return true;
|
||||
var userAsm = userModule.Assembly;
|
||||
var modAsm = module.Assembly;
|
||||
if (IsSameAssembly(userAsm, modAsm))
|
||||
return true;
|
||||
if (userAsm != null && userAsm.IsFriendAssemblyOf(modAsm))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsSameAssembly(IAssembly asm1, IAssembly asm2) {
|
||||
if (asm1 == null || asm2 == null)
|
||||
return false;
|
||||
if (asm1 == asm2)
|
||||
return true;
|
||||
return new AssemblyNameComparer(AssemblyNameComparerFlags.All).Equals(asm1, asm2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether <see cref="userType"/> has access to <paramref name="td"/>.
|
||||
/// <paramref name="td"/> is Family, FamANDAssem, or FamORAssem.
|
||||
/// </summary>
|
||||
/// <param name="td">Type</param>
|
||||
/// <param name="git">Generic instance of <paramref name="td"/> or <c>null</c> if none</param>
|
||||
bool CheckFamily(TypeDef td, GenericInstSig git) {
|
||||
if (td == null)
|
||||
return false;
|
||||
InitializeBaseTypes();
|
||||
|
||||
if (baseTypes.ContainsKey(git ?? (IType)td))
|
||||
return true;
|
||||
|
||||
// td is Family, FamANDAssem, or FamORAssem. If we derive from its enclosing type,
|
||||
// we have access to it.
|
||||
var td2 = td.DeclaringType;
|
||||
if (td2 != null && baseTypes.ContainsKey(td2))
|
||||
return true;
|
||||
|
||||
// If one of our enclosing types derive from it, we also have access to it
|
||||
var userDeclType = userType.DeclaringType;
|
||||
if (userDeclType != null)
|
||||
return new AccessChecker(userDeclType).CheckFamily(td, git);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InitializeBaseTypes() {
|
||||
if (baseTypesInitialized)
|
||||
return;
|
||||
if (baseTypes == null)
|
||||
baseTypes = new Dictionary<IType, bool>(TypeEqualityComparer.Instance);
|
||||
baseTypesInitialized = true;
|
||||
|
||||
ITypeDefOrRef baseType = userType;
|
||||
while (baseType != null) {
|
||||
baseTypes[baseType] = true;
|
||||
baseType = baseType.GetBaseType();
|
||||
}
|
||||
}
|
||||
|
||||
List<TypeDef> InitializeOurEnclosingTypes() {
|
||||
if (!enclosingTypesInitialized) {
|
||||
userTypeEnclosingTypes = GetEnclosingTypes(userType, true);
|
||||
enclosingTypesInitialized = true;
|
||||
}
|
||||
return userTypeEnclosingTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all enclosing types, in order of non-enclosed to most enclosed type
|
||||
/// </summary>
|
||||
/// <param name="td">Type</param>
|
||||
/// <param name="includeInput"><c>true</c> if <paramref name="td"/> should be included</param>
|
||||
/// <returns>A list of all enclosing types</returns>
|
||||
static List<TypeDef> GetEnclosingTypes(TypeDef td, bool includeInput) {
|
||||
var list = new List<TypeDef>();
|
||||
if (includeInput && td != null)
|
||||
list.Add(td);
|
||||
while (td != null) {
|
||||
var dt = td.DeclaringType;
|
||||
if (dt == null)
|
||||
break;
|
||||
if (list.Contains(dt))
|
||||
break;
|
||||
list.Add(dt);
|
||||
td = dt;
|
||||
}
|
||||
list.Reverse();
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a <see cref="FieldDef"/>
|
||||
/// </summary>
|
||||
/// <param name="fd">The field</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(FieldDef fd) => CanAccess(fd, null);
|
||||
|
||||
bool? CanAccess(FieldDef fd, GenericInstSig git) {
|
||||
if (fd == null)
|
||||
return null;
|
||||
var access = GetTypeAccess(fd.DeclaringType, git);
|
||||
if (access == null)
|
||||
return null;
|
||||
var acc = access.Value;
|
||||
if ((acc & CheckTypeAccess.Normal) == 0)
|
||||
return false;
|
||||
if ((acc & CheckTypeAccess.FullMemberAccess) != 0)
|
||||
return true;
|
||||
|
||||
return IsVisible(fd, git);
|
||||
}
|
||||
|
||||
bool IsVisible(FieldDef fd, GenericInstSig git) {
|
||||
if (fd == null)
|
||||
return false;
|
||||
var fdDeclaringType = fd.DeclaringType;
|
||||
if (fdDeclaringType == null)
|
||||
return false;
|
||||
if (userType == fdDeclaringType)
|
||||
return true;
|
||||
|
||||
switch (fd.Access) {
|
||||
case FieldAttributes.PrivateScope:
|
||||
// Private scope aka compiler controlled fields/methods can only be accessed
|
||||
// by a Field/Method token. This means they must be in the same module.
|
||||
return userType.Module == fdDeclaringType.Module;
|
||||
|
||||
case FieldAttributes.Private:
|
||||
return false;
|
||||
|
||||
case FieldAttributes.FamANDAssem:
|
||||
return IsSameAssemblyOrFriendAssembly(fdDeclaringType.Module) &&
|
||||
CheckFamily(fdDeclaringType, git);
|
||||
|
||||
case FieldAttributes.Assembly:
|
||||
return IsSameAssemblyOrFriendAssembly(fdDeclaringType.Module);
|
||||
|
||||
case FieldAttributes.Family:
|
||||
return CheckFamily(fdDeclaringType, git);
|
||||
|
||||
case FieldAttributes.FamORAssem:
|
||||
return IsSameAssemblyOrFriendAssembly(fdDeclaringType.Module) ||
|
||||
CheckFamily(fdDeclaringType, git);
|
||||
|
||||
case FieldAttributes.Public:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a <see cref="MethodDef"/>
|
||||
/// </summary>
|
||||
/// <param name="md">The method</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(MethodDef md) => CanAccess(md, (GenericInstSig)null);
|
||||
|
||||
bool? CanAccess(MethodDef md, GenericInstSig git) {
|
||||
if (md == null)
|
||||
return null;
|
||||
var access = GetTypeAccess(md.DeclaringType, git);
|
||||
if (access == null)
|
||||
return null;
|
||||
var acc = access.Value;
|
||||
if ((acc & CheckTypeAccess.Normal) == 0)
|
||||
return false;
|
||||
if ((acc & CheckTypeAccess.FullMemberAccess) != 0)
|
||||
return true;
|
||||
|
||||
return IsVisible(md, git);
|
||||
}
|
||||
|
||||
bool IsVisible(MethodDef md, GenericInstSig git) {
|
||||
if (md == null)
|
||||
return false;
|
||||
var mdDeclaringType = md.DeclaringType;
|
||||
if (mdDeclaringType == null)
|
||||
return false;
|
||||
if (userType == mdDeclaringType)
|
||||
return true;
|
||||
|
||||
switch (md.Access) {
|
||||
case MethodAttributes.PrivateScope:
|
||||
// Private scope aka compiler controlled fields/methods can only be accessed
|
||||
// by a Field/Method token. This means they must be in the same module.
|
||||
return userType.Module == mdDeclaringType.Module;
|
||||
|
||||
case MethodAttributes.Private:
|
||||
return false;
|
||||
|
||||
case MethodAttributes.FamANDAssem:
|
||||
return IsSameAssemblyOrFriendAssembly(mdDeclaringType.Module) &&
|
||||
CheckFamily(mdDeclaringType, git);
|
||||
|
||||
case MethodAttributes.Assembly:
|
||||
return IsSameAssemblyOrFriendAssembly(mdDeclaringType.Module);
|
||||
|
||||
case MethodAttributes.Family:
|
||||
return CheckFamily(mdDeclaringType, git);
|
||||
|
||||
case MethodAttributes.FamORAssem:
|
||||
return IsSameAssemblyOrFriendAssembly(mdDeclaringType.Module) ||
|
||||
CheckFamily(mdDeclaringType, git);
|
||||
|
||||
case MethodAttributes.Public:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a <see cref="MemberRef"/>
|
||||
/// </summary>
|
||||
/// <param name="mr">The member reference</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(MemberRef mr) {
|
||||
if (mr == null)
|
||||
return null;
|
||||
|
||||
var parent = mr.Class;
|
||||
|
||||
if (parent is TypeDef td)
|
||||
return CanAccess(td, mr);
|
||||
|
||||
if (parent is TypeRef tr)
|
||||
return CanAccess(tr.Resolve(), mr);
|
||||
|
||||
if (parent is TypeSpec ts)
|
||||
return CanAccess(ts.ResolveTypeDef(), ts.TryGetGenericInstSig(), mr);
|
||||
|
||||
if (parent is MethodDef md)
|
||||
return CanAccess(md, mr);
|
||||
|
||||
if (parent is ModuleRef mod)
|
||||
return CanAccess(mod, mr);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool? CanAccess(TypeDef td, MemberRef mr) => CanAccess(td, null, mr);
|
||||
|
||||
bool? CanAccess(TypeDef td, GenericInstSig git, MemberRef mr) {
|
||||
if (mr == null || td == null)
|
||||
return null;
|
||||
|
||||
if (mr.MethodSig != null) {
|
||||
var md = td.FindMethodCheckBaseType(mr.Name, mr.MethodSig);
|
||||
if (md == null) {
|
||||
// Assume that it's an array type if it's one of these methods
|
||||
if (mr.Name == "Get" || mr.Name == "Set" || mr.Name == "Address" || mr.Name == ".ctor")
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
return CanAccess(md, git);
|
||||
}
|
||||
|
||||
if (mr.FieldSig != null)
|
||||
return CanAccess(td.FindFieldCheckBaseType(mr.Name, mr.FieldSig), git);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool? CanAccess(MethodDef md, MemberRef mr) {
|
||||
if (mr == null || md == null)
|
||||
return null;
|
||||
return CanAccess(md);
|
||||
}
|
||||
|
||||
bool? CanAccess(ModuleRef mod, MemberRef mr) {
|
||||
if (mr == null || mod == null || mod.Module == null)
|
||||
return null;
|
||||
|
||||
var userModule = userType.Module;
|
||||
if (userModule == null)
|
||||
return null;
|
||||
var userAsm = userModule.Assembly;
|
||||
if (!IsSameAssembly(userAsm, mod.Module.Assembly))
|
||||
return false;
|
||||
if (userAsm == null)
|
||||
return false;
|
||||
var otherMod = userAsm.FindModule(mod.Name);
|
||||
if (otherMod == null)
|
||||
return false;
|
||||
return CanAccess(otherMod.GlobalType, mr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a <see cref="TypeSpec"/>
|
||||
/// </summary>
|
||||
/// <param name="ts">The type spec</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(TypeSpec ts) => CanAccess(ts.ResolveTypeDef());
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether it can access a <see cref="MethodSpec"/>
|
||||
/// </summary>
|
||||
/// <param name="ms">The method spec</param>
|
||||
/// <returns><c>true</c> if it has access to it, <c>false</c> if not, and <c>null</c>
|
||||
/// if we can't determine it (eg. we couldn't resolve a type or input was <c>null</c>)</returns>
|
||||
public bool? CanAccess(MethodSpec ms) {
|
||||
if (ms == null)
|
||||
return null;
|
||||
|
||||
var mdr = ms.Method;
|
||||
|
||||
if (mdr is MethodDef md)
|
||||
return CanAccess(md);
|
||||
|
||||
if (mdr is MemberRef mr)
|
||||
return CanAccess(mr);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => userType.ToString();
|
||||
}
|
||||
}
|
79
de4dot/de4dot.blocks/cflow/BlockCflowDeobfuscator.cs
Normal file
79
de4dot/de4dot.blocks/cflow/BlockCflowDeobfuscator.cs
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
class BlockCflowDeobfuscator : BlockDeobfuscator, IBranchHandler {
|
||||
Block block;
|
||||
InstructionEmulator instructionEmulator;
|
||||
BranchEmulator branchEmulator;
|
||||
|
||||
public BlockCflowDeobfuscator() {
|
||||
instructionEmulator = new InstructionEmulator();
|
||||
branchEmulator = new BranchEmulator(instructionEmulator, this);
|
||||
}
|
||||
|
||||
protected override bool Deobfuscate(Block block) {
|
||||
this.block = block;
|
||||
if (!block.LastInstr.IsConditionalBranch() && block.LastInstr.OpCode.Code != Code.Switch)
|
||||
return false;
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == block);
|
||||
|
||||
var instructions = block.Instructions;
|
||||
if (instructions.Count == 0)
|
||||
return false;
|
||||
try {
|
||||
for (int i = 0; i < instructions.Count - 1; i++) {
|
||||
var instr = instructions[i].Instruction;
|
||||
instructionEmulator.Emulate(instr);
|
||||
}
|
||||
}
|
||||
catch (NullReferenceException) {
|
||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||
return false;
|
||||
}
|
||||
|
||||
return branchEmulator.Emulate(block.LastInstr.Instruction);
|
||||
}
|
||||
|
||||
void PopPushedArgs(int stackArgs) {
|
||||
// Pop the arguments to the bcc instruction. The dead code remover will get rid of the
|
||||
// pop and any pushed arguments. Insert the pops just before the bcc instr.
|
||||
for (int i = 0; i < stackArgs; i++)
|
||||
block.Insert(block.Instructions.Count - 1, OpCodes.Pop.ToInstruction());
|
||||
}
|
||||
|
||||
void IBranchHandler.HandleNormal(int stackArgs, bool isTaken) {
|
||||
PopPushedArgs(stackArgs);
|
||||
block.ReplaceBccWithBranch(isTaken);
|
||||
}
|
||||
|
||||
bool IBranchHandler.HandleSwitch(Int32Value switchIndex) {
|
||||
var target = CflowUtils.GetSwitchTarget(block.Targets, block.FallThrough, switchIndex);
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
PopPushedArgs(1);
|
||||
block.ReplaceSwitchWithBranch(target);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
50
de4dot/de4dot.blocks/cflow/BlockDeobfuscator.cs
Normal file
50
de4dot/de4dot.blocks/cflow/BlockDeobfuscator.cs
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public abstract class BlockDeobfuscator : IBlocksDeobfuscator {
|
||||
protected List<Block> allBlocks;
|
||||
protected Blocks blocks;
|
||||
|
||||
public bool ExecuteIfNotModified { get; set; }
|
||||
|
||||
public virtual void DeobfuscateBegin(Blocks blocks) => this.blocks = blocks;
|
||||
|
||||
public bool Deobfuscate(List<Block> allBlocks) {
|
||||
Initialize(allBlocks);
|
||||
|
||||
bool modified = false;
|
||||
foreach (var block in allBlocks) {
|
||||
try {
|
||||
modified |= Deobfuscate(block);
|
||||
}
|
||||
catch (NullReferenceException) {
|
||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
protected virtual void Initialize(List<Block> allBlocks) => this.allBlocks = allBlocks;
|
||||
protected abstract bool Deobfuscate(Block block);
|
||||
}
|
||||
}
|
182
de4dot/de4dot.blocks/cflow/BlocksCflowDeobfuscator.cs
Normal file
182
de4dot/de4dot.blocks/cflow/BlocksCflowDeobfuscator.cs
Normal file
@ -0,0 +1,182 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public class BlocksCflowDeobfuscator {
|
||||
Blocks blocks;
|
||||
List<Block> allBlocks = new List<Block>();
|
||||
List<IBlocksDeobfuscator> userBlocksDeobfuscators = new List<IBlocksDeobfuscator>();
|
||||
List<IBlocksDeobfuscator> ourBlocksDeobfuscators = new List<IBlocksDeobfuscator>();
|
||||
|
||||
public BlocksCflowDeobfuscator()
|
||||
: this(false) {
|
||||
}
|
||||
|
||||
public BlocksCflowDeobfuscator(bool disableNewCFCode) => Initialize(disableNewCFCode);
|
||||
|
||||
public BlocksCflowDeobfuscator(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscator)
|
||||
: this(blocksDeobfuscator, false) {
|
||||
}
|
||||
|
||||
public BlocksCflowDeobfuscator(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscator, bool disableNewCFCode) {
|
||||
Initialize(disableNewCFCode);
|
||||
Add(blocksDeobfuscator);
|
||||
}
|
||||
|
||||
void Initialize(bool disableNewCFCode) {
|
||||
ourBlocksDeobfuscators.Add(new BlockCflowDeobfuscator { ExecuteIfNotModified = false });
|
||||
ourBlocksDeobfuscators.Add(new SwitchCflowDeobfuscator { ExecuteIfNotModified = false });
|
||||
ourBlocksDeobfuscators.Add(new DeadStoreRemover { ExecuteIfNotModified = false });
|
||||
ourBlocksDeobfuscators.Add(new DeadCodeRemover { ExecuteIfNotModified = false });
|
||||
ourBlocksDeobfuscators.Add(new ConstantsFolder { ExecuteIfNotModified = true, DisableNewCode = disableNewCFCode });
|
||||
ourBlocksDeobfuscators.Add(new StLdlocFixer { ExecuteIfNotModified = true });
|
||||
ourBlocksDeobfuscators.Add(new DupBlockCflowDeobfuscator { ExecuteIfNotModified = true });
|
||||
}
|
||||
|
||||
public void Add(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscators) {
|
||||
foreach (var bd in blocksDeobfuscators)
|
||||
Add(bd);
|
||||
}
|
||||
|
||||
public void Add(IBlocksDeobfuscator blocksDeobfuscator) {
|
||||
if (blocksDeobfuscator != null)
|
||||
userBlocksDeobfuscators.Add(blocksDeobfuscator);
|
||||
}
|
||||
|
||||
public void Initialize(Blocks blocks) => this.blocks = blocks;
|
||||
|
||||
public void Deobfuscate() {
|
||||
bool modified;
|
||||
int iterations = -1;
|
||||
|
||||
DeobfuscateBegin(userBlocksDeobfuscators);
|
||||
DeobfuscateBegin(ourBlocksDeobfuscators);
|
||||
|
||||
do {
|
||||
iterations++;
|
||||
modified = false;
|
||||
RemoveDeadBlocks();
|
||||
MergeBlocks();
|
||||
|
||||
blocks.MethodBlocks.GetAllBlocks(allBlocks);
|
||||
|
||||
if (iterations == 0)
|
||||
modified |= FixDotfuscatorLoop();
|
||||
|
||||
modified |= Deobfuscate(userBlocksDeobfuscators, allBlocks);
|
||||
modified |= Deobfuscate(ourBlocksDeobfuscators, allBlocks);
|
||||
modified |= DeobfuscateIfNotModified(modified, userBlocksDeobfuscators, allBlocks);
|
||||
modified |= DeobfuscateIfNotModified(modified, ourBlocksDeobfuscators, allBlocks);
|
||||
} while (modified);
|
||||
}
|
||||
|
||||
void DeobfuscateBegin(IEnumerable<IBlocksDeobfuscator> bds) {
|
||||
foreach (var bd in bds)
|
||||
bd.DeobfuscateBegin(blocks);
|
||||
}
|
||||
|
||||
bool Deobfuscate(IEnumerable<IBlocksDeobfuscator> bds, List<Block> allBlocks) {
|
||||
bool modified = false;
|
||||
foreach (var bd in bds) {
|
||||
if (bd.ExecuteIfNotModified)
|
||||
continue;
|
||||
modified |= bd.Deobfuscate(allBlocks);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool DeobfuscateIfNotModified(bool modified, IEnumerable<IBlocksDeobfuscator> bds, List<Block> allBlocks) {
|
||||
foreach (var bd in bds) {
|
||||
if (modified)
|
||||
break;
|
||||
if (!bd.ExecuteIfNotModified)
|
||||
continue;
|
||||
modified |= bd.Deobfuscate(allBlocks);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
// Hack for old Dotfuscator
|
||||
bool FixDotfuscatorLoop() {
|
||||
/*
|
||||
blk1:
|
||||
...
|
||||
ldc.i4.x
|
||||
blk2:
|
||||
dup
|
||||
dup
|
||||
ldc.i4.y
|
||||
some_op
|
||||
bcc blk2
|
||||
blk3:
|
||||
pop
|
||||
...
|
||||
*/
|
||||
bool modified = false;
|
||||
foreach (var block in allBlocks) {
|
||||
if (block.Instructions.Count != 5)
|
||||
continue;
|
||||
var instructions = block.Instructions;
|
||||
if (instructions[0].OpCode.Code != Code.Dup)
|
||||
continue;
|
||||
if (instructions[1].OpCode.Code != Code.Dup)
|
||||
continue;
|
||||
if (!instructions[2].IsLdcI4())
|
||||
continue;
|
||||
if (instructions[3].OpCode.Code != Code.Sub && instructions[3].OpCode.Code != Code.Add)
|
||||
continue;
|
||||
if (instructions[4].OpCode.Code != Code.Blt && instructions[4].OpCode.Code != Code.Blt_S &&
|
||||
instructions[4].OpCode.Code != Code.Bgt && instructions[4].OpCode.Code != Code.Bgt_S)
|
||||
continue;
|
||||
if (block.Sources.Count != 2)
|
||||
continue;
|
||||
var prev = block.Sources[0];
|
||||
if (prev == block)
|
||||
prev = block.Sources[1];
|
||||
if (prev == null || !prev.LastInstr.IsLdcI4())
|
||||
continue;
|
||||
var next = block.FallThrough;
|
||||
if (next.FirstInstr.OpCode.Code != Code.Pop)
|
||||
continue;
|
||||
block.ReplaceLastInstrsWithBranch(5, next);
|
||||
modified = true;
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool RemoveDeadBlocks() => new DeadBlocksRemover(blocks.MethodBlocks).Remove() > 0;
|
||||
|
||||
bool MergeBlocks() {
|
||||
bool modified = false;
|
||||
foreach (var scopeBlock in GetAllScopeBlocks(blocks.MethodBlocks))
|
||||
modified |= scopeBlock.MergeBlocks() > 0;
|
||||
return modified;
|
||||
}
|
||||
|
||||
IEnumerable<ScopeBlock> GetAllScopeBlocks(ScopeBlock scopeBlock) {
|
||||
var list = new List<ScopeBlock>();
|
||||
list.Add(scopeBlock);
|
||||
list.AddRange(scopeBlock.GetAllScopeBlocks());
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
274
de4dot/de4dot.blocks/cflow/BranchEmulator.cs
Normal file
274
de4dot/de4dot.blocks/cflow/BranchEmulator.cs
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public interface IBranchHandler {
|
||||
// stackArgs is the number of args used by the branch instruction (1 or 2)
|
||||
void HandleNormal(int stackArgs, bool isTaken);
|
||||
|
||||
// Returns true if the switch target was found (even if it was the fall-through)
|
||||
bool HandleSwitch(Int32Value switchIndex);
|
||||
}
|
||||
|
||||
public class BranchEmulator {
|
||||
IBranchHandler branchHandler;
|
||||
InstructionEmulator instructionEmulator;
|
||||
|
||||
public BranchEmulator(InstructionEmulator instructionEmulator, IBranchHandler branchHandler) {
|
||||
this.instructionEmulator = instructionEmulator;
|
||||
this.branchHandler = branchHandler;
|
||||
}
|
||||
|
||||
public bool Emulate(Instruction instr) {
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Br:
|
||||
case Code.Br_S: return Emulate_Br();
|
||||
case Code.Beq:
|
||||
case Code.Beq_S: return Emulate_Beq();
|
||||
case Code.Bge:
|
||||
case Code.Bge_S: return Emulate_Bge();
|
||||
case Code.Bge_Un:
|
||||
case Code.Bge_Un_S: return Emulate_Bge_Un();
|
||||
case Code.Bgt:
|
||||
case Code.Bgt_S: return Emulate_Bgt();
|
||||
case Code.Bgt_Un:
|
||||
case Code.Bgt_Un_S: return Emulate_Bgt_Un();
|
||||
case Code.Ble:
|
||||
case Code.Ble_S: return Emulate_Ble();
|
||||
case Code.Ble_Un:
|
||||
case Code.Ble_Un_S: return Emulate_Ble_Un();
|
||||
case Code.Blt:
|
||||
case Code.Blt_S: return Emulate_Blt();
|
||||
case Code.Blt_Un:
|
||||
case Code.Blt_Un_S: return Emulate_Blt_Un();
|
||||
case Code.Bne_Un:
|
||||
case Code.Bne_Un_S: return Emulate_Bne_Un();
|
||||
case Code.Brfalse:
|
||||
case Code.Brfalse_S:return Emulate_Brfalse();
|
||||
case Code.Brtrue:
|
||||
case Code.Brtrue_S: return Emulate_Brtrue();
|
||||
case Code.Switch: return Emulate_Switch();
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool EmulateBranch(int stackArgs, Bool3 cond) {
|
||||
if (cond == Bool3.Unknown)
|
||||
return false;
|
||||
return EmulateBranch(stackArgs, cond == Bool3.True);
|
||||
}
|
||||
|
||||
bool EmulateBranch(int stackArgs, bool isTaken) {
|
||||
branchHandler.HandleNormal(stackArgs, isTaken);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Emulate_Br() => EmulateBranch(0, true);
|
||||
|
||||
bool Emulate_Beq() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareEq((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareEq((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareEq((Real8Value)val1, (Real8Value)val2));
|
||||
else if (val1.IsNull() && val2.IsNull())
|
||||
return EmulateBranch(2, true);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Bne_Un() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareNeq((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareNeq((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareNeq((Real8Value)val1, (Real8Value)val2));
|
||||
else if (val1.IsNull() && val2.IsNull())
|
||||
return EmulateBranch(2, false);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Bge() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareGe((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareGe((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareGe((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Bge_Un() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareGe_Un((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareGe_Un((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareGe_Un((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Bgt() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareGt((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareGt((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareGt((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Bgt_Un() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareGt_Un((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareGt_Un((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareGt_Un((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Ble() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareLe((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareLe((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareLe((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Ble_Un() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareLe_Un((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareLe_Un((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareLe_Un((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Blt() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareLt((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareLt((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareLt((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Blt_Un() {
|
||||
var val2 = instructionEmulator.Pop();
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32() && val2.IsInt32())
|
||||
return EmulateBranch(2, Int32Value.CompareLt_Un((Int32Value)val1, (Int32Value)val2));
|
||||
else if (val1.IsInt64() && val2.IsInt64())
|
||||
return EmulateBranch(2, Int64Value.CompareLt_Un((Int64Value)val1, (Int64Value)val2));
|
||||
else if (val1.IsReal8() && val2.IsReal8())
|
||||
return EmulateBranch(2, Real8Value.CompareLt_Un((Real8Value)val1, (Real8Value)val2));
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Brfalse() {
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32())
|
||||
return EmulateBranch(1, Int32Value.CompareFalse((Int32Value)val1));
|
||||
else if (val1.IsInt64())
|
||||
return EmulateBranch(1, Int64Value.CompareFalse((Int64Value)val1));
|
||||
else if (val1.IsReal8())
|
||||
return EmulateBranch(1, Real8Value.CompareFalse((Real8Value)val1));
|
||||
else if (val1.IsNull())
|
||||
return EmulateBranch(1, true);
|
||||
else if (val1.IsObject() || val1.IsString())
|
||||
return EmulateBranch(1, false);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Brtrue() {
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (val1.IsInt32())
|
||||
return EmulateBranch(1, Int32Value.CompareTrue((Int32Value)val1));
|
||||
else if (val1.IsInt64())
|
||||
return EmulateBranch(1, Int64Value.CompareTrue((Int64Value)val1));
|
||||
else if (val1.IsReal8())
|
||||
return EmulateBranch(1, Real8Value.CompareTrue((Real8Value)val1));
|
||||
else if (val1.IsNull())
|
||||
return EmulateBranch(1, false);
|
||||
else if (val1.IsObject() || val1.IsString())
|
||||
return EmulateBranch(1, true);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Emulate_Switch() {
|
||||
var val1 = instructionEmulator.Pop();
|
||||
|
||||
if (!val1.IsInt32())
|
||||
return false;
|
||||
return branchHandler.HandleSwitch((Int32Value)val1);
|
||||
}
|
||||
}
|
||||
}
|
67
de4dot/de4dot.blocks/cflow/CachedCflowDeobfuscator.cs
Normal file
67
de4dot/de4dot.blocks/cflow/CachedCflowDeobfuscator.cs
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
// Only deobfuscates a method once. A copy of the method (now deobfuscated) is returned.
|
||||
public class CachedCflowDeobfuscator {
|
||||
BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator();
|
||||
Dictionary<MethodDef, MethodDef> deobfuscated = new Dictionary<MethodDef, MethodDef>();
|
||||
|
||||
public CachedCflowDeobfuscator() {
|
||||
}
|
||||
|
||||
public CachedCflowDeobfuscator(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscators) => Add(blocksDeobfuscators);
|
||||
|
||||
public void Add(IEnumerable<IBlocksDeobfuscator> blocksDeobfuscators) {
|
||||
foreach (var bd in blocksDeobfuscators)
|
||||
cflowDeobfuscator.Add(bd);
|
||||
}
|
||||
|
||||
public void Add(IBlocksDeobfuscator blocksDeobfuscator) => cflowDeobfuscator.Add(blocksDeobfuscator);
|
||||
|
||||
public MethodDef Deobfuscate(MethodDef method) {
|
||||
if (deobfuscated.TryGetValue(method, out var deobfuscatedMethod))
|
||||
return deobfuscatedMethod;
|
||||
|
||||
if (method.Body == null || method.Body.Instructions.Count == 0) {
|
||||
deobfuscated[method] = method;
|
||||
return method;
|
||||
}
|
||||
|
||||
deobfuscatedMethod = DotNetUtils.Clone(method);
|
||||
deobfuscated[method] = deobfuscatedMethod;
|
||||
|
||||
var blocks = new Blocks(deobfuscatedMethod);
|
||||
Deobfuscate(blocks);
|
||||
blocks.GetCode(out var allInstructions, out var allExceptionHandlers);
|
||||
DotNetUtils.RestoreBody(deobfuscatedMethod, allInstructions, allExceptionHandlers);
|
||||
|
||||
return deobfuscatedMethod;
|
||||
}
|
||||
|
||||
void Deobfuscate(Blocks blocks) {
|
||||
cflowDeobfuscator.Initialize(blocks);
|
||||
cflowDeobfuscator.Deobfuscate();
|
||||
}
|
||||
}
|
||||
}
|
53
de4dot/de4dot.blocks/cflow/CflowDeobfuscator.cs
Normal file
53
de4dot/de4dot.blocks/cflow/CflowDeobfuscator.cs
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public class CflowDeobfuscator : ICflowDeobfuscator {
|
||||
BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator();
|
||||
|
||||
public CflowDeobfuscator() {
|
||||
}
|
||||
|
||||
public CflowDeobfuscator(IBlocksDeobfuscator blocksDeobfuscator) => cflowDeobfuscator.Add(blocksDeobfuscator);
|
||||
|
||||
public void Deobfuscate(MethodDef method) =>
|
||||
Deobfuscate(method, (blocks) => {
|
||||
cflowDeobfuscator.Initialize(blocks);
|
||||
cflowDeobfuscator.Deobfuscate();
|
||||
});
|
||||
|
||||
static bool HasNonEmptyBody(MethodDef method) => method.Body != null && method.Body.Instructions.Count > 0;
|
||||
|
||||
void Deobfuscate(MethodDef method, Action<Blocks> handler) {
|
||||
if (HasNonEmptyBody(method)) {
|
||||
var blocks = new Blocks(method);
|
||||
|
||||
handler(blocks);
|
||||
|
||||
blocks.GetCode(out var allInstructions, out var allExceptionHandlers);
|
||||
DotNetUtils.RestoreBody(method, allInstructions, allExceptionHandlers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
de4dot/de4dot.blocks/cflow/CflowUtils.cs
Normal file
35
de4dot/de4dot.blocks/cflow/CflowUtils.cs
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
static class CflowUtils {
|
||||
public static Block GetSwitchTarget(IList<Block> targets, Block fallThrough, Int32Value intValue) {
|
||||
if (!intValue.AllBitsValid())
|
||||
return null;
|
||||
|
||||
int index = intValue.Value;
|
||||
if (targets == null || index < 0 || index >= targets.Count)
|
||||
return fallThrough;
|
||||
else
|
||||
return targets[index];
|
||||
}
|
||||
}
|
||||
}
|
244
de4dot/de4dot.blocks/cflow/ConstantsFolder.cs
Normal file
244
de4dot/de4dot.blocks/cflow/ConstantsFolder.cs
Normal file
@ -0,0 +1,244 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
// Very simple constants folder which is all that's needed at the moment
|
||||
class ConstantsFolder : BlockDeobfuscator {
|
||||
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
||||
IList<Parameter> args;
|
||||
|
||||
public bool DisableNewCode { get; set; }
|
||||
|
||||
protected override void Initialize(List<Block> allBlocks) {
|
||||
base.Initialize(allBlocks);
|
||||
args = blocks.Method.Parameters;
|
||||
}
|
||||
|
||||
protected override bool Deobfuscate(Block block) {
|
||||
bool modified = false;
|
||||
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == block);
|
||||
var instrs = block.Instructions;
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var instr = instrs[i];
|
||||
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldarg:
|
||||
case Code.Ldarg_0:
|
||||
case Code.Ldarg_1:
|
||||
case Code.Ldarg_2:
|
||||
case Code.Ldarg_3:
|
||||
case Code.Ldarg_S:
|
||||
modified |= FixLoadInstruction(block, i, instructionEmulator.GetArg(instr.Instruction.GetParameter(args)));
|
||||
break;
|
||||
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
case Code.Ldloc_S:
|
||||
modified |= FixLoadInstruction(block, i, instructionEmulator.GetLocal(instr.Instruction.GetLocal(blocks.Locals)));
|
||||
break;
|
||||
|
||||
case Code.Ldarga:
|
||||
case Code.Ldarga_S:
|
||||
instructionEmulator.MakeArgUnknown((Parameter)instr.Operand);
|
||||
break;
|
||||
|
||||
case Code.Ldloca:
|
||||
case Code.Ldloca_S:
|
||||
instructionEmulator.MakeLocalUnknown((Local)instr.Operand);
|
||||
break;
|
||||
|
||||
case Code.Add:
|
||||
case Code.Add_Ovf:
|
||||
case Code.Add_Ovf_Un:
|
||||
case Code.And:
|
||||
case Code.Ceq:
|
||||
case Code.Cgt:
|
||||
case Code.Cgt_Un:
|
||||
case Code.Clt:
|
||||
case Code.Clt_Un:
|
||||
case Code.Conv_I:
|
||||
case Code.Conv_I1:
|
||||
case Code.Conv_I2:
|
||||
case Code.Conv_I4:
|
||||
case Code.Conv_I8:
|
||||
case Code.Conv_Ovf_I:
|
||||
case Code.Conv_Ovf_I_Un:
|
||||
case Code.Conv_Ovf_I1:
|
||||
case Code.Conv_Ovf_I1_Un:
|
||||
case Code.Conv_Ovf_I2:
|
||||
case Code.Conv_Ovf_I2_Un:
|
||||
case Code.Conv_Ovf_I4:
|
||||
case Code.Conv_Ovf_I4_Un:
|
||||
case Code.Conv_Ovf_I8:
|
||||
case Code.Conv_Ovf_I8_Un:
|
||||
case Code.Conv_Ovf_U:
|
||||
case Code.Conv_Ovf_U_Un:
|
||||
case Code.Conv_Ovf_U1:
|
||||
case Code.Conv_Ovf_U1_Un:
|
||||
case Code.Conv_Ovf_U2:
|
||||
case Code.Conv_Ovf_U2_Un:
|
||||
case Code.Conv_Ovf_U4:
|
||||
case Code.Conv_Ovf_U4_Un:
|
||||
case Code.Conv_Ovf_U8:
|
||||
case Code.Conv_Ovf_U8_Un:
|
||||
case Code.Conv_R_Un:
|
||||
case Code.Conv_R4:
|
||||
case Code.Conv_R8:
|
||||
case Code.Conv_U:
|
||||
case Code.Conv_U1:
|
||||
case Code.Conv_U2:
|
||||
case Code.Conv_U4:
|
||||
case Code.Conv_U8:
|
||||
case Code.Div:
|
||||
case Code.Div_Un:
|
||||
case Code.Dup:
|
||||
case Code.Mul:
|
||||
case Code.Mul_Ovf:
|
||||
case Code.Mul_Ovf_Un:
|
||||
case Code.Neg:
|
||||
case Code.Not:
|
||||
case Code.Or:
|
||||
case Code.Rem:
|
||||
case Code.Rem_Un:
|
||||
case Code.Shl:
|
||||
case Code.Shr:
|
||||
case Code.Shr_Un:
|
||||
case Code.Sub:
|
||||
case Code.Sub_Ovf:
|
||||
case Code.Sub_Ovf_Un:
|
||||
case Code.Xor:
|
||||
if (DisableNewCode)
|
||||
break;
|
||||
if (i + 1 < instrs.Count && instrs[i + 1].OpCode.Code == Code.Pop)
|
||||
break;
|
||||
if (!VerifyValidArgs(instr.Instruction))
|
||||
break;
|
||||
instructionEmulator.Emulate(instr.Instruction);
|
||||
var tos = instructionEmulator.Peek();
|
||||
Instruction newInstr = null;
|
||||
if (tos.IsInt32()) {
|
||||
var val = (Int32Value)tos;
|
||||
if (val.AllBitsValid())
|
||||
newInstr = Instruction.CreateLdcI4(val.Value);
|
||||
}
|
||||
else if (tos.IsInt64()) {
|
||||
var val = (Int64Value)tos;
|
||||
if (val.AllBitsValid())
|
||||
newInstr = OpCodes.Ldc_I8.ToInstruction(val.Value);
|
||||
}
|
||||
else if (tos.IsReal8()) {
|
||||
var val = (Real8Value)tos;
|
||||
if (val.IsValid)
|
||||
newInstr = GetLoadRealInstruction(val.Value);
|
||||
}
|
||||
if (newInstr != null) {
|
||||
block.Insert(i + 1, Instruction.Create(OpCodes.Pop));
|
||||
block.Insert(i + 2, newInstr);
|
||||
i += 2;
|
||||
modified = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
instructionEmulator.Emulate(instr.Instruction);
|
||||
}
|
||||
catch (NullReferenceException) {
|
||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool VerifyValidArgs(Instruction instr) {
|
||||
instr.CalculateStackUsage(out int pushes, out int pops);
|
||||
if (pops < 0)
|
||||
return false;
|
||||
|
||||
bool retVal;
|
||||
Value val2, val1;
|
||||
switch (pops) {
|
||||
case 0:
|
||||
return true;
|
||||
|
||||
case 1:
|
||||
val1 = instructionEmulator.Pop();
|
||||
retVal = VerifyValidArg(val1);
|
||||
instructionEmulator.Push(val1);
|
||||
return retVal;
|
||||
|
||||
case 2:
|
||||
val2 = instructionEmulator.Pop();
|
||||
val1 = instructionEmulator.Pop();
|
||||
retVal = VerifyValidArg(val2) && VerifyValidArg(val1);
|
||||
instructionEmulator.Push(val1);
|
||||
instructionEmulator.Push(val2);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool VerifyValidArg(Value value) {
|
||||
if (value.IsInt32())
|
||||
return ((Int32Value)value).AllBitsValid();
|
||||
if (value.IsInt64())
|
||||
return ((Int64Value)value).AllBitsValid();
|
||||
if (value.IsReal8())
|
||||
return ((Real8Value)value).IsValid;
|
||||
return false;
|
||||
}
|
||||
|
||||
static Instruction GetLoadRealInstruction(double value) {
|
||||
var floatVal = (float)value;
|
||||
if (floatVal == value || double.IsNaN(value))
|
||||
return OpCodes.Ldc_R4.ToInstruction(floatVal);
|
||||
return OpCodes.Ldc_R8.ToInstruction(value);
|
||||
}
|
||||
|
||||
bool FixLoadInstruction(Block block, int index, Value value) {
|
||||
if (value.IsInt32()) {
|
||||
var intValue = (Int32Value)value;
|
||||
if (!intValue.AllBitsValid())
|
||||
return false;
|
||||
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));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
376
de4dot/de4dot.blocks/cflow/DeadCodeRemover.cs
Normal file
376
de4dot/de4dot.blocks/cflow/DeadCodeRemover.cs
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
// Removes dead code that is the result of one of our optimizations, or created by the
|
||||
// obfuscator.
|
||||
class DeadCodeRemover : BlockDeobfuscator {
|
||||
List<int> allDeadInstructions = new List<int>();
|
||||
InstructionExpressionFinder instructionExpressionFinder = new InstructionExpressionFinder();
|
||||
|
||||
protected override bool Deobfuscate(Block block) {
|
||||
allDeadInstructions.Clear();
|
||||
|
||||
bool modified = false;
|
||||
var instructions = block.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var instr = instructions[i];
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Nop:
|
||||
// The NOP is recreated if the block is empty so don't remove it if it's
|
||||
// the only instruction.
|
||||
if (instructions.Count > 1)
|
||||
allDeadInstructions.Add(i);
|
||||
break;
|
||||
|
||||
case Code.Dup:
|
||||
if (i + 1 >= instructions.Count)
|
||||
break;
|
||||
if (instructions[i + 1].OpCode.Code != Code.Pop)
|
||||
break;
|
||||
allDeadInstructions.Add(i);
|
||||
allDeadInstructions.Add(i + 1);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case Code.Leave:
|
||||
case Code.Leave_S:
|
||||
case Code.Endfinally:
|
||||
case Code.Pop:
|
||||
instructionExpressionFinder.Initialize(block, false);
|
||||
if (!instructionExpressionFinder.Find(i))
|
||||
continue;
|
||||
if (!OkInstructions(block, instructionExpressionFinder.DeadInstructions))
|
||||
continue;
|
||||
allDeadInstructions.AddRange(instructionExpressionFinder.DeadInstructions);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allDeadInstructions.Count > 0) {
|
||||
block.Remove(allDeadInstructions);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool OkInstructions(Block block, IEnumerable<int> indexes) {
|
||||
foreach (var index in indexes) {
|
||||
var instr = block.Instructions[index];
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Add:
|
||||
case Code.Add_Ovf:
|
||||
case Code.Add_Ovf_Un:
|
||||
case Code.And:
|
||||
case Code.Arglist:
|
||||
case Code.Beq:
|
||||
case Code.Beq_S:
|
||||
case Code.Bge:
|
||||
case Code.Bge_S:
|
||||
case Code.Bge_Un:
|
||||
case Code.Bge_Un_S:
|
||||
case Code.Bgt:
|
||||
case Code.Bgt_S:
|
||||
case Code.Bgt_Un:
|
||||
case Code.Bgt_Un_S:
|
||||
case Code.Ble:
|
||||
case Code.Ble_S:
|
||||
case Code.Ble_Un:
|
||||
case Code.Ble_Un_S:
|
||||
case Code.Blt:
|
||||
case Code.Blt_S:
|
||||
case Code.Blt_Un:
|
||||
case Code.Blt_Un_S:
|
||||
case Code.Bne_Un:
|
||||
case Code.Bne_Un_S:
|
||||
case Code.Box:
|
||||
case Code.Br:
|
||||
case Code.Br_S:
|
||||
case Code.Break:
|
||||
case Code.Brfalse:
|
||||
case Code.Brfalse_S:
|
||||
case Code.Brtrue:
|
||||
case Code.Brtrue_S:
|
||||
case Code.Castclass:
|
||||
case Code.Ceq:
|
||||
case Code.Cgt:
|
||||
case Code.Cgt_Un:
|
||||
case Code.Ckfinite:
|
||||
case Code.Clt:
|
||||
case Code.Clt_Un:
|
||||
case Code.Constrained:
|
||||
case Code.Conv_I:
|
||||
case Code.Conv_I1:
|
||||
case Code.Conv_I2:
|
||||
case Code.Conv_I4:
|
||||
case Code.Conv_I8:
|
||||
case Code.Conv_Ovf_I:
|
||||
case Code.Conv_Ovf_I1:
|
||||
case Code.Conv_Ovf_I1_Un:
|
||||
case Code.Conv_Ovf_I2:
|
||||
case Code.Conv_Ovf_I2_Un:
|
||||
case Code.Conv_Ovf_I4:
|
||||
case Code.Conv_Ovf_I4_Un:
|
||||
case Code.Conv_Ovf_I8:
|
||||
case Code.Conv_Ovf_I8_Un:
|
||||
case Code.Conv_Ovf_I_Un:
|
||||
case Code.Conv_Ovf_U:
|
||||
case Code.Conv_Ovf_U1:
|
||||
case Code.Conv_Ovf_U1_Un:
|
||||
case Code.Conv_Ovf_U2:
|
||||
case Code.Conv_Ovf_U2_Un:
|
||||
case Code.Conv_Ovf_U4:
|
||||
case Code.Conv_Ovf_U4_Un:
|
||||
case Code.Conv_Ovf_U8:
|
||||
case Code.Conv_Ovf_U8_Un:
|
||||
case Code.Conv_Ovf_U_Un:
|
||||
case Code.Conv_R4:
|
||||
case Code.Conv_R8:
|
||||
case Code.Conv_R_Un:
|
||||
case Code.Conv_U:
|
||||
case Code.Conv_U1:
|
||||
case Code.Conv_U2:
|
||||
case Code.Conv_U4:
|
||||
case Code.Conv_U8:
|
||||
case Code.Div:
|
||||
case Code.Div_Un:
|
||||
case Code.Dup:
|
||||
case Code.Endfilter:
|
||||
case Code.Endfinally:
|
||||
case Code.Isinst:
|
||||
case Code.Jmp:
|
||||
case Code.Ldarg:
|
||||
case Code.Ldarg_0:
|
||||
case Code.Ldarg_1:
|
||||
case Code.Ldarg_2:
|
||||
case Code.Ldarg_3:
|
||||
case Code.Ldarg_S:
|
||||
case Code.Ldarga:
|
||||
case Code.Ldarga_S:
|
||||
case Code.Ldc_I4:
|
||||
case Code.Ldc_I4_0:
|
||||
case Code.Ldc_I4_1:
|
||||
case Code.Ldc_I4_2:
|
||||
case Code.Ldc_I4_3:
|
||||
case Code.Ldc_I4_4:
|
||||
case Code.Ldc_I4_5:
|
||||
case Code.Ldc_I4_6:
|
||||
case Code.Ldc_I4_7:
|
||||
case Code.Ldc_I4_8:
|
||||
case Code.Ldc_I4_M1:
|
||||
case Code.Ldc_I4_S:
|
||||
case Code.Ldc_I8:
|
||||
case Code.Ldc_R4:
|
||||
case Code.Ldc_R8:
|
||||
case Code.Ldelem:
|
||||
case Code.Ldelem_I:
|
||||
case Code.Ldelem_I1:
|
||||
case Code.Ldelem_I2:
|
||||
case Code.Ldelem_I4:
|
||||
case Code.Ldelem_I8:
|
||||
case Code.Ldelem_R4:
|
||||
case Code.Ldelem_R8:
|
||||
case Code.Ldelem_Ref:
|
||||
case Code.Ldelem_U1:
|
||||
case Code.Ldelem_U2:
|
||||
case Code.Ldelem_U4:
|
||||
case Code.Ldelema:
|
||||
case Code.Ldfld:
|
||||
case Code.Ldflda:
|
||||
case Code.Ldftn:
|
||||
case Code.Ldind_I:
|
||||
case Code.Ldind_I1:
|
||||
case Code.Ldind_I2:
|
||||
case Code.Ldind_I4:
|
||||
case Code.Ldind_I8:
|
||||
case Code.Ldind_R4:
|
||||
case Code.Ldind_R8:
|
||||
case Code.Ldind_Ref:
|
||||
case Code.Ldind_U1:
|
||||
case Code.Ldind_U2:
|
||||
case Code.Ldind_U4:
|
||||
case Code.Ldlen:
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Ldloca:
|
||||
case Code.Ldloca_S:
|
||||
case Code.Ldnull:
|
||||
case Code.Ldobj:
|
||||
case Code.Ldsfld:
|
||||
case Code.Ldsflda:
|
||||
case Code.Ldstr:
|
||||
case Code.Ldtoken:
|
||||
case Code.Ldvirtftn:
|
||||
case Code.Leave:
|
||||
case Code.Leave_S:
|
||||
case Code.Localloc:
|
||||
case Code.Mkrefany:
|
||||
case Code.Mul:
|
||||
case Code.Mul_Ovf:
|
||||
case Code.Mul_Ovf_Un:
|
||||
case Code.Neg:
|
||||
case Code.Newarr:
|
||||
case Code.Nop:
|
||||
case Code.Not:
|
||||
case Code.Or:
|
||||
case Code.Pop:
|
||||
case Code.Readonly:
|
||||
case Code.Refanytype:
|
||||
case Code.Refanyval:
|
||||
case Code.Rem:
|
||||
case Code.Rem_Un:
|
||||
case Code.Ret:
|
||||
case Code.Rethrow:
|
||||
case Code.Shl:
|
||||
case Code.Shr:
|
||||
case Code.Shr_Un:
|
||||
case Code.Sizeof:
|
||||
case Code.Sub:
|
||||
case Code.Sub_Ovf:
|
||||
case Code.Sub_Ovf_Un:
|
||||
case Code.Switch:
|
||||
case Code.Tailcall:
|
||||
case Code.Throw:
|
||||
case Code.Unaligned:
|
||||
case Code.Unbox:
|
||||
case Code.Unbox_Any:
|
||||
case Code.Volatile:
|
||||
case Code.Xor:
|
||||
break;
|
||||
|
||||
case Code.Call:
|
||||
case Code.Calli:
|
||||
case Code.Callvirt:
|
||||
case Code.Cpblk:
|
||||
case Code.Cpobj:
|
||||
case Code.Initblk:
|
||||
case Code.Initobj:
|
||||
case Code.Newobj:
|
||||
case Code.Starg:
|
||||
case Code.Starg_S:
|
||||
case Code.Stelem:
|
||||
case Code.Stelem_I:
|
||||
case Code.Stelem_I1:
|
||||
case Code.Stelem_I2:
|
||||
case Code.Stelem_I4:
|
||||
case Code.Stelem_I8:
|
||||
case Code.Stelem_R4:
|
||||
case Code.Stelem_R8:
|
||||
case Code.Stelem_Ref:
|
||||
case Code.Stfld:
|
||||
case Code.Stind_I:
|
||||
case Code.Stind_I1:
|
||||
case Code.Stind_I2:
|
||||
case Code.Stind_I4:
|
||||
case Code.Stind_I8:
|
||||
case Code.Stind_R4:
|
||||
case Code.Stind_R8:
|
||||
case Code.Stind_Ref:
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stobj:
|
||||
case Code.Stsfld:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class InstructionExpressionFinder {
|
||||
List<int> deadInstructions = new List<int>();
|
||||
Block block;
|
||||
bool methodHasReturnValue;
|
||||
|
||||
public List<int> DeadInstructions => deadInstructions;
|
||||
|
||||
public void Initialize(Block block, bool methodHasReturnValue) {
|
||||
deadInstructions.Clear();
|
||||
this.block = block;
|
||||
this.methodHasReturnValue = methodHasReturnValue;
|
||||
}
|
||||
|
||||
public bool Find(int index) => Find(ref index, true);
|
||||
|
||||
bool Find(ref int index, bool addIt) {
|
||||
if (index < 0)
|
||||
return false;
|
||||
|
||||
var startInstr = block.Instructions[index];
|
||||
CalculateStackUsage(startInstr.Instruction, false, out int startInstrPushes, out int startInstrPops);
|
||||
|
||||
// Don't add it if it clears the stack (eg. leave)
|
||||
if (addIt && startInstrPops >= 0)
|
||||
AddIndex(index);
|
||||
|
||||
if (startInstrPops == 0)
|
||||
return true;
|
||||
|
||||
while (index > 0) {
|
||||
var instr = block.Instructions[index - 1];
|
||||
if (startInstrPops == 0 && instr.OpCode.OpCodeType != OpCodeType.Prefix)
|
||||
break;
|
||||
|
||||
CalculateStackUsage(instr.Instruction, methodHasReturnValue, out int pushes, out int pops);
|
||||
if (pops < 0)
|
||||
break; // eg. leave
|
||||
index--;
|
||||
|
||||
if (pops > 0) { // if instr uses any args
|
||||
bool otherExpr = pops > 0 && pushes == 0;
|
||||
if (!Find(ref index, addIt && !otherExpr))
|
||||
break;
|
||||
}
|
||||
else if (pushes != 0 || pops != 0) {
|
||||
if (addIt)
|
||||
AddIndex(index);
|
||||
}
|
||||
if (pushes > 0 && startInstrPops >= 0) {
|
||||
if (pushes > startInstrPops)
|
||||
return false;
|
||||
startInstrPops -= pushes;
|
||||
}
|
||||
}
|
||||
|
||||
return startInstrPops <= 0;
|
||||
}
|
||||
|
||||
void AddIndex(int index) => deadInstructions.Add(index);
|
||||
}
|
||||
|
||||
static void CalculateStackUsage(Instruction instr, bool methodHasReturnValue, out int pushes, out int pops) =>
|
||||
instr.CalculateStackUsage(false, out pushes, out pops);
|
||||
}
|
||||
}
|
156
de4dot/de4dot.blocks/cflow/DeadStoreRemover.cs
Normal file
156
de4dot/de4dot.blocks/cflow/DeadStoreRemover.cs
Normal file
@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
// Removes dead stores by replacing the stloc with a pop. Other optimizations will notice it's
|
||||
// dead code and remove it.
|
||||
// I've only seen Xenocode generate this kind of code, so the code below is a special case of
|
||||
// the more general case.
|
||||
class DeadStoreRemover : IBlocksDeobfuscator {
|
||||
Blocks blocks;
|
||||
List<Block> allBlocks;
|
||||
List<AccessFlags> localFlags = new List<AccessFlags>();
|
||||
List<bool> deadLocals = new List<bool>();
|
||||
|
||||
[Flags]
|
||||
enum AccessFlags {
|
||||
None = 0,
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
}
|
||||
|
||||
public bool ExecuteIfNotModified { get; set; }
|
||||
|
||||
public void DeobfuscateBegin(Blocks blocks) => this.blocks = blocks;
|
||||
|
||||
public bool Deobfuscate(List<Block> allBlocks) {
|
||||
this.allBlocks = allBlocks;
|
||||
return Remove();
|
||||
}
|
||||
|
||||
bool Remove() {
|
||||
if (blocks.Locals.Count == 0)
|
||||
return false;
|
||||
|
||||
localFlags.Clear();
|
||||
deadLocals.Clear();
|
||||
for (int i = 0; i < blocks.Locals.Count; i++) {
|
||||
localFlags.Add(AccessFlags.None);
|
||||
deadLocals.Add(false);
|
||||
}
|
||||
|
||||
FindLoadStores();
|
||||
|
||||
bool deadStores = false;
|
||||
for (int i = 0; i < blocks.Locals.Count; i++) {
|
||||
var flags = localFlags[i];
|
||||
if ((flags & AccessFlags.Read) == AccessFlags.None) {
|
||||
deadLocals[i] = true;
|
||||
deadStores = true;
|
||||
}
|
||||
}
|
||||
if (!deadStores)
|
||||
return false;
|
||||
|
||||
return RemoveDeadStores();
|
||||
}
|
||||
|
||||
void FindLoadStores() {
|
||||
foreach (var block in allBlocks) {
|
||||
foreach (var instr in block.Instructions) {
|
||||
Local local;
|
||||
AccessFlags flags;
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
local = Instr.GetLocalVar(blocks.Locals, instr);
|
||||
flags = AccessFlags.Read;
|
||||
break;
|
||||
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
local = Instr.GetLocalVar(blocks.Locals, instr);
|
||||
flags = AccessFlags.Write;
|
||||
break;
|
||||
|
||||
case Code.Ldloca_S:
|
||||
case Code.Ldloca:
|
||||
local = instr.Operand as Local;
|
||||
flags = AccessFlags.Read | AccessFlags.Write;
|
||||
break;
|
||||
|
||||
default:
|
||||
local = null;
|
||||
flags = AccessFlags.None;
|
||||
break;
|
||||
}
|
||||
|
||||
if (local == null)
|
||||
continue;
|
||||
localFlags[local.Index] |= flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoveDeadStores() {
|
||||
bool modified = false;
|
||||
foreach (var block in allBlocks) {
|
||||
var instructions = block.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var instr = instructions[i];
|
||||
Local local;
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
local = Instr.GetLocalVar(blocks.Locals, instr);
|
||||
break;
|
||||
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (local == null)
|
||||
continue;
|
||||
if (!deadLocals[local.Index])
|
||||
continue;
|
||||
instructions[i] = new Instr(OpCodes.Pop.ToInstruction());
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
}
|
48
de4dot/de4dot.blocks/cflow/DupBlockDeobfuscator.cs
Normal file
48
de4dot/de4dot.blocks/cflow/DupBlockDeobfuscator.cs
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
// If a block is just a dup followed by a bcc, try to append the block
|
||||
// to all its sources. Will fix some SA assemblies.
|
||||
class DupBlockCflowDeobfuscator : BlockDeobfuscator {
|
||||
protected override bool Deobfuscate(Block block) {
|
||||
if (block.Instructions.Count != 2)
|
||||
return false;
|
||||
if (block.Instructions[0].OpCode.Code != Code.Dup)
|
||||
return false;
|
||||
if (!block.LastInstr.IsConditionalBranch() && block.LastInstr.OpCode.Code != Code.Switch)
|
||||
return false;
|
||||
|
||||
bool modified = false;
|
||||
foreach (var source in new List<Block>(block.Sources)) {
|
||||
if (source.GetOnlyTarget() != block)
|
||||
continue;
|
||||
if (!source.CanAppend(block))
|
||||
continue;
|
||||
|
||||
source.Append(block);
|
||||
modified = true;
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
}
|
31
de4dot/de4dot.blocks/cflow/IBlocksDeobfuscator.cs
Normal file
31
de4dot/de4dot.blocks/cflow/IBlocksDeobfuscator.cs
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public interface IBlocksDeobfuscator {
|
||||
bool ExecuteIfNotModified { get; }
|
||||
|
||||
void DeobfuscateBegin(Blocks blocks);
|
||||
|
||||
// Returns true if something was updated
|
||||
bool Deobfuscate(List<Block> allBlocks);
|
||||
}
|
||||
}
|
26
de4dot/de4dot.blocks/cflow/ICflowDeobfuscator.cs
Normal file
26
de4dot/de4dot.blocks/cflow/ICflowDeobfuscator.cs
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using dnlib.DotNet;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public interface ICflowDeobfuscator {
|
||||
void Deobfuscate(MethodDef method);
|
||||
}
|
||||
}
|
1208
de4dot/de4dot.blocks/cflow/InstructionEmulator.cs
Normal file
1208
de4dot/de4dot.blocks/cflow/InstructionEmulator.cs
Normal file
File diff suppressed because it is too large
Load Diff
622
de4dot/de4dot.blocks/cflow/Int32Value.cs
Normal file
622
de4dot/de4dot.blocks/cflow/Int32Value.cs
Normal file
@ -0,0 +1,622 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public class Int32Value : Value {
|
||||
public static readonly Int32Value Zero = new Int32Value(0);
|
||||
public static readonly Int32Value One = new Int32Value(1);
|
||||
|
||||
internal const uint NO_UNKNOWN_BITS = uint.MaxValue;
|
||||
public readonly int Value;
|
||||
public readonly uint ValidMask;
|
||||
|
||||
public Int32Value(int value)
|
||||
: base(ValueType.Int32) {
|
||||
Value = value;
|
||||
ValidMask = NO_UNKNOWN_BITS;
|
||||
}
|
||||
|
||||
public Int32Value(int value, uint validMask)
|
||||
: base(ValueType.Int32) {
|
||||
Value = value;
|
||||
ValidMask = validMask;
|
||||
}
|
||||
|
||||
public bool HasUnknownBits() => ValidMask != NO_UNKNOWN_BITS;
|
||||
public bool AllBitsValid() => !HasUnknownBits();
|
||||
bool IsBitValid(int n) => IsBitValid(ValidMask, n);
|
||||
static bool IsBitValid(uint validMask, int n) => (validMask & (1U << n)) != 0;
|
||||
bool AreBitsValid(uint bitsToTest) => (ValidMask & bitsToTest) == bitsToTest;
|
||||
public static Int32Value CreateUnknownBool() => new Int32Value(0, NO_UNKNOWN_BITS << 1);
|
||||
public static Int32Value CreateUnknownUInt8() => new Int32Value(0, NO_UNKNOWN_BITS << 8);
|
||||
public static Int32Value CreateUnknownUInt16() => new Int32Value(0, NO_UNKNOWN_BITS << 16);
|
||||
public static Int32Value CreateUnknown() => new Int32Value(0, 0U);
|
||||
public bool IsZero() => HasValue(0);
|
||||
public bool IsNonZero() => ((uint)Value & ValidMask) != 0;
|
||||
public bool HasValue(int value) => AllBitsValid() && Value == value;
|
||||
public bool HasValue(uint value) => HasValue((int)value);
|
||||
|
||||
public Int32Value ToBoolean() {
|
||||
if (IsNonZero())
|
||||
return new Int32Value(1, NO_UNKNOWN_BITS);
|
||||
if (IsZero())
|
||||
return this;
|
||||
return CreateUnknownBool();
|
||||
}
|
||||
|
||||
public Int32Value ToInt8() => Conv_I1(this);
|
||||
public Int32Value ToUInt8() => Conv_U1(this);
|
||||
public Int32Value ToInt16() => Conv_I2(this);
|
||||
public Int32Value ToUInt16() => Conv_U2(this);
|
||||
public static Int32Value Conv_U1(Int32Value a) => Conv_U1(a.Value, a.ValidMask);
|
||||
public static Int32Value Conv_U1(Int64Value a) => Conv_U1((int)a.Value, (uint)a.ValidMask);
|
||||
|
||||
public static Int32Value Conv_U1(int value, uint validMask) {
|
||||
value = (int)(byte)value;
|
||||
validMask |= NO_UNKNOWN_BITS << 8;
|
||||
return new Int32Value(value, validMask);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_U1(Real8Value a) {
|
||||
if (!a.IsValid)
|
||||
return CreateUnknownUInt8();
|
||||
return new Int32Value((int)(byte)a.Value);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_I1(Int32Value a) => Conv_I1(a.Value, a.ValidMask);
|
||||
public static Int32Value Conv_I1(Int64Value a) => Conv_I1((int)a.Value, (uint)a.ValidMask);
|
||||
|
||||
public static Int32Value Conv_I1(int value, uint validMask) {
|
||||
value = (int)(sbyte)value;
|
||||
if (IsBitValid(validMask, 7))
|
||||
validMask |= NO_UNKNOWN_BITS << 8;
|
||||
else
|
||||
validMask &= ~(NO_UNKNOWN_BITS << 8);
|
||||
return new Int32Value(value, validMask);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_I1(Real8Value a) {
|
||||
if (!a.IsValid)
|
||||
return CreateUnknown();
|
||||
return new Int32Value((int)(sbyte)a.Value);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_U2(Int32Value a) => Conv_U2(a.Value, a.ValidMask);
|
||||
public static Int32Value Conv_U2(Int64Value a) => Conv_U2((int)a.Value, (uint)a.ValidMask);
|
||||
|
||||
public static Int32Value Conv_U2(int value, uint validMask) {
|
||||
value = (int)(ushort)value;
|
||||
validMask |= NO_UNKNOWN_BITS << 16;
|
||||
return new Int32Value(value, validMask);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_U2(Real8Value a) {
|
||||
if (!a.IsValid)
|
||||
return CreateUnknownUInt16();
|
||||
return new Int32Value((int)(ushort)a.Value);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_I2(Int32Value a) => Conv_I2(a.Value, a.ValidMask);
|
||||
public static Int32Value Conv_I2(Int64Value a) => Conv_I2((int)a.Value, (uint)a.ValidMask);
|
||||
|
||||
public static Int32Value Conv_I2(int value, uint validMask) {
|
||||
value = (int)(short)value;
|
||||
if (IsBitValid(validMask, 15))
|
||||
validMask |= NO_UNKNOWN_BITS << 16;
|
||||
else
|
||||
validMask &= ~(NO_UNKNOWN_BITS << 16);
|
||||
return new Int32Value(value, validMask);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_I2(Real8Value a) {
|
||||
if (!a.IsValid)
|
||||
return CreateUnknown();
|
||||
return new Int32Value((int)(short)a.Value);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_U4(Int32Value a) => a;
|
||||
public static Int32Value Conv_U4(Int64Value a) => new Int32Value((int)(uint)a.Value, (uint)a.ValidMask);
|
||||
public static Int32Value Conv_U4(Real8Value a) {
|
||||
if (!a.IsValid)
|
||||
return CreateUnknown();
|
||||
return new Int32Value((int)(uint)a.Value);
|
||||
}
|
||||
|
||||
public static Int32Value Conv_I4(Int32Value a) => a;
|
||||
public static Int32Value Conv_I4(Int64Value a) => new Int32Value((int)a.Value, (uint)a.ValidMask);
|
||||
|
||||
public static Int32Value Conv_I4(Real8Value a) {
|
||||
if (!a.IsValid)
|
||||
return CreateUnknown();
|
||||
return new Int32Value((int)a.Value);
|
||||
}
|
||||
|
||||
bool CheckSign(uint mask) => ((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) => 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) =>
|
||||
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) => 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) =>
|
||||
new Int64Value((long)(uint)a.Value, a.ValidMask | (Int64Value.NO_UNKNOWN_BITS << 32));
|
||||
|
||||
public static Real8Value Conv_R_Un(Int32Value a) {
|
||||
if (a.AllBitsValid())
|
||||
return new Real8Value((double)(uint)a.Value);
|
||||
return Real8Value.CreateUnknown();
|
||||
}
|
||||
|
||||
public static Real8Value Conv_R4(Int32Value a) {
|
||||
if (a.AllBitsValid())
|
||||
return new Real8Value((double)(int)a.Value);
|
||||
return Real8Value.CreateUnknown();
|
||||
}
|
||||
|
||||
public static Real8Value Conv_R8(Int32Value a) {
|
||||
if (a.AllBitsValid())
|
||||
return new Real8Value((double)(int)a.Value);
|
||||
return Real8Value.CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int32Value Add(Int32Value a, Int32Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
return new Int32Value(a.Value + b.Value);
|
||||
if (ReferenceEquals(a, b))
|
||||
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);
|
||||
if (ReferenceEquals(a, b))
|
||||
return Zero;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int32Value Mul(Int32Value a, Int32Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
return new Int32Value(a.Value * b.Value);
|
||||
if (a.IsZero() || b.IsZero())
|
||||
return Zero;
|
||||
if (a.HasValue(1))
|
||||
return b;
|
||||
if (b.HasValue(1))
|
||||
return a;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int32Value Div(Int32Value a, Int32Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid()) {
|
||||
try {
|
||||
return new Int32Value(a.Value / b.Value);
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if (ReferenceEquals(a, b) && a.IsNonZero())
|
||||
return One;
|
||||
if (b.HasValue(1))
|
||||
return a;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if (ReferenceEquals(a, b) && a.IsNonZero())
|
||||
return One;
|
||||
if (b.HasValue(1))
|
||||
return a;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int32Value Rem(Int32Value a, Int32Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid()) {
|
||||
try {
|
||||
return new Int32Value(a.Value % b.Value);
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1))
|
||||
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));
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1))
|
||||
return Zero;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int32Value Neg(Int32Value a) {
|
||||
if (a.AllBitsValid())
|
||||
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, (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, (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, am & bm);
|
||||
}
|
||||
|
||||
public static Int32Value Not(Int32Value a) => new Int32Value(~a.Value, a.ValidMask);
|
||||
|
||||
public static Int32Value Shl(Int32Value a, Int32Value b) {
|
||||
if (b.HasUnknownBits())
|
||||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
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);
|
||||
}
|
||||
|
||||
public static Int32Value Shr(Int32Value a, Int32Value b) {
|
||||
if (b.HasUnknownBits())
|
||||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
if (b.Value < 0 || b.Value >= sizeof(int) * 8)
|
||||
return CreateUnknown();
|
||||
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);
|
||||
}
|
||||
|
||||
public static Int32Value Shr_Un(Int32Value a, Int32Value b) {
|
||||
if (b.HasUnknownBits())
|
||||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
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);
|
||||
}
|
||||
|
||||
public static Int32Value Create(Bool3 b) {
|
||||
switch (b) {
|
||||
case Bool3.False: return Zero;
|
||||
case Bool3.True: return One;
|
||||
default: return CreateUnknownBool();
|
||||
}
|
||||
}
|
||||
|
||||
public static Int32Value Ceq(Int32Value a, Int32Value b) => Create(CompareEq(a, b));
|
||||
public static Int32Value Cgt(Int32Value a, Int32Value b) => Create(CompareGt(a, b));
|
||||
public static Int32Value Cgt_Un(Int32Value a, Int32Value b) => Create(CompareGt_Un(a, b));
|
||||
public static Int32Value Clt(Int32Value a, Int32Value b) => Create(CompareLt(a, b));
|
||||
public static Int32Value Clt_Un(Int32Value a, Int32Value b) => 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;
|
||||
if (ReferenceEquals(a, b))
|
||||
return Bool3.True;
|
||||
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;
|
||||
if (ReferenceEquals(a, b))
|
||||
return Bool3.False;
|
||||
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;
|
||||
if (a.HasValue(int.MinValue))
|
||||
return Bool3.False; // min > x => false
|
||||
if (b.HasValue(int.MaxValue))
|
||||
return Bool3.False; // x > max => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(uint.MinValue))
|
||||
return Bool3.False; // min > x => false
|
||||
if (b.HasValue(uint.MaxValue))
|
||||
return Bool3.False; // x > max => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareGe(Int32Value a, Int32Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
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))
|
||||
return Bool3.True; // x >= min => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(uint.MaxValue))
|
||||
return Bool3.True; // max >= x => true
|
||||
if (b.HasValue(uint.MinValue))
|
||||
return Bool3.True; // x >= min => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareLe(Int32Value a, Int32Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
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))
|
||||
return Bool3.True; // x <= max => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(uint.MinValue))
|
||||
return Bool3.True; // min <= x => true
|
||||
if (b.HasValue(uint.MaxValue))
|
||||
return Bool3.True; // x <= max => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareLt(Int32Value a, Int32Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
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))
|
||||
return Bool3.False; // x < min => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(uint.MaxValue))
|
||||
return Bool3.False; // max < x => false
|
||||
if (b.HasValue(uint.MinValue))
|
||||
return Bool3.False; // x < min => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareTrue(Int32Value a) {
|
||||
if (a.AllBitsValid())
|
||||
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 (((uint)a.Value & a.ValidMask) != 0)
|
||||
return Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
if (AllBitsValid())
|
||||
return Value.ToString();
|
||||
return $"0x{Value:X8}({ValidMask:X8})";
|
||||
}
|
||||
}
|
||||
}
|
566
de4dot/de4dot.blocks/cflow/Int64Value.cs
Normal file
566
de4dot/de4dot.blocks/cflow/Int64Value.cs
Normal file
@ -0,0 +1,566 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public class Int64Value : Value {
|
||||
public static readonly Int64Value Zero = new Int64Value(0);
|
||||
public static readonly Int64Value One = new Int64Value(1);
|
||||
|
||||
internal const ulong NO_UNKNOWN_BITS = ulong.MaxValue;
|
||||
public readonly long Value;
|
||||
public readonly ulong ValidMask;
|
||||
|
||||
public Int64Value(long value)
|
||||
: base(ValueType.Int64) {
|
||||
Value = value;
|
||||
ValidMask = NO_UNKNOWN_BITS;
|
||||
}
|
||||
|
||||
public Int64Value(long value, ulong validMask)
|
||||
: base(ValueType.Int64) {
|
||||
Value = value;
|
||||
ValidMask = validMask;
|
||||
}
|
||||
|
||||
bool HasUnknownBits() => ValidMask != NO_UNKNOWN_BITS;
|
||||
public bool AllBitsValid() => !HasUnknownBits();
|
||||
bool IsBitValid(int n) => IsBitValid(ValidMask, n);
|
||||
static bool IsBitValid(ulong validMask, int n) => (validMask & (1UL << n)) != 0;
|
||||
bool AreBitsValid(ulong bitsToTest) => (ValidMask & bitsToTest) == bitsToTest;
|
||||
public static Int64Value CreateUnknown() => new Int64Value(0, 0UL);
|
||||
public bool IsZero() => HasValue(0);
|
||||
public bool IsNonZero() => ((ulong)Value & ValidMask) != 0;
|
||||
public bool HasValue(long value) => AllBitsValid() && Value == value;
|
||||
public bool HasValue(ulong value) => HasValue((long)value);
|
||||
|
||||
public static Int64Value Conv_U8(Int32Value a) {
|
||||
long value = (long)(ulong)(uint)a.Value;
|
||||
ulong validMask = a.ValidMask | (NO_UNKNOWN_BITS << 32);
|
||||
return new Int64Value(value, validMask);
|
||||
}
|
||||
|
||||
public static Int64Value Conv_U8(Int64Value a) => a;
|
||||
|
||||
public static Int64Value Conv_U8(Real8Value a) {
|
||||
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;
|
||||
if (IsBitValid(validMask, 31))
|
||||
validMask |= NO_UNKNOWN_BITS << 32;
|
||||
else
|
||||
validMask &= ~(NO_UNKNOWN_BITS << 32);
|
||||
return new Int64Value(value, validMask);
|
||||
}
|
||||
|
||||
public static Int64Value Conv_I8(Int64Value a) => a;
|
||||
|
||||
public static Int64Value Conv_I8(Real8Value a) {
|
||||
if (!a.IsValid)
|
||||
return CreateUnknown();
|
||||
return new Int64Value((long)a.Value);
|
||||
}
|
||||
|
||||
bool CheckSign(ulong mask) => ((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) => 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) => a;
|
||||
|
||||
public static Real8Value Conv_R_Un(Int64Value a) {
|
||||
if (a.AllBitsValid())
|
||||
return new Real8Value((float)(ulong)a.Value);
|
||||
return Real8Value.CreateUnknown();
|
||||
}
|
||||
|
||||
public static Real8Value Conv_R4(Int64Value a) {
|
||||
if (a.AllBitsValid())
|
||||
return new Real8Value((float)(long)a.Value);
|
||||
return Real8Value.CreateUnknown();
|
||||
}
|
||||
|
||||
public static Real8Value Conv_R8(Int64Value a) {
|
||||
if (a.AllBitsValid())
|
||||
return new Real8Value((double)(long)a.Value);
|
||||
return Real8Value.CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int64Value Add(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
return new Int64Value(a.Value + b.Value);
|
||||
if (ReferenceEquals(a, b))
|
||||
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);
|
||||
if (ReferenceEquals(a, b))
|
||||
return Zero;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int64Value Mul(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
return new Int64Value(a.Value * b.Value);
|
||||
if (a.IsZero() || b.IsZero())
|
||||
return Zero;
|
||||
if (a.HasValue(1))
|
||||
return b;
|
||||
if (b.HasValue(1))
|
||||
return a;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int64Value Div(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid()) {
|
||||
try {
|
||||
return new Int64Value(a.Value / b.Value);
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if (ReferenceEquals(a, b) && a.IsNonZero())
|
||||
return One;
|
||||
if (b.HasValue(1))
|
||||
return a;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if (ReferenceEquals(a, b) && a.IsNonZero())
|
||||
return One;
|
||||
if (b.HasValue(1))
|
||||
return a;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int64Value Rem(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid()) {
|
||||
try {
|
||||
return new Int64Value(a.Value % b.Value);
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1))
|
||||
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));
|
||||
}
|
||||
catch (ArithmeticException) {
|
||||
return CreateUnknown();
|
||||
}
|
||||
}
|
||||
if ((ReferenceEquals(a, b) && a.IsNonZero()) || b.HasValue(1))
|
||||
return Zero;
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Int64Value Neg(Int64Value a) {
|
||||
if (a.AllBitsValid())
|
||||
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;
|
||||
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;
|
||||
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 new Int64Value(av ^ bv, am & bm);
|
||||
}
|
||||
|
||||
public static Int64Value Not(Int64Value a) => new Int64Value(~a.Value, a.ValidMask);
|
||||
|
||||
public static Int64Value Shl(Int64Value a, Int32Value b) {
|
||||
if (b.HasUnknownBits())
|
||||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
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);
|
||||
}
|
||||
|
||||
public static Int64Value Shr(Int64Value a, Int32Value b) {
|
||||
if (b.HasUnknownBits())
|
||||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
if (b.Value < 0 || b.Value >= sizeof(long) * 8)
|
||||
return CreateUnknown();
|
||||
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);
|
||||
}
|
||||
|
||||
public static Int64Value Shr_Un(Int64Value a, Int32Value b) {
|
||||
if (b.HasUnknownBits())
|
||||
return CreateUnknown();
|
||||
if (b.Value == 0)
|
||||
return a;
|
||||
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);
|
||||
}
|
||||
|
||||
public static Int32Value Ceq(Int64Value a, Int64Value b) =>
|
||||
Int32Value.Create(CompareEq(a, b));
|
||||
|
||||
public static Int32Value Cgt(Int64Value a, Int64Value b) =>
|
||||
Int32Value.Create(CompareGt(a, b));
|
||||
|
||||
public static Int32Value Cgt_Un(Int64Value a, Int64Value b) =>
|
||||
Int32Value.Create(CompareGt_Un(a, b));
|
||||
|
||||
public static Int32Value Clt(Int64Value a, Int64Value b) =>
|
||||
Int32Value.Create(CompareLt(a, b));
|
||||
|
||||
public static Int32Value Clt_Un(Int64Value a, Int64Value b) =>
|
||||
Int32Value.Create(CompareLt_Un(a, b));
|
||||
|
||||
public static Bool3 CompareEq(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
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))
|
||||
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;
|
||||
if (ReferenceEquals(a, b))
|
||||
return Bool3.False;
|
||||
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;
|
||||
if (a.HasValue(long.MinValue))
|
||||
return Bool3.False; // min > x => false
|
||||
if (b.HasValue(long.MaxValue))
|
||||
return Bool3.False; // x > max => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(ulong.MinValue))
|
||||
return Bool3.False; // min > x => false
|
||||
if (b.HasValue(ulong.MaxValue))
|
||||
return Bool3.False; // x > max => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareGe(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
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))
|
||||
return Bool3.True; // x >= min => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(ulong.MaxValue))
|
||||
return Bool3.True; // max >= x => true
|
||||
if (b.HasValue(ulong.MinValue))
|
||||
return Bool3.True; // x >= min => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareLe(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
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))
|
||||
return Bool3.True; // x <= max => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(ulong.MinValue))
|
||||
return Bool3.True; // min <= x => true
|
||||
if (b.HasValue(ulong.MaxValue))
|
||||
return Bool3.True; // x <= max => true
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareLt(Int64Value a, Int64Value b) {
|
||||
if (a.AllBitsValid() && b.AllBitsValid())
|
||||
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))
|
||||
return Bool3.False; // x < min => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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;
|
||||
if (a.HasValue(ulong.MaxValue))
|
||||
return Bool3.False; // max < x => false
|
||||
if (b.HasValue(ulong.MinValue))
|
||||
return Bool3.False; // x < min => false
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
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 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 Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
if (AllBitsValid())
|
||||
return Value.ToString();
|
||||
return $"0x{Value:X8}L({ValidMask:X8})";
|
||||
}
|
||||
}
|
||||
}
|
134
de4dot/de4dot.blocks/cflow/MethodCallInliner.cs
Normal file
134
de4dot/de4dot.blocks/cflow/MethodCallInliner.cs
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public class MethodCallInliner : MethodCallInlinerBase {
|
||||
protected readonly bool inlineInstanceMethods;
|
||||
|
||||
public MethodCallInliner(bool inlineInstanceMethods) => this.inlineInstanceMethods = inlineInstanceMethods;
|
||||
|
||||
protected override bool DeobfuscateInternal() {
|
||||
bool modified = false;
|
||||
var instructions = block.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var instr = instructions[i].Instruction;
|
||||
if (instr.OpCode.Code == Code.Call)
|
||||
modified |= InlineMethod(instr, i);
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
protected virtual bool CanInline(MethodDef method) {
|
||||
if (method.GenericParameters.Count > 0)
|
||||
return false;
|
||||
if (method == blocks.Method)
|
||||
return false;
|
||||
if (!new SigComparer().Equals(method.DeclaringType, blocks.Method.DeclaringType))
|
||||
return false;
|
||||
|
||||
if (method.IsStatic)
|
||||
return true;
|
||||
if (method.IsVirtual)
|
||||
return false;
|
||||
return inlineInstanceMethods;
|
||||
}
|
||||
|
||||
protected virtual Instruction GetFirstInstruction(IList<Instruction> instrs, ref int index) =>
|
||||
DotNetUtils.GetInstruction(instrs, ref index);
|
||||
|
||||
bool InlineMethod(Instruction callInstr, int instrIndex) {
|
||||
var methodToInline = callInstr.Operand as MethodDef;
|
||||
if (methodToInline == null)
|
||||
return false;
|
||||
|
||||
if (!CanInline(methodToInline))
|
||||
return false;
|
||||
var body = methodToInline.Body;
|
||||
if (body == null)
|
||||
return false;
|
||||
|
||||
int index = 0;
|
||||
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:
|
||||
case Code.Ldarg_0:
|
||||
case Code.Ldarg_1:
|
||||
case Code.Ldarg_2:
|
||||
case Code.Ldarg_3:
|
||||
case Code.Ldarga:
|
||||
case Code.Ldarga_S:
|
||||
case Code.Call:
|
||||
case Code.Callvirt:
|
||||
case Code.Newobj:
|
||||
inlinedMethod = InlineOtherMethod(instrIndex, methodToInline, instr, index);
|
||||
break;
|
||||
|
||||
case Code.Ldc_I4:
|
||||
case Code.Ldc_I4_0:
|
||||
case Code.Ldc_I4_1:
|
||||
case Code.Ldc_I4_2:
|
||||
case Code.Ldc_I4_3:
|
||||
case Code.Ldc_I4_4:
|
||||
case Code.Ldc_I4_5:
|
||||
case Code.Ldc_I4_6:
|
||||
case Code.Ldc_I4_7:
|
||||
case Code.Ldc_I4_8:
|
||||
case Code.Ldc_I4_M1:
|
||||
case Code.Ldc_I4_S:
|
||||
case Code.Ldc_I8:
|
||||
case Code.Ldc_R4:
|
||||
case Code.Ldc_R8:
|
||||
case Code.Ldftn:
|
||||
case Code.Ldnull:
|
||||
case Code.Ldstr:
|
||||
case Code.Ldtoken:
|
||||
case Code.Ldsfld:
|
||||
case Code.Ldsflda:
|
||||
inlinedMethod = InlineLoadMethod(instrIndex, methodToInline, instr, index);
|
||||
break;
|
||||
|
||||
default:
|
||||
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) {
|
||||
if (new SigComparer(SigComparerOptions.IgnoreModifiers).Equals(origType, newType))
|
||||
return true;
|
||||
if (IsValueType(newType) || IsValueType(origType))
|
||||
return false;
|
||||
return newType.FullName == "System.Object";
|
||||
}
|
||||
}
|
||||
}
|
280
de4dot/de4dot.blocks/cflow/MethodCallInlinerBase.cs
Normal file
280
de4dot/de4dot.blocks/cflow/MethodCallInlinerBase.cs
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public abstract class MethodCallInlinerBase : IBlocksDeobfuscator {
|
||||
// We can't catch all infinite loops, so inline methods at most this many times
|
||||
const int MAX_ITERATIONS = 10;
|
||||
|
||||
protected Blocks blocks;
|
||||
protected Block block;
|
||||
int iteration;
|
||||
AccessChecker accessChecker;
|
||||
|
||||
public bool ExecuteIfNotModified { get; set; }
|
||||
|
||||
public void DeobfuscateBegin(Blocks blocks) {
|
||||
this.blocks = blocks;
|
||||
iteration = 0;
|
||||
}
|
||||
|
||||
public bool Deobfuscate(List<Block> allBlocks) {
|
||||
if (iteration++ >= MAX_ITERATIONS)
|
||||
return false;
|
||||
|
||||
bool modified = false;
|
||||
foreach (var block in allBlocks) {
|
||||
this.block = block;
|
||||
modified |= DeobfuscateInternal();
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
protected abstract bool DeobfuscateInternal();
|
||||
|
||||
protected class InstructionPatcher {
|
||||
readonly int patchIndex;
|
||||
public readonly int afterIndex;
|
||||
public readonly Instruction lastInstr;
|
||||
readonly Instr clonedInstr;
|
||||
public InstructionPatcher(int patchIndex, int afterIndex, Instruction lastInstr) {
|
||||
this.patchIndex = patchIndex;
|
||||
this.afterIndex = afterIndex;
|
||||
this.lastInstr = lastInstr;
|
||||
clonedInstr = new Instr(lastInstr.Clone());
|
||||
}
|
||||
|
||||
public void Patch(Block block) => block.Instructions[patchIndex] = clonedInstr;
|
||||
}
|
||||
|
||||
protected bool InlineLoadMethod(int patchIndex, MethodDef methodToInline, Instruction loadInstr, int instrIndex) {
|
||||
if (!IsReturn(methodToInline, instrIndex))
|
||||
return false;
|
||||
|
||||
int methodArgsCount = DotNetUtils.GetArgsCount(methodToInline);
|
||||
for (int i = 0; i < methodArgsCount; i++)
|
||||
block.Insert(patchIndex++, OpCodes.Pop.ToInstruction());
|
||||
|
||||
block.Instructions[patchIndex] = new Instr(loadInstr.Clone());
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool InlineOtherMethod(int patchIndex, MethodDef methodToInline, Instruction instr, int instrIndex) =>
|
||||
InlineOtherMethod(patchIndex, methodToInline, instr, instrIndex, 0);
|
||||
|
||||
protected bool InlineOtherMethod(int patchIndex, MethodDef methodToInline, Instruction instr, int instrIndex, int popLastArgs) =>
|
||||
PatchMethod(methodToInline, TryInlineOtherMethod(patchIndex, methodToInline, instr, instrIndex, popLastArgs));
|
||||
|
||||
protected bool PatchMethod(MethodDef methodToInline, InstructionPatcher patcher) {
|
||||
if (patcher == null)
|
||||
return false;
|
||||
|
||||
if (!IsReturn(methodToInline, patcher.afterIndex))
|
||||
return false;
|
||||
|
||||
patcher.Patch(block);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected InstructionPatcher TryInlineOtherMethod(int patchIndex, MethodDef methodToInline, Instruction instr, int instrIndex) =>
|
||||
TryInlineOtherMethod(patchIndex, methodToInline, instr, instrIndex, 0);
|
||||
|
||||
protected virtual Instruction OnAfterLoadArg(MethodDef methodToInline, Instruction instr, ref int instrIndex) => instr;
|
||||
|
||||
protected InstructionPatcher TryInlineOtherMethod(int patchIndex, MethodDef methodToInline, Instruction instr, int instrIndex, int popLastArgs) {
|
||||
int loadIndex = 0;
|
||||
int methodArgsCount = DotNetUtils.GetArgsCount(methodToInline);
|
||||
bool foundLdarga = false;
|
||||
while (instr != null && loadIndex < methodArgsCount) {
|
||||
bool isLdarg = true;
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldarg:
|
||||
case Code.Ldarg_S:
|
||||
case Code.Ldarg_0:
|
||||
case Code.Ldarg_1:
|
||||
case Code.Ldarg_2:
|
||||
case Code.Ldarg_3:
|
||||
break;
|
||||
case Code.Ldarga:
|
||||
case Code.Ldarga_S:
|
||||
foundLdarga = true;
|
||||
break;
|
||||
default:
|
||||
isLdarg = false;
|
||||
break;
|
||||
}
|
||||
if (!isLdarg)
|
||||
break;
|
||||
|
||||
if (instr.GetParameterIndex() != loadIndex)
|
||||
return null;
|
||||
loadIndex++;
|
||||
instr = DotNetUtils.GetInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
||||
instr = OnAfterLoadArg(methodToInline, instr, ref instrIndex);
|
||||
}
|
||||
if (instr == null || loadIndex != methodArgsCount - popLastArgs)
|
||||
return null;
|
||||
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Call:
|
||||
case Code.Callvirt:
|
||||
if (foundLdarga)
|
||||
return null;
|
||||
var callInstr = instr;
|
||||
var calledMethod = callInstr.Operand as IMethod;
|
||||
if (calledMethod == null)
|
||||
return null;
|
||||
|
||||
if (!IsCompatibleType(-1, calledMethod.MethodSig.RetType, methodToInline.MethodSig.RetType))
|
||||
return null;
|
||||
|
||||
if (!CheckSameMethods(calledMethod, methodToInline, popLastArgs))
|
||||
return null;
|
||||
|
||||
if (!HasAccessTo(instr.Operand))
|
||||
return null;
|
||||
|
||||
return new InstructionPatcher(patchIndex, instrIndex, callInstr);
|
||||
|
||||
case Code.Newobj:
|
||||
if (foundLdarga)
|
||||
return null;
|
||||
var newobjInstr = instr;
|
||||
var ctor = newobjInstr.Operand as IMethod;
|
||||
if (ctor == null)
|
||||
return null;
|
||||
|
||||
if (!IsCompatibleType(-1, ctor.DeclaringType, methodToInline.MethodSig.RetType))
|
||||
return null;
|
||||
|
||||
var methodArgs = methodToInline.Parameters;
|
||||
var calledMethodArgs = DotNetUtils.GetArgs(ctor);
|
||||
if (methodArgs.Count + 1 - popLastArgs != calledMethodArgs.Count)
|
||||
return null;
|
||||
for (int i = 1; i < calledMethodArgs.Count; i++) {
|
||||
if (!IsCompatibleType(i, calledMethodArgs[i], methodArgs[i - 1].Type))
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!HasAccessTo(instr.Operand))
|
||||
return null;
|
||||
|
||||
return new InstructionPatcher(patchIndex, instrIndex, newobjInstr);
|
||||
|
||||
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 (instr.OpCode.OperandType != OperandType.InlineNone && !HasAccessTo(instr.Operand))
|
||||
return null;
|
||||
|
||||
return new InstructionPatcher(patchIndex, instrIndex, ldInstr);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasAccessTo(object operand) {
|
||||
if (operand == null)
|
||||
return false;
|
||||
accessChecker.UserType = blocks.Method.DeclaringType;
|
||||
return accessChecker.CanAccess(operand) ?? GetDefaultAccessResult();
|
||||
}
|
||||
|
||||
protected virtual bool GetDefaultAccessResult() => true;
|
||||
|
||||
protected virtual bool IsReturn(MethodDef methodToInline, int instrIndex) {
|
||||
var instr = DotNetUtils.GetInstruction(methodToInline.Body.Instructions, ref instrIndex);
|
||||
return instr != null && instr.OpCode.Code == Code.Ret;
|
||||
}
|
||||
|
||||
protected bool CheckSameMethods(IMethod method, MethodDef methodToInline) =>
|
||||
CheckSameMethods(method, methodToInline, 0);
|
||||
|
||||
protected bool CheckSameMethods(IMethod method, MethodDef methodToInline, int ignoreLastMethodToInlineArgs) {
|
||||
var methodToInlineArgs = methodToInline.Parameters;
|
||||
var methodArgs = DotNetUtils.GetArgs(method);
|
||||
bool hasImplicitThis = method.MethodSig.ImplicitThis;
|
||||
if (methodToInlineArgs.Count - ignoreLastMethodToInlineArgs != methodArgs.Count)
|
||||
return false;
|
||||
for (int i = 0; i < methodArgs.Count; i++) {
|
||||
var methodArg = methodArgs[i];
|
||||
var methodToInlineArg = GetArgType(methodToInline, methodToInlineArgs[i].Type);
|
||||
if (!IsCompatibleType(i, methodArg, methodToInlineArg)) {
|
||||
if (i != 0 || !hasImplicitThis)
|
||||
return false;
|
||||
if (!IsCompatibleValueThisPtr(methodArg, methodToInlineArg))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static TypeSig GetArgType(MethodDef method, TypeSig arg) {
|
||||
if (arg.GetElementType() != ElementType.MVar)
|
||||
return arg;
|
||||
var mvar = (GenericMVar)arg;
|
||||
foreach (var gp in method.GenericParameters) {
|
||||
if (gp.Number != mvar.Number)
|
||||
continue;
|
||||
foreach (var gpc in gp.GenericParamConstraints)
|
||||
return gpc.Constraint.ToTypeSig();
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
protected virtual bool IsCompatibleType(int paramIndex, IType origType, IType newType) =>
|
||||
new SigComparer().Equals(origType, newType);
|
||||
|
||||
static bool IsCompatibleValueThisPtr(IType origType, IType newType) {
|
||||
var newByRef = newType as ByRefSig;
|
||||
if (newByRef == null)
|
||||
return false;
|
||||
if (!IsValueType(newByRef.Next) || !IsValueType(origType))
|
||||
return false;
|
||||
return new SigComparer().Equals(origType, newByRef.Next);
|
||||
}
|
||||
|
||||
protected static bool IsValueType(IType type) {
|
||||
if (type == null)
|
||||
return false;
|
||||
var ts = type as TypeSig;
|
||||
if (ts == null)
|
||||
return type.IsValueType;
|
||||
return ts.IsValueType && ts.ElementType != ElementType.Void;
|
||||
}
|
||||
}
|
||||
}
|
169
de4dot/de4dot.blocks/cflow/Real8Value.cs
Normal file
169
de4dot/de4dot.blocks/cflow/Real8Value.cs
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public class Real8Value : Value {
|
||||
public readonly double Value;
|
||||
public readonly bool IsValid;
|
||||
|
||||
public Real8Value(double value)
|
||||
: base(ValueType.Real8) {
|
||||
Value = value;
|
||||
IsValid = true;
|
||||
}
|
||||
|
||||
public Real8Value(double value, bool isValid)
|
||||
: base(ValueType.Real8) {
|
||||
Value = value;
|
||||
IsValid = isValid;
|
||||
}
|
||||
|
||||
public static Real8Value CreateUnknown() => 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) => CreateUnknown();
|
||||
public static Real8Value Add_Ovf_Un(Real8Value a, Real8Value b) => CreateUnknown();
|
||||
public static Real8Value Sub_Ovf(Real8Value a, Real8Value b) => CreateUnknown();
|
||||
public static Real8Value Sub_Ovf_Un(Real8Value a, Real8Value b) => CreateUnknown();
|
||||
public static Real8Value Mul_Ovf(Real8Value a, Real8Value b) => CreateUnknown();
|
||||
public static Real8Value Mul_Ovf_Un(Real8Value a, Real8Value b) => CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_I1(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_I1_Un(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_I2(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_I2_Un(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_I4(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_I4_Un(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int64Value Conv_Ovf_I8(Real8Value a) => Int64Value.CreateUnknown();
|
||||
public static Int64Value Conv_Ovf_I8_Un(Real8Value a) => Int64Value.CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_U1(Real8Value a) => Int32Value.CreateUnknownUInt8();
|
||||
public static Int32Value Conv_Ovf_U1_Un(Real8Value a) => Int32Value.CreateUnknownUInt8();
|
||||
public static Int32Value Conv_Ovf_U2(Real8Value a) => Int32Value.CreateUnknownUInt16();
|
||||
public static Int32Value Conv_Ovf_U2_Un(Real8Value a) => Int32Value.CreateUnknownUInt16();
|
||||
public static Int32Value Conv_Ovf_U4(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int32Value Conv_Ovf_U4_Un(Real8Value a) => Int32Value.CreateUnknown();
|
||||
public static Int64Value Conv_Ovf_U8(Real8Value a) => Int64Value.CreateUnknown();
|
||||
public static Int64Value Conv_Ovf_U8_Un(Real8Value a) => Int64Value.CreateUnknown();
|
||||
public static Real8Value Conv_R_Un(Real8Value a) => CreateUnknown();
|
||||
public static Real8Value Conv_R4(Real8Value a) {
|
||||
if (a.IsValid)
|
||||
return new Real8Value((float)a.Value);
|
||||
return CreateUnknown();
|
||||
}
|
||||
|
||||
public static Real8Value Conv_R8(Real8Value a) => a;
|
||||
public static Int32Value Ceq(Real8Value a, Real8Value b) => Int32Value.Create(CompareEq(a, b));
|
||||
public static Int32Value Cgt(Real8Value a, Real8Value b) => Int32Value.Create(CompareGt(a, b));
|
||||
public static Int32Value Cgt_Un(Real8Value a, Real8Value b) => Int32Value.Create(CompareGt_Un(a, b));
|
||||
public static Int32Value Clt(Real8Value a, Real8Value b) => Int32Value.Create(CompareLt(a, b));
|
||||
public static Int32Value Clt_Un(Real8Value a, Real8Value b) => Int32Value.Create(CompareLt_Un(a, b));
|
||||
|
||||
public static Bool3 CompareEq(Real8Value a, Real8Value b) {
|
||||
if (a.IsValid && b.IsValid)
|
||||
return a.Value == b.Value ? Bool3.True : Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareNeq(Real8Value a, Real8Value b) {
|
||||
if (a.IsValid && b.IsValid)
|
||||
return a.Value != b.Value ? Bool3.True : Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareGt(Real8Value a, Real8Value b) {
|
||||
if (a.IsValid && b.IsValid)
|
||||
return a.Value > b.Value ? Bool3.True : Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareGt_Un(Real8Value a, Real8Value b) => Bool3.Unknown;//TODO:
|
||||
|
||||
public static Bool3 CompareGe(Real8Value a, Real8Value b) {
|
||||
if (a.IsValid && b.IsValid)
|
||||
return a.Value >= b.Value ? Bool3.True : Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareGe_Un(Real8Value a, Real8Value b) => Bool3.Unknown;//TODO:
|
||||
|
||||
public static Bool3 CompareLe(Real8Value a, Real8Value b) {
|
||||
if (a.IsValid && b.IsValid)
|
||||
return a.Value <= b.Value ? Bool3.True : Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareLe_Un(Real8Value a, Real8Value b) => Bool3.Unknown;//TODO:
|
||||
|
||||
public static Bool3 CompareLt(Real8Value a, Real8Value b) {
|
||||
if (a.IsValid && b.IsValid)
|
||||
return a.Value < b.Value ? Bool3.True : Bool3.False;
|
||||
return Bool3.Unknown;
|
||||
}
|
||||
|
||||
public static Bool3 CompareLt_Un(Real8Value a, Real8Value b) => Bool3.Unknown;//TODO:
|
||||
public static Bool3 CompareTrue(Real8Value a) => Bool3.Unknown;
|
||||
public static Bool3 CompareFalse(Real8Value a) => Bool3.Unknown;
|
||||
|
||||
public override string ToString() {
|
||||
if (!IsValid)
|
||||
return "<INVALID_REAL8>";
|
||||
return Value.ToString();
|
||||
}
|
||||
}
|
||||
}
|
70
de4dot/de4dot.blocks/cflow/StLdlocFixer.cs
Normal file
70
de4dot/de4dot.blocks/cflow/StLdlocFixer.cs
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
// Replace stloc + ldloc with dup + stloc
|
||||
class StLdlocFixer : BlockDeobfuscator {
|
||||
IList<Local> locals;
|
||||
|
||||
protected override void Initialize(List<Block> allBlocks) {
|
||||
base.Initialize(allBlocks);
|
||||
locals = blocks.Locals;
|
||||
}
|
||||
|
||||
protected override bool Deobfuscate(Block block) {
|
||||
bool modified = false;
|
||||
var instructions = block.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var instr = instructions[i];
|
||||
switch (instr.OpCode.Code) {
|
||||
// Xenocode generates stloc + ldloc (bool). Replace it with dup + stloc. It will eventually
|
||||
// become dup + pop and be removed.
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
if (i + 1 >= instructions.Count)
|
||||
break;
|
||||
if (!instructions[i + 1].IsLdloc())
|
||||
break;
|
||||
var local = Instr.GetLocalVar(locals, instr);
|
||||
if (local.Type.ElementType != ElementType.Boolean)
|
||||
continue;
|
||||
if (local != Instr.GetLocalVar(locals, instructions[i + 1]))
|
||||
break;
|
||||
instructions[i] = new Instr(OpCodes.Dup.ToInstruction());
|
||||
instructions[i + 1] = instr;
|
||||
modified = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
}
|
476
de4dot/de4dot.blocks/cflow/SwitchCflowDeobfuscator.cs
Normal file
476
de4dot/de4dot.blocks/cflow/SwitchCflowDeobfuscator.cs
Normal file
@ -0,0 +1,476 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using dnlib.DotNet.Emit;
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
class SwitchCflowDeobfuscator : BlockDeobfuscator {
|
||||
InstructionEmulator instructionEmulator = new InstructionEmulator();
|
||||
|
||||
protected override bool Deobfuscate(Block switchBlock) {
|
||||
if (switchBlock.LastInstr.OpCode.Code != Code.Switch)
|
||||
return false;
|
||||
|
||||
if (IsSwitchTopOfStack(switchBlock) && DeobfuscateTOS(switchBlock))
|
||||
return true;
|
||||
|
||||
if (IsLdlocBranch(switchBlock, true) && DeobfuscateLdloc(switchBlock))
|
||||
return true;
|
||||
|
||||
if (IsStLdlocBranch(switchBlock, true) && DeobfuscateStLdloc(switchBlock))
|
||||
return true;
|
||||
|
||||
if (IsSwitchType1(switchBlock) && DeobfuscateType1(switchBlock))
|
||||
return true;
|
||||
|
||||
if (IsSwitchType2(switchBlock) && DeobfuscateType2(switchBlock))
|
||||
return true;
|
||||
|
||||
if (switchBlock.FirstInstr.IsLdloc() && FixSwitchBranch(switchBlock))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsSwitchTopOfStack(Block switchBlock) => switchBlock.Instructions.Count == 1;
|
||||
|
||||
static bool IsLdlocBranch(Block switchBlock, bool isSwitch) {
|
||||
int numInstrs = 1 + (isSwitch ? 1 : 0);
|
||||
return switchBlock.Instructions.Count == numInstrs && switchBlock.Instructions[0].IsLdloc();
|
||||
}
|
||||
|
||||
static bool IsSwitchType1(Block switchBlock) => switchBlock.FirstInstr.IsLdloc();
|
||||
|
||||
bool IsSwitchType2(Block switchBlock) {
|
||||
Local local = null;
|
||||
foreach (var instr in switchBlock.Instructions) {
|
||||
if (!instr.IsLdloc())
|
||||
continue;
|
||||
local = Instr.GetLocalVar(blocks.Locals, instr);
|
||||
break;
|
||||
}
|
||||
if (local == null)
|
||||
return false;
|
||||
|
||||
foreach (var source in switchBlock.Sources) {
|
||||
var instrs = source.Instructions;
|
||||
for (int i = 1; i < instrs.Count; i++) {
|
||||
var ldci4 = instrs[i - 1];
|
||||
if (!ldci4.IsLdcI4())
|
||||
continue;
|
||||
var stloc = instrs[i];
|
||||
if (!stloc.IsStloc())
|
||||
continue;
|
||||
if (Instr.GetLocalVar(blocks.Locals, stloc) != local)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsStLdlocBranch(Block switchBlock, bool isSwitch) {
|
||||
int numInstrs = 2 + (isSwitch ? 1 : 0);
|
||||
return switchBlock.Instructions.Count == numInstrs &&
|
||||
switchBlock.Instructions[0].IsStloc() &&
|
||||
switchBlock.Instructions[1].IsLdloc() &&
|
||||
Instr.GetLocalVar(blocks.Locals, switchBlock.Instructions[0]) == Instr.GetLocalVar(blocks.Locals, switchBlock.Instructions[1]);
|
||||
}
|
||||
|
||||
bool DeobfuscateTOS(Block switchBlock) {
|
||||
bool modified = false;
|
||||
if (switchBlock.Targets == null)
|
||||
return modified;
|
||||
var targets = new List<Block>(switchBlock.Targets);
|
||||
|
||||
modified |= DeobfuscateTOS(targets, switchBlock.FallThrough, switchBlock);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool DeobfuscateLdloc(Block switchBlock) {
|
||||
bool modified = false;
|
||||
|
||||
var switchVariable = Instr.GetLocalVar(blocks.Locals, switchBlock.Instructions[0]);
|
||||
if (switchVariable == null)
|
||||
return modified;
|
||||
|
||||
if (switchBlock.Targets == null)
|
||||
return modified;
|
||||
var targets = new List<Block>(switchBlock.Targets);
|
||||
|
||||
modified |= DeobfuscateLdloc(targets, switchBlock.FallThrough, switchBlock, switchVariable);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool DeobfuscateStLdloc(Block switchBlock) {
|
||||
bool modified = false;
|
||||
|
||||
var switchVariable = Instr.GetLocalVar(blocks.Locals, switchBlock.Instructions[0]);
|
||||
if (switchVariable == null)
|
||||
return modified;
|
||||
|
||||
if (switchBlock.Targets == null)
|
||||
return modified;
|
||||
var targets = new List<Block>(switchBlock.Targets);
|
||||
|
||||
modified |= DeobfuscateStLdloc(targets, switchBlock.FallThrough, switchBlock);
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
// Switch deobfuscation when block uses stloc N, ldloc N to load switch constant
|
||||
// blk1:
|
||||
// ldc.i4 X
|
||||
// br swblk
|
||||
// swblk:
|
||||
// stloc N
|
||||
// ldloc N
|
||||
// switch (......)
|
||||
bool DeobfuscateStLdloc(IList<Block> switchTargets, Block switchFallThrough, Block block) {
|
||||
bool modified = false;
|
||||
foreach (var source in new List<Block>(block.Sources)) {
|
||||
if (!IsBranchBlock(source))
|
||||
continue;
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == source);
|
||||
instructionEmulator.Emulate(source.Instructions);
|
||||
|
||||
var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.Pop());
|
||||
if (target == null)
|
||||
continue;
|
||||
source.ReplaceLastNonBranchWithBranch(0, target);
|
||||
source.Add(new Instr(OpCodes.Pop.ToInstruction()));
|
||||
modified = true;
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
// Switch deobfuscation when block uses ldloc N to load switch constant
|
||||
// blk1:
|
||||
// ldc.i4 X
|
||||
// stloc N
|
||||
// br swblk / bcc swblk
|
||||
// swblk:
|
||||
// ldloc N
|
||||
// switch (......)
|
||||
bool DeobfuscateLdloc(IList<Block> switchTargets, Block switchFallThrough, Block block, Local switchVariable) {
|
||||
bool modified = false;
|
||||
foreach (var source in new List<Block>(block.Sources)) {
|
||||
if (IsBranchBlock(source)) {
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == source);
|
||||
instructionEmulator.Emulate(source.Instructions);
|
||||
|
||||
var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.GetLocal(switchVariable));
|
||||
if (target == null)
|
||||
continue;
|
||||
source.ReplaceLastNonBranchWithBranch(0, target);
|
||||
modified = true;
|
||||
}
|
||||
else if (IsBccBlock(source)) {
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == source);
|
||||
instructionEmulator.Emulate(source.Instructions);
|
||||
|
||||
var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.GetLocal(switchVariable));
|
||||
if (target == null)
|
||||
continue;
|
||||
if (source.Targets[0] == block) {
|
||||
source.SetNewTarget(0, target);
|
||||
modified = true;
|
||||
}
|
||||
if (source.FallThrough == block) {
|
||||
source.SetNewFallThrough(target);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
// Switch deobfuscation when block has switch contant on TOS:
|
||||
// blk1:
|
||||
// ldc.i4 X
|
||||
// br swblk
|
||||
// swblk:
|
||||
// switch (......)
|
||||
bool DeobfuscateTOS(IList<Block> switchTargets, Block switchFallThrough, Block block) {
|
||||
bool modified = false;
|
||||
foreach (var source in new List<Block>(block.Sources)) {
|
||||
if (!IsBranchBlock(source))
|
||||
continue;
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == source);
|
||||
instructionEmulator.Emulate(source.Instructions);
|
||||
|
||||
var target = GetSwitchTarget(switchTargets, switchFallThrough, instructionEmulator.Pop());
|
||||
if (target == null) {
|
||||
modified |= DeobfuscateTos_Ldloc(switchTargets, switchFallThrough, source);
|
||||
}
|
||||
else {
|
||||
source.ReplaceLastNonBranchWithBranch(0, target);
|
||||
source.Add(new Instr(OpCodes.Pop.ToInstruction()));
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
// ldloc N
|
||||
// br swblk
|
||||
// or
|
||||
// stloc N
|
||||
// ldloc N
|
||||
// br swblk
|
||||
bool DeobfuscateTos_Ldloc(IList<Block> switchTargets, Block switchFallThrough, Block block) {
|
||||
if (IsLdlocBranch(block, false)) {
|
||||
var switchVariable = Instr.GetLocalVar(blocks.Locals, block.Instructions[0]);
|
||||
if (switchVariable == null)
|
||||
return false;
|
||||
return DeobfuscateLdloc(switchTargets, switchFallThrough, block, switchVariable);
|
||||
}
|
||||
else if (IsStLdlocBranch(block, false))
|
||||
return DeobfuscateStLdloc(switchTargets, switchFallThrough, block);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsBranchBlock(Block block) {
|
||||
if (block.Targets != null)
|
||||
return false;
|
||||
if (block.FallThrough == null)
|
||||
return false;
|
||||
switch (block.LastInstr.OpCode.Code) {
|
||||
case Code.Switch:
|
||||
case Code.Leave:
|
||||
case Code.Leave_S:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsBccBlock(Block block) {
|
||||
if (block.Targets == null || block.Targets.Count != 1)
|
||||
return false;
|
||||
if (block.FallThrough == null)
|
||||
return false;
|
||||
switch (block.LastInstr.OpCode.Code) {
|
||||
case Code.Beq:
|
||||
case Code.Beq_S:
|
||||
case Code.Bge:
|
||||
case Code.Bge_S:
|
||||
case Code.Bge_Un:
|
||||
case Code.Bge_Un_S:
|
||||
case Code.Bgt:
|
||||
case Code.Bgt_S:
|
||||
case Code.Bgt_Un:
|
||||
case Code.Bgt_Un_S:
|
||||
case Code.Ble:
|
||||
case Code.Ble_S:
|
||||
case Code.Ble_Un:
|
||||
case Code.Ble_Un_S:
|
||||
case Code.Blt:
|
||||
case Code.Blt_S:
|
||||
case Code.Blt_Un:
|
||||
case Code.Blt_Un_S:
|
||||
case Code.Bne_Un:
|
||||
case Code.Bne_Un_S:
|
||||
case Code.Brfalse:
|
||||
case Code.Brfalse_S:
|
||||
case Code.Brtrue:
|
||||
case Code.Brtrue_S:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool DeobfuscateType1(Block switchBlock) {
|
||||
if (!EmulateGetTarget(switchBlock, out var target) || target != null)
|
||||
return false;
|
||||
|
||||
bool modified = false;
|
||||
|
||||
foreach (var source in new List<Block>(switchBlock.Sources)) {
|
||||
if (!source.CanAppend(switchBlock))
|
||||
continue;
|
||||
if (!WillHaveKnownTarget(switchBlock, source))
|
||||
continue;
|
||||
|
||||
source.Append(switchBlock);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
bool DeobfuscateType2(Block switchBlock) {
|
||||
bool modified = false;
|
||||
|
||||
var bccSources = new List<Block>();
|
||||
foreach (var source in new List<Block>(switchBlock.Sources)) {
|
||||
if (source.LastInstr.IsConditionalBranch()) {
|
||||
bccSources.Add(source);
|
||||
continue;
|
||||
}
|
||||
if (!source.CanAppend(switchBlock))
|
||||
continue;
|
||||
if (!WillHaveKnownTarget(switchBlock, source))
|
||||
continue;
|
||||
|
||||
source.Append(switchBlock);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
foreach (var bccSource in bccSources) {
|
||||
if (!WillHaveKnownTarget(switchBlock, bccSource))
|
||||
continue;
|
||||
var consts = GetBccLocalConstants(bccSource);
|
||||
if (consts.Count == 0)
|
||||
continue;
|
||||
var newFallThrough = CreateBlock(consts, bccSource.FallThrough);
|
||||
var newTarget = CreateBlock(consts, bccSource.Targets[0]);
|
||||
var oldFallThrough = bccSource.FallThrough;
|
||||
var oldTarget = bccSource.Targets[0];
|
||||
bccSource.SetNewFallThrough(newFallThrough);
|
||||
bccSource.SetNewTarget(0, newTarget);
|
||||
newFallThrough.SetNewFallThrough(oldFallThrough);
|
||||
newTarget.SetNewFallThrough(oldTarget);
|
||||
modified = true;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
static Block CreateBlock(Dictionary<Local, int> consts, Block fallThrough) {
|
||||
var block = new Block();
|
||||
foreach (var kv in consts) {
|
||||
block.Instructions.Add(new Instr(Instruction.CreateLdcI4(kv.Value)));
|
||||
block.Instructions.Add(new Instr(OpCodes.Stloc.ToInstruction(kv.Key)));
|
||||
}
|
||||
fallThrough.Parent.Add(block);
|
||||
return block;
|
||||
}
|
||||
|
||||
Dictionary<Local, int> GetBccLocalConstants(Block block) {
|
||||
var dict = new Dictionary<Local, int>();
|
||||
var instrs = block.Instructions;
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var instr = instrs[i];
|
||||
if (instr.IsStloc()) {
|
||||
var local = Instr.GetLocalVar(blocks.Locals, instr);
|
||||
if (local == null)
|
||||
continue;
|
||||
var ldci4 = i == 0 ? null : instrs[i - 1];
|
||||
if (ldci4 == null || !ldci4.IsLdcI4())
|
||||
dict.Remove(local);
|
||||
else
|
||||
dict[local] = ldci4.GetLdcI4Value();
|
||||
}
|
||||
else if (instr.IsLdloc()) {
|
||||
var local = Instr.GetLocalVar(blocks.Locals, instr);
|
||||
if (local != null)
|
||||
dict.Remove(local);
|
||||
}
|
||||
else if (instr.OpCode.Code == Code.Ldloca || instr.OpCode.Code == Code.Ldloca_S) {
|
||||
if (instr.Operand is Local local)
|
||||
dict.Remove(local);
|
||||
}
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
bool EmulateGetTarget(Block switchBlock, out Block target) {
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == switchBlock);
|
||||
try {
|
||||
instructionEmulator.Emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1);
|
||||
}
|
||||
catch (NullReferenceException) {
|
||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||
target = null;
|
||||
return false;
|
||||
}
|
||||
target = GetTarget(switchBlock);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WillHaveKnownTarget(Block switchBlock, Block source) {
|
||||
instructionEmulator.Initialize(blocks, allBlocks[0] == source);
|
||||
try {
|
||||
instructionEmulator.Emulate(source.Instructions);
|
||||
instructionEmulator.Emulate(switchBlock.Instructions, 0, switchBlock.Instructions.Count - 1);
|
||||
}
|
||||
catch (NullReferenceException) {
|
||||
// Here if eg. invalid metadata token in a call instruction (operand is null)
|
||||
return false;
|
||||
}
|
||||
return GetTarget(switchBlock) != null;
|
||||
}
|
||||
|
||||
Block GetTarget(Block switchBlock) {
|
||||
var val1 = instructionEmulator.Pop();
|
||||
if (!val1.IsInt32())
|
||||
return null;
|
||||
return CflowUtils.GetSwitchTarget(switchBlock.Targets, switchBlock.FallThrough, (Int32Value)val1);
|
||||
}
|
||||
|
||||
static Block GetSwitchTarget(IList<Block> targets, Block fallThrough, Value value) {
|
||||
if (!value.IsInt32())
|
||||
return null;
|
||||
return CflowUtils.GetSwitchTarget(targets, fallThrough, (Int32Value)value);
|
||||
}
|
||||
|
||||
static bool FixSwitchBranch(Block switchBlock) {
|
||||
// Code:
|
||||
// blk1:
|
||||
// ldc.i4 XXX
|
||||
// br common
|
||||
// blk2:
|
||||
// ldc.i4 YYY
|
||||
// br common
|
||||
// common:
|
||||
// stloc X
|
||||
// br swblk
|
||||
// swblk:
|
||||
// ldloc X
|
||||
// switch
|
||||
// Inline common into blk1 and blk2.
|
||||
|
||||
bool modified = false;
|
||||
|
||||
foreach (var commonSource in new List<Block>(switchBlock.Sources)) {
|
||||
if (commonSource.Instructions.Count != 1)
|
||||
continue;
|
||||
if (!commonSource.FirstInstr.IsStloc())
|
||||
continue;
|
||||
foreach (var blk in new List<Block>(commonSource.Sources)) {
|
||||
if (blk.CanAppend(commonSource)) {
|
||||
blk.Append(commonSource);
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
}
|
||||
}
|
81
de4dot/de4dot.blocks/cflow/Value.cs
Normal file
81
de4dot/de4dot.blocks/cflow/Value.cs
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright (C) 2011-2015 de4dot@gmail.com
|
||||
|
||||
This file is part of de4dot.
|
||||
|
||||
de4dot is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
de4dot is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace de4dot.blocks.cflow {
|
||||
public enum ValueType : byte {
|
||||
Unknown,
|
||||
Null,
|
||||
Object,
|
||||
Boxed,
|
||||
Int32,
|
||||
Int64,
|
||||
Real8,
|
||||
String,
|
||||
}
|
||||
|
||||
public enum Bool3 {
|
||||
Unknown = -1,
|
||||
False,
|
||||
True,
|
||||
}
|
||||
|
||||
public abstract class Value {
|
||||
public readonly ValueType valueType;
|
||||
public bool IsUnknown() => valueType == ValueType.Unknown;
|
||||
public bool IsNull() => valueType == ValueType.Null;
|
||||
public bool IsObject() => valueType == ValueType.Object;
|
||||
public bool IsBoxed() => valueType == ValueType.Boxed;
|
||||
public bool IsInt32() => valueType == ValueType.Int32;
|
||||
public bool IsInt64() => valueType == ValueType.Int64;
|
||||
public bool IsReal8() => valueType == ValueType.Real8;
|
||||
public bool IsString() => valueType == ValueType.String;
|
||||
protected Value(ValueType valueType) => this.valueType = valueType;
|
||||
}
|
||||
|
||||
public class UnknownValue : Value {
|
||||
public UnknownValue() : base(ValueType.Unknown) { }
|
||||
public override string ToString() => "<unknown>";
|
||||
}
|
||||
|
||||
public class ObjectValue : Value {
|
||||
public readonly object obj; // can be null but that doesn't mean that this ObjectValue instance is null
|
||||
public ObjectValue() : this(null) { }
|
||||
public ObjectValue(object obj) : base(ValueType.Object) => this.obj = obj;
|
||||
public override string ToString() => "<non-null object>";
|
||||
}
|
||||
|
||||
public class NullValue : Value {
|
||||
// There's only one type of null
|
||||
public static readonly NullValue Instance = new NullValue();
|
||||
NullValue() : base(ValueType.Null) { }
|
||||
public override string ToString() => "null";
|
||||
}
|
||||
|
||||
public class BoxedValue : Value {
|
||||
public readonly Value value;
|
||||
public BoxedValue(Value value) : base(ValueType.Boxed) => this.value = value;
|
||||
public override string ToString() => $"box({value.ToString()})";
|
||||
}
|
||||
|
||||
public class StringValue : Value {
|
||||
public readonly string value;
|
||||
public StringValue(string value) : base(ValueType.String) => this.value = value;
|
||||
public override string ToString() => $"\"{value}\"";
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user