/*
    Copyright (C) 2011-2015 de4dot@gmail.com

    This file is part of de4dot.

    de4dot is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    de4dot is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with de4dot.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections.Generic;
using dnlib.DotNet;
using dnlib.IO;
using de4dot.blocks;
using de4dot.blocks.cflow;

namespace de4dot.code.deobfuscators.Confuser {
	public class DeobfuscatorInfo : DeobfuscatorInfoBase {
		public const string THE_NAME = "Confuser";
		public const string THE_TYPE = "cr";
		const string DEFAULT_REGEX = DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
		BoolOption removeAntiDebug;
		BoolOption removeAntiDump;
		BoolOption decryptMainAsm;

		public DeobfuscatorInfo()
			: base(DEFAULT_REGEX) {
			removeAntiDebug = new BoolOption(null, MakeArgName("antidb"), "Remove anti debug code", true);
			removeAntiDump = new BoolOption(null, MakeArgName("antidump"), "Remove anti dump code", true);
			decryptMainAsm = new BoolOption(null, MakeArgName("decrypt-main"), "Decrypt main embedded assembly", true);
		}

		public override string Name {
			get { return THE_NAME; }
		}

		public override string Type {
			get { return THE_TYPE; }
		}

		public override IDeobfuscator CreateDeobfuscator() {
			return new Deobfuscator(new Deobfuscator.Options {
				ValidNameRegex = validNameRegex.Get(),
				RemoveAntiDebug = removeAntiDebug.Get(),
				RemoveAntiDump = removeAntiDump.Get(),
				DecryptMainAsm = decryptMainAsm.Get(),
			});
		}

		protected override IEnumerable<Option> GetOptionsInternal() {
			return new List<Option>() {
				removeAntiDebug,
				removeAntiDump,
				decryptMainAsm,
			};
		}
	}

	class Deobfuscator : DeobfuscatorBase, IStringDecrypter {
		Options options;
		string obfuscatorName = DeobfuscatorInfo.THE_NAME;
		Version approxVersion;

		List<EmbeddedAssemblyInfo> embeddedAssemblyInfos = new List<EmbeddedAssemblyInfo>();
		JitMethodsDecrypter jitMethodsDecrypter;
		MemoryMethodsDecrypter memoryMethodsDecrypter;
		ProxyCallFixer proxyCallFixer;
		AntiDebugger antiDebugger;
		AntiDumping antiDumping;
		ResourceDecrypter resourceDecrypter;
		ConstantsDecrypterV18 constantsDecrypterV18;
		ConstantsDecrypterV17 constantsDecrypterV17;
		ConstantsDecrypterV15 constantsDecrypterV15;
		Int32ValueInliner int32ValueInliner;
		Int64ValueInliner int64ValueInliner;
		SingleValueInliner singleValueInliner;
		DoubleValueInliner doubleValueInliner;
		StringDecrypter stringDecrypter;
		Unpacker unpacker;
		EmbeddedAssemblyInfo mainAsmInfo;
		RealAssemblyInfo realAssemblyInfo;

		bool startedDeobfuscating = false;

		internal class Options : OptionsBase {
			public bool RemoveAntiDebug { get; set; }
			public bool RemoveAntiDump { get; set; }
			public bool DecryptMainAsm { get; set; }
		}

		public override string Type {
			get { return DeobfuscatorInfo.THE_TYPE; }
		}

		public override string TypeLong {
			get { return DeobfuscatorInfo.THE_NAME; }
		}

		public override string Name {
			get { return obfuscatorName; }
		}

		public override IEnumerable<IBlocksDeobfuscator> BlocksDeobfuscators {
			get {
				var list = new List<IBlocksDeobfuscator>();
				list.Add(new ConstantsFolder { ExecuteIfNotModified = true });

				// Add this one last so all cflow is deobfuscated whenever it executes
				if (!startedDeobfuscating && int32ValueInliner != null)
					list.Add(new ConstantsInliner(int32ValueInliner, int64ValueInliner, singleValueInliner, doubleValueInliner) { ExecuteIfNotModified = true });

				return list;
			}
		}

		public Deobfuscator(Options options)
			: base(options) {
			this.options = options;
			StringFeatures = StringFeatures.AllowStaticDecryption | StringFeatures.AllowDynamicDecryption;
		}

		protected override int DetectInternal() {
			int val = 0;

			int sum = ToInt32(jitMethodsDecrypter != null ? jitMethodsDecrypter.Detected : false) +
					ToInt32(memoryMethodsDecrypter != null ? memoryMethodsDecrypter.Detected : false) +
					ToInt32(proxyCallFixer != null ? proxyCallFixer.Detected : false) +
					ToInt32(antiDebugger != null ? antiDebugger.Detected : false) +
					ToInt32(antiDumping != null ? antiDumping.Detected : false) +
					ToInt32(resourceDecrypter != null ? resourceDecrypter.Detected : false) +
					ToInt32(constantsDecrypterV18 != null ? constantsDecrypterV18.Detected : false) +
					ToInt32(constantsDecrypterV15 != null ? constantsDecrypterV15.Detected : false) +
					ToInt32(constantsDecrypterV17 != null ? constantsDecrypterV17.Detected : false) +
					ToInt32(stringDecrypter != null ? stringDecrypter.Detected : false) +
					ToInt32(unpacker != null ? unpacker.Detected : false);
			if (sum > 0)
				val += 100 + 10 * (sum - 1);

			return val;
		}

		protected override void ScanForObfuscator() {
			RemoveObfuscatorAttribute();
			jitMethodsDecrypter = new JitMethodsDecrypter(module, DeobfuscatedFile);
			try {
				jitMethodsDecrypter.Find();
			}
			catch {
			}
			if (jitMethodsDecrypter.Detected) {
				InitializeObfuscatorName();
				return;
			}
			memoryMethodsDecrypter = new MemoryMethodsDecrypter(module, DeobfuscatedFile);
			memoryMethodsDecrypter.Find();
			if (memoryMethodsDecrypter.Detected) {
				InitializeObfuscatorName();
				return;
			}
			InitializeTheRest(null);
		}

		void InitializeTheRest(Deobfuscator oldOne) {
			resourceDecrypter = new ResourceDecrypter(module, DeobfuscatedFile);
			resourceDecrypter.Find();

			constantsDecrypterV18 = new ConstantsDecrypterV18(module, GetFileData(), DeobfuscatedFile);
			constantsDecrypterV17 = new ConstantsDecrypterV17(module, GetFileData(), DeobfuscatedFile);
			constantsDecrypterV15 = new ConstantsDecrypterV15(module, GetFileData(), DeobfuscatedFile);
			do {
				constantsDecrypterV18.Find();
				if (constantsDecrypterV18.Detected) {
					InitializeConstantsDecrypterV18();
					break;
				}
				constantsDecrypterV17.Find();
				if (constantsDecrypterV17.Detected) {
					InitializeConstantsDecrypterV17();
					break;
				}
				constantsDecrypterV15.Find();
				if (constantsDecrypterV15.Detected) {
					InitializeConstantsDecrypterV15();
					break;
				}
			} while (false);

			proxyCallFixer = new ProxyCallFixer(module, GetFileData());
			proxyCallFixer.FindDelegateCreator(DeobfuscatedFile);
			antiDebugger = new AntiDebugger(module);
			antiDebugger.Find();
			antiDumping = new AntiDumping(module);
			antiDumping.Find(DeobfuscatedFile);
			stringDecrypter = new StringDecrypter(module);
			stringDecrypter.Find(DeobfuscatedFile);
			InitializeStringDecrypter();
			unpacker = new Unpacker(module, oldOne == null ? null : oldOne.unpacker);
			unpacker.Find(DeobfuscatedFile, this);
			InitializeObfuscatorName();
		}

		void InitializeObfuscatorName() {
			var versionString = GetVersionString();
			if (string.IsNullOrEmpty(versionString))
				obfuscatorName = DeobfuscatorInfo.THE_NAME;
			else
				obfuscatorName = string.Format("{0} {1}", DeobfuscatorInfo.THE_NAME, versionString);
		}

		const bool useAttributeVersion = true;
		string GetVersionString() {
			var versionProviders = new IVersionProvider[] {
				jitMethodsDecrypter,
				memoryMethodsDecrypter,
				proxyCallFixer,
				antiDebugger,
				antiDumping,
				resourceDecrypter,
				constantsDecrypterV18,
				constantsDecrypterV17,
				constantsDecrypterV15,
				stringDecrypter,
				unpacker,
			};

			var vd = new VersionDetector();
			foreach (var versionProvider in versionProviders) {
				if (versionProvider == null)
					continue;
				int minRev, maxRev;
				if (versionProvider.GetRevisionRange(out minRev, out maxRev)) {
					if (maxRev == int.MaxValue)
						Logger.v("r{0}-latest : {1}", minRev, versionProvider.GetType().Name);
					else
						Logger.v("r{0}-r{1} : {2}", minRev, maxRev, versionProvider.GetType().Name);
					vd.AddRevs(minRev, maxRev);
				}
			}
			if (useAttributeVersion)
				vd.SetVersion(approxVersion);
			return vd.GetVersionString();
		}

		byte[] GetFileData() {
			if (ModuleBytes != null)
				return ModuleBytes;
			return ModuleBytes = DeobUtils.ReadModule(module);
		}

		[Flags]
		enum DecryptState {
			CanDecryptMethods = 1,
			CanUnpack = 2,
		}
		DecryptState decryptState = DecryptState.CanDecryptMethods | DecryptState.CanUnpack;
		bool hasUnpacked = false;
		public override bool GetDecryptedModule(int count, ref byte[] newFileData, ref DumpedMethods dumpedMethods) {
			hasUnpacked = false;
			byte[] fileData = GetFileData();

			using (var peImage = new MyPEImage(fileData)) {
				if ((decryptState & DecryptState.CanDecryptMethods) != 0) {
					bool decrypted = false;
					if (jitMethodsDecrypter != null && jitMethodsDecrypter.Detected) {
						jitMethodsDecrypter.Initialize();
						if (!jitMethodsDecrypter.Decrypt(peImage, fileData, ref dumpedMethods))
							return false;
						decrypted = true;
					}
					else if (memoryMethodsDecrypter != null && memoryMethodsDecrypter.Detected) {
						memoryMethodsDecrypter.Initialize();
						if (!memoryMethodsDecrypter.Decrypt(peImage, fileData))
							return false;
						decrypted = true;
					}

					if (decrypted) {
						decryptState &= ~DecryptState.CanDecryptMethods;
						decryptState |= DecryptState.CanUnpack;
						newFileData = fileData;
						ModuleBytes = newFileData;
						return true;
					}
				}
			}

			if ((decryptState & DecryptState.CanUnpack) != 0) {
				if (unpacker != null && unpacker.Detected) {
					if (options.DecryptMainAsm) {
						decryptState |= DecryptState.CanDecryptMethods | DecryptState.CanUnpack;
						var mainInfo = unpacker.UnpackMainAssembly(true);
						newFileData = mainInfo.data;
						realAssemblyInfo = mainInfo.realAssemblyInfo;
						embeddedAssemblyInfos.AddRange(unpacker.GetEmbeddedAssemblyInfos());
						ModuleBytes = newFileData;
						hasUnpacked = true;
						return true;
					}
					else {
						decryptState &= ~DecryptState.CanUnpack;
						mainAsmInfo = unpacker.UnpackMainAssembly(false);
						embeddedAssemblyInfos.AddRange(unpacker.GetEmbeddedAssemblyInfos());
						return false;
					}
				}
			}

			return false;
		}

		public override IDeobfuscator ModuleReloaded(ModuleDefMD module) {
			if (module.Assembly != null)
				realAssemblyInfo = null;
			if (realAssemblyInfo != null) {
				realAssemblyInfo.realAssembly.Modules.Insert(0, module);
				if (realAssemblyInfo.entryPointToken != 0)
					module.EntryPoint = module.ResolveToken((int)realAssemblyInfo.entryPointToken) as MethodDef;
				module.Kind = realAssemblyInfo.kind;
				module.Name = new UTF8String(realAssemblyInfo.moduleName);
			}

			var newOne = new Deobfuscator(options);
			DeobfuscatedFile.SetDeobfuscator(newOne);
			newOne.realAssemblyInfo = realAssemblyInfo;
			newOne.decryptState = decryptState;
			newOne.DeobfuscatedFile = DeobfuscatedFile;
			newOne.ModuleBytes = ModuleBytes;
			newOne.embeddedAssemblyInfos.AddRange(embeddedAssemblyInfos);
			newOne.SetModule(module);
			newOne.RemoveObfuscatorAttribute();
			newOne.jitMethodsDecrypter = hasUnpacked ? new JitMethodsDecrypter(module, DeobfuscatedFile) :
						new JitMethodsDecrypter(module, DeobfuscatedFile, jitMethodsDecrypter);
			if ((newOne.decryptState & DecryptState.CanDecryptMethods) != 0) {
				try {
					newOne.jitMethodsDecrypter.Find();
				}
				catch {
				}
				if (newOne.jitMethodsDecrypter.Detected)
					return newOne;
			}
			newOne.memoryMethodsDecrypter = hasUnpacked ? new MemoryMethodsDecrypter(module, DeobfuscatedFile) :
						new MemoryMethodsDecrypter(module, DeobfuscatedFile, memoryMethodsDecrypter);
			if ((newOne.decryptState & DecryptState.CanDecryptMethods) != 0) {
				newOne.memoryMethodsDecrypter.Find();
				if (newOne.memoryMethodsDecrypter.Detected)
					return newOne;
			}
			newOne.InitializeTheRest(this);
			return newOne;
		}

		public override void DeobfuscateBegin() {
			base.DeobfuscateBegin();

			Logger.v("Detected {0}", obfuscatorName);

			InitializeConstantsDecrypterV18();
			InitializeConstantsDecrypterV17();
			InitializeConstantsDecrypterV15();
			InitializeStringDecrypter();

			if (jitMethodsDecrypter != null) {
				AddModuleCctorInitCallToBeRemoved(jitMethodsDecrypter.InitMethod);
				AddTypeToBeRemoved(jitMethodsDecrypter.Type, "Methods decrypter (JIT) type");
			}

			if (memoryMethodsDecrypter != null) {
				AddModuleCctorInitCallToBeRemoved(memoryMethodsDecrypter.InitMethod);
				AddTypeToBeRemoved(memoryMethodsDecrypter.Type, "Methods decrypter (memory) type");
			}

			if (options.RemoveAntiDebug && antiDebugger != null) {
				AddModuleCctorInitCallToBeRemoved(antiDebugger.InitMethod);
				AddTypeToBeRemoved(antiDebugger.Type, "Anti debugger type");
				if (antiDebugger.Type == DotNetUtils.GetModuleType(module))
					AddMethodToBeRemoved(antiDebugger.InitMethod, "Anti debugger method");
			}

			if (options.RemoveAntiDump && antiDumping != null) {
				AddModuleCctorInitCallToBeRemoved(antiDumping.InitMethod);
				AddTypeToBeRemoved(antiDumping.Type, "Anti dumping type");
			}

			if (proxyCallFixer != null)
				proxyCallFixer.Find();

			RemoveInvalidResources();
			DumpEmbeddedAssemblies();

			startedDeobfuscating = true;
		}

		void DumpEmbeddedAssemblies() {
			if (mainAsmInfo != null) {
				var asm = module.Assembly;
				var name = (asm == null ? module.Name : asm.Name).String;
				DeobfuscatedFile.CreateAssemblyFile(mainAsmInfo.data, name + "_real", mainAsmInfo.extension);
				AddResourceToBeRemoved(mainAsmInfo.resource, string.Format("Embedded assembly: {0}", mainAsmInfo.asmFullName));
			}
			foreach (var info in embeddedAssemblyInfos) {
				if (module.Assembly == null || info.asmFullName != module.Assembly.FullName)
					DeobfuscatedFile.CreateAssemblyFile(info.data, info.asmSimpleName, info.extension);
				AddResourceToBeRemoved(info.resource, string.Format("Embedded assembly: {0}", info.asmFullName));
			}
			embeddedAssemblyInfos.Clear();
		}

		void RemoveInvalidResources() {
			foreach (var rsrc in module.Resources) {
				var resource = rsrc as EmbeddedResource;
				if (resource == null)
					continue;
				if (resource.Offset != 0xFFFFFFFF)
					continue;
				AddResourceToBeRemoved(resource, "Invalid resource");
			}
		}

		bool hasInitializedStringDecrypter = false;
		void InitializeStringDecrypter() {
			if (hasInitializedStringDecrypter || (stringDecrypter== null || !stringDecrypter.Detected))
				return;
			hasInitializedStringDecrypter = true;

			DecryptResources();
			stringDecrypter.Initialize();
			staticStringInliner.Add(stringDecrypter.Method, (method, gim, args) => stringDecrypter.Decrypt(staticStringInliner.Method, (int)args[0]));
			DeobfuscatedFile.StringDecryptersAdded();
		}

		bool hasInitializedConstantsDecrypter = false;
		void InitializeConstantsDecrypterV18() {
			if (hasInitializedConstantsDecrypter || (constantsDecrypterV18 == null || !constantsDecrypterV18.Detected))
				return;
			hasInitializedConstantsDecrypter = true;

			DecryptResources();
			constantsDecrypterV18.Initialize();
			int32ValueInliner = new Int32ValueInliner();
			int64ValueInliner = new Int64ValueInliner();
			singleValueInliner = new SingleValueInliner();
			doubleValueInliner = new DoubleValueInliner();
			foreach (var info in constantsDecrypterV18.Decrypters) {
				staticStringInliner.Add(info.method, (method, gim, args) => constantsDecrypterV18.DecryptString(method, gim, (uint)args[0], (ulong)args[1]));
				int32ValueInliner.Add(info.method, (method, gim, args) => constantsDecrypterV18.DecryptInt32(method, gim, (uint)args[0], (ulong)args[1]));
				int64ValueInliner.Add(info.method, (method, gim, args) => constantsDecrypterV18.DecryptInt64(method, gim, (uint)args[0], (ulong)args[1]));
				singleValueInliner.Add(info.method, (method, gim, args) => constantsDecrypterV18.DecryptSingle(method, gim, (uint)args[0], (ulong)args[1]));
				doubleValueInliner.Add(info.method, (method, gim, args) => constantsDecrypterV18.DecryptDouble(method, gim, (uint)args[0], (ulong)args[1]));
			}
			DeobfuscatedFile.StringDecryptersAdded();
			AddTypesToBeRemoved(constantsDecrypterV18.Types, "Constants decrypter type");
			AddFieldsToBeRemoved(constantsDecrypterV18.Fields, "Constants decrypter field");
			AddMethodToBeRemoved(constantsDecrypterV18.NativeMethod, "Constants decrypter native method");
			AddTypeToBeRemoved(constantsDecrypterV18.LzmaType, "LZMA type");
			AddResourceToBeRemoved(constantsDecrypterV18.Resource, "Encrypted constants");
		}

		bool hasInitializedConstantsDecrypter15 = false;
		void InitializeConstantsDecrypterV15() {
			Initialize(constantsDecrypterV15, ref hasInitializedConstantsDecrypter15);
		}

		bool hasInitializedConstantsDecrypter17 = false;
		void InitializeConstantsDecrypterV17() {
			Initialize(constantsDecrypterV17, ref hasInitializedConstantsDecrypter17);
		}

		void Initialize(ConstantsDecrypterBase constDecrypter, ref bool hasInitialized) {
			if (hasInitialized || (constDecrypter == null || !constDecrypter.Detected))
				return;
			hasInitializedConstantsDecrypter15 = true;

			DecryptResources();
			constDecrypter.Initialize();
			int32ValueInliner = new Int32ValueInliner();
			int64ValueInliner = new Int64ValueInliner();
			singleValueInliner = new SingleValueInliner();
			doubleValueInliner = new DoubleValueInliner();
			foreach (var info in constDecrypter.DecrypterInfos) {
				staticStringInliner.Add(info.decryptMethod, (method, gim, args) => constDecrypter.DecryptString(staticStringInliner.Method, method, args));
				int32ValueInliner.Add(info.decryptMethod, (method, gim, args) => constDecrypter.DecryptInt32(int32ValueInliner.Method, method, args));
				int64ValueInliner.Add(info.decryptMethod, (method, gim, args) => constDecrypter.DecryptInt64(int64ValueInliner.Method, method, args));
				singleValueInliner.Add(info.decryptMethod, (method, gim, args) => constDecrypter.DecryptSingle(singleValueInliner.Method, method, args));
				doubleValueInliner.Add(info.decryptMethod, (method, gim, args) => constDecrypter.DecryptDouble(doubleValueInliner.Method, method, args));
			}
			int32ValueInliner.RemoveUnbox = true;
			int64ValueInliner.RemoveUnbox = true;
			singleValueInliner.RemoveUnbox = true;
			doubleValueInliner.RemoveUnbox = true;
			DeobfuscatedFile.StringDecryptersAdded();
			AddFieldsToBeRemoved(constDecrypter.Fields, "Constants decrypter field");
			var moduleType = DotNetUtils.GetModuleType(module);
			foreach (var info in constDecrypter.DecrypterInfos) {
				if (info.decryptMethod.DeclaringType == moduleType)
					AddMethodToBeRemoved(info.decryptMethod, "Constants decrypter method");
				else
					AddTypeToBeRemoved(info.decryptMethod.DeclaringType, "Constants decrypter type");
			}
			AddMethodToBeRemoved(constDecrypter.NativeMethod, "Constants decrypter native method");
			AddResourceToBeRemoved(constDecrypter.Resource, "Encrypted constants");
		}

		void DecryptResources() {
			var rsrc = resourceDecrypter.MergeResources();
			if (rsrc == null)
				return;
			AddResourceToBeRemoved(rsrc, "Encrypted resources");
			AddMethodToBeRemoved(resourceDecrypter.Handler, "Resource decrypter handler");
			AddFieldsToBeRemoved(resourceDecrypter.Fields, "Resource decrypter field");
			AddTypeToBeRemoved(resourceDecrypter.LzmaType, "LZMA type");
		}

		void RemoveObfuscatorAttribute() {
			foreach (var type in module.Types) {
				if (type.FullName == "ConfusedByAttribute") {
					SetConfuserVersion(type);
					AddAttributeToBeRemoved(type, "Obfuscator attribute");
					break;
				}
			}
		}

		void SetConfuserVersion(TypeDef type) {
			var s = DotNetUtils.GetCustomArgAsString(GetModuleAttribute(type) ?? GetAssemblyAttribute(type), 0);
			if (s == null)
				return;
			var val = System.Text.RegularExpressions.Regex.Match(s, @"^Confuser v(\d+)\.(\d+)\.(\d+)\.(\d+)$");
			if (val.Groups.Count < 5)
				return;
			approxVersion = new Version(int.Parse(val.Groups[1].ToString()),
										int.Parse(val.Groups[2].ToString()),
										int.Parse(val.Groups[3].ToString()),
										int.Parse(val.Groups[4].ToString()));
		}

		public override void DeobfuscateMethodEnd(Blocks blocks) {
			if (proxyCallFixer != null)
				proxyCallFixer.Deobfuscate(blocks);
			resourceDecrypter.Deobfuscate(blocks);
			unpacker.Deobfuscate(blocks);
			if (int32ValueInliner != null) {
				int32ValueInliner.Decrypt(blocks);
				int64ValueInliner.Decrypt(blocks);
				singleValueInliner.Decrypt(blocks);
				doubleValueInliner.Decrypt(blocks);
			}
			base.DeobfuscateMethodEnd(blocks);
		}

		public override void DeobfuscateEnd() {
			if (proxyCallFixer != null) {
				if (RemoveProxyDelegates(proxyCallFixer))
					AddFieldsToBeRemoved(proxyCallFixer.Fields, "Proxy delegate instance field");
				proxyCallFixer.CleanUp();
			}
			if (constantsDecrypterV18 != null)
				constantsDecrypterV18.CleanUp();

			if (CanRemoveStringDecrypterType) {
				if (stringDecrypter != null) {
					AddMethodToBeRemoved(stringDecrypter.Method, "String decrypter method");
					AddResourceToBeRemoved(stringDecrypter.Resource, "Encrypted strings");
				}
			}

			module.IsILOnly = true;

			base.DeobfuscateEnd();
		}

		public override IEnumerable<int> GetStringDecrypterMethods() {
			var list = new List<int>();
			if (stringDecrypter != null && stringDecrypter.Method != null)
				list.Add(stringDecrypter.Method.MDToken.ToInt32());
			if (constantsDecrypterV15 != null) {
				foreach (var info in constantsDecrypterV15.DecrypterInfos)
					list.Add(info.decryptMethod.MDToken.ToInt32());
			}
			if (constantsDecrypterV17 != null) {
				foreach (var info in constantsDecrypterV17.DecrypterInfos)
					list.Add(info.decryptMethod.MDToken.ToInt32());
			}
			if (constantsDecrypterV18 != null) {
				foreach (var info in constantsDecrypterV18.Decrypters)
					list.Add(info.method.MDToken.ToInt32());
			}
			return list;
		}

		string IStringDecrypter.ReadUserString(uint token) {
			if (jitMethodsDecrypter == null)
				return null;
			return ((IStringDecrypter)jitMethodsDecrypter).ReadUserString(token);
		}

		protected override void Dispose(bool disposing) {
			if (disposing) {
				if (proxyCallFixer != null)
					proxyCallFixer.Dispose();
				proxyCallFixer = null;
			}
			base.Dispose(disposing);
		}
	}
}
