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

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

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

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

Yuma Kurogome

August 14, 2015
Tweet

More Decks by Yuma Kurogome

Other Decks in Programming

Transcript







  1. View full-size slide







  2. View full-size slide







  3. View full-size slide








  4. View full-size slide









  5. View full-size slide





  6. View full-size slide

  7. • •
    MOV EBX, EAX
    ADD EAX, $0x4

    MOV EBX, EAX
    ADD EAX, $0x4




    mov_i32 ebx, eax
    movi_i32 tmp1, 0x4
    add_i32 eax, tmp1

    View full-size slide








  8. View full-size slide





  9. View full-size slide





  10. View full-size slide






  11. View full-size slide









  12. View full-size slide






  13. ☓ ☓


    View full-size slide



  14. ☓ ☓


    View full-size slide



  15. ☓ ☓


    View full-size slide

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

    View full-size slide



  17. 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

    View full-size slide



  18. 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

    View full-size slide

  19. 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()

    View full-size slide



  20. 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

    View full-size slide



  21. # QEMUのコンソールから
    (qemu) savevm ${SNAPSHOT}
    (qemu) loadvm ${SNAPSHOT}
    qemu-img snapshot -l ${IMAGE_FILE}.qcow2

    View full-size slide


  22. ls ${HOME}/DECAF/decaf/plugins
    callbacktests/ hookapitests/ keylogger/ min_apitracer/ tracecap/ unpacker/

    View full-size slide



  23. 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

    View full-size slide




  24. // 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, },
    };

    View full-size slide


  25. // 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);
    }

    View full-size slide

  26. // 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);
    }

    View full-size slide


  27. // 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();
    }
    }

    View full-size slide

  28. // 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

    View full-size slide

  29. // 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;
    }

    View full-size slide


  30. 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

    View full-size slide










  31. View full-size slide









  32. 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);
    }

    View full-size slide





  33. View full-size slide





  34. View full-size slide



  35. 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);

    View full-size slide


  36. 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);

    View full-size slide


  37. 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 );

    View full-size slide

  38. 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 );
    */

    View full-size slide

  39. 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);
    }
    }

    View full-size slide

  40. 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));
    }

    View full-size slide


  41. /*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);

    View full-size slide


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

    View full-size slide


  43. /// @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);

    View full-size slide


  44. /// @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);

    View full-size slide


  45. /* 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

    View full-size slide


  46. /// @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);

    View full-size slide





  47. git clone https://github.com/ntddk/geteip

    View full-size slide






  48. 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

    View full-size slide




  49. (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

    View full-size slide










  50. View full-size slide





  51. View full-size slide

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

    View full-size slide