15 Feb 2020

c++pwn

c++pwn

c++pwn,就是艹c艹

Name Mangling

先学洋文

mangling	英[ˈmæŋɡlɪŋ]    美[ˈmæŋɡlɪŋ]
v.	压碎; 撕烂; 严重损坏; 糟蹋(如蹩脚的诗朗诵或拙劣的演奏);
[词典] mangle的现在分词;
[例句] Or such mangling can be performed automatically after an executable is created.
或者也可以等到可执行文件生成之后再自动化切碎。
[其他] 原型: mangle

Name Mangling就是编译器把👴起的函数名粉碎了

void func1(int a, int b){}
int func2(double a, int b){return 0;}
g++ -c test.cpp -o test.o
nm -a test.o
0000000000000000 b .bss
0000000000000000 n .comment
0000000000000000 d .data
0000000000000000 r .eh_frame
0000000000000000 n .note.GNU-stack
0000000000000000 t .text
0000000000000000 T _Z5func1ii
000000000000000d T _Z5func2di
0000000000000000 a test.cpp

👴的函数名是func1、func1,Name Mangling后变成_Z5func1ii、_Z5func2di

_Z,5是原来的函数名长度,ii,di是参数类型

namespace wdnmd
{
	class white_give
	{
	public:
		void abcdefgknmlgb(int a){}
	};
}

int main(int argc, char const *argv[])
{
	wdnmd::white_give eggplant;
	eggplant.abcdefgknmlgb(0);
	return 0;
}
g++ -o test test.cpp
nm test
...
00000000000006b4 W _ZN5wdnmd10white_give13abcdefgknmlgbEi
...

_ZN开头,5是命名空间名称长度,10是类名长度,13是函数名长度,E结尾,后面是参数类型

c++filt可以倒回去看看

c++filt _ZN5wdnmd10white_give13abcdefgknmlgbEi
wdnmd::white_give::abcdefgknmlgb(int)

gdb中可以使用set print asm-demangle on

Name mangling使每一个名称转换后有唯一的名称,编码简单可逆

  • g++中Name mangling编码方法
    1. _Z开头,如果有作用域,_ZN开头
    2. 长度+名称
    3. 如果有作用域,E结尾
    4. 参数类型

Virtual function table

虚函数表

类的虚函数表是一块连续的内存,每个内存单元中记录一个地址

虚函数的地址存放于虚函数表之中

虚表属于类,而不是属于某个具体的对象。同一个类的所有对象都使用同一个虚表

类的对象内部会有指向类内部的虚表地址的指针(*__vptr ),类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。 通过这个指针访问虚函数表,然后遍历其中函数指针,并调用相应的函数。

单继承派生类中仅有一个虚函数表,无论派生类有没有重写基类的虚函数,这个虚函数表和基类的虚函数表都不是一个表,如果派生类没有重写基类的虚函数,基类和派生类的虚函数表指向的函数地址相同

1

#include<iostream>

class white_give
{
public:
	virtual void gg1(){}
	virtual void gg2(){}
};

class eggplant : public white_give
{
public:
	virtual void gg1(){std::cout<<"wdnmd"<<std::endl;}
};

void debug(){}
int main(int argc, char const *argv[])
{
	white_give *a = new white_give();
	eggplant *b = new eggplant();
	eggplant *c = new eggplant();
	debug();
	return 0;
}
0x613e60:       0x0000000000000000      0x0000000000000021//a
0x613e70:       0x0000000000600dc8      0x0000000000000000
0x613e80:       0x0000000000000000      0x0000000000000021//b
0x613e90:       0x0000000000600da8      0x0000000000000000
0x613ea0:       0x0000000000000000      0x0000000000000021//c
0x613eb0:       0x0000000000600da8      0x0000000000000000
0x613ec0:       0x0000000000000000      0x000000000000f141

0x600dc8是white_give类的虚表

0x600dc8 <vtable for white_give+16>:    0x0000000000400986      0x0000000000400992

0x600da8是eggplant类的虚表

0x600da8 <vtable for eggplant+16>:      0x000000000040099e      0x0000000000400992

0x400986是white_give类的gg1()

0x40099e是eggplant类重写的gg1()

0x400992是gg2()

多继承情况下,派生类中有多个虚函数表,虚函数的排列方式和继承的顺序一致。派生类重写函数将会覆盖所有虚函数表的同名内容,派生类自定义新的虚函数将会在第一个类的虚函数表的后面进行扩充。

pwndbg> x/64gx 0x614e60
0x614e60:       0x0000000000000000      0x0000000000000021
0x614e70:       0x0000000000601d98      0x0000000000000000
0x614e80:       0x0000000000000000      0x0000000000000021
0x614e90:       0x0000000000601d78      0x0000000000000000
0x614ea0:       0x0000000000000000      0x0000000000000021
0x614eb0:       0x0000000000601d30      0x0000000000601d58
0x614ec0:       0x0000000000000000      0x000000000000f141

0x601d98是white_give类的虚表

0x601d98 <vtable for white_give+16>:    0x0000000000400976      0x0000000000400982

0x400976 <white_give::gg1()>
0x400982 <white_give::gg2()>

0x601d78是eggplant类的虚表

0x601d78 <vtable for eggplant+16>:      0x000000000040098e      0x000000000040099a

0x40098e <eggplant::gg1()>
0x40098e <eggplant::gg1()>

0x601d30是aubergine类的虚表

0x601d30 <vtable for aubergine+16>:     0x0000000000400976      0x00000000004009a6
0x601d40 <vtable for aubergine+32>:     0x00000000004009de      0xfffffffffffffff8

0x400976 <white_give::gg1()>
0x4009a6 <aubergine::gg2()>
0x4009de <aubergine::gg4()>

0x601d58是aubergine类的虚表

0x601d58 <vtable for aubergine+56>:     0x000000000040098e      0x000000000040099a

0x40098e <eggplant::gg1()>
0x40099a <eggplant::gg3()>

劫持虚函数表

虚表指针可写,可以利用uaf、堆溢出等漏洞改写虚表指针

例如在bss写入shellcode,在bss+len(shellcode)写入bss的地址,把虚表指针改写为bss+len(shellcode)

在调用虚函数时,就会跳转到bss执行shellcode

New & Delete

在C++中类的对象建立分为两种,一种是静态建立,如A a;另一种是动态建立A* p=new A()

静态建立对象,由编译器为对象在栈中分配内存,通过直接移动栈顶指针得到适当的空间,然后调用构造函数在这片空间构造对象

动态建立对象,是使用new运算符将对象建立在堆中,在栈中保存指向该对象的指针

new和delete的底层是malloc和free

  • new

    1. operator new

      与malloc类似

      失败时进入exception而malloc是返回null

    2. constructor

  • delete

    1. destructor

    2. operator delete

      与free类似

New Delete
new delete
new[] delete[]
operator new operator delete
operator new[] operator delete[]
malloc free

Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).