0
0
mirror of https://github.com/sp-tarkov/assembly-tool.git synced 2025-02-12 17:30:43 -05:00

Update de4dot to use dnlib 4.4.0 and x64

Adds the source code used for this modification, this de4dot source code has been cleaned of any things not needed for deobfuscating the tarkov assembly

Co-authored-by: 静穏靄 <170472707+seionmoya@users.noreply.github.com>
This commit is contained in:
Archangel 2024-12-30 16:01:39 +01:00
parent 185f8026bc
commit 77a24319b1
194 changed files with 30920 additions and 1 deletions

Binary file not shown.

Binary file not shown.

View 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>

Binary file not shown.

View 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.

View File

@ -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
View 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
View File

@ -0,0 +1,10 @@
*~
.vs/
bin/
obj/
Release/
Debug/
publish-netcoreapp*/
launchSettings.json
*.user
.nuget/

View 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>

View 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) {
}
}
}
}

View 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

View 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);
}
}
}
}

View 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));
}
}
}

View 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);
}
}
}

View 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);
}
}
}

View 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();
}
}

View 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);
}
}

View 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();
}
}

View 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);
}
}

View 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);
}
}

View 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);
}
}

View 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();
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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();
}
}

View 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;
}
}
}

View 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);
}
}

View 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();
}
}

View 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();
}
}

View 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;
}
}

View 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}";
}
}

View 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;
}
}

View 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 + "}";
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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);
}
}
}
}

View 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);
}
}

View 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>

View 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>

View 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
View 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>.

View 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
View 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
View 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
View 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`.

View 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>

View 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);
}
}

View 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>

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}

View 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;
}
}

View 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;
}
}
}

View 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 {
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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 {
}
}

View 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();
}
}

View 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;
}
}
}

View 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();
}
}

View 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 {
}
}

View 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;
}
}
}

View 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}");
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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();
}
}

View 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;
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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);
}
}
}

View 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();
}
}
}

View 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);
}
}
}
}

View 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];
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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);
}
}

File diff suppressed because it is too large Load Diff

View 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})";
}
}
}

View 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})";
}
}
}

View 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";
}
}
}

View 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;
}
}
}

View 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();
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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