Merge branch 'mc'
This commit is contained in:
commit
48d6a3b6fc
|
@ -43,13 +43,14 @@ namespace de4dot.code.PE {
|
||||||
reader.BaseStream.Position = fileOffset;
|
reader.BaseStream.Position = fileOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This table needs to be updated to support the other metadata tables.
|
||||||
static MetadataVarType[] metadataVarType = new MetadataVarType[] {
|
static MetadataVarType[] metadataVarType = new MetadataVarType[] {
|
||||||
MVT.byte2, MVT.stringIndex, MVT.guidIndex, MVT.guidIndex, MVT.guidIndex, MVT.end, // 0
|
MVT.byte2, MVT.stringIndex, MVT.guidIndex, MVT.guidIndex, MVT.guidIndex, MVT.end, // 0
|
||||||
MVT.resolutionScope, MVT.stringIndex, MVT.stringIndex, MVT.end, // 1
|
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.byte4, MVT.stringIndex, MVT.stringIndex, MVT.typeDefOrRef, MVT.fieldIndex, MVT.methodDefIndex, MVT.end, // 2
|
||||||
MVT.end, // 3
|
MVT.end, // 3
|
||||||
MVT.byte2, MVT.stringIndex, MVT.blobIndex, MVT.end, // 4
|
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.byte4, MVT.byte2, MVT.byte2, MVT.stringIndex, MVT.blobIndex, MVT.paramIndex, MVT.end, // 6
|
||||||
MVT.end, // 7
|
MVT.end, // 7
|
||||||
MVT.byte2, MVT.byte2, MVT.stringIndex, MVT.end, // 8
|
MVT.byte2, MVT.byte2, MVT.stringIndex, MVT.end, // 8
|
||||||
|
@ -113,7 +114,7 @@ namespace de4dot.code.PE {
|
||||||
};
|
};
|
||||||
|
|
||||||
void init() {
|
void init() {
|
||||||
var streamTable = metadata.getStream("#~");
|
var streamTable = metadata.getStream("#~") ?? metadata.getStream("#-");
|
||||||
if (streamTable == null)
|
if (streamTable == null)
|
||||||
throw new ApplicationException("Could not find #~ stream");
|
throw new ApplicationException("Could not find #~ stream");
|
||||||
|
|
||||||
|
|
|
@ -164,6 +164,9 @@
|
||||||
<Compile Include="deobfuscators\InitializedDataCreator.cs" />
|
<Compile Include="deobfuscators\InitializedDataCreator.cs" />
|
||||||
<Compile Include="deobfuscators\InlinedMethodsFinder.cs" />
|
<Compile Include="deobfuscators\InlinedMethodsFinder.cs" />
|
||||||
<Compile Include="deobfuscators\ISimpleDeobfuscator.cs" />
|
<Compile Include="deobfuscators\ISimpleDeobfuscator.cs" />
|
||||||
|
<Compile Include="deobfuscators\MaxtoCode\Deobfuscator.cs" />
|
||||||
|
<Compile Include="deobfuscators\MaxtoCode\FileDecrypter.cs" />
|
||||||
|
<Compile Include="deobfuscators\MaxtoCode\MainType.cs" />
|
||||||
<Compile Include="deobfuscators\MethodCollection.cs" />
|
<Compile Include="deobfuscators\MethodCollection.cs" />
|
||||||
<Compile Include="deobfuscators\QuickLZ.cs" />
|
<Compile Include="deobfuscators\QuickLZ.cs" />
|
||||||
<Compile Include="deobfuscators\RandomNameChecker.cs" />
|
<Compile Include="deobfuscators\RandomNameChecker.cs" />
|
||||||
|
|
|
@ -50,6 +50,16 @@ namespace de4dot.code.deobfuscators {
|
||||||
return newDef;
|
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) {
|
public static byte[] readModule(ModuleDefinition module) {
|
||||||
return Utils.readFile(module.FullyQualifiedName);
|
return Utils.readFile(module.FullyQualifiedName);
|
||||||
}
|
}
|
||||||
|
|
121
de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs
Normal file
121
de4dot.code/deobfuscators/MaxtoCode/Deobfuscator.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<int> getStringDecrypterMethods() {
|
||||||
|
return new List<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
664
de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs
Normal file
664
de4dot.code/deobfuscators/MaxtoCode/FileDecrypter.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<uint, DecryptedMethodInfo> infos = new Dictionary<uint, DecryptedMethodInfo>();
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
165
de4dot.code/deobfuscators/MaxtoCode/MainType.cs
Normal file
165
de4dot.code/deobfuscators/MaxtoCode/MainType.cs
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<ModuleReference> ModuleReferences {
|
||||||
|
get {
|
||||||
|
var list = new List<ModuleReference>();
|
||||||
|
if (mcModule1 != null)
|
||||||
|
list.Add(mcModule1);
|
||||||
|
if (mcModule2 != null)
|
||||||
|
list.Add(mcModule2);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<MethodDefinition> InitMethods {
|
||||||
|
get {
|
||||||
|
var list = new List<MethodDefinition>();
|
||||||
|
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>(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<string, List<MethodDefinition>> getPinvokes(TypeDefinition type) {
|
||||||
|
var pinvokes = new Dictionary<string, List<MethodDefinition>>(StringComparer.Ordinal);
|
||||||
|
foreach (var method in type.Methods) {
|
||||||
|
var info = method.PInvokeInfo;
|
||||||
|
if (info == null || info.EntryPoint == null)
|
||||||
|
continue;
|
||||||
|
List<MethodDefinition> list;
|
||||||
|
if (!pinvokes.TryGetValue(info.EntryPoint, out list))
|
||||||
|
pinvokes[info.EntryPoint] = list = new List<MethodDefinition>();
|
||||||
|
list.Add(method);
|
||||||
|
}
|
||||||
|
return pinvokes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<MethodDefinition> getPinvokeList(Dictionary<string, List<MethodDefinition>> pinvokes, string methodName) {
|
||||||
|
List<MethodDefinition> list;
|
||||||
|
if (!pinvokes.TryGetValue(methodName, out list))
|
||||||
|
return null;
|
||||||
|
if (list.Count != 2)
|
||||||
|
return null;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -102,8 +102,6 @@ namespace de4dot.code.deobfuscators.Unknown {
|
||||||
return "CodeFort";
|
return "CodeFort";
|
||||||
if (type.FullName == "ZYXDNGuarder")
|
if (type.FullName == "ZYXDNGuarder")
|
||||||
return "DNGuard HVM";
|
return "DNGuard HVM";
|
||||||
if (type.FullName == "InfaceMaxtoCode")
|
|
||||||
return "MaxtoCode";
|
|
||||||
if (type.Name.Contains("();\t"))
|
if (type.Name.Contains("();\t"))
|
||||||
return "Manco .NET Obfuscator";
|
return "Manco .NET Obfuscator";
|
||||||
if (Regex.IsMatch(type.FullName, @"^EMyPID_\d+_$"))
|
if (Regex.IsMatch(type.FullName, @"^EMyPID_\d+_$"))
|
||||||
|
|
|
@ -47,6 +47,7 @@ namespace de4dot.cui {
|
||||||
new de4dot.code.deobfuscators.dotNET_Reactor.v4.DeobfuscatorInfo(),
|
new de4dot.code.deobfuscators.dotNET_Reactor.v4.DeobfuscatorInfo(),
|
||||||
new de4dot.code.deobfuscators.Eazfuscator_NET.DeobfuscatorInfo(),
|
new de4dot.code.deobfuscators.Eazfuscator_NET.DeobfuscatorInfo(),
|
||||||
new de4dot.code.deobfuscators.Goliath_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.Skater_NET.DeobfuscatorInfo(),
|
||||||
new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(),
|
new de4dot.code.deobfuscators.SmartAssembly.DeobfuscatorInfo(),
|
||||||
new de4dot.code.deobfuscators.Spices_Net.DeobfuscatorInfo(),
|
new de4dot.code.deobfuscators.Spices_Net.DeobfuscatorInfo(),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user