diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj
index f53b4038..73c62d15 100644
--- a/de4dot.code/de4dot.code.csproj
+++ b/de4dot.code/de4dot.code.csproj
@@ -17,7 +17,7 @@
- AnyCPU
+ x86
true
full
false
@@ -159,8 +159,9 @@
-
+
+
diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs
index bb001de1..88f3001a 100644
--- a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs
+++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs
@@ -207,11 +207,11 @@ namespace de4dot.code.deobfuscators.ConfuserEx
return buffer;
}
- private void DecryptArray(uint[] array)
+ private void DecryptArray(uint[] array) //TODO: Automatic detection
{
- uint num = 1680u; // array size?
+ uint num = 960u; // array size?
uint[] array2 = new uint[16];
- uint num2 = 3186233426u;
+ uint num2 = 4136251032u;
for (int i = 0; i < 16; i++)
{
num2 ^= num2 >> 12;
@@ -229,237 +229,189 @@ namespace de4dot.code.deobfuscators.ConfuserEx
{
array3[j] = array[num3 + j];
}
- uint num5 = array3[9] >> 23;
- array3[0] = array3[0] * 865957733u;
- array3[9] = array3[9] << 9;
- array3[9] = (array3[9] | num5);
- array3[3] = (array3[3] ^ 4272581837u);
- array3[8] = (array3[8] ^ array2[8]);
- num5 = (array3[14] & 3955221806u);
- num5 *= 1105615415u;
- array3[14] = (array3[14] & 339745489u);
- array3[14] = (array3[14] | (array3[0] & 3955221806u));
- array3[15] = (array3[15] ^ array3[8]);
- array3[0] = (array3[0] & 339745489u);
- array3[0] = (array3[0] | num5 * 809663367u);
- array3[14] = (array3[14] ^ 1753824488u);
- num5 = array3[11] << 18;
- array3[11] = array3[11] >> 14;
- array3[1] = array3[1] - 301755752u;
- array3[11] = (array3[11] | num5);
- uint num6 = array3[14] << 3;
- num5 = (array3[9] & 2370496838u);
- array3[10] = array3[10] - 4147007853u;
- uint num7 = array3[14] << 4;
- num5 *= 2575732745u;
- num6 += array3[14] << 4;
- array3[9] = (array3[9] & 1924470457u);
- array3[9] = (array3[9] | (array3[2] & 2370496838u));
- array3[12] = array3[12] - array3[6];
- array3[2] = (array3[2] & 1924470457u);
- array3[0] = array3[0] - array3[4];
- array3[2] = (array3[2] | num5 * 593100345u);
- num5 = array3[6] * 993944645u;
- array3[6] = array3[5];
- uint num8 = array3[14] * 19u;
- array3[5] = num5 * 679153293u;
- num5 = (array3[1] & 4141471236u);
- num8 += array3[5] * 67u;
- num8 += array3[10] * 43u;
- array3[13] = (array3[13] ^ array2[13]);
- array3[1] = (array3[1] & 153496059u);
- num6 += array3[5] * 92u;
- num7 += array3[5] * 57u;
- num7 += array3[10] * 37u;
- num5 *= 831032307u;
- array3[1] = (array3[1] | (array3[12] & 4141471236u));
- array3[0] = (array3[0] ^ array2[0]);
- array3[12] = (array3[12] & 153496059u);
- array3[11] = (array3[11] ^ array2[11]);
- num6 += array3[10] << 6;
- array3[12] = (array3[12] | num5 * 419693883u);
- num7 += array3[15] * 107u;
- array3[3] = (array3[3] ^ array2[3]);
- num5 = (array3[13] & 2032982899u);
- array3[13] = (array3[13] & 2261984396u);
- num5 *= 3754449215u;
- num8 += array3[15] * 125u;
- num6 += array3[15] * 179u;
- array3[13] = (array3[13] | (array3[7] & 2032982899u));
- array3[7] = (array3[7] & 2261984396u);
- array3[7] = (array3[7] | num5 * 1302730431u);
- num5 = array3[14] * 7u;
- num5 += array3[5] * 25u;
- array3[12] = (array3[12] ^ ~array3[4]);
- num5 += array3[10] << 4;
- array3[5] = num6;
+ uint num5 = array3[3] * 41u;
+ array3[11] = (array3[11] ^ 3634844963u);
+ uint num6 = array3[3] * 31u;
+ num6 += array3[9] * 47u;
+ num5 += array3[9] * 85u;
+ num5 += array3[10] * 149u;
+ uint num7 = array3[3] << 1;
+ num7 += array3[3];
+ uint num8 = array3[3] << 1;
+ num8 += array3[3] << 3;
+ num7 += array3[9] << 3;
+ num8 += array3[9] * 13u;
+ num7 += array3[9];
+ num6 += array3[10] * 71u;
+ num7 += array3[10] << 1;
+ num6 += array3[1] * 81u;
+ array3[4] = (array3[4] ^ ~array3[6]);
+ num8 += array3[10] << 1;
+ num7 += array3[10] << 4;
+ array3[9] = num6;
+ num8 += array3[10] << 4;
+ array3[6] = array3[6] * 395315459u;
+ num8 += array3[1] * 19u;
+ num7 += array3[1] * 23u;
+ num5 += array3[1] * 184u;
+ num6 = array3[7] * 19u;
array3[10] = num7;
- num6 = array3[2] >> 19;
- num7 = array3[11] >> 19;
- num5 += array3[15] * 46u;
- array3[15] = num8;
- num8 = array3[0] << 2;
- array3[2] = array3[2] << 13;
- array3[11] = array3[11] << 13;
- array3[14] = num5;
- array3[0] = array3[0] >> 30;
- array3[2] = (array3[2] | num6);
- array3[6] = (array3[6] ^ 825592879u);
+ num6 += array3[8] * 28u;
+ array3[14] = (array3[14] ^ array3[0]);
+ array3[3] = num8;
+ num6 += array3[12] << 6;
+ array3[1] = num5;
array3[2] = (array3[2] ^ array2[2]);
- array3[11] = (array3[11] | num7);
- num5 = array3[15] << 1;
- array3[0] = (array3[0] | num8);
- num8 = array3[15] * 23u;
- num7 = array3[15] * 7u;
- num8 += array3[7] * 45u;
- num7 += array3[7] * 11u;
- num5 += array3[15];
- array3[9] = (array3[9] ^ array2[9]);
- num5 += array3[7] * 7u;
- num8 += array3[13] << 7;
- array3[3] = (array3[3] ^ ~array3[8]);
- array3[10] = array3[10] * 2256145475u;
- num6 = array3[15] << 2;
- num6 += array3[15];
- num7 += array3[13] << 5;
- num7 += array3[1] << 1;
- num6 += array3[7] << 2;
- num6 += array3[7] << 3;
- num8 += array3[13];
- num8 += array3[1] * 143u;
- num5 += array3[13] << 2;
- num6 += array3[13] << 1;
- num7 += array3[1] << 5;
- num5 += array3[13] << 4;
+ num5 = array3[7] * 28u;
+ num5 += array3[8] << 2;
+ num8 = array3[7] << 1;
+ num7 = array3[7] << 5;
+ num8 += array3[7] << 3;
+ num8 += array3[8] * 13u;
+ num7 += array3[7];
+ num6 += array3[12];
+ num7 += array3[8] * 42u;
+ array3[4] = array3[4] - array3[10];
+ num8 += array3[12] << 5;
+ num6 += array3[15] * 85u;
+ num5 += array3[8] << 5;
+ array3[7] = num6;
+ array3[11] = array3[11] - 2867139633u;
+ num7 += array3[12] * 108u;
+ num5 += array3[12] * 93u;
+ num8 += array3[12];
+ num5 += array3[15] * 141u;
+ num8 += array3[15] * 49u;
+ num7 += array3[15] * 163u;
+ array3[12] = num5;
array3[15] = num7;
- num5 += array3[1] * 23u;
- num6 += array3[13] << 5;
- array3[7] = num5;
- num6 += array3[1] * 39u;
- array3[1] = num8;
- num8 = array3[1] * 26u;
- num7 = array3[1] << 6;
- array3[7] = (array3[7] ^ array2[7]);
- array3[13] = num6;
- num5 = array3[1] << 1;
- num5 += array3[1] << 3;
- num6 = array3[1] * 13u;
- num5 += array3[13] << 1;
- num7 += array3[1];
- num6 += array3[13] * 45u;
- array3[9] = (array3[9] ^ 786150263u);
- num8 += array3[13] * 88u;
- num6 += array3[2] * 67u;
- array3[8] = (array3[8] ^ 110539985u);
- num5 += array3[13] << 5;
- num6 += array3[11] << 1;
- num8 += array3[2] * 133u;
- array3[10] = (array3[10] ^ array2[10]);
- num8 += array3[11] << 6;
- array3[15] = array3[15] - 2992485470u;
- array3[0] = array3[0] - array3[6];
- num6 += array3[11] << 5;
- num5 += array3[2] * 51u;
- num5 += array3[11] * 25u;
- array3[12] = (array3[12] ^ ~array3[3]);
- num7 += array3[13] * 222u;
- array3[13] = num5;
- array3[1] = num6;
- array3[13] = array3[13] * 3578289835u;
- num6 = (array3[10] & 381620437u);
- array3[10] = (array3[10] & 3913346858u);
- num5 = array3[0] * 14u;
- num7 += array3[2] * 333u;
- array3[2] = num8;
- num5 += array3[3] * 11u;
- array3[10] = (array3[10] | (array3[14] & 381620437u));
- array3[7] = (array3[7] ^ ~array3[4]);
- num6 *= 3323466531u;
- array3[14] = (array3[14] & 3913346858u);
- num8 = array3[0] << 2;
- num5 += array3[5] * 54u;
- array3[14] = (array3[14] | num6 * 1991488651u);
- num7 += array3[11] * 164u;
- num6 = (array3[2] & 2341248020u);
- array3[11] = num7;
- num7 = array3[0] * 11u;
- num8 += array3[0] << 4;
- array3[2] = (array3[2] & 1953719275u);
- num8 += array3[3] << 2;
- array3[2] = (array3[2] | (array3[11] & 2341248020u));
- num8 += array3[3] << 4;
- num6 *= 4030567715u;
- array3[14] = (array3[14] ^ array2[14]);
- array3[11] = (array3[11] & 1953719275u);
- array3[11] = (array3[11] | num6 * 62866059u);
- num6 = array3[0] << 2;
- num8 += array3[5] * 90u;
- num7 += array3[3] << 4;
- num7 += array3[5] << 2;
- num6 += array3[0];
+ array3[8] = num8;
+ num5 = array3[7] >> 21;
+ num6 = array3[15] >> 22;
+ array3[15] = array3[15] << 10;
+ num8 = array3[1] >> 21;
+ array3[15] = (array3[15] | num6);
array3[12] = (array3[12] ^ array2[12]);
- num7 += array3[5] << 6;
- num8 += array3[13] * 117u;
- array3[9] = (array3[9] ^ array3[5]);
- num5 += array3[13] * 52u;
- num6 += array3[3] << 1;
- num6 += array3[3];
- num7 += array3[13] * 126u;
- num6 += array3[5] << 4;
- num6 += array3[5];
- array3[5] = num8;
- array3[3] = num5;
- num6 += array3[13] * 11u;
- array3[0] = num6;
- array3[13] = num7;
- num6 = array3[6] << 1;
- num6 += array3[15] << 1;
- num5 = array3[12] << 29;
- num6 += array3[15] << 2;
- num7 = array3[7] << 12;
- array3[11] = array3[11] - array3[10];
- array3[7] = array3[7] >> 20;
- array3[12] = array3[12] >> 3;
- array3[12] = (array3[12] | num5);
- array3[14] = (array3[14] ^ ~array3[8]);
- array3[1] = (array3[1] ^ array2[1]);
- array3[1] = (array3[1] ^ 3215842197u);
- num8 = array3[6] * 7u;
- array3[7] = (array3[7] | num7);
- num8 += array3[15] * 26u;
- num5 = array3[6] << 2;
- num5 += array3[15] << 2;
- array3[9] = array3[9] - array3[2];
- num7 = array3[6] << 2;
- array3[4] = (array3[4] ^ array2[4]);
- num6 += array3[4] << 4;
- array3[3] = (array3[3] ^ 1425746098u);
- num5 += array3[15];
- num8 += array3[4] * 69u;
- num5 += array3[4] * 15u;
- num7 += array3[6];
- num6 += array3[1] * 15u;
- num8 += array3[1] * 63u;
- array3[6] = num6;
- num7 += array3[15] * 11u;
- num7 += array3[4] * 31u;
- num7 += array3[1] * 30u;
- num5 += array3[1] << 4;
- array3[5] = (array3[5] ^ array2[5]);
- array3[4] = num7;
- num7 = (array3[5] & 2375297997u);
+ num6 = (array3[2] & 3262151220u);
+ array3[1] = array3[1] << 11;
+ array3[1] = (array3[1] | num8);
+ array3[7] = array3[7] << 11;
+ array3[0] = array3[0] - array3[14];
+ num7 = array3[13] << 4;
+ num8 = array3[3] * 954284655u;
+ array3[3] = array3[5];
+ array3[5] = num8 * 3102958735u;
+ array3[7] = (array3[7] | num5);
+ num5 = array3[10] << 4;
+ num8 = array3[9] * 2468501497u;
+ array3[2] = (array3[2] & 1032816075u);
+ array3[13] = array3[13] >> 28;
+ array3[13] = (array3[13] | num7);
+ array3[7] = array3[7] - 888060325u;
+ array3[2] = (array3[2] | (array3[8] & 3262151220u));
+ array3[12] = array3[12] * 4056148675u;
+ array3[9] = array3[13];
+ num7 = array3[6] << 5;
+ array3[13] = num8 * 1746582089u;
+ array3[6] = array3[6] >> 27;
+ array3[6] = (array3[6] | num7);
+ array3[8] = (array3[8] & 1032816075u);
+ array3[7] = (array3[7] ^ array2[7]);
+ num5 += array3[11] * 46u;
+ num6 *= 869722291u;
+ num8 = array3[10] << 1;
+ num5 += array3[3] * 92u;
+ num5 += array3[5] * 149u;
+ array3[7] = array3[7] - 3922202313u;
+ array3[8] = (array3[8] | num6 * 2576221819u);
+ num8 += array3[11] * 15u;
+ num8 += array3[3] * 37u;
+ num6 = array3[10] * 7u;
+ array3[8] = (array3[8] ^ 1878284212u);
+ num8 += array3[5] * 56u;
+ array3[9] = (array3[9] ^ array2[9]);
+ num7 = array3[10] << 3;
+ array3[6] = (array3[6] ^ 2841119440u);
+ num6 += array3[11] << 4;
+ array3[2] = (array3[2] ^ 217219923u);
+ num7 += array3[10];
+ num6 += array3[3] * 29u;
array3[6] = (array3[6] ^ array2[6]);
- num7 *= 3574473459u;
- array3[15] = num5;
- array3[5] = (array3[5] & 1919669298u);
- array3[5] = (array3[5] | (array3[13] & 2375297997u));
+ num7 += array3[11] * 26u;
+ num7 += array3[3] * 52u;
+ num6 += array3[5] * 49u;
+ num7 += array3[5] * 84u;
+ array3[3] = num5;
+ array3[10] = num6;
+ num6 = array3[1] * 15u;
+ array3[12] = (array3[12] ^ 1080861703u);
+ array3[5] = num8;
+ num5 = (array3[4] & 3659960635u);
+ num6 += array3[12] << 1;
+ array3[4] = (array3[4] & 635006660u);
+ array3[4] = (array3[4] | (array3[9] & 3659960635u));
+ num5 *= 1676034815u;
+ array3[11] = num7;
+ num7 = array3[1] * 19u;
+ num6 += array3[12] << 4;
+ array3[9] = (array3[9] & 635006660u);
+ num6 += array3[3] << 6;
+ num7 += array3[12] * 27u;
+ array3[5] = array3[5] - array3[8];
+ array3[9] = (array3[9] | num5 * 1267776767u);
+ num5 = array3[1] << 2;
+ num5 += array3[1];
+ array3[13] = (array3[13] ^ array2[13]);
+ num8 = array3[1];
+ num6 += array3[3];
+ num5 += array3[12] << 3;
+ num8 += array3[12] << 1;
+ num8 += array3[12];
+ num6 += array3[15] * 22u;
+ num5 += array3[3] * 27u;
+ num5 += array3[15] << 3;
+ num7 += array3[3] * 92u;
+ num8 += array3[3] << 3;
+ num8 += array3[3];
+ num5 += array3[15];
+ num8 += array3[15] << 1;
+ num8 += array3[15];
+ array3[3] = num6;
+ array3[0] = (array3[0] ^ array3[13]);
+ array3[14] = array3[14] - array3[15];
+ num7 += array3[15] << 5;
+ array3[13] = (array3[13] ^ ~array3[1]);
+ num6 = array3[10] >> 31;
+ array3[14] = (array3[14] ^ array2[14]);
+ array3[8] = (array3[8] ^ array2[8]);
+ array3[12] = num5;
array3[1] = num8;
+ array3[5] = (array3[5] ^ array2[5]);
+ array3[11] = (array3[11] ^ array2[11]);
+ num5 = (array3[11] & 2204625944u);
+ array3[1] = (array3[1] ^ array2[1]);
+ array3[4] = (array3[4] ^ array2[4]);
+ array3[11] = (array3[11] & 2090341351u);
+ array3[11] = (array3[11] | (array3[4] & 2204625944u));
+ array3[15] = num7;
+ num8 = (array3[14] & 2496954112u);
+ array3[14] = (array3[14] & 1798013183u);
+ array3[4] = (array3[4] & 2090341351u);
array3[15] = (array3[15] ^ array2[15]);
- num8 = array3[0] << 5;
- array3[13] = (array3[13] & 1919669298u);
- array3[13] = (array3[13] | num7 * 2683487803u);
- array3[0] = array3[0] >> 27;
- array3[0] = (array3[0] | num8);
+ array3[10] = array3[10] << 1;
+ num5 *= 338764649u;
+ array3[14] = (array3[14] | (array3[9] & 2496954112u));
+ array3[15] = array3[15] - array3[0];
+ array3[10] = (array3[10] | num6);
+ array3[10] = (array3[10] ^ array2[10]);
+ array3[3] = (array3[3] ^ array2[3]);
+ num8 *= 2292397853u;
+ array3[0] = (array3[0] ^ array2[0]);
+ array3[0] = (array3[0] ^ 2814140307u);
+ array3[2] = (array3[2] ^ ~array3[13]);
+ array3[4] = (array3[4] | num5 * 587046105u);
+ array3[9] = (array3[9] & 1798013183u);
+ array3[9] = (array3[9] | num8 * 1520255797u);
for (int k = 0; k < 16; k++)
{
uint num9 = array3[k];
@@ -471,7 +423,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx
}
num3 += 16;
}
- decryptedBytes = Lzma.Decompress(array4);
+ decryptedBytes = Lzma.Decompress(array4);
}
private void FindStringDecrypters(TypeDef type)
diff --git a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs
similarity index 94%
rename from de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs
rename to de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs
index dcc218df..50b57c12 100644
--- a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowSolver.cs
+++ b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs
@@ -1,322 +1,310 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using de4dot.blocks;
-using de4dot.blocks.cflow;
-using de4dot.code.deobfuscators.ConfuserEx.x86;
-using dnlib.DotNet;
-using dnlib.DotNet.Emit;
-
-namespace de4dot.code.deobfuscators.ConfuserEx
-{
- class ControlFlowSolver : IBlocksDeobfuscator
- {
- public bool ExecuteIfNotModified { get; }
- private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator();
-
- private Blocks _blocks;
- private X86Method _nativeMethod;
- private Local _switchKey;
-
- private int? CalculateKey()
- {
- var popValue = _instructionEmulator.Peek();
-
- if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid())
- return null;
-
- _instructionEmulator.Pop();
- int result = _nativeMethod.Execute(((Int32Value)popValue).Value);
- return result;
- }
-
- private int CalculateSwitchCaseIndex(Block block, int nativeKey)
- {
- _instructionEmulator.Push(new Int32Value(nativeKey));
- _instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1);
-
- var popValue = _instructionEmulator.Peek();
- _instructionEmulator.Pop();
- return ((Int32Value)popValue).Value;
- }
-
- private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch
- {
- var targets = switchBlock.Targets;
-
- _instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value));
- int? key = CalculateKey();
-
- if (!key.HasValue)
- throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
-
- int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
-
- if (targets.Count < switchCaseIndex)
- throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
-
- var targetBlock = targets[switchCaseIndex];
- targetBlock.SwitchData.Key = key;
-
- switchBlock.Instructions.Clear();
- switchBlock.ReplaceLastNonBranchWithBranch(0, targetBlock);
- }
-
- private void ProcessBlock(List switchCaseBlocks, Block block, Block switchBlock)
- {
- var targets = switchBlock.Targets;
-
- _instructionEmulator.Emulate(block.Instructions, 0, block.Instructions.Count);
-
- if (_instructionEmulator.Peek().IsUnknown())
- throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
-
- int? key = CalculateKey();
-
- if (!key.HasValue)
- throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
-
- int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
-
- if (targets.Count < switchCaseIndex)
- throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
-
- var targetBlock = targets[switchCaseIndex];
- targetBlock.SwitchData.Key = key;
-
- block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them
- block.ReplaceLastNonBranchWithBranch(0, targetBlock);
-
- ProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, key.Value);
-
- block.Processed = true;
- }
-
- private void ProcessTernaryBlock(List switchCaseBlocks, Block ternaryBlock, Block switchBlock)
- {
- var targets = switchBlock.Targets;
-
- for (int i = 0; i < 2; i++) // loop both source blocks
- {
- var sourceBlock = ternaryBlock.Sources[0];
-
- if(ternaryBlock.SwitchData.Key.HasValue) // single instruction: pop -- no key!
- SetLocalSwitchKey(ternaryBlock.SwitchData.Key.Value); // set old key for both iterations!
-
- _instructionEmulator.Emulate(sourceBlock.Instructions, 0, sourceBlock.Instructions.Count);
- _instructionEmulator.Emulate(ternaryBlock.Instructions, 0, ternaryBlock.Instructions.Count);
-
- if (_instructionEmulator.Peek().IsUnknown())
- throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
-
- int? key = CalculateKey();
-
- if (!key.HasValue)
- throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
-
- int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
-
- if (targets.Count < switchCaseIndex)
- throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
-
- var targetBlock = targets[switchCaseIndex];
- targetBlock.SwitchData.Key = key;
-
- sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction());
- sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]);
-
- ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value);
- // the second source block now becomes the first one
- }
-
- //switchCaseBlock.Instructions.Clear();
- ternaryBlock.Add(new Instr(OpCodes.Pop.ToInstruction())); // don't add pop before both iterations have finished
- ternaryBlock.Processed = true;
- }
-
-
- public void DeobfuscateBegin(Blocks blocks)
- {
- _blocks = blocks;
- _instructionEmulator.Initialize(_blocks, true);
- }
-
- public bool Deobfuscate(List methodBlocks)
- {
- List switchBlocks = GetSwitchBlocks(methodBlocks); // blocks that contain a switch
- int modifications = 0;
-
- foreach (Block switchBlock in switchBlocks)
- {
- if (!switchBlock.SwitchData.IsConfuserExSwitch())
- {
- Console.WriteLine("Unsupported switch block obfuscation!");
- continue;
- }
-
- if (switchBlock.SwitchData.IsKeyHardCoded)
- {
- ProcessHardcodedSwitch(switchBlock);
- modifications++;
- continue;
- }
-
- _switchKey = Instr.GetLocalVar(_blocks.Locals,
- switchBlock.Instructions[switchBlock.Instructions.Count - 4]);
-
- if (DeobfuscateSwitchBlock(methodBlocks, switchBlock))
- modifications++;
- }
- return modifications > 0;
- }
-
- private bool DeobfuscateSwitchBlock(List methodBlocks, Block switchBlock)
- {
- List switchFallThroughs = methodBlocks.FindAll(b => b.FallThrough == switchBlock); // blocks that fallthrough to the switch block
- _instructionEmulator.Initialize(_blocks, true); //TODO: Remove temporary precaution
-
- int blocksLeft = switchFallThroughs.Count; // how many blocks left to proccess
- int blockIndex = 0; // block that sets the first switch destination
- int failedCount = 0;
-
- while (blocksLeft > 0)
- {
- if (blockIndex > switchFallThroughs.Count - 1)
- {
- blockIndex = 0;
- }
-
- if (failedCount > switchFallThroughs.Count)
- {
- Console.WriteLine("Some blocks couldn't be processed!");
- break;
- }
-
- Block switchCaseBlock = switchFallThroughs[blockIndex];
-
- if (switchCaseBlock.Processed)
- {
- blockIndex++;
- continue;
- }
-
- if (NeedSwitchKey(switchCaseBlock))
- {
- if (!switchCaseBlock.SwitchData.Key.HasValue)
- {
- failedCount++;
- blockIndex++;
- continue;
- }
- SetLocalSwitchKey(switchCaseBlock.SwitchData.Key.Value);
- }
-
- if (switchCaseBlock.IsTernary())
- {
- ProcessTernaryBlock(switchFallThroughs, switchCaseBlock, switchBlock);
- }
- else
- {
- ProcessBlock(switchFallThroughs, switchCaseBlock, switchBlock);
- }
-
- failedCount = 0;
- blocksLeft--;
- blockIndex++;
- }
-
- if (blocksLeft == switchFallThroughs.Count) // Have we modified anything?
- return false;
-
- return true;
- }
-
-
- public bool IsSwitchBlock(Block block)
- {
- if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0)
- return false;
-
- if (!block.SwitchData.IsNative())
- return false;
-
- _nativeMethod = new X86Method(block.SwitchData.GetNativeMethod(), _blocks.Method.Module as ModuleDefMD); //TODO: Possible null
-
- return true;
- }
-
- public List GetSwitchBlocks(List blocks) // get the blocks which contain the switch statement
- {
- List switchBlocks = new List();
-
- foreach (Block block in blocks)
- if (IsSwitchBlock(block))
- switchBlocks.Add(block);
-
- return switchBlocks;
- }
-
-
- private readonly List _processedFallThroughs = new List();
-
- // add the switch key to all appropriate fallthroughs
- private void ProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey)
- {
- DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, switchKey);
- _processedFallThroughs.Clear();
- }
-
- private void DoProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey)
- {
- if (_processedFallThroughs.Contains(targetBlock))
- return;
-
- _processedFallThroughs.Add(targetBlock);
-
- if (targetBlock.FallThrough == switchBlock && switchCaseBlocks.Contains(targetBlock) && !targetBlock.SwitchData.Key.HasValue)
- targetBlock.SwitchData.Key = switchKey;
-
-
- var fallThrough = targetBlock.FallThrough;
-
- if (fallThrough == null)
- return;
-
- if (fallThrough.LastInstr.OpCode != OpCodes.Ret && fallThrough != switchBlock)
- DoProcessFallThroughs(switchCaseBlocks, switchBlock, fallThrough, switchKey);
-
- if (targetBlock.CountTargets() > 1)
- foreach (Block targetBlockTarget in targetBlock.Targets)
- {
- if (targetBlockTarget == switchBlock)
- return;
- DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlockTarget, switchKey);
- }
- }
-
-
- private bool NeedSwitchKey(Block block)
- {
- foreach (var instr in block.Instructions)
- if (instr.IsLdloc() && Instr.GetLocalVar(_blocks.Locals, instr) == _switchKey)
- return true;
- return false;
- }
-
- private int? GetSwitchKey()
- {
- var val = _instructionEmulator.GetLocal(_switchKey);
- if (!val.IsInt32())
- return null;
- var value = val as Int32Value;
- if (value == null || !value.AllBitsValid())
- return null;
- return value.Value;
- }
-
- private void SetLocalSwitchKey(int key)
- {
- _instructionEmulator.SetLocal(_switchKey, new Int32Value(key));
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using de4dot.blocks;
+using de4dot.blocks.cflow;
+using de4dot.code.deobfuscators.ConfuserEx.x86;
+using dnlib.DotNet;
+using dnlib.DotNet.Emit;
+
+namespace de4dot.code.deobfuscators.ConfuserEx
+{
+ class ControlFlowFixer : IBlocksDeobfuscator
+ {
+ public bool ExecuteIfNotModified { get; }
+ public List NativeMethods = new List();
+
+ private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator();
+
+ private Blocks _blocks;
+ private X86Method _nativeMethod;
+ private Local _switchKey;
+
+ private int? CalculateKey()
+ {
+ var popValue = _instructionEmulator.Peek();
+
+ if (popValue == null || !popValue.IsInt32() || !(popValue as Int32Value).AllBitsValid())
+ return null;
+
+ _instructionEmulator.Pop();
+ int result = _nativeMethod.Execute(((Int32Value)popValue).Value);
+ return result;
+ }
+
+ private int CalculateSwitchCaseIndex(Block block, int nativeKey)
+ {
+ _instructionEmulator.Push(new Int32Value(nativeKey));
+ _instructionEmulator.Emulate(block.Instructions, block.SwitchData.IsKeyHardCoded ? 2 : 1, block.Instructions.Count - 1);
+
+ var popValue = _instructionEmulator.Peek();
+ _instructionEmulator.Pop();
+ return ((Int32Value)popValue).Value;
+ }
+
+ private void ProcessHardcodedSwitch(Block switchBlock) // a single-case switch
+ {
+ var targets = switchBlock.Targets;
+ _instructionEmulator.Push(new Int32Value(switchBlock.SwitchData.Key.Value));
+
+ int? key = CalculateKey();
+ if (!key.HasValue)
+ throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
+
+ int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
+ if (targets.Count < switchCaseIndex)
+ throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
+
+ var targetBlock = targets[switchCaseIndex];
+ targetBlock.SwitchData.Key = key;
+
+ switchBlock.Instructions.Clear();
+ switchBlock.ReplaceLastNonBranchWithBranch(0, targetBlock);
+ }
+
+ private void ProcessBlock(List switchCaseBlocks, Block block, Block switchBlock)
+ {
+ var targets = switchBlock.Targets;
+ _instructionEmulator.Emulate(block.Instructions, 0, block.Instructions.Count);
+
+ if (_instructionEmulator.Peek().IsUnknown())
+ throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
+
+ int? key = CalculateKey();
+ if (!key.HasValue)
+ throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
+
+ int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
+ if (targets.Count < switchCaseIndex)
+ throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
+
+ var targetBlock = targets[switchCaseIndex];
+ targetBlock.SwitchData.Key = key;
+
+ block.Add(new Instr(OpCodes.Pop.ToInstruction())); // neutralize the arithmetics and leave de4dot to remove them
+ block.ReplaceLastNonBranchWithBranch(0, targetBlock);
+
+ ProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, key.Value);
+ block.Processed = true;
+ }
+
+ private void ProcessTernaryBlock(List switchCaseBlocks, Block ternaryBlock, Block switchBlock)
+ {
+ var targets = switchBlock.Targets;
+
+ for (int i = 0; i < 2; i++) // loop both source blocks
+ {
+ var sourceBlock = ternaryBlock.Sources[0];
+
+ if(ternaryBlock.SwitchData.Key.HasValue) // single instruction: pop -- no key!
+ SetLocalSwitchKey(ternaryBlock.SwitchData.Key.Value); // set old key for both iterations!
+
+ _instructionEmulator.Emulate(sourceBlock.Instructions, 0, sourceBlock.Instructions.Count);
+ _instructionEmulator.Emulate(ternaryBlock.Instructions, 0, ternaryBlock.Instructions.Count);
+
+ if (_instructionEmulator.Peek().IsUnknown())
+ throw new Exception("CRITICAL ERROR: STACK VALUE UNKNOWN");
+
+ int? key = CalculateKey();
+ if (!key.HasValue)
+ throw new Exception("CRITICAL ERROR: KEY HAS NO VALUE");
+
+ int switchCaseIndex = CalculateSwitchCaseIndex(switchBlock, key.Value);
+ if (targets.Count < switchCaseIndex)
+ throw new Exception("CRITICAL ERROR: KEY OUT OF RANGE");
+
+ var targetBlock = targets[switchCaseIndex];
+ targetBlock.SwitchData.Key = key;
+
+ sourceBlock.Instructions[sourceBlock.Instructions.Count - 1] = new Instr(OpCodes.Pop.ToInstruction());
+ sourceBlock.ReplaceLastNonBranchWithBranch(0, targets[switchCaseIndex]);
+
+ ProcessFallThroughs(switchCaseBlocks, switchBlock, targets[switchCaseIndex], key.Value);
+ // the second source block now becomes the first one
+ }
+
+ //switchCaseBlock.Instructions.Clear();
+ ternaryBlock.Add(new Instr(OpCodes.Pop.ToInstruction())); // don't add pop before both iterations have finished
+ ternaryBlock.Processed = true;
+ }
+
+
+ public void DeobfuscateBegin(Blocks blocks)
+ {
+ _blocks = blocks;
+ _instructionEmulator.Initialize(_blocks, true);
+ }
+
+ public bool Deobfuscate(List methodBlocks)
+ {
+ List switchBlocks = GetSwitchBlocks(methodBlocks); // blocks that contain a switch
+ int modifications = 0;
+
+ foreach (Block switchBlock in switchBlocks)
+ {
+ if (!switchBlock.SwitchData.IsConfuserExSwitch())
+ {
+ Console.WriteLine("Unsupported switch block obfuscation!");
+ continue;
+ }
+
+ if (switchBlock.SwitchData.IsKeyHardCoded)
+ {
+ ProcessHardcodedSwitch(switchBlock);
+ modifications++;
+ continue;
+ }
+
+ _switchKey = Instr.GetLocalVar(_blocks.Locals,
+ switchBlock.Instructions[switchBlock.Instructions.Count - 4]);
+
+ if (DeobfuscateSwitchBlock(methodBlocks, switchBlock))
+ modifications++;
+ }
+ return modifications > 0;
+ }
+
+ private bool DeobfuscateSwitchBlock(List methodBlocks, Block switchBlock)
+ {
+ List switchFallThroughs = methodBlocks.FindAll(b => b.FallThrough == switchBlock); // blocks that fallthrough to the switch block
+ _instructionEmulator.Initialize(_blocks, true); //TODO: Remove temporary precaution
+
+ int blocksLeft = switchFallThroughs.Count; // how many blocks left to proccess
+ int blockIndex = 0; // block that sets the first switch destination
+ int failedCount = 0;
+
+ while (blocksLeft > 0)
+ {
+ if (blockIndex > switchFallThroughs.Count - 1)
+ blockIndex = 0;
+
+ if (failedCount > switchFallThroughs.Count)
+ {
+ Console.WriteLine("Some blocks couldn't be processed!");
+ break;
+ }
+
+ Block switchCaseBlock = switchFallThroughs[blockIndex];
+ if (switchCaseBlock.Processed)
+ {
+ blockIndex++;
+ continue;
+ }
+
+ if (NeedSwitchKey(switchCaseBlock))
+ {
+ if (!switchCaseBlock.SwitchData.Key.HasValue)
+ {
+ failedCount++;
+ blockIndex++;
+ continue;
+ }
+ SetLocalSwitchKey(switchCaseBlock.SwitchData.Key.Value);
+ }
+
+ if (switchCaseBlock.IsTernary()) {
+ ProcessTernaryBlock(switchFallThroughs, switchCaseBlock, switchBlock);
+ }
+ else {
+ ProcessBlock(switchFallThroughs, switchCaseBlock, switchBlock);
+ }
+
+ failedCount = 0;
+ blocksLeft--;
+ blockIndex++;
+ }
+
+ if (blocksLeft == switchFallThroughs.Count) // Have we modified anything?
+ return false;
+
+ return true;
+ }
+
+
+ public bool IsSwitchBlock(Block block)
+ {
+ if (block.LastInstr.OpCode.Code != Code.Switch || ((Instruction[])block.LastInstr.Operand)?.Length == 0)
+ return false;
+ if (!block.SwitchData.IsNative())
+ return false;
+
+ MethodDef nativeMethod = block.SwitchData.GetNativeMethod();
+ _nativeMethod = new X86Method(nativeMethod, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null
+ if (!NativeMethods.Contains(nativeMethod))
+ NativeMethods.Add(nativeMethod);
+
+ return true;
+ }
+
+ public List GetSwitchBlocks(List blocks) // get the blocks which contain the switch statement
+ {
+ List switchBlocks = new List();
+
+ foreach (Block block in blocks)
+ if (IsSwitchBlock(block))
+ switchBlocks.Add(block);
+
+ return switchBlocks;
+ }
+
+
+ private readonly List _processedFallThroughs = new List();
+
+ // add the switch key to all appropriate fallthroughs
+ private void ProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey)
+ {
+ DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlock, switchKey);
+ _processedFallThroughs.Clear();
+ }
+
+ private void DoProcessFallThroughs(List switchCaseBlocks, Block switchBlock, Block targetBlock, int switchKey)
+ {
+ if (_processedFallThroughs.Contains(targetBlock))
+ return;
+ _processedFallThroughs.Add(targetBlock);
+
+ if (targetBlock.FallThrough == switchBlock && switchCaseBlocks.Contains(targetBlock) && !targetBlock.SwitchData.Key.HasValue)
+ targetBlock.SwitchData.Key = switchKey;
+
+ var fallThrough = targetBlock.FallThrough;
+ if (fallThrough == null)
+ return;
+
+ if (fallThrough.LastInstr.OpCode != OpCodes.Ret && fallThrough != switchBlock)
+ DoProcessFallThroughs(switchCaseBlocks, switchBlock, fallThrough, switchKey);
+
+ if (targetBlock.CountTargets() > 1)
+ foreach (Block targetBlockTarget in targetBlock.Targets)
+ {
+ if (targetBlockTarget == switchBlock)
+ return;
+ DoProcessFallThroughs(switchCaseBlocks, switchBlock, targetBlockTarget, switchKey);
+ }
+ }
+
+
+ private bool NeedSwitchKey(Block block)
+ {
+ foreach (var instr in block.Instructions)
+ if (instr.IsLdloc() && Instr.GetLocalVar(_blocks.Locals, instr) == _switchKey)
+ return true;
+ return false;
+ }
+
+ private int? GetSwitchKey()
+ {
+ var val = _instructionEmulator.GetLocal(_switchKey);
+ if (!val.IsInt32())
+ return null;
+ var value = val as Int32Value;
+ if (value == null || !value.AllBitsValid())
+ return null;
+ return value.Value;
+ }
+
+ private void SetLocalSwitchKey(int key)
+ {
+ _instructionEmulator.SetLocal(_switchKey, new Int32Value(key));
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs
index 6f5a67fa..f51db6d3 100644
--- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs
+++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs
@@ -58,12 +58,13 @@ namespace de4dot.code.deobfuscators.ConfuserEx
class Deobfuscator : DeobfuscatorBase
{
-
- bool detectedConfuserExAttribute = false, deobfuscating = false;
- string version = "";
- LzmaFinder lzmaFinder;
- ConstantsDecrypter constantDecrypter;
- ResourceDecrypter resourceDecrypter;
+ private bool _detectedConfuserExAttribute = false, _deobfuscating = false;
+ private string _version = "";
+ private LzmaFinder _lzmaFinder;
+ private ConstantsDecrypter _constantDecrypter;
+ private ResourceDecrypter _resourceDecrypter;
+ private ProxyCallFixer _proxyCallFixer;
+ private ControlFlowFixer _controlFlowFixer = new ControlFlowFixer();
#region ConstantInliners
@@ -97,7 +98,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx
public override string Name
{
- get { return $"{TypeLong} {version}"; }
+ get { return $"{TypeLong} {_version}"; }
}
public Deobfuscator(Options options)
@@ -108,28 +109,33 @@ namespace de4dot.code.deobfuscators.ConfuserEx
protected override int DetectInternal()
{
int val = 0;
- if (detectedConfuserExAttribute) val += 0;
- if (lzmaFinder.FoundLzma) val += 10;
- if (constantDecrypter.Detected) val += 10;
- if (resourceDecrypter.Detected) val += 10;
+ if (_detectedConfuserExAttribute) val += 0;
+ if (_lzmaFinder.FoundLzma) val += 10;
+ if (_constantDecrypter.Detected) val += 10;
+ if (_resourceDecrypter.Detected) val += 10;
return val;
}
protected override void ScanForObfuscator()
{
- lzmaFinder = new LzmaFinder(module, DeobfuscatedFile);
- lzmaFinder.Find();
- constantDecrypter = new ConstantsDecrypter(module, lzmaFinder.Method, DeobfuscatedFile);
- resourceDecrypter = new ResourceDecrypter(module, lzmaFinder.Method, DeobfuscatedFile);
- if (lzmaFinder.FoundLzma)
+ _lzmaFinder = new LzmaFinder(module, DeobfuscatedFile);
+ _lzmaFinder.Find();
+ _constantDecrypter = new ConstantsDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile);
+ _resourceDecrypter = new ResourceDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile);
+ if (_lzmaFinder.FoundLzma)
{
- constantDecrypter.Find();
- resourceDecrypter.Find();
+ _constantDecrypter.Find();
+ _resourceDecrypter.Find();
}
+
+ _proxyCallFixer = new ProxyCallFixer(module, DeobfuscatedFile);
+ _proxyCallFixer.FindDelegateCreatorMethod();
+ _proxyCallFixer.Find();
+
DetectConfuserExAttribute();
}
- public void DetectConfuserExAttribute()
+ private void DetectConfuserExAttribute()
{
var versions = new List();
foreach (var attribute in module.CustomAttributes)
@@ -143,8 +149,8 @@ namespace de4dot.code.deobfuscators.ConfuserEx
var value = argument.Value.ToString();
if (!value.Contains("ConfuserEx"))
continue;
- detectedConfuserExAttribute = true;
- version = value.Replace("ConfuserEx", "");
+ _detectedConfuserExAttribute = true;
+ _version = value.Replace("ConfuserEx", "");
return;
}
}
@@ -152,8 +158,10 @@ namespace de4dot.code.deobfuscators.ConfuserEx
public override void DeobfuscateBegin()
{
- if (constantDecrypter.Detected)
+ if (_constantDecrypter.Detected)
{
+ Logger.w("Constants encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove
+
sbyteValueInliner = new SByteValueInliner();
byteValueInliner = new ByteValueInliner();
int16ValueInliner = new Int16ValueInliner();
@@ -165,37 +173,41 @@ namespace de4dot.code.deobfuscators.ConfuserEx
singleValueInliner = new SingleValueInliner();
doubleValueInliner = new DoubleValueInliner();
arrayValueInliner = new ArrayValueInliner(initializedDataCreator);
- foreach (var info in constantDecrypter.Decrypters)
+ foreach (var info in _constantDecrypter.Decrypters)
{
staticStringInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptString(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptString(info, gim, (uint) args[0]));
sbyteValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptSByte(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptSByte(info, gim, (uint) args[0]));
byteValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptByte(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptByte(info, gim, (uint) args[0]));
int16ValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptInt16(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptInt16(info, gim, (uint) args[0]));
uint16ValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptUInt16(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptUInt16(info, gim, (uint) args[0]));
int32ValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptInt32(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptInt32(info, gim, (uint) args[0]));
uint32ValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptUInt32(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptUInt32(info, gim, (uint) args[0]));
int64ValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptInt64(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptInt64(info, gim, (uint) args[0]));
uint64ValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptUInt64(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptUInt64(info, gim, (uint) args[0]));
singleValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptSingle(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptSingle(info, gim, (uint) args[0]));
doubleValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptDouble(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptDouble(info, gim, (uint) args[0]));
arrayValueInliner.Add(info.Method,
- (method, gim, args) => constantDecrypter.DecryptArray(info, gim, (uint) args[0]));
+ (method, gim, args) => _constantDecrypter.DecryptArray(info, gim, (uint) args[0]));
}
- deobfuscating = true;
+ _deobfuscating = true;
}
- if (resourceDecrypter.Detected)
- resourceDecrypter.Fix();
+ if (_resourceDecrypter.Detected)
+ {
+ Logger.w("Resource encryption detected! Please note that the decryption method has to be set manually!"); //TODO: Remove
+ _resourceDecrypter.Fix();
+ }
+
base.DeobfuscateBegin();
}
@@ -204,9 +216,9 @@ namespace de4dot.code.deobfuscators.ConfuserEx
get
{
var list = new List();
- list.Add(new ControlFlowSolver());
+ list.Add(_controlFlowFixer);
- if (deobfuscating && int32ValueInliner != null)
+ if (_deobfuscating && int32ValueInliner != null)
list.Add(new ConstantsInliner(sbyteValueInliner, byteValueInliner, int16ValueInliner,
uint16ValueInliner,
int32ValueInliner, uint32ValueInliner, int64ValueInliner, uint64ValueInliner,
@@ -216,7 +228,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx
}
}
- bool CanRemoveLzma = true;
+ bool _canRemoveLzma = true;
public override void DeobfuscateEnd()
{
@@ -224,38 +236,48 @@ namespace de4dot.code.deobfuscators.ConfuserEx
List toRemoveFromCctor = new List();
- if (constantDecrypter.Detected)
+ if (_constantDecrypter.Detected)
if (CanRemoveStringDecrypterType)
{
- toRemoveFromCctor.Add(constantDecrypter.Method);
- AddMethodToBeRemoved(constantDecrypter.Method, "Constant Decrypter Initializer");
- foreach (var dec in constantDecrypter.Decrypters)
+ toRemoveFromCctor.Add(_constantDecrypter.Method);
+ AddMethodToBeRemoved(_constantDecrypter.Method, "Constant Decrypter Initializer");
+ foreach (var dec in _constantDecrypter.Decrypters)
AddMethodToBeRemoved(dec.Method, "Constant Decrypter Method");
- AddFieldsToBeRemoved(constantDecrypter.Fields, "Constant Decrypter Fields");
- AddTypeToBeRemoved(constantDecrypter.Type, "Array field signature type");
+ AddFieldsToBeRemoved(_constantDecrypter.Fields, "Constant Decrypter Fields");
+ AddTypeToBeRemoved(_constantDecrypter.Type, "Array field signature type");
}
else
- CanRemoveLzma = false;
+ _canRemoveLzma = false;
- if (resourceDecrypter.Detected && resourceDecrypter.CanRemoveLzma)
+ if (_resourceDecrypter.Detected && _resourceDecrypter.CanRemoveLzma)
{
- toRemoveFromCctor.Add(resourceDecrypter.Method);
- AddMethodToBeRemoved(resourceDecrypter.Method, "Resource decrypter Initializer method");
- AddMethodToBeRemoved(resourceDecrypter.AssembyResolveMethod,
+ toRemoveFromCctor.Add(_resourceDecrypter.Method);
+ AddMethodToBeRemoved(_resourceDecrypter.Method, "Resource decrypter Initializer method");
+ AddMethodToBeRemoved(_resourceDecrypter.AssembyResolveMethod,
"Resource decrypter AssemblyResolve method");
- AddFieldsToBeRemoved(resourceDecrypter.Fields, "Constant Decrypter Fields");
- AddTypeToBeRemoved(resourceDecrypter.Type, "Array field signature type");
+ AddFieldsToBeRemoved(_resourceDecrypter.Fields, "Constant Decrypter Fields");
+ AddTypeToBeRemoved(_resourceDecrypter.Type, "Array field signature type");
}
- if (!constantDecrypter.CanRemoveLzma || !resourceDecrypter.CanRemoveLzma)
- CanRemoveLzma = false;
+ if (!_constantDecrypter.CanRemoveLzma || !_resourceDecrypter.CanRemoveLzma)
+ _canRemoveLzma = false;
- if (lzmaFinder.FoundLzma && CanRemoveLzma)
+ if (_lzmaFinder.FoundLzma && _canRemoveLzma)
{
- AddMethodToBeRemoved(lzmaFinder.Method, "Lzma Decompress method");
- AddTypesToBeRemoved(lzmaFinder.Types, "Lzma Nested Types");
+ AddMethodToBeRemoved(_lzmaFinder.Method, "Lzma Decompress method");
+ AddTypesToBeRemoved(_lzmaFinder.Types, "Lzma Nested Types");
}
+ if (_proxyCallFixer.Detected)
+ {
+ AddTypesToBeRemoved(_proxyCallFixer.DelegateTypes, "Proxy delegates");
+ AddMethodsToBeRemoved(_proxyCallFixer.DelegateCreatorMethods, "Proxy creator methods");
+ AddTypesToBeRemoved(_proxyCallFixer.AttributeTypes, "Proxy creator attributes");
+ AddMethodsToBeRemoved(_proxyCallFixer.NativeMethods, "Proxy native methods");
+ }
+
+ AddMethodsToBeRemoved(_controlFlowFixer.NativeMethods, "Control flow native methods");
+
var moduleCctor = DotNetUtils.GetModuleTypeCctor(module);
foreach (var instr in moduleCctor.Body.Instructions)
if (instr.OpCode == OpCodes.Call && instr.Operand is MethodDef &&
@@ -273,6 +295,12 @@ namespace de4dot.code.deobfuscators.ConfuserEx
var list = new List();
return list;
}
+
+ public override void DeobfuscateMethodEnd(Blocks blocks)
+ {
+ _proxyCallFixer.Deobfuscate(blocks);
+ base.DeobfuscateMethodEnd(blocks);
+ }
}
}
}
\ No newline at end of file
diff --git a/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs
new file mode 100644
index 00000000..a19e50a4
--- /dev/null
+++ b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs
@@ -0,0 +1,415 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using de4dot.blocks;
+using de4dot.blocks.cflow;
+using de4dot.code.deobfuscators.ConfuserEx.x86;
+using dnlib.DotNet;
+using dnlib.DotNet.Emit;
+
+namespace de4dot.code.deobfuscators.ConfuserEx
+{
+ class Context
+ {
+ public uint FieldToken;
+ public int ByteNum;
+ public MethodDef CreateMethod;
+
+ public Context(uint fieldToken, int byteNum, MethodDef createMethod)
+ {
+ this.FieldToken = fieldToken;
+ this.ByteNum = byteNum; // 2nd parameter of the Delegate CreateMethod
+ this.CreateMethod = createMethod;
+ }
+ }
+
+ class ProxyCallFixer : ProxyCallFixer4
+ {
+ public List NativeMethods = new List();
+ public List AttributeTypes = new List();
+ public List DelegateCreatorMethods = new List();
+
+ private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator();
+ private readonly ISimpleDeobfuscator _simpleDeobfuscator;
+ private readonly List _processedMethods = new List();
+
+ public ProxyCallFixer(ModuleDefMD module, ISimpleDeobfuscator simpleDeobfuscator) : base(module)
+ {
+ _simpleDeobfuscator = simpleDeobfuscator;
+ }
+
+ public ProxyCallFixer(ModuleDefMD module, ProxyCallFixer4 oldOne) : base(module, oldOne)
+ {
+ }
+
+ protected override object CheckCctor(TypeDef type, MethodDef cctor)
+ {
+ if (!_processedMethods.Contains(cctor))
+ {
+ _simpleDeobfuscator.Deobfuscate(cctor);
+ _processedMethods.Add(cctor);
+ }
+
+ List contexts = new List();
+ var instructions = cctor.Body.Instructions;
+ instructions.SimplifyMacros(cctor.Body.Variables, cctor.Parameters);
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldtoken, OpCodes.Ldc_I4, OpCodes.Call);
+ if (instrs == null)
+ continue;
+
+ uint fieldToken = ((IField) instrs[0].Operand).MDToken.ToUInt32();
+ int byteNum = (int)instrs[1].Operand;
+ var createMethod = instrs[2].Operand as MethodDef;
+
+ if(!DelegateCreatorMethods.Contains(createMethod))
+ DelegateCreatorMethods.Add(createMethod);
+
+ contexts.Add(new Context(fieldToken, byteNum, createMethod));
+ }
+ return contexts.Count == 0 ? null : contexts;
+ }
+
+ private void DeobfuscateIfNeeded(MethodDef method)
+ {
+ if (!_processedMethods.Contains(method))
+ {
+ _simpleDeobfuscator.Deobfuscate(method);
+ _processedMethods.Add(method);
+ }
+ }
+
+ private byte[] GetExtraDataToken(byte[] sigData)
+ {
+ byte[] extraData = new byte[4];
+
+ // [original signature] [extra signature]
+ // ... X C0 X X X
+ Array.Copy(sigData, sigData.Length - 3, extraData, 1, 3); // last 3 bytes of signature
+ extraData[0] = sigData[sigData.Length - 5]; // the byte before C0
+ Array.Reverse(extraData); // decryptorMethod reads the bytes backwards
+ return extraData;
+ }
+
+ protected override void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, out OpCode callOpcode)
+ {
+ var contexts = (List) context;
+ var ctx = contexts.First(c => c.FieldToken == field.MDToken.ToInt32());
+ var originalMethod = DotNetUtils.Clone(ctx.CreateMethod); // backup original method and restore because changes are not universal
+ DeobfuscateIfNeeded(ctx.CreateMethod);
+
+ var instructions = ctx.CreateMethod.Body.Instructions;
+ var variables = ctx.CreateMethod.Body.Variables;
+ var parameters = ctx.CreateMethod.Parameters;
+
+ instructions.SimplifyMacros(variables, parameters);
+ byte[] sigData = module.ReadBlob(ctx.FieldToken);
+ byte[] extraDataToken = GetExtraDataToken(sigData);
+ int modifierMDToken = ((CModOptSig)field.FieldType).Modifier.MDToken.ToInt32();
+
+ ReplaceMetadataToken(ref instructions, modifierMDToken, variables[0]);
+ ReplaceFieldNameChars(ref instructions, field.Name, variables[0]);
+ InlineArrays(ref instructions, extraDataToken, variables[1], variables[2]);
+ RemoveDecrementorBlock(ref instructions, variables[2]);
+
+ int firstInstruction = GetEmulationStartIndex(instructions, variables[1], variables[2]);
+ int lastInstruction =
+ instructions.IndexOf(
+ instructions.First(
+ i => i.OpCode == OpCodes.Callvirt && i.Operand.ToString().Contains("GetCustomAttributes"))) - 4;
+
+ bool nativeMode = false;
+ if (instructions[lastInstruction - 1].OpCode == OpCodes.Call) // x86 protection
+ {
+ lastInstruction--; // don't try emulating native method
+ nativeMode = true;
+ }
+
+ int result = EmulateManagedMethod(ctx.CreateMethod, firstInstruction, lastInstruction);
+ if (nativeMode)
+ {
+ MethodDef nativeMethod = (MethodDef) instructions[lastInstruction].Operand;
+ if (!NativeMethods.Contains(nativeMethod))
+ NativeMethods.Add(nativeMethod);
+ result = EmulateNativeMethod(nativeMethod, result);
+ }
+
+ result *= GetMagicNumber(field.CustomAttributes[0]);
+ calledMethod = module.ResolveMemberRef(new MDToken(result).Rid);
+
+ if(calledMethod == null)
+ throw new Exception();
+
+ int charNum = GetCharNum(instructions, parameters.Last());
+ callOpcode = GetCallOpCode(calledMethod, charNum, ctx.ByteNum);
+
+ ctx.CreateMethod.Body = originalMethod.Body; // restore
+ }
+
+ private OpCode GetCallOpCode(IMethod calledMethod, int charNum, int byteNum)
+ {
+ if (calledMethod.ResolveMethodDef().IsStatic) {
+ return OpCodes.Call;
+ }
+
+ byte charOpCode = (byte)(charNum ^ byteNum);
+
+ if (charOpCode == 0x28)
+ return OpCodes.Call;
+ else if (charOpCode == 0x6F)
+ return OpCodes.Callvirt;
+ else if (charOpCode == 0x73)
+ return OpCodes.Newobj;
+ else
+ throw new Exception();
+ }
+
+ private int EmulateNativeMethod(MethodDef externalMethod, int parameter)
+ {
+ var nativeMethod = new X86Method(externalMethod, module); //TODO: Possible null
+ return nativeMethod.Execute(parameter);
+ }
+
+ private int EmulateManagedMethod(MethodDef method, int startIndex, int endIndex, params Tuple[] parameters)
+ {
+ _instructionEmulator.Initialize(method, false);
+ foreach(var parameter in parameters)
+ _instructionEmulator.SetArg(parameter.Item1, new Int32Value(parameter.Item2));
+
+ for (int i = startIndex; i < endIndex; i++) {
+ _instructionEmulator.Emulate(method.Body.Instructions[i]);
+ }
+
+ return ((Int32Value) _instructionEmulator.Pop()).Value;
+ }
+
+ private int GetMagicNumber(CustomAttribute customAttribute)
+ {
+ TypeDef attributeType = customAttribute.AttributeType.ResolveTypeDef();
+ if(!AttributeTypes.Contains(attributeType))
+ AttributeTypes.Add(attributeType);
+
+ var ctor = attributeType.FindConstructors().First();
+ DeobfuscateIfNeeded(ctor);
+
+ int magicNum = Convert.ToInt32(customAttribute.ConstructorArguments[0].Value);
+ var parameter = new Tuple();
+ parameter.Item1 = ctor.Parameters[1];
+ parameter.Item2 = magicNum;
+
+ return EmulateManagedMethod(ctor, 3, ctor.Body.Instructions.Count - 2, parameter);
+ }
+
+ public void FindDelegateCreatorMethod()
+ {
+ var globalType = module.GlobalType;
+ foreach (
+ var method in
+ globalType.Methods.Where(
+ m => m.Parameters.Count == 2 && m.Parameters[0].Type.TypeName == "RuntimeFieldHandle"))
+ {
+ _simpleDeobfuscator.Deobfuscate(method);
+ SetDelegateCreatorMethod(method);
+ }
+ } //TODO: Improve detection
+
+
+ /* 0x000005B7 6F1500000A IL_001F: callvirt instance uint8[][mscorlib] System.Reflection.Module::ResolveSignature(int32)
+ 0x000005BC FE0E0100 IL_0024: stloc.1
+ 0x000005C0 FE0C0100 IL_0028: ldloc.1
+ 0x000005C4 8E IL_002C: ldlen
+ 0x000005C5 69 IL_002D: conv.i4
+ 0x000005C6 FE0E0200 IL_002E: stloc.2 */
+ private int GetEmulationStartIndex(IList instructions, Local localArray, Local localArraySize)
+ {
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Callvirt, OpCodes.Stloc,
+ OpCodes.Ldloc, OpCodes.Ldlen, OpCodes.Conv_I4, OpCodes.Stloc);
+
+ if (instrs == null)
+ continue;
+ if (!instrs[0].Operand.ToString().Contains("ResolveSignature"))
+ continue;
+ if ((Local)instrs[1].Operand != localArray)
+ continue;
+ if ((Local)instrs[2].Operand != localArray)
+ continue;
+ if ((Local)instrs[5].Operand != localArraySize)
+ continue;
+
+ return i + 6;
+ }
+ return -1;
+ }
+
+ /* 0x000008F3 03 IL_02BB: ldarg.1
+ 0x000008F4 61 IL_02BC: xor */
+ private int GetCharNum(IList instructions, Parameter byteParam)
+ {
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldarg, OpCodes.Xor);
+
+ if (instrs == null)
+ continue;
+ if ((Parameter) instrs[0].Operand != byteParam)
+ continue;
+
+ return (int) instructions[i - 5].Operand;
+ }
+ throw new Exception();
+ }
+
+
+ private void ReplaceFieldNameChars(ref IList instructions, string fieldName, Local fieldLocal)
+ {
+ bool foundInstrs;
+ do {
+ foundInstrs = ReplaceFieldNameChar(ref instructions, fieldName, fieldLocal);
+ } while (foundInstrs);
+ }
+
+ /* 0x00000375 06 IL_007D: ldloc.0
+ 0x00000376 6F1500000A IL_007E: callvirt
+ 0x0000037B 19 IL_0083: ldc.i4.3
+ 0x0000037C 6F1600000A IL_0084: callvirt */
+ private bool ReplaceFieldNameChar(ref IList instructions, string fieldName, Local fieldLocal)
+ {
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Callvirt,
+ OpCodes.Ldc_I4, OpCodes.Callvirt);
+
+ if(instrs == null)
+ continue;
+ if ((Local)instrs[0].Operand != fieldLocal)
+ continue;
+ if (!instrs[1].Operand.ToString().Contains("get_Name"))
+ continue;
+ if (!instrs[3].Operand.ToString().Contains("get_Chars"))
+ continue;
+
+ int charIndex = (int)instrs[2].Operand;
+ int @char = fieldName[charIndex];
+
+ instructions[i].OpCode = OpCodes.Ldc_I4;
+ instructions[i].Operand = @char;
+ instructions[i+1].OpCode = OpCodes.Nop;
+ instructions[i+2].OpCode = OpCodes.Nop;
+ instructions[i+3].OpCode = OpCodes.Nop;
+ return true;
+ }
+ return false;
+ }
+
+ /* 0x0000034A 08 IL_0052: ldloc.2
+ 0x0000034B 17 IL_0053: ldc.i4.1
+ 0x0000034C 59 IL_0054: sub
+ 0x0000034D 25 IL_0055: dup
+ 0x0000034E 0C IL_0056: stloc.2
+ 0x0000034F 91 IL_0057: ldelem.u1 */
+ private void InlineArrays(ref IList instructions, byte[] values, Local localArray, Local localInt)
+ {
+ bool foundInstrs;
+ int i = 0;
+ do {
+ foundInstrs = InlineArray(ref instructions, values[i++], localArray, localInt);
+ } while (i < 4 && foundInstrs);
+ }
+
+ private bool InlineArray(ref IList instructions, int value, Local localArray, Local localInt)
+ {
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Ldloc, OpCodes.Ldc_I4,
+ OpCodes.Sub, OpCodes.Dup, OpCodes.Stloc, OpCodes.Ldelem_U1);
+
+ if (instrs == null)
+ continue;
+ if ((Local)instrs[0].Operand != localArray)
+ continue;
+ if ((Local)instrs[1].Operand != localInt)
+ continue;
+ if ((int)instrs[2].Operand != 1)
+ continue;
+ if ((Local)instrs[5].Operand != localInt)
+ continue;
+
+ instructions[i].OpCode = OpCodes.Ldc_I4;
+ instructions[i].Operand = value;
+ instructions[i + 1].OpCode = OpCodes.Nop;
+ instructions[i + 2].OpCode = OpCodes.Nop;
+ instructions[i + 3].OpCode = OpCodes.Nop;
+ instructions[i + 4].OpCode = OpCodes.Nop;
+ instructions[i + 5].OpCode = OpCodes.Nop;
+ instructions[i + 6].OpCode = OpCodes.Nop;
+ return true;
+ }
+ return false;
+ }
+
+ /* 0x00000371 08 IL_0079: ldloc.2
+ 0x00000372 17 IL_007A: ldc.i4.1
+ 0x00000373 59 IL_007B: sub
+ 0x00000374 0C IL_007C: stloc.2 */
+ private void RemoveDecrementorBlock(ref IList instructions, Local localInt)
+ {
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Ldc_I4, OpCodes.Sub, OpCodes.Stloc);
+
+ if (instrs == null)
+ continue;
+ if ((Local)instrs[0].Operand != localInt)
+ continue;
+ if ((int)instrs[1].Operand != 1)
+ continue;
+ if ((Local)instrs[3].Operand != localInt)
+ continue;
+
+ instructions[i].OpCode = OpCodes.Nop;
+ instructions[i + 1].OpCode = OpCodes.Nop;
+ instructions[i + 2].OpCode = OpCodes.Nop;
+ instructions[i + 3].OpCode = OpCodes.Nop;
+ return;
+ }
+ }
+
+ /* 0x000005CF FE0C0000 IL_0037: ldloc.0
+ 0x000005D3 6F1600000A IL_003B: callvirt instance class [mscorlib] System.Type[][mscorlib] System.Reflection.FieldInfo::GetOptionalCustomModifiers()
+ 0x000005D8 2000000000 IL_0040: ldc.i4.0
+ 0x000005DD 9A IL_0045: ldelem.ref
+ 0x000005DE 6F1400000A IL_0046: callvirt instance int32[mscorlib] System.Reflection.MemberInfo::get_MetadataToken() */
+ private void ReplaceMetadataToken(ref IList instructions, int metadataToken, Local fieldLocal)
+ {
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instrs = DotNetUtils.GetInstructions(instructions, i, OpCodes.Ldloc, OpCodes.Callvirt, OpCodes.Ldc_I4,
+ OpCodes.Ldelem_Ref, OpCodes.Callvirt);
+
+ if (instrs == null)
+ continue;
+ if ((Local)instrs[0].Operand != fieldLocal)
+ continue;
+ if (!instrs[1].Operand.ToString().Contains("GetOptionalCustomModifiers"))
+ continue;
+ if ((int)instrs[2].Operand != 0)
+ continue;
+ if (!instrs[4].Operand.ToString().Contains("get_MetadataToken"))
+ continue;
+
+ instructions[i].OpCode = OpCodes.Ldc_I4;
+ instructions[i].Operand = metadataToken;
+ instructions[i + 1].OpCode = OpCodes.Nop;
+ instructions[i + 2].OpCode = OpCodes.Nop;
+ instructions[i + 3].OpCode = OpCodes.Nop;
+ instructions[i + 4].OpCode = OpCodes.Nop;
+ return;
+ }
+ }
+ }
+}
diff --git a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs
index 4ab77c4a..bfa15899 100644
--- a/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs
+++ b/de4dot.code/deobfuscators/ConfuserEx/ResourceDecrypter.cs
@@ -171,7 +171,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx
Buffer.BlockCopy(array, 0, buffer, 0, array.Length * l1);
return buffer;
}
- private void DecryptArray(uint[] array)
+ private void DecryptArray(uint[] array) //TODO: Automatic detection
{
int num = array.Length;
uint[] array2 = new uint[16];
diff --git a/de4dot.code/deobfuscators/ProxyCallFixerBase.cs b/de4dot.code/deobfuscators/ProxyCallFixerBase.cs
index 1a7fc3ac..7e51f176 100644
--- a/de4dot.code/deobfuscators/ProxyCallFixerBase.cs
+++ b/de4dot.code/deobfuscators/ProxyCallFixerBase.cs
@@ -525,4 +525,242 @@ namespace de4dot.code.deobfuscators {
return instr.Operand as IMethod;
}
}
+
+ //
+ // Combines the above 1st and 2nd templates
+ //
+ public abstract class ProxyCallFixer4 : ProxyCallFixerBase
+ {
+ FieldDefAndDeclaringTypeDict fieldToDelegateInfo = new FieldDefAndDeclaringTypeDict();
+ MethodDefAndDeclaringTypeDict proxyMethodToDelegateInfo = new MethodDefAndDeclaringTypeDict();
+
+ protected ProxyCallFixer4(ModuleDefMD module)
+ : base(module)
+ {
+ }
+
+ protected ProxyCallFixer4(ModuleDefMD module, ProxyCallFixer4 oldOne)
+ : base(module, oldOne)
+ {
+ foreach (var key in oldOne.fieldToDelegateInfo.GetKeys())
+ fieldToDelegateInfo.Add(Lookup(key, "Could not find field"), Copy(oldOne.fieldToDelegateInfo.Find(key)));
+
+ foreach (var oldMethod in oldOne.proxyMethodToDelegateInfo.GetKeys())
+ {
+ var oldDi = oldOne.proxyMethodToDelegateInfo.Find(oldMethod);
+ var method = Lookup(oldMethod, "Could not find proxy method");
+ proxyMethodToDelegateInfo.Add(method, Copy(oldDi));
+ }
+ }
+
+ protected void AddDelegateInfo(DelegateInfo di)
+ {
+ fieldToDelegateInfo.Add(di.field, di);
+ }
+
+ protected DelegateInfo GetDelegateInfo(IField field)
+ {
+ if (field == null)
+ return null;
+ return fieldToDelegateInfo.Find(field);
+ }
+
+ public void Find()
+ {
+ if (delegateCreatorMethods.Count == 0)
+ return;
+
+ Logger.v("Finding all proxy delegates");
+ foreach (var type in GetDelegateTypes())
+ {
+ var cctor = type.FindStaticConstructor();
+ if (cctor == null || !cctor.HasBody)
+ continue;
+ if (!type.HasFields)
+ continue;
+
+ object context = CheckCctor(type, cctor);
+ if (context == null)
+ continue;
+
+ Logger.v("Found proxy delegate: {0} ({1:X8})", Utils.RemoveNewlines(type), type.MDToken.ToUInt32());
+ RemovedDelegateCreatorCalls++;
+ var fieldToMethod = GetFieldToMethodDictionary(type);
+
+ Logger.Instance.Indent();
+ foreach (var field in type.Fields)
+ {
+ MethodDef proxyMethod;
+ bool supportType1 = fieldToMethod.TryGetValue(field, out proxyMethod);
+ bool supportType2 = field.IsStatic;
+
+ if (!supportType1 && !supportType2)
+ continue;
+
+ IMethod calledMethod;
+ OpCode callOpcode;
+ GetCallInfo(context, field, out calledMethod, out callOpcode);
+
+ if (calledMethod == null)
+ continue;
+
+ if (supportType1)
+ {
+ Add2(proxyMethod, new DelegateInfo(field, calledMethod, callOpcode));
+ }
+ if (supportType2)
+ {
+ AddDelegateInfo(new DelegateInfo(field, calledMethod, callOpcode));
+ }
+
+ Logger.v("Field: {0}, Opcode: {1}, Method: {2} ({3:X8})",
+ Utils.RemoveNewlines(field.Name),
+ callOpcode,
+ Utils.RemoveNewlines(calledMethod),
+ calledMethod.MDToken.Raw);
+ }
+ Logger.Instance.DeIndent();
+ delegateTypesDict[type] = true;
+ }
+ }
+
+ protected void Add2(MethodDef method, DelegateInfo di)
+ {
+ proxyMethodToDelegateInfo.Add(method, di);
+ }
+
+ protected abstract object CheckCctor(TypeDef type, MethodDef cctor);
+ protected abstract void GetCallInfo(object context, FieldDef field, out IMethod calledMethod, out OpCode callOpcode);
+
+ Dictionary GetFieldToMethodDictionary(TypeDef type)
+ {
+ var dict = new Dictionary();
+ foreach (var method in type.Methods)
+ {
+ if (!method.IsStatic || !method.HasBody || method.Name == ".cctor")
+ continue;
+
+ var instructions = method.Body.Instructions;
+ for (int i = 0; i < instructions.Count; i++)
+ {
+ var instr = instructions[i];
+ if (instr.OpCode.Code != Code.Ldsfld)
+ continue;
+ var field = instr.Operand as FieldDef;
+ if (field == null)
+ continue;
+
+ dict[field] = method;
+ break;
+ }
+ }
+ return dict;
+ }
+
+ protected override bool Deobfuscate(Blocks blocks, IList allBlocks)
+ {
+ var removeInfos = new Dictionary>();
+
+ foreach (var block in allBlocks)
+ {
+ var instrs = block.Instructions;
+ for (int i = 0; i < instrs.Count; i++)
+ {
+ var instr = instrs[i];
+
+ if (instr.OpCode == OpCodes.Call)
+ {
+ var method = instr.Operand as IMethod;
+ if (method == null)
+ continue;
+
+ var di = proxyMethodToDelegateInfo.Find(method);
+ if (di == null)
+ continue;
+
+ Add(removeInfos, block, i, di);
+ }
+ else if (instr.OpCode == OpCodes.Ldsfld)
+ {
+ var di = GetDelegateInfo(instr.Operand as IField);
+ if (di == null)
+ continue;
+
+ var callInfo = FindProxyCall(di, block, i);
+ if (callInfo != null)
+ {
+ Add(removeInfos, block, i, null);
+ Add(removeInfos, callInfo.Block, callInfo.Index, di);
+ }
+ else
+ {
+ errors++;
+ Logger.w("Could not fix proxy call. Method: {0} ({1:X8}), Proxy type: {2} ({3:X8})",
+ Utils.RemoveNewlines(blocks.Method),
+ blocks.Method.MDToken.ToInt32(),
+ Utils.RemoveNewlines(di.field.DeclaringType),
+ di.field.DeclaringType.MDToken.ToInt32());
+ }
+ }
+ }
+ }
+
+ return FixProxyCalls(blocks.Method, removeInfos);
+ }
+
+ protected virtual BlockInstr FindProxyCall(DelegateInfo di, Block block, int index)
+ {
+ return FindProxyCall(di, block, index, new Dictionary(), 1);
+ }
+
+ BlockInstr FindProxyCall(DelegateInfo di, Block block, int index, Dictionary visited, int stack)
+ {
+ if (visited.ContainsKey(block))
+ return null;
+ if (index <= 0)
+ visited[block] = true;
+
+ var instrs = block.Instructions;
+ for (int i = index + 1; i < instrs.Count; i++)
+ {
+ if (stack <= 0)
+ return null;
+ var instr = instrs[i];
+ instr.Instruction.UpdateStack(ref stack, false);
+ if (stack < 0)
+ return null;
+
+ if (instr.OpCode != OpCodes.Call && instr.OpCode != OpCodes.Callvirt)
+ {
+ if (stack <= 0)
+ return null;
+ continue;
+ }
+ var calledMethod = instr.Operand as IMethod;
+ if (calledMethod == null)
+ return null;
+ if (stack != (DotNetUtils.HasReturnValue(calledMethod) ? 1 : 0))
+ continue;
+ if (calledMethod.Name != "Invoke")
+ return null;
+
+ return new BlockInstr
+ {
+ Block = block,
+ Index = i,
+ };
+ }
+ if (stack <= 0)
+ return null;
+
+ foreach (var target in block.GetTargets())
+ {
+ var info = FindProxyCall(di, target, -1, visited, stack);
+ if (info != null)
+ return info;
+ }
+
+ return null;
+ }
+ }
}
diff --git a/de4dot.cui/Program.cs b/de4dot.cui/Program.cs
index 69796f28..012c72b4 100644
--- a/de4dot.cui/Program.cs
+++ b/de4dot.cui/Program.cs
@@ -124,7 +124,9 @@ namespace de4dot.cui {
Logger.Instance.LogErrorDontIgnore("{0}", ex.Message);
exitCode = 1;
}
- catch (Exception ex) {
+ /*catch (Exception ex)
+ {
+ throw;
if (PrintFullStackTrace()) {
PrintStackTrace(ex);
Logger.Instance.LogErrorDontIgnore("\nTry the latest version!");
@@ -134,7 +136,7 @@ namespace de4dot.cui {
Logger.Instance.LogErrorDontIgnore("Hmmmm... something didn't work. Try the latest version.");
}
exitCode = 1;
- }
+ }*/
if (Logger.Instance.NumIgnoredMessages > 0) {
if (Logger.Instance.NumIgnoredMessages == 1)