Ejemplo n.º 1
0
def DecodeElf(data, location):
    """Decode an ELF file and return information about it

    Args:
        data: Data from ELF file
        location: Start address of data to return

    Returns:
        ElfInfo object containing information about the decoded ELF file
    """
    file_size = len(data)
    with io.BytesIO(data) as fd:
        elf = ELFFile(fd)
        data_start = 0xffffffff
        data_end = 0
        mem_end = 0
        virt_to_phys = 0

        for i in range(elf.num_segments()):
            segment = elf.get_segment(i)
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
                skipped = 1  # To make code-coverage see this line
                continue
            start = segment['p_paddr']
            mend = start + segment['p_memsz']
            rend = start + segment['p_filesz']
            data_start = min(data_start, start)
            data_end = max(data_end, rend)
            mem_end = max(mem_end, mend)
            if not virt_to_phys:
                virt_to_phys = segment['p_paddr'] - segment['p_vaddr']

        output = bytearray(data_end - data_start)
        for i in range(elf.num_segments()):
            segment = elf.get_segment(i)
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
                skipped = 1  # To make code-coverage see this line
                continue
            start = segment['p_paddr']
            offset = 0
            if start < location:
                offset = location - start
                start = location
            # A legal ELF file can have a program header with non-zero length
            # but zero-length file size and a non-zero offset which, added
            # together, are greater than input->size (i.e. the total file size).
            #  So we need to not even test in the case that p_filesz is zero.
            # Note: All of this code is commented out since we don't have a test
            # case for it.
            size = segment['p_filesz']
            #if not size:
            #continue
            #end = segment['p_offset'] + segment['p_filesz']
            #if end > file_size:
            #raise ValueError('Underflow copying out the segment. File has %#x bytes left, segment end is %#x\n',
            #file_size, end)
            output[start - data_start:start - data_start +
                   size] = (segment.data()[offset:])
    return ElfInfo(output, data_start, elf.header['e_entry'] + virt_to_phys,
                   mem_end - data_start)
Ejemplo n.º 2
0
def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--name", required=True)
    parser.add_argument("elf_file")
    args = parser.parse_args()

    elf = ELFFile(open(args.elf_file, "r+b"))
    assert elf.num_segments() < 6

    # struct bootelf_header {
    # #define BOOTELF_MAGIC "\xaa\xbbBOOT\xe1\xff"
    #     uint8_t magic[8];
    #     uint8_t name[16];
    #     uint64_t entry;
    #     uint8_t num_mappings;
    #     uint8_t padding[7];
    #     struct bootelf_mapping mappings[];
    # } __packed;
    header = struct.pack("8s16sQB7x", BOOTELF_MAGIC, args.name.encode("ascii"),
                         elf.header.e_entry, elf.num_segments())

    for segment in elf.iter_segments():
        assert segment.header.p_vaddr > 0
        assert segment.header.p_type == 'PT_LOAD'
        assert segment.header.p_offset % PAGE_SIZE == 0
        assert segment.header.p_memsz == segment.header.p_filesz or \
            segment.header.p_filesz == 0

        # struct bootelf_mapping {
        #     uint64_t vaddr;
        #     uint32_t offset;
        #     uint16_t num_pages;
        #     uint8_t type;   // 'R': readonly, 'W': writable, 'X': executable.
        #     uint8_t zeroed; // If it's non-zero value, the pages are filled with zeros.
        # } __packed;
        vaddr = segment.header.p_vaddr
        offset = segment.header.p_offset
        num_pages = (segment.header.p_memsz + PAGE_SIZE - 1) // PAGE_SIZE
        zeroed = segment.header.p_filesz == 0
        header += struct.pack("QIHcb", vaddr, offset, num_pages,
                              b'W', # TODO: Support so-called W^X
                              1 if zeroed else 0)

    with open(args.elf_file, "r+b") as f:
        for offset in [0x1000, 0x10000]:
            f.seek(offset)
            if f.read(8) == BOOTELF_PRE_MAGIC:
                break

        f.seek(offset)
        assert f.read(8) == BOOTELF_PRE_MAGIC
        f.seek(offset)
        f.write(header)
Ejemplo n.º 3
0
def read_segments(filename):
    elffile = ELFFile(open(filename, 'rb'))
    segments = list()
    for segment_idx in range(elffile.num_segments()):
        segments.insert(segment_idx, dict())
        segments[segment_idx]['segment'] = elffile.get_segment(segment_idx)
    return segments
Ejemplo n.º 4
0
def read_segments(data):
    """Read segments from an ELF file

    Args:
        data (bytes): Contents of file

    Returns:
        tuple:
            list of segments, each:
                int: Segment number (0 = first)
                int: Start address of segment in memory
                bytes: Contents of segment
            int: entry address for image

    Raises:
        ValueError: elftools is not available
    """
    if not ELF_TOOLS:
        raise ValueError('Python elftools package is not available')
    with io.BytesIO(data) as inf:
        try:
            elf = ELFFile(inf)
        except ELFError as err:
            raise ValueError(err)
        entry = elf.header['e_entry']
        segments = []
        for i in range(elf.num_segments()):
            segment = elf.get_segment(i)
            if segment['p_type'] != 'PT_LOAD' or not segment['p_memsz']:
                skipped = 1  # To make code-coverage see this line
                continue
            start = segment['p_offset']
            rend = start + segment['p_filesz']
            segments.append((i, segment['p_paddr'], data[start:rend]))
    return segments, entry
Ejemplo n.º 5
0
def open(io):
    elf_o = ELFFile(io)
    info('parsed elf file with %s sections and %s segments' %
         (elf_o.num_sections(), elf_o.num_segments()))
    arch = sefi.arch.from_elf_machine_arch(elf_o.get_machine_arch())
    info('  elf file arch is %s' % (arch))

    return (elf_o, arch)
Ejemplo n.º 6
0
 def dump_segments(self):
     with open(self.filename, "rb") as f:
         elf = ELFFile(f)
         self.structs = elf.structs
         segments = []
         for i in range(elf.num_segments()):
             seg = elf.get_segment(i)
             segments.append(seg.header)
     return segments
Ejemplo n.º 7
0
    def test_hello(self):
        with open(os.path.join('test', 'testfiles', 'simple_gcc.elf.arm'), 'rb') as f:
            elf = ELFFile(f)
            self.assertEqual(elf.get_machine_arch(), 'ARM')

            # Check some other properties of this ELF file derived from readelf
            self.assertEqual(elf['e_entry'], 0x8018)
            self.assertEqual(elf.num_sections(), 14)
            self.assertEqual(elf.num_segments(), 2)
Ejemplo n.º 8
0
def open(io):
	elf_o = ELFFile(io)
	info('parsed elf file with %s sections and %s segments' % 
		(elf_o.num_sections(), elf_o.num_segments())
	)
	arch = sefi.arch.from_elf_machine_arch(elf_o.get_machine_arch())
	info('  elf file arch is %s' % (arch))
	
	return (elf_o, arch)
Ejemplo n.º 9
0
    def test_hello(self):
        with open(os.path.join('test', 'testfiles_for_unittests',
                               'simple_gcc.elf.mips'), 'rb') as f:
            elf = ELFFile(f)
            self.assertEqual(elf.get_machine_arch(), 'MIPS')

            # Check some other properties of this ELF file derived from readelf
            self.assertEqual(elf['e_entry'], 0x0)
            self.assertEqual(elf.num_sections(), 25)
            self.assertEqual(elf.num_segments(), 0)
Ejemplo n.º 10
0
    def test_hello(self):
        with open(os.path.join('test', 'testfiles_for_unittests',
                               'simple_gcc.elf.arm'), 'rb') as f:
            elf = ELFFile(f)
            self.assertEqual(elf.get_machine_arch(), 'ARM')

            # Check some other properties of this ELF file derived from readelf
            self.assertEqual(elf['e_entry'], 0x8018)
            self.assertEqual(elf.num_sections(), 14)
            self.assertEqual(elf.num_segments(), 2)
Ejemplo n.º 11
0
def test_disasm_against_objdump(objdump_path, binary_path):
    # TODO: code repetition from test_disasm_standalone, encapsulate inner functionality.

    start_time = time.time()
    total_inst = 0
    match_inst = 0

    print(('Processing file:', binary_path))
    elf_file = ELFFile(open(binary_path, 'rb'))

    if elf_file.num_segments() == 0:
        print('There are no program headers in this file.')
        return

    objdump = ObjdumpWrapper(objdump_path)
    disasm = HexagonDisassembler(objdump_compatible=True)

    for segment in elf_file.iter_segments():
        if segment['p_flags'] & P_FLAGS.PF_X:

            print("Offset: {:x}".format(segment['p_offset']))
            print("VirtAddr: {:x}".format(segment['p_vaddr']))
            print("FileSiz: {:x}".format(segment['p_filesz']))

            segment_data = segment.data()
            data_pos = 0

            while data_pos + INST_SIZE <= len(segment_data):

                addr = segment['p_vaddr'] + data_pos

                inst_as_int = struct.unpack(
                    '<I', segment_data[data_pos:data_pos + 4])[0]

                disasm_output = disasm.disasm_one_inst(inst_as_int,
                                                       addr).text.strip()

                objdump_output = objdump.disasm_packet_raw(
                    segment_data[data_pos:min(data_pos + 4 * 4, segment_data)],
                    addr).strip()

                if (objdump_output != disasm_output):
                    print("[{:08x}] {:s}".format(addr, objdump_output))
                    print("[{:08x}] {:s}".format(addr, disasm_output))
                    print()
                else:
                    match_inst += 1

                data_pos += 4
                total_inst += 1

    elapsed_time = time.time() - start_time

    print("Elapsed time: {0:.2f}".format(elapsed_time))
    print('Match: {0:.2f}%'.format(match_inst / total_inst * 100))
Ejemplo n.º 12
0
def get_bl31_segments_info(bl31_file_name):
    """
    Get load offset, physical offset, file size
    from bl31 elf file program headers.
    """
    with open(bl31_file_name) as bl31_file:
        bl31 = ELFFile(bl31_file)

        num = bl31.num_segments()
        print 'Number of Segments : %d' % bl31.num_segments()
        for i in range(num):
            print 'Segment %d' % i
            seg = bl31.get_segment(i)
            ptype = seg[ELF_SEG_P_TYPE]
            poffset = seg[ELF_SEG_P_OFFSET]
            pmemsz = seg[ELF_SEG_P_MEMSZ]
            pfilesz = seg[ELF_SEG_P_FILESZ]
            print 'type: %s\nfilesz: %08x\nmemsz: %08x\noffset: %08x' % (ptype, pfilesz, pmemsz, poffset)
            paddr = seg[ELF_SEG_P_PADDR]
            print 'paddr: %08x' % paddr
Ejemplo n.º 13
0
def generate_atf_binary(bl31_file_name):
    with open(bl31_file_name) as bl31_file:
        bl31 = ELFFile(bl31_file)
        num = bl31.num_segments()
        for i in range(num):
            seg = bl31.get_segment(i)
            if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)):
                paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                file_name = 'bl31_0x%08x.bin' % paddr
                with open(file_name, "wb") as atf:
                    atf.write(seg.data())
def generate_atf_fit_dts(fit_file_name, bl31_file_name, uboot_file_name,
                         dtbs_file_name):
    """
    Generate FIT script for ATF image.
    """
    if fit_file_name != sys.stdout:
        fit_file = open(fit_file_name, "wb")
    else:
        fit_file = sys.stdout

    num_load_seg = 0
    p_paddr = 0xFFFFFFFF
    with open(uboot_file_name, 'rb') as uboot_file:
        uboot = ELFFile(uboot_file)
        for i in range(uboot.num_segments()):
            seg = uboot.get_segment(i)
            if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)):
                p_paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                num_load_seg = num_load_seg + 1

    assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1)

    fit_file.write(DT_HEADER % p_paddr)

    with open(bl31_file_name, 'rb') as bl31_file:
        bl31 = ELFFile(bl31_file)
        elf_entry = bl31.header['e_entry']
        for i in range(bl31.num_segments()):
            seg = bl31.get_segment(i)
            if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)):
                paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                p = seg.__getitem__(ELF_SEG_P_PADDR)
                append_atf_node(fit_file, i + 1, paddr, elf_entry)
    atf_cnt = i + 1
    append_fdt_node(fit_file, dtbs_file_name)
    fit_file.write('%s\n' % DT_IMAGES_NODE_END)
    append_conf_node(fit_file, dtbs_file_name, atf_cnt)
    fit_file.write('%s\n' % DT_END)

    if fit_file_name != sys.stdout:
        fit_file.close()
def get_bl31_segments_info(bl31_file_name):
    """
    Get load offset, physical offset, file size
    from bl31 elf file program headers.
    """
    with open(bl31_file_name) as bl31_file:
        bl31 = ELFFile(bl31_file)

        num = bl31.num_segments()
        print('Number of Segments : %d' % bl31.num_segments())
        for i in range(num):
            print('Segment %d' % i)
            seg = bl31.get_segment(i)
            ptype = seg[ELF_SEG_P_TYPE]
            poffset = seg[ELF_SEG_P_OFFSET]
            pmemsz = seg[ELF_SEG_P_MEMSZ]
            pfilesz = seg[ELF_SEG_P_FILESZ]
            print('type: %s\nfilesz: %08x\nmemsz: %08x\noffset: %08x' %
                  (ptype, pfilesz, pmemsz, poffset))
            paddr = seg[ELF_SEG_P_PADDR]
            print('paddr: %08x' % paddr)
Ejemplo n.º 16
0
def generate_atf_binary(bl31_file_name):
    with open(bl31_file_name) as bl31_file:
        bl31 = ELFFile(bl31_file)

        num = bl31.num_segments()
        for i in range(num):
            seg = bl31.get_segment(i)
            if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)):
                paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                file_name = 'bl31_0x%08x.bin' % paddr
                with open(file_name, "wb") as atf:
                    atf.write(seg.data());
Ejemplo n.º 17
0
def test_disasm_against_objdump(objdump_path, binary_path):
    # TODO: code repetition from test_disasm_standalone, encapsulate inner functionality.

    start_time = time.time()
    total_inst = 0
    match_inst = 0

    print(('Processing file:', binary_path))
    elf_file = ELFFile(open(binary_path, 'rb'))

    if elf_file.num_segments() == 0:
        print('There are no program headers in this file.')
        return

    objdump = ObjdumpWrapper(objdump_path)
    disasm = HexagonDisassembler(objdump_compatible=True)

    for segment in elf_file.iter_segments():
        if segment['p_flags'] & P_FLAGS.PF_X:

            print("Offset: {:x}".format(segment['p_offset']))
            print("VirtAddr: {:x}".format(segment['p_vaddr']))
            print("FileSiz: {:x}".format(segment['p_filesz']))

            segment_data = segment.data()
            data_pos = 0

            while data_pos + INST_SIZE <= len(segment_data):

                addr = segment['p_vaddr'] + data_pos

                inst_as_int = struct.unpack('<I', segment_data[data_pos: data_pos + 4])[0]

                disasm_output = disasm.disasm_one_inst(inst_as_int, addr).text.strip()

                objdump_output = objdump.disasm_packet_raw(
                    segment_data[data_pos: min(data_pos + 4 * 4, segment_data)],
                    addr).strip()

                if (objdump_output != disasm_output):
                    print("[{:08x}] {:s}".format(addr, objdump_output))
                    print("[{:08x}] {:s}".format(addr, disasm_output))
                    print()
                else:
                    match_inst += 1

                data_pos += 4
                total_inst += 1

    elapsed_time = time.time() - start_time

    print("Elapsed time: {0:.2f}".format(elapsed_time))
    print('Match: {0:.2f}%'.format(match_inst / total_inst * 100))
Ejemplo n.º 18
0
def generate_atf_fit_dts(fit_file_name, bl31_file_name, uboot_file_name, dtbs_file_name):
    """
    Generate FIT script for ATF image.
    """
    if fit_file_name != sys.stdout:
        fit_file = open(fit_file_name, "wb")
    else:
        fit_file = sys.stdout

    num_load_seg = 0
    p_paddr = 0xFFFFFFFF
    with open(uboot_file_name, 'rb') as uboot_file:
        uboot = ELFFile(uboot_file)
        for i in range(uboot.num_segments()):
            seg = uboot.get_segment(i)
            if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)):
                p_paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                num_load_seg = num_load_seg + 1

    assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1)

    fit_file.write(DT_HEADER % p_paddr)

    with open(bl31_file_name, 'rb') as bl31_file:
        bl31 = ELFFile(bl31_file)
        elf_entry = bl31.header['e_entry']
        for i in range(bl31.num_segments()):
            seg = bl31.get_segment(i)
            if ('PT_LOAD' == seg.__getitem__(ELF_SEG_P_TYPE)):
                paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                p= seg.__getitem__(ELF_SEG_P_PADDR)
                append_atf_node(fit_file, i+1, paddr, elf_entry)
    atf_cnt = i+1
    append_fdt_node(fit_file, dtbs_file_name)
    fit_file.write('%s\n' % DT_IMAGES_NODE_END)
    append_conf_node(fit_file, dtbs_file_name, atf_cnt)
    fit_file.write('%s\n' % DT_END)

    if fit_file_name != sys.stdout:
        fit_file.close()
Ejemplo n.º 19
0
def generate_atf_fit_dts_bl31(fit_file, bl31_file_name, dtbs_file_name):
    with open(bl31_file_name, 'rb') as bl31_file:
        bl31 = ELFFile(bl31_file)
        elf_entry = bl31.header['e_entry']
        segments = bl31.num_segments()
        for i in range(segments):
            seg = bl31.get_segment(i)
            if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD':
                paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                append_bl31_node(fit_file, i + 1, paddr, elf_entry)
    append_fdt_node(fit_file, dtbs_file_name)
    fit_file.write(DT_IMAGES_NODE_END)
    append_conf_node(fit_file, dtbs_file_name, segments)
Ejemplo n.º 20
0
def generate_atf_fit_dts_bl31(fit_file, bl31_file_name, dtbs_file_name):
    with open(bl31_file_name, 'rb') as bl31_file:
        bl31 = ELFFile(bl31_file)
        elf_entry = bl31.header['e_entry']
        segments = bl31.num_segments()
        for i in range(segments):
            seg = bl31.get_segment(i)
            if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD':
                paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                append_bl31_node(fit_file, i + 1, paddr, elf_entry)
    append_fdt_node(fit_file, dtbs_file_name)
    fit_file.write(DT_IMAGES_NODE_END)
    append_conf_node(fit_file, dtbs_file_name, segments)
Ejemplo n.º 21
0
def generate_atf_fit_dts_uboot(fit_file, uboot_file_name):
    num_load_seg = 0
    p_paddr = 0xFFFFFFFF
    with open(uboot_file_name, 'rb') as uboot_file:
        uboot = ELFFile(uboot_file)
        for i in range(uboot.num_segments()):
            seg = uboot.get_segment(i)
            if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD':
                p_paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                num_load_seg = num_load_seg + 1

    assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1)

    fit_file.write(DT_UBOOT % p_paddr)
Ejemplo n.º 22
0
def generate_atf_fit_dts_uboot(fit_file, uboot_file_name):
    num_load_seg = 0
    p_paddr = 0xFFFFFFFF
    with open(uboot_file_name, 'rb') as uboot_file:
        uboot = ELFFile(uboot_file)
        for i in range(uboot.num_segments()):
            seg = uboot.get_segment(i)
            if seg.__getitem__(ELF_SEG_P_TYPE) == 'PT_LOAD':
                p_paddr = seg.__getitem__(ELF_SEG_P_PADDR)
                num_load_seg = num_load_seg + 1

    assert (p_paddr != 0xFFFFFFFF and num_load_seg == 1)

    fit_file.write(DT_UBOOT % p_paddr)
Ejemplo n.º 23
0
def test_disasm_standalone(binary_path, timeout=None):

    profile = cProfile.Profile()
    profile.enable()

    start_time = time.time()

    print(('Processing file:', binary_path))
    elf_file = ELFFile(open(binary_path, 'rb'))

    if elf_file.num_segments() == 0:
        print('There are no program headers in this file.')
        return

    disasm = HexagonDisassembler()

    total_inst = 0

    for segment in elf_file.iter_segments():
        if segment['p_flags'] & P_FLAGS.PF_X:
            print("Offset: {:x}".format(segment['p_offset']))
            print("VirtAddr: {:x}".format(segment['p_vaddr']))
            print("FileSiz: {:x}".format(segment['p_filesz']))

            segment_data = segment.data()
            data_pos = 0

            while data_pos + INST_SIZE <= len(segment_data):

                addr = segment['p_vaddr'] + data_pos

                inst_as_int = struct.unpack(
                    '<I', segment_data[data_pos:data_pos + 4])[0]

                dis = disasm.disasm_one_inst(inst_as_int, addr)
                print("[{:08x}] {:s}".format(addr, dis.text))

                data_pos += 4
                total_inst += 1

                if timeout and (time.time() - start_time) > timeout:
                    break

    profile.disable()
    prof_stats = pstats.Stats(profile)
    prof_stats.strip_dirs().sort_stats('cumulative').print_stats(20)

    print("Total instructions: " + str(total_inst))
    elapsed_time = time.time() - start_time
    print("Elapsed time: " + str(elapsed_time))
Ejemplo n.º 24
0
def test_disasm_standalone(binary_path, timeout = None):

    profile = cProfile.Profile()
    profile.enable()

    start_time = time.time()

    print(('Processing file:', binary_path))
    elf_file = ELFFile(open(binary_path, 'rb'))

    if elf_file.num_segments() == 0:
        print('There are no program headers in this file.')
        return

    disasm = HexagonDisassembler()

    total_inst = 0

    for segment in elf_file.iter_segments():
        if segment['p_flags'] & P_FLAGS.PF_X:
            print("Offset: {:x}".format(segment['p_offset']))
            print("VirtAddr: {:x}".format(segment['p_vaddr']))
            print("FileSiz: {:x}".format(segment['p_filesz']))

            segment_data = segment.data()
            data_pos = 0

            while data_pos + INST_SIZE <= len(segment_data):

                addr = segment['p_vaddr'] + data_pos

                inst_as_int = struct.unpack('<I', segment_data[data_pos: data_pos + 4])[0]

                dis = disasm.disasm_one_inst(inst_as_int, addr)
                print("[{:08x}] {:s}".format(addr, dis.text))

                data_pos += 4
                total_inst += 1

                if timeout and (time.time() - start_time) > timeout:
                    break

    profile.disable()
    prof_stats = pstats.Stats(profile)
    prof_stats.strip_dirs().sort_stats('cumulative').print_stats(20)

    print("Total instructions: " + str(total_inst))
    elapsed_time = time.time() - start_time
    print("Elapsed time: " + str(elapsed_time))
Ejemplo n.º 25
0
    def test_basic(self):
        with open(os.path.join('test', 'testfiles_for_unittests',
                               'simple_gcc.elf.mips'), 'rb') as f:
            elf = ELFFile(f)
            self.assertEqual(elf.get_machine_arch(), 'MIPS')

            # Check some other properties of this ELF file derived from readelf
            self.assertEqual(elf['e_entry'], 0x0)
            self.assertEqual(elf.num_sections(), 25)
            self.assertEqual(elf.num_segments(), 0)

            # Test that Mips-specific section types work; these types are
            # available only when the file is identified as MIPS in the
            # e_machine header field.
            sec9 = elf.get_section(9)
            self.assertEqual(sec9['sh_type'], 'SHT_MIPS_DWARF')
    def test_basic(self):
        with open(
                os.path.join('test', 'testfiles_for_unittests',
                             'simple_gcc.elf.mips'), 'rb') as f:
            elf = ELFFile(f)
            self.assertEqual(elf.get_machine_arch(), 'MIPS')

            # Check some other properties of this ELF file derived from readelf
            self.assertEqual(elf['e_entry'], 0x0)
            self.assertEqual(elf.num_sections(), 25)
            self.assertEqual(elf.num_segments(), 0)

            # Test that Mips-specific section types work; these types are
            # available only when the file is identified as MIPS in the
            # e_machine header field.
            sec9 = elf.get_section(9)
            self.assertEqual(sec9['sh_type'], 'SHT_MIPS_DWARF')
Ejemplo n.º 27
0
def elf_to_dol(elf_path, dol_path):
	from elftools.elf.elffile import ELFFile

	with open(elf_path, 'rb') as elf_file, open(dol_path, 'wb') as dol_file:
		elf = ELFFile(elf_file)
		num_segments = elf.num_segments()

		dol_file.write(bytes([0x00] * 0x100))

		idx = 0
		for i in range(num_segments):
			segment = elf.get_segment(i)
			if not segment_is_text(segment):
				continue
			write_segment_to_dol(idx, segment, dol_file)
			idx += 1

		idx = 7
		for i in range(num_segments):
			segment = elf.get_segment(i)
			if not segment_is_data(segment):
				continue
			write_segment_to_dol(idx, segment, dol_file)
			idx += 1

		bss_start = 0
		bss_end = 0
		for i in range(num_segments):
			segment = elf.get_segment(i)
			if not segment_is_bss(segment):
				continue
			if bss_start == 0:
				bss_start = segment["p_vaddr"]
			bss_end = segment["p_vaddr"] + segment["p_memsz"]
		write_to_dol_header(dol_file, 0xd8, bss_start)
		bss_size = bss_end - bss_start
		write_to_dol_header(dol_file, 0xdc, bss_size)

		write_to_dol_header(dol_file, 0xe0, elf["e_entry"])
Ejemplo n.º 28
0
 def _get_buffer_range_elf(self, elf: ELFFile, address: int):
     PT_LOAD = {}
     if not elf.num_segments():
         raise LookupError(
             'The elftools parser did not find any segments in this file.')
     for segment in elf.iter_segments():
         if segment.header.p_type == 'PT_LOAD':
             PT_LOAD[segment.header.p_vaddr] = segment
             self.log_info(
                 F'Found PT_LOAD segment with base address 0x{segment.header.p_vaddr:x}'
             )
     if not PT_LOAD:
         raise LookupError(
             F'Could not find any PT_LOAD segment containing 0x{address:x}.'
         )
     addr = self._rebase(address, min(PT_LOAD))
     for segment in elf.iter_segments():
         begin = segment.header.p_vaddr
         size = segment.header.p_memsz
         delta = addr - begin
         if delta in range(size + 1):
             offset = segment.header.p_offset
             return offset + delta, offset + segment.header.p_filesz
     raise CompartmentNotFound(address)
Ejemplo n.º 29
0
class Elf(object):
    def __init__(self, fileobj):
        self.elffile = ELFFile(fileobj)
        self.output = sys.stdout

    # our code starts here :-)

    def network(self):
        ret = "None"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                # first match IP_PATTERNS
                for pattern in IP_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        return "network-ip"
                # then match LOCAL_PATTERNS
                for pattern in LOCAL_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        ret = "network-local"
                        break
        return ret

    def fortify(self):
        """ NA : FORTIFY_SOURCE was not applicable
            Enabled : unsafe and _chk functions were found
            Disabled : only unsafe functions were found
                       (_chk functions missing)"""
        ret = "NA"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in UNSAFE_FUNCTIONS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        if ret == "NA":
                            ret = "Disabled"
                            break

            if ret == "Disabled":
                # afename = "__" + bytes2str(symbol.name) + "_chk"
                for _, symbol in enumerate(section.iter_symbols()):
                    # first look for corresponding _chk symbol
                    symbolstr = bytes2str(symbol.name)
                    if (symbolstr.startswith("__") and
                            symbolstr.endswith("_chk")) or \
                            symbolstr.endswith(" __chk_fail"):
                        ret = "Enabled"
                        break
        return ret


    def canary(self):
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print ("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                if re.match(STACK_CHK, bytes2str(symbol.name)):
                    return "Enabled"
        return "Disabled"

    def dynamic_tags(self, key="DT_RPATH"):
        for section in self.elffile.iter_sections():
            if not isinstance(section, DynamicSection):
                continue
            for tag in section.iter_tags():
                if tag.entry.d_tag == key:
                    return "Enabled"
        return "Disabled"

    def program_headers(self):
        pflags = P_FLAGS()
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        for segment in self.elffile.iter_segments():
            if re.search("GNU_STACK", segment['p_type']):
                if segment['p_flags'] & pflags.PF_X:
                    return "Disabled"
        return "Enabled"

    def relro(self):
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        have_relro = False
        for segment in self.elffile.iter_segments():
            if re.search("GNU_RELRO", segment['p_type']):
                have_relro = True
                break
        if self.dynamic_tags("DT_BIND_NOW") == "Enabled" and have_relro:
            return "Enabled"
        if have_relro:
            return "Partial"

        return "Disabled"

    def pie(self):
        header = self.elffile.header
        if self.dynamic_tags("EXEC") == "Enabled":
            return "Disabled"
        if "ET_DYN" in header['e_type']:
            if self.dynamic_tags("DT_DEBUG") == "Enabled":
                return "Enabled"
            else:
                return "DSO"
        return "Disabled"

    def getdeps(self):
        deps=[]

        if self.elffile.num_segments() == 0:
            return deps

        for segment in self.elffile.iter_segments():
            if re.search("PT_DYNAMIC", segment['p_type']):
                # this file uses dynamic linking, so read the dynamic section
                # and find DT_SONAME tag
                for section in self.elffile.iter_sections():
                    if not isinstance(section, DynamicSection):
                        continue
                    for tag in section.iter_tags():
                        if tag.entry.d_tag == 'DT_NEEDED':
                            deps.append(bytes2str(tag.needed))
                break

        return deps
Ejemplo n.º 30
0
class Elf(object):
    def __init__(self, fileobj):
        self.elffile = ELFFile(fileobj)
        self.output = sys.stdout

    # our code starts here :-)

    def network(self):
        ret = "None"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print(
                    "\nSymbol table '%s' has a sh_entsize "
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                # first match IP_PATTERNS
                for pattern in IP_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        return "network-ip"
                # then match LOCAL_PATTERNS
                for pattern in LOCAL_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        ret = "network-local"
                        break
        return ret

    def _strings(self):
        stream = self.elffile.stream
        epos = stream.tell()
        stream.seek(0, 0)
        data = stream.read()
        stream.seek(epos, 0)

        ret = []

        # XXX avoid calling eu-strings
        import subprocess
        p = subprocess.Popen(
            "eu-strings", shell=True, stdin=subprocess.PIPE,
            stderr=subprocess.PIPE, stdout=subprocess.PIPE)
        out = p.communicate(input=data)[0]

        for line in out.splitlines():
            if re.match(b"^/tmp/.+", line) and "XXX" not in line:
                ret.append(line)

        return ret

    def tempstuff(self):
        tmp_strings = self._strings()

        # if there are no /tmp references, just return
        if len(tmp_strings) == 0:
            return "None"

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print(
                    "\nSymbol table '%s' has a sh_entsize "
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in TMP_FUNCTIONS:
                    if re.match(pattern, bytes2str(symbol.name)):

                        return "None"

        return "$".join(tmp_strings)

    # XXX implement this
    def chroot_without_chdir(self):
        """
        Check for apps that use chroot(2) without using chdir(2).

        Inspired by http://people.redhat.com/sgrubb/security/find-chroot

        """
        pass

    def fortify(self):
        """
        Check if source code was compiled with FORTIFY_SOURCE.

        Enabled : no unsafe functions were found OR all were translated to _chk versions
        Partial : unprotected unsafe functions were found

        TODO
        ====

        * Print summary report like checksec.sh does

        * Drop CSV output support (it is too restrictive)

        * "addr2line" like feature for unprotected unsafe functions

        """
        unsafe_list = []

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print(
                    "\nSymbol table '%s' has a sh_entsize "
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in UNSAFE_FUNCTIONS:
                    if re.match(pattern + "$", bytes2str(symbol.name)):
                        unsafe_list.append(bytes2str(symbol.name))

        if len(unsafe_list) == 0:
            return "Enabled"
        else:
            return "Partial$" + "$".join(unsafe_list)


    def canary(self):
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print(
                    "\nSymbol table '%s' has a sh_entsize "
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                if bytes2str(symbol.name) in STACK_CHK:
                    return "Enabled"
        return "Disabled"

    def dynamic_tags(self, key="DT_RPATH"):
        for section in self.elffile.iter_sections():
            if not isinstance(section, DynamicSection):
                continue
            for tag in section.iter_tags():
                if tag.entry.d_tag == key:
                    return "Enabled"
        return "Disabled"

    def program_headers(self):
        pflags = P_FLAGS()
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        found = False
        for segment in self.elffile.iter_segments():
            if re.search("GNU_STACK", str(segment['p_type'])):
                found = True
                if segment['p_flags'] & pflags.PF_X:
                    return "Disabled"
        if found:
            return "Enabled"

        return "Disabled"

    def relro(self):
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        have_relro = False
        for segment in self.elffile.iter_segments():
            if re.search("GNU_RELRO", str(segment['p_type'])):
                have_relro = True
                break
        if self.dynamic_tags("DT_BIND_NOW") == "Enabled" and have_relro:
            return "Enabled"
        if have_relro:
            return "Partial"

        return "Disabled"

    def pie(self):
        header = self.elffile.header
        if self.dynamic_tags("EXEC") == "Enabled":
            return "Disabled"
        if "ET_DYN" in header['e_type']:
            if self.dynamic_tags("DT_DEBUG") == "Enabled":
                return "Enabled"
            else:
                return "DSO"
        return "Disabled"

    def getdeps(self):
        deps = []

        if self.elffile.num_segments() == 0:
            return deps

        for segment in self.elffile.iter_segments():
            if re.search("PT_DYNAMIC", str(segment['p_type'])):
                # this file uses dynamic linking, so read the dynamic section
                # and find DT_SONAME tag
                for section in self.elffile.iter_sections():
                    if not isinstance(section, DynamicSection):
                        continue
                    for tag in section.iter_tags():
                        if tag.entry.d_tag == 'DT_NEEDED':
                            deps.append(bytes2str(tag.needed))
                break

        return deps
Ejemplo n.º 31
0
class Elf(object):
    def __init__(self, fileobj):
        self.elffile = ELFFile(fileobj)
        self.output = sys.stdout

    # our code starts here :-)

    def network(self):
        ret = "None"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                # first match IP_PATTERNS
                for pattern in IP_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        return "network-ip"
                # then match LOCAL_PATTERNS
                for pattern in LOCAL_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        ret = "network-local"
                        break
        return ret


    def _strings(self):
        stream = self.elffile.stream
        epos = stream.tell()
        stream.seek(0, 0)
        data = stream.read()
        stream.seek(epos, 0)

        ret = []

        # XXX avoid calling eu-strings
        import subprocess
        p = subprocess.Popen("eu-strings", shell=True, stdin=subprocess.PIPE,
                stderr=subprocess.PIPE, stdout=subprocess.PIPE)
        out = p.communicate(input=data)[0]

        for line in out.splitlines():
            if re.match("^/tmp/.+", line) and "XXX" not in line:
                ret.append(line)

        return ret

    def tempstuff(self):
        tmp_strings = self._strings()

        # if there are no /tmp references, just return
        if len(tmp_strings) == 0:
            return "None"

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in TMP_FUNCTIONS:
                    if re.match(pattern, bytes2str(symbol.name)):

                        return "None"

        return "$".join(tmp_strings)


    # XXX implement this
    def chroot_without_chdir(self):
        """
        This functions looks for apps that use chroot(2) without using chdir(2).
        Inspired by http://people.redhat.com/sgrubb/security/find-chroot
        """
        pass

    def fortify(self):
        """ NA : FORTIFY_SOURCE was not applicable
            Enabled : unsafe and _chk functions were found
            Disabled : only unsafe functions were found
                       (_chk functions missing)"""
        ret = "NA"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in UNSAFE_FUNCTIONS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        if ret == "NA":
                            ret = "Disabled"
                            break

            if ret == "Disabled":
                # afename = "__" + bytes2str(symbol.name) + "_chk"
                for _, symbol in enumerate(section.iter_symbols()):
                    # first look for corresponding _chk symbol
                    symbolstr = bytes2str(symbol.name)
                    if (symbolstr.startswith("__") and
                            symbolstr.endswith("_chk")) or \
                            symbolstr.endswith(" __chk_fail"):
                        ret = "Enabled"
                        break
        return ret


    def canary(self):
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print ("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                if re.match(STACK_CHK, bytes2str(symbol.name)):
                    return "Enabled"
        return "Disabled"

    def dynamic_tags(self, key="DT_RPATH"):
        for section in self.elffile.iter_sections():
            if not isinstance(section, DynamicSection):
                continue
            for tag in section.iter_tags():
                if tag.entry.d_tag == key:
                    return "Enabled"
        return "Disabled"

    def program_headers(self):
        pflags = P_FLAGS()
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        for segment in self.elffile.iter_segments():
            if re.search("GNU_STACK", segment['p_type']):
                if segment['p_flags'] & pflags.PF_X:
                    return "Disabled"
        return "Enabled"

    def relro(self):
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        have_relro = False
        for segment in self.elffile.iter_segments():
            if re.search("GNU_RELRO", segment['p_type']):
                have_relro = True
                break
        if self.dynamic_tags("DT_BIND_NOW") == "Enabled" and have_relro:
            return "Enabled"
        if have_relro:
            return "Partial"

        return "Disabled"

    def pie(self):
        header = self.elffile.header
        if self.dynamic_tags("EXEC") == "Enabled":
            return "Disabled"
        if "ET_DYN" in header['e_type']:
            if self.dynamic_tags("DT_DEBUG") == "Enabled":
                return "Enabled"
            else:
                return "DSO"
        return "Disabled"

    def getdeps(self):
        deps=[]

        if self.elffile.num_segments() == 0:
            return deps

        for segment in self.elffile.iter_segments():
            if re.search("PT_DYNAMIC", segment['p_type']):
                # this file uses dynamic linking, so read the dynamic section
                # and find DT_SONAME tag
                for section in self.elffile.iter_sections():
                    if not isinstance(section, DynamicSection):
                        continue
                    for tag in section.iter_tags():
                        if tag.entry.d_tag == 'DT_NEEDED':
                            deps.append(bytes2str(tag.needed))
                break

        return deps
Ejemplo n.º 32
0
from elftools.elf.elffile import ELFFile

if __name__ == '__main__':

    if len(sys.argv) < 2:
        print(
            "You must provide this script with an elf binary file you want to examine"
        )
        exit(1)

    print(f"Mapping between segments and sections in the file {sys.argv[1]}")

    elffile = ELFFile(open(sys.argv[1], 'rb'))

    segments = list()
    for segment_idx in range(elffile.num_segments()):
        segments.insert(segment_idx, dict())
        segments[segment_idx]['segment'] = elffile.get_segment(segment_idx)
        segments[segment_idx]['sections'] = list()

    for section_idx in range(elffile.num_sections()):
        section = elffile.get_section(section_idx)
        for segment in segments:
            if segment['segment'].section_in_segment(section):
                segment['sections'].append(section)

    for segment in segments:
        seg_head = segment['segment'].header
        print("Segment:")
        print(
            f"Type: {seg_head.p_type}\nOffset: {hex(seg_head.p_offset)}\nVirtual address: {hex(seg_head.p_vaddr)}\nPhysical address: {(seg_head.p_paddr)}\nSize in file: {hex(seg_head.p_filesz)}\nSize in memory: {hex(seg_head.p_memsz)}\n"
Ejemplo n.º 33
0
    def load(cls, fo):
        ef = ELFFile(fo)

        if ef.header['e_type'] != 'ET_EXEC':
            raise RuntimeError(
                'not an ELF executable file (type {})'.format(ef.header['e_type']))
        if ef.header['e_machine'] != 'EM_68K':
            raise RuntimeError('not an M68K ELF file')
        if ef.num_segments() != 2:
            raise RuntimeError('wrong number of segments in ELF file')

        # Look at segments for text and data; note that we
        # expect to see exactly two segments, one RX, one RW,
        # for text and data respectively, with data immediately
        # following text in memory.

        textSegment = ef.get_segment(0)
        textAddress = textSegment['p_vaddr']
        textSize = textSegment['p_filesz']

        dataSegment = ef.get_segment(1)
        dataAddress = dataSegment['p_vaddr']
        dataSize = dataSegment['p_filesz']

        # Look for BSS sections
        bssAddress = None
        bssLimit = None
        for section in ef.iter_sections():
            if (section['sh_type'] == 'SHT_NOBITS') and (section['sh_flags'] & SH_FLAGS.SHF_ALLOC):

                secStart = section['sh_addr']
                secLimit = section['sh_addr'] + section['sh_size']

                # track low BSS address
                if bssAddress is None:
                    bssAddress = secStart
                elif secStart < bssAddress:
                    bssAddress = secStart

                # track BSS limit
                if bssLimit is None:
                    bssLimit = secLimit
                elif secLimit > bssLimit:
                    bssLimit = secLimit

        if bssAddress is None:
            bssAddress = dataAddress + dataSize
            bssSize = 0
        else:
            bssSize = bssLimit - bssAddress

        # extend text to cover the gap created by data segment alignment
        dataGap = dataAddress - (textAddress + textSize)
        if dataGap < 0:
            raise RuntimeError('data segment before text')
        textSize += dataGap

        # extend data to cover the gap created by BSS alignment
        bssGap = bssAddress - (dataAddress + dataSize)
        if bssGap < 0:
            raise RuntimeError('BSS before data segment (0x{:x} inside/before 0x{:x}/0x{:x}'.format(
                bssAddress, dataAddress, dataSize))
        dataSize += bssGap

        # sanity-check the text and data segments
        if (textSegment['p_type'] != 'PT_LOAD') or (dataSegment['p_type'] != 'PT_LOAD'):
            raise RuntimeError('expected two PT_LOAD segments')

        if (textSegment['p_flags'] & cls.P_FLAGS_MASK) != cls.P_FLAGS_RX:
            raise RuntimeError('text segment is not RX')
        if textAddress != 0:
            raise RuntimeError('text segment is not at 0')

        if (dataSegment['p_flags'] & cls.P_FLAGS_MASK) != cls.P_FLAGS_RW:
            raise RuntimeError('data segment is not RW')
        if dataAddress != textAddress + textSize:
            raise RuntimeError('data segment @ 0x{:x} does not follow text 0x{:x}/0x{:x}'.format(
                dataAddress, textAddress, textSize))

        text = textSegment.data().ljust(textSize, '\0')
        data = dataSegment.data().ljust(dataSize, '\0')

        if len(text) != textSize:
            raise RuntimeError('text size mismatch')
        if len(data) != dataSize:
            raise RuntimeError('data size mismatch')

        print('text 0x{:x} data 0x{:x} bss 0x{:x}'.format(textSize, dataSize, bssSize))

        # look for relocations
        relocs = dict()
        for section in ef.iter_sections():
            if isinstance(section, RelocationSection):

                # what section do these relocations affect?
                relocSection = ef.get_section(section['sh_info'])
                if not (relocSection['sh_flags'] & SH_FLAGS.SHF_ALLOC):
                    #print('Not relocating {}'.format(relocSection.name))
                    continue

                #print('Relocate: {} using {}'.format(relocSection.name, section.name))
                symtab = ef.get_section(section['sh_link'])

                for reloc in section.iter_relocations():
                    relAddress = reloc['r_offset']
                    if relAddress >= (len(text) + len(data)):
                        raise RuntimeError('relocation outside known space')

                    if not reloc.is_RELA():
                        raise RuntimeError('unexpected REL reloc')

                    relType = reloc['r_info_type']

                    # get the symbol table entry the reloc refers to
                    if reloc['r_info_sym'] >= symtab.num_symbols():
                        raise RuntimeError(
                            'symbol reference in relocation out of bounds')
                    relTarget = symtab.get_symbol(reloc['r_info_sym'])['st_value']

                    # It looks like we can ignore the addend, as it's already
                    # present in the object file...
                    relAddend = reloc['r_addend']

                    # Sort out what we're going to do with this relocation...
                    if relType == R_68K_32:
                        pass
                    elif relType == R_68K_NONE:
                        #print('ignoring none-reloc @ 0x{:x}'.format(relAddress))
                        continue
                    elif relType == R_68K_PC32:
                        #print('ignoring PC32 reloc @ 0x{:x} -> 0x{:x}+{:x}'.format(relAddress, relTarget, relAddend))
                        continue
                    elif relType == R_68K_PC16:
                        #print('ignoring PC16 reloc @ 0x{:x} -> 0x{:x}+{:x}'.format(relAddress, relTarget, relAddend))
                        continue
                    elif relType == R_68K_PC8:
                        #print('ignoring PC8 reloc @ 0x{:x} -> 0x{:x}+{:x}'.format(relAddress, relTarget, relAddend))
                        continue
                    else:
                        raise RuntimeError('unexpected relocation type {} @ 0x{:x} -> 0x{:x}+x'.format(relType, relAddress, relTarget, relAddend))

                    #print('RELA address 0x{:08x} target 0x{:x} type {} addend 0x{:x}'.format(relAddress, relTarget, relType, relAddend))

                    if relTarget < len(text):
                        relType |= R_TEXT
                        if relAddend > len(text):
                            raise RuntimeError('addend outside of text section')
                    elif relTarget < (len(text) + len(data)):
                        relType |= R_DATA
                        if relAddend > len(data):
                            raise RuntimeError('addend outside of data section')
                    elif relTarget <= (len(text) + len(data) + bssSize):
                        # note, <= to allow pointer to _end, which is immediately *after* the BSS
                        relType |= R_BSS
                        if relAddend > bssSize:
                            raise RuntimeError('addend outside of bss section')
                    else:
                        raise RuntimeError(
                            'relocation target not in known space')

                    #print('    -> type 0x{:03x}'.format(relType))

                    if relAddress < len(text):
                        inSeg = text
                        segOffset = relAddress
                    elif relAddress < (len(text) + len(data)):
                        inSeg = data
                        segOffset = relAddress - len(text)
                    else:
                        raise RuntimeError('relocation not in known space')

                    unRelocated = struct.unpack('>L', inSeg[segOffset:segOffset+4])[0]
                    #print('    unrelocated: 0x{:x}'.format(unRelocated))

                    if unRelocated != (relTarget + relAddend):
                        raise RuntimeError("unrelocated field 0x{:x} != target 0x{:x} + addend 0x{:x}".format(unRelocated, relTarget, relAddend))

                    relocs[relAddress] = relType

        return cls(text=text,
                   data=data,
                   bssSize=bssSize,
                   relocs=relocs)
import msvcrt
from elftools.common import *
from elftools.elf.elffile import ELFFile

with open('jnilibs/libnative-lib.so', 'rb') as f:
    elf = ELFFile(f)

    print(type(elf))
    for seg in elf.iter_segments():
        print(seg['p_type'], hex(seg['p_offset']), hex(seg['p_vaddr']),
              hex(seg['p_paddr']))
    exit()

    sec_num = elf.num_sections()
    print('sec_num:' + str(sec_num))
    seg_num = elf.num_segments()
    print('seg_num:' + str(seg_num))

    for i in range(0, sec_num, 1):
        section = elf.get_section(i)
        print(section)
        print(type(section))
        print(section.data_alignment, section.data_size,
              str(section.data(), encoding='ISO-8859-1'))
        msvcrt.getch()

    # for i in range(0, seg_num, 1):
    #     segment = elf.get_segment(i)
    #     print(str(segment.data(), encoding='ISO-8859-1'))
    # print(segment)
    # print(segment.data_alignment, section.data_size, str(section.data(), encoding='ISO-8859-1'))
Ejemplo n.º 35
0
from Crypto.PublicKey import DSA
import re
def slugify(value):
    """
    Normalizes string, converts to lowercase, removes non-alpha characters,
    and converts spaces to hyphens.
    """
    import unicodedata
    value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
    value=value.decode('utf-8')
    value = re.sub('[^\w\s-]', '_', value).strip()
    value = re.sub('[-\s]+', '_', value)
    return value
fl=open(sys.argv[1], 'rb')
elffile = ELFFile(fl)
print("Segments: {}".format(elffile.num_segments()) )

dprint=False

class Segment(object):
    virtMem=None
    offset=None
    sz=None
    data=None
    def __init__(self,segm,fl):
        self.virtMem=segm['p_vaddr']
        self.offset=segm['p_offset']
        self.sz=segm['p_memsz']
        fl.seek(self.offset)
        self.data=fl.read(self.sz)
        #print(data)
Ejemplo n.º 36
0
class ReadElf(object):
    """ display_* methods are used to emit output into the output stream
    """
    def __init__(self, file, output):
        """ file:
                stream object with the ELF file to read

            output:
                output stream to write to
        """
        self.elffile = ELFFile(file)
        self.output = output

        # Lazily initialized if a debug dump is requested
        self._dwarfinfo = None

    def display_file_header(self):
        """ Display the ELF file header
        """
        self._emitline('ELF Header:')
        self._emit('  Magic:   ')
        self._emitline(' '.join('%2.2x' % byte2int(b)
                                for b in self.elffile.e_ident_raw))
        header = self.elffile.header
        e_ident = header['e_ident']
        self._emitline('  Class:                             %s' %
                       describe_ei_class(e_ident['EI_CLASS']))
        self._emitline('  Data:                              %s' %
                       describe_ei_data(e_ident['EI_DATA']))
        self._emitline('  Version:                           %s' %
                       describe_ei_version(e_ident['EI_VERSION']))
        self._emitline('  OS/ABI:                            %s' %
                       describe_ei_osabi(e_ident['EI_OSABI']))
        self._emitline('  ABI Version:                       %d' %
                       e_ident['EI_ABIVERSION'])
        self._emitline('  Type:                              %s' %
                       describe_e_type(header['e_type']))
        self._emitline('  Machine:                           %s' %
                       describe_e_machine(header['e_machine']))
        self._emitline('  Version:                           %s' %
                       describe_e_version_numeric(header['e_version']))
        self._emitline('  Entry point address:               %s' %
                       self._format_hex(header['e_entry']))
        self._emit('  Start of program headers:          %s' %
                   header['e_phoff'])
        self._emitline(' (bytes into file)')
        self._emit('  Start of section headers:          %s' %
                   header['e_shoff'])
        self._emitline(' (bytes into file)')
        self._emitline('  Flags:                             %s' %
                       self._format_hex(header['e_flags']))
        self._emitline('  Size of this header:               %s (bytes)' %
                       header['e_ehsize'])
        self._emitline('  Size of program headers:           %s (bytes)' %
                       header['e_phentsize'])
        self._emitline('  Number of program headers:         %s' %
                       header['e_phnum'])
        self._emitline('  Size of section headers:           %s (bytes)' %
                       header['e_shentsize'])
        self._emitline('  Number of section headers:         %s' %
                       header['e_shnum'])
        self._emitline('  Section header string table index: %s' %
                       header['e_shstrndx'])

    def display_program_headers(self, show_heading=True):
        """ Display the ELF program headers.
            If show_heading is True, displays the heading for this information
            (Elf file type is...)
        """
        self._emitline()
        if self.elffile.num_segments() == 0:
            self._emitline('There are no program headers in this file.')
            return

        elfheader = self.elffile.header
        if show_heading:
            self._emitline('Elf file type is %s' %
                           describe_e_type(elfheader['e_type']))
            self._emitline('Entry point is %s' %
                           self._format_hex(elfheader['e_entry']))
            # readelf weirness - why isn't e_phoff printed as hex? (for section
            # headers, it is...)
            self._emitline(
                'There are %s program headers, starting at offset %s' %
                (elfheader['e_phnum'], elfheader['e_phoff']))
            self._emitline()

        self._emitline('Program Headers:')

        # Now comes the table of program headers with their attributes. Note
        # that due to different formatting constraints of 32-bit and 64-bit
        # addresses, there are some conditions on elfclass here.
        #
        # First comes the table heading
        #
        if self.elffile.elfclass == 32:
            self._emitline(
                '  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align'
            )
        else:
            self._emitline(
                '  Type           Offset             VirtAddr           PhysAddr'
            )
            self._emitline(
                '                 FileSiz            MemSiz              Flags  Align'
            )

        # Now the entries
        #
        for segment in self.elffile.iter_segments():
            self._emit('  %-14s ' % describe_p_type(segment['p_type']))

            if self.elffile.elfclass == 32:
                self._emitline(
                    '%s %s %s %s %s %-3s %s' %
                    (self._format_hex(segment['p_offset'], fieldsize=6),
                     self._format_hex(segment['p_vaddr'], fullhex=True),
                     self._format_hex(segment['p_paddr'], fullhex=True),
                     self._format_hex(segment['p_filesz'], fieldsize=5),
                     self._format_hex(segment['p_memsz'], fieldsize=5),
                     describe_p_flags(segment['p_flags']),
                     self._format_hex(segment['p_align'])))
            else:  # 64
                self._emitline(
                    '%s %s %s' %
                    (self._format_hex(segment['p_offset'], fullhex=True),
                     self._format_hex(segment['p_vaddr'], fullhex=True),
                     self._format_hex(segment['p_paddr'], fullhex=True)))
                self._emitline('                 %s %s  %-3s    %s' % (
                    self._format_hex(segment['p_filesz'], fullhex=True),
                    self._format_hex(segment['p_memsz'], fullhex=True),
                    describe_p_flags(segment['p_flags']),
                    # lead0x set to False for p_align, to mimic readelf.
                    # No idea why the difference from 32-bit mode :-|
                    self._format_hex(segment['p_align'], lead0x=False)))

            if isinstance(segment, InterpSegment):
                self._emitline('      [Requesting program interpreter: %s]' %
                               bytes2str(segment.get_interp_name()))

        # Sections to segments mapping
        #
        if self.elffile.num_sections() == 0:
            # No sections? We're done
            return

        self._emitline('\n Section to Segment mapping:')
        self._emitline('  Segment Sections...')

        for nseg, segment in enumerate(self.elffile.iter_segments()):
            self._emit('   %2.2d     ' % nseg)

            for section in self.elffile.iter_sections():
                if (not section.is_null()
                        and segment.section_in_segment(section)):
                    self._emit('%s ' % bytes2str(section.name))

            self._emitline('')

    def display_section_headers(self, show_heading=True):
        """ Display the ELF section headers
        """
        elfheader = self.elffile.header
        if show_heading:
            self._emitline(
                'There are %s section headers, starting at offset %s' %
                (elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))

        self._emitline('\nSection Header%s:' %
                       ('s' if elfheader['e_shnum'] > 1 else ''))

        # Different formatting constraints of 32-bit and 64-bit addresses
        #
        if self.elffile.elfclass == 32:
            self._emitline(
                '  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al'
            )
        else:
            self._emitline(
                '  [Nr] Name              Type             Address           Offset'
            )
            self._emitline(
                '       Size              EntSize          Flags  Link  Info  Align'
            )

        # Now the entries
        #
        for nsec, section in enumerate(self.elffile.iter_sections()):
            self._emit(
                '  [%2u] %-17.17s %-15.15s ' % (nsec, bytes2str(
                    section.name), describe_sh_type(section['sh_type'])))

            if self.elffile.elfclass == 32:
                self._emitline(
                    '%s %s %s %s %3s %2s %3s %2s' %
                    (self._format_hex(
                        section['sh_addr'], fieldsize=8, lead0x=False),
                     self._format_hex(
                         section['sh_offset'], fieldsize=6, lead0x=False),
                     self._format_hex(
                         section['sh_size'], fieldsize=6, lead0x=False),
                     self._format_hex(
                         section['sh_entsize'], fieldsize=2,
                         lead0x=False), describe_sh_flags(
                             section['sh_flags']), section['sh_link'],
                     section['sh_info'], section['sh_addralign']))
            else:  # 64
                self._emitline(
                    ' %s  %s' %
                    (self._format_hex(
                        section['sh_addr'], fullhex=True, lead0x=False),
                     self._format_hex(section['sh_offset'],
                                      fieldsize=16 if
                                      section['sh_offset'] > 0xffffffff else 8,
                                      lead0x=False)))
                self._emitline(
                    '       %s  %s %3s      %2s   %3s     %s' %
                    (self._format_hex(
                        section['sh_size'], fullhex=True, lead0x=False),
                     self._format_hex(
                         section['sh_entsize'], fullhex=True,
                         lead0x=False), describe_sh_flags(
                             section['sh_flags']), section['sh_link'],
                     section['sh_info'], section['sh_addralign']))

        self._emitline('Key to Flags:')
        self._emit(
            '  W (write), A (alloc), X (execute), M (merge), S (strings)')
        if self.elffile['e_machine'] in ('EM_X86_64', 'EM_L10M'):
            self._emitline(', l (large)')
        else:
            self._emitline()
        self._emitline(
            '  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)'
        )
        self._emitline(
            '  O (extra OS processing required) o (OS specific), p (processor specific)'
        )

    def display_symbol_tables(self):
        """ Display the symbol tables contained in the file
        """
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue

            if section['sh_entsize'] == 0:
                self._emitline(
                    "\nSymbol table '%s' has a sh_entsize of zero!" %
                    (bytes2str(section.name)))
                continue

            self._emitline("\nSymbol table '%s' contains %s entries:" %
                           (bytes2str(section.name), section.num_symbols()))

            if self.elffile.elfclass == 32:
                self._emitline(
                    '   Num:    Value  Size Type    Bind   Vis      Ndx Name')
            else:  # 64
                self._emitline(
                    '   Num:    Value          Size Type    Bind   Vis      Ndx Name'
                )

            for nsym, symbol in enumerate(section.iter_symbols()):
                # symbol names are truncated to 25 chars, similarly to readelf
                self._emitline(
                    '%6d: %s %5d %-7s %-6s %-7s %4s %.25s' %
                    (nsym,
                     self._format_hex(symbol['st_value'],
                                      fullhex=True,
                                      lead0x=False), symbol['st_size'],
                     describe_symbol_type(symbol['st_info']['type']),
                     describe_symbol_bind(symbol['st_info']['bind']),
                     describe_symbol_visibility(
                         symbol['st_other']['visibility']),
                     describe_symbol_shndx(
                         symbol['st_shndx']), bytes2str(symbol.name)))

    def display_relocations(self):
        """ Display the relocations contained in the file
        """
        has_relocation_sections = False
        for section in self.elffile.iter_sections():
            if not isinstance(section, RelocationSection):
                continue

            has_relocation_sections = True
            self._emitline(
                "\nRelocation section '%s' at offset %s contains %s entries:" %
                (bytes2str(section.name), self._format_hex(
                    section['sh_offset']), section.num_relocations()))
            if section.is_RELA():
                self._emitline(
                    "  Offset          Info           Type           Sym. Value    Sym. Name + Addend"
                )
            else:
                self._emitline(
                    " Offset     Info    Type            Sym.Value  Sym. Name")

            # The symbol table section pointed to in sh_link
            symtable = self.elffile.get_section(section['sh_link'])

            for rel in section.iter_relocations():
                hexwidth = 8 if self.elffile.elfclass == 32 else 12
                self._emit(
                    '%s  %s %-17.17s' %
                    (self._format_hex(
                        rel['r_offset'], fieldsize=hexwidth, lead0x=False),
                     self._format_hex(
                         rel['r_info'], fieldsize=hexwidth, lead0x=False),
                     describe_reloc_type(rel['r_info_type'], self.elffile)))

                if rel['r_info_sym'] == 0:
                    self._emitline()
                    continue

                symbol = symtable.get_symbol(rel['r_info_sym'])
                # Some symbols have zero 'st_name', so instead what's used is
                # the name of the section they point at
                if symbol['st_name'] == 0:
                    symsec = self.elffile.get_section(symbol['st_shndx'])
                    symbol_name = symsec.name
                else:
                    symbol_name = symbol.name
                self._emit(' %s %s%22.22s' % (self._format_hex(
                    symbol['st_value'], fullhex=True,
                    lead0x=False), '  ' if self.elffile.elfclass == 32 else '',
                                              bytes2str(symbol_name)))
                if section.is_RELA():
                    self._emit(' %s %x' %
                               ('+' if rel['r_addend'] >= 0 else '-',
                                abs(rel['r_addend'])))
                self._emitline()

        if not has_relocation_sections:
            self._emitline('\nThere are no relocations in this file.')

    def display_hex_dump(self, section_spec):
        """ Display a hex dump of a section. section_spec is either a section
            number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" %
                           (section_spec))
            return

        self._emitline("\nHex dump of section '%s':" % bytes2str(section.name))
        self._note_relocs_for_section(section)
        addr = section['sh_addr']
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            bytesleft = len(data) - dataptr
            # chunks of 16 bytes per line
            linebytes = 16 if bytesleft > 16 else bytesleft

            self._emit('  %s ' % self._format_hex(addr, fieldsize=8))
            for i in range(16):
                if i < linebytes:
                    self._emit('%2.2x' % byte2int(data[dataptr + i]))
                else:
                    self._emit('  ')
                if i % 4 == 3:
                    self._emit(' ')

            for i in range(linebytes):
                c = data[dataptr + i:dataptr + i + 1]
                if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
                    self._emit(bytes2str(c))
                else:
                    self._emit(bytes2str(b'.'))

            self._emitline()
            addr += linebytes
            dataptr += linebytes

        self._emitline()

    def display_string_dump(self, section_spec):
        """ Display a strings dump of a section. section_spec is either a
            section number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" %
                           (section_spec))
            return

        self._emitline("\nString dump of section '%s':" %
                       bytes2str(section.name))

        found = False
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            while (dataptr < len(data)
                   and not (32 <= byte2int(data[dataptr]) <= 127)):
                dataptr += 1

            if dataptr >= len(data):
                break

            endptr = dataptr
            while endptr < len(data) and byte2int(data[endptr]) != 0:
                endptr += 1

            found = True
            self._emitline('  [%6x]  %s' %
                           (dataptr, bytes2str(data[dataptr:endptr])))

            dataptr = endptr

        if not found:
            self._emitline('  No strings found in this section.')
        else:
            self._emitline()

    def display_debug_dump(self, dump_what):
        """ Dump a DWARF section
        """
        self._init_dwarfinfo()
        if self._dwarfinfo is None:
            return

        set_global_machine_arch(self.elffile.get_machine_arch())

        if dump_what == 'info':
            self._dump_debug_info()
        elif dump_what == 'decodedline':
            self._dump_debug_line_programs()
        elif dump_what == 'frames':
            self._dump_debug_frames()
        elif dump_what == 'frames-interp':
            self._dump_debug_frames_interp()
        else:
            self._emitline('debug dump not yet supported for "%s"' % dump_what)

    def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True):
        """ Format an address into a hexadecimal string.

            fieldsize:
                Size of the hexadecimal field (with leading zeros to fit the
                address into. For example with fieldsize=8, the format will
                be %08x
                If None, the minimal required field size will be used.

            fullhex:
                If True, override fieldsize to set it to the maximal size
                needed for the elfclass

            lead0x:
                If True, leading 0x is added
        """
        s = '0x' if lead0x else ''
        if fullhex:
            fieldsize = 8 if self.elffile.elfclass == 32 else 16
        if fieldsize is None:
            field = '%x'
        else:
            field = '%' + '0%sx' % fieldsize
        return s + field % addr

    def _section_from_spec(self, spec):
        """ Retrieve a section given a "spec" (either number or name).
            Return None if no such section exists in the file.
        """
        try:
            num = int(spec)
            if num < self.elffile.num_sections():
                return self.elffile.get_section(num)
            else:
                return None
        except ValueError:
            # Not a number. Must be a name then
            return self.elffile.get_section_by_name(str2bytes(spec))

    def _note_relocs_for_section(self, section):
        """ If there are relocation sections pointing to the givne section,
            emit a note about it.
        """
        for relsec in self.elffile.iter_sections():
            if isinstance(relsec, RelocationSection):
                info_idx = relsec['sh_info']
                if self.elffile.get_section(info_idx) == section:
                    self._emitline(
                        '  Note: This section has relocations against it, but these have NOT been applied to this dump.'
                    )
                    return

    def _init_dwarfinfo(self):
        """ Initialize the DWARF info contained in the file and assign it to
            self._dwarfinfo.
            Leave self._dwarfinfo at None if no DWARF info was found in the file
        """
        if self._dwarfinfo is not None:
            return

        if self.elffile.has_dwarf_info():
            self._dwarfinfo = self.elffile.get_dwarf_info()
        else:
            self._dwarfinfo = None

    def _dump_debug_info(self):
        """ Dump the debugging info section.
        """
        self._emitline('Contents of the .debug_info section:\n')

        # Offset of the .debug_info section in the stream
        section_offset = self._dwarfinfo.debug_info_sec.global_offset

        for cu in self._dwarfinfo.iter_CUs():
            self._emitline('  Compilation Unit @ offset %s:' %
                           self._format_hex(cu.cu_offset))
            self._emitline('   Length:        %s (%s)' % (self._format_hex(
                cu['unit_length']), '%s-bit' % cu.dwarf_format()))
            self._emitline('   Version:       %s' % cu['version']),
            self._emitline('   Abbrev Offset: %s' % cu['debug_abbrev_offset']),
            self._emitline('   Pointer Size:  %s' % cu['address_size'])

            # The nesting depth of each DIE within the tree of DIEs must be
            # displayed. To implement this, a counter is incremented each time
            # the current DIE has children, and decremented when a null die is
            # encountered. Due to the way the DIE tree is serialized, this will
            # correctly reflect the nesting depth
            #
            die_depth = 0
            for die in cu.iter_DIEs():
                if die.is_null():
                    die_depth -= 1
                    continue
                self._emitline(
                    ' <%s><%x>: Abbrev Number: %s (%s)' %
                    (die_depth, die.offset, die.abbrev_code, die.tag))

                for attr in itervalues(die.attributes):
                    name = attr.name
                    # Unknown attribute values are passed-through as integers
                    if isinstance(name, int):
                        name = 'Unknown AT value: %x' % name
                    self._emitline(
                        '    <%2x>   %-18s: %s' %
                        (attr.offset, name,
                         describe_attr_value(attr, die, section_offset)))

                if die.has_children:
                    die_depth += 1

        self._emitline()

    def _dump_debug_line_programs(self):
        """ Dump the (decoded) line programs from .debug_line
            The programs are dumped in the order of the CUs they belong to.
        """
        self._emitline(
            'Decoded dump of debug contents of section .debug_line:\n')

        for cu in self._dwarfinfo.iter_CUs():
            lineprogram = self._dwarfinfo.line_program_for_CU(cu)

            cu_filename = ''
            if len(lineprogram['include_directory']) > 0:
                cu_filename = '%s/%s' % (
                    bytes2str(lineprogram['include_directory'][0]),
                    bytes2str(lineprogram['file_entry'][0].name))
            else:
                cu_filename = bytes2str(lineprogram['file_entry'][0].name)

            self._emitline('CU: %s:' % cu_filename)
            self._emitline(
                'File name                            Line number    Starting address'
            )

            # Print each state's file, line and address information. For some
            # instructions other output is needed to be compatible with
            # readelf.
            for entry in lineprogram.get_entries():
                state = entry.state
                if state is None:
                    # Special handling for commands that don't set a new state
                    if entry.command == DW_LNS_set_file:
                        file_entry = lineprogram['file_entry'][entry.args[0] -
                                                               1]
                        if file_entry.dir_index == 0:
                            # current directory
                            self._emitline('\n./%s:[++]' %
                                           (bytes2str(file_entry.name)))
                        else:
                            self._emitline(
                                '\n%s/%s:' %
                                (bytes2str(lineprogram['include_directory'][
                                    file_entry.dir_index - 1]),
                                 bytes2str(file_entry.name)))
                    elif entry.command == DW_LNE_define_file:
                        self._emitline(
                            '%s:' % (bytes2str(lineprogram['include_directory']
                                               [entry.args[0].dir_index])))
                elif not state.end_sequence:
                    # readelf doesn't print the state after end_sequence
                    # instructions. I think it's a bug but to be compatible
                    # I don't print them too.
                    self._emitline(
                        '%-35s  %11d  %18s' %
                        (bytes2str(
                            lineprogram['file_entry'][state.file - 1].name),
                         state.line, '0' if state.address == 0 else
                         self._format_hex(state.address)))
                if entry.command == DW_LNS_copy:
                    # Another readelf oddity...
                    self._emitline()

    def _dump_debug_frames(self):
        """ Dump the raw frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return
        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline(
                    '\n%08x %08x %08x CIE' %
                    (entry.offset, entry['length'], entry['CIE_id']))
                self._emitline('  Version:               %d' %
                               entry['version'])
                self._emitline('  Augmentation:          "%s"' %
                               bytes2str(entry['augmentation']))
                self._emitline('  Code alignment factor: %u' %
                               entry['code_alignment_factor'])
                self._emitline('  Data alignment factor: %d' %
                               entry['data_alignment_factor'])
                self._emitline('  Return address column: %d' %
                               entry['return_address_register'])
                self._emitline()
            else:  # FDE
                self._emitline(
                    '\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' %
                    (entry.offset, entry['length'], entry['CIE_pointer'],
                     entry.cie.offset, entry['initial_location'],
                     entry['initial_location'] + entry['address_range']))

            self._emit(describe_CFI_instructions(entry))
        self._emitline()

    def _dump_debug_frames_interp(self):
        """ Dump the interpreted (decoded) frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return

        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' %
                               (entry.offset, entry['length'], entry['CIE_id'],
                                bytes2str(entry['augmentation']),
                                entry['code_alignment_factor'],
                                entry['data_alignment_factor'],
                                entry['return_address_register']))
                ra_regnum = entry['return_address_register']
            else:  # FDE
                self._emitline(
                    '\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' %
                    (entry.offset, entry['length'], entry['CIE_pointer'],
                     entry.cie.offset, entry['initial_location'],
                     entry['initial_location'] + entry['address_range']))
                ra_regnum = entry.cie['return_address_register']

            # Print the heading row for the decoded table
            self._emit('   LOC')
            self._emit('  ' if entry.structs.address_size ==
                       4 else '          ')
            self._emit(' CFA      ')

            # Decode the table nad look at the registers it describes.
            # We build reg_order here to match readelf's order. In particular,
            # registers are sorted by their number, and the register matching
            # ra_regnum is always listed last with a special heading.
            decoded_table = entry.get_decoded()
            reg_order = sorted(
                ifilter(lambda r: r != ra_regnum, decoded_table.reg_order))

            # Headings for the registers
            for regnum in reg_order:
                self._emit('%-6s' % describe_reg_name(regnum))
            self._emitline('ra      ')

            # Now include ra_regnum in reg_order to print its values similarly
            # to the other registers.
            reg_order.append(ra_regnum)
            for line in decoded_table.table:
                self._emit(
                    self._format_hex(line['pc'], fullhex=True, lead0x=False))
                self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa']))

                for regnum in reg_order:
                    if regnum in line:
                        s = describe_CFI_register_rule(line[regnum])
                    else:
                        s = 'u'
                    self._emit('%-6s' % s)
                self._emitline()
        self._emitline()

    def _emit(self, s=''):
        """ Emit an object to output
        """
        self.output.write(str(s))

    def _emitline(self, s=''):
        """ Emit an object to output, followed by a newline
        """
        self.output.write(str(s) + '\n')
Ejemplo n.º 37
0
class ELF(_header):
    _backend = None
    _elf = None
    _versioninfo = None

    def __init__(self, path, filetype, stream=None, backend=None):
        if ELFFile is None:
            raise INSTALLerror(
                "Install the ELFFile module to use the ELF backend!")
        super(ELF, self).__init__(path, filetype)
        self._backend = backend
        if stream is None:
            f = open(path, 'rb')
            self._elf = ELFFile(f)
        else:
            self._elf = ELFFile(stream)

        bindata = self._elf.stream.read()
        self.fileMd5 = hashlib.md5(bindata).hexdigest()
        self.fileSha1 = hashlib.sha1(bindata).hexdigest()
        self.fileSha256 = hashlib.sha256(bindata).hexdigest()
        self.fileSha512 = hashlib.sha512(bindata).hexdigest()
        del bindata

        self._elf.stream.seek(0)
        self.bin_data = self._elf.stream.read()
        self._elf.stream.seek(0)
        self.arch_str = self._elf.header.e_machine
        self._entry = self._elf.header.e_entry
        if (self._elf.little_endian):
            self.endness = "Iend_LE"
        else:
            self.endness = "Iend_BE"
        self.set_arch(ArchSelector().search(self.arch_str, self.endness))

    def read_addr(self, addr):
        return addr

    def get_elf_header(self):
        result = {}
        header = self._elf.header
        if (hasattr(header, 'e_ident')):
            e_ident = header['e_ident']
            EI_MAG = ''
            for MAG in e_ident['EI_MAG']:
                EI_MAG += str(hex(MAG)).replace("0x", '')
            result['EI_MAG'] = EI_MAG
            result['EI_CLASS'] = describe_ei_class(e_ident['EI_CLASS'])
            result['EI_DATA'] = describe_ei_data(e_ident['EI_DATA'])
            result['EI_VERSION'] = describe_ei_version(e_ident['EI_VERSION'])
            result['EI_OSABI'] = describe_ei_osabi(e_ident['EI_OSABI'])
        if (hasattr(header, 'e_type')):
            result['e_type'] = describe_e_type(header['e_type'])
        if (hasattr(header, 'e_machine')):
            result['e_machine'] = describe_e_machine(header['e_machine'])
        if (hasattr(header, 'e_version')):
            result['e_version'] = header['e_version']
        if (hasattr(header, 'e_entry')):
            result['e_entry'] = header['e_entry']
        if (hasattr(header, 'e_phoff')):
            result['e_phoff'] = header['e_phoff']
        if (hasattr(header, 'e_shoff')):
            result['e_shoff'] = header['e_shoff']
        if (hasattr(header, 'e_flags')):
            result['e_flags'] = header['e_flags']
        if (hasattr(header, 'e_ehsize')):
            result['e_ehsize'] = header['e_ehsize']
        if (hasattr(header, 'e_phentsize')):
            result['e_phentsize'] = header['e_phentsize']
        if (hasattr(header, 'e_phnum')):
            result['e_phnum'] = header['e_phnum']
        if (hasattr(header, 'e_shentsize')):
            result['e_shentsize'] = header['e_shentsize']
        if (hasattr(header, 'e_shnum')):
            result['e_shnum'] = header['e_shnum']
        if (hasattr(header, 'e_shstrndx')):
            result['e_shstrndx'] = header['e_shstrndx']
        return result

    def is_section(self, addr):

        for nsec, section in enumerate(self._elf.iter_sections()):
            if addr >= (section['sh_addr']) and addr <= (section['sh_addr'] +
                                                         section['sh_size']):
                section.Name = section.name
                return section

    def get_sections(self):
        sections = []
        for nsec, section in enumerate(self._elf.iter_sections()):
            result = {}
            result['nsec'] = nsec
            result['name'] = section.name
            result['sh_type'] = describe_sh_type(section['sh_type'])
            if self._elf.elfclass == 32:
                result['sh_addr'] = section['sh_addr']
                result['shoffset'] = section['sh_offset']
                result['sh_size'] = section['sh_size']
                result['sh_entsize'] = section['sh_entsize']
                result['sh_flags'] = describe_sh_flags(section['sh_flags'])
                result['sh_link'] = section['sh_link']
                result['sh_info'] = section['sh_info']
                result['sh_addralign'] = section['sh_addralign']
            else:  # 64
                result['sh_addr'] = section['sh_addr']
                result['sh_offset'] = section['sh_offset']
                result['sh_size'] = section['sh_size']
                result['sh_entsize'] = section['sh_entsize']
                result['sh_flags'] = describe_sh_flags(section['sh_flags'])
                result['sh_link'] = section['sh_link'], section['sh_info']
                result['sh_addralign'] = section['sh_addralign']

            # Dynamic Section
            if isinstance(section, DynamicSection):
                result['special_type'] = 'dynamic'
                result['dynamic'] = []
                has_dynamic_sections = True
                for tag in section.iter_tags():
                    dynamic = {}
                    if tag.entry.d_tag == 'DT_NEEDED':
                        parsed = 'Shared library: [%s]' % tag.needed
                    elif tag.entry.d_tag == 'DT_RPATH':
                        parsed = 'Library rpath: [%s]' % tag.rpath
                    elif tag.entry.d_tag == 'DT_RUNPATH':
                        parsed = 'Library runpath: [%s]' % tag.runpath
                    elif tag.entry.d_tag == 'DT_SONAME':
                        parsed = 'Library soname: [%s]' % tag.soname
                    elif tag.entry.d_tag.endswith(('SZ', 'ENT')):
                        parsed = '%i (bytes)' % tag['d_val']
                    elif tag.entry.d_tag.endswith(('NUM', 'COUNT')):
                        parsed = '%i' % tag['d_val']
                    elif tag.entry.d_tag == 'DT_PLTREL':
                        s = describe_dyn_tag(tag.entry.d_val)
                        if s.startswith('DT_'):
                            s = s[3:]
                        parsed = '%s' % s
                    else:
                        parsed = '%#x' % tag['d_val']
                        dynamic['tag'] = ENUM_D_TAG.get(
                            tag.entry.d_tag, tag.entry.d_tag)
                        dynamic['tag_type'] = tag.entry.d_tag[3:]
                        dynamic['tag_value'] = parsed
                    result['dynamic'].append(dynamic)

            #Relocation Section
            if isinstance(section, RelocationSection):
                result['special_type'] = 'relocation'
                result['relocation'] = []
                has_relocation_sections = True
                # The symbol table section pointed to in sh_link
                symtable = self._elf.get_section(section['sh_link'])

                for rel in section.iter_relocations():
                    relocation = {}
                    relocation['r_offset'] = rel['r_offset']
                    relocation['r_info'] = rel['r_info']
                    relocation['r_info_type'] = describe_reloc_type(
                        rel['r_info_type'], self._elf)

                    if rel['r_info_sym'] == 0:
                        continue

                    symbol = symtable.get_symbol(rel['r_info_sym'])
                    # Some symbols have zero 'st_name', so instead what's used is
                    # the name of the section they point at
                    if symbol['st_name'] == 0:
                        symsec = self._elf.get_section(symbol['st_shndx'])
                        relocation['symbol_name'] = symbol_name = symsec.name
                    else:
                        symbol_name = symbol.name
                        relocation['st_value'] = symbol['st_value']
                        relocation['symbol_name'] = symbol_name
                    if section.is_RELA():
                        relocation['r_addend'] = rel['r_addend']
                    result['relocation'].append(relocation)

            #Symbol Section
            if isinstance(section, SymbolTableSection):
                self._init_versioninfo()

                if section['sh_entsize'] == 0:
                    continue
                result['special_type'] = 'symbol'
                result['symbol'] = []
                for nsym, symbol in enumerate(section.iter_symbols()):
                    sym_dic = {}
                    version_info = ''
                    # readelf doesn't display version info for Solaris versioning
                    if (section['sh_type'] == 'SHT_DYNSYM'
                            and self._versioninfo['type'] == 'GNU'):
                        version = self._symbol_version(nsym)
                        if (version['name'] != symbol.name and version['index']
                                not in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL')):
                            if version['filename']:
                                # external symbol
                                version_info = '@%(name)s (%(index)i)' % version
                            else:
                                # internal symbol
                                if version['hidden']:
                                    version_info = '@%(name)s' % version
                                else:
                                    version_info = '@@%(name)s' % version

                        # symbol names are truncated to 25 chars, similarly to readelf
                        sym_dic['nsym'] = nsym
                        sym_dic['st_value'] = symbol['st_value']
                        sym_dic['st_size'] = symbol['st_size']
                        sym_dic['st_type'] = describe_symbol_type(
                            symbol['st_info']['type'])
                        sym_dic['bind'] = describe_symbol_bind(
                            symbol['st_info']['bind'])
                        sym_dic['vis'] = describe_symbol_visibility(
                            symbol['st_other']['visibility'])
                        sym_dic['ndx'] = describe_symbol_shndx(
                            symbol['st_shndx'])
                        sym_dic['name'] = symbol.name
                        sym_dic['version'] = version_info
                        result['symbol'].append(sym_dic)
            sections.append(result)
        return sections

    def get_program_header(self):
        header = []
        if self._elf.num_segments() == 0:
            return []

        for segment in self._elf.iter_segments():
            result = {}
            result['p_type'] = describe_p_type(segment['p_type'])

            if self._elf.elfclass == 32:
                result['p_offset'] = segment['p_offset']
                result['p_vaddr'] = segment['p_vaddr']
                result['p_paddr'] = segment['p_paddr']
                result['p_filesz'] = segment['p_filesz']
                result['p_memsz'] = segment['p_memsz']
                result['p_flags'] = describe_p_flags(segment['p_flags'])
                result['p_align'] = segment['p_align']
            else:  # 64
                result['p_offset'] = segment['p_offset']
                result['p_vaddr'] = segment['p_vaddr']
                result['p_paddr'] = segment['p_paddr']
                result['p_filesz'] = segment['p_filesz']
                result['p_memsz'] = segment['p_memsz']
                result['p_flags'] = describe_p_flags(segment['p_flags'])
                result['p_align'] = segment['p_align']
            if isinstance(segment, InterpSegment):
                result['interp_name'] = segment.get_interp_name()
            result['include_section'] = []
            for section in self._elf.iter_sections():
                if (not section.is_null()
                        and segment.section_in_segment(section)):
                    result['include_section'].append(section.name)

            #NoteSegment
            if isinstance(segment, NoteSegment):
                result['special_type'] = 'note'
                result['note'] = []
                for note in segment.iter_notes():
                    note_dic = {}
                    note_dic['n_offset'] = note['n_offset']
                    note_dic['n_size'] = note['n_size']
                    note_dic['n_name'] = note['n_name']
                    note_dic['n_descsz'] = note['n_descsz']
                    note_dic['note'] = describe_note(note)
                    result['note'].append(note_dic)

            header.append(result)

        return header

    def _init_versioninfo(self):
        """ Search and initialize informations about version related sections
            and the kind of versioning used (GNU or Solaris).
        """
        if self._versioninfo is not None:
            return

        self._versioninfo = {
            'versym': None,
            'verdef': None,
            'verneed': None,
            'type': None
        }

        for section in self._elf.iter_sections():
            if isinstance(section, GNUVerSymSection):
                self._versioninfo['versym'] = section
            elif isinstance(section, GNUVerDefSection):
                self._versioninfo['verdef'] = section
            elif isinstance(section, GNUVerNeedSection):
                self._versioninfo['verneed'] = section
            elif isinstance(section, DynamicSection):
                for tag in section.iter_tags():
                    if tag['d_tag'] == 'DT_VERSYM':
                        self._versioninfo['type'] = 'GNU'
                        break

        if not self._versioninfo['type'] and (self._versioninfo['verneed']
                                              or self._versioninfo['verdef']):
            self._versioninfo['type'] = 'Solaris'

    def _symbol_version(self, nsym):
        """ Return a dict containing information on the
                   or None if no version information is available
        """
        self._init_versioninfo()

        symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))

        if (not self._versioninfo['versym']
                or nsym >= self._versioninfo['versym'].num_symbols()):
            return None

        symbol = self._versioninfo['versym'].get_symbol(nsym)
        index = symbol.entry['ndx']
        if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
            index = int(index)

            if self._versioninfo['type'] == 'GNU':
                # In GNU versioning mode, the highest bit is used to
                # store wether the symbol is hidden or not
                if index & 0x8000:
                    index &= ~0x8000
                    symbol_version['hidden'] = True

            if (self._versioninfo['verdef']
                    and index <= self._versioninfo['verdef'].num_versions()):
                _, verdaux_iter = \
                        self._versioninfo['verdef'].get_version(index)
                symbol_version['name'] = next(verdaux_iter).name
            else:
                verneed, vernaux = \
                        self._versioninfo['verneed'].get_version(index)
                symbol_version['name'] = vernaux.name
                symbol_version['filename'] = verneed.name

        symbol_version['index'] = index
        return symbol_version

    def Header(self):
        header = {}
        header['header'] = self.get_elf_header()
        header['sections'] = self.get_sections()
        header['program'] = self.get_program_header()
        return header
Ejemplo n.º 38
0
def process_file(filename):
    print('Processing file:', filename)
    with open(filename, 'rb') as f:
        elffile = ELFFile(f)

        # The provided linkerscript sorts the sections in the appropriate order
        assert (elffile.num_segments() == 1)
        segment = elffile.get_segment(0)

        symtab = elffile.get_section_by_name('.symtab')

        # A table of exported callbacks where each entry is made of two u32
        # values, the first is the event ID and the second is the address of the
        # callback function
        exports_sym = symtab.get_symbol_by_name('Exports')[0]

        exports_addr = exports_sym['st_value']
        exports_size = exports_sym['st_size']
        assert ((exports_addr & 3) == 0)
        assert ((exports_size & 7) == 0)

        # The text and data sections are merged together, the bss is allocated
        # and zeroed by the loader. The 4 takes into account the extra pointer
        # to the application class that's inserted by the loader.
        code_size = segment['p_filesz'] - 4
        bss_size = segment['p_memsz'] - code_size

        # Address of the funalizer function
        finish_sym = symtab.get_symbol_by_name('_finish')[0]
        finish_addr = finish_sym['st_value']

        asset0 = struct.pack(
            '<16I',
            0x1000,  # Fixed?
            1,  # Version
            code_size,  # Code + Data size
            0,  # Same but for libs
            4,  # Space for the Klass pointer
            bss_size,  # Size of the zeroed area
            code_size,  # Offset for the Klass pointer
            exports_addr,  # Offset for the exports array
            0xffffffff,  # Same but for libs
            0xffffffff,  # Init array start
            0xffffffff,  # Fini array start
            0,  # Init array len (in words)
            0,  # Fini array len (in words)
            elffile['e_entry'],  # Entry point
            finish_addr,  # Exit point
            0x2B0B3ED5)  # Used to check if the .sig file is valid

        # Make it writable, we may perform some modification later
        asset1 = bytearray(segment.data())

        reloc_offsets = []
        reloc_sections = [
            sect for sect in elffile.iter_sections()
            if isinstance(sect, RelocationSection)
        ]

        # The only relocation performed by the loader is adding the address
        # where the app is loaded to a whole u32
        for rel_section in reloc_sections:
            for reloc in rel_section.iter_relocations():
                ty = reloc['r_info_type']
                # Make sure it's not STN_UNDEF
                assert (reloc['r_info_sym'] != 0)

                if ty == ENUM_RELOC_TYPE_ARM['R_ARM_ABS32']:
                    reloc_offsets.append(reloc['r_offset'])
                elif ty == ENUM_RELOC_TYPE_ARM['R_ARM_REL32'] or \
                        ty == ENUM_RELOC_TYPE_ARM['R_ARM_CALL']:
                    # Already PC-independent
                    pass
                else:
                    raise RuntimeError('Unknown relocation type!')

        # Terminates the list
        reloc_offsets.append(0xffffffff)

        asset2 = struct.pack('<IIII', 1,
                             len(reloc_offsets) - 1,
                             16 + len(reloc_offsets) * 4, 0xf0123456)
        for off in reloc_offsets:
            asset2 += struct.pack('<I', off)

        # The key file is simply chopped, shuffled and hashed with SHA1 together
        # with the "magic" word at 0x3c in the asset0.
        with open(filename + '.sig', 'w+b') as out:
            header = struct.pack(
                '<II',
                0x1000,  # Fixed ?
                8)  # Key slot
            out.write(header)
            out.write(
                b'\x63\x9a\x83\xa1\x99\x4b\xfb\x42\x56\xd4\x5e\x44\x2a\x90\x2a\x35'
            )
            out.write(
                b'\xf5\xd5\xdc\xef\xb9\x80\x90\x3d\x80\x33\x94\x5f\x54\x6c\xcc\x9a'
            )
            out.write(
                b'\xe8\xa7\xad\xf7\xe0\x22\x3d\x1e\x59\x30\x38\x3a\x4d\x30\x39\x3a'
            )
            out.write(
                b'\x44\x30\x31\x3a\x54\x30\x39\x3a\x30\x30\xe3\xd1\xa5\xca\xbc\x94'
            )
            out.write(
                b'\x75\xd1\x0c\x79\x70\x03\x1b\x10\x33\x50\x31\xad\x79\xbc')

        with open(filename + '.aab', 'w+b') as out:
            out.write(b'ABHS')
            header = struct.pack(
                '<3I',
                0x1000,  # Fixed ?
                0x10,  # Application type
                3)  # Number of assets
            out.write(header)

            off = 16 + 3 * 12
            for i, asset in enumerate([asset0, asset1, asset2]):
                entry = struct.pack('<3I', i, len(asset), off)
                off += len(asset)
                out.write(entry)

            out.write(asset0)
            out.write(asset1)
            out.write(asset2)
Ejemplo n.º 39
0
Archivo: utils.py Proyecto: zha0/qiling
class QlReadELF(object):
    def __init__(self, ql: Qiling, elf_stream):
        self.ql = ql
        self.elffile = ELFFile(elf_stream)
        self._versioninfo = None

    def elf_file_header(self):
        elf_header = {}

        def add_info(key, value):
            elf_header[key] = value

        header = self.elffile.header
        e_ident = header['e_ident']

        add_info(
            'Magic',
            ' '.join('%2.2x' % byte2int(b) for b in self.elffile.e_ident_raw))
        add_info('Class', describe_ei_class(e_ident['EI_CLASS']))
        add_info('Data', describe_ei_data(e_ident['EI_DATA']))
        add_info('Version', e_ident['EI_VERSION'])
        add_info('OS/ABI', describe_ei_osabi(e_ident['EI_OSABI']))
        add_info('ABI Version', e_ident['EI_ABIVERSION'])
        add_info('Type', describe_e_type(header['e_type']))
        add_info('Machine', describe_e_machine(header['e_machine']))
        add_info('Version_e', describe_e_version_numeric(header['e_version']))
        add_info('Entry point address', self._format_hex(header['e_entry']))
        add_info('Start of program headers', header['e_phoff'])
        add_info('Start of section headers', header['e_shoff'])
        add_info('Flags', [
            self._format_hex(header['e_flags']),
            self.decode_flags(header['e_flags'])
        ])
        add_info('Size of this header', header['e_ehsize'])
        add_info('Size of program headers', header['e_phentsize'])
        add_info('Number of program headers', header['e_phnum'])
        add_info('Size of section headers', header['e_shentsize'])
        add_info('Number of section headers', header['e_shnum'])
        add_info('Section header string table index', header['e_shstrndx'])

        return elf_header

    def elf_program_headers(self):
        program_headers = []

        def add_info(dic):
            program_headers.append(dic)

        if self.elffile.num_segments() == 0:
            return None

        for segment in self.elffile.iter_segments():
            program_hdr = {}
            program_hdr['Type'] = describe_p_type(segment['p_type'])
            program_hdr['Offset'] = self._format_hex(segment['p_offset'],
                                                     fieldsize=6)
            program_hdr['VirtAddr'] = self._format_hex(segment['p_vaddr'],
                                                       fullhex=True)
            program_hdr['PhysAddr'] = self._format_hex(segment['p_paddr'],
                                                       fullhex=True)
            program_hdr['FileSiz'] = self._format_hex(segment['p_filesz'],
                                                      fieldsize=5)
            program_hdr['MemSiz'] = self._format_hex(segment['p_memsz'],
                                                     fieldsize=5)
            program_hdr['Flg'] = describe_p_flags(segment['p_flags'])
            program_hdr['Align'] = self._format_hex(segment['p_align'])

            add_info(program_hdr)

        return program_headers

    def elf_section_headers(self):
        section_headers = []

        def add_info(dic):
            section_headers.append(dic)

        if self.elffile.num_sections() == 0:
            return None

        for nsec, section in enumerate(self.elffile.iter_sections()):
            section_hdr = {}
            section_hdr['index'] = nsec
            section_hdr['Name'] = section.name
            section_hdr['Type'] = describe_sh_type(section['sh_type'])
            section_hdr['Addr'] = self._format_hex(section['sh_addr'],
                                                   fieldsize=8,
                                                   lead0x=False)
            section_hdr['Offset'] = self._format_hex(section['sh_offset'],
                                                     fieldsize=6,
                                                     lead0x=False)
            section_hdr['Size'] = self._format_hex(section['sh_size'],
                                                   fieldsize=6,
                                                   lead0x=False)
            section_hdr['ES'] = self._format_hex(section['sh_entsize'],
                                                 fieldsize=2,
                                                 lead0x=False)
            section_hdr['Flag'] = describe_sh_flags(section['sh_flags'])
            section_hdr['Lk'] = section['sh_link']
            section_hdr['Inf'] = section['sh_info']
            section_hdr['Al'] = section['sh_addralign']

            add_info(section_hdr)

        return section_headers

    def elf_symbol_tables(self):
        symbol_tables_list = []

        def add_info(dic):
            symbol_tables_list.append(dic)

        self._init_versioninfo()

        symbol_tables = [
            s for s in self.elffile.iter_sections()
            if isinstance(s, SymbolTableSection)
        ]

        if not symbol_tables and self.elffile.num_sections() == 0:
            return None

        for section in symbol_tables:
            if not isinstance(section, SymbolTableSection):
                continue

            if section['sh_entsize'] == 0:
                continue

            for nsym, symbol in enumerate(section.iter_symbols()):
                version_info = ''
                if (section['sh_type'] == 'SHT_DYNSYM'
                        and self._versioninfo['type'] == 'GNU'):
                    version = self._symbol_version(nsym)
                    if (version['name'] != symbol.name and version['index']
                            not in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL')):
                        if version['filename']:
                            # external symbol
                            version_info = '@%(name)s (%(index)i)' % version
                        else:
                            # internal symbol
                            if version['hidden']:
                                version_info = '@%(name)s' % version
                            else:
                                version_info = '@@%(name)s' % version

                symbol_info = {}
                symbol_info['index'] = nsym
                symbol_info['Value'] = self._format_hex(symbol['st_value'],
                                                        fullhex=True,
                                                        lead0x=False)
                symbol_info['Size'] = symbol['st_size']
                symbol_info['Type'] = describe_symbol_type(
                    symbol['st_info']['type'])
                symbol_info['Bind'] = describe_symbol_bind(
                    symbol['st_info']['bind'])
                symbol_info['Vis'] = describe_symbol_visibility(
                    symbol['st_other']['visibility'])
                symbol_info['Ndx'] = describe_symbol_shndx(symbol['st_shndx'])
                symbol_info['Name'] = symbol.name
                symbol_info['version_info'] = version_info
                add_info(symbol_info)
        return symbol_tables_list

    def decode_flags(self, flags):
        description = ""
        if self.elffile['e_machine'] == "EM_ARM":
            eabi = flags & E_FLAGS.EF_ARM_EABIMASK
            flags &= ~E_FLAGS.EF_ARM_EABIMASK

            if flags & E_FLAGS.EF_ARM_RELEXEC:
                description += ', relocatable executabl'
                flags &= ~E_FLAGS.EF_ARM_RELEXEC

            if eabi == E_FLAGS.EF_ARM_EABI_VER5:
                EF_ARM_KNOWN_FLAGS = E_FLAGS.EF_ARM_ABI_FLOAT_SOFT | E_FLAGS.EF_ARM_ABI_FLOAT_HARD | E_FLAGS.EF_ARM_LE8 | E_FLAGS.EF_ARM_BE8
                description += ', Version5 EABI'
                if flags & E_FLAGS.EF_ARM_ABI_FLOAT_SOFT:
                    description += ", soft-float ABI"
                elif flags & E_FLAGS.EF_ARM_ABI_FLOAT_HARD:
                    description += ", hard-float ABI"

                if flags & E_FLAGS.EF_ARM_BE8:
                    description += ", BE8"
                elif flags & E_FLAGS.EF_ARM_LE8:
                    description += ", LE8"

                if flags & ~EF_ARM_KNOWN_FLAGS:
                    description += ', <unknown>'
            else:
                description += ', <unrecognized EABI>'

        elif self.elffile['e_machine'] == "EM_MIPS":
            if flags & E_FLAGS.EF_MIPS_NOREORDER:
                description += ", noreorder"
            if flags & E_FLAGS.EF_MIPS_PIC:
                description += ", pic"
            if flags & E_FLAGS.EF_MIPS_CPIC:
                description += ", cpic"
            if (flags & E_FLAGS.EF_MIPS_ABI2):
                description += ", abi2"
            if (flags & E_FLAGS.EF_MIPS_32BITMODE):
                description += ", 32bitmode"
            if (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O32):
                description += ", o32"
            elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_O64):
                description += ", o64"
            elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI32):
                description += ", eabi32"
            elif (flags & E_FLAGS_MASKS.EFM_MIPS_ABI_EABI64):
                description += ", eabi64"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_1:
                description += ", mips1"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_2:
                description += ", mips2"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_3:
                description += ", mips3"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_4:
                description += ", mips4"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_5:
                description += ", mips5"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32R2:
                description += ", mips32r2"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64R2:
                description += ", mips64r2"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_32:
                description += ", mips32"
            if (flags & E_FLAGS.EF_MIPS_ARCH) == E_FLAGS.EF_MIPS_ARCH_64:
                description += ", mips64"

        return description

    def _format_hex(self,
                    addr,
                    fieldsize=None,
                    fullhex=False,
                    lead0x=True,
                    alternate=False):
        """ Format an address into a hexadecimal string.

            fieldsize:
                Size of the hexadecimal field (with leading zeros to fit the
                address into. For example with fieldsize=8, the format will
                be %08x
                If None, the minimal required field size will be used.

            fullhex:
                If True, override fieldsize to set it to the maximal size
                needed for the elfclass

            lead0x:
                If True, leading 0x is added

            alternate:
                If True, override lead0x to emulate the alternate
                hexadecimal form specified in format string with the #
                character: only non-zero values are prefixed with 0x.
                This form is used by readelf.
        """
        if alternate:
            if addr == 0:
                lead0x = False
            else:
                lead0x = True
                fieldsize -= 2

        s = '0x' if lead0x else ''
        if fullhex:
            fieldsize = 8 if self.elffile.elfclass == 32 else 16
        if fieldsize is None:
            field = '%x'
        else:
            field = '%' + '0%sx' % fieldsize
        return s + field % addr

    def _init_versioninfo(self):
        """ Search and initialize informations about version related sections
            and the kind of versioning used (GNU or Solaris).
        """
        if self._versioninfo is not None:
            return

        self._versioninfo = {
            'versym': None,
            'verdef': None,
            'verneed': None,
            'type': None
        }

        for section in self.elffile.iter_sections():
            if isinstance(section, GNUVerSymSection):
                self._versioninfo['versym'] = section
            elif isinstance(section, GNUVerDefSection):
                self._versioninfo['verdef'] = section
            elif isinstance(section, GNUVerNeedSection):
                self._versioninfo['verneed'] = section
            elif isinstance(section, DynamicSection):
                for tag in section.iter_tags():
                    if tag['d_tag'] == 'DT_VERSYM':
                        self._versioninfo['type'] = 'GNU'
                        break

        if not self._versioninfo['type'] and (self._versioninfo['verneed']
                                              or self._versioninfo['verdef']):
            self._versioninfo['type'] = 'Solaris'

    def _symbol_version(self, nsym):
        """ Return a dict containing information on the
                   or None if no version information is available
        """
        self._init_versioninfo()

        symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))

        if (not self._versioninfo['versym']
                or nsym >= self._versioninfo['versym'].num_symbols()):
            return None

        symbol = self._versioninfo['versym'].get_symbol(nsym)
        index = symbol.entry['ndx']
        if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
            index = int(index)

            if self._versioninfo['type'] == 'GNU':
                # In GNU versioning mode, the highest bit is used to
                # store wether the symbol is hidden or not
                if index & 0x8000:
                    index &= ~0x8000
                    symbol_version['hidden'] = True

            if (self._versioninfo['verdef']
                    and index <= self._versioninfo['verdef'].num_versions()):
                _, verdaux_iter = \
                        self._versioninfo['verdef'].get_version(index)
                symbol_version['name'] = next(verdaux_iter).name
            else:
                verneed, vernaux = \
                        self._versioninfo['verneed'].get_version(index)
                symbol_version['name'] = vernaux.name
                symbol_version['filename'] = verneed.name

        symbol_version['index'] = index
        return symbol_version
Ejemplo n.º 40
0
class ELFImage(object):
    """
    Program image in the emulator
    """

    def __init__(self, image_filename, symbols_only=False):
        """
        Read the ELF headers and parse the executable
        """
        self._name = os.path.basename(image_filename)

        self._name_cache = dict()       # names are unique, entries are subdicts with 'address' and 'size'
        self._address_cache = dict()    # addresses are not unique, entries are lists of names at that address
        self._symbol_index = None       # sorted list of unique symbol addresses + sentinel

        self._symbols_only = symbols_only
        self._relocation = 0

        self._elf = ELFFile(open(image_filename, "rb"))
        if self._elf.header['e_type'] != 'ET_EXEC':
            raise RuntimeError('not an ELF executable file')
        if self._elf.header['e_machine'] != 'EM_68K':
            raise RuntimeError('not an M68K ELF file')
        if self._elf.num_segments() == 0:
            raise RuntimeError('no segments in ELF file')

        # build the symbol cache
        for section in self._elf.iter_sections():
            if isinstance(section, SymbolTableSection):
                self._cache_symbols(section)

        # look for a stack
        stack_size = None
        if not self._symbols_only:
            for segment in self._elf.iter_segments():
                if segment['p_type'] == 'PT_GNU_STACK':
                    stack_size = segment['p_memsz']
            if stack_size is None:
                raise RuntimeError(f'no stack defined in {self._name} - did you forget to link with -z stack-size=VALUE?')
            else:
                try:
                    stack_base = self._name_cache['_end']['address']
                except KeyError:
                    raise RuntimeError('no _end symbol, cannot locate stack')
                self._add_symbol('__STACK__', stack_base, stack_size)

        # sort symbol addresses to make index
        self._symbol_index = sorted(self._address_cache.keys())
        if len(self._symbol_index) == 0:
            raise RuntimeError(f'no symbols in {image_filename}')

    def _add_symbol(self, name, address, size):
        self._name_cache[name] = {'address': address, 'size': size}
        try:
            self._address_cache[address].append(name)
        except KeyError:
            self._address_cache[address] = [name]

    def _get_loadable_sections(self):
        if self._symbols_only:
            raise RuntimeError(f'loaded for symbols-only')
        loadable_sections = dict()
        for section in self._elf.iter_sections():
            if section['sh_flags'] & SH_FLAGS.SHF_ALLOC:
                loadable_sections[section['sh_addr']] = bytearray(section.data())
        return loadable_sections

    def relocate(self, relocation):
        # find sections that we want to load
        loadable_sections = self._get_loadable_sections()

        # iterate relocation sections
        did_relocate = False
        for section in self._elf.iter_sections():
            if not isinstance(section, RelocationSection):
                continue

            # do these relocates affect a loaded section?
            reloc_section = self._elf.get_section(section['sh_info'])
            if not (reloc_section['sh_flags'] & SH_FLAGS.SHF_ALLOC):
                continue

            # iterate relocations
            for reloc in section.iter_relocations():
                if not reloc.is_RELA():
                    raise RuntimeError('unexpected REL reloc')

                # Only R_68K_32 relocations are of interest
                if reloc['r_info_type'] != R_68K_32:
                    continue

                # find the section containing the address that needs to be fixed up
                reloc_address = reloc['r_offset']
                for sec_base, sec_data in loadable_sections.items():
                    sec_offset = reloc_address - sec_base
                    if (sec_base <= reloc_address) and (sec_offset < len(sec_data)):
                        unrelocated_value = struct.unpack_from('>L', sec_data, sec_offset)[0]
                        struct.pack_into('>L', sec_data, sec_offset, unrelocated_value + relocation)
                        did_relocate = True
                        break

        if not did_relocate:
            raise RuntimeError(f'no relocations in {self._name} - did you forget to link with --emit-relocs?')

        relocated_sections = dict()
        for sec_base, sec_data in loadable_sections.items():
            relocated_sections[sec_base + relocation] = sec_data

        self._relocation = relocation
        return relocated_sections

    @property
    def entrypoint(self):
        if self._symbols_only:
            raise RuntimeError(f'loaded for symbols-only')
        return self._elf.header['e_entry'] + self._relocation

    @property
    def initstack(self):
        if self._symbols_only:
            raise RuntimeError(f'loaded for symbols-only')
        if self._stack_size is not None:
            end, _ = self.get_symbol_range('_end')
            return end + self._relocation + self._stack_size

    def _cache_symbols(self, section):

        for nsym, symbol in enumerate(section.iter_symbols()):

            s_name = str(symbol.name)
            if len(s_name) == 0:
                continue
            s_type = symbol['st_info']['type']
            if s_type == 'STT_FILE':
                continue
            s_addr = symbol['st_value']
            s_size = symbol['st_size']

            self._name_cache[s_name] = {'address': s_addr, 'size': s_size}
            try:
                self._address_cache[s_addr].append(s_name)
            except KeyError:
                self._address_cache[s_addr] = [s_name]

    def get_symbol_range(self, name):
        try:
            addr = self._name_cache[name]['address'] + self._relocation
            size = self._name_cache[name]['size']
        except KeyError:
            try:
                addr = int(name)
                size = 1
            except Exception:
                raise RuntimeError(f'no symbol {name}'.format(name))

        return addr, size + addr

    def get_symbol_name(self, address):
        address -= self._relocation
        if address in self._address_cache:
            return ','.join(self._address_cache[address])
        index = bisect_left(self._symbol_index, address)
        if not index:
            return None
        symbol_address = self._symbol_index[index - 1]
        if address < symbol_address:
            return None
        names = list()
        for name in self._address_cache[symbol_address]:
            if (address - symbol_address) < self._name_cache[name]['size']:
                names.append(name)
        if len(names) > 0:
            label = ','.join(names) + f'+{address - symbol_address:#x}'
            return label
        return None
Ejemplo n.º 41
0
class Elf(object):
    def __init__(self, fileobj):
        self.elffile = ELFFile(fileobj)
        self.output = sys.stdout

    # our code starts here :-)

    def network(self):
        ret = "None"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize "
                      "of zero!" % (bytes2str(section.name)),
                      file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                # first match IP_PATTERNS
                for pattern in IP_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        return "network-ip"
                # then match LOCAL_PATTERNS
                for pattern in LOCAL_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        ret = "network-local"
                        break
        return ret

    def _strings(self):
        stream = self.elffile.stream
        epos = stream.tell()
        stream.seek(0, 0)
        data = stream.read()
        stream.seek(epos, 0)

        ret = []

        # XXX avoid calling eu-strings
        import subprocess
        p = subprocess.Popen("eu-strings",
                             shell=True,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE)
        out = p.communicate(input=data)[0]

        for line in out.splitlines():
            if re.match(b"^/tmp/.+", line) and "XXX" not in line:
                ret.append(line)

        return ret

    def tempstuff(self):
        tmp_strings = self._strings()

        # if there are no /tmp references, just return
        if len(tmp_strings) == 0:
            return "None"

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize "
                      "of zero!" % (bytes2str(section.name)),
                      file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in TMP_FUNCTIONS:
                    if re.match(pattern, bytes2str(symbol.name)):

                        return "None"

        return "$".join(tmp_strings)

    # XXX implement this
    def chroot_without_chdir(self):
        """
        Check for apps that use chroot(2) without using chdir(2).

        Inspired by http://people.redhat.com/sgrubb/security/find-chroot

        """
        pass

    def fortify(self):
        """
        Check if source code was compiled with FORTIFY_SOURCE.

        Enabled : no unsafe functions were found OR all were translated to _chk versions
        Partial : unprotected unsafe functions were found

        TODO
        ====

        * Print summary report like checksec.sh does

        * Drop CSV output support (it is too restrictive)

        * "addr2line" like feature for unprotected unsafe functions

        """
        unsafe_list = []

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize "
                      "of zero!" % (bytes2str(section.name)),
                      file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in UNSAFE_FUNCTIONS:
                    if re.match(pattern + "$", bytes2str(symbol.name)):
                        unsafe_list.append(bytes2str(symbol.name))

        if len(unsafe_list) == 0:
            return "Enabled"
        else:
            return "Partial$" + "$".join(unsafe_list)

    def canary(self):
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize "
                      "of zero!" % (bytes2str(section.name)),
                      file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                if bytes2str(symbol.name) in STACK_CHK:
                    return "Enabled"
        return "Disabled"

    def dynamic_tags(self, key="DT_RPATH"):
        for section in self.elffile.iter_sections():
            if not isinstance(section, DynamicSection):
                continue
            for tag in section.iter_tags():
                if tag.entry.d_tag == key:
                    return "Enabled"
        return "Disabled"

    def program_headers(self):
        pflags = P_FLAGS()
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        found = False
        for segment in self.elffile.iter_segments():
            if re.search("GNU_STACK", str(segment['p_type'])):
                found = True
                if segment['p_flags'] & pflags.PF_X:
                    return "Disabled"
        if found:
            return "Enabled"

        return "Disabled"

    def relro(self):
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        have_relro = False
        for segment in self.elffile.iter_segments():
            if re.search("GNU_RELRO", str(segment['p_type'])):
                have_relro = True
                break
        if self.dynamic_tags("DT_BIND_NOW") == "Enabled" and have_relro:
            return "Enabled"
        if have_relro:
            return "Partial"

        return "Disabled"

    def pie(self):
        header = self.elffile.header
        if self.dynamic_tags("EXEC") == "Enabled":
            return "Disabled"
        if "ET_DYN" in header['e_type']:
            if self.dynamic_tags("DT_DEBUG") == "Enabled":
                return "Enabled"
            else:
                return "DSO"
        return "Disabled"

    def getdeps(self):
        deps = []

        if self.elffile.num_segments() == 0:
            return deps

        for segment in self.elffile.iter_segments():
            if re.search("PT_DYNAMIC", str(segment['p_type'])):
                # this file uses dynamic linking, so read the dynamic section
                # and find DT_SONAME tag
                for section in self.elffile.iter_sections():
                    if not isinstance(section, DynamicSection):
                        continue
                    for tag in section.iter_tags():
                        if tag.entry.d_tag == 'DT_NEEDED':
                            deps.append(bytes2str(tag.needed))
                break

        return deps
Ejemplo n.º 42
0
#define RESPOND_FRAME_SIZE            0x04
#define END_FLASH_SIZE                0x04
FLASH_NEW_APP_SIZE = 0X10
FLASH_WRITE_SECTOR_SIZE = 0X0C + (MAX_DATA_SIZE)
RESPOND_FRAME_SIZE = 0x04
END_FLASH_SIZE = 0x04
DUMMY_BYTE = 0
##########################################################################################################
port = "COM29"
ser = serialwin32.Serial(port, 9600)
file = open(sys.argv[1], "rb")
elf_Handler = ELFFile(file)
Header = elf_Handler.header  #elf file header
Magic = Header.e_ident['EI_MAG']  #magic number
EntryPoint = Header.e_entry  #entry point
program_header_number = elf_Handler.num_segments(
)  #number of segments in the elf file
elf_Class = elf_Handler.elfclass  #elf file class
segments_header = []
segments = []
Req_num = 0


#Verification function
def verify_Elf_File():
    if Magic[0] != 127 or Magic[1] != 69 or Magic[2] != 76 or Magic[
            3] != 70 or elf_Class != 32:
        return False
    else:
        return True

Ejemplo n.º 43
0
class ReadElf(object):
    """ display_* methods are used to emit output into the output stream
    """
    def __init__(self, file, output):
        """ file:
                stream object with the ELF file to read

            output:
                output stream to write to
        """
        self.elffile = ELFFile(file)
        self.output = output

        # Lazily initialized if a debug dump is requested
        self._dwarfinfo = None

    def display_file_header(self):
        """ Display the ELF file header
        """
        self._emitline('ELF Header:')
        self._emit('  Magic:   ')
        self._emitline(' '.join('%2.2x' % byte2int(b)
                                    for b in self.elffile.e_ident_raw))
        header = self.elffile.header
        e_ident = header['e_ident']
        self._emitline('  Class:                             %s' %
                describe_ei_class(e_ident['EI_CLASS']))
        self._emitline('  Data:                              %s' %
                describe_ei_data(e_ident['EI_DATA']))
        self._emitline('  Version:                           %s' %
                describe_ei_version(e_ident['EI_VERSION']))
        self._emitline('  OS/ABI:                            %s' %
                describe_ei_osabi(e_ident['EI_OSABI']))
        self._emitline('  ABI Version:                       %d' %
                e_ident['EI_ABIVERSION'])
        self._emitline('  Type:                              %s' %
                describe_e_type(header['e_type']))
        self._emitline('  Machine:                           %s' %
                describe_e_machine(header['e_machine']))
        self._emitline('  Version:                           %s' %
                describe_e_version_numeric(header['e_version']))
        self._emitline('  Entry point address:               %s' %
                self._format_hex(header['e_entry']))
        self._emit('  Start of program headers:          %s' %
                header['e_phoff'])
        self._emitline(' (bytes into file)')
        self._emit('  Start of section headers:          %s' %
                header['e_shoff'])
        self._emitline(' (bytes into file)')
        self._emitline('  Flags:                             %s' %
                self._format_hex(header['e_flags']))
        self._emitline('  Size of this header:               %s (bytes)' %
                header['e_ehsize'])
        self._emitline('  Size of program headers:           %s (bytes)' %
                header['e_phentsize'])
        self._emitline('  Number of program headers:         %s' %
                header['e_phnum'])
        self._emitline('  Size of section headers:           %s (bytes)' %
                header['e_shentsize'])
        self._emitline('  Number of section headers:         %s' %
                header['e_shnum'])
        self._emitline('  Section header string table index: %s' %
                header['e_shstrndx'])

    def display_program_headers(self, show_heading=True):
        """ Display the ELF program headers.
            If show_heading is True, displays the heading for this information
            (Elf file type is...)
        """
        self._emitline()
        if self.elffile.num_segments() == 0:
            self._emitline('There are no program headers in this file.')
            return

        elfheader = self.elffile.header
        if show_heading:
            self._emitline('Elf file type is %s' %
                describe_e_type(elfheader['e_type']))
            self._emitline('Entry point is %s' %
                self._format_hex(elfheader['e_entry']))
            # readelf weirness - why isn't e_phoff printed as hex? (for section
            # headers, it is...)
            self._emitline('There are %s program headers, starting at offset %s' % (
                elfheader['e_phnum'], elfheader['e_phoff']))
            self._emitline()

        self._emitline('Program Headers:')

        # Now comes the table of program headers with their attributes. Note
        # that due to different formatting constraints of 32-bit and 64-bit
        # addresses, there are some conditions on elfclass here.
        #
        # First comes the table heading
        #
        if self.elffile.elfclass == 32:
            self._emitline('  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align')
        else:
            self._emitline('  Type           Offset             VirtAddr           PhysAddr')
            self._emitline('                 FileSiz            MemSiz              Flags  Align')

        # Now the entries
        #
        for segment in self.elffile.iter_segments():
            self._emit('  %-14s ' % describe_p_type(segment['p_type']))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %s %-3s %s' % (
                    self._format_hex(segment['p_offset'], fieldsize=6),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True),
                    self._format_hex(segment['p_filesz'], fieldsize=5),
                    self._format_hex(segment['p_memsz'], fieldsize=5),
                    describe_p_flags(segment['p_flags']),
                    self._format_hex(segment['p_align'])))
            else: # 64
                self._emitline('%s %s %s' % (
                    self._format_hex(segment['p_offset'], fullhex=True),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True)))
                self._emitline('                 %s %s  %-3s    %s' % (
                    self._format_hex(segment['p_filesz'], fullhex=True),
                    self._format_hex(segment['p_memsz'], fullhex=True),
                    describe_p_flags(segment['p_flags']),
                    # lead0x set to False for p_align, to mimic readelf.
                    # No idea why the difference from 32-bit mode :-|
                    self._format_hex(segment['p_align'], lead0x=False)))

            if isinstance(segment, InterpSegment):
                self._emitline('      [Requesting program interpreter: %s]' %
                    bytes2str(segment.get_interp_name()))

        # Sections to segments mapping
        #
        if self.elffile.num_sections() == 0:
            # No sections? We're done
            return

        self._emitline('\n Section to Segment mapping:')
        self._emitline('  Segment Sections...')

        for nseg, segment in enumerate(self.elffile.iter_segments()):
            self._emit('   %2.2d     ' % nseg)

            for section in self.elffile.iter_sections():
                if (    not section.is_null() and
                        segment.section_in_segment(section)):
                    self._emit('%s ' % bytes2str(section.name))

            self._emitline('')

    def display_section_headers(self, show_heading=True):
        """ Display the ELF section headers
        """
        elfheader = self.elffile.header
        if show_heading:
            self._emitline('There are %s section headers, starting at offset %s' % (
                elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))

        self._emitline('\nSection Header%s:' % (
            's' if elfheader['e_shnum'] > 1 else ''))

        # Different formatting constraints of 32-bit and 64-bit addresses
        #
        if self.elffile.elfclass == 32:
            self._emitline('  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al')
        else:
            self._emitline('  [Nr] Name              Type             Address           Offset')
            self._emitline('       Size              EntSize          Flags  Link  Info  Align')

        # Now the entries
        #
        for nsec, section in enumerate(self.elffile.iter_sections()):
            self._emit('  [%2u] %-17.17s %-15.15s ' % (
                nsec, bytes2str(section.name), describe_sh_type(section['sh_type'])))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %3s %2s %3s %2s' % (
                    self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False),
                    self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_size'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))
            else: # 64
                self._emitline(' %s  %s' % (
                    self._format_hex(section['sh_addr'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_offset'],
                        fieldsize=16 if section['sh_offset'] > 0xffffffff else 8,
                        lead0x=False)))
                self._emitline('       %s  %s %3s      %2s   %3s     %s' % (
                    self._format_hex(section['sh_size'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))

        self._emitline('Key to Flags:')
        self._emit('  W (write), A (alloc), X (execute), M (merge), S (strings)')
        if self.elffile['e_machine'] in ('EM_X86_64', 'EM_L10M'):
            self._emitline(', l (large)')
        else:
            self._emitline()
        self._emitline('  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
        self._emitline('  O (extra OS processing required) o (OS specific), p (processor specific)')

    def display_symbol_tables(self):
        """ Display the symbol tables contained in the file
        """
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue

            if section['sh_entsize'] == 0:
                self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % (
                    bytes2str(section.name)))
                continue

            self._emitline("\nSymbol table '%s' contains %s entries:" % (
                bytes2str(section.name), section.num_symbols()))

            if self.elffile.elfclass == 32:
                self._emitline('   Num:    Value  Size Type    Bind   Vis      Ndx Name')
            else: # 64
                self._emitline('   Num:    Value          Size Type    Bind   Vis      Ndx Name')

            for nsym, symbol in enumerate(section.iter_symbols()):
                # symbol names are truncated to 25 chars, similarly to readelf
                self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s' % (
                    nsym,
                    self._format_hex(symbol['st_value'], fullhex=True, lead0x=False),
                    symbol['st_size'],
                    describe_symbol_type(symbol['st_info']['type']),
                    describe_symbol_bind(symbol['st_info']['bind']),
                    describe_symbol_visibility(symbol['st_other']['visibility']),
                    describe_symbol_shndx(symbol['st_shndx']),
                    bytes2str(symbol.name)))

    def display_relocations(self):
        """ Display the relocations contained in the file
        """
        has_relocation_sections = False
        for section in self.elffile.iter_sections():
            if not isinstance(section, RelocationSection):
                continue

            has_relocation_sections = True
            self._emitline("\nRelocation section '%s' at offset %s contains %s entries:" % (
                bytes2str(section.name),
                self._format_hex(section['sh_offset']),
                section.num_relocations()))
            if section.is_RELA():
                self._emitline("  Offset          Info           Type           Sym. Value    Sym. Name + Addend")
            else:
                self._emitline(" Offset     Info    Type            Sym.Value  Sym. Name")

            # The symbol table section pointed to in sh_link
            symtable = self.elffile.get_section(section['sh_link'])

            for rel in section.iter_relocations():
                hexwidth = 8 if self.elffile.elfclass == 32 else 12
                self._emit('%s  %s %-17.17s' % (
                    self._format_hex(rel['r_offset'],
                        fieldsize=hexwidth, lead0x=False),
                    self._format_hex(rel['r_info'],
                        fieldsize=hexwidth, lead0x=False),
                    describe_reloc_type(
                        rel['r_info_type'], self.elffile)))

                if rel['r_info_sym'] == 0:
                    self._emitline()
                    continue

                symbol = symtable.get_symbol(rel['r_info_sym'])
                # Some symbols have zero 'st_name', so instead what's used is
                # the name of the section they point at
                if symbol['st_name'] == 0:
                    symsec = self.elffile.get_section(symbol['st_shndx'])
                    symbol_name = symsec.name
                else:
                    symbol_name = symbol.name
                self._emit(' %s %s%22.22s' % (
                    self._format_hex(
                        symbol['st_value'],
                        fullhex=True, lead0x=False),
                    '  ' if self.elffile.elfclass == 32 else '',
                    bytes2str(symbol_name)))
                if section.is_RELA():
                    self._emit(' %s %x' % (
                        '+' if rel['r_addend'] >= 0 else '-',
                        abs(rel['r_addend'])))
                self._emitline()

        if not has_relocation_sections:
            self._emitline('\nThere are no relocations in this file.')

    def display_hex_dump(self, section_spec):
        """ Display a hex dump of a section. section_spec is either a section
            number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nHex dump of section '%s':" % bytes2str(section.name))
        self._note_relocs_for_section(section)
        addr = section['sh_addr']
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            bytesleft = len(data) - dataptr
            # chunks of 16 bytes per line
            linebytes = 16 if bytesleft > 16 else bytesleft

            self._emit('  %s ' % self._format_hex(addr, fieldsize=8))
            for i in range(16):
                if i < linebytes:
                    self._emit('%2.2x' % byte2int(data[dataptr + i]))
                else:
                    self._emit('  ')
                if i % 4 == 3:
                    self._emit(' ')

            for i in range(linebytes):
                c = data[dataptr + i : dataptr + i + 1]
                if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
                    self._emit(bytes2str(c))
                else:
                    self._emit(bytes2str(b'.'))

            self._emitline()
            addr += linebytes
            dataptr += linebytes

        self._emitline()

    def display_string_dump(self, section_spec):
        """ Display a strings dump of a section. section_spec is either a
            section number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nString dump of section '%s':" % bytes2str(section.name))

        found = False
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            while ( dataptr < len(data) and
                    not (32 <= byte2int(data[dataptr]) <= 127)):
                dataptr += 1

            if dataptr >= len(data):
                break

            endptr = dataptr
            while endptr < len(data) and byte2int(data[endptr]) != 0:
                endptr += 1

            found = True
            self._emitline('  [%6x]  %s' % (
                dataptr, bytes2str(data[dataptr:endptr])))

            dataptr = endptr

        if not found:
            self._emitline('  No strings found in this section.')
        else:
            self._emitline()

    def display_debug_dump(self, dump_what):
        """ Dump a DWARF section
        """
        self._init_dwarfinfo()
        if self._dwarfinfo is None:
            return

        set_global_machine_arch(self.elffile.get_machine_arch())

        if dump_what == 'info':
            self._dump_debug_info()
        elif dump_what == 'decodedline':
            self._dump_debug_line_programs()
        elif dump_what == 'frames':
            self._dump_debug_frames()
        elif dump_what == 'frames-interp':
            self._dump_debug_frames_interp()
        else:
            self._emitline('debug dump not yet supported for "%s"' % dump_what)

    def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True):
        """ Format an address into a hexadecimal string.

            fieldsize:
                Size of the hexadecimal field (with leading zeros to fit the
                address into. For example with fieldsize=8, the format will
                be %08x
                If None, the minimal required field size will be used.

            fullhex:
                If True, override fieldsize to set it to the maximal size
                needed for the elfclass

            lead0x:
                If True, leading 0x is added
        """
        s = '0x' if lead0x else ''
        if fullhex:
            fieldsize = 8 if self.elffile.elfclass == 32 else 16
        if fieldsize is None:
            field = '%x'
        else:
            field = '%' + '0%sx' % fieldsize
        return s + field % addr

    def _section_from_spec(self, spec):
        """ Retrieve a section given a "spec" (either number or name).
            Return None if no such section exists in the file.
        """
        try:
            num = int(spec)
            if num < self.elffile.num_sections():
                return self.elffile.get_section(num)
            else:
                return None
        except ValueError:
            # Not a number. Must be a name then
            return self.elffile.get_section_by_name(str2bytes(spec))

    def _note_relocs_for_section(self, section):
        """ If there are relocation sections pointing to the givne section,
            emit a note about it.
        """
        for relsec in self.elffile.iter_sections():
            if isinstance(relsec, RelocationSection):
                info_idx = relsec['sh_info']
                if self.elffile.get_section(info_idx) == section:
                    self._emitline('  Note: This section has relocations against it, but these have NOT been applied to this dump.')
                    return

    def _init_dwarfinfo(self):
        """ Initialize the DWARF info contained in the file and assign it to
            self._dwarfinfo.
            Leave self._dwarfinfo at None if no DWARF info was found in the file
        """
        if self._dwarfinfo is not None:
            return

        if self.elffile.has_dwarf_info():
            self._dwarfinfo = self.elffile.get_dwarf_info()
        else:
            self._dwarfinfo = None

    def _dump_debug_info(self):
        """ Dump the debugging info section.
        """
        self._emitline('Contents of the .debug_info section:\n')

        # Offset of the .debug_info section in the stream
        section_offset = self._dwarfinfo.debug_info_sec.global_offset

        for cu in self._dwarfinfo.iter_CUs():
            self._emitline('  Compilation Unit @ offset %s:' %
                self._format_hex(cu.cu_offset))
            self._emitline('   Length:        %s (%s)' % (
                self._format_hex(cu['unit_length']),
                '%s-bit' % cu.dwarf_format()))
            self._emitline('   Version:       %s' % cu['version']),
            self._emitline('   Abbrev Offset: %s' % cu['debug_abbrev_offset']),
            self._emitline('   Pointer Size:  %s' % cu['address_size'])

            # The nesting depth of each DIE within the tree of DIEs must be
            # displayed. To implement this, a counter is incremented each time
            # the current DIE has children, and decremented when a null die is
            # encountered. Due to the way the DIE tree is serialized, this will
            # correctly reflect the nesting depth
            #
            die_depth = 0
            for die in cu.iter_DIEs():
                if die.is_null():
                    die_depth -= 1
                    continue
                self._emitline(' <%s><%x>: Abbrev Number: %s (%s)' % (
                    die_depth,
                    die.offset,
                    die.abbrev_code,
                    die.tag))

                for attr in itervalues(die.attributes):
                    name = attr.name
                    # Unknown attribute values are passed-through as integers
                    if isinstance(name, int):
                        name = 'Unknown AT value: %x' % name
                    self._emitline('    <%2x>   %-18s: %s' % (
                        attr.offset,
                        name,
                        describe_attr_value(
                            attr, die, section_offset)))

                if die.has_children:
                    die_depth += 1

        self._emitline()

    def _dump_debug_line_programs(self):
        """ Dump the (decoded) line programs from .debug_line
            The programs are dumped in the order of the CUs they belong to.
        """
        self._emitline('Decoded dump of debug contents of section .debug_line:\n')

        for cu in self._dwarfinfo.iter_CUs():
            lineprogram = self._dwarfinfo.line_program_for_CU(cu)

            cu_filename = ''
            if len(lineprogram['include_directory']) > 0:
                cu_filename = '%s/%s' % (
                    bytes2str(lineprogram['include_directory'][0]),
                    bytes2str(lineprogram['file_entry'][0].name))
            else:
                cu_filename = bytes2str(lineprogram['file_entry'][0].name)

            self._emitline('CU: %s:' % cu_filename)
            self._emitline('File name                            Line number    Starting address')

            # Print each state's file, line and address information. For some
            # instructions other output is needed to be compatible with
            # readelf.
            for entry in lineprogram.get_entries():
                state = entry.state
                if state is None:
                    # Special handling for commands that don't set a new state
                    if entry.command == DW_LNS_set_file:
                        file_entry = lineprogram['file_entry'][entry.args[0] - 1]
                        if file_entry.dir_index == 0:
                            # current directory
                            self._emitline('\n./%s:[++]' % (
                                bytes2str(file_entry.name)))
                        else:
                            self._emitline('\n%s/%s:' % (
                                bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
                                bytes2str(file_entry.name)))
                    elif entry.command == DW_LNE_define_file:
                        self._emitline('%s:' % (
                            bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
                elif not state.end_sequence:
                    # readelf doesn't print the state after end_sequence
                    # instructions. I think it's a bug but to be compatible
                    # I don't print them too.
                    self._emitline('%-35s  %11d  %18s' % (
                        bytes2str(lineprogram['file_entry'][state.file - 1].name),
                        state.line,
                        '0' if state.address == 0 else
                               self._format_hex(state.address)))
                if entry.command == DW_LNS_copy:
                    # Another readelf oddity...
                    self._emitline()

    def _dump_debug_frames(self):
        """ Dump the raw frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return
        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE' % (
                    entry.offset, entry['length'], entry['CIE_id']))
                self._emitline('  Version:               %d' % entry['version'])
                self._emitline('  Augmentation:          "%s"' % bytes2str(entry['augmentation']))
                self._emitline('  Code alignment factor: %u' % entry['code_alignment_factor'])
                self._emitline('  Data alignment factor: %d' % entry['data_alignment_factor'])
                self._emitline('  Return address column: %d' % entry['return_address_register'])
                self._emitline()
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))

            self._emit(describe_CFI_instructions(entry))
        self._emitline()

    def _dump_debug_frames_interp(self):
        """ Dump the interpreted (decoded) frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return

        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_id'],
                    bytes2str(entry['augmentation']),
                    entry['code_alignment_factor'],
                    entry['data_alignment_factor'],
                    entry['return_address_register']))
                ra_regnum = entry['return_address_register']
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))
                ra_regnum = entry.cie['return_address_register']

            # Print the heading row for the decoded table
            self._emit('   LOC')
            self._emit('  ' if entry.structs.address_size == 4 else '          ')
            self._emit(' CFA      ')

            # Decode the table nad look at the registers it describes.
            # We build reg_order here to match readelf's order. In particular,
            # registers are sorted by their number, and the register matching
            # ra_regnum is always listed last with a special heading.
            decoded_table = entry.get_decoded()
            reg_order = sorted(ifilter(
                lambda r: r != ra_regnum,
                decoded_table.reg_order))

            # Headings for the registers
            for regnum in reg_order:
                self._emit('%-6s' % describe_reg_name(regnum))
            self._emitline('ra      ')

            # Now include ra_regnum in reg_order to print its values similarly
            # to the other registers.
            reg_order.append(ra_regnum)
            for line in decoded_table.table:
                self._emit(self._format_hex(
                    line['pc'], fullhex=True, lead0x=False))
                self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa']))

                for regnum in reg_order:
                    if regnum in line:
                        s = describe_CFI_register_rule(line[regnum])
                    else:
                        s = 'u'
                    self._emit('%-6s' % s)
                self._emitline()
        self._emitline()

    def _emit(self, s=''):
        """ Emit an object to output
        """
        self.output.write(str(s))

    def _emitline(self, s=''):
        """ Emit an object to output, followed by a newline
        """
        self.output.write(str(s) + '\n')
Ejemplo n.º 44
0
class ReadElf(object):
    """ display_* methods are used to emit output into the output stream
    """
    def __init__(self, file, output):
        """ file:
                stream object with the ELF file to read

            output:
                output stream to write to
        """
        self.elffile = ELFFile(file)
        self.output = output

        # Lazily initialized if a debug dump is requested
        self._dwarfinfo = None

        self._versioninfo = None

    def display_file_header(self):
        """ Display the ELF file header
        """
        self._emitline('ELF Header:')
        self._emit('  Magic:   ')
        self._emitline(' '.join('%2.2x' % byte2int(b)
                                    for b in self.elffile.e_ident_raw))
        header = self.elffile.header
        e_ident = header['e_ident']
        self._emitline('  Class:                             %s' %
                describe_ei_class(e_ident['EI_CLASS']))
        self._emitline('  Data:                              %s' %
                describe_ei_data(e_ident['EI_DATA']))
        self._emitline('  Version:                           %s' %
                describe_ei_version(e_ident['EI_VERSION']))
        self._emitline('  OS/ABI:                            %s' %
                describe_ei_osabi(e_ident['EI_OSABI']))
        self._emitline('  ABI Version:                       %d' %
                e_ident['EI_ABIVERSION'])
        self._emitline('  Type:                              %s' %
                describe_e_type(header['e_type']))
        self._emitline('  Machine:                           %s' %
                describe_e_machine(header['e_machine']))
        self._emitline('  Version:                           %s' %
                describe_e_version_numeric(header['e_version']))
        self._emitline('  Entry point address:               %s' %
                self._format_hex(header['e_entry']))
        self._emit('  Start of program headers:          %s' %
                header['e_phoff'])
        self._emitline(' (bytes into file)')
        self._emit('  Start of section headers:          %s' %
                header['e_shoff'])
        self._emitline(' (bytes into file)')
        self._emitline('  Flags:                             %s%s' %
                (self._format_hex(header['e_flags']),
                self.decode_flags(header['e_flags'])))
        self._emitline('  Size of this header:               %s (bytes)' %
                header['e_ehsize'])
        self._emitline('  Size of program headers:           %s (bytes)' %
                header['e_phentsize'])
        self._emitline('  Number of program headers:         %s' %
                header['e_phnum'])
        self._emitline('  Size of section headers:           %s (bytes)' %
                header['e_shentsize'])
        self._emitline('  Number of section headers:         %s' %
                header['e_shnum'])
        self._emitline('  Section header string table index: %s' %
                header['e_shstrndx'])

    def decode_flags(self, flags):
        description = ""
        if self.elffile['e_machine'] == "EM_ARM":
            if flags & E_FLAGS.EF_ARM_HASENTRY:
                description += ", has entry point"

            version = flags & E_FLAGS.EF_ARM_EABIMASK
            if version == E_FLAGS.EF_ARM_EABI_VER5:
                description += ", Version5 EABI"
        return description

    def display_program_headers(self, show_heading=True):
        """ Display the ELF program headers.
            If show_heading is True, displays the heading for this information
            (Elf file type is...)
        """
        self._emitline()
        if self.elffile.num_segments() == 0:
            self._emitline('There are no program headers in this file.')
            return

        elfheader = self.elffile.header
        if show_heading:
            self._emitline('Elf file type is %s' %
                describe_e_type(elfheader['e_type']))
            self._emitline('Entry point is %s' %
                self._format_hex(elfheader['e_entry']))
            # readelf weirness - why isn't e_phoff printed as hex? (for section
            # headers, it is...)
            self._emitline('There are %s program headers, starting at offset %s' % (
                elfheader['e_phnum'], elfheader['e_phoff']))
            self._emitline()

        self._emitline('Program Headers:')

        # Now comes the table of program headers with their attributes. Note
        # that due to different formatting constraints of 32-bit and 64-bit
        # addresses, there are some conditions on elfclass here.
        #
        # First comes the table heading
        #
        if self.elffile.elfclass == 32:
            self._emitline('  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align')
        else:
            self._emitline('  Type           Offset             VirtAddr           PhysAddr')
            self._emitline('                 FileSiz            MemSiz              Flags  Align')

        # Now the entries
        #
        for segment in self.elffile.iter_segments():
            self._emit('  %-14s ' % describe_p_type(segment['p_type']))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %s %-3s %s' % (
                    self._format_hex(segment['p_offset'], fieldsize=6),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True),
                    self._format_hex(segment['p_filesz'], fieldsize=5),
                    self._format_hex(segment['p_memsz'], fieldsize=5),
                    describe_p_flags(segment['p_flags']),
                    self._format_hex(segment['p_align'])))
            else: # 64
                self._emitline('%s %s %s' % (
                    self._format_hex(segment['p_offset'], fullhex=True),
                    self._format_hex(segment['p_vaddr'], fullhex=True),
                    self._format_hex(segment['p_paddr'], fullhex=True)))
                self._emitline('                 %s %s  %-3s    %s' % (
                    self._format_hex(segment['p_filesz'], fullhex=True),
                    self._format_hex(segment['p_memsz'], fullhex=True),
                    describe_p_flags(segment['p_flags']),
                    # lead0x set to False for p_align, to mimic readelf.
                    # No idea why the difference from 32-bit mode :-|
                    self._format_hex(segment['p_align'], lead0x=False)))

            if isinstance(segment, InterpSegment):
                self._emitline('      [Requesting program interpreter: %s]' %
                    bytes2str(segment.get_interp_name()))

        # Sections to segments mapping
        #
        if self.elffile.num_sections() == 0:
            # No sections? We're done
            return

        self._emitline('\n Section to Segment mapping:')
        self._emitline('  Segment Sections...')

        for nseg, segment in enumerate(self.elffile.iter_segments()):
            self._emit('   %2.2d     ' % nseg)

            for section in self.elffile.iter_sections():
                if (    not section.is_null() and
                        segment.section_in_segment(section)):
                    self._emit('%s ' % bytes2str(section.name))

            self._emitline('')

    def display_section_headers(self, show_heading=True):
        """ Display the ELF section headers
        """
        elfheader = self.elffile.header
        if show_heading:
            self._emitline('There are %s section headers, starting at offset %s' % (
                elfheader['e_shnum'], self._format_hex(elfheader['e_shoff'])))

        self._emitline('\nSection Header%s:' % (
            's' if elfheader['e_shnum'] > 1 else ''))

        # Different formatting constraints of 32-bit and 64-bit addresses
        #
        if self.elffile.elfclass == 32:
            self._emitline('  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al')
        else:
            self._emitline('  [Nr] Name              Type             Address           Offset')
            self._emitline('       Size              EntSize          Flags  Link  Info  Align')

        # Now the entries
        #
        for nsec, section in enumerate(self.elffile.iter_sections()):
            self._emit('  [%2u] %-17.17s %-15.15s ' % (
                nsec, bytes2str(section.name), describe_sh_type(section['sh_type'])))

            if self.elffile.elfclass == 32:
                self._emitline('%s %s %s %s %3s %2s %3s %2s' % (
                    self._format_hex(section['sh_addr'], fieldsize=8, lead0x=False),
                    self._format_hex(section['sh_offset'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_size'], fieldsize=6, lead0x=False),
                    self._format_hex(section['sh_entsize'], fieldsize=2, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))
            else: # 64
                self._emitline(' %s  %s' % (
                    self._format_hex(section['sh_addr'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_offset'],
                        fieldsize=16 if section['sh_offset'] > 0xffffffff else 8,
                        lead0x=False)))
                self._emitline('       %s  %s %3s      %2s   %3s     %s' % (
                    self._format_hex(section['sh_size'], fullhex=True, lead0x=False),
                    self._format_hex(section['sh_entsize'], fullhex=True, lead0x=False),
                    describe_sh_flags(section['sh_flags']),
                    section['sh_link'], section['sh_info'],
                    section['sh_addralign']))

        self._emitline('Key to Flags:')
        self._emit('  W (write), A (alloc), X (execute), M (merge), S (strings)')
        if self.elffile['e_machine'] in ('EM_X86_64', 'EM_L10M'):
            self._emitline(', l (large)')
        else:
            self._emitline()
        self._emitline('  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)')
        self._emitline('  O (extra OS processing required) o (OS specific), p (processor specific)')

    def display_symbol_tables(self):
        """ Display the symbol tables contained in the file
        """
        self._init_versioninfo()

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue

            if section['sh_entsize'] == 0:
                self._emitline("\nSymbol table '%s' has a sh_entsize of zero!" % (
                    bytes2str(section.name)))
                continue

            self._emitline("\nSymbol table '%s' contains %s entries:" % (
                bytes2str(section.name), section.num_symbols()))

            if self.elffile.elfclass == 32:
                self._emitline('   Num:    Value  Size Type    Bind   Vis      Ndx Name')
            else: # 64
                self._emitline('   Num:    Value          Size Type    Bind   Vis      Ndx Name')

            for nsym, symbol in enumerate(section.iter_symbols()):

                version_info = ''
                # readelf doesn't display version info for Solaris versioning
                if (section['sh_type'] == 'SHT_DYNSYM' and
                        self._versioninfo['type'] == 'GNU'):
                    version = self._symbol_version(nsym)
                    if (version['name'] != bytes2str(symbol.name) and
                        version['index'] not in ('VER_NDX_LOCAL',
                                                 'VER_NDX_GLOBAL')):
                        if version['filename']:
                            # external symbol
                            version_info = '@%(name)s (%(index)i)' % version
                        else:
                            # internal symbol
                            if version['hidden']:
                                version_info = '@%(name)s' % version
                            else:
                                version_info = '@@%(name)s' % version

                # symbol names are truncated to 25 chars, similarly to readelf
                self._emitline('%6d: %s %5d %-7s %-6s %-7s %4s %.25s%s' % (
                    nsym,
                    self._format_hex(
                        symbol['st_value'], fullhex=True, lead0x=False),
                    symbol['st_size'],
                    describe_symbol_type(symbol['st_info']['type']),
                    describe_symbol_bind(symbol['st_info']['bind']),
                    describe_symbol_visibility(symbol['st_other']['visibility']),
                    describe_symbol_shndx(symbol['st_shndx']),
                    bytes2str(symbol.name),
                    version_info))

    def display_dynamic_tags(self):
        """ Display the dynamic tags contained in the file
        """
        has_dynamic_sections = False
        for section in self.elffile.iter_sections():
            if not isinstance(section, DynamicSection):
                continue

            has_dynamic_sections = True
            self._emitline("\nDynamic section at offset %s contains %s entries:" % (
                self._format_hex(section['sh_offset']),
                section.num_tags()))
            self._emitline("  Tag        Type                         Name/Value")

            padding = 20 + (8 if self.elffile.elfclass == 32 else 0)
            for tag in section.iter_tags():
                if tag.entry.d_tag == 'DT_NEEDED':
                    parsed = 'Shared library: [%s]' % bytes2str(tag.needed)
                elif tag.entry.d_tag == 'DT_RPATH':
                    parsed = 'Library rpath: [%s]' % bytes2str(tag.rpath)
                elif tag.entry.d_tag == 'DT_RUNPATH':
                    parsed = 'Library runpath: [%s]' % bytes2str(tag.runpath)
                elif tag.entry.d_tag == 'DT_SONAME':
                    parsed = 'Library soname: [%s]' % bytes2str(tag.soname)
                elif (tag.entry.d_tag.endswith('SZ') or
                      tag.entry.d_tag.endswith('ENT')):
                    parsed = '%i (bytes)' % tag['d_val']
                elif (tag.entry.d_tag.endswith('NUM') or
                      tag.entry.d_tag.endswith('COUNT')):
                    parsed = '%i' % tag['d_val']
                elif tag.entry.d_tag == 'DT_PLTREL':
                    s = describe_dyn_tag(tag.entry.d_val)
                    if s.startswith('DT_'):
                        s = s[3:]
                    parsed = '%s' % s
                else:
                    parsed = '%#x' % tag['d_val']

                self._emitline(" %s %-*s %s" % (
                    self._format_hex(ENUM_D_TAG.get(tag.entry.d_tag, tag.entry.d_tag),
                        fullhex=True, lead0x=True),
                    padding,
                    '(%s)' % (tag.entry.d_tag[3:],),
                    parsed))
        if not has_dynamic_sections:
            # readelf only prints this if there is at least one segment
            if self.elffile.num_segments():
                self._emitline("\nThere is no dynamic section in this file.")

    def display_relocations(self):
        """ Display the relocations contained in the file
        """
        has_relocation_sections = False
        for section in self.elffile.iter_sections():
            if not isinstance(section, RelocationSection):
                continue

            has_relocation_sections = True
            self._emitline("\nRelocation section '%s' at offset %s contains %s entries:" % (
                bytes2str(section.name),
                self._format_hex(section['sh_offset']),
                section.num_relocations()))
            if section.is_RELA():
                self._emitline("  Offset          Info           Type           Sym. Value    Sym. Name + Addend")
            else:
                self._emitline(" Offset     Info    Type            Sym.Value  Sym. Name")

            # The symbol table section pointed to in sh_link
            symtable = self.elffile.get_section(section['sh_link'])

            for rel in section.iter_relocations():
                hexwidth = 8 if self.elffile.elfclass == 32 else 12
                self._emit('%s  %s %-17.17s' % (
                    self._format_hex(rel['r_offset'],
                        fieldsize=hexwidth, lead0x=False),
                    self._format_hex(rel['r_info'],
                        fieldsize=hexwidth, lead0x=False),
                    describe_reloc_type(
                        rel['r_info_type'], self.elffile)))

                if rel['r_info_sym'] == 0:
                    self._emitline()
                    continue

                symbol = symtable.get_symbol(rel['r_info_sym'])
                # Some symbols have zero 'st_name', so instead what's used is
                # the name of the section they point at
                if symbol['st_name'] == 0:
                    symsec = self.elffile.get_section(symbol['st_shndx'])
                    symbol_name = symsec.name
                else:
                    symbol_name = symbol.name
                self._emit(' %s %s%22.22s' % (
                    self._format_hex(
                        symbol['st_value'],
                        fullhex=True, lead0x=False),
                    '  ' if self.elffile.elfclass == 32 else '',
                    bytes2str(symbol_name)))
                if section.is_RELA():
                    self._emit(' %s %x' % (
                        '+' if rel['r_addend'] >= 0 else '-',
                        abs(rel['r_addend'])))
                self._emitline()

        if not has_relocation_sections:
            self._emitline('\nThere are no relocations in this file.')

    def display_version_info(self):
        """ Display the version info contained in the file
        """
        self._init_versioninfo()

        if not self._versioninfo['type']:
            self._emitline("\nNo version information found in this file.")
            return

        for section in self.elffile.iter_sections():
            if isinstance(section, GNUVerSymSection):
                self._print_version_section_header(
                    section, 'Version symbols', lead0x=False)

                num_symbols = section.num_symbols()
    
                # Symbol version info are printed four by four entries 
                for idx_by_4 in range(0, num_symbols, 4):

                    self._emit('  %03x:' % idx_by_4)

                    for idx in range(idx_by_4, min(idx_by_4 + 4, num_symbols)):

                        symbol_version = self._symbol_version(idx)
                        if symbol_version['index'] == 'VER_NDX_LOCAL':
                            version_index = 0
                            version_name = '(*local*)'
                        elif symbol_version['index'] == 'VER_NDX_GLOBAL':
                            version_index = 1
                            version_name = '(*global*)'
                        else:
                            version_index = symbol_version['index']
                            version_name = '(%(name)s)' % symbol_version

                        visibility = 'h' if symbol_version['hidden'] else ' '

                        self._emit('%4x%s%-13s' % (
                            version_index, visibility, version_name))

                    self._emitline()

            elif isinstance(section, GNUVerDefSection):
                self._print_version_section_header(
                    section, 'Version definition', indent=2)

                offset = 0
                for verdef, verdaux_iter in section.iter_versions():
                    verdaux = next(verdaux_iter)

                    name = verdaux.name
                    if verdef['vd_flags']:
                        flags = describe_ver_flags(verdef['vd_flags'])
                        # Mimic exactly the readelf output
                        flags += ' '
                    else:
                        flags = 'none'

                    self._emitline('  %s: Rev: %i  Flags: %s  Index: %i'
                                   '  Cnt: %i  Name: %s' % (
                            self._format_hex(offset, fieldsize=6,
                                             alternate=True),
                            verdef['vd_version'], flags, verdef['vd_ndx'],
                            verdef['vd_cnt'], bytes2str(name)))

                    verdaux_offset = (
                            offset + verdef['vd_aux'] + verdaux['vda_next'])
                    for idx, verdaux in enumerate(verdaux_iter, start=1):
                        self._emitline('  %s: Parent %i: %s' %
                            (self._format_hex(verdaux_offset, fieldsize=4),
                                              idx, bytes2str(verdaux.name)))
                        verdaux_offset += verdaux['vda_next']

                    offset += verdef['vd_next']

            elif isinstance(section, GNUVerNeedSection):
                self._print_version_section_header(section, 'Version needs')

                offset = 0
                for verneed, verneed_iter in section.iter_versions():

                    self._emitline('  %s: Version: %i  File: %s  Cnt: %i' % (
                            self._format_hex(offset, fieldsize=6,
                                             alternate=True),
                            verneed['vn_version'], bytes2str(verneed.name),
                            verneed['vn_cnt']))

                    vernaux_offset = offset + verneed['vn_aux']
                    for idx, vernaux in enumerate(verneed_iter, start=1):
                        if vernaux['vna_flags']:
                            flags = describe_ver_flags(vernaux['vna_flags'])
                            # Mimic exactly the readelf output
                            flags += ' '
                        else:
                            flags = 'none'

                        self._emitline(
                            '  %s:   Name: %s  Flags: %s  Version: %i' % (
                                self._format_hex(vernaux_offset, fieldsize=4),
                                bytes2str(vernaux.name), flags,
                                vernaux['vna_other']))

                        vernaux_offset += vernaux['vna_next']

                    offset += verneed['vn_next']

    def display_hex_dump(self, section_spec):
        """ Display a hex dump of a section. section_spec is either a section
            number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nHex dump of section '%s':" % bytes2str(section.name))
        self._note_relocs_for_section(section)
        addr = section['sh_addr']
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            bytesleft = len(data) - dataptr
            # chunks of 16 bytes per line
            linebytes = 16 if bytesleft > 16 else bytesleft

            self._emit('  %s ' % self._format_hex(addr, fieldsize=8))
            for i in range(16):
                if i < linebytes:
                    self._emit('%2.2x' % byte2int(data[dataptr + i]))
                else:
                    self._emit('  ')
                if i % 4 == 3:
                    self._emit(' ')

            for i in range(linebytes):
                c = data[dataptr + i : dataptr + i + 1]
                if byte2int(c[0]) >= 32 and byte2int(c[0]) < 0x7f:
                    self._emit(bytes2str(c))
                else:
                    self._emit(bytes2str(b'.'))

            self._emitline()
            addr += linebytes
            dataptr += linebytes

        self._emitline()

    def display_string_dump(self, section_spec):
        """ Display a strings dump of a section. section_spec is either a
            section number or a name.
        """
        section = self._section_from_spec(section_spec)
        if section is None:
            self._emitline("Section '%s' does not exist in the file!" % (
                section_spec))
            return

        self._emitline("\nString dump of section '%s':" % bytes2str(section.name))

        found = False
        data = section.data()
        dataptr = 0

        while dataptr < len(data):
            while ( dataptr < len(data) and
                    not (32 <= byte2int(data[dataptr]) <= 127)):
                dataptr += 1

            if dataptr >= len(data):
                break

            endptr = dataptr
            while endptr < len(data) and byte2int(data[endptr]) != 0:
                endptr += 1

            found = True
            self._emitline('  [%6x]  %s' % (
                dataptr, bytes2str(data[dataptr:endptr])))

            dataptr = endptr

        if not found:
            self._emitline('  No strings found in this section.')
        else:
            self._emitline()

    def display_debug_dump(self, dump_what):
        """ Dump a DWARF section
        """
        self._init_dwarfinfo()
        if self._dwarfinfo is None:
            return

        set_global_machine_arch(self.elffile.get_machine_arch())

        if dump_what == 'info':
            self._dump_debug_info()
        elif dump_what == 'decodedline':
            self._dump_debug_line_programs()
        elif dump_what == 'frames':
            self._dump_debug_frames()
        elif dump_what == 'frames-interp':
            self._dump_debug_frames_interp()
        else:
            self._emitline('debug dump not yet supported for "%s"' % dump_what)

    def _format_hex(self, addr, fieldsize=None, fullhex=False, lead0x=True,
                    alternate=False):
        """ Format an address into a hexadecimal string.

            fieldsize:
                Size of the hexadecimal field (with leading zeros to fit the
                address into. For example with fieldsize=8, the format will
                be %08x
                If None, the minimal required field size will be used.

            fullhex:
                If True, override fieldsize to set it to the maximal size
                needed for the elfclass

            lead0x:
                If True, leading 0x is added

            alternate:
                If True, override lead0x to emulate the alternate
                hexadecimal form specified in format string with the #
                character: only non-zero values are prefixed with 0x.
                This form is used by readelf.
        """
        if alternate:
            if addr == 0:
                lead0x = False
            else:
                lead0x = True
                fieldsize -= 2

        s = '0x' if lead0x else ''
        if fullhex:
            fieldsize = 8 if self.elffile.elfclass == 32 else 16
        if fieldsize is None:
            field = '%x'
        else:
            field = '%' + '0%sx' % fieldsize
        return s + field % addr

    def _print_version_section_header(self, version_section, name, lead0x=True,
                                      indent=1):
        """ Print a section header of one version related section (versym,
            verneed or verdef) with some options to accomodate readelf
            little differences between each header (e.g. indentation
            and 0x prefixing).
        """
        if hasattr(version_section, 'num_versions'):
            num_entries = version_section.num_versions()
        else:
            num_entries = version_section.num_symbols()

        self._emitline("\n%s section '%s' contains %s entries:" %
            (name, bytes2str(version_section.name), num_entries))
        self._emitline('%sAddr: %s  Offset: %s  Link: %i (%s)' % (
            ' ' * indent,
            self._format_hex(
                version_section['sh_addr'], fieldsize=16, lead0x=lead0x),
            self._format_hex(
                version_section['sh_offset'], fieldsize=6, lead0x=True),
            version_section['sh_link'],
            bytes2str(
                self.elffile.get_section(version_section['sh_link']).name)
            )
        )

    def _init_versioninfo(self):
        """ Search and initialize informations about version related sections
            and the kind of versioning used (GNU or Solaris).
        """
        if self._versioninfo is not None:
            return

        self._versioninfo = {'versym': None, 'verdef': None,
                             'verneed': None, 'type': None}

        for section in self.elffile.iter_sections():
            if isinstance(section, GNUVerSymSection):
                self._versioninfo['versym'] = section
            elif isinstance(section, GNUVerDefSection):
                self._versioninfo['verdef'] = section
            elif isinstance(section, GNUVerNeedSection):
                self._versioninfo['verneed'] = section
            elif isinstance(section, DynamicSection):
                for tag in section.iter_tags():
                    if tag['d_tag'] == 'DT_VERSYM':
                        self._versioninfo['type'] = 'GNU'
                        break

        if not self._versioninfo['type'] and (
                self._versioninfo['verneed'] or self._versioninfo['verdef']):
            self._versioninfo['type'] = 'Solaris'

    def _symbol_version(self, nsym):
        """ Return a dict containing information on the
                   or None if no version information is available
        """
        self._init_versioninfo()

        symbol_version = dict.fromkeys(('index', 'name', 'filename', 'hidden'))

        if (not self._versioninfo['versym'] or
                nsym >= self._versioninfo['versym'].num_symbols()):
            return None

        symbol = self._versioninfo['versym'].get_symbol(nsym)
        index = symbol.entry['ndx']
        if not index in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL'):
            index = int(index)

            if self._versioninfo['type'] == 'GNU':
                # In GNU versioning mode, the highest bit is used to
                # store wether the symbol is hidden or not
                if index & 0x8000:
                    index &= ~0x8000
                    symbol_version['hidden'] = True

            if (self._versioninfo['verdef'] and
                    index <= self._versioninfo['verdef'].num_versions()):
                _, verdaux_iter = \
                        self._versioninfo['verdef'].get_version(index)
                symbol_version['name'] = bytes2str(next(verdaux_iter).name)
            else:
                verneed, vernaux = \
                        self._versioninfo['verneed'].get_version(index)
                symbol_version['name'] = bytes2str(vernaux.name)
                symbol_version['filename'] = bytes2str(verneed.name)

        symbol_version['index'] = index
        return symbol_version

    def _section_from_spec(self, spec):
        """ Retrieve a section given a "spec" (either number or name).
            Return None if no such section exists in the file.
        """
        try:
            num = int(spec)
            if num < self.elffile.num_sections():
                return self.elffile.get_section(num)
            else:
                return None
        except ValueError:
            # Not a number. Must be a name then
            return self.elffile.get_section_by_name(str2bytes(spec))

    def _note_relocs_for_section(self, section):
        """ If there are relocation sections pointing to the givne section,
            emit a note about it.
        """
        for relsec in self.elffile.iter_sections():
            if isinstance(relsec, RelocationSection):
                info_idx = relsec['sh_info']
                if self.elffile.get_section(info_idx) == section:
                    self._emitline('  Note: This section has relocations against it, but these have NOT been applied to this dump.')
                    return

    def _init_dwarfinfo(self):
        """ Initialize the DWARF info contained in the file and assign it to
            self._dwarfinfo.
            Leave self._dwarfinfo at None if no DWARF info was found in the file
        """
        if self._dwarfinfo is not None:
            return

        if self.elffile.has_dwarf_info():
            self._dwarfinfo = self.elffile.get_dwarf_info()
        else:
            self._dwarfinfo = None

    def _dump_debug_info(self):
        """ Dump the debugging info section.
        """
        self._emitline('Contents of the .debug_info section:\n')

        # Offset of the .debug_info section in the stream
        section_offset = self._dwarfinfo.debug_info_sec.global_offset

        for cu in self._dwarfinfo.iter_CUs():
            self._emitline('  Compilation Unit @ offset %s:' %
                self._format_hex(cu.cu_offset))
            self._emitline('   Length:        %s (%s)' % (
                self._format_hex(cu['unit_length']),
                '%s-bit' % cu.dwarf_format()))
            self._emitline('   Version:       %s' % cu['version']),
            self._emitline('   Abbrev Offset: %s' % (
                self._format_hex(cu['debug_abbrev_offset']))),
            self._emitline('   Pointer Size:  %s' % cu['address_size'])

            # The nesting depth of each DIE within the tree of DIEs must be
            # displayed. To implement this, a counter is incremented each time
            # the current DIE has children, and decremented when a null die is
            # encountered. Due to the way the DIE tree is serialized, this will
            # correctly reflect the nesting depth
            #
            die_depth = 0
            for die in cu.iter_DIEs():
                self._emitline(' <%s><%x>: Abbrev Number: %s%s' % (
                    die_depth,
                    die.offset,
                    die.abbrev_code,
                    (' (%s)' % die.tag) if not die.is_null() else ''))
                if die.is_null():
                    die_depth -= 1
                    continue

                for attr in itervalues(die.attributes):
                    name = attr.name
                    # Unknown attribute values are passed-through as integers
                    if isinstance(name, int):
                        name = 'Unknown AT value: %x' % name
                    self._emitline('    <%2x>   %-18s: %s' % (
                        attr.offset,
                        name,
                        describe_attr_value(
                            attr, die, section_offset)))

                if die.has_children:
                    die_depth += 1

        self._emitline()

    def _dump_debug_line_programs(self):
        """ Dump the (decoded) line programs from .debug_line
            The programs are dumped in the order of the CUs they belong to.
        """
        self._emitline('Decoded dump of debug contents of section .debug_line:\n')

        for cu in self._dwarfinfo.iter_CUs():
            lineprogram = self._dwarfinfo.line_program_for_CU(cu)

            cu_filename = bytes2str(lineprogram['file_entry'][0].name)
            if len(lineprogram['include_directory']) > 0:
                dir_index = lineprogram['file_entry'][0].dir_index
                if dir_index > 0:
                    dir = lineprogram['include_directory'][dir_index - 1]
                else:
                    dir = b'.'
                cu_filename = '%s/%s' % (bytes2str(dir), cu_filename)

            self._emitline('CU: %s:' % cu_filename)
            self._emitline('File name                            Line number    Starting address')

            # Print each state's file, line and address information. For some
            # instructions other output is needed to be compatible with
            # readelf.
            for entry in lineprogram.get_entries():
                state = entry.state
                if state is None:
                    # Special handling for commands that don't set a new state
                    if entry.command == DW_LNS_set_file:
                        file_entry = lineprogram['file_entry'][entry.args[0] - 1]
                        if file_entry.dir_index == 0:
                            # current directory
                            self._emitline('\n./%s:[++]' % (
                                bytes2str(file_entry.name)))
                        else:
                            self._emitline('\n%s/%s:' % (
                                bytes2str(lineprogram['include_directory'][file_entry.dir_index - 1]),
                                bytes2str(file_entry.name)))
                    elif entry.command == DW_LNE_define_file:
                        self._emitline('%s:' % (
                            bytes2str(lineprogram['include_directory'][entry.args[0].dir_index])))
                elif not state.end_sequence:
                    # readelf doesn't print the state after end_sequence
                    # instructions. I think it's a bug but to be compatible
                    # I don't print them too.
                    self._emitline('%-35s  %11d  %18s' % (
                        bytes2str(lineprogram['file_entry'][state.file - 1].name),
                        state.line,
                        '0' if state.address == 0 else
                               self._format_hex(state.address)))
                if entry.command == DW_LNS_copy:
                    # Another readelf oddity...
                    self._emitline()

    def _dump_debug_frames(self):
        """ Dump the raw frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return
        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE' % (
                    entry.offset, entry['length'], entry['CIE_id']))
                self._emitline('  Version:               %d' % entry['version'])
                self._emitline('  Augmentation:          "%s"' % bytes2str(entry['augmentation']))
                self._emitline('  Code alignment factor: %u' % entry['code_alignment_factor'])
                self._emitline('  Data alignment factor: %d' % entry['data_alignment_factor'])
                self._emitline('  Return address column: %d' % entry['return_address_register'])
                self._emitline()
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))

            self._emit(describe_CFI_instructions(entry))
        self._emitline()

    def _dump_debug_frames_interp(self):
        """ Dump the interpreted (decoded) frame information from .debug_frame
        """
        if not self._dwarfinfo.has_CFI():
            return

        self._emitline('Contents of the .debug_frame section:')

        for entry in self._dwarfinfo.CFI_entries():
            if isinstance(entry, CIE):
                self._emitline('\n%08x %08x %08x CIE "%s" cf=%d df=%d ra=%d' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_id'],
                    bytes2str(entry['augmentation']),
                    entry['code_alignment_factor'],
                    entry['data_alignment_factor'],
                    entry['return_address_register']))
                ra_regnum = entry['return_address_register']
            else: # FDE
                self._emitline('\n%08x %08x %08x FDE cie=%08x pc=%08x..%08x' % (
                    entry.offset,
                    entry['length'],
                    entry['CIE_pointer'],
                    entry.cie.offset,
                    entry['initial_location'],
                    entry['initial_location'] + entry['address_range']))
                ra_regnum = entry.cie['return_address_register']

            # Print the heading row for the decoded table
            self._emit('   LOC')
            self._emit('  ' if entry.structs.address_size == 4 else '          ')
            self._emit(' CFA      ')

            # Decode the table nad look at the registers it describes.
            # We build reg_order here to match readelf's order. In particular,
            # registers are sorted by their number, and the register matching
            # ra_regnum is always listed last with a special heading.
            decoded_table = entry.get_decoded()
            reg_order = sorted(ifilter(
                lambda r: r != ra_regnum,
                decoded_table.reg_order))

            # Headings for the registers
            for regnum in reg_order:
                self._emit('%-6s' % describe_reg_name(regnum))
            self._emitline('ra      ')

            # Now include ra_regnum in reg_order to print its values similarly
            # to the other registers.
            reg_order.append(ra_regnum)
            for line in decoded_table.table:
                self._emit(self._format_hex(
                    line['pc'], fullhex=True, lead0x=False))
                self._emit(' %-9s' % describe_CFI_CFA_rule(line['cfa']))

                for regnum in reg_order:
                    if regnum in line:
                        s = describe_CFI_register_rule(line[regnum])
                    else:
                        s = 'u'
                    self._emit('%-6s' % s)
                self._emitline()
        self._emitline()

    def _emit(self, s=''):
        """ Emit an object to output
        """
        self.output.write(str(s))

    def _emitline(self, s=''):
        """ Emit an object to output, followed by a newline
        """
        self.output.write(str(s) + '\n')
Ejemplo n.º 45
0
class Elf(object):
    def __init__(self, fileobj):
        self.elffile = ELFFile(fileobj)
        self.output = sys.stdout

    # our code starts here :-)

    def network(self):
        ret = "None"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                # first match IP_PATTERNS
                for pattern in IP_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        return "network-ip"
                # then match LOCAL_PATTERNS
                for pattern in LOCAL_PATTERNS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        ret = "network-local"
                        break
        return ret

    def _strings(self):
        stream = self.elffile.stream
        epos = stream.tell()
        stream.seek(0, 0)
        data = stream.read()
        stream.seek(epos, 0)

        ret = []

        # XXX avoid calling eu-strings
        import subprocess
        p = subprocess.Popen("eu-strings",
                             shell=True,
                             stdin=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             stdout=subprocess.PIPE)
        out = p.communicate(input=data)[0]

        for line in out.splitlines():
            if re.match("^/tmp/.+", line) and "XXX" not in line:
                ret.append(line)

        return ret

    def tempstuff(self):
        tmp_strings = self._strings()

        # if there are no /tmp references, just return
        if len(tmp_strings) == 0:
            return "None"

        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in TMP_FUNCTIONS:
                    if re.match(pattern, bytes2str(symbol.name)):

                        return "None"

        return "$".join(tmp_strings)

    # XXX implement this
    def chroot_without_chdir(self):
        """
        This functions looks for apps that use chroot(2) without using chdir(2).
        Inspired by http://people.redhat.com/sgrubb/security/find-chroot
        """
        pass

    def fortify(self):
        """ NA : FORTIFY_SOURCE was not applicable
            Enabled : unsafe and _chk functions were found
            Disabled : only unsafe functions were found
                       (_chk functions missing)"""
        ret = "NA"
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                for pattern in UNSAFE_FUNCTIONS:
                    if re.match(pattern, bytes2str(symbol.name)):
                        if ret == "NA":
                            ret = "Disabled"
                            break

            if ret == "Disabled":
                # afename = "__" + bytes2str(symbol.name) + "_chk"
                for _, symbol in enumerate(section.iter_symbols()):
                    # first look for corresponding _chk symbol
                    symbolstr = bytes2str(symbol.name)
                    if (symbolstr.startswith("__") and
                            symbolstr.endswith("_chk")) or \
                            symbolstr.endswith(" __chk_fail"):
                        ret = "Enabled"
                        break
        return ret

    def canary(self):
        for section in self.elffile.iter_sections():
            if not isinstance(section, SymbolTableSection):
                continue
            if section['sh_entsize'] == 0:
                print ("\nSymbol table '%s' has a sh_entsize " \
                    "of zero!" % (bytes2str(section.name)), file=sys.stderr)
                continue
            for _, symbol in enumerate(section.iter_symbols()):
                if re.match(STACK_CHK, bytes2str(symbol.name)):
                    return "Enabled"
        return "Disabled"

    def dynamic_tags(self, key="DT_RPATH"):
        for section in self.elffile.iter_sections():
            if not isinstance(section, DynamicSection):
                continue
            for tag in section.iter_tags():
                if tag.entry.d_tag == key:
                    return "Enabled"
        return "Disabled"

    def program_headers(self):
        pflags = P_FLAGS()
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        for segment in self.elffile.iter_segments():
            if re.search("GNU_STACK", segment['p_type']):
                if segment['p_flags'] & pflags.PF_X:
                    return "Disabled"
        return "Enabled"

    def relro(self):
        if self.elffile.num_segments() == 0:
            # print('There are no program headers in this file.', \
            #      file=sys.stderr)
            return

        have_relro = False
        for segment in self.elffile.iter_segments():
            if re.search("GNU_RELRO", segment['p_type']):
                have_relro = True
                break
        if self.dynamic_tags("DT_BIND_NOW") == "Enabled" and have_relro:
            return "Enabled"
        if have_relro:
            return "Partial"

        return "Disabled"

    def pie(self):
        header = self.elffile.header
        if self.dynamic_tags("EXEC") == "Enabled":
            return "Disabled"
        if "ET_DYN" in header['e_type']:
            if self.dynamic_tags("DT_DEBUG") == "Enabled":
                return "Enabled"
            else:
                return "DSO"
        return "Disabled"

    def getdeps(self):
        deps = []

        if self.elffile.num_segments() == 0:
            return deps

        for segment in self.elffile.iter_segments():
            if re.search("PT_DYNAMIC", segment['p_type']):
                # this file uses dynamic linking, so read the dynamic section
                # and find DT_SONAME tag
                for section in self.elffile.iter_sections():
                    if not isinstance(section, DynamicSection):
                        continue
                    for tag in section.iter_tags():
                        if tag.entry.d_tag == 'DT_NEEDED':
                            deps.append(bytes2str(tag.needed))
                break

        return deps
Ejemplo n.º 46
0
def main():
    """Main function"""
    global args
    parse_args()

    if not os.path.exists(args.kernel):
        error("{0} does not exist.".format(args.kernel))

    elf_fd = open(args.kernel, "rb")

    # Create a modifiable byte stream
    raw_elf = elf_fd.read()
    output = create_string_buffer(raw_elf)

    elf = ELFFile(elf_fd)

    if not elf.has_dwarf_info():
        error("ELF file has no DWARF information")

    if elf.num_segments() == 0:
        error("ELF file has no program header table")

    syms = extract_all_symbols_from_elf(elf)

    vm_base = syms["CONFIG_KERNEL_VM_BASE"]
    vm_size = syms["CONFIG_KERNEL_VM_SIZE"]

    sram_base = syms["CONFIG_SRAM_BASE_ADDRESS"]
    sram_size = syms["CONFIG_SRAM_SIZE"] * 1024

    vm_offset = syms["CONFIG_KERNEL_VM_OFFSET"]
    sram_offset = syms.get("CONFIG_SRAM_OFFSET", 0)

    #
    # Calculate virtual-to-physical address translation
    #
    virt_to_phys_offset = (sram_base + sram_offset) - (vm_base + vm_offset)

    log("Virtual address space: 0x%x - 0x%x size 0x%x (offset 0x%x)" %
        (vm_base, vm_base + vm_size, vm_size, vm_offset))
    log("Physical address space: 0x%x - 0x%x size 0x%x (offset 0x%x)" %
        (sram_base, sram_base + sram_size, sram_size, sram_offset))

    #
    # Update the entry address in header
    #
    if elf.elfclass == 32:
        load_entry_type = "I"
    else:
        load_entry_type = "Q"

    entry_virt = struct.unpack_from(load_entry_type, output,
                                    ELF_HDR_ENTRY_OFFSET)[0]
    entry_phys = entry_virt + virt_to_phys_offset
    struct.pack_into(load_entry_type, output, ELF_HDR_ENTRY_OFFSET, entry_phys)

    log("Entry Address: 0x%x -> 0x%x" % (entry_virt, entry_phys))

    #
    # Update load address in program header segments
    #

    # Program header segment offset from beginning of file
    ph_off = elf.header['e_phoff']

    # ph_seg_type: segment type and other fields before virtual address
    # ph_seg_addr: virtual and phyiscal addresses
    # ph_seg_whole: whole segment
    if elf.elfclass == 32:
        ph_seg_type = "II"
        ph_seg_addr = "II"
        ph_seg_whole = "IIIIIIII"
    else:
        ph_seg_type = "IIQ"
        ph_seg_addr = "QQ"
        ph_seg_whole = "IIQQQQQQ"

    # Go through all segments
    for ph_idx in range(elf.num_segments()):
        seg_off = ph_off + struct.calcsize(ph_seg_whole) * ph_idx

        seg_type, _ = struct.unpack_from(ph_seg_type, output, seg_off)

        # Only process LOAD segments
        if seg_type != 0x01:
            continue

        # Add offset to get to the addresses
        addr_off = seg_off + struct.calcsize(ph_seg_type)

        # Grab virtual and physical addresses
        seg_vaddr, seg_paddr = struct.unpack_from(ph_seg_addr, output,
                                                  addr_off)

        # Apply virt-to-phys offset so it will load into
        # physical address
        seg_paddr_new = seg_vaddr + virt_to_phys_offset

        log("Segment %d: physical address 0x%x -> 0x%x" %
            (ph_idx, seg_paddr, seg_paddr_new))

        # Put the addresses back
        struct.pack_into(ph_seg_addr, output, addr_off, seg_vaddr,
                         seg_paddr_new)

    out_fd = open(args.output, "wb")
    out_fd.write(output)
    out_fd.close()

    elf_fd.close()