Mommy, what is Use After Free bug?
ssh [email protected] -p2222 (pw:guest)
#include
#include
#include
#include
#include
using namespace std;
class Human {
private:
virtual void give_shell() {
system("/bin/sh");
}
protected:
int age;
string name;
public:
virtual void introduce() {
cout << "My name is " << name << endl;
cout << "I am " << age << " years old" << endl;
}
};
class Man: public Human {
public:
Man(string name, int age) {
this->name = name;
this->age = age;
}
virtual void introduce() {
Human::introduce();
cout << "I am a nice guy!" << endl;
}
};
class Woman: public Human {
public:
Woman(string name, int age) {
this->name = name;
this->age = age;
}
virtual void introduce() {
Human::introduce();
cout << "I am a cute girl!" << endl;
}
};
int main(int argc, char *argv[]) {
Human *m = new Man("Jack", 25);
Human *w = new Woman("Jill", 21);
size_t len;
char *data;
unsigned int op;
while (1) {
cout << "1. use\n2. after\n3. free\n";
cin >> op;
switch (op) {
case 1:
m->introduce();
w->introduce();
break;
case 2:
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case 3:
delete m;
delete w;
break;
default:
break;
}
}
return 0;
}
从IDA中可以看出,C++中对一个对象的实例化操作(如Human *m = new Man(“Jack”, 25);)分为6个调用的过程.
std::allocator::allocator(void)
std::allocator>::basic_string
operator new(ulong)
Man::Man()
std::allocator>::~basic_string()
std::allocator::~allocator()
其中,第3步代表new分配内存的过程.从mov edi,30h中可以看出,这个对象占用48字节的内存空间.第4步相当于是执行构造函数的过程.当第4步执行完成了之后,该对象对应的虚表指针和虚表都已经分配了内存并进行了初始化.我们可以从gdb-peda中看出这一过程.
其中A,即0x555555768e70代表的是实例化对象m的地址.地址A指向的内容的前8个字节是B,即0x555555755c88.指针B指向的内容的前8个字节是C,即0x555555555504.可以看出,C对应的正是虚函数Human::give_shell()的地址.由此可见,指针B代表的正是实例化对象m的虚表指针.根据虚函数的知识我们知道,虚表指针B指向的内存块中存放的是各个虚函数的地址.如C代表的正是这样的第一个虚函数的地址.即虚函数Human::give_shell()的地址.
从上图中可以看出,指针A指向的内容分别是指针B(实例化对象m的虚表指针),0x0000000000000019(代表m的年龄,25岁),其中0x000000006b63614a代表的是’Jack’,4ah代表J,61h代表a,63h代表c,6bh代表k.
指针B指向的内容分别是虚函数Human::give_shell()的地址,虚函数Man::introduce()的地址,ZTI5Human的地址,虚函数Human::introduce()的地址,虚函数Human::give_shell()的地址.
在释放m和w占用的内存之后,m和w占用的内存就空闲出来了.所以后续分配的内存会占用在释放m和w之前它们占用的内存.而m或者w对象占用的内存的前8个字节都是虚表指针,因此将虚表指针的值,即地址减去8,
如上图所示,即可以完成执行introduce虚函数时实际上执行的是give_shell虚函数.
参考:
virtual function in C++