VSCode+CMake开发NT式驱动程序



  • VSCode是微软出品的一款比较轻量的文本编辑工具,和Github出品的Atom类似,而且都提供了很强大的插件功能。
    CMake是一款跨平台的编译工具,它能够生成各种项目文件或makefile文件。
    决定使用VSCode+CMake编译驱动的主要原因是感觉Visual Studio太过于臃肿了吧,自从写驱动程序以来都经历过了使用文本编辑器或者VC++6.0开发,微软的产品功能越来越全面的同时也会让产品变得太臃肿了。开发驱动程序脚本编辑器足矣。


    下面需要准备的工具(自行安装):

    下载WDK(最后别安装Visual Studio的WDK插件,否则会引起MSB8040异常)

    https://docs.microsoft.com/zh-cn/windows-hardware/drivers/download-the-wdk

    下载VSCode

    https://code.visualstudio.com/

    下载CMake

    https://cmake.org/download/

    下载FindWDK项目(用来搜索WDK相关路径)

    https://github.com/SergiusTheBest/FindWDK.git


    建立工程目录:

    NetFilter(主目录)
    |--cmake
        |--FindWdk.cmake     # 在FindWDK中复制过来
    |--network               # FWPM网络过滤模块
        |--main.c
        |--CMakeLists.txt
    |--CMakeLists.txt        # CMake配置文件
    |--build.bat             # 自动编译脚本
    

    相关代码

    network->main.c

    #pragma once
    #pragma warning(push)
    #pragma warning(disable : 4201)
    #pragma warning(disable : 4324)
    #define NDIS_SUPPORT_NDIS6 1
    #include <ntifs.h>
    #include <ntddk.h>
    #include <ndis.h>
    #include <fwpsk.h>
    #include <fwpmk.h>
    #include <wdm.h>
    #pragma warning(pop)
    #define INITGUID
    #include <guiddef.h>
    
    #define DEVICE_NAME L"\\Device\\GhostNet"
    #define DEVICE_DOSNAME L"\\DosDevices\\GhostNet"
    #define kmalloc(_s) ExAllocatePoolWithTag(NonPagedPool, _s, 'GSYS')
    #define kfree(_p) ExFreePool(_p)
    
    DEFINE_GUID
        (
            GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
            0x6812fc83,
            0x7d3e,
            0x499a,
            0xb8, 0x59,
            0x55, 0xe0, 0xd8, 0x5f, 0x34, 0x8b);
    
    
    
    PDEVICE_OBJECT gDevObj;
    HANDLE gEngineHandle = 0;
    HANDLE gInjectHandle = 0;
    
    UINT32 gAleConnectCalloutId = 0;
    UINT64 gAleConnectFilterId = 0;
    
    NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING regPath);
    VOID DriverUnload(PDRIVER_OBJECT driver);
    
    //协议代码转为名称
    char *ProtocolIdToName(UINT16 id)
    {
        char *ProtocolName = (char *)kmalloc(16);
        switch (id)
        {
        case 1:
            strcpy_s(ProtocolName, 4 + 1, "ICMP");
            break;
        case 2:
            strcpy_s(ProtocolName, 4 + 1, "IGMP");
            break;
        case 6:
            strcpy_s(ProtocolName, 3 + 1, "TCP");
            break;
        case 17:
            strcpy_s(ProtocolName, 3 + 1, "UDP");
            break;
        case 27:
            strcpy_s(ProtocolName, 3 + 1, "RDP");
            break;
        default:
            strcpy_s(ProtocolName, 7 + 1, "UNKNOWN");
            break;
        }
        return ProtocolName;
    }
    
    NTSTATUS NTAPI WallNotifyFn(
        IN FWPS_CALLOUT_NOTIFY_TYPE notifyType,
        IN const GUID *filterKey,
        IN const FWPS_FILTER *filter)
    {
        return STATUS_SUCCESS;
    }
    
    VOID NTAPI WallFlowDeleteFn(
        IN UINT16 layerId,
        IN UINT32 calloutId,
        IN UINT64 flowContext)
    {
        return;
    }
    
    
    
    // 连接过滤
    void NTAPI ALEConnectClassify(
        IN const FWPS_INCOMING_VALUES0 *inFixedValues,
        IN const FWPS_INCOMING_METADATA_VALUES0 *inMetaValues,
        IN OUT void *layerData,
        IN const void *classifyContext,
        IN const FWPS_FILTER *filter,
        IN UINT64 flowContext,
        OUT FWPS_CLASSIFY_OUT *classifyOut)
    {
        char *ProtocolName = NULL;
        DWORD LocalIp, RemoteIP;
        LocalIp = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_ADDRESS].value.uint32;
        RemoteIP = inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_ADDRESS].value.uint32;
        ProtocolName = ProtocolIdToName(inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_PROTOCOL].value.uint16);
        DbgPrint("[WFP]IRQL=%d;PID=%ld;Path=%S;Local=%u.%u.%u.%u:%d;Remote=%u.%u.%u.%u:%d;Protocol=%s\n",
                 (USHORT)KeGetCurrentIrql(),
                 (DWORD)(inMetaValues->processId),
                 (PWCHAR)inMetaValues->processPath->data, //NULL,//
                 (LocalIp >> 24) & 0xFF, (LocalIp >> 16) & 0xFF, (LocalIp >> 8) & 0xFF, LocalIp & 0xFF,
                 inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_LOCAL_PORT].value.uint16,
                 (RemoteIP >> 24) & 0xFF, (RemoteIP >> 16) & 0xFF, (RemoteIP >> 8) & 0xFF, RemoteIP & 0xFF,
                 inFixedValues->incomingValue[FWPS_FIELD_ALE_AUTH_CONNECT_V4_IP_REMOTE_PORT].value.uint16,
                 ProtocolName);
        
        kfree(ProtocolName);
        classifyOut->actionType = FWP_ACTION_PERMIT; //允许连接
    
        //禁止IE联网(设置“行动类型”为FWP_ACTION_BLOCK)
        // if(wcsstr((PWCHAR)inMetaValues->processPath->data,L"iexplore.exe"))
        // {
        // classifyOut->actionType = FWP_ACTION_BLOCK;
        // classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE;
        // classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB;
        // }
        return;
    }
    
    
    /*
    * 注册过滤层回调函数
    */
    NTSTATUS RegisterCalloutForLayer(
        IN const GUID *layerKey,
        IN const GUID *calloutKey,
        IN FWPS_CALLOUT_CLASSIFY_FN classifyFn,
        IN FWPS_CALLOUT_NOTIFY_FN notifyFn,
        IN FWPS_CALLOUT_FLOW_DELETE_NOTIFY_FN flowDeleteNotifyFn,
        OUT UINT32 *calloutId,
        OUT UINT64 *filterId)
    {
        NTSTATUS status = STATUS_SUCCESS;
        FWPS_CALLOUT sCallout = {0};
        FWPM_FILTER mFilter = {0};
        FWPM_FILTER_CONDITION mFilter_condition[1] = {0};
        FWPM_CALLOUT mCallout = {0};
        FWPM_DISPLAY_DATA mDispData = {0};
        BOOLEAN bCalloutRegistered = FALSE;
        sCallout.calloutKey = *calloutKey;
        sCallout.classifyFn = classifyFn;
        sCallout.flowDeleteFn = flowDeleteNotifyFn;
        sCallout.notifyFn = notifyFn;
        status = FwpsCalloutRegister(gDevObj, &sCallout, calloutId);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("FwpsCalloutRegister failed.");
            goto exit;
        }
        bCalloutRegistered = TRUE;
        mDispData.name = L"WFP TEST";
        mDispData.description = L"TESLA.ANGELA's WFP TEST";
        mCallout.applicableLayer = *layerKey;
        mCallout.calloutKey = *calloutKey;
        mCallout.displayData = mDispData;
        status = FwpmCalloutAdd(gEngineHandle, &mCallout, NULL, NULL);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("FwpmCalloutAdd falied");
            goto exit;
        }
        mFilter.action.calloutKey = *calloutKey;
        mFilter.action.type = FWP_ACTION_CALLOUT_TERMINATING;
        mFilter.displayData.name = L"WFP TEST";
        mFilter.displayData.description = L"TESLA.ANGELA's WFP TEST";
        mFilter.layerKey = *layerKey;
        mFilter.numFilterConditions = 0;
        mFilter.filterCondition = mFilter_condition;
        mFilter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL;
        mFilter.weight.type = FWP_EMPTY;
        status = FwpmFilterAdd(gEngineHandle, &mFilter, NULL, filterId);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("FwpmFilterAdd failed");
            goto exit;
        }
    exit:
        if (!NT_SUCCESS(status))
        {
            if (bCalloutRegistered)
            {
                FwpsCalloutUnregisterById(*calloutId);
            }
        }
        return status;
    }
    
    NTSTATUS WallRegisterCallouts()
    {
        NTSTATUS status = STATUS_SUCCESS;
        BOOLEAN bInTransaction = FALSE;
        BOOLEAN bEngineOpened = FALSE;
        FWPM_SESSION session = {0};
        session.flags = FWPM_SESSION_FLAG_DYNAMIC;
        status = FwpmEngineOpen(NULL,
                                RPC_C_AUTHN_WINNT,
                                NULL,
                                &session,
                                &gEngineHandle);
        if (!NT_SUCCESS(status))
            goto exit;
        bEngineOpened = TRUE;
        status = FwpmTransactionBegin(gEngineHandle, 0);
        if (!NT_SUCCESS(status))
            goto exit;
        bInTransaction = TRUE;
        // 注册Connect
        status = RegisterCalloutForLayer(
            &FWPM_LAYER_ALE_AUTH_CONNECT_V4,
            &GUID_ALE_AUTH_CONNECT_CALLOUT_V4,
            ALEConnectClassify,
            WallNotifyFn,
            WallFlowDeleteFn,
            &gAleConnectCalloutId,
            &gAleConnectFilterId);
        if (!NT_SUCCESS(status))
        {
            DbgPrint("RegisterCalloutForLayer-FWPM_LAYER_ALE_AUTH_CONNECT_V4 failed!\n");
            goto exit;
        }
    
        status = FwpmTransactionCommit(gEngineHandle);
        if (!NT_SUCCESS(status))
            goto exit;
        bInTransaction = FALSE;
    exit:
        if (!NT_SUCCESS(status))
        {
            if (bInTransaction)
            {
                FwpmTransactionAbort(gEngineHandle);
            }
            if (bEngineOpened)
            {
                FwpmEngineClose(gEngineHandle);
                gEngineHandle = 0;
            }
        }
        return status;
    }
    
    NTSTATUS WallUnRegisterCallouts()
    {
        if (gEngineHandle != 0)
        {
            // 删除FilterId
            FwpmFilterDeleteById(gEngineHandle, gAleConnectFilterId);
            // 删除CalloutId
            FwpmCalloutDeleteById(gEngineHandle, gAleConnectCalloutId);
            // 清空FilterId
            gAleConnectFilterId = 0;
            // 反注册CalloutId
            FwpsCalloutUnregisterById(gAleConnectCalloutId);
            // 清空CalloutId
            gAleConnectCalloutId = 0;
            // 关闭引擎
            FwpmEngineClose(gEngineHandle);
            gEngineHandle = 0;
        }
        return STATUS_SUCCESS;
    }
    
    // #pragma alloc_text(PAGE, DriverUnload)
    VOID DriverUnload(PDRIVER_OBJECT driver)
    {
        UNICODE_STRING DeviceDosName;
        WallUnRegisterCallouts();
        RtlInitUnicodeString(&DeviceDosName,DEVICE_DOSNAME);
        IoDeleteSymbolicLink(&DeviceDosName);
        IoDeleteDevice(driver->DeviceObject);
    }
    
    NTSTATUS DefIRPHandler(IN PDEVICE_OBJECT devobj, IN PIRP Irp){
        Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
    }
    
    NTSTATUS CreateIRPHandler(IN PDEVICE_OBJECT devobj, IN PIRP Irp){
        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
    }
    
    NTSTATUS CloseIRPHandler(IN PDEVICE_OBJECT devobj, IN PIRP Irp){
        Irp->IoStatus.Status = STATUS_SUCCESS;
        Irp->IoStatus.Information = 0;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return STATUS_SUCCESS;
    }
    
    // #pragma alloc_text(INIT, DriverEntry)
    NTSTATUS DriverEntry(PDRIVER_OBJECT driver, PUNICODE_STRING regPath)
    {
        // PAGED_CODE();
        NTSTATUS status;
        UNICODE_STRING DeviceName,DeviceDosName;
        RtlInitUnicodeString(&DeviceName,DEVICE_NAME);
        RtlInitUnicodeString(&DeviceDosName,DEVICE_DOSNAME);
        // 设置默认IRP派遣处理函数
        for(UINT i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++){
            driver->MajorFunction[i] = DefIRPHandler;
        }
        //  IRP 打开关闭处理函数
        driver->MajorFunction[IRP_MJ_CREATE] = CreateIRPHandler;
        driver->MajorFunction[IRP_MJ_CLOSE] = CloseIRPHandler;
        driver->DriverUnload = DriverUnload;
        status = IoCreateDevice(driver,
                                0,
                                NULL,
                                FILE_DEVICE_UNKNOWN,
                                0, TRUE,
                                &gDevObj);
        if (!NT_SUCCESS(status))
            return status;
        if (!gDevObj)
            return STATUS_UNEXPECTED_IO_ERROR;
        status = WallRegisterCallouts();
        if (!NT_SUCCESS(status))
            return status;
        
    
        // 设置设备读写方式
        gDevObj->Flags |= DO_DIRECT_IO;
        // 创建符号链接
        status = IoCreateSymbolicLink(&DeviceDosName, &DeviceName);
        gDevObj->Flags &= ~ DO_DEVICE_INITIALIZING;
        return STATUS_SUCCESS;
    }
    

    network->CMakeLists.txt

    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/./build)
    wdk_add_driver(network main.c)
    target_link_libraries(network ntoskrnl ndis fwpkclnt uuid)
    

    NetFilter->CMakeLists.txt
    其中需要自己设置一下WDK Library链接的路径

    cmake_minimum_required(VERSION 3.0)
    
    project(NetFilter)
    
    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0 /WX")
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W0 /WX")
    
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/./cmake")
    find_package(WDK REQUIRED)
    
    # 设置WDK Library路径
    link_directories("C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17134.0\\km\\x86")
    add_subdirectory(comport)
    add_subdirectory(network)
    

    NetFilter->build.bat
    如果需要编译Release版本的请替换脚本中的Debug字符串

    cmake .
    devenv ./NetFilter.sln /Build "Debug"
    

    祝你成功编译出sys文件 😈
    0_1540261742778_WX20181023-102839@2x.png



  • @pipi vscode还是比较好用的,我现在都很少用IDE了,除了开发Qt程序会用QtCreator😐



  • vscode可以


Log in to reply