From e0a2e805d4b6728527a346a54ef9514ace15e6d0 Mon Sep 17 00:00:00 2001 From: ViR Dash Date: Tue, 25 Jul 2017 17:37:41 +0300 Subject: [PATCH] ConfuserEx deobfuscator updates and misc changes ConfuserEx changes: * Implement Proxy Call Fixer * Refactor Control Flow Fixer Disable main exception handler to let de4dot throw on error --- de4dot.code/de4dot.code.csproj | 5 +- .../ConfuserEx/ConstantDecrypter.cs | 408 +++++------ ...ntrolFlowSolver.cs => ControlFlowFixer.cs} | 632 +++++++++--------- .../deobfuscators/ConfuserEx/Deobfuscator.cs | 146 ++-- .../ConfuserEx/ProxyCallFixer.cs | 415 ++++++++++++ .../ConfuserEx/ResourceDecrypter.cs | 2 +- .../deobfuscators/ProxyCallFixerBase.cs | 238 +++++++ de4dot.cui/Program.cs | 6 +- 8 files changed, 1238 insertions(+), 614 deletions(-) rename de4dot.code/deobfuscators/ConfuserEx/{ControlFlowSolver.cs => ControlFlowFixer.cs} (94%) create mode 100644 de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs 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)