/*
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 .
*/
using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
namespace dumpMethods {
class ProtectedFile {
string filename;
string methodsFilename;
public string Filename {
get { return filename; }
}
public string MethodsFilename {
get { return methodsFilename; }
set { methodsFilename = value; }
}
public ProtectedFile(string filename, string methodsFilename = null) {
this.filename = filename;
this.methodsFilename = methodsFilename;
if (this.methodsFilename == null)
this.methodsFilename = this.filename + ".methods";
}
}
class Program {
[DllImport(@"dumpMethodsN.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool initialize1(int logLevel, [MarshalAs(UnmanagedType.LPWStr)] string filename);
[DllImport(@"dumpMethodsN.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool initialize2();
[DllImport(@"dumpMethodsN.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool foundAssembly();
[DllImport(@"dumpMethodsN.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool dumpCode([MarshalAs(UnmanagedType.LPWStr)] string methodsFilename);
delegate void CallCctor();
static int Main(string[] args) {
try {
Console.OutputEncoding = new UTF8Encoding(false);
var files = parseCommandLine(args);
if (files.Count != 1)
exitError("Exactly one file must be given.");
foreach (var file in files) {
Log.v("calling initialize1()");
if (!initialize1((int)Log.logLevel, file.Filename))
fatal("initialize1() failed");
Log.v("Loading assembly: {0}", file.Filename);
Assembly assembly = Assembly.LoadFrom(file.Filename);
loadProtection(assembly);
if (!foundAssembly())
fatal("Could not find the assembly in memory");
Log.v("calling initialize2()");
if (!initialize2())
fatal("initialize2() failed");
Log.n("Dumping all methods...");
if (!dumpCode(file.MethodsFilename))
fatal("dumpCode() failed");
Log.n("Done");
}
}
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);
if (ex is ReflectionTypeLoadException) {
var rex = (ReflectionTypeLoadException)ex;
Log.e("\nReflectionTypeLoadException.LoaderExceptions =");
foreach (var ex2 in rex.LoaderExceptions)
Log.e(" {0}", ex2.Message);
}
return 1;
}
return 0;
}
static void exit(int exitCode) {
Environment.Exit(exitCode);
}
static void fatal(string msg) {
Log.e("{0}", msg);
exit(3);
}
static void loadProtection(Assembly assembly) {
Log.n("Loading protection...");
foreach (var type2 in assembly.GetTypes()) {
if (foundAssembly())
return;
var cctor = type2.TypeInitializer;
if (cctor == null)
continue;
// Tell the jitter to compile the method
RuntimeHelpers.PrepareMethod(cctor.MethodHandle);
}
}
static IList parseCommandLine(string[] args) {
var files = new List();
ProtectedFile file = null;
for (int i = 0; i < args.Length; i++) {
var arg = args[i];
var next = i + 1 < args.Length ? args[i + 1] : null;
if (arg == "-h" || arg == "--help") {
usage();
exit(0);
}
else if (arg == "-v" || arg == "--verbose") {
Log.logLevel = Log.LogLevel.verbose;
}
else if (arg == "-vv" || arg == "--veryverbose") {
Log.logLevel = Log.LogLevel.veryverbose;
}
else if (arg == "-m" || arg == "--methods") {
if (next == null)
exitError("Missing methods filename");
if (file == null)
exitError("No input file given yet");
file.MethodsFilename = next;
i++;
}
else {
if (arg == "-f" || arg == "--file") {
if (next == null)
exitError("Missing filename");
i++;
arg = next;
}
if (!new FileInfo(arg).Exists)
exitError(string.Format("File \"{0}\" does not exist.", arg));
files.Add(file = new ProtectedFile(arg));
}
}
return files;
}
static void exitError(string msg) {
usage();
Log.e("\n\nERROR: {0}\n", msg);
exit(2);
}
static void usage() {
string progName = getProgramBaseName();
Log.n("Dumps encrypted .NET methods. This works only with the supported obfuscators.");
Log.n("{0} [options] file", progName);
Log.n("Options:");
Log.n(" -v Verbose");
Log.n(" -vv Very verbose");
Log.n(" -f name Name of .NET file");
Log.n(" -m name Name of .methods file");
Log.n("Examples:");
Log.n("{0} file1", progName);
Log.n("{0} -f file1", progName);
Log.n("{0} -f file1 -m file1.methods", progName);
}
static string getProgramBaseName() {
var name = Environment.GetCommandLineArgs()[0];
int index = name.LastIndexOf(Path.DirectorySeparatorChar);
if (index < 0)
return name;
return name.Substring(index + 1);
}
}
}