CreateSectionTable.py
1 # This file is used to process section data generated by `objdump -s` 2 import re 3 4 5 class Section(object): 6 """ 7 One Section of section table. contains info about section name, address and raw data 8 """ 9 SECTION_START_PATTERN = re.compile(b"Contents of section (.+?):") 10 DATA_PATTERN = re.compile(b"([0-9a-f]{4,8})") 11 12 def __init__(self, name, start_address, data): 13 self.name = name 14 self.start_address = start_address 15 self.data = data 16 17 def __contains__(self, item): 18 """ check if the section name and address match this section """ 19 if (item["section"] == self.name or item["section"] == "any") \ 20 and (self.start_address <= item["address"] < (self.start_address + len(self.data))): 21 return True 22 else: 23 return False 24 25 def __getitem__(self, item): 26 """ 27 process slice. 28 convert absolute address to relative address in current section and return slice result 29 """ 30 if isinstance(item, int): 31 return self.data[item - self.start_address] 32 elif isinstance(item, slice): 33 start = item.start if item.start is None else item.start - self.start_address 34 stop = item.stop if item.stop is None else item.stop - self.start_address 35 return self.data[start:stop] 36 return self.data[item] 37 38 def __str__(self): 39 return "%s [%08x - %08x]" % (self.name, self.start_address, self.start_address + len(self.data)) 40 41 __repr__ = __str__ 42 43 @classmethod 44 def parse_raw_data(cls, raw_data): 45 """ 46 process raw data generated by `objdump -s`, create section and return un-processed lines 47 :param raw_data: lines of raw data generated by `objdump -s` 48 :return: one section, un-processed lines 49 """ 50 name = "" 51 data = "" 52 start_address = 0 53 # first find start line 54 for i, line in enumerate(raw_data): 55 if b"Contents of section " in line: # do strcmp first to speed up 56 match = cls.SECTION_START_PATTERN.search(line) 57 if match is not None: 58 name = match.group(1) 59 raw_data = raw_data[i + 1:] 60 break 61 else: 62 # do some error handling 63 raw_data = [b""] # add a dummy first data line 64 65 def process_data_line(line_to_process): 66 # first remove the ascii part 67 hex_part = line_to_process.split(b" ")[0] 68 # process rest part 69 data_list = cls.DATA_PATTERN.findall(hex_part) 70 try: 71 _address = int(data_list[0], base=16) 72 except IndexError: 73 _address = -1 74 75 def hex_to_str(hex_data): 76 if len(hex_data) % 2 == 1: 77 hex_data = b"0" + hex_data # append zero at the beginning 78 _length = len(hex_data) 79 return "".join([chr(int(hex_data[_i:_i + 2], base=16)) 80 for _i in range(0, _length, 2)]) 81 82 return _address, "".join([hex_to_str(x) for x in data_list[1:]]) 83 84 # handle first line: 85 address, _data = process_data_line(raw_data[0]) 86 if address != -1: 87 start_address = address 88 data += _data 89 raw_data = raw_data[1:] 90 for i, line in enumerate(raw_data): 91 address, _data = process_data_line(line) 92 if address == -1: 93 raw_data = raw_data[i:] 94 break 95 else: 96 data += _data 97 else: 98 # do error handling 99 raw_data = [] 100 101 section = cls(name, start_address, data) if start_address != -1 else None 102 unprocessed_data = None if len(raw_data) == 0 else raw_data 103 return section, unprocessed_data 104 105 106 class SectionTable(object): 107 """ elf section table """ 108 109 def __init__(self, file_name): 110 with open(file_name, "rb") as f: 111 raw_data = f.readlines() 112 self.table = [] 113 while raw_data: 114 section, raw_data = Section.parse_raw_data(raw_data) 115 self.table.append(section) 116 117 def get_unsigned_int(self, section, address, size=4, endian="LE"): 118 """ 119 get unsigned int from section table 120 :param section: section name; use "any" will only match with address 121 :param address: start address 122 :param size: size in bytes 123 :param endian: LE or BE 124 :return: int or None 125 """ 126 if address % 4 != 0 or size % 4 != 0: 127 print("warning: try to access without 4 bytes aligned") 128 key = {"address": address, "section": section} 129 for section in self.table: 130 if key in section: 131 tmp = section[address:address + size] 132 value = 0 133 for i in range(size): 134 if endian == "LE": 135 value += ord(tmp[i]) << (i * 8) 136 elif endian == "BE": 137 value += ord(tmp[i]) << ((size - i - 1) * 8) 138 else: 139 print("only support LE or BE for parameter endian") 140 assert False 141 break 142 else: 143 value = None 144 return value 145 146 def get_string(self, section, address): 147 """ 148 get string ('\0' terminated) from section table 149 :param section: section name; use "any" will only match with address 150 :param address: start address 151 :return: string or None 152 """ 153 value = None 154 key = {"address": address, "section": section} 155 for section in self.table: 156 if key in section: 157 value = section[address:] 158 for i, c in enumerate(value): 159 if c == '\0': 160 value = value[:i] 161 break 162 break 163 return value