Commit 82bb3107927ea4f4356789843698cba652713dd6

Authored by Vincent Brillault
1 parent c5d4ec7b

WIP: Separate object (ExtendedStream) from logic (functions)

oletools/oleform.py
@@ -49,33 +49,43 @@ class MorphDataPropMask(Mask): @@ -49,33 +49,43 @@ class MorphDataPropMask(Mask):
49 'fBorderColor', 'fSpecialEffect', 'fMouseIcon', 'fPicture', 'fAccelerator', 49 'fBorderColor', 'fSpecialEffect', 'fMouseIcon', 'fPicture', 'fAccelerator',
50 'UnusedBits2', 'Reserved', 'fGroupName'] 50 'UnusedBits2', 'Reserved', 'fGroupName']
51 51
52 -class OleUserFormParser(object):  
53 - def __init__(self, control_stream, data_stream):  
54 - self.variables = []  
55 - self.set_stream(control_stream)  
56 - self.consume_FormControl()  
57 - self.set_stream(data_stream)  
58 - self.consume_stored_data()  
59 -  
60 - def set_stream(self, stream): 52 +class ExtendedStream(object):
  53 + def __init__(self, stream, path):
61 self._pos = 0 54 self._pos = 0
62 - self._frozen_pos = [] 55 + self._jumps = []
63 self._stream = stream 56 self._stream = stream
  57 + self._path = path
  58 +
  59 + @classmethod
  60 + def open(cls, ole_file, path):
  61 + stream = ole_file.openstream(path)
  62 + return cls(stream, path)
64 63
65 def read(self, size): 64 def read(self, size):
66 self._pos += size 65 self._pos += size
67 return self._stream.read(size) 66 return self._stream.read(size)
68 67
69 - def freeze(self):  
70 - self._frozen_pos.append(self._pos) 68 + def will_jump_to(self, size):
  69 + self._next_jump = (True, size)
  70 + return self
  71 +
  72 + def will_pad(self, pad=4):
  73 + self._next_jump = (False, pad)
  74 + return self
71 75
72 - def unfreeze(self, size):  
73 - self.read(self._frozen_pos.pop() - self._pos + size) 76 + def __enter__(self):
  77 + (jump_type, size) = self._next_jump
  78 + self._jumps.append((self._pos, jump_type, size))
74 79
75 - def unfreeze_pad(self):  
76 - align_pos = (self._pos - self._frozen_pos.pop()) % 4  
77 - if align_pos:  
78 - self.read(4 - align_pos) 80 + def __exit__(self, exc_type, exc_value, traceback):
  81 + if exc_type is None:
  82 + (start, jump_type, size) = self._jumps.pop()
  83 + if jump_type:
  84 + self.read(size - (self._pos - start))
  85 + else:
  86 + align = (self._pos - start) % size
  87 + if align:
  88 + self.read(size - align)
79 89
80 def unpacks(self, format, size): 90 def unpacks(self, format, size):
81 return struct.unpack(format, self.read(size)) 91 return struct.unpack(format, self.read(size))
@@ -83,177 +93,174 @@ class OleUserFormParser(object): @@ -83,177 +93,174 @@ class OleUserFormParser(object):
83 def unpack(self, format, size): 93 def unpack(self, format, size):
84 return self.unpacks(format, size)[0] 94 return self.unpacks(format, size)[0]
85 95
  96 + def raise_error(self, reason, back=0):
  97 + raise OleFormParsingError('{0}:{1}: {2}'.format(self.path, self._pos - back))
  98 +
86 def check_values(self, name, format, size, expected): 99 def check_values(self, name, format, size, expected):
87 value = self.unpacks(format, size) 100 value = self.unpacks(format, size)
88 if value != expected: 101 if value != expected:
89 - raise OleFormParsingError('Invalid {0} at {1}: expected {2} got {3}'.format(name, self._pos - size, str(expected), str(value))) 102 + self.raise_error('Invalid {0}: expected {1} got {2}'.format(name, str(expected), str(value)))
90 103
91 def check_value(self, name, format, size, expected): 104 def check_value(self, name, format, size, expected):
92 self.check_values(name, format, size, (expected,)) 105 self.check_values(name, format, size, (expected,))
93 106
94 - def consume_TextProps(self):  
95 - # TextProps: [MS-OFORMS] 2.3.1  
96 - self.check_values('TextProps (versions)', '<BB', 2, (0, 2))  
97 - cbTextProps = self.unpack('<H', 2)  
98 - self.read(cbTextProps)  
99 -  
100 - def consume_GuidAndFont(self):  
101 - # GuidAndFont: [MS-OFORMS] 2.4.7  
102 - UUIDS = self.unpacks('<LHH', 8) + self.unpacks('>Q', 8)  
103 - if UUIDS == (199447043, 36753, 4558, 11376937813817407569L):  
104 - # UUID == {0BE35203-8F91-11CE-9DE300AA004BB851}  
105 - # StdFont: [MS-OFORMS] 2.4.12  
106 - self.check_value('StdFont (version)', '<B', 1, 1)  
107 - # Skip sCharset, bFlags, sWeight, ulHeight  
108 - self.read(9)  
109 - bFaceLen = self.unpack('<B', 1)  
110 - self.read(bFaceLen)  
111 - elif UUIDs == (2948729120, 55886, 4558, 13349514450607572916L):  
112 - # UUID == {AFC20920-DA4E-11CE-B94300AA006887B4}  
113 - self.consume_TextProps()  
114 - else:  
115 - raise OleFormParsingError('Invalid GuidAndFont at {0}: UUID'.format(self._pos - 16))  
116 -  
117 - def consume_GuidAndPicture(self):  
118 - # GuidAndPicture: [MS-OFORMS] 2.4.8  
119 - # UUID == {0BE35204-8F91-11CE-9DE3-00AA004BB851}  
120 - self.check_values('GuidAndPicture (UUID part 1)', '<LHH', 8, (199447044, 36753, 4558))  
121 - self.check_value('GuidAndPicture (UUID part 1)', '>Q', 8, 11376937813817407569L)  
122 - # StdPicture: [MS-OFORMS] 2.4.13  
123 - self.check_value('StdPicture (Preamble)', '<L', 4, 0x0000746C)  
124 - size = self.unpack('<L', 4)  
125 - self.read(size)  
126 -  
127 - def consume_CountOfBytesWithCompressionFlag(self):  
128 - # CountOfBytesWithCompressionFlag or CountOfCharsWithCompressionFlag: [MS-OFORMS] 2.4.14.2 or 2.4.14.3  
129 - count = self.unpack('<L', 4)  
130 - if not count & 0x80000000 and count != 0:  
131 - raise OleFormParsingError('Uncompress string length at {0}', self._pos - 4)  
132 - return count & 0x7FFFFFFF  
133 -  
134 - def consume_SiteClassInfo(self):  
135 - # SiteClassInfo: [MS-OFORMS] 2.2.10.10.1  
136 - self.check_value('SiteClassInfo (version)', '<H', 2, 0)  
137 - cbClassTable = self.unpack('<H', 2)  
138 - self.read(cbClassTable)  
139 -  
140 - def consume_FormObjectDepthTypeCount(self):  
141 - # FormObjectDepthTypeCount: [MS-OFORMS] 2.2.10.7  
142 - (depth, mixed) = self.unpacks('<BB', 2)  
143 - if mixed & 0x80:  
144 - self.check_value('FormObjectDepthTypeCount (SITE_TYPE)', '<B', 1, 1)  
145 - return mixed ^ 0x80  
146 - if mixed != 1:  
147 - raise OleFormParsingError('Invalid FormObjectDepthTypeCount (SITE_TYPE) at {0}: expected 1 got {3}'.format(self._pos - 2, str(mixed)))  
148 - return 1  
149 -  
150 - def consume_OleSiteConcreteControl(self):  
151 - # OleSiteConcreteControl: [MS-OFORMS] 2.2.10.12.1  
152 - self.check_value('OleSiteConcreteControl (version)', '<H', 2, 0)  
153 - cbSite = self.unpack('<H', 2)  
154 - self.freeze()  
155 - propmask = SitePropMask(self.unpack('<L', 4)) 107 +
  108 +def consume_TextProps(stream):
  109 + # TextProps: [MS-OFORMS] 2.3.1
  110 + stream.check_values('TextProps (versions)', '<BB', 2, (0, 2))
  111 + cbTextProps = stream.unpack('<H', 2)
  112 + stream.read(cbTextProps)
  113 +
  114 +def consume_GuidAndFont(stream):
  115 + # GuidAndFont: [MS-OFORMS] 2.4.7
  116 + UUIDS = stream.unpacks('<LHH', 8) + stream.unpacks('>Q', 8)
  117 + if UUIDS == (199447043, 36753, 4558, 11376937813817407569L):
  118 + # UUID == {0BE35203-8F91-11CE-9DE300AA004BB851}
  119 + # StdFont: [MS-OFORMS] 2.4.12
  120 + stream.check_value('StdFont (version)', '<B', 1, 1)
  121 + # Skip sCharset, bFlags, sWeight, ulHeight
  122 + stream.read(9)
  123 + bFaceLen = stream.unpack('<B', 1)
  124 + stream.read(bFaceLen)
  125 + elif UUIDs == (2948729120, 55886, 4558, 13349514450607572916L):
  126 + # UUID == {AFC20920-DA4E-11CE-B94300AA006887B4}
  127 + consume_TextProps(stream)
  128 + else:
  129 + stream.raise_error('Invalid GuidAndFont (UUID)', 16)
  130 +
  131 +def consume_GuidAndPicture(stream):
  132 + # GuidAndPicture: [MS-OFORMS] 2.4.8
  133 + # UUID == {0BE35204-8F91-11CE-9DE3-00AA004BB851}
  134 + stream.check_values('GuidAndPicture (UUID part 1)', '<LHH', 8, (199447044, 36753, 4558))
  135 + stream.check_value('GuidAndPicture (UUID part 1)', '>Q', 8, 11376937813817407569L)
  136 + # StdPicture: [MS-OFORMS] 2.4.13
  137 + stream.check_value('StdPicture (Preamble)', '<L', 4, 0x0000746C)
  138 + size = stream.unpack('<L', 4)
  139 + stream.read(size)
  140 +
  141 +def consume_CountOfBytesWithCompressionFlag(stream):
  142 + # CountOfBytesWithCompressionFlag or CountOfCharsWithCompressionFlag: [MS-OFORMS] 2.4.14.2 or 2.4.14.3
  143 + count = stream.unpack('<L', 4)
  144 + if not count & 0x80000000 and count != 0:
  145 + stream.aise_error('Uncompress string length', 4)
  146 + return count & 0x7FFFFFFF
  147 +
  148 +def consume_SiteClassInfo(stream):
  149 + # SiteClassInfo: [MS-OFORMS] 2.2.10.10.1
  150 + stream.check_value('SiteClassInfo (version)', '<H', 2, 0)
  151 + cbClassTable = stream.unpack('<H', 2)
  152 + stream.read(cbClassTable)
  153 +
  154 +def consume_FormObjectDepthTypeCount(stream):
  155 + # FormObjectDepthTypeCount: [MS-OFORMS] 2.2.10.7
  156 + (depth, mixed) = stream.unpacks('<BB', 2)
  157 + if mixed & 0x80:
  158 + stream.check_value('FormObjectDepthTypeCount (SITE_TYPE)', '<B', 1, 1)
  159 + return mixed ^ 0x80
  160 + if mixed != 1:
  161 + stream.raise_error('Invalid FormObjectDepthTypeCount (SITE_TYPE): expected 1 got {0}'.format(str(mixed)))
  162 + return 1
  163 +
  164 +def consume_OleSiteConcreteControl(stream):
  165 + # OleSiteConcreteControl: [MS-OFORMS] 2.2.10.12.1
  166 + stream.check_value('OleSiteConcreteControl (version)', '<H', 2, 0)
  167 + cbSite = stream.unpack('<H', 2)
  168 + with stream.will_jump_to(cbSite):
  169 + propmask = SitePropMask(stream.unpack('<L', 4))
156 # SiteDataBlock: [MS-OFORMS] 2.2.10.12.3 170 # SiteDataBlock: [MS-OFORMS] 2.2.10.12.3
157 name_len = tag_len = id = 0 171 name_len = tag_len = id = 0
158 if propmask.fName: 172 if propmask.fName:
159 - name_len = self.consume_CountOfBytesWithCompressionFlag() 173 + name_len = consume_CountOfBytesWithCompressionFlag(stream)
160 if propmask.fTag: 174 if propmask.fTag:
161 - tag_len = self.consume_CountOfBytesWithCompressionFlag() 175 + tag_len = consume_CountOfBytesWithCompressionFlag(stream)
162 if propmask.fID: 176 if propmask.fID:
163 - id = self.unpack('<L', 4) 177 + id = stream.unpack('<L', 4)
164 for prop in ['fHelpContextID', 'fBitFlags', 'fObjectStreamSize']: 178 for prop in ['fHelpContextID', 'fBitFlags', 'fObjectStreamSize']:
165 if propmask[prop]: 179 if propmask[prop]:
166 - self.read(4) 180 + stream.read(4)
167 tabindex = ClsidCacheIndex = 0 181 tabindex = ClsidCacheIndex = 0
168 - self.freeze()  
169 - if propmask.fTabIndex:  
170 - tabindex = self.unpack('<H', 2)  
171 - if propmask.fClsidCacheIndex:  
172 - ClsidCacheIndex = self.unpack('<H', 2)  
173 - if propmask.fGroupID:  
174 - self.read(2)  
175 - self.unfreeze_pad() 182 + with stream.will_pad():
  183 + if propmask.fTabIndex:
  184 + tabindex = stream.unpack('<H', 2)
  185 + if propmask.fClsidCacheIndex:
  186 + ClsidCacheIndex = stream.unpack('<H', 2)
  187 + if propmask.fGroupID:
  188 + stream.read(2)
176 # For the next 4 entries, the documentation adds padding, but it should already be aligned?? 189 # For the next 4 entries, the documentation adds padding, but it should already be aligned??
177 for prop in ['fControlTipText', 'fRuntimeLicKey', 'fControlSource', 'fRowSource']: 190 for prop in ['fControlTipText', 'fRuntimeLicKey', 'fControlSource', 'fRowSource']:
178 if propmask[prop]: 191 if propmask[prop]:
179 - self.read(4) 192 + stream.read(4)
180 # SiteExtraDataBlock: [MS-OFORMS] 2.2.10.12.4 193 # SiteExtraDataBlock: [MS-OFORMS] 2.2.10.12.4
181 - name = self.read(name_len)  
182 - tag = self.read(tag_len)  
183 - self.variables.append({'name': name, 'tag': tag, 'id': id,  
184 - 'tabindex': tabindex,  
185 - 'ClsidCacheIndex': ClsidCacheIndex})  
186 - self.unfreeze(cbSite)  
187 -  
188 - def consume_FormControl(self):  
189 - # FormControl: [MS-OFORMS] 2.2.10.1  
190 - self.check_values('FormControl (versions)', '<BB', 2, (0, 4))  
191 - cbform = self.unpack('<H', 2)  
192 - self.freeze()  
193 - propmask = FormPropMask(self.unpack('<L', 4)) 194 + name = stream.read(name_len)
  195 + tag = stream.read(tag_len)
  196 + return {'name': name, 'tag': tag, 'id': id, 'tabindex': tabindex,
  197 + 'ClsidCacheIndex': ClsidCacheIndex}
  198 +
  199 +def consume_FormControl(stream):
  200 + # FormControl: [MS-OFORMS] 2.2.10.1
  201 + stream.check_values('FormControl (versions)', '<BB', 2, (0, 4))
  202 + cbform = stream.unpack('<H', 2)
  203 + with stream.will_jump_to(cbform):
  204 + propmask = FormPropMask(stream.unpack('<L', 4))
194 # FormDataBlock: [MS-OFORMS] 2.2.10.3 205 # FormDataBlock: [MS-OFORMS] 2.2.10.3
195 for prop in ['fBackColor', 'fForeColor', 'fNextAvailableID']: 206 for prop in ['fBackColor', 'fForeColor', 'fNextAvailableID']:
196 if propmask[prop]: 207 if propmask[prop]:
197 - self.read(4) 208 + stream.read(4)
198 if propmask.fBooleanProperties: 209 if propmask.fBooleanProperties:
199 - BooleanProperties = self.unpack('<L', 4) 210 + BooleanProperties = stream.unpack('<L', 4)
200 FORM_FLAG_DONTSAVECLASSTABLE = (BooleanProperties & (1<<15)) >> 15 211 FORM_FLAG_DONTSAVECLASSTABLE = (BooleanProperties & (1<<15)) >> 15
201 else: 212 else:
202 FORM_FLAG_DONTSAVECLASSTABLE = 0 213 FORM_FLAG_DONTSAVECLASSTABLE = 0
203 # Skip the rest of DataBlock and ExtraDataBlock 214 # Skip the rest of DataBlock and ExtraDataBlock
204 - self.unfreeze(cbform)  
205 - # FormStreamData: [MS-OFORMS] 2.2.10.5  
206 - if propmask.fMouseIcon:  
207 - self.consume_GuidAndPicture()  
208 - if propmask.fFont:  
209 - self.consume_GuidAndFont()  
210 - if propmask.fPicture:  
211 - self.consume_GuidAndPicture()  
212 - # FormSiteData: [MS-OFORMS] 2.2.10.6  
213 - if not FORM_FLAG_DONTSAVECLASSTABLE:  
214 - CountOfSiteClassInfo = self.unpack('<H', 2)  
215 - for i in range(CountOfSiteClassInfo):  
216 - self.consume_SiteClassInfo()  
217 - (CountOfSites, CountOfBytes) = self.unpacks('<LL', 8)  
218 - remaining_SiteDepthsAndTypes = CountOfSites  
219 - self.freeze() 215 + # FormStreamData: [MS-OFORMS] 2.2.10.5
  216 + if propmask.fMouseIcon:
  217 + consume_GuidAndPicture(stream)
  218 + if propmask.fFont:
  219 + consume_GuidAndFont(stream)
  220 + if propmask.fPicture:
  221 + consume_GuidAndPicture(stream)
  222 + # FormSiteData: [MS-OFORMS] 2.2.10.6
  223 + if not FORM_FLAG_DONTSAVECLASSTABLE:
  224 + CountOfSiteClassInfo = stream.unpack('<H', 2)
  225 + for i in range(CountOfSiteClassInfo):
  226 + consume_SiteClassInfo(stream)
  227 + (CountOfSites, CountOfBytes) = stream.unpacks('<LL', 8)
  228 + remaining_SiteDepthsAndTypes = CountOfSites
  229 + with stream.will_pad():
220 while remaining_SiteDepthsAndTypes > 0: 230 while remaining_SiteDepthsAndTypes > 0:
221 - remaining_SiteDepthsAndTypes -= self.consume_FormObjectDepthTypeCount()  
222 - self.unfreeze_pad()  
223 - for i in range(CountOfSites):  
224 - self.consume_OleSiteConcreteControl()  
225 -  
226 - def consume_MorphDataControl(self):  
227 - # MorphDataControl: [MS-OFORMS] 2.2.5.1  
228 - self.check_values('MorphDataControl (versions)', '<BB', 2, (0, 2))  
229 - cbMorphData = self.unpack('<H', 2)  
230 - self.freeze()  
231 - propmask = MorphDataPropMask(self.unpack('<Q', 8)) 231 + remaining_SiteDepthsAndTypes -= consume_FormObjectDepthTypeCount(stream)
  232 + for i in range(CountOfSites):
  233 + yield consume_OleSiteConcreteControl(stream)
  234 +
  235 +def consume_MorphDataControl(stream):
  236 + # MorphDataControl: [MS-OFORMS] 2.2.5.1
  237 + stream.check_values('MorphDataControl (versions)', '<BB', 2, (0, 2))
  238 + cbMorphData = stream.unpack('<H', 2)
  239 + with stream.will_jump_to(cbMorphData):
  240 + propmask = MorphDataPropMask(stream.unpack('<Q', 8))
232 # MorphDataDataBlock: [MS-OFORMS] 2.2.5.3 241 # MorphDataDataBlock: [MS-OFORMS] 2.2.5.3
233 for prop in ['fVariousPropertyBits', 'fBackColor', 'fForeColor', 'fMaxLength']: 242 for prop in ['fVariousPropertyBits', 'fBackColor', 'fForeColor', 'fMaxLength']:
234 if propmask[prop]: 243 if propmask[prop]:
235 - self.read(4)  
236 - self.freeze()  
237 - for prop in ['fBorderStyle', 'fScrollBars', 'fDisplayStyle', 'fMousePointer']:  
238 - if propmask[prop]:  
239 - self.read(1)  
240 - self.unfreeze_pad() 244 + stream.read(4)
  245 + with stream.will_pad():
  246 + for prop in ['fBorderStyle', 'fScrollBars', 'fDisplayStyle', 'fMousePointer']:
  247 + if propmask[prop]:
  248 + stream.read(1)
241 # PasswordChar, BoundColumn, TextColumn, ColumnCount, and ListRows are 2B + pad = 4B 249 # PasswordChar, BoundColumn, TextColumn, ColumnCount, and ListRows are 2B + pad = 4B
242 # ListWidth is 4B + pad = 4B 250 # ListWidth is 4B + pad = 4B
243 for prop in ['fPasswordChar', 'fListWidth', 'fBoundColumn', 'fTextColumn', 'fColumnCount', 251 for prop in ['fPasswordChar', 'fListWidth', 'fBoundColumn', 'fTextColumn', 'fColumnCount',
244 'fListRows']: 252 'fListRows']:
245 if propmask[prop]: 253 if propmask[prop]:
246 - self.read(4)  
247 - self.freeze()  
248 - if propmask.fcColumnInfo:  
249 - self.read(2)  
250 - for prop in ['fMatchEntry', 'fListStyle', 'fShowDropButtonWhen', 'fDropButtonStyle',  
251 - 'fMultiSelect']:  
252 - if propmask[prop]:  
253 - self.read(1)  
254 - self.unfreeze_pad() 254 + stream.read(4)
  255 + with stream.will_pad():
  256 + if propmask.fcColumnInfo:
  257 + stream.read(2)
  258 + for prop in ['fMatchEntry', 'fListStyle', 'fShowDropButtonWhen', 'fDropButtonStyle',
  259 + 'fMultiSelect']:
  260 + if propmask[prop]:
  261 + stream.read(1)
255 if propmask.fValue: 262 if propmask.fValue:
256 - value_size = self.consume_CountOfBytesWithCompressionFlag() 263 + value_size = consume_CountOfBytesWithCompressionFlag(stream)
257 else: 264 else:
258 value_size = 0 265 value_size = 0
259 # Caption, PicturePosition, BorderColor, SpecialEffect, GroupName are 4B + pad = 4B 266 # Caption, PicturePosition, BorderColor, SpecialEffect, GroupName are 4B + pad = 4B
@@ -261,27 +268,24 @@ class OleUserFormParser(object): @@ -261,27 +268,24 @@ class OleUserFormParser(object):
261 for prop in ['fCaption', 'fPicturePosition', 'fBorderColor', 'fSpecialEffect', 268 for prop in ['fCaption', 'fPicturePosition', 'fBorderColor', 'fSpecialEffect',
262 'fMouseIcon', 'fPicture', 'fAccelerator', 'fGroupName']: 269 'fMouseIcon', 'fPicture', 'fAccelerator', 'fGroupName']:
263 if propmask[prop]: 270 if propmask[prop]:
264 - self.read(4) 271 + stream.read(4)
265 # MorphDataExtraDataBlock: [MS-OFORMS] 2.2.5.4 272 # MorphDataExtraDataBlock: [MS-OFORMS] 2.2.5.4
266 - self.read(8)  
267 - value = self.read(value_size)  
268 - self.unfreeze(cbMorphData)  
269 - # MorphDataStreamData: [MS-OFORMS] 2.2.5.5  
270 - if propmask.fMouseIcon:  
271 - self.consume_GuidAndPicture()  
272 - if propmask.fPicture:  
273 - self.consume_GuidAndPicture()  
274 - self.consume_TextProps()  
275 - return value  
276 -  
277 - def consume_stored_data(self):  
278 - for var in self.variables:  
279 - if var['ClsidCacheIndex'] != 23:  
280 - raise OleFormParsingError('Unsupported stored type: {0}'.format(str(var['ClsidCacheIndex'])))  
281 - var['value'] = self.consume_MorphDataControl()  
282 -  
283 -def OleFormVariables(ole_file, stream_dir):  
284 - control_stream = ole_file.openstream('/'.join(stream_dir + ['f']))  
285 - data_stream = ole_file.openstream('/'.join(stream_dir + ['o']))  
286 - form = OleUserFormParser(control_stream, data_stream)  
287 - return form.variables 273 + stream.read(8)
  274 + value = stream.read(value_size)
  275 + # MorphDataStreamData: [MS-OFORMS] 2.2.5.5
  276 + if propmask.fMouseIcon:
  277 + consume_GuidAndPicture(stream)
  278 + if propmask.fPicture:
  279 + consume_GuidAndPicture(stream)
  280 + consume_TextProps(stream)
  281 + return value
  282 +
  283 +def extract_OleFormVariables(ole_file, stream_dir):
  284 + control = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['f']))
  285 + variables = list(consume_FormControl(control))
  286 + data = ExtendedStream.open(ole_file, '/'.join(stream_dir + ['o']))
  287 + for var in variables:
  288 + if var['ClsidCacheIndex'] != 23:
  289 + raise OleFormParsingError('Unsupported stored type: {0}'.format(str(var['ClsidCacheIndex'])))
  290 + var['value'] = consume_MorphDataControl(data)
  291 + return variables
oletools/olevba.py
@@ -254,7 +254,7 @@ except ImportError: @@ -254,7 +254,7 @@ except ImportError:
254 + "see http://codespeak.net/lxml " \ 254 + "see http://codespeak.net/lxml " \
255 + "or http://effbot.org/zone/element-index.htm") 255 + "or http://effbot.org/zone/element-index.htm")
256 256
257 -from oleform import OleFormVariables 257 +from oleform import extract_OleFormVariables
258 258
259 import thirdparty.olefile as olefile 259 import thirdparty.olefile as olefile
260 from thirdparty.prettytable import prettytable 260 from thirdparty.prettytable import prettytable
@@ -2924,7 +2924,7 @@ class VBA_Parser(object): @@ -2924,7 +2924,7 @@ class VBA_Parser(object):
2924 self.find_vba_forms() 2924 self.find_vba_forms()
2925 ole = self.ole_file 2925 ole = self.ole_file
2926 for form_storage in self.vba_forms: 2926 for form_storage in self.vba_forms:
2927 - for variable in OleFormVariables(ole, form_storage): 2927 + for variable in extract_OleFormVariables(ole, form_storage):
2928 yield (self.filename, '/'.join(form_storage), variable) 2928 yield (self.filename, '/'.join(form_storage), variable)
2929 2929
2930 def close(self): 2930 def close(self):