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 Slide

  2. View Slide




  3. View Slide







  4. View Slide



  5. View Slide







  6. View Slide







  7. View Slide

  8. View Slide








  9. View Slide









  10. View Slide





  11. View Slide

  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

    View Slide








  13. View Slide





  14. View Slide





  15. View Slide




  16. View Slide






  17. View Slide


  18. View Slide


  19. View Slide


  20. View Slide


  21. View Slide









  22. View Slide






  23. ☓ ☓


    View Slide



  24. ☓ ☓


    View Slide



  25. ☓ ☓


    View Slide

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

    View Slide



  27. 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 Slide



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

  29. 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 Slide



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



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

    View Slide


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

    View Slide



  33. 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 Slide




  34. // 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 Slide


  35. // 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 Slide

  36. // 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 Slide


  37. // 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 Slide

  38. View Slide

  39. View Slide

  40. // 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 Slide

  41. // 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 Slide


  42. View Slide


  43. 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 Slide

  44. View Slide










  45. View Slide









  46. 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 Slide





  47. View Slide



  48. View Slide





  49. View Slide




  50. View Slide



  51. 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 Slide


  52. 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 Slide


  53. 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 Slide

  54. 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 Slide

  55. 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 Slide

  56. 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 Slide


  57. /*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 Slide


  58. 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 Slide


  59. /// @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 Slide


  60. /// @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 Slide


  61. /* 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 Slide


  62. /// @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 Slide





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

    View Slide






  64. 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 Slide




  65. (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 Slide

  66. View Slide



  67. View Slide










  68. View Slide





  69. View Slide

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

    View Slide

  71. View Slide



  72. View Slide




  73. View Slide