1.首先把android 64位的so拖进010 editor
可以看到这里有4个struct,这些struct怎么去看,这里我们重点关注 elf_header, program_header_table, section_header_table
- elf_header就是elf文件的头文件,里面涵盖了各种信息说明 这里主要解释几个字段的意思,其实你查010 editor
注释里的英文字母也能看懂各个字段的意思
1. 前16个字节是elf标识
2. 2个字节.dynsym表示该节区包含了要动态连接的符号
3.e_mechine表示arm版本
4.e_version表示文件版本
5.表示so的开始地址一般为0x000000
6.program_header_offset,程序头位移 52 段头表
7.section_header_offset,节区位移 节头表
8.elf header size elf头的大小
9.paogram_header_entry_size 每个paramitem的大小
10.number of program_header_entries program的个数
11.section_header_size 节的个数
12.numver_of_section_header_entry_size, 每个节的大小
13.e_shtrndx_string_table_index, shtrndx在setion的index
- 那么program_header和section_header又是干嘛的
这又要说elf的两种视图
1.program视图 段视图, 执行视频
2.section视图 节视图,ida视图,加载so的时候会把节变为段
- 那么节又是怎么把section(节)加载为program(段)的呢
分三步
1.装载
2.分配数据块
3.连接 (重点)
大概理清了他们之间的关系,那么怎么去写一个elf文件的解析
- 首先我们读取elf里的header字段
import mmap # 将一个文件或者其它对象映射到进程的地址空间
import struct
endian_prefix = ""
def is_elf(f):
magic_code = f.read(4).decode()
if magic_code.find("ELF") == -1:
raise ValueError("not a valid elf file")
def get_ei_data(mm):
# 判断大小端
ei_data = struct.unpack("<B", mm.read(1))[0]
# 其他没啥用
endian_prefix = '<' if ei_data == 1 else '>'
return endian_prefix
# elf头
class ElfHeader:
e_type = ""
e_machine = ""
e_version = ""
e_entry = ""
e_phoff = ""
e_shoff = ""
e_flags = ""
e_ehsize = ""
e_phentsize = ""
e_phnum = ""
e_shentsize = ""
e_shnum = ""
e_shtrndx = ""
def read_elf_header(f):
is_elf(f)
global endian_prefix
endian_prefix = get_ei_data(f)
f.seek(16)
ElfHeader.e_type = unpack_data("H", 2, f)
ElfHeader.e_machine = unpack_data("H", 2, f)
ElfHeader.e_version = unpack_data("I", 4, f)
ElfHeader.e_entry = unpack_data("I", 4, f)
ElfHeader.e_phoff = unpack_data("I", 4, f)
ElfHeader.e_shoff = unpack_data("I", 4, f)
ElfHeader.e_flags = unpack_data("I", 4, f)
ElfHeader.e_ehsize = unpack_data("H", 2, f)
ElfHeader.e_phentsize = unpack_data("H", 2, f)
ElfHeader.e_phnum = unpack_data("H", 2, f)
ElfHeader.e_shentsize = unpack_data("H", 2, f)
ElfHeader.e_shnum = unpack_data("H", 2, f)
ElfHeader.e_shtrndx = unpack_data("H", 2, f)
def main(so_path):
with open(so_path, "r+b") as f:
with mmap.mmap(f.fileno(), 0) as mm:
mm = mm
read_elf_header(mm)
- 读取program数据
def read_data(f, off, size):
print("off", off)
f.seek(off)
return b''.join([t[0] for t in struct.iter_unpack('<s', f.read(size))])
def unpack_data(data_type, size, f):
return struct.unpack(endian_prefix + data_type, f.read(size))[0]
def read_program_header_table(f, off, size):
"""
读取程序视图
:param f:
:param off:
:param size:
:return:
"""
f.seek(off)
for _ in range(size):
info = ProgramInfo()
info.p_type = unpack_data("I", 4, f)
info.p_elf_begin = unpack_data("I", 4, f)
info.p_var_addr = unpack_data("I", 4, f)
info.p_p_addr = unpack_data("I", 4, f)
info.p_seg_length = unpack_data("I", 4, f)
info.p_memsz = unpack_data("I", 4, f)
info.p_flag = unpack_data("I", 4, f)
info.p_align = unpack_data("I", 4, f)
current = f.tell()
data = read_data(f, info.p_elf_begin, info.p_seg_length)
info.data = data
f.seek(current)
- 读取节区数据
def read_section_header(f, off, size):
"""
节区视图
:param f:
:param off:
:param size:
:return:
"""
f.seek(off)
for _ in range(size):
info = SectionInfo()
info.s_name = unpack_data("I", 4, f)
info.p_elf_begin = unpack_data("I", 4, f)
info.s_type = unpack_data("I", 4, f)
info.s_flag = unpack_data("I", 4, f)
info.s_addr = unpack_data("I", 4, f)
info.s_size = unpack_data("I", 4, f)
info.s_link = unpack_data("I", 4, f)
info.s_info = unpack_data("I", 4, f)
info.s_addr_align = unpack_data("I", 4, f)
info.s_ent_size = unpack_data("I", 4, f)
current = f.tell()
char_data = read_data(f, info.s_addr, info.s_size)
print("char_data", char_data.decode('utf-8', errors='ignore'))
f.seek(current)
注意
1.字段的大小怎么看,可以根据010editor里的size字段去看
2.需要注意大端小端的数据怎么去取