def _CreateLoadForCompressedSection(data): """Creates a LOAD segment to previously created COMPRESSED_SECTION_NAME.""" elf_hdr = elf_headers.ElfHeader(data) section_offset = None section_size = None for shdr in elf_hdr.GetSectionHeaders(): if shdr.GetStrName() == COMPRESSED_SECTION_NAME: section_offset = shdr.sh_offset section_size = shdr.sh_size break if section_offset is None: raise RuntimeError('Failed to locate {} section in file'.format( COMPRESSED_SECTION_NAME)) unaligned_new_vaddr = _FindNewVaddr(elf_hdr.GetProgramHeaders()) new_vaddr = MatchVaddrAlignment(unaligned_new_vaddr, section_offset) elf_hdr.AddProgramHeader( elf_headers.ProgramHeader.Create( elf_hdr.byte_order, p_type=elf_headers.ProgramHeader.Type.PT_LOAD, p_flags=elf_headers.ProgramHeader.Flags.PF_R, p_offset=section_offset, p_vaddr=new_vaddr, p_paddr=new_vaddr, p_filesz=section_size, p_memsz=section_size, p_align=ADDRESS_ALIGN, )) elf_hdr.PatchData(data)
def testElfHeaderNoopPatching(self): """Patching the ELF without any changes.""" with open(self.library_path, 'rb') as f: data = bytearray(f.read()) data_copy = data[:] elf = elf_headers.ElfHeader(data) elf.PatchData(data) self.assertEqual(data, data_copy)
def testElfHeaderPatchingAndParsing(self): """Patching the ELF and validating that it worked.""" with open(self.library_path, 'rb') as f: data = bytearray(f.read()) elf = elf_headers.ElfHeader(data) # Changing some values. elf.e_ehsize = 42 elf.GetProgramHeaders()[0].p_align = 1 elf.GetProgramHeaders()[0].p_filesz = 10 elf.PatchData(data) updated_elf = elf_headers.ElfHeader(data) # Validating all of the ELF header fields. self.assertEqual(updated_elf.e_type, elf.e_type) self.assertEqual(updated_elf.e_entry, elf.e_entry) self.assertEqual(updated_elf.e_phoff, elf.e_phoff) self.assertEqual(updated_elf.e_shoff, elf.e_shoff) self.assertEqual(updated_elf.e_flags, elf.e_flags) self.assertEqual(updated_elf.e_ehsize, 42) self.assertEqual(updated_elf.e_phentsize, elf.e_phentsize) self.assertEqual(updated_elf.e_phnum, elf.e_phnum) self.assertEqual(updated_elf.e_shentsize, elf.e_shentsize) self.assertEqual(updated_elf.e_shnum, elf.e_shnum) self.assertEqual(updated_elf.e_shstrndx, elf.e_shstrndx) # Validating all of the fields of the first segment. load_phdr = elf.GetProgramHeaders()[0] updated_load_phdr = updated_elf.GetProgramHeaders()[0] self.assertEqual(updated_load_phdr.p_offset, load_phdr.p_offset) self.assertEqual(updated_load_phdr.p_vaddr, load_phdr.p_vaddr) self.assertEqual(updated_load_phdr.p_paddr, load_phdr.p_paddr) self.assertEqual(updated_load_phdr.p_filesz, 10) self.assertEqual(updated_load_phdr.p_memsz, load_phdr.p_memsz) self.assertEqual(updated_load_phdr.p_flags, load_phdr.p_flags) self.assertEqual(updated_load_phdr.p_align, 1)
def testElfHeaderParsing(self): with open(self.library_path, 'rb') as f: data = f.read() elf = elf_headers.ElfHeader(data) # Validating all of the ELF header fields. self.assertEqual(elf.e_type, elf_headers.ElfHeader.EType.ET_DYN) self.assertEqual(elf.e_entry, 0x1040) self.assertEqual(elf.e_phoff, 64) self.assertEqual(elf.e_shoff, 14136) self.assertEqual(elf.e_flags, 0) self.assertEqual(elf.e_ehsize, 64) self.assertEqual(elf.e_phentsize, 56) self.assertEqual(elf.e_phnum, 8) self.assertEqual(elf.e_shentsize, 64) self.assertEqual(elf.e_shnum, 26) self.assertEqual(elf.e_shstrndx, 25) # Validating types and amounts of all segments excluding GNU specific ones. phdrs = elf.GetPhdrs() self.assertEqual(len(phdrs), 8) phdr_types = [ elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_DYNAMIC, None, None, None, ] for i in range(0, len(phdrs)): if phdr_types[i] is not None: self.assertEqual(phdrs[i].p_type, phdr_types[i]) # Validating all of the fields of the first segment. load_phdr = phdrs[0] self.assertEqual(load_phdr.p_offset, 0x0) self.assertEqual(load_phdr.p_vaddr, 0x0) self.assertEqual(load_phdr.p_paddr, 0x0) self.assertEqual(load_phdr.p_filesz, 0x468) self.assertEqual(load_phdr.p_memsz, 0x468) self.assertEqual(load_phdr.p_flags, 0b100) self.assertEqual(load_phdr.p_align, 0x1000) # Validating offsets of the second segment load_phdr = phdrs[1] self.assertEqual(load_phdr.p_offset, 0x1000) self.assertEqual(load_phdr.p_vaddr, 0x1000) self.assertEqual(load_phdr.p_paddr, 0x1000)
def _MovePhdrToTheEnd(data): """Moves Phdrs to the end of the file and adjusts all references to it.""" elf_hdr = elf_headers.ElfHeader(data) # If program headers are already in the end of the file, nothing to do. if elf_hdr.e_phoff + elf_hdr.e_phnum * elf_hdr.e_phentsize == len(data): return old_phoff = elf_hdr.e_phoff new_phoff = elf_hdr.e_phoff = len(data) unaligned_new_vaddr = _FindNewVaddr(elf_hdr.GetProgramHeaders()) new_vaddr = MatchVaddrAlignment(unaligned_new_vaddr, new_phoff) # Since we moved the PHDR section to the end of the file, we need to create a # new LOAD segment to load it in. current_filesize = elf_hdr.e_phnum * elf_hdr.e_phentsize # We are using current_filesize while adding new program header due to # AddProgramHeader handling the increase of size due to addition of new # header. elf_hdr.AddProgramHeader( elf_headers.ProgramHeader.Create( elf_hdr.byte_order, p_type=elf_headers.ProgramHeader.Type.PT_LOAD, p_flags=elf_headers.ProgramHeader.Flags.PF_R, p_offset=new_phoff, p_vaddr=new_vaddr, p_paddr=new_vaddr, p_filesz=current_filesize, p_memsz=current_filesize, p_align=ADDRESS_ALIGN, )) # PHDR segment if it exists should point to the new location. for phdr in elf_hdr.GetProgramHeadersByType( elf_headers.ProgramHeader.Type.PT_PHDR): phdr.p_offset = new_phoff phdr.p_vaddr = new_vaddr phdr.p_paddr = new_vaddr phdr.p_align = ADDRESS_ALIGN # We need to replace the previous phdr placement with zero bytes to fail # fast if dynamic linker doesn't like the new program header. previous_phdr_size = (elf_hdr.e_phnum - 1) * elf_hdr.e_phentsize data[old_phoff:old_phoff + previous_phdr_size] = [0] * previous_phdr_size # Updating ELF header to point to the new location. elf_hdr.PatchData(data)
def _FileRangeToVirtualAddressRange(data, l, r): """Returns virtual address range corresponding to given file range. Since we have to resolve them by their virtual address, parsing of LOAD segments is required here. """ elf = elf_headers.ElfHeader(data) for phdr in elf.GetPhdrs(): if phdr.p_type == elf_headers.ProgramHeader.Type.PT_LOAD.value: # Current version of the prototype only supports ranges which are fully # contained inside one LOAD segment. It should cover most of the common # cases. if phdr.p_offset >= r or phdr.p_offset + phdr.p_filesz <= l: # Range doesn't overlap. continue if phdr.p_offset > l or phdr.p_offset + phdr.p_filesz < r: # Range overlap with LOAD segment but isn't fully covered by it. raise RuntimeError( 'Range is not contained within one LOAD segment') l_virt = phdr.p_vaddr + (l - phdr.p_offset) r_virt = phdr.p_vaddr + (r - phdr.p_offset) return l_virt, r_virt raise RuntimeError('Specified range is outside of all LOAD segments.')
def testElfHeaderSectionNames(self): """Test that the section names are correctly resolved""" with open(self.library_path, 'rb') as f: data = f.read() elf = elf_headers.ElfHeader(data) section_names = [ '', '.hash', '.gnu.hash', '.dynsym', '.dynstr', '.gnu.version', '.gnu.version_r', '.rela.dyn', '.init', '.plt', '.plt.got', '.text', '.fini', '.eh_frame_hdr', '.eh_frame', '.init_array', '.fini_array', '.dynamic', '.got', '.got.plt', '.data', '.bss', '.comment', '.symtab', '.strtab', '.shstrtab', ] shdrs = elf.GetSectionHeaders() for i in range(0, len(shdrs)): self.assertEqual(shdrs[i].GetStrName(), section_names[i])
def testElfHeaderParsing(self): with open(self.library_path, 'rb') as f: data = f.read() elf = elf_headers.ElfHeader(data) # Validating all of the ELF header fields. self.assertEqual(elf.e_type, elf_headers.ElfHeader.EType.ET_DYN) self.assertEqual(elf.e_entry, 0x1040) self.assertEqual(elf.e_phoff, 64) self.assertEqual(elf.e_shoff, 14136) self.assertEqual(elf.e_flags, 0) self.assertEqual(elf.e_ehsize, 64) self.assertEqual(elf.e_phentsize, 56) self.assertEqual(elf.e_phnum, 8) self.assertEqual(elf.e_shentsize, 64) self.assertEqual(elf.e_shnum, 26) self.assertEqual(elf.e_shstrndx, 25) # Validating types and amount of all segments excluding GNU specific ones. phdrs = elf.GetProgramHeaders() self.assertEqual(len(phdrs), 8) phdr_types = [ elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_LOAD, elf_headers.ProgramHeader.Type.PT_DYNAMIC, None, # Non-standard segment: GNU_EH_FRAME None, # Non-standard segment: GNU_STACK None, # Non-standard segment: GNU_RELRO ] for i in range(0, len(phdrs)): if phdr_types[i] is not None: self.assertEqual(phdrs[i].p_type, phdr_types[i]) # Validating all of the fields of the first segment. load_phdr = phdrs[0] self.assertEqual(load_phdr.p_offset, 0x0) self.assertEqual(load_phdr.p_vaddr, 0x0) self.assertEqual(load_phdr.p_paddr, 0x0) self.assertEqual(load_phdr.p_filesz, 0x468) self.assertEqual(load_phdr.p_memsz, 0x468) self.assertEqual(load_phdr.p_flags, 0b100) self.assertEqual(load_phdr.p_align, 0x1000) # Validating offsets of the second segment load_phdr = phdrs[1] self.assertEqual(load_phdr.p_offset, 0x1000) self.assertEqual(load_phdr.p_vaddr, 0x1000) self.assertEqual(load_phdr.p_paddr, 0x1000) # Validating types and amount of sections excluding GNU ones. shdrs = elf.GetSectionHeaders() self.assertEqual(len(shdrs), 26) shdr_types = [ elf_headers.SectionHeader.Type.SHT_NULL, elf_headers.SectionHeader.Type.SHT_HASH, None, # Non-standard section: GNU_HASH elf_headers.SectionHeader.Type.SHT_DYNSYM, elf_headers.SectionHeader.Type.SHT_STRTAB, None, # Non-standard section: VERSYM None, # Non-standard section: VERNEED elf_headers.SectionHeader.Type.SHT_RELA, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, None, # Non-standard section: INIT_ARRAY None, # Non-standard section: FINI_ARRAY elf_headers.SectionHeader.Type.SHT_DYNAMIC, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_NOBITS, elf_headers.SectionHeader.Type.SHT_PROGBITS, elf_headers.SectionHeader.Type.SHT_SYMTAB, elf_headers.SectionHeader.Type.SHT_STRTAB, elf_headers.SectionHeader.Type.SHT_STRTAB, ] for i in range(0, len(shdrs)): if shdr_types[i] is not None: self.assertEqual(shdrs[i].sh_type, shdr_types[i]) # Validate all fields of the first and second section, since the first one # is NULL section. shdr = shdrs[0] self.assertEqual(shdr.sh_flags, 0) self.assertEqual(shdr.sh_addr, 0x0) self.assertEqual(shdr.sh_offset, 0x0) self.assertEqual(shdr.sh_size, 0x0) self.assertEqual(shdr.sh_link, 0) self.assertEqual(shdr.sh_info, 0) self.assertEqual(shdr.sh_addralign, 0) self.assertEqual(shdr.sh_entsize, 0) shdr = shdrs[1] self.assertEqual(shdr.sh_flags, 2) self.assertEqual(shdr.sh_addr, 0x200) self.assertEqual(shdr.sh_offset, 0x200) self.assertEqual(shdr.sh_size, 0x30) self.assertEqual(shdr.sh_link, 3) self.assertEqual(shdr.sh_info, 0) self.assertEqual(shdr.sh_addralign, 8) self.assertEqual(shdr.sh_entsize, 4)
def _SplitLoadSegmentAndNullifyRange(data, l, r): """Find LOAD segment covering [l, r) and splits it into three segments. Split is done so one of the LOAD segments contains only [l, r) and nothing else. If the range is located at the start or at the end of the segment less than three segments may be created. The resulting LOAD segment containing [l, r) is edited so it sets the corresponding virtual address range to zeroes, ignoring file content. """ elf_hdr = elf_headers.ElfHeader(data) range_phdr = None for phdr in elf_hdr.GetProgramHeadersByType( elf_headers.ProgramHeader.Type.PT_LOAD): if phdr.p_offset <= l and phdr.FilePositionEnd() >= r: range_phdr = phdr break if range_phdr is None: raise RuntimeError('No LOAD segment covering the range found') # The range_phdr will become the LOAD segment containing the [l, r) range # but we need to create the additional two segments. left_segment_size = l - range_phdr.p_offset if left_segment_size > 0: # Creating LOAD segment containing the [phdr.p_offset, l) part. elf_hdr.AddProgramHeader( elf_headers.ProgramHeader.Create( elf_hdr.byte_order, p_type=range_phdr.p_type, p_flags=range_phdr.p_flags, p_offset=range_phdr.p_offset, p_vaddr=range_phdr.p_vaddr, p_paddr=range_phdr.p_paddr, p_filesz=left_segment_size, p_memsz=left_segment_size, p_align=range_phdr.p_align, )) if range_phdr.p_offset + range_phdr.p_memsz > r: # Creating LOAD segment containing the [r, phdr.p_offset + phdr.p_memsz). right_segment_delta = r - range_phdr.p_offset right_segment_address = range_phdr.p_vaddr + right_segment_delta right_segment_filesize = max(range_phdr.p_filesz - right_segment_delta, 0) right_segment_memsize = range_phdr.p_memsz - right_segment_delta elf_hdr.AddProgramHeader( elf_headers.ProgramHeader.Create( elf_hdr.byte_order, p_type=range_phdr.p_type, p_flags=range_phdr.p_flags, p_offset=r, p_vaddr=right_segment_address, p_paddr=right_segment_address, p_filesz=right_segment_filesize, p_memsz=right_segment_memsize, p_align=range_phdr.p_align, )) # Modifying the range_phdr central_segment_address = range_phdr.p_vaddr + left_segment_size range_phdr.p_offset = l range_phdr.p_vaddr = central_segment_address range_phdr.p_paddr = central_segment_address range_phdr.p_filesz = 0 range_phdr.p_memsz = r - l elf_hdr.PatchData(data)