packages feed

hurdle-0.1.0: src/Hurdle/Parser.hs

{-# 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)