#!/usr/bin/env python3 from collections import defaultdict import argparse import elftools.elf.elffile import struct import sys import zlib from lib.io import warn def warn_sym(name, start, size, msg): warn(f'{name}[{start:x}+{size:x}]: {msg}') def get_lang_symbols(elf, symtab): # fetch language markers pri_start = symtab.get_symbol_by_name("__loc_pri_start")[0].entry.st_value pri_end = symtab.get_symbol_by_name("__loc_pri_end")[0].entry.st_value text_data = elf.get_section_by_name('.text').data() # extract translatable symbols syms = [] sym_id = 0 for sym in sorted(symtab.iter_symbols(), key=lambda x: x.entry.st_value): sym_start = sym.entry.st_value sym_size = sym.entry.st_size sym_end = sym_start + sym_size if sym_start >= pri_start and sym_end < pri_end and sym_size > 0: data = text_data[sym_start:sym_end] # perform basic checks on the language section if data[0] != 255 or data[1] != 255: warn_sym(sym.name, sym_start, sym_size, 'invalid location offset') if data[-1] != 0: warn_sym(sym.name, sym_start, sym_size, 'unterminated string') syms.append({'start': sym_start, 'size': sym_size, 'name': sym.name, 'id': sym_id, 'data': data[2:-1]}) sym_id += 1 return syms def fw_signature(syms): # any id which is stable when the translatable string do not change would do, so build it out of # the firmware translation symbol table itself data = b'' for sym in syms: data += struct.pack(" 1: sym_names = [x['name'] for x in sym_list] warn(f'symbols {sym_names} contain the same data: {data}') def output_map(syms): print('OFFSET\tSIZE\tNAME\tID\tSTRING') for sym in syms: print('{:04x}\t{:04x}\t{}\t{}\t{}'.format(sym['start'], sym['size'], sym['name'], sym['id'], sym['data'])) def main(): ap = argparse.ArgumentParser() ap.add_argument('elf', help='Firmware ELF file') ap.add_argument('bin', nargs='?', help='Firmware BIN file') args = ap.parse_args() # extract translatable symbols elf = elftools.elf.elffile.ELFFile(open(args.elf, "rb")) symtab = elf.get_section_by_name('.symtab') syms = get_lang_symbols(elf, symtab) pri_sym = get_sig_sym(symtab, syms) # do one additional pass to check for symbols containing the same data check_duplicate_data(syms) # output the symbol table map output_map(syms + [pri_sym]) # patch the symbols in the final binary if args.bin is not None: patch_binary(args.bin, syms, pri_sym) return 0 if __name__ == '__main__': exit(main())