Initial commit
This commit is contained in:
commit
865ed5a47a
|
@ -0,0 +1 @@
|
|||
*~
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "cecil"]
|
||||
path = cecil
|
||||
url = git@github.com:0xd4d/cecil.git
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{FBD84077-9D35-41FE-89DF-8D79EFE0B595}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>AssemblyData</RootNamespace>
|
||||
<AssemblyName>AssemblyData</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyServer.cs" />
|
||||
<Compile Include="AssemblyService.cs" />
|
||||
<Compile Include="DelegateStringDecrypter.cs" />
|
||||
<Compile Include="IAssemblyService.cs" />
|
||||
<Compile Include="IStringDecrypter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="SimpleData.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Runtime.Remoting" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.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 != 2)
|
||||
Environment.Exit(1);
|
||||
var channelName = args[0];
|
||||
var uri = args[1];
|
||||
|
||||
var service = new AssemblyService();
|
||||
startServer(service, channelName, uri);
|
||||
service.waitExit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void startServer(AssemblyService service, string name, string uri) {
|
||||
ChannelServices.RegisterChannel(new IpcServerChannel(name), false);
|
||||
RemotingServices.Marshal(service, uri);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 class AssemblyService : MarshalByRefObject, IAssemblyService {
|
||||
IStringDecrypter stringDecrypter = null;
|
||||
ManualResetEvent exitEvent = new ManualResetEvent(false);
|
||||
Assembly assembly = null;
|
||||
|
||||
public void doNothing() {
|
||||
}
|
||||
|
||||
void checkStringDecrypter() {
|
||||
if (stringDecrypter == null)
|
||||
throw new ApplicationException("setStringDecrypterType() hasn't been called yet.");
|
||||
}
|
||||
|
||||
void checkAssembly() {
|
||||
if (assembly == null)
|
||||
throw new ApplicationException("loadAssembly() hasn't been called yet.");
|
||||
}
|
||||
|
||||
public void loadAssembly(string filename) {
|
||||
if (assembly != null)
|
||||
throw new ApplicationException("Only one assembly can be explicitly loaded");
|
||||
try {
|
||||
assembly = Assembly.LoadFrom(filename);
|
||||
}
|
||||
catch (BadImageFormatException) {
|
||||
throw new ApplicationException(string.Format("Could not load assembly {0}. Maybe it's 32-bit or 64-bit only?", filename));
|
||||
}
|
||||
}
|
||||
|
||||
public void setStringDecrypterType(StringDecrypterType type) {
|
||||
if (stringDecrypter != null)
|
||||
throw new ApplicationException("StringDecrypterType already set");
|
||||
|
||||
switch (type) {
|
||||
case StringDecrypterType.Delegate:
|
||||
stringDecrypter = new DelegateStringDecrypter();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Unknown StringDecrypterType {0}", type));
|
||||
}
|
||||
}
|
||||
|
||||
public int defineStringDecrypter(int methodToken, int typeToken) {
|
||||
checkStringDecrypter();
|
||||
var methodInfo = findMethod(methodToken, typeToken);
|
||||
if (methodInfo == null)
|
||||
throw new ApplicationException(string.Format("Could not find method {0:X8}", methodToken));
|
||||
if (methodInfo.ReturnType != typeof(string))
|
||||
throw new ApplicationException(string.Format("Method return type must be string: {0}", methodInfo));
|
||||
return stringDecrypter.defineStringDecrypter(methodInfo);
|
||||
}
|
||||
|
||||
public object[] decryptStrings(int stringDecrypterMethod, object[] args) {
|
||||
checkStringDecrypter();
|
||||
foreach (var arg in args)
|
||||
SimpleData.unpack((object[])arg);
|
||||
return SimpleData.pack(stringDecrypter.decryptStrings(stringDecrypterMethod, args));
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
exitEvent.Set();
|
||||
}
|
||||
|
||||
public void waitExit() {
|
||||
exitEvent.WaitOne();
|
||||
}
|
||||
|
||||
public override object InitializeLifetimeService() {
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodInfo findMethod(int methodToken, int typeToken) {
|
||||
checkAssembly();
|
||||
|
||||
const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
|
||||
|
||||
foreach (var module in assembly.GetModules()) {
|
||||
foreach (var method in module.GetMethods(bindingFlags)) {
|
||||
if (method.MetadataToken == methodToken)
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var type in assembly.GetTypes()) {
|
||||
if (type.MetadataToken == typeToken || typeToken == 0) {
|
||||
var methods = type.GetMethods(bindingFlags);
|
||||
foreach (var method in methods) {
|
||||
if (method.MetadataToken == methodToken)
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright (C) 2011 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) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
Copyright (C) 2011 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,
|
||||
}
|
||||
|
||||
public interface IAssemblyService {
|
||||
void doNothing();
|
||||
void loadAssembly(string filename);
|
||||
void setStringDecrypterType(StringDecrypterType type);
|
||||
int defineStringDecrypter(int methodToken, int typeToken = 0);
|
||||
object[] decryptStrings(int stringDecrypterMethod, object[] args);
|
||||
void exit();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright (C) 2011 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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("AssemblyData")]
|
||||
[assembly: AssemblyDescription(".NET Assembly data for use by client and server")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AssemblyData")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright (C) 2011 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++) {
|
||||
var s = args[i] as string;
|
||||
if (s != null)
|
||||
args[i] = new MyString(s);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
public static object[] unpack(object[] args) {
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
var s = args[i] as MyString;
|
||||
if (s != null)
|
||||
args[i] = s.ToString();
|
||||
}
|
||||
return args;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Text;
|
||||
|
||||
namespace AssemblyData {
|
||||
internal delegate void Action();
|
||||
internal delegate TResult Func<out TResult>();
|
||||
internal delegate TResult Func<in T1, out TResult>(T1 arg);
|
||||
internal delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9);
|
||||
|
||||
static class Utils {
|
||||
static Random random = new Random();
|
||||
|
||||
public static Type getDelegateType(MethodInfo method) {
|
||||
var parameters = method.GetParameters();
|
||||
var types = new Type[parameters.Length + 1];
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
types[i] = parameters[i].ParameterType;
|
||||
types[types.Length - 1] = method.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);
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Too many arguments: {0}", method));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{F458A89A-AEAB-4965-AA4D-393C9F7411D0}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>AssemblyServer_x64</RootNamespace>
|
||||
<AssemblyName>AssemblyServer-x64</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- Fix a warning that the referenced mscorlib is not 64-bit... -->
|
||||
<Reference Include="$(Windir)\Microsoft.NET\Framework64\v2.0.50727\mscorlib.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssemblyData\AssemblyData.csproj">
|
||||
<Project>{FBD84077-9D35-41FE-89DF-8D79EFE0B595}</Project>
|
||||
<Name>AssemblyData</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2011 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) {
|
||||
return AssemblyServer.Start.main(args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("AssemblyServer-x64")]
|
||||
[assembly: AssemblyDescription("Assembly Server - x64")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AssemblyServer-x64")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{30107571-320D-443B-A304-4C16F3EB8866}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>AssemblyServer_x86</RootNamespace>
|
||||
<AssemblyName>AssemblyServer-x86</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssemblyData\AssemblyData.csproj">
|
||||
<Project>{FBD84077-9D35-41FE-89DF-8D79EFE0B595}</Project>
|
||||
<Name>AssemblyData</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2011 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_x86 {
|
||||
class Program {
|
||||
static int Main(string[] args) {
|
||||
return AssemblyServer.Start.main(args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("AssemblyServer-x86")]
|
||||
[assembly: AssemblyDescription("Assembly Server - x86")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AssemblyServer-x86")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{593AB87D-7B92-47CF-8201-3574CF6A6457}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>AssemblyServer</RootNamespace>
|
||||
<AssemblyName>AssemblyServer</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssemblyData\AssemblyData.csproj">
|
||||
<Project>{FBD84077-9D35-41FE-89DF-8D79EFE0B595}</Project>
|
||||
<Name>AssemblyData</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2011 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_AnyCpu {
|
||||
class Program {
|
||||
static int Main(string[] args) {
|
||||
return AssemblyServer.Start.main(args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("AssemblyServer")]
|
||||
[assembly: AssemblyDescription("Assembly Server - AnyCpu")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("AssemblyServer")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -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>.
|
Binary file not shown.
|
@ -0,0 +1,180 @@
|
|||
|
||||
de4dot -- Deobfuscator for .NET
|
||||
===============================
|
||||
|
||||
This is still in *BETA*! Report any bugs, problems and/or improvements! In
|
||||
case you're reading this somewhere else, the project url is
|
||||
https://github.com/0xd4d/de4dot
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
* Supports some popular obfuscators
|
||||
* Deobfuscates control flow
|
||||
* Cross-assembly symbol renaming
|
||||
* Decrypts strings
|
||||
* Decrypts resources
|
||||
* Dumps embedded assemblies
|
||||
* Dumps encrypted methods
|
||||
* Deobfuscated files are runnable
|
||||
* Removes other obfuscator junk
|
||||
* Supports pure managed .NET files only
|
||||
* Fixes peverify errors created by the obfuscator
|
||||
* 100% Open Source
|
||||
|
||||
Many features work even if it's an unsupported obfuscator but the result may
|
||||
or may not be runnable.
|
||||
|
||||
Who would need a deobfuscator?
|
||||
------------------------------
|
||||
|
||||
* Security experts who need to deobfuscate obfuscated .NET malware.
|
||||
* You lost your source code but have a copy of your obfuscated .NET
|
||||
assemblies. You can then use ILSpy to decompile them.
|
||||
* You must verify that an obfuscated compiled .NET assembly contains the
|
||||
claimed source code, and no backdoors.
|
||||
* Some obfuscators are not Mono-compatible. If you deobfuscate it, it may run
|
||||
on Mono.
|
||||
* Some obfuscated programs crash on 64-bit Windows. Deobfuscating it and
|
||||
removing the obfuscator code solves that problem.
|
||||
* You can only run verifiable .NET code, but the obfuscated program is
|
||||
non-verifiable due to the obfuscated code.
|
||||
* You don't want string decryption and tons of useless CIL instructions to
|
||||
slow down your favorite program.
|
||||
|
||||
Features explained
|
||||
------------------
|
||||
|
||||
### Supports some popular obfuscators
|
||||
|
||||
I won't list the supported obfuscators because it would be outdated soon
|
||||
anyway, and I'd forget to update the list. Run `de4dot -h` to get a list of
|
||||
the supported obfuscators. It's usually very easy to add support for another
|
||||
obfuscator.
|
||||
Other obfuscators are partially supported. Eg. control flow deobfuscation,
|
||||
symbol renaming, dynamic string decryption.
|
||||
|
||||
### Deobfuscates control flow
|
||||
|
||||
Most obfuscators can rearrange the control flow so the code is harder to
|
||||
understand. A simple method that is 10 lines long and easy to read, could
|
||||
become 30-40 lines and be very hard to read. Control flow deobfuscation will
|
||||
remove all of the obfuscated code, leaving just the original code. All dead
|
||||
(non-executed) code blocks are also removed as part of control flow
|
||||
deobfuscation.
|
||||
|
||||
### Cross-assembly symbol renaming
|
||||
|
||||
Many obfuscators can rename public classes if they're part of a private
|
||||
assembly. This deobfuscator will properly rename not only the obfuscated class
|
||||
and all references within that assembly, but also all references in other
|
||||
assemblies. If you don't need symbol renaming, you should disable it.
|
||||
|
||||
### Decrypts strings
|
||||
|
||||
Most, if not all, obfuscators support encrypting the strings. They usually
|
||||
replace the original string with an encrypted string, or an integer. The
|
||||
encrypted string or integer is then handed over to the string decrypter which
|
||||
returns the original string. This deobfuscator supports static decryption and
|
||||
dynamic decryption. Dynamic decryption will load the assembly and then call
|
||||
the string decrypter and save the decrypted string. You can tell it which
|
||||
method is the string decrypter and it will do the rest. The default is to use
|
||||
static string decryption. Dynamic string decryption can be useful if you're
|
||||
deobfuscating an assembly obfuscated with an unsupported obfuscator.
|
||||
|
||||
### Decrypts resources
|
||||
|
||||
Resources are usually encrypted by the obfuscator. The deobfuscator supports
|
||||
static decryption only.
|
||||
|
||||
### Dumps embedded assemblies
|
||||
|
||||
Some obfuscators can embed assemblies. They usually encrypt and compress the
|
||||
assembly and put it in the resources section. These assemblies will be
|
||||
decypted and decompressed and then saved to disk.
|
||||
|
||||
### Dumps encrypted methods
|
||||
|
||||
Some obfuscators encrypt all methods and only decrypt each method when
|
||||
requested by the .NET runtime. These methods can be dumped dynamically by
|
||||
using some special tricks. It's not a generic methods decrypter but will work
|
||||
with the supported obfuscators that encrypt methods.
|
||||
|
||||
### Deobfuscated files are runnable
|
||||
|
||||
If it's a supported obfuscator, the output is runnable. This is an important
|
||||
feature. If you can't run the resulting file, it's almost useless. Note that
|
||||
you may need to resign all modified assemblies that were signed. Some programs
|
||||
have internal checks for modifications that aren't part of the obfuscator.
|
||||
These kinds of protections usually cause a crash on purpose. Those aren't
|
||||
removed since they're part of the original assembly.
|
||||
|
||||
### Removes other obfuscator junk
|
||||
|
||||
Many obfuscation products add other junk to the file. Eg., they could add code
|
||||
to log every single exception, or detect deobfuscation, etc. Since that's not
|
||||
part of the original file, it's also removed. Some obfuscators add so many
|
||||
junk classes that it's difficult to find all of them, though. You have to
|
||||
remove those manually for now.
|
||||
|
||||
### Supports pure managed .NET files only
|
||||
|
||||
This is a limitation of the Mono.Cecil library. Most .NET assemblies, however,
|
||||
are pure managed .NET assemblies.
|
||||
|
||||
### Fixes peverify errors created by the obfuscator
|
||||
|
||||
Usually when an obfuscator adds control flow obfuscation, the resulting code
|
||||
doesn't pass all peverify tests, resulting in an unverifiable assembly. By
|
||||
removing that obfuscation, and making sure to re-arrange the code blocks in a
|
||||
certain order, those obfuscator created errors will be gone. If the assembly
|
||||
was verifiable before obfuscation, it must be verifiable after deobfuscation.
|
||||
|
||||
### 100% Open Source
|
||||
|
||||
Can't get any better than this!
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Show help:
|
||||
|
||||
de4dot -h
|
||||
|
||||
Deobfuscate a few files:
|
||||
|
||||
de4dot file1.exe file2.dll file3.exe
|
||||
|
||||
Deobfuscate all files found:
|
||||
|
||||
de4dot -r c:\path1 -ro c:\out
|
||||
|
||||
Detect obfuscator recursively:
|
||||
|
||||
de4dot -d -r c:\path1
|
||||
|
||||
Dump methods and deobfuscate (currently only CliSecure):
|
||||
|
||||
dumpMethods file1
|
||||
dumpMethods file2
|
||||
dumpMethods file3
|
||||
de4dot file1 file2 file3
|
||||
|
||||
Deobfuscate and get a detailed log of what was changed:
|
||||
|
||||
de4dot -v file1.exe file2.dll file3.exe > log.txt
|
||||
|
||||
Deobfuscate and override string decrypter detection, finding and using all
|
||||
static methods with string and int args that return a string. A dynamic method
|
||||
is created and used to call the string decrypter method(s). Make sure you
|
||||
don't include any non-string decrypter methods or you will get an exception:
|
||||
|
||||
de4dot --default-strtyp delegate --default-strtok "(System.String, System.Int32)" file1.exe file2.dll
|
||||
|
||||
Same as above but use a metadata token:
|
||||
|
||||
de4dot --default-strtyp delegate file1.exe --strtok 06000123 file2.dll --strtok 06004567 --strtok 06009ABC
|
||||
|
||||
Don't remove obfuscator types, methods, etc:
|
||||
|
||||
de4dot --keep-types file1.exe
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("Test.Rename.Dll")]
|
||||
[assembly: AssemblyDescription("Renamer tests")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Test.Rename.Dll")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{3B408415-5203-4618-ABC1-F88D0BBE849B}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Test.Rename.Dll</RootNamespace>
|
||||
<AssemblyName>Test.Rename.Dll</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn>0693, 0414, 0169, 0108, 0067</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn>0693, 0414, 0169, 0108, 0067</NoWarn>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Tests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
Copyright (C) 2011 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/>.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
How to test it:
|
||||
- Compile this assembly and the other test assembly
|
||||
- Force rename of everything (set name regex to eg. asdfasdfasdf)
|
||||
Make sure the code doesn't rename any types inheriting from
|
||||
Delegate or MulticastDelegate. renameMembers() should
|
||||
return before renaming the members
|
||||
if (TypeDefinition.BaseType != null && TypeDefinition.BaseType.Name.Contains("Delegate"))
|
||||
return;
|
||||
...real code...
|
||||
- Run peverify /IL /MD on both files
|
||||
- Decompile them and create a solution for both projects. I recommend using ILSpy.
|
||||
- Compile with VS. If it fails to build, make sure the decompiler isn't buggy.
|
||||
- Some manual inspection may be required as well even if it builds 100%.
|
||||
|
||||
Yes, it's not 100% automated. You get what you pay for! Buy something
|
||||
nice with the money you saved.
|
||||
*/
|
||||
|
||||
namespace Test.Rename {
|
||||
namespace test1 {
|
||||
public class Class1 : Test.Rename.Dll.test.pub1.Class1 {
|
||||
public override int Prop2 {
|
||||
get { return 1; }
|
||||
}
|
||||
public override int Prop3 {
|
||||
set { }
|
||||
}
|
||||
public override void meth1(int i) { }
|
||||
public override void meth1(string s) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace test2 {
|
||||
public class Class1 : Test.Rename.Dll.test.pub2.IFace1 {
|
||||
public void meth1(int i) { }
|
||||
public void meth1(string s) { }
|
||||
}
|
||||
public class Class2 : Test.Rename.Dll.test.pub2.IFace1 {
|
||||
void Test.Rename.Dll.test.pub2.IFace1.meth1(int i) { }
|
||||
void Test.Rename.Dll.test.pub2.IFace1.meth1(string s) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace test3 {
|
||||
public class Class1 : Test.Rename.Dll.test.pub3.Class1<int>.IFace1<string> {
|
||||
public void meth1(int t) { }
|
||||
public void meth1(string u) { }
|
||||
public void meth1<V>(V v) { }
|
||||
}
|
||||
public class Class2 : Test.Rename.Dll.test.pub3.Class1<int>.IFace1<string> {
|
||||
void Test.Rename.Dll.test.pub3.Class1<int>.IFace1<string>.meth1(int t) { }
|
||||
void Test.Rename.Dll.test.pub3.Class1<int>.IFace1<string>.meth1(string u) { }
|
||||
void Test.Rename.Dll.test.pub3.Class1<int>.IFace1<string>.meth1<V>(V v) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace test4 {
|
||||
public interface IFace {
|
||||
void meth1(int i);
|
||||
void meth1(string s);
|
||||
}
|
||||
public class Class1 : Test.Rename.Dll.test.pub4.Class1.EnclosedClass, IFace {
|
||||
public override void meth1() { }
|
||||
public void meth1(string s) { }
|
||||
}
|
||||
}
|
||||
|
||||
namespace test5 {
|
||||
public class Class1 : Test.Rename.Dll.test.pub5.Class1, Test.Rename.Dll.test.pub5.IFace1 {
|
||||
// The C# compiler will create a private virtual method for us, calling Class1's
|
||||
// non-virtual method.
|
||||
}
|
||||
}
|
||||
|
||||
class Program {
|
||||
static void Main(string[] args) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("Test.Rename")]
|
||||
[assembly: AssemblyDescription("Other renamer tests")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Test.Rename")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{3FEB2023-B4B0-4DF3-BECF-AD727B55FF37}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Test.Rename</RootNamespace>
|
||||
<AssemblyName>Test.Rename</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Test.Rename.Dll\Test.Rename.Dll.csproj">
|
||||
<Project>{3B408415-5203-4618-ABC1-F88D0BBE849B}</Project>
|
||||
<Name>Test.Rename.Dll</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1 @@
|
|||
Subproject commit bd300e0f349f509cf46d565f05a0df32561a7477
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2011 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) {
|
||||
return de4dot.Program.main(de4dot.StartUpArch.x64, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("de4dot-x64")]
|
||||
[assembly: AssemblyDescription("Deobfuscates obfuscated .NET applications - x64")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("de4dot-x64")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,60 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{A9F35AC2-D016-444B-B7E6-FC1D55569021}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>de4dot_x64</RootNamespace>
|
||||
<AssemblyName>de4dot-x64</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- Fix a warning that the referenced mscorlib is not 64-bit... -->
|
||||
<Reference Include="$(Windir)\Microsoft.NET\Framework64\v2.0.50727\mscorlib.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\de4dot.code\de4dot.code.csproj">
|
||||
<Project>{4D10B9EB-3BF1-4D61-A389-CB019E8C9622}</Project>
|
||||
<Name>d4d.code</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0"/>
|
||||
<supportedRuntime version="v2.0.50727"/>
|
||||
</startup>
|
||||
</configuration>
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2011 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_x86 {
|
||||
class Program {
|
||||
static int Main(string[] args) {
|
||||
return de4dot.Program.main(de4dot.StartUpArch.x86, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("de4dot-x86")]
|
||||
[assembly: AssemblyDescription("Deobfuscates obfuscated .NET applications - x86")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("de4dot-x86")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{1FDE7AB1-1D32-43C1-A3D9-32FDD33142F0}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>de4dot_x86</RootNamespace>
|
||||
<AssemblyName>de4dot-x86</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\de4dot.code\de4dot.code.csproj">
|
||||
<Project>{4D10B9EB-3BF1-4D61-A389-CB019E8C9622}</Project>
|
||||
<Name>d4d.code</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Runtime.Remoting;
|
||||
using System.Threading;
|
||||
using AssemblyData;
|
||||
|
||||
namespace de4dot.AssemblyClient {
|
||||
sealed class AssemblyClient : IAssemblyClient {
|
||||
const int WAIT_TIME_BEFORE_CONNECTING = 1000;
|
||||
const int MAX_CONNECT_WAIT_TIME_MS = 2000;
|
||||
IAssemblyServerLoader loader;
|
||||
IAssemblyService service;
|
||||
DateTime serverLoadedTime;
|
||||
|
||||
public IAssemblyService Service {
|
||||
get { return service; }
|
||||
}
|
||||
|
||||
public AssemblyClient()
|
||||
: this(new NewProcessAssemblyServerLoader()) {
|
||||
}
|
||||
|
||||
public AssemblyClient(IAssemblyServerLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
public void connect() {
|
||||
loader.loadServer();
|
||||
service = loader.createService();
|
||||
serverLoadedTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
public void waitConnected() {
|
||||
// If we don't wait here, we'll sometimes get stuck in doNothing(). Make sure the
|
||||
// server has had time to start... This only seems to be needed when starting a
|
||||
// server in a different process, though.
|
||||
var loadedTime = DateTime.UtcNow - serverLoadedTime;
|
||||
var waitTime = WAIT_TIME_BEFORE_CONNECTING - (int)loadedTime.TotalMilliseconds;
|
||||
if (waitTime > 0)
|
||||
Thread.Sleep(waitTime);
|
||||
|
||||
var startTime = DateTime.UtcNow;
|
||||
while (true) {
|
||||
try {
|
||||
service.doNothing();
|
||||
break;
|
||||
}
|
||||
catch (RemotingException) {
|
||||
// Couldn't connect
|
||||
}
|
||||
var elapsedTime = DateTime.UtcNow - startTime;
|
||||
if (elapsedTime.TotalMilliseconds >= MAX_CONNECT_WAIT_TIME_MS)
|
||||
throw new ApplicationException("Could not connect to server");
|
||||
Thread.Sleep(20);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
if (service != null) {
|
||||
try {
|
||||
service.exit();
|
||||
}
|
||||
catch (RemotingException) {
|
||||
// Couldn't connect
|
||||
}
|
||||
service = null;
|
||||
}
|
||||
if (loader != null) {
|
||||
loader.Dispose();
|
||||
loader = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.AssemblyClient {
|
||||
interface IAssemblyClientFactory {
|
||||
IAssemblyClient create();
|
||||
}
|
||||
|
||||
class SameAppDomainAssemblyClientFactory : IAssemblyClientFactory {
|
||||
public IAssemblyClient create() {
|
||||
return new AssemblyClient(new SameAppDomainAssemblyServerLoader());
|
||||
}
|
||||
}
|
||||
|
||||
class NewAppDomainAssemblyClientFactory : IAssemblyClientFactory {
|
||||
public IAssemblyClient create() {
|
||||
return new AssemblyClient(new NewAppDomainAssemblyServerLoader());
|
||||
}
|
||||
}
|
||||
|
||||
class NewProcessAssemblyClientFactory : IAssemblyClientFactory {
|
||||
public IAssemblyClient create() {
|
||||
return new AssemblyClient(new NewProcessAssemblyServerLoader());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 AssemblyData;
|
||||
|
||||
namespace de4dot.AssemblyClient {
|
||||
interface IAssemblyClient : IDisposable {
|
||||
IAssemblyService Service { get; }
|
||||
void connect();
|
||||
void waitConnected();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 AssemblyData;
|
||||
|
||||
namespace de4dot.AssemblyClient {
|
||||
interface IAssemblyServerLoader : IDisposable {
|
||||
void loadServer();
|
||||
IAssemblyService createService();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 AssemblyData;
|
||||
|
||||
namespace de4dot.AssemblyClient {
|
||||
abstract class IpcAssemblyServerLoader : IAssemblyServerLoader {
|
||||
const string ASSEMBLY_SERVER_FILENAME_ANYCPU = "AssemblyServer.exe";
|
||||
const string ASSEMBLY_SERVER_FILENAME_X86 = "AssemblyServer-x86.exe";
|
||||
const string ASSEMBLY_SERVER_FILENAME_X64 = "AssemblyServer-x64.exe";
|
||||
readonly string assemblyServerFilename;
|
||||
protected string ipcName;
|
||||
protected string ipcUri;
|
||||
string url;
|
||||
|
||||
protected IpcAssemblyServerLoader() {
|
||||
assemblyServerFilename = Utils.getArchString(ASSEMBLY_SERVER_FILENAME_ANYCPU, ASSEMBLY_SERVER_FILENAME_X86, ASSEMBLY_SERVER_FILENAME_X64);
|
||||
ipcName = Utils.randomName(15, 20);
|
||||
ipcUri = Utils.randomName(15, 20);
|
||||
url = string.Format("ipc://{0}/{1}", ipcName, ipcUri);
|
||||
}
|
||||
|
||||
public void loadServer() {
|
||||
loadServer(Utils.getPathOfOurFile(assemblyServerFilename));
|
||||
}
|
||||
|
||||
public abstract void loadServer(string filename);
|
||||
|
||||
public IAssemblyService createService() {
|
||||
return (IAssemblyService)Activator.GetObject(typeof(AssemblyService), url);
|
||||
}
|
||||
|
||||
public abstract void Dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Threading;
|
||||
|
||||
namespace de4dot.AssemblyClient {
|
||||
// Starts the server in a new app domain.
|
||||
sealed class NewAppDomainAssemblyServerLoader : IpcAssemblyServerLoader {
|
||||
AppDomain appDomain;
|
||||
Thread thread;
|
||||
|
||||
public override void loadServer(string filename) {
|
||||
if (appDomain != null)
|
||||
throw new ApplicationException("Server is already loaded");
|
||||
|
||||
appDomain = AppDomain.CreateDomain(Utils.randomName(15, 20));
|
||||
thread = new Thread(new ThreadStart(() => {
|
||||
try {
|
||||
appDomain.ExecuteAssembly(filename, null, new string[] { ipcName, ipcUri });
|
||||
}
|
||||
catch (NullReferenceException) {
|
||||
// Here if appDomain was set to null by Dispose() before this thread started
|
||||
}
|
||||
catch (AppDomainUnloadedException) {
|
||||
// Here if it was unloaded by Dispose()
|
||||
}
|
||||
unloadAppDomain(appDomain);
|
||||
appDomain = null;
|
||||
}));
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
unloadAppDomain(appDomain);
|
||||
if (thread != null) {
|
||||
try {
|
||||
if (!thread.Join(100))
|
||||
thread.Abort();
|
||||
}
|
||||
catch (ThreadStateException) {
|
||||
// Here if eg. the thread wasn't started
|
||||
}
|
||||
thread = null;
|
||||
}
|
||||
// It could still be loaded if the thread was aborted so do it again
|
||||
unloadAppDomain(appDomain);
|
||||
appDomain = null;
|
||||
}
|
||||
|
||||
static void unloadAppDomain(AppDomain appDomain) {
|
||||
if (appDomain != null) {
|
||||
try {
|
||||
AppDomain.Unload(appDomain);
|
||||
}
|
||||
catch (AppDomainUnloadedException) {
|
||||
}
|
||||
catch (CannotUnloadAppDomainException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Diagnostics;
|
||||
|
||||
namespace de4dot.AssemblyClient {
|
||||
// Starts the server in a new process
|
||||
class NewProcessAssemblyServerLoader : IpcAssemblyServerLoader {
|
||||
Process process;
|
||||
|
||||
public override void loadServer(string filename) {
|
||||
if (process != null)
|
||||
throw new ApplicationException("Server is already loaded");
|
||||
|
||||
var psi = new ProcessStartInfo {
|
||||
Arguments = string.Format("{0} {1}", Utils.shellEscape(ipcName), Utils.shellEscape(ipcUri)),
|
||||
CreateNoWindow = true,
|
||||
ErrorDialog = false,
|
||||
FileName = filename,
|
||||
LoadUserProfile = false,
|
||||
UseShellExecute = false,
|
||||
WorkingDirectory = Utils.getOurBaseDir(),
|
||||
};
|
||||
process = Process.Start(psi);
|
||||
if (process == null)
|
||||
throw new ApplicationException("Could not start process");
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
if (process != null) {
|
||||
if (!process.WaitForExit(300)) {
|
||||
try {
|
||||
process.Kill();
|
||||
}
|
||||
catch (InvalidOperationException) {
|
||||
// Here if process has already exited.
|
||||
}
|
||||
}
|
||||
process = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 AssemblyData;
|
||||
|
||||
namespace de4dot.AssemblyClient {
|
||||
// Starts the server in the current app domain.
|
||||
class SameAppDomainAssemblyServerLoader : IAssemblyServerLoader {
|
||||
IAssemblyService service;
|
||||
|
||||
public void loadServer() {
|
||||
if (service != null)
|
||||
throw new ApplicationException("Server already loaded");
|
||||
service = new AssemblyService();
|
||||
}
|
||||
|
||||
public IAssemblyService createService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Collections.Generic;
|
||||
using Mono.Cecil;
|
||||
using Mono.MyStuff;
|
||||
|
||||
namespace de4dot {
|
||||
class AssemblyModule {
|
||||
string filename;
|
||||
string methodsFilename;
|
||||
Dictionary<uint, DumpedMethod> dumpedMethods;
|
||||
ModuleDefinition module;
|
||||
|
||||
public AssemblyModule(string filename, string methodsFilename = null) {
|
||||
this.filename = Utils.getFullPath(filename);
|
||||
this.methodsFilename = methodsFilename;
|
||||
|
||||
if (this.methodsFilename == null)
|
||||
this.methodsFilename = this.filename + ".methods";
|
||||
}
|
||||
|
||||
public ModuleDefinition load() {
|
||||
readMethodsFile();
|
||||
readFile();
|
||||
return module;
|
||||
}
|
||||
|
||||
public void save(string newFilename) {
|
||||
module.Assembly.Write(newFilename);
|
||||
}
|
||||
|
||||
void readMethodsFile() {
|
||||
if (new FileInfo(methodsFilename).Exists) {
|
||||
using (var reader = new BinaryReader(File.Open(methodsFilename, FileMode.Open))) {
|
||||
dumpedMethods = new DumpedMethodsReader(reader).read();
|
||||
}
|
||||
}
|
||||
else {
|
||||
dumpedMethods = new Dictionary<uint, DumpedMethod>();
|
||||
}
|
||||
}
|
||||
|
||||
void readFile() {
|
||||
var assemblyResolver = AssemblyResolver.Instance;
|
||||
var readerParameters = new ReaderParameters(ReadingMode.Deferred);
|
||||
readerParameters.AssemblyResolver = assemblyResolver;
|
||||
module = ModuleDefinition.ReadModule(filename, readerParameters, dumpedMethods);
|
||||
assemblyResolver.addModule(module);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.IO;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace de4dot {
|
||||
class AssemblyResolver : DefaultAssemblyResolver {
|
||||
public static readonly AssemblyResolver Instance = new AssemblyResolver();
|
||||
Dictionary<string, bool> addedAssemblies = new Dictionary<string, bool>(StringComparer.Ordinal);
|
||||
Dictionary<string, bool> addedDirectories = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
static AssemblyResolver() {
|
||||
// Make sure there's only ONE assembly resolver
|
||||
GlobalAssemblyResolver.Instance = Instance;
|
||||
}
|
||||
|
||||
public void addSearchDirectory(string dir) {
|
||||
if (!addedDirectories.ContainsKey(dir)) {
|
||||
addedDirectories[dir] = true;
|
||||
AddSearchDirectory(dir);
|
||||
}
|
||||
}
|
||||
|
||||
public void addModule(ModuleDefinition module) {
|
||||
var assembly = module.Assembly;
|
||||
var name = assembly.Name.FullName;
|
||||
if (!addedAssemblies.ContainsKey(name) && cache.ContainsKey(name))
|
||||
throw new ApplicationException(string.Format("Assembly {0} was loaded by other code.", name));
|
||||
addedAssemblies[name] = true;
|
||||
|
||||
var dir = Path.GetDirectoryName(module.FullyQualifiedName);
|
||||
addSearchDirectory(dir);
|
||||
RegisterAssembly(assembly);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,358 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Collections.Generic;
|
||||
using de4dot.deobfuscators;
|
||||
using de4dot.AssemblyClient;
|
||||
|
||||
namespace de4dot {
|
||||
class CommandLineParser {
|
||||
static Infos stringDecrypterTypes = new Infos();
|
||||
|
||||
ObfuscatedFile.Options newFileOptions = null;
|
||||
IList<IObfuscatedFile> files = new List<IObfuscatedFile>();
|
||||
Dictionary<string, Option> optionsDict = new Dictionary<string, Option>(StringComparer.Ordinal);
|
||||
IList<IDeobfuscatorInfo> deobfuscatorInfos;
|
||||
IList<Option> miscOptions = new List<Option>();
|
||||
IList<Option> fileOptions = new List<Option>();
|
||||
Option defaultOption;
|
||||
FilesDeobfuscator.Options filesOptions;
|
||||
FilesDeobfuscator.SearchDir searchDir;
|
||||
DecrypterType? defaultStringDecrypterType;
|
||||
List<string> defaultStringDecrypterMethods = new List<string>();
|
||||
|
||||
class Info {
|
||||
public object value;
|
||||
public string name;
|
||||
public string desc;
|
||||
|
||||
public Info(object value, string name, string desc) {
|
||||
this.value = value;
|
||||
this.name = name;
|
||||
this.desc = desc;
|
||||
}
|
||||
}
|
||||
|
||||
class Infos {
|
||||
List<Info> infos = new List<Info>();
|
||||
|
||||
public void add(object value, string name, string desc) {
|
||||
infos.Add(new Info(value, name, desc));
|
||||
}
|
||||
|
||||
public IEnumerable<Info> getInfos() {
|
||||
return infos;
|
||||
}
|
||||
|
||||
public bool getValue(string name, out object value) {
|
||||
foreach (var info in infos) {
|
||||
if (name.Equals(info.name, StringComparison.OrdinalIgnoreCase)) {
|
||||
value = info.value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static CommandLineParser() {
|
||||
stringDecrypterTypes.add(DecrypterType.None, "none", "Don't decrypt strings");
|
||||
stringDecrypterTypes.add(DecrypterType.Static, "static", "Use static string decrypter if available");
|
||||
stringDecrypterTypes.add(DecrypterType.Delegate, "delegate", "Use a delegate to call the real string decrypter");
|
||||
}
|
||||
|
||||
public CommandLineParser(IList<IDeobfuscatorInfo> deobfuscatorInfos, FilesDeobfuscator.Options filesOptions) {
|
||||
this.deobfuscatorInfos = deobfuscatorInfos;
|
||||
this.filesOptions = filesOptions;
|
||||
this.filesOptions.DeobfuscatorInfos = deobfuscatorInfos;
|
||||
this.filesOptions.AssemblyClientFactory = new NewAppDomainAssemblyClientFactory();
|
||||
|
||||
addAllOptions();
|
||||
}
|
||||
|
||||
void addAllOptions() {
|
||||
miscOptions.Add(new OneArgOption("r", null, "Scan for .NET files in all subdirs", "dir", (val) => {
|
||||
addSearchDir();
|
||||
searchDir = new FilesDeobfuscator.SearchDir();
|
||||
if (!new DirectoryInfo(val).Exists)
|
||||
exitError(string.Format("Directory {0} does not exist", val));
|
||||
searchDir.InputDirectory = val;
|
||||
}));
|
||||
miscOptions.Add(new OneArgOption("ro", null, "Output base dir for recursively found files", "dir", (val) => {
|
||||
if (searchDir == null)
|
||||
exitError("Missing -r option");
|
||||
searchDir.OutputDirectory = val;
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption("ru", null, "Skip recursively found files with unknown obfuscator", () => {
|
||||
if (searchDir == null)
|
||||
exitError("Missing -r option");
|
||||
searchDir.SkipUnknownObfuscators = true;
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption("d", null, "Detect obfuscators and exit", () => {
|
||||
filesOptions.DetectObfuscators = true;
|
||||
}));
|
||||
miscOptions.Add(new OneArgOption(null, "asmpath", "Add an assembly search path", "path", (val) => {
|
||||
AssemblyResolver.Instance.addSearchDirectory(val);
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption(null, "dont-rename", "Don't rename classes, methods, etc.", () => {
|
||||
filesOptions.RenameSymbols = false;
|
||||
}));
|
||||
miscOptions.Add(new OneArgOption(null, "default-strtyp", "Default string decrypter type", "type", (val) => {
|
||||
object decrypterType;
|
||||
if (!stringDecrypterTypes.getValue(val, out decrypterType))
|
||||
exitError(string.Format("Invalid string decrypter type '{0}'", val));
|
||||
defaultStringDecrypterType = (DecrypterType)decrypterType;
|
||||
}));
|
||||
miscOptions.Add(new OneArgOption(null, "default-strtok", "Default string decrypter method token or [type::][name][(args,...)]", "method", (val) => {
|
||||
defaultStringDecrypterMethods.Add(val);
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption(null, "no-control-flow-deob", "No control flow deobfuscation (NOT recommended)", () => {
|
||||
filesOptions.ControlFlowDeobfuscation = false;
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption(null, "load-new-process", "Load executed assemblies into a new process", () => {
|
||||
filesOptions.AssemblyClientFactory = new NewProcessAssemblyClientFactory();
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption(null, "keep-types", "Keep obfuscator types, fields, methods", () => {
|
||||
filesOptions.KeepObfuscatorTypes = true;
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption("v", null, "Verbose", () => {
|
||||
Log.logLevel = Log.LogLevel.verbose;
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption("vv", null, "Very verbose", () => {
|
||||
Log.logLevel = Log.LogLevel.veryverbose;
|
||||
}));
|
||||
miscOptions.Add(new NoArgOption("h", "help", "Show this help message", () => {
|
||||
usage();
|
||||
exit(0);
|
||||
}));
|
||||
|
||||
defaultOption = new OneArgOption("f", null, "Name of .NET file", "file", (val) => {
|
||||
addFile();
|
||||
if (!new FileInfo(val).Exists)
|
||||
exitError(string.Format("File \"{0}\" does not exist.", val));
|
||||
newFileOptions = new ObfuscatedFile.Options {
|
||||
Filename = val,
|
||||
RenameSymbols = filesOptions.RenameSymbols,
|
||||
ControlFlowDeobfuscation = filesOptions.ControlFlowDeobfuscation,
|
||||
KeepObfuscatorTypes = filesOptions.KeepObfuscatorTypes,
|
||||
};
|
||||
if (defaultStringDecrypterType != null)
|
||||
newFileOptions.StringDecrypterType = defaultStringDecrypterType.Value;
|
||||
newFileOptions.StringDecrypterMethods.AddRange(defaultStringDecrypterMethods);
|
||||
});
|
||||
fileOptions.Add(defaultOption);
|
||||
fileOptions.Add(new OneArgOption("m", null, "Name of .methods file", "file", (val) => {
|
||||
if (newFileOptions == null)
|
||||
exitError("Missing input file");
|
||||
if (!new FileInfo(val).Exists)
|
||||
exitError(string.Format("File \"{0}\" does not exist.", val));
|
||||
newFileOptions.MethodsFilename = val;
|
||||
}));
|
||||
fileOptions.Add(new OneArgOption("o", null, "Name of output file", "file", (val) => {
|
||||
if (newFileOptions == null)
|
||||
exitError("Missing input file");
|
||||
if (string.Equals(Utils.getFullPath(newFileOptions.Filename), Utils.getFullPath(val), StringComparison.OrdinalIgnoreCase))
|
||||
exitError(string.Format("Output file can't be same as input file ({0})", val));
|
||||
newFileOptions.NewFilename = val;
|
||||
}));
|
||||
fileOptions.Add(new OneArgOption("p", null, "Obfuscator type (see below)", "type", (val) => {
|
||||
if (newFileOptions == null)
|
||||
exitError("Missing input file");
|
||||
if (!isValidObfuscatorType(val))
|
||||
exitError(string.Format("Invalid obfuscator type '{0}'", val));
|
||||
newFileOptions.ForcedObfuscatorType = val;
|
||||
}));
|
||||
fileOptions.Add(new OneArgOption(null, "strtyp", "String decrypter type", "type", (val) => {
|
||||
if (newFileOptions == null)
|
||||
exitError("Missing input file");
|
||||
object decrypterType;
|
||||
if (!stringDecrypterTypes.getValue(val, out decrypterType))
|
||||
exitError(string.Format("Invalid string decrypter type '{0}'", val));
|
||||
newFileOptions.StringDecrypterType = (DecrypterType)decrypterType;
|
||||
}));
|
||||
fileOptions.Add(new OneArgOption(null, "strtok", "String decrypter method token or [type::][name][(args,...)]", "method", (val) => {
|
||||
if (newFileOptions == null)
|
||||
exitError("Missing input file");
|
||||
newFileOptions.StringDecrypterMethods.Add(val);
|
||||
}));
|
||||
|
||||
addOptions(miscOptions);
|
||||
addOptions(fileOptions);
|
||||
foreach (var info in deobfuscatorInfos)
|
||||
addOptions(info.getOptions());
|
||||
}
|
||||
|
||||
void addOptions(IEnumerable<Option> options) {
|
||||
foreach (var option in options) {
|
||||
addOption(option, option.ShortName);
|
||||
addOption(option, option.LongName);
|
||||
}
|
||||
}
|
||||
|
||||
void addOption(Option option, string name) {
|
||||
if (name == null)
|
||||
return;
|
||||
if (optionsDict.ContainsKey(name))
|
||||
throw new ApplicationException(string.Format("Option {0} is present twice!", name));
|
||||
optionsDict[name] = option;
|
||||
}
|
||||
|
||||
public void parse(string[] args) {
|
||||
if (args.Length == 0) {
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.Length; i++) {
|
||||
var arg = args[i];
|
||||
|
||||
string val = null;
|
||||
Option option;
|
||||
if (optionsDict.TryGetValue(arg, out option)) {
|
||||
if (option.NeedArgument) {
|
||||
if (++i >= args.Length)
|
||||
exitError("Missing options value");
|
||||
val = args[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
option = defaultOption;
|
||||
val = arg;
|
||||
}
|
||||
|
||||
string errorString;
|
||||
if (!option.set(val, out errorString))
|
||||
exitError(errorString);
|
||||
}
|
||||
addFile();
|
||||
addSearchDir();
|
||||
filesOptions.Files = files;
|
||||
filesOptions.DefaultStringDecrypterMethods.AddRange(defaultStringDecrypterMethods);
|
||||
filesOptions.DefaultStringDecrypterType = defaultStringDecrypterType;
|
||||
}
|
||||
|
||||
void addFile() {
|
||||
if (newFileOptions == null)
|
||||
return;
|
||||
files.Add(new ObfuscatedFile(newFileOptions, filesOptions.AssemblyClientFactory));
|
||||
newFileOptions = null;
|
||||
}
|
||||
|
||||
void addSearchDir() {
|
||||
if (searchDir == null)
|
||||
return;
|
||||
filesOptions.SearchDirs.Add(searchDir);
|
||||
searchDir = null;
|
||||
}
|
||||
|
||||
bool isValidObfuscatorType(string type) {
|
||||
foreach (var info in deobfuscatorInfos) {
|
||||
if (string.Equals(info.Type, type, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void exitError(string msg) {
|
||||
usage();
|
||||
Log.e("\n\nERROR: {0}\n", msg);
|
||||
exit(2);
|
||||
}
|
||||
|
||||
void exit(int exitCode) {
|
||||
Environment.Exit(exitCode);
|
||||
}
|
||||
|
||||
void usage() {
|
||||
string progName = getProgramBaseName();
|
||||
Log.n("Some of the advanced options may be incompatible, causing a nice exception.");
|
||||
Log.n("With great power comes great responsibility.");
|
||||
Log.n("");
|
||||
Log.n("{0} <options> <file options>", progName);
|
||||
Log.n("Options:");
|
||||
foreach (var option in miscOptions)
|
||||
printOption(option);
|
||||
Log.n("");
|
||||
Log.n("File options:");
|
||||
foreach (var option in fileOptions)
|
||||
printOption(option);
|
||||
Log.n("");
|
||||
Log.n("Deobfuscator options:");
|
||||
foreach (var info in deobfuscatorInfos) {
|
||||
Log.n("Type {0}", info.Type);
|
||||
foreach (var option in info.getOptions())
|
||||
printOption(option);
|
||||
Log.n("");
|
||||
}
|
||||
printInfos("String decrypter types", stringDecrypterTypes);
|
||||
Log.n("");
|
||||
Log.n("Multiple regexes can be used if separated by '{0}'.", NameRegexes.regexSeparatorChar);
|
||||
Log.n("Use '{0}' if you want to invert the regex. Example: {0}^[a-z\\d]{{1,2}}${1}{0}^[A-Z]_\\d+${1}^[\\w.]+$", NameRegex.invertChar, NameRegexes.regexSeparatorChar);
|
||||
Log.n("");
|
||||
Log.n("Examples:");
|
||||
Log.n("{0} -r c:\\my\\files -ro c:\\my\\output", progName);
|
||||
Log.n("{0} file1 file2 file3", progName);
|
||||
Log.n("{0} file1 -f file2 -o file2.out -f file3 -m file3.methods -o file3.out", progName);
|
||||
Log.n("{0} file1 --strtyp delegate --strtok \"<Module>::(System.String, System.Int32)\"", progName);
|
||||
}
|
||||
|
||||
string getProgramBaseName() {
|
||||
return Utils.getBaseName(Environment.GetCommandLineArgs()[0]);
|
||||
}
|
||||
|
||||
void printInfos(string desc, Infos infos) {
|
||||
Log.n("{0}", desc);
|
||||
foreach (var info in infos.getInfos())
|
||||
printOptionAndExplanation(info.name, info.desc);
|
||||
}
|
||||
|
||||
void printOption(Option option) {
|
||||
string defaultAndDesc;
|
||||
if (option.NeedArgument && option.Default != null)
|
||||
defaultAndDesc = string.Format("{0} ({1})", option.Description, option.Default);
|
||||
else
|
||||
defaultAndDesc = option.Description;
|
||||
printOptionAndExplanation(getOptionAndArgName(option, option.ShortName ?? option.LongName), defaultAndDesc);
|
||||
if (option.ShortName != null && option.LongName != null)
|
||||
printOptionAndExplanation(option.LongName, string.Format("Same as {0}", option.ShortName));
|
||||
}
|
||||
|
||||
void printOptionAndExplanation(string option, string explanation) {
|
||||
const int maxCols = 16;
|
||||
const string prefix = " ";
|
||||
string left = string.Format(string.Format("{{0,-{0}}}", maxCols), option);
|
||||
if (option.Length > maxCols) {
|
||||
Log.n("{0}{1}", prefix, left);
|
||||
Log.n("{0}{1} {2}", prefix, new string(' ', maxCols), explanation);
|
||||
}
|
||||
else
|
||||
Log.n("{0}{1} {2}", prefix, left, explanation);
|
||||
}
|
||||
|
||||
string getOptionAndArgName(Option option, string optionName) {
|
||||
if (option.NeedArgument)
|
||||
return optionName + " " + option.ArgumentValueName.ToUpperInvariant();
|
||||
else
|
||||
return optionName;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,584 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.IO;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot {
|
||||
class CallCounter {
|
||||
Dictionary<MethodReferenceAndDeclaringTypeKey, int> calls = new Dictionary<MethodReferenceAndDeclaringTypeKey, int>();
|
||||
|
||||
public void add(MethodReference calledMethod) {
|
||||
int count;
|
||||
var key = new MethodReferenceAndDeclaringTypeKey(calledMethod);
|
||||
calls.TryGetValue(key, out count);
|
||||
calls[key] = count + 1;
|
||||
}
|
||||
|
||||
public MethodReference most() {
|
||||
MethodReference method = null;
|
||||
int callCount = 0;
|
||||
foreach (var key in calls.Keys) {
|
||||
if (calls[key] > callCount) {
|
||||
callCount = calls[key];
|
||||
method = key.MethodReference;
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
class MethodCalls {
|
||||
Dictionary<string, int> methodCalls = new Dictionary<string, int>(StringComparer.Ordinal);
|
||||
|
||||
public void addMethodCalls(MethodDefinition method) {
|
||||
if (!method.HasBody)
|
||||
return;
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
var calledMethod = instr.Operand as MethodReference;
|
||||
if (calledMethod != null)
|
||||
add(calledMethod);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(MethodReference method) {
|
||||
string key = method.FullName;
|
||||
if (!methodCalls.ContainsKey(key))
|
||||
methodCalls[key] = 0;
|
||||
methodCalls[key]++;
|
||||
}
|
||||
|
||||
public int count(string methodFullName) {
|
||||
int count;
|
||||
methodCalls.TryGetValue(methodFullName, out count);
|
||||
return count;
|
||||
}
|
||||
|
||||
public bool called(string methodFullName) {
|
||||
return count(methodFullName) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class DotNetUtils {
|
||||
public static bool isLdcI4(Instruction instruction) {
|
||||
return isLdcI4(instruction.OpCode.Code);
|
||||
}
|
||||
|
||||
public static bool isLdcI4(Code code) {
|
||||
switch (code) {
|
||||
case Code.Ldc_I4_M1:
|
||||
case Code.Ldc_I4_0:
|
||||
case Code.Ldc_I4_1:
|
||||
case Code.Ldc_I4_2:
|
||||
case Code.Ldc_I4_3:
|
||||
case Code.Ldc_I4_4:
|
||||
case Code.Ldc_I4_5:
|
||||
case Code.Ldc_I4_6:
|
||||
case Code.Ldc_I4_7:
|
||||
case Code.Ldc_I4_8:
|
||||
case Code.Ldc_I4_S:
|
||||
case Code.Ldc_I4:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getLdcI4Value(Instruction instruction) {
|
||||
switch (instruction.OpCode.Code) {
|
||||
case Code.Ldc_I4_M1:return -1;
|
||||
case Code.Ldc_I4_0: return 0;
|
||||
case Code.Ldc_I4_1: return 1;
|
||||
case Code.Ldc_I4_2: return 2;
|
||||
case Code.Ldc_I4_3: return 3;
|
||||
case Code.Ldc_I4_4: return 4;
|
||||
case Code.Ldc_I4_5: return 5;
|
||||
case Code.Ldc_I4_6: return 6;
|
||||
case Code.Ldc_I4_7: return 7;
|
||||
case Code.Ldc_I4_8: return 8;
|
||||
case Code.Ldc_I4_S: return (sbyte)instruction.Operand;
|
||||
case Code.Ldc_I4: return (int)instruction.Operand;
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Not an ldc.i4 instruction: {0}", instruction));
|
||||
}
|
||||
}
|
||||
|
||||
public static bool isConditionalBranch(Code code) {
|
||||
switch (code) {
|
||||
case Code.Bge:
|
||||
case Code.Bge_S:
|
||||
case Code.Bge_Un:
|
||||
case Code.Bge_Un_S:
|
||||
case Code.Blt:
|
||||
case Code.Blt_S:
|
||||
case Code.Blt_Un:
|
||||
case Code.Blt_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.Brfalse:
|
||||
case Code.Brfalse_S:
|
||||
case Code.Brtrue:
|
||||
case Code.Brtrue_S:
|
||||
case Code.Beq:
|
||||
case Code.Beq_S:
|
||||
case Code.Bne_Un:
|
||||
case Code.Bne_Un_S:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static TypeDefinition getModuleType(ModuleDefinition module) {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.FullName == "<Module>")
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool isEmpty(MethodDefinition method) {
|
||||
if (!method.HasBody)
|
||||
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 FieldDefinition findFieldType(TypeDefinition typeDefinition, string typeName, bool isStatic) {
|
||||
if (typeDefinition == null)
|
||||
return null;
|
||||
foreach (var field in typeDefinition.Fields) {
|
||||
if (field.FieldType.FullName == typeName && field.IsStatic == isStatic)
|
||||
return field;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodDefinition> findMethods(IEnumerable<MethodDefinition> methods, string returnType, string[] argsTypes, bool isStatic = true) {
|
||||
foreach (var method in methods) {
|
||||
if (!method.HasBody || method.CallingConvention != MethodCallingConvention.Default)
|
||||
continue;
|
||||
if (method.IsStatic != isStatic || method.Parameters.Count != argsTypes.Length)
|
||||
continue;
|
||||
if (method.GenericParameters.Count > 0)
|
||||
continue;
|
||||
if (method.MethodReturnType.ReturnType.FullName != returnType)
|
||||
continue;
|
||||
for (int i = 0; i < argsTypes.Length; i++) {
|
||||
if (method.Parameters[i].ParameterType.FullName != argsTypes[i])
|
||||
goto next;
|
||||
}
|
||||
yield return method;
|
||||
next: ;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool isDelegateType(TypeDefinition type) {
|
||||
return type != null && type.BaseType != null && type.BaseType.FullName == "System.MulticastDelegate";
|
||||
}
|
||||
|
||||
public static bool isMethod(MethodReference method, string returnType, string parameters) {
|
||||
return method != null && method.FullName == returnType + " " + method.DeclaringType.FullName + "::" + method.Name + parameters;
|
||||
}
|
||||
|
||||
public static MethodDefinition getPInvokeMethod(TypeDefinition type, string dll, string funcName) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (isPinvokeMethod(method, dll, funcName))
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static bool isPinvokeMethod(MethodDefinition method, string dll, string funcName) {
|
||||
if (method == null)
|
||||
return false;
|
||||
if (!method.HasPInvokeInfo || method.PInvokeInfo.EntryPoint != funcName)
|
||||
return false;
|
||||
return getDllName(dll).Equals(getDllName(method.PInvokeInfo.Module.Name), StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public static string getDllName(string dll) {
|
||||
if (dll.EndsWith(".dll", StringComparison.OrdinalIgnoreCase))
|
||||
return dll.Substring(0, dll.Length - 4);
|
||||
return dll;
|
||||
}
|
||||
|
||||
public static MethodDefinition getMethod(TypeDefinition type, string name) {
|
||||
if (type == null)
|
||||
return null;
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.Name == name)
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MethodDefinition getMethod(TypeDefinition type, MethodReference methodReference) {
|
||||
if (type == null || methodReference == null)
|
||||
return null;
|
||||
if (methodReference is MethodDefinition)
|
||||
return (MethodDefinition)methodReference;
|
||||
foreach (var method in type.Methods) {
|
||||
if (MemberReferenceHelper.compareMethodReference(method, methodReference))
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static MethodDefinition getMethod(ModuleDefinition module, MethodReference method) {
|
||||
if (method == null)
|
||||
return null;
|
||||
if (method is MethodDefinition)
|
||||
return (MethodDefinition)method;
|
||||
return getMethod(getType(module, method.DeclaringType), method);
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodDefinition> getNormalMethods(TypeDefinition type) {
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.HasPInvokeInfo)
|
||||
continue;
|
||||
if (method.Name == ".ctor" || method.Name == ".cctor")
|
||||
continue;
|
||||
|
||||
yield return method;
|
||||
}
|
||||
}
|
||||
|
||||
public static TypeDefinition getType(ModuleDefinition module, TypeReference typeReference) {
|
||||
if (typeReference == null)
|
||||
return null;
|
||||
if (typeReference is TypeDefinition)
|
||||
return (TypeDefinition)typeReference;
|
||||
foreach (var type in module.GetTypes()) {
|
||||
if (MemberReferenceHelper.compareTypes(type, typeReference))
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static FieldDefinition getField(ModuleDefinition module, FieldReference field) {
|
||||
if (field is FieldDefinition)
|
||||
return (FieldDefinition)field;
|
||||
return getField(getType(module, field.DeclaringType), field);
|
||||
}
|
||||
|
||||
public static FieldDefinition getField(TypeDefinition type, FieldReference fieldReference) {
|
||||
if (type == null || fieldReference == null)
|
||||
return null;
|
||||
if (fieldReference is FieldDefinition)
|
||||
return (FieldDefinition)fieldReference;
|
||||
foreach (var field in type.Fields) {
|
||||
if (MemberReferenceHelper.compareFieldReference(field, fieldReference))
|
||||
return field;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static IEnumerable<MethodReference> getMethodCalls(MethodDefinition method) {
|
||||
var list = new List<MethodReference>();
|
||||
if (method.HasBody) {
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
var calledMethod = instr.Operand as MethodReference;
|
||||
if (calledMethod != null)
|
||||
list.Add(calledMethod);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static MethodCalls getMethodCallCounts(MethodDefinition method) {
|
||||
var methodCalls = new MethodCalls();
|
||||
methodCalls.addMethodCalls(method);
|
||||
return methodCalls;
|
||||
}
|
||||
|
||||
public static IList<string> getCodeStrings(MethodDefinition method) {
|
||||
var strings = new List<string>();
|
||||
if (method.HasBody) {
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode.Code == Code.Ldstr)
|
||||
strings.Add((string)instr.Operand);
|
||||
}
|
||||
}
|
||||
return strings;
|
||||
}
|
||||
|
||||
public static Resource getResource(ModuleDefinition module, string name) {
|
||||
return getResource(module, new List<string> { name });
|
||||
}
|
||||
|
||||
public static Resource getResource(ModuleDefinition module, IEnumerable<string> strings) {
|
||||
if (!module.HasResources)
|
||||
return null;
|
||||
|
||||
var resources = module.Resources;
|
||||
foreach (var resourceName in strings) {
|
||||
if (resourceName == null)
|
||||
continue;
|
||||
foreach (var resource in resources) {
|
||||
if (resource.Name == resourceName)
|
||||
return resource;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void copyBody(MethodDefinition 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) {
|
||||
var newInstr = new Instruction {
|
||||
Offset = oldInstr.Offset,
|
||||
OpCode = oldInstr.OpCode,
|
||||
Operand = oldInstr.Operand,
|
||||
SequencePoint = oldInstr.SequencePoint,
|
||||
};
|
||||
instructions.Add(newInstr);
|
||||
}
|
||||
|
||||
foreach (var newInstr in instructions) {
|
||||
var operand = newInstr.Operand;
|
||||
if (operand is Instruction)
|
||||
newInstr.Operand = instructions[oldToIndex[(Instruction)operand]];
|
||||
else if (operand is Instruction[]) {
|
||||
var oldArray = (Instruction[])operand;
|
||||
var newArray = new Instruction[oldArray.Length];
|
||||
for (int i = 0; i < oldArray.Length; 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(MethodDefinition method, IEnumerable<Instruction> instructions, IEnumerable<ExceptionHandler> exceptionHandlers) {
|
||||
if (method == null || !method.HasBody)
|
||||
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 IEnumerable<CustomAttribute> findAttributes(AssemblyDefinition asm, TypeReference attr) {
|
||||
var list = new List<CustomAttribute>();
|
||||
foreach (var cattr in asm.CustomAttributes) {
|
||||
if (MemberReferenceHelper.compareTypes(attr, cattr.AttributeType))
|
||||
list.Add(cattr);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
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.FullName != "System.String")
|
||||
return null;
|
||||
return (string)carg.Value;
|
||||
}
|
||||
|
||||
public static IEnumerable<Tuple<TypeDefinition, MethodDefinition>> getCalledMethods(ModuleDefinition module, MethodDefinition 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 MethodReference;
|
||||
var type = DotNetUtils.getType(module, methodRef.DeclaringType);
|
||||
var methodDef = DotNetUtils.getMethod(type, methodRef);
|
||||
if (methodDef != null) {
|
||||
yield return new Tuple<TypeDefinition, MethodDefinition> {
|
||||
Item1 = type,
|
||||
Item2 = methodDef,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IList<Instruction> getInstructions(IList<Instruction> instructions, int i, params OpCode[] opcodes) {
|
||||
if (i + opcodes.Length > instructions.Count)
|
||||
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 void decryptAndAddResources(ModuleDefinition module, string encryptedName, Func<byte[]> decryptResource) {
|
||||
Log.v("Decrypting resources, name: {0}", Utils.toCsharpString(encryptedName));
|
||||
var decryptedResourceData = decryptResource();
|
||||
if (decryptedResourceData == null)
|
||||
throw new ApplicationException("decryptedResourceData is null");
|
||||
var resourceModule = ModuleDefinition.ReadModule(new MemoryStream(decryptedResourceData));
|
||||
|
||||
Log.indent();
|
||||
foreach (var rsrc in resourceModule.Resources) {
|
||||
Log.v("Adding decrypted resource {0}", Utils.toCsharpString(rsrc.Name));
|
||||
module.Resources.Add(rsrc);
|
||||
}
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
public static bool hasReturnValue(IMethodSignature method) {
|
||||
return !MemberReferenceHelper.verifyType(method.MethodReturnType.ReturnType, "mscorlib", "System.Void");
|
||||
}
|
||||
|
||||
public static void updateStack(Instruction instr, ref int stack) {
|
||||
if (instr.OpCode.FlowControl == FlowControl.Call) {
|
||||
var method = (IMethodSignature)instr.Operand;
|
||||
if (hasReturnValue(method))
|
||||
stack++;
|
||||
if (method.HasThis && instr.OpCode.Code == Code.Newobj)
|
||||
stack++;
|
||||
|
||||
if (method.HasParameters)
|
||||
stack -= method.Parameters.Count;
|
||||
if (method.HasThis && instr.OpCode.Code != Code.Newobj)
|
||||
stack--;
|
||||
}
|
||||
else
|
||||
updateStack_nonCall(instr, ref stack);
|
||||
}
|
||||
|
||||
static void updateStack_nonCall(Instruction instr, ref int stack) {
|
||||
StackBehaviour stackBehavior;
|
||||
|
||||
stackBehavior = instr.OpCode.StackBehaviourPush;
|
||||
switch (stackBehavior) {
|
||||
case StackBehaviour.Push0:
|
||||
break;
|
||||
|
||||
case StackBehaviour.Push1:
|
||||
case StackBehaviour.Pushi:
|
||||
case StackBehaviour.Pushi8:
|
||||
case StackBehaviour.Pushr4:
|
||||
case StackBehaviour.Pushr8:
|
||||
case StackBehaviour.Pushref:
|
||||
stack++;
|
||||
break;
|
||||
|
||||
case StackBehaviour.Push1_push1:
|
||||
stack += 2;
|
||||
break;
|
||||
|
||||
case StackBehaviour.Varpush: // only call, calli, callvirt which are handled elsewhere
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Unknown push StackBehavior {0}", stackBehavior));
|
||||
}
|
||||
|
||||
stackBehavior = instr.OpCode.StackBehaviourPop;
|
||||
switch (stackBehavior) {
|
||||
case StackBehaviour.Pop0:
|
||||
break;
|
||||
|
||||
case StackBehaviour.Pop1:
|
||||
case StackBehaviour.Popi:
|
||||
case StackBehaviour.Popref:
|
||||
stack--;
|
||||
break;
|
||||
|
||||
case StackBehaviour.Pop1_pop1:
|
||||
case StackBehaviour.Popi_pop1:
|
||||
case StackBehaviour.Popi_popi:
|
||||
case StackBehaviour.Popi_popi8:
|
||||
case StackBehaviour.Popi_popr4:
|
||||
case StackBehaviour.Popi_popr8:
|
||||
case StackBehaviour.Popref_pop1:
|
||||
case StackBehaviour.Popref_popi:
|
||||
stack -= 2;
|
||||
break;
|
||||
|
||||
case StackBehaviour.Popi_popi_popi:
|
||||
case StackBehaviour.Popref_popi_popi:
|
||||
case StackBehaviour.Popref_popi_popi8:
|
||||
case StackBehaviour.Popref_popi_popr4:
|
||||
case StackBehaviour.Popref_popi_popr8:
|
||||
case StackBehaviour.Popref_popi_popref:
|
||||
stack -= 3;
|
||||
break;
|
||||
|
||||
case StackBehaviour.PopAll:
|
||||
stack = 0;
|
||||
break;
|
||||
|
||||
case StackBehaviour.Varpop: // call, calli, callvirt, newobj (all handled elsewhere), and ret
|
||||
// It's RET. Ignore decrementing stack once if method returns something
|
||||
// since it's not important and we don't know whether it returns anything.
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Unknown pop StackBehavior {0}", stackBehavior));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Collections.Generic;
|
||||
using de4dot.renamer;
|
||||
using de4dot.deobfuscators;
|
||||
using de4dot.AssemblyClient;
|
||||
|
||||
namespace de4dot {
|
||||
class FilesDeobfuscator {
|
||||
Options options;
|
||||
|
||||
public class Options {
|
||||
public IList<IDeobfuscatorInfo> DeobfuscatorInfos { get; set; }
|
||||
public IList<IObfuscatedFile> Files { get; set; }
|
||||
public IList<SearchDir> SearchDirs { get; set; }
|
||||
public bool DetectObfuscators { get; set; }
|
||||
public bool RenameSymbols { get; set; }
|
||||
public bool ControlFlowDeobfuscation { get; set; }
|
||||
public bool KeepObfuscatorTypes { get; set; }
|
||||
public DecrypterType? DefaultStringDecrypterType { get; set; }
|
||||
public List<string> DefaultStringDecrypterMethods { get; private set; }
|
||||
public IAssemblyClientFactory AssemblyClientFactory { get; set; }
|
||||
|
||||
public Options() {
|
||||
DeobfuscatorInfos = new List<IDeobfuscatorInfo>();
|
||||
Files = new List<IObfuscatedFile>();
|
||||
SearchDirs = new List<SearchDir>();
|
||||
DefaultStringDecrypterMethods = new List<string>();
|
||||
RenameSymbols = true;
|
||||
ControlFlowDeobfuscation = true;
|
||||
}
|
||||
}
|
||||
|
||||
public class SearchDir {
|
||||
public string InputDirectory { get; set; }
|
||||
public string OutputDirectory { get; set; }
|
||||
public bool SkipUnknownObfuscators { get; set; }
|
||||
}
|
||||
|
||||
public FilesDeobfuscator(Options options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public void doIt() {
|
||||
if (options.DetectObfuscators)
|
||||
loadAllFiles();
|
||||
else
|
||||
deobfuscateAll();
|
||||
}
|
||||
|
||||
void deobfuscateAll() {
|
||||
loadAllFiles();
|
||||
deobfuscateAllFiles();
|
||||
renameAllFiles();
|
||||
saveAllFiles();
|
||||
}
|
||||
|
||||
void loadAllFiles() {
|
||||
var loader = new DotNetFileLoader(new DotNetFileLoader.Options {
|
||||
PossibleFiles = options.Files,
|
||||
SearchDirs = options.SearchDirs,
|
||||
CreateDeobfuscators = () => createDeobfuscators(),
|
||||
DefaultStringDecrypterType = options.DefaultStringDecrypterType,
|
||||
DefaultStringDecrypterMethods = options.DefaultStringDecrypterMethods,
|
||||
AssemblyClientFactory = options.AssemblyClientFactory,
|
||||
RenameSymbols = options.RenameSymbols,
|
||||
ControlFlowDeobfuscation = options.ControlFlowDeobfuscation,
|
||||
KeepObfuscatorTypes = options.KeepObfuscatorTypes,
|
||||
});
|
||||
options.Files = loader.load();
|
||||
}
|
||||
|
||||
class DotNetFileLoader {
|
||||
Options options;
|
||||
Dictionary<string, bool> allFiles = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
Dictionary<string, bool> visitedDirectory = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||
IList<IObfuscatedFile> files;
|
||||
|
||||
public class Options {
|
||||
public IEnumerable<IObfuscatedFile> PossibleFiles { get; set; }
|
||||
public IEnumerable<SearchDir> SearchDirs { get; set; }
|
||||
public Func<IEnumerable<IDeobfuscator>> CreateDeobfuscators { get; set; }
|
||||
public DecrypterType? DefaultStringDecrypterType { get; set; }
|
||||
public List<string> DefaultStringDecrypterMethods { get; set; }
|
||||
public IAssemblyClientFactory AssemblyClientFactory { get; set; }
|
||||
public bool RenameSymbols { get; set; }
|
||||
public bool ControlFlowDeobfuscation { get; set; }
|
||||
public bool KeepObfuscatorTypes { get; set; }
|
||||
}
|
||||
|
||||
public DotNetFileLoader(Options options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public IList<IObfuscatedFile> load() {
|
||||
files = new List<IObfuscatedFile>();
|
||||
|
||||
foreach (var file in options.PossibleFiles)
|
||||
add(file);
|
||||
|
||||
foreach (var searchDir in options.SearchDirs)
|
||||
recursiveAdd(searchDir);
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
void add(IObfuscatedFile file, bool skipUnknownObfuscator = false) {
|
||||
var key = Utils.getFullPath(file.Filename);
|
||||
if (allFiles.ContainsKey(key)) {
|
||||
Log.w("Ingoring duplicate file: {0}", file.Filename);
|
||||
return;
|
||||
}
|
||||
allFiles[key] = true;
|
||||
|
||||
try {
|
||||
file.load(options.CreateDeobfuscators());
|
||||
}
|
||||
catch (NotSupportedException) {
|
||||
return; // Eg. unsupported architecture
|
||||
}
|
||||
catch (BadImageFormatException) {
|
||||
return; // Not a .NET file
|
||||
}
|
||||
catch (IOException) {
|
||||
Log.w("Could not load file: {0}", file.Filename);
|
||||
return;
|
||||
}
|
||||
|
||||
var deob = file.Deobfuscator;
|
||||
if (skipUnknownObfuscator && deob is deobfuscators.Unknown.Deobfuscator) {
|
||||
Log.v("Skipping unknown obfuscator: {0}", file.Filename);
|
||||
}
|
||||
else {
|
||||
Log.n("Detected {0} ({1})", deob.Name, file.Filename);
|
||||
files.Add(file);
|
||||
createDirectories(Path.GetDirectoryName(file.NewFilename));
|
||||
}
|
||||
}
|
||||
|
||||
void recursiveAdd(SearchDir searchDir) {
|
||||
DirectoryInfo di;
|
||||
try {
|
||||
di = new DirectoryInfo(searchDir.InputDirectory);
|
||||
if (!di.Exists)
|
||||
return;
|
||||
}
|
||||
catch (System.Security.SecurityException) {
|
||||
return;
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
return;
|
||||
}
|
||||
doDirectoryInfo(searchDir, di);
|
||||
}
|
||||
|
||||
void recursiveAdd(SearchDir searchDir, IEnumerable<FileSystemInfo> fileSystemInfos) {
|
||||
foreach (var fsi in fileSystemInfos) {
|
||||
if ((int)(fsi.Attributes & FileAttributes.Directory) != 0)
|
||||
doDirectoryInfo(searchDir, (DirectoryInfo)fsi);
|
||||
else
|
||||
doFileInfo(searchDir, (FileInfo)fsi);
|
||||
}
|
||||
}
|
||||
|
||||
void doDirectoryInfo(SearchDir searchDir, DirectoryInfo di) {
|
||||
if (!di.Exists)
|
||||
return;
|
||||
|
||||
if (visitedDirectory.ContainsKey(di.FullName))
|
||||
return;
|
||||
visitedDirectory[di.FullName] = true;
|
||||
|
||||
FileSystemInfo[] fsinfos;
|
||||
try {
|
||||
fsinfos = di.GetFileSystemInfos();
|
||||
}
|
||||
catch (UnauthorizedAccessException) {
|
||||
return;
|
||||
}
|
||||
catch (IOException) {
|
||||
return;
|
||||
}
|
||||
recursiveAdd(searchDir, fsinfos);
|
||||
}
|
||||
|
||||
void doFileInfo(SearchDir searchDir, FileInfo fi) {
|
||||
if (!fi.Exists)
|
||||
return;
|
||||
|
||||
var fileOptions = new ObfuscatedFile.Options {
|
||||
Filename = Utils.getFullPath(fi.FullName),
|
||||
RenameSymbols = options.RenameSymbols,
|
||||
ControlFlowDeobfuscation = options.ControlFlowDeobfuscation,
|
||||
KeepObfuscatorTypes = options.KeepObfuscatorTypes,
|
||||
};
|
||||
if (options.DefaultStringDecrypterType != null)
|
||||
fileOptions.StringDecrypterType = options.DefaultStringDecrypterType.Value;
|
||||
fileOptions.StringDecrypterMethods.AddRange(options.DefaultStringDecrypterMethods);
|
||||
|
||||
if (!string.IsNullOrEmpty(searchDir.OutputDirectory)) {
|
||||
var inDir = Utils.getFullPath(searchDir.InputDirectory);
|
||||
var outDir = Utils.getFullPath(searchDir.OutputDirectory);
|
||||
|
||||
if (!fileOptions.Filename.StartsWith(inDir, StringComparison.OrdinalIgnoreCase))
|
||||
throw new UserException(string.Format("Filename {0} does not start with inDir {1}", fileOptions.Filename, inDir));
|
||||
|
||||
var subDirs = fileOptions.Filename.Substring(inDir.Length);
|
||||
if (subDirs.Length > 0 && subDirs[0] == Path.DirectorySeparatorChar)
|
||||
subDirs = subDirs.Substring(1);
|
||||
fileOptions.NewFilename = Utils.getFullPath(Path.Combine(outDir, subDirs));
|
||||
|
||||
if (fileOptions.Filename.Equals(fileOptions.NewFilename, StringComparison.OrdinalIgnoreCase))
|
||||
throw new UserException(string.Format("Input and output filename is the same: {0}", fileOptions.Filename));
|
||||
}
|
||||
|
||||
add(new ObfuscatedFile(fileOptions, options.AssemblyClientFactory), searchDir.SkipUnknownObfuscators);
|
||||
}
|
||||
|
||||
void createDirectories(string path) {
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return;
|
||||
var di = new DirectoryInfo(path);
|
||||
if (!di.Exists)
|
||||
di.Create();
|
||||
}
|
||||
}
|
||||
|
||||
void deobfuscateAllFiles() {
|
||||
try {
|
||||
foreach (var file in options.Files)
|
||||
file.deobfuscateBegin();
|
||||
foreach (var file in options.Files) {
|
||||
file.deobfuscate();
|
||||
file.deobfuscateEnd();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
foreach (var file in options.Files)
|
||||
file.deobfuscateCleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
void renameAllFiles() {
|
||||
if (!options.RenameSymbols)
|
||||
return;
|
||||
new DefinitionsRenamer(options.Files).renameAll();
|
||||
}
|
||||
|
||||
void saveAllFiles() {
|
||||
foreach (var file in options.Files)
|
||||
file.save();
|
||||
}
|
||||
|
||||
IEnumerable<IDeobfuscator> createDeobfuscators() {
|
||||
var list = new List<IDeobfuscator>(options.DeobfuscatorInfos.Count);
|
||||
foreach (var info in options.DeobfuscatorInfos)
|
||||
list.Add(info.createDeobfuscator());
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 de4dot.deobfuscators;
|
||||
using Mono.Cecil;
|
||||
|
||||
namespace de4dot {
|
||||
public enum DecrypterType {
|
||||
None,
|
||||
Static,
|
||||
Delegate,
|
||||
}
|
||||
|
||||
interface IObfuscatedFile {
|
||||
ModuleDefinition ModuleDefinition { get; }
|
||||
IDeobfuscator Deobfuscator { get; }
|
||||
string Filename { get; }
|
||||
string NewFilename { get; }
|
||||
Func<string, bool> IsValidName { get; }
|
||||
bool RenameResourcesInCode { get; }
|
||||
bool RenameSymbols { get; }
|
||||
|
||||
void deobfuscateBegin();
|
||||
void deobfuscate();
|
||||
void deobfuscateEnd();
|
||||
void deobfuscateCleanUp();
|
||||
|
||||
void load(IEnumerable<IDeobfuscator> deobfuscators);
|
||||
void save();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 {
|
||||
static class Log {
|
||||
static int indentLevel = 0;
|
||||
const int indentSize = 2;
|
||||
|
||||
public enum LogLevel {
|
||||
error,
|
||||
warning,
|
||||
normal,
|
||||
verbose,
|
||||
veryverbose,
|
||||
}
|
||||
public static LogLevel logLevel = LogLevel.normal;
|
||||
|
||||
public static void indent() {
|
||||
indentLevel++;
|
||||
}
|
||||
|
||||
public static void deIndent() {
|
||||
if (indentLevel <= 0)
|
||||
throw new ApplicationException("Can't de-indent!");
|
||||
indentLevel--;
|
||||
}
|
||||
|
||||
static void log(LogLevel l, string format, params object[] args) {
|
||||
if (l > logLevel)
|
||||
return;
|
||||
string indent = new string(' ', indentLevel * indentSize);
|
||||
if (l <= LogLevel.warning)
|
||||
indent = "";
|
||||
Console.WriteLine(indent + format, args);
|
||||
}
|
||||
|
||||
public static void e(string format, params object[] args) {
|
||||
log(LogLevel.error, format, args);
|
||||
}
|
||||
|
||||
public static void w(string format, params object[] args) {
|
||||
log(LogLevel.warning, format, args);
|
||||
}
|
||||
|
||||
public static void n(string format, params object[] args) {
|
||||
log(LogLevel.normal, format, args);
|
||||
}
|
||||
|
||||
public static void v(string format, params object[] args) {
|
||||
log(LogLevel.verbose, format, args);
|
||||
}
|
||||
|
||||
public static void vv(string format, params object[] args) {
|
||||
log(LogLevel.veryverbose, format, args);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,718 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
|
||||
namespace de4dot {
|
||||
enum CecilType {
|
||||
ArrayType,
|
||||
ByReferenceType,
|
||||
EventDefinition,
|
||||
FieldDefinition,
|
||||
FieldReference,
|
||||
FunctionPointerType,
|
||||
GenericInstanceMethod,
|
||||
GenericInstanceType,
|
||||
GenericParameter,
|
||||
MethodDefinition,
|
||||
MethodReference,
|
||||
OptionalModifierType,
|
||||
PinnedType,
|
||||
PointerType,
|
||||
PropertyDefinition,
|
||||
RequiredModifierType,
|
||||
SentinelType,
|
||||
TypeDefinition,
|
||||
TypeReference,
|
||||
}
|
||||
|
||||
class FieldReferenceKey {
|
||||
FieldReference fieldRef;
|
||||
|
||||
public FieldReference FieldReference {
|
||||
get { return fieldRef; }
|
||||
}
|
||||
|
||||
public FieldReferenceKey(FieldReference fieldRef) {
|
||||
this.fieldRef = fieldRef;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return MemberReferenceHelper.fieldReferenceHashCode(fieldRef);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as FieldReferenceKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return MemberReferenceHelper.compareFieldReference(fieldRef, other.fieldRef);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return fieldRef.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class PropertyReferenceKey {
|
||||
PropertyReference propRef;
|
||||
|
||||
public PropertyReference PropertyReference {
|
||||
get { return propRef; }
|
||||
}
|
||||
|
||||
public PropertyReferenceKey(PropertyReference propRef) {
|
||||
this.propRef = propRef;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return MemberReferenceHelper.propertyReferenceHashCode(propRef);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as PropertyReferenceKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return MemberReferenceHelper.comparePropertyReference(propRef, other.propRef);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return propRef.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class EventReferenceKey {
|
||||
EventReference eventRef;
|
||||
|
||||
public EventReference EventReference {
|
||||
get { return eventRef; }
|
||||
}
|
||||
|
||||
public EventReferenceKey(EventReference eventRef) {
|
||||
this.eventRef = eventRef;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return MemberReferenceHelper.eventReferenceHashCode(eventRef);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as EventReferenceKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return MemberReferenceHelper.compareEventReference(eventRef, other.eventRef);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return eventRef.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class MethodReferenceKey {
|
||||
MethodReference methodRef;
|
||||
|
||||
public MethodReference MethodReference {
|
||||
get { return methodRef; }
|
||||
}
|
||||
|
||||
public MethodReferenceKey(MethodReference methodRef) {
|
||||
this.methodRef = methodRef;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return MemberReferenceHelper.methodReferenceHashCode(methodRef);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as MethodReferenceKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return MemberReferenceHelper.compareMethodReference(methodRef, other.methodRef);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return methodRef.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class FieldReferenceAndDeclaringTypeKey {
|
||||
FieldReference fieldRef;
|
||||
|
||||
public FieldReference FieldReference {
|
||||
get { return fieldRef; }
|
||||
}
|
||||
|
||||
public FieldReferenceAndDeclaringTypeKey(FieldReference fieldRef) {
|
||||
this.fieldRef = fieldRef;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return MemberReferenceHelper.fieldReferenceHashCode(fieldRef) +
|
||||
MemberReferenceHelper.typeHashCode(fieldRef.DeclaringType);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as FieldReferenceAndDeclaringTypeKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return MemberReferenceHelper.compareFieldReference(fieldRef, other.fieldRef) &&
|
||||
MemberReferenceHelper.compareTypes(fieldRef.DeclaringType, other.fieldRef.DeclaringType);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return fieldRef.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class MethodReferenceAndDeclaringTypeKey {
|
||||
MethodReference methodRef;
|
||||
|
||||
public MethodReference MethodReference {
|
||||
get { return methodRef; }
|
||||
}
|
||||
|
||||
public MethodReferenceAndDeclaringTypeKey(MethodReference methodRef) {
|
||||
this.methodRef = methodRef;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
return MemberReferenceHelper.methodReferenceHashCode(methodRef) +
|
||||
MemberReferenceHelper.typeHashCode(methodRef.DeclaringType);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
var other = obj as MethodReferenceAndDeclaringTypeKey;
|
||||
if (other == null)
|
||||
return false;
|
||||
return MemberReferenceHelper.compareMethodReference(methodRef, other.methodRef) &&
|
||||
MemberReferenceHelper.compareTypes(methodRef.DeclaringType, other.methodRef.DeclaringType);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return methodRef.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
static class MemberReferenceHelper {
|
||||
static Dictionary<Type, CecilType> typeToCecilTypeDict = new Dictionary<Type, CecilType>();
|
||||
static MemberReferenceHelper() {
|
||||
typeToCecilTypeDict[typeof(ArrayType)] = CecilType.ArrayType;
|
||||
typeToCecilTypeDict[typeof(ByReferenceType)] = CecilType.ByReferenceType;
|
||||
typeToCecilTypeDict[typeof(EventDefinition)] = CecilType.EventDefinition;
|
||||
typeToCecilTypeDict[typeof(FieldDefinition)] = CecilType.FieldDefinition;
|
||||
typeToCecilTypeDict[typeof(FieldReference)] = CecilType.FieldReference;
|
||||
typeToCecilTypeDict[typeof(FunctionPointerType)] = CecilType.FunctionPointerType;
|
||||
typeToCecilTypeDict[typeof(GenericInstanceMethod)] = CecilType.GenericInstanceMethod;
|
||||
typeToCecilTypeDict[typeof(GenericInstanceType)] = CecilType.GenericInstanceType;
|
||||
typeToCecilTypeDict[typeof(GenericParameter)] = CecilType.GenericParameter;
|
||||
typeToCecilTypeDict[typeof(MethodDefinition)] = CecilType.MethodDefinition;
|
||||
typeToCecilTypeDict[typeof(MethodReference)] = CecilType.MethodReference;
|
||||
typeToCecilTypeDict[typeof(OptionalModifierType)] = CecilType.OptionalModifierType;
|
||||
typeToCecilTypeDict[typeof(PinnedType)] = CecilType.PinnedType;
|
||||
typeToCecilTypeDict[typeof(PointerType)] = CecilType.PointerType;
|
||||
typeToCecilTypeDict[typeof(PropertyDefinition)] = CecilType.PropertyDefinition;
|
||||
typeToCecilTypeDict[typeof(RequiredModifierType)] = CecilType.RequiredModifierType;
|
||||
typeToCecilTypeDict[typeof(SentinelType)] = CecilType.SentinelType;
|
||||
typeToCecilTypeDict[typeof(TypeDefinition)] = CecilType.TypeDefinition;
|
||||
typeToCecilTypeDict[typeof(TypeReference)] = CecilType.TypeReference;
|
||||
}
|
||||
|
||||
public static CecilType getMemberReferenceType(MemberReference memberReference) {
|
||||
CecilType cecilType;
|
||||
var type = memberReference.GetType();
|
||||
if (typeToCecilTypeDict.TryGetValue(type, out cecilType))
|
||||
return cecilType;
|
||||
throw new ApplicationException(string.Format("Unknown MemberReference type: {0}", type));
|
||||
}
|
||||
|
||||
public static bool verifyType(TypeReference typeReference, string assembly, string type, string extra = "") {
|
||||
return MemberReferenceHelper.getCanonicalizedTypeRefName(typeReference.GetElementType()) == "[" + assembly + "]" + type &&
|
||||
typeReference.FullName == type + extra;
|
||||
}
|
||||
|
||||
public static string getCanonicalizedTypeRefName(TypeReference typeRef) {
|
||||
return getCanonicalizedTypeRefName(typeRef.Scope, typeRef.FullName);
|
||||
}
|
||||
|
||||
public static string getCanonicalizedTypeRefName(IMetadataScope scope, string fullName) {
|
||||
return string.Format("[{0}]{1}", getCanonicalizedScopeName(scope), fullName);
|
||||
}
|
||||
|
||||
static string getCanonicalizedScopeName(IMetadataScope scope) {
|
||||
var name = scope.Name.ToLowerInvariant();
|
||||
if (scope is ModuleDefinition) {
|
||||
if (name.EndsWith(".exe", StringComparison.Ordinal) || name.EndsWith(".dll", StringComparison.Ordinal))
|
||||
name = name.Remove(name.Length - 4);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
static bool compareScope(IMetadataScope a, IMetadataScope b) {
|
||||
if (ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
return getCanonicalizedScopeName(a) == getCanonicalizedScopeName(b);
|
||||
}
|
||||
|
||||
static int scopeHashCode(IMetadataScope a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return getCanonicalizedScopeName(a).GetHashCode();
|
||||
}
|
||||
|
||||
public static bool compareEventReference(EventReference a, EventReference b) {
|
||||
if (ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
return a.Name == b.Name &&
|
||||
compareTypes(a.EventType, b.EventType);
|
||||
}
|
||||
|
||||
public static int eventReferenceHashCode(EventReference a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
int res = 0;
|
||||
res += a.Name.GetHashCode();
|
||||
res += typeHashCode(a.EventType);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static bool compareFieldReference(FieldReference a, FieldReference b) {
|
||||
if (ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
return a.Name == b.Name &&
|
||||
compareTypes(a.FieldType, b.FieldType);
|
||||
}
|
||||
|
||||
public static int fieldReferenceHashCode(FieldReference a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
int res = 0;
|
||||
res += a.Name.GetHashCode();
|
||||
res += typeHashCode(a.FieldType);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static bool compareMethodReferenceAndDeclaringType(MethodReference a, MethodReference b) {
|
||||
if (!compareMethodReference(a, b))
|
||||
return false;
|
||||
if (a == null)
|
||||
return true;
|
||||
return compareTypes(a.DeclaringType, b.DeclaringType);
|
||||
}
|
||||
|
||||
public static bool compareMethodReference(MethodReference a, MethodReference b) {
|
||||
if (ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
if (a.Name != b.Name || a.HasThis != b.HasThis || a.ExplicitThis != b.ExplicitThis)
|
||||
return false;
|
||||
if (a.CallingConvention != b.CallingConvention)
|
||||
return false;
|
||||
if (!compareTypes(a.MethodReturnType.ReturnType, b.MethodReturnType.ReturnType))
|
||||
return false;
|
||||
if (a.HasParameters != b.HasParameters)
|
||||
return false;
|
||||
if (a.HasParameters) {
|
||||
if (a.Parameters.Count != b.Parameters.Count)
|
||||
return false;
|
||||
for (int i = 0; i < a.Parameters.Count; i++) {
|
||||
if (!compareTypes(a.Parameters[i].ParameterType, b.Parameters[i].ParameterType))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (a.HasGenericParameters != b.HasGenericParameters)
|
||||
return false;
|
||||
if (a.HasGenericParameters && a.GenericParameters.Count != b.GenericParameters.Count)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int methodReferenceHashCode(MethodReference a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
int res = 0;
|
||||
|
||||
res += a.Name.GetHashCode();
|
||||
res += a.HasThis.GetHashCode();
|
||||
res += a.ExplicitThis.GetHashCode();
|
||||
res += a.CallingConvention.GetHashCode();
|
||||
res += typeHashCode(a.MethodReturnType.ReturnType);
|
||||
res += a.HasParameters.GetHashCode();
|
||||
if (a.HasParameters) {
|
||||
res += a.Parameters.Count.GetHashCode();
|
||||
foreach (var param in a.Parameters)
|
||||
res += typeHashCode(param.ParameterType);
|
||||
}
|
||||
res += a.HasGenericParameters.GetHashCode();
|
||||
if (a.HasGenericParameters)
|
||||
res += a.GenericParameters.Count.GetHashCode();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static bool comparePropertyReference(PropertyReference a, PropertyReference b) {
|
||||
if (ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
if ((a.Parameters == null && b.Parameters != null) || (a.Parameters != null && b.Parameters == null))
|
||||
return false;
|
||||
if (a.Parameters != null) {
|
||||
if (a.Parameters.Count != b.Parameters.Count)
|
||||
return false;
|
||||
for (int i = 0; i < a.Parameters.Count; i++) {
|
||||
if (!compareTypes(a.Parameters[i].ParameterType, b.Parameters[i].ParameterType))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return a.Name == b.Name &&
|
||||
compareTypes(a.PropertyType, b.PropertyType);
|
||||
}
|
||||
|
||||
public static int propertyReferenceHashCode(PropertyReference a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
int res = 0;
|
||||
|
||||
if (a.Parameters != null) {
|
||||
res += a.Parameters.Count.GetHashCode();
|
||||
foreach (var param in a.Parameters)
|
||||
res += typeHashCode(param.ParameterType);
|
||||
}
|
||||
|
||||
res += a.Name.GetHashCode();
|
||||
res += typeHashCode(a.PropertyType);
|
||||
return res;
|
||||
}
|
||||
|
||||
public static bool compareTypes(TypeReference a, TypeReference b) {
|
||||
if (ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
|
||||
var atype = a.GetType();
|
||||
var btype = b.GetType();
|
||||
if (atype != btype) {
|
||||
if ((atype == typeof(TypeReference) || atype == typeof(TypeDefinition)) &&
|
||||
(btype == typeof(TypeReference) || btype == typeof(TypeDefinition)))
|
||||
return compareTypeReferences(a, b);
|
||||
return false;
|
||||
}
|
||||
|
||||
var type = getMemberReferenceType(a);
|
||||
switch (type) {
|
||||
case CecilType.ArrayType:
|
||||
return compareArrayTypes((ArrayType)a, (ArrayType)b);
|
||||
case CecilType.ByReferenceType:
|
||||
return compareByReferenceTypes((ByReferenceType)a, (ByReferenceType)b);
|
||||
case CecilType.FunctionPointerType:
|
||||
return compareFunctionPointerTypes((FunctionPointerType)a, (FunctionPointerType)b);
|
||||
case CecilType.GenericInstanceType:
|
||||
return compareGenericInstanceTypes((GenericInstanceType)a, (GenericInstanceType)b);
|
||||
case CecilType.GenericParameter:
|
||||
return compareGenericParameters((GenericParameter)a, (GenericParameter)b);
|
||||
case CecilType.OptionalModifierType:
|
||||
return compareOptionalModifierTypes((OptionalModifierType)a, (OptionalModifierType)b);
|
||||
case CecilType.PinnedType:
|
||||
return comparePinnedTypes((PinnedType)a, (PinnedType)b);
|
||||
case CecilType.PointerType:
|
||||
return comparePointerTypes((PointerType)a, (PointerType)b);
|
||||
case CecilType.RequiredModifierType:
|
||||
return compareRequiredModifierTypes((RequiredModifierType)a, (RequiredModifierType)b);
|
||||
case CecilType.SentinelType:
|
||||
return compareSentinelTypes((SentinelType)a, (SentinelType)b);
|
||||
case CecilType.TypeDefinition:
|
||||
return compareTypeDefinitions((TypeDefinition)a, (TypeDefinition)b);
|
||||
case CecilType.TypeReference:
|
||||
return compareTypeReferences((TypeReference)a, (TypeReference)b);
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Unknown cecil type {0}", type));
|
||||
}
|
||||
}
|
||||
|
||||
public static int typeHashCode(TypeReference a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
|
||||
var type = getMemberReferenceType(a);
|
||||
switch (type) {
|
||||
case CecilType.ArrayType:
|
||||
return arrayTypeHashCode((ArrayType)a);
|
||||
case CecilType.ByReferenceType:
|
||||
return byReferenceTypeHashCode((ByReferenceType)a);
|
||||
case CecilType.FunctionPointerType:
|
||||
return functionPointerTypeHashCode((FunctionPointerType)a);
|
||||
case CecilType.GenericInstanceType:
|
||||
return genericInstanceTypeHashCode((GenericInstanceType)a);
|
||||
case CecilType.GenericParameter:
|
||||
return genericParameterHashCode((GenericParameter)a);
|
||||
case CecilType.OptionalModifierType:
|
||||
return optionalModifierTypeHashCode((OptionalModifierType)a);
|
||||
case CecilType.PinnedType:
|
||||
return pinnedTypeHashCode((PinnedType)a);
|
||||
case CecilType.PointerType:
|
||||
return pointerTypeHashCode((PointerType)a);
|
||||
case CecilType.RequiredModifierType:
|
||||
return requiredModifierTypeHashCode((RequiredModifierType)a);
|
||||
case CecilType.SentinelType:
|
||||
return sentinelTypeHashCode((SentinelType)a);
|
||||
case CecilType.TypeDefinition:
|
||||
return typeDefinitionHashCode((TypeDefinition)a);
|
||||
case CecilType.TypeReference:
|
||||
return typeReferenceHashCode((TypeReference)a);
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Unknown cecil type {0}", type));
|
||||
}
|
||||
}
|
||||
|
||||
static bool compareArrayTypes(ArrayType a, ArrayType b) {
|
||||
if (a.Rank != b.Rank || a.IsVector != b.IsVector)
|
||||
return false;
|
||||
if (!a.IsVector) {
|
||||
for (int i = 0; i < a.Dimensions.Count; i++) {
|
||||
if (!compareArrayDimensions(a.Dimensions[i], b.Dimensions[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int arrayTypeHashCode(ArrayType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
int res = 0;
|
||||
|
||||
res += a.Rank.GetHashCode();
|
||||
res += a.IsVector.GetHashCode();
|
||||
if (!a.IsVector) {
|
||||
foreach (var dim in a.Dimensions)
|
||||
res += arrayDimensionHashCode(dim);
|
||||
}
|
||||
res += typeSpecificationHashCode(a);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool compareArrayDimensions(ArrayDimension a, ArrayDimension b) {
|
||||
return a.LowerBound == b.LowerBound && a.UpperBound == b.UpperBound;
|
||||
}
|
||||
|
||||
static int arrayDimensionHashCode(ArrayDimension a) {
|
||||
return a.LowerBound.GetHashCode() + a.UpperBound.GetHashCode();
|
||||
}
|
||||
|
||||
static bool compareGenericInstanceTypes(GenericInstanceType a, GenericInstanceType b) {
|
||||
if (a.HasGenericArguments != b.HasGenericArguments)
|
||||
return false;
|
||||
if (a.HasGenericArguments) {
|
||||
if (a.GenericArguments.Count != b.GenericArguments.Count)
|
||||
return false;
|
||||
for (int i = 0; i < a.GenericArguments.Count; i++) {
|
||||
if (!compareTypes(a.GenericArguments[i], b.GenericArguments[i]))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int genericInstanceTypeHashCode(GenericInstanceType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
int res = 0;
|
||||
|
||||
res += a.HasGenericArguments.GetHashCode();
|
||||
if (a.HasGenericArguments) {
|
||||
res += a.GenericArguments.Count.GetHashCode();
|
||||
foreach (var arg in a.GenericArguments)
|
||||
res += typeHashCode(arg);
|
||||
}
|
||||
res += typeSpecificationHashCode(a);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool comparePointerTypes(PointerType a, PointerType b) {
|
||||
return compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int pointerTypeHashCode(PointerType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeSpecificationHashCode(a);
|
||||
}
|
||||
|
||||
static bool compareByReferenceTypes(ByReferenceType a, ByReferenceType b) {
|
||||
return compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int byReferenceTypeHashCode(ByReferenceType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeSpecificationHashCode(a);
|
||||
}
|
||||
|
||||
static bool comparePinnedTypes(PinnedType a, PinnedType b) {
|
||||
return compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int pinnedTypeHashCode(PinnedType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeSpecificationHashCode(a);
|
||||
}
|
||||
|
||||
static bool compareFunctionPointerTypes(FunctionPointerType a, FunctionPointerType b) {
|
||||
return compareMethodReference(a.function, b.function) &&
|
||||
compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int functionPointerTypeHashCode(FunctionPointerType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return methodReferenceHashCode(a.function) + typeSpecificationHashCode(a);
|
||||
}
|
||||
|
||||
static bool compareOptionalModifierTypes(OptionalModifierType a, OptionalModifierType b) {
|
||||
return compareTypes(a.ModifierType, b.ModifierType) &&
|
||||
compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int optionalModifierTypeHashCode(OptionalModifierType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeHashCode(a.ModifierType) + typeSpecificationHashCode(a);
|
||||
}
|
||||
|
||||
static bool compareRequiredModifierTypes(RequiredModifierType a, RequiredModifierType b) {
|
||||
return compareTypes(a.ModifierType, b.ModifierType) &&
|
||||
compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int requiredModifierTypeHashCode(RequiredModifierType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeHashCode(a.ModifierType) + typeSpecificationHashCode(a);
|
||||
}
|
||||
|
||||
static bool compareSentinelTypes(SentinelType a, SentinelType b) {
|
||||
return compareTypeSpecifications(a, b);
|
||||
}
|
||||
|
||||
static int sentinelTypeHashCode(SentinelType a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeSpecificationHashCode(a);
|
||||
}
|
||||
|
||||
static bool compareTypeSpecifications(TypeSpecification a, TypeSpecification b) {
|
||||
// It overrides everything of importance in TypeReference. The only thing to check
|
||||
// is the ElementType.
|
||||
return compareTypes(a.ElementType, b.ElementType);
|
||||
}
|
||||
|
||||
static int typeSpecificationHashCode(TypeSpecification a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeHashCode(a.ElementType);
|
||||
}
|
||||
|
||||
static bool compareTypeDefinitions(TypeDefinition a, TypeDefinition b) {
|
||||
// They're all cached, so compare by reference
|
||||
return ReferenceEquals(a, b);
|
||||
}
|
||||
|
||||
static int typeDefinitionHashCode(TypeDefinition a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return typeReferenceHashCode(a);
|
||||
}
|
||||
|
||||
static bool compareGenericParameters(GenericParameter a, GenericParameter b) {
|
||||
return a.Position == b.Position &&
|
||||
compareGenericParameterProvider(a.Owner, b.Owner);
|
||||
}
|
||||
|
||||
static int genericParameterHashCode(GenericParameter a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
return a.Position.GetHashCode() + genericParameterProviderHashCode(a.Owner);
|
||||
}
|
||||
|
||||
// a and b must be either exactly a TypeReference or exactly a TypeDefinition
|
||||
static bool compareTypeReferences(TypeReference a, TypeReference b) {
|
||||
if (a.GetType() != typeof(TypeReference) && a.GetType() != typeof(TypeDefinition) &&
|
||||
b.GetType() != typeof(TypeReference) && b.GetType() != typeof(TypeDefinition))
|
||||
throw new ApplicationException("arg must be exactly of type TypeReference or TypeDefinition");
|
||||
|
||||
return a.Name == b.Name &&
|
||||
a.Namespace == b.Namespace &&
|
||||
compareTypes(a.DeclaringType, b.DeclaringType) &&
|
||||
compareScope(a.Scope, b.Scope);
|
||||
}
|
||||
|
||||
// a must be exactly a TypeReference or a TypeDefinition
|
||||
static int typeReferenceHashCode(TypeReference a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
|
||||
if (a.GetType() != typeof(TypeReference) && a.GetType() != typeof(TypeDefinition))
|
||||
throw new ApplicationException("arg must be exactly of type TypeReference or TypeDefinition");
|
||||
|
||||
int res = 0;
|
||||
|
||||
res += a.Name.GetHashCode();
|
||||
res += a.Namespace.GetHashCode();
|
||||
res += typeHashCode(a.DeclaringType);
|
||||
res += scopeHashCode(a.Scope);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool compareGenericParameterProvider(IGenericParameterProvider a, IGenericParameterProvider b) {
|
||||
if (ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
if (a is TypeReference && b is TypeReference)
|
||||
return compareTypes((TypeReference)a, (TypeReference)b);
|
||||
if (a is MethodReference && b is MethodReference)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int genericParameterProviderHashCode(IGenericParameterProvider a) {
|
||||
if (a == null)
|
||||
return 0;
|
||||
if (a is TypeReference)
|
||||
return typeHashCode((TypeReference)a);
|
||||
if (a is MethodReference)
|
||||
return 0;
|
||||
throw new ApplicationException(string.Format("Unknown IGenericParameterProvider type {0}", a.GetType()));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Text.RegularExpressions;
|
||||
|
||||
namespace de4dot {
|
||||
class NameRegex {
|
||||
Regex regex;
|
||||
public const char invertChar = '!';
|
||||
|
||||
public bool MatchValue { get; private set; }
|
||||
|
||||
public NameRegex(string regex) {
|
||||
if (regex.Length > 0 && regex[0] == invertChar) {
|
||||
regex = regex.Substring(1);
|
||||
MatchValue = false;
|
||||
}
|
||||
else
|
||||
MatchValue = true;
|
||||
|
||||
this.regex = new Regex(regex);
|
||||
}
|
||||
|
||||
// Returns true if the regex matches. Use MatchValue to get result.
|
||||
public bool isMatch(string s) {
|
||||
return regex.IsMatch(s);
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
if (!MatchValue)
|
||||
return invertChar + regex.ToString();
|
||||
return regex.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
class NameRegexes {
|
||||
IList<NameRegex> regexes;
|
||||
public bool DefaultValue { get; set; }
|
||||
public const char regexSeparatorChar = '&';
|
||||
|
||||
public NameRegexes(string regex = "") {
|
||||
set(regex);
|
||||
}
|
||||
|
||||
public void set(string regexesString) {
|
||||
regexes = new List<NameRegex>();
|
||||
if (regexesString != "") {
|
||||
foreach (var regex in regexesString.Split(new char[] { regexSeparatorChar }))
|
||||
regexes.Add(new NameRegex(regex));
|
||||
}
|
||||
}
|
||||
|
||||
public bool isMatch(string s) {
|
||||
foreach (var regex in regexes) {
|
||||
if (regex.isMatch(s))
|
||||
return regex.MatchValue;
|
||||
}
|
||||
|
||||
return DefaultValue;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
var s = "";
|
||||
for (int i = 0; i < regexes.Count; i++) {
|
||||
if (i > 0)
|
||||
s += regexSeparatorChar;
|
||||
s += regexes[i].ToString();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,522 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Globalization;
|
||||
using System.IO;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.deobfuscators;
|
||||
using de4dot.blocks;
|
||||
using de4dot.AssemblyClient;
|
||||
|
||||
namespace de4dot {
|
||||
class ObfuscatedFile : IObfuscatedFile, IDeobfuscatedFile {
|
||||
Options options;
|
||||
ModuleDefinition module;
|
||||
IList<MethodDefinition> allMethods;
|
||||
IDeobfuscator deob;
|
||||
AssemblyModule assemblyModule;
|
||||
IAssemblyClient assemblyClient;
|
||||
DynamicStringDecrypter dynamicStringDecrypter;
|
||||
IAssemblyClientFactory assemblyClientFactory;
|
||||
SavedMethodBodies savedMethodBodies;
|
||||
bool userStringDecrypterMethods = false;
|
||||
|
||||
class SavedMethodBodies {
|
||||
Dictionary<MethodDefinition, SavedMethodBody> savedMethodBodies = new Dictionary<MethodDefinition, SavedMethodBody>();
|
||||
|
||||
class SavedMethodBody {
|
||||
MethodDefinition method;
|
||||
IList<Instruction> instructions;
|
||||
IList<ExceptionHandler> exceptionHandlers;
|
||||
|
||||
public SavedMethodBody(MethodDefinition method) {
|
||||
this.method = method;
|
||||
DotNetUtils.copyBody(method, out instructions, out exceptionHandlers);
|
||||
}
|
||||
|
||||
public void restore() {
|
||||
DotNetUtils.restoreBody(method, instructions, exceptionHandlers);
|
||||
}
|
||||
}
|
||||
|
||||
public void save(MethodDefinition method) {
|
||||
if (isSaved(method))
|
||||
return;
|
||||
savedMethodBodies[method] = new SavedMethodBody(method);
|
||||
}
|
||||
|
||||
public void restoreAll() {
|
||||
foreach (var smb in savedMethodBodies.Values)
|
||||
smb.restore();
|
||||
savedMethodBodies.Clear();
|
||||
}
|
||||
|
||||
public bool isSaved(MethodDefinition method) {
|
||||
return savedMethodBodies.ContainsKey(method);
|
||||
}
|
||||
}
|
||||
|
||||
public class Options {
|
||||
public string Filename { get; set; }
|
||||
public string MethodsFilename { get; set; }
|
||||
public string NewFilename { get; set; }
|
||||
public string ForcedObfuscatorType { get; set; }
|
||||
public DecrypterType StringDecrypterType { get; set; }
|
||||
public List<string> StringDecrypterMethods { get; private set; }
|
||||
public bool RenameSymbols { get; set; }
|
||||
public bool ControlFlowDeobfuscation { get; set; }
|
||||
public bool KeepObfuscatorTypes { get; set; }
|
||||
|
||||
public Options() {
|
||||
StringDecrypterType = DecrypterType.Static;
|
||||
StringDecrypterMethods = new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public string Filename {
|
||||
get { return options.Filename; }
|
||||
}
|
||||
|
||||
public string NewFilename {
|
||||
get { return options.NewFilename; }
|
||||
}
|
||||
|
||||
public ModuleDefinition ModuleDefinition {
|
||||
get { return module; }
|
||||
}
|
||||
|
||||
public Func<string, bool> IsValidName {
|
||||
get { return deob.IsValidName; }
|
||||
}
|
||||
|
||||
public bool RenameResourcesInCode {
|
||||
get { return deob.TheOptions.RenameResourcesInCode; }
|
||||
}
|
||||
|
||||
public bool RenameSymbols {
|
||||
get { return options.RenameSymbols; }
|
||||
}
|
||||
|
||||
public IDeobfuscator Deobfuscator {
|
||||
get { return deob; }
|
||||
}
|
||||
|
||||
public ObfuscatedFile(Options options, IAssemblyClientFactory assemblyClientFactory) {
|
||||
this.assemblyClientFactory = assemblyClientFactory;
|
||||
this.options = options;
|
||||
userStringDecrypterMethods = options.StringDecrypterMethods.Count > 0;
|
||||
options.Filename = Utils.getFullPath(options.Filename);
|
||||
assemblyModule = new AssemblyModule(options.Filename, options.MethodsFilename);
|
||||
|
||||
if (options.NewFilename == null)
|
||||
options.NewFilename = getDefaultNewFilename();
|
||||
|
||||
if (string.Equals(options.Filename, options.NewFilename, StringComparison.OrdinalIgnoreCase))
|
||||
throw new UserException(string.Format("filename is same as new filename! ({0})", options.Filename));
|
||||
}
|
||||
|
||||
string getDefaultNewFilename() {
|
||||
int dotIndex = options.Filename.LastIndexOf('.');
|
||||
string noExt, ext;
|
||||
if (dotIndex != -1) {
|
||||
noExt = options.Filename.Substring(0, dotIndex);
|
||||
ext = options.Filename.Substring(dotIndex);
|
||||
}
|
||||
else {
|
||||
noExt = options.Filename;
|
||||
ext = "";
|
||||
}
|
||||
return noExt + "-fixed" + ext;
|
||||
}
|
||||
|
||||
public void load(IEnumerable<IDeobfuscator> deobfuscators) {
|
||||
module = assemblyModule.load();
|
||||
AssemblyResolver.Instance.addSearchDirectory(Utils.getDirName(Filename));
|
||||
AssemblyResolver.Instance.addSearchDirectory(Utils.getDirName(NewFilename));
|
||||
|
||||
allMethods = getAllMethods();
|
||||
|
||||
detectObfuscator(deobfuscators);
|
||||
if (deob == null)
|
||||
throw new ApplicationException("Could not detect obfuscator!");
|
||||
|
||||
deob.Operations = createOperations();
|
||||
}
|
||||
|
||||
IOperations createOperations() {
|
||||
var op = new Operations();
|
||||
|
||||
if (options.StringDecrypterType == DecrypterType.None)
|
||||
op.DecryptStrings = OpDecryptString.None;
|
||||
else if (options.StringDecrypterType == DecrypterType.Static)
|
||||
op.DecryptStrings = OpDecryptString.Static;
|
||||
else
|
||||
op.DecryptStrings = OpDecryptString.Dynamic;
|
||||
|
||||
op.RenameSymbols = options.RenameSymbols;
|
||||
op.KeepObfuscatorTypes = options.KeepObfuscatorTypes;
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
void detectObfuscator(IEnumerable<IDeobfuscator> deobfuscators) {
|
||||
IList<MemberReference> memberReferences = new List<MemberReference>(module.GetMemberReferences());
|
||||
|
||||
// The deobfuscators may call methods to deobfuscate control flow and decrypt
|
||||
// strings (statically) in order to detect the obfuscator.
|
||||
if (!options.ControlFlowDeobfuscation || options.StringDecrypterType == DecrypterType.None)
|
||||
savedMethodBodies = new SavedMethodBodies();
|
||||
|
||||
foreach (var deob in deobfuscators) {
|
||||
deob.DeobfuscatedFile = this;
|
||||
deob.init(module, memberReferences);
|
||||
}
|
||||
|
||||
if (options.ForcedObfuscatorType != null) {
|
||||
foreach (var deob in deobfuscators) {
|
||||
if (string.Equals(options.ForcedObfuscatorType, deob.Type, StringComparison.OrdinalIgnoreCase)) {
|
||||
this.deob = deob;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
int detectVal = 0;
|
||||
foreach (var deob in deobfuscators) {
|
||||
int val = deob.detect();
|
||||
Log.v("{0,3}: {1}", val, deob.Type);
|
||||
if (val > detectVal) {
|
||||
detectVal = val;
|
||||
this.deob = deob;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void save() {
|
||||
Log.n("Saving {0}", options.NewFilename);
|
||||
assemblyModule.save(options.NewFilename);
|
||||
}
|
||||
|
||||
IList<MethodDefinition> getAllMethods() {
|
||||
var list = new List<MethodDefinition>();
|
||||
|
||||
foreach (var type in module.GetTypes()) {
|
||||
foreach (var method in type.Methods)
|
||||
list.Add(method);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public void deobfuscateBegin() {
|
||||
switch (options.StringDecrypterType) {
|
||||
case DecrypterType.None:
|
||||
checkSupportedStringDecrypter(StringFeatures.AllowNoDecryption);
|
||||
break;
|
||||
|
||||
case DecrypterType.Static:
|
||||
checkSupportedStringDecrypter(StringFeatures.AllowStaticDecryption);
|
||||
break;
|
||||
|
||||
case DecrypterType.Delegate:
|
||||
checkSupportedStringDecrypter(StringFeatures.AllowDynamicDecryption);
|
||||
assemblyClient = assemblyClientFactory.create();
|
||||
assemblyClient.connect();
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Invalid string decrypter type '{0}'", options.StringDecrypterType));
|
||||
}
|
||||
}
|
||||
|
||||
public void checkSupportedStringDecrypter(StringFeatures feature) {
|
||||
if ((deob.StringFeatures & feature) == feature)
|
||||
return;
|
||||
throw new UserException(string.Format("Deobfuscator {0} does not support this string decryption type", deob.Type));
|
||||
}
|
||||
|
||||
public void deobfuscate() {
|
||||
Log.n("Cleaning {0}", options.Filename);
|
||||
initAssemblyClient();
|
||||
deob.deobfuscateBegin();
|
||||
deobfuscateMethods();
|
||||
deob.deobfuscateEnd();
|
||||
}
|
||||
|
||||
void initAssemblyClient() {
|
||||
if (assemblyClient == null)
|
||||
return;
|
||||
|
||||
assemblyClient.waitConnected();
|
||||
assemblyClient.Service.loadAssembly(options.Filename);
|
||||
|
||||
if (options.StringDecrypterType == DecrypterType.Delegate)
|
||||
assemblyClient.Service.setStringDecrypterType(AssemblyData.StringDecrypterType.Delegate);
|
||||
else
|
||||
throw new ApplicationException(string.Format("Invalid string decrypter type '{0}'", options.StringDecrypterType));
|
||||
|
||||
dynamicStringDecrypter = new DynamicStringDecrypter(assemblyClient);
|
||||
updateDynamicStringDecrypter();
|
||||
}
|
||||
|
||||
void updateDynamicStringDecrypter() {
|
||||
if (dynamicStringDecrypter != null)
|
||||
dynamicStringDecrypter.init(getMethodTokens());
|
||||
}
|
||||
|
||||
IEnumerable<int> getMethodTokens() {
|
||||
var tokens = new List<int>();
|
||||
|
||||
if (!userStringDecrypterMethods) {
|
||||
options.StringDecrypterMethods.Clear();
|
||||
options.StringDecrypterMethods.AddRange(deob.getStringDecrypterMethods());
|
||||
}
|
||||
|
||||
foreach (var val in options.StringDecrypterMethods) {
|
||||
var tokenStr = val.Trim();
|
||||
if (tokenStr.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||
tokenStr = tokenStr.Substring(2);
|
||||
int methodToken;
|
||||
if (int.TryParse(tokenStr, NumberStyles.HexNumber, null, out methodToken))
|
||||
tokens.Add(methodToken);
|
||||
else
|
||||
tokens.AddRange(findMethodTokens(val));
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
IEnumerable<int> findMethodTokens(string methodDesc) {
|
||||
var tokens = new List<int>();
|
||||
|
||||
string typeString, methodName;
|
||||
string[] argsStrings;
|
||||
splitMethodDesc(methodDesc, out typeString, out methodName, out argsStrings);
|
||||
|
||||
foreach (var type in module.GetTypes()) {
|
||||
if (typeString != null && typeString != type.FullName)
|
||||
continue;
|
||||
foreach (var method in type.Methods) {
|
||||
if (!method.IsStatic || method.MethodReturnType.ReturnType.FullName != "System.String")
|
||||
continue;
|
||||
if (methodName != null && methodName != method.Name)
|
||||
continue;
|
||||
|
||||
if (argsStrings == null) {
|
||||
if (method.Parameters.Count == 0)
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if (argsStrings.Length != method.Parameters.Count)
|
||||
continue;
|
||||
for (int i = 0; i < argsStrings.Length; i++) {
|
||||
if (argsStrings[i] != method.Parameters[i].ParameterType.FullName)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Log.v("Adding string decrypter; token: {0:X8}, method: {1}", method.MetadataToken.ToInt32(), method.FullName);
|
||||
tokens.Add(method.MetadataToken.ToInt32());
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
static void splitMethodDesc(string methodDesc, out string type, out string name, out string[] args) {
|
||||
string stringArgs = null;
|
||||
args = null;
|
||||
type = null;
|
||||
name = null;
|
||||
|
||||
var remaining = methodDesc;
|
||||
int index = remaining.LastIndexOf("::");
|
||||
if (index >= 0) {
|
||||
type = remaining.Substring(0, index);
|
||||
remaining = remaining.Substring(index + 2);
|
||||
}
|
||||
|
||||
index = remaining.IndexOf('(');
|
||||
if (index >= 0) {
|
||||
name = remaining.Substring(0, index);
|
||||
remaining = remaining.Substring(index);
|
||||
}
|
||||
else {
|
||||
name = remaining;
|
||||
remaining = "";
|
||||
}
|
||||
|
||||
if (remaining.StartsWith("(", StringComparison.Ordinal)) {
|
||||
stringArgs = remaining;
|
||||
}
|
||||
else if (remaining.Length > 0)
|
||||
throw new UserException(string.Format("Invalid method desc: '{0}'", methodDesc));
|
||||
|
||||
if (stringArgs != null) {
|
||||
if (stringArgs.StartsWith("(", StringComparison.Ordinal))
|
||||
stringArgs = stringArgs.Substring(1);
|
||||
if (stringArgs.EndsWith(")", StringComparison.Ordinal))
|
||||
stringArgs = stringArgs.Substring(0, stringArgs.Length - 1);
|
||||
args = stringArgs.Split(',');
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
args[i] = args[i].Trim();
|
||||
}
|
||||
|
||||
if (type == "")
|
||||
type = null;
|
||||
if (name == "")
|
||||
name = null;
|
||||
}
|
||||
|
||||
public void deobfuscateEnd() {
|
||||
deobfuscateCleanUp();
|
||||
}
|
||||
|
||||
public void deobfuscateCleanUp() {
|
||||
if (assemblyClient != null) {
|
||||
assemblyClient.Dispose();
|
||||
assemblyClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
void deobfuscateMethods() {
|
||||
if (savedMethodBodies != null) {
|
||||
savedMethodBodies.restoreAll();
|
||||
savedMethodBodies = null;
|
||||
}
|
||||
deob.DeobfuscatedFile = null;
|
||||
|
||||
Log.v("Deobfuscating methods");
|
||||
foreach (var method in allMethods) {
|
||||
Log.v("Deobfuscating {0} ({1:X8})", method, method.MetadataToken.ToUInt32());
|
||||
Log.indent();
|
||||
|
||||
if (method.HasBody) {
|
||||
var blocks = new Blocks(method);
|
||||
|
||||
deob.deobfuscateMethodBegin(blocks);
|
||||
if (options.ControlFlowDeobfuscation)
|
||||
blocks.deobfuscate();
|
||||
deobfuscateStrings(blocks);
|
||||
deob.deobfuscateMethodEnd(blocks);
|
||||
if (options.ControlFlowDeobfuscation)
|
||||
blocks.deobfuscateLeaveObfuscation();
|
||||
|
||||
IList<Instruction> allInstructions;
|
||||
IList<ExceptionHandler> allExceptionHandlers;
|
||||
blocks.getCode(out allInstructions, out allExceptionHandlers);
|
||||
DotNetUtils.restoreBody(method, allInstructions, allExceptionHandlers);
|
||||
}
|
||||
|
||||
removeNoInliningAttribute(method);
|
||||
|
||||
Log.deIndent();
|
||||
}
|
||||
}
|
||||
|
||||
void deobfuscateStrings(Blocks blocks) {
|
||||
switch (options.StringDecrypterType) {
|
||||
case DecrypterType.None:
|
||||
break;
|
||||
|
||||
case DecrypterType.Static:
|
||||
deob.deobfuscateStrings(blocks);
|
||||
break;
|
||||
|
||||
case DecrypterType.Delegate:
|
||||
dynamicStringDecrypter.decrypt(blocks);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Invalid string decrypter type '{0}'", options.StringDecrypterType));
|
||||
}
|
||||
}
|
||||
|
||||
void removeNoInliningAttribute(MethodDefinition method) {
|
||||
method.ImplAttributes = method.ImplAttributes & ~MethodImplAttributes.NoInlining;
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
if (options == null || options.Filename == null)
|
||||
return base.ToString();
|
||||
return options.Filename;
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum SimpleDeobFlags {
|
||||
HasDeobfuscated = 0x1,
|
||||
}
|
||||
Dictionary<MethodDefinition, SimpleDeobFlags> simpleDeobfuscatorFlags = new Dictionary<MethodDefinition, SimpleDeobFlags>();
|
||||
bool check(MethodDefinition method, SimpleDeobFlags flag) {
|
||||
SimpleDeobFlags oldFlags;
|
||||
simpleDeobfuscatorFlags.TryGetValue(method, out oldFlags);
|
||||
simpleDeobfuscatorFlags[method] = oldFlags | flag;
|
||||
return (oldFlags & flag) == flag;
|
||||
}
|
||||
|
||||
void deobfuscate(MethodDefinition method, string msg, Action<Blocks> handler) {
|
||||
if (savedMethodBodies != null)
|
||||
savedMethodBodies.save(method);
|
||||
|
||||
Log.v("{0}: {1} ({2:X8})", msg, method, method.MetadataToken.ToUInt32());
|
||||
Log.indent();
|
||||
|
||||
if (method.HasBody) {
|
||||
var blocks = new Blocks(method);
|
||||
|
||||
handler(blocks);
|
||||
|
||||
IList<Instruction> allInstructions;
|
||||
IList<ExceptionHandler> allExceptionHandlers;
|
||||
blocks.getCode(out allInstructions, out allExceptionHandlers);
|
||||
DotNetUtils.restoreBody(method, allInstructions, allExceptionHandlers);
|
||||
}
|
||||
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void ISimpleDeobfuscator.deobfuscate(MethodDefinition method) {
|
||||
if (check(method, SimpleDeobFlags.HasDeobfuscated))
|
||||
return;
|
||||
|
||||
deobfuscate(method, "Deobfuscating control flow", (blocks) => blocks.deobfuscate());
|
||||
}
|
||||
|
||||
void ISimpleDeobfuscator.decryptStrings(MethodDefinition method, IDeobfuscator theDeob) {
|
||||
deobfuscate(method, "Static string decryption", (blocks) => theDeob.deobfuscateStrings(blocks));
|
||||
}
|
||||
|
||||
void IDeobfuscatedFile.createAssemblyFile(byte[] data, string assemblyName) {
|
||||
var baseDir = Utils.getDirName(options.NewFilename);
|
||||
var newName = Path.Combine(baseDir, assemblyName + ".dll");
|
||||
Log.n("Creating file {0}", newName);
|
||||
using (var writer = new BinaryWriter(new FileStream(newName, FileMode.Create))) {
|
||||
writer.Write(data);
|
||||
}
|
||||
}
|
||||
|
||||
void IDeobfuscatedFile.stringDecryptersAdded() {
|
||||
updateDynamicStringDecrypter();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Text.RegularExpressions;
|
||||
|
||||
namespace de4dot {
|
||||
abstract class Option {
|
||||
const string SHORTNAME_PREFIX = "-";
|
||||
const string LONGNAME_PREFIX = "--";
|
||||
|
||||
string shortName;
|
||||
string longName;
|
||||
string description;
|
||||
object defaultVal;
|
||||
|
||||
public string ShortName {
|
||||
get { return shortName; }
|
||||
}
|
||||
|
||||
public string LongName {
|
||||
get { return longName; }
|
||||
}
|
||||
|
||||
public string Description {
|
||||
get { return description; }
|
||||
}
|
||||
|
||||
public object Default {
|
||||
get { return defaultVal; }
|
||||
protected set { defaultVal = value; }
|
||||
}
|
||||
|
||||
public virtual bool NeedArgument {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual string ArgumentValueName {
|
||||
get { return "value"; }
|
||||
}
|
||||
|
||||
// Returns true if the new value is set, or false on error. error string is also updated.
|
||||
public abstract bool set(string val, out string error);
|
||||
|
||||
public Option(string shortName, string longName, string description) {
|
||||
if (shortName != null)
|
||||
this.shortName = SHORTNAME_PREFIX + shortName;
|
||||
if (longName != null)
|
||||
this.longName = LONGNAME_PREFIX + longName;
|
||||
this.description = description;
|
||||
}
|
||||
}
|
||||
|
||||
class BoolOption : Option {
|
||||
bool val;
|
||||
public BoolOption(string shortName, string longName, string description, bool val)
|
||||
: base(shortName, longName, description) {
|
||||
Default = this.val = val;
|
||||
}
|
||||
|
||||
public override string ArgumentValueName {
|
||||
get { return "bool"; }
|
||||
}
|
||||
|
||||
public override bool set(string newVal, out string error) {
|
||||
if (string.Equals(newVal, "false", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(newVal, "off", StringComparison.OrdinalIgnoreCase) ||
|
||||
string.Equals(newVal, "0", StringComparison.OrdinalIgnoreCase)) {
|
||||
val = false;
|
||||
}
|
||||
else
|
||||
val = true;
|
||||
error = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool get() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
class IntOption : Option {
|
||||
int val;
|
||||
public IntOption(string shortName, string longName, string description, int val)
|
||||
: base(shortName, longName, description) {
|
||||
Default = this.val = val;
|
||||
}
|
||||
|
||||
public override string ArgumentValueName {
|
||||
get { return "int"; }
|
||||
}
|
||||
|
||||
public override bool set(string newVal, out string error) {
|
||||
int newInt;
|
||||
if (!int.TryParse(newVal, out newInt)) {
|
||||
error = string.Format("Not an integer: '{0}'", newVal);
|
||||
return false;
|
||||
}
|
||||
val = newInt;
|
||||
error = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public int get() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
class StringOption : Option {
|
||||
string val;
|
||||
|
||||
public override string ArgumentValueName {
|
||||
get { return "string"; }
|
||||
}
|
||||
|
||||
public StringOption(string shortName, string longName, string description, string val)
|
||||
: base(shortName, longName, description) {
|
||||
Default = this.val = val;
|
||||
}
|
||||
|
||||
public override bool set(string newVal, out string error) {
|
||||
val = newVal;
|
||||
error = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public string get() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
class NameRegexOption : Option {
|
||||
NameRegexes val;
|
||||
|
||||
public override string ArgumentValueName {
|
||||
get { return "regex"; }
|
||||
}
|
||||
|
||||
public NameRegexOption(string shortName, string longName, string description, string val)
|
||||
: base(shortName, longName, description) {
|
||||
Default = this.val = new NameRegexes(val);
|
||||
}
|
||||
|
||||
public override bool set(string newVal, out string error) {
|
||||
try {
|
||||
var regexes = new NameRegexes();
|
||||
regexes.set(newVal);
|
||||
val = regexes;
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
error = string.Format("Could not parse regex '{0}'", newVal);
|
||||
return false;
|
||||
}
|
||||
error = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public NameRegexes get() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
class RegexOption : Option {
|
||||
Regex val;
|
||||
|
||||
public override string ArgumentValueName {
|
||||
get { return "regex"; }
|
||||
}
|
||||
|
||||
public RegexOption(string shortName, string longName, string description, string val)
|
||||
: base(shortName, longName, description) {
|
||||
Default = this.val = new Regex(val);
|
||||
}
|
||||
|
||||
public override bool set(string newVal, out string error) {
|
||||
try {
|
||||
val = new Regex(newVal);
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
error = string.Format("Could not parse regex '{0}'", newVal);
|
||||
return false;
|
||||
}
|
||||
error = "";
|
||||
return true;
|
||||
}
|
||||
|
||||
public Regex get() {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
class NoArgOption : Option {
|
||||
Action action;
|
||||
|
||||
public override bool NeedArgument {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public NoArgOption(string shortName, string longName, string description, Action action)
|
||||
: base(shortName, longName, description) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public override bool set(string val, out string error) {
|
||||
action();
|
||||
error = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class OneArgOption : Option {
|
||||
Action<string> action;
|
||||
string typeName;
|
||||
|
||||
public override string ArgumentValueName {
|
||||
get { return typeName; }
|
||||
}
|
||||
|
||||
public OneArgOption(string shortName, string longName, string description, string typeName, Action<string> action)
|
||||
: base(shortName, longName, description) {
|
||||
this.typeName = typeName ?? "value";
|
||||
this.action = action;
|
||||
Default = null;
|
||||
}
|
||||
|
||||
public override bool set(string val, out string error) {
|
||||
action(val);
|
||||
error = "";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Text;
|
||||
using de4dot.deobfuscators;
|
||||
|
||||
namespace de4dot {
|
||||
public class Program {
|
||||
static IList<IDeobfuscatorInfo> deobfuscatorInfos = createDeobfuscatorInfos();
|
||||
|
||||
static IList<IDeobfuscatorInfo> createDeobfuscatorInfos() {
|
||||
return new List<IDeobfuscatorInfo> {
|
||||
new de4dot.deobfuscators.Unknown.DeobfuscatorInfo(),
|
||||
new de4dot.deobfuscators.CliSecure.DeobfuscatorInfo(),
|
||||
new de4dot.deobfuscators.Dotfuscator.DeobfuscatorInfo(),
|
||||
new de4dot.deobfuscators.Eazfuscator.DeobfuscatorInfo(),
|
||||
new de4dot.deobfuscators.SmartAssembly.DeobfuscatorInfo(),
|
||||
};
|
||||
}
|
||||
|
||||
public static int main(StartUpArch startUpArch, string[] args) {
|
||||
Utils.startUpArch = startUpArch;
|
||||
|
||||
try {
|
||||
Console.OutputEncoding = new UTF8Encoding(false);
|
||||
|
||||
Log.n("");
|
||||
Log.n("de4dot v{0} (BETA) Copyright (C) 2011 de4dot@gmail.com", System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
|
||||
Log.n("Latest version and source code: https://github.com/0xd4d/de4dot");
|
||||
Log.n("");
|
||||
|
||||
var options = new FilesDeobfuscator.Options();
|
||||
parseCommandLine(args, options);
|
||||
new FilesDeobfuscator(options).doIt();
|
||||
}
|
||||
catch (UserException ex) {
|
||||
Log.e("ERROR: {0}", ex.Message);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
var line = new string('-', 78);
|
||||
Log.e("\n\nERROR: Caught an exception:\n\n");
|
||||
Log.e(line);
|
||||
Log.e("Message: {0}", ex.Message);
|
||||
Log.e("Type: {0}", ex.GetType());
|
||||
Log.e(line);
|
||||
Log.e("\n\nStack trace:\n{0}", ex.StackTrace);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void parseCommandLine(string[] args, FilesDeobfuscator.Options options) {
|
||||
new CommandLineParser(deobfuscatorInfos, options).parse(args);
|
||||
|
||||
Log.vv("Args:");
|
||||
Log.indent();
|
||||
foreach (var arg in args)
|
||||
Log.vv("{0}", Utils.toCsharpString(arg));
|
||||
Log.deIndent();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
[assembly: AssemblyTitle("de4dot.code")]
|
||||
[assembly: AssemblyDescription("Deobfuscates obfuscated .NET applications")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("de4dot.code")]
|
||||
[assembly: AssemblyCopyright("Copyright (C) 2011 de4dot@gmail.com")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("1.0.0.3405")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.3405")]
|
|
@ -0,0 +1,418 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.AssemblyClient;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot {
|
||||
// A simple class that statically detects the values of some local variables
|
||||
class VariableValues {
|
||||
IList<Block> allBlocks;
|
||||
IList<VariableDefinition> locals;
|
||||
Dictionary<VariableDefinition, Variable> variableToValue = new Dictionary<VariableDefinition, Variable>();
|
||||
|
||||
public class Variable {
|
||||
int writes = 0;
|
||||
object value;
|
||||
bool unknownValue = false;
|
||||
|
||||
public bool isValid() {
|
||||
return !unknownValue && writes == 1;
|
||||
}
|
||||
|
||||
public object Value {
|
||||
get {
|
||||
if (!isValid())
|
||||
throw new ApplicationException("Unknown variable value");
|
||||
return value;
|
||||
}
|
||||
set { this.value = value; }
|
||||
}
|
||||
|
||||
public void addWrite() {
|
||||
writes++;
|
||||
}
|
||||
|
||||
public void setUnknown() {
|
||||
unknownValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public VariableValues(IList<VariableDefinition> locals, IList<Block> allBlocks) {
|
||||
this.locals = locals;
|
||||
this.allBlocks = allBlocks;
|
||||
init();
|
||||
}
|
||||
|
||||
void init() {
|
||||
foreach (var variable in locals)
|
||||
variableToValue[variable] = new Variable();
|
||||
|
||||
foreach (var block in allBlocks) {
|
||||
for (int i = 0; i < block.Instructions.Count; i++) {
|
||||
var instr = block.Instructions[i];
|
||||
|
||||
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:
|
||||
var variable = Instr.getLocalVar(locals, instr);
|
||||
var val = variableToValue[variable];
|
||||
val.addWrite();
|
||||
object obj;
|
||||
if (!getValue(block, i, out obj))
|
||||
val.setUnknown();
|
||||
val.Value = obj;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool getValue(Block block, int index, out object obj) {
|
||||
while (true) {
|
||||
if (index <= 0) {
|
||||
obj = null;
|
||||
return false;
|
||||
}
|
||||
var instr = block.Instructions[--index];
|
||||
if (instr.OpCode == OpCodes.Nop)
|
||||
continue;
|
||||
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldc_I4:
|
||||
case Code.Ldc_I8:
|
||||
case Code.Ldc_R4:
|
||||
case Code.Ldc_R8:
|
||||
case Code.Ldstr:
|
||||
obj = instr.Operand;
|
||||
return true;
|
||||
case Code.Ldc_I4_S:
|
||||
obj = (int)(sbyte)instr.Operand;
|
||||
return true;
|
||||
|
||||
case Code.Ldc_I4_0: obj = 0; return true;
|
||||
case Code.Ldc_I4_1: obj = 1; return true;
|
||||
case Code.Ldc_I4_2: obj = 2; return true;
|
||||
case Code.Ldc_I4_3: obj = 3; return true;
|
||||
case Code.Ldc_I4_4: obj = 4; return true;
|
||||
case Code.Ldc_I4_5: obj = 5; return true;
|
||||
case Code.Ldc_I4_6: obj = 6; return true;
|
||||
case Code.Ldc_I4_7: obj = 7; return true;
|
||||
case Code.Ldc_I4_8: obj = 8; return true;
|
||||
case Code.Ldc_I4_M1:obj = -1; return true;
|
||||
case Code.Ldnull: obj = null; return true;
|
||||
|
||||
default:
|
||||
obj = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Variable getValue(VariableDefinition variable) {
|
||||
return variableToValue[variable];
|
||||
}
|
||||
}
|
||||
|
||||
abstract class StringDecrypterBase {
|
||||
protected List<DecryptCall> decryptCalls;
|
||||
List<Block> allBlocks;
|
||||
Blocks blocks;
|
||||
VariableValues variableValues;
|
||||
|
||||
protected class DecryptCall {
|
||||
public Block block;
|
||||
public int callStartIndex;
|
||||
public int callEndIndex;
|
||||
public object[] args;
|
||||
public string decryptedString;
|
||||
|
||||
public DecryptCall(Block block, int callEndIndex) {
|
||||
this.block = block;
|
||||
this.callEndIndex = callEndIndex;
|
||||
}
|
||||
|
||||
public MethodReference getMethodReference() {
|
||||
return (MethodReference)block.Instructions[callEndIndex].Operand;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void decryptAllCalls();
|
||||
|
||||
// Returns null if method is not a string decrypter
|
||||
protected abstract DecryptCall createDecryptCall(MethodReference method, Block block, int callInstrIndex);
|
||||
|
||||
public void decrypt(Blocks theBlocks) {
|
||||
try {
|
||||
blocks = theBlocks;
|
||||
decryptCalls = new List<DecryptCall>();
|
||||
allBlocks = new List<Block>(blocks.MethodBlocks.getAllBlocks());
|
||||
|
||||
findAllDecryptCalls();
|
||||
decryptAllCalls();
|
||||
restoreDecryptedStrings();
|
||||
}
|
||||
finally {
|
||||
blocks = null;
|
||||
decryptCalls = null;
|
||||
allBlocks = null;
|
||||
variableValues = null;
|
||||
}
|
||||
}
|
||||
|
||||
void getLocalVariableValue(VariableDefinition variable, out object value) {
|
||||
if (variableValues == null)
|
||||
variableValues = new VariableValues(blocks.Locals, allBlocks);
|
||||
var val = variableValues.getValue(variable);
|
||||
if (!val.isValid())
|
||||
throw new ApplicationException("Could not get value of local variable");
|
||||
value = val.Value;
|
||||
}
|
||||
|
||||
void findAllDecryptCalls() {
|
||||
foreach (var block in allBlocks)
|
||||
findDecryptCalls(block);
|
||||
}
|
||||
|
||||
void findDecryptCalls(Block block) {
|
||||
for (int i = 0; i < block.Instructions.Count; i++) {
|
||||
var instr = block.Instructions[i];
|
||||
if (instr.OpCode != OpCodes.Call)
|
||||
continue;
|
||||
var method = instr.Operand as MethodReference;
|
||||
if (method == null)
|
||||
continue;
|
||||
|
||||
var decryptCall = createDecryptCall(method, block, i);
|
||||
if (decryptCall == null)
|
||||
continue;
|
||||
|
||||
decryptCalls.Add(decryptCall);
|
||||
findArgs(decryptCall);
|
||||
}
|
||||
}
|
||||
|
||||
void findArgs(DecryptCall decryptCall) {
|
||||
var block = decryptCall.block;
|
||||
var method = decryptCall.getMethodReference();
|
||||
int numArgs = method.Parameters.Count + (method.HasThis ? 1 : 0);
|
||||
var args = new object[numArgs];
|
||||
|
||||
int instrIndex = decryptCall.callEndIndex - 1;
|
||||
for (int i = numArgs - 1; i >= 0; i--)
|
||||
getArg(method, block, ref args[i], ref instrIndex);
|
||||
|
||||
decryptCall.args = args;
|
||||
decryptCall.callStartIndex = instrIndex + 1;
|
||||
}
|
||||
|
||||
void getArg(MethodReference method, Block block, ref object arg, ref int instrIndex) {
|
||||
while (true) {
|
||||
if (instrIndex < 0)
|
||||
throw new ApplicationException(string.Format("Could not find all arguments to method {0}", method));
|
||||
|
||||
var instr = block.Instructions[instrIndex--];
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldc_I4:
|
||||
case Code.Ldc_I8:
|
||||
case Code.Ldc_R4:
|
||||
case Code.Ldc_R8:
|
||||
case Code.Ldstr:
|
||||
arg = instr.Operand;
|
||||
break;
|
||||
case Code.Ldc_I4_S:
|
||||
arg = (int)(sbyte)instr.Operand;
|
||||
break;
|
||||
|
||||
case Code.Ldc_I4_0: arg = 0; break;
|
||||
case Code.Ldc_I4_1: arg = 1; break;
|
||||
case Code.Ldc_I4_2: arg = 2; break;
|
||||
case Code.Ldc_I4_3: arg = 3; break;
|
||||
case Code.Ldc_I4_4: arg = 4; break;
|
||||
case Code.Ldc_I4_5: arg = 5; break;
|
||||
case Code.Ldc_I4_6: arg = 6; break;
|
||||
case Code.Ldc_I4_7: arg = 7; break;
|
||||
case Code.Ldc_I4_8: arg = 8; break;
|
||||
case Code.Ldc_I4_M1:arg = -1; break;
|
||||
case Code.Ldnull: arg = null; break;
|
||||
|
||||
case Code.Nop:
|
||||
continue;
|
||||
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
getLocalVariableValue(Instr.getLocalVar(blocks.Locals, instr), out arg);
|
||||
break;
|
||||
|
||||
case Code.Ldsfld:
|
||||
arg = instr.Operand;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException(string.Format("Could not find all arguments to method {0}, instr: {1}", method, instr));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void restoreDecryptedStrings() {
|
||||
decryptCalls.Sort((a, b) => {
|
||||
int i1 = allBlocks.FindIndex((x) => a.block == x);
|
||||
int i2 = allBlocks.FindIndex((x) => b.block == x);
|
||||
if (i1 < i2) return -1;
|
||||
if (i1 > i2) return 1;
|
||||
|
||||
if (a.callStartIndex < b.callStartIndex) return -1;
|
||||
if (a.callStartIndex > b.callStartIndex) return 1;
|
||||
|
||||
return 0;
|
||||
});
|
||||
decryptCalls.Reverse();
|
||||
|
||||
foreach (var decryptCall in decryptCalls) {
|
||||
var block = decryptCall.block;
|
||||
int num = decryptCall.callEndIndex - decryptCall.callStartIndex + 1;
|
||||
block.replace(decryptCall.callStartIndex, num, Instruction.Create(OpCodes.Ldstr, decryptCall.decryptedString));
|
||||
Log.v("Decrypted string: {0}", Utils.toCsharpString(decryptCall.decryptedString));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicStringDecrypter : StringDecrypterBase {
|
||||
IAssemblyClient assemblyClient;
|
||||
Dictionary<int, int> methodTokenToId = new Dictionary<int, int>();
|
||||
|
||||
class MyDecryptCall : DecryptCall {
|
||||
public int methodId;
|
||||
public MyDecryptCall(Block block, int callEndIndex, int methodId)
|
||||
: base(block, callEndIndex) {
|
||||
this.methodId = methodId;
|
||||
}
|
||||
}
|
||||
|
||||
public DynamicStringDecrypter(IAssemblyClient assemblyClient) {
|
||||
this.assemblyClient = assemblyClient;
|
||||
}
|
||||
|
||||
public void init(IEnumerable<int> methodTokens) {
|
||||
methodTokenToId.Clear();
|
||||
foreach (var methodToken in methodTokens) {
|
||||
if (methodTokenToId.ContainsKey(methodToken))
|
||||
continue;
|
||||
methodTokenToId[methodToken] = assemblyClient.Service.defineStringDecrypter(methodToken);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DecryptCall createDecryptCall(MethodReference method, Block block, int callInstrIndex) {
|
||||
int methodId;
|
||||
if (!methodTokenToId.TryGetValue(method.MetadataToken.ToInt32(), out methodId))
|
||||
return null;
|
||||
return new MyDecryptCall(block, callInstrIndex, methodId);
|
||||
}
|
||||
|
||||
protected override void decryptAllCalls() {
|
||||
var sortedCalls = new Dictionary<int, List<MyDecryptCall>>();
|
||||
foreach (var tmp in decryptCalls) {
|
||||
var decryptCall = (MyDecryptCall)tmp;
|
||||
List<MyDecryptCall> list;
|
||||
if (!sortedCalls.TryGetValue(decryptCall.methodId, out list))
|
||||
sortedCalls[decryptCall.methodId] = list = new List<MyDecryptCall>(decryptCalls.Count);
|
||||
list.Add(decryptCall);
|
||||
}
|
||||
|
||||
foreach (var methodId in sortedCalls.Keys) {
|
||||
var list = sortedCalls[methodId];
|
||||
var args = new object[list.Count];
|
||||
for (int i = 0; i < list.Count; i++) {
|
||||
AssemblyData.SimpleData.pack(list[i].args);
|
||||
args[i] = list[i].args;
|
||||
}
|
||||
var decryptedStrings = assemblyClient.Service.decryptStrings(methodId, args);
|
||||
if (decryptedStrings.Length != args.Length)
|
||||
throw new ApplicationException("Invalid decrypted strings array length");
|
||||
AssemblyData.SimpleData.unpack(decryptedStrings);
|
||||
for (int i = 0; i < list.Count; i++) {
|
||||
var s = decryptedStrings[i];
|
||||
if (s == null)
|
||||
throw new ApplicationException(string.Format("Decrypted string is null. Method: {0}", list[i].getMethodReference()));
|
||||
list[i].decryptedString = (string)s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StaticStringDecrypter : StringDecrypterBase {
|
||||
Dictionary<MethodReferenceAndDeclaringTypeKey, Func<MethodDefinition, object[], string>> stringDecrypters = new Dictionary<MethodReferenceAndDeclaringTypeKey, Func<MethodDefinition, object[], string>>();
|
||||
|
||||
public bool HasHandlers {
|
||||
get { return stringDecrypters.Count != 0; }
|
||||
}
|
||||
|
||||
public IEnumerable<MethodDefinition> Methods {
|
||||
get {
|
||||
var list = new List<MethodDefinition>(stringDecrypters.Count);
|
||||
foreach (var key in stringDecrypters.Keys)
|
||||
list.Add((MethodDefinition)key.MethodReference);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
class MyDecryptCall : DecryptCall {
|
||||
public MethodReferenceAndDeclaringTypeKey methodKey;
|
||||
public MyDecryptCall(Block block, int callEndIndex, MethodReference method)
|
||||
: base(block, callEndIndex) {
|
||||
this.methodKey = new MethodReferenceAndDeclaringTypeKey(method);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(MethodDefinition method, Func<MethodReference, object[], string> handler) {
|
||||
if (method != null)
|
||||
stringDecrypters[new MethodReferenceAndDeclaringTypeKey(method)] = handler;
|
||||
}
|
||||
|
||||
protected override void decryptAllCalls() {
|
||||
foreach (var tmp in decryptCalls) {
|
||||
var decryptCall = (MyDecryptCall)tmp;
|
||||
var handler = stringDecrypters[decryptCall.methodKey];
|
||||
decryptCall.decryptedString = handler((MethodDefinition)decryptCall.methodKey.MethodReference, decryptCall.args);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DecryptCall createDecryptCall(MethodReference method, Block block, int callInstrIndex) {
|
||||
if (!stringDecrypters.ContainsKey(new MethodReferenceAndDeclaringTypeKey(method)))
|
||||
return null;
|
||||
return new MyDecryptCall(block, callInstrIndex, method);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 {
|
||||
class UserException : Exception {
|
||||
public UserException(string message)
|
||||
: base(message) {
|
||||
}
|
||||
|
||||
public UserException(string message, Exception innerException)
|
||||
: base(message, innerException) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace de4dot {
|
||||
public enum StartUpArch {
|
||||
AnyCpu,
|
||||
x86,
|
||||
x64,
|
||||
}
|
||||
|
||||
// These are in .NET 3.5 and later...
|
||||
internal delegate TResult Func<out TResult>();
|
||||
internal delegate TResult Func<in T, out TResult>(T arg);
|
||||
internal delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
|
||||
internal delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
|
||||
internal delegate void Action();
|
||||
internal delegate void Action<in T>(T arg);
|
||||
internal delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
|
||||
internal delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3);
|
||||
|
||||
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() {
|
||||
return Item1.GetHashCode() + Item2.GetHashCode();
|
||||
}
|
||||
public override string ToString() {
|
||||
return "<" + Item1.ToString() + "," + Item2.ToString() + ">";
|
||||
}
|
||||
}
|
||||
|
||||
static class Utils {
|
||||
static Random random = new Random();
|
||||
public static StartUpArch startUpArch = StartUpArch.AnyCpu;
|
||||
|
||||
public static string getArchString(string anyCpu, string x86, string x64) {
|
||||
switch (startUpArch) {
|
||||
case StartUpArch.AnyCpu: return anyCpu;
|
||||
case StartUpArch.x86: return x86;
|
||||
case StartUpArch.x64: return x64;
|
||||
default: throw new ApplicationException(string.Format("Invalid startUpArch {0}", startUpArch));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static string toCsharpString(string s) {
|
||||
var sb = new StringBuilder(s.Length + 2);
|
||||
sb.Append('"');
|
||||
foreach (var c in s) {
|
||||
if ((int)c < 0x20) {
|
||||
switch (c) {
|
||||
case '\a': appendEscape(sb, 'a'); break;
|
||||
case '\b': appendEscape(sb, 'b'); break;
|
||||
case '\f': appendEscape(sb, 'f'); break;
|
||||
case '\n': appendEscape(sb, 'n'); break;
|
||||
case '\r': appendEscape(sb, 'r'); break;
|
||||
case '\t': appendEscape(sb, 't'); break;
|
||||
case '\v': appendEscape(sb, 'v'); break;
|
||||
default:
|
||||
sb.Append(string.Format(@"\u{0:X4}", (int)c));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (c == '\\' || c == '"') {
|
||||
appendEscape(sb, c);
|
||||
}
|
||||
else
|
||||
sb.Append(c);
|
||||
}
|
||||
sb.Append('"');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static string shellEscape(string s) {
|
||||
var sb = new StringBuilder(s.Length + 2);
|
||||
sb.Append('"');
|
||||
foreach (var c in s) {
|
||||
if (c == '"')
|
||||
appendEscape(sb, c);
|
||||
else
|
||||
sb.Append(c);
|
||||
}
|
||||
sb.Append('"');
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
static void appendEscape(StringBuilder sb, char c) {
|
||||
sb.Append('\\');
|
||||
sb.Append(c);
|
||||
}
|
||||
|
||||
public static string getFullPath(string path) {
|
||||
try {
|
||||
return Path.GetFullPath(path);
|
||||
}
|
||||
catch (Exception) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
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 string getBaseName(string name) {
|
||||
int index = name.LastIndexOf(Path.DirectorySeparatorChar);
|
||||
if (index < 0)
|
||||
return name;
|
||||
return name.Substring(index + 1);
|
||||
}
|
||||
|
||||
public static string getDirName(string name) {
|
||||
return Path.GetDirectoryName(name);
|
||||
}
|
||||
|
||||
static string ourBaseDir = null;
|
||||
public static string getOurBaseDir() {
|
||||
if (ourBaseDir != null)
|
||||
return ourBaseDir;
|
||||
return ourBaseDir = getDirName(getFullPath(Environment.GetCommandLineArgs()[0]));
|
||||
}
|
||||
|
||||
public static string getPathOfOurFile(string filename) {
|
||||
return Path.Combine(getOurBaseDir(), filename);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 {
|
||||
abstract class BaseBlock {
|
||||
BaseBlock parent = null;
|
||||
|
||||
public BaseBlock Parent {
|
||||
get { return parent; }
|
||||
set { parent = value; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
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 { return fallThrough; }
|
||||
set { fallThrough = value; }
|
||||
}
|
||||
|
||||
public List<Block> Targets {
|
||||
get { return targets; }
|
||||
set { targets = value; }
|
||||
}
|
||||
|
||||
public List<Block> Sources {
|
||||
get { return sources; }
|
||||
}
|
||||
|
||||
public Instr FirstInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
add(new Instr(Instruction.Create(OpCodes.Nop)));
|
||||
return instructions[0];
|
||||
}
|
||||
}
|
||||
|
||||
public Instr LastInstr {
|
||||
get {
|
||||
if (instructions.Count == 0)
|
||||
add(new Instr(Instruction.Create(OpCodes.Nop)));
|
||||
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 {
|
||||
get { return 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 || targets == null || targets.Count != 1)
|
||||
throw new ApplicationException("Invalid block state when last instr is a br/br.s");
|
||||
fallThrough = targets[0];
|
||||
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>(indexes);
|
||||
instrsToDelete.Sort();
|
||||
instrsToDelete.Reverse();
|
||||
foreach (var index in instrsToDelete)
|
||||
remove(index, 1);
|
||||
}
|
||||
|
||||
// Removes all instructions that do nothing, nop and eg. ldc/pop, etc.
|
||||
public bool removeNops() {
|
||||
bool removed = false;
|
||||
|
||||
bool keepLooping = true;
|
||||
while (keepLooping) {
|
||||
var instrsToRemove = new List<int>();
|
||||
for (int i = 0; i < Instructions.Count; i++) {
|
||||
var instr = Instructions[i];
|
||||
if (instr.OpCode.Code == Code.Nop) {
|
||||
// The nop instruction is auto created when we access LastInstr so
|
||||
// make we don't get an infinite loop.
|
||||
if (Instructions.Count != 1)
|
||||
instrsToRemove.Add(i);
|
||||
continue;
|
||||
}
|
||||
if (i + 1 >= Instructions.Count)
|
||||
continue;
|
||||
var next = Instructions[i + 1];
|
||||
if (instr.isSimpleLoad() && next.isPop()) {
|
||||
instrsToRemove.Add(i);
|
||||
instrsToRemove.Add(i + 1);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
keepLooping = instrsToRemove.Count != 0;
|
||||
if (keepLooping) {
|
||||
removed = true;
|
||||
remove(instrsToRemove);
|
||||
}
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
// 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 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) {
|
||||
return sources.Count == 1 && sources[0] == other;
|
||||
}
|
||||
|
||||
// Returns true if we can merge other with this
|
||||
public bool canMerge(Block other) {
|
||||
if (other == null || other == this && getOnlyTarget() != other || !other.isOnlySource(this))
|
||||
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);
|
||||
}
|
||||
|
||||
// Merge two blocks into one
|
||||
public void merge(Block other) {
|
||||
if (!canMerge(other))
|
||||
throw new ApplicationException("Can't merge the two blocks!");
|
||||
|
||||
removeLastBr(); // Get rid of last br/br.s if present
|
||||
|
||||
var newInstructions = new List<Instr>();
|
||||
addInstructions(newInstructions, instructions);
|
||||
addInstructions(newInstructions, other.instructions);
|
||||
instructions = newInstructions;
|
||||
|
||||
disconnectFromFallThroughAndTargets();
|
||||
if (other.targets != null)
|
||||
targets = new List<Block>(other.targets);
|
||||
else
|
||||
targets = null;
|
||||
fallThrough = other.fallThrough;
|
||||
|
||||
other.disconnectFromFallThroughAndTargets();
|
||||
other.Parent = null;
|
||||
updateSources();
|
||||
}
|
||||
|
||||
void addInstructions(IList<Instr> dest, IEnumerable<Instr> instrs) {
|
||||
foreach (var instr in instrs) {
|
||||
if (!instr.isNop())
|
||||
dest.Add(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() {
|
||||
return targets == null && fallThrough != null;
|
||||
}
|
||||
|
||||
public bool canFlipConditionalBranch() {
|
||||
return 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() {
|
||||
return LastInstr.isConditionalBranch();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
class Blocks {
|
||||
MethodDefinition method;
|
||||
IList<VariableDefinition> locals;
|
||||
MethodBlocks methodBlocks;
|
||||
|
||||
public MethodBlocks MethodBlocks {
|
||||
get { return methodBlocks; }
|
||||
}
|
||||
|
||||
public IList<VariableDefinition> Locals {
|
||||
get { return locals; }
|
||||
}
|
||||
|
||||
public MethodDefinition Method {
|
||||
get { return method; }
|
||||
}
|
||||
|
||||
public Blocks(MethodDefinition method) {
|
||||
var body = method.Body;
|
||||
this.method = method;
|
||||
this.locals = body.Variables;
|
||||
methodBlocks = new InstructionListParser(body.Instructions, body.ExceptionHandlers).parse();
|
||||
}
|
||||
|
||||
public void deobfuscateLeaveObfuscation() {
|
||||
foreach (var scopeBlock in getAllScopeBlocks(methodBlocks))
|
||||
scopeBlock.deobfuscateLeaveObfuscation();
|
||||
}
|
||||
|
||||
public void deobfuscate() {
|
||||
foreach (var scopeBlock in getAllScopeBlocks(methodBlocks))
|
||||
scopeBlock.deobfuscate(this);
|
||||
|
||||
removeDeadBlocks();
|
||||
|
||||
foreach (var scopeBlock in getAllScopeBlocks(methodBlocks)) {
|
||||
scopeBlock.mergeBlocks();
|
||||
scopeBlock.repartitionBlocks();
|
||||
scopeBlock.deobfuscateLeaveObfuscation();
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<ScopeBlock> getAllScopeBlocks(ScopeBlock scopeBlock) {
|
||||
var list = new List<ScopeBlock>();
|
||||
list.Add(scopeBlock);
|
||||
list.AddRange(scopeBlock.getAllScopeBlocks());
|
||||
return list;
|
||||
}
|
||||
|
||||
void removeDeadBlocks() {
|
||||
int numDeadBlocks = new DeadBlocksRemover(methodBlocks).remove();
|
||||
if (numDeadBlocks > 0)
|
||||
Log.v("Removed {0} dead block(s)", numDeadBlocks);
|
||||
}
|
||||
|
||||
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;
|
||||
ScopeBlock parent = (ScopeBlock)baseBlock.Parent;
|
||||
ScopeBlockInfo info;
|
||||
if (!infos.TryGetValue(parent, out 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) {
|
||||
var block = (Block)baseBlock;
|
||||
foreach (var block2 in block.getTargets())
|
||||
addBaseBlock(block2);
|
||||
}
|
||||
else if (baseBlock is ScopeBlock) {
|
||||
var scopeBlock = (ScopeBlock)baseBlock;
|
||||
addScopeBlock(scopeBlock);
|
||||
if (scopeBlock.BaseBlocks != null && scopeBlock.BaseBlocks.Count > 0)
|
||||
addBaseBlock(scopeBlock.BaseBlocks[0]);
|
||||
}
|
||||
else
|
||||
throw new ApplicationException(string.Format("Unknown BaseBlock type {0}", baseBlock.GetType()));
|
||||
}
|
||||
|
||||
// Add a block to be processed later, including all its enclosing ScopeBlocks.
|
||||
void addBaseBlock(BaseBlock baseBlock) {
|
||||
for (BaseBlock 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) {
|
||||
var tryBlock = (TryBlock)scopeBlock;
|
||||
foreach (var handler in tryBlock.TryHandlerBlocks)
|
||||
addScopeBlock(handler);
|
||||
}
|
||||
else if (scopeBlock is TryHandlerBlock) {
|
||||
var tryHandlerBlock = (TryHandlerBlock)scopeBlock;
|
||||
addScopeBlock(tryHandlerBlock.FilterHandlerBlock);
|
||||
addScopeBlock(tryHandlerBlock.HandlerBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void getCode(out IList<Instruction> allInstructions, out IList<ExceptionHandler> allExceptionHandlers) {
|
||||
new CodeGenerator(methodBlocks).getCode(out allInstructions, out allExceptionHandlers);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 {
|
||||
class BlocksSorter {
|
||||
ScopeBlock scopeBlock;
|
||||
Dictionary<BaseBlock, bool> visited;
|
||||
List<BaseBlock> sorted;
|
||||
|
||||
public BlocksSorter(ScopeBlock scopeBlock) {
|
||||
this.scopeBlock = scopeBlock;
|
||||
}
|
||||
|
||||
bool hasVisited(BaseBlock bb) {
|
||||
bool hasVisited;
|
||||
if (visited.TryGetValue(bb, out hasVisited))
|
||||
return hasVisited;
|
||||
visited[bb] = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<BaseBlock> sort() {
|
||||
visited = new Dictionary<BaseBlock, bool>();
|
||||
sorted = new List<BaseBlock>(scopeBlock.BaseBlocks.Count);
|
||||
|
||||
search(scopeBlock.BaseBlocks[0]);
|
||||
sorted.Reverse(); // It's in reverse order
|
||||
|
||||
// Just in case there's dead code or unreferenced exception blocks
|
||||
foreach (var bb in scopeBlock.BaseBlocks) {
|
||||
if (hasVisited(bb))
|
||||
continue;
|
||||
sorted.Add(bb);
|
||||
}
|
||||
|
||||
sorted = new ForwardScanOrder(scopeBlock, sorted).fix();
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
// Depth-first order
|
||||
void search(BaseBlock bb) {
|
||||
if (hasVisited(bb))
|
||||
return;
|
||||
|
||||
visited[bb] = true;
|
||||
var block = bb as Block; // Block or ScopeBlock
|
||||
if (block != null) {
|
||||
// Since the sorted array will be in reverse order, and we want the
|
||||
// conditional branches to fall through to their fall-through target, make
|
||||
// sure the FallThrough target is added last! Some conditional instructions
|
||||
// aren't reversible (eg. beq and bne.un) since they don't take the same
|
||||
// types of arguments. This will also make sure .NET Reflector doesn't
|
||||
// crash (sometimes).
|
||||
var targets = new List<Block>(block.getTargets());
|
||||
targets.Reverse();
|
||||
|
||||
foreach (var target in targets) {
|
||||
var child = scopeBlock.toChild(target);
|
||||
if (child != null)
|
||||
search(child);
|
||||
}
|
||||
}
|
||||
sorted.Add(bb);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
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>();
|
||||
|
||||
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 TypeReference catchType;
|
||||
public ExceptionHandlerType handlerType;
|
||||
public ExceptionInfo(int tryStart, int tryEnd, int filterStart,
|
||||
int handlerStart, int handlerEnd, TypeReference catchType,
|
||||
ExceptionHandlerType handlerType) {
|
||||
if (tryStart > tryEnd || filterStart > handlerStart || handlerStart > handlerEnd ||
|
||||
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);
|
||||
|
||||
foreach (var instr in allInstructions) {
|
||||
if (instr.OpCode == OpCodes.Br_S) instr.OpCode = OpCodes.Br;
|
||||
else if (instr.OpCode == OpCodes.Brfalse_S) instr.OpCode = OpCodes.Brfalse;
|
||||
else if (instr.OpCode == OpCodes.Brtrue_S) instr.OpCode = OpCodes.Brtrue;
|
||||
else if (instr.OpCode == OpCodes.Beq_S) instr.OpCode = OpCodes.Beq;
|
||||
else if (instr.OpCode == OpCodes.Bge_S) instr.OpCode = OpCodes.Bge;
|
||||
else if (instr.OpCode == OpCodes.Bgt_S) instr.OpCode = OpCodes.Bgt;
|
||||
else if (instr.OpCode == OpCodes.Ble_S) instr.OpCode = OpCodes.Ble;
|
||||
else if (instr.OpCode == OpCodes.Blt_S) instr.OpCode = OpCodes.Blt;
|
||||
else if (instr.OpCode == OpCodes.Bne_Un_S) instr.OpCode = OpCodes.Bne_Un;
|
||||
else if (instr.OpCode == OpCodes.Bge_Un_S) instr.OpCode = OpCodes.Bge_Un;
|
||||
else if (instr.OpCode == OpCodes.Bgt_Un_S) instr.OpCode = OpCodes.Bgt_Un;
|
||||
else if (instr.OpCode == OpCodes.Ble_Un_S) instr.OpCode = OpCodes.Ble_Un;
|
||||
else if (instr.OpCode == OpCodes.Blt_Un_S) instr.OpCode = OpCodes.Blt_Un;
|
||||
else if (instr.OpCode == OpCodes.Leave_S) instr.OpCode = OpCodes.Leave;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (!optimizeBranches(allInstructions))
|
||||
break;
|
||||
}
|
||||
|
||||
recalculateInstructionOffsets(allInstructions);
|
||||
}
|
||||
|
||||
void recalculateInstructionOffsets(IList<Instruction> allInstructions) {
|
||||
int offset = 0;
|
||||
foreach (var instr in allInstructions) {
|
||||
instr.Offset = offset;
|
||||
offset += instr.GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
bool getShortBranch(Instruction instruction, out OpCode opcode) {
|
||||
switch (instruction.OpCode.Code) {
|
||||
case Code.Br: opcode = OpCodes.Br_S; return true;
|
||||
case Code.Brfalse: opcode = OpCodes.Brfalse_S; return true;
|
||||
case Code.Brtrue: opcode = OpCodes.Brtrue_S; return true;
|
||||
case Code.Beq: opcode = OpCodes.Beq_S; return true;
|
||||
case Code.Bge: opcode = OpCodes.Bge_S; return true;
|
||||
case Code.Bgt: opcode = OpCodes.Bgt_S; return true;
|
||||
case Code.Ble: opcode = OpCodes.Ble_S; return true;
|
||||
case Code.Blt: opcode = OpCodes.Blt_S; return true;
|
||||
case Code.Bne_Un: opcode = OpCodes.Bne_Un_S; return true;
|
||||
case Code.Bge_Un: opcode = OpCodes.Bge_Un_S; return true;
|
||||
case Code.Bgt_Un: opcode = OpCodes.Bgt_Un_S; return true;
|
||||
case Code.Ble_Un: opcode = OpCodes.Ble_Un_S; return true;
|
||||
case Code.Blt_Un: opcode = OpCodes.Blt_Un_S; return true;
|
||||
case Code.Leave: opcode = OpCodes.Leave_S; return true;
|
||||
default: opcode = OpCodes.Nop; return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if something was changed
|
||||
bool optimizeBranches(IList<Instruction> allInstructions) {
|
||||
bool changed = false;
|
||||
|
||||
recalculateInstructionOffsets(allInstructions);
|
||||
for (int i = 0; i < allInstructions.Count; i++) {
|
||||
var instruction = allInstructions[i];
|
||||
OpCode opcode;
|
||||
if (getShortBranch(instruction, out opcode)) {
|
||||
const int instrSize = 5; // It's a long branch instruction
|
||||
var target = (Instruction)instruction.Operand;
|
||||
int distance = target.Offset - (instruction.Offset + instrSize);
|
||||
if (-0x80 <= distance && distance <= 0x7F) {
|
||||
instruction.OpCode = opcode;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
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(Instruction.Create(OpCodes.Br, 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 = blockInfos[ex.tryStart].start;
|
||||
var tryEnd = blockInfos[ex.tryEnd].end;
|
||||
var filterStart = ex.filterStart == -1 ? -1 : blockInfos[ex.filterStart].start;
|
||||
var handlerStart = blockInfos[ex.handlerStart].start;
|
||||
var handlerEnd = 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 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(Instruction.Create(OpCodes.Nop)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
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];
|
||||
var block = bb as Block;
|
||||
if (block != null && 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) {
|
||||
BlockState current = stateStack.Peek();
|
||||
|
||||
if (current.scopeBlock.isOurBlockBase(bb))
|
||||
return current.scopeBlock;
|
||||
return (ScopeBlock)current.scopeBlock.toChild(bb);
|
||||
}
|
||||
|
||||
void doBaseBlock(BaseBlock bb) {
|
||||
BlockState current = stateStack.Peek();
|
||||
ScopeBlock newOne = getScopeBlock(bb);
|
||||
if (newOne == null)
|
||||
return; // Not a BaseBlock somewhere inside this ScopeBlock
|
||||
if (newOne != current.scopeBlock)
|
||||
bb = newOne;
|
||||
|
||||
bool hasVisited;
|
||||
if (!visited.TryGetValue(bb, out 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
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot {
|
||||
abstract class CondBranchDeobfuscator {
|
||||
ScopeBlock scopeBlock;
|
||||
IEnumerable<Block> blocks;
|
||||
|
||||
public CondBranchDeobfuscator(ScopeBlock scopeBlock, IEnumerable<Block> blocks) {
|
||||
this.scopeBlock = scopeBlock;
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
protected abstract bool isTaken(int value);
|
||||
|
||||
public bool deobfuscate() {
|
||||
bool removed = false;
|
||||
|
||||
int value = 0;
|
||||
var deadBlocks = new List<Block>();
|
||||
foreach (var block in blocks) {
|
||||
if (block.Instructions.Count > 1) {
|
||||
if (getLdcValue(block.Instructions, block.Instructions.Count - 2, ref value)) {
|
||||
removed = true;
|
||||
if (isTaken(value)) {
|
||||
deadBlocks.Add(block.FallThrough);
|
||||
block.replaceLastInstrsWithBranch(2, block.Targets[0]);
|
||||
}
|
||||
else {
|
||||
deadBlocks.Add(block.Targets[0]);
|
||||
block.replaceLastInstrsWithBranch(2, block.FallThrough);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach (var source in new List<Block>(block.Sources)) {
|
||||
int count = source.Instructions.Count;
|
||||
if (count > 0 && getLdcValue(source.Instructions, count - 1, ref value)) {
|
||||
removed = true;
|
||||
if (isTaken(value))
|
||||
source.replaceLastNonBranchWithBranch(1, block.Targets[0]);
|
||||
else
|
||||
source.replaceLastNonBranchWithBranch(1, block.FallThrough);
|
||||
}
|
||||
}
|
||||
deadBlocks.Add(block);
|
||||
}
|
||||
}
|
||||
scopeBlock.removeDeadBlocks(deadBlocks);
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
bool getLdcValue(IList<Instr> instrs, int i, ref int value) {
|
||||
var instr = instrs[i];
|
||||
if (instr.OpCode != OpCodes.Dup)
|
||||
return scopeBlock.getLdcValue(instr, out value);
|
||||
|
||||
if (i == 0)
|
||||
return false;
|
||||
return scopeBlock.getLdcValue(instrs[i - 1], out value);
|
||||
}
|
||||
}
|
||||
|
||||
class BrFalseDeobfuscator : CondBranchDeobfuscator {
|
||||
public BrFalseDeobfuscator(ScopeBlock scopeBlock, IEnumerable<Block> blocks)
|
||||
: base(scopeBlock, blocks) {
|
||||
}
|
||||
|
||||
protected override bool isTaken(int value) {
|
||||
return value == 0;
|
||||
}
|
||||
}
|
||||
|
||||
class BrTrueDeobfuscator : CondBranchDeobfuscator {
|
||||
public BrTrueDeobfuscator(ScopeBlock scopeBlock, IEnumerable<Block> blocks)
|
||||
: base(scopeBlock, blocks) {
|
||||
}
|
||||
|
||||
protected override bool isTaken(int value) {
|
||||
return value != 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
class FilterHandlerBlock : ScopeBlock {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
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 Mono.Cecil 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() {
|
||||
Block block = baseBlock as Block;
|
||||
if (block == null) {
|
||||
stackEnd = stackStart;
|
||||
return;
|
||||
}
|
||||
|
||||
int stack = stackStart;
|
||||
foreach (var instr in block.Instructions)
|
||||
DotNetUtils.updateStack(instr.Instruction, ref stack);
|
||||
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 = scopeBlock is TryHandlerBlock ? 1 : 0;
|
||||
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(string.Format("Didn't add all blocks: {0} vs {1}", blockInfos.Count, 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.isOurBlockBase(source))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void scanBaseBlock(BaseBlock bb, int stackStart) {
|
||||
if (blockInfos.ContainsKey(bb) || !scopeBlock.isOurBlockBase(bb))
|
||||
return;
|
||||
|
||||
var blockInfo = new BlockInfo(bb, stackStart);
|
||||
blockInfos[bb] = blockInfo;
|
||||
|
||||
var block = bb as Block;
|
||||
if (block == null) { // i.e., if try, filter, or handler block
|
||||
// It's not important to know the exact values, so we set them both to 0.
|
||||
// Compilers must make sure the stack is empty when entering a try block.
|
||||
blockInfo.stackStart = blockInfo.stackEnd = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
blockInfo.calculateStackUsage();
|
||||
|
||||
foreach (var target in block.getTargets())
|
||||
scanBaseBlock(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(string.Format("Too many/few blocks after sorting: {0} vs {1}", newList.Count, 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.isOurBlockBase(bb))
|
||||
return;
|
||||
inNewList[bb] = true;
|
||||
|
||||
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.isOurBlockBase(source)) {
|
||||
addToNewList(source); // Make sure it's before this block
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newList.Add(bb);
|
||||
}
|
||||
|
||||
bool isInNewList(IEnumerable<Block> blocks) {
|
||||
foreach (var block in blocks) {
|
||||
if (inNewList.ContainsKey(block))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// This is the block inside catch(xxx) { }.
|
||||
class HandlerBlock : ScopeBlock {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil.Cil;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
class Instr {
|
||||
Instruction instruction;
|
||||
|
||||
public OpCode OpCode {
|
||||
get { return instruction.OpCode; }
|
||||
}
|
||||
|
||||
public object Operand {
|
||||
get { return instruction.Operand; }
|
||||
set { instruction.Operand = value; }
|
||||
}
|
||||
|
||||
public Instr(Instruction instruction) {
|
||||
this.instruction = instruction;
|
||||
}
|
||||
|
||||
public Instruction Instruction {
|
||||
get { return instruction; }
|
||||
}
|
||||
|
||||
// Returns the variable or null if it's not a ldloc/stloc instruction
|
||||
public static VariableDefinition getLocalVar(IList<VariableDefinition> locals, Instr instr) {
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Ldloc:
|
||||
case Code.Ldloc_S:
|
||||
case Code.Stloc:
|
||||
case Code.Stloc_S:
|
||||
return (VariableDefinition)instr.Operand;
|
||||
|
||||
case Code.Ldloc_0:
|
||||
case Code.Ldloc_1:
|
||||
case Code.Ldloc_2:
|
||||
case Code.Ldloc_3:
|
||||
return locals[instr.OpCode.Code - Code.Ldloc_0];
|
||||
|
||||
case Code.Stloc_0:
|
||||
case Code.Stloc_1:
|
||||
case Code.Stloc_2:
|
||||
case Code.Stloc_3:
|
||||
return locals[instr.OpCode.Code - Code.Stloc_0];
|
||||
|
||||
default:
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if it's one of the ldc.i4 instructions
|
||||
public bool isLdcI4() {
|
||||
return DotNetUtils.isLdcI4(OpCode.Code);
|
||||
}
|
||||
|
||||
public int getLdcI4Value() {
|
||||
return DotNetUtils.getLdcI4Value(instruction);
|
||||
}
|
||||
|
||||
// Return true if it's one of the stloc instructions
|
||||
public bool isStloc() {
|
||||
return OpCode == OpCodes.Stloc ||
|
||||
OpCode == OpCodes.Stloc_0 ||
|
||||
OpCode == OpCodes.Stloc_1 ||
|
||||
OpCode == OpCodes.Stloc_2 ||
|
||||
OpCode == OpCodes.Stloc_3 ||
|
||||
OpCode == OpCodes.Stloc_S;
|
||||
}
|
||||
|
||||
// Returns true if it's one of the ldloc instructions
|
||||
public bool isLdloc() {
|
||||
return OpCode == OpCodes.Ldloc ||
|
||||
OpCode == OpCodes.Ldloc_0 ||
|
||||
OpCode == OpCodes.Ldloc_1 ||
|
||||
OpCode == OpCodes.Ldloc_2 ||
|
||||
OpCode == OpCodes.Ldloc_3 ||
|
||||
OpCode == OpCodes.Ldloc_S;
|
||||
}
|
||||
|
||||
public bool isNop() {
|
||||
return OpCode == OpCodes.Nop;
|
||||
}
|
||||
|
||||
public bool isPop() {
|
||||
return OpCode == OpCodes.Pop;
|
||||
}
|
||||
|
||||
// Returns true if it's a leave/leave.s
|
||||
public bool isLeave() {
|
||||
return OpCode == OpCodes.Leave || OpCode == OpCodes.Leave_S;
|
||||
}
|
||||
|
||||
// Returns true if it's a br or br.s instruction
|
||||
public bool isBr() {
|
||||
return OpCode == OpCodes.Br || OpCode == OpCodes.Br_S;
|
||||
}
|
||||
|
||||
// Returns true if it's a brfalse/brfalse.s instr
|
||||
public bool isBrfalse() {
|
||||
return OpCode == OpCodes.Brfalse || OpCode == OpCodes.Brfalse_S;
|
||||
}
|
||||
|
||||
// Returns true if it's a brtrue/brtrue.s instr
|
||||
public bool isBrtrue() {
|
||||
return OpCode == OpCodes.Brtrue || OpCode == OpCodes.Brtrue_S;
|
||||
}
|
||||
|
||||
public bool isConditionalBranch() {
|
||||
return DotNetUtils.isConditionalBranch(OpCode.Code);
|
||||
}
|
||||
|
||||
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() {
|
||||
OpCode opcode;
|
||||
if (!getFlippedBranchOpCode(out 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() {
|
||||
OpCode opcode;
|
||||
return getFlippedBranchOpCode(out 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:
|
||||
if (targets.Count == 0)
|
||||
throw new ApplicationException("No targets!");
|
||||
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() {
|
||||
return instruction.ToString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil.Cil;
|
||||
|
||||
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;
|
||||
this.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) {
|
||||
List<int> targetsList;
|
||||
if (!branches.TryGetValue(index, out 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:
|
||||
targets = new List<int> { instrToIndex[(Instruction)instr.Operand] };
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var switchTargets = (Instruction[])instr.Operand;
|
||||
targets = new List<int>(switchTargets.Length);
|
||||
for (int j = 0; j < switchTargets.Length; j++)
|
||||
targets.Add(instrToIndex[switchTargets[j]]);
|
||||
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++) {
|
||||
List<int> branchSources;
|
||||
if (branches.TryGetValue(i, out branchSources) || block == null) {
|
||||
block = new Block();
|
||||
allBlocks.Add(block);
|
||||
}
|
||||
|
||||
block.add(new Instr(this.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:
|
||||
block.Targets = new List<Block> { instrToBlock[instrToIndex[(Instruction)lastInstr.Operand]] };
|
||||
break;
|
||||
|
||||
case OperandType.InlineSwitch:
|
||||
var switchTargets = (Instruction[])lastInstr.Operand;
|
||||
var newSwitchTargets = new List<Block>();
|
||||
block.Targets = newSwitchTargets;
|
||||
foreach (var target in switchTargets)
|
||||
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) {
|
||||
List<ExceptionHandler> handlers;
|
||||
if (!exInfos.TryGetValue(new EHInfo(eh), out 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, BaseBlock bb) {
|
||||
int startIndex, endIndex;
|
||||
var rv = getBlocks(startInstr, endInstr, out startIndex, out 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(BaseBlock parent) {
|
||||
if (blocksLeft.Count == 0)
|
||||
return new List<BaseBlock>();
|
||||
int startIndex, endIndex;
|
||||
var lb = getBlocks(0, blocksLeft[blocksLeft.Count - 1].endInstr, out startIndex, out endIndex);
|
||||
return updateParent(lb, parent);
|
||||
}
|
||||
|
||||
List<BaseBlock> updateParent(List<BaseBlock> lb, BaseBlock 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 {
|
||||
// Start of a method
|
||||
class MethodBlocks : ScopeBlock {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil.Cil;
|
||||
|
||||
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.
|
||||
abstract class ScopeBlock : BaseBlock {
|
||||
protected List<BaseBlock> baseBlocks;
|
||||
|
||||
public List<BaseBlock> BaseBlocks {
|
||||
get { return baseBlocks; }
|
||||
set { baseBlocks = value; }
|
||||
}
|
||||
|
||||
public IEnumerable<BaseBlock> getBaseBlocks() {
|
||||
if (baseBlocks != null) {
|
||||
foreach (var bb in baseBlocks)
|
||||
yield return bb;
|
||||
}
|
||||
}
|
||||
|
||||
public IList<BaseBlock> getAllBaseBlocks() {
|
||||
return getAllBlocks(new List<BaseBlock>());
|
||||
}
|
||||
|
||||
public IList<Block> getAllBlocks() {
|
||||
return getAllBlocks(new List<Block>());
|
||||
}
|
||||
|
||||
public IList<ScopeBlock> getAllScopeBlocks() {
|
||||
return getAllBlocks(new List<ScopeBlock>());
|
||||
}
|
||||
|
||||
IList<T> getAllBlocks<T>(IList<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()) {
|
||||
T t = bb as T;
|
||||
if (t != null)
|
||||
list.Add(t);
|
||||
if (bb is ScopeBlock)
|
||||
addBlocks(list, (ScopeBlock)bb);
|
||||
}
|
||||
}
|
||||
|
||||
List<Block> findBlocks(Func<Block, bool> blockChecker = null) {
|
||||
var blocks = new List<Block>();
|
||||
foreach (var bb in getBaseBlocks()) {
|
||||
Block block = bb as Block;
|
||||
if (block != null && (blockChecker == null || blockChecker(block)))
|
||||
blocks.Add(block);
|
||||
}
|
||||
return blocks;
|
||||
}
|
||||
|
||||
public void deobfuscateLeaveObfuscation() {
|
||||
foreach (var block in findBlocks())
|
||||
deobfuscateLeaveObfuscation(block);
|
||||
}
|
||||
|
||||
void deobfuscateLeaveObfuscation(Block block) {
|
||||
var instrs = block.Instructions;
|
||||
int index = instrs.Count - 1;
|
||||
if (index <= 0)
|
||||
return;
|
||||
var lastInstr = instrs[index];
|
||||
if (!lastInstr.isLeave() && lastInstr.OpCode != OpCodes.Endfinally && lastInstr.OpCode != OpCodes.Rethrow)
|
||||
return;
|
||||
|
||||
int end = index;
|
||||
int start = end;
|
||||
if (start <= 0)
|
||||
return;
|
||||
while (start > 0) {
|
||||
var instr = instrs[--start];
|
||||
|
||||
bool valid = false;
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Dup:
|
||||
case Code.Ldarg:
|
||||
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.Ldarg_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.Ldftn:
|
||||
case Code.Ldloc:
|
||||
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.Ldloc_S:
|
||||
case Code.Ldnull:
|
||||
case Code.Ldsfld:
|
||||
case Code.Ldsflda:
|
||||
case Code.Ldstr:
|
||||
case Code.Ldtoken:
|
||||
case Code.Nop:
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
if (!valid) {
|
||||
start++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int num = end - start;
|
||||
if (num > 0)
|
||||
block.remove(start, num);
|
||||
}
|
||||
|
||||
public void deobfuscate(Blocks blocks) {
|
||||
while (true) {
|
||||
mergeBlocks();
|
||||
|
||||
bool removed = false;
|
||||
removed |= removeNops();
|
||||
removed |= deobfuscateConditionalBranches();
|
||||
if (!removed)
|
||||
break;
|
||||
}
|
||||
|
||||
var switchDeobfuscator = new SwitchControlFlowDeobfuscator(blocks);
|
||||
switchDeobfuscator.deobfuscate(this);
|
||||
}
|
||||
|
||||
bool removeNops() {
|
||||
bool removed = false;
|
||||
|
||||
foreach (var block in findBlocks())
|
||||
removed |= block.removeNops();
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool deobfuscateConditionalBranches() {
|
||||
bool removed = false;
|
||||
removed |= new BrFalseDeobfuscator(this, findBlocks((block) => block.LastInstr.isBrfalse())).deobfuscate();
|
||||
removed |= new BrTrueDeobfuscator(this, findBlocks((block) => block.LastInstr.isBrtrue())).deobfuscate();
|
||||
return removed;
|
||||
}
|
||||
|
||||
// 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 (!isOurBlockBase(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 isOurBlockBase(BaseBlock bb) {
|
||||
return 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 void mergeBlocks() {
|
||||
var blocks = findBlocks();
|
||||
for (int i = 0; i < blocks.Count; i++) {
|
||||
var block = blocks[i];
|
||||
var target = block.getOnlyTarget();
|
||||
if (!isOurBlockBase(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
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (isOurBlockBase(bb))
|
||||
return bb;
|
||||
|
||||
for (bb = bb.Parent; bb != null; bb = bb.Parent) {
|
||||
var sb = bb as ScopeBlock;
|
||||
if (sb == null)
|
||||
throw new ApplicationException("Parent is not a ScopeBlock");
|
||||
if (isOurBlockBase(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");
|
||||
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.getAllBlocks(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) {
|
||||
bool val;
|
||||
if (!removedDict.TryGetValue(source, out 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) {
|
||||
var sb = (ScopeBlock)bb;
|
||||
allDeadBlocks.AddRange(sb.getAllBlocks());
|
||||
}
|
||||
else
|
||||
throw new ApplicationException(string.Format("Unknown BaseBlock type {0}", 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot {
|
||||
class SwitchControlFlowDeobfuscator {
|
||||
Blocks blocks;
|
||||
Dictionary<Block, bool> foundBlocks = new Dictionary<Block, bool>();
|
||||
|
||||
class SwitchObfuscationInfo {
|
||||
public Block switchBlock;
|
||||
public VariableDefinition stateVar;
|
||||
Func<Instr, VariableDefinition> getLocalVar;
|
||||
Dictionary<Block, bool> switchTargetBlocks = new Dictionary<Block, bool>();
|
||||
|
||||
public IEnumerable<Block> SwitchTargetBlocks {
|
||||
get { return switchTargetBlocks.Keys; }
|
||||
}
|
||||
|
||||
internal SwitchObfuscationInfo(Func<Instr, VariableDefinition> getLocalVar) {
|
||||
this.getLocalVar = getLocalVar;
|
||||
}
|
||||
|
||||
void findAllSwitchTargetBlocks() {
|
||||
addSwitchTargetBlock(switchBlock);
|
||||
}
|
||||
|
||||
void addSwitchTargetBlock(Block block) {
|
||||
if (switchTargetBlocks.ContainsKey(block))
|
||||
return;
|
||||
switchTargetBlocks[block] = true;
|
||||
foreach (var source in block.Sources) {
|
||||
if (isNopBlock(source))
|
||||
addSwitchTargetBlock(source);
|
||||
}
|
||||
}
|
||||
|
||||
bool isNopBlock(Block block) {
|
||||
foreach (var instr in block.Instructions) {
|
||||
if (instr.OpCode.Code != Code.Nop && instr.OpCode.Code != Code.Br && instr.OpCode.Code != Code.Br_S)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void fixSwitchBranches(ScopeBlock scopeBlock) {
|
||||
findAllSwitchTargetBlocks();
|
||||
foreach (var switchTargetBlock in new List<Block>(switchTargetBlocks.Keys)) {
|
||||
foreach (var block in new List<Block>(switchTargetBlock.Sources)) {
|
||||
int numInstrs;
|
||||
Block switchTarget;
|
||||
if (getSwitchIndex(block, out numInstrs, out switchTarget))
|
||||
block.replaceLastInstrsWithBranch(numInstrs, switchTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool getSwitchIndex(Block block, out int numInstrs, out Block switchTarget) {
|
||||
numInstrs = -1;
|
||||
switchTarget = null;
|
||||
|
||||
if (block.Instructions.Count < 2)
|
||||
return false;
|
||||
|
||||
int count = block.Instructions.Count;
|
||||
if (!block.Instructions[count - 2].isLdcI4())
|
||||
return false;
|
||||
if (!block.Instructions[count - 1].isStloc() || getLocalVar(block.Instructions[count - 1]) != stateVar)
|
||||
return false;
|
||||
if (!block.isFallThrough() || !switchTargetBlocks.ContainsKey(block.FallThrough))
|
||||
return false;
|
||||
|
||||
int switchIndex = (int)block.Instructions[count - 2].getLdcI4Value();
|
||||
if (switchIndex < 0 || switchIndex >= switchBlock.Targets.Count)
|
||||
return false;
|
||||
|
||||
numInstrs = 2;
|
||||
switchTarget = switchBlock.Targets[switchIndex];
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public SwitchControlFlowDeobfuscator(Blocks blocks) {
|
||||
this.blocks = blocks;
|
||||
}
|
||||
|
||||
public void deobfuscate(ScopeBlock scopeBlock) {
|
||||
while (true) {
|
||||
var switchObfuscationInfo = new SwitchObfuscationInfo((instr) => getLocalVar(instr));
|
||||
if (!findSwitchObfuscation(scopeBlock, switchObfuscationInfo))
|
||||
break;
|
||||
switchObfuscationInfo.fixSwitchBranches(scopeBlock);
|
||||
scopeBlock.removeDeadBlocks(new List<Block>(switchObfuscationInfo.SwitchTargetBlocks));
|
||||
scopeBlock.mergeBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
VariableDefinition getLocalVar(Instr instr) {
|
||||
return Instr.getLocalVar(blocks.Locals, instr);
|
||||
}
|
||||
|
||||
bool findSwitchObfuscation(ScopeBlock scopeBlock, SwitchObfuscationInfo switchObfuscationInfo) {
|
||||
foreach (var bb in scopeBlock.getBaseBlocks()) {
|
||||
var block = bb as Block;
|
||||
if (block == null || foundBlocks.ContainsKey(block))
|
||||
continue;
|
||||
|
||||
if (block.Instructions.Count != 2 || !block.Instructions[0].isLdloc() || block.Instructions[1].OpCode != OpCodes.Switch)
|
||||
continue;
|
||||
switchObfuscationInfo.switchBlock = block;
|
||||
switchObfuscationInfo.stateVar = getLocalVar(block.Instructions[0]);
|
||||
var typeName = switchObfuscationInfo.stateVar.VariableType.FullName;
|
||||
if (typeName != "System.Int32" && typeName != "System.UInt32")
|
||||
continue;
|
||||
|
||||
foundBlocks[block] = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 { }.
|
||||
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 {
|
||||
get { return handlerBlocks; }
|
||||
}
|
||||
|
||||
public void addTryHandler(TryHandlerBlock tryHandlerBlock) {
|
||||
handlerBlocks.Add(tryHandlerBlock);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.blocks {
|
||||
// Contains the filter handler block and the catch handler block.
|
||||
class TryHandlerBlock : ScopeBlock {
|
||||
FilterHandlerBlock filterHandlerBlock = new FilterHandlerBlock();
|
||||
HandlerBlock handlerBlock = new HandlerBlock();
|
||||
|
||||
// State for an ExceptionHandler instance
|
||||
TypeReference catchType;
|
||||
ExceptionHandlerType handlerType;
|
||||
|
||||
public TypeReference CatchType {
|
||||
get { return catchType; }
|
||||
}
|
||||
|
||||
public ExceptionHandlerType HandlerType {
|
||||
get { return handlerType; }
|
||||
}
|
||||
|
||||
public FilterHandlerBlock FilterHandlerBlock {
|
||||
get { return filterHandlerBlock; }
|
||||
}
|
||||
|
||||
public HandlerBlock HandlerBlock {
|
||||
get { return handlerBlock; }
|
||||
}
|
||||
|
||||
public TryHandlerBlock(ExceptionHandler handler) {
|
||||
this.catchType = handler.CatchType;
|
||||
this.handlerType = handler.HandlerType;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{4D10B9EB-3BF1-4D61-A389-CB019E8C9622}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>de4dot</RootNamespace>
|
||||
<AssemblyName>de4dot.code</AssemblyName>
|
||||
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<TargetFrameworkProfile />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>..\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ICSharpCode.SharpZipLib">
|
||||
<HintPath>..\ICSharpCode.SharpZipLib.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Runtime.Remoting" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AssemblyClient\AssemblyClient.cs" />
|
||||
<Compile Include="AssemblyClient\AssemblyClientFactory.cs" />
|
||||
<Compile Include="AssemblyClient\IAssemblyClient.cs" />
|
||||
<Compile Include="AssemblyClient\IAssemblyServerLoader.cs" />
|
||||
<Compile Include="AssemblyClient\IpcAssemblyServerLoader.cs" />
|
||||
<Compile Include="AssemblyClient\NewAppDomainAssemblyServerLoader.cs" />
|
||||
<Compile Include="AssemblyClient\NewProcessAssemblyServerLoader.cs" />
|
||||
<Compile Include="AssemblyClient\SameAppDomainAssemblyServerLoader.cs" />
|
||||
<Compile Include="AssemblyResolver.cs" />
|
||||
<Compile Include="blocks\BaseBlock.cs" />
|
||||
<Compile Include="blocks\Block.cs" />
|
||||
<Compile Include="blocks\Blocks.cs" />
|
||||
<Compile Include="blocks\BlocksSorter.cs" />
|
||||
<Compile Include="blocks\CodeGenerator.cs" />
|
||||
<Compile Include="blocks\CondBranchDeobfuscator.cs" />
|
||||
<Compile Include="blocks\FilterHandlerBlock.cs" />
|
||||
<Compile Include="blocks\ForwardScanOrder.cs" />
|
||||
<Compile Include="blocks\HandlerBlock.cs" />
|
||||
<Compile Include="blocks\Instr.cs" />
|
||||
<Compile Include="blocks\InstructionListParser.cs" />
|
||||
<Compile Include="blocks\MethodBlocks.cs" />
|
||||
<Compile Include="blocks\ScopeBlock.cs" />
|
||||
<Compile Include="blocks\SwitchControlFlowDeobfuscator.cs" />
|
||||
<Compile Include="blocks\TryBlock.cs" />
|
||||
<Compile Include="blocks\TryHandlerBlock.cs" />
|
||||
<Compile Include="CommandLineParser.cs" />
|
||||
<Compile Include="deobfuscators\CliSecure\Deobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\CliSecure\ProxyDelegateFinder.cs" />
|
||||
<Compile Include="deobfuscators\DeobfuscatorBase.cs" />
|
||||
<Compile Include="deobfuscators\DeobfuscatorInfoBase.cs" />
|
||||
<Compile Include="deobfuscators\Dotfuscator\Deobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\Eazfuscator\Deobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\ExceptionLoggerRemover.cs" />
|
||||
<Compile Include="deobfuscators\IDeobfuscatedFile.cs" />
|
||||
<Compile Include="deobfuscators\IDeobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\IDeobfuscatorInfo.cs" />
|
||||
<Compile Include="deobfuscators\ISimpleDeobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\Operations.cs" />
|
||||
<Compile Include="deobfuscators\ProxyDelegateFinderBase.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\AssemblyResolver.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\AssemblyResolverInfo.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\AutomatedErrorReportingFinder.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\Deobfuscator.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\MemoryManagerInfo.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\ProxyDelegateFinder.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\ResolverInfoBase.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\ResourceDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\ResourceDecrypterInfo.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\ResourceResolver.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\ResourceResolverInfo.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\SA_Utils.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\SimpleZipInfo.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\StringDecrypter.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\StringDecrypterInfo.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\StringEncoderClassFinder.cs" />
|
||||
<Compile Include="deobfuscators\SmartAssembly\TamperProtectionRemover.cs" />
|
||||
<Compile Include="deobfuscators\Unknown\Deobfuscator.cs" />
|
||||
<Compile Include="DotNetUtils.cs" />
|
||||
<Compile Include="FilesDeobfuscator.cs" />
|
||||
<Compile Include="IObfuscatedFile.cs" />
|
||||
<Compile Include="Log.cs" />
|
||||
<Compile Include="MemberReferenceHelper.cs" />
|
||||
<Compile Include="AssemblyModule.cs" />
|
||||
<Compile Include="NameRegexes.cs" />
|
||||
<Compile Include="ObfuscatedFile.cs" />
|
||||
<Compile Include="Option.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="renamer\DefinitionsRenamer.cs" />
|
||||
<Compile Include="renamer\MemberReferenceExpander.cs" />
|
||||
<Compile Include="renamer\MemberRefFinder.cs" />
|
||||
<Compile Include="renamer\MemberRefs.cs" />
|
||||
<Compile Include="renamer\MemberRenameState.cs" />
|
||||
<Compile Include="renamer\Misc.cs" />
|
||||
<Compile Include="renamer\Module.cs" />
|
||||
<Compile Include="renamer\NameCreators.cs" />
|
||||
<Compile Include="renamer\RefExpander.cs" />
|
||||
<Compile Include="renamer\TypeNames.cs" />
|
||||
<Compile Include="renamer\TypeNameState.cs" />
|
||||
<Compile Include="renamer\VariableNameState.cs" />
|
||||
<Compile Include="StringDecrypter.cs" />
|
||||
<Compile Include="UserException.cs" />
|
||||
<Compile Include="Utils.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AssemblyData\AssemblyData.csproj">
|
||||
<Project>{FBD84077-9D35-41FE-89DF-8D79EFE0B595}</Project>
|
||||
<Name>AssemblyData</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\cecil\Mono.Cecil.csproj">
|
||||
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
|
||||
<Name>Mono.Cecil</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.deobfuscators.CliSecure {
|
||||
class DeobfuscatorInfo : DeobfuscatorInfoBase {
|
||||
const string DEFAULT_REGEX = @"[a-zA-Z_0-9>}$]$";
|
||||
BoolOption fixResources;
|
||||
BoolOption removeStackFrameHelper;
|
||||
|
||||
public DeobfuscatorInfo()
|
||||
: base("cs", DEFAULT_REGEX) {
|
||||
fixResources = new BoolOption(null, makeArgName("rsrc"), "Decrypt resources", true);
|
||||
removeStackFrameHelper = new BoolOption(null, makeArgName("stack"), "Remove all StackFrameHelper code", true);
|
||||
}
|
||||
|
||||
internal static string ObfuscatorType {
|
||||
get { return "CliSecure"; }
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override IDeobfuscator createDeobfuscator() {
|
||||
return new Deobfuscator(new Deobfuscator.Options {
|
||||
ValidNameRegex = validNameRegex.get(),
|
||||
FixResources = fixResources.get(),
|
||||
RemoveStackFrameHelper = removeStackFrameHelper.get(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override IEnumerable<Option> getOptionsInternal() {
|
||||
return new List<Option>() {
|
||||
fixResources,
|
||||
removeStackFrameHelper,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Deobfuscator : DeobfuscatorBase {
|
||||
Options options;
|
||||
bool foundCliSecureAttribute = false;
|
||||
bool foundProxyDelegateMethod = false;
|
||||
|
||||
MethodDefinition decryptStringMethod;
|
||||
byte[] decryptStringBuffer;
|
||||
|
||||
ProxyDelegateFinder proxyDelegateFinder;
|
||||
|
||||
TypeDefinition cliSecureRtType;
|
||||
MethodDefinition postInitializeMethod;
|
||||
MethodDefinition initializeMethod;
|
||||
TypeDefinition stackFrameHelperType;
|
||||
|
||||
ExceptionLoggerRemover exceptionLoggerRemover = new ExceptionLoggerRemover();
|
||||
|
||||
MethodDefinition rsrcRrrMethod;
|
||||
MethodDefinition rsrcDecryptMethod;
|
||||
|
||||
internal class Options : OptionsBase {
|
||||
public bool FixResources { get; set; }
|
||||
public bool RemoveStackFrameHelper { get; set; }
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return DeobfuscatorInfo.ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return Type; }
|
||||
}
|
||||
|
||||
public Deobfuscator(Options options)
|
||||
: base(options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public override void init(ModuleDefinition module, IList<MemberReference> memberReferences) {
|
||||
base.init(module, memberReferences);
|
||||
proxyDelegateFinder = new ProxyDelegateFinder(module, memberReferences);
|
||||
}
|
||||
|
||||
public override int detect() {
|
||||
scanForObfuscator();
|
||||
|
||||
int val = 0;
|
||||
|
||||
if (cliSecureRtType != null || foundCliSecureAttribute)
|
||||
val += 100;
|
||||
if (decryptStringBuffer != null)
|
||||
val += 10;
|
||||
if (foundProxyDelegateMethod)
|
||||
val += 10;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
protected override void scanForObfuscatorInternal() {
|
||||
findCliSecureAttribute();
|
||||
findCliSecureRtType();
|
||||
findStringDecryptBuffer();
|
||||
findDelegateCreatorType();
|
||||
}
|
||||
|
||||
void findCliSecureAttribute() {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.FullName == "SecureTeam.Attributes.ObfuscatedByCliSecureAttribute") {
|
||||
this.addAttributeToBeRemoved(type, "Obfuscator attribute");
|
||||
foundCliSecureAttribute = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void findCliSecureRtType() {
|
||||
if (cliSecureRtType != null)
|
||||
return;
|
||||
|
||||
foreach (var type in module.Types) {
|
||||
if (type.Namespace != "")
|
||||
continue;
|
||||
var typeName = type.FullName;
|
||||
|
||||
MethodDefinition cs = null;
|
||||
MethodDefinition initialize = null;
|
||||
MethodDefinition postInitialize = null;
|
||||
MethodDefinition load = null;
|
||||
|
||||
int methods = 0;
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.FullName == "System.String " + typeName + "::cs(System.String)") {
|
||||
cs = method;
|
||||
methods++;
|
||||
}
|
||||
else if (method.FullName == "System.Void " + typeName + "::Initialize()") {
|
||||
initialize = method;
|
||||
methods++;
|
||||
}
|
||||
else if (method.FullName == "System.Void " + typeName + "::PostInitialize()") {
|
||||
postInitialize = method;
|
||||
methods++;
|
||||
}
|
||||
else if (method.FullName == "System.IntPtr " + typeName + "::Load()") {
|
||||
load = method;
|
||||
methods++;
|
||||
}
|
||||
}
|
||||
if (methods < 2)
|
||||
continue;
|
||||
|
||||
decryptStringMethod = cs;
|
||||
initializeMethod = initialize;
|
||||
postInitializeMethod = postInitialize;
|
||||
cliSecureRtType = type;
|
||||
if (load != null)
|
||||
findPossibleNamesToRemove(load);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void findStringDecryptBuffer() {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.FullName == "<D234>" || type.FullName == "<ClassD234>") {
|
||||
addTypeToBeRemoved(type, "Obfuscator string decrypter type");
|
||||
foreach (var field in type.Fields) {
|
||||
if (field.FullName == "<D234> <D234>::345" || field.FullName == "<ClassD234>/D234 <ClassD234>::345") {
|
||||
decryptStringBuffer = field.InitialValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void findDelegateCreatorType() {
|
||||
foreach (var type in module.Types) {
|
||||
var methodName = "System.Void " + type.FullName + "::icgd(System.Int32)";
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.FullName == methodName) {
|
||||
proxyDelegateFinder.setDelegateCreatorMethod(method);
|
||||
foundProxyDelegateMethod = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void deobfuscateBegin() {
|
||||
base.deobfuscateBegin();
|
||||
|
||||
foreach (var type in module.Types) {
|
||||
if (type.FullName == "InitializeDelegate" && DotNetUtils.isDelegateType(type))
|
||||
this.addTypeToBeRemoved(type, "Obfuscator type");
|
||||
else if (findResourceDecrypter(type)) {
|
||||
// Nothing
|
||||
}
|
||||
if (options.RemoveStackFrameHelper)
|
||||
findStackFrameHelper(type);
|
||||
}
|
||||
|
||||
proxyDelegateFinder.find();
|
||||
|
||||
if (decryptStringMethod != null)
|
||||
staticStringDecrypter.add(decryptStringMethod, (method, args) => decryptString((string)args[0]));
|
||||
|
||||
addCctorInitCallToBeRemoved(initializeMethod);
|
||||
addCctorInitCallToBeRemoved(postInitializeMethod);
|
||||
if (options.FixResources)
|
||||
addCctorInitCallToBeRemoved(rsrcRrrMethod);
|
||||
}
|
||||
|
||||
bool findResourceDecrypter(TypeDefinition type) {
|
||||
MethodDefinition rrrMethod = null;
|
||||
MethodDefinition decryptMethod = null;
|
||||
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.Name == "rrr" && DotNetUtils.isMethod(method, "System.Void", "()"))
|
||||
rrrMethod = method;
|
||||
else if (DotNetUtils.isMethod(method, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)"))
|
||||
decryptMethod = method;
|
||||
}
|
||||
if (rrrMethod == null || decryptMethod == null)
|
||||
return false;
|
||||
|
||||
var methodCalls = DotNetUtils.getMethodCallCounts(rrrMethod);
|
||||
if (methodCalls.count("System.Void System.ResolveEventHandler::.ctor(System.Object,System.IntPtr)") != 1)
|
||||
return false;
|
||||
|
||||
rsrcRrrMethod = rrrMethod;
|
||||
rsrcDecryptMethod = decryptMethod;
|
||||
return true;
|
||||
}
|
||||
|
||||
void decryptResources() {
|
||||
if (rsrcDecryptMethod == null)
|
||||
return;
|
||||
var resource = getResource(DotNetUtils.getCodeStrings(rsrcDecryptMethod)) as EmbeddedResource;
|
||||
if (resource == null)
|
||||
return;
|
||||
|
||||
DotNetUtils.decryptAndAddResources(module, resource.Name, () => decryptResource(resource));
|
||||
|
||||
addResourceToBeRemoved(resource, "Encrypted resource");
|
||||
if (rsrcDecryptMethod != null)
|
||||
addTypeToBeRemoved(rsrcDecryptMethod.DeclaringType, "Obfuscator resource decrypter type");
|
||||
}
|
||||
|
||||
byte[] decryptResource(EmbeddedResource resource) {
|
||||
using (var rsrcStream = resource.GetResourceStream()) {
|
||||
using (var reader = new BinaryReader(rsrcStream)) {
|
||||
var key = reader.ReadString();
|
||||
var data = reader.ReadBytes((int)(rsrcStream.Length - rsrcStream.Position));
|
||||
var cryptoTransform = new DESCryptoServiceProvider {
|
||||
Key = Encoding.ASCII.GetBytes(key),
|
||||
IV = Encoding.ASCII.GetBytes(key),
|
||||
}.CreateDecryptor();
|
||||
var memStream = new MemoryStream(data);
|
||||
using (var reader2 = new BinaryReader(new CryptoStream(memStream, cryptoTransform, CryptoStreamMode.Read))) {
|
||||
return reader2.ReadBytes((int)memStream.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void findStackFrameHelper(TypeDefinition type) {
|
||||
if (!type.HasMethods)
|
||||
return;
|
||||
if (type.Methods.Count > 3)
|
||||
return;
|
||||
|
||||
MethodDefinition errorMethod = null;
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.IsRuntimeSpecialName && method.Name == ".ctor" && method.HasParameters == false)
|
||||
continue; // .ctor is allowed
|
||||
if (method.IsRuntimeSpecialName && method.Name == ".cctor" && method.HasParameters == false)
|
||||
continue; // .cctor is allowed
|
||||
if (method.IsStatic && method.CallingConvention == MethodCallingConvention.Default &&
|
||||
method.ExplicitThis == false && method.HasThis == false &&
|
||||
method.HasBody && method.IsManaged && method.IsIL && method.HasParameters &&
|
||||
method.Parameters.Count == 2 && method.HasGenericParameters == false &&
|
||||
!DotNetUtils.hasReturnValue(method) &&
|
||||
MemberReferenceHelper.verifyType(method.Parameters[0].ParameterType, "mscorlib", "System.Exception") &&
|
||||
MemberReferenceHelper.verifyType(method.Parameters[1].ParameterType, "mscorlib", "System.Object", "[]")) {
|
||||
errorMethod = method;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
if (errorMethod != null) {
|
||||
if (stackFrameHelperType != null)
|
||||
throw new ApplicationException("Found another StackFrameHelper");
|
||||
stackFrameHelperType = type;
|
||||
exceptionLoggerRemover.add(errorMethod);
|
||||
}
|
||||
}
|
||||
|
||||
public override void deobfuscateMethodEnd(Blocks blocks) {
|
||||
proxyDelegateFinder.deobfuscate(blocks);
|
||||
removeStackFrameHelperCode(blocks);
|
||||
base.deobfuscateMethodEnd(blocks);
|
||||
}
|
||||
|
||||
public override void deobfuscateEnd() {
|
||||
if (options.FixResources)
|
||||
decryptResources();
|
||||
removeProxyDelegates(proxyDelegateFinder);
|
||||
if (exceptionLoggerRemover.NumRemovedExceptionLoggers > 0)
|
||||
addTypeToBeRemoved(stackFrameHelperType, "StackFrameHelper type");
|
||||
if (Operations.DecryptStrings != OpDecryptString.None)
|
||||
addTypeToBeRemoved(cliSecureRtType, "Obfuscator type");
|
||||
addResources("Obfuscator protection files");
|
||||
addModuleReferences("Obfuscator protection files");
|
||||
|
||||
base.deobfuscateEnd();
|
||||
}
|
||||
|
||||
public override IEnumerable<string> getStringDecrypterMethods() {
|
||||
var list = new List<string>();
|
||||
if (decryptStringMethod != null)
|
||||
list.Add(decryptStringMethod.MetadataToken.ToInt32().ToString("X8"));
|
||||
return list;
|
||||
}
|
||||
|
||||
string decryptString(string es) {
|
||||
if (decryptStringBuffer == null)
|
||||
throw new ApplicationException("Trying to decrypt strings when decryptStringBuffer is null (could not find it!)");
|
||||
StringBuilder sb = new StringBuilder(es.Length);
|
||||
for (int i = 0; i < es.Length; i++)
|
||||
sb.Append(Convert.ToChar((int)(es[i] ^ decryptStringBuffer[i % decryptStringBuffer.Length])));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
void removeStackFrameHelperCode(Blocks blocks) {
|
||||
if (exceptionLoggerRemover.remove(blocks))
|
||||
Log.v("Removed StackFrameHelper code");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
|
||||
namespace de4dot.deobfuscators.CliSecure {
|
||||
class ProxyDelegateFinder : ProxyDelegateFinderBase {
|
||||
public ProxyDelegateFinder(ModuleDefinition module, IList<MemberReference> memberReferences)
|
||||
: base(module, memberReferences) {
|
||||
}
|
||||
|
||||
protected override void getCallInfo(FieldDefinition field, out int methodIndex, out bool isVirtual) {
|
||||
var name = field.Name;
|
||||
isVirtual = false;
|
||||
if (name.EndsWith("%", StringComparison.Ordinal)) {
|
||||
isVirtual = true;
|
||||
name = name.TrimEnd(new char[] { '%' });
|
||||
}
|
||||
byte[] value = Convert.FromBase64String(name);
|
||||
methodIndex = BitConverter.ToInt32(value, 0); // 0-based memberRef index
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,424 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.deobfuscators {
|
||||
abstract class DeobfuscatorBase : IDeobfuscator {
|
||||
public const string DEFAULT_VALID_NAME_REGEX = @"^[a-zA-Z_<{$][a-zA-Z_0-9<>{}$.`-]*$";
|
||||
|
||||
class RemoveInfo<T> {
|
||||
public T obj;
|
||||
public string reason;
|
||||
public RemoveInfo(T obj, string reason) {
|
||||
this.obj = obj;
|
||||
this.reason = reason;
|
||||
}
|
||||
}
|
||||
|
||||
OptionsBase optionsBase;
|
||||
protected ModuleDefinition module;
|
||||
protected StaticStringDecrypter staticStringDecrypter = new StaticStringDecrypter();
|
||||
IList<RemoveInfo<TypeDefinition>> typesToRemove = new List<RemoveInfo<TypeDefinition>>();
|
||||
IList<RemoveInfo<MethodDefinition>> methodsToRemove = new List<RemoveInfo<MethodDefinition>>();
|
||||
IList<RemoveInfo<FieldDefinition>> fieldsToRemove = new List<RemoveInfo<FieldDefinition>>();
|
||||
IList<RemoveInfo<TypeDefinition>> attrsToRemove = new List<RemoveInfo<TypeDefinition>>();
|
||||
IList<RemoveInfo<Resource>> resourcesToRemove = new List<RemoveInfo<Resource>>();
|
||||
IList<RemoveInfo<ModuleReference>> modrefsToRemove = new List<RemoveInfo<ModuleReference>>();
|
||||
List<string> namesToPossiblyRemove = new List<string>();
|
||||
bool scanForObfuscatorCalled = false;
|
||||
Dictionary<MethodReferenceAndDeclaringTypeKey, MethodDefinition> cctorInitCallsToRemove = new Dictionary<MethodReferenceAndDeclaringTypeKey, MethodDefinition>();
|
||||
|
||||
internal class OptionsBase : IDeobfuscatorOptions {
|
||||
public bool RenameResourcesInCode { get; set; }
|
||||
public NameRegexes ValidNameRegex { get; set; }
|
||||
public bool DecryptStrings { get; set; }
|
||||
|
||||
public OptionsBase() {
|
||||
RenameResourcesInCode = true;
|
||||
}
|
||||
}
|
||||
|
||||
public IDeobfuscatorOptions TheOptions {
|
||||
get { return optionsBase; }
|
||||
}
|
||||
|
||||
public IOperations Operations { get; set; }
|
||||
public IDeobfuscatedFile DeobfuscatedFile { get; set; }
|
||||
public virtual StringFeatures StringFeatures { get; set; }
|
||||
|
||||
public abstract string Type { get; }
|
||||
public abstract string Name { get; }
|
||||
|
||||
public Func<string, bool> IsValidName {
|
||||
get { return (name) => optionsBase.ValidNameRegex.isMatch(name); }
|
||||
}
|
||||
|
||||
public DeobfuscatorBase(OptionsBase optionsBase) {
|
||||
this.optionsBase = optionsBase;
|
||||
StringFeatures = StringFeatures.AllowAll;
|
||||
}
|
||||
|
||||
public virtual void init(ModuleDefinition module, IList<MemberReference> memberReferences) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
protected void scanForObfuscator() {
|
||||
if (scanForObfuscatorCalled)
|
||||
return;
|
||||
scanForObfuscatorCalled = true;
|
||||
scanForObfuscatorInternal();
|
||||
}
|
||||
|
||||
protected virtual void scanForObfuscatorInternal() {
|
||||
}
|
||||
|
||||
public abstract int detect();
|
||||
|
||||
public virtual void deobfuscateBegin() {
|
||||
scanForObfuscator();
|
||||
}
|
||||
|
||||
public virtual void deobfuscateMethodBegin(Blocks blocks) {
|
||||
}
|
||||
|
||||
public virtual void deobfuscateMethodEnd(Blocks blocks) {
|
||||
removeInitializeMethodCalls(blocks);
|
||||
}
|
||||
|
||||
public virtual void deobfuscateStrings(Blocks blocks) {
|
||||
if (staticStringDecrypter.HasHandlers)
|
||||
staticStringDecrypter.decrypt(blocks);
|
||||
}
|
||||
|
||||
public virtual void deobfuscateEnd() {
|
||||
if (!Operations.KeepObfuscatorTypes) {
|
||||
deleteEmptyCctors();
|
||||
deleteMethods();
|
||||
deleteFields();
|
||||
deleteCustomAttributes();
|
||||
deleteOtherAttributes();
|
||||
|
||||
// Delete types after removing methods, fields, and attributes. The reason is
|
||||
// that the Scope property will be null if we remove a type. Comparing a
|
||||
// typeref with a typedef will then fail.
|
||||
deleteTypes();
|
||||
|
||||
deleteDllResources();
|
||||
deleteModuleReferences();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual IEnumerable<string> getStringDecrypterMethods() {
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
public void addCctorInitCallToBeRemoved(MethodDefinition method) {
|
||||
if (method == null)
|
||||
return;
|
||||
if (method.Parameters.Count != 0)
|
||||
throw new ApplicationException(string.Format("Method takes params: {0}", method));
|
||||
if (DotNetUtils.hasReturnValue(method))
|
||||
throw new ApplicationException(string.Format("Method has a return value: {0}", method));
|
||||
cctorInitCallsToRemove[new MethodReferenceAndDeclaringTypeKey(method)] = method;
|
||||
}
|
||||
|
||||
void removeInitializeMethodCalls(Blocks blocks) {
|
||||
if (blocks.Method.Name != ".cctor")
|
||||
return;
|
||||
|
||||
foreach (var block in blocks.MethodBlocks.getAllBlocks()) {
|
||||
var instrsToDelete = new List<int>();
|
||||
for (int i = 0; i < block.Instructions.Count; i++) {
|
||||
var instr = block.Instructions[i];
|
||||
if (instr.OpCode == OpCodes.Call) {
|
||||
var destMethod = instr.Operand as MethodReference;
|
||||
if (destMethod == null)
|
||||
continue;
|
||||
var key = new MethodReferenceAndDeclaringTypeKey(destMethod);
|
||||
if (cctorInitCallsToRemove.ContainsKey(key)) {
|
||||
Log.v("Removed call to {0}", destMethod);
|
||||
instrsToDelete.Add(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
block.remove(instrsToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addMethodsToBeRemoved(IEnumerable<MethodDefinition> methods, string reason) {
|
||||
foreach (var method in methods)
|
||||
addMethodToBeRemoved(method, reason);
|
||||
}
|
||||
|
||||
protected void addMethodToBeRemoved(MethodDefinition method, string reason) {
|
||||
methodsToRemove.Add(new RemoveInfo<MethodDefinition>(method, reason));
|
||||
}
|
||||
|
||||
protected void addFieldsToBeRemoved(IEnumerable<FieldDefinition> fields, string reason) {
|
||||
foreach (var field in fields)
|
||||
addFieldToBeRemoved(field, reason);
|
||||
}
|
||||
|
||||
protected void addFieldToBeRemoved(FieldDefinition field, string reason) {
|
||||
fieldsToRemove.Add(new RemoveInfo<FieldDefinition>(field, reason));
|
||||
}
|
||||
|
||||
protected void addAttributeToBeRemoved(TypeDefinition attr, string reason) {
|
||||
addTypeToBeRemoved(attr, reason);
|
||||
attrsToRemove.Add(new RemoveInfo<TypeDefinition>(attr, reason));
|
||||
}
|
||||
|
||||
protected void addTypesToBeRemoved(IEnumerable<TypeDefinition> types, string reason) {
|
||||
foreach (var type in types)
|
||||
addTypeToBeRemoved(type, reason);
|
||||
}
|
||||
|
||||
protected void addTypeToBeRemoved(TypeDefinition type, string reason) {
|
||||
typesToRemove.Add(new RemoveInfo<TypeDefinition>(type, reason));
|
||||
}
|
||||
|
||||
protected void addResourceToBeRemoved(Resource resource, string reason) {
|
||||
resourcesToRemove.Add(new RemoveInfo<Resource>(resource, reason));
|
||||
}
|
||||
|
||||
protected void addModuleReferenceToBeRemoved(ModuleReference modref, string reason) {
|
||||
modrefsToRemove.Add(new RemoveInfo<ModuleReference>(modref, reason));
|
||||
}
|
||||
|
||||
void deleteEmptyCctors() {
|
||||
var emptyCctorsToRemove = new List<MethodDefinition>();
|
||||
foreach (var type in module.GetTypes()) {
|
||||
var cctor = DotNetUtils.getMethod(type, ".cctor");
|
||||
if (cctor != null && DotNetUtils.isEmpty(cctor))
|
||||
emptyCctorsToRemove.Add(cctor);
|
||||
}
|
||||
|
||||
if (emptyCctorsToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
Log.v("Removing empty .cctor methods");
|
||||
Log.indent();
|
||||
foreach (var cctor in emptyCctorsToRemove) {
|
||||
var type = cctor.DeclaringType;
|
||||
if (type.Methods.Remove(cctor))
|
||||
Log.v("{0:X8}, type: {1} ({2:X8})", cctor.MetadataToken.ToUInt32(), type, type.MetadataToken.ToUInt32());
|
||||
}
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void deleteMethods() {
|
||||
if (methodsToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
Log.v("Removing methods");
|
||||
Log.indent();
|
||||
foreach (var info in methodsToRemove) {
|
||||
var method = info.obj;
|
||||
if (method == null)
|
||||
continue;
|
||||
var type = method.DeclaringType;
|
||||
if (type.Methods.Remove(method))
|
||||
Log.v("Removed method {0} ({1:X8}) (Type: {2}) (reason: {3})", method, method.MetadataToken.ToUInt32(), type, info.reason);
|
||||
}
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void deleteFields() {
|
||||
if (fieldsToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
Log.v("Removing fields");
|
||||
Log.indent();
|
||||
foreach (var info in fieldsToRemove) {
|
||||
var field = info.obj;
|
||||
if (field == null)
|
||||
continue;
|
||||
var type = field.DeclaringType;
|
||||
if (type.Fields.Remove(field))
|
||||
Log.v("Removed field {0} ({1:X8}) (Type: {2}) (reason: {3})", field, field.MetadataToken.ToUInt32(), type, info.reason);
|
||||
}
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void deleteTypes() {
|
||||
var types = module.Types;
|
||||
if (types == null || typesToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
Log.v("Removing types");
|
||||
Log.indent();
|
||||
foreach (var info in typesToRemove) {
|
||||
var typeDef = info.obj;
|
||||
if (typeDef == null)
|
||||
continue;
|
||||
if (types.Remove(typeDef))
|
||||
Log.v("Removed type {0} ({1:X8}) (reason: {2})", typeDef, typeDef.MetadataToken.ToUInt32(), info.reason);
|
||||
}
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void deleteCustomAttributes() {
|
||||
if (attrsToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
Log.v("Removing custom attributes");
|
||||
Log.indent();
|
||||
deleteCustomAttributes(module.CustomAttributes);
|
||||
deleteCustomAttributes(module.Assembly.CustomAttributes);
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void deleteCustomAttributes(IList<CustomAttribute> customAttrs) {
|
||||
if (customAttrs == null)
|
||||
return;
|
||||
foreach (var info in attrsToRemove) {
|
||||
var typeDef = info.obj;
|
||||
if (typeDef == null)
|
||||
continue;
|
||||
for (int i = 0; i < customAttrs.Count; i++) {
|
||||
if (MemberReferenceHelper.compareTypes(customAttrs[i].AttributeType, typeDef)) {
|
||||
customAttrs.RemoveAt(i);
|
||||
Log.v("Removed custom attribute {0} ({1:X8}) (reason: {2})", typeDef, typeDef.MetadataToken.ToUInt32(), info.reason);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteOtherAttributes() {
|
||||
Log.v("Removing other attributes");
|
||||
Log.indent();
|
||||
deleteOtherAttributes(module.CustomAttributes);
|
||||
deleteOtherAttributes(module.Assembly.CustomAttributes);
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void deleteOtherAttributes(IList<CustomAttribute> customAttributes) {
|
||||
for (int i = customAttributes.Count - 1; i >= 0; i--) {
|
||||
var attr = customAttributes[i].AttributeType;
|
||||
if (attr.FullName == "System.Runtime.CompilerServices.SuppressIldasmAttribute") {
|
||||
Log.v("Removed attribute {0}", attr.FullName);
|
||||
customAttributes.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteDllResources() {
|
||||
if (!module.HasResources || resourcesToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
var resources = module.Resources;
|
||||
Log.v("Removing resources");
|
||||
Log.indent();
|
||||
foreach (var info in resourcesToRemove) {
|
||||
var resource = info.obj;
|
||||
if (resource == null)
|
||||
continue;
|
||||
if (module.Resources.Remove(resource))
|
||||
Log.v("Removed resource {0} (reason: {1})", Utils.toCsharpString(resource.Name), info.reason);
|
||||
}
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
void deleteModuleReferences() {
|
||||
if (!module.HasModuleReferences || modrefsToRemove.Count == 0)
|
||||
return;
|
||||
|
||||
Log.v("Removing module references");
|
||||
Log.indent();
|
||||
foreach (var info in modrefsToRemove) {
|
||||
var modref = info.obj;
|
||||
if (modref == null)
|
||||
continue;
|
||||
if (module.ModuleReferences.Remove(modref))
|
||||
Log.v("Removed module reference {0} (reason: {1})", modref, info.reason);
|
||||
}
|
||||
Log.deIndent();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return Name;
|
||||
}
|
||||
|
||||
protected void findPossibleNamesToRemove(MethodDefinition method) {
|
||||
if (method == null || !method.HasBody)
|
||||
return;
|
||||
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode == OpCodes.Ldstr)
|
||||
namesToPossiblyRemove.Add((string)instr.Operand);
|
||||
}
|
||||
}
|
||||
|
||||
protected void addResources(string reason) {
|
||||
if (!module.HasResources)
|
||||
return;
|
||||
|
||||
var resources = module.Resources;
|
||||
foreach (var name in namesToPossiblyRemove) {
|
||||
foreach (var resource in module.Resources) {
|
||||
if (resource.Name == name) {
|
||||
addResourceToBeRemoved(resource, reason);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void addModuleReferences(string reason) {
|
||||
if (!module.HasModuleReferences)
|
||||
return;
|
||||
|
||||
foreach (var name in namesToPossiblyRemove) {
|
||||
foreach (var moduleRef in module.ModuleReferences) {
|
||||
if (moduleRef.Name.StartsWith(name, StringComparison.OrdinalIgnoreCase))
|
||||
addModuleReferenceToBeRemoved(moduleRef, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeProxyDelegates(ProxyDelegateFinderBase proxyDelegateFinder) {
|
||||
addTypesToBeRemoved(proxyDelegateFinder.DelegateTypes, "Proxy delegate type");
|
||||
if (proxyDelegateFinder.RemovedDelegateCreatorCalls > 0)
|
||||
addTypeToBeRemoved(proxyDelegateFinder.DelegateCreatorMethod.DeclaringType, "Proxy delegate creator type");
|
||||
}
|
||||
|
||||
protected TypeDefinition getModuleType() {
|
||||
return DotNetUtils.getModuleType(module);
|
||||
}
|
||||
|
||||
protected TypeDefinition getType(TypeReference typeReference) {
|
||||
return DotNetUtils.getType(module, typeReference);
|
||||
}
|
||||
|
||||
protected Resource getResource(IEnumerable<string> strings) {
|
||||
return DotNetUtils.getResource(module, strings);
|
||||
}
|
||||
|
||||
protected CustomAttribute getAssemblyAttribute(TypeReference attr) {
|
||||
var list = new List<CustomAttribute>(DotNetUtils.findAttributes(module.Assembly, attr));
|
||||
return list.Count == 0 ? null : list[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.deobfuscators {
|
||||
abstract class DeobfuscatorInfoBase : IDeobfuscatorInfo {
|
||||
string argPrefix;
|
||||
protected NameRegexOption validNameRegex;
|
||||
|
||||
public DeobfuscatorInfoBase(string argPrefix, string nameRegex = null) {
|
||||
this.argPrefix = argPrefix;
|
||||
validNameRegex = new NameRegexOption(null, makeArgName("name"), "Valid name regex pattern", nameRegex ?? DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX);
|
||||
}
|
||||
|
||||
protected string makeArgName(string name) {
|
||||
return string.Format("{0}-{1}", argPrefix, name);
|
||||
}
|
||||
|
||||
public abstract string Type { get; }
|
||||
public abstract IDeobfuscator createDeobfuscator();
|
||||
|
||||
protected virtual IEnumerable<Option> getOptionsInternal() {
|
||||
return new List<Option>();
|
||||
}
|
||||
|
||||
public IEnumerable<Option> getOptions() {
|
||||
var options = new List<Option>();
|
||||
options.Add(validNameRegex);
|
||||
options.AddRange(getOptionsInternal());
|
||||
return options;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.deobfuscators.Dotfuscator {
|
||||
class DeobfuscatorInfo : DeobfuscatorInfoBase {
|
||||
const string DEFAULT_REGEX = @"!^[a-z][a-z0-9]{0,2}$&!^A_[0-9]+$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
|
||||
public DeobfuscatorInfo()
|
||||
: base("df", DEFAULT_REGEX) {
|
||||
}
|
||||
|
||||
internal static string ObfuscatorType {
|
||||
get { return "Dotfuscator"; }
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override IDeobfuscator createDeobfuscator() {
|
||||
return new Deobfuscator(new Deobfuscator.Options {
|
||||
RenameResourcesInCode = false,
|
||||
ValidNameRegex = validNameRegex.get(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Deobfuscator : DeobfuscatorBase {
|
||||
Options options;
|
||||
string obfuscatorName = "Dotfuscator";
|
||||
Dictionary<MethodReference, StringDecrypterInfo> stringDecrypterMethods = new Dictionary<MethodReference, StringDecrypterInfo>();
|
||||
bool foundDotfuscatorAttribute = false;
|
||||
|
||||
class StringDecrypterInfo {
|
||||
public MethodDefinition method;
|
||||
public int magic;
|
||||
public StringDecrypterInfo(MethodDefinition method, int magic) {
|
||||
this.method = method;
|
||||
this.magic = magic;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Options : OptionsBase {
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return DeobfuscatorInfo.ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return obfuscatorName; }
|
||||
}
|
||||
|
||||
public Deobfuscator(Options options)
|
||||
: base(options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public override int detect() {
|
||||
scanForObfuscator();
|
||||
|
||||
int val = 0;
|
||||
|
||||
if (foundDotfuscatorAttribute)
|
||||
val += 100;
|
||||
if (stringDecrypterMethods.Count > 0)
|
||||
val += 10;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
protected override void scanForObfuscatorInternal() {
|
||||
findDotfuscatorAttribute();
|
||||
findStringDecrypterMethods();
|
||||
}
|
||||
|
||||
void findDotfuscatorAttribute() {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.FullName == "DotfuscatorAttribute") {
|
||||
foundDotfuscatorAttribute = true;
|
||||
addAttributeToBeRemoved(type, "Obfuscator attribute");
|
||||
initializeVersion(type);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initializeVersion(TypeDefinition attr) {
|
||||
var s = DotNetUtils.getCustomArgAsString(getAssemblyAttribute(attr), 0);
|
||||
if (s == null)
|
||||
return;
|
||||
|
||||
var val = System.Text.RegularExpressions.Regex.Match(s, @"^(\d+:\d+:\d+:\d+\.\d+\.\d+\.\d+)$");
|
||||
if (val.Groups.Count < 2)
|
||||
return;
|
||||
obfuscatorName = "Dotfuscator " + val.Groups[1].ToString();
|
||||
}
|
||||
|
||||
void findStringDecrypterMethods() {
|
||||
foreach (var type in module.GetTypes())
|
||||
findStringDecrypterMethods(type);
|
||||
}
|
||||
|
||||
void findStringDecrypterMethods(TypeDefinition type) {
|
||||
foreach (var method in DotNetUtils.findMethods(type.Methods, "System.String", new string[] { "System.String", "System.Int32" })) {
|
||||
if (method.Body.HasExceptionHandlers)
|
||||
continue;
|
||||
|
||||
var methodCalls = DotNetUtils.getMethodCallCounts(method);
|
||||
if (methodCalls.count("System.Char[] System.String::ToCharArray()") != 1)
|
||||
continue;
|
||||
if (methodCalls.count("System.String System.String::Intern(System.String)") != 1)
|
||||
continue;
|
||||
|
||||
DeobfuscatedFile.deobfuscate(method);
|
||||
var instructions = method.Body.Instructions;
|
||||
for (int i = 0; i <= instructions.Count - 3; i++) {
|
||||
var ldci4 = method.Body.Instructions[i];
|
||||
if (!DotNetUtils.isLdcI4(ldci4))
|
||||
continue;
|
||||
if (instructions[i + 1].OpCode.Code != Code.Ldarg_1)
|
||||
continue;
|
||||
if (instructions[i + 2].OpCode.Code != Code.Add)
|
||||
continue;
|
||||
|
||||
var info = new StringDecrypterInfo(method, DotNetUtils.getLdcI4Value(ldci4));
|
||||
Log.v("Found string decrypter method: {0}, magic: 0x{1:X8}", info.method, info.magic);
|
||||
stringDecrypterMethods[info.method] = info;
|
||||
staticStringDecrypter.add(info.method, (method2, args) => decryptString(stringDecrypterMethods[method2], (string)args[0], (int)args[1]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void deobfuscateEnd() {
|
||||
if (Operations.DecryptStrings != OpDecryptString.None) {
|
||||
foreach (var method in stringDecrypterMethods.Keys)
|
||||
addMethodToBeRemoved(stringDecrypterMethods[method].method, "String decrypter method");
|
||||
}
|
||||
|
||||
base.deobfuscateEnd();
|
||||
}
|
||||
|
||||
static string decryptString(StringDecrypterInfo info, string encrypted, int value) {
|
||||
char[] chars = encrypted.ToCharArray();
|
||||
byte key = (byte)(info.magic + value);
|
||||
for (int i = 0; i < chars.Length; i++) {
|
||||
char c = chars[i];
|
||||
byte b1 = (byte)((byte)c ^ key++);
|
||||
byte b2 = (byte)((byte)(c >> 8) ^ key++);
|
||||
chars[i] = (char)((b1 << 8) | b2);
|
||||
}
|
||||
return new string(chars);
|
||||
}
|
||||
|
||||
public override IEnumerable<string> getStringDecrypterMethods() {
|
||||
var list = new List<string>();
|
||||
foreach (var method in stringDecrypterMethods.Keys)
|
||||
list.Add(method.MetadataToken.ToInt32().ToString("X8"));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.deobfuscators.Eazfuscator {
|
||||
class DeobfuscatorInfo : DeobfuscatorInfoBase {
|
||||
public DeobfuscatorInfo()
|
||||
: base("ez") {
|
||||
}
|
||||
|
||||
internal static string ObfuscatorType {
|
||||
get { return "Eazfuscator"; }
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override IDeobfuscator createDeobfuscator() {
|
||||
return new Deobfuscator(new Deobfuscator.Options {
|
||||
ValidNameRegex = validNameRegex.get(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Deobfuscator : DeobfuscatorBase {
|
||||
Options options;
|
||||
TypeDefinition decryptStringType;
|
||||
MethodDefinition decryptStringMethod;
|
||||
|
||||
internal class Options : OptionsBase {
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return DeobfuscatorInfo.ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return "Eazfuscator.NET"; }
|
||||
}
|
||||
|
||||
public Deobfuscator(Options options)
|
||||
: base(options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public override int detect() {
|
||||
scanForObfuscator();
|
||||
if (decryptStringMethod != null)
|
||||
return 100;
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override void scanForObfuscatorInternal() {
|
||||
findStringDecrypterMethod();
|
||||
}
|
||||
|
||||
void findStringDecrypterMethod() {
|
||||
foreach (var type in module.Types) {
|
||||
if (!string.IsNullOrEmpty(type.Namespace))
|
||||
continue;
|
||||
if (DotNetUtils.findFieldType(type, "System.IO.BinaryReader", true) == null)
|
||||
continue;
|
||||
if (DotNetUtils.findFieldType(type, "System.Collections.Generic.Dictionary`2<System.Int32,System.String>", true) == null)
|
||||
continue;
|
||||
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.IsStatic && method.HasBody && method.MethodReturnType.ReturnType.FullName == "System.String" &&
|
||||
method.Parameters.Count == 1 && method.Parameters[0].ParameterType.FullName == "System.Int32") {
|
||||
foreach (var instr in method.Body.Instructions) {
|
||||
if (instr.OpCode != OpCodes.Callvirt)
|
||||
continue;
|
||||
var calledMethod = instr.Operand as MethodReference;
|
||||
if (calledMethod != null && calledMethod.FullName == "System.IO.Stream System.Reflection.Assembly::GetManifestResourceStream(System.String)") {
|
||||
decryptStringType = type;
|
||||
decryptStringMethod = method;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void deobfuscateEnd() {
|
||||
if (Operations.DecryptStrings == OpDecryptString.Dynamic) {
|
||||
addTypeToBeRemoved(decryptStringType, "String decrypter type");
|
||||
findPossibleNamesToRemove(decryptStringMethod);
|
||||
}
|
||||
addResources("Encrypted strings");
|
||||
|
||||
base.deobfuscateEnd();
|
||||
}
|
||||
|
||||
public override IEnumerable<string> getStringDecrypterMethods() {
|
||||
var list = new List<string>();
|
||||
if (decryptStringMethod != null)
|
||||
list.Add(decryptStringMethod.MetadataToken.ToInt32().ToString("X8"));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.deobfuscators {
|
||||
class ExceptionLoggerRemover {
|
||||
Dictionary<MethodReference, bool> exceptionLoggerMethods = new Dictionary<MethodReference, bool>();
|
||||
|
||||
public int NumRemovedExceptionLoggers { get; set; }
|
||||
|
||||
public void add(MethodDefinition exceptionLogger) {
|
||||
exceptionLoggerMethods[exceptionLogger] = true;
|
||||
}
|
||||
|
||||
bool find(Blocks blocks, out TryBlock tryBlock) {
|
||||
tryBlock = null;
|
||||
|
||||
foreach (var bb in blocks.MethodBlocks.BaseBlocks) {
|
||||
tryBlock = bb as TryBlock;
|
||||
if (tryBlock == null)
|
||||
continue;
|
||||
if (tryBlock.TryHandlerBlocks.Count != 1)
|
||||
continue;
|
||||
var catchBlock = tryBlock.TryHandlerBlocks[0];
|
||||
if (catchBlock.HandlerType != ExceptionHandlerType.Catch ||
|
||||
!MemberReferenceHelper.verifyType(catchBlock.CatchType, "mscorlib", "System.Exception")) {
|
||||
continue;
|
||||
}
|
||||
if (catchBlock.BaseBlocks.Count != 1)
|
||||
continue;
|
||||
var handlerBlock = catchBlock.BaseBlocks[0] as HandlerBlock;
|
||||
if (handlerBlock == null)
|
||||
continue;
|
||||
|
||||
int calls = 0;
|
||||
Instr callInstr = null;
|
||||
bool failed = false;
|
||||
foreach (var bb2 in handlerBlock.BaseBlocks) {
|
||||
var block = bb2 as Block;
|
||||
if (block == null) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
foreach (var instr in block.Instructions) {
|
||||
switch (instr.OpCode.Code) {
|
||||
case Code.Call:
|
||||
case Code.Calli:
|
||||
case Code.Callvirt:
|
||||
calls++;
|
||||
callInstr = instr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (failed || calls != 1 || callInstr.OpCode.Code != Code.Call)
|
||||
continue;
|
||||
var calledMethod = callInstr.Operand as MethodReference;
|
||||
if (calledMethod == null)
|
||||
continue;
|
||||
if (!exceptionLoggerMethods.ContainsKey(calledMethod))
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool remove(Blocks blocks) {
|
||||
if (exceptionLoggerMethods.Count == 0)
|
||||
return false;
|
||||
|
||||
TryBlock tryBlock;
|
||||
if (!find(blocks, out tryBlock))
|
||||
return false;
|
||||
|
||||
blocks.MethodBlocks.removeTryBlock(tryBlock);
|
||||
NumRemovedExceptionLoggers++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.deobfuscators {
|
||||
interface IDeobfuscatedFile : ISimpleDeobfuscator {
|
||||
void createAssemblyFile(byte[] data, string assemblyName);
|
||||
void stringDecryptersAdded();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.deobfuscators {
|
||||
interface IDeobfuscatorOptions {
|
||||
bool RenameResourcesInCode { get; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum StringFeatures {
|
||||
AllowNoDecryption,
|
||||
AllowStaticDecryption,
|
||||
AllowDynamicDecryption,
|
||||
AllowAll = AllowNoDecryption | AllowStaticDecryption | AllowDynamicDecryption,
|
||||
}
|
||||
|
||||
interface IDeobfuscator {
|
||||
string Type { get; }
|
||||
string Name { get; }
|
||||
Func<string, bool> IsValidName { get; }
|
||||
IDeobfuscatorOptions TheOptions { get; }
|
||||
IOperations Operations { get; set; }
|
||||
StringFeatures StringFeatures { get; set; }
|
||||
|
||||
// This is non-null only in init(), detect() and deobfuscateBegin().
|
||||
IDeobfuscatedFile DeobfuscatedFile { get; set; }
|
||||
|
||||
void init(ModuleDefinition module, IList<MemberReference> memberReferences);
|
||||
|
||||
// Returns 0 if it's not detected, or > 0 if detected (higher value => more likely true)
|
||||
int detect();
|
||||
|
||||
// Called before all other deobfuscation methods
|
||||
void deobfuscateBegin();
|
||||
|
||||
// Called before the code is deobfuscated
|
||||
void deobfuscateMethodBegin(Blocks blocks);
|
||||
|
||||
// Called after deobfuscateMethodBegin() but before deobfuscateMethodEnd()
|
||||
void deobfuscateStrings(Blocks blocks);
|
||||
|
||||
// Called after the code has been deobfuscated
|
||||
void deobfuscateMethodEnd(Blocks blocks);
|
||||
|
||||
// Called after all deobfuscation methods
|
||||
void deobfuscateEnd();
|
||||
|
||||
// Called to get method token / pattern of string decrypters
|
||||
IEnumerable<string> getStringDecrypterMethods();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.deobfuscators {
|
||||
interface IDeobfuscatorInfo {
|
||||
string Type { get; }
|
||||
IDeobfuscator createDeobfuscator();
|
||||
IEnumerable<Option> getOptions();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
|
||||
namespace de4dot.deobfuscators {
|
||||
interface ISimpleDeobfuscator {
|
||||
void deobfuscate(MethodDefinition method);
|
||||
void decryptStrings(MethodDefinition method, IDeobfuscator deob);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.deobfuscators {
|
||||
enum OpDecryptString {
|
||||
None,
|
||||
Static,
|
||||
Dynamic,
|
||||
}
|
||||
|
||||
interface IOperations {
|
||||
bool RenameSymbols { get; }
|
||||
bool KeepObfuscatorTypes { get; }
|
||||
OpDecryptString DecryptStrings { get; }
|
||||
}
|
||||
|
||||
class Operations : IOperations {
|
||||
public bool RenameSymbols { get; set; }
|
||||
public bool KeepObfuscatorTypes { get; set; }
|
||||
public OpDecryptString DecryptStrings { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
namespace de4dot.deobfuscators {
|
||||
abstract class ProxyDelegateFinderBase {
|
||||
ModuleDefinition module;
|
||||
IList<MemberReference> memberReferences;
|
||||
MethodDefinition delegateCreatorMethod;
|
||||
Dictionary<TypeDefinition, bool> delegateTypesDict = new Dictionary<TypeDefinition, bool>();
|
||||
Dictionary<FieldReferenceAndDeclaringTypeKey, DelegateInfo> fieldToDelegateInfo = new Dictionary<FieldReferenceAndDeclaringTypeKey, DelegateInfo>();
|
||||
|
||||
class DelegateInfo {
|
||||
public MethodReference methodRef; // Method we should call
|
||||
public FieldDefinition field; // Field holding the Delegate instance
|
||||
public bool isVirtual;
|
||||
public DelegateInfo(FieldDefinition field, MethodReference methodRef, bool isVirtual) {
|
||||
this.field = field;
|
||||
this.methodRef = methodRef;
|
||||
this.isVirtual = isVirtual;
|
||||
}
|
||||
}
|
||||
|
||||
public int RemovedDelegateCreatorCalls { get; set; }
|
||||
public IEnumerable<TypeDefinition> DelegateTypes {
|
||||
get { return delegateTypesDict.Keys; }
|
||||
}
|
||||
public MethodDefinition DelegateCreatorMethod {
|
||||
get { return delegateCreatorMethod; }
|
||||
}
|
||||
|
||||
public bool Detected {
|
||||
get { return delegateCreatorMethod != null; }
|
||||
}
|
||||
|
||||
public ProxyDelegateFinderBase(ModuleDefinition module, IList<MemberReference> memberReferences) {
|
||||
this.module = module;
|
||||
this.memberReferences = memberReferences;
|
||||
}
|
||||
|
||||
public void setDelegateCreatorMethod(MethodDefinition delegateCreatorMethod) {
|
||||
this.delegateCreatorMethod = delegateCreatorMethod;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
if (delegateCreatorMethod == null)
|
||||
return;
|
||||
|
||||
Log.v("Finding all proxy delegates");
|
||||
foreach (var type in module.Types) {
|
||||
if (type.BaseType == null || type.BaseType.FullName != "System.MulticastDelegate")
|
||||
continue;
|
||||
var cctor = findMethod(type, ".cctor");
|
||||
if (cctor == null || !cctor.HasBody)
|
||||
continue;
|
||||
if (!type.HasFields)
|
||||
continue;
|
||||
|
||||
var instrs = cctor.Body.Instructions;
|
||||
if (instrs.Count != 3)
|
||||
continue;
|
||||
if (!DotNetUtils.isLdcI4(instrs[0].OpCode.Code))
|
||||
continue;
|
||||
if (instrs[1].OpCode != OpCodes.Call || instrs[1].Operand != delegateCreatorMethod)
|
||||
continue;
|
||||
if (instrs[2].OpCode != OpCodes.Ret)
|
||||
continue;
|
||||
|
||||
int delegateToken = 0x02000001 + DotNetUtils.getLdcI4Value(instrs[0]);
|
||||
if (type.MetadataToken.ToInt32() != delegateToken) {
|
||||
Log.w("Delegate token is not current type");
|
||||
continue;
|
||||
}
|
||||
|
||||
Log.v("Found proxy delegate: {0} ({1:X8})", type, type.MetadataToken.ToUInt32());
|
||||
RemovedDelegateCreatorCalls++;
|
||||
Log.indent();
|
||||
foreach (var field in type.Fields) {
|
||||
if (!field.IsStatic || field.IsPublic)
|
||||
continue;
|
||||
|
||||
int methodIndex;
|
||||
bool isVirtual;
|
||||
getCallInfo(field, out methodIndex, out isVirtual);
|
||||
if (methodIndex >= memberReferences.Count)
|
||||
throw new ApplicationException(string.Format("methodIndex ({0}) >= memberReferences.Count ({1})", methodIndex, memberReferences.Count));
|
||||
|
||||
var methodRef = memberReferences[methodIndex] as MethodReference;
|
||||
if (methodRef == null)
|
||||
throw new ApplicationException("methodRef is null");
|
||||
fieldToDelegateInfo[new FieldReferenceAndDeclaringTypeKey(field)] = new DelegateInfo(field, methodRef, isVirtual);
|
||||
Log.v("Field: {0}, Virtual: {1}, Method: {2}, RID: {3}", field.Name, isVirtual, methodRef, methodIndex + 1);
|
||||
}
|
||||
Log.deIndent();
|
||||
delegateTypesDict[type] = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void getCallInfo(FieldDefinition field, out int methodIndex, out bool isVirtual);
|
||||
|
||||
MethodDefinition findMethod(TypeDefinition type, string name) {
|
||||
if (!type.HasMethods)
|
||||
return null;
|
||||
foreach (var method in type.Methods) {
|
||||
if (method.Name == name)
|
||||
return method;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
DelegateInfo getDelegateInfo(FieldReference field) {
|
||||
if (field == null)
|
||||
return null;
|
||||
DelegateInfo di;
|
||||
if (fieldToDelegateInfo.TryGetValue(new FieldReferenceAndDeclaringTypeKey(field), out di))
|
||||
return di;
|
||||
return null;
|
||||
}
|
||||
|
||||
class BlockInstr {
|
||||
public Block Block { get; set; }
|
||||
public int Index { get; set; }
|
||||
}
|
||||
|
||||
class RemoveInfo {
|
||||
public int Index { get; set; }
|
||||
public DelegateInfo DelegateInfo { get; set; }
|
||||
public bool IsCall {
|
||||
get { return DelegateInfo != null; }
|
||||
}
|
||||
}
|
||||
|
||||
public void deobfuscate(Blocks blocks) {
|
||||
var removeInfos = new Dictionary<Block, List<RemoveInfo>>();
|
||||
|
||||
var allBlocks = blocks.MethodBlocks.getAllBlocks();
|
||||
foreach (var block in allBlocks) {
|
||||
var instrs = block.Instructions;
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var ldsfld = instrs[i];
|
||||
if (ldsfld.OpCode != OpCodes.Ldsfld)
|
||||
continue;
|
||||
var di = getDelegateInfo(ldsfld.Operand as FieldReference);
|
||||
if (di == null)
|
||||
continue;
|
||||
|
||||
var visited = new Dictionary<Block, bool>();
|
||||
var callInfo = findProxyCall(di, block, i, visited, 1);
|
||||
if (callInfo != null) {
|
||||
add(removeInfos, block, i, null);
|
||||
add(removeInfos, callInfo.Block, callInfo.Index, di);
|
||||
}
|
||||
else {
|
||||
Log.w("Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})",
|
||||
blocks.Method, blocks.Method.MetadataToken.ToInt32(),
|
||||
di.field.DeclaringType, di.field.DeclaringType.MetadataToken.ToInt32());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var block in removeInfos.Keys) {
|
||||
var list = removeInfos[block];
|
||||
var removeIndexes = new List<int>(list.Count);
|
||||
foreach (var info in list) {
|
||||
if (info.IsCall) {
|
||||
var opcode = info.DelegateInfo.isVirtual ? OpCodes.Callvirt : OpCodes.Call;
|
||||
var newInstr = Instruction.Create(opcode, info.DelegateInfo.methodRef);
|
||||
block.replace(info.Index, 1, newInstr);
|
||||
}
|
||||
else
|
||||
removeIndexes.Add(info.Index);
|
||||
}
|
||||
block.remove(removeIndexes);
|
||||
}
|
||||
|
||||
fixBrokenCalls(blocks.Method, allBlocks);
|
||||
}
|
||||
|
||||
void add(Dictionary<Block, List<RemoveInfo>> removeInfos, Block block, int index, DelegateInfo di) {
|
||||
List<RemoveInfo> list;
|
||||
if (!removeInfos.TryGetValue(block, out list))
|
||||
removeInfos[block] = list = new List<RemoveInfo>();
|
||||
list.Add(new RemoveInfo {
|
||||
Index = index,
|
||||
DelegateInfo = di,
|
||||
});
|
||||
}
|
||||
|
||||
BlockInstr findProxyCall(DelegateInfo di, Block block, int index, Dictionary<Block, bool> visited, int stack) {
|
||||
if (visited.ContainsKey(block))
|
||||
return null;
|
||||
if (index <= 0)
|
||||
visited[block] = true;
|
||||
|
||||
var instrs = block.Instructions;
|
||||
for (int i = index + 1; i < instrs.Count; i++) {
|
||||
if (stack <= 0)
|
||||
return null;
|
||||
var instr = instrs[i];
|
||||
DotNetUtils.updateStack(instr.Instruction, ref stack);
|
||||
if (stack < 0)
|
||||
return null;
|
||||
|
||||
if (instr.OpCode != OpCodes.Call && instr.OpCode != OpCodes.Callvirt)
|
||||
continue;
|
||||
var method = DotNetUtils.getMethod(module, instr.Operand as MethodReference);
|
||||
if (method == null)
|
||||
continue;
|
||||
if (stack != (DotNetUtils.hasReturnValue(method) ? 1 : 0))
|
||||
continue;
|
||||
if (method.DeclaringType != di.field.DeclaringType)
|
||||
continue;
|
||||
|
||||
return new BlockInstr {
|
||||
Block = block,
|
||||
Index = i,
|
||||
};
|
||||
}
|
||||
if (stack <= 0)
|
||||
return null;
|
||||
|
||||
foreach (var target in block.getTargets()) {
|
||||
var info = findProxyCall(di, target, -1, visited, stack);
|
||||
if (info != null)
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// The obfuscator could be buggy and call a proxy delegate without pushing the
|
||||
// instance field. SA has done it, so let's fix it.
|
||||
void fixBrokenCalls(MethodDefinition obfuscatedMethod, IList<Block> allBlocks) {
|
||||
foreach (var block in allBlocks) {
|
||||
var instrs = block.Instructions;
|
||||
for (int i = 0; i < instrs.Count; i++) {
|
||||
var call = instrs[i];
|
||||
if (call.OpCode != OpCodes.Call && call.OpCode != OpCodes.Callvirt)
|
||||
continue;
|
||||
var methodRef = call.Operand as MethodReference;
|
||||
if (methodRef.Name != "Invoke")
|
||||
continue;
|
||||
var method = DotNetUtils.getMethod(module, methodRef);
|
||||
if (method == null)
|
||||
continue;
|
||||
var declaringType = DotNetUtils.getType(module, method.DeclaringType);
|
||||
if (declaringType == null)
|
||||
continue;
|
||||
if (!delegateTypesDict.ContainsKey(declaringType))
|
||||
continue;
|
||||
|
||||
// Oooops!!! The obfuscator is buggy. Well, let's hope it is, or it's my code. ;)
|
||||
|
||||
Log.w("Holy obfuscator bugs, Batman! Found a proxy delegate call with no instance push in {0:X8}. Replacing it with a throw...", obfuscatedMethod.MetadataToken.ToInt32());
|
||||
block.insert(i, Instruction.Create(OpCodes.Ldnull));
|
||||
block.replace(i + 1, 1, Instruction.Create(OpCodes.Throw));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
|
||||
namespace de4dot.deobfuscators.SmartAssembly {
|
||||
class AssemblyResolver {
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
AssemblyResolverInfo assemblyResolverInfo;
|
||||
|
||||
public AssemblyResolver(ResourceDecrypter resourceDecrypter, AssemblyResolverInfo assemblyResolverInfo) {
|
||||
this.resourceDecrypter = resourceDecrypter;
|
||||
this.assemblyResolverInfo = assemblyResolverInfo;
|
||||
}
|
||||
|
||||
public bool resolveResources() {
|
||||
return assemblyResolverInfo.resolveResources();
|
||||
}
|
||||
|
||||
public bool canDecryptResource(EmbeddedResource resource) {
|
||||
var info = getEmbeddedAssemblyInfo(resource);
|
||||
if (info == null || !info.isCompressed)
|
||||
return true;
|
||||
return resourceDecrypter.CanDecrypt;
|
||||
}
|
||||
|
||||
public IEnumerable<Tuple<AssemblyResolverInfo.EmbeddedAssemblyInfo, byte[]>> getDecryptedResources() {
|
||||
var returned = new Dictionary<Resource, bool>();
|
||||
foreach (var info in assemblyResolverInfo.EmbeddedAssemblyInfos) {
|
||||
if (info.resource == null) {
|
||||
Log.w("Could not find embedded resource {0}", Utils.toCsharpString(info.resourceName));
|
||||
continue;
|
||||
}
|
||||
if (returned.ContainsKey(info.resource))
|
||||
continue;
|
||||
returned[info.resource] = true;
|
||||
|
||||
yield return new Tuple<AssemblyResolverInfo.EmbeddedAssemblyInfo, byte[]> {
|
||||
Item1 = info,
|
||||
Item2 = decryptResource(info),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
AssemblyResolverInfo.EmbeddedAssemblyInfo getEmbeddedAssemblyInfo(EmbeddedResource resource) {
|
||||
foreach (var info in assemblyResolverInfo.EmbeddedAssemblyInfos) {
|
||||
if (info.resource == resource)
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public byte[] removeDecryptedResource(EmbeddedResource resource) {
|
||||
if (resource == null)
|
||||
return null;
|
||||
|
||||
var info = getEmbeddedAssemblyInfo(resource);
|
||||
if (info == null)
|
||||
return null;
|
||||
|
||||
var data = decryptResource(info);
|
||||
if (!assemblyResolverInfo.EmbeddedAssemblyInfos.Remove(info))
|
||||
throw new ApplicationException(string.Format("Could not remove resource {0}", Utils.toCsharpString(info.resourceName)));
|
||||
return data;
|
||||
}
|
||||
|
||||
byte[] decryptResource(AssemblyResolverInfo.EmbeddedAssemblyInfo info) {
|
||||
if (info.isCompressed)
|
||||
return resourceDecrypter.decrypt(info.resource);
|
||||
else
|
||||
return info.resource.GetResourceData();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.Text;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.deobfuscators.SmartAssembly {
|
||||
class AssemblyResolverInfo : ResolverInfoBase {
|
||||
TypeDefinition simpleZipType;
|
||||
List<EmbeddedAssemblyInfo> embeddedAssemblyInfos = new List<EmbeddedAssemblyInfo>();
|
||||
|
||||
public class EmbeddedAssemblyInfo {
|
||||
public string assemblyName;
|
||||
public string simpleName;
|
||||
public string resourceName;
|
||||
public EmbeddedResource resource;
|
||||
public bool isCompressed = false;
|
||||
public bool isTempFile = false;
|
||||
public string flags = "";
|
||||
|
||||
public override string ToString() {
|
||||
return assemblyName ?? base.ToString();
|
||||
}
|
||||
|
||||
public static EmbeddedAssemblyInfo create(ModuleDefinition module, string encName, string rsrcName) {
|
||||
var info = new EmbeddedAssemblyInfo();
|
||||
|
||||
try {
|
||||
if (encName == "" || Convert.ToBase64String(Convert.FromBase64String(encName)) != encName)
|
||||
return null;
|
||||
}
|
||||
catch (FormatException) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (rsrcName.Length > 0 && rsrcName[0] == '[') {
|
||||
int i = rsrcName.IndexOf(']');
|
||||
if (i < 0)
|
||||
return null;
|
||||
info.flags = rsrcName.Substring(1, i - 1);
|
||||
info.isTempFile = info.flags.IndexOf('t') >= 0;
|
||||
info.isCompressed = info.flags.IndexOf('z') >= 0;
|
||||
rsrcName = rsrcName.Substring(i + 1);
|
||||
}
|
||||
if (rsrcName == "")
|
||||
return null;
|
||||
|
||||
info.assemblyName = Encoding.UTF8.GetString(Convert.FromBase64String(encName));
|
||||
info.resourceName = rsrcName;
|
||||
info.resource = DotNetUtils.getResource(module, rsrcName) as EmbeddedResource;
|
||||
info.simpleName = SA_Utils.getAssemblySimpleName(info.assemblyName);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
public TypeDefinition SimpleZipType {
|
||||
get { return simpleZipType; }
|
||||
}
|
||||
|
||||
public IList<EmbeddedAssemblyInfo> EmbeddedAssemblyInfos {
|
||||
get { return embeddedAssemblyInfos; }
|
||||
}
|
||||
|
||||
public AssemblyResolverInfo(ModuleDefinition module, ISimpleDeobfuscator simpleDeobfuscator, IDeobfuscator deob)
|
||||
: base(module, simpleDeobfuscator, deob) {
|
||||
}
|
||||
|
||||
public bool resolveResources() {
|
||||
bool ok = true;
|
||||
|
||||
foreach (var info in embeddedAssemblyInfos) {
|
||||
if (info.resource != null)
|
||||
continue;
|
||||
info.resource = DotNetUtils.getResource(module, info.resourceName) as EmbeddedResource;
|
||||
if (info.resource == null)
|
||||
ok = false;
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
protected override bool checkResolverType(TypeDefinition type) {
|
||||
if (DotNetUtils.findFieldType(type, "System.Collections.Hashtable", true) != null)
|
||||
return true;
|
||||
|
||||
foreach (var field in type.Fields) {
|
||||
if (!DotNetUtils.isDelegateType(DotNetUtils.getType(module, field.FieldType)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool checkHandlerMethod(MethodDefinition method) {
|
||||
if (!method.IsStatic || !method.HasBody)
|
||||
return false;
|
||||
|
||||
var infos = new List<EmbeddedAssemblyInfo>();
|
||||
foreach (var s in DotNetUtils.getCodeStrings(method)) {
|
||||
if (string.IsNullOrEmpty(s))
|
||||
continue;
|
||||
if (!initInfos(infos, s.Split(',')))
|
||||
continue;
|
||||
|
||||
embeddedAssemblyInfos = infos;
|
||||
findSimpleZipType(method);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool initInfos(IList<EmbeddedAssemblyInfo> list, string[] strings) {
|
||||
list.Clear();
|
||||
if (strings.Length % 2 == 1)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < strings.Length; i += 2) {
|
||||
var info = EmbeddedAssemblyInfo.create(module, strings[i], strings[i + 1]);
|
||||
if (info == null)
|
||||
return false;
|
||||
list.Add(info);
|
||||
}
|
||||
|
||||
Log.v("Found embedded assemblies:");
|
||||
Log.indent();
|
||||
foreach (var info in list)
|
||||
Log.v("{0}", info.assemblyName);
|
||||
Log.deIndent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void findSimpleZipType(MethodDefinition method) {
|
||||
if (method == null || !method.HasBody)
|
||||
return;
|
||||
foreach (var call in method.Body.Instructions) {
|
||||
if (call.OpCode.Code != Code.Call)
|
||||
continue;
|
||||
var calledMethod = call.Operand as MethodReference;
|
||||
if (calledMethod == null)
|
||||
continue;
|
||||
if (!SimpleZipInfo.isSimpleZipDecryptMethod_QuickCheck(module, calledMethod, out simpleZipType))
|
||||
continue;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
|
||||
namespace de4dot.deobfuscators.SmartAssembly {
|
||||
class AutomatedErrorReportingFinder {
|
||||
ModuleDefinition module;
|
||||
ExceptionLoggerRemover exceptionLoggerRemover = new ExceptionLoggerRemover();
|
||||
TypeDefinition automatedErrorReportingType;
|
||||
|
||||
public ExceptionLoggerRemover ExceptionLoggerRemover {
|
||||
get { return exceptionLoggerRemover; }
|
||||
}
|
||||
|
||||
public TypeDefinition AutomatedErrorReportingType {
|
||||
get { return automatedErrorReportingType; }
|
||||
}
|
||||
|
||||
public bool Detected {
|
||||
get { return automatedErrorReportingType != null; }
|
||||
}
|
||||
|
||||
public AutomatedErrorReportingFinder(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
}
|
||||
|
||||
public void find() {
|
||||
foreach (var type in module.Types) {
|
||||
if (detectAutomatedErrorReportingType(type))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool detectAutomatedErrorReportingType(TypeDefinition type) {
|
||||
if (automatedErrorReportingType != null)
|
||||
return false;
|
||||
|
||||
const int MIN_HELPER_METHODS = 6;
|
||||
|
||||
var methods = new List<MethodDefinition>();
|
||||
MethodDefinition mainMethod = null;
|
||||
foreach (var method in type.Methods) {
|
||||
if (isAutomatedErrorReportingMethodHelper(method))
|
||||
methods.Add(method);
|
||||
else if (isAutomatedErrorReportingMethod(method))
|
||||
mainMethod = method;
|
||||
}
|
||||
if (mainMethod == null || methods.Count < MIN_HELPER_METHODS)
|
||||
return false;
|
||||
|
||||
methods.Sort((a, b) => {
|
||||
if (a.Parameters.Count < b.Parameters.Count) return -1;
|
||||
if (a.Parameters.Count > b.Parameters.Count) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
for (int i = 0; i < methods.Count; i++) {
|
||||
var method = methods[i];
|
||||
if (method.Parameters.Count != i + 1)
|
||||
return false;
|
||||
var methodCalls = DotNetUtils.getMethodCallCounts(method);
|
||||
if (methodCalls.count(mainMethod.FullName) != 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
exceptionLoggerRemover.add(mainMethod);
|
||||
foreach (var method in methods)
|
||||
exceptionLoggerRemover.add(method);
|
||||
automatedErrorReportingType = type;
|
||||
|
||||
initUnhandledExceptionFilterMethods();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isAutomatedErrorReportingMethodHelper(MethodDefinition method) {
|
||||
if (!method.HasBody || !method.IsStatic || method.Name == ".ctor")
|
||||
return false;
|
||||
if (DotNetUtils.hasReturnValue(method))
|
||||
return false;
|
||||
if (method.Parameters.Count == 0)
|
||||
return false;
|
||||
if (method.Parameters[0].ParameterType.FullName != "System.Exception")
|
||||
return false;
|
||||
for (int i = 1; i < method.Parameters.Count; i++) {
|
||||
if (method.Parameters[i].ParameterType.FullName != "System.Object")
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool isAutomatedErrorReportingMethod(MethodDefinition method) {
|
||||
if (!method.HasBody || !method.IsStatic || method.Name == ".ctor")
|
||||
return false;
|
||||
if (!DotNetUtils.isMethod(method, "System.Void", "(System.Exception,System.Object[])"))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void initUnhandledExceptionFilterMethods() {
|
||||
var main = module.EntryPoint;
|
||||
if (main == null || !main.HasBody)
|
||||
return;
|
||||
if (!main.Body.HasExceptionHandlers)
|
||||
return;
|
||||
|
||||
MethodDefinition mainExceptionHandlerMethod = null;
|
||||
var instructions = main.Body.Instructions;
|
||||
for (int i = 0; i < instructions.Count; i++) {
|
||||
var call = instructions[i];
|
||||
if (call.OpCode != OpCodes.Call)
|
||||
continue;
|
||||
var method = getMainExceptionHandlerMethod(call.Operand as MethodReference);
|
||||
if (method == null)
|
||||
continue;
|
||||
|
||||
mainExceptionHandlerMethod = method; // Use the last one we find
|
||||
}
|
||||
|
||||
if (mainExceptionHandlerMethod != null)
|
||||
exceptionLoggerRemover.add(mainExceptionHandlerMethod);
|
||||
}
|
||||
|
||||
MethodDefinition getMainExceptionHandlerMethod(MethodReference methodReference) {
|
||||
var type = DotNetUtils.getType(module, methodReference.DeclaringType);
|
||||
var method = DotNetUtils.getMethod(type, methodReference);
|
||||
if (method == null || !method.IsStatic)
|
||||
return null;
|
||||
|
||||
if (method.Parameters.Count < 2)
|
||||
return null;
|
||||
if (DotNetUtils.hasReturnValue(method))
|
||||
return null;
|
||||
if (method.Parameters[0].ParameterType.ToString() != "System.Exception")
|
||||
return null;
|
||||
if (method.Parameters[method.Parameters.Count - 1].ParameterType.ToString() != "System.Object[]")
|
||||
return null;
|
||||
|
||||
return method;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
Copyright (C) 2011 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.IO;
|
||||
using System.Text;
|
||||
using Mono.Cecil;
|
||||
using Mono.Cecil.Cil;
|
||||
using de4dot.blocks;
|
||||
|
||||
// SmartAssembly can add so much junk that it's very difficult to find and remove all of it.
|
||||
// I remove some safe types that are almost guaranteed not to have any references in the code.
|
||||
|
||||
namespace de4dot.deobfuscators.SmartAssembly {
|
||||
class DeobfuscatorInfo : DeobfuscatorInfoBase {
|
||||
BoolOption removeAutomatedErrorReporting;
|
||||
BoolOption removeTamperProtection;
|
||||
BoolOption removeMemoryManager;
|
||||
|
||||
public DeobfuscatorInfo()
|
||||
: base("sa") {
|
||||
removeAutomatedErrorReporting = new BoolOption(null, makeArgName("error"), "Remove automated error reporting code", true);
|
||||
removeTamperProtection = new BoolOption(null, makeArgName("tamper"), "Remove tamper protection code", true);
|
||||
removeMemoryManager = new BoolOption(null, makeArgName("memory"), "Remove memory manager code", true);
|
||||
}
|
||||
|
||||
internal static string ObfuscatorType {
|
||||
get { return "SmartAssembly"; }
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override IDeobfuscator createDeobfuscator() {
|
||||
return new Deobfuscator(new Deobfuscator.Options {
|
||||
ValidNameRegex = validNameRegex.get(),
|
||||
RemoveAutomatedErrorReporting = removeAutomatedErrorReporting.get(),
|
||||
RemoveTamperProtection = removeTamperProtection.get(),
|
||||
RemoveMemoryManager = removeMemoryManager.get(),
|
||||
});
|
||||
}
|
||||
|
||||
protected override IEnumerable<Option> getOptionsInternal() {
|
||||
return new List<Option>() {
|
||||
removeAutomatedErrorReporting,
|
||||
removeTamperProtection,
|
||||
removeMemoryManager,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Deobfuscator : DeobfuscatorBase {
|
||||
Options options;
|
||||
string obfuscatorName = "SmartAssembly";
|
||||
bool foundSmartAssemblyAttribute = false;
|
||||
|
||||
IList<StringDecrypterInfo> stringDecrypterInfos = new List<StringDecrypterInfo>();
|
||||
IList<StringDecrypter> stringDecrypters = new List<StringDecrypter>();
|
||||
ResourceDecrypterInfo resourceDecrypterInfo;
|
||||
ResourceDecrypter resourceDecrypter;
|
||||
AssemblyResolverInfo assemblyResolverInfo;
|
||||
AssemblyResolver assemblyResolver;
|
||||
ResourceResolverInfo resourceResolverInfo;
|
||||
ResourceResolver resourceResolver;
|
||||
MemoryManagerInfo memoryManagerInfo;
|
||||
|
||||
ProxyDelegateFinder proxyDelegateFinder;
|
||||
AutomatedErrorReportingFinder automatedErrorReportingFinder;
|
||||
TamperProtectionRemover tamperProtectionRemover;
|
||||
|
||||
internal class Options : OptionsBase {
|
||||
public bool RemoveAutomatedErrorReporting { get; set; }
|
||||
public bool RemoveTamperProtection { get; set; }
|
||||
public bool RemoveMemoryManager { get; set; }
|
||||
}
|
||||
|
||||
public override string Type {
|
||||
get { return DeobfuscatorInfo.ObfuscatorType; }
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return obfuscatorName; }
|
||||
}
|
||||
|
||||
public Deobfuscator(Options options)
|
||||
: base(options) {
|
||||
this.options = options;
|
||||
StringFeatures = StringFeatures.AllowStaticDecryption;
|
||||
}
|
||||
|
||||
public override void init(ModuleDefinition module, IList<MemberReference> memberReferences) {
|
||||
base.init(module, memberReferences);
|
||||
proxyDelegateFinder = new ProxyDelegateFinder(module, memberReferences);
|
||||
automatedErrorReportingFinder = new AutomatedErrorReportingFinder(module);
|
||||
tamperProtectionRemover = new TamperProtectionRemover(module);
|
||||
}
|
||||
|
||||
public override int detect() {
|
||||
scanForObfuscator();
|
||||
|
||||
int val = 0;
|
||||
|
||||
if (foundSmartAssemblyAttribute)
|
||||
val += 100;
|
||||
|
||||
// Since we don't remove this type, we will detect it even when we've deobfuscated
|
||||
// an assembly. Don't use this code for now. When the type is removed, this code
|
||||
// should be re-enabled.
|
||||
// if (automatedErrorReportingFinder.Detected)
|
||||
// val += 10;
|
||||
|
||||
if (memoryManagerInfo.Detected)
|
||||
val += 10;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
protected override void scanForObfuscatorInternal() {
|
||||
findSmartAssemblyAttributes();
|
||||
findAutomatedErrorReportingType();
|
||||
memoryManagerInfo = new MemoryManagerInfo(module);
|
||||
proxyDelegateFinder.findDelegateCreator(module);
|
||||
}
|
||||
|
||||
void findSmartAssemblyAttributes() {
|
||||
foreach (var type in module.Types) {
|
||||
if (type.FullName.StartsWith("SmartAssembly.Attributes.PoweredByAttribute", StringComparison.Ordinal)) {
|
||||
foundSmartAssemblyAttribute = true;
|
||||
addAttributeToBeRemoved(type, "Obfuscator attribute");
|
||||
initializeVersion(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initializeVersion(TypeDefinition attr) {
|
||||
var s = DotNetUtils.getCustomArgAsString(getAssemblyAttribute(attr), 0);
|
||||
if (s == null)
|
||||
return;
|
||||
|
||||
var val = System.Text.RegularExpressions.Regex.Match(s, @"^Powered by (SmartAssembly \d+\.\d+\.\d+\.\d+)$");
|
||||
if (val.Groups.Count < 2)
|
||||
return;
|
||||
obfuscatorName = val.Groups[1].ToString();
|
||||
}
|
||||
|
||||
void findAutomatedErrorReportingType() {
|
||||
automatedErrorReportingFinder.find();
|
||||
}
|
||||
|
||||
public override void deobfuscateBegin() {
|
||||
base.deobfuscateBegin();
|
||||
if (options.RemoveMemoryManager)
|
||||
addCctorInitCallToBeRemoved(memoryManagerInfo.CctorInitMethod);
|
||||
initDecrypters();
|
||||
proxyDelegateFinder.find();
|
||||
}
|
||||
|
||||
void initDecrypters() {
|
||||
assemblyResolverInfo = new AssemblyResolverInfo(module, DeobfuscatedFile, this);
|
||||
resourceDecrypterInfo = new ResourceDecrypterInfo(module, assemblyResolverInfo.SimpleZipType, DeobfuscatedFile);
|
||||
resourceResolverInfo = new ResourceResolverInfo(module, DeobfuscatedFile, this);
|
||||
resourceDecrypter = new ResourceDecrypter(resourceDecrypterInfo);
|
||||
assemblyResolver = new AssemblyResolver(resourceDecrypter, assemblyResolverInfo);
|
||||
resourceResolver = new ResourceResolver(module, assemblyResolver, resourceResolverInfo);
|
||||
|
||||
initStringDecrypterInfos();
|
||||
assemblyResolverInfo.findTypes();
|
||||
resourceResolverInfo.findTypes();
|
||||
|
||||
addCctorInitCallToBeRemoved(assemblyResolverInfo.CallResolverMethod);
|
||||
addCctorInitCallToBeRemoved(resourceResolverInfo.CallResolverMethod);
|
||||
|
||||
resourceDecrypterInfo.setSimpleZipType(getGlobalSimpleZipType(), DeobfuscatedFile);
|
||||
|
||||
if (!decryptResources())
|
||||
throw new ApplicationException("Could not decrypt resources");
|
||||
|
||||
dumpEmbeddedAssemblies();
|
||||
}
|
||||
|
||||
void dumpEmbeddedAssemblies() {
|
||||
assemblyResolver.resolveResources();
|
||||
foreach (var tuple in assemblyResolver.getDecryptedResources()) {
|
||||
DeobfuscatedFile.createAssemblyFile(tuple.Item2, tuple.Item1.simpleName);
|
||||
addResourceToBeRemoved(tuple.Item1.resource, string.Format("Embedded assembly: {0}", tuple.Item1.assemblyName));
|
||||
}
|
||||
}
|
||||
|
||||
bool decryptResources() {
|
||||
if (!resourceResolver.canDecryptResource())
|
||||
return false;
|
||||
var rsrc = resourceResolver.mergeResources();
|
||||
if (rsrc == null)
|
||||
return true;
|
||||
addResourceToBeRemoved(rsrc, "Encrypted resources");
|
||||
assemblyResolver.resolveResources();
|
||||
return true;
|
||||
}
|
||||
|
||||
TypeDefinition getGlobalSimpleZipType() {
|
||||
if (assemblyResolverInfo.SimpleZipType != null)
|
||||
return assemblyResolverInfo.SimpleZipType;
|
||||
foreach (var info in stringDecrypterInfos) {
|
||||
if (info.SimpleZipType != null)
|
||||
return info.SimpleZipType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void initStringDecrypterInfos() {
|
||||
var stringEncoderClassFinder = new StringEncoderClassFinder(module, DeobfuscatedFile);
|
||||
stringEncoderClassFinder.find();
|
||||
foreach (var info in stringEncoderClassFinder.StringsEncoderInfos) {
|
||||
var sinfo = new StringDecrypterInfo(module, info.StringDecrypterClass) {
|
||||
GetStringDelegate = info.GetStringDelegate,
|
||||
StringsType = info.StringsType,
|
||||
CreateStringDelegateMethod = info.CreateStringDelegateMethod,
|
||||
};
|
||||
stringDecrypterInfos.Add(sinfo);
|
||||
}
|
||||
|
||||
// There may be more than one string decrypter. The strings in the first one's
|
||||
// methods may be decrypted by the other string decrypter.
|
||||
|
||||
var initd = new Dictionary<StringDecrypterInfo, bool>(stringDecrypterInfos.Count);
|
||||
while (initd.Count != stringDecrypterInfos.Count) {
|
||||
StringDecrypterInfo initdInfo = null;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
foreach (var info in stringDecrypterInfos) {
|
||||
if (initd.ContainsKey(info))
|
||||
continue;
|
||||
if (info.init(this, DeobfuscatedFile)) {
|
||||
resourceDecrypterInfo.setSimpleZipType(info.SimpleZipType, DeobfuscatedFile);
|
||||
initdInfo = info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (initdInfo != null)
|
||||
break;
|
||||
|
||||
decryptResources();
|
||||
}
|
||||
|
||||
if (initdInfo == null)
|
||||
throw new ApplicationException("Could not initialize all stringDecrypterInfos");
|
||||
|
||||
initd[initdInfo] = true;
|
||||
initStringDecrypter(initdInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void initStringDecrypter(StringDecrypterInfo info) {
|
||||
Log.v("Adding string decrypter. Resource: {0}", Utils.toCsharpString(info.StringsResource.Name));
|
||||
var decrypter = new StringDecrypter(info);
|
||||
if (decrypter.CanDecrypt) {
|
||||
staticStringDecrypter.add(DotNetUtils.getMethod(info.GetStringDelegate, "Invoke"), (method, args) => {
|
||||
var fieldDefinition = DotNetUtils.getField(module, (FieldReference)args[0]);
|
||||
return decrypter.decrypt(fieldDefinition.MetadataToken.ToInt32(), (int)args[1]);
|
||||
});
|
||||
staticStringDecrypter.add(info.StringDecrypterMethod, (method, args) => {
|
||||
return decrypter.decrypt(0, (int)args[0]);
|
||||
});
|
||||
}
|
||||
stringDecrypters.Add(decrypter);
|
||||
DeobfuscatedFile.stringDecryptersAdded();
|
||||
}
|
||||
|
||||
public override void deobfuscateMethodEnd(Blocks blocks) {
|
||||
proxyDelegateFinder.deobfuscate(blocks);
|
||||
removeAutomatedErrorReportingCode(blocks);
|
||||
removeTamperProtection(blocks);
|
||||
removeStringsInitCode(blocks);
|
||||
base.deobfuscateMethodEnd(blocks);
|
||||
}
|
||||
|
||||
public override void deobfuscateEnd() {
|
||||
removeProxyDelegates(proxyDelegateFinder);
|
||||
removeMemoryManagerStuff();
|
||||
removeTamperProtectionStuff();
|
||||
removeStringDecryptionStuff();
|
||||
removeResolverInfoTypes(assemblyResolverInfo, "Assembly");
|
||||
removeResolverInfoTypes(resourceResolverInfo, "Resource");
|
||||
base.deobfuscateEnd();
|
||||
}
|
||||
|
||||
void removeResolverInfoTypes(ResolverInfoBase info, string typeName) {
|
||||
addTypeToBeRemoved(info.CallResolverType, string.Format("{0} resolver type #1", typeName));
|
||||
addTypeToBeRemoved(info.ResolverType, string.Format("{0} resolver type #2", typeName));
|
||||
}
|
||||
|
||||
void removeAutomatedErrorReportingCode(Blocks blocks) {
|
||||
if (!options.RemoveAutomatedErrorReporting)
|
||||
return;
|
||||
if (automatedErrorReportingFinder.ExceptionLoggerRemover.remove(blocks))
|
||||
Log.v("Removed Automated Error Reporting code");
|
||||
}
|
||||
|
||||
void removeTamperProtection(Blocks blocks) {
|
||||
if (!options.RemoveTamperProtection)
|
||||
return;
|
||||
if (tamperProtectionRemover.remove(blocks))
|
||||
Log.v("Removed Tamper Protection code");
|
||||
}
|
||||
|
||||
void removeMemoryManagerStuff() {
|
||||
if (!options.RemoveMemoryManager)
|
||||
return;
|
||||
addTypeToBeRemoved(memoryManagerInfo.MemoryManagerType, "Memory manager type");
|
||||
}
|
||||
|
||||
void removeTamperProtectionStuff() {
|
||||
if (!options.RemoveTamperProtection)
|
||||
return;
|
||||
addMethodsToBeRemoved(tamperProtectionRemover.PinvokeMethods, "Tamper protection PInvoke method");
|
||||
}
|
||||
|
||||
bool canRemoveStringDecrypterStuff() {
|
||||
return Operations.DecryptStrings != OpDecryptString.None;
|
||||
}
|
||||
|
||||
void removeStringDecryptionStuff() {
|
||||
if (!canRemoveStringDecrypterStuff())
|
||||
return;
|
||||
|
||||
foreach (var decrypter in stringDecrypters) {
|
||||
var info = decrypter.StringDecrypterInfo;
|
||||
addResourceToBeRemoved(info.StringsResource, "Encrypted strings");
|
||||
addFieldsToBeRemoved(info.getAllStringDelegateFields(), "String decrypter delegate field");
|
||||
|
||||
addTypeToBeRemoved(info.StringsEncodingClass, "String decrypter type");
|
||||
addTypeToBeRemoved(info.StringsType, "Creates the string decrypter delegates");
|
||||
addTypeToBeRemoved(info.GetStringDelegate, "String decrypter delegate type");
|
||||
}
|
||||
}
|
||||
|
||||
void removeStringsInitCode(Blocks blocks) {
|
||||
if (!canRemoveStringDecrypterStuff())
|
||||
return;
|
||||
|
||||
if (blocks.Method.Name == ".cctor") {
|
||||
foreach (var decrypter in stringDecrypters)
|
||||
decrypter.StringDecrypterInfo.removeInitCode(blocks);
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<string> getStringDecrypterMethods() {
|
||||
var list = new List<string>();
|
||||
foreach (var method in staticStringDecrypter.Methods)
|
||||
list.Add(method.MetadataToken.ToInt32().ToString("X8"));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright (C) 2011 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 Mono.Cecil;
|
||||
|
||||
namespace de4dot.deobfuscators.SmartAssembly {
|
||||
class MemoryManagerInfo {
|
||||
ModuleDefinition module;
|
||||
TypeDefinition memoryManagerType;
|
||||
MethodDefinition attachAppMethod;
|
||||
|
||||
public bool Detected {
|
||||
get { return memoryManagerType != null; }
|
||||
}
|
||||
|
||||
public TypeDefinition MemoryManagerType {
|
||||
get { return memoryManagerType; }
|
||||
}
|
||||
|
||||
public MethodDefinition CctorInitMethod {
|
||||
get { return attachAppMethod; }
|
||||
}
|
||||
|
||||
public MemoryManagerInfo(ModuleDefinition module) {
|
||||
this.module = module;
|
||||
find();
|
||||
}
|
||||
|
||||
bool find() {
|
||||
var cctor = DotNetUtils.getMethod(DotNetUtils.getModuleType(module), ".cctor");
|
||||
if (cctor == null)
|
||||
return false;
|
||||
|
||||
foreach (var tuple in DotNetUtils.getCalledMethods(module, cctor)) {
|
||||
var method = tuple.Item2;
|
||||
if (method.Name == ".cctor" || method.Name == ".ctor")
|
||||
continue;
|
||||
if (!method.IsStatic || !DotNetUtils.isMethod(method, "System.Void", "()"))
|
||||
continue;
|
||||
if (checkMemoryManagerType(tuple.Item1, method)) {
|
||||
memoryManagerType = tuple.Item1;
|
||||
attachAppMethod = method;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool checkMemoryManagerType(TypeDefinition type, MethodDefinition method) {
|
||||
// Only two fields: itself and a long
|
||||
int fields = 0;
|
||||
foreach (var field in type.Fields) {
|
||||
if (MemberReferenceHelper.compareTypes(field.FieldType, type) ||
|
||||
field.FieldType.FullName == "System.Int64") {
|
||||
fields++;
|
||||
continue;
|
||||
}
|
||||
if (DotNetUtils.isDelegateType(DotNetUtils.getType(module, field.FieldType)))
|
||||
continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
if (fields != 2)
|
||||
return false;
|
||||
|
||||
if (DotNetUtils.getPInvokeMethod(type, "kernel32", "SetProcessWorkingSetSize") == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue