hurdle (empty) → 0.1.0
raw patch · 9 files changed
+1146/−0 lines, 9 filesdep +arraydep +basedep +prettysetup-changed
Dependencies added: array, base, pretty
Files
- LICENSE +30/−0
- Setup.hs +4/−0
- hurdle.cabal +45/−0
- src/Hurdle.hs +63/−0
- src/Hurdle/Datatypes.hs +183/−0
- src/Hurdle/DefOutput.hs +36/−0
- src/Hurdle/ParseMonad.hs +238/−0
- src/Hurdle/Parser.hs +252/−0
- src/Hurdle/TextDump.hs +295/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2008 Stephen Peter Tetley++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell++import Distribution.Simple+main = defaultMain
+ hurdle.cabal view
@@ -0,0 +1,45 @@+name: hurdle+version: 0.1.0+license: BSD3+license-file: LICENSE+copyright: Stephen Tetley <stephen.tetley@gmail.com>+maintainer: Stephen Tetley <stephen.tetley@gmail.com>+homepage: http://code.google.com/p/copperbox/+category: System+synopsis: Extract function names from Windows DLLs. +description:+ Hurdle - (H)askell (U)tilty (R)egarding (DL)l (E)xports.+ .+ Extract function names from Windows DLLs a-la pexports.+ . + Hurdle has minimal dependencies: base, array and pretty.+ .+ Currently Hurdle also has minimal utility - please consider + pexports instead as Hurdle doesn't yet print ordinals + etcetera (Hurdle was a Sunday afternoon hack that took a wee + bit longer). But... if anyone has a compelling use case that + would benefit the community, I'm willing to look at extending + Hurdle.+ .+build-type: Simple+stability: unstable+cabal-version: >= 1.2++Executable hurdle+ Build-Depends: base < 5, + array >= 0.2.0.0 && < 0.3,+ pretty ++ Main-Is: Hurdle.hs+ Hs-Source-Dirs: src+ Other-Modules: Hurdle.Datatypes,+ Hurdle.DefOutput,+ Hurdle.ParseMonad,+ Hurdle.Parser,+ Hurdle.TextDump+ ++++ +
+ src/Hurdle.hs view
@@ -0,0 +1,63 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Hurdle+-- Copyright : (c) Stephen Tetley 2009+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : to be determined.+--+-- main... +--+--------------------------------------------------------------------------------++module Main where++import Hurdle.Datatypes+import Hurdle.DefOutput+import Hurdle.Parser+import Hurdle.TextDump++import System.Console.GetOpt+import System.Environment++data Flag = Usage | Verbose+ deriving (Eq, Show)++header :: String+header = "Usage: pexports file"++options :: [OptDescr Flag]+options =+ [ Option ['h'] ["help"] (NoArg Usage) help_text+ , Option ['v'] ["verbose"] (NoArg Verbose) verb_text+ + ]+ where+ help_text = "please use pexports for the time being." + verb_text = "(very) verbose mode"+main :: IO ()+main = do+ args <- getArgs+ let (opts, nonopts, errs) = getOpt Permute options args+ main2 opts nonopts errs++++main2 :: [Flag] -> [FilePath] -> [String] -> IO ()+main2 opts [fname] [] + | Usage `elem` opts = putStrLn $ usageInfo header options+ | Verbose `elem` opts = run printImage fname+ | otherwise = run printDef fname++main2 _ _ _ = putStrLn $ "hurdle only handles one file at once."++run :: (Image -> IO ()) -> FilePath -> IO ()+run mf filename = do { img <- readDLL filename+ ; mf img+ ; putStrLn "" + }+
+ src/Hurdle/Datatypes.hs view
@@ -0,0 +1,183 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Hurdle.Datatypes+-- Copyright : (c) Stephen Tetley 2009+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : to be determined.+--+-- +--+--------------------------------------------------------------------------------++module Hurdle.Datatypes where++import Data.Word++data Image = Image + { image_dos_header :: ImageDOSHeader+ , image_signature :: (Char,Char,Char,Char)+ , image_coff_header :: ImageCOFFHeader+ , image_opt_header :: ImageOptionalHeader+ , image_section_headers :: [SectionHeader]+ , image_export_data :: ExportData+ }+ deriving Show++image_DOS_HEADER_size :: Int+image_DOS_HEADER_size = 64++data ImageDOSHeader = ImageDOSHeader + { idh_magic_number :: Word16+ , idh_bytes_last_page :: Word16+ , idh_pages_in_file :: Word16+ , idh_relocations :: Word16+ , idh_size_header_paras :: Word16+ , idh_min_extra_paras :: Word16+ , idh_max_extra_paras :: Word16+ , idh_initial_relative_ss :: Word16+ , idh_initial_sp :: Word16+ , idh_header_checksum :: Word16+ , idh_initial_ip :: Word16 + , idh_initial_relative_cs :: Word16+ , idh_reltable_file_addr :: Word16+ , idh_overlay_number :: Word16+ , idh_reserved_words :: (Word16,Word16,Word16,Word16)+ , idh_oem_identifier :: Word16+ , idh_oem_info :: Word16+ , idh_reserved_words_two :: [Word16] -- length 10+ , idh_new_exe_header_addr :: Word32+ } + deriving Show++++image_COFF_HEADER_size :: Int+image_COFF_HEADER_size = 20++data ImageCOFFHeader = ImageCOFFHeader + { ich_machine :: Word16+ , ich_num_sections :: Word16+ , ich_timedate_stamp :: Word32+ , ich_sym_table_ptr :: Word32+ , ich_num_symbols :: Word32+ , ich_opt_header_size :: Word16+ , ich_characteristics :: Word16+ }+ deriving Show++image_OPTIONAL_HEADER_size :: Int+image_OPTIONAL_HEADER_size = + image_OPTIONAL_STANDARD_size + image_OPTIONAL_NT_SPECIFIC_size+ + 16*image_DATA_DIRECTORY_size++data ImageOptionalHeader = ImageOptionalHeader+ { ioh_header_std_fields :: ImageOptionalStandard+ , ioh_nt_specific_fields :: ImageOptionalNTSpecific+ , ioh_data_directory :: [ImageDataDirectory]+ }+ deriving Show+ ++image_OPTIONAL_STANDARD_size :: Int+image_OPTIONAL_STANDARD_size = 28++data ImageOptionalStandard = ImageOptionalStandard+ { ios_magic :: Word16+ , ios_major_linker_version :: Word8+ , ios_minor_linker_version :: Word8+ , ios_size_of_code :: Word32+ , ios_size_of_inited_data :: Word32+ , ios_size_of_uninited_data :: Word32+ , ios_entry_point_addr :: Word32+ , ios_base_of_code :: Word32+ , ios_base_of_data :: Word32+ }+ deriving Show++image_OPTIONAL_NT_SPECIFIC_size :: Int+image_OPTIONAL_NT_SPECIFIC_size = 68++data ImageOptionalNTSpecific = ImageOptionalNTSpecific+ { iont_image_base :: Word32+ , iont_section_alignment :: Word32+ , iont_file_alignment :: Word32+ , iont_major_os_version :: Word16+ , iont_minor_os_version :: Word16+ , iont_major_image_version :: Word16+ , iont_minor_image_version :: Word16+ , iont_major_subsys_version :: Word16+ , iont_minor_subsys_version :: Word16+ , iont_win32_version :: Word32+ , iont_size_of_image :: Word32+ , iont_size_of_headers :: Word32+ , iont_checksum :: Word32+ , iont_subsystem :: Word16+ , iont_dll_characteristics :: Word16+ , iont_size_stack_reserve :: Word32+ , iont_size_stack_commit :: Word32+ , iont_size_heap_reserve :: Word32+ , iont_size_heap_commit :: Word32+ , iont_loader_flags :: Word32+ , iont_rva_num_and_sizes :: Word32+ } + deriving Show++image_DATA_DIRECTORY_size :: Int+image_DATA_DIRECTORY_size = 4++data ImageDataDirectory = ImageDataDirectory+ { idd_virtual_addr :: Word32+ , idd_size :: Word32+ }+ deriving Show++data SectionHeader = SectionHeader + { sh_name :: String -- 8 bytes+ , sh_virtual_size :: Word32+ , sh_virtual_addr :: Word32+ , sh_size_raw_data :: Word32+ , sh_ptr_raw_data :: Word32+ , sh_ptr_relocations :: Word32+ , sh_ptr_linenums :: Word32+ , sh_num_relocations :: Word16+ , sh_num_linenums :: Word16+ , sh_characteristics :: Word32+ }+ deriving Show++-- | \'.edata\'+-- name_ptrs and ordinals should be zipped...+data ExportData = ExportData+ { ed_directory_table :: ExportDirectoryTable+ , ed_export_address_table :: [ExportAddress]+ , ed_name_ptr_table :: [Word32]+ , ed_ordinal_table :: [Word16]+ , ed_dll_name :: String+ , ed_name_table :: [String]+ }+ deriving Show++data ExportDirectoryTable = ExportDirectoryTable+ { edt_export_flags :: Word32+ , edt_timedate_stamp :: Word32+ , edt_major_version :: Word16+ , edt_minor_version :: Word16+ , edt_name_rva :: Word32+ , edt_ordinal_base :: Word32+ , edt_num_addr_table_entries :: Word32+ , edt_num_name_ptrs :: Word32+ , edt_export_addr_table_rva :: Word32+ , edt_name_ptr_table_rva :: Word32+ , edt_ordinal_table_rva :: Word32+ }+ deriving Show++ +data ExportAddress = EA_Export_RVA Word32+ | EA_Forwarder_RVA Word32 + deriving Show
+ src/Hurdle/DefOutput.hs view
@@ -0,0 +1,36 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Hurdle.DefOutput+-- Copyright : (c) Stephen Tetley 2009+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : +--+-- Print in .def format+--+--------------------------------------------------------------------------------++module Hurdle.DefOutput where++import Hurdle.Datatypes++import Text.PrettyPrint.HughesPJ++printDef :: Image -> IO ()+printDef = putStr . defText++defText :: Image -> String+defText = renderStyle (Style PageMode 80 1.5) . defs+++defs :: Image -> Doc+defs a = text "LIBRARY" <+> text (ed_dll_name $ image_export_data a)+ $+$ text "EXPORTS"+ $+$ outputExports ( image_export_data a)++outputExports :: ExportData -> Doc+outputExports = vcat . map text . ed_name_table
+ src/Hurdle/ParseMonad.hs view
@@ -0,0 +1,238 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Hurdle.ParseMonad+-- Copyright : (c) Stephen Tetley 2009+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : to be determined.+--+-- Random access parse monad +--+--------------------------------------------------------------------------------++module Hurdle.ParseMonad where++import Control.Applicative+import Control.Monad+import Data.Array.IO+import Data.Bits+import Data.Char+import Data.Int+import Data.Word+import System.IO++type ParseErr = String++type ImageData = IOUArray Int Word8 -- Is Int big enough for index?++type St = Int -- 'file' position+type Env = ImageData+ ++newtype Parser a = Parser { + getParser :: Env -> St -> IO (St,Either ParseErr a) }+ +instance Functor Parser where+ fmap f (Parser x) = Parser $ + \env st -> x env st `bindIO` \(st',a) -> return (st',fmap f a)++bindIO :: IO a -> (a -> IO b) -> IO b+bindIO = (>>=)++returnIO :: a -> IO a+returnIO = return+++instance Monad Parser where+ return a = Parser $ \_ st -> returnIO (st,Right a)+ (Parser x) >>= f = Parser $ + \env st -> x env st `bindIO` \(st',ans) ->+ case ans of + Left err -> returnIO (st',Left err)+ Right a -> getParser (f a) env st'++++instance Applicative Parser where+ pure = return+ (<*>) = ap+++getSt :: Parser St+getSt = Parser $ \_ st -> return (st, Right st)++putSt :: St -> Parser ()+putSt st = Parser $ \_ _ -> return (st, Right ())++modifySt :: (St -> St) -> Parser ()+modifySt f = Parser $ \_ st -> return (f st, Right ())++askEnv :: Parser Env+askEnv = Parser $ \env st -> return (st, Right env)+++throwErr :: String -> Parser a+throwErr msg = Parser $ \_ st -> return (st,Left msg)++liftIOAction :: IO a -> Parser a+liftIOAction ma = Parser $ \_ st -> ma >>= \a -> return (st,Right a) +++runParser :: Parser a -> FilePath -> IO (Either ParseErr a)+runParser p filename = withBinaryFile filename ReadMode $ \ handle -> do + sz' <- hFileSize handle+ let sz = fromIntegral sz'+ arr <- newArray_ (0,sz-1)+ _rsz <- hGetArray handle arr (fromIntegral sz)+ (_,ans) <- runP p arr+ return ans + where + runP :: Parser a -> ImageData -> IO (St, Either ParseErr a) + runP (Parser x) arr = x arr 0+++--------------------------------------------------------------------------------+-- +++ +getWord8 :: Parser Word8+getWord8 = do+ ix <- getSt+ arr <- askEnv+ a <- liftIOAction $ readArray arr ix+ putSt $ ix+1+ return a+++--------------------------------------------------------------------------------+-- helpers++-- | applicative cons+(<:>) :: Applicative f => f a -> f [a] -> f [a]+(<:>) p1 p2 = (:) <$> p1 <*> p2+++jumpto :: Int -> Parser ()+jumpto = putSt+++reportFail :: String -> Parser a+reportFail s = do + posn <- getSt+ throwErr $ s ++ posStr posn+ where+ posStr p = " position " ++ show p ++satisfy :: (Word8 -> Bool) -> Parser Word8+satisfy p = getWord8 >>= + (\x -> if p x then return x else reportFail $ "satisfy...")++try :: Parser a -> Parser a+try p = Parser $ \env st -> (getParser p) env st >>= \ ans -> + case ans of+ (_,Left err) -> return (st,Left err)+ okay -> return okay++opt :: Parser a -> Parser (Maybe a)+opt p = Parser $ \env st -> (getParser p) env st >>= \ ans -> + case ans of+ (_, Left _) -> return (st, Right Nothing)+ (st', Right a) -> return (st', Right $ Just a)++manyTill :: Parser a -> Parser b -> Parser [a]+manyTill p end = do + ans <- opt end+ case ans of+ Just _ -> return []+ Nothing -> p <:> manyTill p end +++eof :: Parser Bool+eof = do+ ix <- getSt+ arr <- askEnv+ (_,up) <- liftIOAction $ getBounds arr+ return $ (ix>=up) ++-- | Read a null-terminated string+cstring :: Parser String+cstring = manyTill char w8Zero+++w8Zero :: Parser Word8+w8Zero = satisfy (==0)++getBytes :: Integral a => a -> Parser [Word8]+getBytes i = count (fromIntegral i) getWord8++char :: Parser Char+char = (chr . fromIntegral) <$> getWord8 ++getChar8bit :: Parser Char+getChar8bit = (chr . fromIntegral) <$> getWord8 ++filePosition :: Parser Int+filePosition = getSt+++count :: Int -> Parser a -> Parser [a]+count i p = step i [] where+ step n xs | n <= 0 = return (reverse xs)+ | otherwise = p >>= \a -> step (n-1) (a:xs)+ +++getInt8 :: Parser Int8+getInt8 = (fromIntegral . unwrap) <$> getWord8+ where+ unwrap :: Word8 -> Int+ unwrap i | i > 128 = (fromIntegral i) - 256+ | otherwise = fromIntegral i++getWord16be :: Parser Word16+getWord16be = w16be <$> getWord8 <*> getWord8 ++getWord32be :: Parser Word32+getWord32be = w32be <$> getWord8 <*> getWord8 <*> getWord8 <*> getWord8++getWord16le :: Parser Word16+getWord16le = w16le <$> getWord8 <*> getWord8 ++getWord32le :: Parser Word32+getWord32le = w32le <$> getWord8 <*> getWord8 <*> getWord8 <*> getWord8++ ++w16be :: Word8 -> Word8 -> Word16+w16be a b = (shiftL8 a) + fromIntegral b+ + +w32be :: Word8 -> Word8 -> Word8 -> Word8 -> Word32+w32be a b c d = (shiftL24 a) + (shiftL16 b) + (shiftL8 c) + fromIntegral d+++w16le :: Word8 -> Word8 -> Word16+w16le a b = fromIntegral a + (shiftL8 b)++w32le :: Word8 -> Word8 -> Word8 -> Word8 -> Word32+w32le a b c d = fromIntegral a + (shiftL8 b) + (shiftL16 c) + (shiftL24 d) ++++shiftL8 :: (Bits b, Integral b) => Word8 -> b+shiftL8 = (`shiftL` 8) . fromIntegral+++shiftL16 :: (Bits b, Integral b) => Word8 -> b+shiftL16 = (`shiftL` 16) . fromIntegral+++shiftL24 :: (Bits b, Integral b) => Word8 -> b+shiftL24 = (`shiftL` 24) . fromIntegral++
+ src/Hurdle/Parser.hs view
@@ -0,0 +1,252 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Hurdle.Parser+-- Copyright : (c) Stephen Tetley 2009+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : to be determined.+--+-- Read a DLL...+--+--------------------------------------------------------------------------------++module Hurdle.Parser where++import Hurdle.Datatypes+import Hurdle.ParseMonad++import Control.Applicative+import Data.Word+import System.IO+++readDLL :: FilePath -> IO Image+readDLL filename = do + ans <- runParser dllFile filename+ case ans of + Left err -> putStrLn err >> error "readDLL failed"+ Right mf -> return mf++ ++--------------------------------------------------------------------------------+-- + + +dllFile :: Parser Image+dllFile = do+ dosH <- imageDOSHeader+ toNewExeHeader (idh_new_exe_header_addr dosH)+ sig <- signature+ coffH <- imageCOFFHeader+ optH <- imageOptionalHeader+ secHs <- count (fromIntegral $ ich_num_sections coffH) sectionHeader+ expH <- exportSectionHeader secHs+ jumpto (fromIntegral $ sh_ptr_raw_data expH)+ expD <- exportData expH+ return $ Image { image_dos_header = dosH+ , image_signature = sig+ , image_coff_header = coffH+ , image_opt_header = optH+ , image_section_headers = secHs+ , image_export_data = expD+ }++-- bit crummy...+exportSectionHeader :: [SectionHeader] -> Parser SectionHeader+exportSectionHeader (_:_:_:_:edata:_) = return $ edata+exportSectionHeader _ = reportFail "no .edata" +++toNewExeHeader :: Word32 -> Parser ()+toNewExeHeader n = do + getBytes (n - dosHSize)+ return () + where+ dosHSize = 0x0040++imageDOSHeader :: Parser ImageDOSHeader+imageDOSHeader = ImageDOSHeader <$> + magic + <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> reserved1+ <*> getWord16le+ <*> getWord16le+ <*> reserved2+ <*> getWord32le+ where+ -- | Magic number 0x5a4d++ magic :: Parser Word16+ magic = getWord16le+ + reserved1 :: Parser (Word16,Word16,Word16,Word16)+ reserved1 = (,,,) <$> getWord16le <*> getWord16le + <*> getWord16le <*> getWord16le++ reserved2 :: Parser [Word16]+ reserved2 = count 10 getWord16le+++signature :: Parser (Char,Char,Char,Char) +signature = (,,,) <$> getChar8bit <*> getChar8bit + <*> getChar8bit <*> getChar8bit++imageCOFFHeader :: Parser ImageCOFFHeader+imageCOFFHeader = ImageCOFFHeader <$> + getWord16le+ <*> getWord16le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord16le+ <*> getWord16le+ ++imageOptionalHeader :: Parser ImageOptionalHeader+imageOptionalHeader = ImageOptionalHeader <$> + imageOptionalStandard+ <*> imageOptionalNTSpecific+ <*> count 16 imageDataDirectory+ ++imageOptionalStandard :: Parser ImageOptionalStandard+imageOptionalStandard = ImageOptionalStandard <$>+ getWord16le+ <*> getWord8+ <*> getWord8+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le++imageOptionalNTSpecific :: Parser ImageOptionalNTSpecific+imageOptionalNTSpecific = ImageOptionalNTSpecific <$>+ getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord16le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord16le+ <*> getWord16le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+++imageDataDirectory :: Parser ImageDataDirectory+imageDataDirectory = ImageDataDirectory <$>+ getWord32le+ <*> getWord32le+++sectionHeader :: Parser SectionHeader+sectionHeader = SectionHeader <$>+ count 8 getChar8bit+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord16le+ <*> getWord16le+ <*> getWord32le+++-- At some point... I'll tidy this up.++exportData :: SectionHeader -> Parser ExportData+exportData section = do+ edt <- exportDirectoryTable+ let eac = fromIntegral $ edt_num_addr_table_entries edt+ let ea_rva = fromIntegral $ + rvaToOffset (edt_export_addr_table_rva edt) section+ jumpto ea_rva+ ats <- count eac exportAddress++ let enc = fromIntegral $ edt_num_name_ptrs edt+ let en_rva = fromIntegral $ + rvaToOffset (edt_name_ptr_table_rva edt) section+ jumpto en_rva+ nptrs <- count enc getWord32le++ let eo_rva = fromIntegral $ + rvaToOffset (edt_ordinal_table_rva edt) section+ jumpto eo_rva+ ords <- count enc getWord16le++ names <- exportNames section nptrs+ + let nm_rva = fromIntegral $ + rvaToOffset (edt_name_rva edt) section++ jumpto nm_rva+ dllname <- cstring++ return $ ExportData { ed_directory_table = edt+ , ed_export_address_table = ats+ , ed_name_ptr_table = nptrs+ , ed_ordinal_table = ords+ , ed_dll_name = dllname+ , ed_name_table = names+ }++exportDirectoryTable :: Parser ExportDirectoryTable+exportDirectoryTable = ExportDirectoryTable <$>+ getWord32le+ <*> getWord32le+ <*> getWord16le+ <*> getWord16le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le+ <*> getWord32le++-- WRONG (for now) +exportAddress :: Parser ExportAddress+exportAddress = EA_Export_RVA <$>+ getWord32le++exportNames :: SectionHeader -> [Word32] -> Parser [String]+exportNames _ [] = return []+exportNames section (x:xs) = mf <:> exportNames section xs+ where+ mf = jumpto (fromIntegral $ rvaToOffset x section) >> cstring+++rvaToOffset :: Word32 -> SectionHeader -> Word32+rvaToOffset rva section = + rva - (sh_virtual_addr section - sh_ptr_raw_data section)
+ src/Hurdle/TextDump.hs view
@@ -0,0 +1,295 @@+{-# OPTIONS -Wall #-}++--------------------------------------------------------------------------------+-- |+-- Module : Hurdle.TextDump+-- Copyright : (c) Stephen Tetley 2009+-- License : BSD3+--+-- Maintainer : Stephen Tetley <stephen.tetley@gmail.com>+-- Stability : highly unstable+-- Portability : +--+-- Pretty print as text...+--+--------------------------------------------------------------------------------++module Hurdle.TextDump where++import Hurdle.Datatypes++import Data.Word+import Numeric+import Text.PrettyPrint.HughesPJ+++-- applyfs is 'sequence' from Control.Monad but I don't to drag in+-- Control.Monad.Instances+--+applyfs :: [(a -> b)] -> a -> [b]+applyfs [] _ = [] +applyfs (f:fs) a = f a : applyfs fs a++printImage :: Image -> IO ()+printImage = putStr . imageText++imageText :: Image -> String+imageText = renderStyle (Style PageMode 80 1.5) . ppImage++ppImage :: Image -> Doc+ppImage a = + ppImageDOSHeader (image_dos_header a)+ $+$ columnSep+ $+$ ppSignature (image_signature a)+ $+$ ppImageCOFFHeader (image_coff_header a)+ $+$ ppImageOptionalHeader (image_opt_header a)+ $+$ (vcat $ map ppSectionHeader $ image_section_headers a)+ $+$ ppExportData (image_export_data a)+++ppImageDOSHeader :: ImageDOSHeader -> Doc+ppImageDOSHeader a = + tableProlog "IMAGE_DOS_HEADER" (24,6) (applyfs fields a) + where+ ppf = ppField 4 24 + fields = + [ ppf 2 "magic" (ppHex 4 . idh_magic_number)+ , ppf 2 "bytes last page" (ppHex 4 . idh_bytes_last_page)+ , ppf 2 "pages in file" (ppHex 4 . idh_pages_in_file)+ , ppf 2 "relocations" (ppHex 4 . idh_relocations)+ , ppf 2 "header para size" (ppHex 4 . idh_size_header_paras)+ , ppf 2 "min extra paragraphs" (ppHex 4 . idh_min_extra_paras)+ , ppf 2 "max extra paragraphs" (ppHex 4 . idh_max_extra_paras)+ , ppf 2 "initial SS value" (ppHex 4 . idh_initial_relative_ss)+ , ppf 2 "initial SP value" (ppHex 4 . idh_initial_sp)+ , ppf 2 "checksum" (ppHex 4 . idh_header_checksum)++ , ppf 2 "initial IP value" (ppHex 4 . idh_initial_ip)+ , ppf 2 "initial CS value" (ppHex 4 . idh_initial_relative_cs)+ , ppf 2 "relocation table addr" (ppHex 4 . idh_reltable_file_addr)+ , ppf 2 "overlay number" (ppHex 4 . idh_overlay_number)+ , ppf 8 "reserved 1" (tup4 . idh_reserved_words)+ , ppf 2 "oem identifier" (ppHex 4 . idh_oem_identifier)+ , ppf 2 "oem info" (ppHex 4 . idh_oem_info)+ , ppf 20 "reserved 2" (text . show . idh_reserved_words_two)+ , ppf 4 "new exe header addr" (ppHex 8 . idh_new_exe_header_addr)+ ]++ tup4 (s,t,u,v) = text $ show [s,t,u,v]++ppSignature :: (Char,Char,Char,Char) -> Doc+ppSignature (s,t,u,v) = + text "Signature:" <+> (listDoc $ map (text . show) [s,t,u,v])++ppImageCOFFHeader :: ImageCOFFHeader -> Doc+ppImageCOFFHeader a = + tableProlog "IMAGE COFF HEADER" (24,6) (applyfs fields a) + where+ ppf = ppField 4 24 + fields = + [ ppf 2 "machine" (ppHex 4 . ich_machine)+ , ppf 2 "num sections" (ppHex 4 . ich_num_sections)+ , ppf 4 "timedatestamp" (ppHex 8 . ich_timedate_stamp)+ , ppf 4 "ptr to sym table" (ppHex 8 . ich_sym_table_ptr)+ , ppf 4 "num symbols" (ppHex 8 . ich_num_symbols)+ , ppf 2 "size optional header" (ppHex 4 . ich_opt_header_size)+ , ppf 2 "characteristics" (ppHex 4 . ich_characteristics)+ ]+++ppImageOptionalHeader :: ImageOptionalHeader -> Doc+ppImageOptionalHeader a = + ppImageOptionalStandard (ioh_header_std_fields a)+ $+$ ppImageOptionalNTSpecific (ioh_nt_specific_fields a)+ $+$ (vcat $ zipWith ppImageDataDirectory names (ioh_data_directory a))+ where+ names = [ ".edata"+ , ".idata"+ , ".rsrc"+ , ".pdata"+ , "attribute certificate table"+ , ".reloc"+ , ".debug"+ , "architecture"+ , "global ptr"+ , ".tls"+ , "load config"+ , "bound impact"+ , "import address table"+ , "delay import descriptor"+ , ".cormeta"+ , "reserved"+ ]++ppImageOptionalStandard :: ImageOptionalStandard -> Doc+ppImageOptionalStandard a =+ tableProlog "IMAGE OPTIONAL HEADER STANDARD" (24,6) (applyfs fields a) + where+ ppf = ppField 4 24+ fields = + [ ppf 2 "magic" (ppHex 4 . ios_magic)+ , ppf 1 "major linker ver." (ppHex 2 . ios_major_linker_version)+ , ppf 1 "minor linker ver." (ppHex 2 . ios_minor_linker_version)+ , ppf 4 "size of code" (ppHex 8 . ios_size_of_code)+ , ppf 4 "size of init. data" (ppHex 8 . ios_size_of_inited_data)+ , ppf 4 "size of uninit. data" (ppHex 8 . ios_size_of_uninited_data)+ , ppf 4 "entry ptr addr" (ppHex 8 . ios_entry_point_addr)+ , ppf 4 "base of code" (ppHex 8 . ios_base_of_code)+ , ppf 4 "base of data" (ppHex 8 . ios_base_of_data)+ ]+++ppImageOptionalNTSpecific :: ImageOptionalNTSpecific -> Doc+ppImageOptionalNTSpecific a =+ tableProlog "IMAGE OPTIONAL HEADER NT SPECIFIC" (24,6) (applyfs fields a)+ where+ ppf = ppField 4 24+ fields = + [ ppf 4 "image base" (ppHex 8 . iont_image_base)+ , ppf 4 "section alignment" (ppHex 8 . iont_section_alignment)+ , ppf 4 "file alignment" (ppHex 8 . iont_file_alignment)+ , ppf 2 "major os version" (ppHex 4 . iont_major_os_version)+ , ppf 2 "minor os version" (ppHex 4 . iont_minor_os_version)+ , ppf 2 "major image version" (ppHex 4 . iont_major_image_version)+ , ppf 2 "minor image version" (ppHex 4 . iont_minor_image_version)+ , ppf 2 "major subsys version" (ppHex 4 . iont_major_subsys_version)+ , ppf 2 "minor subsys version" (ppHex 4 . iont_minor_subsys_version)+ , ppf 4 "win32 version" (ppHex 8 . iont_win32_version)+ , ppf 4 "size of image" (ppHex 8 . iont_size_of_image)+ , ppf 4 "size of headers" (ppHex 8 . iont_size_of_headers)+ , ppf 4 "checksum" (ppHex 8 . iont_checksum)+ , ppf 2 "subsystem" (ppHex 4 . iont_subsystem)+ , ppf 2 "dll characteristics" (ppHex 4 . iont_dll_characteristics)+ , ppf 4 "size of stack reserve" (ppHex 8 . iont_size_stack_reserve)+ , ppf 4 "size of stack commit" (ppHex 8 . iont_size_stack_commit)+ , ppf 4 "size of heap reserve" (ppHex 8 . iont_size_heap_reserve)+ , ppf 4 "size of heap commit" (ppHex 8 . iont_size_heap_commit)+ , ppf 4 "loader flags" (ppHex 8 . iont_loader_flags)+ , ppf 4 "rva num and sizes" (ppHex 8 . iont_rva_num_and_sizes)+ ]++ppImageDataDirectory :: String -> ImageDataDirectory -> Doc+ppImageDataDirectory s a =+ tableProlog s (24,6) (applyfs fields a) + where+ ppf = ppField 4 24+ fields = + [ ppf 4 "virtual address" (ppHex 8 . idd_virtual_addr)+ , ppf 4 "size" (ppHex 8 . idd_size)+ ]+++ppSectionHeader :: SectionHeader -> Doc+ppSectionHeader a = + tableProlog "SECTION HEADER" (24,6) (applyfs fields a)+ where+ ppf = ppField 4 24+ fields = + [ ppf 8 "name" (text . sh_name)+ , ppf 4 "virtual size" (ppHex 8 . sh_virtual_size)+ , ppf 4 "virtual addr" (ppHex 8 . sh_virtual_addr)+ , ppf 4 "size of raw data" (ppHex 8 . sh_size_raw_data)+ , ppf 4 "ptr to raw data" (ppHex 8 . sh_ptr_raw_data)+ , ppf 4 "ptr to relocations" (ppHex 8 . sh_ptr_relocations)+ , ppf 4 "ptr to line numbers" (ppHex 8 . sh_ptr_linenums)+ , ppf 2 "num of relocations" (ppHex 4 . sh_num_relocations)+ , ppf 2 "num of line numbers" (ppHex 4 . sh_num_linenums)+ , ppf 4 "characteristics" (ppHex 8 . sh_characteristics)+ ]++ppExportData :: ExportData -> Doc+ppExportData a = + ppExportDirectoryTable (ed_directory_table a)+ $+$ ppExportAddressTable (ed_export_address_table a)+ $+$ ppExportNamePtrTable (ed_name_ptr_table a)+ $+$ ppExportOrdinalTable (ed_ordinal_table a)+ $+$ ppExportNames (ed_name_table a)++ppExportDirectoryTable :: ExportDirectoryTable -> Doc+ppExportDirectoryTable a = + tableProlog ".edata (Export directory table)" (24,6) (applyfs fields a)+ where+ ppf = ppField 4 24+ fields = + [ ppf 8 "export flags" (ppHex 8 . edt_export_flags)+ , ppf 8 "timedate stamp" (ppHex 8 . edt_timedate_stamp)+ , ppf 4 "major version" (ppHex 4 . edt_major_version)+ , ppf 4 "minor version" (ppHex 4 . edt_minor_version)+ , ppf 8 "name rva" (ppHex 8 . edt_name_rva)+ , ppf 8 "oridinal base" (ppHex 8 . edt_ordinal_base)+ , ppf 8 "addr table entries" (ppHex 8 . edt_num_addr_table_entries)+ , ppf 8 "num name ptrs" (ppHex 8 . edt_num_name_ptrs)+ , ppf 8 "export addr table rva" (ppHex 8 . edt_export_addr_table_rva)+ , ppf 8 "name ptr rva" (ppHex 8 . edt_name_ptr_table_rva)+ , ppf 8 "ordinal table rva" (ppHex 8 . edt_ordinal_table_rva)+ ]++ppExportAddressTable :: [ExportAddress] -> Doc+ppExportAddressTable a = + tableProlog "Export address" (24,6) (map field a)+ where+ ppf = ppField 4 24+ field (EA_Export_RVA w32) = ppf 8 "export RVA" (ppHex 8) w32+ field (EA_Forwarder_RVA w32) = ppf 8 "forwarder RVA" (ppHex 8) w32+ ++ppExportNamePtrTable :: [Word32] -> Doc+ppExportNamePtrTable a = + tableProlog "Export name pointers" (24,6) (map field a)+ where+ ppf = ppField 4 24+ field = ppf 8 "name ptr" (ppHex 8)++ppExportOrdinalTable :: [Word16] -> Doc+ppExportOrdinalTable a = + tableProlog "Export ordinals" (24,6) (map field a)+ where+ ppf = ppField 4 24+ field = ppf 4 "ordinal" (ppHex 4) ++ppExportNames :: [String] -> Doc+ppExportNames a = + tableProlog "Export names" (24,6) (map field a)+ where+ ppf = ppField 4 24+ field = ppf 4 "export name" text ++++--------------------------------------------------------------------------------+-- Helpers++tableProlog :: String -> (Int,Int) -> [Doc] -> Doc+tableProlog s (m,n) ds =+ columnSep + $+$ text s + $+$ columnSep+ $+$ columnHeadings m n+ $+$ columnSep+ $+$ vcat ds+ where+ columnHeadings fsz vsz = + text "size" <+> text (pad fsz ' ' "field") <+> text (pad vsz ' ' "value")+++columnSep :: Doc+columnSep = text $ replicate 60 '-' +++ppField :: Int -> Int -> Int -> String -> (a -> Doc) -> a -> Doc+ppField n1 n2 sz field_name f a = text sz' <+> text field_name' <+> f a+ where+ sz' = pad n1 ' ' (show sz)+ field_name' = pad n2 ' ' field_name++ppHex :: Integral a => Int -> a -> Doc+ppHex n i = text "0x" <> (text $ pad n '0' $ showHex i "")+ ++pad :: Int -> Char -> String -> String+pad n ch s | length s < n = replicate (n - length s) ch ++ s+ | otherwise = s++listDoc :: [Doc] -> Doc+listDoc = brackets . hcat . punctuate comma+