先把之前的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的虚函数表