Skip to content

Commit a63c652

Browse files
Structure encoding feature (#1958)
* Structure: Adds a class variable "ENCODING" which specifies which encoding to use in case that a unicode string needs to be encoded (while packing/unpacking), defaults to latin-1 to keep compatibility with older versions * Changed AsciiOrUnicodeStructure encoding from latin-1 to utf-8, fixes issue with share names containing non latin-1 characters. * Re-added whitespaces Added comment linking to PR and a brief explanation * Re added whitespaces
1 parent 75707ea commit a63c652

File tree

2 files changed

+26
-16
lines changed

2 files changed

+26
-16
lines changed

impacket/smb.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,8 @@ def __init__(self, commandOrData = None, data = None, **kargs):
814814
class AsciiOrUnicodeStructure(Structure):
815815
UnicodeStructure = ()
816816
AsciiStructure = ()
817+
ENCODING = 'utf-8'
818+
817819
def __init__(self, flags = 0, **kargs):
818820
if flags & SMB.FLAGS2_UNICODE:
819821
self.structure = self.UnicodeStructure

impacket/structure.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from __future__ import division
1313
from __future__ import print_function
1414
from struct import pack, unpack, calcsize
15+
16+
import six
1517
from six import b, PY3
1618
from binascii import hexlify
1719

@@ -80,13 +82,19 @@ class is the class to use when unpacking ':' fields.
8082
commonHdr = ()
8183
structure = ()
8284
debug = 0
85+
# Encoding defaults to latin-1 which already was the de facto encoding for structures and works for most use cases.
86+
# Now it can be configured to another encoding if needed.
87+
ENCODING = 'latin-1' # https://github.com/fortra/impacket/pull/1958
8388

8489
def __init__(self, data = None, alignment = 0):
8590
if not hasattr(self, 'alignment'):
8691
self.alignment = alignment
8792

8893
self.fields = {}
8994
self.rawData = data
95+
96+
self.b = lambda x: six.ensure_binary(x, encoding=self.ENCODING)
97+
9098
if data is not None:
9199
self.fromString(data)
92100
else:
@@ -197,7 +205,7 @@ def pack(self, format, data, field = None):
197205

198206
# quote specifier
199207
if format[:1] == "'" or format[:1] == '"':
200-
return b(format[1:])
208+
return self.b(format[1:])
201209

202210
# code specifier
203211
two = format.split('=')
@@ -245,26 +253,26 @@ def pack(self, format, data, field = None):
245253
# "printf" string specifier
246254
if format[:1] == '%':
247255
# format string like specifier
248-
return b(format % data)
256+
return self.b(format % data)
249257

250258
# asciiz specifier
251259
if format[:1] == 'z':
252260
if isinstance(data,bytes):
253-
return data + b('\0')
254-
return bytes(b(data)+b('\0'))
261+
return data + self.b('\0')
262+
return bytes(self.b(data)+self.b('\0'))
255263

256264
# unicode specifier
257265
if format[:1] == 'u':
258-
return bytes(data+b('\0\0') + (len(data) & 1 and b('\0') or b''))
266+
return bytes(data+self.b('\0\0') + (len(data) & 1 and self.b('\0') or b''))
259267

260268
# DCE-RPC/NDR string specifier
261269
if format[:1] == 'w':
262270
if len(data) == 0:
263-
data = b('\0\0')
271+
data = self.b('\0\0')
264272
elif len(data) % 2:
265-
data = b(data) + b('\0')
273+
data = self.b(data) + self.b('\0')
266274
l = pack('<L', len(data)//2)
267-
return b''.join([l, l, b('\0\0\0\0'), data])
275+
return b''.join([l, l, self.b('\0\0\0\0'), data])
268276

269277
if data is None:
270278
raise Exception("Trying to pack None")
@@ -279,7 +287,7 @@ def pack(self, format, data, field = None):
279287
elif isinstance(data, int):
280288
return bytes(data)
281289
elif isinstance(data, bytes) is not True:
282-
return bytes(b(data))
290+
return bytes(self.b(data))
283291
else:
284292
return data
285293

@@ -288,7 +296,7 @@ def pack(self, format, data, field = None):
288296
if isinstance(data, bytes) or isinstance(data, bytearray):
289297
return pack(format, data)
290298
else:
291-
return pack(format, b(data))
299+
return pack(format, self.b(data))
292300

293301
# struct like specifier
294302
return pack(format, data)
@@ -315,7 +323,7 @@ def unpack(self, format, data, dataClassOrCode = b, field = None):
315323
# quote specifier
316324
if format[:1] == "'" or format[:1] == '"':
317325
answer = format[1:]
318-
if b(answer) != data:
326+
if self.b(answer) != data:
319327
raise Exception("Unpacked data doesn't match constant value '%r' should be '%r'" % (data, answer))
320328
return answer
321329

@@ -361,7 +369,7 @@ def unpack(self, format, data, dataClassOrCode = b, field = None):
361369

362370
# asciiz specifier
363371
if format == 'z':
364-
if data[-1:] != b('\x00'):
372+
if data[-1:] != self.b('\x00'):
365373
raise Exception("%s 'z' field is not NUL terminated: %r" % (field, data))
366374
if PY3:
367375
return data[:-1].decode('latin-1')
@@ -370,7 +378,7 @@ def unpack(self, format, data, dataClassOrCode = b, field = None):
370378

371379
# unicode specifier
372380
if format == 'u':
373-
if data[-2:] != b('\x00\x00'):
381+
if data[-2:] != self.b('\x00\x00'):
374382
raise Exception("%s 'u' field is not NUL-NUL terminated: %r" % (field, data))
375383
return data[:-2] # remove trailing NUL
376384

@@ -524,11 +532,11 @@ def calcUnpackSize(self, format, data, field = None):
524532

525533
# asciiz specifier
526534
if format[:1] == 'z':
527-
return data.index(b('\x00'))+1
535+
return data.index(self.b('\x00'))+1
528536

529537
# asciiz specifier
530538
if format[:1] == 'u':
531-
l = data.index(b('\x00\x00'))
539+
l = data.index(self.b('\x00\x00'))
532540
return l + (l & 1 and 3 or 2)
533541

534542
# DCE-RPC/NDR string specifier
@@ -584,7 +592,7 @@ def zeroValue(self, format):
584592
if format in ['z',':','u']:
585593
return b''
586594
if format == 'w':
587-
return b('\x00\x00')
595+
return self.b('\x00\x00')
588596

589597
return 0
590598

0 commit comments

Comments
 (0)