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