kernel_samsung_a53x/scripts/crypto/ELF.py
2024-06-15 16:02:09 -03:00

676 lines
29 KiB
Python
Executable file

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Module ELF contains ELF, Symbol, Section classes for manipulation over ELF files.
It can parse, and change ELF file. This version works only with vmlinux and doesn't properly work with ELF that contains
UND symbols
"""
import subprocess
import re
import os
import sys
import struct
from collections import OrderedDict
from binascii import unhexlify
from Utils import Utils
from math import ceil
__author__ = "Vadym Stupakov"
__copyright__ = "Copyright (c) 2017 Samsung Electronics"
__credits__ = ["Vadym Stupakov"]
__version__ = "1.0"
__maintainer__ = "Vadym Stupakov"
__email__ = "v.stupakov@samsung.com"
__status__ = "Production"
DEFAULT_NAME_JUMP_TABLE_START_SYM = "__start___jump_table"
DEFAULT_NAME_JUMP_TABLE_END_SYM = "__stop___jump_table"
DEFAULT_ARM_INST_WIDTH = 4
class Sec_Jumptable_Data:
target_sec_idx = -1
target_offset = -1
code = -1
key = -1
class Symbol:
def __init__(self, name=str(), sym_type=str(), bind=str(), visibility=str(), addr=int(), size=int(), ndx=str()):
self.utils = Utils()
self.name = str(name)
self.type = str(sym_type)
self.bind = str(bind)
self.ndx = str(ndx)
self.visibility = str(visibility)
self.addr = self.utils.to_int(addr)
self.size = self.utils.to_int(size)
def __str__(self):
return "name: '{}', type: '{}', bind: '{}', ndx: '{}', visibility: '{}', address: '{}', size: '{}'".format(
self.name, self.type, self.bind, self.ndx, self.visibility, hex(self.addr), hex(self.size)
)
def __lt__(self, other):
return self.addr <= other.addr
class Section:
def __init__(self, name=str(), sec_type=str(), addr=int(), offset=int(), size=int()):
self.utils = Utils()
self.name = str(name)
self.type = str(sec_type)
self.addr = self.utils.to_int(addr)
self.offset = self.utils.to_int(offset)
self.size = self.utils.to_int(size)
def __str__(self):
return "name: '{}', type: '{}', address: '{}', offset: '{}', size: '{}'".format(
self.name, self.type, hex(self.addr), hex(self.offset), hex(self.size)
)
def __lt__(self, other):
return self.addr <= other.addr
class ELF:
"""
Utils for manipulating over ELF
"""
def __init__(self, elf_file, first_obj_file):
self.__elf_file = elf_file
self.utils = Utils()
self.__readelf_path = None
self.__obj_parser_tool = None
self.__parsers_elf_list = ["llvm-readelf", "readelf"]
self.__parsers_obj_list = ["llvm-nm", "nm"]
self.__sections = OrderedDict()
self.__symbols = OrderedDict()
self.__symbols_list_text = None
self.__symbols_list_rodata = None
self.__symbols_list_init_data = None
self.__relocs_text = None
self.__relocs_rodata = None
self.__re_hexadecimal = "\s*[0-9A-Fa-f]+\s*"
self.__re_sec_name = "\s*[._a-zA-Z]+\s*"
self.__re_type = "\s*[A-Z]+\s*"
self.__altinstr_text = None
self.__altinstr_rodata = None
self.__readelf_path, self.__obj_parser_tool = self.select_parser_tools(elf_file, first_obj_file)
self.jumptable_struct_format = '<iiQ'
self.__jt_rec = []
def get_raw_by_tool(self, tool_name, options):
"""
Execute tool_name with options and print raw output
:param tool_name: name of applied tool
:param options: options of applied tool: ["opt1", "opt2", "opt3", ..., "optN"]
:returns raw output
"""
ret = subprocess.Popen(args=[tool_name] + options,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = ret.communicate()
err_msg = stderr.decode("utf-8").strip()
if ret.returncode != 0 and ("error" in err_msg or "Error" in err_msg):
raise ChildProcessError(stderr.decode("utf-8"))
return stdout.decode("utf-8")
def check_tool_on_error(self, tool_name, options):
"""
Execute tool_name with options and print raw output
:param tool_name: name of applied tool
:param options: options of applied tool: ["opt1", "opt2", "opt3", ..., "optN"]
:returns raw output
"""
ret = subprocess.Popen(args=[tool_name] + options,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
_, _ = ret.communicate()
if ret.returncode != 0:
return False
return True
def check_is_parser(self, parser):
try:
ret = subprocess.Popen(args=[parser] + ["--help"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
_, _ = ret.communicate()
except FileNotFoundError:
return False
return True
def get_elf_file(self):
return os.path.abspath(self.__elf_file)
def get_sections(self):
""""
Execute -> parse -> transform to dict() readelf output
:returns dict: {sec_addr : Section()}
"""
if len(self.__sections) == 0:
sec_header = self.get_raw_by_tool(self.__readelf_path, ["-SW", self.__elf_file]).strip()
secs = re.compile("^.*\[.*\](" + self.__re_sec_name + self.__re_type + self.__re_hexadecimal +
self.__re_hexadecimal + self.__re_hexadecimal + ")", re.MULTILINE)
found = secs.findall(sec_header)
for line in found:
line = line.split()
if len(line) == 5:
self.__sections[int(line[2], 16)] = Section(name=line[0], sec_type=line[1], addr=int(line[2], 16),
offset=int(line[3], 16), size=int(line[4], 16))
self.__sections = OrderedDict(sorted(self.__sections.items()))
return self.__sections
def find_str_in_text(self, req_str, text):
for line in text.splitlines():
if req_str in line:
return True
return False
def select_parser_tools(self, elf_file, first_obj_file):
"""
Select parser tool for output ELF file
If ELF file has section "relr.dyn" than "llvm-readelf" parser will be to employed
otherwise "readelf"
"llvm-nm" is used to parse object files
:param elf_file: name of output ELF file
:return "output ELF parser", "object files parser"
"""
ret_tool_readelf = None
ret_tool_nm = None
for parser in self.__parsers_elf_list:
if self.check_is_parser(parser):
raw_sections = self.get_raw_by_tool(parser, ["-SW", elf_file]).strip()
if self.find_str_in_text(" .relr.dyn ", raw_sections) and parser == "llvm-readelf":
ret_tool_readelf = parser
break
if not self.find_str_in_text(" .relr.dyn ", raw_sections) and parser == "readelf":
ret_tool_readelf = parser
break
for parser in self.__parsers_obj_list:
if self.check_is_parser(parser):
abs_path_file = os.path.abspath(first_obj_file)
if self.check_tool_on_error(parser, ["--defined-only", abs_path_file]):
ret_tool_nm = parser
break
if ret_tool_readelf is None:
print("\nERROR: Neither required ELF parsers {} is found\n".format(", ".join(self.__parsers_elf_list)))
if ret_tool_nm is None:
print("\nERROR: Neither required OBJs parsers {} is found\n".format(", ".join(self.__parsers_obj_list)))
if ret_tool_readelf is None or ret_tool_nm is None:
sys.exit(-1)
print("Used parsers is: ", ret_tool_readelf, ret_tool_nm)
return ret_tool_readelf, ret_tool_nm
def get_rodata_text_scope(self):
raw_sections = self.get_raw_by_tool(self.__readelf_path, ["-SW", self.__elf_file]).strip()
section_rodata = list()
section_text = list()
for line in raw_sections.splitlines():
line_list = list(line.split())
i = 0
len_list = len(line_list)
while i < len_list:
if "." not in line_list[i]:
del line_list[i]
len_list = len(line_list)
else:
break
if len(line_list) >= 6:
if line_list[0].strip().startswith('.rodata'):
if int(line_list[4].strip(), 16) != 0:
section_rodata.append([line_list[2].strip(), line_list[4].strip()])
elif line_list[0].strip().startswith('.text') or line_list[0].strip() == ".init.text":
section_text.append([line_list[2].strip(), line_list[4].strip()])
return section_text, section_rodata
def get_list_symbols_from_file(self, path_to_files, file_name):
"""
Extract from object file the symbols from section .text and all .data sections
:param path_to_files: path to object files
:param file_name: name of parsing object file
:return: symbols_text, symbols_rodata
"""
not_allowed_syms = ["__UNIQUE_ID_", "__kstrtab_", "__ksym_marker_", "__ksymtab_", "__exitcall_", "__initcall_", "$x", "$d"]
abs_path_file = os.path.abspath(os.path.join(path_to_files, file_name))
raw_syms_output = self.get_raw_by_tool(self.__obj_parser_tool,["--defined-only", abs_path_file])
symbols_text = list()
symbols_rodata = list()
for line in raw_syms_output.splitlines():
line_split = line.split()
if len(line_split) == 3:
skip_symbol = False
if line_split[1] in ("D", "d", "T", "t", "R", "r"):
for l_sort in not_allowed_syms:
if line_split[2].startswith(str(l_sort)):
skip_symbol = True
break
if not skip_symbol:
if line_split[1] in ("T", "t"):
symbols_text.append([line_split[1], line_split[2]])
elif line_split[1] in ("D", "d", "R", "r"):
symbols_rodata.append([line_split[1], line_split[2]])
return symbols_text, symbols_rodata
def get_symbols_from_obj_files(self, path_to_files, list_files):
"""
Forming list with candidates to canister
"""
text_obj_symbols = list()
rodata_obj_symbols = list()
for l_file in list_files:
if os.path.isfile(str(path_to_files + "/" + l_file)):
file_obj_text, file_obj_data = self.get_list_symbols_from_file(path_to_files, l_file)
text_obj_symbols.extend(file_obj_text)
rodata_obj_symbols.extend(file_obj_data)
else:
print("\nSKC file ", l_file, "is not found")
return text_obj_symbols, rodata_obj_symbols
def filtered_addr_by_section(self, addr, section_gap):
for l_addr in section_gap:
start_addr = self.utils.to_int(l_addr[0])
end_addr = start_addr + self.utils.to_int(l_addr[1])
if self.utils.to_int(addr) >= start_addr and self.utils.to_int(addr) < end_addr:
return True
return False
def get_single_symbol_raw(self, name: str) -> Symbol:
sym_tab = self.get_raw_by_tool(self.__readelf_path, ["-sW", self.__elf_file])
syms = re.compile(r"^.*\d+:\s(.*$)", re.MULTILINE)
found = syms.findall(sym_tab.strip())
for line in found:
line = line.split()
if len(line) == 7:
size = line[1]
# This needs, because readelf prints sizes in hex if size is large
if size[:2].upper() == "0X":
size = int(size, 16)
else:
size = int(size, 10)
addr_symbol=self.utils.to_int(line[0])
one_symbol = Symbol(addr=int(line[0], 16), size=size, sym_type=line[2],
bind=line[3], visibility=line[4], ndx=line[5],
name=line[6])
if one_symbol.name == name:
return one_symbol
return None
def get_elf_symbols_list(self):
""""
Execute -> parse -> transform readelf symbols output into lists [symbols in .text, .init.text]
and [symbols in .rodata]
:returns lists: [symbols in .text, .init.text], [symbols in .rodata], [symbols in .init.data]
"""
if self.__symbols_list_text is None or self.__symbols_list_rodata is None:
self.__symbols_list_text = list()
self.__symbols_list_rodata = list()
self.__symbols_list_init_data = list()
section_text, section_rodata = self.get_rodata_text_scope()
section_obj_init_data = self.get_section_by_name(".init.data")
sym_tab = self.get_raw_by_tool(self.__readelf_path, ["-sW", self.__elf_file])
syms = re.compile(r"^.*\d+:\s(.*$)", re.MULTILINE)
found = syms.findall(sym_tab.strip())
for line in found:
line = line.split()
if len(line) == 7:
size = line[1]
# This needs, because readelf prints sizes in hex if size is large
if size[:2].upper() == "0X":
size = int(size, 16)
else:
size = int(size, 10)
addr_symbol=self.utils.to_int(line[0])
one_symbol = Symbol(addr=int(line[0], 16), size=size, sym_type=line[2],
bind=line[3], visibility=line[4], ndx=line[5],
name=line[6])
if not line[6].startswith('$') and (size != 0) and ".cfi_jt" not in line[6]:
if self.filtered_addr_by_section(addr_symbol, section_text):
self.__symbols_list_text.append(one_symbol)
elif self.filtered_addr_by_section(addr_symbol, section_rodata):
self.__symbols_list_rodata.append(one_symbol)
else:
if section_obj_init_data is not None:
if self.filtered_addr_by_section(addr_symbol, \
[[section_obj_init_data.addr, section_obj_init_data.size]]):
self.__symbols_list_init_data.append(one_symbol)
return self.__symbols_list_text, self.__symbols_list_rodata, self.__symbols_list_init_data
def get_text_symbols(self):
list_text, _, _ = self.get_elf_symbols_list()
return list_text
def get_rodata_symbols(self):
_, list_rodata, _ = self.get_elf_symbols_list()
return list_rodata
def get_init_data_symbols(self):
_, _, list_init_data = self.get_elf_symbols_list()
return list_init_data
def get_symbols(self):
""""
Execute -> parse -> transform the to dict() readelf output
:returns dict: {sym_addr : Symbol()}
"""
if len(self.__symbols) == 0:
list_text, list_rodata, _ = self.get_elf_symbols_list()
for l_symbol in list_text:
self.__symbols[l_symbol.addr] = l_symbol
for l_symbol in list_rodata:
self.__symbols[l_symbol.addr] = l_symbol
self.__symbols = OrderedDict(sorted(self.__symbols.items()))
return self.__symbols
def get_relocs_text_rodata(self):
"""
returns list: [reloc_text1, reloc_text2, ..., reloc_textN], [reloc_rodata1, reloc_rodata2, ..., reloc_rodataN]
"""
if self.__relocs_text is None or self.__relocs_rodata is None:
self.__relocs_text = list()
self.__relocs_rodata = list()
relocs = self.get_raw_by_tool(self.__readelf_path, ["-rW", self.__elf_file])
rel = re.compile(r"^(" + self.__re_hexadecimal + ")\s*", re.MULTILINE)
section_text, section_rodata = self.get_rodata_text_scope()
for el in rel.findall(relocs.strip()):
rel_addr = self.utils.to_int(el)
if self.filtered_addr_by_section(rel_addr, section_rodata):
self.__relocs_rodata.append(rel_addr)
elif self.filtered_addr_by_section(rel_addr, section_text):
self.__relocs_text.append(rel_addr)
self.__relocs_text.sort()
self.__relocs_rodata.sort()
return self.__relocs_text, self.__relocs_rodata
def get_relocs_for_symbol(self, relocs_list, start_addr=None, end_addr=None):
""""
:param relocs_list: input relocation list
:param start_addr: start address :int
:param end_addr: end address: int
:returns list: [reloc1, reloc2, reloc3, ..., relocN]
"""
ranged_rela = list()
if start_addr and end_addr is not None:
for el in relocs_list:
if self.utils.to_int(end_addr) <= el:
break
if self.utils.to_int(start_addr) <= self.utils.to_int(el) < self.utils.to_int(end_addr):
ranged_rela.append(el)
return ranged_rela
def get_text_rodata_altinstructions_lists(self):
"""
:returns list: [[text_alt_inst1_addr, length1], [text_alt_inst2_addr, length2], ...], [[rodata_alt_inst1_addr, length1], [rodata_alt_inst2_addr, length2], ...]
.altinstructions section contains an array of struct alt_instr.
As instance, for kernel 4.14 from /arch/arm64/include/asm/alternative.h
struct alt_instr {
s32 orig_offset; /* offset to original instruction */
s32 alt_offset; /* offset to replacement instruction */
u16 cpufeature; /* cpufeature bit set for replacement */
u8 orig_len; /* size of original instruction(s) */
u8 alt_len; /* size of new instruction(s), <= orig_len */
};
Later, address of original instruction can be calculated as
at runtime : &(alt_instr->orig_offset) + alt_instr->orig_offset + kernel offset
ELF processing : address of .altinstruction section + in section offset of alt_instr structure + value of alt_instr.orig_offset
details in /arch/arm64/kernel/alternative.c, void __apply_alternatives(void *, bool)
"""
# The struct_format should reflect <struct alt_instr> content
struct_format = '<iiHBB'
pattern_altinst_section_content = "^ *0x[0-9A-Fa-f]{16} (.*) .*.{16}$"
pattern_altinstr_section_addr = "^ *(0x[0-9A-Fa-f]{16}).*.*.{16}$"
if self.__altinstr_text is not None:
return self.__altinstr_text, self.__altinstr_rodata
self.__altinstr_text = list()
self.__altinstr_rodata = list()
__hex_dump = self.get_raw_by_tool(self.__readelf_path, ["--hex-dump=.altinstructions", self.__elf_file])
if len(__hex_dump) == 0:
return self.__altinstr_text, self.__altinstr_rodata
# .altinstruction section start addr in ELF
__altinstr_section_addr = int(re.findall(pattern_altinstr_section_addr, __hex_dump, re.MULTILINE)[0], 16)
# To provide .altinstruction section content using host readelf only
# some magic with string parcing is needed
hex_dump_list = re.findall(pattern_altinst_section_content, __hex_dump, re.MULTILINE)
__hex_dump_str = ''.join(hex_dump_list).replace(" ", "")
__altinstr_section_bin = unhexlify(__hex_dump_str)
__struct_size = struct.calcsize(struct_format)
if (len(__altinstr_section_bin) % __struct_size) != 0:
return self.__altinstr_text, self.__altinstr_rodata
section_text, section_rodata = self.get_rodata_text_scope()
if len(section_text) !=0 or len(section_rodata) !=0:
__i = 0
while __i < (len(__altinstr_section_bin) - __struct_size):
__struct_byte = __altinstr_section_bin[__i: __i + __struct_size]
__struct_value = list(struct.unpack(struct_format, __struct_byte))
# original instruction addr (going to be replaced) considered as "gap"
__original_instruction_addr = __struct_value[0] + __altinstr_section_addr + __i
# derive the target ARM instruction(s) length.
__target_instruction_len = __struct_value[4]
if self.filtered_addr_by_section( __original_instruction_addr, section_text):
self.__altinstr_text.append([__original_instruction_addr, __target_instruction_len])
elif self.filtered_addr_by_section(__original_instruction_addr, section_rodata):
self.__altinstr_rodata.append([__original_instruction_addr, __target_instruction_len])
__i = __i + __struct_size
self.__altinstr_text.sort()
self.__altinstr_rodata.sort()
return self.__altinstr_text, self.__altinstr_rodata
def add_addrs_space_to_list(self, addr_list, addr_start, addr_end):
for addr in range(addr_start, addr_end):
addr_list.append(addr)
def get_altinstructions(self, alt_instr_list, start_addr=None, end_addr=None):
"""
:param start_addr: start address :int
:param end_addr: end address: int
:param alt_instr_list: list alternative instractions
:returns list: [[alt_inst1_addr, length1], [alt_inst2_addr, length2], ...]
"""
ranged_altinst = list()
if len(alt_instr_list) == 0:
return ranged_altinst
if start_addr is not None and end_addr is not None:
start_addr_int = self.utils.to_int(start_addr)
end_addr_int = self.utils.to_int(end_addr)
for l_instr in alt_instr_list:
l_instr_addr_end = l_instr[0] + l_instr[1]
if end_addr_int <= l_instr[0]:
break
if start_addr_int <= l_instr[0] < end_addr_int and l_instr_addr_end < end_addr_int:
self.add_addrs_space_to_list(ranged_altinst, l_instr[0], l_instr_addr_end)
elif start_addr_int <= l_instr[0] < end_addr_int and l_instr_addr_end >= end_addr_int:
self.add_addrs_space_to_list(ranged_altinst, l_instr[0], end_addr_int)
elif start_addr_int > l_instr[0] and l_instr_addr_end < end_addr_int:
self.add_addrs_space_to_list(ranged_altinst, start_addr_int, l_instr_addr_end)
elif start_addr_int > l_instr[0] and l_instr_addr_end > end_addr_int:
self.add_addrs_space_to_list(ranged_altinst, start_addr_int, end_addr_int)
return ranged_altinst
def get_jump_table_list(self) -> list:
"""
:param start_addr: seek start address :int
:param end_addr: seek end address: int
:param alt_instr_list: list of instruction addrs modified in frame of jump_lables patch
:returns list: [[inst1_addr, length1], [inst2_addr, length2], ...]
"""
jump_table_start_sym = self.get_single_symbol_raw(DEFAULT_NAME_JUMP_TABLE_START_SYM)
jump_table_end_sym = self.get_single_symbol_raw(DEFAULT_NAME_JUMP_TABLE_END_SYM)
if jump_table_start_sym == None or jump_table_end_sym == None:
return []
__jumptable_struct_size = struct.calcsize(self.jumptable_struct_format)
jump_table_content = self.get_data_by_vaddr(jump_table_start_sym.addr,
jump_table_end_sym.addr - jump_table_start_sym.addr)
for i in range(ceil((jump_table_end_sym.addr - jump_table_start_sym.addr)/__jumptable_struct_size)):
__jtr = Sec_Jumptable_Data()
__begin = i * __jumptable_struct_size
__end = __begin + __jumptable_struct_size
[ __jtr.code,
__jtr.target_offset,
__jtr.key ] = list(struct.unpack(self.jumptable_struct_format,
jump_table_content[__begin: __end]))
__jtr.code = __jtr.code + jump_table_start_sym.addr + i * __jumptable_struct_size
__jtr.target_offset = __jtr.target_offset + jump_table_start_sym.addr + i * __jumptable_struct_size
self.__jt_rec.append(__jtr)
return self.__jt_rec
def get_jump_table_module(self, start_addr: int, end_addr: int, jump_table: list) -> list:
"""
Return JT related gaps are in range of our module
:param start_addr: int
:param end_addr: int
:param jump_table: list full list (over whole kernel) of JT items
:returns list of addrs to be excluded [exclude_addr1, exclude_addr2, ...]
"""
result_jt_gaps = list()
for jt_item in jump_table:
if start_addr <= jt_item.code and end_addr > jt_item.code:
for __addr in range(jt_item.code, jt_item.code + DEFAULT_ARM_INST_WIDTH):
result_jt_gaps.append(__addr)
return result_jt_gaps
def get_symbol_by_name_text(self, sym_name: str) -> Symbol:
"""
Get symbol by_name in section .rodata
:param sym_name: name of symbol
:return: Symbol()
"""
for symbol_obj in self.get_text_symbols():
if symbol_obj.name == sym_name:
return symbol_obj
return None
def get_symbol_by_name_rodata(self, sym_name: str):
"""
Get symbol by_name in section .rodata
:param sym_name: name of symbol
:return: Symbol()
"""
for symbol_obj in self.get_rodata_symbols():
if symbol_obj.name == sym_name:
return symbol_obj
return None
def get_symbol_by_name_init_data(self, sym_name: str):
"""
Get symbol by_name in section .init.data
:param sym_name: name of symbol
:return: Symbol()
"""
for symbol_obj in self.get_init_data_symbols():
if symbol_obj.name == sym_name:
return symbol_obj
return None
def get_symbol_by_vaddr(self, vaddrs=None):
"""
Get symbol by virtual address
:param vaddrs: vaddr : int or list
:return: Symbol() or [Symbol()]
"""
if isinstance(vaddrs, int):
if vaddrs in self.get_symbols():
return self.get_symbols()[vaddrs]
for addr, symbol_obj in self.get_symbols().items():
if (addr + symbol_obj.size) >= vaddrs >= addr:
return symbol_obj
elif isinstance(vaddrs, list):
symbol = [self.get_symbol_by_vaddr(vaddr) for vaddr in vaddrs]
return symbol
else:
raise ValueError
return None
def get_section_by_name(self, sec_names=None):
"""
Get section by_name
:param sec_names: "sec_name" : str or list
:return: Section() or [Section()]
"""
if isinstance(sec_names, str):
for _, section_obj in self.get_sections().items():
if section_obj.name == sec_names:
return section_obj
elif isinstance(sec_names, list):
sections = [self.get_section_by_name(sec_name) for sec_name in sec_names]
return sections
else:
raise ValueError
return None
def get_section_by_vaddr(self, vaddrs=None):
"""
Get section by virtual address
:param vaddrs: vaddr : int or list
:return: Section() or [Section()]
"""
if isinstance(vaddrs, int):
if vaddrs in self.get_sections():
return self.get_sections()[vaddrs]
for addr, section_obj in self.get_sections().items():
if (addr + section_obj.size) >= vaddrs >= addr:
return section_obj
elif isinstance(vaddrs, list):
sections = [self.get_symbol_by_vaddr(vaddr) for vaddr in vaddrs]
return sections
else:
raise ValueError
return None
def vaddr_to_file_offset(self, vaddrs):
"""
Transform virtual address to file offset
:param vaddrs: addr string or int or list
:returns file offset or list
"""
if isinstance(vaddrs, str) or isinstance(vaddrs, int):
section = self.get_section_by_vaddr(vaddrs)
return self.utils.to_int(vaddrs, 16) - section.addr + section.offset
elif isinstance(vaddrs, list):
return [self.vaddr_to_file_offset(vaddr) for vaddr in vaddrs]
else:
raise ValueError
def read_data_from_vaddr(self, vaddr, size, out_file):
with open(self.__elf_file, "rb") as elf_fp:
elf_fp.seek(self.vaddr_to_file_offset(vaddr))
with open(out_file, "wb") as out_fp:
out_fp.write(elf_fp.read(size))
def get_data_by_vaddr(self, vaddr, size) -> bytearray:
with open(self.__elf_file, "rb") as elf_fp:
elf_fp.seek(self.vaddr_to_file_offset(vaddr))
outbuff = elf_fp.read(size)
return outbuff