Skip to content

Commit 474a7ae

Browse files
committed
BonusAnalyzer: Add experimental non-VGA option ROM string extraction
1 parent d46d5a5 commit 474a7ae

File tree

2 files changed

+34
-20
lines changed

2 files changed

+34
-20
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ Depending on the contents of each BIOS, the following tags may be displayed on t
111111
* **Build**: Build information contained within the BIOS.
112112
* **ID**: How the BIOS identifies itself during POST.
113113
* **LAN**: PXE or Novell NetWare RPL-compliant network boot ROM, usually associated with on-board Ethernet.
114+
* **OROM**: Non-PCI option ROM, usually associated with on-board devices.
114115
* **SCSI**: Adaptec or NCR/Symbios SCSI option ROM. Model (Adaptec) or SDMS version (NCR/Symbios) information is extracted from the ROM.
115116
* **SLI**: NVIDIA SLI license for non-nForce motherboards. Model information is extracted from the license header.
116117
* **Table**: Register table information contained within the BIOS. May help in identifying chipset and Super I/O devices.

biostools/analyzers.py

+33-20
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,8 @@ def __init__(self, *args, **kwargs):
10581058
self._dmi_strings_pattern = re.compile(b'''(?:[\\x20-\\x7E]{1,255}\\x00){1,255}\\x00''')
10591059
self._dmi_date_pattern = re.compile('''[0-9]{2}/[0-9]{2}/[0-9]{2}(?:[0-9]{2})?$''')
10601060
self._ncr_pattern = re.compile(b''' SDMS \\(TM\\) V([0-9\\.]+)''')
1061-
self._orom_pattern = re.compile(b'''\\x55\\xAA([\\x01-\\xFF])[\\x00-\\xFF]{21}([\\x00-\\xFF]{4})([\\x00-\\xFF]{2}IBM)?''')
1061+
self._orom_pattern = re.compile(b'''\\x55\\xAA([\\x01-\\xFF])([\\x00-\\xFF])[\\x00-\\xFF]{20}([\\x00-\\xFF]{4})([\\x00-\\xFF]{2}IBM)?''')
1062+
self._orom_string_pattern = re.compile(b'''[\\x0D\\x0A\\x20-\\x7E]{9,}''')
10621063
self._pxe_patterns = (
10631064
re.compile(b'''PXE-M0F: Exiting '''),
10641065
re.compile(b'''PXE-EC6: UNDI driver image is invalid\\.'''),
@@ -1311,30 +1312,42 @@ def _read_dmi_strings(header_match, string_value_offsets):
13111312
self._enumerate_metadata('LAN', lan_roms)
13121313

13131314
# Check for the VGA BIOS compatibility marker string and add it as metadata.
1314-
vga_marker = match.group(3)
1315-
if vga_marker:
1315+
orom_marker = match.group(4)
1316+
orom_is_vga = False
1317+
if orom_marker:
1318+
orom_is_vga = True
1319+
13161320
# Find ASCII strings around the marker. There must be a space before/after
13171321
# the marker to avoid parsing of non-text bytes as ASCII characters.
1318-
vga_start = match.start(3) + 2 - rom_offset
1322+
vga_start = match.start(4) + 2 - rom_offset
13191323
if rom_data[vga_start - 1:vga_start] == b' ':
13201324
while vga_start > 0 and rom_data[vga_start - 1] >= 0x20 and rom_data[vga_start - 1] <= 0x7e:
13211325
vga_start -= 1
1322-
vga_end = match.end(3) - rom_offset
1326+
vga_end = match.end(4) - rom_offset
13231327
if rom_data[vga_end:vga_end + 1] == b' ':
13241328
while vga_end < len(rom_data) and rom_data[vga_end] >= 0x20 and rom_data[vga_end] <= 0x7e:
13251329
vga_end += 1
1326-
vga_marker = util.read_string(rom_data[vga_start:vga_end]).strip()
1330+
orom_marker = util.read_string(rom_data[vga_start:vga_end]).strip()
13271331

13281332
# Find an ASCII string after the IBM header, and if one is found, use it instead.
13291333
additional_match = self._vga_string_pattern.search(rom_data[vga_end:])
13301334
if additional_match:
1331-
vga_marker = self._vga_trim_pattern.sub('', vga_marker).strip()
1332-
if vga_marker:
1333-
vga_marker += '\n'
1334-
vga_marker += util.read_string(additional_match.group(0).replace(b'\x00', b' ')).strip()
1335+
orom_marker = self._vga_trim_pattern.sub('', orom_marker).strip()
1336+
if orom_marker:
1337+
orom_marker += '\n'
1338+
orom_marker += util.read_string(additional_match.group(0).replace(b'\x00', b' ')).strip()
1339+
elif match.group(2) in b'\xe9\xeb':
1340+
# Find ASCII strings near the header if the entry point jump looks valid.
1341+
string_match = self._orom_string_pattern.search(rom_data[5:4096]) # spec says data starts at 6, but a short jump can make it start at 5
1342+
if string_match and b'NetWare Ready ROM' in string_match.group(0): # ignore RPL signature
1343+
string_match = self._orom_string_pattern.search(rom_data[string_match.end(0):4096])
1344+
if string_match:
1345+
orom_marker = util.read_string(string_match.group(0)).strip()
1346+
if len(orom_marker) > 256: # ignore Adaptec's essay about 1 GB drives
1347+
orom_marker = None
13351348

13361349
# Extract PCI and PnP data structure pointers.
1337-
pci_header_ptr, pnp_header_ptr = struct.unpack('<HH', match.group(2))
1350+
pci_header_ptr, pnp_header_ptr = struct.unpack('<HH', match.group(3))
13381351

13391352
# Check for a valid PCI data structure.
13401353
if pci_header_ptr >= 26:
@@ -1348,8 +1361,8 @@ def _read_dmi_strings(header_match, string_value_offsets):
13481361

13491362
# Make sure the vendor ID is not bogus.
13501363
if vendor_id not in (0x0000, 0xffff):
1351-
# The generic VGA marker is no longer required.
1352-
vga_marker = None
1364+
# The extracted option ROM marker is no longer required.
1365+
orom_marker = None
13531366

13541367
# Add IDs to the option ROM list.
13551368
self.oroms.append((vendor_id, device_id))
@@ -1401,19 +1414,19 @@ def _read_dmi_strings(header_match, string_value_offsets):
14011414

14021415
# Take valid data only.
14031416
if device_id[:2] != b'\x00\x00' and (vendor or device):
1404-
# The generic VGA marker is no longer required.
1405-
vga_marker = None
1417+
# The extracted option ROM marker is no longer required.
1418+
orom_marker = None
14061419

14071420
# Add PnP ID (endianness swapped to help the front-end in
14081421
# processing it), vendor name and device name to the list.
14091422
self.oroms.append((struct.unpack('>I', device_id)[0], vendor, device))
14101423

1411-
# Add generic VGA marker if no PCI/PnP data was found.
1412-
if vga_marker:
1424+
# Add extracted option ROM marker if no PCI/PnP data was found.
1425+
if orom_marker:
14131426
# Strip lines that are too short or have a single repeated character.
1414-
stripped = (x.strip() for x in vga_marker.replace('\r', '\n').split('\n'))
1415-
vga_marker = '\n'.join(x for x in stripped if len(x) > 3 and x[:10] != (x[0] * min(len(x), 10))).strip('\n')
1416-
self.oroms.append((-1, 'VGA', vga_marker))
1427+
stripped = (x.strip() for x in orom_marker.replace('\r', '\n').split('\n'))
1428+
orom_marker = '\n'.join(x for x in stripped if len(x) > 3 and x[:10] != (x[0] * min(len(x), 10))).strip('\n')
1429+
self.oroms.append((-1, 'VGA' if orom_is_vga else 'OROM', orom_marker))
14171430

14181431
# This analyzer should never return True.
14191432
return False

0 commit comments

Comments
 (0)