Skip to content

Commit 8d6a71a

Browse files
elektrischermoenchqkaiser
authored andcommitted
fix(unblob): unnecessary carving of padding in qnx elf file
1 parent 2d6aa91 commit 8d6a71a

File tree

1 file changed

+59
-0
lines changed
  • python/unblob/handlers/executable

1 file changed

+59
-0
lines changed

python/unblob/handlers/executable/elf.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636

3737
logger = get_logger()
3838

39+
QNX_PADDING_SIZE = 4096
40+
QNX_SECTION_NAME_PREFIX = "QNX_"
41+
3942
KERNEL_MODULE_SIGNATURE_INFO_LEN = 12
4043
KERNEL_MODULE_SIGNATURE_FOOTER = b"~Module signature appended~\n"
4144

@@ -371,6 +374,59 @@ def get_upx_end_offset(self, file: File, start_offset: int, end_offset: int) ->
371374
# no matching UPX footer found
372375
return end_offset
373376

377+
def get_end_offset_with_qnx_null_padding(
378+
self, file: File, current_end_offset: int
379+
) -> int:
380+
# Check for exactly one 4KB (4096 bytes) page of null bytes, QNX
381+
# binaries are often padded this way.
382+
if (
383+
file[current_end_offset : current_end_offset + QNX_PADDING_SIZE]
384+
== b"\x00" * QNX_PADDING_SIZE
385+
):
386+
return current_end_offset + QNX_PADDING_SIZE
387+
return current_end_offset
388+
389+
def is_qnx_elf(self, file: File, start_offset: int, header, endian) -> bool:
390+
# Preliminary checks for section header table validity
391+
if (
392+
header.e_shoff == 0
393+
or header.e_shnum == 0
394+
or header.e_shstrndx >= header.e_shnum
395+
):
396+
return False
397+
398+
# Get the section header string table section
399+
file.seek(
400+
start_offset + header.e_shoff + header.e_shstrndx * header.e_shentsize,
401+
io.SEEK_SET,
402+
)
403+
shstrtab_shdr = self._struct_parser.parse(
404+
self.SECTION_HEADER_STRUCT, file, endian
405+
)
406+
407+
# Read the section header string table content
408+
file.seek(start_offset + shstrtab_shdr.sh_offset, io.SEEK_SET)
409+
shstrtab_content = file.read(shstrtab_shdr.sh_size)
410+
411+
# Iterate through all section headers
412+
file.seek(start_offset + header.e_shoff, io.SEEK_SET)
413+
for _ in range(header.e_shnum):
414+
section_header = self._struct_parser.parse(
415+
self.SECTION_HEADER_STRUCT, file, endian
416+
)
417+
# Get the section name from the string table
418+
name_offset = section_header.sh_name
419+
name_end_offset = shstrtab_content.find(b"\x00", name_offset)
420+
if name_end_offset == -1:
421+
name_end_offset = len(shstrtab_content)
422+
section_name = shstrtab_content[name_offset:name_end_offset].decode(
423+
"utf-8", errors="ignore"
424+
)
425+
426+
if section_name.startswith(QNX_SECTION_NAME_PREFIX):
427+
return True
428+
return False
429+
374430
def calculate_chunk(self, file: File, start_offset: int) -> Optional[ElfChunk]:
375431
endian = self.get_endianness(file, start_offset)
376432
file.seek(start_offset, io.SEEK_SET)
@@ -386,6 +442,9 @@ def calculate_chunk(self, file: File, start_offset: int) -> Optional[ElfChunk]:
386442
if self.is_upx(file=file, start_offset=start_offset, end_offset=end_offset):
387443
end_offset = self.get_upx_end_offset(file, start_offset, end_offset)
388444

445+
if self.is_qnx_elf(file, start_offset, header, endian):
446+
end_offset = self.get_end_offset_with_qnx_null_padding(file, end_offset)
447+
389448
# do a special extraction of ELF files with ElfChunk
390449
return ElfChunk(
391450
start_offset=start_offset,

0 commit comments

Comments
 (0)