最近跟着老师做一个关于decaf的项目,因此对读的文章做点笔记。
- 简介
decaf是一个虚拟机,为开发者提供了许多调用接口,可以在运行时Hook。使用这些接口可以完全在客户机之外检索OS-level semantics(操作系统级别的语义),例如进程、系统API、按键、网络等。 - 样例插件
以下插件可以指定一个你想要追踪的进程,并在进程开始时打印进程名。
当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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/15599.html