一、建立通信端口
在DriverEntry函数里创建一个安全性叙述子。
函数FltBuildDefaultSecurityDescriptor是用来申请一个安全叙述子(简单点就是给使用通信端口的用户申请个权限,这里可以看到申请的权限是FLT_PORT_ALL_ACCESS,意思就是:用户层的程序连接到设置了这个权限级别的通信端口后,可以获得所有操作权限)。函数InitializeObejectAttributes就是用来给我们要创建的对象(名称是:MINISPY_PORT_NAME)设置一些属性值。
FltCreateCommunicationPort就是给这个端口定义所需要的三个函数,同时注册这个端口(注册了才能用)。这里注册的三个函数分别是:
MiniConnect用户层和内核层建立连接的时候会调用该函数
MiniDisconnect用户层和内核层断开连接的时候会调用该函数
MiniMessage用户层和内核层进行通讯的时候会调用
当然,既然称他们为回调函数,那他们就不是我们用户层的程序去调用的,工作原理是这样的,我们在用户层通过两个api:FilterConnectCommunicationPort和FilterSendMessage来发出请求,然后通讯端口会根据我们的请求自己去调用这三个函数完成具体的工作。其中前者对应NPMiniConnect,后者对应NPMiniMessage。
完成上面三个回调函数后,内核中的通讯代码已经准备好了。
#define MINISPY_PORT_NAME L"\\MiniPort" //通信端口名字
PFLT_PORT gServerPort;//服务端口
PFLT_PORT gClientPort;//客户端口
// Defines the commands between the utility and the filter
typedef enum _MINI_COMMAND {
ENUM_PASS = 0,
ENUM_BLOCK
} MINI_COMMAND;
// Defines the command structure between the utility and the filter.
typedef struct _COMMAND_MESSAGE {
MINI_COMMAND Command;
} COMMAND_MESSAGE, *PCOMMAND_MESSAGE;
NTSTATUS
DriverEntry (
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS status;
PSECURITY_DESCRIPTOR sd;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING uniString;
UNREFERENCED_PARAMETER( RegistryPath );
PT_DBG_PRINT( PTDBG_TRACE_ROUTINES,
("MiniFilter!DriverEntry: Entered\n") );
//
// Register with FltMgr to tell it our callback routines
//
status = FltRegisterFilter( DriverObject,
&FilterRegistration,
&gFilterHandle );
FLT_ASSERT( NT_SUCCESS( status ) );
if (NT_SUCCESS( status )) {
//
// Start filtering i/o
//
status = FltStartFiltering( gFilterHandle );
if (!NT_SUCCESS( status )) {
FltUnregisterFilter( gFilterHandle );
}
}
//产生一个安全性叙述子
status=FltBuildDefaultSecurityDescriptor(&sd,FLT_PORT_ALL_ACCESS);
RtlInitUnicodeString( &uniString, MINISPY_PORT_NAME );
//初始化对象属性
InitializeObjectAttributes( &oa,
&uniString,
OBJ_KERNEL_HANDLE|OBJ_CASE_INSENSITIVE,
NULL,
sd );
//内核建立通信端口
status = FltCreateCommunicationPort(gFilterHandle,&gServerPort,&oa,NULL,MiniConnect,MiniDisconnect,MiniMessage,1);
FltFreeSecurityDescriptor( sd );
return status;
}
//用户态和内核态建立连接
NTSTATUS
MiniConnect(
__in PFLT_PORT ClientPort,
__in PVOID ServerPortCookie,
__in_bcount(SizeOfContext) PVOID ConnectionContext,
__in ULONG SizeOfContext,
__deref_out_opt PVOID *ConnectionCookie
)
{
DbgPrint("[mini-filter] NPMiniConnect");
PAGED_CODE();
UNREFERENCED_PARAMETER( ServerPortCookie );
UNREFERENCED_PARAMETER( ConnectionContext );
UNREFERENCED_PARAMETER( SizeOfContext);
UNREFERENCED_PARAMETER( ConnectionCookie );
ASSERT( gClientPort == NULL );
gClientPort = ClientPort;
return STATUS_SUCCESS;
}
//用户态和内核断开连接
VOID
MiniDisconnect(
__in_opt PVOID ConnectionCookie
)
{
PAGED_CODE();
UNREFERENCED_PARAMETER( ConnectionCookie );
DbgPrint("[mini-filter] NPMiniDisconnect");
// Close our handle
FltCloseClientPort( gFilterHandle, &gClientPort );
}
//用户态和内核态传送数据
NTSTATUS
MiniMessage (
__in PVOID ConnectionCookie,
__in_bcount_opt(InputBufferSize) PVOID InputBuffer,
__in ULONG InputBufferSize,
__out_bcount_part_opt(OutputBufferSize,*ReturnOutputBufferLength) PVOID OutputBuffer,
__in ULONG OutputBufferSize,
__out PULONG ReturnOutputBufferLength
)
{
MINI_COMMAND command;
NTSTATUS status;
PAGED_CODE();
UNREFERENCED_PARAMETER( ConnectionCookie );
UNREFERENCED_PARAMETER( OutputBufferSize );
UNREFERENCED_PARAMETER( OutputBuffer );
DbgPrint("[mini-filter] NPMiniMessage");
if ((InputBuffer != NULL) &&
(InputBufferSize >= (FIELD_OFFSET(COMMAND_MESSAGE,Command) +
sizeof(MINI_COMMAND)))) {
try {
// Probe and capture input message: the message is raw user mode
// buffer, so need to protect with exception handler
command = ((PCOMMAND_MESSAGE) InputBuffer)->Command;
} except( EXCEPTION_EXECUTE_HANDLER ) {
return GetExceptionCode();
}
switch (command) {
//释放规则
case ENUM_PASS:
{
DbgPrint("[mini-filter] ENUM_PASS");
gCommand = ENUM_PASS;
status = STATUS_SUCCESS;
break;
}
//阻擋規則
case ENUM_BLOCK:
{
DbgPrint("[mini-filter] ENUM_BLOCK");
gCommand = ENUM_BLOCK;
status = STATUS_SUCCESS;
break;
}
default:
DbgPrint("[mini-filter] default");
status = STATUS_INVALID_PARAMETER;
break;
}
} else {
status = STATUS_INVALID_PARAMETER;
}
return status;
}
二、应用层通过DLL使用通信端口
在用户态编写应用程序时,开发者可以编写一个简单的动态链接库(DLL)来提供与内核中的MiniFilter内核驱动程序通信的功能。
动态库
.h
#ifndef _MINIFILTER_H_
#define _MINIFILTER_H_
#include <windows.h>
#include <stdio.h>
#include <FltUser.h>
#pragma comment(lib, "fltLib.lib")
extern HANDLE g_hPort;
#define MIN_PORT_NAME L"\\MiniPort"
#ifdef MINI_EXPORTS
#define MINI_API _declspec(dllexport)
#else
#define MINI_API _declspec(dllexport)
#endif // NPMINI_EXPORTS
extern "C" MINI_API int InitialCommuicationPort(void);
extern "C" MINI_API int NPSendMessage(PVOID InputBuffer);
typedef enum _MINI_COMMAND {
ENUM_PASS = 0,
ENUM_BLOCK
}MIN_COMMAND;
typedef struct _COMAND_MESSAGE
{
MIN_COMMAND Command;
} COMMAND_MESSAGE,*PCOMMAND_MESSAGE;
#endif
.cpp
#include "stdafx.h"
#include "MiniFilter_dll.h"
//初始化句柄
HANDLE g_hPort = INVALID_HANDLE_VALUE;
int InitialCommuicationPort(void)
{
DWORD hResult = FilterConnectCommunicationPort(MIN_PORT_NAME,0,NULL,0,NULL,&g_hPort);
printf("进入了通信端口初始化\n");
if (hResult != S_OK)
{
return hResult;
printf("通信端口初始化不成功\n");
}
printf("通信端口初始化成功\n");
return 0;
}
int NPSendMessage(PVOID InputBuffer)
{
DWORD bytesReturned = 0;
DWORD hResult = 0;
PCOMMAND_MESSAGE command_message = (PCOMMAND_MESSAGE)InputBuffer;
printf("进入发送消息\n");
hResult = FilterSendMessage(g_hPort,command_message,sizeof(command_message),NULL,NULL,&bytesReturned);
if (hResult != S_OK)
{
return hResult;
}
return 0;
}
为了在任意目标机器上使用,此动态库以静态链接C/C++运行时库
三、用户程序调用
在此是显示调用动态库
.h
#include <windows.h>
#include <vector>
#include <string>
using namespace std;
HANDLE g_hPort = INVALID_HANDLE_VALUE;
typedef enum _MINI_COMMAND {
ENUM_PASS = 0,
ENUM_BLOCK
}MIN_COMMAND;
typedef struct _COMAND_MESSAGE
{
MIN_COMMAND Command;
}COMMAND_MESSAGE,*PCOMMAND_MESSAGE;
class CApp
{
public:
CApp();
virtual ~CApp();
int Init();
void Message(COMMAND_MESSAGE data);
private:
HINSTANCE m_hModule;
bool LoadminifilterDll();
};
.cpp
#include "App.h"
#include <iostream>
using namespace std;
typedef int (*pSendMessage)(PVOID pInBufffer);
typedef int (*pInitiaCommunicationPort)();
CApp::CApp()
{
m_hModule = NULL;
LoadminifilterDll();
}
CApp::~CApp()
{
if (m_hModule)
{
FreeLibrary(m_hModule);
}
}
bool CApp::LoadminifilterDll()
{
printf("进入了LoadminifilterDll,此函数是为了Minifilter_dll.dll中的NPSendMessage\n");
m_hModule = LoadLibrary(L"MiniFilter_dll.dll");
if (m_hModule != NULL)
{
pInitiaCommunicationPort pInit = (pInitiaCommunicationPort)GetProcAddress(m_hModule,"InitialCommuicationPort");
if (!pInit) {
printf("调用了pInit ,但是失败了\n");
return false;
}
pInit();
printf("调用了pInit ,并链接成功了\n");
return true;
}
printf("获得MiniFilter_dll.dll的句柄失败\n");
return false;
}
void CApp::Message(COMMAND_MESSAGE data)
{
if (m_hModule == NULL)
{
if (LoadminifilterDll() == false)
{
return ;
}
}
printf("发送数据\n");
//根据符号名得到函数地址
pSendMessage send = (pSendMessage)GetProcAddress(m_hModule,"NPSendMessage");
send(&data);
}
void main()
{
CApp app;
char input;
while (true)
{
cout << "Enter 'a' for PASS MODE, 'b' for BLOCKMODE or 'q' to EXIT" << endl;
cin >> input;
if (input=='a' || input=='A')
{
COMMAND_MESSAGE data;
data.Command =ENUM_PASS;
printf("上一句是 data.Command = ENUM_PASS;\n");
app.Message(data);
printf("==>NOTEPAD.EXE PASS MODE\n");
}
else if (input=='b' || input=='B')
{
COMMAND_MESSAGE data;
data.Command = ENUM_BLOCK;
app.Message(data);
printf("==>NOTEPAD.EXE BLOCK MODE\n");
}
else if (input== 'q' || input=='Q')
{
printf("EXIT\n");
break;
}
else
{
printf("Wrong Parameter!!!\n");
}
}
system("pause");
}
DLL是以静态链接运行时库的,所以用户程序也要以静态链接运行时库。
四、
驱动装好后,以管理员权限在cmd下net start MiniFilter开启服务
UseMiniFilter.exe 和 MiniFilter_dll.dll放在同一目录下
以管理员权限打开UseMiniFilter.exe
输入字符 b (阻止打开)
打开一个txt
通信成功了!!!!