-- 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>"