动态修改进程代码段(四)

有矛就有盾,动态修改进程的代码段当然有很多用处,例如用来做热升级以减少宕机时间,提高可用性。但是很多时候,它同样也可以被用来实施攻击。

例如最常见的一种攻击方式是直接修改进程的代码段,将里面的代码修改为攻击者想要的代码,这时候,我们需要提供一种方法来检测到这种攻击,也就是说需要检测到进程代码段的修改。

这种代码段的防护可以基于进程空间内的内存管理来做,首先通过进程数据结构(task_struct)中的进程空间成员(struct mm_struct *mm)使用四级页表结构查找进程内的所有页表,再对页表进行遍历与摘要即可。在此处需要注意的是在Linux操作系统下,部分页可能处于换页状态(paging out),此时对应的页数据为空,读取出来的页数据是错误的,需要进行特殊处理。而一旦生成了每页的数据,即可定时对进程的代码段页面进行扫描,以比较代码段页面摘要是否正确,一旦发现异常即可报警或者终止进程的运行。下面是代码段页面摘要计算的流程描述(需要在内核里做):

  • 获取execve系统调用通知(例如可以通过kprobes或者ftrace
  • 通过进程标识(PID)定位到进程任务结构体(基于内核函数find_vpidpid_task
  • 通过任务结构体task_structmm→start_codemm→end_code确定进程虚拟地址空间代码段的起始地址与结束地址
  • 根据当前系统的页面大小获取地址范围内每个页面的起始地址
  • 是用页面虚拟起始地址通过pgd_offsetpud_offsetpmd_offset进行页面查找,最后使用pte_offset_map定位到实际的页面标识pte_t*
  • 若对应的页面处于换页状态(pte_present返回0),则跳过此页面,并将其标记为暂无页面数据,等待下次重新获取数据,否则利用pte_page内核函数将页面标识转换为页面结构体struct page*
  • 一旦获得了页面结构体,即可调用内核函数kmap将页面结构体里的数据获取出来,继而使用内核的crypto_shash_系列的函数对其进行散列计算,其后使用kunmap释放页面,得到页面数据摘要,并将其保存下来

上述对代码段的动态度量保护技术方案可以达到动态度量保护的目标,但是其仍然有下面几个问题:

  • 需要进行大量的内核开发,包括遍历页面以及对页面进行散列计算
  • 页面可能产生缺页/换页问题,导致一次无法对所有页面进行完整性计算,降低了效率,留下了安全隐患

要解决上述问题,可以将对ELF可执行程序的代码段扫描的实现从内核态转移到用户态,基于对进程地址空间的分析以及/proc/#/maps的分析,可以直接对ELF文件进行分析,获取相应的代码段地址,再对其进行分页数据的散列计算得到相应的页面摘要,从而在用户态完成完整性校验,其流程如下:

  • 基于libelf读取ELF文件的程序头(program header/phdr),获取代码段的文件偏移量与大小
  • 通过sysconf(_SC_PAGE_SIZE)获取页面大小
  • 跳转到代码段偏移量开始以页大小读取数据,直到读取完了所有的代码段数据
  • 每个页面读取的数据都使用散列运算得到摘要,并将其保存下来

使用上述用户态基于ELF文件的分析,可以基于静态文件分析的技术方案获取动态代码段的度量值,避免了内核编程的不稳定性,并且可以解决换页导致的页面数据无法获取的问题,使得整个技术方案更加完善。

发表评论

电子邮件地址不会被公开。 必填项已用*标注