summaryrefslogtreecommitdiff
path: root/readpackets.hs
diff options
context:
space:
mode:
Diffstat (limited to 'readpackets.hs')
-rw-r--r--readpackets.hs286
1 files changed, 249 insertions, 37 deletions
diff --git a/readpackets.hs b/readpackets.hs
index f02df538..690aa91e 100644
--- a/readpackets.hs
+++ b/readpackets.hs
@@ -1,6 +1,9 @@
1{-# LANGUAGE PackageImports #-} 1{-# LANGUAGE PackageImports #-}
2{-# LANGUAGE StandaloneDeriving #-}
2module Main where 3module Main where
3 4
5import Control.Arrow
6import Data.Maybe
4import Data.Binary.Get (runGet) 7import Data.Binary.Get (runGet)
5import qualified Data.ByteString as BS 8import qualified Data.ByteString as BS
6import qualified Data.ByteString as B 9import qualified Data.ByteString as B
@@ -8,30 +11,63 @@ import qualified Data.ByteString.Lazy as LZ
8import qualified Data.ByteString.Lazy.Char8 as L8 11import qualified Data.ByteString.Lazy.Char8 as L8
9import Data.IORef 12import Data.IORef
10import Data.List 13import Data.List
14import Data.Word
11import Debug.Trace 15import Debug.Trace
12import Text.Printf 16import Text.Printf
13import Text.Show.Pretty as PP 17import Text.Show.Pretty as PP
14import "network-house" Net.Packet 18import "network-house" Net.Packet as House
15import qualified "network-house" Net.IPv4 as IP4 19import qualified "network-house" Net.IPv4 as IP4
16import qualified "network-house" Net.IPv6 as IP6 20import qualified "network-house" Net.IPv6 as IP6
17import "network-house" Net.PacketParsing 21import "network-house" Net.PacketParsing as House
22import "network-house" Net.PortNumber as TCP
23import "network-house" Net.ICMP as ICMP
24import "network-house" Net.TCP as TCP
18import "network-house" Net.UDP as UDP 25import "network-house" Net.UDP as UDP
19import "pcap" Network.Pcap 26import "pcap" Network.Pcap
20import qualified Data.Serialize as S 27import qualified Data.Serialize as S
21import qualified Network.Socket as HS 28import qualified Network.Socket as HS
22import Control.Applicative 29import Control.Applicative
30import Control.Monad
31import System.Environment
32import Control.Exception
33
34import Hans.Checksum
35import WifiHeader as Wifi
23 36
24import Crypto.Tox 37import Crypto.Tox
25import Network.Tox.DHT.Transport as Tox 38import Network.Tox.DHT.Transport as Tox
26import Data.BEncode as BE 39import Data.BEncode as BE
27import Data.BEncode.Pretty 40import Data.BEncode.Pretty
28-- import Data.IKE.Message 41-- import Data.IKE.Message
42import Text.XXD
29 43
30-- traceM string = trace string $ return () 44-- traceM string = trace string $ return ()
31 45
46
47-- IPv4 : rfc 791
48-- IPv6 : rfc 2460 (8200)
49-- TCP : rfc 793
50-- UDP : rfc 768
51-- ICMP : rfc 792
52-- ICMPv6 : rfc 4443
53-- ARP : rfc 826
54-- SNAP : rfc 1042
55
56deriving instance Show IP6.Word4
57deriving instance Show IP6.Word20
58deriving instance Show IP6.Addr
59deriving instance Show a => Show (IP6.Packet a)
60
32bs2chunk :: BS.ByteString -> UArray Int Word8 61bs2chunk :: BS.ByteString -> UArray Int Word8
33bs2chunk bs = listArray (0,subtract 33 $ BS.length bs) $ drop 32 $ BS.unpack bs 62bs2chunk bs = listArray (0,subtract 33 $ BS.length bs) $ drop 32 $ BS.unpack bs
63-- 23 + 9 = 32
64
65mkchunk :: Int -> BS.ByteString -> Maybe (UArray Int Word8)
66mkchunk n bs
67 | (BS.length bs < n) = Nothing
68 | otherwise = Just $ listArray (0,subtract (n+1) $ BS.length bs) $ drop n $ BS.unpack bs
34 69
70{-
35hex :: BS.ByteString -> String 71hex :: BS.ByteString -> String
36hex = concatMap (printf "%02x") . B.unpack 72hex = concatMap (printf "%02x") . B.unpack
37 73
@@ -40,43 +76,219 @@ hexlines bs = ss
40 where xs = zip [0..] $ hex bs 76 where xs = zip [0..] $ hex bs
41 ls = groupBy (\(n,_) (m,_)-> n `div` 32 == m `div` 32) xs 77 ls = groupBy (\(n,_) (m,_)-> n `div` 32 == m `div` 32) xs
42 ss = map (map snd) ls 78 ss = map (map snd) ls
79-}
80
81udpPacket :: PktHdr -> BS.ByteString -> Maybe (IO ())
82udpPacket hdr buf = do
83 chunk <- mkchunk 32 buf
84 udp <- House.doParse $ House.toInPack chunk
85 -- traceM $ "got udp: " ++ show udp
86 -- traceM $ "got udp content: " ++ show (content udp)
87 let plen = House.len $ UDP.content udp
88 bs <- fmap BS.pack . parseInPacket (bytes plen) $ UDP.content udp
89 -- traceM $ "Got bs " ++ show bs
90 (checksum,blob) <- Just $ BS.splitAt 2 bs -- extra 2 bytes in pcap capture, i'm assuming its a checksum
91 -- (first4,truncated) = BS.splitAt 4 blob
92 -- -- First 4 bytes being zero is how we distinguish between
93 -- -- ESP and IKEv2 packets on port 4500.
94 -- dta = if destPort udp /= Port 500 && first4==BS.pack [0,0,0,0]
95 -- then truncated
96 -- else blob
97 Just $ do
98 putStrLn $ "UDP." ++ show udp
99 mapM_ putStrLn $ xxd2 0 blob
100
101parseIP4 :: InPacket -> Maybe (IP4.Packet InPacket)
102parseIP4 inpkt = do
103 ip <- House.doParse inpkt
104 4 <- Just $ IP4.version ip
105 return ip
106
107ipPacket4 :: PktHdr -> BS.ByteString -> Maybe (IO ())
108ipPacket4 hdr buf = do
109 chunk <- mkchunk 23 buf
110 ip <- parseIP4 $ House.toInPack chunk
111 Just $ do
112 putStrLn $ "-- ipPacket4 --"
113 putStrLn $ "IP4." ++ show ip
114
115parseIP6 :: InPacket -> Maybe (IP6.Packet InPacket)
116parseIP6 inpkt = do
117 ip <- House.doParse inpkt
118 IP6.Word4 6 <- Just $ IP6.version ip
119 return ip
120
121ipPacket6 :: PktHdr -> BS.ByteString -> Maybe (IO ())
122ipPacket6 hdr buf = do
123 chunk <- mkchunk 23 buf
124 ip <- parseIP6 $ House.toInPack chunk
125 Just $ do
126 putStrLn $ "-- ipPacket6 --"
127 putStrLn $ "IP6." ++ show ip
128
129showTruncated hdr s
130 | hdrCaptureLength hdr == hdrWireLength hdr
131 = s
132 | otherwise = unwords
133 [ s
134 , "(truncated"
135 , show $ hdrCaptureLength hdr
136 , "of"
137 , show (hdrWireLength hdr) ++ ")"
138 ]
139
140wifiPacket hdr buf = do
141 (Data,_) <- wifiTypeBits $ BS.index buf 0 -- Filter all but data packets.
142 guard (BS.length buf >= 28)
143 chunk <- mkchunk 0 buf
144 wifi <- House.doParse $ House.toInPack chunk
145 Just $ do
146 -- putStrLn $ "-- wifiPacket --"
147 -- putStrLn $ "Wifi." ++ show wifi
148 let parseip = do
149 Wifi.Data <- Just $ wifiType wifi
150 c <- Just $ Wifi.content wifi
151 (Left <$> parseIP4 c) <|> (Right <$> parseIP6 c)
152 let mdta = parseip >>= ipPacket
153 forM_ mdta $ \dta -> do
154 let src = saddr (left fst $ right fst $ ipAddrs dta)
155 (fromMaybe 0 $ fst <$> ipPorts dta)
156 dst = saddr (left snd $ right snd $ ipAddrs dta)
157 (fromMaybe 0 $ snd <$> ipPorts dta)
158 srcmac = Wifi.srcAddr (Wifi.addresses wifi)
159 dstmac = Wifi.dstAddr (Wifi.addresses wifi)
160 r = [ maybe "" (("bssid "++) . show) $ Wifi.bssid (Wifi.addresses wifi)
161 , "<-- " ++ show src ++ " (" ++ show srcmac ++ ")"
162 , "--> " ++ show dst ++ " (" ++ show dstmac ++ ")"
163 , showTruncated hdr (ipProtocol dta)
164 ]
165 case ipPayload dta of
166 Nothing -> mapM_ putStrLn r
167 Just bs -> mapM_ putStrLn $ sideBySide (xxd2 0 bs) r
168 putStrLn ""
169
170sideBySide (x:xs) (y:ys) = (take 72 (x ++ repeat ' ') ++ y) : sideBySide xs ys
171sideBySide [] (y:ys) = (replicate 72 ' ' ++ y) : sideBySide [] ys
172sideBySide xs [] = xs
173
174saddr (Left ip4) port = HS.SockAddrInet (fromIntegral port) addr
175 where IP4.Addr a b c d = ip4
176 addr = fromIntegral a
177 + fromIntegral b * 256
178 + fromIntegral c * 65536
179 + fromIntegral d * 16777216
180saddr (Right ip6) port = HS.SockAddrInet6 (fromIntegral port) 0 addr 0
181 where IP6.Addr ah al bh bl ch cl dh dl = ip6
182 addr = ( fromIntegral ah * 65536 + fromIntegral al
183 , fromIntegral bh * 65536 + fromIntegral bl
184 , fromIntegral ch * 65536 + fromIntegral cl
185 , fromIntegral dh * 65536 + fromIntegral dl
186 )
187
188data IPData = IPData
189 { ipPayload :: Maybe BS.ByteString
190 , ipProtocol :: String
191 , ipPorts :: Maybe (Word16,Word16)
192 , ipAddrs :: Either (IP4.Addr,IP4.Addr)
193 (IP6.Addr,IP6.Addr)
194 }
195
196ip4addrs pkt = (IP4.source pkt, IP4.dest pkt)
197
198ip6addrs pkt = (IP6.source pkt, IP6.dest pkt)
199
200ipPacket ip = do
201 let udp = do
202 IP4.UDP <- Just $ either IP4.protocol IP6.next_header ip
203 pkt <- House.doParse $ either IP4.content IP6.content ip
204 let ulen = House.len $ UDP.content pkt
205 mbs = fmap BS.pack . parseInPacket (bytes ulen) $ UDP.content pkt
206 let UDP.Port sport = UDP.sourcePort pkt
207 UDP.Port dport = UDP.destPort pkt
208 return IPData
209 { ipPayload = mbs
210 , ipProtocol = "UDP"
211 , ipPorts = Just (sport,dport)
212 , ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
213 }
214 tcp = do
215 IP4.TCP <- Just $ either IP4.protocol IP6.next_header ip
216 pkt <- House.doParse $ either IP4.content IP6.content ip
217 let ulen = House.len $ TCP.content pkt
218 mbs = fmap BS.pack . parseInPacket (bytes ulen) $ TCP.content pkt
219 let TCP.Port sport = TCP.sourcePort pkt
220 TCP.Port dport = TCP.destPort pkt
221 return IPData
222 { ipPayload = mbs
223 , ipProtocol = "TCP"
224 , ipPorts = Just (sport,dport)
225 , ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
226 }
227 icmp = do
228 IP4.ICMP <- Just $ either IP4.protocol IP6.next_header ip
229 pkt <- House.doParse $ either IP4.content IP6.content ip
230 let ping = do
231 EchoRequest msg <- Just pkt
232 return ("Ping", echoData msg)
233 pong = do
234 EchoReply msg <- Just pkt
235 return ("Pong", echoData msg)
236 other = do
237 Other { type_ = typ, content = chunk } <- Just pkt
238 return (show typ, chunk)
239 (typ,chunk) <- ping <|> pong <|> other
240 let p = House.toInPack chunk
241 ulen = House.len p
242 mbs = fmap BS.pack . parseInPacket (bytes ulen) $ p
243 return IPData
244 { ipPayload = mbs
245 , ipProtocol = "ICMP "++typ
246 , ipPorts = Nothing
247 , ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
248 }
249 unknown = do
250 IP4.Unknown proto <- Just $ either IP4.protocol IP6.next_header ip
251 pkt <- Just $ either IP4.content IP6.content ip
252 let ulen = House.len pkt
253 mbs = fmap BS.pack . parseInPacket (bytes ulen) $ pkt
254 return IPData
255 { ipPayload = mbs
256 , ipProtocol = "proto "++show proto
257 , ipPorts = Nothing
258 , ipAddrs = either (Left . ip4addrs) (Right . ip6addrs) ip
259 }
260 udp <|> icmp <|> tcp <|> unknown
261
43 262
44parsePacket :: IORef Int -> PktHdr -> BS.ByteString -> IO () 263parsePacket :: IORef Int -> PktHdr -> BS.ByteString -> IO ()
45parsePacket cnt hdr buf = do 264parsePacket cnt hdr buf = do
46 print hdr 265 sequence_ $ wifiPacket hdr buf
47 let -- mb :: Maybe (BS.ByteString, Message Encrypted)
48 mb = do
49 udp <- doParse $ toInPack $ bs2chunk buf
50 -- traceM $ "got udp: " ++ show udp
51 -- traceM $ "got udp content: " ++ show (content udp)
52 let plen = Net.Packet.len $ content udp
53 bs <- fmap BS.pack . parseInPacket (bytes plen) $ content udp
54 -- traceM $ "Got bs " ++ show bs
55 let (checksum,blob) = BS.splitAt 2 bs -- extra 2 bytes in pcap capture, i'm assuming its a checksum
56-- (first4,truncated) = BS.splitAt 4 blob
57-- -- First 4 bytes being zero is how we distinguish between
58-- -- ESP and IKEv2 packets on port 4500.
59-- dta = if destPort udp /= Port 500 && first4==BS.pack [0,0,0,0]
60-- then truncated
61-- else blob
62 dta = blob
63 let d = BE.decode {- runGet getMessage $ LZ.fromStrict -} dta
64 saddr = HS.SockAddrInet 0 0 -- TODO
65 -- e = S.decode dta :: Either String (DHTMessage Encrypted8)
66 e = case Tox.parseDHTAddr (dta,saddr) :: Either (DHTMessage Encrypted8,NodeInfo) (BS.ByteString,HS.SockAddr) of
67 Left toxpkt -> Right toxpkt
68 Right _ -> Left "tox parse fail"
69 return $ (udp, bs, fmap Left d <|> fmap Right e)
70 flip (maybe $ return ()) mb $ \(udp, bs,m) -> do
71 putStrLn $ show udp
72 mapM_ putStrLn $ hexlines bs
73 -- putStrLn $ PP.ppShow m
74 either putStrLn (L8.putStrLn . either showBEncode (L8.pack . show)) m
75 modifyIORef' cnt (+1) 266 modifyIORef' cnt (+1)
76 267
268sampleHdr :: PktHdr
269sampleHdr = PktHdr {hdrSeconds = 1511875620, hdrUseconds = 464364, hdrCaptureLength = 347, hdrWireLength = 347}
270
271sampleBytes :: BS.ByteString
272sampleBytes = "\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"
273
274sampleHdr2 = PktHdr {hdrSeconds = 1511841845, hdrUseconds = 681965, hdrCaptureLength = 10, hdrWireLength = 10}
275sampleBytes2 =
276 [ 0xc4, 0x00, 0x8a, 0x00
277 , 0x8c, 0xf5, 0xa3, 0x12, 0xdf, 0x80 ]
278
77main = do 279main = do
78 cnt <- newIORef 0 280 args <- getArgs
79 pcap <- openOffline "packets.pcap" 281 forM_ args $ \fname -> do
80 loopResult <- loopBS pcap (-1) $ parsePacket cnt 282 cnt <- newIORef 0
81 pktcnt <- readIORef cnt 283 parsePacket cnt sampleHdr sampleBytes
82 putStrLn $ "read "++show pktcnt ++" packets." 284 pcap <- openOffline fname -- "packets.pcap"
285 linktype <- datalink pcap
286 putStrLn $ unwords ["Opened:",fname,"Link:",show linktype]
287 -- Opened: opnwep.dat-08.cap Link: DLT_IEEE802_11
288 -- Opened: packets.pcap Link: DLT_EN10MB
289 -- DLT_IEEE802_11
290 -- DLT_IEEE802_11_RADIO
291 -- DLT_IEEE802_11_RADIO_AVS
292 loopResult <- loopBS pcap (-1) $ parsePacket cnt
293 pktcnt <- readIORef cnt
294 putStrLn $ "read "++show pktcnt ++" packets from " ++ fname ++ "."