decaf 接口用法

来源:互联网 发布:三国吴国知乎 编辑:程序博客网 时间:2024/06/06 07:36

最近跟着老师做一个关于decaf的项目,因此对读的文章做点笔记。

  1. 简介
    decaf是一个虚拟机,为开发者提供了许多调用接口,可以在运行时Hook。使用这些接口可以完全在客户机之外检索OS-level semantics(操作系统级别的语义),例如进程、系统API、按键、网络等。
  2. 样例插件
    以下插件可以指定一个你想要追踪的进程,并在进程开始时打印进程名。
    当DECAF加载该插件,它将首先调用init_plugin(void),可以通过plugin_interface_t(DECAF_main.h) 定义插件的行为。但最重要的是指定plugin_cleanup接口,通常用这个接口释放分配给该插件的资源,不这么做DECAF可能会崩。plugin_interface_t 还可以为DECAF定义你自己的命令,以便运行时与插件交互。
#include "DECAF_types.h" #include "DECAF_main.h" #include "DECAF_callback.h" #include "DECAF_callback_common.h" #include "vmi_callback.h" #include "utils/Output.h" #include "DECAF_target.h"//basic stub for plugins static plugin_interface_t my_interface; static DECAF_Handle processbegin_handle = DECAF_NULL_HANDLE;char targetname[512];// 当客户系统中开始一个新进程时callback被调用static void my_loadmainmodule_callback(VMI_Callback_Params params) {     if(strcmp(params->cp.name,targetname)==0)         DECAF_printf("Process %s you spcecified starts \n",params->cp.name); } // 执行monitor_proc命令的处理器 void do_monitor_proc(Monitor* mon, const QDict* qdict) {    //复制被监测进程的名字      if ((qdict != NULL) && (qdict_haskey(qdict, "procname"))) {          strncpy(targetname, qdict_get_str(qdict, "procname"), 512);     }     targetname[511] = '\0'; }static int my_init(void) {    DECAF_printf("Hello World\n");    //为创建进程与移除进程注册    processbegin_handle =VMI_register_callback(VMI_CREATEPROC_CB, &my_loadmainmodule_callback, NULL);    if (processbegin_handle == DECAF_NULL_HANDLE){        DECAF_printf("Could not register for the create or remove proc events\n");      }    return (0);}// 当插件卸载时调用该函数static void my_cleanup(void) {     DECAF_printf("Bye world\n"); // 注销进程的启动与退出的调用     if (processbegin_handle != DECAF_NULL_HANDLE) {         VMI_unregister_callback(VMI_CREATEPROC_CB, processbegin_handle);         processbegin_handle = DECAF_NULL_HANDLE;     }} // 支持插件的命令,在plugin_cmds.h中 static mon_cmd_t my_term_cmds[] = {         {.name = "monitor_proc",         .args_type = "procname:s?",         .params = "[procname]",         .help = "Run the tests with program [procname]" },         {NULL, NULL, }, };//该函数通过DECAF注册插件接口(plugin_interface)。该接口用于注册自定义命令,让DECAF清楚插件卸载后调用哪个卸载函数,等等 plugin_interface_t init_plugin(void) {     my_interface.mon_cmds = my_term_cmds;     my_interface.plugin_cleanup = &my_cleanup;    my_init();    return (&my_interface);} 

在函数init_plugin(void)中,我们用my_term_cmds定义自己的命令。在my_term_cmds,我们指定命令名、命令处理程序,命令参数和帮助信息。plugin_cleanup也由my_cleanup定义。
my_init()中我们注册VMI_CREATEPROC_CB调用以及它的处理器my_loadmainmodule_callback 。故当一个进程开始时,DECAF会调用my_loadmainmodule_callback(),并且从它的参数中可以得到进程PID、名称和CR3。故你可以检查该检查是否是你用monitor_proc命令指定的进程,若是则打印进程名。当然,我们还可以做许多其他事,例如可以在这里注册DECAF_INSN_BEGIN_CB回调与其处理程序。在它的处理程序中,我们检查它是否属于指定的进程,并使用下面的代码打印出指令。

uint32_t target_cr3; static void my_insn_begin_callback(DECAF_Callback_Params* params) {     if(params->ib.env->cr[3]==target_cr3) {        DECAF_printf("EIP 0x%08x \n",params->ib.env->eip);     }} //当客户系统中开始一个新进程时该callback被调用 static void my_loadmainmodule_callback(VMI_Callback_Params params) {     if(strcmp(params->cp.name,targetname)==0){         DECAF_printf("Process %s you spcecified starts \n",params->cp.name);         target_cr3=params->cp.cr3;        DECAF_register_callback(DECAF_INSN_BEGIN_CB, &my_insn_begin_callback,NULL);    } }

有一个需要注意的是VMI_register_callback的第三个参数,如果它是NULL,这意味着这个回调将在整个过程中一直被调用。如果指针指向1,那么这个回调将一直被调用。如果指针指向0,则此回调被禁用。其他类型的回调注册函数也遵循此约定。
现在就已经知道了如何注册或注销,启用或禁用回调了。
3. Hook API
对恶意软件分析时,api跟踪对于理解恶意软件的行为是至关重要的。传统的分析工具通过在不同的容易绕过的(尤其是被Rootkit绕过)层次上hook api来实现api跟踪。DECAF可以在虚拟机外更可靠得跟踪API。你可以在你的插件中hook任意的API或者EIP。在hookapitest插件中展现了如何hook api以及检索api参数。
现在,我们修改上面的样例代码来hook api NtCreateFile并从栈中检索它的参数。首先,当目标进程开始时(my_loadmainmodule_callback被调用时),我们通过hookapi_hook_function_byname注册了api hook。在下面的代码中,当客户系统调用NtCreateFile时,NtCreateFile_call将会被调用。对于被标记为“IN”的参数,你可以在栈中检索到它,但是对于“OUT”参数,当NtCreateFile返回时它才被赋值。为了处理这种情况,我们使用hookapi_hook_return hook api 的返回值。当NtCreateFile_call被调用,NtCreateFile返回的返回地址在EBP,我们应该把这个返回值传递给hookapi_hook_return函数,另外,你可以通过hookapi_hook_return的第三个、第四个参数把数据传递给NtCreateFile_ret。在NtCreateFile_ret 中,我们在栈上检索文件句柄。在返回时,EBP存储第一个参数——文件句柄的地址,我们使用DECAF_read_mem从EBP中读取该文件句柄。你还可以在hookapitests/custom_handlers.c中找到更复杂的参数检索函数,但它们的基本原理都是相同的。
正确检索参数的关键在于正确地理解“地址”。地址有三种:客户操作系统的虚拟地址、客户操作系统的物理地址和主机操作系统的虚拟地址。EBP的值为客户操作系统的虚拟地址。DECAF_read_mem获取客户机操作系统内存中指定虚拟地址存放的内容。有时,参数的API是一个指针,你需要先读这个指针的值,然后使用decaf_read_mem读取指针指向的内存。
另一件需要注意的事情是字符集问题。Windows内部使用unicode字符,如果你得到一些不可读的编码,你可能需要将其转换为可读的字符集。
以下为代码

DECAF_handle ntcreatefile_handle;typedef struct {    uint32_t call_stack[12]; //参数与返回地址    DECAF_Handle hook_handle;}NtCreateFile_hook_context_t;/* NTSTATUS NtCreateFile( Out PHANDLE FileHandle, In ACCESS_MASK DesiredAccess, In POBJECT_ATTRIBUTES ObjectAttributes, Out PIO_STATUS_BLOCK IoStatusBlock, _In_opt_ PLARGE_INTEGER AllocationSize, In ULONG FileAttributes, In ULONG ShareAccess, In ULONG CreateDisposition, In ULONG CreateOptions, In PVOID EaBuffer, In ULONG EaLength ); */ static void NtCreateFile_ret(void *param) {    NtCreateFile_hook_context_t *ctx = (NtCreateFile_hook_context_t *)param;     DECAF_printf("NtCreateFile exit:");    hookapi_remove_hook(ctx->hook_handle);    uint32_t out_handle;    DECAF_read_mem(NULL, ctx->call_stack[1], 4, &out_handle);    DECAF_printf("out_handle=%08x\n", out_handle);    free(ctx);}static void NtCreateFile_call(void *opaque) {    DECAF_printf("NtCreateFile entry\n");     NtCreateFile_hook_context_t ctx = (NtCreateFile_hook_context_t) malloc(sizeof(NtCreateFile_hook_context_t));     if(!ctx) //run out of memory return;        DECAF_read_mem(NULL, cpu_single_env->regs[R_ESP], 12*4, ctx->call_stack);    ctx->hook_handle = hookapi_hook_return(ctx->call_stack[0],NtCreateFile_ret, ctx, sizeof(*ctx));}static void my_loadmainmodule_callback(VMI_Callback_Params* params) {    if(strcmp(params->cp.name,targetname)==0){         DECAF_printf("Process %s you spcecified starts \n",params->cp.name);         target_cr3=params->cp.cr3;/// @ingroup hookapi /// install a hook at the function entry by specifying module name and function name /// @param mod module name that this function is located in    /// @param func function name /// @param is_global flag specifies if this hook should be invoked globally or only in certain execution context (when should_monitor is true) /// @param cr3 the memory space that this hook is installed. 0 for all memory spaces. /// @param fnhook address of function hook /// @param opaque address of an opaque structure provided by caller (has to be globally allocated) // @param sizeof_opaque size of the opaque structure (if opaque is an integer, not a pointer to a structure, sizeof_opaque must be zero) /// @return a handle that uniquely identifies this hook /// Note that the handle that is returned, might not actually be active yet - you can check the eip value of the handle to find out /// the default value is 0.         ntcreatefile_handle = hookapi_hook_function_byname( "ntdll.dll", "NtCreateFile", 1, target_cr3, NtCreateFile_call, NULL, 0);    } }

文章来源:decaf-platform - decaf_plugins.wiki