diff --git a/bindings/dotnet/UnicornManaged/Binding/IBinding.fs b/bindings/dotnet/UnicornManaged/Binding/IBinding.fs index df3eb381..2ed038bc 100644 --- a/bindings/dotnet/UnicornManaged/Binding/IBinding.fs +++ b/bindings/dotnet/UnicornManaged/Binding/IBinding.fs @@ -22,7 +22,7 @@ type IBinding = abstract MemUnmap : UIntPtr * UInt64 * UIntPtr -> Int32 abstract MemProtect : UIntPtr * UInt64 * UIntPtr * UInt32 -> Int32 abstract HookAddNoarg : UIntPtr * UIntPtr * Int32 * UIntPtr * IntPtr -> Int32 - abstract HookAddArg0 : UIntPtr * UIntPtr * Int32 * UIntPtr * IntPtr * IntPtr -> Int32 + abstract HookAddArg0 : UIntPtr * UIntPtr * Int32 * UIntPtr * IntPtr * Int32 -> Int32 abstract HookAddArg0Arg1 : UIntPtr * UIntPtr * Int32 * UIntPtr * IntPtr * UInt64 * UInt64 -> Int32 end diff --git a/bindings/dotnet/UnicornManaged/Binding/NativeBinding.fs b/bindings/dotnet/UnicornManaged/Binding/NativeBinding.fs index 9f83ce21..4f37c7c8 100644 --- a/bindings/dotnet/UnicornManaged/Binding/NativeBinding.fs +++ b/bindings/dotnet/UnicornManaged/Binding/NativeBinding.fs @@ -63,7 +63,7 @@ module NativeBinding = extern Int32 uc_hook_add_noarg(UIntPtr eng, UIntPtr hh, Int32 callbackType, UIntPtr callback, IntPtr userData) [] - extern Int32 uc_hook_add_arg0(UIntPtr eng, UIntPtr hh, Int32 callbackType, UIntPtr callback, IntPtr userData, IntPtr arg0) + extern Int32 uc_hook_add_arg0(UIntPtr eng, UIntPtr hh, Int32 callbackType, UIntPtr callback, IntPtr userData, Int32 arg0) [] extern Int32 uc_hook_add_arg0_arg1(UIntPtr eng, UIntPtr hh, Int32 callbackType, UIntPtr callback, IntPtr userData, UInt64 arg0, UInt64 arg1) diff --git a/bindings/dotnet/UnicornManaged/Const/Arm.fs b/bindings/dotnet/UnicornManaged/Const/Arm.fs index 2132cee7..34eb91b6 100644 --- a/bindings/dotnet/UnicornManaged/Const/Arm.fs +++ b/bindings/dotnet/UnicornManaged/Const/Arm.fs @@ -7,7 +7,7 @@ open System [] module Arm = -// ARM registers + // ARM registers let UC_ARM_REG_INVALID = 0 let UC_ARM_REG_APSR = 1 @@ -122,7 +122,7 @@ module Arm = let UC_ARM_REG_S31 = 110 let UC_ARM_REG_ENDING = 111 -// alias registers + // alias registers let UC_ARM_REG_R13 = 12 let UC_ARM_REG_R14 = 10 let UC_ARM_REG_R15 = 11 diff --git a/bindings/dotnet/UnicornManaged/Const/Arm64.fs b/bindings/dotnet/UnicornManaged/Const/Arm64.fs index 553747ae..bb1a2140 100644 --- a/bindings/dotnet/UnicornManaged/Const/Arm64.fs +++ b/bindings/dotnet/UnicornManaged/Const/Arm64.fs @@ -7,7 +7,7 @@ open System [] module Arm64 = -// ARM64 registers + // ARM64 registers let UC_ARM64_REG_INVALID = 0 let UC_ARM64_REG_X29 = 1 @@ -270,11 +270,11 @@ module Arm64 = let UC_ARM64_REG_V30 = 258 let UC_ARM64_REG_V31 = 259 -// pseudo registers + // pseudo registers let UC_ARM64_REG_PC = 260 let UC_ARM64_REG_ENDING = 261 -// alias registers + // alias registers let UC_ARM64_REG_IP1 = 215 let UC_ARM64_REG_IP0 = 216 let UC_ARM64_REG_FP = 1 diff --git a/bindings/dotnet/UnicornManaged/Const/M68k.fs b/bindings/dotnet/UnicornManaged/Const/M68k.fs index e4495f45..8fc5157e 100644 --- a/bindings/dotnet/UnicornManaged/Const/M68k.fs +++ b/bindings/dotnet/UnicornManaged/Const/M68k.fs @@ -7,7 +7,7 @@ open System [] module M68k = -// M68K registers + // M68K registers let UC_M68K_REG_INVALID = 0 let UC_M68K_REG_A0 = 1 diff --git a/bindings/dotnet/UnicornManaged/Const/Mips.fs b/bindings/dotnet/UnicornManaged/Const/Mips.fs index d0fa03a9..4cc02627 100644 --- a/bindings/dotnet/UnicornManaged/Const/Mips.fs +++ b/bindings/dotnet/UnicornManaged/Const/Mips.fs @@ -7,11 +7,11 @@ open System [] module Mips = -// MIPS registers + // MIPS registers let UC_MIPS_REG_INVALID = 0 -// General purpose registers + // General purpose registers let UC_MIPS_REG_PC = 1 let UC_MIPS_REG_0 = 2 let UC_MIPS_REG_1 = 3 @@ -46,7 +46,7 @@ module Mips = let UC_MIPS_REG_30 = 32 let UC_MIPS_REG_31 = 33 -// DSP registers + // DSP registers let UC_MIPS_REG_DSPCCOND = 34 let UC_MIPS_REG_DSPCARRY = 35 let UC_MIPS_REG_DSPEFI = 36 @@ -59,13 +59,13 @@ module Mips = let UC_MIPS_REG_DSPPOS = 43 let UC_MIPS_REG_DSPSCOUNT = 44 -// ACC registers + // ACC registers let UC_MIPS_REG_AC0 = 45 let UC_MIPS_REG_AC1 = 46 let UC_MIPS_REG_AC2 = 47 let UC_MIPS_REG_AC3 = 48 -// COP registers + // COP registers let UC_MIPS_REG_CC0 = 49 let UC_MIPS_REG_CC1 = 50 let UC_MIPS_REG_CC2 = 51 @@ -75,7 +75,7 @@ module Mips = let UC_MIPS_REG_CC6 = 55 let UC_MIPS_REG_CC7 = 56 -// FPU registers + // FPU registers let UC_MIPS_REG_F0 = 57 let UC_MIPS_REG_F1 = 58 let UC_MIPS_REG_F2 = 59 @@ -117,7 +117,7 @@ module Mips = let UC_MIPS_REG_FCC6 = 95 let UC_MIPS_REG_FCC7 = 96 -// AFPR128 + // AFPR128 let UC_MIPS_REG_W0 = 97 let UC_MIPS_REG_W1 = 98 let UC_MIPS_REG_W2 = 99 diff --git a/bindings/dotnet/UnicornManaged/Const/Sparc.fs b/bindings/dotnet/UnicornManaged/Const/Sparc.fs index 84ebff7f..9a91e1a7 100644 --- a/bindings/dotnet/UnicornManaged/Const/Sparc.fs +++ b/bindings/dotnet/UnicornManaged/Const/Sparc.fs @@ -7,7 +7,7 @@ open System [] module Sparc = -// SPARC registers + // SPARC registers let UC_SPARC_REG_INVALID = 0 let UC_SPARC_REG_F0 = 1 diff --git a/bindings/dotnet/UnicornManaged/Const/X86.fs b/bindings/dotnet/UnicornManaged/Const/X86.fs index 53687a7d..01fca68e 100644 --- a/bindings/dotnet/UnicornManaged/Const/X86.fs +++ b/bindings/dotnet/UnicornManaged/Const/X86.fs @@ -7,7 +7,7 @@ open System [] module X86 = -// X86 registers + // X86 registers let UC_X86_REG_INVALID = 0 let UC_X86_REG_AH = 1 @@ -253,7 +253,7 @@ module X86 = let UC_X86_REG_R15W = 241 let UC_X86_REG_ENDING = 242 -// X86 instructions + // X86 instructions let UC_X86_INS_INVALID = 0 let UC_X86_INS_AAA = 1 @@ -378,7 +378,7 @@ module X86 = let UC_X86_INS_CVTPS2DQ = 120 let UC_X86_INS_CVTPS2PD = 121 let UC_X86_INS_CVTSD2SI = 122 - (*let UC_X86_INS_CVTSD2SS = 123 + let UC_X86_INS_CVTSD2SS = 123 let UC_X86_INS_CVTSI2SD = 124 let UC_X86_INS_CVTSI2SS = 125 let UC_X86_INS_CVTSS2SD = 126 @@ -472,9 +472,9 @@ module X86 = let UC_X86_INS_HSUBPS = 214 let UC_X86_INS_IDIV = 215 let UC_X86_INS_FILD = 216 - let UC_X86_INS_IMUL = 217*) + let UC_X86_INS_IMUL = 217 let UC_X86_INS_IN = 218 - (*let UC_X86_INS_INC = 219 + let UC_X86_INS_INC = 219 let UC_X86_INS_INSB = 220 let UC_X86_INS_INSERTPS = 221 let UC_X86_INS_INSERTQ = 222 @@ -754,9 +754,9 @@ module X86 = let UC_X86_INS_MWAIT = 496 let UC_X86_INS_NEG = 497 let UC_X86_INS_NOP = 498 - let UC_X86_INS_NOT = 499*) + let UC_X86_INS_NOT = 499 let UC_X86_INS_OUT = 500 - (*let UC_X86_INS_OUTSB = 501 + let UC_X86_INS_OUTSB = 501 let UC_X86_INS_OUTSD = 502 let UC_X86_INS_OUTSW = 503 let UC_X86_INS_PACKUSDW = 504 @@ -953,9 +953,9 @@ module X86 = let UC_X86_INS_FSUB = 695 let UC_X86_INS_FISUB = 696 let UC_X86_INS_FSUBP = 697 - let UC_X86_INS_SWAPGS = 698*) + let UC_X86_INS_SWAPGS = 698 let UC_X86_INS_SYSCALL = 699 - (*let UC_X86_INS_SYSENTER = 700 + let UC_X86_INS_SYSENTER = 700 let UC_X86_INS_SYSEXIT = 701 let UC_X86_INS_SYSRET = 702 let UC_X86_INS_T1MSKC = 703 @@ -1594,4 +1594,4 @@ module X86 = let UC_X86_INS_FDISI8087_NOP = 1336 let UC_X86_INS_FENI8087_NOP = 1337 let UC_X86_INS_ENDING = 1338 - *) + diff --git a/bindings/dotnet/UnicornManaged/ConvertUtility.fs b/bindings/dotnet/UnicornManaged/ConvertUtility.fs new file mode 100644 index 00000000..5e455bf7 --- /dev/null +++ b/bindings/dotnet/UnicornManaged/ConvertUtility.fs @@ -0,0 +1,21 @@ +namespace UnicornManaged + +open System + +[] +module internal ConvertUtility = + + let int64ToBytes(v: Int64) = + let res = Array.zeroCreate 8 + let mutable uv = uint64 v + for i = 0 to res.Length-1 do + res.[i] <- byte (uv &&& uint64 0xFF) + uv <- uv >>> 8 + res + + let bytesToInt64(v: Byte array) = + let mutable res = uint64 0 + for i = 0 to v.Length-1 do + let tmpV = v.[i] &&& byte 0xFF + res <- res + (uint64 tmpV <<< (i * 8)) + int64 res \ No newline at end of file diff --git a/bindings/dotnet/UnicornManaged/Hooks.fs b/bindings/dotnet/UnicornManaged/InternalHooks.fs similarity index 90% rename from bindings/dotnet/UnicornManaged/Hooks.fs rename to bindings/dotnet/UnicornManaged/InternalHooks.fs index c00e0a75..36cfc3ce 100644 --- a/bindings/dotnet/UnicornManaged/Hooks.fs +++ b/bindings/dotnet/UnicornManaged/InternalHooks.fs @@ -20,10 +20,10 @@ type internal MemReadHookInternal = delegate of IntPtr * Int64 * Int32 * IntPtr type internal MemWriteHookInternal = delegate of IntPtr * Int64 * Int32 * Int64 * IntPtr -> unit [] -type internal EventMemHookInternal = delegate of IntPtr * Int64 * Int32 * Int64 * IntPtr -> unit +type internal EventMemHookInternal = delegate of IntPtr * Int32 * Int64 * Int32 * Int64 * IntPtr-> Boolean [] -type internal InHookInternal = delegate of IntPtr * Int32 * Int32 * IntPtr -> unit +type internal InHookInternal = delegate of IntPtr * Int32 * Int32 * IntPtr -> Int32 [] type internal OutHookInternal = delegate of IntPtr * Int32 * Int32 * Int32 * IntPtr -> unit diff --git a/bindings/dotnet/UnicornManaged/Unicorn.fs b/bindings/dotnet/UnicornManaged/Unicorn.fs index e567ac74..cd8db3c1 100644 --- a/bindings/dotnet/UnicornManaged/Unicorn.fs +++ b/bindings/dotnet/UnicornManaged/Unicorn.fs @@ -4,6 +4,7 @@ open System open System.Threading open System.Collections.Generic open System.Runtime.InteropServices +open System.Linq open UnicornManaged.Const open UnicornManaged.Binding @@ -13,8 +14,8 @@ and BlockHook = delegate of Unicorn * Int64 * Int32 * Object -> unit and InterruptHook = delegate of Unicorn * Int32 * Object -> unit and MemReadHook = delegate of Unicorn * Int64 * Int32 * Object -> unit and MemWriteHook = delegate of Unicorn * Int64 * Int32 * Int64 * Object -> unit -and EventMemHook = delegate of Unicorn * Int64 * Int32 * Int64 * Object -> unit -and InHook = delegate of Unicorn * Int32 * Int32 * Object -> unit +and EventMemHook = delegate of Unicorn * Int32 * Int64 * Int32 * Int64 * Object -> Boolean +and InHook = delegate of Unicorn * Int32 * Int32 * Object -> Int32 and OutHook = delegate of Unicorn * Int32 * Int32 * Int32 * Object -> unit and SyscallHook = delegate of Unicorn * Object -> unit @@ -22,30 +23,36 @@ and SyscallHook = delegate of Unicorn * Object -> unit and Unicorn(arch: Int32, mode: Int32, binding: IBinding) = // hook callback list - let _codeHooks = new Dictionary() - let _blockHooks = new Dictionary() - let _interruptHooks = new Dictionary() - let _memReadHooks = new Dictionary() - let _memWriteHooks = new Dictionary() - let _memEventHooks = new Dictionary() - let _inHooks = new Dictionary() - let _outHooks = new Dictionary() - let _syscallHooks = new Dictionary() + let _codeHooks = new List<(CodeHook * Object)>() + let _blockHooks = new List<(BlockHook * Object)>() + let _interruptHooks = new List<(InterruptHook * Object)>() + let _memReadHooks = new List<(MemReadHook * Object)>() + let _memWriteHooks = new List<(MemWriteHook * Object)>() + let _memEventHooks = new Dictionary>() + let _inHooks = new List<(InHook * Object)>() + let _outHooks = new List<(OutHook * Object)>() + let _syscallHooks = new List<(SyscallHook * Object)>() let _disposablePointers = new List() + let _eventMemMap = + [ + (UC_HOOK_MEM_READ_UNMAPPED, UC_MEM_READ_UNMAPPED) + (UC_HOOK_MEM_WRITE_UNMAPPED, UC_MEM_WRITE_UNMAPPED) + (UC_HOOK_MEM_FETCH_UNMAPPED, UC_MEM_FETCH_UNMAPPED) + (UC_HOOK_MEM_READ_PROT, UC_MEM_READ_PROT) + (UC_HOOK_MEM_WRITE_PROT, UC_MEM_WRITE_PROT) + (UC_HOOK_MEM_FETCH_PROT, UC_MEM_FETCH_PROT) + ] |> dict + let mutable _eng = [|UIntPtr.Zero|] let checkResult(errCode: Int32, errMsg: String) = if errCode <> Common.UC_ERR_OK then raise(ApplicationException(String.Format("{0}. Error: {1}", errMsg, errCode))) - let getId = - let counter = ref 0 - fun () -> new IntPtr(Interlocked.Increment(counter)) - - let hookDel(callbacks: Dictionary) (callback: 'a)= + let hookDel(callbacks: List<'a * Object>) (callback: 'a)= // TODO: invoke the native function in order to not call the trampoline anymore - callbacks.Keys - |> Seq.tryFind(fun k -> match callbacks.[k] with | (c, _) -> c = callback) + callbacks + |> Seq.tryFind(fun item -> match item with | (c, _) -> c = callback) |> (fun k -> if k.IsSome then callbacks.Remove(k.Value) |> ignore) let allocate(size: Int32) = @@ -54,6 +61,12 @@ and Unicorn(arch: Int32, mode: Int32, binding: IBinding) = mem.ToPointer() do + // initialize event list + _eventMemMap + |> Seq.map(fun kv -> kv.Key) + |> Seq.iter (fun eventType -> _memEventHooks.Add(eventType, new List())) + + // init engine _eng <- [|new UIntPtr(allocate(IntPtr.Size))|] let err = binding.UcOpen(uint32 arch, uint32 mode, _eng) checkResult(err, "Unable to open the Unicorn Engine") @@ -101,10 +114,18 @@ and Unicorn(arch: Int32, mode: Int32, binding: IBinding) = match binding.RegWrite(_eng.[0], regId, value) |> this.CheckResult with | Some e -> raise e | None -> () + member this.RegWrite(regId: Int32, value: Int64) = + this.RegWrite(regId, int64ToBytes value) + member this.RegRead(regId: Int32, regValue: Byte array) = match binding.RegRead(_eng.[0], regId, regValue) |> this.CheckResult with | Some e -> raise e | None -> () + member this.RegRead(regId: Int32) = + let buffer = Array.zeroCreate 8 + this.RegRead(regId, buffer) + bytesToInt64 buffer + member this.EmuStart(beginAddr: Int64, untilAddr: Int64, timeout: Int64, count: Int64) = match binding.EmuStart(_eng.[0], uint64 beginAddr, uint64 untilAddr, uint64 timeout, uint64 count) |> this.CheckResult with | Some e -> raise e | None -> () @@ -129,18 +150,18 @@ and Unicorn(arch: Int32, mode: Int32, binding: IBinding) = member this.AddCodeHook(callback: CodeHook, userData: Object, beginAddr: Int64, endAddr: Int64) = let trampoline(u: IntPtr) (addr: Int64) (size: Int32) (user: IntPtr) = - let (exist, (callback, userData)) = _codeHooks.TryGetValue(user) - if exist then callback.Invoke(this, addr, size, userData) + _codeHooks + |> Seq.iter(fun (callback, userData) -> callback.Invoke(this, addr, size, userData)) - let id = getId() - _codeHooks.Add(id, (callback, userData)) + if _codeHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new CodeHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_CODE, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with + | Some e -> raise e | None -> () - let funcPointer = Marshal.GetFunctionPointerForDelegate(new CodeHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_CODE, new UIntPtr(funcPointer.ToPointer()), id, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with - | Some e -> raise e | None -> () + _codeHooks.Add(callback, userData) - member this.AddCodeHook(callback: CodeHook, beginAddr: Int64, endAddr: Int64) = + member this.AddCodeHook(callback: CodeHook, beginAddr: Int64, endAddr: Int64) = this.AddCodeHook(callback, null, beginAddr, endAddr) member this.HookDel(callback: CodeHook) = @@ -148,32 +169,32 @@ and Unicorn(arch: Int32, mode: Int32, binding: IBinding) = member this.AddBlockHook(callback: BlockHook, userData: Object, beginAddr: Int64, endAddr: Int64) = let trampoline(u: IntPtr) (addr: Int64) (size: Int32) (user: IntPtr) = - let (exist, (callback, userData)) = _blockHooks.TryGetValue(user) - if exist then callback.Invoke(this, addr, size, userData) + _blockHooks + |> Seq.iter(fun (callback, userData) -> callback.Invoke(this, addr, size, userData)) - let id = getId() - _blockHooks.Add(id, (callback, userData)) + if _blockHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new BlockHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_BLOCK, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with + | Some e -> raise e | None -> () - let funcPointer = Marshal.GetFunctionPointerForDelegate(new BlockHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_BLOCK, new UIntPtr(funcPointer.ToPointer()), id, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with - | Some e -> raise e | None -> () + _blockHooks.Add(callback, userData) member this.HookDel(callback: BlockHook) = hookDel _blockHooks callback member this.AddInterruptHook(callback: InterruptHook, userData: Object) = let trampoline(u: IntPtr) (intNumber: Int32) (user: IntPtr) = - let (exist, (callback, userData)) = _interruptHooks.TryGetValue(user) - if exist then callback.Invoke(this, intNumber, userData) + _interruptHooks + |> Seq.iter(fun (callback, userData) -> callback.Invoke(this, intNumber, userData)) + + if _interruptHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new InterruptHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddNoarg(_eng.[0], hh, Common.UC_HOOK_INTR, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero) |> this.CheckResult with + | Some e -> raise e | None -> () - let id = getId() - _interruptHooks.Add(id, (callback, userData)) - - let funcPointer = Marshal.GetFunctionPointerForDelegate(new InterruptHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddNoarg(_eng.[0], hh, Common.UC_HOOK_INTR, new UIntPtr(funcPointer.ToPointer()), id) |> this.CheckResult with - | Some e -> raise e | None -> () + _interruptHooks.Add(callback, userData) member this.AddInterruptHook(callback: InterruptHook) = this.AddInterruptHook(callback, null) @@ -183,104 +204,113 @@ and Unicorn(arch: Int32, mode: Int32, binding: IBinding) = member this.AddMemReadHook(callback: MemReadHook, userData: Object, beginAddr: Int64, endAddr: Int64) = let trampoline(u: IntPtr) (addr: Int64) (size: Int32) (user: IntPtr) = - let (exist, (callback, userData)) = _memReadHooks.TryGetValue(user) - if exist then callback.Invoke(this, addr, size, userData) + _memReadHooks + |> Seq.iter(fun (callback, userData) -> callback.Invoke(this, addr, size, userData)) - let id = getId() - _memReadHooks.Add(id, (callback, userData)) + if _memReadHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new MemReadHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_MEM_READ, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with + | Some e -> raise e | None -> () - let funcPointer = Marshal.GetFunctionPointerForDelegate(new MemReadHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_MEM_READ, new UIntPtr(funcPointer.ToPointer()), id, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with - | Some e -> raise e | None -> () + _memReadHooks.Add(callback, userData) member this.HookDel(callback: MemReadHook) = hookDel _memReadHooks callback member this.AddMemWriteHook(callback: MemWriteHook, userData: Object, beginAddr: Int64, endAddr: Int64) = let trampoline(u: IntPtr) (addr: Int64) (size: Int32) (value: Int64) (user: IntPtr) = - let (exist, (callback, userData)) = _memWriteHooks.TryGetValue(user) - if exist then callback.Invoke(this, addr, size, value, userData) + _memWriteHooks + |> Seq.iter(fun (callback, userData) -> callback.Invoke(this, addr, size, value, userData)) + + if _memWriteHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new MemWriteHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_MEM_WRITE, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with + | Some e -> raise e | None -> () - let id = getId() - _memWriteHooks.Add(id, (callback, userData)) - - let funcPointer = Marshal.GetFunctionPointerForDelegate(new MemWriteHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddArg0Arg1(_eng.[0], hh, Common.UC_HOOK_MEM_WRITE, new UIntPtr(funcPointer.ToPointer()), id, uint64 beginAddr, uint64 endAddr) |> this.CheckResult with - | Some e -> raise e | None -> () + _memWriteHooks.Add(callback, userData) member this.HookDel(callback: MemWriteHook) = hookDel _memWriteHooks callback member this.AddEventMemHook(callback: EventMemHook, eventType: Int32, userData: Object) = - let trampoline(u: IntPtr) (addr: Int64) (size: Int32) (value: Int64) (user: IntPtr) = - let (exist, (callback, userData)) = _memEventHooks.TryGetValue(user) - if exist then callback.Invoke(this, addr, size, value, userData) - - let registEventMemHook(check: Int32) = - let id = getId() - _memEventHooks.Add(id, (callback, userData)) + let trampoline(u: IntPtr) (eventType: Int32) (addr: Int64) (size: Int32) (value: Int64) (user: IntPtr) = + _memEventHooks.Keys + |> Seq.filter(fun eventFlag -> (eventType &&& eventFlag) <> 0) + |> Seq.map(fun eventflag -> _memEventHooks.[eventflag]) + |> Seq.concat + |> Seq.map(fun (callback, userData) -> callback.Invoke(this, eventType, addr, size, value, userData)) + |> Seq.forall id + // register the event if not already done + _memEventHooks.Keys + |> Seq.filter(fun eventFlag -> (eventType &&& eventFlag) <> 0) + |> Seq.filter(fun eventFlag -> _memEventHooks.[eventFlag] |> Seq.isEmpty) + |> Seq.iter(fun eventFlag -> let funcPointer = Marshal.GetFunctionPointerForDelegate(new EventMemHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddNoarg(_eng.[0], hh, check, new UIntPtr(funcPointer.ToPointer()), id) |> this.CheckResult with + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddNoarg(_eng.[0], hh, eventFlag, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero) |> this.CheckResult with | Some e -> raise e | None -> () + ) - // test all the events types agains the input eventType - [ - Common.UC_HOOK_MEM_READ_UNMAPPED - Common.UC_HOOK_MEM_WRITE_UNMAPPED - Common.UC_HOOK_MEM_FETCH_UNMAPPED - Common.UC_HOOK_MEM_READ_PROT - Common.UC_HOOK_MEM_WRITE_PROT - Common.UC_HOOK_MEM_FETCH_PROT - ] - |> List.filter(fun eventFlag -> eventType &&& eventFlag <> 0) - |> List.map registEventMemHook - |> List.rev |> List.head + // register the callbacks + _memEventHooks.Keys + |> Seq.filter(fun eventFlag -> (eventType &&& eventFlag) <> 0) + |> Seq.iter(fun eventFlag -> _memEventHooks.[eventFlag].Add((callback, userData))) + + member this.AddEventMemHook(callback: EventMemHook, eventType: Int32) = + this.AddEventMemHook(callback, eventType, null) member this.HookDel(callback: EventMemHook) = - hookDel _memEventHooks callback + let callbacks = (_memEventHooks.Values |> Seq.concat).ToList() + hookDel callbacks callback member this.AddInHook(callback: InHook, userData: Object) = let trampoline(u: IntPtr) (port: Int32) (size: Int32) (user: IntPtr) = - let (exist, (callback, userData)) = _inHooks.TryGetValue(user) - if exist then callback.Invoke(this, port, size, userData) + _inHooks + |> Seq.map(fun (callback, userData) -> callback.Invoke(this, port, size, userData)) + |> Seq.last + + if _inHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new InHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddArg0(_eng.[0], hh, Common.UC_HOOK_INSN, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero, X86.UC_X86_INS_IN) |> this.CheckResult with + | Some e -> raise e | None -> () - let id = getId() - _inHooks.Add(id, (callback, userData)) + _inHooks.Add(callback, userData) - let funcPointer = Marshal.GetFunctionPointerForDelegate(new InHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddArg0(_eng.[0], hh, Common.UC_HOOK_INSN, new UIntPtr(funcPointer.ToPointer()), id, new IntPtr(X86.UC_X86_INS_IN)) |> this.CheckResult with - | Some e -> raise e | None -> () + member this.AddInHook(callback: InHook) = + this.AddInHook(callback, null) member this.AddOutHook(callback: OutHook, userData: Object) = let trampoline(u: IntPtr) (port: Int32) (size: Int32) (value: Int32) (user: IntPtr) = - let (exist, (callback, userData)) = _outHooks.TryGetValue(user) - if exist then callback.Invoke(this, port, size, value, userData) + _outHooks + |> Seq.iter(fun (callback, userData) -> callback.Invoke(this, port, size, value, userData)) + + if _outHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new OutHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddArg0(_eng.[0], hh, Common.UC_HOOK_INSN, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero, X86.UC_X86_INS_OUT) |> this.CheckResult with + | Some e -> raise e | None -> () - let id = getId() - _outHooks.Add(id, (callback, userData)) + _outHooks.Add(callback, userData) - let funcPointer = Marshal.GetFunctionPointerForDelegate(new OutHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddArg0(_eng.[0], hh, Common.UC_HOOK_INSN, new UIntPtr(funcPointer.ToPointer()), id, new IntPtr(X86.UC_X86_INS_OUT)) |> this.CheckResult with - | Some e -> raise e | None -> () + member this.AddOutHook(callback: OutHook) = + this.AddOutHook(callback, null) member this.AddSyscallHook(callback: SyscallHook, userData: Object) = let trampoline(u: IntPtr) (user: IntPtr) = - let (exist, (callback, userData)) = _syscallHooks.TryGetValue(user) - if exist then callback.Invoke(this, userData) + _syscallHooks + |> Seq.iter(fun (callback, userData) -> callback.Invoke(this, userData)) + + if _syscallHooks |> Seq.isEmpty then + let funcPointer = Marshal.GetFunctionPointerForDelegate(new SyscallHookInternal(trampoline)) + let hh = new UIntPtr(allocate(IntPtr.Size)) + match binding.HookAddArg0(_eng.[0], hh, Common.UC_HOOK_INSN, new UIntPtr(funcPointer.ToPointer()), IntPtr.Zero, X86.UC_X86_INS_SYSCALL) |> this.CheckResult with + | Some e -> raise e | None -> () - let id = getId() - _syscallHooks.Add(id, (callback, userData)) - - let funcPointer = Marshal.GetFunctionPointerForDelegate(new SyscallHookInternal(trampoline)) - let hh = new UIntPtr(allocate(IntPtr.Size)) - match binding.HookAddArg0(_eng.[0], hh, Common.UC_HOOK_INSN, new UIntPtr(funcPointer.ToPointer()), id, new IntPtr(X86.UC_X86_INS_SYSCALL)) |> this.CheckResult with - | Some e -> raise e | None -> () + _syscallHooks.Add(callback, userData) member this.AddSyscallHook(callback: SyscallHook) = this.AddSyscallHook(callback, null) diff --git a/bindings/dotnet/UnicornManaged/UnicornManaged.fsproj b/bindings/dotnet/UnicornManaged/UnicornManaged.fsproj index b97c3ed6..e10cd735 100644 --- a/bindings/dotnet/UnicornManaged/UnicornManaged.fsproj +++ b/bindings/dotnet/UnicornManaged/UnicornManaged.fsproj @@ -57,7 +57,8 @@ - + + diff --git a/bindings/dotnet/UnicornSamples/Program.cs b/bindings/dotnet/UnicornSamples/Program.cs index 9206e083..41cfb19c 100644 --- a/bindings/dotnet/UnicornSamples/Program.cs +++ b/bindings/dotnet/UnicornSamples/Program.cs @@ -6,11 +6,11 @@ namespace UnicornSamples { static void Main(string[] args) { - // X86 tests - X86Sample.X86Code32(); - //X86Sample.X86Code32Loop(); - X86Sample.X86Code32InvalidMemRead(); - X86Sample.X86Code32InvalidMemWrite(); + // X86 tests 32bit + X86Sample32.X86Code32(); + X86Sample32.X86Code32InvalidMemRead(); + X86Sample32.X86Code32InvalidMemWriteWithRuntimeFix(); + X86Sample32.X86Code32InOut(); // Run all shellcode tests ShellcodeSample.X86Code32Self(); diff --git a/bindings/dotnet/UnicornSamples/ShellcodeSample.cs b/bindings/dotnet/UnicornSamples/ShellcodeSample.cs index 42e467dc..be654d6f 100644 --- a/bindings/dotnet/UnicornSamples/ShellcodeSample.cs +++ b/bindings/dotnet/UnicornSamples/ShellcodeSample.cs @@ -69,11 +69,7 @@ namespace UnicornSamples // write machine code to be emulated to memory u.MemWrite(address, code); - - //var read = new Byte[code.Length]; - //u.MemRead(address, read); - //Console.WriteLine(Disassemble(disassembler, code));// - + // initialize machine registers u.RegWrite(X86.UC_X86_REG_ESP, Utils.Int64ToBytes(address + 0x200000)); @@ -88,7 +84,7 @@ namespace UnicornSamples // handle SYSCALL u.AddSyscallHook(SyscallHookCallback); - + Console.WriteLine(">>> Start tracing code"); // emulate machine code in infinite time @@ -134,7 +130,7 @@ namespace UnicornSamples u.RegRead(X86.UC_X86_REG_EAX, eaxBuffer); var eax = Utils.ToInt(eaxBuffer); - Console.WriteLine("Syscall >>> EAX = 0x{0}", eax.ToString("X")); + Console.WriteLine("[!] Syscall EAX = 0x{0}", eax.ToString("X")); u.EmuStop(); } @@ -159,10 +155,10 @@ namespace UnicornSamples switch (eax) { default: - Console.WriteLine("Interrupt >>> 0x{0} num {1}, EAX=0x{2}", eip.ToString("X"), intNumber.ToString("X"), eax.ToString("X")); + Console.WriteLine("[!] Interrupt 0x{0} num {1}, EAX=0x{2}", eip.ToString("X"), intNumber.ToString("X"), eax.ToString("X")); break; case 1: // sys_exit - Console.WriteLine("Interrupt >>> 0x{0} num {1}, SYS_EXIT", eip.ToString("X"), intNumber.ToString("X")); + Console.WriteLine("[!] Interrupt 0x{0} num {1}, SYS_EXIT", eip.ToString("X"), intNumber.ToString("X")); u.EmuStop(); break; case 4: // sys_write @@ -186,7 +182,7 @@ namespace UnicornSamples var content = Encoding.Default.GetString(buffer); Console.WriteLine( - "Interrupt >>> 0x{0}: num {1}, SYS_WRITE. buffer = 0x{2}, size = , content = '{3}'", + "[!] Interrupt 0x{0}: num {1}, SYS_WRITE. buffer = 0x{2}, size = , content = '{3}'", eip.ToString("X"), ecx.ToString("X"), edx.ToString("X"), diff --git a/bindings/dotnet/UnicornSamples/UnicornSamples.csproj b/bindings/dotnet/UnicornSamples/UnicornSamples.csproj index 39845091..5fb55b61 100644 --- a/bindings/dotnet/UnicornSamples/UnicornSamples.csproj +++ b/bindings/dotnet/UnicornSamples/UnicornSamples.csproj @@ -69,7 +69,7 @@ - + diff --git a/bindings/dotnet/UnicornSamples/X86Sample.cs b/bindings/dotnet/UnicornSamples/X86Sample32.cs similarity index 53% rename from bindings/dotnet/UnicornSamples/X86Sample.cs rename to bindings/dotnet/UnicornSamples/X86Sample32.cs index ff662a61..f4b5da98 100644 --- a/bindings/dotnet/UnicornSamples/X86Sample.cs +++ b/bindings/dotnet/UnicornSamples/X86Sample32.cs @@ -12,7 +12,7 @@ using UnicornManaged.Const; namespace UnicornSamples { - internal class X86Sample + internal class X86Sample32 { private const Int64 ADDRESS = 0x1000000; @@ -25,17 +25,7 @@ namespace UnicornSamples }; Run(X86_CODE32); } - - public static void X86Code32Loop() - { - Byte[] X86_CODE32_LOOP = - { - // INC ecx; DEC edx; JMP self-loop - 0x41, 0x4a, 0xeb, 0xfe - }; - Run(X86_CODE32_LOOP); - } - + public static void X86Code32InvalidMemRead() { Byte[] X86_CODE32_MEM_READ = @@ -46,16 +36,27 @@ namespace UnicornSamples Run(X86_CODE32_MEM_READ, true); } - public static void X86Code32InvalidMemWrite() + public static void X86Code32InvalidMemWriteWithRuntimeFix() { Byte[] X86_CODE32_MEM_WRITE = { // mov [0xaaaaaaaa], ecx; INC ecx; DEC edx 0x89, 0x0D, 0xAA, 0xAA, 0xAA, 0xAA, 0x41, 0x4a }; - Run(X86_CODE32_MEM_WRITE, true); + Run(X86_CODE32_MEM_WRITE); } + public static void X86Code32InOut() + { + Byte[] X86_CODE32_INOUT = + { + // INC ecx; IN AL, 0x3f; DEC edx; OUT 0x46, AL; INC ebx + 0x41, 0xE4, 0x3F, 0x4a, 0xE6, 0x46, 0x43 + }; + Run(X86_CODE32_INOUT); + } + + private static void Run(Byte[] code, Boolean raiseException = false) { Console.WriteLine(); @@ -67,7 +68,7 @@ namespace UnicornSamples Exception e = null; try { - RunTest(code, ADDRESS); + RunTest(code, ADDRESS, Common.UC_MODE_32); } catch (UnicornEngineException ex) { @@ -83,9 +84,9 @@ namespace UnicornSamples Console.WriteLine(); } - private static void RunTest(Byte[] code, Int64 address) + private static void RunTest(Byte[] code, Int64 address, Int32 mode) { - using (var u = new Unicorn(Common.UC_ARCH_X86, Common.UC_MODE_32)) + using (var u = new Unicorn(Common.UC_ARCH_X86, mode)) using (var disassembler = CapstoneDisassembler.CreateX86Disassembler(DisassembleMode.Bit32)) { Console.WriteLine("Unicorn version: {0}", u.Version()); @@ -93,12 +94,21 @@ namespace UnicornSamples // map 2MB of memory for this emulation u.MemMap(address, 2 * 1024 * 1024, Common.UC_PROT_ALL); + // initialize machine registers + u.RegWrite(X86.UC_X86_REG_EAX, 0x1234); + u.RegWrite(X86.UC_X86_REG_ECX, 0x1234); + u.RegWrite(X86.UC_X86_REG_EDX, 0x7890); + // write machine code to be emulated to memory u.MemWrite(address, code); // initialize machine registers u.RegWrite(X86.UC_X86_REG_ESP, Utils.Int64ToBytes(address + 0x200000)); + // handle IN & OUT instruction + u.AddInHook(InHookCallback); + u.AddOutHook(OutHookCallback); + // tracing all instructions by having @begin > @end u.AddCodeHook((uc, addr, size, userData) => CodeHookCallback(disassembler, uc, addr, size, userData), 1, 0); @@ -108,14 +118,119 @@ namespace UnicornSamples // handle SYSCALL u.AddSyscallHook(SyscallHookCallback); + // intercept invalid memory events + u.AddEventMemHook(MemMapHookCallback, Common.UC_HOOK_MEM_READ_UNMAPPED | Common.UC_HOOK_MEM_WRITE_UNMAPPED); + Console.WriteLine(">>> Start tracing code"); // emulate machine code in infinite time u.EmuStart(address, address + code.Length, 0u, 0u); + // print registers + var ecx = u.RegRead(X86.UC_X86_REG_ECX); + var edx = u.RegRead(X86.UC_X86_REG_EDX); + var eax = u.RegRead(X86.UC_X86_REG_EAX); + Console.WriteLine("[!] EAX = {0}", eax.ToString("X")); + Console.WriteLine("[!] ECX = {0}", ecx.ToString("X")); + Console.WriteLine("[!] EDX = {0}", edx.ToString("X")); + Console.WriteLine(">>> Emulation Done!"); } - } + } + + private static Int32 InHookCallback(Unicorn u, Int32 port, Int32 size, Object userData) + { + var eip = u.RegRead(X86.UC_X86_REG_EIP); + Console.WriteLine("[!] Reading from port 0x{0}, size: {1}, address: 0x{2}", port.ToString("X"), size.ToString("X"), eip.ToString("X")); + var res = 0; + switch (size) + { + case 1: + // read 1 byte to AL + res = 0xf1; + break; + case 2: + // read 2 byte to AX + res = 0xf2; + break; + case 4: + // read 4 byte to EAX + res = 0xf4; + break; + } + + Console.WriteLine("[!] Return value: {0}", res.ToString("X")); + return res; + } + + private static void OutHookCallback(Unicorn u, Int32 port, Int32 size, Int32 value, Object userData) + { + var eip = u.RegRead(X86.UC_X86_REG_EIP); + Console.WriteLine("[!] Writing to port 0x{0}, size: {1}, value: 0x{2}, address: 0x{3}", port.ToString("X"), size.ToString("X"), value.ToString("X"), eip.ToString("X")); + + // confirm that value is indeed the value of AL/ AX / EAX + var v = 0L; + var regName = String.Empty; + switch (size) + { + case 1: + // read 1 byte in AL + v = u.RegRead(X86.UC_X86_REG_AL); + regName = "AL"; + break; + case 2: + // read 2 byte in AX + v = u.RegRead(X86.UC_X86_REG_AX); + regName = "AX"; + break; + case 4: + // read 4 byte in EAX + v = u.RegRead(X86.UC_X86_REG_EAX); + regName = "EAX"; + break; + } + + Console.WriteLine("[!] Register {0}: {1}", regName, v.ToString("X")); + } + + private static Boolean MemMapHookCallback(Unicorn u, Int32 eventType, Int64 address, Int32 size, Int64 value, Object userData) + { + if (eventType == Common.UC_MEM_WRITE_UNMAPPED) + { + Console.WriteLine("[!] Missing memory is being WRITE at 0x{0}, data size = {1}, data value = 0x{2}. Map memory.", address.ToString("X"), size.ToString("X"), value.ToString("X")); + u.MemMap(0xaaaa0000, 2 * 1024 * 1024, Common.UC_PROT_ALL); + return true; + } + else + { + return false; + } + } + + private static void CodeHookCallback1( + CapstoneDisassembler disassembler, + Unicorn u, + Int64 addr, + Int32 size, + Object userData) + { + Console.Write("[+] 0x{0}: ", addr.ToString("X")); + + var eipBuffer = new Byte[4]; + u.RegRead(X86.UC_X86_REG_EIP, eipBuffer); + + var effectiveSize = Math.Min(16, size); + var tmp = new Byte[effectiveSize]; + u.MemRead(addr, tmp); + + var sb = new StringBuilder(); + foreach (var t in tmp) + { + sb.AppendFormat("{0} ", (0xFF & t).ToString("X")); + } + Console.Write("{0,-20}", sb); + Console.WriteLine(Utils.Disassemble(disassembler, tmp)); + } private static void CodeHookCallback( CapstoneDisassembler disassembler, @@ -148,7 +263,7 @@ namespace UnicornSamples u.RegRead(X86.UC_X86_REG_EAX, eaxBuffer); var eax = Utils.ToInt(eaxBuffer); - Console.WriteLine("Syscall >>> EAX = 0x{0}", eax.ToString("X")); + Console.WriteLine("[!] Syscall EAX = 0x{0}", eax.ToString("X")); u.EmuStop(); } @@ -173,10 +288,10 @@ namespace UnicornSamples switch (eax) { default: - Console.WriteLine("Interrupt >>> 0x{0} num {1}, EAX=0x{2}", eip.ToString("X"), intNumber.ToString("X"), eax.ToString("X")); + Console.WriteLine("[!] Interrupt 0x{0} num {1}, EAX=0x{2}", eip.ToString("X"), intNumber.ToString("X"), eax.ToString("X")); break; case 1: // sys_exit - Console.WriteLine("Interrupt >>> 0x{0} num {1}, SYS_EXIT", eip.ToString("X"), intNumber.ToString("X")); + Console.WriteLine("[!] Interrupt 0x{0} num {1}, SYS_EXIT", eip.ToString("X"), intNumber.ToString("X")); u.EmuStop(); break; case 4: // sys_write @@ -200,7 +315,7 @@ namespace UnicornSamples var content = Encoding.Default.GetString(buffer); Console.WriteLine( - "Interrupt >>> 0x{0}: num {1}, SYS_WRITE. buffer = 0x{2}, size = , content = '{3}'", + "[!] Interrupt 0x{0}: num {1}, SYS_WRITE. buffer = 0x{2}, size = , content = '{3}'", eip.ToString("X"), ecx.ToString("X"), edx.ToString("X"),