• 天书夜读笔记——深入虚函数virtual


    友链

    先把之前的MyDriver类中的虚函数给实现一下

    extern "C" {
        #include<ntifs.h>
    }
    
    class MyDriver 
    {
    public:
        MyDriver(PDRIVER_OBJECT driver);
        virtual NTSTATUS OnDispatch(PDEVICE_OBJECT dev, PIRP irp) {
            return STATUS_UNSUCCESSFUL;
        };
        static MyDriver *d_my_driver;
    
    private:
        static NTSTATUS sDispatch(PDEVICE_OBJECT dev, PIRP irp);
        PDRIVER_OBJECT d_driver;
    };
    
    MyDriver::MyDriver(PDRIVER_OBJECT driver) : d_driver(driver)
    {
        size_t i;
        for(i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++) {
            d_driver->MajorFunction[i] = sDispatch;
        }
        d_my_driver = this;
    };
    
    NTSTATUS MyDriver::sDispatch(PDEVICE_OBJECT dev, PIRP irp) {
        return d_my_driver->OnDispatch(dev, irp);
    }
    
    MyDriver* MyDriver::d_my_driver = NULL;
    
    
    void* __cdecl operator new(unsigned int size) {
        void* pt = ExAllocatePool(NonPagedPool, size);
        if(NULL != pt) 
            memset(pt, 0, size);
        return pt;
    }
    
    class MySubDriver : public MyDriver
    {
    public:
        MySubDriver(PDRIVER_OBJECT driver) : MyDriver(driver) {};
        virtual NTSTATUS OnDispatch(PDEVICE_OBJECT dev, PIRP irp) {
            DbgPrint("this is the dispatch function of MySubDriver\r\n");
            return STATUS_UNSUCCESSFUL;
        }
    };
    
    extern "C" NTSTATUS DriverEntry(
        PDRIVER_OBJECT driver, PUNICODE_STRING reg
    ) {
    	MyDriver::d_my_driver = new MySubDriver(driver);
        return STATUS_UNSUCCESSFUL;
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    build之后扔到ida中

    .text:00010486 ; int __stdcall MyDriver::sDispatch(_DEVICE_OBJECT *dev, _IRP *irp)
    .text:00010486 ?sDispatch@MyDriver@@CGJPAU_DEVICE_OBJECT@@PAU_IRP@@@Z proc near
    .text:00010486                                         ; DATA XREF: MyDriver::MyDriver(_DRIVER_OBJECT *)+19o
    .text:00010486
    .text:00010486 dev             = dword ptr  8
    .text:00010486 irp             = dword ptr  0Ch
    .text:00010486
    .text:00010486                 mov     edi, edi
    .text:00010488                 push    ebp
    .text:00010489                 mov     ebp, esp
    .text:0001048B                 mov     ecx, ?d_my_driver@MyDriver@@2PAV1@A ; MyDriver * MyDriver::d_my_driver
    .text:00010491                 mov     eax, [ecx]
    .text:00010493                 pop     ebp
    .text:00010494                 jmp     dword ptr [eax]
    .text:00010494 ?sDispatch@MyDriver@@CGJPAU_DEVICE_OBJECT@@PAU_IRP@@@Z endp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这个是MyDriver类的sDispatch函数,在我们的C++代码中,在该函数内部,调用了OnDispatch函数

    NTSTATUS MyDriver::sDispatch(PDEVICE_OBJECT dev, PIRP irp) {
        return d_my_driver->OnDispatch(dev, irp);
    }
    
    • 1
    • 2
    • 3

    但是在上面的汇编代码中,并没有看到有call指令,只有一个jmp跳转指令

    根据汇编代码,可以看到最终程序跳到了eax所指向的内存中存储的地址。而eax的值又来自于[ecx],ecx的值又来自于d_my_driver,也就是this指针,根据我们上一篇文章的介绍,ecx指向的是虚函数表,那么[ecx]就是虚函数表的真正内容了,我们可以通过给c++文件中的mydriver类再增加一个虚函数并在sdispatch进行调用来看一下一个虚函数在表中占用多少个字节

    增加一个虚函数并进行调用:

    extern "C" {
        #include<ntifs.h>
    }
    
    class MyDriver 
    {
    public:
        MyDriver(PDRIVER_OBJECT driver);
        virtual NTSTATUS OnDispatch(PDEVICE_OBJECT dev, PIRP irp) {
            return STATUS_UNSUCCESSFUL;
        };
        virtual NTSTATUS test_virtual_fucn(PDEVICE_OBJECT dev, PIRP irp) {
            return STATUS_UNSUCCESSFUL;
        };
        static MyDriver *d_my_driver;
    
    private:
        static NTSTATUS sDispatch(PDEVICE_OBJECT dev, PIRP irp);
        PDRIVER_OBJECT d_driver;
    };
    
    MyDriver::MyDriver(PDRIVER_OBJECT driver) : d_driver(driver)
    {
        size_t i;
        for(i=0; i<=IRP_MJ_MAXIMUM_FUNCTION; i++) {
            d_driver->MajorFunction[i] = sDispatch;
        }
        d_my_driver = this;
    };
    
    NTSTATUS MyDriver::sDispatch(PDEVICE_OBJECT dev, PIRP irp) {
        return d_my_driver->OnDispatch(dev, irp) + d_my_driver->test_virtual_fucn(dev, irp);
    }
    
    MyDriver* MyDriver::d_my_driver = NULL;
    
    
    void* __cdecl operator new(unsigned int size) {
        void* pt = ExAllocatePool(NonPagedPool, size);
        if(NULL != pt) 
            memset(pt, 0, size);
        return pt;
    }
    
    class MySubDriver : public MyDriver
    {
    public:
        MySubDriver(PDRIVER_OBJECT driver) : MyDriver(driver) {};
        virtual NTSTATUS OnDispatch(PDEVICE_OBJECT dev, PIRP irp) {
            DbgPrint("this is the dispatch function of MySubDriver\r\n");
            return STATUS_UNSUCCESSFUL;
        }
    };
    
    extern "C" NTSTATUS DriverEntry(
        PDRIVER_OBJECT driver, PUNICODE_STRING reg
    ) {
    	MyDriver::d_my_driver = new MySubDriver(driver);
        return STATUS_UNSUCCESSFUL;
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    sObDispatch的汇编代码

    .text:00010486 ; int __stdcall MyDriver::sDispatch(_DEVICE_OBJECT *dev, _IRP *irp)
    .text:00010486 ?sDispatch@MyDriver@@CGJPAU_DEVICE_OBJECT@@PAU_IRP@@@Z proc near
    .text:00010486                                         ; DATA XREF: MyDriver::MyDriver(_DRIVER_OBJECT *)+19o
    .text:00010486
    .text:00010486 dev             = dword ptr  8
    .text:00010486 irp             = dword ptr  0Ch
    .text:00010486
    .text:00010486                 mov     edi, edi
    .text:00010488                 push    ebp
    .text:00010489                 mov     ebp, esp
    .text:0001048B                 mov     ecx, ?d_my_driver@MyDriver@@2PAV1@A ; MyDriver * MyDriver::d_my_driver
    .text:00010491                 mov     eax, [ecx]
    .text:00010493                 push    esi
    .text:00010494                 push    [ebp+irp]
    .text:00010497                 push    [ebp+dev]
    .text:0001049A                 call    dword ptr [eax+4]
    .text:0001049D                 push    [ebp+irp]
    .text:000104A0                 mov     ecx, ?d_my_driver@MyDriver@@2PAV1@A ; MyDriver * MyDriver::d_my_driver
    .text:000104A6                 push    [ebp+dev]
    .text:000104A9                 mov     esi, eax
    .text:000104AB                 mov     eax, [ecx]
    .text:000104AD                 call    dword ptr [eax]
    .text:000104AF                 add     eax, esi
    .text:000104B1                 pop     esi
    .text:000104B2                 pop     ebp
    .text:000104B3                 retn    8
    .text:000104B3 ?sDispatch@MyDriver@@CGJPAU_DEVICE_OBJECT@@PAU_IRP@@@Z endp
    
    • 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

    可以看到有两次调用,
    ···
    .text:0001049A call dword ptr [eax+4]

    .text:000104AD call dword ptr [eax]

    ···

    因此,我们可以确定一个虚函数表占用4个字节,其实这个不用测试也可以推测出来,对于x86的应用程序,地址就是32bit的,一个虚函数就是一个地址罢了(函数地址),也就是4bytes

    派生类mysubdriver的虚函数表和基类mydriver的虚函数表一定存在着某种关系,才能够实现调用mydriver的虚函数却能够调用到mysubdriver的对应实现函数

    来看一下mysubdriver的构造函数是如何初始化虚函数表的

    mysubdriver的构造函数:

    .text:00010534 ; void __thiscall MySubDriver::MySubDriver(MySubDriver *this, _DRIVER_OBJECT *driver)
    .text:00010534 ??0MySubDriver@@QAE@PAU_DRIVER_OBJECT@@@Z proc near
    .text:00010534                                         ; CODE XREF: DriverEntry(x,x)+16p
    .text:00010534
    .text:00010534 driver          = dword ptr  8
    .text:00010534
    .text:00010534 this = ecx
    .text:00010534                 mov     edi, edi
    .text:00010536                 push    ebp
    .text:00010537                 mov     ebp, esp
    .text:00010539                 push    esi
    .text:0001053A                 push    [ebp+driver]    ; driver
    .text:0001053D                 mov     esi, this
    .text:0001053F                 call    ??0MyDriver@@QAE@PAU_DRIVER_OBJECT@@@Z ; MyDriver::MyDriver(_DRIVER_OBJECT *)
    .text:00010544                 mov     dword ptr [esi], offset ??_7MySubDriver@@6B@ ; const MySubDriver::`vftable'
    .text:0001054A                 mov     eax, esi
    .text:0001054C                 pop     esi
    .text:0001054D                 pop     ebp
    .text:0001054E                 retn    4
    .text:0001054E ??0MySubDriver@@QAE@PAU_DRIVER_OBJECT@@@Z endp
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    可以看到mydriver的虚函数表实际上被mysubdriver的虚函数表给替换掉了,在调用mydriver构造函数之前ecx没有发生任何改变,那么传入mydriver构造函数的this指针和mysubdriver的this指针是一样的,在mydriver构造函数调用完成后,this将会指向虚函数表,但是后面的一条指令

    .text:00010544                 mov     dword ptr [esi], offset ??_7MySubDriver@@6B@ ; const MySubDriver::`vftable'
    
    
    • 1
    • 2

    使得this(esi在mydriver构造函数调用之前保存了this的值)指向的内容发生了改变,变成了mysubdriver的虚函数表

    在这里插入图片描述

  • 相关阅读:
    决策树笔记1
    思必驰周强:AI 和传统信号技术在实时音频通话中的应用
    ubuntu16.04+cuda10.0+cudnn7.6+tensorflow_gpu-1.11.0环境安装
    Java.lang.Class类 getClasses()方法有什么功能呢?
    Python-requests库入门指南
    新电脑验机步骤(2)
    一体化伺服电机在新能源汽车充电设备中的应用
    halcon知识:矩阵专题【01】
    Nginx安装与常见命令
    中国石油大学(北京)-《钻井液工艺原理》第一阶段在线作业
  • 原文地址:https://blog.csdn.net/ma_de_hao_mei_le/article/details/125430441