Skip to content

Commit c562598

Browse files
committed
Access GUIDS stream like a sequence.
Closes #84
1 parent 1f22aed commit c562598

File tree

3 files changed

+56
-10
lines changed

3 files changed

+56
-10
lines changed

HISTORY.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ HEAD
66
----
77

88
* BREAKING CHANGE: resource type DateTime is parsed into an object that exposes Kind value
9+
* FEATURE: access GUIDS stream like a 0-based sequence. GuidHeap subclasses abc.Sequence
910

1011
0.15.1 (2024)
1112
-------------

src/dnfile/stream.py

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@
1313

1414
import struct as _struct
1515
import logging
16-
from typing import Dict, List, Tuple, Union, Optional
16+
from typing import Dict, List, Tuple, Union, Optional, overload
1717
from binascii import hexlify as _hexlify
18+
from collections.abc import Sequence
1819

1920
from pefile import MAX_STRING_LENGTH, Structure
2021

@@ -268,6 +269,8 @@ def get(self, index, encoding="utf-16") -> Optional[UserString]:
268269

269270
class HeapItemGuid(base.HeapItem):
270271

272+
ITEM_SIZE = 128 // 8 # number of bytes in a guid
273+
271274
def __init__(self, data: bytes, rva: Optional[int] = None):
272275
super().__init__(data, rva)
273276

@@ -288,7 +291,7 @@ def __repr__(self):
288291
return f"HeapItemGuid(data={self.__data__},rva={self.rva})"
289292

290293

291-
class GuidHeap(base.ClrHeap):
294+
class GuidHeap(base.ClrHeap, Sequence):
292295
offset_size = 0
293296

294297
def get_str(self, index, as_bytes=False):
@@ -302,21 +305,48 @@ def get_str(self, index, as_bytes=False):
302305

303306
return str(item)
304307

305-
def get(self, index) -> Optional[HeapItemGuid]:
306-
if index is None or index < 1:
308+
def get(self, index: int) -> Optional[HeapItemGuid]:
309+
if not isinstance(index, int):
310+
raise IndexError(f"unexpected type: {type(index)}")
311+
312+
# 1-based indexing
313+
if index < 1 or index > len(self):
307314
return None
308315

309-
size = 128 // 8 # number of bytes in a guid
310316
# offset into the GUID stream
311-
offset = (index - 1) * size
317+
offset = (index - 1) * HeapItemGuid.ITEM_SIZE
312318

313-
if offset + size > len(self.__data__):
314-
raise IndexError("index out of range")
315-
316-
item = HeapItemGuid(self.__data__[offset:offset + size], self.rva + offset)
319+
item = HeapItemGuid(self.__data__[offset:offset + HeapItemGuid.ITEM_SIZE], self.rva + offset)
317320

318321
return item
319322

323+
def __len__(self) -> int:
324+
return len(self.__data__) // HeapItemGuid.ITEM_SIZE
325+
326+
@overload
327+
def __getitem__(self, i: int) -> HeapItemGuid:
328+
...
329+
330+
@overload
331+
def __getitem__(self, i: slice) -> List[HeapItemGuid]:
332+
...
333+
334+
def __getitem__(self, i):
335+
if isinstance(i, int):
336+
if i < 0:
337+
# convert negative index into positive
338+
index0 = len(self) + i
339+
else:
340+
index0 = i
341+
item = self.get(index0 + 1)
342+
if item is None:
343+
raise IndexError(f"unexpected index: {i}")
344+
return item
345+
elif isinstance(i, slice):
346+
start, stop, step = i.indices(len(self))
347+
return [self[index] for index in range(start, stop, step)]
348+
raise IndexError(f"unexpected type '{type(i)}'")
349+
320350

321351
class MDTablesStruct(Structure):
322352
Reserved_1: int

tests/test_parse.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,24 @@ def test_guids():
6262
assert hasattr(dn.net, "guids")
6363

6464
assert dn.net.guids.get(0) is None
65+
assert isinstance(dn.net.guids.get(1), dnfile.stream.HeapItemGuid)
6566
assert dn.net.guids.get(1).value == b"\x8c\x8b\xc5\x48\xff\x24\x91\x45\x9e\xc8\x94\xbf\xea\xbd\x9f\x3e"
6667

6768

69+
def test_guids_as_sequence():
70+
path = fixtures.get_data_path_by_name("hello-world.exe")
71+
72+
dn = dnfile.dnPE(path)
73+
assert dn.net is not None
74+
assert b"#GUID" in dn.net.metadata.streams
75+
assert hasattr(dn.net, "guids")
76+
77+
assert len(dn.net.guids) == 1
78+
assert dn.net.guids[0] is not None
79+
assert hasattr(dn.net.guids[0], "value")
80+
assert dn.net.guids[0].value == b"\x8c\x8b\xc5\x48\xff\x24\x91\x45\x9e\xc8\x94\xbf\xea\xbd\x9f\x3e"
81+
82+
6883
def test_tables():
6984
path = fixtures.get_data_path_by_name("hello-world.exe")
7085

0 commit comments

Comments
 (0)