File: Shellcode.hs

package info (click to toggle)
unicorn-engine 2.1.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 23,912 kB
  • sloc: ansic: 379,830; python: 9,213; sh: 9,011; java: 8,609; ruby: 4,241; pascal: 1,805; haskell: 1,379; xml: 490; cs: 424; makefile: 348; cpp: 298; asm: 64
file content (153 lines) | stat: -rw-r--r-- 5,606 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
-- 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>"