当前位置: 网站首页>小程序开发>网站优化

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

发表日期: 2021-04-30 10:43:33 浏览次数:104

长垣网站推广【长垣办理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\n", c);
    }
    virtual void d() {
        int  d = 2;
        printf("%d\n", 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\n", c);
    }
    virtual void d() {
        int  d = 2;
        printf("%d\n", d);
    }
    virtual void e() {
        int e = 3;
        printf("%d\n", e);
    }
    virtual void f() {
        int f = 4;
        printf("%d\n", 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开发

400-111-6878
服务热线
顶部

备案号: 苏ICP备11067224号

CopyRight © 2011 书生商友信息科技 All Right Reserved

24小时服务热线:400-111-6878   E-MAIL:1120768800@qq.com   QQ:1120768800

  网址: https://www.768800.com  网站建设上往建站

关键词: 网站建设| 域名邮箱| 服务器空间| 网站推广| 上往建站| 网站制作| 网站设计| 域名注册| 网络营销| 网站维护|

企业邮箱| 虚拟主机| 网络建站| 网站服务| 网页设计| 网店美工设计| 网站定制| 企业建站| 网站设计制作| 网页制作公司|

400电话办理| 书生商友软件| 葬花网| 调温纤维| 海洋馆运营维护| 北京保安公司| 殡仪馆服务| 殡葬服务| 昌平殡葬| 朝阳殡葬|

预约专家

欢迎您免费咨询,请填写以下信息,我们收到后会尽快与您联系

  

服务热线:400-111-6878