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(),