diff --git a/de4dot.code/PE/MetadataTables.cs b/de4dot.code/PE/MetadataTables.cs
index 3fd81872..8b5f65c3 100644
--- a/de4dot.code/PE/MetadataTables.cs
+++ b/de4dot.code/PE/MetadataTables.cs
@@ -43,13 +43,14 @@ namespace de4dot.code.PE {
reader.BaseStream.Position = fileOffset;
}
+ // TODO: This table needs to be updated to support the other metadata tables.
static MetadataVarType[] metadataVarType = new MetadataVarType[] {
MVT.byte2, MVT.stringIndex, MVT.guidIndex, MVT.guidIndex, MVT.guidIndex, MVT.end, // 0
MVT.resolutionScope, MVT.stringIndex, MVT.stringIndex, MVT.end, // 1
MVT.byte4, MVT.stringIndex, MVT.stringIndex, MVT.typeDefOrRef, MVT.fieldIndex, MVT.methodDefIndex, MVT.end, // 2
MVT.end, // 3
MVT.byte2, MVT.stringIndex, MVT.blobIndex, MVT.end, // 4
- MVT.end, // 5
+ MVT.methodDefIndex, MVT.end, // 5
MVT.byte4, MVT.byte2, MVT.byte2, MVT.stringIndex, MVT.blobIndex, MVT.paramIndex, MVT.end, // 6
MVT.end, // 7
MVT.byte2, MVT.byte2, MVT.stringIndex, MVT.end, // 8
@@ -113,7 +114,7 @@ namespace de4dot.code.PE {
};
void init() {
- var streamTable = metadata.getStream("#~");
+ var streamTable = metadata.getStream("#~") ?? metadata.getStream("#-");
if (streamTable == null)
throw new ApplicationException("Could not find #~ stream");
diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index 91f95806..f69d657e 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -164,6 +164,9 @@
+
+
+
diff --git a/de4dot.code/deobfuscators/DeobUtils.cs b/de4dot.code/deobfuscators/DeobUtils.cs
index 45d52c5a..f09b1616 100644
--- a/de4dot.code/deobfuscators/DeobUtils.cs
+++ b/de4dot.code/deobfuscators/DeobUtils.cs
@@ -50,6 +50,16 @@ namespace de4dot.code.deobfuscators {
return newDef;
}
+ public static ModuleReference lookup(ModuleDefinition module, ModuleReference other, string errorMessage) {
+ if (other == null)
+ return null;
+ foreach (var modRef in module.ModuleReferences) {
+ if (modRef.MetadataToken.ToInt32() == other.MetadataToken.ToInt32())
+ return modRef;
+ }
+ throw new ApplicationException(errorMessage);
+ }
+
public static byte[] readModule(ModuleDefinition module) {
return Utils.readFile(module.FullyQualifiedName);
}
diff --git a/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs
new file mode 100644
index 00000000..d39b46a6
--- /dev/null
+++ b/de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs
@@ -0,0 +1,121 @@
+/*
+ Copyright (C) 2011-2012 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.Collections.Generic;
+using Mono.Cecil;
+using Mono.MyStuff;
+
+namespace de4dot.code.deobfuscators.MaxtoCode {
+ public class DeobfuscatorInfo : DeobfuscatorInfoBase {
+ public const string THE_NAME = "MaxtoCode";
+ public const string THE_TYPE = "mc";
+ const string DEFAULT_REGEX = @"!^[oO01l]{4,}$&" + DeobfuscatorBase.DEFAULT_VALID_NAME_REGEX;
+ public DeobfuscatorInfo()
+ : base(DEFAULT_REGEX) {
+ }
+
+ 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 {
+ RenameResourcesInCode = false,
+ ValidNameRegex = validNameRegex.get(),
+ });
+ }
+ }
+
+ class Deobfuscator : DeobfuscatorBase {
+ Options options;
+ MainType mainType;
+
+ internal class Options : OptionsBase {
+ }
+
+ public override string Type {
+ get { return DeobfuscatorInfo.THE_TYPE; }
+ }
+
+ public override string TypeLong {
+ get { return DeobfuscatorInfo.THE_NAME; }
+ }
+
+ public override string Name {
+ get { return DeobfuscatorInfo.THE_NAME; }
+ }
+
+ internal Deobfuscator(Options options)
+ : base(options) {
+ this.options = options;
+ }
+
+ protected override int detectInternal() {
+ int val = 0;
+
+ if (mainType.Detected)
+ val = 150;
+
+ return val;
+ }
+
+ protected override void scanForObfuscator() {
+ mainType = new MainType(module);
+ mainType.find();
+ }
+
+ public override bool getDecryptedModule(ref byte[] newFileData, ref DumpedMethods dumpedMethods) {
+ if (!mainType.Detected)
+ return false;
+
+ var fileDecrypter = new FileDecrypter(mainType);
+
+ var fileData = DeobUtils.readModule(module);
+ if (!fileDecrypter.decrypt(fileData, ref dumpedMethods))
+ return false;
+
+ newFileData = fileData;
+ return true;
+ }
+
+ public override IDeobfuscator moduleReloaded(ModuleDefinition module) {
+ var newOne = new Deobfuscator(options);
+ newOne.setModule(module);
+ newOne.mainType = new MainType(module, mainType);
+ return newOne;
+ }
+
+ public override void deobfuscateBegin() {
+ base.deobfuscateBegin();
+
+ foreach (var method in mainType.InitMethods)
+ addCctorInitCallToBeRemoved(method);
+ addTypeToBeRemoved(mainType.Type, "Obfuscator type");
+ addModuleReferencesToBeRemoved(mainType.ModuleReferences, "MC runtime module reference");
+ }
+
+ public override IEnumerable getStringDecrypterMethods() {
+ return new List();
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs
new file mode 100644
index 00000000..8102c96c
--- /dev/null
+++ b/de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs
@@ -0,0 +1,664 @@
+/*
+ Copyright (C) 2011-2012 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 Mono.MyStuff;
+using de4dot.code.PE;
+
+namespace de4dot.code.deobfuscators.MaxtoCode {
+ // Decrypts methods and resources
+ class FileDecrypter {
+ MainType mainType;
+
+ class PeHeader {
+ const int XOR_KEY = 0x7ABF931;
+
+ byte[] headerData;
+ uint rvaDispl1;
+ uint rvaDispl2;
+
+ public PeHeader(MainType mainType, PeImage peImage) {
+ headerData = getPeHeaderData(peImage);
+
+ if (!mainType.IsOld && peImage.readUInt32(0x2008) != 0x48) {
+ rvaDispl1 = readUInt32(0x0FB0) ^ XOR_KEY;
+ rvaDispl2 = readUInt32(0x0FB4) ^ XOR_KEY;
+ }
+ }
+
+ public uint getMcHeaderRva() {
+ return getRva2(0x0FFC, XOR_KEY);
+ }
+
+ public uint getRva1(int offset, uint xorKey) {
+ return (readUInt32(offset) ^ xorKey) - rvaDispl1;
+ }
+
+ public uint getRva2(int offset, uint xorKey) {
+ return (readUInt32(offset) ^ xorKey) - rvaDispl2;
+ }
+
+ public uint readUInt32(int offset) {
+ return BitConverter.ToUInt32(headerData, offset);
+ }
+
+ static byte[] getPeHeaderData(PeImage peImage) {
+ var data = new byte[0x1000];
+
+ var firstSection = peImage.Sections[0];
+ readTo(peImage, data, 0, 0, firstSection.pointerToRawData);
+
+ foreach (var section in peImage.Sections) {
+ if (section.virtualAddress >= data.Length)
+ continue;
+ int offset = (int)section.virtualAddress;
+ readTo(peImage, data, offset, section.pointerToRawData, section.sizeOfRawData);
+ }
+
+ return data;
+ }
+
+ static void readTo(PeImage peImage, byte[] data, int destOffset, uint imageOffset, uint maxLength) {
+ if (destOffset > data.Length)
+ return;
+ int len = Math.Min(data.Length - destOffset, (int)maxLength);
+ var newData = peImage.offsetReadBytes(imageOffset, len);
+ Array.Copy(newData, 0, data, destOffset, newData.Length);
+ }
+ }
+
+ class McHeader {
+ PeHeader peHeader;
+ byte[] data;
+
+ public byte this[int index] {
+ get { return data[index]; }
+ }
+
+ public McHeader(PeImage peImage, PeHeader peHeader) {
+ this.peHeader = peHeader;
+ this.data = peImage.readBytes(peHeader.getMcHeaderRva(), 0x2000);
+ }
+
+ public byte readByte(int offset) {
+ return data[offset];
+ }
+
+ public void readBytes(int offset, Array dest, int size) {
+ Buffer.BlockCopy(data, offset, dest, 0, size);
+ }
+
+ public uint readUInt32(int offset) {
+ return BitConverter.ToUInt32(data, offset);
+ }
+ }
+
+ enum EncryptionVersion {
+ Unknown,
+ V1,
+ V2,
+ V3,
+ }
+
+ class EncryptionInfo {
+ public uint MagicLo { get; set; }
+ public uint MagicHi { get; set; }
+ public EncryptionVersion Version { get; set; }
+ }
+
+ static EncryptionInfo[] encryptionInfos_Rva900h = new EncryptionInfo[] {
+ // PE header timestamp
+ // 462FA2D2 = Wed, 25 Apr 2007 18:49:54 (3.20)
+ new EncryptionInfo {
+ MagicLo = 0xA098B387,
+ MagicHi = 0x1E8EBCA3,
+ Version = EncryptionVersion.V1,
+ },
+ // 482384FB = Thu, 08 May 2008 22:55:55 (3.36)
+ new EncryptionInfo {
+ MagicLo = 0xAA98B387,
+ MagicHi = 0x1E8EECA3,
+ Version = EncryptionVersion.V2,
+ },
+ // 4A5EEC64 = Thu, 16 Jul 2009 09:01:24
+ // 4C6220EC = Wed, 11 Aug 2010 04:02:52
+ // 4C622357 = Wed, 11 Aug 2010 04:13:11
+ new EncryptionInfo {
+ MagicLo = 0xAA98B387,
+ MagicHi = 0x128EECA3,
+ Version = EncryptionVersion.V2,
+ },
+ // 4C6E4605 = Fri, 20 Aug 2010 09:08:21
+ // 4D0E220D = Sun, 19 Dec 2010 15:17:33
+ // 4DC2FC75 = Thu, 05 May 2011 19:37:25
+ // 4DFA3D5D = Thu, 16 Jun 2011 17:29:01
+ new EncryptionInfo {
+ MagicLo = 0xAA98B387,
+ MagicHi = 0xF28EECA3,
+ Version = EncryptionVersion.V2,
+ },
+ // 4DC2FE0C = Thu, 05 May 2011 19:44:12
+ new EncryptionInfo {
+ MagicLo = 0xAA98B387,
+ MagicHi = 0xF28EEAA3,
+ Version = EncryptionVersion.V2,
+ },
+ // 4ED76740 = Thu, 01 Dec 2011 11:38:40
+ // 4EE1FAD1 = Fri, 09 Dec 2011 12:10:57
+ new EncryptionInfo {
+ MagicLo = 0xAA983B87,
+ MagicHi = 0xF28EECA3,
+ Version = EncryptionVersion.V3,
+ },
+ };
+
+ static EncryptionInfo[] encryptionInfos_McHeader8C0h = new EncryptionInfo[] {
+ // 462FA2D2 = Wed, 25 Apr 2007 18:49:54 (3.20)
+ new EncryptionInfo {
+ MagicLo = 0x6AA13B13,
+ MagicHi = 0xD72B991F,
+ Version = EncryptionVersion.V1,
+ },
+ // 482384FB = Thu, 08 May 2008 22:55:55 (3.36)
+ new EncryptionInfo {
+ MagicLo = 0x6A713B13,
+ MagicHi = 0xD72B891F,
+ Version = EncryptionVersion.V2,
+ },
+ // 4A5EEC64 = Thu, 16 Jul 2009 09:01:24
+ // 4C6220EC = Wed, 11 Aug 2010 04:02:52
+ // 4C622357 = Wed, 11 Aug 2010 04:13:11
+ // 4C6E4605 = Fri, 20 Aug 2010 09:08:21
+ // 4D0E220D = Sun, 19 Dec 2010 15:17:33
+ // 4DC2FC75 = Thu, 05 May 2011 19:37:25
+ // 4DC2FE0C = Thu, 05 May 2011 19:44:12
+ // 4DFA3D5D = Thu, 16 Jun 2011 17:29:01
+ new EncryptionInfo {
+ MagicLo = 0x6A713B13,
+ MagicHi = 0xD72B891F,
+ Version = EncryptionVersion.V2,
+ },
+ // 4ED76740 = Thu, 01 Dec 2011 11:38:40
+ // 4EE1FAD1 = Fri, 09 Dec 2011 12:10:57
+ new EncryptionInfo {
+ MagicLo = 0x6A731B13,
+ MagicHi = 0xD72B891F,
+ Version = EncryptionVersion.V3,
+ },
+ };
+
+ class MethodInfos {
+ MainType mainType;
+ PeImage peImage;
+ PeHeader peHeader;
+ McHeader mcHeader;
+ uint structSize;
+ uint methodInfosOffset;
+ uint encryptedDataOffset;
+ uint xorKey;
+ Dictionary infos = new Dictionary();
+ IDecrypter decrypter;
+ const int ENCRYPTED_DATA_INFO_SIZE = 0x13;
+
+ public class DecryptedMethodInfo {
+ public uint bodyRva;
+ public byte[] body;
+
+ public DecryptedMethodInfo(uint bodyRva, byte[] body) {
+ this.bodyRva = bodyRva;
+ this.body = body;
+ }
+ }
+
+ public MethodInfos(MainType mainType, PeImage peImage, PeHeader peHeader, McHeader mcHeader) {
+ this.mainType = mainType;
+ this.peImage = peImage;
+ this.peHeader = peHeader;
+ this.mcHeader = mcHeader;
+
+ structSize = getStructSize(mcHeader);
+
+ uint methodInfosRva = peHeader.getRva2(0x0FF8, mcHeader.readUInt32(0x005A));
+ uint encryptedDataRva = peHeader.getRva2(0x0FF0, mcHeader.readUInt32(0x0046));
+
+ methodInfosOffset = peImage.rvaToOffset(methodInfosRva);
+ encryptedDataOffset = peImage.rvaToOffset(encryptedDataRva);
+ }
+
+ static uint getStructSize(McHeader mcHeader) {
+ uint magicLo = mcHeader.readUInt32(0x8C0);
+ uint magicHi = mcHeader.readUInt32(0x8C4);
+ foreach (var info in encryptionInfos_McHeader8C0h) {
+ if (magicLo == info.MagicLo && magicHi == info.MagicHi)
+ return 0xC + 6 * ENCRYPTED_DATA_INFO_SIZE;
+ }
+ return 0xC + 3 * ENCRYPTED_DATA_INFO_SIZE;
+ }
+
+ EncryptionVersion getVersion() {
+ uint m1lo = peHeader.readUInt32(0x900);
+ uint m1hi = peHeader.readUInt32(0x904);
+ uint m2lo = mcHeader.readUInt32(0x8C0);
+ uint m2hi = mcHeader.readUInt32(0x8C4);
+
+ foreach (var info in encryptionInfos_McHeader8C0h) {
+ if (info.MagicLo == m2lo && info.MagicHi == m2hi)
+ return info.Version;
+ }
+
+ foreach (var info in encryptionInfos_Rva900h) {
+ if (info.MagicLo == m1lo && info.MagicHi == m1hi)
+ return info.Version;
+ }
+
+ Log.w("Could not detect MC version. Magic1: {0:X8} {1:X8}, Magic2: {2:X8} {3:X8}", m1lo, m1hi, m2lo, m2hi);
+ return EncryptionVersion.Unknown;
+ }
+
+ public DecryptedMethodInfo lookup(uint bodyRva) {
+ DecryptedMethodInfo info;
+ infos.TryGetValue(bodyRva, out info);
+ return info;
+ }
+
+ byte readByte(uint offset) {
+ return peImage.offsetReadByte(methodInfosOffset + offset);
+ }
+
+ short readInt16(uint offset) {
+ return (short)peImage.offsetReadUInt16(methodInfosOffset + offset);
+ }
+
+ uint readUInt32(uint offset) {
+ return peImage.offsetReadUInt32(methodInfosOffset + offset);
+ }
+
+ int readInt32(uint offset) {
+ return (int)readUInt32(offset);
+ }
+
+ short readEncryptedInt16(uint offset) {
+ return (short)(readInt16(offset) ^ xorKey);
+ }
+
+ int readEncryptedInt32(uint offset) {
+ return (int)readEncryptedUInt32(offset);
+ }
+
+ uint readEncryptedUInt32(uint offset) {
+ return readUInt32(offset) ^ xorKey;
+ }
+
+ interface IDecrypter {
+ byte[] decrypt(int type, byte[] encrypted);
+ }
+
+ class DecrypterV1 : IDecrypter {
+ MethodInfos methodInfos;
+
+ public DecrypterV1(MethodInfos methodInfos) {
+ this.methodInfos = methodInfos;
+ }
+
+ public byte[] decrypt(int type, byte[] encrypted) {
+ switch (type) {
+ case 1: return methodInfos.decrypt1(encrypted);
+ case 2: return methodInfos.decrypt4(encrypted);
+ case 3: return methodInfos.decrypt2(encrypted);
+ case 4: return methodInfos.decrypt3(encrypted);
+ case 5: return methodInfos.decrypt5(encrypted);
+ case 6: return methodInfos.decrypt6(encrypted);
+ case 7: return methodInfos.decrypt7(encrypted);
+ default: throw new ApplicationException(string.Format("Invalid encryption type: {0:X2}", type));
+ }
+ }
+ }
+
+ class DecrypterV2 : IDecrypter {
+ MethodInfos methodInfos;
+
+ public DecrypterV2(MethodInfos methodInfos) {
+ this.methodInfos = methodInfos;
+ }
+
+ public byte[] decrypt(int type, byte[] encrypted) {
+ switch (type) {
+ case 1: return methodInfos.decrypt3(encrypted);
+ case 2: return methodInfos.decrypt2(encrypted);
+ case 3: return methodInfos.decrypt1(encrypted);
+ case 4: return methodInfos.decrypt4(encrypted);
+ case 5: return methodInfos.decrypt5(encrypted);
+ case 6: return methodInfos.decrypt6(encrypted);
+ case 7: return methodInfos.decrypt7(encrypted);
+ default: throw new ApplicationException(string.Format("Invalid encryption type: {0:X2}", type));
+ }
+ }
+ }
+
+ class DecrypterV3 : IDecrypter {
+ MethodInfos methodInfos;
+
+ public DecrypterV3(MethodInfos methodInfos) {
+ this.methodInfos = methodInfos;
+ }
+
+ public byte[] decrypt(int type, byte[] encrypted) {
+ switch (type) {
+ case 1: return methodInfos.decrypt1(encrypted);
+ case 2: return methodInfos.decrypt2(encrypted);
+ case 3: return methodInfos.decrypt3(encrypted);
+ case 4: return methodInfos.decrypt4(encrypted);
+ case 5: return methodInfos.decrypt5(encrypted);
+ case 6: return methodInfos.decrypt6(encrypted);
+ case 7: return methodInfos.decrypt7(encrypted);
+ default: throw new ApplicationException(string.Format("Invalid encryption type: {0:X2}", type));
+ }
+ }
+ }
+
+ void initializeDecrypter() {
+ switch (getVersion()) {
+ case EncryptionVersion.V1:
+ decrypter = new DecrypterV1(this);
+ break;
+
+ case EncryptionVersion.V2:
+ decrypter = new DecrypterV2(this);
+ break;
+
+ case EncryptionVersion.V3:
+ decrypter = new DecrypterV3(this);
+ break;
+
+ case EncryptionVersion.Unknown:
+ default:
+ throw new ApplicationException("Unknown MC version");
+ }
+ }
+
+ public void initializeInfos() {
+ initializeDecrypter();
+
+ int numMethods = readInt32(0) ^ readInt32(4);
+ if (numMethods < 0)
+ throw new ApplicationException("Invalid number of encrypted methods");
+
+ xorKey = (uint)numMethods;
+ uint rvaDispl = !mainType.IsOld && peImage.readUInt32(0x2008) != 0x48 ? 0x1000U : 0;
+ int numEncryptedDataInfos = ((int)structSize - 0xC) / ENCRYPTED_DATA_INFO_SIZE;
+ var encryptedDataInfos = new byte[numEncryptedDataInfos][];
+
+ uint offset = 8;
+ for (int i = 0; i < numMethods; i++, offset += structSize) {
+ uint methodBodyRva = readEncryptedUInt32(offset) - rvaDispl;
+ uint totalSize = readEncryptedUInt32(offset + 4);
+ uint methodInstructionRva = readEncryptedUInt32(offset + 8) - rvaDispl;
+
+ var decryptedData = new byte[totalSize];
+
+ // Read the method body header and method body (instrs + exception handlers).
+ // The method body header is always in the first one. The instrs + ex handlers
+ // are always in the last 4, and evenly divided (each byte[] is totalLen / 4).
+ // The 2nd one is for the exceptions (or padding), but it may be null.
+ uint offset2 = offset + 0xC;
+ int exOffset = 0;
+ for (int j = 0; j < encryptedDataInfos.Length; j++, offset2 += ENCRYPTED_DATA_INFO_SIZE) {
+ // readByte(offset2); <-- index
+ int encryptionType = readEncryptedInt16(offset2 + 1);
+ uint dataOffset = readEncryptedUInt32(offset2 + 3);
+ uint encryptedSize = readEncryptedUInt32(offset2 + 7);
+ uint realSize = readEncryptedUInt32(offset2 + 11);
+ if (j == 1)
+ exOffset = readEncryptedInt32(offset2 + 15);
+ if (j == 1 && exOffset == 0)
+ encryptedDataInfos[j] = null;
+ else
+ encryptedDataInfos[j] = decrypt(encryptionType, dataOffset, encryptedSize, realSize);
+ }
+
+ int copyOffset = 0;
+ copyOffset = copyData(decryptedData, encryptedDataInfos[0], copyOffset);
+ for (int j = 2; j < encryptedDataInfos.Length; j++)
+ copyOffset = copyData(decryptedData, encryptedDataInfos[j], copyOffset);
+ copyData(decryptedData, encryptedDataInfos[1], exOffset); // Exceptions or padding
+
+ var info = new DecryptedMethodInfo(methodBodyRva, decryptedData);
+ infos[info.bodyRva] = info;
+ }
+ }
+
+ static int copyData(byte[] dest, byte[] source, int offset) {
+ if (source == null)
+ return offset;
+ Array.Copy(source, 0, dest, offset, source.Length);
+ return offset + source.Length;
+ }
+
+ byte[] readData(uint offset, int size) {
+ return peImage.offsetReadBytes(encryptedDataOffset + offset, size);
+ }
+
+ byte[] decrypt(int type, uint dataOffset, uint encryptedSize, uint realSize) {
+ if (realSize == 0)
+ return null;
+ if (realSize > encryptedSize)
+ throw new ApplicationException("Invalid realSize");
+
+ var encrypted = readData(dataOffset, (int)encryptedSize);
+ var decrypted = decrypter.decrypt(type, encrypted);
+ if (realSize > decrypted.Length)
+ throw new ApplicationException("Invalid decrypted length");
+ Array.Resize(ref decrypted, (int)realSize);
+ return decrypted;
+ }
+
+ byte[] decrypt1(byte[] encrypted) {
+ var decrypted = new byte[encrypted.Length];
+ for (int i = 0; i < decrypted.Length; i++)
+ decrypted[i] = (byte)(encrypted[i] ^ mcHeader.readByte(i % 0x2000));
+ return decrypted;
+ }
+
+ byte[] decrypt2(byte[] encrypted) {
+ if ((encrypted.Length & 7) != 0)
+ throw new ApplicationException("Invalid encryption #2 length");
+ const int offset = 0x00FA;
+ uint key4 = mcHeader.readUInt32(offset + 4 * 4);
+ uint key5 = mcHeader.readUInt32(offset + 5 * 4);
+
+ byte[] decrypted = new byte[encrypted.Length & ~7];
+ var writer = new BinaryWriter(new MemoryStream(decrypted));
+
+ int loopCount = encrypted.Length / 8;
+ for (int i = 0; i < loopCount; i++) {
+ uint val0 = BitConverter.ToUInt32(encrypted, i * 8);
+ uint val1 = BitConverter.ToUInt32(encrypted, i * 8 + 4);
+ uint x = (val1 >> 26) + (val0 << 6);
+ uint y = (val0 >> 26) + (val1 << 6);
+
+ writer.Write(x ^ key4);
+ writer.Write(y ^ key5);
+ }
+
+ return decrypted;
+ }
+
+ static byte[] decrypt3Shifts = new byte[16] { 5, 11, 14, 21, 6, 20, 17, 29, 4, 10, 3, 2, 7, 1, 26, 18 };
+ byte[] decrypt3(byte[] encrypted) {
+ if ((encrypted.Length & 7) != 0)
+ throw new ApplicationException("Invalid encryption #3 length");
+ const int offset = 0x015E;
+ uint key0 = mcHeader.readUInt32(offset + 0 * 4);
+ uint key3 = mcHeader.readUInt32(offset + 3 * 4);
+
+ byte[] decrypted = new byte[encrypted.Length & ~7];
+ var writer = new BinaryWriter(new MemoryStream(decrypted));
+
+ int loopCount = encrypted.Length / 8;
+ for (int i = 0; i < loopCount; i++) {
+ uint x = BitConverter.ToUInt32(encrypted, i * 8);
+ uint y = BitConverter.ToUInt32(encrypted, i * 8 + 4);
+ foreach (var shift in decrypt3Shifts) {
+ int shift1 = 32 - shift;
+ uint x1 = (y >> shift1) + (x << shift);
+ uint y1 = (x >> shift1) + (y << shift);
+ x = x1;
+ y = y1;
+ }
+
+ writer.Write(x ^ key0);
+ writer.Write(y ^ key3);
+ }
+
+ return decrypted;
+ }
+
+ byte[] decrypt4(byte[] encrypted) {
+ var decrypted = new byte[encrypted.Length / 3 * 2 + 1];
+
+ int count = encrypted.Length / 3;
+ int i = 0, j = 0, k = 0;
+ while (count-- > 0) {
+ byte k1 = mcHeader.readByte(j + 1);
+ byte k2 = mcHeader.readByte(j + 2);
+ byte k3 = mcHeader.readByte(j + 3);
+ decrypted[k++] = (byte)(((encrypted[i + 1] ^ k2) >> 4) | ((encrypted[i] ^ k1) & 0xF0));
+ decrypted[k++] = (byte)(((encrypted[i + 1] ^ k2) << 4) + ((encrypted[i + 2] ^ k3) & 0x0F));
+ i += 3;
+ j = (j + 4) % 0x2000;
+ }
+
+ if ((encrypted.Length % 3) != 0)
+ decrypted[k] = (byte)(encrypted[i] ^ mcHeader.readByte(j));
+
+ return decrypted;
+ }
+
+ byte[] decrypt5(byte[] encrypted) {
+ throw new NotImplementedException("Encryption type #5 not implemented yet");
+ }
+
+ byte[] decrypt6(byte[] encrypted) {
+ throw new NotImplementedException("Encryption type #6 not implemented yet");
+ }
+
+ byte[] decrypt7(byte[] encrypted) {
+ throw new NotImplementedException("Encryption type #7 not implemented yet");
+ }
+ }
+
+ public FileDecrypter(MainType mainType) {
+ this.mainType = mainType;
+ }
+
+ public bool decrypt(byte[] fileData, ref DumpedMethods dumpedMethods) {
+ var peImage = new PeImage(fileData);
+ var peHeader = new PeHeader(mainType, peImage);
+ var mcHeader = new McHeader(peImage, peHeader);
+
+ dumpedMethods = decryptMethods(peImage, peHeader, mcHeader);
+ if (dumpedMethods == null)
+ return false;
+
+ decryptResources(fileData, peImage, peHeader, mcHeader);
+
+ return true;
+ }
+
+ DumpedMethods decryptMethods(PeImage peImage, PeHeader peHeader, McHeader mcHeader) {
+ var dumpedMethods = new DumpedMethods();
+
+ var methodInfos = new MethodInfos(mainType, peImage, peHeader, mcHeader);
+ methodInfos.initializeInfos();
+
+ var metadataTables = peImage.Cor20Header.createMetadataTables();
+ var methodDef = metadataTables.getMetadataType(MetadataIndex.iMethodDef);
+ uint methodDefOffset = methodDef.fileOffset;
+ for (int i = 0; i < methodDef.rows; i++, methodDefOffset += methodDef.totalSize) {
+ uint bodyRva = peImage.offsetReadUInt32(methodDefOffset);
+ if (bodyRva == 0)
+ continue;
+
+ var info = methodInfos.lookup(bodyRva);
+ if (info == null)
+ continue;
+
+ uint bodyOffset = peImage.rvaToOffset(bodyRva);
+ ushort magic = peImage.offsetReadUInt16(bodyOffset);
+ if (magic != 0xFFF3)
+ continue;
+
+ var dm = new DumpedMethod();
+ dm.token = (uint)(0x06000001 + i);
+ dm.mdImplFlags = peImage.offsetReadUInt16(methodDefOffset + (uint)methodDef.fields[1].offset);
+ dm.mdFlags = peImage.offsetReadUInt16(methodDefOffset + (uint)methodDef.fields[2].offset);
+ dm.mdName = peImage.offsetRead(methodDefOffset + (uint)methodDef.fields[3].offset, methodDef.fields[3].size);
+ dm.mdSignature = peImage.offsetRead(methodDefOffset + (uint)methodDef.fields[4].offset, methodDef.fields[4].size);
+ dm.mdParamList = peImage.offsetRead(methodDefOffset + (uint)methodDef.fields[5].offset, methodDef.fields[5].size);
+
+ var reader = new BinaryReader(new MemoryStream(info.body));
+ byte b = reader.ReadByte();
+ if ((b & 3) == 2) {
+ dm.mhFlags = 2;
+ dm.mhMaxStack = 8;
+ dm.mhCodeSize = (uint)(b >> 2);
+ dm.mhLocalVarSigTok = 0;
+ }
+ else {
+ reader.BaseStream.Position--;
+ dm.mhFlags = reader.ReadUInt16();
+ dm.mhMaxStack = reader.ReadUInt16();
+ dm.mhCodeSize = reader.ReadUInt32();
+ dm.mhLocalVarSigTok = reader.ReadUInt32();
+ uint codeOffset = (uint)(dm.mhFlags >> 12) * 4;
+ reader.BaseStream.Position += codeOffset - 12;
+ }
+
+ dm.code = reader.ReadBytes((int)dm.mhCodeSize);
+ if ((dm.mhFlags & 8) != 0) {
+ reader.BaseStream.Position = (reader.BaseStream.Position + 3) & ~3;
+ dm.extraSections = reader.ReadBytes((int)(reader.BaseStream.Length - reader.BaseStream.Position));
+ }
+
+ dumpedMethods.add(dm);
+ }
+
+ return dumpedMethods;
+ }
+
+ void decryptResources(byte[] fileData, PeImage peImage, PeHeader peHeader, McHeader mcHeader) {
+ uint resourceRva = peHeader.getRva1(0x0E10, mcHeader.readUInt32(0x00A0));
+ uint resourceSize = peHeader.readUInt32(0x0E14) ^ mcHeader.readUInt32(0x00AA);
+ if (resourceRva == 0 || resourceSize == 0)
+ return;
+ if (resourceRva != peImage.Cor20Header.resources.virtualAddress ||
+ resourceSize != peImage.Cor20Header.resources.size) {
+ Log.w("Invalid resource RVA and size found");
+ }
+
+ Log.v("Decrypting resources @ RVA {0:X8}, {1} bytes", resourceRva, resourceSize);
+
+ int resourceOffset = (int)peImage.rvaToOffset(resourceRva);
+ for (int i = 0; i < resourceSize; i++)
+ fileData[resourceOffset + i] ^= mcHeader[i % 0x2000];
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/MaxtoCode/MainType.cs b/de4dot.code/deobfuscators/MaxtoCode/MainType.cs
new file mode 100644
index 00000000..37184301
--- /dev/null
+++ b/de4dot.code/deobfuscators/MaxtoCode/MainType.cs
@@ -0,0 +1,165 @@
+/*
+ Copyright (C) 2011-2012 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.Collections.Generic;
+using Mono.Cecil;
+using de4dot.blocks;
+
+namespace de4dot.code.deobfuscators.MaxtoCode {
+ class MainType {
+ ModuleDefinition module;
+ TypeDefinition mcType;
+ ModuleReference mcModule1, mcModule2;
+ bool isOld;
+
+ public bool IsOld {
+ get { return isOld; }
+ }
+
+ public TypeDefinition Type {
+ get { return mcType; }
+ }
+
+ public IEnumerable ModuleReferences {
+ get {
+ var list = new List();
+ if (mcModule1 != null)
+ list.Add(mcModule1);
+ if (mcModule2 != null)
+ list.Add(mcModule2);
+ return list;
+ }
+ }
+
+ public IEnumerable InitMethods {
+ get {
+ var list = new List();
+ if (mcType == null)
+ return list;
+ foreach (var method in mcType.Methods) {
+ if (method.IsStatic && DotNetUtils.isMethod(method, "System.Void", "()"))
+ list.Add(method);
+ }
+ return list;
+ }
+ }
+
+ public bool Detected {
+ get { return mcType != null; }
+ }
+
+ public MainType(ModuleDefinition module) {
+ this.module = module;
+ }
+
+ public MainType(ModuleDefinition module, MainType oldOne) {
+ this.module = module;
+ this.mcType = lookup(oldOne.mcType, "Could not find main type");
+ this.mcModule1 = DeobUtils.lookup(module, oldOne.mcModule1, "Could not find MC runtime module ref #1");
+ this.mcModule2 = DeobUtils.lookup(module, oldOne.mcModule2, "Could not find MC runtime module ref #2");
+ }
+
+ T lookup(T def, string errorMessage) where T : MemberReference {
+ return DeobUtils.lookup(module, def, errorMessage);
+ }
+
+ public void find() {
+ var cctor = getCctor();
+ if (cctor == null)
+ return;
+
+ foreach (var info in DotNetUtils.getCalledMethods(module, cctor)) {
+ var method = info.Item2;
+ if (method.Name != "Startup")
+ continue;
+ if (!DotNetUtils.isMethod(method, "System.Void", "()"))
+ continue;
+
+ ModuleReference module1, module2;
+ bool isOldTmp;
+ if (!checkType(method.DeclaringType, out module1, out module2, out isOldTmp))
+ return;
+
+ mcType = method.DeclaringType;
+ mcModule1 = module1;
+ mcModule2 = module2;
+ isOld = isOldTmp;
+ break;
+ }
+ }
+
+ MethodDefinition getCctor() {
+ int checksLeft = 3;
+ foreach (var type in module.GetTypes()) {
+ var cctor = DotNetUtils.getMethod(type, ".cctor");
+ if (cctor != null)
+ return cctor;
+ if (!type.IsEnum && --checksLeft <= 0)
+ return null;
+ }
+ return null;
+ }
+
+ static bool checkType(TypeDefinition type, out ModuleReference module1, out ModuleReference module2, out bool isOld) {
+ module1 = module2 = null;
+ isOld = false;
+
+ if (DotNetUtils.getMethod(type, "Startup") == null)
+ return false;
+
+ var pinvokes = getPinvokes(type);
+ var pinvokeList = getPinvokeList(pinvokes, "CheckRuntime");
+ if (pinvokeList == null)
+ return false;
+ if (getPinvokeList(pinvokes, "MainDLL") == null)
+ return false;
+
+ // Newer versions (3.4+ ???) also have GetModuleBase()
+ isOld = getPinvokeList(pinvokes, "GetModuleBase") == null;
+
+ module1 = pinvokeList[0].PInvokeInfo.Module;
+ module2 = pinvokeList[1].PInvokeInfo.Module;
+ return true;
+ }
+
+ static Dictionary> getPinvokes(TypeDefinition type) {
+ var pinvokes = new Dictionary>(StringComparer.Ordinal);
+ foreach (var method in type.Methods) {
+ var info = method.PInvokeInfo;
+ if (info == null || info.EntryPoint == null)
+ continue;
+ List list;
+ if (!pinvokes.TryGetValue(info.EntryPoint, out list))
+ pinvokes[info.EntryPoint] = list = new List();
+ list.Add(method);
+ }
+ return pinvokes;
+ }
+
+ static List getPinvokeList(Dictionary> pinvokes, string methodName) {
+ List list;
+ if (!pinvokes.TryGetValue(methodName, out list))
+ return null;
+ if (list.Count != 2)
+ return null;
+ return list;
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs b/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs
index 205a8b35..f17d42b3 100644
--- a/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/Unknown/Deobfuscator.cs
@@ -102,8 +102,6 @@ namespace de4dot.code.deobfuscators.Unknown {
return "CodeFort";
if (type.FullName == "ZYXDNGuarder")
return "DNGuard HVM";
- if (type.FullName == "InfaceMaxtoCode")
- return "MaxtoCode";
if (type.Name.Contains("();\t"))
return "Manco .NET Obfuscator";
if (Regex.IsMatch(type.FullName, @"^EMyPID_\d+_$"))
diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs
index 530fef91..79551516 100644
--- a/de4dot.cui/Program.cs
+++ b/de4dot.cui/Program.cs
@@ -47,6 +47,7 @@ namespace de4dot.cui {
new de4dot.code.deobfuscators.dotNET_Reactor.v4.DeobfuscatorInfo(),
new de4dot.code.deobfuscators.Eazfuscator_NET.DeobfuscatorInfo(),
new de4dot.code.deobfuscators.Goliath_NET.DeobfuscatorInfo(),
+ new de4dot.code.deobfuscators.MaxtoCode.DeobfuscatorInfo(),
new de4dot.code.deobfuscators.Skater_NET.DeobfuscatorInfo(),
new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(),
new de4dot.code.deobfuscators.Spices_Net.DeobfuscatorInfo(),