ELF头部读取失败:BinaryReader
直接读会错位
ELF文件头部前4字节是魔数
\x7fELF,但C#默认用
BinaryReader读
ushort或
uint时,若没设
BitConverter.IsLittleEndian匹配目标平台字节序,就会把32位/64位ELF的
e_ident之后字段全读反。Linux ELF一律小端,Windows x64也是小端,看似没问题——但一旦遇到交叉编译生成的ARM64 ELF(仍是小端),或你误用
ReadUInt32()读64位字段(如
e_entry在64位ELF中占8字节),立刻越界或解析出0。
实操建议:
不用BinaryReader.ReadXXX()自动类型读取,改用
ReadBytes(n)配合
BitConverter.ToUInt32(bytes, 0)手动解包,显式指定偏移 先读
e_ident[4]判断
ELFCLASS32(1)还是
ELFCLASS64(2),再决定后续字段长度和偏移
e_ident[5]是字节序:值为1=小端,2=大端;别硬编码假设小端 头部固定部分长度:32位ELF是52字节,64位ELF是64字节——
e_phoff、
e_shoff等关键偏移都依赖这个基准
节头表(Section Header Table)找不到:e_shoff
为0或e_shnum
为0
很多strip过的ELF(如发行版二进制)会删掉节头表,只留程序头表(Program Header Table)。此时
e_shoff == 0或
e_shnum == 0是合法状态,不是你读错了。想查.text/.data节内容?靠节头表不行,得转向程序头表+内存段映射,或者用
readelf -S确认原始文件是否真有节头。
实操建议:
检查e_shoff > 0 && e_shnum > 0 && e_shentsize > 0三者同时成立,才尝试读节头表 节头表本身不存字符串,节名存在
.shstrtab节里,而
.shstrtab的索引由
e_shstrndx给出——这个字段可能为
SHN_UNDEF(0),意味着无节名字符串表 32位ELF节头单条长40字节,64位是64字节;别用同一结构体硬套两种格式
用System.IO.File.ReadAllBytes
加载大ELF文件卡死
一个500MB的调试版ELF,
File.ReadAllBytes会一次性分配500MB托管内存,GC压力陡增,且后续解析时还得反复
Array.Copy切片——实际只需读头部几十字节和若干节数据,没必要全载入。
实操建议:
用FileStream+
Seek()跳转读,例如:
fs.Seek(e_shoff, SeekOrigin.Begin); fs.Read(buffer, 0, e_shentsize * e_shnum)节内容按需读:拿到
sh_offset和
sh_size后,再
Seek过去读对应块,避免预分配大数组 如果只是校验或提取符号,优先用
objdump -h或
readelf -h做验证,别在C#里重复造轮子
符号表解析失败:.symtab
节存在但st_name
指向乱码
st_name是符号名在字符串表(如
.strtab)里的偏移,不是字符串本身。常见错误是直接把
st_name当ASCII码打印,或者用
Encoding.UTF8.GetString()去解整个
.strtab节——但
.strtab是null分隔的C字符串集合,中间有大量\x00,UTF8解码会截断或报错。
实操建议:
先定位.strtab节内容(通过节头表找到其
sh_offset和
sh_size),再用
Array.IndexOf(bytes, (byte)0, st_name)找下一个\x00位置,然后
Encoding.ASCII.GetString(bytes, st_name, length)注意:有些ELF用
.dynsym代替
.symtab(动态链接库常见),别只盯
.symtab名字
st_info低4位是绑定属性(
STB_GLOBAL等),高4位是类型(
STT_FUNC等),需用
st_info & 0xf和
(st_info >> 4) & 0xf分别提取
ELF格式本身不复杂,但32/64位混用、大小端隐含假设、节表可选、字符串表分离这些设计,会让C#这种强类型语言容易在边界上栽跟头。最稳的方式是:每读一个字段,立刻用
readelf -h或
xxd -l 64核对原始字节,而不是猜结构体布局。
