Add DNR 3.x application mode unpacker

This commit is contained in:
de4dot 2011-12-20 20:16:18 +01:00
parent 4a0a3fb2fc
commit 746997dfe3
7 changed files with 593 additions and 0 deletions

View File

@ -77,6 +77,8 @@
<Compile Include="deobfuscators\DeobfuscatorInfoBase.cs" />
<Compile Include="deobfuscators\DeobUtils.cs" />
<Compile Include="deobfuscators\Dotfuscator\Deobfuscator.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\v3\ApplicationModeDecrypter.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\v3\ApplicationModeUnpacker.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\AssemblyResolver.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\BooleanDecrypter.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\BoolValueInliner.cs" />
@ -91,6 +93,9 @@
<Compile Include="deobfuscators\dotNET_Reactor\ResourceResolver.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\StringDecrypter.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\AntiStrongName.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\v3\AssemblyResolver.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\v3\DecryptMethod.cs" />
<Compile Include="deobfuscators\dotNET_Reactor\v3\MemoryPatcher.cs" />
<Compile Include="deobfuscators\Eazfuscator\Deobfuscator.cs" />
<Compile Include="deobfuscators\ExceptionLoggerRemover.cs" />
<Compile Include="deobfuscators\IDeobfuscatedFile.cs" />
@ -119,6 +124,7 @@
<Compile Include="deobfuscators\SmartAssembly\TamperProtectionRemover.cs" />
<Compile Include="deobfuscators\TypesRestorer.cs" />
<Compile Include="deobfuscators\Unknown\Deobfuscator.cs" />
<Compile Include="deobfuscators\UnpackedFile.cs" />
<Compile Include="deobfuscators\Xenocode\Deobfuscator.cs" />
<Compile Include="deobfuscators\Xenocode\StringDecrypter.cs" />
<Compile Include="IObfuscatedFile.cs" />

View File

@ -0,0 +1,34 @@
/*
Copyright (C) 2011 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/>.
*/
namespace de4dot.code.deobfuscators {
class UnpackedFile {
public string filename;
public byte[] data;
public UnpackedFile(string filename, byte[] data) {
this.filename = filename;
this.data = data;
}
public override string ToString() {
return string.Format("{0:X8} - {1}", data.Length, filename);
}
}
}

View File

@ -0,0 +1,74 @@
/*
Copyright (C) 2011 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 Mono.Cecil;
using de4dot.blocks;
using de4dot.blocks.cflow;
namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 {
class ApplicationModeDecrypter {
ModuleDefinition module;
AssemblyResolver assemblyResolver;
MemoryPatcher memoryPatcher;
public byte[] AssemblyKey {
get { return assemblyResolver.Key; }
}
public byte[] AssemblyIv {
get { return assemblyResolver.Iv; }
}
public MemoryPatcher MemoryPatcher {
get { return memoryPatcher; }
}
public bool Detected {
get { return assemblyResolver != null; }
}
public ApplicationModeDecrypter(ModuleDefinition module) {
this.module = module;
find();
}
void find() {
var cflowDeobfuscator = new CflowDeobfuscator() {
InlineMethods = true,
InlineInstanceMethods = true,
};
foreach (var type in module.Types) {
if (DotNetUtils.getPInvokeMethod(type, "kernel32", "CloseHandle") == null)
continue;
var resolver = new AssemblyResolver(type, cflowDeobfuscator);
if (!resolver.Detected)
continue;
var patcher = new MemoryPatcher(type, cflowDeobfuscator);
if (!patcher.Detected)
continue;
assemblyResolver = resolver;
memoryPatcher = patcher;
return;
}
}
}
}

View File

@ -0,0 +1,190 @@
/*
Copyright (C) 2011 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 System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Mono.Cecil;
using de4dot.code.PE;
namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 {
class IniFile {
Dictionary<string, string> nameToValue = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
public string this[string name] {
get {
string value;
nameToValue.TryGetValue(name, out value);
return value;
}
}
public IniFile(byte[] data) {
using (var reader = new StreamReader(new MemoryStream(data), Encoding.UTF8)) {
while (true) {
var line = reader.ReadLine();
if (line == null)
break;
var match = Regex.Match(line, @"^([^=]+)=([^;]+);?\s*$");
if (match.Groups.Count < 3)
continue;
var name = match.Groups[1].ToString().Trim();
var value = match.Groups[2].ToString().Trim();
nameToValue[name] = value;
}
}
}
}
// Unpacks "application mode" files (DNR 3.x)
class ApplicationModeUnpacker {
static byte[] key1 = new byte[32] {
0x6B, 0x6C, 0xA7, 0x24, 0x25, 0x37, 0x67, 0x68,
0x4A, 0x2F, 0x28, 0x29, 0x33, 0x77, 0x34, 0x35,
0x5A, 0x5A, 0x48, 0x57, 0x24, 0x35, 0x24, 0x25,
0x26, 0x67, 0x77, 0x53, 0x41, 0x44, 0x46, 0x32,
};
static byte[] iv1 = new byte[16] {
0x73, 0x64, 0xA7, 0x35, 0x24, 0xA7, 0x26, 0x67,
0x34, 0x35, 0x37, 0x21, 0x32, 0x33, 0x6E, 0x6D,
};
static byte[] key2 = new byte[32] {
0x28, 0x24, 0x29, 0x28, 0x2F, 0x29, 0x28, 0x29,
0x3D, 0x66, 0x67, 0x35, 0x35, 0x6A, 0x6D, 0x2C,
0xA7, 0x39, 0x38, 0x2A, 0x6A, 0x67, 0x74, 0x36,
0x35, 0x3D, 0xA7, 0x43, 0x33, 0x33, 0x24, 0x74,
};
static byte[] iv2 = new byte[16] {
0x67, 0x26, 0x35, 0xA7, 0x24, 0xA7, 0x37, 0x21,
0x73, 0x33, 0x6E, 0x6D, 0x34, 0x32, 0x64, 0x35,
};
PeImage peImage;
List<UnpackedFile> satelliteAssemblies = new List<UnpackedFile>();
uint[] sizes;
string[] filenames;
public IEnumerable<UnpackedFile> EmbeddedAssemblies {
get { return satelliteAssemblies; }
}
public ApplicationModeUnpacker(PeImage peImage) {
this.peImage = peImage;
}
public byte[] unpack() {
try {
return unpack2();
}
catch {
return null;
}
}
byte[] unpack2() {
uint headerOffset = peImage.ImageLength - 12;
uint offsetEncryptedAssembly = checkOffset(peImage.offsetReadUInt32(headerOffset));
uint ezencryptionLibLength = peImage.offsetReadUInt32(headerOffset + 4);
uint iniFileLength = peImage.offsetReadUInt32(headerOffset + 8);
uint offsetClrVersionNumber = checked(offsetEncryptedAssembly - 12);
uint iniFileOffset = checked(headerOffset - iniFileLength);
uint ezencryptionLibOffset = checked(iniFileOffset - ezencryptionLibLength);
uint clrVerMajor = peImage.offsetReadUInt32(offsetClrVersionNumber);
uint clrVerMinor = peImage.offsetReadUInt32(offsetClrVersionNumber + 4);
uint clrVerBuild = peImage.offsetReadUInt32(offsetClrVersionNumber + 8);
if (clrVerMajor <= 0 || clrVerMajor >= 20 || clrVerMinor >= 20 || clrVerBuild >= 1000000)
return null;
var settings = new IniFile(decompress2(peImage.offsetReadBytes(iniFileOffset, (int)iniFileLength)));
sizes = getSizes(settings["General_App_Satellite_Assemblies_Sizes"]);
if (sizes == null || sizes.Length <= 1)
return null;
if (sizes[0] != offsetEncryptedAssembly)
return null;
filenames = settings["General_App_Satellite_Assemblies"].Split('|');
if (sizes.Length - 1 != filenames.Length)
return null;
byte[] ezencryptionLibData = decompress1(peImage.offsetReadBytes(ezencryptionLibOffset, (int)ezencryptionLibLength));
var ezencryptionLibModule = ModuleDefinition.ReadModule(new MemoryStream(ezencryptionLibData));
var decrypter = new ApplicationModeDecrypter(ezencryptionLibModule);
if (!decrypter.Detected)
return null;
var mainAssembly = unpackEmbeddedFile(0, decrypter);
decrypter.MemoryPatcher.patch(mainAssembly.data);
for (int i = 1; i < filenames.Length; i++)
satelliteAssemblies.Add(unpackEmbeddedFile(i, decrypter));
return mainAssembly.data;
}
UnpackedFile unpackEmbeddedFile(int index, ApplicationModeDecrypter decrypter) {
uint offset = 0;
for (int i = 0; i < index + 1; i++)
offset += sizes[i];
string filename = Path.GetFileName(filenames[index]);
var data = peImage.offsetReadBytes(offset, (int)sizes[index + 1]);
data = DeobUtils.decrypt(data, decrypter.AssemblyKey, decrypter.AssemblyIv);
data = decompress(data);
return new UnpackedFile(filename, data);
}
static uint[] getSizes(string sizes) {
if (sizes == null)
return null;
var list = new List<uint>();
foreach (var num in sizes.Split('|'))
list.Add(uint.Parse(num));
return list.ToArray();
}
uint checkOffset(uint offset) {
if (offset >= peImage.ImageLength)
throw new Exception();
return offset;
}
static byte[] decompress1(byte[] data) {
return decompress(decrypt1(data));
}
static byte[] decompress2(byte[] data) {
return decompress(decrypt2(data));
}
static byte[] decompress(byte[] data) {
if (!QuickLZ.isCompressed(data))
return data;
return QuickLZ.decompress(data);
}
static byte[] decrypt1(byte[] data) {
return DeobUtils.decrypt(data, key1, iv1);
}
static byte[] decrypt2(byte[] data) {
return DeobUtils.decrypt(data, key2, iv2);
}
}
}

View File

@ -0,0 +1,66 @@
/*
Copyright (C) 2011 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 de4dot.blocks;
using de4dot.blocks.cflow;
namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 {
class AssemblyResolver {
DecryptMethod decryptMethod = new DecryptMethod();
public byte[] Key {
get { return decryptMethod.Key; }
}
public byte[] Iv {
get { return decryptMethod.Iv; }
}
public bool Detected {
get { return decryptMethod.Detected; }
}
public AssemblyResolver(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) {
find(type, cflowDeobfuscator);
}
void find(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) {
var additionalTypes = new List<string> {
"System.IO.BinaryReader",
"System.IO.FileStream",
"System.Reflection.Assembly",
"System.Reflection.Assembly[]",
"System.String",
};
foreach (var method in type.Methods) {
if (!DotNetUtils.isMethod(method, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)"))
continue;
if (!DecryptMethod.couldBeDecryptMethod(method, additionalTypes))
continue;
cflowDeobfuscator.deobfuscateCflow(method);
if (!decryptMethod.getKey(method))
continue;
return;
}
}
}
}

View File

@ -0,0 +1,77 @@
/*
Copyright (C) 2011 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;
namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 {
class DecryptMethod {
MethodDefinition decryptionMethod;
byte[] key;
byte[] iv;
public byte[] Key {
get { return key; }
}
public byte[] Iv {
get { return iv; }
}
public bool Detected {
get { return decryptionMethod != null; }
}
public static bool couldBeDecryptMethod(MethodDefinition method, IEnumerable<string> additionalTypes) {
if (method.Body == null)
return false;
var localTypes = new LocalTypes(method);
var requiredTypes = new List<string> {
"System.Byte[]",
"System.IO.MemoryStream",
"System.Security.Cryptography.CryptoStream",
"System.Security.Cryptography.ICryptoTransform",
};
requiredTypes.AddRange(additionalTypes);
if (!localTypes.all(requiredTypes))
return false;
if (!localTypes.exists("System.Security.Cryptography.RijndaelManaged") &&
!localTypes.exists("System.Security.Cryptography.AesManaged"))
return false;
return true;
}
public bool getKey(MethodDefinition method) {
var tmpKey = ArrayFinder.getInitializedByteArray(method, 32);
if (tmpKey == null)
return false;
var tmpIv = ArrayFinder.getInitializedByteArray(method, 16);
if (tmpIv == null)
return false;
decryptionMethod = method;
key = tmpKey;
iv = tmpIv;
return true;
}
}
}

View File

@ -0,0 +1,146 @@
/*
Copyright (C) 2011 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 System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;
using de4dot.blocks;
using de4dot.blocks.cflow;
using de4dot.code.PE;
namespace de4dot.code.deobfuscators.dotNET_Reactor.v3 {
class MemoryPatcher {
DecryptMethod decryptMethod = new DecryptMethod();
List<PatchInfo> patchInfos = new List<PatchInfo>();
class PatchInfo {
public int[] offsets;
public int[] values;
public PatchInfo(int[] offsets, int[] values) {
this.offsets = offsets;
this.values = values;
}
}
public bool Detected {
get { return decryptMethod.Detected; }
}
public MemoryPatcher(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) {
find(type, cflowDeobfuscator);
}
void find(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) {
var additionalTypes = new List<string> {
"System.IO.BinaryWriter",
};
foreach (var method in type.Methods) {
if (!DotNetUtils.isMethod(method, "System.Void", "(System.Int32[],System.UInt32[])"))
continue;
if (!DecryptMethod.couldBeDecryptMethod(method, additionalTypes))
continue;
cflowDeobfuscator.deobfuscateCflow(method);
if (!decryptMethod.getKey(method))
continue;
findPatchData(type, cflowDeobfuscator);
return;
}
}
void findPatchData(TypeDefinition type, ICflowDeobfuscator cflowDeobfuscator) {
var locals = new List<string> {
"System.Int32[]",
"System.UInt32[]",
};
foreach (var method in type.Methods) {
if (method.Attributes != MethodAttributes.Private)
continue;
if (!DotNetUtils.isMethod(method, "System.Void", "()"))
continue;
if (!new LocalTypes(method).exactly(locals))
continue;
cflowDeobfuscator.deobfuscateCflow(method);
var patchInfo = getPatchInfo(method);
if (patchInfo == null)
continue;
patchInfos.Add(patchInfo);
}
}
PatchInfo getPatchInfo(MethodDefinition method) {
int index1 = 0, index2, index3, size1, size2, size3;
if (!ArrayFinder.findNewarr(method, ref index1, out size1))
return null;
index2 = index1 + 1;
if (!ArrayFinder.findNewarr(method, ref index2, out size2))
return null;
index3 = index2 + 1;
if (ArrayFinder.findNewarr(method, ref index3, out size3))
return null;
if (size1 <= 0 || size1 > 35)
return null;
var ary1 = ArrayFinder.getInitializedInt32Array(size1, method, ref index1);
var ary2 = ArrayFinder.getInitializedInt32Array(size2, method, ref index2);
if (ary1 == null || ary2 == null)
return null;
ary2 = decrypt(ary2);
if (ary2 == null || ary2.Length != ary2.Length)
return null;
for (int i = 0; i < ary1.Length; i++)
ary1[i] = -ary1[i];
return new PatchInfo(ary1, ary2);
}
int[] decrypt(int[] data) {
var memStream = new MemoryStream();
var writer = new BinaryWriter(memStream);
foreach (var value in data)
writer.Write(value);
byte[] decrypted;
try {
decrypted = DeobUtils.decrypt(memStream.ToArray(), decryptMethod.Key, decryptMethod.Iv);
}
catch {
return null;
}
if (decrypted.Length / 4 * 4 != decrypted.Length)
return null;
var newData = new int[decrypted.Length / 4];
for (int i = 0; i < newData.Length; i++)
newData[i] = BitConverter.ToInt32(decrypted, i * 4);
return newData;
}
public void patch(byte[] peImageData) {
var peImage = new PeImage(peImageData);
foreach (var info in patchInfos) {
for (int i = 0; i < info.offsets.Length; i++)
peImage.dotNetSafeWriteOffset((uint)info.offsets[i], BitConverter.GetBytes(info.values[i]));
}
}
}
}