Page 329 - 《软件学报》2021年第10期
P. 329

陈兴蜀  等:VMOffset:虚拟机自省中一种语义重构改进方法                                                3301


                 真实进程总数一致.基于此,制定其约束条件如下:
                                                       Num     Num  ,                    
                           TF can id     tf                real   tf                               (5)
                                     Num real    Count fork    Count exit , Num   tf  Traverselinklist (Con Addr   (  ts  tf  ))  
                    公式(5)中,Num real 为 TVM 中真实进程数目,通过 VMM 拦截的 do_fork()的调用次数 Count fork 及 release_
                 task()的调用次数 Count exit 得到.将 Con(Addr ts +tf)视为一个进程链表节点,依次遍历即可得到该节点所在链表的
                 成员总数 Num tf .基于公式(5)筛选 tf 的过程中需注意:
                    (1)  Linux 内核的第 1 个进程 swapper 由内核自身由无到有创建,其使用静态分配的数据结构,不通过调用
                        内核函数 do_fork()得到.该进程作为 Linux 内核的祖先进程,负责创建 Linux 中的其他进程完成内核
                        的初始化;
                    (2)  当 VMM 中拦截到 do_fork()及 release_task()的调用事件时,函数的具体内容尚未执行,因此当前时刻,
                        TVM 中进程的数目并未改变;待函数执行完毕后,TVM 中进程总数才会增加或者减少.如 VMM 中第
                        1 次拦截到 do_fork()时,可知 TVM 中当前进程为 swapper 进程,且该时刻 TVM 中仅有 1 个进程,即
                        Num real 为 1.
                    2)  成员 pid.
                    pid 成员作为进程的标识,其约束条件可用公式(6)表示:
                                               Con  (Addr   i  pf  )   Con  (Addr   j  pf  ),    
                           PF candi     pf     i  i    ts        j    ts                           (6)
                                    0≤  Con i (Addr   ts  pf  )   PidLimit ,PidLimit   32768, i j    ,  Prolist ,i   j    
                 其中,Prolist 为 TVM 中进程链表,i、j 分别为链表中某个进程.公式(6)表示对进程链表中的任意不同进程,其 pid
                 的值都是不同的,且 pid 均处于 0~PidLimit 的范围内.Linux 系统一般从 0 开始,依次连续分配 pid 直到可分配的
                 pid 上限,之后则重新从某个值开始依次连续查找未被使用的 pid 进行分配.默认情况下,不同系统的 pid 上限是
                 不同的,且可被修改.但是由于内核初始化阶段分配的 pid 均较小,且部分系统的 pid 上限默认为 32 768,因此
                 VMOffset 将 PidLimit 设为 32 768.基于此,对 pf 进行筛选.
                    3)  成员 comm.
                    comm 成员用于描述进程名称.如前所述,Linux 内核静态初始化其第 1 个进程的 task_struct 结构体内容,其
                 中,进程名通过宏定义为“swapper”,可通过匹配“swapper”字符串筛选 comm 的偏移量候选项.但若内核源码被修
                 改,“swapper”被改为其他任意字符串,则此种匹配方式得到的偏移量将是错误的,进而会导致自省结果发生错
                 误.因此,VMOffset 基于其自身属性制定约束条件.如公式(7)所示:成员 comm 表示进程名称,故其每个字符的
                 ASCII 码均在可显示字符的 ASCII 码范围内,且该字符数组的内容不可为空,并以字符‘\0’结尾:
                                                Con  (Addr   cf  )[ ] j   ShowCharSet ,    
                                                          i
                           CF      f c             i    ts                                         (7)
                             can di          i
                                    Con i (Addr   ts  cf  [ ) end ]'\ 0', i      Pr olist , j     [0,comm nle  ),commlen   0    
                    公式(7)所描述的约束条件不会因内核源码的修改而发生变化,VMOffset 基于此筛选 cf.
                    4)  成员 mm.
                    mm 成员用于描述进程虚拟地址空间信息.由内核源码可知:一般而言,mm 成员的下一个成员即为 active_
                 mm.而 swapper 进程的 active_mm 成员由内核静态初始化为 init_mm 的地址,该地址可由 TVM 内核符号表得到,
                 可基于此获取 mm 的偏移量.但是为了保证内核源码被修改后所得偏移量的正确性,VMOffset 不以成员位置关
                 系为依据,而是基于成员自身属性进行约束条件的制定.成员 mm 的特点如公式(8)所示:Linux中,内核线程的 mm
                 成员为 0,用户进程 mm 成员的值则与 active_mm 成员的内容一致:
                                    |
                                        (
                          MF candi   {mf Con Addr   ts i  mf  )   0 || Con Addr   ts i  mf  ) Con  i  (Addr   ts i  amf  ), i    Prolist }  (8)
                                                         (
                                       i
                                                         i
                    公式(8)中,amf 为成员 active_mm 的偏移量,Prolist 的含义同公式(6).
                    5)  成员 pgd.
                    mm_struct 结构体中成员 pgd 用于存储进程的页目录基地址,该值对应的物理地址会被载入 CR3 寄存器,
   324   325   326   327   328   329   330   331   332   333   334