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
}

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
}

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;
}

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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

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
}