Initial commit

This commit is contained in:
de4dot 2011-09-22 04:55:30 +02:00
commit 865ed5a47a
148 changed files with 21918 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*~

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "cecil"]
path = cecil
url = git@github.com:0xd4d/cecil.git

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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")]

View File

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

102
AssemblyData/Utils.cs Normal file
View File

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

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

View File

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

View File

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

View File

@ -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")]

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

View File

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

View File

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

View File

@ -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")]

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

View File

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

26
AssemblyServer/Program.cs Normal file
View File

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

View File

@ -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")]

674
COPYING Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

BIN
ICSharpCode.SharpZipLib.dll Normal file

Binary file not shown.

180
README.md Normal file
View File

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

View File

@ -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")]

View File

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

1509
Test.Rename.Dll/Tests.cs Normal file

File diff suppressed because it is too large Load Diff

100
Test.Rename/Program.cs Normal file
View File

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

View File

@ -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")]

View File

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

1
cecil Submodule

@ -0,0 +1 @@
Subproject commit bd300e0f349f509cf46d565f05a0df32561a7477

7
de4dot-x64/App.config Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

26
de4dot-x64/Program.cs Normal file
View File

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

View File

@ -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")]

View File

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

7
de4dot-x86/App.config Normal file
View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"/>
<supportedRuntime version="v2.0.50727"/>
</startup>
</configuration>

26
de4dot-x86/Program.cs Normal file
View File

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

View File

@ -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")]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {
}
}
}

View File

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

View File

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

View File

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

584
de4dot.code/DotNetUtils.cs Normal file
View File

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

View File

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

View File

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

76
de4dot.code/Log.cs Normal file
View File

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

View File

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

View File

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

View File

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

249
de4dot.code/Option.cs Normal file
View File

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

81
de4dot.code/Program.cs Normal file
View File

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

View File

@ -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")]

View File

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

View File

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

191
de4dot.code/Utils.cs Normal file
View File

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

View File

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

313
de4dot.code/blocks/Block.cs Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

268
de4dot.code/blocks/Instr.cs Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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++;
}
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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