/*
禁止远程计算机访问本地共享文件。
另一个功能是禁止本计算机访问别的计算机的资源。估计也就是处理设备名或者路径的开头标志,这也是一个思路。
效果是可以看到远程计算机的顶层共享文件夹,但是里面的内容打不开。
前操作没有成功,后操作成功。
很简单,也值得收藏。
注意:
The IoIsFileOriginRemote routine determines whether a given file object is for a remote create request.
File system filter drivers call IoIsFileOriginRemote for a file object to determine whether it represents a remote create request.
IoIsFileOriginRemote must be called after the create request has entirely completed. In other words, it cannot be called in the create dispatch ("pre-create") path or the create completion ("post-create") path.
IoIsFileOriginRemote checks the FO_REMOTE_ORIGIN flag on the file object pointed to by FileObject. Network file systems set or clear this flag by calling IoSetFileOrigin.
Network file systems call IoSetFileOrigin to set or clear the FO_REMOTE_ORIGIN flag on the file object pointed to by FileObject.
This flag is set to indicate that the file object was created to satisfy a remote create request.
Network file systems should call IoSetFileOrigin in their servers for any file objects that are created to satisfy a create request from a network client.
File system filter drivers should not call IoSetFileOrigin.
made by correy
made at 2013.08.05
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/
#include <fltKernel.h>
#define _In_ //在vs2012+wdk8.0中可以去掉此行。
PFLT_FILTER gFilterHandle;
FLT_POSTOP_CALLBACK_STATUS CreatePostOperation (__inout PFLT_CALLBACK_DATA Data,__in PCFLT_RELATED_OBJECTS FltObjects, __in_opt PVOID CompletionContext,__in FLT_POST_OPERATION_FLAGS Flags)
{
if ( !NT_SUCCESS( Data->IoStatus.Status ) || ( STATUS_REPARSE == Data->IoStatus.Status ) )
{
return FLT_POSTOP_FINISHED_PROCESSING;
}
//Data->Iopb等于当前IRP栈。
if (Data->Iopb->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext.ImpersonationLevel == SecurityImpersonation)
{
FltCancelFileOpen(FltObjects->Instance, FltObjects->FileObject);//这一行加不加无所谓,最好加上。
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
}
return FLT_POSTOP_FINISHED_PROCESSING;//FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_CREATE, 0, NULL, CreatePostOperation},
{ IRP_MJ_OPERATION_END }
};
#pragma PAGEDCODE
NTSTATUS PtInstanceSetup (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_SETUP_FLAGS Flags,__in DEVICE_TYPE VolumeDeviceType,__in FLT_FILESYSTEM_TYPE VolumeFilesystemType)
{
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
NTSTATUS PtInstanceQueryTeardown (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
{
return STATUS_SUCCESS;
}
#pragma PAGEDCODE//#pragma alloc_text(PAGE, PtUnload)
NTSTATUS PtUnload (__in FLT_FILTER_UNLOAD_FLAGS Flags)
{
FltUnregisterFilter( gFilterHandle );
return STATUS_SUCCESS;
}
CONST FLT_REGISTRATION FilterRegistration = {
sizeof( FLT_REGISTRATION ), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
NULL, // Context
Callbacks, // Operation callbacks
PtUnload, // MiniFilterUnload
PtInstanceSetup, // InstanceSetup
PtInstanceQueryTeardown, // InstanceQueryTeardown
NULL, // InstanceTeardownStart
NULL, // InstanceTeardownComplete
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL // NormalizeNameComponent
};
DRIVER_INITIALIZE DriverEntry;
#pragma alloc_text(INIT, DriverEntry)//#pragma INITCODE
NTSTATUS DriverEntry (_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
UNREFERENCED_PARAMETER( RegistryPath );
KdBreakPoint();//DbgBreakPoint()
status = FltRegisterFilter( DriverObject, &FilterRegistration, &gFilterHandle );//Register with FltMgr to tell it our callback routines
if (NT_SUCCESS( status )) //FLT_ASSERT( NT_SUCCESS( status ) );
{
status = FltStartFiltering( gFilterHandle );
if (!NT_SUCCESS( status )) {
FltUnregisterFilter( gFilterHandle );
}
}
return status;
}
2013年8月5日星期一
Minifilter禁止远程访问本地共享文件
2013年8月1日星期四
Minifilter禁止创建文件或者文件夹
/*
禁止创建文件或者文件夹。
很简单,也值得收藏。
这里没有加禁止的条件,所以整个计算机都不可以。
注意:
1.普通的删除,就是移往回收站的操作也会拦截,会失败。
2.对网络文件无效,如本地的网络盘符(不是本地盘符),即路径的前面有\\Device\\Mup的,这种情况下会弹出问题。
3.对于subst创建的盘符依然有效。
4.对于本地的共享在远程的操作依然有效。
made by correy
made at 2013.08.01
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/
#include <fltKernel.h>
PFLT_FILTER gFilterHandle;
ULONG_PTR OperationStatusCtx = 1;
#define PT_DBG_PRINT( _dbgLevel, _string ) (FlagOn(1,(_dbgLevel)) ? DbgPrint _string : ((int)0))
VOID PtOperationStatusCallback (__in PCFLT_RELATED_OBJECTS FltObjects,__in PFLT_IO_PARAMETER_BLOCK ParameterSnapshot,__in NTSTATUS OperationStatus,__in PVOID RequesterContext)
{
PT_DBG_PRINT( 2,("PassThrough!PtOperationStatusCallback: Status=%08x ctx=%p IrpMj=%02x.%02x \"%s\"\n",
OperationStatus,RequesterContext,ParameterSnapshot->MajorFunction,ParameterSnapshot->MinorFunction, FltGetIrpName(ParameterSnapshot->MajorFunction)) );
}
BOOLEAN PtDoRequestOperationStatus(__in PFLT_CALLBACK_DATA Data)
{
PFLT_IO_PARAMETER_BLOCK iopb = Data->Iopb;
return (BOOLEAN)( ( (iopb->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
((iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_FILTER_OPLOCK) ||
(iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_BATCH_OPLOCK) ||
(iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_1) ||
(iopb->Parameters.FileSystemControl.Common.FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2))) ||
((iopb->MajorFunction == IRP_MJ_DIRECTORY_CONTROL) && (iopb->MinorFunction == IRP_MN_NOTIFY_CHANGE_DIRECTORY)));
}
FLT_PREOP_CALLBACK_STATUS CreatePreOperation (__inout PFLT_CALLBACK_DATA Data,__in PCFLT_RELATED_OBJECTS FltObjects,__deref_out_opt PVOID *CompletionContext)
{
NTSTATUS status;
if (PtDoRequestOperationStatus( Data ))
{
status = FltRequestOperationStatusCallback( Data,PtOperationStatusCallback,(PVOID)(++OperationStatusCtx) );
if (!NT_SUCCESS(status)) {
PT_DBG_PRINT( 2,("FltRequestOperationStatusCallback Failed, status=%08x\n",status) );
}
}
return FLT_PREOP_SUCCESS_WITH_CALLBACK;
}
FLT_POSTOP_CALLBACK_STATUS CreatePostOperation (__inout PFLT_CALLBACK_DATA Data,__in PCFLT_RELATED_OBJECTS FltObjects,
__in_opt PVOID CompletionContext,__in FLT_POST_OPERATION_FLAGS Flags)
{
PFLT_FILE_NAME_INFORMATION pfni;
NTSTATUS status;
FILE_DISPOSITION_INFORMATION fdi;
BOOLEAN IsDirectory = FALSE ;
if ( !NT_SUCCESS( Data->IoStatus.Status ) || ( STATUS_REPARSE == Data->IoStatus.Status ) ) // If we have an error then just exit
{
return FLT_POSTOP_FINISHED_PROCESSING;
}
/*
FltGetFileNameInformation cannot get file name information if the TopLevelIrp field of the current thread is not NULL,
because the resulting file system recursion could cause deadlocks or stack overflows. (For more information about this issue, see IoGetTopLevelIrp.)
FltGetFileNameInformation cannot get file name information in the paging I/O path.
FltGetFileNameInformation cannot get file name information in the post-close path.
FltGetFileNameInformation cannot get the short name of a file in the pre-create path.
*/
if (FlagOn(Data->Iopb->IrpFlags, IRP_PAGING_IO) || FlagOn(Data->Iopb->IrpFlags, IRP_SYNCHRONOUS_PAGING_IO) || IoGetTopLevelIrp()) //IRP_NOCACHE
{
return FLT_POSTOP_FINISHED_PROCESSING;
}
status = FltGetFileNameInformation( Data, FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT, &pfni);
if (!NT_SUCCESS( status )) // If we could not get the name information then exit
{
return FLT_POSTOP_FINISHED_PROCESSING;
}
FltParseFileNameInformation(pfni);
//一个有用的函数,还有FsRtlIsDbcsInExpression。
//FsRtlIsNameInExpression( &gProtectedData->NameInfo.VolumeName, &FNameInfo->Volume, TRUE, NULL )
//判断操作意图。
//if ( FltObjects->FileObject->DeletePending || FltObjects->FileObject->WriteAccess || FltObjects->FileObject->DeleteAccess ||
// FltObjects->FileObject->SharedWrite || FltObjects->FileObject->SharedDelete)
{
if ( Data->IoStatus.Information == FILE_CREATED ) //如果是新建操作。
{
status = FltIsDirectory(FltObjects->FileObject, FltObjects->Instance, &IsDirectory);//在打开的前操作中FileObject不可用,所以这个函数不可用。
if (NT_SUCCESS(status) && IsDirectory) //如果是目录.
{
//这两行最好加上,不然出其他问题。
fdi.DeleteFile = TRUE;
FltSetInformationFile( FltObjects->Instance, FltObjects->FileObject, &fdi, sizeof( FILE_DISPOSITION_INFORMATION ), FileDispositionInformation );
/*
Callers of FltCancelFileOpen must be running at IRQL <= APC_LEVEL.
However, a minifilter driver can safely call this routine from a post-create callback routine, because, for IRP_MJ_CREATE operations, the postoperation callback routine is called at IRQL = PASSIVE_LEVEL, in the context of the thread that originated the create operation.
*/
FltCancelFileOpen( FltObjects->Instance, FltObjects->FileObject );//干掉句柄。
Data->IoStatus.Status = STATUS_ACCESS_DENIED;
Data->IoStatus.Information = 0;
}
}
}
FltReleaseFileNameInformation(pfni);
return FLT_POSTOP_FINISHED_PROCESSING;
}
FLT_PREOP_CALLBACK_STATUS PtPreOperationNoPostOperationPassThrough (__inout PFLT_CALLBACK_DATA Data,__in PCFLT_RELATED_OBJECTS FltObjects,__deref_out_opt PVOID *CompletionContext)
{
return FLT_PREOP_SUCCESS_NO_CALLBACK;
}
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
{ IRP_MJ_CREATE, 0, CreatePreOperation, CreatePostOperation},
{ IRP_MJ_SHUTDOWN, 0, PtPreOperationNoPostOperationPassThrough, NULL }, //post operations not supported
{ IRP_MJ_OPERATION_END }
};
#pragma PAGEDCODE
NTSTATUS PtInstanceSetup (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_SETUP_FLAGS Flags,__in DEVICE_TYPE VolumeDeviceType,__in FLT_FILESYSTEM_TYPE VolumeFilesystemType)
{
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
NTSTATUS PtInstanceQueryTeardown (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_QUERY_TEARDOWN_FLAGS Flags)
{
return STATUS_SUCCESS;
}
#pragma PAGEDCODE
VOID PtInstanceTeardownStart (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_TEARDOWN_FLAGS Flags)
{
}
#pragma PAGEDCODE
VOID PtInstanceTeardownComplete (__in PCFLT_RELATED_OBJECTS FltObjects,__in FLT_INSTANCE_TEARDOWN_FLAGS Flags)
{
}
#pragma PAGEDCODE//#pragma alloc_text(PAGE, PtUnload)
NTSTATUS PtUnload (__in FLT_FILTER_UNLOAD_FLAGS Flags)
{
FltUnregisterFilter( gFilterHandle );
return STATUS_SUCCESS;
}
CONST FLT_REGISTRATION FilterRegistration = {
sizeof( FLT_REGISTRATION ), // Size
FLT_REGISTRATION_VERSION, // Version
0, // Flags
NULL, // Context
Callbacks, // Operation callbacks
PtUnload, // MiniFilterUnload
PtInstanceSetup, // InstanceSetup
PtInstanceQueryTeardown, // InstanceQueryTeardown
PtInstanceTeardownStart, // InstanceTeardownStart
PtInstanceTeardownComplete, // InstanceTeardownComplete
NULL, // GenerateFileName
NULL, // GenerateDestinationFileName
NULL // NormalizeNameComponent
};
DRIVER_INITIALIZE DriverEntry;
#pragma alloc_text(INIT, DriverEntry)//#pragma INITCODE
NTSTATUS DriverEntry (_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
UNREFERENCED_PARAMETER( RegistryPath );
KdBreakPoint();//DbgBreakPoint()
status = FltRegisterFilter( DriverObject, &FilterRegistration, &gFilterHandle );// Register with FltMgr to tell it our callback routines
if (NT_SUCCESS( status )) //FLT_ASSERT( NT_SUCCESS( status ) );
{
status = FltStartFiltering( gFilterHandle );// Start filtering i/o
if (!NT_SUCCESS( status )) {
FltUnregisterFilter( gFilterHandle );
}
}
return status;
}
2013年7月12日星期五
WinDbg远程调试
注意windbg的版本要尽量相同。
第一步:在被调试的机器上,也就是服务端。
一种是命令行方式启动windbgWinDBG.exe -server tcp:port=8888 -p 2010
注释:(使用TCP协议,在端口8888建立调试服务, 调试机器上进程号为2010的程序)
另一种是启动windbg,打开要调试的程序,然后在命令窗口输入.server tcp:port=8888来建立server,
成功后windbg会提示你Client如何连接到Server,例如:
0:000> .server tcp:port=8888
Server started. Client can connect with any of these command lines
0: <debugger> -remote tcp:Port=8888,Server=KOUDAQIANG-WIN8
第二步:在本地
命令行:windbg -remote tcp:Port=8888,Server=KOUDAQIANG-WIN8
或者启动后用菜单文件->Connect to remote session(CTRL+R)来连接,输入: tcp:Port=8888,Server=KOUDAQIANG-WIN8
显示的内容和运行的命令如下:
Microsoft (R) Windows Debugger Version 6.2.9200.16384 X86
Copyright (c) Microsoft Corporation. All rights reserved.
Server started. Client can connect with any of these command lines
0: <debugger> -remote tcp:Port=8888,Server=KOUDAQIANG-WIN8
KOUDAQIANG-WIN7\Administrator (tcp 10.121.23.3:3544) connected at Fri Jul 12 15:09:46 2013
0:000> t
eax=00000000 ebx=00000003 ecx=3ba60000 edx=00000000 esi=00000000 edi=001b0000
eip=772a04cb esp=0018fac4 ebp=0018faf0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000244
ntdll!LdrInitShimEngineDynamic+0x2ed:
772a04cb eb07 jmp ntdll!LdrInitShimEngineDynamic+0x2f6 (772a04d4)
0:000> p
eax=00000000 ebx=00000003 ecx=3ba60000 edx=00000000 esi=00000000 edi=001b0000
eip=772a04d4 esp=0018fac4 ebp=0018faf0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!LdrInitShimEngineDynamic+0x2f6:
772a04d4 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh ss:002b:0018faec=00000000
同时另一个计算机的windbg上也会显示同样的内容,并且同步.
ok!
会者不难,难者不会。
但是要源代码的符号调试,需在被调试的机器上设置符号路径,这时的符号路径大多是远程的,也就是本台的地址.
参考:
http://msdn.microsoft.com/en-us/library/windows/hardware/hh451173(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/ff554390.aspx
windbg帮助文件等。
其实还有一种办法:就是在内核调试的时候,在应用层的代码中加入:DebugBreak();
不过这种办法调试的速度很慢,比vs双机调试和上面的办法要慢,
不过这个办法也有个好处可以同时调试r3和r0的代码,在这种情况下vs双机调试容易中断,我想也有解决的办法.
made by correy
made at 2013.02.05
第一步:在被调试的机器上,也就是服务端。
一种是命令行方式启动windbgWinDBG.exe -server tcp:port=8888 -p 2010
注释:(使用TCP协议,在端口8888建立调试服务, 调试机器上进程号为2010的程序)
另一种是启动windbg,打开要调试的程序,然后在命令窗口输入.server tcp:port=8888来建立server,
成功后windbg会提示你Client如何连接到Server,例如:
0:000> .server tcp:port=8888
Server started. Client can connect with any of these command lines
0: <debugger> -remote tcp:Port=8888,Server=KOUDAQIANG-WIN8
第二步:在本地
命令行:windbg -remote tcp:Port=8888,Server=KOUDAQIANG-WIN8
或者启动后用菜单文件->Connect to remote session(CTRL+R)来连接,输入: tcp:Port=8888,Server=KOUDAQIANG-WIN8
显示的内容和运行的命令如下:
Microsoft (R) Windows Debugger Version 6.2.9200.16384 X86
Copyright (c) Microsoft Corporation. All rights reserved.
Server started. Client can connect with any of these command lines
0: <debugger> -remote tcp:Port=8888,Server=KOUDAQIANG-WIN8
KOUDAQIANG-WIN7\Administrator (tcp 10.121.23.3:3544) connected at Fri Jul 12 15:09:46 2013
0:000> t
eax=00000000 ebx=00000003 ecx=3ba60000 edx=00000000 esi=00000000 edi=001b0000
eip=772a04cb esp=0018fac4 ebp=0018faf0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000244
ntdll!LdrInitShimEngineDynamic+0x2ed:
772a04cb eb07 jmp ntdll!LdrInitShimEngineDynamic+0x2f6 (772a04d4)
0:000> p
eax=00000000 ebx=00000003 ecx=3ba60000 edx=00000000 esi=00000000 edi=001b0000
eip=772a04d4 esp=0018fac4 ebp=0018faf0 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!LdrInitShimEngineDynamic+0x2f6:
772a04d4 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh ss:002b:0018faec=00000000
同时另一个计算机的windbg上也会显示同样的内容,并且同步.
ok!
会者不难,难者不会。
但是要源代码的符号调试,需在被调试的机器上设置符号路径,这时的符号路径大多是远程的,也就是本台的地址.
参考:
http://msdn.microsoft.com/en-us/library/windows/hardware/hh451173(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/ff554390.aspx
windbg帮助文件等。
其实还有一种办法:就是在内核调试的时候,在应用层的代码中加入:DebugBreak();
不过这种办法调试的速度很慢,比vs双机调试和上面的办法要慢,
不过这个办法也有个好处可以同时调试r3和r0的代码,在这种情况下vs双机调试容易中断,我想也有解决的办法.
made by correy
made at 2013.02.05
2013年7月4日星期四
编程注意事项
这里记载一些心得经验和想法(没有实验). 这里大多是血的教训!请大家谨记. 1.ExAllocatePoolWithTag不但检查成功否和释放ExFreePoolWithTag,更重要的是要RtlZeroMemory,不然会有乱码等奇怪的现象.最好立马使用,最近使用. 不会C++,没有C++的思想。不过写下面的两个函数倒有进步的思想。 #define TAG 'tset' //驱动在内存的标志,即test //new delete PVOID allocate(IN SIZE_T NumberOfBytes) /* 不建议对申请内存函数的封装,这样对内存泄漏不好定位. */ { PVOID p = NULL; //PAGED_CODE(); if (KeGetCurrentIrql() > DISPATCH_LEVEL) { KdBreakPoint();//DbgBreakPoint() } /* Callers of ExAllocatePoolWithTag must be executing at IRQL <= DISPATCH_LEVEL. A caller executing at DISPATCH_LEVEL must specify a NonPagedXxx value for PoolType. A caller executing at IRQL <= APC_LEVEL can specify any POOL_TYPE value, but the IRQL and environment must also be considered for determining the page type. */ p = ExAllocatePoolWithTag(NonPagedPool, NumberOfBytes, TAG); if (p == NULL ) { return p; } /* Warning Memory that ExAllocatePoolWithTag allocates is uninitialized. A kernel-mode driver must first zero this memory if it is going to make it visible to user-mode software (to avoid leaking potentially privileged contents). */ RtlZeroMemory(p, NumberOfBytes); return p; } VOID free(IN PVOID p) { unsigned long r; /* Callers of ExFreePoolWithTag must be running at IRQL <= DISPATCH_LEVEL. A caller at DISPATCH_LEVEL must have specified a NonPagedXxx PoolType when the memory was allocated. Otherwise, the caller must be running at IRQL <= APC_LEVEL. */ //PAGED_CODE(); if (KeGetCurrentIrql() > DISPATCH_LEVEL) { KdBreakPoint();//DbgBreakPoint() } if (p) //防止多次释放导致的蓝屏。 { __try //防止传入非法的地址。 { r = MmIsAddressValid(p); ExFreePoolWithTag(p, TAG);//KeGetCurrentIrql() > DISPATCH_LEVEL时依旧蓝屏。 } __except (EXCEPTION_EXECUTE_HANDLER) { r = GetExceptionCode();//啥也不做。 } p = NULL; } } 2.不可用BOOL与true或者TRUE比较,因为:typedef int BOOL; 所以: BOOL b = PathIsDirectory(buffer); //if (b == true) //00B4161A cmp dword ptr [ebp-268h],1 //if (b == TRUE) //00B4161A cmp dword ptr [ebp-268h],1 if (b) //cmp dword ptr [ebp-268h],0 3.要对用:RtlAppendUnicodeStringToString或者RtlAppendUnicodeToString或者RtlAppendStringToString对STRING或者UNICODE_STRING追加字符,原有字符结构必须有内存,不能是初始化的. 就是初始化的时候要使用:RtlInitEmptyUnicodeString,而不能使用:RtlInitUnicodeString(&us1,L"\\REGISTRY\\USER\\"); 系统生成的UNICODE_STRING,如文件对象(FileObject)里面的,这是最好传递UNICODE_STRING指针,前提是不能修改这个输入参数,如要修改请备份或者复制一份. 如果传递字符串的地址,再用RtlInitUnicodeString初始化,不仅麻烦而且易出错,因为:字符串的地址没有结束标志,且字符串后面的地址有可能不可以访问,所以会蓝屏. 如果非要传递字符串的地址,建议一定加上字符串的长度,用原始的方法初始UNICODE_STRING,不要用RtlInitUnicodeString了. 4.FltRegisterFilter函数返回STATUS_OBJECT_NAME_NOT_FOUND #define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L) 原因是注册表中没有如下内容: Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\xxxxxx\Instances] "DefaultInstance"="xxxxxx" [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\xxxxxx\Instances\xxxxxx] "Altitude"="371100" "Flags"=dword:00000000 并注意:Altitude的值和注册时要一致. 说明:必须有一个名为"Instances"的子项用于存放驱动的实例信息,该子项下面的字符串值"DefaultInstance"指定了默认实例的名称。 "Instances"项下面的每一个子项表示一个实例,每个实例子项必须有一个字符串值"Altitude"。 FltRegisterFilter函数执行时,如果在注册表中没有找到默认实例的"Altitude"值,将会返回STATUS_OBJECT_NAME_NOT_FOUND错误。 5.ObRegisterCallbacks返回STATUS_ACCESS_DENIED (0xc0000022) 解决办法: 首选办法:sources里面加入:LINKER_FLAGS = $(LINKER_FLAGS)/INTEGRITYCHECK 第二个方案: 注册回调前加段代码,改一个比特位就解决了 代码: PLDR_DATA_TABLE_ENTRY pLdrEntry=(PLDR_DATA_TABLE_ENTRY)pDrvObj->DriverSection; pLdrEntry->Flags |=0x20; 6.如果有两个桌面,在内核中判断或者区分? 1.进程回调加父子关系。我是用链表实现了。 2.未公开的函数,详细的请查询http://doxygen.reactos.org。 3.判断进程内的某个类型的对象的值,即:DeskTop。 4.对象里面有个结构,就是使用这个对象的所有的进程的链表。 7.判断一个文件或者文件夹是不是在另一个文件夹里面。 应用层和驱动层通吃,这是一个方法和思路。 1.判断是不是这个文件夹,之前最好判断一下是不是目录。 2.如果待判断的文件或者目录的长度大于特定的目录,取代判断的目录或者文件夹的长度加一,然后和特定的目录加"\\"比较。 如果是miniFilter,有更方便的办法,因为它解析好了。 另外内核还有一些特殊的函数,至少三类: 1.以str,wcs,_wcs开头的函数。由内核导出,但是不建议使用,因为好多字符串不是以0x00结尾的. 2.刚开始还以为是:RtlLeftChild呢?后来发现了RtlPrefixUnicodeString,实验成功。不过要调用两次,后一次加一个\. 3.FsRtlIsNameInExpression或者FsRtlIsDbcsInExpression等。这个实验始终失败. 8.KeSetEvent使用的一个要点. LONG KeSetEvent(IN PRKEVENT Event, IN KPRIORITY Increment, IN BOOLEAN Wait); 最后一个参数为真,必须马上调用等待函数,不然蓝屏,注意IRQL还会升高,具体的看说明. 这是加班到凌晨4点才解决,后来才明白的. 9.UserMode or KernelMode 很多函数的说明中有这么一句话:Lower-level drivers should specify KernelMode. 实际要怎么做?其实大多说是UserMode.只有系统进程或者驱动专用的是KernelMode. 其实完美的办法是调用ObIsKernelHandle函数.ObOpenObjectByPointer这个函数有点麻烦. 彻底的解决办法是ExGetPreviousMode(VOID). 10.由于VS2013没有一键转换的功能,所以依旧用vs2012,但是要编译:Windows Driver Kit (WDK) 8.1 Preview Samples下的例子,要把工程的编译平台修改为8.0(WindowsKernelModeDriver8.0)即可,注意有两处.不然编译出错. error MSB8020: The builds tools for WindowsKernelModeDriver8.1 (Platform Toolset = 'WindowsKernelModeDriver8.1') cannot be found. To build using the WindowsKernelModeDriver8.1 build tools, either click the Project menu or right-click the solution, and then select "Update VC++ Projects...". Install WindowsKernelModeDriver8.1 to build using the WindowsKernelModeDriver8.1 build tools. 11.数字签名. 这个我也不太懂,有的要5级签名,微软的在最上等. 今天遇到一个,即使签名了也会出现:577 = 0x241 . 其含义为:Windows 无法验证此文件的数字签名。某软件或硬件最近有所更改,可能安装了签名错误或损毁的文件,或者安装的文件可能是来路不明的恶意软件。 改进办法是用命令行签名,最好加上时间信息,不过这又要开发环境了. 12.minifilter在卷挂载(PFLT_INSTANCE_SETUP_CALLBACK)的时候,获取卷设备的一些信息,更多信息请自己扩展. BOOLEAN PrintVolume(__in PCFLT_RELATED_OBJECTS FltObjects) /* 功能:打印挂载的卷的信息。 */ { NTSTATUS status; PVOID Buffer; BOOLEAN r = FALSE; ULONG BufferSizeNeeded; UNICODE_STRING Volume; status = FltGetVolumeName(FltObjects->Volume, NULL, &BufferSizeNeeded); if (status != STATUS_BUFFER_TOO_SMALL) { return FALSE; } Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSizeNeeded + 2, TAG); if (Buffer == NULL) { return FALSE; } RtlZeroMemory(Buffer,BufferSizeNeeded + 2); Volume.Buffer = Buffer; Volume.Length = (USHORT)BufferSizeNeeded; Volume.MaximumLength = (USHORT)BufferSizeNeeded + 2; status = FltGetVolumeName(FltObjects->Volume, &Volume, &BufferSizeNeeded);//最后一个参数为NULL失败。 if (!NT_SUCCESS(status)) { KdPrint(("FltGetVolumeName fail with error 0x%x!\n",status)); ExFreePoolWithTag(Buffer, TAG); return FALSE; } KdPrint(("挂载的卷为:%wZ\n",&Volume)); ExFreePoolWithTag(Buffer, TAG); return r; } 打印信息有: 挂载的卷为:\Device\Mup 挂载的卷为:\Device\HarddiskVolume1 挂载的卷为:\Device\HarddiskVolume2 挂载的卷为:\Device\HarddiskVolume4 挂载的卷为:\Device\HarddiskVolume3 挂载的卷为:\Device\HarddiskVolume5 挂载的卷为:\Device\CdRom0 13. BOOLEAN IsMyVolume(__in PCFLT_RELATED_OBJECTS FltObjects) { BOOLEAN b = FALSE; PFILE_OBJECT FileObject; UNICODE_STRING uni_disk; UNICODE_STRING Hide_name; NTSTATUS status = STATUS_SUCCESS; FileObject = FltObjects->FileObject;//sp->FileObject; //On Windows Vista and later operating systems, you must ensure that APCs are not disabled before calling this routine. //Call KeAreAllApcsDisabled for this purpose. //ObReferenceObjectByPointer((PVOID)FileObject,0,NULL,KernelMode); status = IoVolumeDeviceToDosName(FileObject->DeviceObject,&uni_disk);//RtlVolumeDeviceToDosName 支持xp之前,但是prefast警告. //ObDereferenceObject(FileObject); if (!NT_SUCCESS( status )) //如果失败了puni_disk->buffer == 0,下面的比较会蓝屏. { return b; } RtlInitUnicodeString(&Hide_name,L"X:"); if (RtlEqualUnicodeString(&Hide_name,&uni_disk,TRUE)) { b = TRUE; } ExFreePool(uni_disk.Buffer);//或者下面的办法. //RtlFreeUnicodeString(&uni_disk); return b; } 14.OACR的使用. 正常情况下,编译之后,双击图标即可显示. 但是非正常情况下: 1.编译驱动 2.check now -> 选项. 3.view warnings -> 选项. 以上是个人理解,并非正确. 15.C和CPP与布尔变量的关系。 C中默认情况下只能使用大写的布尔变量。 C++中注意这是两种不同的数据类型。 在C中测试效果如下: BOOLEAN BX;//BYTE //BOOL B;//int //bool b; BOOLEAN BX1 = TRUE; //BOOLEAN BX2 = true; 注意注释的是错误的,不过在CPP中是都可以的。 谨记,这是一会开发驱动程序,一会写应用层代码所得的。 16.再论UNICODE_STRING。 // // Unicode strings are counted 16-bit character strings. If they are // NULL terminated, Length does not include trailing NULL. // typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; #ifdef MIDL_PASS [size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer; #else // MIDL_PASS _Field_size_bytes_part_(MaximumLength, Length) PWCH Buffer; #endif // MIDL_PASS } UNICODE_STRING; //上面是文件中的定义。 //下面是文档中的说明。 typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer; } UNICODE_STRING, *PUNICODE_STRING; Length The length in bytes of the string stored in Buffer. MaximumLength The length in bytes of Buffer. Buffer Pointer to a buffer used to contain a string of wide characters. If the string is NULL-terminated, Length does not include the trailing NULL. 个人理解: 上面的必须看懂并记住。 尽管微软所这是安全的字符串,不会出所谓的问题。 但是使用不当还是会出现蓝屏和莫名奇怪的问题。 因为好像没有函数检验字符串的有效性。 至少在WINDBG的本地变量里面显示的字符串和长度是可以不相符的。 反过来说,理解了这个结构,可以写出一些技巧的代码。 再次重复,长度是字节的长度, 所以在内存地址中定位字符的时候要除以Buffer的单位再减一。 所以复制的时候千万不要长度再乘以Buffer的单位。 这些问题很难发现和定位,费了我两天的时间,才解决,所以写此心得。 17.看《windows内核情景分析》的9.12MDL章节: 更喜欢叫DeviceObject->Flags的: DO_BUFFERED_IO为复制方式,特点:系统申请非分页内存,然后再复制。 DO_DIRECT_IO为映射方式,特点:获取用户地址的物理地址的内核地址。 neither buffered nor direct I/O为直接方式,特点:很少使用或者直接使用,注意DPC/ISR中不可以用。 更多的官方信息: http://msdn.microsoft.com/en-us/library/windows/hardware/ff550869(v=vs.85).aspx Neither I/O Operations http://msdn.microsoft.com/en-us/library/windows/hardware/ff565381(v=vs.85).aspx Using Direct I/O with PIO http://msdn.microsoft.com/en-us/library/windows/hardware/ff565374(v=vs.85).aspx Using Direct I/O with DMA http://msdn.microsoft.com/en-us/library/windows/hardware/ff565356(v=vs.85).aspx Using Buffered I/O 具体的例子可看:\7600.16385.1\src\general\ioctl\wdm\sys。 18.win32程序显示控制台 一个函数就ok了! AllocConsole() //这个会闪一下。 AttachConsole(-1) //这个正好。 但是要想显示,还可以: GetStdHandle WriteFile 19.不显示Console窗口的Console程序 /* 控制台程序不显示控制台窗口。 wmainCRTStartup对应:wmain 宽字符。 mainCRTStartup对应:main 单字符。 另一种思路是WIN32程序隐藏窗口。 */ #pragma comment(linker, "/subsystem:\"windows\" /entry:\"wmainCRTStartup\"") 其实如果不想看到Console窗口,还有一个更直接的方法:那就是直接在EXE文件中将PE文件头的Subsystem从3改成2。在EXE文件中,PE文件头的偏移地址是0x3c,Subsystem是一个WORD,它在PE文件头中的偏移是0x5c。 20.RtlIsNameLegalDOS8Dot3的说明。 1.第一个参数是文件名,不是路径。 2.凡是文件名长度小于12的都认为是的,包括NTFS的元文件,目录等。 21.minifilter中的IRP操作。 PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Cbd->Iopb->Parameters.NetworkQueryOpen.Irp); 22.看UNICODE_STRING,一定不要在图形界面中看,如本地变量和全局变量。 1.可能不显示汉字。 2.可能显示的是错误的。 也不要用dt UNICODE_STRING XXXXXXXX查看,这个和上面的是一样的。 而要这样看: db US.Buffer LUS.Length. 谨记:这是血的教训,很难查的问题。 23.高IRQL的注意事项,特别是IRQL >= DISPATCH_LEVEL,IRQL <= APC_LEVEL出问题的不多。 大家都知道那些规则。 但是这是我的心得和体会: 1.IRQL >= DISPATCH_LEVEL时,不要调用PASSIVE_LEVEL,APC_LEVEL级别的函数。 这个看似简单的问题你做到了吗?检查检查你的代码。 2.IRQL >= DISPATCH_LEVEL时,不要访问分页内存,包括用户的内存/地址。 如果非得访问用户的内存不可,可以考虑MDL,这肯定要锁定操作的。 一般的访问用户地址建议用异常处理,可是此时IQRL低,对于同步处理不好,如链表操作。 3.高IRQL的优点,大家都知道的,请不要忘了缺点。 4.XP是没有查看IRQL的WINDBG命令的,!PCR等查看的是不对的,永远为0,不信你试试看。
2013年6月29日星期六
IoRegisterDriverReinitialization.C
/*
文本就命名为:IoRegisterDriverReinitialization.C吧!
made by correy
made at 2013.06.29
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
*/
#include <ntifs.h>
DRIVER_REINITIALIZE Reinitialize;
VOID Reinitialize(__in struct _DRIVER_OBJECT *DriverObject, __in_opt PVOID Context, __in ULONG Count)
{
//如果是IoRegisterDriverReinitialization注册的,DriverEntry运行之后就走到这里了。
//如果是IoRegisterBootDriverReinitialization注册的,DriverEntry运行之后不会走到这里,可能在系统启动的某个时候运行。
/*
A driver can call this routine only if its DriverEntry routine will return STATUS_SUCCESS.
If the driver-supplied Reinitialize routine must use the registry,
the DriverEntry routine should include a copy of the string to which RegistryPath points as part of the context passed to the Reinitialize routine in this call.
If the driver is loaded dynamically,
it is possible for this to occur during a normally running system,
so all references to the reinitialization queue must be synchronized.
The Count input to a DriverReinitializationRoutine indicates how many times this routine has been called, including the current call.
The DriverEntry routine can call IoRegisterDriverReinitialization only once.
If the Reinitialize routine should be run again after any other drivers' Reinitialize routines have returned control,
the Reinitialize routine also can call IoRegisterDriverReinitialization as many times as the driver's Reinitialize routine should be run.
Usually, a driver with a Reinitialize routine is a higher-level driver that controls both PnP and legacy devices.
Such a driver must not only create device objects for the devices that the PnP manager detects (and for which the PnP manager calls the driver's AddDevice routine),
the driver must also create device objects for legacy devices that the PnP manager does not detect.
A driver can use a Reinitialize routine to create those device objects and layer the driver over the next-lower driver for the underlying device.
*/
KdBreakPoint();
}
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
}
#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
//IoRegisterBootDriverReinitialization(DriverObject,Reinitialize,0);
IoRegisterDriverReinitialization(DriverObject,Reinitialize,0);
return STATUS_SUCCESS;
}
文本就命名为:IoRegisterDriverReinitialization.C吧!
made by correy
made at 2013.06.29
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
*/
#include <ntifs.h>
DRIVER_REINITIALIZE Reinitialize;
VOID Reinitialize(__in struct _DRIVER_OBJECT *DriverObject, __in_opt PVOID Context, __in ULONG Count)
{
//如果是IoRegisterDriverReinitialization注册的,DriverEntry运行之后就走到这里了。
//如果是IoRegisterBootDriverReinitialization注册的,DriverEntry运行之后不会走到这里,可能在系统启动的某个时候运行。
/*
A driver can call this routine only if its DriverEntry routine will return STATUS_SUCCESS.
If the driver-supplied Reinitialize routine must use the registry,
the DriverEntry routine should include a copy of the string to which RegistryPath points as part of the context passed to the Reinitialize routine in this call.
If the driver is loaded dynamically,
it is possible for this to occur during a normally running system,
so all references to the reinitialization queue must be synchronized.
The Count input to a DriverReinitializationRoutine indicates how many times this routine has been called, including the current call.
The DriverEntry routine can call IoRegisterDriverReinitialization only once.
If the Reinitialize routine should be run again after any other drivers' Reinitialize routines have returned control,
the Reinitialize routine also can call IoRegisterDriverReinitialization as many times as the driver's Reinitialize routine should be run.
Usually, a driver with a Reinitialize routine is a higher-level driver that controls both PnP and legacy devices.
Such a driver must not only create device objects for the devices that the PnP manager detects (and for which the PnP manager calls the driver's AddDevice routine),
the driver must also create device objects for legacy devices that the PnP manager does not detect.
A driver can use a Reinitialize routine to create those device objects and layer the driver over the next-lower driver for the underlying device.
*/
KdBreakPoint();
}
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
}
#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
//IoRegisterBootDriverReinitialization(DriverObject,Reinitialize,0);
IoRegisterDriverReinitialization(DriverObject,Reinitialize,0);
return STATUS_SUCCESS;
}
2013年6月28日星期五
IShellDispatch2.Cpp
// 本工程的位置在:Microsoft SDKs\Windows\v7.1\Samples\winui\shell\appplatform\ExecInExplorer。
#include <windows.h>
#include <shlwapi.h>
#include <shlobj.h>
#pragma comment(lib, "shlwapi.lib")
// use the shell view for the desktop using the shell windows automation to find the desktop web browser and then grabs its view
// returns:IShellView, IFolderView and related interfaces
HRESULT GetShellViewForDesktop(REFIID riid, void **ppv)
{
*ppv = NULL;
IShellWindows *psw;
HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
if (SUCCEEDED(hr))
{
HWND hwnd;
IDispatch* pdisp;
VARIANT vEmpty = {}; // VT_EMPTY
if (S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp))
{
IShellBrowser *psb;
hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
if (SUCCEEDED(hr))
{
IShellView *psv;
hr = psb->QueryActiveShellView(&psv);
if (SUCCEEDED(hr))
{
hr = psv->QueryInterface(riid, ppv);
psv->Release();
}
psb->Release();
}
pdisp->Release();
} else {
hr = E_FAIL;
}
psw->Release();
}
return hr;
}
// From a shell view object gets its automation interface and from that gets the shell application object that implements IShellDispatch2 and related interfaces.
HRESULT GetShellDispatchFromView(IShellView *psv, REFIID riid, void **ppv)
{
*ppv = NULL;
IDispatch *pdispBackground;
HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
if (SUCCEEDED(hr))
{
IShellFolderViewDual *psfvd;
hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
if (SUCCEEDED(hr))
{
IDispatch *pdisp;
hr = psfvd->get_Application(&pdisp);
if (SUCCEEDED(hr))
{
hr = pdisp->QueryInterface(riid, ppv);
pdisp->Release();
}
psfvd->Release();
}
pdispBackground->Release();
}
return hr;
}
HRESULT ShellExecInExplorerProcess(PCWSTR pszFile)
{
IShellView *psv;
HRESULT hr = GetShellViewForDesktop(IID_PPV_ARGS(&psv));
if (SUCCEEDED(hr))
{
IShellDispatch2 *psd;
hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd));
if (SUCCEEDED(hr))
{
BSTR bstrFile = SysAllocString(pszFile);
hr = bstrFile ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
VARIANT vtEmpty = {}; // VT_EMPTY
hr = psd->ShellExecuteW(bstrFile, vtEmpty, vtEmpty, vtEmpty, vtEmpty);
SysFreeString(bstrFile);
}
psd->Release();
}
psv->Release();
}
return hr;
}
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
ShellExecInExplorerProcess(L"c:\\windows\\system32\\calc.exe");//http://www.msn.com
CoUninitialize();
}
MessageBox(0,0,0,0);//测试。
return 0;
}
#include <windows.h>
#include <shlwapi.h>
#include <shlobj.h>
#pragma comment(lib, "shlwapi.lib")
// use the shell view for the desktop using the shell windows automation to find the desktop web browser and then grabs its view
// returns:IShellView, IFolderView and related interfaces
HRESULT GetShellViewForDesktop(REFIID riid, void **ppv)
{
*ppv = NULL;
IShellWindows *psw;
HRESULT hr = CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&psw));
if (SUCCEEDED(hr))
{
HWND hwnd;
IDispatch* pdisp;
VARIANT vEmpty = {}; // VT_EMPTY
if (S_OK == psw->FindWindowSW(&vEmpty, &vEmpty, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &pdisp))
{
IShellBrowser *psb;
hr = IUnknown_QueryService(pdisp, SID_STopLevelBrowser, IID_PPV_ARGS(&psb));
if (SUCCEEDED(hr))
{
IShellView *psv;
hr = psb->QueryActiveShellView(&psv);
if (SUCCEEDED(hr))
{
hr = psv->QueryInterface(riid, ppv);
psv->Release();
}
psb->Release();
}
pdisp->Release();
} else {
hr = E_FAIL;
}
psw->Release();
}
return hr;
}
// From a shell view object gets its automation interface and from that gets the shell application object that implements IShellDispatch2 and related interfaces.
HRESULT GetShellDispatchFromView(IShellView *psv, REFIID riid, void **ppv)
{
*ppv = NULL;
IDispatch *pdispBackground;
HRESULT hr = psv->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&pdispBackground));
if (SUCCEEDED(hr))
{
IShellFolderViewDual *psfvd;
hr = pdispBackground->QueryInterface(IID_PPV_ARGS(&psfvd));
if (SUCCEEDED(hr))
{
IDispatch *pdisp;
hr = psfvd->get_Application(&pdisp);
if (SUCCEEDED(hr))
{
hr = pdisp->QueryInterface(riid, ppv);
pdisp->Release();
}
psfvd->Release();
}
pdispBackground->Release();
}
return hr;
}
HRESULT ShellExecInExplorerProcess(PCWSTR pszFile)
{
IShellView *psv;
HRESULT hr = GetShellViewForDesktop(IID_PPV_ARGS(&psv));
if (SUCCEEDED(hr))
{
IShellDispatch2 *psd;
hr = GetShellDispatchFromView(psv, IID_PPV_ARGS(&psd));
if (SUCCEEDED(hr))
{
BSTR bstrFile = SysAllocString(pszFile);
hr = bstrFile ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
VARIANT vtEmpty = {}; // VT_EMPTY
hr = psd->ShellExecuteW(bstrFile, vtEmpty, vtEmpty, vtEmpty, vtEmpty);
SysFreeString(bstrFile);
}
psd->Release();
}
psv->Release();
}
return hr;
}
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
ShellExecInExplorerProcess(L"c:\\windows\\system32\\calc.exe");//http://www.msn.com
CoUninitialize();
}
MessageBox(0,0,0,0);//测试。
return 0;
}
IoRegisterShutdownNotification.C
/*
文本就命名为:IoRegisterShutdownNotification.C吧!
made by correy
made at 2013.06.28
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
*/
#include <ntifs.h>
//#include <ntddk.h> //这两个次序不能乱,有上面的,这个可以注释掉。
DRIVER_DISPATCH DispatchShutdown;
NTSTATUS DispatchShutdown(__in struct _DEVICE_OBJECT *DeviceObject, __in struct _IRP *Irp)
{
/*
The IoRegisterShutdownNotification routine registers the driver to receive an IRP_MJ_SHUTDOWN IRP for the specified device when the system shuts down.
The driver receives one such IRP for each device it registers to receive notification for.
Drivers handle IRP_MJ_SHUTDOWN IRPs within their DispatchShutdown routines.
If the driver ceases to require shutdown notification for the device,
use IoUnregisterShutdownNotification to remove the driver from the shutdown notification queue.
Only one driver in a device stack should register to receive shutdown notification.
The system sends the driver the IRP_MJ_SHUTDOWN request before it flushes the file systems.
Some drivers, such as drivers for mass storage devices, can require shutdown notification after the system flushes the file systems.
To receive shutdown notification for a device after the file systems are flushed, use the IoRegisterLastChanceShutdownNotification routine instead.
The registered DispatchShutdown routine is called before the power manager sends an IRP_MN_SET_POWER request for PowerSystemShutdown.
The DispatchShutdown routine is not called for transitions to any other power states.
A driver writer can make no assumptions about the order in which the driver's DispatchShutdown routine will be called in relation to other such routines or to other shutdown activities.
A PnP driver might register a shutdown routine to perform certain tasks before system shutdown starts, such as locking down code.
一下是IoRegisterLastChanceShutdownNotification特有的性质:
For any device that is registered with this routine, the system sends the IRP_MJ_SHUTDOWN IRP after all file systems are flushed.
Only one driver in a device stack should register to receive shutdown notification, by calling either IoRegisterShutdownNotification or IoRegisterLastChanceShutdownNotification.
A driver that calls IoRegisterLastChanceShutdownNotification must satisfy the following restrictions in its DispatchShutdown routine:
The DispatchShutdown routine must not call any pageable routines.
The DispatchShutdown routine must not access pageable memory.
The DispatchShutdown routine must not perform any file I/O operations.
Most drivers that require shutdown notification should call the IoRegisterShutdownNotification routine,
which does not impose these limitations on the DispatchShutdown routine,
and which causes the DispatchShutdown routine to be called before the file systems are flushed.
Only drivers that must do some cleanup after the file systems are flushed,
such as a driver for a mass storage device, should use IoRegisterLastChanceShutdownNotification.
*/
KdBreakPoint();
return 0;
}
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PDEVICE_OBJECT DevObj;
UNICODE_STRING LinkName;
DevObj = DriverObject->DeviceObject;
RtlInitUnicodeString( &LinkName, L"\\DosDevices\\shutdown" );
IoDeleteSymbolicLink( &LinkName );// Remove symbolic link from Object namespace...
IoDeleteDevice( DevObj ); // Unload the callbacks from the kernel to this driver
IoUnregisterShutdownNotification(DevObj);
}
#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNICODE_STRING DeviceName;
UNICODE_STRING LinkName;
PDEVICE_OBJECT DeviceObject;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
/*
经过测试,如果不注册关机回调,仅仅这一行代码是不起作用的。
注册关机回调,没有这一行代码也是不起作用的。
*/
DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = DispatchShutdown;
RtlInitUnicodeString( &DeviceName, L"\\Device\\shutdown" );
status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
RtlInitUnicodeString( &LinkName, L"\\DosDevices\\shutdown" );
status = IoCreateSymbolicLink( &LinkName, &DeviceName );
if ( !NT_SUCCESS( status )) {
IoDeleteDevice( DeviceObject );
return status;
}
DeviceObject->Flags |= DO_BUFFERED_IO;
//status = IoRegisterShutdownNotification(DeviceObject);
status = IoRegisterLastChanceShutdownNotification(DeviceObject);//必须注册设备。方能收到消息。
if (!NT_SUCCESS(status))
{
DbgPrint("IoRegisterShutdownNotification fail!");
IoDeleteSymbolicLink( &LinkName );// Remove symbolic link from Object namespace...
IoDeleteDevice( DeviceObject );
}
return status;//STATUS_SUCCESS
}
文本就命名为:IoRegisterShutdownNotification.C吧!
made by correy
made at 2013.06.28
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
*/
#include <ntifs.h>
//#include <ntddk.h> //这两个次序不能乱,有上面的,这个可以注释掉。
DRIVER_DISPATCH DispatchShutdown;
NTSTATUS DispatchShutdown(__in struct _DEVICE_OBJECT *DeviceObject, __in struct _IRP *Irp)
{
/*
The IoRegisterShutdownNotification routine registers the driver to receive an IRP_MJ_SHUTDOWN IRP for the specified device when the system shuts down.
The driver receives one such IRP for each device it registers to receive notification for.
Drivers handle IRP_MJ_SHUTDOWN IRPs within their DispatchShutdown routines.
If the driver ceases to require shutdown notification for the device,
use IoUnregisterShutdownNotification to remove the driver from the shutdown notification queue.
Only one driver in a device stack should register to receive shutdown notification.
The system sends the driver the IRP_MJ_SHUTDOWN request before it flushes the file systems.
Some drivers, such as drivers for mass storage devices, can require shutdown notification after the system flushes the file systems.
To receive shutdown notification for a device after the file systems are flushed, use the IoRegisterLastChanceShutdownNotification routine instead.
The registered DispatchShutdown routine is called before the power manager sends an IRP_MN_SET_POWER request for PowerSystemShutdown.
The DispatchShutdown routine is not called for transitions to any other power states.
A driver writer can make no assumptions about the order in which the driver's DispatchShutdown routine will be called in relation to other such routines or to other shutdown activities.
A PnP driver might register a shutdown routine to perform certain tasks before system shutdown starts, such as locking down code.
一下是IoRegisterLastChanceShutdownNotification特有的性质:
For any device that is registered with this routine, the system sends the IRP_MJ_SHUTDOWN IRP after all file systems are flushed.
Only one driver in a device stack should register to receive shutdown notification, by calling either IoRegisterShutdownNotification or IoRegisterLastChanceShutdownNotification.
A driver that calls IoRegisterLastChanceShutdownNotification must satisfy the following restrictions in its DispatchShutdown routine:
The DispatchShutdown routine must not call any pageable routines.
The DispatchShutdown routine must not access pageable memory.
The DispatchShutdown routine must not perform any file I/O operations.
Most drivers that require shutdown notification should call the IoRegisterShutdownNotification routine,
which does not impose these limitations on the DispatchShutdown routine,
and which causes the DispatchShutdown routine to be called before the file systems are flushed.
Only drivers that must do some cleanup after the file systems are flushed,
such as a driver for a mass storage device, should use IoRegisterLastChanceShutdownNotification.
*/
KdBreakPoint();
return 0;
}
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PDEVICE_OBJECT DevObj;
UNICODE_STRING LinkName;
DevObj = DriverObject->DeviceObject;
RtlInitUnicodeString( &LinkName, L"\\DosDevices\\shutdown" );
IoDeleteSymbolicLink( &LinkName );// Remove symbolic link from Object namespace...
IoDeleteDevice( DevObj ); // Unload the callbacks from the kernel to this driver
IoUnregisterShutdownNotification(DevObj);
}
#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNICODE_STRING DeviceName;
UNICODE_STRING LinkName;
PDEVICE_OBJECT DeviceObject;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
/*
经过测试,如果不注册关机回调,仅仅这一行代码是不起作用的。
注册关机回调,没有这一行代码也是不起作用的。
*/
DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = DispatchShutdown;
RtlInitUnicodeString( &DeviceName, L"\\Device\\shutdown" );
status = IoCreateDevice(DriverObject, 0, &DeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &DeviceObject);
if (!NT_SUCCESS(status)) {
return status;
}
RtlInitUnicodeString( &LinkName, L"\\DosDevices\\shutdown" );
status = IoCreateSymbolicLink( &LinkName, &DeviceName );
if ( !NT_SUCCESS( status )) {
IoDeleteDevice( DeviceObject );
return status;
}
DeviceObject->Flags |= DO_BUFFERED_IO;
//status = IoRegisterShutdownNotification(DeviceObject);
status = IoRegisterLastChanceShutdownNotification(DeviceObject);//必须注册设备。方能收到消息。
if (!NT_SUCCESS(status))
{
DbgPrint("IoRegisterShutdownNotification fail!");
IoDeleteSymbolicLink( &LinkName );// Remove symbolic link from Object namespace...
IoDeleteDevice( DeviceObject );
}
return status;//STATUS_SUCCESS
}
2013年6月3日星期一
GetVolumeNameForVolumeMountPoint.Cpp
/*
GetVolumeNameForVolumeMountPoint.Cpp
参考:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365238(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364037(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365717(v=vs.85).aspx
*/
#include <Windows.h>
#define BUFSIZE MAX_PATH
int MountedFolder( TCHAR * szVolumeMountPoint , TCHAR * szVolumeMountPoint2 )
{
BOOL bFlag;
TCHAR Buf[BUFSIZE]; // temporary buffer for volume name
// We should do some error checking on the inputs. Make sure there
// are colons and backslashes in the right places, and so on
bFlag = GetVolumeNameForVolumeMountPoint(
szVolumeMountPoint2, // input volume mount point or directory
Buf, // output volume name buffer
BUFSIZE); // size of volume name buffer
if (bFlag != TRUE) {
int x = GetLastError();//2 系统找不到指定的文件。
return (-2);
}
//_tprintf( TEXT("Volume name of %s is %s\n"), argv[2], Buf );
bFlag = SetVolumeMountPoint(
szVolumeMountPoint, // mount point
Buf); // volume to be mounted
if (bFlag == 0) {
int x = GetLastError();
x = 0;//目录不是空的。 0x00000091 还有0x57 系统找不到指定的文件。
}
return (bFlag);
}
void EnumeratingVolumeGUIDPaths()
{
BOOL bFlag;
TCHAR Buf[BUFSIZE]; // temporary buffer for volume name
TCHAR Drive[] = TEXT("c:\\"); // template drive specifier
TCHAR I; // generic loop counter
// Walk through legal drive letters, skipping floppies.
for (I = TEXT('c'); I < TEXT('z'); I++ )
{
// Stamp the drive for the appropriate letter.
Drive[0] = I;
bFlag = GetVolumeNameForVolumeMountPoint(
Drive, // input volume mount point or directory
Buf, // output volume name buffer
BUFSIZE ); // size of volume name buffer
if (bFlag) {
_tprintf (TEXT("The ID of drive \"%s\" is \"%s\"\n"), Drive, Buf);
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//这两个必须存在,不然返回错误。
wchar_t * path = L"d:\\test\\";//必须为空。
wchar_t * Volume = L"f:\\";
int x = MountedFolder(path , Volume );
BOOL bFlag = DeleteVolumeMountPoint(path);// Path of the volume mount point
EnumeratingVolumeGUIDPaths();
return 0;
}
GetVolumeNameForVolumeMountPoint.Cpp
参考:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365238(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364037(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365717(v=vs.85).aspx
*/
#include <Windows.h>
#define BUFSIZE MAX_PATH
int MountedFolder( TCHAR * szVolumeMountPoint , TCHAR * szVolumeMountPoint2 )
{
BOOL bFlag;
TCHAR Buf[BUFSIZE]; // temporary buffer for volume name
// We should do some error checking on the inputs. Make sure there
// are colons and backslashes in the right places, and so on
bFlag = GetVolumeNameForVolumeMountPoint(
szVolumeMountPoint2, // input volume mount point or directory
Buf, // output volume name buffer
BUFSIZE); // size of volume name buffer
if (bFlag != TRUE) {
int x = GetLastError();//2 系统找不到指定的文件。
return (-2);
}
//_tprintf( TEXT("Volume name of %s is %s\n"), argv[2], Buf );
bFlag = SetVolumeMountPoint(
szVolumeMountPoint, // mount point
Buf); // volume to be mounted
if (bFlag == 0) {
int x = GetLastError();
x = 0;//目录不是空的。 0x00000091 还有0x57 系统找不到指定的文件。
}
return (bFlag);
}
void EnumeratingVolumeGUIDPaths()
{
BOOL bFlag;
TCHAR Buf[BUFSIZE]; // temporary buffer for volume name
TCHAR Drive[] = TEXT("c:\\"); // template drive specifier
TCHAR I; // generic loop counter
// Walk through legal drive letters, skipping floppies.
for (I = TEXT('c'); I < TEXT('z'); I++ )
{
// Stamp the drive for the appropriate letter.
Drive[0] = I;
bFlag = GetVolumeNameForVolumeMountPoint(
Drive, // input volume mount point or directory
Buf, // output volume name buffer
BUFSIZE ); // size of volume name buffer
if (bFlag) {
_tprintf (TEXT("The ID of drive \"%s\" is \"%s\"\n"), Drive, Buf);
}
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//这两个必须存在,不然返回错误。
wchar_t * path = L"d:\\test\\";//必须为空。
wchar_t * Volume = L"f:\\";
int x = MountedFolder(path , Volume );
BOOL bFlag = DeleteVolumeMountPoint(path);// Path of the volume mount point
EnumeratingVolumeGUIDPaths();
return 0;
}
2013年5月8日星期三
IoAllocateErrorLogEntry.C
/*
文本就命名为:IoAllocateErrorLogEntry.C吧!
made by correy
made at 2013.05.07
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
刚开始,我以为必须有mc文件才能生成时间日志呢?
其实不需要mc文件也可以生成日志。
不过需要mc文件生成时间日志也不难,很简单,source里面加入一行代码就可以编译了。
不过,还得编写mc文件。
不过编写mc文件也不难。
不过这很麻烦,所以我就不用mc文件了。
如果自己的驱动定义自己的错误码:可以修改:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog\System\DriverName下面的值
EventMessageFile (REG_EXPAND_SZ) 和 TypesSupported (REG_DWORD)
刚开始我还以为是生成记录需要添加这呢!
参考资料:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff560866(v=vs.85).aspx
http://blog.csdn.net/peterwtu/article/details/8179674
http://driverentry.com.br/en/blog/?p=324
http://driverentry.com.br/en/blog/?p=348
http://www.osronline.com/showThread.cfm?link=28746
http://hi.baidu.com/wesley0312/item/a35737511c3e13dbd58bac51
*/
#include <ntddk.h>
#define _In_
#define _Inout_
#define _Inout_opt_
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PVOID p = 0;
PIO_ERROR_LOG_PACKET pioelp;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//方法一:
p = IoAllocateErrorLogEntry(
DriverObject, //也可以:Pointer to a device object representing the device on which an I/O error occurred,
sizeof(IO_ERROR_LOG_PACKET) //sizeof(IO_ERROR_LOG_PACKET) + size of the DumpData member + combined size of any driver-supplied insertion strings.
);
//Drivers must not treat IoAllocateErrorLogEntry returning NULL as a fatal error.
//The driver must continue to function normally, whether or not it can log errors.
if (p == NULL) {
return status;
}
pioelp = p;
RtlZeroMemory(p, sizeof(IO_ERROR_LOG_PACKET));
pioelp->ErrorCode = 9;//查看日志的时候显示的是的:时间id == 9
//IoWriteErrorLogEntry frees the error log entry.
//Drivers must not call IoFreeErrorLogEntry on a log entry that they have already passed to IoWriteErrorLogEntry.
IoWriteErrorLogEntry(p);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//方法二:复杂一点。
p = IoAllocateErrorLogEntry(DriverObject, sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG));
if (p == NULL) {
return status;
}
pioelp = p;
RtlZeroMemory(p, sizeof(IO_ERROR_LOG_PACKET));
pioelp->ErrorCode = 9;//查看日志的时候显示的是的:时间id == 9
pioelp->DumpData[0] = 0x12345678;//不过这个也能显示,要在数据里面查找。
pioelp->DumpDataSize = sizeof(ULONG);
IoWriteErrorLogEntry(p);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//方法三:再复杂一点,包含的信息也多一点。
{
UNICODE_STRING usNtStatus;
ANSI_STRING asNtStatus;
UCHAR ucFinalSize;
PWSTR pwzTarget;
PIO_ERROR_LOG_PACKET pLogPacket;
wchar_t ErroeMessage[] = L"made by correy"; //这个信息显示在前面。
RtlInitAnsiString(&asNtStatus, "QQ:112426112"); //这个信息显示在后面。
RtlAnsiStringToUnicodeString(&usNtStatus, &asNtStatus, TRUE);
ucFinalSize = sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG) + usNtStatus.Length + sizeof(WCHAR) + (wcslen(ErroeMessage) + 1) * sizeof(WCHAR);
pLogPacket = IoAllocateErrorLogEntry(DriverObject, ucFinalSize);
RtlZeroMemory(pLogPacket, sizeof(IO_ERROR_LOG_PACKET));
//A variable-size array that can be used to store driver-specific binary data,
//Drivers must specify the size, in bytes, of the array in the DumpDataSize member of this structure.
pLogPacket->DumpData[0] = 0x12345678;//感觉这个显示的没啥用。
//Indicates the size, in bytes, of the variable-length DumpData member of this structure.
//The specified value must be a multiple of sizeof(ULONG).
pLogPacket->DumpDataSize = sizeof(ULONG);//估计是DumpData的个数 * sizeof(ULONG)
//Indicates the offset, in bytes, from the beginning of the structure, at which any driver-supplied insertion string data begins.
//Normally this will be sizeof(IO_ERROR_LOG_PACKET) plus the value of the DumpDataSize member.
//If there are no driver-supplied insertion strings, StringOffset can be zero.
pLogPacket->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + pLogPacket->DumpDataSize;
//Indicates the number of insertion strings the driver will supply with this error log entry.
//Drivers set this value to zero for errors that need no insertion strings.
//The Event Viewer uses these strings to fill in the "%2" through "%n" entries in the string template for this error code.
//The null-terminated Unicode strings themselves follow the IO_ERROR_LOG_PACKET structure in memory.
pLogPacket->NumberOfStrings = 2;//有两个字符串。
//复制ErroeMessage信息
pwzTarget = (PWSTR) ((PCHAR)pLogPacket + pLogPacket->StringOffset);
wcscpy(pwzTarget, ErroeMessage);//追加数据。追加是最好的用词。 The strcpy function copies strSource, including the terminating null character
//复制usNtStatus信息
pwzTarget += wcslen(ErroeMessage) + 1;//这个空一个0.就是跳过一个0.
wcsncpy(pwzTarget, usNtStatus.Buffer, usNtStatus.Length / sizeof(WCHAR));//追加数据。感觉没有比较用字符串结构:UNICODE_STRING和ANSI_STRING
pwzTarget += usNtStatus.Length / sizeof(WCHAR);
*pwzTarget = 0;//结尾置零。
//Specifies the type of error. The Event Viewer uses the error code to determine which string to display as the Description value for the error.
//The Event Viewer takes the string template for the error supplied in the driver's message catalog,
//replaces "%1" in the template with the name of the driver's device object,
//and replaces "%2" through "%n" with the insertion strings supplied with the error log entry.
//ErrorCode is a system-defined or driver-defined constant;
pLogPacket->ErrorCode = 9;//如果赋值为0x12345678,得到的结果为: 22136 == 0x5678.注意这个结构的这个成员的大小。
//还有更多的参数没有填写。
IoWriteErrorLogEntry(pLogPacket);
RtlFreeUnicodeString(&usNtStatus);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return status;//STATUS_SUCCESS
}
文本就命名为:IoAllocateErrorLogEntry.C吧!
made by correy
made at 2013.05.07
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
刚开始,我以为必须有mc文件才能生成时间日志呢?
其实不需要mc文件也可以生成日志。
不过需要mc文件生成时间日志也不难,很简单,source里面加入一行代码就可以编译了。
不过,还得编写mc文件。
不过编写mc文件也不难。
不过这很麻烦,所以我就不用mc文件了。
如果自己的驱动定义自己的错误码:可以修改:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog\System\DriverName下面的值
EventMessageFile (REG_EXPAND_SZ) 和 TypesSupported (REG_DWORD)
刚开始我还以为是生成记录需要添加这呢!
参考资料:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff560866(v=vs.85).aspx
http://blog.csdn.net/peterwtu/article/details/8179674
http://driverentry.com.br/en/blog/?p=324
http://driverentry.com.br/en/blog/?p=348
http://www.osronline.com/showThread.cfm?link=28746
http://hi.baidu.com/wesley0312/item/a35737511c3e13dbd58bac51
*/
#include <ntddk.h>
#define _In_
#define _Inout_
#define _Inout_opt_
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
PVOID p = 0;
PIO_ERROR_LOG_PACKET pioelp;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//方法一:
p = IoAllocateErrorLogEntry(
DriverObject, //也可以:Pointer to a device object representing the device on which an I/O error occurred,
sizeof(IO_ERROR_LOG_PACKET) //sizeof(IO_ERROR_LOG_PACKET) + size of the DumpData member + combined size of any driver-supplied insertion strings.
);
//Drivers must not treat IoAllocateErrorLogEntry returning NULL as a fatal error.
//The driver must continue to function normally, whether or not it can log errors.
if (p == NULL) {
return status;
}
pioelp = p;
RtlZeroMemory(p, sizeof(IO_ERROR_LOG_PACKET));
pioelp->ErrorCode = 9;//查看日志的时候显示的是的:时间id == 9
//IoWriteErrorLogEntry frees the error log entry.
//Drivers must not call IoFreeErrorLogEntry on a log entry that they have already passed to IoWriteErrorLogEntry.
IoWriteErrorLogEntry(p);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//方法二:复杂一点。
p = IoAllocateErrorLogEntry(DriverObject, sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG));
if (p == NULL) {
return status;
}
pioelp = p;
RtlZeroMemory(p, sizeof(IO_ERROR_LOG_PACKET));
pioelp->ErrorCode = 9;//查看日志的时候显示的是的:时间id == 9
pioelp->DumpData[0] = 0x12345678;//不过这个也能显示,要在数据里面查找。
pioelp->DumpDataSize = sizeof(ULONG);
IoWriteErrorLogEntry(p);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//方法三:再复杂一点,包含的信息也多一点。
{
UNICODE_STRING usNtStatus;
ANSI_STRING asNtStatus;
UCHAR ucFinalSize;
PWSTR pwzTarget;
PIO_ERROR_LOG_PACKET pLogPacket;
wchar_t ErroeMessage[] = L"made by correy"; //这个信息显示在前面。
RtlInitAnsiString(&asNtStatus, "QQ:112426112"); //这个信息显示在后面。
RtlAnsiStringToUnicodeString(&usNtStatus, &asNtStatus, TRUE);
ucFinalSize = sizeof(IO_ERROR_LOG_PACKET) + sizeof(ULONG) + usNtStatus.Length + sizeof(WCHAR) + (wcslen(ErroeMessage) + 1) * sizeof(WCHAR);
pLogPacket = IoAllocateErrorLogEntry(DriverObject, ucFinalSize);
RtlZeroMemory(pLogPacket, sizeof(IO_ERROR_LOG_PACKET));
//A variable-size array that can be used to store driver-specific binary data,
//Drivers must specify the size, in bytes, of the array in the DumpDataSize member of this structure.
pLogPacket->DumpData[0] = 0x12345678;//感觉这个显示的没啥用。
//Indicates the size, in bytes, of the variable-length DumpData member of this structure.
//The specified value must be a multiple of sizeof(ULONG).
pLogPacket->DumpDataSize = sizeof(ULONG);//估计是DumpData的个数 * sizeof(ULONG)
//Indicates the offset, in bytes, from the beginning of the structure, at which any driver-supplied insertion string data begins.
//Normally this will be sizeof(IO_ERROR_LOG_PACKET) plus the value of the DumpDataSize member.
//If there are no driver-supplied insertion strings, StringOffset can be zero.
pLogPacket->StringOffset = sizeof(IO_ERROR_LOG_PACKET) + pLogPacket->DumpDataSize;
//Indicates the number of insertion strings the driver will supply with this error log entry.
//Drivers set this value to zero for errors that need no insertion strings.
//The Event Viewer uses these strings to fill in the "%2" through "%n" entries in the string template for this error code.
//The null-terminated Unicode strings themselves follow the IO_ERROR_LOG_PACKET structure in memory.
pLogPacket->NumberOfStrings = 2;//有两个字符串。
//复制ErroeMessage信息
pwzTarget = (PWSTR) ((PCHAR)pLogPacket + pLogPacket->StringOffset);
wcscpy(pwzTarget, ErroeMessage);//追加数据。追加是最好的用词。 The strcpy function copies strSource, including the terminating null character
//复制usNtStatus信息
pwzTarget += wcslen(ErroeMessage) + 1;//这个空一个0.就是跳过一个0.
wcsncpy(pwzTarget, usNtStatus.Buffer, usNtStatus.Length / sizeof(WCHAR));//追加数据。感觉没有比较用字符串结构:UNICODE_STRING和ANSI_STRING
pwzTarget += usNtStatus.Length / sizeof(WCHAR);
*pwzTarget = 0;//结尾置零。
//Specifies the type of error. The Event Viewer uses the error code to determine which string to display as the Description value for the error.
//The Event Viewer takes the string template for the error supplied in the driver's message catalog,
//replaces "%1" in the template with the name of the driver's device object,
//and replaces "%2" through "%n" with the insertion strings supplied with the error log entry.
//ErrorCode is a system-defined or driver-defined constant;
pLogPacket->ErrorCode = 9;//如果赋值为0x12345678,得到的结果为: 22136 == 0x5678.注意这个结构的这个成员的大小。
//还有更多的参数没有填写。
IoWriteErrorLogEntry(pLogPacket);
RtlFreeUnicodeString(&usNtStatus);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
return status;//STATUS_SUCCESS
}
2013年5月6日星期一
IoRegisterPlugPlayNotification.C
/*
文本就命名为:IoRegisterPlugPlayNotification.C吧!
made by correy
made at 2013.05.05
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
usb的GUID可以用工具或者编程得到。如usbview工程。这个工程也能监控usb的变化。
参考资料:http://www.osronline.com/showthread.cfm?link=172042等。
还有待研究的信息有:
GUID_DEVINTERFACE_DISK,GUID_DEVINTERFACE_VOLUME and/or GUID_DEVINTERFACE_PARTITION
*/
#include <ntddk.h>
#include <initguid.h> //解决:error LNK2001: unresolved external symbol _GUID_XXXXXXXXXXXXXXXXXX
//#include <Ntddstor.h> //GUID_DEVINTERFACE_VOLUME
#define _In_
#define _Inout_
#define _Inout_opt_
/* A5DCBF10-6530-11D2-901F-00C04FB951ED*/
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE,0xA5DCBF10L,0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED ); //没有找到usbiodef.h
PVOID NotificationEntry;
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
/*
In Windows 7 and later versions of Windows, this function is obsolete and is provided only to support existing drivers.
Use the IoUnregisterPlugPlayNotificationEx routine instead.
Warning The system does not synchronize between the execution of the notification routine and IoUnregisterPlugPlayNotification.
Therefore, the routine can be called after the IoUnregisterPlugPlayNotification method has returned.
If necessary, a driver should implement its own mechanism to ignore any notifications after IoUnregisterPlugPlayNotification has been called.
IoUnregisterPlugPlayNotification removes one PnP notification registration; that is, the registration of one driver callback routine for one PnP event category.
Drivers should unregister a notification first, then free any related context buffer.
A driver cannot be unloaded until it removes all of its PnP notification registrations because there is a reference on its driver object for each active registration.
*/
NTSTATUS status = 0;
status = IoUnregisterPlugPlayNotification(NotificationEntry);
if (!NT_SUCCESS(status)) {
DbgPrint("IoUnregisterPlugPlayNotification fail!\n");
}
}
DRIVER_NOTIFICATION_CALLBACK_ROUTINE driver_notification_callback_routine;
//typedef
NTSTATUS driver_notification_callback_routine(_In_ PVOID NotificationStructure, _Inout_opt_ PVOID Context)
{
//这里不是插上usb接口就能发现了,必须是连接上,比如我选择手机里面的数据存储,才能发现的。
//相应的usb的拔出不是简单的硬件的拔出,在手机上点击取消连接,即可以运行到这里。
//奇怪的是QQ电脑管家没有注册这个回调,可是获取的比这个函数得到的消息还要早。
//理论是用别的内核办法,难道是用应用层的办法?
//这里更深层次的操作,请看:http://www.osronline.com/article.cfm?id=24
DbgPrint("pnp发生了?停下来看看吧!\n");
KdBreakPoint();
return STATUS_SUCCESS;
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
status = IoRegisterPlugPlayNotification(
EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,//还有:EventCategoryHardwareProfileChange 和 EventCategoryTargetDeviceChange 。
(PVOID)&GUID_DEVINTERFACE_USB_DEVICE,//GUID_DEVINTERFACE_VOLUME GUID_DEVINTERFACE_USB_DEVICE
DriverObject,//To ensure that the driver remains loaded while it is registered for PnP notification, this call increments the reference count on DriverObject.
//The PnP manager decrements the reference count when this registration is removed.要ObDereferenceObject一下?没有用也没有蓝屏。
driver_notification_callback_routine,
0,//传递的参数。
&NotificationEntry);
if (!NT_SUCCESS(status)) {
DbgPrint("IoRegisterPlugPlayNotification fail!\n");
return status;
}
return status;//STATUS_SUCCESS
}
文本就命名为:IoRegisterPlugPlayNotification.C吧!
made by correy
made at 2013.05.05
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
usb的GUID可以用工具或者编程得到。如usbview工程。这个工程也能监控usb的变化。
参考资料:http://www.osronline.com/showthread.cfm?link=172042等。
还有待研究的信息有:
GUID_DEVINTERFACE_DISK,GUID_DEVINTERFACE_VOLUME and/or GUID_DEVINTERFACE_PARTITION
*/
#include <ntddk.h>
#include <initguid.h> //解决:error LNK2001: unresolved external symbol _GUID_XXXXXXXXXXXXXXXXXX
//#include <Ntddstor.h> //GUID_DEVINTERFACE_VOLUME
#define _In_
#define _Inout_
#define _Inout_opt_
/* A5DCBF10-6530-11D2-901F-00C04FB951ED*/
DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE,0xA5DCBF10L,0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED ); //没有找到usbiodef.h
PVOID NotificationEntry;
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
/*
In Windows 7 and later versions of Windows, this function is obsolete and is provided only to support existing drivers.
Use the IoUnregisterPlugPlayNotificationEx routine instead.
Warning The system does not synchronize between the execution of the notification routine and IoUnregisterPlugPlayNotification.
Therefore, the routine can be called after the IoUnregisterPlugPlayNotification method has returned.
If necessary, a driver should implement its own mechanism to ignore any notifications after IoUnregisterPlugPlayNotification has been called.
IoUnregisterPlugPlayNotification removes one PnP notification registration; that is, the registration of one driver callback routine for one PnP event category.
Drivers should unregister a notification first, then free any related context buffer.
A driver cannot be unloaded until it removes all of its PnP notification registrations because there is a reference on its driver object for each active registration.
*/
NTSTATUS status = 0;
status = IoUnregisterPlugPlayNotification(NotificationEntry);
if (!NT_SUCCESS(status)) {
DbgPrint("IoUnregisterPlugPlayNotification fail!\n");
}
}
DRIVER_NOTIFICATION_CALLBACK_ROUTINE driver_notification_callback_routine;
//typedef
NTSTATUS driver_notification_callback_routine(_In_ PVOID NotificationStructure, _Inout_opt_ PVOID Context)
{
//这里不是插上usb接口就能发现了,必须是连接上,比如我选择手机里面的数据存储,才能发现的。
//相应的usb的拔出不是简单的硬件的拔出,在手机上点击取消连接,即可以运行到这里。
//奇怪的是QQ电脑管家没有注册这个回调,可是获取的比这个函数得到的消息还要早。
//理论是用别的内核办法,难道是用应用层的办法?
//这里更深层次的操作,请看:http://www.osronline.com/article.cfm?id=24
DbgPrint("pnp发生了?停下来看看吧!\n");
KdBreakPoint();
return STATUS_SUCCESS;
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
status = IoRegisterPlugPlayNotification(
EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,//还有:EventCategoryHardwareProfileChange 和 EventCategoryTargetDeviceChange 。
(PVOID)&GUID_DEVINTERFACE_USB_DEVICE,//GUID_DEVINTERFACE_VOLUME GUID_DEVINTERFACE_USB_DEVICE
DriverObject,//To ensure that the driver remains loaded while it is registered for PnP notification, this call increments the reference count on DriverObject.
//The PnP manager decrements the reference count when this registration is removed.要ObDereferenceObject一下?没有用也没有蓝屏。
driver_notification_callback_routine,
0,//传递的参数。
&NotificationEntry);
if (!NT_SUCCESS(status)) {
DbgPrint("IoRegisterPlugPlayNotification fail!\n");
return status;
}
return status;//STATUS_SUCCESS
}
2013年5月3日星期五
GetCurrentUserAndDomain.Cpp
#include "stdafx.h"
#include <windows.h>
/*
文本就命名为:GetCurrentUserAndDomain.Cpp吧!
made by correy
made at 2013.05.03
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
本文摘自:// http://support.microsoft.com/kb/111544/zh-cn
*/
//**********************************************************************
// FUNCTION: GetCurrentUserAndDomain - This function looks up the user name and domain name for the user account associated with the calling thread.
//
// PARAMETERS: szUser - a buffer that receives the user name
// pcchUser - the size, in characters, of szUser
// szDomain - a buffer that receives the domain name
// pcchDomain - the size, in characters, of szDomain
//
// RETURN VALUE: TRUE if the function succeeds. Otherwise, FALSE and GetLastError() will return the failure reason.
//
// If either of the supplied buffers are too small,
// GetLastError() will return ERROR_INSUFFICIENT_BUFFER and pcchUser and pcchDomain will be adjusted to reflect the required buffer sizes.
//**********************************************************************
BOOL GetCurrentUserAndDomain(PTSTR szUser, PDWORD pcchUser, PTSTR szDomain, PDWORD pcchDomain)
{
BOOL fSuccess = FALSE;
HANDLE hToken = NULL;
PTOKEN_USER ptiUser = NULL;
DWORD cbti = 0;
SID_NAME_USE snu;
__try
{
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))// Get the calling thread's access token.
{
if (GetLastError() != ERROR_NO_TOKEN) {
__leave;
}
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {// Retry against process token if no thread token exists.
__leave;
}
}
if (GetTokenInformation(hToken, TokenUser, NULL, 0, &cbti)) { // Obtain the size of the user information in the token.
__leave;// Call should have failed due to zero-length buffer.
} else {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {// Call should have failed due to zero-length buffer.
__leave;
}
}
ptiUser = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), 0, cbti);// Allocate buffer for user information in the token.
if (!ptiUser) {
__leave;
}
if (!GetTokenInformation(hToken, TokenUser, ptiUser, cbti, &cbti)) {// Retrieve the user information from the token.
__leave;
}
if (!LookupAccountSid(NULL, ptiUser->User.Sid, szUser, pcchUser, szDomain, pcchDomain, &snu)) {// Retrieve user name and domain name based on user's SID.
__leave;
}
fSuccess = TRUE;
} __finally {
// Free resources.
if (hToken) {
CloseHandle(hToken);
}
if (ptiUser) {
HeapFree(GetProcessHeap(), 0, ptiUser);
}
}
return fSuccess;
}
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t szUser[MAX_PATH] = {0};//估计最大值不是这个。
wchar_t szDomain[MAX_PATH] = {0};//估计最大值不是这个。这个好像和计算机名一样。
DWORD d = MAX_PATH;
//bool b = (szUser, &d, szDomain, &d);//这一行编译通过。但是用汇编写就不会出现这样的未达到预期的错误。
bool b = GetCurrentUserAndDomain(szUser, &d, szDomain, &d);
MessageBox(0,szUser,szDomain,0);
return 0;
}
#include <windows.h>
/*
文本就命名为:GetCurrentUserAndDomain.Cpp吧!
made by correy
made at 2013.05.03
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
本文摘自:// http://support.microsoft.com/kb/111544/zh-cn
*/
//**********************************************************************
// FUNCTION: GetCurrentUserAndDomain - This function looks up the user name and domain name for the user account associated with the calling thread.
//
// PARAMETERS: szUser - a buffer that receives the user name
// pcchUser - the size, in characters, of szUser
// szDomain - a buffer that receives the domain name
// pcchDomain - the size, in characters, of szDomain
//
// RETURN VALUE: TRUE if the function succeeds. Otherwise, FALSE and GetLastError() will return the failure reason.
//
// If either of the supplied buffers are too small,
// GetLastError() will return ERROR_INSUFFICIENT_BUFFER and pcchUser and pcchDomain will be adjusted to reflect the required buffer sizes.
//**********************************************************************
BOOL GetCurrentUserAndDomain(PTSTR szUser, PDWORD pcchUser, PTSTR szDomain, PDWORD pcchDomain)
{
BOOL fSuccess = FALSE;
HANDLE hToken = NULL;
PTOKEN_USER ptiUser = NULL;
DWORD cbti = 0;
SID_NAME_USE snu;
__try
{
if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))// Get the calling thread's access token.
{
if (GetLastError() != ERROR_NO_TOKEN) {
__leave;
}
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) {// Retry against process token if no thread token exists.
__leave;
}
}
if (GetTokenInformation(hToken, TokenUser, NULL, 0, &cbti)) { // Obtain the size of the user information in the token.
__leave;// Call should have failed due to zero-length buffer.
} else {
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {// Call should have failed due to zero-length buffer.
__leave;
}
}
ptiUser = (PTOKEN_USER) HeapAlloc(GetProcessHeap(), 0, cbti);// Allocate buffer for user information in the token.
if (!ptiUser) {
__leave;
}
if (!GetTokenInformation(hToken, TokenUser, ptiUser, cbti, &cbti)) {// Retrieve the user information from the token.
__leave;
}
if (!LookupAccountSid(NULL, ptiUser->User.Sid, szUser, pcchUser, szDomain, pcchDomain, &snu)) {// Retrieve user name and domain name based on user's SID.
__leave;
}
fSuccess = TRUE;
} __finally {
// Free resources.
if (hToken) {
CloseHandle(hToken);
}
if (ptiUser) {
HeapFree(GetProcessHeap(), 0, ptiUser);
}
}
return fSuccess;
}
int _tmain(int argc, _TCHAR* argv[])
{
wchar_t szUser[MAX_PATH] = {0};//估计最大值不是这个。
wchar_t szDomain[MAX_PATH] = {0};//估计最大值不是这个。这个好像和计算机名一样。
DWORD d = MAX_PATH;
//bool b = (szUser, &d, szDomain, &d);//这一行编译通过。但是用汇编写就不会出现这样的未达到预期的错误。
bool b = GetCurrentUserAndDomain(szUser, &d, szDomain, &d);
MessageBox(0,szUser,szDomain,0);
return 0;
}
2013年5月2日星期四
服务的基本框架
/*
文本就命名为:RegisterServiceCtrlHandler.Cpp吧!
made by correy
made at 2013.05.02
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
微软上有个服务的例子:A basic Windows service in C++,是用类写的:
http://code.msdn.microsoft.com/windowsdesktop/CppWindowsService-cacf4948
这个修改自msdn,比上面的简单,容易理解:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb540475(v=vs.85).aspx
其余可参考的还有:这是些辅助的功能。
http://msdn.microsoft.com/en-us/library/windows/desktop/bb540473(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/bb540474(v=vs.85).aspx
运行和调试方法:
命令行参数:install
然后:net start svcname
再:net stop svcname
或者:sc query svcname等。
*/
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
//#include "sample.h"
#define SVCNAME TEXT("SvcName")
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
VOID SvcInstall(void);
VOID WINAPI SvcCtrlHandler( DWORD );
VOID WINAPI SvcMain( DWORD, LPTSTR * );
VOID ReportSvcStatus( DWORD, DWORD, DWORD );
VOID SvcInit( DWORD, LPTSTR * );
VOID SvcReportEvent( LPTSTR );
// Purpose: Entry point for the process
void __cdecl _tmain(int argc, TCHAR *argv[])
{
DebugBreak();
// If command-line parameter is "install", install the service. 如果命令行参数是:install就安装服务。
if( lstrcmpi( argv[1], TEXT("install")) == 0 )
{
SvcInstall();
return;
} else if ( lstrcmpi( argv[1], TEXT("start")) == 0 ) {
//DoStartSvc();
return;
} //等等,可以添加很多操作。
// Otherwise, the service is probably being started by the SCM. 其他的就运行下面,下面的代码是作为服务运行的。
// TO_DO: Add any additional services for the process to this table.
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
{ NULL, NULL }
};
// This call returns when the service has stopped. The process should simply terminate when the call returns.
if (!StartServiceCtrlDispatcher( DispatchTable ))
{
SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
}
}
VOID SvcInstall()// Purpose: Installs a service in the SCM database
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) {
printf("Cannot install service (%d)\n", GetLastError());
return;
}
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager) {
printf("OpenSCManager failed (%d)\n", GetLastError());
return;
}
// Create the service
schService = CreateService(
schSCManager, // SCM database
SVCNAME, // name of service
SVCNAME, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL) {
printf("CreateService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return;
} else {
printf("Service installed successfully\n");
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
// Purpose: Entry point for the service
//
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of the service and subsequent strings are passed by the process
// that called the StartService function to start the service.
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv ) //net start svcname会走到这里。
{
//运行这行代码需要开启Interactive Services Detection服务,并把服务设置为可交互的。
//MessageBox(0,0,0,0);
DebugBreak();//这个简单,附加即可。
// Register the handler function for the service
gSvcStatusHandle = RegisterServiceCtrlHandler( SVCNAME, SvcCtrlHandler);
if( !gSvcStatusHandle ) {
SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));
return;
}
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // These SERVICE_STATUS members remain as set here
gSvcStatus.dwServiceSpecificExitCode = 0;
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );// Report initial status to the SCM
SvcInit( dwArgc, lpszArgv );// Perform service-specific initialization and work.
}
// Purpose: The service code
//
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of the service and subsequent strings are passed by the process
// that called the StartService function to start the service.
VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
{
// TO_DO: Declare and set any required variables.
// Be sure to periodically call ReportSvcStatus() with SERVICE_START_PENDING. If initialization fails, call ReportSvcStatus with SERVICE_STOPPED.
// Create an event. The control handler function, SvcCtrlHandler,signals this event when it receives the stop control code.
ghSvcStopEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
NULL); // no name
if ( ghSvcStopEvent == NULL) {
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );// Report running status when initialization is complete.
// TO_DO: Perform work until service stops.
//添加代码,大部分是开线程。
//等待net stop svcname操作等。
while(1)
{
WaitForSingleObject(ghSvcStopEvent, INFINITE);// Check whether to stop the service.
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
}
// Purpose:
// Sets the current service status and reports it to the SCM.
//
// Parameters:
// dwCurrentState - The current state (see SERVICE_STATUS)
// dwWin32ExitCode - The system error code
// dwWaitHint - Estimated time for pending operation, in milliseconds
VOID ReportSvcStatus( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
// Fill in the SERVICE_STATUS structure.
gSvcStatus.dwCurrentState = dwCurrentState;
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING) {
gSvcStatus.dwControlsAccepted = 0;
} else {
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) ) {
gSvcStatus.dwCheckPoint = 0;
} else {
gSvcStatus.dwCheckPoint = dwCheckPoint++;
}
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );// Report the status of the service to the SCM.
}
// Purpose: Called by SCM whenever a control code is sent to the service using the ControlService function.
// Parameters: dwCtrl - control code
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
{
// Handle the requested control code.
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP: //net stop svcname等类似的操作会走到这里。
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(ghSvcStopEvent);// Signal the service to stop.
return;
case SERVICE_CONTROL_INTERROGATE:
// Fall through to send current status.
break;
default:
break;
}
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
}
// Purpose: Logs messages to the event log
// Parameters: szFunction - name of function that failed
// Remarks: The service must have an entry in the Application event log.
VOID SvcReportEvent(LPTSTR szFunction)
{
HANDLE hEventSource;
LPCTSTR lpszStrings[2];
TCHAR Buffer[80];
hEventSource = RegisterEventSource(NULL, SVCNAME);
if( NULL != hEventSource )
{
StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());
lpszStrings[0] = SVCNAME;
lpszStrings[1] = Buffer;
ReportEvent(hEventSource, // event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
(DWORD)0xC0020100L, //SVC_ERROR, // event identifier
NULL, // no security identifier
2, // size of lpszStrings array
0, // no binary data
lpszStrings, // array of strings
NULL); // no binary data
DeregisterEventSource(hEventSource);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
改进点的如下:(没有测试!)
*/
#include <windows.h>
#include <strsafe.h>
#define SVCNAME TEXT("SvcName")
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
VOID SvcInstall()// Purpose: Installs a service in the SCM database
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) {
printf("Cannot install service (%d)\n", GetLastError());
return;
}
schSCManager = OpenSCManager(// Get a handle to the SCM database.
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager) {
printf("OpenSCManager failed (%d)\n", GetLastError());
return;
}
schService = CreateService( // Create the service
schSCManager, // SCM database
SVCNAME, // name of service
SVCNAME, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL) {
printf("CreateService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return;
} else {
printf("Service installed successfully\n");
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
VOID ReportSvcStatus( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
/*
Purpose: Sets the current service status and reports it to the SCM.
Parameters:
dwCurrentState - The current state (see SERVICE_STATUS)
dwWin32ExitCode - The system error code
dwWaitHint - Estimated time for pending operation, in milliseconds
*/
{
static DWORD dwCheckPoint = 1;
gSvcStatus.dwCurrentState = dwCurrentState;// Fill in the SERVICE_STATUS structure.
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING) {
gSvcStatus.dwControlsAccepted = 0;
} else {
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) ) {
gSvcStatus.dwCheckPoint = 0;
} else {
gSvcStatus.dwCheckPoint = dwCheckPoint++;
}
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );// Report the status of the service to the SCM.
}
VOID SvcReportEvent(LPTSTR szFunction)
// Purpose: Logs messages to the event log
// Parameters: szFunction - name of function that failed
// Remarks: The service must have an entry in the Application event log.
{
HANDLE hEventSource;
LPCTSTR lpszStrings[2];
TCHAR Buffer[80];
hEventSource = RegisterEventSource(NULL, SVCNAME);
if( NULL != hEventSource )
{
StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());
lpszStrings[0] = SVCNAME;
lpszStrings[1] = Buffer;
ReportEvent(hEventSource,// event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
(DWORD)0xC0020100L, //SVC_ERROR, // event identifier
NULL, // no security identifier
2, // size of lpszStrings array
0, // no binary data
lpszStrings, // array of strings
NULL); // no binary data
DeregisterEventSource(hEventSource);
}
}
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
// Purpose: Called by SCM whenever a control code is sent to the service using the ControlService function.
// Parameters: dwCtrl - control code
{
switch(dwCtrl) // Handle the requested control code.
{
case SERVICE_CONTROL_STOP: //net stop svcname等类似的操作会走到这里。
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(ghSvcStopEvent);// Signal the service to stop.
return;
case SERVICE_CONTROL_INTERROGATE:// Fall through to send current status.
break;
default:
break;
}
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
}
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv ) //net start svcname会走到这里。
/*
Purpose: Entry point for the service
Parameters:
dwArgc - Number of arguments in the lpszArgv array
lpszArgv - Array of strings.
The first string is the name of the service and subsequent strings are passed by the process that called the StartService function to start the service.
*/
{
gSvcStatusHandle = RegisterServiceCtrlHandler( SVCNAME, SvcCtrlHandler);
if( !gSvcStatusHandle ) { // Register the handler function for the service:注册处理服务消息的回调函数.
SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));
return;
}
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // These SERVICE_STATUS members remain as set here
gSvcStatus.dwServiceSpecificExitCode = 0;
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );// Report initial status to the SCM
// Perform service-specific initialization and work.
// TO_DO: Declare and set any required variables.
// Be sure to periodically call ReportSvcStatus() with SERVICE_START_PENDING. If initialization fails, call ReportSvcStatus with SERVICE_STOPPED.
// Create an event. The control handler function, SvcCtrlHandler,signals this event when it receives the stop control code.
ghSvcStopEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
NULL); // no name
if ( ghSvcStopEvent == NULL) {
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );// Report running status when initialization is complete.
// TO_DO: Perform work until service stops.
//添加代码,大部分是开线程。
while(1)//等待net stop svcname操作等。
{
WaitForSingleObject(ghSvcStopEvent, INFINITE);// Check whether to stop the service.
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
}
void __cdecl _tmain(int argc, TCHAR *argv[]) // Purpose: Entry point for the process
{
if( lstrcmpi( argv[1], TEXT("install")) == 0 )
{// If command-line parameter is "install", install the service. 如果命令行参数是:install就安装服务。
SvcInstall();
return;
} else if ( lstrcmpi( argv[1], TEXT("start")) == 0 ) {
//DoStartSvc();
return;
} //等等,可以添加很多操作。
// Otherwise, the service is probably being started by the SCM. 其他的就运行下面,下面的代码是作为服务运行的。
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, // TO_DO: Add any additional services for the process to this table.
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcher( DispatchTable )) //启动服务,即服务入口.
{// This call returns when the service has stopped. The process should simply terminate when the call returns.
SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
一些说明:
其实服务很简单:
1.程序的入口调用StartServiceCtrlDispatcher.
2.服务的入口调用RegisterServiceCtrlHandler.
3.服务的线程(即服务入口函数)要有循环,等待等函数,不然服务结束了.
4.服务的入口函数一般开启有线程.一个服务主线程很简单.
5.服务一般没有界面,避免处理消息.
关于服务的保护的一点内容:
1.保护服务不被停止.
需要在服务的处理服务消息的回调函数中屏蔽掉这些消息(SERVICE_CONTROL_STOP),
而且还要在服务的入口中设置SERVICE_STATUS类型的变量的dwControlsAccepted值中一定不要包含SERVICE_ACCEPT_STOP.
这样做了,会导致在服务控制面板里面的此服务的停止按钮灰化,和net/sc stop命令失败.
2.保护服务不被卸载.
一个办法是用驱动保护注册表,建议用注册表回调.说明:隐藏会有一些意外的效果,如查看,还有就是不能手工启动(计算机启动的时候可以启动的).
3.关于服务弹消息框的设置要点:
1).vista以前只要设置服务为可交互式的(可手工修改,包括界面和注册表,也可以编程的时候修改代码),加MessageBox即可.
2).vista及以后到win 8之前,这需要开启interactive service detection服务.
命令行的操作是:net start ui0detect.
这时弹出的消息不在本桌面,要切换桌面方能看到.
3).win 8及以后.
需要先修改HKLM\SYSTEM\CurrentControlSet\Control\Windows\NoInteractiveServices的值为0,原先默认的是1.
再开启交互式的服务,不然会出错误的.
4.关于调试服务(包括服务的启动),请在搜索本站点.
made by correy
made at 2013.12.26
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
文本就命名为:RegisterServiceCtrlHandler.Cpp吧!
made by correy
made at 2013.05.02
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
微软上有个服务的例子:A basic Windows service in C++,是用类写的:
http://code.msdn.microsoft.com/windowsdesktop/CppWindowsService-cacf4948
这个修改自msdn,比上面的简单,容易理解:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb540475(v=vs.85).aspx
其余可参考的还有:这是些辅助的功能。
http://msdn.microsoft.com/en-us/library/windows/desktop/bb540473(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/bb540474(v=vs.85).aspx
运行和调试方法:
命令行参数:install
然后:net start svcname
再:net stop svcname
或者:sc query svcname等。
*/
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
//#include "sample.h"
#define SVCNAME TEXT("SvcName")
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
VOID SvcInstall(void);
VOID WINAPI SvcCtrlHandler( DWORD );
VOID WINAPI SvcMain( DWORD, LPTSTR * );
VOID ReportSvcStatus( DWORD, DWORD, DWORD );
VOID SvcInit( DWORD, LPTSTR * );
VOID SvcReportEvent( LPTSTR );
// Purpose: Entry point for the process
void __cdecl _tmain(int argc, TCHAR *argv[])
{
DebugBreak();
// If command-line parameter is "install", install the service. 如果命令行参数是:install就安装服务。
if( lstrcmpi( argv[1], TEXT("install")) == 0 )
{
SvcInstall();
return;
} else if ( lstrcmpi( argv[1], TEXT("start")) == 0 ) {
//DoStartSvc();
return;
} //等等,可以添加很多操作。
// Otherwise, the service is probably being started by the SCM. 其他的就运行下面,下面的代码是作为服务运行的。
// TO_DO: Add any additional services for the process to this table.
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
{ NULL, NULL }
};
// This call returns when the service has stopped. The process should simply terminate when the call returns.
if (!StartServiceCtrlDispatcher( DispatchTable ))
{
SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
}
}
VOID SvcInstall()// Purpose: Installs a service in the SCM database
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) {
printf("Cannot install service (%d)\n", GetLastError());
return;
}
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager) {
printf("OpenSCManager failed (%d)\n", GetLastError());
return;
}
// Create the service
schService = CreateService(
schSCManager, // SCM database
SVCNAME, // name of service
SVCNAME, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL) {
printf("CreateService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return;
} else {
printf("Service installed successfully\n");
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
// Purpose: Entry point for the service
//
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of the service and subsequent strings are passed by the process
// that called the StartService function to start the service.
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv ) //net start svcname会走到这里。
{
//运行这行代码需要开启Interactive Services Detection服务,并把服务设置为可交互的。
//MessageBox(0,0,0,0);
DebugBreak();//这个简单,附加即可。
// Register the handler function for the service
gSvcStatusHandle = RegisterServiceCtrlHandler( SVCNAME, SvcCtrlHandler);
if( !gSvcStatusHandle ) {
SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));
return;
}
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // These SERVICE_STATUS members remain as set here
gSvcStatus.dwServiceSpecificExitCode = 0;
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );// Report initial status to the SCM
SvcInit( dwArgc, lpszArgv );// Perform service-specific initialization and work.
}
// Purpose: The service code
//
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of the service and subsequent strings are passed by the process
// that called the StartService function to start the service.
VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
{
// TO_DO: Declare and set any required variables.
// Be sure to periodically call ReportSvcStatus() with SERVICE_START_PENDING. If initialization fails, call ReportSvcStatus with SERVICE_STOPPED.
// Create an event. The control handler function, SvcCtrlHandler,signals this event when it receives the stop control code.
ghSvcStopEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
NULL); // no name
if ( ghSvcStopEvent == NULL) {
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );// Report running status when initialization is complete.
// TO_DO: Perform work until service stops.
//添加代码,大部分是开线程。
//等待net stop svcname操作等。
while(1)
{
WaitForSingleObject(ghSvcStopEvent, INFINITE);// Check whether to stop the service.
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
}
// Purpose:
// Sets the current service status and reports it to the SCM.
//
// Parameters:
// dwCurrentState - The current state (see SERVICE_STATUS)
// dwWin32ExitCode - The system error code
// dwWaitHint - Estimated time for pending operation, in milliseconds
VOID ReportSvcStatus( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
// Fill in the SERVICE_STATUS structure.
gSvcStatus.dwCurrentState = dwCurrentState;
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING) {
gSvcStatus.dwControlsAccepted = 0;
} else {
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) ) {
gSvcStatus.dwCheckPoint = 0;
} else {
gSvcStatus.dwCheckPoint = dwCheckPoint++;
}
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );// Report the status of the service to the SCM.
}
// Purpose: Called by SCM whenever a control code is sent to the service using the ControlService function.
// Parameters: dwCtrl - control code
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
{
// Handle the requested control code.
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP: //net stop svcname等类似的操作会走到这里。
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(ghSvcStopEvent);// Signal the service to stop.
return;
case SERVICE_CONTROL_INTERROGATE:
// Fall through to send current status.
break;
default:
break;
}
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
}
// Purpose: Logs messages to the event log
// Parameters: szFunction - name of function that failed
// Remarks: The service must have an entry in the Application event log.
VOID SvcReportEvent(LPTSTR szFunction)
{
HANDLE hEventSource;
LPCTSTR lpszStrings[2];
TCHAR Buffer[80];
hEventSource = RegisterEventSource(NULL, SVCNAME);
if( NULL != hEventSource )
{
StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());
lpszStrings[0] = SVCNAME;
lpszStrings[1] = Buffer;
ReportEvent(hEventSource, // event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
(DWORD)0xC0020100L, //SVC_ERROR, // event identifier
NULL, // no security identifier
2, // size of lpszStrings array
0, // no binary data
lpszStrings, // array of strings
NULL); // no binary data
DeregisterEventSource(hEventSource);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
改进点的如下:(没有测试!)
*/
#include <windows.h>
#include <strsafe.h>
#define SVCNAME TEXT("SvcName")
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
VOID SvcInstall()// Purpose: Installs a service in the SCM database
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) {
printf("Cannot install service (%d)\n", GetLastError());
return;
}
schSCManager = OpenSCManager(// Get a handle to the SCM database.
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager) {
printf("OpenSCManager failed (%d)\n", GetLastError());
return;
}
schService = CreateService( // Create the service
schSCManager, // SCM database
SVCNAME, // name of service
SVCNAME, // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS, // service type
SERVICE_DEMAND_START, // start type
SERVICE_ERROR_NORMAL, // error control type
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL) {
printf("CreateService failed (%d)\n", GetLastError());
CloseServiceHandle(schSCManager);
return;
} else {
printf("Service installed successfully\n");
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
VOID ReportSvcStatus( DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
/*
Purpose: Sets the current service status and reports it to the SCM.
Parameters:
dwCurrentState - The current state (see SERVICE_STATUS)
dwWin32ExitCode - The system error code
dwWaitHint - Estimated time for pending operation, in milliseconds
*/
{
static DWORD dwCheckPoint = 1;
gSvcStatus.dwCurrentState = dwCurrentState;// Fill in the SERVICE_STATUS structure.
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING) {
gSvcStatus.dwControlsAccepted = 0;
} else {
gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
}
if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) ) {
gSvcStatus.dwCheckPoint = 0;
} else {
gSvcStatus.dwCheckPoint = dwCheckPoint++;
}
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );// Report the status of the service to the SCM.
}
VOID SvcReportEvent(LPTSTR szFunction)
// Purpose: Logs messages to the event log
// Parameters: szFunction - name of function that failed
// Remarks: The service must have an entry in the Application event log.
{
HANDLE hEventSource;
LPCTSTR lpszStrings[2];
TCHAR Buffer[80];
hEventSource = RegisterEventSource(NULL, SVCNAME);
if( NULL != hEventSource )
{
StringCchPrintf(Buffer, 80, TEXT("%s failed with %d"), szFunction, GetLastError());
lpszStrings[0] = SVCNAME;
lpszStrings[1] = Buffer;
ReportEvent(hEventSource,// event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
(DWORD)0xC0020100L, //SVC_ERROR, // event identifier
NULL, // no security identifier
2, // size of lpszStrings array
0, // no binary data
lpszStrings, // array of strings
NULL); // no binary data
DeregisterEventSource(hEventSource);
}
}
VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
// Purpose: Called by SCM whenever a control code is sent to the service using the ControlService function.
// Parameters: dwCtrl - control code
{
switch(dwCtrl) // Handle the requested control code.
{
case SERVICE_CONTROL_STOP: //net stop svcname等类似的操作会走到这里。
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
SetEvent(ghSvcStopEvent);// Signal the service to stop.
return;
case SERVICE_CONTROL_INTERROGATE:// Fall through to send current status.
break;
default:
break;
}
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
}
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv ) //net start svcname会走到这里。
/*
Purpose: Entry point for the service
Parameters:
dwArgc - Number of arguments in the lpszArgv array
lpszArgv - Array of strings.
The first string is the name of the service and subsequent strings are passed by the process that called the StartService function to start the service.
*/
{
gSvcStatusHandle = RegisterServiceCtrlHandler( SVCNAME, SvcCtrlHandler);
if( !gSvcStatusHandle ) { // Register the handler function for the service:注册处理服务消息的回调函数.
SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));
return;
}
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // These SERVICE_STATUS members remain as set here
gSvcStatus.dwServiceSpecificExitCode = 0;
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );// Report initial status to the SCM
// Perform service-specific initialization and work.
// TO_DO: Declare and set any required variables.
// Be sure to periodically call ReportSvcStatus() with SERVICE_START_PENDING. If initialization fails, call ReportSvcStatus with SERVICE_STOPPED.
// Create an event. The control handler function, SvcCtrlHandler,signals this event when it receives the stop control code.
ghSvcStopEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
NULL); // no name
if ( ghSvcStopEvent == NULL) {
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );// Report running status when initialization is complete.
// TO_DO: Perform work until service stops.
//添加代码,大部分是开线程。
while(1)//等待net stop svcname操作等。
{
WaitForSingleObject(ghSvcStopEvent, INFINITE);// Check whether to stop the service.
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
}
void __cdecl _tmain(int argc, TCHAR *argv[]) // Purpose: Entry point for the process
{
if( lstrcmpi( argv[1], TEXT("install")) == 0 )
{// If command-line parameter is "install", install the service. 如果命令行参数是:install就安装服务。
SvcInstall();
return;
} else if ( lstrcmpi( argv[1], TEXT("start")) == 0 ) {
//DoStartSvc();
return;
} //等等,可以添加很多操作。
// Otherwise, the service is probably being started by the SCM. 其他的就运行下面,下面的代码是作为服务运行的。
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain }, // TO_DO: Add any additional services for the process to this table.
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcher( DispatchTable )) //启动服务,即服务入口.
{// This call returns when the service has stopped. The process should simply terminate when the call returns.
SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
一些说明:
其实服务很简单:
1.程序的入口调用StartServiceCtrlDispatcher.
2.服务的入口调用RegisterServiceCtrlHandler.
3.服务的线程(即服务入口函数)要有循环,等待等函数,不然服务结束了.
4.服务的入口函数一般开启有线程.一个服务主线程很简单.
5.服务一般没有界面,避免处理消息.
关于服务的保护的一点内容:
1.保护服务不被停止.
需要在服务的处理服务消息的回调函数中屏蔽掉这些消息(SERVICE_CONTROL_STOP),
而且还要在服务的入口中设置SERVICE_STATUS类型的变量的dwControlsAccepted值中一定不要包含SERVICE_ACCEPT_STOP.
这样做了,会导致在服务控制面板里面的此服务的停止按钮灰化,和net/sc stop命令失败.
2.保护服务不被卸载.
一个办法是用驱动保护注册表,建议用注册表回调.说明:隐藏会有一些意外的效果,如查看,还有就是不能手工启动(计算机启动的时候可以启动的).
3.关于服务弹消息框的设置要点:
1).vista以前只要设置服务为可交互式的(可手工修改,包括界面和注册表,也可以编程的时候修改代码),加MessageBox即可.
2).vista及以后到win 8之前,这需要开启interactive service detection服务.
命令行的操作是:net start ui0detect.
这时弹出的消息不在本桌面,要切换桌面方能看到.
3).win 8及以后.
需要先修改HKLM\SYSTEM\CurrentControlSet\Control\Windows\NoInteractiveServices的值为0,原先默认的是1.
再开启交互式的服务,不然会出错误的.
4.关于调试服务(包括服务的启动),请在搜索本站点.
made by correy
made at 2013.12.26
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2013年5月1日星期三
wdf.c
/*
最简单的wdf内核驱动,参考一下资料:
http://msdn.microsoft.com/zh-cn/library/hh439665.aspx
http://msdn.microsoft.com/en-us/library/hh439665.aspx
没有用vc2012编译成功,用wdk7600.16385.1编译成功,但是没有加载和调试。
不敢说原创,以此记载。
*/
#include <ntddk.h>
#include <wdf.h>
#define _In_
#define _Inout_
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD KmdfSmallEvtDeviceAdd;
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfSmall: DriverEntry\n" ));
WDF_DRIVER_CONFIG_INIT(&config, KmdfSmallEvtDeviceAdd);
status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
return status;
}
NTSTATUS KmdfSmallEvtDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS status;
WDFDEVICE hDevice;
UNREFERENCED_PARAMETER(Driver);
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfSmall: KmdfSmallEvtDeviceAdd\n" ));
status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &hDevice);
return status;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
source文件如下:
TARGETNAME=c
TARGETTYPE=DRIVER
#error C1083: Cannot open include file: 'wdf.h': No such file or directory的解决办法:
#KMDF_VERSION = 1
KMDF_VERSION_MAJOR=1
#另一种是:没有试验。
#$(WLHBASE)\lib\wdf\kmdf\i386\1.9;
#$(WLHBASE)\inc\wdf\kmdf\1.9
LINKER_FLAGS = $(LINKER_FLAGS)/INTEGRITYCHECK
SOURCES=c.c
TARGETPATH=obj
最简单的wdf内核驱动,参考一下资料:
http://msdn.microsoft.com/zh-cn/library/hh439665.aspx
http://msdn.microsoft.com/en-us/library/hh439665.aspx
没有用vc2012编译成功,用wdk7600.16385.1编译成功,但是没有加载和调试。
不敢说原创,以此记载。
*/
#include <ntddk.h>
#include <wdf.h>
#define _In_
#define _Inout_
DRIVER_INITIALIZE DriverEntry;
EVT_WDF_DRIVER_DEVICE_ADD KmdfSmallEvtDeviceAdd;
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfSmall: DriverEntry\n" ));
WDF_DRIVER_CONFIG_INIT(&config, KmdfSmallEvtDeviceAdd);
status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
return status;
}
NTSTATUS KmdfSmallEvtDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS status;
WDFDEVICE hDevice;
UNREFERENCED_PARAMETER(Driver);
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "KmdfSmall: KmdfSmallEvtDeviceAdd\n" ));
status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &hDevice);
return status;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
source文件如下:
TARGETNAME=c
TARGETTYPE=DRIVER
#error C1083: Cannot open include file: 'wdf.h': No such file or directory的解决办法:
#KMDF_VERSION = 1
KMDF_VERSION_MAJOR=1
#另一种是:没有试验。
#$(WLHBASE)\lib\wdf\kmdf\i386\1.9;
#$(WLHBASE)\inc\wdf\kmdf\1.9
LINKER_FLAGS = $(LINKER_FLAGS)/INTEGRITYCHECK
SOURCES=c.c
TARGETPATH=obj
ExRegisterCallback.C
/*
文本就命名为:ExRegisterCallback.C吧!
made by correy
made at 2013.05.01
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
本文参考了msdn/wdk.
http://www.osronline.com/article.cfm?id=24
http://blog.csdn.net/svtanto/article/details/6255808
http://hi.baidu.com/sysnap/item/7ac898db93094e3be3108fff
http://hi.baidu.com/antbean1988/item/6c14a89205487ceb2816475f
具体的用途有不多说了,没有深究。
觉得很有用,以后再研究添加功能。
*/
#include <ntddk.h>
#define _In_
#define _Inout_
#define _Inout_opt_
PVOID CbRegistration;
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
if (CbRegistration) {
ExUnregisterCallback(CbRegistration);//运行之后,对象还存在。可以用工具查看。
}
}
//PCALLBACK_FUNCTION pcallback_function;
VOID pcallback_function (IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2)
{
DbgPrint("停下来看看吧!\n");
KdBreakPoint();
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
BOOLEAN b = 0;
UNICODE_STRING CallbackName;
OBJECT_ATTRIBUTES InitializedAttributes;
PCALLBACK_OBJECT PCallbackObject;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
RtlInitUnicodeString(&CallbackName, L"\\Callback\\correy");
InitializeObjectAttributes(&InitializedAttributes, &CallbackName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL);
status = ExCreateCallback(&PCallbackObject, &InitializedAttributes, TRUE, 0);//TRUE
if(!NT_SUCCESS(status) ) {
DbgPrint("ExCreateCallback failed 0x%0x\n", status);
return status;
}
//CbRegistration = (PCallbackObject, pcallback_function, NULL);//错误的书写格式,总是返回值为0。
CbRegistration = ExRegisterCallback(PCallbackObject, pcallback_function, NULL);
if(CbRegistration == 0) {//如果已经注册成功,再此注册之前不成功运行ExUnregisterCallback,会返回值是0.
DbgPrint("CbRegistration failed\n");
return STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(PCallbackObject);
ExNotifyCallback(PCallbackObject, NULL, NULL);//这个调用是测试。第一个参数加个&会导致:BugCheck A, {2c, 2, 0
return status;//STATUS_SUCCESS
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <ntifs.h>
/*
Using a System-Defined Callback Object
http://msdn.microsoft.com/en-us/library/windows/hardware/ff564933(v=vs.85).aspx
有如下内容:
The system defines three callback objects for driver use:
\Callback\SetSystemTime
\Callback\PowerState
\Callback\ProcessorAdd
Drivers that use the system time (for example, file system drivers) might register for the \Callback\SetSystemTime callback object.
This callback provides for notification when the system time changes.
......
To use a system-defined callback, a driver initializes an attribute block (InitializeObjectAttributes) with the callback's name,
then opens the callback object (ExCreateCallback), just as for a driver-defined callback.
The driver should not request that the callback object be created.
With the handle returned by ExCreateCallback, the driver calls ExRegisterCallback to register a notification routine,
passing a pointer to an arbitrary context and a pointer to its routine.
A driver can register its callback routine any time.
When the specified condition occurs, the system calls the registered callback routine at IRQL<=DISPATCH_LEVEL.
本文就是按照链接的说明以\Callback\SetSystemTime演示的。
made by correy
made at 2014.05.07
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/
PVOID CbRegistration;
PCALLBACK_OBJECT PCallbackObject;
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
//When the driver no longer requires notification,
//it should call ExUnregisterCallback to delete its callback routine from the list of registered callbacks and to remove its reference to the callback object.
if (CbRegistration) {
ExUnregisterCallback(CbRegistration);
}
ObDereferenceObject(PCallbackObject);
}
VOID pcallback_function (IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2)
/*
注意:IRQL<=DISPATCH_LEVEL,解决办法是加工作线程和同步对象。
如果是:\Callback\SetSystemTime
Argument1 Not used.
Argument2 Not used.
不过经观察:CallbackContext的都为0.
*/
{
DbgPrint("时间被修改了。\n");
KdBreakPoint();
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNICODE_STRING CallbackName;
OBJECT_ATTRIBUTES InitializedAttributes;
KdBreakPoint();
DriverObject->DriverUnload = Unload;
RtlInitUnicodeString(&CallbackName, L"\\Callback\\SetSystemTime");
InitializeObjectAttributes(&InitializedAttributes, &CallbackName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL);
status = ExCreateCallback(&PCallbackObject, &InitializedAttributes, TRUE, 0);//TRUE
if(!NT_SUCCESS(status) ) {
DbgPrint("ExCreateCallback failed 0x%0x\n", status);
return status;
}
CbRegistration = ExRegisterCallback(PCallbackObject, pcallback_function, NULL);
if(CbRegistration == 0) {
DbgPrint("CbRegistration failed\n");
ObDereferenceObject(PCallbackObject);
return STATUS_UNSUCCESSFUL;
}
return status;//STATUS_SUCCESS
}
文本就命名为:ExRegisterCallback.C吧!
made by correy
made at 2013.05.01
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
本文参考了msdn/wdk.
http://www.osronline.com/article.cfm?id=24
http://blog.csdn.net/svtanto/article/details/6255808
http://hi.baidu.com/sysnap/item/7ac898db93094e3be3108fff
http://hi.baidu.com/antbean1988/item/6c14a89205487ceb2816475f
具体的用途有不多说了,没有深究。
觉得很有用,以后再研究添加功能。
*/
#include <ntddk.h>
#define _In_
#define _Inout_
#define _Inout_opt_
PVOID CbRegistration;
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
if (CbRegistration) {
ExUnregisterCallback(CbRegistration);//运行之后,对象还存在。可以用工具查看。
}
}
//PCALLBACK_FUNCTION pcallback_function;
VOID pcallback_function (IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2)
{
DbgPrint("停下来看看吧!\n");
KdBreakPoint();
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
BOOLEAN b = 0;
UNICODE_STRING CallbackName;
OBJECT_ATTRIBUTES InitializedAttributes;
PCALLBACK_OBJECT PCallbackObject;
KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
DriverObject->DriverUnload = Unload;
RtlInitUnicodeString(&CallbackName, L"\\Callback\\correy");
InitializeObjectAttributes(&InitializedAttributes, &CallbackName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL);
status = ExCreateCallback(&PCallbackObject, &InitializedAttributes, TRUE, 0);//TRUE
if(!NT_SUCCESS(status) ) {
DbgPrint("ExCreateCallback failed 0x%0x\n", status);
return status;
}
//CbRegistration = (PCallbackObject, pcallback_function, NULL);//错误的书写格式,总是返回值为0。
CbRegistration = ExRegisterCallback(PCallbackObject, pcallback_function, NULL);
if(CbRegistration == 0) {//如果已经注册成功,再此注册之前不成功运行ExUnregisterCallback,会返回值是0.
DbgPrint("CbRegistration failed\n");
return STATUS_UNSUCCESSFUL;
}
ObDereferenceObject(PCallbackObject);
ExNotifyCallback(PCallbackObject, NULL, NULL);//这个调用是测试。第一个参数加个&会导致:BugCheck A, {2c, 2, 0
return status;//STATUS_SUCCESS
}
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <ntifs.h>
/*
Using a System-Defined Callback Object
http://msdn.microsoft.com/en-us/library/windows/hardware/ff564933(v=vs.85).aspx
有如下内容:
The system defines three callback objects for driver use:
\Callback\SetSystemTime
\Callback\PowerState
\Callback\ProcessorAdd
Drivers that use the system time (for example, file system drivers) might register for the \Callback\SetSystemTime callback object.
This callback provides for notification when the system time changes.
......
To use a system-defined callback, a driver initializes an attribute block (InitializeObjectAttributes) with the callback's name,
then opens the callback object (ExCreateCallback), just as for a driver-defined callback.
The driver should not request that the callback object be created.
With the handle returned by ExCreateCallback, the driver calls ExRegisterCallback to register a notification routine,
passing a pointer to an arbitrary context and a pointer to its routine.
A driver can register its callback routine any time.
When the specified condition occurs, the system calls the registered callback routine at IRQL<=DISPATCH_LEVEL.
本文就是按照链接的说明以\Callback\SetSystemTime演示的。
made by correy
made at 2014.05.07
email:kouleguan at hotmail dot com
homepage:http://correy.webs.com
*/
PVOID CbRegistration;
PCALLBACK_OBJECT PCallbackObject;
DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{
//When the driver no longer requires notification,
//it should call ExUnregisterCallback to delete its callback routine from the list of registered callbacks and to remove its reference to the callback object.
if (CbRegistration) {
ExUnregisterCallback(CbRegistration);
}
ObDereferenceObject(PCallbackObject);
}
VOID pcallback_function (IN PVOID CallbackContext, IN PVOID Argument1, IN PVOID Argument2)
/*
注意:IRQL<=DISPATCH_LEVEL,解决办法是加工作线程和同步对象。
如果是:\Callback\SetSystemTime
Argument1 Not used.
Argument2 Not used.
不过经观察:CallbackContext的都为0.
*/
{
DbgPrint("时间被修改了。\n");
KdBreakPoint();
}
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry( __in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
UNICODE_STRING CallbackName;
OBJECT_ATTRIBUTES InitializedAttributes;
KdBreakPoint();
DriverObject->DriverUnload = Unload;
RtlInitUnicodeString(&CallbackName, L"\\Callback\\SetSystemTime");
InitializeObjectAttributes(&InitializedAttributes, &CallbackName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL);
status = ExCreateCallback(&PCallbackObject, &InitializedAttributes, TRUE, 0);//TRUE
if(!NT_SUCCESS(status) ) {
DbgPrint("ExCreateCallback failed 0x%0x\n", status);
return status;
}
CbRegistration = ExRegisterCallback(PCallbackObject, pcallback_function, NULL);
if(CbRegistration == 0) {
DbgPrint("CbRegistration failed\n");
ObDereferenceObject(PCallbackObject);
return STATUS_UNSUCCESSFUL;
}
return status;//STATUS_SUCCESS
}
订阅:
博文 (Atom)