Upgrade to Pro — share decks privately, control downloads, hide ads and more …

仮想化技術を用いたマルウェア解析

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

 仮想化技術を用いたマルウェア解析

セキュリティ・キャンプ全国大会2015 講義資料

Avatar for Yuma Kurogome

Yuma Kurogome

August 14, 2015
Tweet

More Decks by Yuma Kurogome

Other Decks in Programming

Transcript

  1. • • MOV EBX, EAX ADD EAX, $0x4 … MOV

    EBX, EAX ADD EAX, $0x4 … • • • mov_i32 ebx, eax movi_i32 tmp1, 0x4 add_i32 eax, tmp1
  2. • • sudo apt-get install qemu binutils-dev libboost-all-dev sudo apt-get

    build-dep qemu git clone [email protected]:sycurelab/DECAF.git cd DECAF/decaf/ ./configure make # QEMUの中間表現でテイント解析(後述)を行う ./configure -–enable-tcg-taint # QEMUの中間表現をロギングする ./configure -–enable-tcg-ir-log # VMIの無効化 ./configure –-disable-vmi
  3. • • qemu-img create -f qcow2 ${IMAGE_FILE}.qcow2 ${SIZE}G qemu-system-i386 -m

    1024 -drive file=${IMAGE_FILE}.qcow2 -cdrom ${DISK}.iso cd ${HOME}/DECAF/decaf/i386-softmmu/ ./qemu-system-i386 -monitor stdio -m 1024 -drive file=${IMAGE_FILE}.qcow2
  4. import subprocess, os, fcntl, time def input_cmd(p, cmd): if not

    cmd.endswith("¥n"): cmd += "¥n" p.stdin.write(cmd) p.stdin.write("MARK¥n") while True: time.sleep(0.1) try: s = p.stdout.read() except Exception, e: continue print s if "unknown command: 'MARK'" in s: break def main(): decaf_path = raw_input("Enter the root directory of DECAF (i386-softmmu/qemu-system-i386 should be there):") image_path = raw_input("Enter the image path:") os.chdir(decaf_path) p = subprocess.Popen(args=“i386-softmmu/qemu-system-i386 {image_path} -m 512 -monitor stdio”.format(image_path=image_path), stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) fl = fcntl.fcntl(p.stdout, fcntl.F_GETFL) fcntl.fcntl(p.stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK) time.sleep(3) input_cmd(p, "ps") input_cmd(p, "help") if __name__ == '__main__': main()
  5. • • modprobe nbd qemu-nbd -c /dev/nbd0 ${IMAGE_FILE}.qcow2 /sbin/fdisk -l

    /dev/nbd0 # /mnt/にWindowsのディレクトリ構造がそのまま展開される mount -o loop,offset=$((63*512)) /dev/nbd0 /mnt/ umount /mnt qemu-nbd -d /dev/nbd0 rmmod nbd
  6. • • cd ${HOME}/DECAF/decaf/plugins/${PLUGIN} ./configure –-decaf-path=${HOME}/DECAF/decaf/ make # 標準入出力からコマンドを通じて操作 ./qemu-system-i386

    -monitor stdio -m 1024 -drive file=${IMAGE_FILE}.qcow2 # コマンド一覧表示 (qemu) help # プラグインのロード (qemu) load_plugin ${HOME}/DECAF/decaf/plugins/${PLUGIN}/${PLUGIN}.so # プラグインのアンロード (qemu) load_plugin
  7. • • • // plugin_cmds.h { .name = "do_callbacktests", .args_type

    = "procname:s?", .mhandler.cmd = do_callbacktests, .params = "[procname]", .help = "Run the tests with program [procname]“ }, // callbacktests.c static mon_cmd_t callbacktests_term_cmds[] = { #include "plugin_cmds.h" {NULL, NULL, }, };
  8. • // callbacktests.c plugin_interface_t* init_plugin(void) { callbacktests_interface.mon_cmds = callbacktests_term_cmds; callbacktests_interface.plugin_cleanup

    = &callbacktests_cleanup; //initialize the plugin callbacktests_init(); return (&callbacktests_interface); }
  9. // callbacktests.c static int callbacktests_init(void) { DECAF_output_init(NULL); DECAF_printf("Hello World¥n"); //register

    for process create and process remove events processbegin_handle = VMI_register_callback(VMI_CREATEPROC_CB, &callbacktests_loadmainmodule_callback, NULL); removeproc_handle = VMI_register_callback(VMI_REMOVEPROC_CB, &callbacktests_removeproc_callback, NULL); targetname[0] = '¥0'; targetcr3 = 0; targetpid = (uint32_t)(-1); do_callbacktests(NULL, NULL); return (0); } •
  10. • // callbacktests.c static void callbacktests_loadmainmodule_callback(VMI_Callback_Params* params) { char procname[64];

    uint32_t pid; //DECAF_printf("Process with pid = %d and cr3 = %u was just created¥n", params->lmm.pid, params->lmm.cr3); VMI_find_process_by_cr3_c(params->cp.cr3, procname, 64, &pid); if (strcmp(targetname, procname) == 0) { targetpid = pid; targetcr3 = params->cp.cr3; runTests(); } }
  11. // windows_vmi.h #define KPCR_OFFSET 0x1c #define KDVB_OFFSET 0x34 // base

    on KPCR #define PSLM_OFFSET 0x70 #define PSAPH_OFFSET 0x78 // base on KDVB
  12. // windows_vmi.cpp static process * find_new_process(CPUState *env, uint32_t cr3) {

    uint32_t kdvb, psAPH, curr_proc, next_proc; process *pe; DECAF_read_mem(env, gkpcr + KDVB_OFFSET, 4, &kdvb); DECAF_read_mem(env, kdvb + PSAPH_OFFSET, 4, &psAPH); DECAF_read_mem(env, psAPH, 4, &curr_proc); while (curr_proc != 0 && curr_proc != psAPH) { uint32_t pid, proc_cr3; uint32_t curr_proc_base = curr_proc - handle_funds[GuestOS_index].offset->PSAPL_OFFSET; DECAF_read_mem(env, curr_proc_base + handle_funds[GuestOS_index].offset->PSAPID_OFFSET, 4, &pid); if (VMI_find_process_by_pid(pid) != NULL) //we have seen this process goto next; DECAF_read_mem(env, curr_proc_base + 0x18, 4, &proc_cr3); if(cr3 != proc_cr3) //This is a new process, but not the current one. Skip it! goto next; //This is the one we are looking for pe = new process(); pe->EPROC_base_addr = curr_proc_base; pe->pid = pid; pe->cr3 = proc_cr3; DECAF_read_mem(env, curr_proc_base + handle_funds[GuestOS_index].offset->PSAPNAME_OFFSET, NAMESIZE, pe->name); DECAF_read_mem(env, curr_proc_base + handle_funds[GuestOS_index].offset->PSAPPID_OFFSET, 4, &pe->parent_pid); VMI_create_process(pe); return pe; next: DECAF_read_mem(env, curr_proc, 4, &next_proc); if (curr_proc == next_proc) { //why do we need this check? break; } curr_proc = next_proc; } return NULL; }
  13. • MOV EBX, EAX ADD EAX, $0x4 mov_i32 ebx, eax

    movi_i32 tmp1, 0x4 add_i32 eax, tmp1 mov_i32 ebx, eax mov_i32 taint_ebx, taint_eax movi_i32 tmp1, 0x4 movi_i32 tmp51, 0x0 add_i32 eax, tmp1 MOV ESI, $0x4 MOV EDI, $0x0 ADD EAX, ESI
  14. • • • • • • • • static void

    block_begin_callback(DECAF_Callback_Params* params) { uint32_t eip,eax; if(params->bb.env->cr[3]!=target_cr3) break; eip =cpu_single_env->eip; eax =cpu_single_env->regs[R_EAX]; DECAF_printf("EIP = 0x%08x ¥n",eip); DECAF_printf("EAX = 0x%08x ¥n",eax); }
  15. • • DECAF_Handle VMI_register_callback(procmod_callback_type_t cb_type, procmod_callback_func_t cb_func, int *cb_cond );

    int VMI_unregister_callback(procmod_callback_type_t cb_type, DECAF_Handle handle); DECAF_Handle DECAF_register_callback( DECAF_callback_type_t cb_type, DECAF_callback_func_t cb_func, int *cb_cond ); int DECAF_unregister_callback(DECAF_callback_type_t cb_type, DECAF_Handle handle);
  16. • DECAF_Handle DECAF_registerOptimizedBlockBeginCallback( DECAF_callback_func_t cb_func, int *cb_cond, gva_t addr, OCB_t

    type); DECAF_Handle DECAF_registerOptimizedBlockEndCallback( DECAF_callback_func_t cb_func, int *cb_cond, gva_t from, gva_t to); int DECAF_unregisterOptimizedBlockBeginCallback(DECAF_Handle handle); int DECAF_unregisterOptimizedBlockEndCallback(DECAF_Handle handle);
  17. • uintptr_t hookapi_hook_function_byname(const char *mod,const char *func,int is_global,target_ulong cr3, hook_proc_t

    fnhook,void *opaque,uint32_t sizeof_opaque); void hookapi_remove_hook(uintptr_t handle); uintptr_t hookapi_hook_return(target_ulong pc,hook_proc_t fnhook, void *opaque, uint32_t sizeof_opaque); uintptr_t hookapi_hook_function(int is_global, target_ulong pc, target_ulong cr3, hook_proc_t fnhook, void *opaque, uint32_t sizeof_opaque );
  18. typedef struct { uint32_t call_stack[12]; //paramters and return address 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 ); */
  19. 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); } }
  20. 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)); }
  21. • /*given a virtual address of guest os, get the

    corresponded physical address */ gpa_t DECAF_get_phys_addr(CPUState* env, gva_t addr); DECAF_errno_t DECAF_memory_rw(CPUState* env, uint32_t addr, void *buf, int len,int is_write); DECAF_errno_t DECAF_memory_rw_with_cr3(CPUState* env, target_ulong cr3,gva_t addr, void *buf, int len, int is_write); DECAF_errno_t DECAF_read_mem(CPUState* env, gva_t vaddr, int len, void *buf); DECAF_errno_t DECAF_write_mem(CPUState* env, gva_t vaddr, int len, void *buf); DECAF_errno_t DECAF_read_mem_with_cr3(CPUState* env, target_ulong cr3,gva_t vaddr, int len, void *buf); DECAF_errno_t DECAF_write_mem_with_cr3(CPUState* env, target_ulong cr3,gva_t vaddr, int len, void *buf); DECAF_get_page_access(CPUState* env, gva_t addr);
  22. • module * VMI_find_module_by_pc(target_ulong pc, target_ulong pgd, target_ulong *base); module

    * VMI_find_module_by_name(const char *name, target_ulong pgd, target_ulong *base); module * VMI_find_module_by_base(target_ulong pgd, uint32_t base); process * VMI_find_process_by_pid(uint32_t pid); process * VMI_find_process_by_pgd(uint32_t pgd); process* VMI_find_process_by_name(char *name);
  23. • /// @ingroup semantics /// locate the module that a

    given instruction belongs to /// @param eip virtual address of a given instruction /// @param cr3 memory space id: physical address of page table /// @param proc process name (output argument) /// @param tm return tmodinfo_t structure extern int VMI_locate_module_c(gva_t eip, gva_t cr3, char proc[],tmodinfo_t *tm); extern int VMI_locate_module_byname_c(const char *name, uint32_t pid,tmodinfo_t * tm); extern int VMI_find_cr3_by_pid_c(uint32_t pid); extern int VMI_find_pid_by_cr3_c(uint32_t cr3); extern int VMI_find_pid_by_name_c(char* proc_name);
  24. • /// @ingroup semantics /// find process given a memory

    space id /// @param cr3 memory space id: physical address of page table /// @param proc process name (output argument) /// @param pid process pid (output argument) /// @return number of modules in this process extern int VMI_find_process_by_cr3_c(uint32_t cr3, char proc_name[], size_t len, uint32_t *pid);
  25. • /* find process name and CR3 using the PID

    as search key */ extern int VMI_find_process_by_pid_c(uint32_t pid, char proc_name[], size_t len, uint32_t *cr3); extern int VMI_get_proc_modules_c(uint32_t pid, uint32_t mod_no, tmodinfo_t *buf); extern int VMI_get_all_processes_count_c(void); /* Create array with info about all processes running in system */ extern int VMI_find_all_processes_info_c(size_t num_proc, procinfo_t *arr); //Aravind - added to get the number of loaded modules for the process. //This is needed to create the memory required by get_proc_modules extern int VMI_get_loaded_modules_count_c(uint32_t pid); //end - Aravind
  26. • /// @ingroup semantics /// @return the current thread id.

    If for some reason, this operation / // is not successful, the return value is set to -1. /// This function only works in Windows XP for Now. extern int VMI_get_current_tid_c(CPUState* env); //0 unknown 1 windows 2 linux extern int VMI_get_guest_version_c(void);
  27. • • • • • export SDL_VIDEO_X11_DGAMOUSE=0 gdb --args ${HOME}/DECAF/decaf/i386-softmmu/qemu-system-i386

    -m 1024 -monitor stdio ${IMAGE_FILE}.qcow2 (gdb) handle SIG38 "nostop" "pass" "noprint” (gdb) r
  28. • • • (qemu) x/10i $eip 0x7c917ab8: addl $0x2,0x18(%ebp) 0x7c917abc:

    inc %ecx 0x7c917abd: inc %ecx 0x7c917abe: test %dl,%dl 0x7c917ac0: jne 0x7c917a94 0x7c917ac2: xor %ecx,%ecx 0x7c917ac4: test %ecx,%ecx 0x7c917ac6: mov 0xc(%ebp),%ebx 0x7c917ac9: jne 0x7c917d2d 0x7c917acf: mov -0x4(%ebp),%eax