编写C语言代码使用回调函数保护进程不被读写

1. 驱动中注册一个回调函数,当驱动被注册时打印消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <ntifs.h>
#define Log(X) DbgPrint("qi:"X##)
VOID processcreate_callback(_In_ HANDLE ParentId, _In_ HANDLE ProcessId, _In_ BOOLEAN Create);
VOID TestCallback();

NTSTATUS DriverUnload(PDRIVER_OBJECT pdriver)
{
PsSetCreateProcessNotifyRoutine(processcreate_callback, TRUE);
DbgPrint("unload...\n");
return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pdriver, PUNICODE_STRING path)
{
pdriver->DriverUnload = DriverUnload;
DbgPrint("loading...\n");
TestCallback();
return STATUS_SUCCESS;
}

UCHAR* PsGetProcessImageFileName(PEPROCESS Process);

VOID processcreate_callback(
_In_ HANDLE ParentId,
_In_ HANDLE ProcessId,
_In_ BOOLEAN Create
)
{
if (Create)
{
Log("create\n");
}
else
Log("close\n");

PEPROCESS pe = NULL;
NTSTATUS status = PsLookupProcessByProcessId(ProcessId, &pe);
if (NT_SUCCESS(status))
{
ObDereferenceObject(pe);
PUCHAR name = PsGetProcessImageFileName(pe);
Log("%s\n", name);
}
}
VOID TestCallback()
{
NTSTATUS status = PsSetCreateProcessNotifyRoutine(processcreate_callback, FALSE);
if (!NT_SUCCESS(status))DbgPrint("error\n");
}

2.注册一个驱动中的Object Manager 回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <ntifs.h>

PVOID g_pRegiHandle = NULL;

// 回调函数
OB_PREOP_CALLBACK_STATUS precessCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
UNREFERENCED_PARAMETER(RegistrationContext);

// 这里可以添加对特定进程的判断及权限修改逻辑
// 比如修改句柄的访问权限

return OB_PREOP_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING DriverPath)
{
OB_OPERATION_REGISTRATION oor;
OB_CALLBACK_REGISTRATION ob;

// 注册回调函数
oor.ObjectType = PsProcessType;
oor.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE; // 只关心创建句柄操作
oor.PreOperation = precessCallBack;
oor.PostOperation = NULL;

ob.Version = OB_FLT_REGISTRATION_VERSION;
ob.OperationRegistrationCount = 1;
ob.OperationRegistration = &oor;
RtlInitUnicodeString(&ob.Altitude, L"321000"); // 设置回调的高度
ob.RegistrationContext = NULL;

// 注册回调
return ObRegisterCallbacks(&ob, &g_pRegiHandle);
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);

// 卸载时注销回调
ObUnRegisterCallbacks(g_pRegiHandle);
}

3.编写C语言代码使用回调函数保护进程不被读写打开或者终止

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <ntifs.h>

#define PROCESS_TERMINATE 0x0001
#define PROCESS_VM_OPERATION 0x0008
#define PROCESS_VM_READ 0x0010
#define PROCESS_VM_WRITE 0x0020

PVOID g_pRegiHandle = NULL;

extern UCHAR *PsGetProcessImageFileName(__in PEPROCESS Process);

char* GetProcessImageNameByProcessID(ULONG ulProcessID);

typedef struct _LDR_DATA_TABLE_ENTRY64
{
LIST_ENTRY InLoadOrderLinks;
LIST_ENTRY InMemoryOrderLinks;
LIST_ENTRY InInitializationOrderLinks;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
PVOID SectionPointer;
ULONG CheckSum;
PVOID LoadedImports;
PVOID EntryPointActivationContext;
PVOID PatchInformation;
LIST_ENTRY ForwarderLinks;
LIST_ENTRY ServiceTagLinks;
LIST_ENTRY StaticLinks;
PVOID ContextInformation;
ULONG OriginalBase;
LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;

OB_PREOP_CALLBACK_STATUS precessCallBack(PVOID, POB_PRE_OPERATION_INFORMATION);
VOID DriverUnload(PDRIVER_OBJECT);

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING DriverPath)
{
OB_OPERATION_REGISTRATION oor;
OB_CALLBACK_REGISTRATION ob;

PLDR_DATA_TABLE_ENTRY64 ldr;
ldr = (PLDR_DATA_TABLE_ENTRY64)DriverObject->DriverSection;
ldr->Flags |= 0x20;//加载驱动的时候会判断此值。必须有特殊签名才行,增加0x20即可。否则将调用失败
DriverObject->DriverUnload = DriverUnload;


//注册回调函数
oor.ObjectType = PsProcessType;
oor.Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE;
oor.PreOperation = precessCallBack;
oor.PostOperation = NULL;

ob.Version = OB_FLT_REGISTRATION_VERSION;
ob.OperationRegistrationCount = 1;
ob.OperationRegistration = &oor;
RtlInitUnicodeString(&ob.Altitude, L"321000");
ob.RegistrationContext = NULL;

return ObRegisterCallbacks(&ob, &g_pRegiHandle);
}

OB_PREOP_CALLBACK_STATUS precessCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
HANDLE pid = PsGetProcessId((PEPROCESS)pOperationInformation->Object);
char szProcName[17] = { 0 };
UNREFERENCED_PARAMETER(RegistrationContext);
strcpy(szProcName, GetProcessImageNameByProcessID(pid));

if (!_stricmp(szProcName, "Notepad.exe"))//要保护的进程名字
{
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)//进程终止
{
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_TERMINATE;
}
if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_OPERATION) == PROCESS_VM_OPERATION)//openprocess
{
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_OPERATION;
}
if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_READ) == PROCESS_VM_READ)//内存读
{
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_READ;
}
if ((pOperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess & PROCESS_VM_WRITE) == PROCESS_VM_WRITE)//内存写
{
pOperationInformation->Parameters->CreateHandleInformation.DesiredAccess &= ~PROCESS_VM_WRITE;
}
}
}
return OB_PREOP_SUCCESS;
}

char* GetProcessImageNameByProcessID(ULONG ulProcessID)
{
NTSTATUS Status;
PEPROCESS EProcess = NULL;
Status = PsLookupProcessByProcessId((HANDLE)ulProcessID, &EProcess); //EPROCESS
//通过句柄获取EProcess
if (!NT_SUCCESS(Status))
{
return FALSE;
}
ObDereferenceObject(EProcess);
//通过EProcess获得进程名称
return (char*)PsGetProcessImageFileName(EProcess);
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
ObUnRegisterCallbacks(g_pRegiHandle);
}

4.编写C语言代码使用回调函数保护进程不被读写,使回调开启与关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <ntifs.h>

#define Log(X) DbgPrint("qi:"X##)
#define PROCESS_TERMINATE (0x0001)
#define PROCESS_CREATE_THREAD (0x0002)
#define PROCESS_SET_SESSIONID (0x0004)
#define PROCESS_VM_OPERATION (0x0008)
#define PROCESS_VM_READ (0x0010)
#define PROCESS_VM_WRITE (0x0020)
#define PROCESS_DUP_HANDLE (0x0040)
#define PROCESS_CREATE_PROCESS (0x0080)
#define PROCESS_SET_QUOTA (0x0100)
#define PROCESS_SET_INFORMATION (0x0200)
#define PROCESS_QUERY_INFORMATION (0x0400)
#define PROCESS_SUSPEND_RESUME (0x0800)
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
#define PROCESS_SET_LIMITED_INFORMATION (0X2000)

#define DELAY_ONE_MICRSECOND (-10)
#define DELAY_ONE_MILLISECOND (1000 * DELAY_ONE_MICRSECOND)


//回调拦截读写与结束进程
OB_PREOP_CALLBACK_STATUS PobPreOperationCallback(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION OperationInformation)
{
if (OperationInformation->Operation == OB_OPERATION_HANDLE_CREATE && OperationInformation->ObjectType == *PsProcessType)
{
HANDLE cmpid = PsGetProcessId((PEPROCESS)OperationInformation->Object);
if (cmpid == *(PHANDLE)RegistrationContext)
{
PACCESS_MASK OriginalDesiredAccess = &OperationInformation->Parameters->CreateHandleInformation.OriginalDesiredAccess;
PACCESS_MASK DesiredAccess = &OperationInformation->Parameters->CreateHandleInformation.DesiredAccess;
if (*OriginalDesiredAccess & PROCESS_TERMINATE == PROCESS_TERMINATE)
{
*DesiredAccess &= ~PROCESS_TERMINATE;
Log("R0:拦截了一次结束进程\n");
}
if (*OriginalDesiredAccess & PROCESS_VM_READ == PROCESS_VM_READ)
{
*DesiredAccess &= ~PROCESS_VM_READ;
Log("R0:拦截了一次读操作\n");
}
if (*OriginalDesiredAccess & PROCESS_TERMINATE == PROCESS_TERMINATE)
{
*DesiredAccess &= ~PROCESS_VM_WRITE;
Log("R0:拦截了一次写操作\n");
}

}
}
return OB_PREOP_SUCCESS;
}

HANDLE g_pid = 0;
PVOID g_callback = NULL;
BOOLEAN g_success = FALSE;
//创建回调函数保护进程
VOID create_callback(HANDLE pid)
{
//CHAR pname[] = "notepad.exe";
//PHANDLE pid=0;
//NTSTATUS status = GetProcessId_byName(pname,sizeof(pname),pid);
if (g_callback == TRUE)
{
Log("already hava create_callback,Please uninstall First\n");
return;
}
g_pid = pid;
if (!g_pid)
{
Log("g_pid ERROR\n");
return;
}

OB_CALLBACK_REGISTRATION ob = { 0 };
ob.Version = OB_FLT_REGISTRATION_VERSION;
ob.OperationRegistrationCount = 1;
RtlInitUnicodeString(&ob.Altitude, L"366665");
ob.RegistrationContext = &g_pid;
OB_OPERATION_REGISTRATION operation = { 0 };

operation.ObjectType = PsProcessType;
operation.Operations = OB_OPERATION_HANDLE_CREATE;
operation.PreOperation = PobPreOperationCallback;

ob.OperationRegistration = &operation;
if (NT_SUCCESS(ObRegisterCallbacks(&ob, &g_callback)))
{
g_success = TRUE;
}
else
Log("error\n");

}

//卸载调用的保护进程回调函数
char* UnloadCallback()
{
char* str = NULL;
//卸载回调
if (g_success)
{
ObUnRegisterCallbacks(g_callback);
g_success = FALSE;
str = "R0:卸载回调成功";
}
else
{
str = "R0:无需卸载";
}
return str;
}




NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
UnloadCallback();
Log("Unloaded Successfully!");
return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = UnloadDriver;
Log("Loaded Successfully!");
//回调验证签名
*(PULONG)((ULONG64)pDriverObject->DriverSection + 0x68) |= 0x20;
create_callback(0x4c8);
return STATUS_SUCCESS;
}

5.回调设置句柄权限保护进程,设置为0会直接关闭打开的句柄

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//https://www.bilibili.com/video/BV1814y1U7hd/?spm_id_from=333.999.0.0&vd_source=5777815674b3f4e5c227299a8de61c9b
#include <ntifs.h>
#include <stdbool.h>
#define Log(X) DbgPrint("qi:"X##)

OB_PREOP_CALLBACK_STATUS Pre(void*, POB_PRE_OPERATION_INFORMATION info);
bool RegisterCallback();

VOID DriverUnload(PDRIVER_OBJECT pdriver)
{
Log("unload...\n");
return;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING path)
{
DriverObject->DriverUnload = DriverUnload;
DbgPrint("loading...\n");
RegisterCallback();
return STATUS_SUCCESS;
}

void* handle;

OB_PREOP_CALLBACK_STATUS Pre(void*p, POB_PRE_OPERATION_INFORMATION info)
{
PEPROCESS process = (PEPROCESS)(info->Object);
char* fileName = (char*)((LONG_PTR)process + 0x5a8);
if (strcmp(fileName, "Notepad.exe") == 0)
{
info->Parameters->CreateHandleInformation.DesiredAccess = 0;
}
return OB_PREOP_SUCCESS;
}

bool RegisterCallback()
{
OB_OPERATION_REGISTRATION operations[] =
{
{ PsProcessType,
OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
Pre,
NULL
}
};
OB_CALLBACK_REGISTRATION reg = {
OB_FLT_REGISTRATION_VERSION,
1,
RTL_CONSTANT_STRING(L"12345.678"),
NULL,
operations
};

auto status = ObRegisterCallbacks(&reg, &handle);

return NT_SUCCESS(status);
}

6.保护进程不被读写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//https://tttang.com/archive/1682/
#include <ntifs.h>

#define PROCESS_TERMINATE (0x0001)
#define PROCESS_CREATE_THREAD (0x0002)
#define PROCESS_SET_SESSIONID (0x0004)
#define PROCESS_VM_OPERATION (0x0008)
#define PROCESS_VM_READ (0x0010)
#define PROCESS_VM_WRITE (0x0020)
#define PROCESS_DUP_HANDLE (0x0040)
#define PROCESS_CREATE_PROCESS (0x0080)
#define PROCESS_SET_QUOTA (0x0100)
#define PROCESS_SET_INFORMATION (0x0200)
#define PROCESS_QUERY_INFORMATION (0x0400)
#define PROCESS_SUSPEND_RESUME (0x0800)
#define PROCESS_QUERY_LIMITED_INFORMATION (0x1000)
#define PROCESS_SET_LIMITED_INFORMATION (0X2000)

#define Log(X) DbgPrint("qi:"X##)
typedef union _EXHANDLE
{
struct
{
int TagBits : 2;
int Index : 30;
} u;
void* GenericHandleOverlay;
ULONG_PTR Value;
} EXHANDLE, * PEXHANDLE;

typedef struct _HANDLE_TABLE_ENTRY // Size=16
{
union
{
ULONG_PTR VolatileLowValue; // Size=8 Offset=0
ULONG_PTR LowValue; // Size=8 Offset=0
struct _HANDLE_TABLE_ENTRY_INFO* InfoTable; // Size=8 Offset=0
struct
{
ULONG_PTR Unlocked : 1; // Size=8 Offset=0 BitOffset=0 BitCount=1
ULONG_PTR RefCnt : 16; // Size=8 Offset=0 BitOffset=1 BitCount=16
ULONG_PTR Attributes : 3; // Size=8 Offset=0 BitOffset=17 BitCount=3
ULONG_PTR ObjectPointerBits : 44; // Size=8 Offset=0 BitOffset=20 BitCount=44
};
};
union
{
ULONG_PTR HighValue; // Size=8 Offset=8
struct _HANDLE_TABLE_ENTRY* NextFreeHandleEntry; // Size=8 Offset=8
union _EXHANDLE LeafHandleValue; // Size=8 Offset=8
struct
{
ULONG GrantedAccessBits : 25; // Size=4 Offset=8 BitOffset=0 BitCount=25
ULONG NoRightsUpgrade : 1; // Size=4 Offset=8 BitOffset=25 BitCount=1
ULONG Spare : 6; // Size=4 Offset=8 BitOffset=26 BitCount=6
};
};
ULONG TypeInfo; // Size=4 Offset=12
} HANDLE_TABLE_ENTRY, * PHANDLE_TABLE_ENTRY;

BOOLEAN NTAPI enumRoutine(
IN PVOID HandleTable,
IN PHANDLE_TABLE_ENTRY HandleTableEntry,
PVOID Unknow,
IN PVOID eprocess
);

VOID ProtectProcessByEprocess(PEPROCESS protected_eprocess);

NTSTATUS DriverUnload(PDRIVER_OBJECT pdriver)
{
Log("unload...\n");
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pReg)
{
PEPROCESS eprocess = NULL;
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)0x1D24, &eprocess);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "Open process unsuccessfully!\n");
return;
}
ObDereferenceObject(eprocess);
ProtectProcessByEprocess(eprocess);
pDriver->DriverUnload = DriverUnload;
return STATUS_SUCCESS;
}

NTKERNELAPI
VOID
FASTCALL
ExfUnblockPushLock(
IN OUT PEX_PUSH_LOCK PushLock,
IN OUT PVOID WaitBlock
);
BOOLEAN NTAPI enumRoutine(
IN PVOID HandleTable,
IN PHANDLE_TABLE_ENTRY HandleTableEntry,
PVOID Unknow,
IN PVOID eprocess
)
{
BOOLEAN result = FALSE;
if (HandleTableEntry)
{

ULONG_PTR object_header = (*(PLONG_PTR)(HandleTableEntry) >> 0x10) & 0xFFFFFFFFFFFFFFF0;

ULONG_PTR object = object_header + 0x30;

// 若该对象类型为进程,则该对象为该进程的EPROCESS
if (object == (ULONG_PTR)eprocess)
{
//DbgBreakPoint();
DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "[db]:HandleTableEntry= %llx \r\n", HandleTableEntry);
HandleTableEntry->GrantedAccessBits &= ~(PROCESS_VM_READ | PROCESS_VM_WRITE);
result = TRUE;

}

}
_InterlockedExchangeAdd64(HandleTableEntry, 1); // 注意 这里必须释放,参考ObpEnumFindHandleProcedure的实现
if (*(PULONG_PTR)((ULONG_PTR)HandleTable + 0x30)) {
ExfUnblockPushLock((ULONG_PTR)HandleTable + 0x30, 0); // 同上
}
return result;
}
typedef struct _HANDLE_TABLE HANDLE_TABLE, * PHANDLE_TABLE;
typedef BOOLEAN(*EX_ENUMERATE_HANDLE_ROUTINE)(
#if !defined(_WIN7_)
IN PHANDLE_TABLE HandleTable,
#endif
IN PHANDLE_TABLE_ENTRY HandleTableEntry,
IN HANDLE Handle,
IN PVOID EnumParameter
);
NTKERNELAPI
BOOLEAN
ExEnumHandleTable(
IN PHANDLE_TABLE HandleTable,
IN EX_ENUMERATE_HANDLE_ROUTINE EnumHandleProcedure,
IN PVOID EnumParameter,
OUT PHANDLE Handle
);
VOID ProtectProcessByEprocess(PEPROCESS protected_eprocess)
{
PEPROCESS eprocess = NULL;
// 这里作为测试,直接写死CE的pid为0x464
NTSTATUS status = PsLookupProcessByProcessId((HANDLE)0x464, &eprocess);
if (!NT_SUCCESS(status))
{
KdPrint(("Open process unsuccessfully!\n"));
return;
}
// 在我使用的windows版本中 handle_table的偏移为0x418
PVOID handle_table = *(PVOID*)((LONG_PTR)eprocess + 0x418);
PVOID Handle = NULL;
ExEnumHandleTable(handle_table, enumRoutine, protected_eprocess, Handle);
ObDereferenceObject(eprocess);

}

驱动程序中的回调函数是什么?

1.驱动程序中的回调函数有什么用?

在驱动程序中,回调函数是由操作系统或硬件调用的函数,通常是由驱动程序注册并在特定事件或操作完成时被执行。回调函数在内核模式驱动程序中非常常见,用于异步操作或事件处理。

回调函数的作用:

  1. 异步处理:
    在驱动程序中,许多操作是异步的。例如,设备 I/O 操作(如读取、写入、设备控制)可能会在后台执行。回调函数通常会被用来通知驱动程序某个操作完成,或者在操作发生错误时处理相应的情况。
  2. 事件处理:
    驱动程序可能会注册回调函数来响应特定的事件。例如,当硬件设备完成某个操作或发生状态变化时,操作系统会通过调用回调函数来通知驱动程序。常见的事件包括中断处理、定时器超时、设备连接或断开等。
  3. 中断处理:
    在硬件设备驱动程序中,中断回调函数用于处理中断请求。当硬件设备发出中断信号时,操作系统会调用与该中断相关联的回调函数来处理事件。
  4. 资源管理:
    驱动程序可能需要管理设备的资源(如内存、端口等)。回调函数有时用于在资源分配或释放时执行清理工作,例如释放内存、关闭句柄、注销回调等。
  5. 回调通知:
    回调函数还用于驱动程序与应用程序或其他内核模块之间的通信。当驱动程序完成某些操作时,回调函数会通知应用程序或其他模块处理后续工作。

回调函数的常见场景:

  1. I/O 操作的完成回调:
    在异步 I/O 操作(如 ReadFileWriteFile)中,回调函数在操作完成时被触发。例如,驱动程序可以注册一个回调函数,在数据传输完成后处理数据。

    1
    2
    3
    4
    5
    6
    7
    cCopyEditNTSTATUS MyDriverReadCallback(PDEVICE_OBJECT DeviceObject, PIRP Irp)
    {
    // 处理读操作完成后的逻辑
    DbgPrint("Read completed\n");
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
    }
  2. 中断回调:
    当硬件设备发出中断时,驱动程序会注册一个中断回调函数来处理该中断。

    1
    2
    3
    4
    5
    6
    cCopyEditNTSTATUS MyDriverInterruptCallback(PKINTERRUPT Interrupt, PVOID Context)
    {
    // 处理中断
    DbgPrint("Interrupt occurred\n");
    return STATUS_SUCCESS;
    }
  3. 定时器回调:
    驱动程序可能需要定期执行任务。定时器回调函数在定时器到期时被调用。

    1
    2
    3
    4
    5
    cCopyEditVOID MyDriverTimerCallback(PDEVICE_OBJECT DeviceObject, PVOID Context)
    {
    // 执行定时任务
    DbgPrint("Timer callback triggered\n");
    }
  4. 设备控制操作的回调:
    驱动程序处理来自用户态或其他驱动的控制命令时,可以使用回调函数来处理特定的命令。

    1
    2
    3
    4
    5
    6
    7
    cCopyEditNTSTATUS MyDriverControlCallback(PDEVICE_OBJECT DeviceObject, PIRP Irp)
    {
    // 处理设备控制操作
    DbgPrint("Control operation received\n");
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
    }

回调函数的类型:

  • 同步回调(Synchronous Callback):
    在这种情况下,回调函数会在当前执行上下文中被调用,并且在回调函数执行完毕之前,其他代码不能继续执行。同步回调通常用于简单的任务,或者那些需要在执行完成之前返回结果的操作。
  • 异步回调(Asynchronous Callback):
    在这种情况下,回调函数是在后台执行的,调用者在回调函数完成之前可以继续执行其他任务。常见的异步回调应用包括 I/O 操作和中断处理。

总结:

回调函数在驱动程序中非常重要,主要用于处理异步事件、硬件中断、I/O 操作和资源管理等任务。它们通过通知驱动程序某些操作的完成,帮助实现高效的事件驱动处理,尤其在涉及异步和中断的场景中不可或缺。

2.手动注册的回调函数有什么用?

手动注册的回调函数在驱动程序中通常用于处理特定的事件或任务。它们是在驱动程序开发时由开发者显式注册的函数,当相关事件发生时,操作系统或硬件会调用这些函数。手动注册的回调函数主要用于异步操作、资源管理、事件通知等场景。

手动注册回调函数的作用:

  1. 异步操作的处理: 在异步操作中,驱动程序通常会注册一个回调函数来处理操作完成后的结果。例如,在执行 I/O 操作时,驱动程序可能会注册一个回调函数,当 I/O 操作完成时,操作系统会通知驱动程序,并调用回调函数来处理返回的结果。这有助于驱动程序实现非阻塞的 I/O 操作。
  2. 事件驱动: 驱动程序通过手动注册回调函数来响应硬件或系统事件。例如,当硬件设备发出中断信号时,驱动程序会注册一个中断回调函数,用于处理中断事件。驱动程序通过这种方式能够动态响应硬件状态的变化,而不需要在程序的其他部分主动轮询硬件状态。
  3. 硬件资源管理: 对于需要管理硬件资源的驱动程序,可以通过注册回调函数来处理特定的资源管理任务。例如,驱动程序可以注册一个回调函数来处理设备的插入和拔出操作,这样驱动程序就可以在硬件状态变化时及时做出响应。
  4. 定时器和延迟任务: 驱动程序可以手动注册定时器回调函数,用于执行周期性的任务或延迟任务。例如,如果需要定期检查硬件设备的状态,驱动程序可以注册一个回调函数,每隔一定时间由系统调用该函数来执行任务。
  5. 自定义操作的处理: 驱动程序可以注册自定义的回调函数来处理特定的设备控制命令或用户请求。例如,某些设备可能会定义特定的控制命令,驱动程序需要注册回调函数来处理这些命令并执行相应的操作。

常见的手动注册回调函数的场景:

  1. 中断回调:

在硬件驱动程序中,当硬件设备发出中断信号时,驱动程序需要注册一个中断回调函数来处理中断。通过这种方式,驱动程序能够及时响应硬件设备的状态变化。

示例:注册中断回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
cCopyEditNTSTATUS MyDriverInitialize(PDRIVER_OBJECT DriverObject)
{
// 假设设备中断资源已初始化
NTSTATUS status;
PKINTERRUPT InterruptObject = NULL;

// 注册回调函数
status = IoConnectInterrupt(
&InterruptObject, // 中断对象
MyDriverInterruptCallback, // 回调函数
NULL, // 回调函数的上下文(可选)
NULL, // 中断处理的同步上下文(可选)
IRQ_LEVEL, // 中断级别
IRQ_PRIORITY, // 中断优先级
TRUE // 是否共享中断
);
return status;
}

NTSTATUS MyDriverInterruptCallback(PKINTERRUPT Interrupt, PVOID Context)
{
// 中断处理逻辑
DbgPrint("Interrupt occurred\n");
return STATUS_SUCCESS;
}
  1. 定时器回调:

驱动程序可能需要定期检查某些条件或状态。在这种情况下,可以注册一个定时器回调函数,在每个定时间隔后执行某些任务。

示例:定时器回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
cCopyEditVOID MyDriverTimerCallback(PDEVICE_OBJECT DeviceObject, PVOID Context)
{
// 执行定时任务
DbgPrint("Timer callback triggered\n");
}

NTSTATUS MyDriverInitialize(PDRIVER_OBJECT DriverObject)
{
NTSTATUS status;
PKEVENT event;
LARGE_INTEGER interval;

interval.QuadPart = -10000000L; // 设置定时器的间隔时间(100ms)

// 创建定时器
status = KeSetTimerEx(
&Timer, // 定时器对象
interval, // 时间间隔
100, // 定时器周期
MyDriverTimerCallback, // 回调函数
NULL // 上下文(可选)
);
return status;
}
  1. 资源管理回调:

驱动程序可能需要处理设备的插拔、资源分配或释放等。可以通过注册回调函数来响应这些事件,确保设备在插入时初始化,在拔出时清理资源。

示例:设备插入和拔出回调

1
2
3
4
5
6
7
8
9
10
11
12
13
cCopyEditNTSTATUS MyDriverDeviceAddCallback(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT DeviceObject)
{
// 设备插入时初始化资源
DbgPrint("Device added: %p\n", DeviceObject);
return STATUS_SUCCESS;
}

NTSTATUS MyDriverUnload(PDRIVER_OBJECT DriverObject)
{
// 设备拔出时清理资源
DbgPrint("Driver unloaded\n");
return STATUS_SUCCESS;
}
  1. I/O 完成回调:

在异步 I/O 操作中,驱动程序可以注册一个回调函数,以在 I/O 操作完成时进行处理。

示例:I/O 完成回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cCopyEditNTSTATUS MyDriverIoCompletionCallback(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context)
{
// 处理 I/O 操作完成后的逻辑
DbgPrint("I/O completed: %p\n", Irp);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}

NTSTATUS MyDriverRead(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
// 注册 I/O 完成回调
IoSetCompletionRoutine(Irp, MyDriverIoCompletionCallback, NULL, TRUE, TRUE, TRUE);
IoCallDriver(DeviceObject, Irp);
return STATUS_PENDING;
}

总结:

手动注册的回调函数是驱动程序中处理异步事件、硬件中断、资源管理等任务的重要工具。通过注册回调函数,驱动程序能够响应外部事件、执行特定任务,并在操作系统或硬件设备发生变化时及时做出反应。这些回调函数能够使驱动程序更加灵活和高效,尤其是在处理异步操作和硬件交互时。

3.监控进程创建,线程创建的回调函数怎么注册?

在 Windows 驱动程序中,监控进程和线程创建通常通过回调函数来实现。Windows 提供了一些机制来注册回调函数,以便在特定事件发生时进行处理。对于进程和线程的创建,通常可以使用 Process Notification CallbackThread Notification Callback

  1. 监控进程创建与结束

监控进程的创建和结束,Windows 提供了 PsSetCreateProcessNotifyRoutineEx 函数来注册进程创建和退出的回调函数。

注册进程回调:

使用 PsSetCreateProcessNotifyRoutineEx 注册进程创建和退出的回调函数。这个回调函数会在进程创建和退出时被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
cCopyEdit#include <ntddk.h>
#include <wdf.h>

NTSTATUS MyProcessNotifyCallback(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
UNREFERENCED_PARAMETER(Process);

if (CreateInfo != NULL) {
// 进程创建事件
DbgPrint("进程创建: %wZ\n", &CreateInfo->ImageFileName);
} else {
// 进程退出事件
DbgPrint("进程退出: %u\n", ProcessId);
}

return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status;

// 注册进程回调
status = PsSetCreateProcessNotifyRoutineEx(MyProcessNotifyCallback, FALSE);
if (!NT_SUCCESS(status)) {
DbgPrint("注册进程回调失败,错误码: 0x%X\n", status);
return status;
}

DbgPrint("驱动程序加载成功!\n");
return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
// 卸载驱动时注销回调
PsSetCreateProcessNotifyRoutineEx(MyProcessNotifyCallback, TRUE);
DbgPrint("驱动程序卸载成功!\n");
}

说明:

  • PsSetCreateProcessNotifyRoutineEx:注册回调函数,用于监控进程的创建与退出。
    • 第一个参数是回调函数的地址,第二个参数表示是否卸载回调函数。
    • 如果 CreateInfoNULL,则表示进程创建;如果为 NULL,则表示进程退出。
  • DriverUnload 中,需要注销该回调函数。
  1. 监控线程创建与结束

监控线程创建与结束,Windows 提供了 PsSetCreateThreadNotifyRoutine 函数来注册线程创建和退出的回调函数。

注册线程回调:

使用 PsSetCreateThreadNotifyRoutine 注册线程创建和退出的回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
cCopyEdit#include <ntddk.h>
#include <wdf.h>

NTSTATUS MyThreadNotifyCallback(PEPROCESS Process, THREAD_CREATE_FLAGS CreateFlags, PVOID StartAddress)
{
UNREFERENCED_PARAMETER(Process);
UNREFERENCED_PARAMETER(CreateFlags);
UNREFERENCED_PARAMETER(StartAddress);

DbgPrint("线程创建/退出通知\n");
return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status;

// 注册线程回调
status = PsSetCreateThreadNotifyRoutine(MyThreadNotifyCallback);
if (!NT_SUCCESS(status)) {
DbgPrint("注册线程回调失败,错误码: 0x%X\n", status);
return status;
}

DbgPrint("驱动程序加载成功!\n");
return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
// 卸载驱动时注销回调
PsRemoveCreateThreadNotifyRoutine(MyThreadNotifyCallback);
DbgPrint("驱动程序卸载成功!\n");
}

说明:

  • PsSetCreateThreadNotifyRoutine:注册线程创建和退出的回调函数。
    • 回调函数会在每次线程创建或结束时被调用。
  • DriverUnload 中,需要调用 PsRemoveCreateThreadNotifyRoutine 卸载该回调。
  1. 总结
  • 进程回调:使用 PsSetCreateProcessNotifyRoutineEx 来注册进程创建与退出的回调。
  • 线程回调:使用 PsSetCreateThreadNotifyRoutine 来注册线程创建与退出的回调。

通过这两种回调机制,驱动程序可以实时监控系统中的进程和线程的生命周期,并做出相应的处理。这种机制在实现安全监控、调试工具、或者一些高级功能时非常有用。

4.进程创建回调中的参数不用手动传输吗?CreateInfo中有哪些信息?

进程创建回调 中,CreateInfo 参数提供了进程创建时的详细信息,并且这个参数是由 Windows 内核自动填充的,你不需要手动传输它。这个结构体包含了进程创建时的相关信息,特别是关于进程映像(比如进程的可执行文件)和启动参数等。

PPS_CREATE_NOTIFY_INFO` 结构体

PPS_CREATE_NOTIFY_INFO 是一个结构体,它包含了创建进程时所需的各种信息。以下是该结构体的定义:

1
2
3
4
5
6
7
8
9
cCopyEdittypedef struct _PS_CREATE_NOTIFY_INFO {
ULONG Size; // 结构体的大小
ULONG Reserved; // 保留字段
BOOLEAN ImageFileNameIsUtf8; // 如果为TRUE,表示图像文件名为UTF-8编码
UNICODE_STRING ImageFileName; // 图像文件名(即进程的可执行文件名)
HANDLE ParentProcessId; // 父进程的ID
HANDLE ParentProcessHandle; // 父进程的句柄
ULONG Flags; // 进程启动标志(如是否为32位应用程序)
} PS_CREATE_NOTIFY_INFO, *PPS_CREATE_NOTIFY_INFO;

PS_CREATE_NOTIFY_INFO 结构体字段解释

  1. Size:
    • 表示结构体的大小,通常在使用该结构时需要设置此字段。Windows 内核会用它来确认结构体的大小是否符合要求。
  2. Reserved:
    • 这是一个保留字段,当前没有用到,可以设置为 0。
  3. ImageFileNameIsUtf8:
    • 如果该字段为 TRUE,则说明 ImageFileName 使用 UTF-8 编码,否则是一个 Unicode 字符串。你可以根据该标志判断如何处理 ImageFileName
  4. ImageFileName:
    • 这是进程的可执行文件名。Windows 会在进程创建时将启动的可执行文件的路径(包括文件名)保存在此字段中,通常用于确定正在启动的进程是哪一个。
  5. ParentProcessId:
    • 父进程的 ID。它是一个 HANDLE 类型,表示创建此进程的父进程的 ID。这个信息通常用于追踪进程之间的关系。
  6. ParentProcessHandle:
    • 父进程的句柄,表示父进程的句柄。你可以通过该句柄进一步操作父进程(如打开父进程的句柄)。
  7. Flags:
    • 这个字段包含了进程的一些标志信息。例如,标志可以指示该进程是否为 32 位应用程序、是否是系统进程等。

示例代码

以下是一个修改后的回调函数示例,展示了如何在进程创建时访问和打印 CreateInfo 中的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
cCopyEdit#include <ntddk.h>
#include <wdf.h>

NTSTATUS MyProcessNotifyCallback(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
UNREFERENCED_PARAMETER(Process);

if (CreateInfo != NULL) {
// 进程创建时的回调
DbgPrint("进程创建: %wZ\n", &CreateInfo->ImageFileName); // 打印进程的可执行文件名
DbgPrint("父进程ID: %p\n", CreateInfo->ParentProcessId); // 打印父进程ID

// 如果进程名是UTF8编码
if (CreateInfo->ImageFileNameIsUtf8) {
DbgPrint("进程名称是UTF8编码\n");
}
} else {
// 进程退出时的回调
DbgPrint("进程退出: %u\n", ProcessId);
}

return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status;

// 注册进程回调
status = PsSetCreateProcessNotifyRoutineEx(MyProcessNotifyCallback, FALSE);
if (!NT_SUCCESS(status)) {
DbgPrint("注册进程回调失败,错误码: 0x%X\n", status);
return status;
}

DbgPrint("驱动程序加载成功!\n");
return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
// 卸载驱动时注销回调
PsSetCreateProcessNotifyRoutineEx(MyProcessNotifyCallback, TRUE);
DbgPrint("驱动程序卸载成功!\n");
}

说明

  • MyProcessNotifyCallback 回调函数中,CreateInfo 包含了进程创建时的详细信息。你可以通过 CreateInfo->ImageFileName 获取到进程的可执行文件名,通过 CreateInfo->ParentProcessId 获取父进程的 ID。
  • CreateInfo->ImageFileNameIsUtf8 可以判断 ImageFileName 是否使用 UTF-8 编码。
  • 通过 PsSetCreateProcessNotifyRoutineEx 注册该回调函数时,FALSE 表示注册进程创建通知,TRUE 则表示注销回调。

总结

CreateInfo 提供了非常有用的进程创建信息,允许驱动程序在进程启动时获取进程的详细元数据(例如可执行文件的路径、父进程信息等)。这些信息可以用来实现进程监控、审计、调试等功能

5.有没有打开进程句柄的回调?

是的,Windows 提供了一个用于监视进程句柄打开的回调函数,称为 **PsSetCreateProcessNotifyRoutineEx**。然而,关于专门的“打开进程句柄”的回调函数,Windows 内核没有直接提供一个类似的机制来监控用户模式对进程句柄的操作(如 OpenProcess 函数调用)。但可以通过一些间接方法来获取类似的功能:

  1. PsSetCreateProcessNotifyRoutineEx - 监控进程创建与退出

这是最常见的方法,它会在进程创建时通知驱动,虽然它不能直接监控打开的进程句柄,但它能提供有关进程生命周期的信息(包括进程的启动、终止等)。

  1. 监控 NtOpenProcess 系统调用(高级方法)

NtOpenProcess 是 Windows 内核中用于打开进程句柄的内部系统调用。虽然没有提供专门的 API 来直接监控进程句柄的打开,但可以通过以下方式来间接监控这些操作:

  • Hooking 方法:你可以通过钩取 NtOpenProcessZwOpenProcess 函数来监控所有进程句柄的打开。该方法相对复杂,并且通常不建议在生产环境中使用,因为这会改变系统调用的行为。可以通过内核模块拦截系统调用,或者使用驱动程序和其他工具来钩取该函数。
  • Windows 事件日志:Windows 安全日志可以记录系统的各种安全事件,包括进程句柄的访问,尤其是在启用了审核功能时。
  1. 利用 Object ManagerHANDLE 监控

通过 Object ManagerHANDLE 系统结构,你可以查看哪些对象(例如进程对象)正在被打开。这通常涉及一些内核级别的 API,例如查询和拦截句柄创建的过程,或者通过钩取 NtOpenObject

使用 PsSetCreateProcessNotifyRoutineEx 示例

虽然没有直接的进程句柄回调,仍然可以通过进程创建的回调函数来获取有关进程的信息,并对进程进行监控。在创建进程时,我们能够知道哪些进程会被启动,并在适当的时候获取进程句柄。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
cCopyEdit#include <ntddk.h>

NTSTATUS MyProcessNotifyCallback(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
if (CreateInfo != NULL) {
DbgPrint("进程创建: %wZ\n", &CreateInfo->ImageFileName); // 打印进程的可执行文件名
DbgPrint("父进程ID: %p\n", CreateInfo->ParentProcessId); // 打印父进程ID
} else {
DbgPrint("进程退出: %p\n", ProcessId); // 打印退出的进程ID
}

return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
UNREFERENCED_PARAMETER(RegistryPath);
NTSTATUS status;

// 注册进程回调
status = PsSetCreateProcessNotifyRoutineEx(MyProcessNotifyCallback, FALSE);
if (!NT_SUCCESS(status)) {
DbgPrint("注册进程回调失败,错误码: 0x%X\n", status);
return status;
}

DbgPrint("驱动程序加载成功!\n");
return STATUS_SUCCESS;
}

VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
// 卸载驱动时注销回调
PsSetCreateProcessNotifyRoutineEx(MyProcessNotifyCallback, TRUE);
DbgPrint("驱动程序卸载成功!\n");
}

在该例子中,PsSetCreateProcessNotifyRoutineEx 用于在进程创建时通知驱动,虽然它不能直接监控进程句柄的打开,但它允许你在进程创建时捕获进程的信息。

监控进程句柄创建的高级方法(钩取 NtOpenProcess

如前所述,NtOpenProcess 是打开进程句柄的系统调用。通过钩取该函数,你可以在用户代码调用时获取有关进程句柄的信息。一个简单的钩取方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cCopyEdittypedef NTSTATUS(*pNtOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId);

pNtOpenProcess OriginalNtOpenProcess = NULL;

NTSTATUS HookedNtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId)
{
DbgPrint("进程句柄打开,PID: %u\n", ClientId->UniqueProcess); // 打印进程ID
return OriginalNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId);
}

VOID HookNtOpenProcess()
{
UNICODE_STRING functionName = RTL_CONSTANT_STRING(L"NtOpenProcess");
OriginalNtOpenProcess = (pNtOpenProcess)MmGetSystemRoutineAddress(&functionName);
if (OriginalNtOpenProcess) {
// 使用钩取逻辑来替代原函数
}
}

这种方法通常涉及较复杂的内核编程技巧,包括系统调用钩取和内存保护等,因此需要谨慎使用。

总结

虽然 Windows 不提供直接监控进程句柄打开的回调函数,但你可以使用以下几种方法来间接地获取相关信息:

  • 进程创建通知回调 (PsSetCreateProcessNotifyRoutineEx):通过监控进程的创建和退出,获取进程的元数据。
  • 钩取 NtOpenProcess 系统调用:可以间接监控进程句柄的打开,但这通常会涉及修改内核代码或使用更底层的技巧。
  • Windows 安全日志和审核机制:可以通过启用 Windows 审核来记录进程的打开事件。

6.可以在回调函数中降低其他程序打开某个程序的进程句柄权限吗?

在 Windows 内核驱动中,回调函数本身不能直接修改其他进程打开的句柄的权限。操作系统的安全机制严格控制了用户态进程与内核态驱动之间的交互,尤其是关于进程句柄权限的部分。

然而,你可以通过某些间接的方式来影响进程句柄的打开,例如:

  1. 修改访问控制列表 (ACLs)

你可以通过改变进程对象的权限(Access Control List, ACL)来控制哪些用户或进程可以访问该进程。这通常是通过设置 OBJECT_ATTRIBUTES 中的 SecurityDescriptor 来完成的。

在创建进程或打开进程句柄的过程中,你可以通过修改这些 SecurityDescriptor 来指定哪些权限可以被授予。这样,只有具备适当权限的进程才能成功打开该进程的句柄。

  1. 使用 PsSetCreateProcessNotifyRoutineEx 限制进程创建

你可以在进程创建时,拦截该事件并通过修改传入的 CreateInfo 对象来阻止某些程序创建进程。这并不是直接限制“打开进程句柄”的权限,而是通过阻止进程的创建来间接实现。

举个例子,你可以在回调函数中检查 CreateInfo->ImageFileName(进程的可执行文件名),如果它符合某些条件,可以选择拒绝该进程的创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cCopyEditNTSTATUS MyProcessNotifyCallback(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo)
{
if (CreateInfo != NULL) {
DbgPrint("进程创建: %wZ\n", &CreateInfo->ImageFileName);

// 如果进程的路径符合某些条件,拒绝其创建
if (RtlEqualUnicodeString(&CreateInfo->ImageFileName, &ForbiddenProcess, TRUE)) {
// 可以在此处阻止进程的创建(通过返回一个错误状态)
CreateInfo->CreationStatus = STATUS_ACCESS_DENIED;
DbgPrint("禁止进程创建: %wZ\n", &CreateInfo->ImageFileName);
}
}
return STATUS_SUCCESS;
}
  1. 限制进程句柄的访问权限

尽管无法直接在回调函数中修改其他进程句柄的权限,但可以通过其他手段控制进程句柄的权限,例如:

  • 在使用 ZwOpenProcessOpenProcess 等 API 时,如果你能修改进程对象的安全描述符(例如通过 SetSecurityInfo 或通过控制对象的 ACL),你可以限制哪些用户或进程能访问该对象。
  • 通过修改进程的 AccessMask 权限,可以阻止某些进程或用户访问进程句柄。
  1. 拦截系统调用并修改权限

通过钩取 NtOpenProcessZwOpenProcess 系统调用,你可以在进程句柄打开时检查请求的访问权限,并拒绝不符合条件的访问。此方法的关键在于在内核层拦截这些调用,并根据你的要求修改访问权限。

1
2
3
4
5
6
7
8
cCopyEditNTSTATUS HookedNtOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId)
{
// 检查进程ID或进程名
if (SomeConditionToBlockAccess(ClientId)) {
return STATUS_ACCESS_DENIED; // 阻止不符合条件的访问
}
return OriginalNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); // 否则正常调用
}
  1. 使用安全标识符 (SID) 和标识权限

通过 PsSetCreateProcessNotifyRoutineEx 或其他回调,你可以设置进程的 安全标识符(SID)或通过访问控制列表(ACL)来限制对某些进程的访问。这也可以间接影响其他进程是否能够获取到对该进程的句柄。

例如,在创建进程时,您可以通过设置 OBJECT_ATTRIBUTESSecurityDescriptor 来指定 ACL:

1
2
3
4
5
6
7
8
9
10
11
cCopyEditOBJECT_ATTRIBUTES objAttr;
InitializeObjectAttributes(&objAttr, &ObjectName, OBJ_KERNEL_HANDLE, NULL, NULL);

// 设置SecurityDescriptor并控制权限
SECURITY_DESCRIPTOR securityDescriptor;
InitializeSecurityDescriptor(&securityDescriptor, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&securityDescriptor, TRUE, NULL, FALSE); // 设定访问控制

objAttr.SecurityDescriptor = &securityDescriptor;

NTSTATUS status = ZwOpenProcess(&ProcessHandle, DesiredAccess, &objAttr, &ClientId);

总结

  1. 直接通过回调函数修改进程句柄的权限是不可能的,回调主要是用于监控和拦截进程创建、终止等操作。
  2. 你可以通过 修改安全描述符控制进程的访问权限 来间接影响其他进程打开某个进程句柄。
  3. 通过钩取 NtOpenProcess 等 API,你可以在进程句柄打开时拦截请求,并拒绝不符合条件的访问。
  4. 使用 PsSetCreateProcessNotifyRoutineEx 等回调函数可以阻止进程的创建,但无法修改已创建进程的句柄权限。

因此,虽然没有直接的API来改变已经打开的进程句柄的权限,但通过其他手段,如拦截和修改安全策略、访问控制列表、或拦截系统调用等,可以达到类似的效果。

7.pOperationInformation与Parameters与CreateHandleInformation中分别有哪些变量?

pOperationInformationParametersCreateHandleInformation 是在 Object Manager 回调函数中用于传递有关操作信息的结构体。这些结构体中的各个字段帮助驱动程序了解句柄操作的上下文,包括操作的对象、操作的类型和句柄访问权限等。

以下是这些结构体的详细介绍:

  1. POB_PRE_OPERATION_INFORMATION (pOperationInformation)

POB_PRE_OPERATION_INFORMATION 是传递给回调函数的参数,包含了关于正在进行的操作的详细信息。该结构体通常具有以下字段:

1
2
3
4
5
6
7
cCopyEdittypedef struct _OB_PRE_OPERATION_INFORMATION {
PVOID Object; // 对象的指针(例如,指向进程的 EPROCESS)
HANDLE Handle; // 操作的句柄
ACCESS_MASK DesiredAccess; // 期望的访问权限(如 PROCESS_TERMINATE,PROCESS_VM_READ 等)
ULONG Operation; // 操作类型(如 OB_OPERATION_HANDLE_CREATE)
PVOID Parameters; // 参数指针,具体结构与操作类型有关
} OB_PRE_OPERATION_INFORMATION, *POB_PRE_OPERATION_INFORMATION;
  1. pOperationInformation->Parameters

Parameters 是指向与特定操作类型相关的数据结构。在不同的操作类型下,Parameters 指向不同的结构体,通常这些结构体用于存储更具体的操作数据。

例如,OB_OPERATION_HANDLE_CREATEOB_OPERATION_HANDLE_DUPLICATE 操作会使用 OB_CREATE_HANDLE_INFORMATION 结构体。其定义如下:

  1. OB_CREATE_HANDLE_INFORMATION (CreateHandleInformation)

对于 OB_OPERATION_HANDLE_CREATEOB_OPERATION_HANDLE_DUPLICATE 类型的操作,Parameters 会指向 OB_CREATE_HANDLE_INFORMATION 结构体。这个结构体包含了操作的详细信息,包括目标句柄的访问权限、创建信息等。

1
2
3
4
5
6
7
8
9
cCopyEdittypedef struct _OB_CREATE_HANDLE_INFORMATION {
ACCESS_MASK DesiredAccess; // 请求的访问权限(如 PROCESS_VM_READ, PROCESS_TERMINATE 等)
ACCESS_MASK OriginalDesiredAccess; // 最初请求的权限
PVOID Object; // 对象指针,通常是进程对象的指针
ULONG HandleAttributes; // 句柄属性(例如 HANDLE_FLAG_PROTECT_FROM_CLOSE)
ULONG GrantedAccess; // 实际授予的访问权限
ULONG ProcessId; // 进程 ID
ULONG TargetPid; // 被操作的目标进程 ID
} OB_CREATE_HANDLE_INFORMATION, *POB_CREATE_HANDLE_INFORMATION;

主要字段:

  • **DesiredAccess**:此字段表示请求的访问权限,例如 PROCESS_TERMINATEPROCESS_VM_READ 等。这是通过句柄请求的权限。
  • **OriginalDesiredAccess**:这是请求操作时最初希望的访问权限。该字段与 DesiredAccess 的差异表明实际请求和最终授予之间的权限差异。
  • **Object**:指向被操作的对象,例如进程对象。
  • **HandleAttributes**:句柄的属性,通常使用的是句柄保护等属性。
  • **GrantedAccess**:实际授予的访问权限,表示执行操作后会允许的权限。
  • **ProcessId**:操作所在的进程 ID。
  • **TargetPid**:目标进程的 ID(被访问的进程)。
  1. pOperationInformation->Operation

Operation 字段表示正在进行的操作类型。它是 OB_OPERATION_REGISTRATION 中操作类型的标志之一。常见的操作类型包括:

  • OB_OPERATION_HANDLE_CREATE:创建句柄(如打开进程、创建文件等)。
  • OB_OPERATION_HANDLE_DUPLICATE:复制句柄。
  • OB_OPERATION_HANDLE_CLOSE:关闭句柄。

示例:

如果你注册了针对 OB_OPERATION_HANDLE_CREATE 的回调,则 Parameters 会指向 OB_CREATE_HANDLE_INFORMATION 结构体,帮助你获取有关创建句柄操作的信息,如期望的权限等。

例子:

在回调函数中,你可以根据 DesiredAccessOriginalDesiredAccess 修改进程句柄的权限。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
cCopyEditOB_PREOP_CALLBACK_STATUS precessCallBack(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION pOperationInformation)
{
UNREFERENCED_PARAMETER(RegistrationContext);

// 获取进程 ID 和进程名称
HANDLE pid = PsGetProcessId((PEPROCESS)pOperationInformation->Object);
char szProcName[17] = { 0 };
strcpy(szProcName, PsGetProcessImageFileName((PEPROCESS)pOperationInformation->Object));

// 判断是否是目标进程
if (!_stricmp(szProcName, "calc.exe")) // 比如 "calc.exe"
{
// 如果是创建句柄操作(例如打开进程)
if (pOperationInformation->Operation == OB_OPERATION_HANDLE_CREATE)
{
// 获取与创建句柄相关的权限信息
POB_CREATE_HANDLE_INFORMATION createInfo = pOperationInformation->Parameters;

// 检查并修改句柄的访问权限
if ((createInfo->OriginalDesiredAccess & PROCESS_TERMINATE) == PROCESS_TERMINATE)
{
// 移除进程终止权限
createInfo->DesiredAccess &= ~PROCESS_TERMINATE;
}

if ((createInfo->OriginalDesiredAccess & PROCESS_VM_OPERATION) == PROCESS_VM_OPERATION)
{
// 移除虚拟内存操作权限
createInfo->DesiredAccess &= ~PROCESS_VM_OPERATION;
}

if ((createInfo->OriginalDesiredAccess & PROCESS_VM_READ) == PROCESS_VM_READ)
{
// 移除内存读取权限
createInfo->DesiredAccess &= ~PROCESS_VM_READ;
}

if ((createInfo->OriginalDesiredAccess & PROCESS_VM_WRITE) == PROCESS_VM_WRITE)
{
// 移除内存写入权限
createInfo->DesiredAccess &= ~PROCESS_VM_WRITE;
}
}
}

// 如果没有匹配到目标进程,允许操作继续
return OB_PREOP_SUCCESS;
}

总结:

  • **pOperationInformation**:提供了关于操作的元数据(如对象、句柄、操作类型等)。
  • **Parameters**:指向具体的操作参数,根据不同的操作类型,会指向不同的结构体。
  • **CreateHandleInformation**:具体的创建句柄操作参数结构,包含请求的访问权限、操作类型等信息。

通过这些信息,你可以在回调函数中检查操作的性质,并在需要时修改句柄访问权限。