2011-09-22 10:55:30 +08:00
|
|
|
|
/*
|
2014-03-12 05:15:43 +08:00
|
|
|
|
Copyright (C) 2011-2014 de4dot@gmail.com
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
This file is part of de4dot.
|
|
|
|
|
|
|
|
|
|
de4dot is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
de4dot is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
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;
|
2011-10-13 01:47:51 +08:00
|
|
|
|
using System.Text;
|
2012-12-20 09:06:09 +08:00
|
|
|
|
using dnlib.DotNet;
|
|
|
|
|
using dnlib.DotNet.Emit;
|
|
|
|
|
using dnlib.DotNet.Writer;
|
|
|
|
|
using dnlib.PE;
|
2013-10-03 00:07:28 +08:00
|
|
|
|
using AssemblyData;
|
2011-12-09 16:02:06 +08:00
|
|
|
|
using de4dot.code.deobfuscators;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
using de4dot.blocks;
|
2011-10-17 06:22:22 +08:00
|
|
|
|
using de4dot.blocks.cflow;
|
2011-12-09 16:02:06 +08:00
|
|
|
|
using de4dot.code.AssemblyClient;
|
|
|
|
|
using de4dot.code.renamer;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2011-12-09 16:02:06 +08:00
|
|
|
|
namespace de4dot.code {
|
|
|
|
|
public class ObfuscatedFile : IObfuscatedFile, IDeobfuscatedFile {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
Options options;
|
2012-11-01 21:39:39 +08:00
|
|
|
|
ModuleDefMD module;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
IDeobfuscator deob;
|
2012-04-05 03:06:10 +08:00
|
|
|
|
IDeobfuscatorContext deobfuscatorContext;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
AssemblyModule assemblyModule;
|
|
|
|
|
IAssemblyClient assemblyClient;
|
2012-01-20 02:16:44 +08:00
|
|
|
|
DynamicStringInliner dynamicStringInliner;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
IAssemblyClientFactory assemblyClientFactory;
|
|
|
|
|
SavedMethodBodies savedMethodBodies;
|
|
|
|
|
bool userStringDecrypterMethods = false;
|
|
|
|
|
|
|
|
|
|
class SavedMethodBodies {
|
2012-11-01 21:39:39 +08:00
|
|
|
|
Dictionary<MethodDef, SavedMethodBody> savedMethodBodies = new Dictionary<MethodDef, SavedMethodBody>();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
class SavedMethodBody {
|
2012-11-01 21:39:39 +08:00
|
|
|
|
MethodDef method;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
IList<Instruction> instructions;
|
|
|
|
|
IList<ExceptionHandler> exceptionHandlers;
|
|
|
|
|
|
2012-11-01 21:39:39 +08:00
|
|
|
|
public SavedMethodBody(MethodDef method) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
this.method = method;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
DotNetUtils.CopyBody(method, out instructions, out exceptionHandlers);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Restore() {
|
|
|
|
|
DotNetUtils.RestoreBody(method, instructions, exceptionHandlers);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Save(MethodDef method) {
|
|
|
|
|
if (IsSaved(method))
|
2011-09-22 10:55:30 +08:00
|
|
|
|
return;
|
|
|
|
|
savedMethodBodies[method] = new SavedMethodBody(method);
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void RestoreAll() {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
foreach (var smb in savedMethodBodies.Values)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
smb.Restore();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
savedMethodBodies.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public bool IsSaved(MethodDef method) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
return savedMethodBodies.ContainsKey(method);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class Options {
|
|
|
|
|
public string Filename { 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 ControlFlowDeobfuscation { get; set; }
|
|
|
|
|
public bool KeepObfuscatorTypes { get; set; }
|
2012-12-01 10:24:12 +08:00
|
|
|
|
public bool PreserveTokens { get; set; }
|
|
|
|
|
public MetaDataFlags MetaDataFlags { get; set; }
|
2012-12-07 22:06:38 +08:00
|
|
|
|
public RenamerFlags RenamerFlags { get; set; }
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
public Options() {
|
2011-09-28 22:06:10 +08:00
|
|
|
|
StringDecrypterType = DecrypterType.Default;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
StringDecrypterMethods = new List<string>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string Filename {
|
|
|
|
|
get { return options.Filename; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string NewFilename {
|
|
|
|
|
get { return options.NewFilename; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-01 21:39:39 +08:00
|
|
|
|
public ModuleDefMD ModuleDefMD {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
get { return module; }
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-17 11:17:03 +08:00
|
|
|
|
public INameChecker NameChecker {
|
|
|
|
|
get { return deob; }
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool RenameResourcesInCode {
|
|
|
|
|
get { return deob.TheOptions.RenameResourcesInCode; }
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-09 19:08:48 +08:00
|
|
|
|
public bool RemoveNamespaceWithOneType {
|
|
|
|
|
get { return (deob.RenamingOptions & RenamingOptions.RemoveNamespaceIfOneType) != 0; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-03 22:47:34 +08:00
|
|
|
|
public bool RenameResourceKeys {
|
|
|
|
|
get { return (deob.RenamingOptions & RenamingOptions.RenameResourceKeys) != 0; }
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-22 10:55:30 +08:00
|
|
|
|
public IDeobfuscator Deobfuscator {
|
|
|
|
|
get { return deob; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-05 03:06:10 +08:00
|
|
|
|
public IDeobfuscatorContext DeobfuscatorContext {
|
|
|
|
|
get { return deobfuscatorContext; }
|
|
|
|
|
set { deobfuscatorContext = value; }
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-05 03:06:58 +08:00
|
|
|
|
public ObfuscatedFile(Options options, ModuleContext moduleContext, IAssemblyClientFactory assemblyClientFactory) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
this.assemblyClientFactory = assemblyClientFactory;
|
|
|
|
|
this.options = options;
|
|
|
|
|
userStringDecrypterMethods = options.StringDecrypterMethods.Count > 0;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
options.Filename = Utils.GetFullPath(options.Filename);
|
2012-11-05 03:06:58 +08:00
|
|
|
|
assemblyModule = new AssemblyModule(options.Filename, moduleContext);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
if (options.NewFilename == null)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
options.NewFilename = GetDefaultNewFilename();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
if (string.Equals(options.Filename, options.NewFilename, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
throw new UserException(string.Format("filename is same as new filename! ({0})", options.Filename));
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
string GetDefaultNewFilename() {
|
2015-04-24 13:50:52 +08:00
|
|
|
|
string newFilename = Path.GetFileNameWithoutExtension(options.Filename) + "-cleaned" + Path.GetExtension(options.Filename);
|
|
|
|
|
return Path.Combine(Path.GetDirectoryName(options.Filename), newFilename);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Load(IList<IDeobfuscator> deobfuscators) {
|
2012-11-21 20:57:13 +08:00
|
|
|
|
try {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
LoadModule(deobfuscators);
|
|
|
|
|
TheAssemblyResolver.Instance.AddSearchDirectory(Utils.GetDirName(Filename));
|
|
|
|
|
TheAssemblyResolver.Instance.AddSearchDirectory(Utils.GetDirName(NewFilename));
|
|
|
|
|
DetectObfuscator(deobfuscators);
|
2012-11-21 20:57:13 +08:00
|
|
|
|
if (deob == null)
|
|
|
|
|
throw new ApplicationException("Could not detect obfuscator!");
|
2013-01-19 20:03:57 +08:00
|
|
|
|
InitializeDeobfuscator();
|
2012-11-21 20:57:13 +08:00
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
foreach (var d in deobfuscators) {
|
|
|
|
|
if (d != deob && d != null)
|
|
|
|
|
d.Dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-10-26 20:32:50 +08:00
|
|
|
|
}
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void LoadModule(IEnumerable<IDeobfuscator> deobfuscators) {
|
2012-11-18 14:32:57 +08:00
|
|
|
|
ModuleDefMD oldModule = module;
|
2011-12-01 01:26:36 +08:00
|
|
|
|
try {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
module = assemblyModule.Load();
|
2011-12-01 01:26:36 +08:00
|
|
|
|
}
|
|
|
|
|
catch (BadImageFormatException) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!UnpackNativeImage(deobfuscators))
|
2011-12-01 01:26:36 +08:00
|
|
|
|
throw new BadImageFormatException();
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.v("Unpacked native file");
|
2011-12-01 01:26:36 +08:00
|
|
|
|
}
|
2012-11-18 14:32:57 +08:00
|
|
|
|
finally {
|
|
|
|
|
if (oldModule != null)
|
|
|
|
|
oldModule.Dispose();
|
|
|
|
|
}
|
2011-12-01 01:26:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool UnpackNativeImage(IEnumerable<IDeobfuscator> deobfuscators) {
|
2012-11-21 18:07:25 +08:00
|
|
|
|
using (var peImage = new PEImage(Filename)) {
|
|
|
|
|
foreach (var deob in deobfuscators) {
|
|
|
|
|
byte[] unpackedData = null;
|
|
|
|
|
try {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
unpackedData = deob.UnpackNativeFile(peImage);
|
2012-11-21 18:07:25 +08:00
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
}
|
|
|
|
|
if (unpackedData == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var oldModule = module;
|
|
|
|
|
try {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
module = assemblyModule.Load(unpackedData);
|
2012-11-21 18:07:25 +08:00
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
Logger.w("Could not load unpacked data. File: {0}, deobfuscator: {0}", peImage.FileName ?? "(unknown filename)", deob.TypeLong);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
finally {
|
|
|
|
|
if (oldModule != null)
|
|
|
|
|
oldModule.Dispose();
|
|
|
|
|
}
|
|
|
|
|
this.deob = deob;
|
|
|
|
|
return true;
|
2012-11-18 14:32:57 +08:00
|
|
|
|
}
|
2011-12-01 01:26:36 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void InitializeDeobfuscator() {
|
2011-09-28 22:06:10 +08:00
|
|
|
|
if (options.StringDecrypterType == DecrypterType.Default)
|
|
|
|
|
options.StringDecrypterType = deob.DefaultDecrypterType;
|
|
|
|
|
if (options.StringDecrypterType == DecrypterType.Default)
|
|
|
|
|
options.StringDecrypterType = DecrypterType.Static;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.Operations = CreateOperations();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
IOperations CreateOperations() {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
var op = new Operations();
|
|
|
|
|
|
2011-09-28 22:06:10 +08:00
|
|
|
|
switch (options.StringDecrypterType) {
|
|
|
|
|
case DecrypterType.None:
|
2011-09-22 10:55:30 +08:00
|
|
|
|
op.DecryptStrings = OpDecryptString.None;
|
2011-09-28 22:06:10 +08:00
|
|
|
|
break;
|
|
|
|
|
case DecrypterType.Static:
|
2011-09-22 10:55:30 +08:00
|
|
|
|
op.DecryptStrings = OpDecryptString.Static;
|
2011-09-28 22:06:10 +08:00
|
|
|
|
break;
|
|
|
|
|
default:
|
2011-09-22 10:55:30 +08:00
|
|
|
|
op.DecryptStrings = OpDecryptString.Dynamic;
|
2011-09-28 22:06:10 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
op.KeepObfuscatorTypes = options.KeepObfuscatorTypes;
|
2012-12-01 10:24:12 +08:00
|
|
|
|
op.MetaDataFlags = options.MetaDataFlags;
|
2012-12-07 22:06:38 +08:00
|
|
|
|
op.RenamerFlags = options.RenamerFlags;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
return op;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void DetectObfuscator(IEnumerable<IDeobfuscator> deobfuscators) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
2011-12-01 01:26:36 +08:00
|
|
|
|
// It's not null if it unpacked a native file
|
|
|
|
|
if (this.deob != null) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.Initialize(module);
|
2011-12-01 01:26:36 +08:00
|
|
|
|
deob.DeobfuscatedFile = this;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.Detect();
|
2011-12-01 01:26:36 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-22 10:55:30 +08:00
|
|
|
|
foreach (var deob in deobfuscators) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.Initialize(module);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
deob.DeobfuscatedFile = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options.ForcedObfuscatorType != null) {
|
|
|
|
|
foreach (var deob in deobfuscators) {
|
|
|
|
|
if (string.Equals(options.ForcedObfuscatorType, deob.Type, StringComparison.OrdinalIgnoreCase)) {
|
|
|
|
|
this.deob = deob;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.Detect();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-08-13 06:54:46 +08:00
|
|
|
|
else
|
2013-01-19 20:03:57 +08:00
|
|
|
|
this.deob = DetectObfuscator2(deobfuscators);
|
2011-10-09 01:33:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
IDeobfuscator DetectObfuscator2(IEnumerable<IDeobfuscator> deobfuscators) {
|
2012-01-10 05:47:29 +08:00
|
|
|
|
var allDetected = new List<IDeobfuscator>();
|
2011-10-09 01:33:12 +08:00
|
|
|
|
IDeobfuscator detected = null;
|
|
|
|
|
int detectVal = 0;
|
|
|
|
|
foreach (var deob in deobfuscators) {
|
2011-11-01 21:19:53 +08:00
|
|
|
|
this.deob = deob; // So we can call deob.CanInlineMethods in deobfuscate()
|
2012-07-30 00:12:29 +08:00
|
|
|
|
int val;
|
|
|
|
|
try {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
val = deob.Detect();
|
2012-07-30 00:12:29 +08:00
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
val = deob.Type == "un" ? 1 : 0;
|
|
|
|
|
}
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.v("{0,3}: {1}", val, deob.TypeLong);
|
2012-01-10 05:47:29 +08:00
|
|
|
|
if (val > 0 && deob.Type != "un")
|
|
|
|
|
allDetected.Add(deob);
|
2011-10-09 01:33:12 +08:00
|
|
|
|
if (val > detectVal) {
|
|
|
|
|
detectVal = val;
|
|
|
|
|
detected = deob;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-01 21:19:53 +08:00
|
|
|
|
this.deob = null;
|
2012-01-10 05:47:29 +08:00
|
|
|
|
|
|
|
|
|
if (allDetected.Count > 1) {
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.n("More than one obfuscator detected:");
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.Instance.Indent();
|
2012-01-10 05:47:29 +08:00
|
|
|
|
foreach (var deob in allDetected)
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.n("{0} (use: -p {1})", deob.Name, deob.Type);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.Instance.DeIndent();
|
2012-01-10 05:47:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-09 01:33:12 +08:00
|
|
|
|
return detected;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
MetaDataFlags GetMetaDataFlags() {
|
2012-12-01 10:24:12 +08:00
|
|
|
|
var mdFlags = options.MetaDataFlags | deob.MetaDataFlags;
|
|
|
|
|
|
|
|
|
|
// Always preserve tokens if it's an unknown obfuscator
|
|
|
|
|
if (deob.Type == "un") {
|
|
|
|
|
mdFlags |= MetaDataFlags.PreserveRids |
|
|
|
|
|
MetaDataFlags.PreserveUSOffsets |
|
|
|
|
|
MetaDataFlags.PreserveBlobOffsets |
|
|
|
|
|
MetaDataFlags.PreserveExtraSignatureData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mdFlags;
|
2012-11-05 06:24:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Save() {
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.n("Saving {0}", options.NewFilename);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var mdFlags = GetMetaDataFlags();
|
2012-12-01 10:24:12 +08:00
|
|
|
|
if (!options.ControlFlowDeobfuscation)
|
|
|
|
|
mdFlags |= MetaDataFlags.KeepOldMaxStack;
|
2013-11-18 01:12:21 +08:00
|
|
|
|
assemblyModule.Save(options.NewFilename, mdFlags, new PrintNewTokens(module, deob as IModuleWriterListener));
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
IList<MethodDef> GetAllMethods() {
|
2012-11-01 21:39:39 +08:00
|
|
|
|
var list = new List<MethodDef>();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
foreach (var type in module.GetTypes()) {
|
|
|
|
|
foreach (var method in type.Methods)
|
|
|
|
|
list.Add(method);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void DeobfuscateBegin() {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
switch (options.StringDecrypterType) {
|
|
|
|
|
case DecrypterType.None:
|
2013-01-19 20:03:57 +08:00
|
|
|
|
CheckSupportedStringDecrypter(StringFeatures.AllowNoDecryption);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DecrypterType.Static:
|
2013-01-19 20:03:57 +08:00
|
|
|
|
CheckSupportedStringDecrypter(StringFeatures.AllowStaticDecryption);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DecrypterType.Delegate:
|
2011-09-24 16:26:29 +08:00
|
|
|
|
case DecrypterType.Emulate:
|
2013-01-19 20:03:57 +08:00
|
|
|
|
CheckSupportedStringDecrypter(StringFeatures.AllowDynamicDecryption);
|
2012-12-01 08:40:23 +08:00
|
|
|
|
var newProcFactory = assemblyClientFactory as NewProcessAssemblyClientFactory;
|
|
|
|
|
if (newProcFactory != null)
|
2013-10-03 00:07:28 +08:00
|
|
|
|
assemblyClient = newProcFactory.Create(AssemblyServiceType.StringDecrypter, module);
|
2012-12-01 08:40:23 +08:00
|
|
|
|
else
|
2013-10-03 00:07:28 +08:00
|
|
|
|
assemblyClient = assemblyClientFactory.Create(AssemblyServiceType.StringDecrypter);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
assemblyClient.Connect();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new ApplicationException(string.Format("Invalid string decrypter type '{0}'", options.StringDecrypterType));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void CheckSupportedStringDecrypter(StringFeatures feature) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
if ((deob.StringFeatures & feature) == feature)
|
|
|
|
|
return;
|
2011-11-12 18:31:07 +08:00
|
|
|
|
throw new UserException(string.Format("Deobfuscator {0} does not support this string decryption type", deob.TypeLong));
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void Deobfuscate() {
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.n("Cleaning {0}", options.Filename);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
InitAssemblyClient();
|
2011-10-26 20:32:50 +08:00
|
|
|
|
|
2012-07-24 23:02:27 +08:00
|
|
|
|
for (int i = 0; ; i++) {
|
|
|
|
|
byte[] fileData = null;
|
|
|
|
|
DumpedMethods dumpedMethods = null;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!deob.GetDecryptedModule(i, ref fileData, ref dumpedMethods))
|
2012-07-24 23:02:27 +08:00
|
|
|
|
break;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
ReloadModule(fileData, dumpedMethods);
|
2012-07-24 23:02:27 +08:00
|
|
|
|
}
|
2011-10-26 20:32:50 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.DeobfuscateBegin();
|
|
|
|
|
DeobfuscateMethods();
|
|
|
|
|
deob.DeobfuscateEnd();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void ReloadModule(byte[] newModuleData, DumpedMethods dumpedMethods) {
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.v("Reloading decrypted assembly (original filename: {0})", Filename);
|
2011-10-30 13:14:22 +08:00
|
|
|
|
simpleDeobfuscatorFlags.Clear();
|
2012-11-18 14:32:57 +08:00
|
|
|
|
using (var oldModule = module) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
module = assemblyModule.Reload(newModuleData, CreateDumpedMethodsRestorer(dumpedMethods), deob as IStringDecrypter);
|
|
|
|
|
deob = deob.ModuleReloaded(module);
|
2012-11-18 14:32:57 +08:00
|
|
|
|
}
|
2013-01-19 20:03:57 +08:00
|
|
|
|
InitializeDeobfuscator();
|
2011-10-26 20:32:50 +08:00
|
|
|
|
deob.DeobfuscatedFile = this;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
UpdateDynamicStringInliner();
|
2011-10-26 20:32:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
DumpedMethodsRestorer CreateDumpedMethodsRestorer(DumpedMethods dumpedMethods) {
|
2012-11-07 07:53:16 +08:00
|
|
|
|
if (dumpedMethods == null || dumpedMethods.Count == 0)
|
|
|
|
|
return null;
|
|
|
|
|
return new DumpedMethodsRestorer(dumpedMethods);
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void InitAssemblyClient() {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
if (assemblyClient == null)
|
|
|
|
|
return;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
assemblyClient.WaitConnected();
|
2013-10-03 00:07:28 +08:00
|
|
|
|
assemblyClient.StringDecrypterService.LoadAssembly(options.Filename);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
if (options.StringDecrypterType == DecrypterType.Delegate)
|
2013-10-03 00:07:28 +08:00
|
|
|
|
assemblyClient.StringDecrypterService.SetStringDecrypterType(AssemblyData.StringDecrypterType.Delegate);
|
2011-09-24 16:26:29 +08:00
|
|
|
|
else if (options.StringDecrypterType == DecrypterType.Emulate)
|
2013-10-03 00:07:28 +08:00
|
|
|
|
assemblyClient.StringDecrypterService.SetStringDecrypterType(AssemblyData.StringDecrypterType.Emulate);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
else
|
|
|
|
|
throw new ApplicationException(string.Format("Invalid string decrypter type '{0}'", options.StringDecrypterType));
|
|
|
|
|
|
2012-01-20 02:16:44 +08:00
|
|
|
|
dynamicStringInliner = new DynamicStringInliner(assemblyClient);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
UpdateDynamicStringInliner();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void UpdateDynamicStringInliner() {
|
2012-01-20 02:16:44 +08:00
|
|
|
|
if (dynamicStringInliner != null)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
dynamicStringInliner.Initialize(GetMethodTokens());
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
IEnumerable<int> GetMethodTokens() {
|
2012-02-25 13:25:40 +08:00
|
|
|
|
if (!userStringDecrypterMethods)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
return deob.GetStringDecrypterMethods();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2012-02-25 13:25:40 +08:00
|
|
|
|
var tokens = new List<int>();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
foreach (var val in options.StringDecrypterMethods) {
|
|
|
|
|
var tokenStr = val.Trim();
|
2011-10-09 19:19:26 +08:00
|
|
|
|
if (Utils.StartsWith(tokenStr, "0x", StringComparison.OrdinalIgnoreCase))
|
2011-09-22 10:55:30 +08:00
|
|
|
|
tokenStr = tokenStr.Substring(2);
|
|
|
|
|
int methodToken;
|
|
|
|
|
if (int.TryParse(tokenStr, NumberStyles.HexNumber, null, out methodToken))
|
|
|
|
|
tokens.Add(methodToken);
|
|
|
|
|
else
|
2013-01-19 20:03:57 +08:00
|
|
|
|
tokens.AddRange(FindMethodTokens(val));
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
IEnumerable<int> FindMethodTokens(string methodDesc) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
var tokens = new List<int>();
|
|
|
|
|
|
|
|
|
|
string typeString, methodName;
|
|
|
|
|
string[] argsStrings;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
SplitMethodDesc(methodDesc, out typeString, out methodName, out argsStrings);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
|
|
|
|
foreach (var type in module.GetTypes()) {
|
|
|
|
|
if (typeString != null && typeString != type.FullName)
|
|
|
|
|
continue;
|
|
|
|
|
foreach (var method in type.Methods) {
|
2012-11-01 23:42:02 +08:00
|
|
|
|
if (!method.IsStatic)
|
|
|
|
|
continue;
|
2012-11-06 07:17:58 +08:00
|
|
|
|
if (method.MethodSig.GetRetType().GetElementType() != ElementType.String && method.MethodSig.GetRetType().GetElementType() != ElementType.Object)
|
2011-09-22 10:55:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
if (methodName != null && methodName != method.Name)
|
|
|
|
|
continue;
|
|
|
|
|
|
2012-11-05 05:41:45 +08:00
|
|
|
|
var sig = method.MethodSig;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
if (argsStrings == null) {
|
2012-11-05 05:41:45 +08:00
|
|
|
|
if (sig.Params.Count == 0)
|
2011-09-22 10:55:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2012-11-05 05:41:45 +08:00
|
|
|
|
if (argsStrings.Length != sig.Params.Count)
|
2011-09-22 10:55:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
for (int i = 0; i < argsStrings.Length; i++) {
|
2012-11-05 05:41:45 +08:00
|
|
|
|
if (argsStrings[i] != sig.Params[i].FullName)
|
2011-09-22 10:55:30 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.v("Adding string decrypter; token: {0:X8}, method: {1}", method.MDToken.ToInt32(), Utils.RemoveNewlines(method.FullName));
|
2012-11-01 23:42:02 +08:00
|
|
|
|
tokens.Add(method.MDToken.ToInt32());
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tokens;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static void SplitMethodDesc(string methodDesc, out string type, out string name, out string[] args) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
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 = "";
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-09 19:19:26 +08:00
|
|
|
|
if (Utils.StartsWith(remaining, "(", StringComparison.Ordinal)) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
stringArgs = remaining;
|
|
|
|
|
}
|
|
|
|
|
else if (remaining.Length > 0)
|
|
|
|
|
throw new UserException(string.Format("Invalid method desc: '{0}'", methodDesc));
|
|
|
|
|
|
|
|
|
|
if (stringArgs != null) {
|
2011-10-09 19:19:26 +08:00
|
|
|
|
if (Utils.StartsWith(stringArgs, "(", StringComparison.Ordinal))
|
2011-09-22 10:55:30 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void DeobfuscateEnd() {
|
|
|
|
|
DeobfuscateCleanUp();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
public void DeobfuscateCleanUp() {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
if (assemblyClient != null) {
|
|
|
|
|
assemblyClient.Dispose();
|
|
|
|
|
assemblyClient = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void DeobfuscateMethods() {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
if (savedMethodBodies != null) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
savedMethodBodies.RestoreAll();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
savedMethodBodies = null;
|
|
|
|
|
}
|
|
|
|
|
deob.DeobfuscatedFile = null;
|
|
|
|
|
|
2011-12-27 03:30:30 +08:00
|
|
|
|
if (!options.ControlFlowDeobfuscation) {
|
2012-12-01 10:24:12 +08:00
|
|
|
|
if (options.KeepObfuscatorTypes || deob.Type == "un")
|
2011-12-27 03:30:30 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-11 18:37:40 +08:00
|
|
|
|
bool isVerbose = !Logger.Instance.IgnoresEvent(LoggerEvent.Verbose);
|
|
|
|
|
bool isVV = !Logger.Instance.IgnoresEvent(LoggerEvent.VeryVerbose);
|
|
|
|
|
if (isVerbose)
|
|
|
|
|
Logger.v("Deobfuscating methods");
|
2011-10-13 01:47:51 +08:00
|
|
|
|
var methodPrinter = new MethodPrinter();
|
2012-04-30 04:22:43 +08:00
|
|
|
|
var cflowDeobfuscator = new BlocksCflowDeobfuscator(deob.BlocksDeobfuscators);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
foreach (var method in GetAllMethods()) {
|
2012-11-11 18:37:40 +08:00
|
|
|
|
if (isVerbose) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.v("Deobfuscating {0} ({1:X8})", Utils.RemoveNewlines(method), method.MDToken.ToUInt32());
|
|
|
|
|
Logger.Instance.Indent();
|
2012-11-11 18:37:40 +08:00
|
|
|
|
}
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2012-11-11 12:31:11 +08:00
|
|
|
|
int oldIndentLevel = Logger.Instance.IndentLevel;
|
2011-12-24 00:28:20 +08:00
|
|
|
|
try {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Deobfuscate(method, cflowDeobfuscator, methodPrinter, isVerbose, isVV);
|
2011-12-24 00:28:20 +08:00
|
|
|
|
}
|
2011-12-26 06:06:37 +08:00
|
|
|
|
catch (Exception ex) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!CanLoadMethodBody(method)) {
|
2012-11-11 18:37:40 +08:00
|
|
|
|
if (isVerbose)
|
|
|
|
|
Logger.v("Invalid method body. {0:X8}", method.MDToken.ToInt32());
|
2012-11-06 22:58:55 +08:00
|
|
|
|
method.Body = new CilBody();
|
2012-05-24 22:42:04 +08:00
|
|
|
|
}
|
|
|
|
|
else {
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.w("Could not deobfuscate method {0:X8}. Hello, E.T.: {1}", // E.T. = exception type
|
2012-11-01 23:42:02 +08:00
|
|
|
|
method.MDToken.ToInt32(),
|
2011-12-26 06:06:37 +08:00
|
|
|
|
ex.GetType());
|
2012-05-24 22:42:04 +08:00
|
|
|
|
}
|
2011-12-24 00:28:20 +08:00
|
|
|
|
}
|
2012-02-25 12:21:00 +08:00
|
|
|
|
finally {
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.Instance.IndentLevel = oldIndentLevel;
|
2012-02-25 12:21:00 +08:00
|
|
|
|
}
|
2013-01-19 20:03:57 +08:00
|
|
|
|
RemoveNoInliningAttribute(method);
|
2011-10-27 04:16:51 +08:00
|
|
|
|
|
2012-11-11 18:37:40 +08:00
|
|
|
|
if (isVerbose)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.Instance.DeIndent();
|
2011-12-24 00:28:20 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-10-27 04:16:51 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static bool CanLoadMethodBody(MethodDef method) {
|
2012-05-24 22:42:04 +08:00
|
|
|
|
try {
|
2012-11-06 22:58:55 +08:00
|
|
|
|
var body = method.Body;
|
2012-05-24 22:42:04 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-01 10:24:12 +08:00
|
|
|
|
bool CanOptimizeLocals() {
|
|
|
|
|
// Don't remove any locals if we must preserve StandAloneSig table
|
2013-01-19 20:03:57 +08:00
|
|
|
|
return (GetMetaDataFlags() & MetaDataFlags.PreserveStandAloneSigRids) == 0;
|
2012-12-01 10:24:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void Deobfuscate(MethodDef method, BlocksCflowDeobfuscator cflowDeobfuscator, MethodPrinter methodPrinter, bool isVerbose, bool isVV) {
|
|
|
|
|
if (!HasNonEmptyBody(method))
|
2011-12-24 00:28:20 +08:00
|
|
|
|
return;
|
2011-10-27 04:16:51 +08:00
|
|
|
|
|
2011-12-24 00:28:20 +08:00
|
|
|
|
var blocks = new Blocks(method);
|
|
|
|
|
int numRemovedLocals = 0;
|
2012-11-06 22:58:55 +08:00
|
|
|
|
int oldNumInstructions = method.Body.Instructions.Count;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.DeobfuscateMethodBegin(blocks);
|
2011-12-24 00:28:20 +08:00
|
|
|
|
if (options.ControlFlowDeobfuscation) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
cflowDeobfuscator.Initialize(blocks);
|
|
|
|
|
cflowDeobfuscator.Deobfuscate();
|
2011-12-24 00:28:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (deob.DeobfuscateOther(blocks) && options.ControlFlowDeobfuscation)
|
|
|
|
|
cflowDeobfuscator.Deobfuscate();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2011-12-24 00:28:20 +08:00
|
|
|
|
if (options.ControlFlowDeobfuscation) {
|
2012-12-01 10:24:12 +08:00
|
|
|
|
if (CanOptimizeLocals())
|
2013-01-19 20:03:57 +08:00
|
|
|
|
numRemovedLocals = blocks.OptimizeLocals();
|
|
|
|
|
blocks.RepartitionBlocks();
|
2011-12-24 00:28:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
DeobfuscateStrings(blocks);
|
|
|
|
|
deob.DeobfuscateMethodEnd(blocks);
|
2011-12-24 00:28:20 +08:00
|
|
|
|
|
|
|
|
|
IList<Instruction> allInstructions;
|
|
|
|
|
IList<ExceptionHandler> allExceptionHandlers;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
blocks.GetCode(out allInstructions, out allExceptionHandlers);
|
|
|
|
|
DotNetUtils.RestoreBody(method, allInstructions, allExceptionHandlers);
|
2011-12-24 00:28:20 +08:00
|
|
|
|
|
2012-11-11 18:37:40 +08:00
|
|
|
|
if (isVerbose && numRemovedLocals > 0)
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.v("Removed {0} unused local(s)", numRemovedLocals);
|
2012-11-06 22:58:55 +08:00
|
|
|
|
int numRemovedInstructions = oldNumInstructions - method.Body.Instructions.Count;
|
2012-11-11 18:37:40 +08:00
|
|
|
|
if (isVerbose && numRemovedInstructions > 0)
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.v("Removed {0} dead instruction(s)", numRemovedInstructions);
|
2011-12-24 00:28:20 +08:00
|
|
|
|
|
2012-11-11 18:37:40 +08:00
|
|
|
|
if (isVV) {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.Log(LoggerEvent.VeryVerbose, "Deobfuscated code:");
|
|
|
|
|
Logger.Instance.Indent();
|
|
|
|
|
methodPrinter.Print(LoggerEvent.VeryVerbose, allInstructions, allExceptionHandlers);
|
|
|
|
|
Logger.Instance.DeIndent();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool HasNonEmptyBody(MethodDef method) {
|
2012-11-06 22:58:55 +08:00
|
|
|
|
return method.HasBody && method.Body.Instructions.Count > 0;
|
2011-09-25 00:48:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void DeobfuscateStrings(Blocks blocks) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
switch (options.StringDecrypterType) {
|
|
|
|
|
case DecrypterType.None:
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DecrypterType.Static:
|
2013-01-19 20:03:57 +08:00
|
|
|
|
deob.DeobfuscateStrings(blocks);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case DecrypterType.Delegate:
|
2011-09-24 16:26:29 +08:00
|
|
|
|
case DecrypterType.Emulate:
|
2013-01-19 20:03:57 +08:00
|
|
|
|
dynamicStringInliner.Decrypt(blocks);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
throw new ApplicationException(string.Format("Invalid string decrypter type '{0}'", options.StringDecrypterType));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void RemoveNoInliningAttribute(MethodDef method) {
|
2012-11-01 23:42:02 +08:00
|
|
|
|
method.IsNoInlining = false;
|
2011-12-26 06:06:37 +08:00
|
|
|
|
for (int i = 0; i < method.CustomAttributes.Count; i++) {
|
|
|
|
|
var cattr = method.CustomAttributes[i];
|
2012-11-01 23:42:02 +08:00
|
|
|
|
if (cattr.TypeFullName != "System.Runtime.CompilerServices.MethodImplAttribute")
|
2011-12-26 06:06:37 +08:00
|
|
|
|
continue;
|
|
|
|
|
int options = 0;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (!GetMethodImplOptions(cattr, ref options))
|
2011-12-26 06:06:37 +08:00
|
|
|
|
continue;
|
|
|
|
|
if (options != 0 && options != (int)MethodImplAttributes.NoInlining)
|
|
|
|
|
continue;
|
|
|
|
|
method.CustomAttributes.RemoveAt(i);
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
static bool GetMethodImplOptions(CustomAttribute cattr, ref int value) {
|
2012-11-06 07:17:58 +08:00
|
|
|
|
if (cattr.IsRawBlob)
|
|
|
|
|
return false;
|
2012-11-17 06:50:52 +08:00
|
|
|
|
if (cattr.ConstructorArguments.Count != 1)
|
2011-12-26 06:06:37 +08:00
|
|
|
|
return false;
|
2012-11-17 06:50:52 +08:00
|
|
|
|
if (cattr.ConstructorArguments[0].Type.ElementType != ElementType.I2 &&
|
|
|
|
|
cattr.ConstructorArguments[0].Type.FullName != "System.Runtime.CompilerServices.MethodImplOptions")
|
2011-12-26 06:06:37 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
2012-11-17 06:50:52 +08:00
|
|
|
|
var arg = cattr.ConstructorArguments[0].Value;
|
2011-12-26 06:06:37 +08:00
|
|
|
|
if (arg is short) {
|
|
|
|
|
value = (short)arg;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (arg is int) {
|
|
|
|
|
value = (int)arg;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString() {
|
|
|
|
|
if (options == null || options.Filename == null)
|
|
|
|
|
return base.ToString();
|
|
|
|
|
return options.Filename;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Flags]
|
|
|
|
|
enum SimpleDeobFlags {
|
|
|
|
|
HasDeobfuscated = 0x1,
|
|
|
|
|
}
|
2012-11-01 21:39:39 +08:00
|
|
|
|
Dictionary<MethodDef, SimpleDeobFlags> simpleDeobfuscatorFlags = new Dictionary<MethodDef, SimpleDeobFlags>();
|
2013-01-19 20:03:57 +08:00
|
|
|
|
bool Check(MethodDef method, SimpleDeobFlags flag) {
|
2013-09-29 01:43:46 +08:00
|
|
|
|
if (method == null)
|
|
|
|
|
return false;
|
2011-09-22 10:55:30 +08:00
|
|
|
|
SimpleDeobFlags oldFlags;
|
|
|
|
|
simpleDeobfuscatorFlags.TryGetValue(method, out oldFlags);
|
|
|
|
|
simpleDeobfuscatorFlags[method] = oldFlags | flag;
|
|
|
|
|
return (oldFlags & flag) == flag;
|
|
|
|
|
}
|
2013-09-29 01:43:46 +08:00
|
|
|
|
bool Clear(MethodDef method, SimpleDeobFlags flag) {
|
|
|
|
|
if (method == null)
|
|
|
|
|
return false;
|
|
|
|
|
SimpleDeobFlags oldFlags;
|
|
|
|
|
if (!simpleDeobfuscatorFlags.TryGetValue(method, out oldFlags))
|
|
|
|
|
return false;
|
|
|
|
|
simpleDeobfuscatorFlags[method] = oldFlags & ~flag;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void Deobfuscate(MethodDef method, string msg, Action<Blocks> handler) {
|
2011-09-22 10:55:30 +08:00
|
|
|
|
if (savedMethodBodies != null)
|
2013-01-19 20:03:57 +08:00
|
|
|
|
savedMethodBodies.Save(method);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.v("{0}: {1} ({2:X8})", msg, Utils.RemoveNewlines(method), method.MDToken.ToUInt32());
|
|
|
|
|
Logger.Instance.Indent();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
if (HasNonEmptyBody(method)) {
|
2012-07-28 10:18:11 +08:00
|
|
|
|
try {
|
|
|
|
|
var blocks = new Blocks(method);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2012-07-28 10:18:11 +08:00
|
|
|
|
handler(blocks);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
|
2012-07-28 10:18:11 +08:00
|
|
|
|
IList<Instruction> allInstructions;
|
|
|
|
|
IList<ExceptionHandler> allExceptionHandlers;
|
2013-01-19 20:03:57 +08:00
|
|
|
|
blocks.GetCode(out allInstructions, out allExceptionHandlers);
|
|
|
|
|
DotNetUtils.RestoreBody(method, allInstructions, allExceptionHandlers);
|
2012-07-28 10:18:11 +08:00
|
|
|
|
}
|
|
|
|
|
catch {
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.v("Could not deobfuscate {0:X8}", method.MDToken.ToInt32());
|
2012-07-28 10:18:11 +08:00
|
|
|
|
}
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Logger.Instance.DeIndent();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-29 01:43:46 +08:00
|
|
|
|
void ISimpleDeobfuscator.MethodModified(MethodDef method) {
|
|
|
|
|
Clear(method, SimpleDeobFlags.HasDeobfuscated);
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void ISimpleDeobfuscator.Deobfuscate(MethodDef method) {
|
|
|
|
|
((ISimpleDeobfuscator)this).Deobfuscate(method, false);
|
2012-07-17 00:02:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void ISimpleDeobfuscator.Deobfuscate(MethodDef method, bool force) {
|
2013-09-29 03:28:59 +08:00
|
|
|
|
if (method == null || (!force && Check(method, SimpleDeobFlags.HasDeobfuscated)))
|
2011-09-22 10:55:30 +08:00
|
|
|
|
return;
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
Deobfuscate(method, "Deobfuscating control flow", (blocks) => {
|
2012-04-30 04:22:43 +08:00
|
|
|
|
var cflowDeobfuscator = new BlocksCflowDeobfuscator(deob.BlocksDeobfuscators);
|
2013-01-19 20:03:57 +08:00
|
|
|
|
cflowDeobfuscator.Initialize(blocks);
|
|
|
|
|
cflowDeobfuscator.Deobfuscate();
|
2011-10-22 02:35:13 +08:00
|
|
|
|
});
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void ISimpleDeobfuscator.DecryptStrings(MethodDef method, IDeobfuscator theDeob) {
|
|
|
|
|
Deobfuscate(method, "Static string decryption", (blocks) => theDeob.DeobfuscateStrings(blocks));
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void IDeobfuscatedFile.CreateAssemblyFile(byte[] data, string assemblyName, string extension) {
|
2011-10-22 20:31:38 +08:00
|
|
|
|
if (extension == null)
|
|
|
|
|
extension = ".dll";
|
2013-01-19 20:03:57 +08:00
|
|
|
|
var baseDir = Utils.GetDirName(options.NewFilename);
|
2011-10-22 20:31:38 +08:00
|
|
|
|
var newName = Path.Combine(baseDir, assemblyName + extension);
|
2012-11-11 12:31:11 +08:00
|
|
|
|
Logger.n("Creating file {0}", newName);
|
2012-11-21 18:07:40 +08:00
|
|
|
|
File.WriteAllBytes(newName, data);
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void IDeobfuscatedFile.StringDecryptersAdded() {
|
|
|
|
|
UpdateDynamicStringInliner();
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
2012-07-29 19:19:12 +08:00
|
|
|
|
|
2013-01-19 20:03:57 +08:00
|
|
|
|
void IDeobfuscatedFile.SetDeobfuscator(IDeobfuscator deob) {
|
2012-07-29 19:19:12 +08:00
|
|
|
|
this.deob = deob;
|
|
|
|
|
}
|
2012-11-18 10:17:53 +08:00
|
|
|
|
|
|
|
|
|
public void Dispose() {
|
2013-01-19 20:03:57 +08:00
|
|
|
|
DeobfuscateCleanUp();
|
2012-11-18 10:17:53 +08:00
|
|
|
|
if (module != null)
|
|
|
|
|
module.Dispose();
|
2012-11-21 20:57:13 +08:00
|
|
|
|
if (deob != null)
|
|
|
|
|
deob.Dispose();
|
2012-11-18 10:17:53 +08:00
|
|
|
|
module = null;
|
|
|
|
|
deob = null;
|
|
|
|
|
}
|
2011-09-22 10:55:30 +08:00
|
|
|
|
}
|
|
|
|
|
}
|