AArch64架构下共享库ELF加壳器实现的加载器放置问题
AArch64架构下ELF共享库加壳加载器放置方案
核心思路
共享库没有可执行文件那样的e_entry入口点,但动态链接器加载共享库时会优先执行库内置的初始化回调函数,你可以把加载器挂载到这些初始化回调点上,在共享库任意业务代码执行前完成.text段解密。你之前为可执行文件开发的加载器解密逻辑可以几乎完全复用,只需要调整挂载逻辑和最后的控制权移交逻辑即可。
推荐挂载位置(按兼容性优先级排序)
.init_array段
这是通用性、兼容性最好的挂载点。动态链接器完成共享库加载后,会按顺序执行.init_array段中存储的所有函数指针。
操作方式:将你的加载器入口地址写入
.init_array的首个元素,同时备份原来的.init_array中的所有函数指针,等加载器完成.text段解密后,再按顺序执行这些备份的原初始化函数,保证原有初始化逻辑不丢失。
AArch64适配注意:.init_array中的函数遵循AArch64标准调用约定,入参依次为int argc, char **argv, char **envp,编写加载器汇编代码时不要破坏栈结构和参数寄存器。DT_INIT动态段项
ELF动态段中的DT_INIT标签存储的是共享库的旧版初始化函数入口地址,动态链接器会在执行.init_array前调用该地址的函数。
操作方式:备份原有
DT_INIT指向的函数地址,将DT_INIT的值修改为你的加载器入口地址,加载器执行完解密后再跳转回备份的原初始化函数即可。
注意部分新编译的共享库可能不使用DT_INIT,完全依赖.init_array,该方案兼容性比.init_array稍差。
必要的适配注意事项
- 解密
.text段前,需要先调用mprotect系统调用将.text段所在的页权限修改为PROT_READ | PROT_WRITE,AArch64下要注意页地址必须对齐到系统页大小(通常为4K或64K),否则mprotect会调用失败。 - 解密完成后建议再次调用
mprotect将页权限改回PROT_READ | PROT_EXEC,既可以避免内存破坏漏洞,也符合常规ELF段的权限配置,降低被特征检测的概率。 - 加载器执行完所有逻辑后,必须将控制权交回原有的初始化流程,不能直接跳转至某个导出函数,否则会破坏动态链接器的正常加载逻辑。
内容的提问来源于stack exchange,提问作者prgbenz
相关产品推荐
相关产品推荐

