2011-11-06 19:19:26 +08:00
|
|
|
|
/*
|
2012-01-10 06:02:47 +08:00
|
|
|
|
Copyright (C) 2011-2012 de4dot@gmail.com
|
2011-11-06 19:19:26 +08:00
|
|
|
|
|
|
|
|
|
This file is part of de4dot.
|
|
|
|
|
|
|
|
|
|
de4dot is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
de4dot is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2012-04-22 20:16:33 +08:00
|
|
|
|
using System.IO;
|
2011-11-06 19:19:26 +08:00
|
|
|
|
using Mono.MyStuff;
|
2012-04-10 22:32:15 +08:00
|
|
|
|
using de4dot.PE;
|
2011-11-06 19:19:26 +08:00
|
|
|
|
|
2011-12-09 16:02:06 +08:00
|
|
|
|
namespace de4dot.code.deobfuscators.CliSecure {
|
2011-11-06 19:19:26 +08:00
|
|
|
|
class CodeHeader {
|
|
|
|
|
public byte[] signature;
|
|
|
|
|
public byte[] decryptionKey;
|
|
|
|
|
public uint totalCodeSize;
|
|
|
|
|
public uint numMethods;
|
2011-11-07 23:16:18 +08:00
|
|
|
|
public uint methodDefTableOffset; // Relative to start of metadata
|
2011-11-06 19:19:26 +08:00
|
|
|
|
public uint methodDefElemSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct MethodInfo {
|
|
|
|
|
public uint codeOffs, codeSize, flags, localVarSigTok;
|
|
|
|
|
|
|
|
|
|
public MethodInfo(uint codeOffs, uint codeSize, uint flags, uint localVarSigTok) {
|
|
|
|
|
this.codeOffs = codeOffs;
|
|
|
|
|
this.codeSize = codeSize;
|
|
|
|
|
this.flags = flags;
|
|
|
|
|
this.localVarSigTok = localVarSigTok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override string ToString() {
|
|
|
|
|
return string.Format("{0:X8} {1:X8} {2:X8} {3:X8}", codeOffs, codeSize, flags, localVarSigTok);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MethodsDecrypter {
|
2012-04-23 21:00:42 +08:00
|
|
|
|
static readonly byte[] normalSignature = new byte[16] { 0x08, 0x44, 0x65, 0xE1, 0x8C, 0x82, 0x13, 0x4C, 0x9C, 0x85, 0xB4, 0x17, 0xDA, 0x51, 0xAD, 0x25 };
|
|
|
|
|
static readonly byte[] proSignature = new byte[16] { 0x68, 0xA0, 0xBB, 0x60, 0x13, 0x65, 0x5F, 0x41, 0xAE, 0x42, 0xAB, 0x42, 0x9B, 0x6B, 0x4E, 0xC1 };
|
2011-11-07 23:16:18 +08:00
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
enum SigType {
|
|
|
|
|
Unknown,
|
|
|
|
|
Normal,
|
|
|
|
|
Pro,
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-01 01:27:01 +08:00
|
|
|
|
PeImage peImage;
|
2012-04-23 03:03:06 +08:00
|
|
|
|
CliSecureRtType csRtType;
|
2011-11-06 19:19:26 +08:00
|
|
|
|
CodeHeader codeHeader = new CodeHeader();
|
2011-11-07 23:16:18 +08:00
|
|
|
|
IDecrypter decrypter;
|
|
|
|
|
|
|
|
|
|
interface IDecrypter {
|
2012-04-23 02:35:01 +08:00
|
|
|
|
MethodBodyHeader decrypt(MethodInfo methodInfo, out byte[] code, out byte[] extraSections);
|
2012-04-22 20:16:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-07 23:16:18 +08:00
|
|
|
|
abstract class DecrypterBase : IDecrypter {
|
2012-04-23 21:00:42 +08:00
|
|
|
|
protected readonly PeImage peImage;
|
|
|
|
|
protected readonly CodeHeader codeHeader;
|
|
|
|
|
protected readonly uint endOfMetadata;
|
2011-11-07 23:16:18 +08:00
|
|
|
|
|
2011-12-01 01:27:01 +08:00
|
|
|
|
public DecrypterBase(PeImage peImage, CodeHeader codeHeader) {
|
2011-11-07 23:16:18 +08:00
|
|
|
|
this.peImage = peImage;
|
|
|
|
|
this.codeHeader = codeHeader;
|
|
|
|
|
endOfMetadata = peImage.rvaToOffset(peImage.Cor20Header.metadataDirectory.virtualAddress + peImage.Cor20Header.metadataDirectory.size);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
public abstract MethodBodyHeader decrypt(MethodInfo methodInfo, out byte[] code, out byte[] extraSections);
|
|
|
|
|
|
|
|
|
|
protected MethodBodyHeader getCodeBytes(byte[] methodBody, out byte[] code, out byte[] extraSections) {
|
|
|
|
|
return MethodBodyParser.parseMethodBody(new BinaryReader(new MemoryStream(methodBody)), out code, out extraSections);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
// CS 3.0 (could be other versions too)
|
|
|
|
|
class Decrypter30 : DecrypterBase {
|
|
|
|
|
public Decrypter30(PeImage peImage, CodeHeader codeHeader)
|
|
|
|
|
: base(peImage, codeHeader) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override MethodBodyHeader decrypt(MethodInfo methodInfo, out byte[] code, out byte[] extraSections) {
|
|
|
|
|
peImage.Reader.BaseStream.Position = peImage.rvaToOffset(methodInfo.codeOffs);
|
|
|
|
|
return MethodBodyParser.parseMethodBody(peImage.Reader, out code, out extraSections);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CS 4.0 (could be other versions too)
|
2012-04-23 03:24:32 +08:00
|
|
|
|
class Decrypter40 : DecrypterBase {
|
|
|
|
|
public Decrypter40(PeImage peImage, CodeHeader codeHeader)
|
|
|
|
|
: base(peImage, codeHeader) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override MethodBodyHeader decrypt(MethodInfo methodInfo, out byte[] code, out byte[] extraSections) {
|
|
|
|
|
peImage.Reader.BaseStream.Position = endOfMetadata + methodInfo.codeOffs;
|
|
|
|
|
return MethodBodyParser.parseMethodBody(peImage.Reader, out code, out extraSections);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
// CS 4.5 (could be other versions too)
|
2012-04-23 02:35:01 +08:00
|
|
|
|
class Decrypter45 : DecrypterBase {
|
|
|
|
|
public Decrypter45(PeImage peImage, CodeHeader codeHeader)
|
|
|
|
|
: base(peImage, codeHeader) {
|
|
|
|
|
}
|
2011-11-07 23:16:18 +08:00
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
public override MethodBodyHeader decrypt(MethodInfo methodInfo, out byte[] code, out byte[] extraSections) {
|
|
|
|
|
var data = peImage.offsetReadBytes(endOfMetadata + methodInfo.codeOffs, (int)methodInfo.codeSize);
|
|
|
|
|
for (int i = 0; i < data.Length; i++) {
|
|
|
|
|
byte b = data[i];
|
|
|
|
|
b ^= codeHeader.decryptionKey[(methodInfo.codeOffs - 0x28 + i) % 16];
|
|
|
|
|
data[i] = b;
|
|
|
|
|
}
|
|
|
|
|
return getCodeBytes(data, out code, out extraSections);
|
2011-11-07 23:16:18 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 21:02:15 +08:00
|
|
|
|
// CS 5.0+
|
|
|
|
|
class Decrypter5 : DecrypterBase {
|
2012-04-23 21:00:42 +08:00
|
|
|
|
readonly uint codeHeaderSize;
|
2012-04-23 03:03:06 +08:00
|
|
|
|
|
2012-04-23 21:02:15 +08:00
|
|
|
|
public Decrypter5(PeImage peImage, CodeHeader codeHeader, uint codeHeaderSize)
|
2011-11-07 23:16:18 +08:00
|
|
|
|
: base(peImage, codeHeader) {
|
2012-04-23 03:03:06 +08:00
|
|
|
|
this.codeHeaderSize = codeHeaderSize;
|
2011-11-07 23:16:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
public override MethodBodyHeader decrypt(MethodInfo methodInfo, out byte[] code, out byte[] extraSections) {
|
2011-11-07 23:16:18 +08:00
|
|
|
|
byte[] data = peImage.offsetReadBytes(endOfMetadata + methodInfo.codeOffs, (int)methodInfo.codeSize);
|
|
|
|
|
for (int i = 0; i < data.Length; i++) {
|
|
|
|
|
byte b = data[i];
|
2012-04-23 03:03:06 +08:00
|
|
|
|
b ^= codeHeader.decryptionKey[(methodInfo.codeOffs - codeHeaderSize + i) % 16];
|
|
|
|
|
b ^= codeHeader.decryptionKey[(methodInfo.codeOffs - codeHeaderSize + i + 7) % 16];
|
2011-11-07 23:16:18 +08:00
|
|
|
|
data[i] = b;
|
|
|
|
|
}
|
2012-04-23 02:35:01 +08:00
|
|
|
|
return getCodeBytes(data, out code, out extraSections);
|
2011-11-07 23:16:18 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 21:02:15 +08:00
|
|
|
|
// CS 5.4+. Used when the anti-debugger protection is enabled
|
2011-11-07 23:16:18 +08:00
|
|
|
|
class ProDecrypter : DecrypterBase {
|
2012-04-23 21:00:42 +08:00
|
|
|
|
readonly uint[] key = new uint[4];
|
2011-11-07 23:16:18 +08:00
|
|
|
|
|
2011-12-01 01:27:01 +08:00
|
|
|
|
public ProDecrypter(PeImage peImage, CodeHeader codeHeader)
|
2011-11-07 23:16:18 +08:00
|
|
|
|
: base(peImage, codeHeader) {
|
|
|
|
|
for (int i = 0; i < 4; i++)
|
2012-04-24 01:37:05 +08:00
|
|
|
|
key[i] = be_readUInt32(codeHeader.decryptionKey, i * 4);
|
2011-11-07 23:16:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
public override MethodBodyHeader decrypt(MethodInfo methodInfo, out byte[] code, out byte[] extraSections) {
|
2011-11-07 23:16:18 +08:00
|
|
|
|
byte[] data = peImage.offsetReadBytes(endOfMetadata + methodInfo.codeOffs, (int)methodInfo.codeSize);
|
|
|
|
|
|
|
|
|
|
int numQwords = (int)(methodInfo.codeSize / 8);
|
|
|
|
|
for (int i = 0; i < numQwords; i++) {
|
|
|
|
|
int offset = i * 8;
|
2012-04-24 01:37:05 +08:00
|
|
|
|
uint q0 = be_readUInt32(data, offset);
|
|
|
|
|
uint q1 = be_readUInt32(data, offset + 4);
|
2011-11-07 23:16:18 +08:00
|
|
|
|
|
|
|
|
|
const uint magic = 0x9E3779B8;
|
|
|
|
|
uint val = 0xC6EF3700; // magic * 0x20
|
|
|
|
|
for (int j = 0; j < 32; j++) {
|
|
|
|
|
q1 -= ((q0 << 4) + key[2]) ^ (val + q0) ^ ((q0 >> 5) + key[3]);
|
|
|
|
|
q0 -= ((q1 << 4) + key[0]) ^ (val + q1) ^ ((q1 >> 5) + key[1]);
|
|
|
|
|
val -= magic;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 01:37:05 +08:00
|
|
|
|
be_writeUInt32(data, offset, q0);
|
|
|
|
|
be_writeUInt32(data, offset + 4, q1);
|
2011-11-07 23:16:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
return getCodeBytes(data, out code, out extraSections);
|
2011-11-07 23:16:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 01:37:05 +08:00
|
|
|
|
static uint be_readUInt32(byte[] data, int offset) {
|
2011-11-07 23:16:18 +08:00
|
|
|
|
return (uint)((data[offset] << 24) +
|
|
|
|
|
(data[offset + 1] << 16) +
|
|
|
|
|
(data[offset + 2] << 8) +
|
|
|
|
|
data[offset + 3]);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-24 01:37:05 +08:00
|
|
|
|
static void be_writeUInt32(byte[] data, int offset, uint value) {
|
2011-11-07 23:16:18 +08:00
|
|
|
|
data[offset] = (byte)(value >> 24);
|
|
|
|
|
data[offset + 1] = (byte)(value >> 16);
|
|
|
|
|
data[offset + 2] = (byte)(value >> 8);
|
|
|
|
|
data[offset + 3] = (byte)value;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-06 19:19:26 +08:00
|
|
|
|
|
2012-04-22 20:16:33 +08:00
|
|
|
|
interface ICsHeader {
|
2012-04-23 02:35:01 +08:00
|
|
|
|
IDecrypter createDecrypter();
|
2012-04-22 20:16:33 +08:00
|
|
|
|
List<MethodInfo> getMethodInfos(uint codeHeaderOffset);
|
|
|
|
|
void patchMethodDefTable(MetadataType methodDefTable, IList<MethodInfo> methodInfos);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
abstract class CsHeaderBase : ICsHeader {
|
|
|
|
|
protected readonly MethodsDecrypter methodsDecrypter;
|
|
|
|
|
protected readonly uint codeHeaderSize;
|
2012-04-22 20:16:33 +08:00
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
public CsHeaderBase(MethodsDecrypter methodsDecrypter, uint codeHeaderSize) {
|
2012-04-22 20:16:33 +08:00
|
|
|
|
this.methodsDecrypter = methodsDecrypter;
|
2012-04-23 03:03:06 +08:00
|
|
|
|
this.codeHeaderSize = codeHeaderSize;
|
2012-04-22 20:16:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
public abstract IDecrypter createDecrypter();
|
|
|
|
|
|
|
|
|
|
public virtual void patchMethodDefTable(MetadataType methodDefTable, IList<MethodInfo> methodInfos) {
|
2012-04-23 02:35:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
public abstract List<MethodInfo> getMethodInfos(uint codeHeaderOffset);
|
|
|
|
|
|
|
|
|
|
protected List<MethodInfo> getMethodInfos1(uint codeHeaderOffset) {
|
|
|
|
|
uint offset = codeHeaderOffset + methodsDecrypter.codeHeader.totalCodeSize + codeHeaderSize;
|
2012-04-22 20:16:33 +08:00
|
|
|
|
var methodInfos = new List<MethodInfo>((int)methodsDecrypter.codeHeader.numMethods);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
for (int i = 0; i < (int)methodsDecrypter.codeHeader.numMethods; i++, offset += 4) {
|
2012-04-22 20:16:33 +08:00
|
|
|
|
uint codeOffs = methodsDecrypter.peImage.offsetReadUInt32(offset);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
methodInfos.Add(new MethodInfo(codeOffs, 0, 0, 0));
|
2012-04-22 20:16:33 +08:00
|
|
|
|
}
|
|
|
|
|
return methodInfos;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
protected List<MethodInfo> getMethodInfos2(uint codeHeaderOffset) {
|
|
|
|
|
uint offset = codeHeaderOffset + methodsDecrypter.codeHeader.totalCodeSize + codeHeaderSize;
|
2012-04-22 20:16:33 +08:00
|
|
|
|
var methodInfos = new List<MethodInfo>((int)methodsDecrypter.codeHeader.numMethods);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
for (int i = 0; i < (int)methodsDecrypter.codeHeader.numMethods; i++, offset += 8) {
|
2012-04-22 20:16:33 +08:00
|
|
|
|
uint codeOffs = methodsDecrypter.peImage.offsetReadUInt32(offset);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
uint codeSize = methodsDecrypter.peImage.offsetReadUInt32(offset + 4);
|
|
|
|
|
methodInfos.Add(new MethodInfo(codeOffs, codeSize, 0, 0));
|
2012-04-22 20:16:33 +08:00
|
|
|
|
}
|
|
|
|
|
return methodInfos;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
protected List<MethodInfo> getMethodInfos4(uint codeHeaderOffset) {
|
|
|
|
|
uint offset = codeHeaderOffset + methodsDecrypter.codeHeader.totalCodeSize + codeHeaderSize;
|
|
|
|
|
var methodInfos = new List<MethodInfo>((int)methodsDecrypter.codeHeader.numMethods);
|
|
|
|
|
for (int i = 0; i < (int)methodsDecrypter.codeHeader.numMethods; i++, offset += 16) {
|
|
|
|
|
uint codeOffs = methodsDecrypter.peImage.offsetReadUInt32(offset);
|
|
|
|
|
uint codeSize = methodsDecrypter.peImage.offsetReadUInt32(offset + 4);
|
|
|
|
|
uint flags = methodsDecrypter.peImage.offsetReadUInt32(offset + 8);
|
|
|
|
|
uint localVarSigTok = methodsDecrypter.peImage.offsetReadUInt32(offset + 12);
|
|
|
|
|
methodInfos.Add(new MethodInfo(codeOffs, codeSize, flags, localVarSigTok));
|
|
|
|
|
}
|
|
|
|
|
return methodInfos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
// CS 3.0 (could be other versions too)
|
|
|
|
|
class CsHeader30 : CsHeaderBase {
|
|
|
|
|
public CsHeader30(MethodsDecrypter methodsDecrypter)
|
|
|
|
|
: base(methodsDecrypter, 0x28) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override IDecrypter createDecrypter() {
|
|
|
|
|
return new Decrypter30(methodsDecrypter.peImage, methodsDecrypter.codeHeader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override List<MethodInfo> getMethodInfos(uint codeHeaderOffset) {
|
|
|
|
|
return getMethodInfos1(codeHeaderOffset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CS 4.0 (could be other versions too)
|
2012-04-23 03:03:06 +08:00
|
|
|
|
class CsHeader40 : CsHeaderBase {
|
|
|
|
|
public CsHeader40(MethodsDecrypter methodsDecrypter)
|
|
|
|
|
: base(methodsDecrypter, 0x28) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override IDecrypter createDecrypter() {
|
2012-04-23 03:24:32 +08:00
|
|
|
|
return new Decrypter40(methodsDecrypter.peImage, methodsDecrypter.codeHeader);
|
2012-04-23 03:03:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override List<MethodInfo> getMethodInfos(uint codeHeaderOffset) {
|
|
|
|
|
return getMethodInfos1(codeHeaderOffset);
|
2012-04-22 20:16:33 +08:00
|
|
|
|
}
|
2012-04-23 02:35:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
// CS 4.5 (could be other versions too)
|
2012-04-23 03:03:06 +08:00
|
|
|
|
class CsHeader45 : CsHeaderBase {
|
|
|
|
|
public CsHeader45(MethodsDecrypter methodsDecrypter)
|
|
|
|
|
: base(methodsDecrypter, 0x28) {
|
|
|
|
|
}
|
2012-04-22 20:16:33 +08:00
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
public override IDecrypter createDecrypter() {
|
|
|
|
|
return new Decrypter45(methodsDecrypter.peImage, methodsDecrypter.codeHeader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override List<MethodInfo> getMethodInfos(uint codeHeaderOffset) {
|
|
|
|
|
return getMethodInfos2(codeHeaderOffset);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CS 5.0+
|
|
|
|
|
class CsHeader5 : CsHeaderBase {
|
|
|
|
|
public CsHeader5(MethodsDecrypter methodsDecrypter, uint codeHeaderSize)
|
|
|
|
|
: base(methodsDecrypter, codeHeaderSize) {
|
2012-04-23 02:35:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
public override IDecrypter createDecrypter() {
|
2012-04-23 02:35:01 +08:00
|
|
|
|
switch (getSigType(methodsDecrypter.codeHeader.signature)) {
|
|
|
|
|
case SigType.Normal:
|
2012-04-23 21:02:15 +08:00
|
|
|
|
return new Decrypter5(methodsDecrypter.peImage, methodsDecrypter.codeHeader, codeHeaderSize);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
|
|
|
|
|
case SigType.Pro:
|
|
|
|
|
return new ProDecrypter(methodsDecrypter.peImage, methodsDecrypter.codeHeader);
|
|
|
|
|
|
|
|
|
|
case SigType.Unknown:
|
|
|
|
|
default:
|
|
|
|
|
throw new ArgumentException("sig");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
public override List<MethodInfo> getMethodInfos(uint codeHeaderOffset) {
|
|
|
|
|
if (codeHeaderSize == 0x28)
|
|
|
|
|
return getMethodInfos2(codeHeaderOffset);
|
|
|
|
|
return getMethodInfos4(codeHeaderOffset);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 03:03:06 +08:00
|
|
|
|
public override void patchMethodDefTable(MetadataType methodDefTable, IList<MethodInfo> methodInfos) {
|
2012-04-23 02:35:01 +08:00
|
|
|
|
uint offset = methodDefTable.fileOffset - methodDefTable.totalSize;
|
|
|
|
|
foreach (var methodInfo in methodInfos) {
|
|
|
|
|
offset += methodDefTable.totalSize;
|
|
|
|
|
if (methodInfo.flags == 0 || methodInfo.codeOffs == 0)
|
|
|
|
|
continue;
|
|
|
|
|
uint rva = methodsDecrypter.peImage.offsetReadUInt32(offset);
|
2012-04-23 20:47:05 +08:00
|
|
|
|
methodsDecrypter.peImage.writeUInt16(rva, (ushort)methodInfo.flags);
|
|
|
|
|
methodsDecrypter.peImage.writeUInt32(rva + 8, methodInfo.localVarSigTok);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
}
|
2012-04-22 20:16:33 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
enum CsHeaderVersion {
|
|
|
|
|
Unknown,
|
2012-04-25 19:32:14 +08:00
|
|
|
|
V30,
|
2012-04-23 02:35:01 +08:00
|
|
|
|
V40,
|
|
|
|
|
V45,
|
2012-04-23 03:03:06 +08:00
|
|
|
|
V50, // 5.0, possibly also 5.1
|
|
|
|
|
V52, // 5.2+ (or maybe 5.1+)
|
2012-04-23 02:35:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
List<CsHeaderVersion> getCsHeaderVersions(uint codeHeaderOffset, MetadataType methodDefTable) {
|
2012-04-23 02:35:01 +08:00
|
|
|
|
if (!isOldHeader(methodDefTable))
|
2012-04-25 19:32:14 +08:00
|
|
|
|
return new List<CsHeaderVersion> { CsHeaderVersion.V52 };
|
2012-04-23 03:03:06 +08:00
|
|
|
|
if (csRtType.isAtLeastVersion50())
|
2012-04-25 19:32:14 +08:00
|
|
|
|
return new List<CsHeaderVersion> { CsHeaderVersion.V50 };
|
|
|
|
|
if (isCsHeader40(codeHeaderOffset)) {
|
|
|
|
|
return new List<CsHeaderVersion> {
|
|
|
|
|
CsHeaderVersion.V40,
|
|
|
|
|
CsHeaderVersion.V30,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return new List<CsHeaderVersion> {
|
|
|
|
|
CsHeaderVersion.V45,
|
|
|
|
|
CsHeaderVersion.V50,
|
|
|
|
|
};
|
2012-04-23 02:35:01 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool isCsHeader40(uint codeHeaderOffset) {
|
|
|
|
|
try {
|
|
|
|
|
uint offset = codeHeaderOffset + codeHeader.totalCodeSize + 0x28;
|
|
|
|
|
uint prevCodeOffs = 0;
|
|
|
|
|
for (int i = 0; i < (int)codeHeader.numMethods; i++, offset += 4) {
|
|
|
|
|
uint codeOffs = peImage.offsetReadUInt32(offset);
|
|
|
|
|
if (prevCodeOffs != 0 && codeOffs != 0 && codeOffs < prevCodeOffs)
|
|
|
|
|
return false;
|
|
|
|
|
if (codeOffs != 0)
|
|
|
|
|
prevCodeOffs = codeOffs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (IOException) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-22 20:16:33 +08:00
|
|
|
|
bool isOldHeader(MetadataType methodDefTable) {
|
|
|
|
|
if (methodDefTable.totalSize != codeHeader.methodDefElemSize)
|
|
|
|
|
return true;
|
|
|
|
|
if (methodDefTable.fileOffset - peImage.rvaToOffset(peImage.Cor20Header.metadataDirectory.virtualAddress) != codeHeader.methodDefTableOffset)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
ICsHeader createCsHeader(CsHeaderVersion version) {
|
|
|
|
|
switch (version) {
|
|
|
|
|
case CsHeaderVersion.V30: return new CsHeader30(this);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
case CsHeaderVersion.V40: return new CsHeader40(this);
|
|
|
|
|
case CsHeaderVersion.V45: return new CsHeader45(this);
|
2012-04-23 03:03:06 +08:00
|
|
|
|
case CsHeaderVersion.V50: return new CsHeader5(this, 0x28);
|
|
|
|
|
case CsHeaderVersion.V52: return new CsHeader5(this, 0x30);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
default: throw new ApplicationException("Unknown CS header");
|
2012-04-22 20:16:33 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 07:48:59 +08:00
|
|
|
|
enum DecryptResult {
|
|
|
|
|
NotEncrypted,
|
|
|
|
|
Decrypted,
|
|
|
|
|
Error,
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
public bool decrypt(PeImage peImage, string filename, CliSecureRtType csRtType, ref DumpedMethods dumpedMethods) {
|
|
|
|
|
this.peImage = peImage;
|
|
|
|
|
this.csRtType = csRtType;
|
2012-04-26 07:48:59 +08:00
|
|
|
|
|
|
|
|
|
switch (decrypt2(ref dumpedMethods)) {
|
|
|
|
|
case DecryptResult.Decrypted: return true;
|
|
|
|
|
case DecryptResult.NotEncrypted: return false;
|
|
|
|
|
|
|
|
|
|
case DecryptResult.Error:
|
|
|
|
|
Log.w("Using dynamic method decryption");
|
|
|
|
|
byte[] moduleCctorBytes = getModuleCctorBytes(csRtType);
|
|
|
|
|
dumpedMethods = de4dot.code.deobfuscators.MethodsDecrypter.decrypt(filename, moduleCctorBytes);
|
2012-04-25 19:32:14 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
2012-04-26 07:48:59 +08:00
|
|
|
|
default:
|
|
|
|
|
throw new ApplicationException("Invalid DecryptResult");
|
|
|
|
|
}
|
2012-04-25 19:32:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static byte[] getModuleCctorBytes(CliSecureRtType csRtType) {
|
|
|
|
|
var initMethod = csRtType.InitializeMethod;
|
|
|
|
|
if (initMethod == null)
|
|
|
|
|
return null;
|
|
|
|
|
uint initToken = initMethod.MetadataToken.ToUInt32();
|
|
|
|
|
var moduleCctorBytes = new byte[6];
|
|
|
|
|
moduleCctorBytes[0] = 0x28; // call
|
|
|
|
|
moduleCctorBytes[1] = (byte)initToken;
|
|
|
|
|
moduleCctorBytes[2] = (byte)(initToken >> 8);
|
|
|
|
|
moduleCctorBytes[3] = (byte)(initToken >> 16);
|
|
|
|
|
moduleCctorBytes[4] = (byte)(initToken >> 24);
|
|
|
|
|
moduleCctorBytes[5] = 0x2A; // ret
|
|
|
|
|
return moduleCctorBytes;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-22 22:18:41 +08:00
|
|
|
|
static uint getCodeHeaderOffset(PeImage peImage) {
|
|
|
|
|
return peImage.rvaToOffset(peImage.Cor20Header.metadataDirectory.virtualAddress + peImage.Cor20Header.metadataDirectory.size);
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 07:48:59 +08:00
|
|
|
|
DecryptResult decrypt2(ref DumpedMethods dumpedMethods) {
|
2012-04-22 22:18:41 +08:00
|
|
|
|
uint codeHeaderOffset = getCodeHeaderOffset(peImage);
|
2012-04-26 07:48:59 +08:00
|
|
|
|
readCodeHeader(codeHeaderOffset);
|
|
|
|
|
if (!isValidSignature(codeHeader.signature))
|
|
|
|
|
return DecryptResult.NotEncrypted;
|
2011-11-06 19:19:26 +08:00
|
|
|
|
|
|
|
|
|
var metadataTables = peImage.Cor20Header.createMetadataTables();
|
2012-04-10 22:32:15 +08:00
|
|
|
|
var methodDefTable = metadataTables.getMetadataType(MetadataIndex.iMethodDef);
|
2011-11-07 23:16:18 +08:00
|
|
|
|
|
2012-04-25 19:32:14 +08:00
|
|
|
|
foreach (var version in getCsHeaderVersions(codeHeaderOffset, methodDefTable)) {
|
|
|
|
|
try {
|
|
|
|
|
decryptMethods(codeHeaderOffset, methodDefTable, createCsHeader(version), ref dumpedMethods);
|
2012-04-26 07:48:59 +08:00
|
|
|
|
return DecryptResult.Decrypted;
|
2012-04-25 19:32:14 +08:00
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 07:48:59 +08:00
|
|
|
|
return DecryptResult.Error;
|
2012-04-25 19:32:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void decryptMethods(uint codeHeaderOffset, MetadataType methodDefTable, ICsHeader csHeader, ref DumpedMethods dumpedMethods) {
|
2012-04-22 20:16:33 +08:00
|
|
|
|
var methodInfos = csHeader.getMethodInfos(codeHeaderOffset);
|
|
|
|
|
csHeader.patchMethodDefTable(methodDefTable, methodInfos);
|
2011-11-06 19:19:26 +08:00
|
|
|
|
|
2012-02-25 13:14:19 +08:00
|
|
|
|
dumpedMethods = new DumpedMethods();
|
2012-04-22 20:16:33 +08:00
|
|
|
|
uint offset = methodDefTable.fileOffset;
|
2012-04-23 02:35:01 +08:00
|
|
|
|
decrypter = csHeader.createDecrypter();
|
2011-11-06 19:19:26 +08:00
|
|
|
|
for (int i = 0; i < methodInfos.Count; i++, offset += methodDefTable.totalSize) {
|
|
|
|
|
var methodInfo = methodInfos[i];
|
|
|
|
|
if (methodInfo.codeOffs == 0)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
var dm = new DumpedMethod();
|
|
|
|
|
dm.token = 0x06000001 + (uint)i;
|
|
|
|
|
|
|
|
|
|
dm.mdImplFlags = peImage.offsetReadUInt16(offset + (uint)methodDefTable.fields[1].offset);
|
|
|
|
|
dm.mdFlags = peImage.offsetReadUInt16(offset + (uint)methodDefTable.fields[2].offset);
|
|
|
|
|
dm.mdName = peImage.offsetRead(offset + (uint)methodDefTable.fields[3].offset, methodDefTable.fields[3].size);
|
|
|
|
|
dm.mdSignature = peImage.offsetRead(offset + (uint)methodDefTable.fields[4].offset, methodDefTable.fields[4].size);
|
|
|
|
|
dm.mdParamList = peImage.offsetRead(offset + (uint)methodDefTable.fields[5].offset, methodDefTable.fields[5].size);
|
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
var mbHeader = decrypter.decrypt(methodInfo, out dm.code, out dm.extraSections);
|
|
|
|
|
dm.mhFlags = mbHeader.flags;
|
|
|
|
|
dm.mhMaxStack = mbHeader.maxStack;
|
|
|
|
|
dm.mhCodeSize = (uint)dm.code.Length;
|
|
|
|
|
dm.mhLocalVarSigTok = mbHeader.localVarSigTok;
|
2011-11-06 19:19:26 +08:00
|
|
|
|
|
2012-02-25 13:14:19 +08:00
|
|
|
|
dumpedMethods.add(dm);
|
2011-11-06 19:19:26 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-26 07:48:59 +08:00
|
|
|
|
void readCodeHeader(uint offset) {
|
2011-11-06 19:19:26 +08:00
|
|
|
|
codeHeader.signature = peImage.offsetReadBytes(offset, 16);
|
|
|
|
|
codeHeader.decryptionKey = peImage.offsetReadBytes(offset + 0x10, 16);
|
|
|
|
|
codeHeader.totalCodeSize = peImage.offsetReadUInt32(offset + 0x20);
|
|
|
|
|
codeHeader.numMethods = peImage.offsetReadUInt32(offset + 0x24);
|
|
|
|
|
codeHeader.methodDefTableOffset = peImage.offsetReadUInt32(offset + 0x28);
|
|
|
|
|
codeHeader.methodDefElemSize = peImage.offsetReadUInt32(offset + 0x2C);
|
|
|
|
|
}
|
2012-04-22 22:18:41 +08:00
|
|
|
|
|
2012-04-23 02:35:01 +08:00
|
|
|
|
static SigType getSigType(byte[] sig) {
|
|
|
|
|
if (Utils.compare(sig, normalSignature))
|
|
|
|
|
return SigType.Normal;
|
|
|
|
|
else if (Utils.compare(sig, proSignature))
|
|
|
|
|
return SigType.Pro;
|
|
|
|
|
return SigType.Unknown;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool isValidSignature(byte[] signature) {
|
|
|
|
|
return getSigType(signature) != SigType.Unknown;
|
|
|
|
|
}
|
|
|
|
|
|
2012-04-22 22:18:41 +08:00
|
|
|
|
public static bool detect(PeImage peImage) {
|
|
|
|
|
try {
|
|
|
|
|
uint codeHeaderOffset = getCodeHeaderOffset(peImage);
|
|
|
|
|
var sig = peImage.offsetReadBytes(codeHeaderOffset, 16);
|
2012-04-23 02:35:01 +08:00
|
|
|
|
return isValidSignature(sig);
|
2012-04-22 22:18:41 +08:00
|
|
|
|
}
|
|
|
|
|
catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-06 19:19:26 +08:00
|
|
|
|
}
|
|
|
|
|
}
|