域名邮箱
当前位置:首页 >> 网络信息 >> 域名邮箱
【网站建设一条龙】长垣网站推广【长垣办理400电话】长垣SEO优化、长垣微信公众号APP客户端小程序开发、长垣网站托管、长垣APP开发-企业官网制作
发布日期:2022-09-07 阅读次数:57 字体大小:


上往是老品牌营销型网站建设公司主营:营销型网站建设网站制作网站开发网页设计网站设计做网站百度seo优化杭州做网站专业建站公司提供企业网站建设设计手机网站建设设计响应式网站设计商城网站制作B2B门户网站开发等业务,有效促进公司发展,突破瓶颈,提升品牌形象!将情感融入用户体验设计,走向市场新格局!。服务范围:各城市都可以服务,需要咨询请拨打电话,免费咨询。

服务热线:400-111-6878 手机微信同号:18118153152(各城市商务人员可上门服务)


长垣网站推广【长垣办理400电话】长垣SEO优化、长垣微信公众号APP客户端小程序开发、长垣网站托管、长垣APP开发


长垣市,河南省辖县级市,新乡市代管, [1]  位于豫东北地区,介于东经114°29'—114°59'、北纬34°59'—35°23'之间,总面积1051平方千米。 [2]  截至2020年6月,长垣市下辖5个街道、11镇、2乡, [3]  总人口88.18万人(2019年)。 [4]  市政府驻蒲西街道人民路368号。 [1] 

前221年,秦改首邑为长垣县。2019年8月,河南省撤销长垣县,设立县级长垣市。 [1]  属暖温带大陆性季风型气候。 [2]  长济高速、大庆—广州高速公路、济东高速公路、新菏铁路、省道308线、213线穿境而过。

长垣市是全国文明城市 [5]  、国家园林县城 [6]  、国家新型城镇化综合试点地区 [7]  、全国科普示范区 [8]  、节水型社会建设达标县 [9]  、国家农产品质量安全县 [10]  、全国农村创新创业典型县 [11]  、革命文物保护利用片区分县。 [12] 


C++ 虚函数解析

    一;普通函数与虚函数的比较

    我们都知道,可以在类当中定义函数,当然也可以利用关键字“virtual”定义虚函数。接下来,我们使用如下代码看看,在汇编层当中两者之间有什么区别呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
 
class a
{
public:
    int b;
    void c() {
        int c = 1;
        printf("%d ", c);
    }
    virtual void d() {
        int  d = 2;
        printf("%d ", d);
    }
};
 
int main() {
    a test;
    a* p = &test;
 
    p->c();
    p->d();
    return 0;
}

    代码当中定义了一个类,其中包含了函数c()以及虚函数d()。接下来,我们在“p->c();”位置下断点,然后转到汇编层来看看。我们看到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
    21:    p->c();
00FD1A30  mov         ecx,dword ptr [p]  
00FD1A33  call        a::c (0FD10FFh)  
    22:    p->d();
00FD1A38  mov         eax,dword ptr [p]  
00FD1A3B  mov         edx,dword ptr [eax]  
00FD1A3D  mov         esi,esp  
00FD1A3F  mov         ecx,dword ptr [p]  
00FD1A42  mov         eax,dword ptr [edx]  
00FD1A44  call        eax  
00FD1A46  cmp         esi,esp  
00FD1A48  call        __RTC_CheckEsp (0FD1258h)

    1.1;普通函数的汇编代码

    首先,我们来看在调用c()函数的对应的汇编代码

1
2
00FD1A30  mov         ecx,dword ptr [p]  
00FD1A33  call        a::c (0FD10FFh)

    我们来看一下,它call 指令后面跟了一个地址“0FD10FFh”。那么我们来跟踪一下这个地址。麻烦这个地址对应的指令是一个jmp指令。

1
2
a::c:
00FD10FF  jmp         a::c (0FD1810h)

    接着,我们继续跟踪,发现,该跳转的地址就是我们类当中c()函数的地址。

1
2
3
4
5
6
7
8
     7:   void c() {
00FD1810  push        ebp  
00FD1811  mov         ebp,esp  
00FD1813  sub         esp,0D8h  
00FD1819  push        ebx  
00FD181A  push        esi  
00FD181B  push        edi  
00FD181C  push        ecx

    总结来说:普通函数,是直接Call一个固定的地址。该固定的地址最终是指向了类调用函数的地址。

    1.2;虚函数

    接下来我们来看看虚函数所对应的汇编代码,初次看,我们就发现,它对应的汇编代码行数上就比普通函数要多。那么,我们就慢慢来看。

1
2
3
4
5
6
00FD1A38  mov         eax,dword ptr [p]  
00FD1A3B  mov         edx,dword ptr [eax]  
00FD1A3D  mov         esi,esp  
00FD1A3F  mov         ecx,dword ptr [p]  
00FD1A42  mov         eax,dword ptr [edx]  
00FD1A44  call        eax

    我们主要关注“mov eax,dword ptr [p]”,"mov edx,dword ptr [eax]","mov eax,dword ptr [edx]","call eax"。首先它指针p当中的值放入到寄存器edx当中。最后在从寄存器当中将值取出放入寄存器eax 当中,并且执行call eax 指令。

    总结来说:虚函数执行的call 指令后面并不是一个绝对的地址,而是寄存器eax , 其中eax 的值是什么地址,那么将执行什么函数。


    二;同类多虚函数情况

    我们在同一个类当中定义多个虚函数,那么在执行调用,在底层会是一个什么样的情况呢?我们使用下面案例来测试一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
 
class a
{
public:
    int b;
    void c() {
        int c = 1;
        printf("%d ", c);
    }
    virtual void d() {
        int  d = 2;
        printf("%d ", d);
    }
    virtual void e() {
        int e = 3;
        printf("%d ", e);
    }
    virtual void f() {
        int f = 4;
        printf("%d ", f);
    }
};
 
int main() {
    a test;
    a* p = &test;
 
    p->c();
    p->d();
    p->e();
    p->f();
    return 0;
}

    我们在调用虚函数位置使用断点,执行并且进入到“反汇编”窗口,详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    29:    p->c();
00E34C30  mov         ecx,dword ptr [p]  
00E34C33  call        a::c (0E310FFh)  
    30:    p->d();
00E34C38  mov         eax,dword ptr [p]  
00E34C3B  mov         edx,dword ptr [eax]  
00E34C3D  mov         esi,esp  
00E34C3F  mov         ecx,dword ptr [p]  
00E34C42  mov         eax,dword ptr [edx]  
00E34C44  call        eax  
00E34C46  cmp         esi,esp  
00E34C48  call        __RTC_CheckEsp (0E31258h)  
    31:    p->e();
00E34C4D  mov         eax,dword ptr [p]  
00E34C50  mov         edx,dword ptr [eax]  
00E34C52  mov         esi,esp  
00E34C54  mov         ecx,dword ptr [p]  
00E34C57  mov         eax,dword ptr [edx+4]  
00E34C5A  call        eax  
00E34C5C  cmp         esi,esp  
00E34C5E  call        __RTC_CheckEsp (0E31258h)  
    32:    p->f();
00E34C63  mov         eax,dword ptr [p]  
00E34C66  mov         edx,dword ptr [eax]  
00E34C68  mov         esi,esp  
00E34C6A  mov         ecx,dword ptr [p]  
00E34C6D  mov         eax,dword ptr [edx+8]  
00E34C70  call        eax  
00E34C72  cmp         esi,esp  
00E34C74  call        __RTC_CheckEsp (0E31258h)

    我们来对比调用虚函数d(),e(),f() ,大体上的汇编代码是一样的,有区别的是在每次调用的第5行。熟悉汇编代码的人可能很快就知道这是利用定长偏移来寻址的一种方式(即:[ebp]表示某个地址A,[ebp+4]即表示A地址下一位地址。)。为了结论的严谨,这里我们耐心的追踪一下。

    2.1 p->d()

    我们首先追踪一下第一个虚函数d(),首先会将指针p里面的内容放入寄存器EAX,然后将寄存器EAX当中的值作为地址来搜索内容并且放入寄存器EDX。

    此时,EAX=1EFA20,EDX=E37BEC

    直到执行到地址“00E34C44”在寄存器EDX的值为地址去两个字大小的内容放入寄存器EAX。

    

    我们来看一下,此时EAX是指的哪个指令,它指向了一个jmp 指令(地址:00E3135C)。

    继续执行跟踪跳转后的地址,最终我们来到了虚函数d()的代码位置。

    2.2p->e()

    刚刚我们已经分析了。在分析“p->d()”虚函数的时候,寄存器EDX当中存储的值作为地址寻址操作,最终定位的位置正是虚函数d()代码存储的位置。那么接下来我们看看虚函数e(),同样我们将代码执行到地址“00E34C5A”位置。此时我们发现EAX的值为00E313DE。

    同样的,我们追踪一下,发现代码定位到jmp 指令位置。详细如下图:

    继续更新,我们发现同样定位到虚函数e()

    

    2.3p->f()

    同样的追踪过程,可以定位到虚函数f()

    

    2.4 总结

    通过刚刚的定位,我们都可以找到对应的虚函数,那么,这些虚函数有什么关系呢?我们首先观察指令“mov         eax,dword ptr [p]”,“ mov         edx,dword ptr [eax] ”。定位到该地址,然后在内存当中找到对应位置:

    

    此时,EDX=00E37BEC。那么EDX+4 = 00E37BF0,EDX+8 = 00E37BF4,所以值为[EDX+4]= 00e313de,[EDX+8]= 00e313e3 。 我们将这些值当做地址来寻址,发现正好对应的就是虚函数的JMP跳转地址。

    所以,我们知道了。在调用虚函数的时候,首先会将多个虚函数用一些JMP指令指向,存储这些jmp指令的地址连续的存储在一个地址空间当中。(这个地址空间,我们通常认为这是虚函数地址表)。在调用对应虚函数之前,首先将虚函数地址表的地址放入寄存器EDX当中。通过偏移定长来决定选择哪个虚函数。详细如下图:


微信图片_20210425092605.jpg


长垣网站推广长垣办理400电话长垣SEO优化、长垣微信公众号APP客户端小程序开发、长垣网站托管、长垣APP开发


高端网站制作领跑者,1对1的专业官网定制开发及百度seo排名优化维护报价方案,精准把握网页设计核心诉求,提供网站搭建推广服务,让您在做网站开发过程中高枕无忧!

服务范围:北京网站制作上海网站制作广州网站制作深圳网站制作成都网站设计杭州网站设计南京网站设计天津网站设计武汉做网站重庆做网站苏州做网站昆山做网站嘉善做网站无锡网站建设江阴网站建设河北网站建设山西网站建设 辽宁网站建设吉林小程序制作黑龙江小程序制作  、江苏小程序制作  、浙江小程序制作 、安徽小程序制作 、福建小程序制作 、江西400电话山东400电话河南400电话湖北400电话湖南400电话广东400电话海南400电话四川400电话贵州400电话云南公众号陕西公众号甘肃公众号青海公众号广西公众号内蒙公众号西藏企业网站宁夏企业网站新疆企业网站

服务热线:400-111-6878 手机微信同号:18118153152(各城市商务人员可上门服务)

推荐文章
优化文章