Thursday, September 23, 2010

Executing a ByteString

Why not!

{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign ( FunPtr, withForeignPtr, castPtrToFunPtr )
import System ( getArgs )
import qualified Data.ByteString as B ( pack, ByteString )
import qualified Data.ByteString.Internal as B ( toForeignPtr )

payload :: B.ByteString
payload = B.pack [
0x55, -- push %rbp
0x48, 0x89, 0xe5, -- mov %rsp,%rbp
0x89, 0x7d, 0xec, -- mov %edi,-0x14(%rbp)
0xc7, 0x45, 0xfc,
1, 0, 0, 0, -- movl $0x1,-0x4(%rbp)
0xeb, 0x0e, -- jmp 1e <fact+0x1e>
0x8b, 0x45, 0xfc, -- mov -0x4(%rbp),%eax
0x0f, 0xaf, 0x45, 0xec, -- imul -0x14(%rbp),%eax
0x89, 0x45, 0xfc, -- mov %eax,-0x4(%rbp)
0x83, 0x6d, 0xec, 1, -- subl $0x1,-0x14(%rbp)
0x83, 0x7d, 0xec, 1, -- cmpl $0x1,-0x14(%rbp)
0x7f, 0xec, -- jg 10 <fact+0x10>
0x8b, 0x45, 0xfc, -- mov -0x4(%rbp),%eax
0xc9, -- leaveq
0xc3 ] -- retq

type F = Int -> Int

foreign import ccall "dynamic"
getF :: FunPtr F -> F

main :: IO ()
main = do
[s] <- getArgs
n <- readIO s
let (fptr, _, _) = B.toForeignPtr payload
withForeignPtr fptr $ \ptr -> do
let f = getF $ castPtrToFunPtr ptr
print $ f n

Testing it:

$ runhaskell foo.hs 6
720

This code will only work on x86-64.

5 comments:

  1. For completeness, how about posting the code for getF?

    ReplyDelete
  2. Yitz:

    This code is complete. 'foreign import "dynamic"' is a special type of import, producing a function which turns function pointers into Haskell functions.

    This might involve some extra C / Cmm code, but it's generated automatically by GHC.

    ReplyDelete
  3. Now we just need a quasiquoter that calls an assembler to make inline assembly :)

    ReplyDelete
  4. Martin Grabm├╝llerSeptember 29, 2010 at 2:15 AM

    copumpkin: you can try out Harpy (search for package harpy on Hackage) for an assembler DSL for 32-bit Intel code.

    ReplyDelete
  5. >> Why not!

    For security reasons, of course !
    Unless you want Haskell to be the new C, as far as execution of arbitrary code is concerned…

    ReplyDelete