|
1 | 1 | # This file is part of Scapy |
2 | 2 | # See http://www.secdev.org/projects/scapy for more informations |
| 3 | +# Scapy is free software: you can redistribute it and/or modify |
| 4 | +# it under the terms of the GNU General Public License as published by |
| 5 | +# the Free Software Foundation, either version 2 of the License, or |
| 6 | +# any later version. |
| 7 | +# |
| 8 | +# Scapy is distributed in the hope that it will be useful, |
| 9 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | +# GNU General Public License for more details. |
| 12 | +# |
| 13 | +# You should have received a copy of the GNU General Public License |
| 14 | +# along with Scapy. If not, see <http://www.gnu.org/licenses/>. |
| 15 | + |
3 | 16 | # Copyright (C) Philippe Biondi <[email protected]> |
4 | | -# This program is published under a GPLv2 license |
5 | 17 |
|
6 | 18 | """ |
7 | 19 | Wireless LAN according to IEEE 802.11. |
8 | 20 | """ |
9 | 21 |
|
10 | 22 | from __future__ import print_function |
| 23 | +import math |
11 | 24 | import re |
12 | 25 | import struct |
13 | 26 | from zlib import crc32 |
@@ -90,27 +103,118 @@ def answers(self, other): |
90 | 103 | return self.payload.answers(other) |
91 | 104 |
|
92 | 105 |
|
| 106 | +class _RadiotapReversePadField(ReversePadField): |
| 107 | + def __init__(self, fld): |
| 108 | + self._fld = fld |
| 109 | + self._padwith = b"\x00" |
| 110 | + # Quote from https://www.radiotap.org/: |
| 111 | + # ""Radiotap requires that all fields in the radiotap header are aligned to natural boundaries. |
| 112 | + # For radiotap, that means all 8-, 16-, 32-, and 64-bit fields must begin on 8-, 16-, 32-, and 64-bit boundaries, respectively."" |
| 113 | + if isinstance(self._fld, BitField): |
| 114 | + self._align = int(math.ceil(self.i2len(None, None))) |
| 115 | + else: |
| 116 | + self._align = struct.calcsize(self._fld.fmt) |
| 117 | + |
| 118 | + |
| 119 | +class _dbmField(ByteField): |
| 120 | + def i2m(self, pkt, x): |
| 121 | + return super(ByteField, self).i2m(pkt, x + 256) |
| 122 | + |
| 123 | + def m2i(self, pkt, x): |
| 124 | + return super(ByteField, self).m2i(pkt, x) - 256 |
| 125 | + |
| 126 | + def i2repr(self, pkt, x): |
| 127 | + return "%sdBm" % x |
| 128 | + |
| 129 | + |
| 130 | +_vht_bandwidth = {0: "20MHz", 1: "40MHz", 2: "40MHz", 3: "40MHz", 4: "80MHz", 5: "80MHz", |
| 131 | + 6: "80MHz", 7: "80MHz", 8: "80MHz", 9: "80MHz", 10: "80MHz", 11: "160MHz", |
| 132 | + 12: "160MHz", 13: "160MHz", 14: "160MHz", 15: "160MHz", 16: "160MHz", 17: "160MHz", |
| 133 | + 18: "160MHz", 19: "160MHz", 20: "160MHz", 21: "160MHz", 22: "160MHz", 23: "160MHz", |
| 134 | + 24: "160MHz", 25: "160MHz"} |
| 135 | + |
| 136 | + |
93 | 137 | class RadioTap(Packet): |
94 | 138 | name = "RadioTap dummy" |
95 | 139 | fields_desc = [ByteField('version', 0), |
96 | 140 | ByteField('pad', 0), |
97 | | - FieldLenField('len', None, 'notdecoded', '<H', adjust=lambda pkt, x:x + 8), |
| 141 | + LEShortField('len', None), |
98 | 142 | FlagsField('present', None, -32, ['TSFT', 'Flags', 'Rate', 'Channel', 'FHSS', 'dBm_AntSignal', |
99 | 143 | 'dBm_AntNoise', 'Lock_Quality', 'TX_Attenuation', 'dB_TX_Attenuation', |
100 | 144 | 'dBm_TX_Power', 'Antenna', 'dB_AntSignal', 'dB_AntNoise', |
101 | | - 'b14', 'b15', 'b16', 'b17', 'b18', 'b19', 'b20', 'b21', 'b22', 'b23', |
102 | | - 'b24', 'b25', 'b26', 'b27', 'b28', 'b29', 'b30', 'Ext']), |
103 | | - StrLenField('notdecoded', "", length_from=lambda pkt:pkt.len - 8)] |
104 | | - |
105 | | - |
106 | | -class PPI(Packet): |
107 | | - name = "Per-Packet Information header (partial)" |
108 | | - fields_desc = [ByteField("version", 0), |
109 | | - ByteField("flags", 0), |
110 | | - FieldLenField("len", None, fmt="<H", length_of="notdecoded", adjust=lambda pkt, x:x + 8), |
111 | | - LEIntField("dlt", 0), |
112 | | - StrLenField("notdecoded", "", length_from=lambda pkt:pkt.len - 8) |
113 | | - ] |
| 145 | + 'RXFlags', 'b16', 'b17', 'b18', 'ChannelPlus', 'MCS', 'A_MPDU', |
| 146 | + 'VHT', 'timestamp', 'b24', 'b25', 'b26', 'b27', 'b28', 'b29', |
| 147 | + 'RadiotapNS', 'VendorNS', 'Ext']), |
| 148 | + ConditionalField(_RadiotapReversePadField(BitField("mac_timestamp", 0, -64)), lambda pkt: pkt.present and pkt.present.TSFT), |
| 149 | + ConditionalField( |
| 150 | + _RadiotapReversePadField( |
| 151 | + FlagsField("Flags", None, -8, ['CFP', 'ShortPreamble', 'wep', 'fragment', |
| 152 | + 'FCS', 'pad', 'badFCS', 'ShortGI']) |
| 153 | + ), |
| 154 | + lambda pkt: pkt.present and pkt.present.Flags), |
| 155 | + ConditionalField(_RadiotapReversePadField(ByteField("Rate", 0)), lambda pkt: pkt.present and pkt.present.Rate), |
| 156 | + ConditionalField(_RadiotapReversePadField(LEShortField("Channel", 0)), lambda pkt: pkt.present and pkt.present.Channel), |
| 157 | + ConditionalField( |
| 158 | + _RadiotapReversePadField( |
| 159 | + FlagsField("ChannelFlags", None, -16, ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', |
| 160 | + 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', |
| 161 | + 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz']) |
| 162 | + ), |
| 163 | + lambda pkt: pkt.present and pkt.present.Channel), |
| 164 | + ConditionalField(_RadiotapReversePadField(_dbmField("dBm_AntSignal", 0)), lambda pkt: pkt.present and pkt.present.dBm_AntSignal), |
| 165 | + ConditionalField(_RadiotapReversePadField(_dbmField("dBm_AntNoise", 0)), lambda pkt: pkt.present and pkt.present.dBm_AntNoise), |
| 166 | + ConditionalField(_RadiotapReversePadField(ByteField("Antenna", 0)), lambda pkt: pkt.present and pkt.present.Antenna), |
| 167 | + # ChannelPlus |
| 168 | + ConditionalField( |
| 169 | + _RadiotapReversePadField( |
| 170 | + FlagsField("ChannelFlags2", None, -32, ['res1', 'res2', 'res3', 'res4', 'Turbo', 'CCK', |
| 171 | + 'OFDM', '2GHz', '5GHz', 'Passive', 'Dynamic_CCK_OFDM', |
| 172 | + 'GFSK', 'GSM', 'StaticTurbo', '10MHz', '5MHz', |
| 173 | + '20MHz', '40MHz_ext_channel_above', '40MHz_ext_channel_below', |
| 174 | + 'res5', 'res6', 'res7', 'res8', 'res9']) |
| 175 | + ), |
| 176 | + lambda pkt: pkt.present and pkt.present.ChannelPlus), |
| 177 | + ConditionalField(_RadiotapReversePadField(LEShortField("ChannelFrequency", 0)), lambda pkt: pkt.present and pkt.present.ChannelPlus), |
| 178 | + ConditionalField(_RadiotapReversePadField(ByteField("ChannelNumber", 0)), lambda pkt: pkt.present and pkt.present.ChannelPlus), |
| 179 | + # A_MPDU |
| 180 | + ConditionalField(_RadiotapReversePadField(LEIntField("A_MPDU_ref", 0)), lambda pkt: pkt.present and pkt.present.A_MPDU), |
| 181 | + ConditionalField( |
| 182 | + _RadiotapReversePadField( |
| 183 | + FlagsField("A_MPDU_flags", None, -32, ['Report0Subframe', 'Is0Subframe', 'KnownLastSubframe', |
| 184 | + 'LastSubframe', 'CRCerror', 'EOFsubframe', 'KnownEOF', |
| 185 | + 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7', 'res8']) |
| 186 | + ), |
| 187 | + lambda pkt: pkt.present and pkt.present.A_MPDU), |
| 188 | + # VHT |
| 189 | + ConditionalField( |
| 190 | + _RadiotapReversePadField( |
| 191 | + FlagsField("KnownVHT", None, -16, ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', |
| 192 | + 'LDPCextraOFDM', 'Beamformed', 'Bandwidth', 'GroupID', 'PartialAID', |
| 193 | + 'res1', 'res2', 'res3', 'res4', 'res5', 'res6', 'res7']) |
| 194 | + ), |
| 195 | + lambda pkt: pkt.present and pkt.present.VHT), |
| 196 | + ConditionalField( |
| 197 | + _RadiotapReversePadField( |
| 198 | + FlagsField("PresentVHT", None, -8, ['STBC', 'TXOP_PS_NOT_ALLOWED', 'GuardInterval', 'SGINsysmDis', |
| 199 | + 'LDPCextraOFDM', 'Beamformed', 'res1', 'res2']) |
| 200 | + ), |
| 201 | + lambda pkt: pkt.present and pkt.present.VHT), |
| 202 | + ConditionalField(_RadiotapReversePadField(ByteEnumField("bandwidth", 0, _vht_bandwidth)), lambda pkt: pkt.present and pkt.present.VHT), |
| 203 | + ConditionalField(_RadiotapReversePadField(StrFixedLenField("mcs_nss", 0, length=5)), lambda pkt: pkt.present and pkt.present.VHT), |
| 204 | + ConditionalField(_RadiotapReversePadField(ByteField("GroupID", 0)), lambda pkt: pkt.present and pkt.present.VHT), |
| 205 | + ConditionalField(_RadiotapReversePadField(ShortField("PartialAID", 0)), lambda pkt: pkt.present and pkt.present.VHT), |
| 206 | + StrLenField('notdecoded', "", length_from=lambda pkt: pkt.len - pkt._tmp_dissect_pos)] |
| 207 | + |
| 208 | + def guess_payload_class(self, payload): |
| 209 | + if self.Flags.FCS: |
| 210 | + return Dot11FCS |
| 211 | + else: |
| 212 | + return Dot11 |
| 213 | + |
| 214 | + def post_build(self, p, pay): |
| 215 | + if self.len is None: |
| 216 | + p = p[:2] + struct.pack("!H", len(p))[::-1] + p[3:] |
| 217 | + return p + pay |
114 | 218 |
|
115 | 219 |
|
116 | 220 | class Dot11(Packet): |
@@ -138,11 +242,12 @@ class Dot11(Packet): |
138 | 242 | MACField("addr4", ETHER_ANY), |
139 | 243 | lambda pkt: (pkt.type == 2 and |
140 | 244 | pkt.FCfield & 3 == 3), # from-DS+to-DS |
141 | | - ), |
| 245 | + ) |
142 | 246 | ] |
143 | 247 |
|
144 | 248 | def mysummary(self): |
145 | | - return self.sprintf("802.11 %Dot11.type% %Dot11.subtype% %Dot11.addr2% > %Dot11.addr1%") |
| 249 | + # Supports both Dot11 and Dot11FCS |
| 250 | + return self.sprintf("802.11 %%%s.type%% %%%s.subtype%% %%%s.addr2%% > %%%s.addr1%%" % ((self.__class__.__name__,) * 4)) |
146 | 251 |
|
147 | 252 | def guess_payload_class(self, payload): |
148 | 253 | if self.type == 0x02 and (0x08 <= self.subtype <= 0xF and self.subtype != 0xD): |
@@ -185,6 +290,30 @@ def unwep(self, key=None, warn=1): |
185 | 290 | self.payload = self.payload.payload |
186 | 291 |
|
187 | 292 |
|
| 293 | +class Dot11FCS(Dot11): |
| 294 | + name = "802.11-FCS" |
| 295 | + fields_desc = Dot11.fields_desc + [XLEIntField("fcs", None)] # Automatically moved to the end of the packet |
| 296 | + |
| 297 | + def compute_fcs(self, s): |
| 298 | + return struct.pack("!I", crc32(s) & 0xffffffff)[::-1] |
| 299 | + |
| 300 | + def post_build(self, p, pay): |
| 301 | + # Switch payload and frame check sequence |
| 302 | + return p[:-4] + pay + (p[-4:] if self.fcs is not None else self.compute_fcs(p[:-4] + pay)) |
| 303 | + |
| 304 | + def post_dissect(self, s): |
| 305 | + self.raw_packet_cache = None # Reset packet to allow post_build |
| 306 | + return s |
| 307 | + |
| 308 | + def pre_dissect(self, s): |
| 309 | + # Get the frame check sequence |
| 310 | + sty = orb(s[0]) |
| 311 | + ty = orb(s[1]) >> 2 |
| 312 | + fc = struct.unpack("!H", s[2:4])[0] |
| 313 | + length = 12 + 6 * ((ty != 1 or sty in [0x8, 0x9, 0xa, 0xb, 0xe, 0xf]) + (ty in [0, 2]) + (ty == 2 and fc & 3 == 3)) |
| 314 | + return s[:length] + s[-4:] + s[length:-4] |
| 315 | + |
| 316 | + |
188 | 317 | class Dot11QoS(Packet): |
189 | 318 | name = "802.11 QoS" |
190 | 319 | fields_desc = [BitField("Reserved", None, 1), |
@@ -224,10 +353,37 @@ class Dot11Beacon(Packet): |
224 | 353 | FlagsField("cap", 0, 16, capability_list)] |
225 | 354 |
|
226 | 355 |
|
| 356 | +_dot11_info_elts_ids = { |
| 357 | + 0: "SSID", |
| 358 | + 1: "Rates", |
| 359 | + 2: "FHset", |
| 360 | + 3: "DSset", |
| 361 | + 4: "CFset", |
| 362 | + 5: "TIM", |
| 363 | + 6: "IBSSset", |
| 364 | + 7: "Country", |
| 365 | + 10: "Request", |
| 366 | + 16: "challenge", |
| 367 | + 33: "PowerCapability", |
| 368 | + 36: "Channels", |
| 369 | + 42: "ERPinfo", |
| 370 | + 45: "HTCapabilities", |
| 371 | + 46: "QoSCapability", |
| 372 | + 47: "ERPinfo", |
| 373 | + 48: "RSNinfo", |
| 374 | + 50: "ESRates", |
| 375 | + 52: "PowerConstraint", |
| 376 | + 107: "Interworking", |
| 377 | + 127: "ExtendendCapatibilities", |
| 378 | + 191: "VHTCapabilities", |
| 379 | + 221: "vendor", |
| 380 | + 68: "reserved" |
| 381 | +} |
| 382 | + |
| 383 | + |
227 | 384 | class Dot11Elt(Packet): |
228 | 385 | name = "802.11 Information Element" |
229 | | - fields_desc = [ByteEnumField("ID", 0, {0: "SSID", 1: "Rates", 2: "FHset", 3: "DSset", 4: "CFset", 5: "TIM", 6: "IBSSset", 16: "challenge", |
230 | | - 42: "ERPinfo", 46: "QoS Capability", 47: "ERPinfo", 48: "RSNinfo", 50: "ESRates", 221: "vendor", 68: "reserved"}), |
| 386 | + fields_desc = [ByteEnumField("ID", 0, _dot11_info_elts_ids), |
231 | 387 | FieldLenField("len", None, "info", "B"), |
232 | 388 | StrLenField("info", "", length_from=lambda x: x.len, |
233 | 389 | max_length=255)] |
@@ -539,8 +695,6 @@ class Dot11Ack(Packet): |
539 | 695 |
|
540 | 696 |
|
541 | 697 | bind_layers(PrismHeader, Dot11,) |
542 | | -bind_layers(RadioTap, Dot11,) |
543 | | -bind_layers(PPI, Dot11, dlt=105) |
544 | 698 | bind_layers(Dot11, LLC, type=2) |
545 | 699 | bind_layers(Dot11QoS, LLC,) |
546 | 700 | bind_layers(Dot11, Dot11AssoReq, subtype=0, type=0) |
@@ -572,7 +726,6 @@ class Dot11Ack(Packet): |
572 | 726 | conf.l2types.register_num2layer(802, PrismHeader) |
573 | 727 | conf.l2types.register(DLT_IEEE802_11_RADIO, RadioTap) |
574 | 728 | conf.l2types.register_num2layer(803, RadioTap) |
575 | | -conf.l2types.register(DLT_PPI, PPI) |
576 | 729 |
|
577 | 730 |
|
578 | 731 | class WiFi_am(AnsweringMachine): |
|
0 commit comments