oneself是什么意思 elf是什么意思( 五 )


仅仅去掉符号感觉还不够 , 熟悉汇编的人放到反编译工具中还是可以慢慢还原程序逻辑 。通过前面的分析我们知道 , ELF执行需要的只是Program Header中的几个段 , Section Header实际上是不需要的 , 只不过在运行时动态链接过程会引用到部分关联的区域 。大部分反编译工具 , 如IDA、Ghidra等 , 处理ELF是需要某些section信息来构建程序视图的 , 所以我们可以通过构造一个损坏Section Table或者ELF Header令这些反编译工具出错 , 从而干扰逆向人员 。
当然 , 这个 *** 并不总是奏效 , 逆向人员可以通过动态调试把程序dump出来并对运行视图进行还原 。一个典型的例子是Android中的JNI动态库 , 有的安全人员对这些so文件进行了加密处理 , 并且在 .init/.initarray 这些动态库初始化函数中进行动态解密 。破解这种加固 *** 的策略就是将其从内存中复制出来并进行重建 , 重建的过程可根据segment对section进行还原 , 因为segment和section之间共享了许多内存空间 , 例如:
$ readelf -l main1... Section to Segment mapping:Segment Sections...0001.interp02.interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame03.init_array .fini_array .dynamic .got .got.plt .data .bss04.dynamic05.note.ABI-tag .note.gnu.build-id06.eh_frame_hdr0708.init_array .fini_array .dynamic .got在 Section to Segment mapping 中可以看到这些段的内容是跟对应section的内容重叠的 , 虽然一个segment可能对应多个section , 但是可以根据内存的读写属性、内存特征以及对应段的一般顺序进行区分 。
如果程序中有比较详细的日志函数 , 我们还可以通过反编译工具的脚本拓展去修改 .symtab/.strtab 段来批量还原ELF文件的符号 , 从而高效地辅助动态调试 。
Binary Fuzzing考虑这么一种场景 , 我们在分析某个IoT设备时发现了一个定制的ELF *** 程序 , 类似于httpd , 其中有个静态函数负责处理输入数据 。现在想要单独对这个函数进行fuzz应该怎么做?直接从 *** 请求中进行变异是一种 ***  , 但是 *** 请求的效率太低 , 而且触达该函数的程序逻辑也可能太长 。
既然我们已经了解了ELF , 那就可以有更好的办法将该函数抽取出来进行独立调用 。在介绍ELF类型的时候其实有提到 , 可执行文件可以有两种类型 , 即可执行类型( ET_EXEC )和共享对象( ET_DYN ) , 一个动态链接的可执行程序默认是共享对象类型的:
$ gcc hello.c -o hello$ readelf -h hello | grep TypeType:DYN (Shared object file)而动态库(.so)本身也是共享对象类型 , 他们之间的本质区别在于前者链接了libc并且定义了main函数 。对于动态库 , 我们可以通过 dlopen/dlsym 获取对应的符号进行调用 , 因此对于上面的场景 , 一个解决方式就是修改目标ELF文件 , 并且将对应的静态函数导出添加到dynamic section中 , 并修复对应的ELF头 。
这个思想其实很早就已经有人实现了 , 比如lief的 bin2lib。通过该 ***  , 我们就能将目标程序任意的函数抽取出来执行 , 比如hugsy就用这个方式复现了Exim中的溢出漏洞(CVE-2018-6789) , 详见 Fuzzing arbitrary functions in ELF binaries ( 中文翻译 ) 。
总结本文主要介绍了32位环境下ELF文件的格式和布局 , 然后从内核空间和用户空间两个方向分析了ELF程序的加载过程 , 最后列举了几个依赖于ELF文件特性的案例进行具体分析 , 包括dynamic linker的滥用、程序加固和反加固以及在二进制fuzzing中的应用 。
ELF文件本身并不复杂 , 只有三个关键部分 , 只不过在section和segment的类型上保留了极大的拓展性 。操作系统可以根据自己的需求在不同字段上实现和拓展自己的功能 , 比如Linux中通过dymamic类型实现动态加载 。但这不是必须的 , 例如在Android中就通过ELF格式封装了特有的 .odex 、 .oat 文件来保存优化后的dex 。另外对于64位环境 , 大部分字段含义都是类似的 , 只是字段大小稍有变化(Elf32->Elf64) , 并不影响文中的结论 。
【oneself是什么意思elf是什么意思】作者:PansLabyrinth

推荐阅读