«上一篇
文章快速检索     高级检索
下一篇»
  哈尔滨工程大学学报  2017, Vol. 38 Issue (12): 1969-1976  DOI: 10.11990/jheu.201607055
0

引用本文  

王丹, 陈嘉, 赵文兵, 等. 基于虚拟机的程序运行时监控方法[J]. 哈尔滨工程大学学报, 2017, 38(12): 1969-1976. DOI: 10.11990/jheu.201607055.
WANG Dan, CHEN Jia, ZHAO Wenbing, et al. Virtual machine-based method for runtime monitoring of executing program[J]. Journal of Harbin Engineering University, 2017, 38(12): 1969-1976. DOI: 10.11990/jheu.201607055.

基金项目

北京市自然科学基金项目(4173072);信息网络安全公安部重点实验室开放课题

通信作者

林九川, E-mail:linjiuchuan@stars.org.cn

作者简介

王丹(1969-), 女, 教授, 博士导师;
林九川(1980-), 男, 副研究员, 博士

文章历史

收稿日期:2016-07-21
网络出版日期:2017-10-25
基于虚拟机的程序运行时监控方法
王丹1, 陈嘉1, 赵文兵1, 林九川2    
1. 北京工业大学 信息学部, 北京 100124;
2. 公安部第三研究所 信息网络安全公安部重点实验室, 上海 201204
摘要:为实现在系统层面对程序运行时行为的监控,本文设计了基于虚拟机的程序运行时动态监控框架。利用事件驱动机制,借助虚拟机翻译程序的原理,选取特定事件作为被关注事件进行注册,从虚拟环境中提取CPU状态进行分析,从而获得相关程序动态运行信息。以基于控制流的可疑程序分析作为应用实例,描述了具体实现过程。测试结果表明,该框架能够在系统层进行有效的行为监控,方便获取操作系统内核状态和进程的信息,为程序的动态行为分析提供了有利的支持。
关键词运行时监控    动态二进制分析    虚拟机    事件    翻译    控制流    
Virtual machine-based method for runtime monitoring of executing program
WANG Dan1, CHEN Jia1, ZHAO Wenbing1, LIN Jiuchuan2    
1. College of Computer Science, Beijing University of Technology, Beijing 100124, China;
2. Information and Network Safety Department, The Third Research Institute of Ministry of Public Security, Shanghai 201204, China
Abstract: To provide runtime monitoring for executing programs at system level, a dynamic monitoring framework based on virtual machine was designed and implemented. By utilizing an event-driven mechanism based on the theory of a translation program for virtual machines, this study selected a specific event as the target for registration, and the CPU state was obtained for analysis to obtain dynamic running information on the tested program. This paper describes the structure of the dynamic monitoring framework, analyzes the working principle, and introduces the process of acquiring monitoring information. The analysis of suspicious programs based on control flow technique was used as an example to describe the entire process. The test results show that this method is effective in conducting comprehensive monitoring. Furthermore, this method facilitates obtaining the kernel status of the operating system and process information to support the analysis of the dynamic behavior of the executing program.
Key words: runtime monitoring    dynamic binary analysis    virtual machine    event    translation    control flow    

为对程序运行时行为进行监控和分析,通常是在运行时通过插桩、跟踪调试等程序动态分析技术来完成。动态二进制插桩技术[1](dynamic binary instrument, DBI),是在不改变程序原有逻辑的基础上,将一些探针插入到程序中,通过执行探针得到程序运行的特征数据,再分析这些特征数据完成对程序的分析[2-3]。DynamoRIO[4]、Valgrind[5]等是常用的动态二进制插桩工具,它们具有较完善的结构,但均是面向进程的,工作在用户层,依靠动态插桩技术实现的,导致其难以实现对驱动、多进程、管道通信等的分析检测,因此并不适合做与系统运行相关的内核分析。

跟踪调试技术使用调试器来加载程序,通过跟踪程序运行时调用的函数及执行的指令等来获取相关信息。常用的跟踪方法有两种:一种是使用HOOK技术监视程序运行调用的函数并进行分析;二是以较长的时间代价单步跟踪程序的执行过程并进行分析。但是,使用插桩技术会破坏程序运行现场和数据的完整性,也容易被恶意木马反调试技术察觉和规避。

将程序置于虚拟机或虚拟机模拟器环境中运行以获取其运行过程中的信息是另一种常用的程序运行时监控方法,且该方法不受多态、变形和加密及程序中的间接跳转和间接调用指令的影响。目前常用的虚拟机模拟分析工具有TEMU[6]、DECAF(dynamic executable code analysis framework)等,它们都基于QEMU平台。QEMU是一个通用的开源模拟器和虚拟机。作为虚拟机使用时,QEMU可以在不同体系结构的机器上运行同一个虚拟环境,安装操作系统并运行程序,QEMU提供了多种接口供开发人员使用,采用动态翻译技术,将虚拟环境中的目标程序翻译成宿主机器语言,先对基本块进行翻译,然后再执行并加以缓存,后续可直接使用缓存的代码,无需重复翻译。TEMU是基于QEMU开发的全系统的动态二进制分析平台,它定义了一组API,用于获取和设置内存及CPU寄存器的值,并增加了语义提取模块和污点分析模块,为用户开发相关插件提供了接口。DECAF是在相关研究人员改进TEMU不足的基础上生成的一个新的二进制框架,具有高效、污点信息分析完整等特性。DECAF也为开发人员提供了许多回调接口,通过这些回调接口开发人员可以开发出用于分析Guest操作系统执行过程的强有力的插件。由于QEMU是面向系统的和基于事件驱动的,工作在系统层,能够较为全面地对程序执行流程实施监控,避免插桩技术易被反调试的弊端。因此,本文基于虚拟机模拟技术的软件行为监控系统,从系统视角获取操作系统内核状态和多个进程的信息并进行分析。设计并实现了基于QEMU并配置DECAF的动态监控平台,被测程序在QEMU虚拟环境中运行,通过DECAF从外部监视整个模拟器的运行情况,获取系统级信息。

1 监控框架及关键技术 1.1 监控框架

图 1是总体的监控系统架构。首先在宿主机器上安装QEMU并配置DECAF平台,然后在全系统模拟器QEMU上安装Windows XP操作系统,再在虚拟环境的操作系统中执行被监控对象(如图 1中所示的PE文件)。DECAF从外部监视整个模拟器的运行情况,获取QEMU系统级语义信息。

图 1 基于虚拟机的监控系统架构 Fig.1 Monitoring framework based on virtual machine

图 2所示,被测程序在QEMU虚拟环境中运行时,QEMU通过微指令生成器将多种不同结构的程序代码,如x86上的Windows、x86上的Linux、MIPS上的Linux等,翻译为统一的中间语言。然后,QEMU再根据宿主系统不同的结构,将中间语言翻译为宿主操作系统可识别的机器码,进而运行程序。这一系列操作对用户来说都是透明的。与此同时,DECAF在QEMU翻译的基础之上,获取用户分析所需的相关信息,如当前进程名称、程序计数器、当前系统环境等信息并进行处理。另外,通过事件处理机制,可处理用户注册的感兴趣的事件。当DECAF检测到用户注册的事件在模拟系统中发生时,会触发DECAF调用用户注册的响应函数,获取当前系统环境数据和被监控程序的信息并进行处理。

图 2 平台对照 Fig.2 Platform contrast
1.2 动态二进制翻译

基于虚拟机的动态二进制分析技术,首先会对程序进行动态二进制翻译,然后再去执行翻译的结果。这一功能主要依赖它的动态代码生成器(TCG)。TCG采用动态的翻译方式,主要负责分析并优化目标代码,然后将其翻译为主机代码,遵循从目标代码、到TCG代码、再到主机可识别代码这样的流程。具体翻译过程如下:首先,将每一条目标代码指令切分为若干个单元操作,每个单元操作由一段简单的C语言代码实现,作为一个翻译块(TB)。TB是QEMU指令翻译的基本单位,对应目标代码的一条指令。在程序运行时,利用动态代码生成器将以上单元组合操作,以函数形式展现。系统调用这个函数,就相当于执行了一条目标代码的指令。在翻译过程中会涉及很多事件,如块开始执行/结束事件、内存读写、被修改的内存位置读写等。这些事件会在事件驱动中触发函数调用。通过QEMU,目标代码以翻译块为单位翻译并生成中间代码后放入翻译缓存中,执行过程如图 3所示。

图 3 翻译过程 Fig.3 Translation process
1.3 事件驱动

DECAF提供了简单易用的事件驱动程序接口,当监测到被注册的事件发生时,它会调用事先定义的相应的事件处理函数。利用这一接口,分析人员根据自己的需求选择注册事件,并在事件发生时,进行相应信息的提取和处理工作。因为要在事件发生时进行实时信息获取,即注册的事件在QEMU中发生后,会触发所对应的回调函数,所以QEMU暂停客户操作系统的运行,将DECAF的回调函数作为辅助函数插入到QEMU中,然后恢复QEMU的运行,这时回调函数就会被运行,如图 4所示。

图 4 回调函数触发过程 Fig.4 Trigger process of callback function

当“翻译块开始/结束”,“读取/写入被污染的内存位置”这样的事件发生时,相应的回调函数就会开始工作。DECAF通过接口将事件处理函数与事件相关联。接收到事件的信号后,DECAF会在某个翻译块中插入一个辅助函数,使客户操作系统暂停运行,将辅助函数中的指令两次翻译到翻译块中,并覆盖原有指令,然后恢复客户系统的运行。为这些事件注册的回调函数就会利用这些数据开始分析处理工作。因为回调函数的触发和客户系统的执行是内联的,所以当事件发生时,回调函数的触发和客户系统的执行也会同时发生。

//开始翻译块

//触发DECAF_BLOCK_BEGIN回调

movi_i32 tmp21,$ < CURRENT_ADDRESS >

movi_i32 tmp22,$DECAF_invoke_block_begin_callback

call   tmp22,$0x0,$0,env,tmp21

//初始化指令: orl %ebx,%eax

//触发DECAF_INSN_BEGIN回调

movi_i32 tmp23,$DECAF_invoke_insn_begin_callback

call   tmp23,$0x0,$0,env

mov_i32 tmp11,ebx

mov_i32 tmp12,eax

or_i32  tmp13,tmp12,tmp11

//触发DECAF_INSN_END回调

movi_i32 tmp24,$DECAF_invoke_insn_end_callback

call   tmp24,$0x0,$0,env

//初始化指令: addl $0x01,%eax

// Insert DECAF_INSN_BEGIN callback

movi_i32 tmp25,$DECAF_invoke_insn_begin_callback

call   tmp25,$0x0,$0,env

movi_i32 tmp14,$0x01

add_i32 tmp15,tmp14,tmp13

mov_i32 eax,tmp15

//触发DECAF_INSN_END回调

movi_i32 tmp26,$DECAF_invoke_insn_end_callback

call   tmp26,$0x0,$0,env

//翻译块结束

//触发DECAF_BLOCK_END回调

movi_i32 tmp27,$DECAF_invoke_block_end_callback

call   tmp27,$0x0,$0,env

goto_tb $0x0

上面的代码展示了辅助函数DECAF_invoke_insn_begin_callback和DECAF_invoke_insn_end_callback的使用,即在相应的事件发生时,这两个函数会被插入到客户指令的开头和结尾。

这里需要说明:事件对应的回调函数调度机制的设计,即对于每一个事件,例如翻译块开始,只能插入一个辅助函数,而不能在一个事件发生时,同时调用多个函数对数据进行处理,且在辅助函数内,会遍历为该事件注册的所有回调函数,并且决定触发哪个。该设计有两个理由:1)各种插件和平台自身对于同一个事件会注册很多回调函数,重复调用这些辅助函数会对系统的性能产生负面影响。而上面设计的调度机制可以避免辅助函数的内联重复调用这一问题。2)在全系统仿真模拟器中,插入到代码流中的回调函数会在整个客户操作系统环境中执行,例如,指令码被插入到一个共享库中,就会被所有使用这个库的程序执行。所以需要调度机制来确保当前的执行环境是否对每一个注册的回调函数都是正确的,之后才可以决定执行。

2 实例分析

目前的基于软件行为的可疑软件的分析和监控可通过静态分析或者动态学习,建立软件的控制流行为模型来实现,如统调用模型和函数调用模型[7-9]。控制流行为度量模型已被证明可以有效地检测对于控制数据的攻击[10-11],如恶意代码注入攻击、返回库函数攻击等常见的攻击类型。在此背景下,本文从控制流的角度出发,通过使用本文的监控框架对程序的可疑性进行分析判断。以图 5为例,这是一个简单的判断输入信息是否为数字的程序。当程序正常运行时,即用户从终端输入数字的情况下,将susData赋值给memData,但是当用户输入的信息不是数字时,即视为程序异常,将会弹出对话框并对用户进行提示。

图 5 程序正常运行路径 Fig.5 Normal runtime path

//判断输入是否为数字

1) if(susData < ‘0’ || susData > ‘9’)

2) MessageBox(NULL,TEXT(“WRONG”),TEXT(“NOT A NUMBER”),MB_OK);

3) else

4) memData=susData;

5) cout 《susData;

正常的运行流程的执行顺序应为1→3→4→5,但是当程序异常时,执行顺序则为1→2→5,造成了程序的行为异常。这是由于在节点1的控制信息不同引发的。因此,可以通过监测程序的控制流信息,了解到程序是否在预期的轨迹上正常运行,并以此为依据,判断程序的行为是否可疑。

2.1 基于标志寄存器的控制流信息搜集

在执行条件跳转指令时,汇编语言通过判断标志位寄存器的情况来判断是否进行跳转。如汇编语言的JE指令在执行的时候,系统会查询标志寄存器中的ZF标志位的内容。如果ZF=1,符合跳转条件,则程序计数器会跳转到目标地址;如果ZF=0,则说明跳转条件不成立,程序计数器将会指向紧接着的下一条指令地址,顺序执行。通过获取在每次跳转时的标志位寄存器信息,加以处理整合,就可以得到程序运行过程中的控制流信息,并确定跳转的目的地址。这里不考虑CALL指令和JMP指令这两个跳转指令,因为跳转的目的地址在程序编写时已经确定,不受控制流信息影响。

2.2 事件驱动与回调函数的设计

基于事件驱动机制,本文设计了回调函数来收集控制流信息。由于QEMU中每一个翻译块对应进程的一条指令,因此为了获取条件跳转指令,对代码块执行结束事件(VM_BLOCK_END_CB)进行注册。在事件发生时,调用回调函数,该函数根据DECAF提供的用户接口,对数据进行分析处理,完成控制流信息收集工作。

在回调函数中,需要获取翻译执行结束时的系统状态、翻译块的信息、当前程序计数器及下一条指令的程序计数器值。DECAF提供了DECAF_Block_End_Params数据结构,且将对这些变量的赋值过程进行了封装,可直接使用。

typedef struct _DECAF_Block_End_Params

{

CPUState* env;         //CPU环境

TranslationBlock* tb; //翻译块

gva_t cur_pc;         //当前PE值

gva_t next_pc;         //下一个PC值

} DECAF_Block_End_Params;

本文首先通过当前的PC值获取当前指令,然后通过操作码判断指令类型。对于指令JE/JZ (ZF=1),其操作码为01111110。可以通过操作码判断当前指令是否为条件跳转指令,如果是,继续进行下一步处理。由于DECAF通过QEMU提供的接口获取CPU状态,并将CPU信息存储在结构体CPUState中,所以本文将该结构体中的eflag的信息提取出来,并保存在记录文件中。

在回调函数中,首先要初始化插件,然后获取要检测的程序名称和保存结果的记录文件名称。根据事件驱动原理,当有新的程序被调用的时候,判断是否为被测程序,如果是,则开始信息获取工作,再根据指令的操作码判断当前指令是否为条件跳转指令。如果否,则获取虚拟机当前的操作系统状态,并且保存在记录文件中。然后,继续按照此判断和记录原则监控和跟踪程序的动态运行。当程序结束时,注销翻译块结束事件发生时要调用的相关函数,最后清理插件资源。

3 测试平台部署与运行 3.1 测试环境构建

本文选用虚拟机QEMU,可以直接使用如下命令进行QEMU安装。

$ sudo apt-get install qemu sudo apt-get build-dep qemu

对于DECAF,需要安装BFD库和boost库,命令如下:

$ sudo apt-get install binutils-dev

$ sudo apt-get install libboost-all-dev

在准备好库后,开始进行配置和编译工作。DECAF有三个基本的功能设置:TCG污点传播、VMI和TCG IR日志。因本文只涉及到虚拟机自省技术(virtual machine instropection, VMI),所以只启用这一功能。然后进行编译,QEMU就可以运行了,指令如下:

$ sudo config make install

完成QEMU的安装之后,就可以开始在其上安装操作系统镜像,创建所需使用的虚拟机。虽然QEMU本身已经几乎和任何运行x86体系结构的客户操作系统兼容,然而DECAF需要更多关于操作系统的信息,进行语义翻译,提供更多诸如进程的操作系统抽象信息,所以从打开虚拟机开始,使用DECAF控制QEMU运行。

镜像准备好之后,可以加载QCOW格式的镜像到QEMU中,完成虚拟机的创建。接下来,通过终端启动QEMU并运行虚拟机,QEMU窗口就会弹出,可以看到虚拟机启动情况。

3.2 控制流信息获取过程 3.2.1 注册关注事件

根据DECAF的事件驱动机制,用户可选择所关注的事件进行注册,并根据这个事件的发生,获取系统信息,进行相应的处理与分析。首先通过如下命令注册新进程开始事件:handle_load_mainmodule=VMI_register_callback(VMI_CREATEPROC_CB,ProcessTrace_loadmainmodule_notify,&should_monitor);

同样地,在被测程序关闭后,监控工作也应该自动结束,并且对注册的时间关联函数进行注销,实现代码如下:

handle_remov_process=VMI_register_callback(VMI_REMOVEPROC_CB,ProcessTrace_procexit,&should_monitor);

当新的进程开始运行这一事件发生后,所需要进行的操作如下。首先需要判断新开始运行的这个进程是否为被测进程,这可以通过获取QEMU当前系统环境信息实现。具体数据结构设计如下:

typedef struct _VMI_CreateProc_Params {

      uint32_t pid; //新进程PID

      uint32_t cr3; //新进程CR3

      char *name; //新进程名称

}VMI_CreateProc_Params;

将当前程序的名称与被检测的进程名称进行对比,如calc.exe。判断当前程序的名称是否与用户输入的要监控的进程名称一致。如果不一致,继续等待下一个新进程被运行;如果一致,首先在终端输出,告知用户被监控程序已经开始。

程序运行后,系统会为程序分配一个进程PID,并根据当前程序运行情况,将被测程序的PID存入到被测程序的数据结构中。同样,将CR3寄存器的内容进行保存。根据以上需求,被监控程序需要采用如下数据结构来保存相关信息。

struct monitored_proc {

   char name[512]; //被测进程名称

   char configfile[512]; //编译文件

    uint32_t cr3; //被测进程CR3

    uint32_t pid; //被测进程PID

    FILE *tracefile; //记录文件地址

  };

接下来,注册事件发生时要调用的相关函数,DECAF_registerOptimizedBlockEndCallback,具体实现方法如下:

static void

ProcessTrace_loadmainmodule_notify(VMI_Callback_Params *pcp)

{ char *errorstring;

  int i;

  uint32_t pid=pcp->cp.pid; //获取进程的id

  char *name=pcp->cp.name; //获取进程的名称

  if(strlen(mon_proc.name) > 0)

  { //检查目标进程的名称是否有效

      if(strcmp(mon_proc.name,name)==0)

      { //创建的进程名称与目标进程名称一致

        printf("%s is starting!\\n",mon_proc.name);

           //输出目标进程开始执行的信息

   mon_proc.pid=pid; //设置目标进程的id

   mon_proc.cr3=VMI_find_cr3_by_pid_c(pid);

      //设置目标进程的cr3

   / /进行开始执行,注册回调函数

   if (!handle_block_end)

//注册block end事件,开始对进程的控制流进行监控和收集

      handle_block_end=

DECAF_registerOptimizedBlockEndCallback(do_block_end,NULL,INV_ADDR,INV_ADDR);

      }

     }

  }

相应地,在被测程序关闭后,需要注销已经注册的事件。首先,在终端提示用户被监控进程已关闭,监控任务结束后,关闭记录文件;然后清空被监控程序的数据结构中对应的相应数据,最后利用DECAF提供的指令完成事件注销。具体代码如下:

int i=0;

uint32_t pid=pcp->rp.pid; //被删除进程Pid

if(pid==mon_proc.pid) { //如果是被测进程

                        //输出相关信息

   printf("%s is closed.\\n",mon_proc.name);

   printf("%d kernel calls found.\\n",kcount);

       //关闭文件

   fclose(mon_proc.tracefile);

   mon_proc.tracefile=NULL;

   if (handle_block_end) //如果有回调函数

   DECAF_unregisterOptimizedBlockEndCallback(handle_block_end);   //注销

  }

3.2.2 判断获取时机

确定被监测程序之后,可以开始控制流信息的获取。当翻译块执行结束这一事件发生时,可以获取到当前指令内容。首先通过当前系统的环境变量参数来判断系统是否处于内核态,如果是内核态,则说明正在执行内核相关的系统调用指令,不需要进行分析处理,直接返回,等待下一个关注事件发生。DECAF提供了一个回调函数联合体,根据不同的事件,提供不同的参数。联合体的数据结构如下:

typedef struct _DECAF_Callback_Params

{

  DECAF_Handle cbhandle;

  union{

        DECAF_Block_Begin_Params bb;

        DECAF_Block_End_Params be;

        DECAF_Insn_Begin_Params ib;

        DECAF_Insn_End_Params ie;

        DECAF_Mem_Read_Params mr;

        DECAF_Mem_Write_Params mw;

        DECAF_EIP_Check_Params ec;

        DECAF_Keystroke_Params ks;

        DECAF_Nic_Rec_Params nr;

        DECAF_Nic_Send_Params ns;

        DECAF_Opcode_Range_Params op;

        DECAF_Tlb_Exec_Params tx;

        DECAF_Read_Taint_Mem rt;

        DECAF_Write_Taint_Mem wt;

  #ifdef CONFIG_TCG_LLVM

        DECAF_Block_Trans_Params bt;

  #endif /* CONFIG_TCG_LLVM */

        };

} DECAF_Callback_Params;

在这部分,本文关注翻译块执行结束事件,即“DECAF_Block_End_Params”这一类型。这一类型中有四个变量:当前CPU状态、翻译块指针、当前程序PC值和下一个PC值,数据结构如下:

typedef struct _DECAF_Block_End_Params

{

  CPUState* env;   //CPU状态

  TranslationBlock* tb; //翻译块

  gva_t cur_pc;    //当前PC

  gva_t next_pc;    //下一个PC

    } DECAF_Block_End_Params;

通过传入第一个变量env可以利用DECAF的判断函数判断当前系统是否处于内核状态。若系统不处于内核态,则意味着当前指令属于被测进程,通过当前PC值,获取汇编指令对应的指令编码。由于本文主要关注条件跳转指令,通过其获取控制流信息内容,而在Windows_x86系统中,编码第一字节的内容为指令的操作码,可以根据操作码判断当前指令是否为条件跳转指令。

然后,判断当前指令所属的进程是否为监视进程。可通过DECAF提供的宏,获取当前CR3寄存器的值。因为每一个进程都有自己的CR3寄存器内容,所以可以根据这个值进行判断。如果指令不属于被监控程序,则直接返回,等待下一个翻译块执行事件的发生。如果是被监控进程,则继续下面的相关信息的提取工作。

3.2.3 提取相关信息

确定当前指令是属于被监控程序的一条跳转指令后,开始获取当前系统标志位寄存器状态的工作。在x86系统中,有一个32位的标志寄存器。在初始化时,x86的标志寄存器状态为00000002H,第1、3、5、15以及22~31位被系统预留,内容固定,不表示任何具体信息,程序的运行也不会以这些位的内容为依据。标志位寄存器的值不能被整体复制或修改,因为它们都是根据特定指令,由系统进行修改,例如cmp指令,执行后不保留运算结果,只对标志寄存器产生影响,然后通过其他相关指令访问这些被影响的标志寄存器,来产生对整个程序的影响。但是,通过DECAF提供的接口,可以直接将标志寄存器的信息赋值给特定类型的变量,然后将其输出到记录文件中。

本文设计的获取eflags的变量类型定义如下。因为DECAF支持多种体系结构和操作系统的虚拟机,所以根据不同的虚拟机类型,有一套对应机制。如果是32位系统,则将获取eflags的变量类型定位为32位,对于64位的操作系统也进行相应的定义。

#if TARGET_LONG_SIZE==4

        //如果目标代码长4字节,按如下方式进行定义

        //定义long型

typedef int32_t target_long

__attribute__((aligned(TARGET_LONG_ALIGNMENT)));

        //定义无符号long型

typedef uint32_t target_ulong

__attribute__((aligned(TARGET_LONG_ALIGNMENT)));

        //定义lx

#define TARGET_FMT_lx "%08x"

        //定义ld

#define TARGET_FMT_ld "%d"

        //定义lu

#define TARGET_FMT_lu "%u"

#elif TARGET_LONG_SIZE==8

        //如果目标代码长8字节,按如下方式进行定义

typedef int64_t target_long

__attribute__((aligned(TARGET_LONG_ALIGNMENT)));

typedef uint64_t target_ulong

__attribute__((aligned(TARGET_LONG_ALIGNMENT)));

#define TARGET_FMT_lx "%016" /*PRIx64*/

#define TARGET_FMT_ld "%" PRId64

#define TARGET_FMT_lu "%" PRIu64

#else

#error TARGET_LONG_SIZE undefined

#endif

3.3 封装功能

通过以上过程得到了程序动态运行过程中的控制流信息。下面需要对功能进行封装,完成与DECAF平台接口的连接工作和用户交互的实现。

用户使用本项目所提供的功能时,需要在命令行输入如下命令:

trace_process process_name trace_file_name

其中,包含两个参数:进程名和记录文件名称。

在Linux中,通过定义命令配置文件,将实现功能的函数与命令绑定。具体来说,当用户在终端输入trace_process命令时,调用do_trace_process()函数。实现代码如下:

{//命令名称

    .name=“trace_process”,

    //命令类型

    .args_type="proc_name:s,tracefile:F",

    //命令处理函数

    .mhandler.cmd=do_trace_process,

    //记录文件

    .params=“process_name trace_file_name”,

    //使用说明

  .help="Trace by name of the process. The process must not have started yet. The trace is saved to the said file.",

}

  然后,do_trace_process()函数从命令行提取参数,具体实现代码如下:

  //获取进程名称

const char *procname=qdict_get_str(qdict,

      “proc_name”);

    //获取记录文件路径

const char *tracefile_path=qdict_get_str(qdict,“tracefile”);

    存放被监测程序信息的结构体设计如下。

struct monitored_proc {

    char name[512];       //进程名称

    char configfile[512]; //配置文件

    uint32_t cr3;         //CR3寄存器

    uint32_t pid;        //进程ID

    FILE *tracefile; }   //记录文件

首先,命令的参数中,用户输入的被测程序的名字记录到结构体对应的变量中; 然后,根据用户输入的记录文件路径尝试打开文件,如果路径无效或者文件无法打开,则在终端给用户提示,不进行后续工作。该处理可以增强程序的健壮性。如果文件可以成功打开,则将记录文件路径写入结构体,然后等待被测程序运行,并通过终端告知用户程序开始运行。

下面是Linux命令处理模块的部分代码。模块的输入信息为被测程序和记录文件。

int i;

FILE *tracefile;

//新建进程如果是被测进程

if(strcmp(mon_proc.name,procname)==0) {

    printf("Process %s already being traced.\\n",procname);

    return;

}

  //保存被测进程名称

  strncpy(mon_proc.name,procname,512);

  mon_proc.name[511]='\\0';

  //打开记录文件

  tracefile=fopen(tracefile_path,“w”);

  //如果文件路径不合法

  if(tracefile==NULL) {

    printf("Unable to open trace file %s for writing. Check filename.\\n",tracefile_path);

    goto done;

}

//输出表头

fprintf(tracefile,"标志寄存器信息  \\t\\t当前PC值  \\t\\t进程名称\\n");

//保存记录文件

mon_proc.tracefile=tracefile;

//提示用户监视工作已经开始

    printf("Waiting for process: %s (case sensitive) to start.\\n",mon_proc.name);

4 结论

1) 将程序置于虚拟机或虚拟机模拟器环境中运行以获取其运行过程中信息可以不受多态、变形和加密,以及程序中的间接跳转和间接调用指令的影响,方便获取操作系统内核状态和进程的信息,实现平台无关的动态监控。

2) 结合虚拟机、动态翻译、事件驱动等在全系统虚拟环境中对程序进行动态分析,除了可提取用户代码运行时信息以外,还可支持系统运行时相关信息的提取,如进程列表、进程状态等,更好地实现系统层的动态行为监控。

3) 通过选取用户感兴趣的特定事件进行注册,然后对所执行的指令和系统环境信息进行提取,能够为用户开展后续的深度分析提供数据基础。

参考文献
[1]
HONG D Y, WU J J, YEW P C, et al. Efficient and retargetable dynamic binary translation on multicores[J]. IEEE transactions on parallel & distributed system, 2014, 25(3): 622-632. (0)
[2]
DOSTAL M, EICHLER Z. A hybrid approach to user activity instrumentation in software applications[M]. Berlin: Springer Berlin Heidelberg, 2011: 566-570. (0)
[3]
FEINER P, BROWN A D, GOEL A. Comprehensive kernel instrumentation via dynamic binary translation[J]. ACM sigarch computer architecture news, 2012, 40(1): 135-146. DOI:10.1145/2189750 (0)
[4]
王乾, 舒辉, 李洋, 等. 基于DynamoRIO的恶意代码行为分析[J]. 计算机工程, 2011, 37(18): 37-144.
WANG Qian, SHU Hui, LI Yang, et al. Malicious code behavior analysis based on dynamoRIO[J]. Computer engineering, 2011, 37: 37-144. (0)
[5]
唐立斐. 基于检测应用程序的研究与实现[D]. 武汉: 华东理工大学, 2014: 8-10.
PEI Lifei. Analysing OSE application based on Valgrind tools[D]. Wuhan: East China University of Science and Technology, 2014: 8-10. (0)
[6]
FABRICE B. QEMU, a fast and portable dynamic translator[C]//Proceedings of the USENIX 2005 Annual Technical Conference. Anaheim: USENIX Association, 2005: 41-46. (0)
[7]
CHRISTODORESCU M, JHA S, SESHIA S A, et al. Semantics-aware malware detection[J]. Security and privacy, 2005: 32-46. (0)
[8]
付文, 魏博, 赵荣彩, 等. 基于模糊推理的程序恶意性分析模型研究[J]. 通信学报, 2010, 31(1): 44-50.
FU Wen, WEI Bo, ZHAO Rongcai, et al. Fuzzy reasoning model for analysis of program maliciousness[J]. Journal on communications, 2010, 31(1): 44-50. (0)
[9]
张鹏涛, 王维, 谭营. 基于带有惩罚因子的阴性选择算法的恶意程序检测模型[J]. 中国科学:信息科学, 2011, 41(7): 798-812.
ZHANG Pengtao, WANG Wei, TAN Ying. A malware detection model based on a negtive selection algorithm with penalty factor[J]. China science:information science, 2011, 41(7): 798-812. (0)
[10]
MARTIN A, MIHAI B, ULFAR E, et al. Control-flow integrity principles, implementations, and applications[J]. ACM transactions on information and system security, 2009, 13(1): 1-40. (0)
[11]
MIGUEL C, MANUEL C. Securing software by enforcing data-flow integrity[C]//Proceedings of USENIX Symposium on Operating Systems Design and Implementation. Berkeley, USA, 2006: 147-160. (0)