extern "C" {
#include
}
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;
}
extern "C" NTSTATUS DriverEntry(
PDRIVER_OBJECT driver, PUNICODE_STRING reg
) {
MyDriver::d_my_driver = new MyDriver(driver);
return STATUS_UNSUCCESSFUL;
}
类中还定义了一个虚函数(OnDispatch)可以让后续继承该类的子类来进行自己的实现
由于driver是一个singleton,所以我们可以用d_my_driver指针来指向this,这样就解决了静态函数的调用问题(静态函数无法引用类的非静态成员)
因为只有静态成员函数才能像C函数一样被使用,但是静态成员函数无法调用虚函数,因此我们需要一个静态成员d_my_drvier,一个指针,让他指向MyDriver,就和this一样,这样我们就能通过d_my_drvier来调用虚函数了
通过反汇编代码,可以看到,new函数只负责分配内存,构造函数是在driver_entry函数体内被调用的,也就是说编译器在看到new之后会自动调用构造函数
我们来看一下构造函数的实现
.text:000104CC ; void __thiscall MyDriver::MyDriver(MyDriver *this, _DRIVER_OBJECT *driver)
.text:000104CC ??0MyDriver@@QAE@PAU_DRIVER_OBJECT@@@Z proc near
.text:000104CC ; CODE XREF: DriverEntry(x,x)+16p
.text:000104CC
.text:000104CC driver = dword ptr 8
.text:000104CC
.text:000104CC this = ecx
.text:000104CC mov edi, edi
.text:000104CE push ebp
.text:000104CF mov ebp, esp
.text:000104D1 mov eax, this
.text:000104D3 mov this, [ebp+driver]
.text:000104D6 push 38h
.text:000104D8 mov [eax+4], this
.text:000104DB mov dword ptr [eax], offset ??_7MyDriver@@6B@ ; const MyDriver::`vftable'
.text:000104E1 pop this
.text:000104E2
.text:000104E2 loc_104E2: ; CODE XREF: MyDriver::MyDriver(_DRIVER_OBJECT *)+29j
.text:000104E2 mov edx, [eax+4]
.text:000104E5 mov dword ptr [this+edx], offset ?sDispatch@MyDriver@@CGJPAU_DEVICE_OBJECT@@PAU_IRP@@@Z ; MyDriver::sDispatch(_DEVICE_OBJECT *,_IRP *)
.text:000104EC add this, 4
.text:000104EF cmp this, 0A4h
.text:000104F5 jbe short loc_104E2
.text:000104F7 mov ?d_my_driver@MyDriver@@2PAV1@A, eax ; MyDriver * MyDriver::d_my_driver
.text:000104FC pop ebp
.text:000104FD retn 4
.text:000104FD ??0MyDriver@@QAE@PAU_DRIVER_OBJECT@@@Z endp
可以看到调用约定为thiscall
this指针通过ecx传入函数,ecx是new函数的返回值,也就是新分配的内存地址
this->eax,然后ebp+driver->this, this->eax+4
那么也就是说在内存地址偏移量为0x4的地方存入了driver这个参数,就是我们构造函数的那个列表初始化中的d_driver成员变量
那么前四个字节干嘛去了呢,继续往下看可以看到
.text:000104DB mov dword ptr [eax], offset ??_7MyDriver@@6B@ ; const MyDriver::`vftable'
可以看到,前四个字节存放的是虚函数表
然后就开始对driver结构体的成员MajorFunction数组依次进行赋值
你可能会疑问,这里的[this+edx]是什么东西,仔细看一下代码,你会发现,前面有一对push pop操作,使得this的值变成了0x38h,正好是MajorFunction成员在driver结构体中的偏移量,而edx的值是[eax+4],也就是driver成员变量,所以一切都解释得通
另外,从代码中可以看出MyDriver::d_my_driver是一个全局变量
构造函数返回后,在driverentry中,构造函数的返回值eax->d_my_driver,这里的eax其实还是new函数的返回值,只不过它所指向的地址被构造函数填满了数据
看到这里,你应该就能够意识到,类其实就是一堆变量和函数的集合,没什么复杂的东西
另外需要补充的一点就是,我们必须要生命一个全局变量
MyDriver* MyDriver::d_my_driver = NULL;
因为在构造函数MyDriver::MyDriver中,我们用到了d_my_driver