static void Main(string[] args) { Console.WriteLine("Version: " + BeaEngine.Version); Console.WriteLine("Revision: " + BeaEngine.Revision); UnmanagedBuffer buffer = new UnmanagedBuffer(File.ReadAllBytes("BeaEngine.dll")); var disasm = new Disasm(); disasm.EIP = new IntPtr(buffer.Ptr.ToInt64() + 0x400); for(int counter = 0; counter < 100; ++counter) { int result = BeaEngine.Disasm(disasm); if (result == (int)BeaConstants.SpecialInfo.UNKNOWN_OPCODE) break; Console.WriteLine("0x" + disasm.EIP.ToString("X") + " " + disasm.CompleteInstr); disasm.EIP = new IntPtr(disasm.EIP.ToInt64() + result); } }
public static extern int Disasm([In, Out, MarshalAs(UnmanagedType.LPStruct)] Disasm disasm);
private bool ExecuteJump(string disasmStr, Disasm disasm, string mnemonic) { bool shouldJump = false; if (mnemonic == "jmp") shouldJump = true; else if (mnemonic == "je" || mnemonic == "jz") // Jump short if equal (ZF=1). shouldJump = EFlags.HasFlag(EFlags.ZeroFlag); else if (mnemonic == "jne" || mnemonic == "jnz") // Jump short if not equal (ZF=0). shouldJump = !EFlags.HasFlag(EFlags.ZeroFlag); else if (mnemonic == "ja" || mnemonic == "jnbe") // Jump short if not below or equal (CF=0 and ZF=0). shouldJump = !EFlags.HasFlag(EFlags.CarryFlag) && !EFlags.HasFlag(EFlags.ZeroFlag); else if (mnemonic == "jb" || mnemonic == "jc" || mnemonic == "jnae") // Jump short if not above or equal (CF=1). shouldJump = EFlags.HasFlag(EFlags.CarryFlag); else if (mnemonic == "jng" || mnemonic == "jle") // Jump short if not greater (ZF=1 or SF != OF). shouldJump = EFlags.HasFlag(EFlags.ZeroFlag) || (EFlags.HasFlag(EFlags.SignFlag) != EFlags.HasFlag(EFlags.OverflowFlag)); else if (mnemonic == "jnc" || mnemonic == "jnb" || mnemonic == "jae") // Jump short if not carry (CF=0). shouldJump = !EFlags.HasFlag(EFlags.CarryFlag); else if (mnemonic == "jna" || mnemonic == "jbe") // Jump short if not above (CF=1 or ZF=1). shouldJump = EFlags.HasFlag(EFlags.CarryFlag) || EFlags.HasFlag(EFlags.ZeroFlag); else if (mnemonic == "jge" || mnemonic == "jnl") // Jump short if greater or equal (SF=OF). shouldJump = EFlags.HasFlag(EFlags.SignFlag) == EFlags.HasFlag(EFlags.OverflowFlag); else if (mnemonic == "jg" || mnemonic == "jnle") // Jump short if greater (ZF=0 and SF=OF). shouldJump = !EFlags.HasFlag(EFlags.ZeroFlag) && (EFlags.HasFlag(EFlags.SignFlag) == EFlags.HasFlag(EFlags.OverflowFlag)); else if (mnemonic == "jl" || mnemonic == "jnge") // Jump short if less (SF != OF). shouldJump = EFlags.HasFlag(EFlags.SignFlag) != EFlags.HasFlag(EFlags.OverflowFlag); else if (mnemonic == "jecxz") // Jump short if ECX register is 0. shouldJump = Ecx.Value == 0; else if (mnemonic == "jcxz") // Jump short if CX register is 0. shouldJump = Ecx.Cx == 0; else if (mnemonic == "jno") // Jump short if not overflow (OF=0) shouldJump = !EFlags.HasFlag(EFlags.OverflowFlag); else if (mnemonic == "jnp" || mnemonic == "jpo") // Jump short if not parity (PF=0) shouldJump = !EFlags.HasFlag(EFlags.ParityFlag); else if (mnemonic == "jns") // Jump short if not sign (SF=0) shouldJump = !EFlags.HasFlag(EFlags.SignFlag); else if (mnemonic == "jo") // Jump short if overflow (OF=1) shouldJump = !EFlags.HasFlag(EFlags.OverflowFlag); else if (mnemonic == "jp" || mnemonic == "jpe") // Jump short if parity (PF=1) shouldJump = !EFlags.HasFlag(EFlags.ParityFlag); else if (mnemonic == "js") // Jump short if sign (SF=1) shouldJump = !EFlags.HasFlag(EFlags.SignFlag); else throw new Exception("Invalid jump type detected."); if (!shouldJump) return false; // We should jump. return true; }
private bool ExecuteASM(Disasm disasm, UnmanagedBuffer buffer, bool followCalls) { if (Interrupted) return false; var entryPoint = buffer.Ptr.ToInt32(); var disassemblyString = disasm.CompleteInstr; // Sanitize the instruction. if (disassemblyString.IndexOf(";") != -1) disassemblyString = disassemblyString.Remove(disassemblyString.IndexOf(";")); disassemblyString = Regex.Replace(disassemblyString, @"\s+", " ", RegexOptions.IgnoreCase).Trim(); if (Regex.IsMatch(disassemblyString, @"[!@#$%^&()={}\\|/?<>.~`""'_]")) return false; if (disassemblyString.Length == 0 || disassemblyString.EndsWith(":")) return false; var tokens = disassemblyString.Split(' '); var mnemonic = tokens[0]; //Console.WriteLine("0x{0:X8} {1}", disasm.EIP.ToInt64(), disasm.CompleteInstr); if (disassemblyString.Contains("call far") || disassemblyString.Contains("xmm")) return false; List<InstructionArgument> arguments = new List<InstructionArgument>(); // TODO Support for floating point opcodes var parametersList = disassemblyString.Replace(mnemonic, String.Empty).Trim().Split(new char[]{ ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (var parameter in parametersList) arguments.Add(new InstructionArgument(this, parameter.Trim())); if (mnemonic[0] == 'j') { if (ExecuteJump(disassemblyString, disasm, mnemonic)) { var jumpOffset = arguments[0].GetValue(); if (arguments[0].Type == ArgumentType.Memory) jumpOffset += (uint)entryPoint - 0x400C00; disasm.EIP = new IntPtr(jumpOffset); return true; } else return false; } else { InstructionArgument src = null; InstructionArgument dst = null; InstructionArgument extra = null; if (parametersList.Length == 1) dst = src = arguments[0]; else if (parametersList.Length == 2) { dst = arguments[0]; src = arguments[1]; } else if (parametersList.Length == 3) extra = arguments[2]; switch (mnemonic) { case "ret": case "retn": // Actually use the stack here. if (JumpBacks.Count > 0) { Pop(4); disasm.EIP = new IntPtr(JumpBacks[0]); JumpBacks.RemoveAt(0); return true; } return false; case "leave": break; case "nop": break; #region Data transfer case "mov": if (parametersList.Length != 2 || dst.Type == ArgumentType.Immediate) break; if (src.Type == ArgumentType.Immediate && src.Size > dst.Size) break; if (src.Type != ArgumentType.Immediate && src.Size != dst.Size) break; dst.SetValue(src.GetValue()); break; case "movzx": { if (parametersList.Length != 2 || dst.Type != ArgumentType.Register || src.Type == ArgumentType.Immediate || src.Size >= dst.Size) break; //! TODO Unhack this - Handles indirect jumptables //! TODO: Really need to fix memory - this superbly fails now //if (dst.Register == null) //{ /*if (src.Size == 4) addr = buffer.ReadDword(src.Address - 0x400C00); else if (src.Size == 2) addr = (uint)buffer.ReadWord(src.Address - 0x400C00); else if (src.Size == 1) addr = (uint)buffer.ReadByte(src.Address - 0x400C00);*/ //} dst.SetValue(src.GetValue()); break; } case "movsb": WriteByteToMemory(Edi.Value, ReadByteFromMemory(Esi.Value)); Ecx.Value--; if (EFlags.HasFlag(EFlags.DirectionFlag)) { Edi.Value--; Esi.Value--; } else { Edi.Value++; Esi.Value++; } break; case "movsw": WriteWordToMemory(Edi.Value, ReadWordFromMemory(Esi.Value)); Ecx.Value--; if (EFlags.HasFlag(EFlags.DirectionFlag)) { Edi.Value -= 2; Esi.Value -= 2; } else { Edi.Value += 2; Esi.Value += 2; } break; case "movsd": WriteDwordToMemory(Edi.Value, ReadDwordFromMemory(Esi.Value)); Ecx.Value--; if (EFlags.HasFlag(EFlags.DirectionFlag)) { Edi.Value -= 4; Esi.Value -= 4; } else { Edi.Value += 4; Esi.Value += 4; } break; case "stosb": WriteByteToMemory(Edi.Value, Eax.Al); Ecx.Value--; if (EFlags.HasFlag(EFlags.DirectionFlag)) { Edi.Value--; Esi.Value--; } else { Edi.Value++; Esi.Value++; } break; case "stosw": WriteWordToMemory(Edi.Value, Eax.Ax); Ecx.Value--; if (EFlags.HasFlag(EFlags.DirectionFlag)) { Edi.Value -= 2; Esi.Value -= 2; } else { Edi.Value += 2; Esi.Value += 2; } break; case "stosd": WriteDwordToMemory(Edi.Value, Eax.Value); Ecx.Value--; if (EFlags.HasFlag(EFlags.DirectionFlag)) { Edi.Value -= 4; Esi.Value -= 4; } else { Edi.Value += 4; Esi.Value += 4; } break; case "xchg": if (parametersList.Length != 2 || dst.Type != ArgumentType.Register || src.Type == ArgumentType.Immediate || src.Size != dst.Size) break; uint tmp = src.GetValue(); src.SetValue(dst.GetValue()); dst.SetValue(tmp); break; case "lea": if (parametersList.Length == 2 && dst.Type == ArgumentType.Register && src.Type == ArgumentType.Memory && src.Size > 1) dst.SetValue(src.Address); break; #endregion #region Basic maths case "inc": if (parametersList.Length == 1 && dst.Type != ArgumentType.Immediate) { ulong val = (ulong)dst.GetValue() + 1; UpdateFlags(val, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.AdjustFlag | EFlags.ParityFlag); dst.SetValue((uint)val); } break; case "dec": if (parametersList.Length == 1 && dst.Type != ArgumentType.Immediate) { ulong val = (ulong)dst.GetValue() - 1; UpdateFlags(val, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.AdjustFlag | EFlags.ParityFlag); dst.SetValue((uint)val); } break; case "not": if (parametersList.Length == 1 && dst.Type != ArgumentType.Immediate) dst.SetValue(~dst.GetValue()); break; case "neg": if (parametersList.Length == 1 && dst.Type != ArgumentType.Immediate) { ulong val = (ulong)(-(long)dst.GetValue()); UpdateFlags(val, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.AdjustFlag | EFlags.ParityFlag | EFlags.CarryFlag); dst.SetValue((uint)val); } break; case "bswap": if (parametersList.Length != 1 || dst.Type != ArgumentType.Register || dst.Size != 4) break; uint dval = dst.GetValue(); dst.SetValue((uint)((dval << 24) | ((dval & 0xFF00) << 8) | ((dval & 0xFF0000) >> 8) | (dval >> 24))); break; case "add": if (parametersList.Length == 2) { ulong val = (ulong)dst.GetValue() + src.GetValue(); UpdateFlags(val, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.AdjustFlag | EFlags.ParityFlag | EFlags.CarryFlag); dst.SetValue((uint)val); } break; case "sub": if (parametersList.Length == 2) { ulong val = (ulong)dst.GetValue() - (ulong)src.GetValue(); UpdateFlags(val, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.AdjustFlag | EFlags.ParityFlag | EFlags.CarryFlag); dst.SetValue((uint)val); } break; case "mul": // unsigned multiply // The OF and CF flags are set to 0 if the upper half of the result is 0, otherwise they are set to 1 if (parametersList.Length != 1 || src.Type == ArgumentType.Immediate) break; switch (src.Size) { case 1: ushort r16 = (ushort)(Eax.Al * src.GetValue()); Eax.Ax = r16; ToggleFlagIf(EFlags.OverflowFlag | EFlags.CarryFlag, (r16 >> 8) == 0); break; case 2: uint r32 = Eax.Ax * src.GetValue(); Edx.Dx = (ushort)(r32 >> 16); // high order Eax.Ax = (ushort)(r32 & 0xFFFF); // low order ToggleFlagIf(EFlags.OverflowFlag | EFlags.CarryFlag, Edx.Dx == 0); break; case 4: ulong r64 = Eax.Ax * src.GetValue(); Edx.Value = (uint)(r64 >> 32); // high order Eax.Value = (uint)(r64 & 0xFFFFFFFF); // low order ToggleFlagIf(EFlags.OverflowFlag | EFlags.CarryFlag, Edx.Value == 0); break; } break; case "div": if (src.Type == ArgumentType.Immediate) break; ulong dividend, quotient; uint divisor = src.GetValue(); if (divisor == 0) break; if (src.Size == 1) { dividend = Eax.Ax; quotient = dividend / divisor; if (quotient > 0xFF) break; Eax.Al = (byte)quotient; Eax.Ah = (byte)(dividend % divisor); } else if (src.Size == 2) { dividend = ((uint)Edx.Dx << 16) & Eax.Ax; quotient = dividend / divisor; if (quotient > 0xFFFF) break; Eax.Ax = (ushort)quotient; Edx.Dx = (ushort)(dividend % divisor); } else if (src.Size == 4) { dividend = ((ulong)Edx.Value << 32) & Eax.Value; quotient = dividend / divisor; if (quotient > 0xFFFFFFFF) break; Eax.Value = (uint)quotient; Edx.Value = (uint)(dividend % divisor); } break; case "imul": // Signed multiply case "idiv": // Signed division break; // NYI #endregion #region Bitwise operators case "and": if (parametersList.Length == 2) { uint result = dst.GetValue() & src.GetValue(); dst.SetValue(result); ClearFlags(EFlags.OverflowFlag | EFlags.CarryFlag); UpdateFlags(result, dst.Size, EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag); } break; case "or": if (parametersList.Length == 2) { uint result = dst.GetValue() | src.GetValue(); dst.SetValue(result); ClearFlags(EFlags.OverflowFlag | EFlags.CarryFlag); UpdateFlags(result, dst.Size, EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag); } else throw new ArgumentException(); break; case "xor": if (parametersList.Length == 2) { uint result = dst.GetValue() ^ src.GetValue(); dst.SetValue(result); ClearFlags(EFlags.OverflowFlag | EFlags.CarryFlag); UpdateFlags(result, dst.Size, EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag); } break; case "stc": // set carry flag SetFlags(EFlags.CarryFlag); break; case "std": // set direction flag SetFlags(EFlags.DirectionFlag); break; case "clc": // clear carry flag ClearFlags(EFlags.CarryFlag); break; case "cld": // clear direction flag ClearFlags(EFlags.DirectionFlag); break; case "shr": // shift right { if (parametersList.Length != 2 || dst.Type == ArgumentType.Immediate || src.Type == ArgumentType.Memory || (src.Type == ArgumentType.Register && src.RegisterName != "cl")) break; uint result = dst.GetValue() >> (byte)src.GetValue(); dst.SetValue(result); if (src.Type == ArgumentType.Immediate && src.GetValue() == 1) UpdateFlags(result, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag | EFlags.CarryFlag); else UpdateFlags(result, dst.Size, EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag | EFlags.CarryFlag); break; } case "shl": // shift left case "sal": // shift arithmetic left (signed) { if (parametersList.Length != 2 || dst.Type == ArgumentType.Immediate || src.Type == ArgumentType.Memory || (src.Type == ArgumentType.Register && src.RegisterName != "cl")) break; ulong result = (ulong)dst.GetValue() << (byte)src.GetValue(); dst.SetValue((uint)result); if (src.Type == ArgumentType.Immediate && src.GetValue() == 1) UpdateFlags(result, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag | EFlags.CarryFlag); else UpdateFlags(result, dst.Size, EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag | EFlags.CarryFlag); break; } case "sar": // shift arithmetic right (signed) { if (parametersList.Length != 2 || dst.Type == ArgumentType.Immediate || src.Type == ArgumentType.Memory || (src.Type == ArgumentType.Register && src.RegisterName != "cl")) break; uint result = (uint)((int)dst.GetValue() >> (byte)src.GetValue()); dst.SetValue(result); if (src.Type == ArgumentType.Immediate && src.GetValue() == 1) UpdateFlags(result, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag | EFlags.CarryFlag); else UpdateFlags(result, dst.Size, EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag | EFlags.CarryFlag); break; } case "ror": // rotate right { // bits >> n | (bits << (32 - n)); uint data = dst.GetValue(); uint bitsToShift = src.GetValue(); data = data >> (int)bitsToShift | (data << (32 - (int)bitsToShift)); dst.SetValue(data); if (src.Type == ArgumentType.Immediate && src.GetValue() == 1) UpdateFlags(data, dst.Size, EFlags.OverflowFlag | EFlags.CarryFlag); else UpdateFlags(data, dst.Size, EFlags.CarryFlag); break; } case "rol": // rotate left { // bits << n | (bits >> (32 - n)); uint data2 = dst.GetValue(); uint bitsToShift2 = src.GetValue(); uint data = data2 << (int)bitsToShift2 | (data2 >> (32 - (int)bitsToShift2)); dst.SetValue(data); if (src.Type == ArgumentType.Immediate && src.GetValue() == 1) UpdateFlags(data, dst.Size, EFlags.OverflowFlag | EFlags.CarryFlag); else UpdateFlags(data, dst.Size, EFlags.CarryFlag); break; } #endregion case "call": // TODO Use the stack to keep s and r (saved registers (0) and jumpback offset (+4)) // TODO Half done. (see below) var offset = (int)disasm.Instruction.AddrValue - buffer.Ptr.ToInt32(); CallOffsets.Add((uint)offset); if (followCalls) { Push(offset+5); // Push the return address on the stack JumpBacks.Add((uint)(disasm.EIP.ToInt32() + 5)); disasm.EIP = new IntPtr((int)disasm.Instruction.AddrValue); return true; } return false; case "cmp": UpdateFlags((ulong)dst.GetValue() - (ulong)src.GetValue(), dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.AdjustFlag | EFlags.ParityFlag | EFlags.CarryFlag); break; case "test": ClearFlags(EFlags.OverflowFlag | EFlags.CarryFlag); UpdateFlags(dst.GetValue() & src.GetValue(), dst.Size, EFlags.SignFlag | EFlags.ZeroFlag | EFlags.ParityFlag); break; case "sbb": if (parametersList.Length != 2) break; ulong newValue = (ulong)dst.GetValue() - (ulong)src.GetValue(); if (EFlags.HasFlag(EFlags.CarryFlag)) newValue -= 1; dst.SetValue((uint)newValue); UpdateFlags(newValue, dst.Size, EFlags.OverflowFlag | EFlags.SignFlag | EFlags.ZeroFlag | EFlags.AdjustFlag | EFlags.CarryFlag); break; #region Stack operations case "push": // todo: check for stack overflow (stack pointer < base address) if (parametersList.Length == 1) { uint s = dst.Size; if (s == 2 || s == 4) { WriteMemory(Esp - s, dst.GetValue()); Esp.Value -= s; } } break; case "pop": // todo: check for stack overflow (stack pointer > base address + memory size) if (parametersList.Length == 1 && dst.Type != ArgumentType.Immediate) { uint s = dst.Size; if (s == 2 || s == 4) { dst.SetValue(ReadMemory(Esp, s)); Esp.Value += s; } } break; case "pusha": if (parametersList.Length == 0) { WriteMemory(Esp - 2, Eax.Ax); WriteMemory(Esp - 4, Ecx.Cx); WriteMemory(Esp - 6, Edx.Dx); WriteMemory(Esp - 8, Ebx.Bx); WriteMemory(Esp - 10, Esp.Sp); WriteMemory(Esp - 12, Ebp.Bp); WriteMemory(Esp - 14, Esi.Si); WriteMemory(Esp - 16, Edi.Di); Esp.Value -= 16; } break; case "pushad": if (parametersList.Length == 0) { WriteMemory(Esp - 4, Eax.Value); WriteMemory(Esp - 8, Ecx.Value); WriteMemory(Esp - 12, Edx.Value); WriteMemory(Esp - 16, Ebx.Value); WriteMemory(Esp - 20, Esp.Value); WriteMemory(Esp - 24, Ebp.Value); WriteMemory(Esp - 28, Esi.Value); WriteMemory(Esp - 32, Edi.Value); Esp.Value -= 32; } break; case "popa": if (parametersList.Length == 0) { Edi.Di = ReadWordFromMemory(Esp); Esi.Si = ReadWordFromMemory(Esp + 2); Ebp.Bp = ReadWordFromMemory(Esp + 4); //Esp.Sp = ReadWord(Esp + 6); // ignored Ebx.Bx = ReadWordFromMemory(Esp + 8); Edx.Dx = ReadWordFromMemory(Esp + 10); Ecx.Cx = ReadWordFromMemory(Esp + 12); Eax.Ax = ReadWordFromMemory(Esp + 14); Esp.Value += 16; } else throw new ArgumentException(); break; case "popad": if (parametersList.Length == 0) { Edi.Value = ReadDwordFromMemory(Esp); Esi.Value = ReadDwordFromMemory(Esp + 4); Ebp.Value = ReadDwordFromMemory(Esp + 8); //Esp.Value = ReadDoubleword(Esp + 12); // ignored Ebx.Value = ReadDwordFromMemory(Esp + 16); Edx.Value = ReadDwordFromMemory(Esp + 20); Ecx.Value = ReadDwordFromMemory(Esp + 24); Eax.Value = ReadDwordFromMemory(Esp + 28); Esp.Value += 32; } break; // todo: pushf and popf, and effects on eflags #endregion } } return false; }
public void Execute(uint offset, UnmanagedBuffer buffer, bool followCalls = false) { try { Esp.Value -= 4; // Assume we are calling from the beginning of a function. return address is on the stack Disasm disasm = new Disasm(); disasm.EIP = new IntPtr(buffer.Ptr.ToInt64() + offset); // new IntPtr(buffer.Ptr.ToInt64()/* + offset*/); while (true) { var result = BeaEngine.Disassemble(disasm); if (result == BeaConstants.SpecialInfo.UNKNOWN_OPCODE) return; // Returns true if jumped or following calls var jumped = ExecuteASM(disasm, buffer, followCalls); if ((disasm.CompleteInstr.Contains("leave") || disasm.CompleteInstr.Contains("ret")) && !jumped) break; Debugger.TryTrigger(this, BreakpointType.InstructionName, disasm.Instruction.Mnemonic); if (!jumped) disasm.EIP = new IntPtr(disasm.EIP.ToInt64() + (int)result); } for (var i = 0; i < CallOffsets.Count; ++i) CallOffsets[i] += 0x400C00; } catch (Exception e) { Console.WriteLine(e); } }
public static BeaConstants.SpecialInfo Disassemble(Disasm disasm) { return (BeaConstants.SpecialInfo)Disasm(disasm); }
public static BeaConstants.SpecialInfo Disassemble(Disasm disasm) { return((BeaConstants.SpecialInfo)Disasm(disasm)); }