2021-01-05汇编学习00
请注意,本文编写于 693 天前,最后修改于 693 天前,其中某些信息可能已经过时。

目录


一、虚函数:间接调用

虚函数表:

class Base
{
public:
    void Function_1()
    {
        printf("Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Function_2...\n");
    }
};

观察反汇编:

void TestMethod()
{
	Base base;

	base.Function_1();
	00401090 8D 4D FC             lea         ecx,[ebp-4]
	00401093 E8 9F FF FF FF       call        @ILT+50(Base::Function_1) (00401037)

	base.Function_2();
	00401098 8D 4D FC             lea         ecx,[ebp-4]
	0040109B E8 65 FF FF FF       call        @ILT+0(Base::Function_2) (00401005)

	Base* pb = &base;

	pb->Function_1();
	004010A6 8B 4D F8             mov         ecx,dword ptr [ebp-8]
	004010A9 E8 89 FF FF FF       call        @ILT+50(Base::Function_1) (00401037)

	pb->Function_2();
	004010AE 8B 4D F8             mov         ecx,dword ptr [ebp-8]
	004010B1 8B 11                mov         edx,dword ptr [ecx]
	004010B3 8B F4                mov         esi,esp
	004010B5 8B 4D F8             mov         ecx,dword ptr [ebp-8]
	004010B8 FF 12                call        dword ptr [edx]
}

总结:

  1. 通过对象调用时,virtual函数与普通函数都是E8 Call

  2. 通过指针调用时,virtual函数是FF Call,也就是间接Call

二、深入虚函数调用

class Base
{
public:
    int x;
    int y;
    virtual void Function_1()
    {
        printf("Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Function_2...\n");
    }
};

1、当类中有虚函数时,观察大小的变化.

Base base;
printf("%x\n",sizeof(base));

思考:多出这4个字节是什么?
虚函数表的大小为4个字节,不管有多少个virtual

2、观察虚函数通过指针调用时的反汇编:

pb->Function_1();
0040D9E3 8B 4D F0             mov         ecx,dword ptr [ebp-10h]
0040D9E6 8B 11                mov         edx,dword ptr [ecx]
0040D9E8 8B F4                mov         esi,esp
0040D9EA 8B 4D F0             mov         ecx,dword ptr [ebp-10h]
0040D9ED FF 12                call        dword ptr [edx]


pb->Function_2();
0040D9F6 8B 45 F0             mov         eax,dword ptr [ebp-10h]
0040D9F9 8B 10                mov         edx,dword ptr [eax]
0040D9FB 8B F4                mov         esi,esp
0040D9FD 8B 4D F0             mov         ecx,dword ptr [ebp-10h]
0040DA00 FF 52 04             call        dword ptr [edx+4]

总结:

  1. 当类中有虚函数时,会多一个属性,4个字节,首地址也就是所有成员前边

  2. 多出的属性是一个地址,指向一张表(数组),里面存储了所有虚函数的地址

三、打印虚函数表

class Base
{
public:
	int x;
	int y;
    virtual void Function_1()
    {
        printf("Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Function_3...\n");
    }
};

void TestMethod()
{
	//查看 Sub 的虚函数表	
    Base base;

	//对象的前四个字节就是虚函数表
	printf("base 的虚函数表地址为:%x\n",*(int*)&base);

	//通过函数指针调用函数,验证正确性
    typedef void(*pFunction)(void);

	pFunction pFn;

	for(int i=0;i<3;i++)
	{
		int temp = *((int*)(*(int*)&base)+i);
		pFn = (pFunction)temp;
		pFn();
	}

}

总结

作业

1、单继承无函数覆盖(打印Sub对象的虚函数表)

# include "stdafx.h"

struct Base
{
public:
    virtual void Function_1()
    {
        printf("Base:Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Base:Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Base:Function_3...\n");
    }
};
struct Sub:Base
{
public:
    virtual void Function_4()
    {
        printf("Sub:Function_4...\n");
    }
    virtual void Function_5()
    {
        printf("Sub:Function_5...\n");
    }
    virtual void Function_6()
    {
        printf("Sub:Function_6...\n");
    }
};

void Test()
{

	Sub sub;
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);

	//通过函数指针调用函数,验证正确性
	//声明一个函数指针,没有返回值,没有参数
	typedef void(*pFun)(void);
	//定义一个函数指针的变量
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		pF = (pFun)*((int*)(*(int*)&sub)+i);
		pF();
		printf("Function_%d的地址为:%x\n",i+1,pF);
	}
}

int main(int argc, char* argv[])
{
	Test();
	return 0;
}


2、单继承有函数覆盖(打印Sub对象的虚函数表)

# include "stdafx.h"

struct Base
{
public:
    virtual void Function_1()
    {
        printf("Base:Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Base:Function_2...\n");
    }
    virtual void Function_3()
    {
        printf("Base:Function_3...\n");
    }
};
struct Sub:Base
{
public:
    virtual void Function_1()
    {
        printf("Sub:Function_1...\n");
    }
    virtual void Function_2()
    {
        printf("Sub:Function_2...\n");
    }
    virtual void Function_6()
    {
        printf("Sub:Function_6...\n");
    }
};

void Test()
{
	Sub sub;
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);


	//通过函数指针调用函数,验证正确性
	typedef void(*pFun)(void);
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		int temp = *((int*)(*(int*)&sub)+i);
		if(temp == 0)	//数组 到0结束
		{
			break;
		}
		pF = (pFun)temp;
		pF();
		printf("Function_%d的地址为:%x\n",i+1,pF);
	}
}

int main(int argc, char* argv[])
{
	Test();
	return 0;
}


3、多继承无函数覆盖

# include "stdafx.h"

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2
{
public:
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
    virtual void Fn_4()
    {
        printf("Base2:Fn_4...\n");
    }
};
struct Sub:Base1,Base2
{
public:
    virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }
    virtual void Fn_6()
    {
        printf("Sub:Fn_6...\n");
    }
};


void Test()
{

	Sub sub;
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);
	//通过函数指针调用函数,验证正确性
	typedef void(*pFun)(void);
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		int temp = *((int*)(*(int*)&sub)+i);
		if(temp == 0)	//数组 到0结束
		{
			break;
		}
		pF = (pFun)temp;
		pF();
		//printf("Function_%d的地址为:%x\n",i+1,pF);
	}

	//对象的第二个四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)((int)&sub+4));
	pFun pF1;
	for(int j = 0;j < 2;j++)
	{
		int temp = *((int*)(*(int*)((int)&sub+4))+j);
		pF1 = (pFun)temp;
		pF1();
		//printf("Function_%d的地址为:%x\n",j+1,pF1);
	}
}

int main(int argc, char* argv[])
{
	Test();
	return 0;
}


4、多继承有函数覆盖

覆盖的是哪个 ,那就在哪个表里

# include "stdafx.h"

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2
{
public:
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
    virtual void Fn_4()
    {
        printf("Base2:Fn_4...\n");
    }
};
struct Sub:Base1,Base2
{
public:
    virtual void Fn_1()
    {
        printf("Sub:Fn_1...\n");
    }
    virtual void Fn_3()
    {
        printf("Sub:Fn_3...\n");
    }
	virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }
};


void Test()
{

	Sub sub;
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);
	//通过函数指针调用函数,验证正确性
	typedef void(*pFun)(void);
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		int temp = *((int*)(*(int*)&sub)+i);
		if(temp == 0)	//数组 到0结束
		{
			break;
		}
		pF = (pFun)temp;
		pF();
		//printf("Function_%d的地址为:%x\n",i+1,pF);
	}

	//对象的第二个四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)((int)&sub+4));
	pFun pF1;
	for(int j = 0;j < 2;j++)
	{
		int temp = *((int*)(*(int*)((int)&sub+4))+j);
		pF1 = (pFun)temp;
		pF1();
		//printf("Function_%d的地址为:%x\n",j+1,pF1);
	}
}


int main(int argc, char* argv[])
{
	Test();
	return 0;
}


5、多重继承无函数覆盖

# include "stdafx.h"

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2:Base1
{
public:
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
    virtual void Fn_4()
    {
        printf("Base2:Fn_4...\n");
    }
};
struct Sub:Base2
{
public:
    virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }
    virtual void Fn_6()
    {
        printf("Sub:Fn_6...\n");
    }
};


void Test()
{

	Sub sub;
	printf("大小:%d\n",sizeof(sub));
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);
	//通过函数指针调用函数,验证正确性
	typedef void(*pFun)(void);
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		int temp = *((int*)(*(int*)&sub)+i);
		if(temp == 0)	//数组 到0结束
		{
			break;
		}
		pF = (pFun)temp;
		pF();
		printf("Function_%d的地址为:%x\n",i+1,pF);
	}

}

int main(int argc, char* argv[])
{
	Test();
	return 0;
}

6、多重继承有函数覆盖(1)

# include "stdafx.h"

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2:Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base2:Fn_1...\n");
    }
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
};
struct Sub:Base2
{
public:
	virtual void Fn_5()
	{
		printf("Sub:Fn_5...\n");
	}
};



void Test()
{

	Sub sub;
	printf("大小:%d\n",sizeof(sub));
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);
	//通过函数指针调用函数,验证正确性
	typedef void(*pFun)(void);
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		int temp = *((int*)(*(int*)&sub)+i);
		if(temp == 0)	//数组 到0结束
		{
			break;
		}
		pF = (pFun)temp;
		pF();
		printf("地址为:%x\n",pF);
	}

}

int main(int argc, char* argv[])
{
	Test();
	return 0;
}


7、多重继承有函数覆盖(2)

# include "stdafx.h"

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2:Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base2:Fn_1...\n");
    }
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
};
struct Sub:Base2
{
public:
	virtual void Fn_1()
    {
        printf("Sub:Fn_1...\n");
    }
	virtual void Fn_5()
    {
        printf("Sub:Fn_5...\n");
    }
};




void Test()
{

	Sub sub;
	printf("大小:%d\n",sizeof(sub));
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);
	//通过函数指针调用函数,验证正确性
	typedef void(*pFun)(void);
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		int temp = *((int*)(*(int*)&sub)+i);
		if(temp == 0)	//数组 到0结束
		{
			break;
		}
		pF = (pFun)temp;
		pF();
		printf("地址为:%x\n",pF);
	}

}

int main(int argc, char* argv[])
{
	Test();
	return 0;
}

8、多重继承有函数覆盖(3)

# include "stdafx.h"

struct Base1
{
public:
    virtual void Fn_1()
    {
        printf("Base1:Fn_1...\n");
    }
    virtual void Fn_2()
    {
        printf("Base1:Fn_2...\n");
    }
};
struct Base2:Base1
{
public:
    virtual void Fn_3()
    {
        printf("Base2:Fn_3...\n");
    }
};
struct Sub:Base2
{
public:
	virtual void Fn_1()
    {
        printf("Sub:Fn_1...\n");
    }
	virtual void Fn_3()
    {
        printf("Sub:Fn_3...\n");
    }
};

void Test()
{

	Sub sub;
	printf("大小:%d\n",sizeof(sub));
	//对象的前四个字节就是虚函数表
	printf("sub的虚函数表地址为:%x\n",*(int*)&sub);
	//通过函数指针调用函数,验证正确性
	typedef void(*pFun)(void);
	pFun pF;
	for(int i = 0;i < 6;i++)
	{
		int temp = *((int*)(*(int*)&sub)+i);
		if(temp == 0)	//数组 到0结束
		{
			break;
		}
		pF = (pFun)temp;
		pF();
		printf("地址为:%x\n",pF);
	}

}

int main(int argc, char* argv[])
{
	Test();
	return 0;
}



笔记来自滴水三期

本文作者:Na1r

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!