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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE StandaloneDeriving #-}
module Main where
import Control.Arrow
import Data.Maybe
import Data.Binary.Get (runGet)
import qualified Data.ByteString as BS
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as LZ
import qualified Data.ByteString.Lazy.Char8 as L8
import Data.IORef
import Data.List
import Data.Word
import Debug.Trace
import Text.Printf
import Text.Show.Pretty as PP
import "network-house" Net.Packet as House
import qualified "network-house" Net.IPv4 as IP4
import qualified "network-house" Net.IPv6 as IP6
import "network-house" Net.PacketParsing as House
import "network-house" Net.PortNumber as TCP
import "network-house" Net.ICMP as ICMP
import "network-house" Net.TCP as TCP
import "network-house" Net.UDP as UDP
import "pcap" Network.Pcap
import qualified Data.Serialize as S
import qualified Network.Socket as HS
import Control.Applicative
import Control.Monad
import System.Environment
import Control.Exception
import Hans.Checksum
import WifiHeader as Wifi
import Crypto.Tox
import Network.Tox.DHT.Transport as Tox
import Data.BEncode as BE
import Data.BEncode.Pretty
-- import Data.IKE.Message
import Text.XXD
-- traceM string = trace string $ return ()
-- IPv4 : rfc 791
-- IPv6 : rfc 2460 (8200)
-- TCP : rfc 793
-- UDP : rfc 768
-- ICMP : rfc 792
-- ICMPv6 : rfc 4443
-- ARP : rfc 826
-- SNAP : rfc 1042
deriving instance Show IP6.Word4
deriving instance Show IP6.Word20
deriving instance Show IP6.Addr
deriving instance Show a => Show (IP6.Packet a)
bs2chunk :: BS.ByteString -> UArray Int Word8
bs2chunk bs = listArray (0,subtract 33 $ BS.length bs) $ drop 32 $ BS.unpack bs
-- 23 + 9 = 32
mkchunk :: Int -> BS.ByteString -> Maybe (UArray Int Word8)
mkchunk n bs
| (BS.length bs < n) = Nothing
| otherwise = Just $ listArray (0,subtract (n+1) $ BS.length bs) $ drop n $ BS.unpack bs
{-
hex :: BS.ByteString -> String
hex = concatMap (printf "%02x") . B.unpack
hexlines :: BS.ByteString -> [String]
hexlines bs = ss
where xs = zip [0..] $ hex bs
ls = groupBy (\(n,_) (m,_)-> n `div` 32 == m `div` 32) xs
ss = map (map snd) ls
-}
udpPacket :: PktHdr -> BS.ByteString -> Maybe (IO ())
udpPacket hdr buf = do
chunk <- mkchunk 32 buf
udp <- House.doParse $ House.toInPack chunk
-- traceM $ "got udp: " ++ show udp
-- traceM $ "got udp content: " ++ show (content udp)
let plen = House.len $ UDP.content udp
bs <- fmap BS.pack . parseInPacket (bytes plen) $ UDP.content udp
-- traceM $ "Got bs " ++ show bs
(checksum,blob) <- Just $ BS.splitAt 2 bs -- extra 2 bytes in pcap capture, i'm assuming its a checksum
-- (first4,truncated) = BS.splitAt 4 blob
-- -- First 4 bytes being zero is how we distinguish between
-- -- ESP and IKEv2 packets on port 4500.
-- dta = if destPort udp /= Port 500 && first4==BS.pack [0,0,0,0]
-- then truncated
-- else blob
Just $ do
putStrLn $ "UDP." ++ show udp
mapM_ putStrLn $ xxd2 0 blob
parseIP4 :: InPacket -> Maybe (IP4.Packet InPacket)
parseIP4 inpkt = do
ip <- House.doParse inpkt
4 <- Just $ IP4.version ip
return ip
ipPacket4 :: PktHdr -> BS.ByteString -> Maybe (IO ())
ipPacket4 hdr buf = do
chunk <- mkchunk 23 buf
ip <- parseIP4 $ House.toInPack chunk
Just $ do
putStrLn $ "-- ipPacket4 --"
putStrLn $ "IP4." ++ show ip
parseIP6 :: InPacket -> Maybe (IP6.Packet InPacket)
parseIP6 inpkt = do
ip <- House.doParse inpkt
IP6.Word4 6 <- Just $ IP6.version ip
return ip
ipPacket6 :: PktHdr -> BS.ByteString -> Maybe (IO ())
ipPacket6 hdr buf = do
chunk <- mkchunk 23 buf
ip <- parseIP6 $ House.toInPack chunk
Just $ do
putStrLn $ "-- ipPacket6 --"
putStrLn $ "IP6." ++ show ip
showTruncated hdr s
| hdrCaptureLength hdr == hdrWireLength hdr
= s
| otherwise = unwords
[ s
, "(truncated"
, show $ hdrCaptureLength hdr
, "of"
, show (hdrWireLength hdr) ++ ")"
]
wifiPacket hdr buf = do
(Data,_) <- wifiTypeBits $ BS.index buf 0 -- Filter all but data packets.
guard (BS.length buf >= 28)
chunk <- mkchunk 0 buf
wifi <- House.doParse $ House.toInPack chunk
Just $ do
-- putStrLn $ "-- wifiPacket --"
-- putStrLn $ "Wifi." ++ show wifi
let parseip = do
Wifi.Data <- Just $ wifiType wifi
c <- Just $ Wifi.content wifi
(Left <$> parseIP4 c) <|> (Right <$> parseIP6 c)
let mdta = parseip >>= ipPacket
forM_ mdta $ \dta -> do
let src = saddr (left fst $ right fst $ ipAddrs dta)
(fromMaybe 0 $ fst <$> ipPorts dta)
dst = saddr (left snd $ right snd $ ipAddrs dta)
(fromMaybe 0 $ snd <$> ipPorts dta)
srcmac = Wifi.srcAddr (Wifi.addresses wifi)
dstmac = Wifi.dstAddr (Wifi.addresses wifi)
r = [ maybe "" (("bssid "++) . show) $ Wifi.bssid (Wifi.addresses wifi)
, "<-- " ++ show src ++ " (" ++ show srcmac ++ ")"
, "--> " ++ show dst ++ " (" ++ show dstmac ++ ")"
, showTruncated hdr (ipProtocol dta)
]
case ipPayload dta of
Nothing -> mapM_ putStrLn r
Just bs -> mapM_ putStrLn $ sideBySide (xxd2 0 bs) r
putStrLn ""
sideBySide (x:xs) (y:ys) = (take 72 (x ++ repeat ' ') ++ y) : sideBySide xs ys
sideBySide [] (y:ys) = (replicate 72 ' ' ++ y) : sideBySide [] ys
sideBySide xs [] = xs
saddr (Left ip4) port = HS.SockAddrInet (fromIntegral port) addr
where IP4.Addr a b c d = ip4
addr = fromIntegral a
+ fromIntegral b * 256
+ fromIntegral c * 65536
+ fromIntegral d * 16777216
saddr (Right ip6) port = HS.SockAddrInet6 (fromIntegral port) 0 addr 0
where IP6.Addr ah al bh bl ch cl dh dl = ip6
addr = ( fromIntegral ah * 65536 + fromIntegral al
, fromIntegral bh * 65536 + fromIntegral bl
, fromIntegral ch * 65536 + fromIntegral cl
, fromIntegral dh * 65536 + fromIntegral dl
)
data IPData = IPData
{ ipPayload :: Maybe BS.ByteString
, ipProtocol :: String
, ipPorts :: Maybe (Word16,Word16)
, ipAddrs :: Either (IP4.Addr,IP4.Addr)
(IP6.Addr,IP6.Addr)
}
ip4addrs pkt = (IP4.source pkt, IP4.dest pkt)
ip6addrs pkt = (IP6.source pkt, IP6.dest pkt)
ipPacket ip = do
let udp = do
IP4.UDP <- Just $ either IP4.protocol IP6.next_header ip
pkt <- House.doParse $ either IP4.content IP6.content ip
let ulen = House.len $ UDP.content pkt
mbs = fmap BS.pack . parseInPacket (bytes ulen) $ UDP.content pkt
let UDP.Port sport = UDP.sourcePort pkt
UDP.Port dport = UDP.destPort pkt
return IPData
{ ipPayload = mbs
, ipProtocol = "UDP"
, ipPorts = Just (sport,dport)
, ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
}
tcp = do
IP4.TCP <- Just $ either IP4.protocol IP6.next_header ip
pkt <- House.doParse $ either IP4.content IP6.content ip
let ulen = House.len $ TCP.content pkt
mbs = fmap BS.pack . parseInPacket (bytes ulen) $ TCP.content pkt
let TCP.Port sport = TCP.sourcePort pkt
TCP.Port dport = TCP.destPort pkt
return IPData
{ ipPayload = mbs
, ipProtocol = "TCP"
, ipPorts = Just (sport,dport)
, ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
}
icmp = do
IP4.ICMP <- Just $ either IP4.protocol IP6.next_header ip
pkt <- House.doParse $ either IP4.content IP6.content ip
let ping = do
EchoRequest msg <- Just pkt
return ("Ping", echoData msg)
pong = do
EchoReply msg <- Just pkt
return ("Pong", echoData msg)
other = do
Other { type_ = typ, content = chunk } <- Just pkt
return (show typ, chunk)
(typ,chunk) <- ping <|> pong <|> other
let p = House.toInPack chunk
ulen = House.len p
mbs = fmap BS.pack . parseInPacket (bytes ulen) $ p
return IPData
{ ipPayload = mbs
, ipProtocol = "ICMP "++typ
, ipPorts = Nothing
, ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
}
unknown = do
IP4.Unknown proto <- Just $ either IP4.protocol IP6.next_header ip
pkt <- Just $ either IP4.content IP6.content ip
let ulen = House.len pkt
mbs = fmap BS.pack . parseInPacket (bytes ulen) $ pkt
return IPData
{ ipPayload = mbs
, ipProtocol = "proto "++show proto
, ipPorts = Nothing
, ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
}
udp <|> icmp <|> tcp <|> unknown
parsePacket :: IORef Int -> PktHdr -> BS.ByteString -> IO ()
parsePacket cnt hdr buf = do
sequence_ $ wifiPacket hdr buf
modifyIORef' cnt (+1)
sampleHdr :: PktHdr
sampleHdr = PktHdr {hdrSeconds = 1511875620, hdrUseconds = 464364, hdrCaptureLength = 347, hdrWireLength = 347}
sampleBytes :: BS.ByteString
sampleBytes = "\136\n0\NULT\242\SOH\243\ENQ\153\NUL\rg\144\162\a\244>\157\ETXV\213\240 \ETX\NUL\170\170\ETX\NUL\NUL\NUL\b\NULE\NUL\SOH9\191\170@\NUL@\ACK\174\176\192,D\ENQ\n\235\188G\NULP\202\220R\199)\154R\199+\247\128\DC1\b\NUL\204\196\NUL\NUL\SOH\SOH\b\n\NUL\243\150\&6\NUL\243\150\&1HTTP/1.1 302 Found \r\nLocation: https://wifilogin.xfinity.com/start.php?h=kozHorIQv1nb851zZcMfnWdD4wtjyIymVSIwanl8KXaQLMHBm0oJhcubjREusjojI%2B8f0UNFoG1p2lD33OXGlpw3uvJ5SeKeX37jANRRu%2FPZOwWw%2F70foAx70wBVohtmhWksbshKhR4lAWuv2FAB0%2BPVkpfSjNIE0HwQO%2BnRZR8%3D\r\n\r\n"
sampleHdr2 = PktHdr {hdrSeconds = 1511841845, hdrUseconds = 681965, hdrCaptureLength = 10, hdrWireLength = 10}
sampleBytes2 =
[ 0xc4, 0x00, 0x8a, 0x00
, 0x8c, 0xf5, 0xa3, 0x12, 0xdf, 0x80 ]
main = do
args <- getArgs
forM_ args $ \fname -> do
cnt <- newIORef 0
parsePacket cnt sampleHdr sampleBytes
pcap <- openOffline fname -- "packets.pcap"
linktype <- datalink pcap
putStrLn $ unwords ["Opened:",fname,"Link:",show linktype]
-- Opened: opnwep.dat-08.cap Link: DLT_IEEE802_11
-- Opened: packets.pcap Link: DLT_EN10MB
-- DLT_IEEE802_11
-- DLT_IEEE802_11_RADIO
-- DLT_IEEE802_11_RADIO_AVS
loopResult <- loopBS pcap (-1) $ parsePacket cnt
pktcnt <- readIORef cnt
putStrLn $ "read "++show pktcnt ++" packets from " ++ fname ++ "."
|