netarch

Network Archaeology library for Python
git clone https://git.woozle.org/neale/netarch.git

commit
6a1cbae
parent
8cd7cb0
author
Neale Pickett
date
2021-07-02 17:18:59 -0600 MDT
Add xmodem example
7 files changed,  +69, -17
M README.md
+5, -2
 1@@ -11,6 +11,9 @@ and room to grow these explorations into full-blown decoders.
 2 Get going
 3 =========
 4 
 5-Documentation sucks, sorry.
 6-The way we go about things is to copy `dumbdecode.py` to a new file,
 7+Documentation doesn't exist. Sorry.
 8+The way we go about things is to copy [dumbdecode](examples/dumbdecode.py) to a new file,
 9 and start hacking onto it.
10+
11+You may find the [example xmodem decoder](examples/xmodem.py) to be helpful!
12+It illustrates a fair amount of what the library provides.
R netarch/dumbdecode.py => examples/dumbdecode.py
+1, -2
 1@@ -1,7 +1,6 @@
 2 #! /usr/bin/python3
 3 
 4 import netarch
 5-import sys
 6 
 7 class DumbPacket(netarch.Packet):
 8 	def parse(self, data):
 9@@ -10,4 +9,4 @@ class DumbPacket(netarch.Packet):
10 class DumbSession(netarch.Session):
11 	Packet = DumbPacket
12 
13-netarch.main(DumbSession, sys.argv[1:])
14+netarch.main(DumbSession)
A examples/xmodem.pcap
+0, -0
A examples/xmodem.py
+45, -0
 1@@ -0,0 +1,45 @@
 2+#! /usr/bin/python3
 3+
 4+import netarch
 5+
 6+class XmodemPacket(netarch.Packet):
 7+    def parse(self, data):
 8+        datastream = netarch.Unpacker(data)
 9+        self.opcode = datastream.uint8()
10+        self.payload = datastream.buf
11+
12+    def opcode_1(self):
13+        "Xfer"
14+        datastream = netarch.Unpacker(self.payload)
15+        self["seq"] = datastream.uint8()
16+        self["~seq"] = datastream.uint8()
17+
18+        assert self["seq"] == 255 - self["~seq"]
19+        assert len(datastream.buf) == 0x81
20+
21+        self["checksum"] = datastream.uint8(pos=-1)
22+        self.payload = datastream.buf
23+
24+    def opcode_4(self):
25+        "EOT"
26+
27+    def opcode_6(self):
28+        "ACK"
29+
30+    def opcode_21(self):
31+        "Begin transmission?"
32+
33+class XmodemSession(netarch.Session):
34+    Packet = XmodemPacket
35+
36+    def process(self, packet):
37+        packet.show()
38+
39+        if packet.opcode == 21:
40+            # Open a new file for output
41+            self.out = self.open_out("data.bin")
42+        elif packet.opcode == 1:
43+            self.out.write(bytes(packet.payload))
44+
45+
46+netarch.main(XmodemSession)
M netarch/__init__.py
+5, -2
 1@@ -2,8 +2,9 @@
 2 
 3 import binascii
 4 import struct
 5+import sys
 6 from . import ip
 7-
 8+from .unpack import Unpacker
 9 
10 
11 def cstring(buf):
12@@ -180,8 +181,10 @@ def _registry(encoding):
13 
14 codecs.register(_registry)
15 
16-def main(session, pcaps):
17+def main(session, pcaps=None):
18     s = None
19+    if not pcaps:
20+        pcaps = sys.argv[1:]
21     reseq = ip.Dispatch(*pcaps)
22     for _, d in reseq:
23         srv, first, chunk = d
M netarch/unpack.py
+12, -10
 1@@ -39,7 +39,7 @@ class Unpacker:
 2         self.endian = endian
 3         self.buf = buf
 4 
 5-    def uint(self, size, endian=None):
 6+    def uint(self, size, pos=0, endian=None):
 7         endian = endian or self.endian
 8         if size not in (8, 16, 32, 64):
 9             # XXX: I'm pretty sure this can be done, but I don't want to code it up right now.
10@@ -53,20 +53,22 @@ class Unpacker:
11             r = (1, 0, 3, 2,   5, 4, 7, 6)[:noctets]
12         else:
13             raise ValueError("Unsupported byte order")
14-        pull, self.buf = self.buf[:noctets], self.buf[noctets:]
15+        if pos < 0:
16+            pos += len(self.buf)
17+        pull, self.buf = self.buf[pos:pos+noctets], self.buf[:pos] + self.buf[pos+noctets:]
18         acc = 0
19         for i in r:
20             acc = (acc << 8) | pull[i]
21         return acc
22         
23-    def uint8(self):
24-        return self.uint(8)
25-    def uint16(self, endian=None):
26-        return self.uint(16, endian)
27-    def uint32(self, endian=None):
28-        return self.uint(32, endian)
29-    def uint64(self, endian=None):
30-        return self.uint(64, endian)
31+    def uint8(self, pos=0):
32+        return self.uint(8, pos)
33+    def uint16(self, pos=0, endian=None):
34+        return self.uint(16, pos, endian)
35+    def uint32(self, pos=0, endian=None):
36+        return self.uint(32, pos, endian)
37+    def uint64(self, pos=0, endian=None):
38+        return self.uint(64, pos, endian)
39 
40 if __name__ == "__main__":
41     import doctest
M setup.py
+1, -1
1@@ -38,7 +38,7 @@ setup(
2     # For a discussion on single-sourcing the version across setup.py and the
3     # project code, see
4     # https://packaging.python.org/en/latest/single_source_version.html
5-    version='1.0.1',  # Required
6+    version='2.1.0',  # Required
7 
8     # This is a one-line description or tagline of what your project does. This
9     # corresponds to the "Summary" metadata field: