天书夜读笔记——深入虚函数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;
}

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

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

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

但是在上面的汇编代码中,并没有看到有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;
}

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

可以看到有两次调用,
···
.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

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

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

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

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值