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

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

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

5c6358240ec94522f70cf7b0e657f58f?s=128

Yuma Kurogome

August 14, 2015
Tweet

Transcript

  1. None
  2. None
  3. • • •

  4. • • • • • •

  5. • •

  6. • • • • • •

  7. • • • • • •

  8. None
  9. • • • • • • •

  10. • • • • • • • •

  11. • • • •

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

    EBX, EAX ADD EAX, $0x4 … • • • mov_i32 ebx, eax movi_i32 tmp1, 0x4 add_i32 eax, tmp1
  13. • • • • • • •

  14. • • • •

  15. • • • •

  16. • • •

  17. • • • • •

  18. • • • • • • • •

  19. • • • • • ☓ ☓ ☓ ☓

  20. • • ☓ ☓ ☓ ☓

  21. • • ☓ ☓ ☓ ☓

  22. http://sycurelab.ecs.syr.edu/image/overall_decaf.jpg

  23. • • sudo apt-get install qemu binutils-dev libboost-all-dev sudo apt-get

    build-dep qemu git clone git@github.com:sycurelab/DECAF.git cd DECAF/decaf/ ./configure make # QEMUの中間表現でテイント解析(後述)を行う ./configure -–enable-tcg-taint # QEMUの中間表現をロギングする ./configure -–enable-tcg-ir-log # VMIの無効化 ./configure –-disable-vmi
  24. • • 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
  25. 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()
  26. • • 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
  27. • • # QEMUのコンソールから (qemu) savevm ${SNAPSHOT} (qemu) loadvm ${SNAPSHOT}

    qemu-img snapshot -l ${IMAGE_FILE}.qcow2
  28. • ls ${HOME}/DECAF/decaf/plugins callbacktests/ hookapitests/ keylogger/ min_apitracer/ tracecap/ unpacker/

  29. • • 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
  30. • • • // 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, }, };
  31. • // 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); }
  32. // 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); } •
  33. • // 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(); } }
  34. None
  35. None
  36. // 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
  37. // 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; }
  38. • 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
  39. None
  40. • • • • • • • • •

  41. • • • • • • • • 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); }
  42. • • • •

  43. • •

  44. • • • •

  45. • • •

  46. • • 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);
  47. • 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);
  48. • 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 );
  49. 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 ); */
  50. 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); } }
  51. 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)); }
  52. • /*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);
  53. • 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);
  54. • /// @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);
  55. • /// @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);
  56. • /* 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
  57. • /// @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);
  58. • • • • git clone https://github.com/ntddk/geteip

  59. • • • • • 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
  60. • • • (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
  61. None
  62. • •

  63. • • • • • • • • •

  64. • • • •

  65. cmp eax, 0x7DF je 0xdeadbaad if(x!=2015) Invalid. ASSERT( INPUT_*_*_* =0hex7DF

    );
  66. None
  67. • •

  68. • • •