• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# SPDX-License-Identifier: GPL-2.0-only
2# This file is part of Scapy
3# See https://scapy.net/ for more information
4# Copyright (C) Philippe Biondi <phil@secdev.org>
5# Copyright (C) Gabriel Potter
6
7"""
8SMB 1.0 (Server Message Block), also known as CIFS.
9
10.. note::
11    You will find more complete documentation for this layer over at
12    `SMB <https://scapy.readthedocs.io/en/latest/layers/smb.html>`_
13
14Specs:
15
16- [MS-CIFS] (base)
17- [MS-SMB] (extension of CIFS - SMB v1)
18"""
19
20import struct
21
22from scapy.config import conf
23from scapy.packet import Packet, bind_layers, bind_top_down
24from scapy.fields import (
25    ByteEnumField,
26    ByteField,
27    ConditionalField,
28    FieldLenField,
29    FieldListField,
30    FlagsField,
31    IPField,
32    LEFieldLenField,
33    LEIntEnumField,
34    LEIntField,
35    LELongField,
36    LEShortEnumField,
37    LEShortField,
38    MultipleTypeField,
39    PacketField,
40    PacketLenField,
41    PacketListField,
42    ReversePadField,
43    ScalingField,
44    ShortField,
45    StrFixedLenField,
46    StrNullField,
47    StrNullFieldUtf16,
48    UTCTimeField,
49    UUIDField,
50    XLEShortField,
51    XStrLenField,
52)
53
54from scapy.layers.dns import (
55    DNSStrField,
56    DNSCompressedPacket,
57)
58from scapy.layers.ntlm import (
59    _NTLMPayloadPacket,
60    _NTLMPayloadField,
61    _NTLM_ENUM,
62    _NTLM_post_build,
63)
64from scapy.layers.netbios import NBTSession, NBTDatagram
65from scapy.layers.gssapi import (
66    GSSAPI_BLOB,
67)
68from scapy.layers.smb2 import (
69    STATUS_ERREF,
70    SMB2_Header,
71)
72
73
74SMB_COM = {
75    0x00: "SMB_COM_CREATE_DIRECTORY",
76    0x01: "SMB_COM_DELETE_DIRECTORY",
77    0x02: "SMB_COM_OPEN",
78    0x03: "SMB_COM_CREATE",
79    0x04: "SMB_COM_CLOSE",
80    0x05: "SMB_COM_FLUSH",
81    0x06: "SMB_COM_DELETE",
82    0x07: "SMB_COM_RENAME",
83    0x08: "SMB_COM_QUERY_INFORMATION",
84    0x09: "SMB_COM_SET_INFORMATION",
85    0x0A: "SMB_COM_READ",
86    0x0B: "SMB_COM_WRITE",
87    0x0C: "SMB_COM_LOCK_BYTE_RANGE",
88    0x0D: "SMB_COM_UNLOCK_BYTE_RANGE",
89    0x0E: "SMB_COM_CREATE_TEMPORARY",
90    0x0F: "SMB_COM_CREATE_NEW",
91    0x10: "SMB_COM_CHECK_DIRECTORY",
92    0x11: "SMB_COM_PROCESS_EXIT",
93    0x12: "SMB_COM_SEEK",
94    0x13: "SMB_COM_LOCK_AND_READ",
95    0x14: "SMB_COM_WRITE_AND_UNLOCK",
96    0x1A: "SMB_COM_READ_RAW",
97    0x1B: "SMB_COM_READ_MPX",
98    0x1C: "SMB_COM_READ_MPX_SECONDARY",
99    0x1D: "SMB_COM_WRITE_RAW",
100    0x1E: "SMB_COM_WRITE_MPX",
101    0x1F: "SMB_COM_WRITE_MPX_SECONDARY",
102    0x20: "SMB_COM_WRITE_COMPLETE",
103    0x21: "SMB_COM_QUERY_SERVER",
104    0x22: "SMB_COM_SET_INFORMATION2",
105    0x23: "SMB_COM_QUERY_INFORMATION2",
106    0x24: "SMB_COM_LOCKING_ANDX",
107    0x25: "SMB_COM_TRANSACTION",
108    0x26: "SMB_COM_TRANSACTION_SECONDARY",
109    0x27: "SMB_COM_IOCTL",
110    0x28: "SMB_COM_IOCTL_SECONDARY",
111    0x29: "SMB_COM_COPY",
112    0x2A: "SMB_COM_MOVE",
113    0x2B: "SMB_COM_ECHO",
114    0x2C: "SMB_COM_WRITE_AND_CLOSE",
115    0x2D: "SMB_COM_OPEN_ANDX",
116    0x2E: "SMB_COM_READ_ANDX",
117    0x2F: "SMB_COM_WRITE_ANDX",
118    0x30: "SMB_COM_NEW_FILE_SIZE",
119    0x31: "SMB_COM_CLOSE_AND_TREE_DISC",
120    0x32: "SMB_COM_TRANSACTION2",
121    0x33: "SMB_COM_TRANSACTION2_SECONDARY",
122    0x34: "SMB_COM_FIND_CLOSE2",
123    0x35: "SMB_COM_FIND_NOTIFY_CLOSE",
124    0x70: "SMB_COM_TREE_CONNECT",
125    0x71: "SMB_COM_TREE_DISCONNECT",
126    0x72: "SMB_COM_NEGOTIATE",
127    0x73: "SMB_COM_SESSION_SETUP_ANDX",
128    0x74: "SMB_COM_LOGOFF_ANDX",
129    0x75: "SMB_COM_TREE_CONNECT_ANDX",
130    0x7E: "SMB_COM_SECURITY_PACKAGE_ANDX",
131    0x80: "SMB_COM_QUERY_INFORMATION_DISK",
132    0x81: "SMB_COM_SEARCH",
133    0x82: "SMB_COM_FIND",
134    0x83: "SMB_COM_FIND_UNIQUE",
135    0x84: "SMB_COM_FIND_CLOSE",
136    0xA0: "SMB_COM_NT_TRANSACT",
137    0xA1: "SMB_COM_NT_TRANSACT_SECONDARY",
138    0xA2: "SMB_COM_NT_CREATE_ANDX",
139    0xA4: "SMB_COM_NT_CANCEL",
140    0xA5: "SMB_COM_NT_RENAME",
141    0xC0: "SMB_COM_OPEN_PRINT_FILE",
142    0xC1: "SMB_COM_WRITE_PRINT_FILE",
143    0xC2: "SMB_COM_CLOSE_PRINT_FILE",
144    0xC3: "SMB_COM_GET_PRINT_QUEUE",
145    0xD8: "SMB_COM_READ_BULK",
146    0xD9: "SMB_COM_WRITE_BULK",
147    0xDA: "SMB_COM_WRITE_BULK_DATA",
148    0xFE: "SMB_COM_INVALID",
149    0xFF: "SMB_COM_NO_ANDX_COMMAND",
150}
151
152
153class SMB_Header(Packet):
154    name = "SMB 1 Protocol Request Header"
155    fields_desc = [
156        StrFixedLenField("Start", b"\xffSMB", 4),
157        ByteEnumField("Command", 0x72, SMB_COM),
158        LEIntEnumField("Status", 0, STATUS_ERREF),
159        FlagsField(
160            "Flags",
161            0x18,
162            8,
163            [
164                "LOCK_AND_READ_OK",
165                "BUF_AVAIL",
166                "res",
167                "CASE_INSENSITIVE",
168                "CANONICALIZED_PATHS",
169                "OPLOCK",
170                "OPBATCH",
171                "REPLY",
172            ],
173        ),
174        FlagsField(
175            "Flags2",
176            0x0000,
177            -16,
178            [
179                "LONG_NAMES",
180                "EAS",
181                "SMB_SECURITY_SIGNATURE",
182                "COMPRESSED",
183                "SMB_SECURITY_SIGNATURE_REQUIRED",
184                "res",
185                "IS_LONG_NAME",
186                "res",
187                "res",
188                "res",
189                "REPARSE_PATH",
190                "EXTENDED_SECURITY",
191                "DFS",
192                "PAGING_IO",
193                "NT_STATUS",
194                "UNICODE",
195            ],
196        ),
197        LEShortField("PIDHigh", 0x0000),
198        StrFixedLenField("SecuritySignature", b"", length=8),
199        LEShortField("Reserved", 0x0),
200        LEShortField("TID", 0),
201        LEShortField("PIDLow", 0),
202        LEShortField("UID", 0),
203        LEShortField("MID", 0),
204    ]
205
206    def guess_payload_class(self, payload):
207        # type: (bytes) -> Packet
208        if not payload:
209            return super(SMB_Header, self).guess_payload_class(payload)
210        WordCount = ord(payload[:1])
211        if self.Command == 0x72:
212            if self.Flags.REPLY:
213                if self.Flags2.EXTENDED_SECURITY:
214                    return SMBNegotiate_Response_Extended_Security
215                else:
216                    return SMBNegotiate_Response_Security
217            else:
218                return SMBNegotiate_Request
219        elif self.Command == 0x73:
220            if WordCount == 0:
221                return SMBSession_Null
222            if self.Flags.REPLY:
223                if WordCount == 0x04:
224                    return SMBSession_Setup_AndX_Response_Extended_Security
225                elif WordCount == 0x03:
226                    return SMBSession_Setup_AndX_Response
227                if self.Flags2.EXTENDED_SECURITY:
228                    return SMBSession_Setup_AndX_Response_Extended_Security
229                else:
230                    return SMBSession_Setup_AndX_Response
231            else:
232                if WordCount == 0x0C:
233                    return SMBSession_Setup_AndX_Request_Extended_Security
234                elif WordCount == 0x0D:
235                    return SMBSession_Setup_AndX_Request
236                if self.Flags2.EXTENDED_SECURITY:
237                    return SMBSession_Setup_AndX_Request_Extended_Security
238                else:
239                    return SMBSession_Setup_AndX_Request
240        elif self.Command == 0x25:
241            if self.Flags.REPLY:
242                if WordCount == 0x11:
243                    return SMBMailslot_Write
244                else:
245                    return SMBTransaction_Response
246            else:
247                if WordCount == 0x11:
248                    return SMBMailslot_Write
249                else:
250                    return SMBTransaction_Request
251        return super(SMB_Header, self).guess_payload_class(payload)
252
253    def answers(self, pkt):
254        return SMB_Header in pkt
255
256
257# SMB Negotiate Request
258
259
260class SMB_Dialect(Packet):
261    name = "SMB Dialect"
262    fields_desc = [
263        ByteField("BufferFormat", 0x02),
264        StrNullField("DialectString", "NT LM 0.12"),
265    ]
266
267    def default_payload_class(self, payload):
268        return conf.padding_layer
269
270
271class SMBNegotiate_Request(Packet):
272    name = "SMB Negotiate Request"
273    fields_desc = [
274        ByteField("WordCount", 0),
275        LEFieldLenField("ByteCount", None, length_of="Dialects"),
276        PacketListField(
277            "Dialects",
278            [SMB_Dialect()],
279            SMB_Dialect,
280            length_from=lambda pkt: pkt.ByteCount,
281        ),
282    ]
283
284
285bind_layers(SMB_Header, SMBNegotiate_Request, Command=0x72)
286
287# SMBNegociate Protocol Response
288
289
290def _SMBStrNullField(name, default):
291    """
292    Returns a StrNullField that is either normal or UTF-16 depending
293    on the SMB headers.
294    """
295
296    def _isUTF16(pkt):
297        while not hasattr(pkt, "Flags2") and pkt.underlayer:
298            pkt = pkt.underlayer
299        return hasattr(pkt, "Flags2") and pkt.Flags2.UNICODE
300
301    return MultipleTypeField(
302        [(StrNullFieldUtf16(name, default), _isUTF16)],
303        StrNullField(name, default),
304    )
305
306
307def _len(pkt, name):
308    """
309    Returns the length of a field, works with Unicode strings.
310    """
311    fld, v = pkt.getfield_and_val(name)
312    return len(fld.addfield(pkt, v, b""))
313
314
315class _SMBNegotiate_Response(Packet):
316    @classmethod
317    def dispatch_hook(cls, _pkt=None, *args, **kargs):
318        if _pkt and len(_pkt) >= 2:
319            # Yes this is inspired by
320            # https://github.com/wireshark/wireshark/blob/925e01b23fd5aad2fa929fafd894128a88832e74/epan/dissectors/packet-smb.c#L2902
321            wc = struct.unpack("<H", _pkt[:1])
322            # dialect = struct.unpack("<H", _pkt[1:3])
323            if wc == 1:
324                # Core Protocol
325                return SMBNegotiate_Response_NoSecurity
326            elif wc == 0xD:
327                # LAN Manager 1.0 - LAN Manager 2.1
328                # TODO
329                pass
330            elif wc == 0x11:
331                # NT LAN Manager
332                return cls
333        return cls
334
335
336_SMB_ServerCapabilities = [
337    "RAW_MODE",
338    "MPX_MODE",
339    "UNICODE",
340    "LARGE_FILES",
341    "NT_SMBS",
342    "RPC_REMOTE_APIS",
343    "STATUS32",
344    "LEVEL_II_OPLOCKS",
345    "LOCK_AND_READ",
346    "NT_FIND",
347    "res",
348    "res",
349    "DFS",
350    "INFOLEVEL_PASSTHRU",
351    "LARGE_READX",
352    "LARGE_WRITEX",
353    "LWIO",
354    "res",
355    "res",
356    "res",
357    "res",
358    "res",
359    "res",
360    "UNIX",
361    "res",
362    "COMPRESSED_DATA",
363    "res",
364    "res",
365    "res",
366    "DYNAMIC_REAUTH",
367    "PERSISTENT_HANDLES",
368    "EXTENDED_SECURITY",
369]
370
371
372# CIFS sect 2.2.4.52.2
373
374
375class SMBNegotiate_Response_NoSecurity(_SMBNegotiate_Response):
376    name = "SMB Negotiate No-Security Response (CIFS)"
377    fields_desc = [
378        ByteField("WordCount", 0x1),
379        LEShortField("DialectIndex", 7),
380        FlagsField(
381            "SecurityMode",
382            0x03,
383            8,
384            [
385                "USER_SECURITY",
386                "ENCRYPT_PASSWORDS",
387                "SECURITY_SIGNATURES_ENABLED",
388                "SECURITY_SIGNATURES_REQUIRED",
389            ],
390        ),
391        LEShortField("MaxMpxCount", 50),
392        LEShortField("MaxNumberVC", 1),
393        LEIntField("MaxBufferSize", 16144),  # Windows: 4356
394        LEIntField("MaxRawSize", 65536),
395        LEIntField("SessionKey", 0x0000),
396        FlagsField("ServerCapabilities", 0xF3F9, -32, _SMB_ServerCapabilities),
397        UTCTimeField(
398            "ServerTime",
399            None,
400            fmt="<Q",
401            epoch=[1601, 1, 1, 0, 0, 0],
402            custom_scaling=1e7,
403        ),
404        ScalingField("ServerTimeZone", 0x3C, fmt="<h", unit="min-UTC"),
405        FieldLenField(
406            "ChallengeLength",
407            None,
408            # aka EncryptionKeyLength
409            length_of="Challenge",
410            fmt="<B",
411        ),
412        LEFieldLenField(
413            "ByteCount",
414            None,
415            length_of="DomainName",
416            adjust=lambda pkt, x: x + len(pkt.Challenge),
417        ),
418        XStrLenField(
419            "Challenge",
420            b"",  # aka EncryptionKey
421            length_from=lambda pkt: pkt.ChallengeLength,
422        ),
423        StrNullField("DomainName", "WORKGROUP"),
424    ]
425
426
427bind_top_down(SMB_Header, SMBNegotiate_Response_NoSecurity, Command=0x72, Flags=0x80)
428
429# SMB sect 2.2.4.5.2.1
430
431
432class SMBNegotiate_Response_Extended_Security(_SMBNegotiate_Response):
433    name = "SMB Negotiate Extended Security Response (SMB)"
434    WordCount = 0x11
435    fields_desc = SMBNegotiate_Response_NoSecurity.fields_desc[:12] + [
436        LEFieldLenField(
437            "ByteCount", None, length_of="SecurityBlob", adjust=lambda _, x: x + 16
438        ),
439        SMBNegotiate_Response_NoSecurity.fields_desc[13],
440        UUIDField("GUID", None, uuid_fmt=UUIDField.FORMAT_LE),
441        PacketLenField(
442            "SecurityBlob", None, GSSAPI_BLOB, length_from=lambda x: x.ByteCount - 16
443        ),
444    ]
445
446
447bind_top_down(
448    SMB_Header,
449    SMBNegotiate_Response_Extended_Security,
450    Command=0x72,
451    Flags=0x80,
452    Flags2=0x800,
453)
454
455# SMB sect 2.2.4.5.2.2
456
457
458class SMBNegotiate_Response_Security(_SMBNegotiate_Response):
459    name = "SMB Negotiate Non-Extended Security Response (SMB)"
460    WordCount = 0x11
461    fields_desc = SMBNegotiate_Response_NoSecurity.fields_desc[:12] + [
462        LEFieldLenField(
463            "ByteCount",
464            None,
465            length_of="DomainName",
466            adjust=lambda pkt, x: x
467            + 2
468            + _len(pkt, "Challenge")
469            + _len(pkt, "ServerName"),
470        ),
471        XStrLenField(
472            "Challenge",
473            b"",  # aka EncryptionKey
474            length_from=lambda pkt: pkt.ChallengeLength,
475        ),
476        _SMBStrNullField("DomainName", "WORKGROUP"),
477        _SMBStrNullField("ServerName", "RMFF1"),
478    ]
479
480
481bind_top_down(SMB_Header, SMBNegotiate_Response_Security, Command=0x72, Flags=0x80)
482
483# Session Setup AndX Request
484
485# CIFS sect 2.2.4.53
486
487
488class SMBSession_Setup_AndX_Request(Packet):
489    name = "Session Setup AndX Request (CIFS)"
490    fields_desc = [
491        ByteField("WordCount", 0x0D),
492        ByteEnumField("AndXCommand", 0xFF, SMB_COM),
493        ByteField("AndXReserved", 0),
494        LEShortField("AndXOffset", None),
495        LEShortField("MaxBufferSize", 16144),  # Windows: 4356
496        LEShortField("MaxMPXCount", 50),
497        LEShortField("VCNumber", 0),
498        LEIntField("SessionKey", 0),
499        LEFieldLenField("OEMPasswordLength", None, length_of="OEMPassword"),
500        LEFieldLenField("UnicodePasswordLength", None, length_of="UnicodePassword"),
501        LEIntField("Reserved", 0),
502        FlagsField("ServerCapabilities", 0x05, -32, _SMB_ServerCapabilities),
503        LEShortField("ByteCount", None),
504        XStrLenField("OEMPassword", "Pass", length_from=lambda x: x.OEMPasswordLength),
505        XStrLenField(
506            "UnicodePassword", "Pass", length_from=lambda x: x.UnicodePasswordLength
507        ),
508        ReversePadField(_SMBStrNullField("AccountName", "GUEST"), 2, b"\0"),
509        _SMBStrNullField("PrimaryDomain", ""),
510        _SMBStrNullField("NativeOS", "Windows 4.0"),
511        _SMBStrNullField("NativeLanMan", "Windows 4.0"),
512    ]
513
514    def post_build(self, pkt, pay):
515        if self.AndXOffset is None and self.AndXCommand != 0xFF:
516            pkt = pkt[:3] + struct.pack("<H", len(pkt) + 32) + pkt[5:]
517        if self.ByteCount is None:
518            pkt = pkt[:27] + struct.pack("<H", len(pkt) - 29) + pkt[29:]
519        if self.payload and hasattr(self.payload, "AndXOffset") and pay:
520            pay = pay[:3] + struct.pack("<H", len(pkt) + len(pay) + 32) + pay[5:]
521        return pkt + pay
522
523
524bind_top_down(SMB_Header, SMBSession_Setup_AndX_Request, Command=0x73)
525
526# SMB sect 2.2.4.7
527
528
529class SMBTree_Connect_AndX(Packet):
530    name = "Session Tree Connect AndX"
531    WordCount = 0x04
532    fields_desc = SMBSession_Setup_AndX_Request.fields_desc[:4] + [
533        FlagsField(
534            "Flags",
535            "",
536            -16,
537            ["DISCONNECT_TID", "r2", "EXTENDED_SIGNATURES", "EXTENDED_RESPONSE"],
538        ),
539        FieldLenField("PasswordLength", None, length_of="Password", fmt="<H"),
540        LEShortField("ByteCount", None),
541        XStrLenField("Password", b"", length_from=lambda pkt: pkt.PasswordLength),
542        ReversePadField(_SMBStrNullField("Path", "\\\\WIN2K\\IPC$"), 2),
543        StrNullField("Service", "?????"),
544    ]
545
546    def post_build(self, pkt, pay):
547        pkt += pay
548        if self.ByteCount is None:
549            pkt = pkt[:9] + struct.pack("<H", len(pkt) - 11) + pkt[11:]
550        return pkt
551
552
553bind_layers(SMB_Header, SMBTree_Connect_AndX, Command=0x75)
554bind_layers(SMBSession_Setup_AndX_Request, SMBTree_Connect_AndX, AndXCommand=0x75)
555
556# SMB sect 2.2.4.6.1
557
558
559class SMBSession_Setup_AndX_Request_Extended_Security(Packet):
560    name = "Session Setup AndX Extended Security Request (SMB)"
561    WordCount = 0x0C
562    fields_desc = (
563        SMBSession_Setup_AndX_Request.fields_desc[:8]
564        + [
565            LEFieldLenField("SecurityBlobLength", None, length_of="SecurityBlob"),
566        ]
567        + SMBSession_Setup_AndX_Request.fields_desc[10:12]
568        + [
569            LEShortField("ByteCount", None),
570            PacketLenField(
571                "SecurityBlob",
572                None,
573                GSSAPI_BLOB,
574                length_from=lambda x: x.SecurityBlobLength,
575            ),
576            ReversePadField(
577                _SMBStrNullField("NativeOS", "Windows 4.0"),
578                2,
579                b"\0",
580            ),
581            _SMBStrNullField("NativeLanMan", "Windows 4.0"),
582        ]
583    )
584
585    def post_build(self, pkt, pay):
586        if self.ByteCount is None:
587            pkt = pkt[:25] + struct.pack("<H", len(pkt) - 27) + pkt[27:]
588        return pkt + pay
589
590
591bind_top_down(
592    SMB_Header,
593    SMBSession_Setup_AndX_Request_Extended_Security,
594    Command=0x73,
595    Flags2=0x800,
596)
597
598# Session Setup AndX Response
599
600
601# CIFS sect 2.2.4.53.2
602
603
604class SMBSession_Setup_AndX_Response(Packet):
605    name = "Session Setup AndX Response (CIFS)"
606    fields_desc = [
607        ByteField("WordCount", 0x3),
608        ByteEnumField("AndXCommand", 0xFF, SMB_COM),
609        ByteField("AndXReserved", 0),
610        LEShortField("AndXOffset", None),
611        FlagsField(
612            "Action",
613            0,
614            -16,
615            {
616                0x0001: "SMB_SETUP_GUEST",
617                0x0002: "SMB_SETUP_USE_LANMAN_KEY",
618            },
619        ),
620        LEShortField("ByteCount", 25),
621        _SMBStrNullField("NativeOS", "Windows 4.0"),
622        _SMBStrNullField("NativeLanMan", "Windows 4.0"),
623        _SMBStrNullField("PrimaryDomain", ""),
624        # Off spec?
625        ByteField("WordCount2", 3),
626        ByteEnumField("AndXCommand2", 0xFF, SMB_COM),
627        ByteField("Reserved3", 0),
628        LEShortField("AndXOffset2", 80),
629        LEShortField("OptionalSupport", 0x01),
630        LEShortField("ByteCount2", 5),
631        StrNullField("Service", "IPC"),
632        StrNullField("NativeFileSystem", ""),
633    ]
634
635    def post_build(self, pkt, pay):
636        if self.AndXOffset is None:
637            pkt = pkt[:3] + struct.pack("<H", len(pkt) + 32) + pkt[5:]
638        return pkt + pay
639
640
641bind_top_down(SMB_Header, SMBSession_Setup_AndX_Response, Command=0x73, Flags=0x80)
642
643# SMB sect 2.2.4.6.2
644
645
646class SMBSession_Setup_AndX_Response_Extended_Security(
647    SMBSession_Setup_AndX_Response
648):  # noqa: E501
649    name = "Session Setup AndX Extended Security Response (SMB)"
650    WordCount = 0x4
651    fields_desc = (
652        SMBSession_Setup_AndX_Response.fields_desc[:5]
653        + [SMBSession_Setup_AndX_Request_Extended_Security.fields_desc[8]]
654        + SMBSession_Setup_AndX_Request_Extended_Security.fields_desc[11:]
655    )
656
657    def post_build(self, pkt, pay):
658        if self.ByteCount is None:
659            pkt = pkt[:9] + struct.pack("<H", len(pkt) - 11) + pkt[11:]
660        return super(SMBSession_Setup_AndX_Response_Extended_Security, self).post_build(
661            pkt, pay
662        )
663
664
665bind_top_down(
666    SMB_Header,
667    SMBSession_Setup_AndX_Response_Extended_Security,
668    Command=0x73,
669    Flags=0x80,
670    Flags2=0x800,
671)
672
673# SMB null (no wordcount)
674
675
676class SMBSession_Null(Packet):
677    fields_desc = [ByteField("WordCount", 0), LEShortField("ByteCount", 0)]
678
679
680bind_top_down(SMB_Header, SMBSession_Null, Command=0x73)
681
682# [MS-CIFS] sect 2.2.4.33.1
683
684_SMB_CONFIG = [
685    ("Len", _NTLM_ENUM.LEN),
686    ("BufferOffset", _NTLM_ENUM.OFFSET),
687]
688
689
690class _SMB_TransactionRequest_Data(PacketLenField):
691    def m2i(self, pkt, m):
692        if pkt.Name == b"\\MAILSLOT\\NET\\NETLOGON":
693            return NETLOGON(m)
694        elif pkt.Name == b"\\MAILSLOT\\BROWSE" or pkt.name == b"\\MAILSLOT\\LANMAN":
695            return BRWS(m)
696        return conf.raw_layer(m)
697
698
699def _optlen(pkt, x):
700    try:
701        return len(getattr(pkt, x))
702    except AttributeError:
703        return 0
704
705
706class SMBTransaction_Request(_NTLMPayloadPacket):
707    name = "SMB COM Transaction Request"
708    _NTLM_PAYLOAD_FIELD_NAME = "Buffer"
709
710    fields_desc = [
711        FieldLenField(
712            "WordCount",
713            None,
714            length_of="SetupCount",
715            adjust=lambda pkt, x: x + 0x0E,
716            fmt="B",
717        ),
718        FieldLenField(
719            "TotalParamCount",
720            None,
721            length_of="Buffer",
722            fmt="<H",
723            adjust=lambda pkt, _: _optlen(pkt, "Parameter"),
724        ),
725        FieldLenField(
726            "TotalDataCount",
727            None,
728            length_of="Buffer",
729            fmt="<H",
730            adjust=lambda pkt, _: _optlen(pkt, "Data"),
731        ),
732        LEShortField("MaxParamCount", 0),
733        LEShortField("MaxDataCount", 0),
734        ByteField("MaxSetupCount", 0),
735        ByteField("Reserved1", 0),
736        FlagsField("Flags", 0, -16, {0x1: "DISCONNECT_TID", 0x2: "NO_RESPONSE"}),
737        LEIntField("Timeout", 1000),
738        ShortField("Reserved2", 0),
739        LEShortField("ParameterLen", None),
740        LEShortField("ParameterBufferOffset", None),
741        LEShortField("DataLen", None),
742        LEShortField("DataBufferOffset", None),
743        FieldLenField("SetupCount", 3, count_of="Setup", fmt="B"),
744        ByteField("Reserved3", 0),
745        FieldListField(
746            "Setup",
747            [1, 1, 2],
748            LEShortField("", 0),
749            count_from=lambda pkt: pkt.SetupCount,
750        ),
751        # SMB Data
752        FieldLenField(
753            "ByteCount",
754            None,
755            length_of="Name",
756            fmt="<H",
757            adjust=lambda pkt, x: x + _optlen(pkt, "Parameter") + _optlen(pkt, "Data"),
758        ),
759        StrNullField("Name", "\\MAILSLOT\\NET\\NETLOGON"),
760        _NTLMPayloadField(
761            "Buffer",
762            lambda pkt: 32 + 31 + len(pkt.Setup) * 2 + len(pkt.Name) + 1,
763            [
764                XStrLenField(
765                    "Parameter", b"", length_from=lambda pkt: pkt.ParameterLen
766                ),
767                _SMB_TransactionRequest_Data(
768                    "Data", None, conf.raw_layer, length_from=lambda pkt: pkt.DataLen
769                ),
770            ],
771        ),
772    ]
773
774    def post_build(self, pkt, pay):
775        # type: (bytes, bytes) -> bytes
776        return (
777            _NTLM_post_build(
778                self,
779                pkt,
780                32 + 31 + len(self.Setup) * 2 + len(self.Name) + 1,
781                {
782                    "Parameter": 19,
783                    "Data": 23,
784                },
785                config=_SMB_CONFIG,
786            )
787            + pay
788        )
789
790    def mysummary(self):
791        if getattr(self, "Data", None) is not None:
792            return self.sprintf("Tran %Name% ") + self.Data.mysummary()
793        return self.sprintf("Tran %Name%")
794
795
796bind_top_down(SMB_Header, SMBTransaction_Request, Command=0x25)
797
798
799class SMBMailslot_Write(SMBTransaction_Request):
800    WordCount = 0x11
801
802
803# [MS-CIFS] sect 2.2.4.33.2
804
805
806class SMBTransaction_Response(_NTLMPayloadPacket):
807    name = "SMB COM Transaction Response"
808    _NTLM_PAYLOAD_FIELD_NAME = "Buffer"
809    fields_desc = [
810        FieldLenField(
811            "WordCount",
812            None,
813            length_of="SetupCount",
814            adjust=lambda pkt, x: x + 0x0A,
815            fmt="B",
816        ),
817        FieldLenField(
818            "TotalParamCount",
819            None,
820            length_of="Buffer",
821            fmt="<H",
822            adjust=lambda pkt, _: _optlen(pkt, "Parameter"),
823        ),
824        FieldLenField(
825            "TotalDataCount",
826            None,
827            length_of="Buffer",
828            fmt="<H",
829            adjust=lambda pkt, _: _optlen(pkt, "Data"),
830        ),
831        LEShortField("Reserved1", None),
832        LEShortField("ParameterLen", None),
833        LEShortField("ParameterBufferOffset", None),
834        LEShortField("ParameterDisplacement", 0),
835        LEShortField("DataLen", None),
836        LEShortField("DataBufferOffset", None),
837        LEShortField("DataDisplacement", 0),
838        FieldLenField("SetupCount", 3, count_of="Setup", fmt="B"),
839        ByteField("Reserved2", 0),
840        FieldListField(
841            "Setup",
842            [1, 1, 2],
843            LEShortField("", 0),
844            count_from=lambda pkt: pkt.SetupCount,
845        ),
846        # SMB Data
847        FieldLenField(
848            "ByteCount",
849            None,
850            length_of="Buffer",
851            fmt="<H",
852            adjust=lambda pkt, x: _optlen(pkt, "Parameter") + _optlen(pkt, "Data"),
853        ),
854        _NTLMPayloadField(
855            "Buffer",
856            lambda pkt: 32 + 22 + len(pkt.Setup) * 2,
857            [
858                XStrLenField(
859                    "Parameter", b"", length_from=lambda pkt: pkt.ParameterLen
860                ),
861                XStrLenField("Data", b"", length_from=lambda pkt: pkt.DataLen),
862            ],
863        ),
864    ]
865
866    def post_build(self, pkt, pay):
867        # type: (bytes, bytes) -> bytes
868        return (
869            _NTLM_post_build(
870                self,
871                pkt,
872                32 + 22 + len(self.Setup) * 2,
873                {
874                    "Parameter": 7,
875                    "Data": 13,
876                },
877                config=_SMB_CONFIG,
878            )
879            + pay
880        )
881
882
883bind_top_down(SMB_Header, SMBTransaction_Response, Command=0x25, Flags=0x80)
884
885
886# [MS-ADTS] sect 6.3.1.4
887
888_NETLOGON_opcodes = {
889    0x7: "LOGON_PRIMARY_QUERY",
890    0x12: "LOGON_SAM_LOGON_REQUEST",
891    0x13: "LOGON_SAM_LOGON_RESPONSE",
892    0x15: "LOGON_SAM_USER_UNKNOWN",
893    0x17: "LOGON_SAM_LOGON_RESPONSE_EX",
894    0x19: "LOGON_SAM_USER_UNKNOWN_EX",
895}
896
897_NV_VERSION = {
898    0x00000001: "V1",
899    0x00000002: "V5",
900    0x00000004: "V5EX",
901    0x00000008: "V5EX_WITH_IP",
902    0x00000010: "V5EX_WITH_CLOSEST_SITE",
903    0x01000000: "AVOID_NT4EMUL",
904    0x10000000: "PDC",
905    0x20000000: "IP",
906    0x40000000: "LOCAL",
907    0x80000000: "GC",
908}
909
910
911class NETLOGON(Packet):
912    @classmethod
913    def dispatch_hook(cls, _pkt=None, *args, **kargs):
914        if _pkt:
915            if _pkt[0] == 0x07:  # LOGON_PRIMARY_QUERY
916                return NETLOGON_LOGON_QUERY
917            elif _pkt[0] == 0x12:  # LOGON_SAM_LOGON_REQUEST
918                return NETLOGON_SAM_LOGON_REQUEST
919            elif _pkt[0] == 0x13:  # LOGON_SAM_USER_RESPONSE
920                try:
921                    i = _pkt.index(b"\xff\xff\xff\xff")
922                    NtVersion = (
923                        NETLOGON_SAM_LOGON_RESPONSE_NT40.fields_desc[-3].getfield(
924                            None, _pkt[i - 4:i]
925                        )[1]
926                    )
927                    if NtVersion.V1 and not NtVersion.V5:
928                        return NETLOGON_SAM_LOGON_RESPONSE_NT40
929                except Exception:
930                    pass
931                return NETLOGON_SAM_LOGON_RESPONSE
932            elif _pkt[0] == 0x15:  # LOGON_SAM_USER_UNKNOWN
933                return NETLOGON_SAM_LOGON_RESPONSE
934            elif _pkt[0] == 0x17:  # LOGON_SAM_LOGON_RESPONSE_EX
935                return NETLOGON_SAM_LOGON_RESPONSE_EX
936            elif _pkt[0] == 0x19:  # LOGON_SAM_USER_UNKNOWN_EX
937                return NETLOGON_SAM_LOGON_RESPONSE
938        return cls
939
940
941class NETLOGON_LOGON_QUERY(NETLOGON):
942    fields_desc = [
943        LEShortEnumField("OpCode", 0x7, _NETLOGON_opcodes),
944        StrNullField("ComputerName", ""),
945        StrNullField("MailslotName", ""),
946        StrNullFieldUtf16("UnicodeComputerName", ""),
947        FlagsField("NtVersion", 0xB, -32, _NV_VERSION),
948        XLEShortField("LmNtToken", 0xFFFF),
949        XLEShortField("Lm20Token", 0xFFFF),
950    ]
951
952
953# [MS-ADTS] sect 6.3.1.6
954
955
956class NETLOGON_SAM_LOGON_REQUEST(NETLOGON):
957    fields_desc = [
958        LEShortEnumField("OpCode", 0x12, _NETLOGON_opcodes),
959        LEShortField("RequestCount", 0),
960        StrNullFieldUtf16("UnicodeComputerName", ""),
961        StrNullFieldUtf16("UnicodeUserName", ""),
962        StrNullField("MailslotName", "\\MAILSLOT\\NET\\GETDC701253F9"),
963        LEIntField("AllowableAccountControlBits", 0),
964        FieldLenField("DomainSidSize", None, fmt="<I", length_of="DomainSid"),
965        XStrLenField("DomainSid", b"", length_from=lambda pkt: pkt.DomainSidSize),
966        FlagsField("NtVersion", 0xB, -32, _NV_VERSION),
967        XLEShortField("LmNtToken", 0xFFFF),
968        XLEShortField("Lm20Token", 0xFFFF),
969    ]
970
971
972# [MS-ADTS] sect 6.3.1.7
973
974
975class NETLOGON_SAM_LOGON_RESPONSE_NT40(NETLOGON):
976    fields_desc = [
977        LEShortEnumField("OpCode", 0x13, _NETLOGON_opcodes),
978        StrNullFieldUtf16("UnicodeLogonServer", ""),
979        StrNullFieldUtf16("UnicodeUserName", ""),
980        StrNullFieldUtf16("UnicodeDomainName", ""),
981        FlagsField("NtVersion", 0x1, -32, _NV_VERSION),
982        XLEShortField("LmNtToken", 0xFFFF),
983        XLEShortField("Lm20Token", 0xFFFF),
984    ]
985
986
987# [MS-ADTS] sect 6.3.1.2
988
989
990_NETLOGON_FLAGS = {
991    0x00000001: "PDC",
992    0x00000004: "GC",
993    0x00000008: "LDAP",
994    0x00000010: "DC",
995    0x00000020: "KDC",
996    0x00000040: "TIMESERV",
997    0x00000080: "CLOSEST",
998    0x00000100: "RODC",
999    0x00000200: "GOOD_TIMESERV",
1000    0x00000400: "NC",
1001    0x00000800: "SELECT_SECRET_DOMAIN_6",
1002    0x00001000: "FULL_SECRET_DOMAIN_6",
1003    0x00002000: "WS",
1004    0x00004000: "DS_8",
1005    0x00008000: "DS_9",
1006    0x00010000: "DS_10",  # guess
1007    0x00020000: "DS_11",  # guess
1008    0x20000000: "DNS_CONTROLLER",
1009    0x40000000: "DNS_DOMAIN",
1010    0x80000000: "DNS_FOREST",
1011}
1012
1013
1014# [MS-ADTS] sect 6.3.1.8
1015
1016class NETLOGON_SAM_LOGON_RESPONSE(NETLOGON, DNSCompressedPacket):
1017    fields_desc = [
1018        LEShortEnumField("OpCode", 0x17, _NETLOGON_opcodes),
1019        StrNullFieldUtf16("UnicodeLogonServer", ""),
1020        StrNullFieldUtf16("UnicodeUserName", ""),
1021        StrNullFieldUtf16("UnicodeDomainName", ""),
1022        UUIDField("DomainGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
1023        UUIDField("NullGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
1024        DNSStrField("DnsForestName", ""),
1025        DNSStrField("DnsDomainName", ""),
1026        DNSStrField("DnsHostName", ""),
1027        IPField("DcIpAddress", "0.0.0.0"),
1028        FlagsField("Flags", 0, -32, _NETLOGON_FLAGS),
1029        FlagsField("NtVersion", 0x1, -32, _NV_VERSION),
1030        XLEShortField("LmNtToken", 0xFFFF),
1031        XLEShortField("Lm20Token", 0xFFFF),
1032    ]
1033
1034    def get_full(self):
1035        return self.original
1036
1037
1038# [MS-ADTS] sect 6.3.1.9
1039
1040
1041class DcSockAddr(Packet):
1042    fields_desc = [
1043        LEShortField("sin_family", 2),
1044        LEShortField("sin_port", 0),
1045        IPField("sin_addr", None),
1046        LELongField("sin_zero", 0),
1047    ]
1048
1049    def default_payload_class(self, payload):
1050        return conf.padding_layer
1051
1052
1053class NETLOGON_SAM_LOGON_RESPONSE_EX(NETLOGON, DNSCompressedPacket):
1054    fields_desc = [
1055        LEShortEnumField("OpCode", 0x17, _NETLOGON_opcodes),
1056        LEShortField("Sbz", 0),
1057        FlagsField("Flags", 0, -32, _NETLOGON_FLAGS),
1058        UUIDField("DomainGuid", None, uuid_fmt=UUIDField.FORMAT_LE),
1059        DNSStrField("DnsForestName", ""),
1060        DNSStrField("DnsDomainName", ""),
1061        DNSStrField("DnsHostName", ""),
1062        DNSStrField("NetbiosDomainName", ""),
1063        DNSStrField("NetbiosComputerName", ""),
1064        DNSStrField("UserName", ""),
1065        DNSStrField("DcSiteName", "Default-First-Site-Name"),
1066        DNSStrField("ClientSiteName", "Default-First-Site-Name"),
1067        ConditionalField(
1068            ByteField("DcSockAddrSize", 0x10),
1069            lambda pkt: pkt.NtVersion.V5EX_WITH_IP,
1070        ),
1071        ConditionalField(
1072            PacketField("DcSockAddr", DcSockAddr(), DcSockAddr),
1073            lambda pkt: pkt.NtVersion.V5EX_WITH_IP,
1074        ),
1075        ConditionalField(
1076            DNSStrField("NextClosestSiteName", ""),
1077            lambda pkt: pkt.NtVersion.V5EX_WITH_CLOSEST_SITE,
1078        ),
1079        FlagsField("NtVersion", 0xB, -32, _NV_VERSION),
1080        XLEShortField("LmNtToken", 0xFFFF),
1081        XLEShortField("Lm20Token", 0xFFFF),
1082    ]
1083
1084    def pre_dissect(self, s):
1085        try:
1086            i = s.index(b"\xff\xff\xff\xff")
1087            self.fields["NtVersion"] = self.fields_desc[-3].getfield(
1088                self,
1089                s[i - 4:i]
1090            )[1]
1091        except Exception:
1092            self.NtVersion = 0xB
1093        return s
1094
1095    def get_full(self):
1096        return self.original
1097
1098
1099# [MS-BRWS] sect 2.2
1100
1101class BRWS(Packet):
1102    fields_desc = [
1103        ByteEnumField("OpCode", 0x00, {
1104            0x01: "HostAnnouncement",
1105            0x02: "AnnouncementRequest",
1106            0x08: "RequestElection",
1107            0x09: "GetBackupListRequest",
1108            0x0A: "GetBackupListResponse",
1109            0x0B: "BecomeBackup",
1110            0x0C: "DomainAnnouncement",
1111            0x0D: "MasterAnnouncement",
1112            0x0E: "ResetStateRequest",
1113            0x0F: "LocalMasterAnnouncement",
1114        }),
1115    ]
1116
1117    def mysummary(self):
1118        return self.sprintf("%OpCode%")
1119
1120    registered_opcodes = {}
1121
1122    @classmethod
1123    def register_variant(cls):
1124        cls.registered_opcodes[cls.OpCode.default] = cls
1125
1126    @classmethod
1127    def dispatch_hook(cls, _pkt=None, *args, **kargs):
1128        if _pkt:
1129            return cls.registered_opcodes.get(_pkt[0], cls)
1130        return cls
1131
1132    def default_payload_class(self, payload):
1133        return conf.padding_layer
1134
1135
1136# [MS-BRWS] sect 2.2.1
1137
1138class BRWS_HostAnnouncement(BRWS):
1139    OpCode = 0x01
1140    fields_desc = [
1141        BRWS,
1142        ByteField("UpdateCount", 0),
1143        LEIntField("Periodicity", 128000),
1144        StrFixedLenField("ServerName", b"", length=16),
1145        ByteField("OSVersionMajor", 6),
1146        ByteField("OSVersionMinor", 1),
1147        LEIntField("ServerType", 4611),
1148        ByteField("BrowserConfigVersionMajor", 21),
1149        ByteField("BrowserConfigVersionMinor", 1),
1150        XLEShortField("Signature", 0xAA55),
1151        StrNullField("Comment", ""),
1152    ]
1153
1154    def mysummary(self):
1155        return self.sprintf("%OpCode% for %ServerName%")
1156
1157
1158# [MS-BRWS] sect 2.2.6
1159
1160class BRWS_BecomeBackup(BRWS):
1161    OpCode = 0x0B
1162    fields_desc = [
1163        BRWS,
1164        StrNullField("BrowserToPromote", b""),
1165    ]
1166
1167    def mysummary(self):
1168        return self.sprintf("%OpCode% from %BrowserToPromote%")
1169
1170
1171# [MS-BRWS] sect 2.2.10
1172
1173class BRWS_LocalMasterAnnouncement(BRWS_HostAnnouncement):
1174    OpCode = 0x0F
1175
1176
1177# SMB dispatcher
1178
1179
1180class _SMBGeneric(Packet):
1181    name = "SMB Generic dispatcher"
1182    fields_desc = [StrFixedLenField("Start", b"\xffSMB", 4)]
1183
1184    @classmethod
1185    def dispatch_hook(cls, _pkt=None, *args, **kargs):
1186        """
1187        Depending on the first 4 bytes of the packet,
1188        dispatch to the correct version of Header
1189        (either SMB or SMB2)
1190        """
1191        if _pkt and len(_pkt) >= 4:
1192            if _pkt[:4] == b"\xffSMB":
1193                return SMB_Header
1194            if _pkt[:4] == b"\xfeSMB":
1195                return SMB2_Header
1196        return cls
1197
1198
1199bind_layers(NBTSession, _SMBGeneric)
1200bind_layers(NBTDatagram, _SMBGeneric)
1201