逆向XX引擎:通用dump

通用dump就是把该程序在虚拟机内执行完后的内存dump下来,然后扫描引擎进行扫描。对于杀软来说,dump下来的数据不需要是否能够运行。

实际上上图中每个区段的数据都是在样本在虚拟机执行完后的进程内存。

遍历在该进程内创建的所有内存,进程创建的内存类型有

  • VMA_USER 用户内存
  • VMA_STACK 栈
  • VMA_HEAP 堆
  • VMA_PENV 存放peb 环境数据
  • VMA_BUILTIN 存放api模拟代码和通过tdl配置构造的pe文件

我们只需要 VMA_USER类型的内存和可执行的VMA_HEAP类型的内存

下列代码为遍历所有创建的内存并将相关信息存如结构体dump_section.

struct dump_section {
    _QWORD rva;
    int vsize;
    int psize;
    int flag;
    list_head list;
};

list_for_each(list, &process->alloced_vma_head) {
    CMemInfo* vma = container_of(list, CMemInfo, list);

    region = vma->m_region;
    if (region.mem_start == process->m_main_module->BaseOfDll) {
        //主模块的mz头抹掉,因为通用脱壳后不使用这个mz头
        process->write_mem(process->m_main_module->BaseOfDll, ""21);
    }

    if (region.alloc_base != cur_mod->BaseOfDll) {
        auto pageAttr = region.PageAttr;
        if (pageAttr.mem_useage != VMA_USER &&
            //如果类型是堆,则必须是可读可执行的
            (pageAttr.mem_useage != VMA_HEAP || pageAttr.host_map_flag != 6))
            continue;
    }

    int vm_size = region.mem_end - region.mem_start;
    int read_size = 0x10000;
    int read_off = 0;
    int end = region.mem_end;
    while (true) {
        read_size = min(vm_size, 0x10000);
        //从尾部开始读
        read_off = end - read_size;
        int readed_size = process->read_mem(read_off, buf, read_size);
        if (readed_size < 0)
            //读取失败,可能内存以及被释放了
            break;

        int non_zero_off = readed_size - 1;
        if (non_zero_off < 0)
            continue;
        //尾部的0可能时为了对齐而填充的,找到非0处,从该位置开始到下一个内存区域前被省略
        while (!buf[non_zero_off]) {
            if (--non_zero_off <= 0)
                break;
        }
        if (non_zero_off > 0) {
            dump_section* ts = new dump_section;
            ts->rva = region.mem_start;
            ts->vsize = region.mem_end - region.mem_start;
            ts->psize = read_off + non_zero_off - region.mem_start;
            ts->flag = 0x40000000;
            //设置区段属性
            if (region.PageAttr.proctect & VMA_PAGE_WRITE)
                ts->flag = 0xC0000040;
            if (region.PageAttr.proctect & VMA_PAGE_READ)
                ts->flag |= 0x20000020;
            ++sec_cnt;
            list_add_tail(&ts->list, &head);

            break;
        }

        end = read_off;
        vm_size -= readed_size;
    }
}

构造PE文件

DOS头固定为下列值

unsigned char genpack_mz_head[224] = {
    0x4D0x5A0x900x000x030x000x000x000x040x000x000x000xFF0xFF0x000x00,
    0xB80x000x000x000x000x000x000x000x400x000x000x000x000x000x000x00,
    0x000x000x000x000x000x000x000x000x000x000x000x000x000x000x000x00,
    0x000x000x000x000x000x000x000x000x000x000x000x000xE00x000x000x00,
    0x0E0x1F0xBA0x0E0x000xB40x090xCD0x210xB80x010x4C0xCD0x210x540x68,
    0x690x730x200x700x720x6F0x670x720x610x6D0x200x630x610x6E0x6E0x6F,
    0x740x200x620x650x200x720x750x6E0x200x690x6E0x200x440x4F0x530x20,
    0x6D0x6F0x640x650x2E0x0D0x0D0x0A0x240x000x000x000x000x000x000x00,
    0xE60xFA0x3E0xFC0xA20x9B0x500xAF0xA20x9B0x500xAF0xA20x9B0x500xAF,
    0x610x940x5F0xAF0xA60x9B0x500xAF0x610x940x0D0xAF0xB70x9B0x500xAF,
    0xA20x9B0x510xAF0x190x9B0x500xAF0x610x940x0C0xAF0xA30x9B0x500xAF,
    0x610x940x0E0xAF0xA30x9B0x500xAF0x610x940x0F0xAF0xB30x9B0x500xAF,
    0x610x940x300xAF0xE70x9B0x500xAF0x610x940x0A0xAF0xA30x9B0x500xAF,
    0x520x690x630x680xA20x9B0x500xAF0x000x000x000x000x000x000x000x00
};

填充pe头

peheader pehead = {};
//设置pe结构
pehead.signature = 0x4550;
pehead.nobjs = sec_cnt;
pehead.machine = 332;
pehead.objalign = 0x1000;
pehead.filealign = 0x200;
pehead.subsys = 2;
pehead.stackres = 0x40000;
pehead.stackcom = 0x1000;
pehead.heapres = 0x100000;
pehead.heapcom = 0x1000;
pehead.nrvas = 0x10;

int sec_size = 0x28 * sec_cnt;
uint32_t min_off = -1;
x86_env* env = &process->cur_thread->_x86_env;
CArc_pe* arc_pe = process->m_main_module->m_cpe;
pehead.flags = 0x10e;
pehead.hdrsize = 0xe0;
if (arc_pe->peHead.flags & 0x2000)
    pehead.flags = 8462;
pehead.magic = 267;
pehead.allhdrsize = (sec_size + 0x3D7) & 0xFFFFFE00;
pehead.imagesize = (sec_size + 0x11D7) & 0xFFFFF000;
int file_addr = (sec_size + 0x3D7) & 0xFFFFFE00;

list_head* list;
//计算最小偏移、数据段大小和代码段大小
list_for_each(list, head) {
    dump_section* ds = container_of(list, dump_section, list);
    ds->vsize = (ds->vsize + 4095) & 0xFFFFF000;
    pehead.imagesize += ds->vsize;
    if (ds->rva < min_off) {
        min_off = ds->rva;
    }
    if (ds->flag & 0x40)
        pehead.dsize += ds->vsize;
    if (ds->flag & 0x20)
        pehead.tsize += ds->vsize;
}
pehead.imagebase32 = min_off - ((sec_size + 0x11D7) & 0xFFFFF000);
pehead.text_start = min_off;
pehead.data_start = min_off;

//多少个目录结构,最大只能由16个
int nrvars = min(arc_pe->peHead.nrvas, 0x10);
petab mem_pe_tab[0x10] = {};
//读取数据目录表
_QWORD va = process->m_main_module->BaseOfDll + arc_pe->dosHeader.lfanew + 0x78;
if (process->read_mem(va, mem_pe_tab, 0x80) != 0x80)
    return;
petab* genpack_petab = &pehead.expdir;
for (int i = 0; i < 0x10; i++) {
    if (i == 1)//导入表不用管
        continue;
    auto t = &mem_pe_tab[i];
    if (!t->rva)
        continue;
    genpack_petab[i].size = t->size;
    //修复rva地址
    genpack_petab[i].rva = (process->m_main_module->BaseOfDll + t->rva) - pehead.imagebase32;
}

填充pesection数据

pesection* sec_buf = (pesection*)malloc(sec_size);
if (!sec_buf)
    return;
memset(sec_buf, 0, sec_size);

auto sec = sec_buf;
int sec_index = 0;
//插入内存中的各个区段到pe文件
list_for_each(list, head) {
    auto ds = container_of(list, dump_section, list);
    snprintf(sec->s_name, 8"libvxf%d", sec_index);
    sec->s_name[7] = 0;
    sec->s_vsize = ds->vsize;
    sec->s_vaddr = ds->rva - pehead.imagebase32;
    sec->s_psize = (ds->psize + 0x1FF) & 0xFFFFFE00;
    sec->s_scnptr = file_addr;
    sec->s_flags = ds->flag;
    if (EIP >= ds->rva && EIP < ds->rva + ds->vsize)
        pehead.entry = EIP - pehead.imagebase32;

    file_addr = sec->s_psize + file_addr;
    ++sec_index;
    ++sec;
}

填充区段数据

list_for_each(list, head) {
    auto ts = container_of(list, dump_section, list);
    cur_off = ts->rva;

    psize = ts->psize;
    write_off = sec->s_scnptr;
    if (!psize) {
        ++sec;
        continue;
    }

    while (1) {
        read_size2 = min(psize, 0x10000);
        read_size = process->read_mem((_QWORD)cur_off, (DWORD*)buf, read_size2);
        if (read_size < 0)
            read_size = 0;
        algin_size = (read_size2 + 0x1FF) & 0xFFFFFE00;
        if (read_size < algin_size)
            memset((void*)(read_size + buf)0, algin_size - read_size);
        write_size = stream->write(write_off, (DWORD*)buf, algin_size);

        if (write_size < 0) {
            return;
        }
        cur_off = read_size2 + cur_off;
        write_off += read_size2;

        psize -= read_size2;
        if (!psize)
            break;
    }

    ++sec;
}
文章目录
  1. 1. 构造PE文件
    1. 1.1. 填充pe头
    2. 1.2. 填充pesection数据
    3. 1.3. 填充区段数据
,