1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """Decompression support for the LZNT1 compression algorithm.
22
23 Reference:
24 http://msdn.microsoft.com/en-us/library/jj665697.aspx
25 (2.5 LZNT1 Algorithm Details)
26
27 https://github.com/libyal/reviveit/
28 https://github.com/sleuthkit/sleuthkit/blob/develop/tsk/fs/ntfs.c
29 """
30 import array
31 import cStringIO
32 import logging
33 import struct
34
35
36
38 """Calculate the displacement."""
39 result = 0
40 while offset >= 0x10:
41 offset >>= 1
42 result += 1
43
44 return result
45
46
47 DISPLACEMENT_TABLE = array.array(
48 'B', [get_displacement(x) for x in xrange(8192)])
49
50 COMPRESSED_MASK = 1 << 15
51 SIGNATURE_MASK = 3 << 12
52 SIZE_MASK = (1 << 12) - 1
53 TAG_MASKS = [(1 << i) for i in range(0, 8)]
54
55
57 """Decompresses the data."""
58
59 if not logger:
60 lznt1_logger = logging.getLogger("ntfs.lznt1")
61 else:
62 lznt1_logger = logger.getChild("lznt1")
63
64 lznt1_logger.setLevel(logging.ERROR)
65 in_fd = cStringIO.StringIO(cdata)
66 output_fd = cStringIO.StringIO()
67 block_end = 0
68
69 while in_fd.tell() < len(cdata):
70 block_offset = in_fd.tell()
71 uncompressed_chunk_offset = output_fd.tell()
72
73 block_header = struct.unpack("<H", in_fd.read(2))[0]
74 lznt1_logger.debug("Header %#x @ %#x", block_header, block_offset)
75 if block_header & SIGNATURE_MASK != SIGNATURE_MASK:
76 break
77
78 size = (block_header & SIZE_MASK)
79 lznt1_logger.debug("Block size %s", size + 3)
80
81 block_end = block_offset + size + 3
82
83 if block_header & COMPRESSED_MASK:
84 while in_fd.tell() < block_end:
85 header = ord(in_fd.read(1))
86 lznt1_logger.debug("Tag %#x", header)
87 for mask in TAG_MASKS:
88 if in_fd.tell() >= block_end:
89 break
90
91 if header & mask:
92 pointer = struct.unpack("<H", in_fd.read(2))[0]
93 displacement = DISPLACEMENT_TABLE[
94 output_fd.tell() - uncompressed_chunk_offset - 1]
95
96 symbol_offset = (pointer >> (12 - displacement)) + 1
97 symbol_length = (pointer & (0xFFF >> displacement)) + 3
98
99 output_fd.seek(-symbol_offset, 2)
100 data = output_fd.read(symbol_length)
101
102
103 if 0 < len(data) < symbol_length:
104 data = data * (symbol_length / len(data) + 1)
105 data = data[:symbol_length]
106
107 output_fd.seek(0, 2)
108 lznt1_logger.debug(
109 "Wrote %s @ %s/%s: Phrase %s %s %x",
110 len(data), in_fd.tell(),
111 output_fd.tell(), symbol_length, symbol_offset,
112 pointer)
113
114 output_fd.write(data)
115
116 else:
117 data = in_fd.read(1)
118 lznt1_logger.debug("Symbol %#x", ord(data))
119 output_fd.write(data)
120
121 else:
122
123 data = in_fd.read(size + 1)
124 output_fd.write(data)
125
126 result = output_fd.getvalue()
127
128 return result
129