• C++反汇编-虚函数


    学无止尽,积土成山,积水成渊-《C++反汇编与逆向分析技术揭秘》 读书笔记

    在C++中,使用关键字virtual声明为虚函数。

    • 虚函数地址表(虚表)
      • 定义:当类中定义有虚函数时,编译器会把该类中所有虚函数的首地址保存在一张地址表中,即虚函数地址表。
      • 虚表信息在编译后被链接到执行文件中,因此所获得的虚表地址是一个固定的地址。
      • 虚表中虚函数的地址排列顺序依据虚函数在类中的声明顺序而定。
    • 虚表指针
      • 同时编译器还会在类的每个对象添加一个隐藏数据成员,称为虚表指针,保存着虚表的首地址,用于记录和查找虚函数。
      • 虚表指针的初始化是通过编译器在构造函数中插入代码实现的。由于必须初始化虚表指针,编译器会提供默认的构造函数。
    • 虚函数调用过程
      • 虚表间接寻址访问:
        使用对象的指针或引用调用虚函数。根据对象的首地址,取出相应的虚表指针,在虚表查找对应的虚函数的首地址,并调用执行。
      • 直接调用访问:
        使用对象调用虚函数,和调用普通成员函数一样。
     
     
     
    虚函数的识别:
    • 类中隐式定义一个数据成员
    • 数据成员在首地址处,占4字节
    • 构造函数初始化该数据成员为某个数组的首地址
    • 地址属于数据区,相对固定的地址
    • 数组的成员是函数指针
    • 函数被调用方式是thiscall
    构造函数与析构函数都会将虚表指针设置为当前对象所属类中的虚表地址。
    • 构造函数中是完成虚表指针的初始化,此时虚表指针并没有指向虚表函数。
    • 执行析构函数时,其对象的虚表指针已经指向某个虚表首地址。虚函数是在还原虚表指针,让其指向自身的虚表首地址,防止在析构函数中调用虚函数时取到非自身虚表。

    示例C++源码

     1 #include <iostream>
     2 using namespace std;
     3 
     4 class CVirtual {
     5 public:
     6             virtual int GetNumber() { return m_nNumber; }
     7             virtual void SetNumber(int nNumber) { m_nNumber = nNumber;}
     8             ~CVirtual(){ printf("~CVirtual!"); }
     9 private:
    10             int m_nNumber;
    11 
    12  
    13 };
    14  
    15 int main()
    16 {
    17     CVirtual myVirtual ,*pVirtual;
    18     pVirtual = &myVirtual;
    19     pVirtual->SetNumber(10);
    20     printf("%d ", pVirtual->GetNumber());
    21     return 0;

    汇编代码

    1.构造函数 

    mov     [ebp-8], ecx    ;=>保存this指针
    mov     eax, [ebp-8]    ;=>eax获得this指针
    mov     dword ptr [eax], offset ??_7CVirtual@@6B@ ; const CVirtual::`vftable'  ;=>虚表指针初始化

     2.析构函数

    mov     [ebp-4], ecx    ;=>保存this指针
    mov     eax, [ebp-4]    ;=>eax获得this指针
    mov     dword ptr [eax], offset ??_7CVirtual@@6B@ ; const CVirtual::`vftable'    =>虚表指针重置
    push    offset aCvirtual ; "~CVirtual!"

    3.虚函数调用

    pVirtual->SetNumber(10); 

    push    0Ah                ;=>参数10压栈
    mov     eax, [ebp-18h]     ;=>eax为this指针
    mov     edx, [eax]         ;=>edx为虚表指针
    mov     ecx, [ebp-18h]     ;=>ecx传递this指针
    mov     eax, [edx+4]       ;=>虚函数SetNumber的地址=虚表+offset 4
    call    eax
     
  • 相关阅读:
    Qt编写ffmpeg本地摄像头显示(16路本地摄像头占用3.2%CPU)
    Qt编写本地摄像头综合应用示例(qcamera/ffmpeg/v4l2等)
    Qt编写音频播放示例(带音频曲线/振幅/传输/录制等)
    Qt编写跨平台RTSP/RTMP/HTTP视频流播放器
    Qt编写4K/8K大分辨率播放器(8K占用1%CPU)
    Qt编写的视频播放综合应用示例(qmedia/ffmpeg/vlc/mpv/海康sdk等)
    Qt编写linux上视频流播放器(支持海康大华宇视等各种网络摄像机)
    Qt编写视频监控显示安卓版
    Qt音视频开发02海康sdk解码(支持句柄/回调/gpu模式/支持win/linux)
    Qt音视频开发01共享解码线程(耗时一年/性能凶残/至臻完美)
  • 原文地址:https://www.cnblogs.com/bigrabbit/p/3456953.html
Copyright © 2020-2023  润新知