unicorn/bindings/haskell/samples/Shellcode.hs

154 lines
5.5 KiB
Haskell
Raw Normal View History

-- Sample code to trace code with Linux code with syscall
import Unicorn
import Unicorn.Hook
import qualified Unicorn.CPU.X86 as X86
import Control.Monad.Trans.Class (lift)
import qualified Data.ByteString as BS
import Data.Word
import qualified Numeric as N (showHex)
import System.Environment
-- Code to be emulated
x86Code32 :: BS.ByteString
x86Code32 = BS.pack [0xeb, 0x19, 0x31, 0xc0, 0x31, 0xdb, 0x31, 0xd2, 0x31,
0xc9, 0xb0, 0x04, 0xb3, 0x01, 0x59, 0xb2, 0x05, 0xcd,
0x80, 0x31, 0xc0, 0xb0, 0x01, 0x31, 0xdb, 0xcd, 0x80,
0xe8, 0xe2, 0xff, 0xff, 0xff, 0x68, 0x65, 0x6c, 0x6c,
0x6f]
x86Code32Self :: BS.ByteString
x86Code32Self = BS.pack [0xeb, 0x1c, 0x5a, 0x89, 0xd6, 0x8b, 0x02, 0x66, 0x3d,
0xca, 0x7d, 0x75, 0x06, 0x66, 0x05, 0x03, 0x03, 0x89,
0x02, 0xfe, 0xc2, 0x3d, 0x41, 0x41, 0x41, 0x41, 0x75,
0xe9, 0xff, 0xe6, 0xe8, 0xdf, 0xff, 0xff, 0xff, 0x31,
0xd2, 0x6a, 0x0b, 0x58, 0x99, 0x52, 0x68, 0x2f, 0x2f,
0x73, 0x68, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3,
0x52, 0x53, 0x89, 0xe1, 0xca, 0x7d, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41]
-- Memory address where emulation starts
address :: Word64
address = 0x1000000
-- Pretty-print integral as hex
showHex :: (Integral a, Show a) => a -> String
showHex =
flip N.showHex ""
-- Pretty-print byte string as hex
showHexBS :: BS.ByteString -> String
showHexBS =
concatMap (flip N.showHex " ") . BS.unpack
-- Write a string (with a newline character) to standard output in the emulator
emuPutStrLn :: String -> Emulator ()
emuPutStrLn =
lift . putStrLn
-- Calculate code length
codeLength :: Num a => BS.ByteString -> a
codeLength =
fromIntegral . BS.length
-- Callback for tracing instructions
hookCode :: CodeHook ()
hookCode uc addr size _ = do
runEmulator $ do
emuPutStrLn $ "Tracing instruction at 0x" ++ showHex addr ++
", instruction size = 0x" ++ (maybe "0" showHex size)
eip <- regRead uc X86.Eip
tmp <- memRead uc addr (maybe 0 id size)
emuPutStrLn $ "*** EIP = " ++ showHex eip ++ " ***: " ++ showHexBS tmp
return ()
-- Callback for handling interrupts
-- ref: http://syscalls.kernelgrok.com
hookIntr :: InterruptHook ()
hookIntr uc intno _
| intno == 0x80 = do
runEmulator $ do
eax <- regRead uc X86.Eax
eip <- regRead uc X86.Eip
case eax of
-- sys_exit
1 -> do
emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", SYS_EXIT. quit!\n"
stop uc
-- sys_write
4 -> do
-- ECX = buffer address
ecx <- regRead uc X86.Ecx
-- EDX = buffer size
edx <- regRead uc X86.Edx
-- Read the buffer in
buffer <- memRead uc (fromIntegral ecx) (fromIntegral edx)
err <- errno uc
if err == ErrOk then
emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", SYS_WRITE. buffer = 0x" ++
showHex ecx ++ ", size = " ++
show edx ++ ", content = " ++
showHexBS buffer
else
emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", SYS_WRITE. buffer = 0x" ++
showHex ecx ++ ", size = " ++ show edx ++
" (cannot get content)"
_ -> emuPutStrLn $ ">>> 0x" ++ showHex eip ++
": interrupt 0x" ++ showHex intno ++
", EAX = 0x" ++ showHex eax
return ()
| otherwise = return ()
testI386 :: IO ()
testI386 = do
result <- runEmulator $ do
emuPutStrLn "Emulate i386 code"
-- Initialize emulator in X86-32bit mode
uc <- open ArchX86 [Mode32]
-- Map 2MB memory for this emulation
memMap uc address (2 * 1024 * 1024) [ProtAll]
-- Write machine code to be emulated to memory
memWrite uc address x86Code32Self
-- Initialize machine registers
regWrite uc X86.Esp (fromIntegral address + 0x200000)
-- Tracing all instructions by having @begin > @end
codeHookAdd uc hookCode () 1 0
-- Handle interrupt ourself
interruptHookAdd uc hookIntr () 1 0
emuPutStrLn "\n>>> Start tracing this Linux code"
-- Emulate machine code in infinite time
let codeLen = codeLength x86Code32Self
start uc address (address + codeLen) Nothing Nothing
case result of
Right _ -> putStrLn "\n>>> Emulation done."
Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++
strerror err
main :: IO ()
main = do
progName <- getProgName
args <- getArgs
case args of
["-32"] -> testI386
_ -> putStrLn $ "Syntax: " ++ progName ++ " <-32|-64>"