mirror of
https://github.com/yuzu-emu/unicorn
synced 2024-11-24 18:38:29 +00:00
74aaf3b321
These Haskell bindings make large use of c2hs to generate much of the code, so Unicorn's const_generator is not used. The emulator is based on the Either monad transformer. The IO monad is used to run the underlying Unicorn library, while the Either monad is used to handle errors. Instructions on how to build the bindings are located in bindings/haskell/README.TXT. The same samples found in samples/ can be found in bindings/haskell/samples. They should produce the same output, with slight differences in their error handling and messaging.
153 lines
5.5 KiB
Haskell
153 lines
5.5 KiB
Haskell
-- 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>"
|