{-| Module : Unicorn.Hook Description : Unicorn hooks. Copyright : (c) Adrian Herrera, 2016 License : GPL-2 Insert hook points into the Unicorn emulator engine. -} module Unicorn.Hook ( -- * Hook types Hook , MemoryHookType(..) , MemoryEventHookType(..) , MemoryAccess(..) -- * Hook callbacks , CodeHook , InterruptHook , BlockHook , InHook , OutHook , SyscallHook , MemoryHook , MemoryReadHook , MemoryWriteHook , MemoryEventHook -- * Hook callback management , codeHookAdd , interruptHookAdd , blockHookAdd , inHookAdd , outHookAdd , syscallHookAdd , memoryHookAdd , memoryEventHookAdd , hookDel ) where import Control.Monad import Control.Monad.Trans.Class import Control.Monad.Trans.Except (ExceptT (..), throwE) import Foreign import Unicorn.Internal.Core import Unicorn.Internal.Hook import qualified Unicorn.CPU.X86 as X86 ------------------------------------------------------------------------------- -- Hook callback management (registration and deletion) ------------------------------------------------------------------------------- -- | Register a callback for a code hook event. codeHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> CodeHook a -- ^ Code hook callback -> a -- ^ User-defined data. This will be passed to -- the callback function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or an 'Error' -- on failure codeHookAdd uc callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalCodeHook callback getResult $ ucHookAdd uc HookCode funPtr userDataPtr begin end -- | Register a callback for an interrupt hook event. interruptHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> InterruptHook a -- ^ Interrupt callback -> a -- ^ User-defined data. This will be passed -- to the callback function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or 'Error' -- on failure interruptHookAdd uc callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalInterruptHook callback getResult $ ucHookAdd uc HookIntr funPtr userDataPtr begin end -- | Register a callback for a block hook event. blockHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> BlockHook a -- ^ Block callback -> a -- ^ User-defined data. This will be passed to -- the callback function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or an 'Error' -- on failure blockHookAdd uc callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalBlockHook callback getResult $ ucHookAdd uc HookBlock funPtr userDataPtr begin end -- | Register a callback for an IN instruction hook event (X86). inHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> InHook a -- ^ IN instruction callback -> a -- ^ User-defined data. This will be passed to the -- callback function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or an 'Error' on -- failure inHookAdd uc callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalInHook callback getResult $ ucInsnHookAdd uc HookInsn funPtr userDataPtr begin end X86.In -- | Register a callback for an OUT instruction hook event (X86). outHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> OutHook a -- ^ OUT instruction callback -> a -- ^ User-defined data. This will be passed to the -- callback function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or an 'Error' on -- failure outHookAdd uc callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalOutHook callback getResult $ ucInsnHookAdd uc HookInsn funPtr userDataPtr begin end X86.Out -- | Register a callback for a SYSCALL instruction hook event (X86). syscallHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> SyscallHook a -- ^ SYSCALL instruction callback -> a -- ^ User-defined data. This will be passed to -- the callback function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or an 'Error' -- on failure syscallHookAdd uc callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalSyscallHook callback getResult $ ucInsnHookAdd uc HookInsn funPtr userDataPtr begin end X86.Syscall -- | Register a callback for a valid memory access event. memoryHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> MemoryHookType -- ^ A valid memory access (e.g. read, write, -- etc.) to trigger the callback on -> MemoryHook a -- ^ Memory access callback -> a -- ^ User-defined data. This will be passed to -- the callback function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or an 'Error' -- on failure memoryHookAdd uc memHookType callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalMemoryHook callback getResult $ ucHookAdd uc memHookType funPtr userDataPtr begin end -- | Register a callback for an invalid memory access event. memoryEventHookAdd :: Storable a => Engine -- ^ 'Unicorn' engine handle -> MemoryEventHookType -- ^ An invalid memory access (e.g. -- read, write, etc.) to trigger -- the callback on -> MemoryEventHook a -- ^ Invalid memory access callback -> a -- ^ User-defined data. This will -- be passed to the callback -- function -> Word64 -- ^ Start address -> Word64 -- ^ End address -> Emulator Hook -- ^ The hook handle on success, or -- an 'Error' on failure memoryEventHookAdd uc memEventHookType callback userData begin end = ExceptT . alloca $ \userDataPtr -> do poke userDataPtr userData funPtr <- marshalMemoryEventHook callback getResult $ ucHookAdd uc memEventHookType funPtr userDataPtr begin end -- | Unregister (remove) a hook callback. hookDel :: Engine -- ^ 'Unicorn' engine handle -> Hook -- ^ 'Hook' handle -> Emulator () -- ^ 'ErrOk' on success, or other value on failure hookDel uc hook = do err <- lift $ ucHookDel uc hook if err == ErrOk then pure () else throwE err ------------------------------------------------------------------------------- -- Helper functions ------------------------------------------------------------------------------- -- Takes the tuple returned by `ucHookAdd`, an IO (Error, Hook), and -- returns either a `Right Hook` if no error occurred or a `Left Error` if an -- error occurred getResult :: IO (Error, Hook) -> IO (Either Error Hook) getResult = liftM (uncurry checkResult) where checkResult err hook = if err == ErrOk then Right hook else Left err