友链
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; } 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
也就是说,内存地址的偏移量为0x存入4个地方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