@@ -1584,3 +1584,70 @@ def test_decomp_inport_is_word_array_300(self):
15841584 elem = data .type .element_type
15851585 assert isinstance (elem , BaseType )
15861586 assert elem == BaseType .WORD
1587+
1588+
1589+ # ---------------------------------------------------------------------------
1590+ # StatusCodeHandlerPei.efi regression: hidden .reloc segment (issue #115)
1591+ # ---------------------------------------------------------------------------
1592+
1593+
1594+ @requires_ida
1595+ class TestRelocSegmentExport :
1596+ """Export a PE/EFI binary whose ``.reloc`` segment is hidden by IDA.
1597+
1598+ Regression test for https://github.com/quarkslab/quokka/issues/115.
1599+
1600+ IDA marks the ``.reloc`` segment as hidden (``SFL_HIDDEN``) but still
1601+ creates a data item inside it (referenced from the PE header). Before the
1602+ fix, ``ExportSegments`` skipped every non-visible segment, so the linear
1603+ scan reached that data item, ``getseg`` returned a segment absent from
1604+ Quokka's collection, and the export aborted with
1605+ ``Data at address 0x... doesn't belong to any segment``.
1606+
1607+ Simply reaching the assertions below means ``from_binary`` returned instead
1608+ of raising ``QuokkaError`` on the abort, i.e. the crash no longer happens.
1609+ """
1610+
1611+ # Start of the .reloc segment in this sample; the crashing data item.
1612+ RELOC_START = 0x480
1613+
1614+ @pytest .fixture (autouse = True )
1615+ def _export (self , root_directory : Path , tmp_path : Path ):
1616+ """Export StatusCodeHandlerPei.efi through IDA into a temp directory."""
1617+ binary = root_directory / "tests" / "dataset" / "StatusCodeHandlerPei.efi"
1618+ if not binary .exists ():
1619+ pytest .skip ("StatusCodeHandlerPei.efi not found in tests/dataset/" )
1620+
1621+ output = tmp_path / "StatusCodeHandlerPei.quokka"
1622+ self .prog = quokka .Program .from_binary (
1623+ binary ,
1624+ output_file = output ,
1625+ database_file = tmp_path / "StatusCodeHandlerPei.i64" ,
1626+ timeout = 600 ,
1627+ )
1628+
1629+ def test_export_produces_program (self ):
1630+ """Export completes without aborting on the hidden .reloc segment."""
1631+ assert self .prog is not None
1632+
1633+ def test_reloc_segment_is_exported (self ):
1634+ """The hidden .reloc segment is present in the export."""
1635+ # IDA exports the segment under its visible name ("_reloc").
1636+ segments = list (self .prog .segments .values ())
1637+ reloc = [s for s in segments if s .start == self .RELOC_START ]
1638+ assert len (reloc ) == 1 , (
1639+ f"Expected exactly one segment starting at 0x{ self .RELOC_START :x} , "
1640+ f"got { [(s .name , hex (s .start )) for s in segments ]} "
1641+ )
1642+ assert reloc [0 ].name == "_reloc"
1643+
1644+ def test_reloc_data_belongs_to_a_segment (self ):
1645+ """The data item that used to crash the scan resolves to a segment."""
1646+ covering = [
1647+ s for s in self .prog .segments .values () if s .in_segment (self .RELOC_START )
1648+ ]
1649+ assert len (covering ) == 1 , (
1650+ f"Address 0x{ self .RELOC_START :x} should belong to exactly one segment, "
1651+ f"got { len (covering )} "
1652+ )
1653+ assert self .RELOC_START in self .prog .data
0 commit comments