{- | Print ROP gadgets having some desired property.

This module provides the quickest way to get started:

> $ cat find.hs
>
> import Dewdrop
> main = dewdrop (any (usesRegister RBP))
>
> $ runhaskell find.hs /bin/ls
> 00402e56:
>   pop %rbp
>   ret
>
> 0040afe7:
>   shl %cl, -0x15(%rbp)
>   rep ret
>
> ...

If you need more control, see "Dewdrop.Analyze".

-}

module Dewdrop
    ( -- * Finding gadgets
      dewdrop

      -- * Helpers for selecting gadgets
    , usesRegister, usesSegment, opcode

      -- * Re-export of disassembler
      --
      -- | The types and functions of @Hdis86@
      -- are re-exported for convenience.
    , module Hdis86
    ) where

import Dewdrop.Analyze

import System.Environment
import Control.Monad
import Control.Applicative
import Control.Exception ( throwIO, ErrorCall(..) )

import Data.Typeable ( Typeable )
import Data.Data     ( Data )

import qualified Data.ByteString as B
import qualified Generics.SYB    as G

import Data.Elf
import Hdis86

-- | Opens the ELF binary file passed as the first command-line
-- argument, and prints all ROP gadgets satisfying the specified
-- property.
dewdrop :: ([Metadata] -> Bool) -> IO ()
dewdrop wanted = do
    args@(~(elf_file:_)) <- getArgs
    when (null args) $ do
        progname <- getProgName
        throwIO $ ErrorCall ("Usage: " ++ progname ++ " ELF-FILE")
    elf <- parseElf <$> B.readFile elf_file
    mapM_ print . filter (\g@(Gadget xs) -> valid g && wanted xs) . gadgets $ elf

hasSub :: (Typeable a, Eq a, Data b) => a -> b -> Bool
hasSub x = not . null . G.listify (== x)

-- | Does this instruction use a given register?
--
-- This only includes registers explicitly mentioned in disassembly,
-- and not e.g. the @rsi@ / @rdi@ operands of @movsd@.
usesRegister :: GPR -> Metadata -> Bool
usesRegister = hasSub

-- | Does this instruction mention a given segment register?
--
-- This only includes explicit overrides, and loads/stores of
-- segment registers.
usesSegment :: Segment -> Metadata -> Bool
usesSegment = hasSub

-- | Get the @'Opcode'@ directly from an instruction-with-metadata.
opcode :: Metadata -> Opcode
opcode = inOpcode . mdInst