1-- Sample code to trace code with Linux code with syscall 2 3import Unicorn 4import Unicorn.Hook 5import qualified Unicorn.CPU.X86 as X86 6 7import Control.Monad.Trans.Class (lift) 8import qualified Data.ByteString as BS 9import Data.Word 10import qualified Numeric as N (showHex) 11import System.Environment 12 13-- Code to be emulated 14x86Code32 :: BS.ByteString 15x86Code32 = BS.pack [0xeb, 0x19, 0x31, 0xc0, 0x31, 0xdb, 0x31, 0xd2, 0x31, 16 0xc9, 0xb0, 0x04, 0xb3, 0x01, 0x59, 0xb2, 0x05, 0xcd, 17 0x80, 0x31, 0xc0, 0xb0, 0x01, 0x31, 0xdb, 0xcd, 0x80, 18 0xe8, 0xe2, 0xff, 0xff, 0xff, 0x68, 0x65, 0x6c, 0x6c, 19 0x6f] 20 21x86Code32Self :: BS.ByteString 22x86Code32Self = BS.pack [0xeb, 0x1c, 0x5a, 0x89, 0xd6, 0x8b, 0x02, 0x66, 0x3d, 23 0xca, 0x7d, 0x75, 0x06, 0x66, 0x05, 0x03, 0x03, 0x89, 24 0x02, 0xfe, 0xc2, 0x3d, 0x41, 0x41, 0x41, 0x41, 0x75, 25 0xe9, 0xff, 0xe6, 0xe8, 0xdf, 0xff, 0xff, 0xff, 0x31, 26 0xd2, 0x6a, 0x0b, 0x58, 0x99, 0x52, 0x68, 0x2f, 0x2f, 27 0x73, 0x68, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3, 28 0x52, 0x53, 0x89, 0xe1, 0xca, 0x7d, 0x41, 0x41, 0x41, 29 0x41, 0x41, 0x41, 0x41, 0x41] 30 31-- Memory address where emulation starts 32address :: Word64 33address = 0x1000000 34 35-- Pretty-print integral as hex 36showHex :: (Integral a, Show a) => a -> String 37showHex = 38 flip N.showHex "" 39 40-- Pretty-print byte string as hex 41showHexBS :: BS.ByteString -> String 42showHexBS = 43 concatMap (flip N.showHex " ") . BS.unpack 44 45-- Write a string (with a newline character) to standard output in the emulator 46emuPutStrLn :: String -> Emulator () 47emuPutStrLn = 48 lift . putStrLn 49 50-- Calculate code length 51codeLength :: Num a => BS.ByteString -> a 52codeLength = 53 fromIntegral . BS.length 54 55-- Callback for tracing instructions 56hookCode :: CodeHook () 57hookCode uc addr size _ = do 58 runEmulator $ do 59 emuPutStrLn $ "Tracing instruction at 0x" ++ showHex addr ++ 60 ", instruction size = 0x" ++ (maybe "0" showHex size) 61 62 eip <- regRead uc X86.Eip 63 tmp <- memRead uc addr (maybe 0 id size) 64 65 emuPutStrLn $ "*** EIP = " ++ showHex eip ++ " ***: " ++ showHexBS tmp 66 return () 67 68-- Callback for handling interrupts 69-- ref: http://syscalls.kernelgrok.com 70hookIntr :: InterruptHook () 71hookIntr uc intno _ 72 | intno == 0x80 = do 73 runEmulator $ do 74 eax <- regRead uc X86.Eax 75 eip <- regRead uc X86.Eip 76 77 case eax of 78 -- sys_exit 79 1 -> do 80 emuPutStrLn $ ">>> 0x" ++ showHex eip ++ 81 ": interrupt 0x" ++ showHex intno ++ 82 ", SYS_EXIT. quit!\n" 83 stop uc 84 -- sys_write 85 4 -> do 86 -- ECX = buffer address 87 ecx <- regRead uc X86.Ecx 88 89 -- EDX = buffer size 90 edx <- regRead uc X86.Edx 91 92 -- Read the buffer in 93 buffer <- memRead uc (fromIntegral ecx) (fromIntegral edx) 94 err <- errno uc 95 if err == ErrOk then 96 emuPutStrLn $ ">>> 0x" ++ showHex eip ++ 97 ": interrupt 0x" ++ showHex intno ++ 98 ", SYS_WRITE. buffer = 0x" ++ 99 showHex ecx ++ ", size = " ++ 100 show edx ++ ", content = " ++ 101 showHexBS buffer 102 else 103 emuPutStrLn $ ">>> 0x" ++ showHex eip ++ 104 ": interrupt 0x" ++ showHex intno ++ 105 ", SYS_WRITE. buffer = 0x" ++ 106 showHex ecx ++ ", size = " ++ show edx ++ 107 " (cannot get content)" 108 _ -> emuPutStrLn $ ">>> 0x" ++ showHex eip ++ 109 ": interrupt 0x" ++ showHex intno ++ 110 ", EAX = 0x" ++ showHex eax 111 return () 112 | otherwise = return () 113 114testI386 :: IO () 115testI386 = do 116 result <- runEmulator $ do 117 emuPutStrLn "Emulate i386 code" 118 119 -- Initialize emulator in X86-32bit mode 120 uc <- open ArchX86 [Mode32] 121 122 -- Map 2MB memory for this emulation 123 memMap uc address (2 * 1024 * 1024) [ProtAll] 124 125 -- Write machine code to be emulated to memory 126 memWrite uc address x86Code32Self 127 128 -- Initialize machine registers 129 regWrite uc X86.Esp (fromIntegral address + 0x200000) 130 131 -- Tracing all instructions by having @begin > @end 132 codeHookAdd uc hookCode () 1 0 133 134 -- Handle interrupt ourself 135 interruptHookAdd uc hookIntr () 1 0 136 137 emuPutStrLn "\n>>> Start tracing this Linux code" 138 139 -- Emulate machine code in infinite time 140 let codeLen = codeLength x86Code32Self 141 start uc address (address + codeLen) Nothing Nothing 142 case result of 143 Right _ -> putStrLn "\n>>> Emulation done." 144 Left err -> putStrLn $ "Failed with error " ++ show err ++ ": " ++ 145 strerror err 146 147main :: IO () 148main = do 149 progName <- getProgName 150 args <- getArgs 151 case args of 152 ["-32"] -> testI386 153 _ -> putStrLn $ "Syntax: " ++ progName ++ " <-32|-64>" 154