diff --git a/.vs/de4dot/v15/Server/sqlite3/db.lock b/.vs/de4dot/v15/Server/sqlite3/db.lock new file mode 100644 index 00000000..e69de29b diff --git a/.vs/de4dot/v15/Server/sqlite3/storage.ide b/.vs/de4dot/v15/Server/sqlite3/storage.ide new file mode 100644 index 00000000..57a761ee Binary files /dev/null and b/.vs/de4dot/v15/Server/sqlite3/storage.ide differ diff --git a/de4dot.code/de4dot.code.csproj b/de4dot.code/de4dot.code.csproj index 3f660a8f..ea058209 100644 --- a/de4dot.code/de4dot.code.csproj +++ b/de4dot.code/de4dot.code.csproj @@ -166,6 +166,7 @@ + diff --git a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs index 69441811..e722dcf1 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ConstantDecrypter.cs @@ -7,7 +7,7 @@ using System.Runtime.InteropServices; using System.Text; using de4dot.blocks; using de4dot.blocks.cflow; -using de4dot.code.deobfuscators.ConfuserEx.x86; +//using de4dot.code.deobfuscators.ConfuserEx.x86; using dnlib.DotNet; using dnlib.DotNet.Writer; using FieldAttributes = dnlib.DotNet.FieldAttributes; @@ -20,7 +20,8 @@ namespace de4dot.code.deobfuscators.ConfuserEx public class ConstantDecrypterBase { private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); - private X86Method _nativeMethod; + private x86Emulator _nativeEmulator; + //private X86Method _nativeMethod; public MethodDef Method { get; set; } public byte[] Decrypted { get; set; } @@ -28,6 +29,11 @@ namespace de4dot.code.deobfuscators.ConfuserEx public uint Magic2 { get; set; } public bool CanRemove { get; set; } = true; + public ConstantDecrypterBase(x86Emulator nativeEmulator) + { + _nativeEmulator = nativeEmulator; + } + // native mode public MethodDef NativeMethod { get; internal set; } @@ -43,7 +49,8 @@ namespace de4dot.code.deobfuscators.ConfuserEx return null; _instructionEmulator.Pop(); - var result = _nativeMethod.Execute(((Int32Value) popValue).Value); + // var result = _nativeMethod.Execute(((Int32Value) popValue).Value); + var result = (int?)_nativeEmulator.Emulate(NativeMethod, ((Int32Value)popValue).Value); return result; } @@ -53,7 +60,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx if (NativeMethod != null) { _instructionEmulator.Push(new Int32Value((int)index)); - _nativeMethod = new X86Method(NativeMethod, Method.Module as ModuleDefMD); //TODO: Possible null + //_nativeMethod = new X86Method(NativeMethod, Method.Module as ModuleDefMD); //TODO: Possible null var key = CalculateKey(); uint_0 = (uint)key.Value; @@ -115,12 +122,14 @@ namespace de4dot.code.deobfuscators.ConfuserEx private byte[] _decryptedBytes; private FieldDef _decryptedField, _arrayField; internal TypeDef ArrayType; + private x86Emulator _nativeEmulator; - public ConstantsDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator) + public ConstantsDecrypter(ModuleDef module, MethodDef lzmaMethod, ISimpleDeobfuscator deobfsucator, x86Emulator nativeEmulator) { _module = module; _lzmaMethod = lzmaMethod; _deobfuscator = deobfsucator; + _nativeEmulator = nativeEmulator; } public bool CanRemoveLzma { get; private set; } @@ -295,7 +304,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx if (IsNativeStringDecrypter(method, out MethodDef nativeMethod)) { - yield return new ConstantDecrypterBase + yield return new ConstantDecrypterBase(_nativeEmulator) { Decrypted = _decryptedBytes, Method = method, @@ -304,7 +313,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx } if (IsNormalStringDecrypter(method, out int num1, out int num2)) { - yield return new ConstantDecrypterBase + yield return new ConstantDecrypterBase(_nativeEmulator) { Decrypted = _decryptedBytes, Method = method, diff --git a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs index 8dcc6265..aff82be5 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ControlFlowFixer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using de4dot.blocks; using de4dot.blocks.cflow; -using de4dot.code.deobfuscators.ConfuserEx.x86; +//using de4dot.code.deobfuscators.ConfuserEx.x86; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -15,10 +15,16 @@ namespace de4dot.code.deobfuscators.ConfuserEx public List NativeMethods = new List(); private readonly InstructionEmulator _instructionEmulator = new InstructionEmulator(); + private x86Emulator _nativeEmulator; private Blocks _blocks; private Local _switchKey; + public ControlFlowFixer(x86Emulator nativeEmulator) + { + _nativeEmulator = nativeEmulator; + } + private int? CalculateKey(SwitchData switchData) { var popValue = _instructionEmulator.Peek(); @@ -31,8 +37,9 @@ namespace de4dot.code.deobfuscators.ConfuserEx if (switchData is NativeSwitchData) { var nativeSwitchData = (NativeSwitchData)switchData; - var nativeMethod = new X86Method(nativeSwitchData.NativeMethodDef, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null - return nativeMethod.Execute(num); + //var nativeMethod = new X86Method(nativeSwitchData.NativeMethodDef, _blocks.Method.Module as ModuleDefMD); //TODO: Possible null + //return nativeMethod.Execute(num); + return (int?)_nativeEmulator.Emulate(nativeSwitchData.NativeMethodDef, num); } if (switchData is NormalSwitchData) { diff --git a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs index a0e11c5b..8fcf7d0d 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/Deobfuscator.cs @@ -50,7 +50,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx private class Deobfuscator : DeobfuscatorBase { - private readonly ControlFlowFixer _controlFlowFixer = new ControlFlowFixer(); + private ControlFlowFixer _controlFlowFixer; private bool _canRemoveLzma = true; private ConstantsDecrypter _constantDecrypter; @@ -58,6 +58,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx private LzmaFinder _lzmaFinder; private ProxyCallFixer _proxyCallFixer; private ResourceDecrypter _resourceDecrypter; + private x86Emulator _nativeEmulator; private string _version = ""; public Deobfuscator(Options options) @@ -103,10 +104,13 @@ namespace de4dot.code.deobfuscators.ConfuserEx protected override void ScanForObfuscator() { + _nativeEmulator = new x86Emulator(DeobUtils.ReadModule(module)); + + _controlFlowFixer = new ControlFlowFixer(_nativeEmulator); _lzmaFinder = new LzmaFinder(module, DeobfuscatedFile); _lzmaFinder.Find(); - _constantDecrypter = new ConstantsDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); + _constantDecrypter = new ConstantsDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile, _nativeEmulator); _resourceDecrypter = new ResourceDecrypter(module, _lzmaFinder.Method, DeobfuscatedFile); if (_lzmaFinder.FoundLzma) @@ -115,7 +119,7 @@ namespace de4dot.code.deobfuscators.ConfuserEx _resourceDecrypter.Find(); } - _proxyCallFixer = new ProxyCallFixer(module, DeobfuscatedFile); + _proxyCallFixer = new ProxyCallFixer(module, DeobfuscatedFile, _nativeEmulator); _proxyCallFixer.FindDelegateCreatorMethod(); _proxyCallFixer.Find(); diff --git a/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs index 6a8e4611..2466ae27 100644 --- a/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs +++ b/de4dot.code/deobfuscators/ConfuserEx/ProxyCallFixer.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Linq; using de4dot.blocks; using de4dot.blocks.cflow; -using de4dot.code.deobfuscators.ConfuserEx.x86; +//using de4dot.code.deobfuscators.ConfuserEx.x86; using dnlib.DotNet; using dnlib.DotNet.Emit; @@ -31,10 +31,12 @@ namespace de4dot.code.deobfuscators.ConfuserEx public List AttributeTypes = new List(); public List DelegateCreatorMethods = new List(); public List NativeMethods = new List(); + private x86Emulator _nativeEmulator; - public ProxyCallFixer(ModuleDefMD module, ISimpleDeobfuscator simpleDeobfuscator) : base(module) + public ProxyCallFixer(ModuleDefMD module, ISimpleDeobfuscator simpleDeobfuscator, x86Emulator nativeEmulator) : base(module) { _simpleDeobfuscator = simpleDeobfuscator; + _nativeEmulator = nativeEmulator; } public ProxyCallFixer(ModuleDefMD module, ProxyCallFixer4 oldOne) : base(module, oldOne) @@ -167,8 +169,9 @@ namespace de4dot.code.deobfuscators.ConfuserEx private int EmulateNativeMethod(MethodDef externalMethod, int parameter) { - var nativeMethod = new X86Method(externalMethod, module); //TODO: Possible null - return nativeMethod.Execute(parameter); + //var nativeMethod = new X86Method(externalMethod, module); //TODO: Possible null + //return nativeMethod.Execute(parameter); + return (int)_nativeEmulator.Emulate(externalMethod, parameter); } private int EmulateManagedMethod(MethodDef method, int startIndex, int endIndex, diff --git a/de4dot.code/deobfuscators/ConfuserEx/x86Emulator.cs b/de4dot.code/deobfuscators/ConfuserEx/x86Emulator.cs new file mode 100644 index 00000000..6fbf0dc0 --- /dev/null +++ b/de4dot.code/deobfuscators/ConfuserEx/x86Emulator.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using dnlib.DotNet; +using dnlib.IO; + +namespace de4dot.code.deobfuscators.ConfuserEx +{ + public class x86Emulator : IDisposable { + + static readonly byte[] prolog2 = new byte[] { + 0x89, 0xE0, 0x53, 0x57, 0x56, 0x29, 0xe0, + 0x83, 0xf8, 0x18, 0x74, 0x07, 0x8b, 0x44, + 0x24, 0x10, 0x50, 0xeb, 0x01, 0x51 + }; + static readonly byte[] epilog2 = new byte[] { + 0x5E, 0x5F, 0x5B, 0xC3, + }; + + MyPEImage peImage; + IBinaryReader reader; + uint[] args; + int nextArgIndex; + uint[] regs = new uint[8]; + byte modRM, mod, reg, rm; + enum OpCode { + Add_RI, + Add_RR, + Mov_RI, + Mov_RR, + IMul_RI, + IMul_RR, + Neg_R, + Not_R, + Pop_R, + Sub_RI, + Sub_RR, + Xor_RI, + Xor_RR, + } + interface IOperand {} + class RegOperand : IOperand { + static readonly string[] names = new string[8] { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", }; + public readonly int reg; + public RegOperand(int reg) { this.reg = reg; } + public override string ToString() { return names[reg]; } + } + + class ImmOperand : IOperand { + public readonly int imm; + + public ImmOperand(int imm) { + this.imm = imm; + } + + public override string ToString() { + return string.Format("{0:X2}h", imm); + } + } + + class Instruction { + public readonly OpCode opCode; + public IOperand op1; + public IOperand op2; + + public Instruction(OpCode opCode) : this(opCode, null, null) { } + public Instruction(OpCode opCode, IOperand op1) : this(opCode, op1, null) { } + + public Instruction(OpCode opCode, IOperand op1, IOperand op2) { + this.opCode = opCode; + this.op1 = op1; + this.op2 = op2; + } + + public override string ToString() { + if (op1 != null && op2 != null) + return string.Format("{0} {1},{2}", opCode, op1, op2); + if (op1 != null) + return string.Format("{0} {1}", opCode, op1); + return string.Format("{0}", opCode); + } + } + + public x86Emulator(byte[] fileData) { + peImage = new MyPEImage(fileData); + reader = peImage.Reader; + } + + public uint Emulate(MethodDef method, int arg) + { + return Emulate((uint)method.RVA, new uint[] { (uint)arg }); + } + + public uint Emulate(uint rva, uint arg) { + return Emulate(rva, new uint[] { arg }); + } + + public uint Emulate(uint rva, uint[] args) { + Initialize(args); + reader.Position = peImage.RvaToOffset(rva); + byte[] prolog, epilog; + if (IsBytes(prolog2)) { + prolog = prolog2; + epilog = epilog2; + }else + throw new ApplicationException(string.Format("Missing prolog @ RVA {0:X8}", rva)); + reader.Position += prolog.Length; + while (!IsBytes(epilog)) + Emulate(); + + return regs[0]; + } + + void Initialize(uint[] args) { + this.args = args; + nextArgIndex = 0; + for (int i = 0; i < regs.Length; i++) + regs[i] = 0; + } + + bool IsBytes(IList bytes) { + long oldPos = reader.Position; + bool result = true; + for (int i = 0; i < bytes.Count; i++) { + if (bytes[i] != reader.ReadByte()) { + result = false; + break; + } + } + reader.Position = oldPos; + return result; + } + + void Emulate() { + var instr = Decode(); + switch (instr.opCode) + { + case OpCode.Add_RI: + case OpCode.Add_RR: + WriteReg(instr.op1, ReadOp(instr.op1) + ReadOp(instr.op2)); + break; + + case OpCode.Mov_RI: + case OpCode.Mov_RR: + WriteReg(instr.op1, ReadOp(instr.op2)); + break; + case OpCode.IMul_RI: + case OpCode.IMul_RR: + WriteReg(instr.op1, ReadOp(instr.op1) * ReadOp(instr.op2)); + break; + case OpCode.Neg_R: + WriteReg(instr.op1, (uint)-(int)ReadOp(instr.op1)); + break; + + case OpCode.Not_R: + WriteReg(instr.op1, ~ReadOp(instr.op1)); + break; + + case OpCode.Pop_R: + WriteReg(instr.op1, GetNextArg()); + break; + + case OpCode.Sub_RI: + case OpCode.Sub_RR: + WriteReg(instr.op1, ReadOp(instr.op1) - ReadOp(instr.op2)); + break; + + case OpCode.Xor_RI: + case OpCode.Xor_RR: + WriteReg(instr.op1, ReadOp(instr.op1) ^ ReadOp(instr.op2)); + break; + + default: throw new NotSupportedException(); + } + } + + uint GetNextArg() { + if (nextArgIndex >= args.Length) + throw new ApplicationException("No more args"); + return args[nextArgIndex++]; + } + + void WriteReg(IOperand op, uint val) { + regs[((RegOperand)op).reg] = val; + } + + uint ReadOp(IOperand op) { + if (op is RegOperand regOp) + return regs[regOp.reg]; + if (op is ImmOperand immOp) + return (uint)immOp.imm; + throw new NotSupportedException(); + } + + Instruction Decode() + { + byte opc = reader.ReadByte(); + switch (opc) + { + case 0x01: // ADD Ed,Gd + ParseModRM(); + return new Instruction(OpCode.Add_RR, new RegOperand(rm), new RegOperand(reg)); + + case 0x0F: // IMUL Ed,Gd + ParseModRM(); + return new Instruction(OpCode.IMul_RR, new RegOperand(rm), new RegOperand(reg)); + + case 0x29: // SUB Ed,Gd + ParseModRM(); + return new Instruction(OpCode.Sub_RR, new RegOperand(rm), new RegOperand(reg)); + + case 0x31: // XOR Ed,Gd + ParseModRM(); + return new Instruction(OpCode.Xor_RR, new RegOperand(rm), new RegOperand(reg)); + + case 0x58: // POP EAX + case 0x59: // POP ECX + case 0x5A: // POP EDX + case 0x5B: // POP EBX + case 0x5C: // POP ESP + case 0x5D: // POP EBP + case 0x5E: // POP ESI + case 0x5F: // POP EDI + return new Instruction(OpCode.Pop_R, new RegOperand(opc - 0x58)); + + case 0x69: // Imul Ed, Id + ParseModRM(); + return new Instruction(OpCode.IMul_RI, new RegOperand(rm), new ImmOperand(reader.ReadInt32())); + + case 0x81: // Grp1 Ed,Id + ParseModRM(); + switch (reg) + { + case 0: return new Instruction(OpCode.Add_RI, new RegOperand(rm), new ImmOperand(reader.ReadInt32())); + case 5: return new Instruction(OpCode.Sub_RI, new RegOperand(rm), new ImmOperand(reader.ReadInt32())); + case 6: return new Instruction(OpCode.Xor_RI, new RegOperand(rm), new ImmOperand(reader.ReadInt32())); + default: throw new NotSupportedException(); + } + + case 0x89: // MOV Ed,Gd + ParseModRM(); + return new Instruction(OpCode.Mov_RR, new RegOperand(rm), new RegOperand(reg)); + + case 0xB8: // MOV EAX,Id + case 0xB9: // MOV ECX,Id + case 0xBA: // MOV EDX,Id + case 0xBB: // MOV EBX,Id + case 0xBC: // MOV ESP,Id + case 0xBD: // MOV EBP,Id + case 0xBE: // MOV ESI,Id + case 0xBF: // MOV EDI,Id + return new Instruction(OpCode.Mov_RI, new RegOperand(opc - 0xB8), new ImmOperand(reader.ReadInt32())); + + case 0xF7: // Grp3 Ev + ParseModRM(); + switch (reg) + { + case 2: return new Instruction(OpCode.Not_R, new RegOperand(rm)); + case 3: return new Instruction(OpCode.Neg_R, new RegOperand(rm)); + default: throw new NotSupportedException(); + } + + default: throw new NotSupportedException(string.Format("Invalid opcode: {0:X2}", opc)); + } + } + + void ParseModRM() { + modRM = reader.ReadByte(); + mod = (byte)((modRM >> 6) & 7); + reg = (byte)((modRM >> 3) & 7); + rm = (byte)(modRM & 7); + if (mod != 3) + throw new ApplicationException("Memory operand"); + } + + public void Dispose() { + if (peImage != null) + peImage.Dispose(); + peImage = null; + reader = null; + } + } +}