A C++ class wrapper to load/unload device drivers

Àâòîð: Perrone Antonio

Ãîä: 2008

Èñòî÷íèê: codeproject.com

Introduction

A loader/unloader tool is very useful if you frequently play with device derivers. There are a lot of tools that are good, the most popular I think is OSRLoader. But, I decided to write this wrapper in order to explain which Win32 APIs are involved in this process.

The Win32 APIs

A device driver is different from a normal user level program, so its installation and execution are more complex, because we are in the kernel space. We have to do two main operations:

  1. Register the driver as a Windows service.
  2. Start the driver as a service.

The reason for this is that Windows sees the device drivers as normal services. Now, we can explore the APIs that allow us to work with a process. The Service Control Manager (SCM) is the component that manages a database with all the services and device drivers installed. Let us see the API that establishes a connection to the SCM on our box.

SC_HANDLE WINAPI OpenSCManager(
    __in_opt LPCTSTR lpMachineName,
    __in_opt LPCTSTR lpDatabaseName,
    __in DWORD dwDesiredAccess
);

After this, we can create a new service like this:

SC_HANDLE WINAPI CreateService(
    __in SC_HANDLE hSCManager,
    __in LPCTSTR lpServiceName,
    __in_opt LPCTSTR lpDisplayName,
    __in DWORD dwDesiredAccess,
    __in DWORD dwServiceType,
    __in DWORD dwStartType,
    __in DWORD dwErrorControl,
    __in_opt LPCTSTR lpBinaryPathName,
    __in_opt LPCTSTR lpLoadOrderGroup,
    __out_opt LPDWORD lpdwTagId,
    __in_opt LPCTSTR lpDependencies,
    __in_opt LPCTSTR lpServiceStartName,
    __in_opt LPCTSTR lpPassword
);

The explanation for each parameter is available on the MSDN. Now, I will show you how to use this API to create a new service:

DWORD CService::CreateSvc(void)
{
    if (!IsInit())
        return SVC_NOT_INIT;

    if (IsLoaded())
        return SVC_OK;

    SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);

    if (hSCManager == NULL)
        return SVC_ERROR_SCMANAGER;

    hService = CreateService(hSCManager,lpServiceName,lpDisplayName,
        SERVICE_ALL_ACCESS,
        SERVICE_KERNEL_DRIVER,
        dwStartType,
        SERVICE_ERROR_NORMAL,
        lpFilePath,
        NULL,
        NULL,
        NULL,
        NULL,
        NULL);

    if (hService == NULL){

    hService = OpenService(hSCManager, lpServiceName, SERVICE_ALL_ACCESS);

        if (hService == NULL) {
            CloseServiceHandle(hSCManager);
            return SVC_ERROR_CREATE;
        }
    }

    loaded = true;
    CloseServiceHandle(hSCManager);

    return SVC_OK;
}

The OpenSCManager(…)returns the handle of the SCM, so we use it in CreateService(…) for installing the device driver. hService is the handle of the service installed. Now, we can start it. The API:

BOOL WINAPI StartService(
    __in SC_HANDLE hService,
    __in DWORD dwNumServiceArgs,
    __in_opt LPCTSTR *lpServiceArgVectors
);

Here is the code:

DWORD CService::StartSvc(void)
{
    if (!IsLoaded())
        return SVC_NOT_CREATE;

    if (IsStarted())
        return SVC_OK;

    SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);

    if (hSCManager == NULL)
        return SVC_ERROR_SCMANAGER;

    hService = OpenService(hSCManager, lpServiceName, SERVICE_ALL_ACCESS);

    if (hService == NULL) {
        CloseServiceHandle(hSCManager);
        return SVC_ERROR_OPEN;
    }

    if (StartService(hService,0,NULL)== NULL){
        CloseServiceHandle(hSCManager);
        CloseServiceHandle(hService);
        return SVC_ERROR_START;
    }

    CloseServiceHandle(hSCManager);
    started = true;

    return SVC_OK;
}

The StartService function take three parameters: the handle of the service, the number of arguments, and an array with the arguments.

OK, after working, it’s time to shutdown the driver. To stop the driver, we use this API:

BOOL WINAPI ControlService(
    __in SC_HANDLE hService,
    __in DWORD dwControl,
    __out LPSERVICE_STATUS lpServiceStatus
);

This sends the control message to the service, in our case dwControl = SERVICE_CONTROL_STOP. The out SERVICE_STATUS struct contains the status information about the service.

DWORD CService::StopSvc(void)
{
    SERVICE_STATUS ss;

    if (!IsStarted())
        return SVC_OK;

    SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);

    if (hSCManager == NULL)
        return SVC_ERROR_SCMANAGER;

    hService = OpenService(hSCManager, lpServiceName, SERVICE_ALL_ACCESS);

    if (hService == NULL) {
        CloseServiceHandle(hSCManager);
        return SVC_ERROR_OPEN;
    }

    if (ControlService(hService,SERVICE_CONTROL_STOP,&ss) == NULL){
        CloseServiceHandle(hSCManager);
        CloseServiceHandle(hService);
        return SVC_ERROR_STOP;
    }

    started = false;

    CloseServiceHandle(hSCManager);
    CloseServiceHandle(hService);
    return SVC_OK;
}

Finally, we have to unload the driver.

BOOL WINAPI DeleteService(
    __in SC_HANDLE hService
);

This is the API to delete the driver from the SCM, and this is how we use it:

DWORD CService::UnloadSvc(void)
{
    if (!IsLoaded())
        return SVC_OK;

    if (IsStarted())
        if (StopSvc() != SVC_OK)
            return SVC_ERROR_UNLOAD;

    SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);

    if (hSCManager == NULL)
        return SVC_ERROR_SCMANAGER;

    hService = OpenService(hSCManager, lpServiceName, SERVICE_ALL_ACCESS);

    if (hService == NULL) {
        CloseServiceHandle(hSCManager);
        return SVC_ERROR_OPEN;
    }

    DeleteService(hService);
    CloseServiceHandle(hSCManager);

    loaded = false;

    return SVC_OK;
}

Using the code

Well, if you are lazy, or you can’t waste your time to understand how all these APIs work, I have written this simple wrapper that I hope you will find useful:

#include
#include "stdafx.h"

//Error codes

#define SVC_OK (DWORD)0
#define SVC_NOT_CREATE (DWORD)1
#define SVC_NOT_START (DWORD)2
#define SVC_NOT_INIT (DWORD)3
#define SVC_ERROR_SCMANAGER (DWORD)4
#define SVC_ERROR_CREATE (DWORD)5
#define SVC_ERROR_START (DWORD)6
#define SVC_ERROR_OPEN (DWORD) 7
#define SVC_ERROR_STOP (DWORD)8
#define SVC_ERROR_UNLOAD (DWORD) 9


class CService
{
public:
    //Constructor
    CService(void);
    CService(LPTSTR _lpFilePath,LPTSTR _lpServiceName,
        LPTSTR _lpDisplayName,DWORD _dwStartType);
    //Destructor
    ~CService(void);

    //init function
    DWORD InitSvc(LPTSTR _lpFilePath,LPTSTR _lpServiceName,
        LPTSTR _lpDisplayName,DWORD _dwStartType);
    DWORD CreateSvc(void); //create service
    DWORD UnloadSvc(void); //unload service
    DWORD StartSvc(void); //start service
    DWORD StopSvc(void); //stop service


    BOOL IsInit(void); //check if service is init
    BOOL IsLoaded(void); //check if service is loaded
    BOOL IsStarted(void); //check if service is started

private:

    LPTSTR lpFilePath; //driver file path
    LPTSTR lpServiceName; //service name
    LPTSTR lpDisplayName; //service's dos name

    DWORD dwStartType; //start type

    SC_HANDLE hService; //service's handle

    // Status variables
    BOOL init;
    BOOL loaded;
    BOOL started;
};

The CService object creation:

CService svc(“C:\\test.sys”,”Test”,”Test”,SERVICE_DEMAND_START);

Driver registration:

if (svc.CreateSvc() == SVC_OK)
    …
else{
    …
}

Driver startup:

if (svc.StartSvc() == SVC_OK)
    …
else{
    …
}

Driver stop:

if (svc.StopSvc() == SVC_OK)
    …
else{
    …
}

Driver erase:

if (svc.UnloadSvc() == SVC_OK)
    …
else{
    …
}

Conclusion

OK, that’s all. I also wrote a little app that use the CService class. I hope you will find these few lines of code useful.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)