Navigation Menu

Skip to content

Commit

Permalink
Updates. Adding tools for import analysis.
Browse files Browse the repository at this point in the history
  • Loading branch information
igraves committed Jun 13, 2012
1 parent ce4ad99 commit 9882285
Show file tree
Hide file tree
Showing 7 changed files with 588 additions and 36 deletions.
184 changes: 184 additions & 0 deletions Data/PE/PEFile.hs
@@ -0,0 +1,184 @@
module Gadgets.PEFile where

import Data.PE.Parser
import Data.PE.Structures
import Data.PE.Utils
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import Data.Maybe
import System.Environment
import Data.Word
import System.IO.Unsafe
import Data.Binary
import Data.Binary.Get
import Data.Char
import Data.Bits
import Data.Array.Unboxed
import Data.List

type Filename = String
type Secname = String
type SectionMeta = (SectionTable, LBS.ByteString)

getsecandinfo :: Filename -> Secname -> IO ((Maybe SectionMeta, MachineType))
getsecandinfo fn sn = buildFile fn >>= \pefile -> return (getsection pefile sn, getmachinetype pefile)

getsec :: Filename -> Secname -> IO (Maybe SectionMeta)
getsec fn sn = buildFile fn >>= \pefile -> return $ getsection pefile sn

getsecs :: Filename -> [SectionMeta]
getsecs fn = unsafePerformIO (buildFile fn >>= \pefile -> return $ (sectionTables.peHeader) pefile)

getary fn = arrayrep $ getsecs fn

getdirs :: Filename -> [DirectoryEntry]
getdirs fn = unsafePerformIO (buildFile fn >>= \pefile -> return $ (dataDirectories.peHeader) pefile)

getsection :: PEFile -> Secname -> Maybe SectionMeta
getsection pefile secn = let sections = (sectionTables.peHeader) pefile in
find (\x -> secn == (sectionHeaderName $ fst x)) sections

getmachinetype :: PEFile -> MachineType
getmachinetype pe = targetMachine $ coffHeader $ peHeader pe

showsections :: Filename -> IO ()
showsections filename = do
pefile <- buildFile filename
let sections = (sectionTables.peHeader) pefile
let coff = (coffHeader.peHeader) pefile
let std = (standardFields.peHeader) pefile
let showme = \x -> (sectionHeaderName $ fst x)
--putStr $ show datadirs
putStr $ show $ coff
putStr $ show $ std
putStr $ show $ map showme sections
--putStr $ show $ (numberOfRVAandSizes.windowsSpecFields.peHeader) pefile
--putStr $ show pefile
return ()


--Import Table Parsing stuff. This should eventually move to the PE library.

type ImportDirectory = [ImportDirectoryEntry]
type ImportLookupTable = [ImportLookupTableEntry]

data ImportDirectoryEntry = ID {
lookupTableRVA :: Word32,
timeStamp :: Word32,
forwarderChain :: Word32,
nameRVA :: Word32,
importAddressTableRVA :: Word32
} | IDNull deriving (Show,Eq)

data HintNameEntry = HNE {
hint :: Word16,
name :: String
} deriving (Show, Eq)

data ImportLookupTableEntry = ILTOrd Word16 | ILTHint Word32 | ILTNull deriving (Show,Eq)


getImpDir :: Get ImportDirectory
getImpDir = do
entry <- get
case (entry) of
IDNull -> return [IDNull]
x -> getImpDir >>= \y -> return (x : y)


getLT :: Get ImportLookupTable
getLT = do
entry <- get
case (entry) of
ILTNull -> return [ILTNull]
x -> getLT >>= \y -> return (x : y)


instance Binary HintNameEntry where
put (HNE h n) = let words = (map fromIntegral $ map ord n)::[Word8] in
do
put h
put words
if (length words `mod` 2 == 0)
then put (0x0::Word8)
else return ()
get = do
ordinal <- getWord16le
astr <- getAStr
if (length astr `mod` 2 == 0)
then getWord8 >>= \_ -> return (HNE ordinal astr)
else return (HNE ordinal astr)

instance Binary ImportDirectoryEntry where
put (ID lut ts fc nrva iarva) = put lut >> put ts >> put fc >> put nrva >> put iarva
put (IDNull) = put (0x0::Word32) >> put (0x0::Word32) >> put (0x0::Word32) >> put (0x0::Word32) >> put (0x0::Word32)
get = do
lut <- getWord32le
ts <- getWord32le
fc <- getWord32le
nrva <- getWord32le
iarva <- getWord32le
case (lut + ts + fc + nrva + iarva) of
0 -> return IDNull
_ -> return (ID lut ts fc nrva iarva)

instance Binary ImportLookupTableEntry where
put (ILTOrd ord ) = put (0x80::Word8) >> put ord >> put (0x00::Word8)
put (ILTHint rva) = put (setBit rva 31)
put ILTNull = put (0x0::Word32)
get = do
word <- getWord32le
case (word) of
0 -> return ILTNull
_ -> case (testBit word 31) of
True -> return $ ILTOrd $ fromIntegral word
False -> return $ ILTHint (clearBit word 31)
--More PE Data structure stuff

importInfo fn = importInfo' (getsecs fn) (getdirs fn)

importInfo' secns dirs = map infos ientries
where ary = arrayrep secns
ientries = delete IDNull $ buildImport ary dirs
lookups = (buildLookup ary)
hnts = (buildHintName ary)
infos = \x -> (getdllname ary x, map name $ map hnts $ delete ILTNull $ lookups x)

--Build the Import table.
buildImport ary dirs = runGet getImpDir bstr
where itaddr = virtualAddr (dirs !! 1)
bstr = grabAt (fromIntegral itaddr) ary

buildLookup ary ientry = runGet getLT (grabAt (fromIntegral rva) ary)
where rva = lookupTableRVA ientry

buildHintName ary ltentry = case (ltentry) of
(ILTHint x) -> runGet hnte (grabAt (fromIntegral x) ary)
(ILTNull) -> error "Null encountered"
_ -> error "Not working with ords today"
where hnte = get >>= \x -> return x::Get HintNameEntry

getdllname ary ientry = case (ientry) of
(IDNull) -> ""
_ -> runGet getAStr (grabAt (fromIntegral rva) ary)
where rva = nameRVA ientry
--Building an array to represent the file structure
sectoblist (secn, bytes) = let words = LBS.unpack bytes in
let indxs x = x : indxs (x+1) in
zip (indxs $ fromIntegral $ virtualAddress secn) words

arrayrep :: [SectionMeta] -> UArray Word32 Word8
arrayrep secn = array (0,maxaddr) words
where
words = concat $ map sectoblist secn
maxaddr = maximum $ map fst words

--Ask for an address to begin a new head for a bytestring to build from, simple enough.
{-
grabAt :: Word32 -> UArray Word32 Word8 -> LBS.ByteString
grabAt indx ary = LBS.pack $ elems newarray
where maxdx = maximum $ indices ary
newarray = ixmap (0,maxdx-indx) (\i -> i - indx) ary --remap the array
-}
grabAt :: Int -> UArray Word32 Word8 -> LBS.ByteString
grabAt indx ary = LBS.pack $ drop (indx) $ elems ary
55 changes: 27 additions & 28 deletions Data/PE/Parser.hs
@@ -1,7 +1,6 @@
module Data.PE.Parser (buildFile, buildFileFromBS) where
import Data.PE.Structures
import Data.PE.Utils
import Data.Word
import qualified Data.ByteString.Lazy as B
import Data.Binary.Get
import Data.Maybe
Expand All @@ -17,16 +16,16 @@ buildFile fName = do
buildFileFromBS :: B.ByteString -- ^ByteString representing a PE file
-> PEFile -- ^The data structure returned
buildFileFromBS fbstring =
let peheader = (runGet header fbstring) in
let mapSections = \sections -> (secBytes fbstring sections) in
let peheader = (runGet pheader fbstring) in
let mapSections = \sections' -> (secBytes fbstring sections') in
let secTables = sectionTables peheader in
let binsections = map mapSections $ map fst secTables in
let fixsec = \x -> (x, fromJust $ lookup (sectionHeaderName x) binsections) in
let newsections = map fixsec $ map fst secTables in
PEFile{peHeader=peheader{sectionTables=newsections}}

header :: Get (PEHeader)
header = do
pheader :: Get (PEHeader)
pheader = do
dosheader <- buildMSDOSHead
bytes <- bytesRead
let peoffset = (fromIntegral (offset dosheader)) - (fromIntegral bytes)
Expand All @@ -47,10 +46,10 @@ sections 0 = return []
sections n = sections (n - 1) >>= \rest -> buildSectionTable >>= \item -> return (item:rest)

secBytes :: B.ByteString -> SectionTable -> (String,B.ByteString)
secBytes bs sec = let offset = (fromIntegral . pointerToRawData) sec in
secBytes bs sec = let offset' = (fromIntegral . pointerToRawData) sec in
let size = (fromIntegral . sizeOfRawData) sec in
let name = sectionHeaderName sec in
let pbs = B.drop offset bs in
let pbs = B.drop offset' bs in
let sbs = B.take size pbs in
(name, sbs)

Expand All @@ -70,26 +69,26 @@ buildMSDOSHead = do
cs' <- getWord16le
relocTableOffset' <- getWord16le
overlayNumber' <- getWord16le
getWord16le -- chew through
getWord16le
getWord16le
getWord16le
_ <- getWord16le -- chew through
_ <- getWord16le -- chew through
_ <- getWord16le -- chew through
_ <- getWord16le -- chew through
oemIdentifier' <- getWord16le
oemInformation' <- getWord16le
getWord32le -- chew through, there are actually 10 16-bit reserved slots, 32 here for brevity
getWord32le
getWord32le
getWord32le
getWord32le
_ <- getWord32le -- chew through, there are actually 10 16-bit reserved slots, 32 here for brevity
_ <-getWord32le
_ <-getWord32le
_ <-getWord32le
_ <-getWord32le
offset' <- getWord32le -- this should be 0x80, we could check later if we wanted to
let header = MSDOSHeader {signature=signature', lastsize=lastsize', pagesInFile=pagesInFile',
let header' = MSDOSHeader {signature=signature', lastsize=lastsize', pagesInFile=pagesInFile',
relocations=relocations', headerSizeInParagraph=headerSizeInParagraph',
minExtraParagraphs=minExtraParagraphs', maxExtraParagraphs=maxExtraParagraphs',
ss=ss', sp=sp', checksum=checksum', ip=ip', cs=cs',
relocTableOffset=relocTableOffset', overlayNumber=overlayNumber',
oemIdentifier=oemIdentifier', oemInformation=oemInformation', offset=offset'}

return header
return header'

buildPESignature :: Get (PESignature)
buildPESignature = do
Expand All @@ -105,11 +104,11 @@ buildCOFFHeader = do
numberOfSymbols' <- getWord32le
sizeofOptionalHeaders' <- getWord16le
coffCharacteristics' <- getWord16le
let header = COFFHeader { targetMachine=(mapMachine targetMachine'), numberOfSections=numberOfSections',
let header' = COFFHeader { targetMachine=(mapMachine targetMachine'), numberOfSections=numberOfSections',
timeDateStamp=timeDateStamp', pointerToSymbolTable=pointerToSymbolTable',
numberOfSymbols=numberOfSymbols', sizeofOptionalHeaders=sizeofOptionalHeaders',
coffCharacteristics=coffCharacteristics'}
return header
return header'



Expand All @@ -128,17 +127,17 @@ buildSFHeader = do
case (standardSig') of
0x10B -> do
baseOfData' <- getWord32le
let header = StandardFields { standardSig=standardSig', lnMajorVersion=lnMajorVersion',
let header' = StandardFields { standardSig=standardSig', lnMajorVersion=lnMajorVersion',
lnMinorVersion=lnMinorVersion', sizeOfCode=sizeOfCode', sizeOfInitializedData=sizeOfInitializedData',
sizeOfUninitData=sizeOfUninitData', addressOfEntryPoint=addressOfEntryPoint',
baseOfCode=baseOfCode', baseOfData=baseOfData'}
return header
return header'
0x20B -> do
let header = SFPlus { standardSig=standardSig', lnMajorVersion=lnMajorVersion',
let header' = SFPlus { standardSig=standardSig', lnMajorVersion=lnMajorVersion',
lnMinorVersion=lnMinorVersion', sizeOfCode=sizeOfCode', sizeOfInitializedData=sizeOfInitializedData',
sizeOfUninitData=sizeOfUninitData', addressOfEntryPoint=addressOfEntryPoint',
baseOfCode=baseOfCode'}
return header
return header'
_ -> error "Unrecognized PE format Magic Number"


Expand Down Expand Up @@ -166,7 +165,7 @@ buildWSFHeader = do
sizeOfHeapCommit' <- getWord32le
loaderFlags' <- getWord32le
numberOfRVAandSizes' <- getWord32le
let header = WindowsSpecFields { imageBase=imageBase', sectionAlignment=sectionAlignment',
let header' = WindowsSpecFields { imageBase=imageBase', sectionAlignment=sectionAlignment',
fileAlignment=fileAlignment', majorOSVersion=majorOSVersion',
minorOSVersion=minorOSVersion', majorImageVersion=majorImageVersion',
minorImageVersion=minorImageVersion', majorSubSystemVersion=majorSubSystemVersion',
Expand All @@ -175,7 +174,7 @@ buildWSFHeader = do
checkSum16=checkSum16', dllCharacteristics=dllCharacteristics', sizeOfStackReserve=sizeOfStackReserve',
sizeOfStackCommit=sizeOfStackCommit', sizeOfHeapReserve=sizeOfHeapReserve',
sizeOfHeapCommit=sizeOfHeapCommit', loaderFlags=loaderFlags', numberOfRVAandSizes=numberOfRVAandSizes' }
return header
return header'

buildWSFPlus :: Get (WindowsSpecFields)
buildWSFPlus = do
Expand All @@ -200,7 +199,7 @@ buildWSFPlus = do
sizeOfHeapCommit' <- getWord64le
loaderFlags' <- getWord32le
numberOfRVAandSizes' <- getWord32le
let header = WSFPlus { imgBase=imageBase', sectionAlignment=sectionAlignment',
let header' = WSFPlus { imgBase=imageBase', sectionAlignment=sectionAlignment',
fileAlignment=fileAlignment', majorOSVersion=majorOSVersion',
minorOSVersion=minorOSVersion', majorImageVersion=majorImageVersion',
minorImageVersion=minorImageVersion', majorSubSystemVersion=majorSubSystemVersion',
Expand All @@ -209,7 +208,7 @@ buildWSFPlus = do
checkSum16=checkSum16', dllCharacteristics=dllCharacteristics', szOfStackReserve=sizeOfStackReserve',
szOfStackCommit=sizeOfStackCommit', szOfHeapReserve=sizeOfHeapReserve',
szOfHeapCommit=sizeOfHeapCommit', loaderFlags=loaderFlags', numberOfRVAandSizes=numberOfRVAandSizes' }
return header
return header'


buildDataDirectories :: Int -> Get ([DirectoryEntry])
Expand Down
9 changes: 5 additions & 4 deletions Data/PE/Structures.hs
Expand Up @@ -99,7 +99,7 @@ data COFFHeader = COFFHeader {
instance Show COFFHeader where
show hdr = "Target Machine: " ++ (show $ targetMachine hdr) ++"\n"
++ "Number of Sections: " ++ (show (numberOfSections hdr)) ++"\n"
++ "Timestamp: " ++ (show $ fromIntegral . timeDateStamp $ hdr) ++ "\n"
++ "Timestamp: " ++ (show $ timeDateStamp $ hdr) ++ "\n"
++ "Symbol Table Pointer: 0x" ++ (showHex (pointerToSymbolTable hdr) "") ++ "\n"
++ "Number of Symbols: " ++ (show $ numberOfSymbols hdr) ++ "\n"
++ "Size of Optional Headers: " ++ (show $ sizeofOptionalHeaders hdr) ++ "\n"
Expand Down Expand Up @@ -215,7 +215,7 @@ instance Binary DirectoryEntry where
size <- getWord32le
let entry = DirEntry {virtualAddr=addr, entrySize=size}
return $ entry
put x = error "Serialization of DirectoryEntry not supported."
put _ = error "Serialization of DirectoryEntry not supported."

data SectionTable = SectionTable {
sectionHeaderName :: String,
Expand Down Expand Up @@ -249,7 +249,7 @@ instance Binary SectionTable where
numberOfLineNumbers=numberOfLineNumbers', secCharacteristics=secCharacteristics'}
return header

put x = error "SectionTable serialization not supported"
put _ = error "SectionTable serialization not supported"



Expand All @@ -262,7 +262,7 @@ instance Binary MachineType where
get = do
x <- getWord16le
return $ mapMachine x
put x = error "Serialization of MachineType not supported"
put _ = error "Serialization of MachineType not supported"



Expand All @@ -289,6 +289,7 @@ mapMachine w = case w of
0x1a8 -> SH5
0x1c2 -> THUMB
0x169 -> WCE
_ -> error "Bad machine type."

getAStr :: Get String
getAStr = do
Expand Down

0 comments on commit 9882285

Please sign in to comment.