Source code for apkutils.elf.elfparser
import zipfile
import io
import binascii
from elftools.elf.elffile import ELFFile
from elftools.common.exceptions import ELFError
from elftools.common.py3compat import byte2int
from cigam import Magic
[docs]class ELF():
def __init__(self, file_path):
if Magic(file_path).get_type() != 'elf':
return
self.elf_data = open(file_path, 'rb')
self.elf_file = ELFFile(self.elf_data)
[docs] def close(self):
self.elf_data.close()
[docs] def get_dynsym_datas(self, skip_import=True):
dynsym_datas = []
symbol_table = self.elf_file.get_section_by_name('.dynsym')
for symbol in symbol_table.iter_symbols():
if skip_import and symbol.entry.st_size == 0 or symbol.entry.st_info.type != 'STT_FUNC':
continue
self.elf_data.seek(0)
symbol_addr = symbol.entry.st_value & 0xFFFE
self.elf_data.seek(symbol_addr)
symbol_hexs = ''
size = symbol.entry.st_size
if symbol.entry.st_size > 80:
size = 80
for x in self.elf_data.read(size):
op = str(hex(x)).upper()[2:]
if len(op) == 1:
op = '0' + op
symbol_hexs = symbol_hexs + op
dynsym_datas.append(
(symbol.name, hex(symbol_addr), symbol_hexs))
return dynsym_datas
[docs] def get_rodata_strings(self):
try:
return display_string_dump(self.elf_file, '.rodata')
except ELFError as ex:
print('ELF error: %s\n' % ex)
[docs] 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 = _section_from_spec(self.elf_file, section_spec)
if section is None:
print("Section '%s' does not exist in the file!" % section_spec)
return None
data = section.data()
dataptr = 0
strs = []
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
strs.append(binascii.b2a_hex(
data[dataptr:endptr]).decode().upper())
dataptr = endptr
return strs
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.elf_file.num_sections():
return self.elf_file.get_section(num)
else:
return None
except ValueError:
# Not a number. Must be a name then
return self.elf_file.get_section_by_name(spec)
[docs]def get_elf_files(apk_path):
files = list()
if zipfile.is_zipfile(apk_path):
try:
with zipfile.ZipFile(apk_path, mode="r") as zf:
for name in zf.namelist():
try:
data = zf.read(name)
mime = Magic(data).get_type()
if mime == 'elf':
elf_data = io.BytesIO(data)
elf_file = ELFFile(elf_data)
files.append((name, elf_data, elf_file))
except Exception as ex:
continue
except Exception as ex:
raise ex
return files
[docs]def get_dynsym_datas(elf_data, elf_file, skip_import=True):
"""
获取符号/方法的相关信息(符号名、符号数据)。
"""
f = elf_data
dynsym_datas = []
symbol_table = elf_file.get_section_by_name('.dynsym')
if symbol_table:
for symbol in symbol_table.iter_symbols():
if skip_import and symbol.entry.st_size == 0 or symbol.entry.st_info.type != 'STT_FUNC':
continue
f.seek(0)
symbol_addr = symbol.entry.st_value & 0xFFFE
f.seek(symbol_addr)
symbol_hexs = ''
size = symbol.entry.st_size
if symbol.entry.st_size > 80:
size = 80
for x in f.read(size):
op = str(hex(x)).upper()[2:]
if len(op) == 1:
op = '0' + op
symbol_hexs = symbol_hexs + op
item = {}
item["name"] = symbol.name
item["data"] = symbol_hexs
dynsym_datas.append(item)
return dynsym_datas
[docs]def get_rodata_strings(elf_file):
"""
获取字符串列表,以hex格式表示,避免字符编码问题。
"""
try:
return display_string_dump(elf_file, '.rodata')
except ELFError as ex:
import sys
sys.stderr.write('ELF error: %s\n' % ex)
sys.exit(1)
[docs]def display_string_dump(elf_file, section_spec):
""" Display a strings dump of a section. section_spec is either a
section number or a name.
"""
section = _section_from_spec(elf_file, section_spec)
if section is None:
print("Section '%s' does not exist in the file!" % section_spec)
return None
data = section.data()
dataptr = 0
strs = []
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
strs.append(binascii.b2a_hex(data[dataptr:endptr]).decode().upper())
dataptr = endptr
return strs
def _section_from_spec(elf_file, spec):
'''
Retrieve a section given a "spec" (either number or name).
Return None if no such section exists in the file.
'''
if isinstance(spec, int):
num = int(spec)
if num < elf_file.num_sections():
return elf_file.get_section(num)
# Not a number. Must be a name then
if isinstance(spec, str):
try:
return elf_file.get_section_by_name(spec)
except AttributeError:
return None