WFP 实现的一个简单防火墙

转载地址 http://www.cnblogs.com/nevergone/archive/2013/04/05/3001765.html



一个简单的防火墙

《星际争霸2》虫群之心发布了,万众期待啊,可惜国内没有同步上市,屌丝们只能破解玩台版。现在星际争霸客户端2.05可用星际管家玩,但星际争霸客户端有强制升级的逻辑,新版本已经不能用星际管家玩了,网上有玩家用360去阻止星际争霸客户端升级,保留2.05版本。作为程序员,必须坚决抵制360,本着DIY精神,我使用WFP开发了个非常简单的网络防火墙,在星际争霸客户端访问网络时,拒绝之!

看代码,关键代码已经标红:

复制代码
#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>
#pragma warning(pop)

#define INITGUID
#include <guiddef.h>
#include "SimpleFirewall.h"

// 
// Callout and sublayer GUIDs
//

// 76b743d4-1249-4614-a632-6f9c4d08d25a
DEFINE_GUID(
    SF_ALE_CONNECT_CALLOUT_V4,
    0x76b743d4,
    0x1249,
    0x4614,
    0xa6, 0x32, 0x6f, 0x9c, 0x4d, 0x08, 0xd2, 0x5a
    );

// 7ec7f7f5-0c55-4121-adc5-5d07d2ac0cef
DEFINE_GUID(
    SF_ALE_RECV_ACCEPT_CALLOUT_V4,
    0x7ec7f7f5,
    0x0c55,
    0x4121,
    0xad, 0xc5, 0x5d, 0x07, 0xd2, 0xac, 0x0c, 0xef
    );

// 2e207682-d95f-4525-b966-969f26587f03
DEFINE_GUID(
    SF_SUBLAYER,
    0x2e207682,
    0xd95f,
    0x4525,
    0xb9, 0x66, 0x96, 0x9f, 0x26, 0x58, 0x7f, 0x03
    );

PDEVICE_OBJECT  gControlDeviceObject;
HANDLE          gInjectionHandle;
HANDLE          gEngineHandle;
UINT32          gAleConnectCalloutIdV4;
UINT32          gAleRecvAcceptCalloutIdV4;

#define MAX_MONITOR_PROCESS_ID  10
ULONG           gProcessIdTable[MAX_MONITOR_PROCESS_ID];
EX_SPIN_LOCK    gProcessIdTableLock;
UNICODE_STRING  gTargetPath = RTL_CONSTANT_STRING(L"\\??\\D:\\Program Files (x86)\\sc2manager\\SC2M.exe");

NTSTATUS
DriverEntry(
    __in PDRIVER_OBJECT DriverObject,
    __in PUNICODE_STRING RegistryPath
    )
{
    NTSTATUS Status;

    UNREFERENCED_PARAMETER(RegistryPath);

#ifdef _AMD64_
    *((PCHAR)DriverObject->DriverSection + 0x68) |= 0x20;
#else
    *((PCHAR)DriverObject->DriverSection + 0x34) |= 0x20;
#endif // _AMD64_

    Status = SFCreateCDO(DriverObject);

    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    Status = SFRegistryProcessCallback();

    if (!NT_SUCCESS(Status))
    {
        SFDeleteCDO(DriverObject);
        return Status;
    }

    Status = SFRegistryCallouts(gControlDeviceObject);

    if (!NT_SUCCESS(Status))
    {
        SFDeleteCDO(DriverObject);
        SFDeregistryProcessCallback();
        return Status;
    }

    return STATUS_SUCCESS;
}

NTSTATUS
SFCreateClose(
    __in PDEVICE_OBJECT DeviceObject,
    __in PIRP Irp
    )
{
    UNREFERENCED_PARAMETER(DeviceObject);

    PAGED_CODE();

    Irp->IoStatus.Status        = STATUS_SUCCESS;
    Irp->IoStatus.Information   = FILE_OPENED;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

NTSTATUS
SFDeviceControl(
    __in PDEVICE_OBJECT DeviceObject,
    __in PIRP Irp
    )
{
    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);

    // TODO:

    return STATUS_INVALID_DEVICE_REQUEST;
}

VOID 
SFUnload(
    __in PDRIVER_OBJECT DriverObject
    )
{
    SFDeregistryProcessCallback();
    SFDeregistryCallouts(gControlDeviceObject);
    SFDeleteCDO(DriverObject);
}

NTSTATUS
SFCreateCDO(
    __in PDRIVER_OBJECT DriverObject
    )
{
    NTSTATUS        Status;
    UNICODE_STRING  DeviceName;
    UNICODE_STRING  LinkName;

    //
    //  Create control device object
    //

    RtlInitUnicodeString(&DeviceName, NT_DEVICE_NAME);

    Status = IoCreateDevice(DriverObject,
                            0,
                            &DeviceName,
                            FILE_DEVICE_UNKNOWN,
                            FILE_DEVICE_SECURE_OPEN,
                            FALSE,
                            &gControlDeviceObject);

    if (!NT_SUCCESS(Status))
    {
        return Status;
    }

    //
    // Initialize the driver object with this driver's entry points.
    //

    DriverObject->MajorFunction[IRP_MJ_CREATE]          = SFCreateClose;
    DriverObject->MajorFunction[IRP_MJ_CLOSE]           = SFCreateClose;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = SFDeviceControl;

    DriverObject->DriverUnload = SFUnload;

    //
    //  Initialize symbolic name for our control device object
    //

    RtlInitUnicodeString(&LinkName, DOS_DEVICE_NAME);

    //
    // Create a symbolic link between our device name  and the Win32 name
    //

    Status = IoCreateSymbolicLink(&LinkName, &DeviceName);

    if (!NT_SUCCESS(Status))
    {
        IoDeleteDevice( gControlDeviceObject );

        return Status;
    }

    return Status;
}

VOID
SFDeleteCDO(
    __in PDRIVER_OBJECT DriverObject
    )
{
    UNICODE_STRING LinkName;

    UNREFERENCED_PARAMETER(DriverObject);

    PAGED_CODE();

    //
    //  Delete symbolic link
    //

    RtlInitUnicodeString(&LinkName, DOS_DEVICE_NAME);
    IoDeleteSymbolicLink(&LinkName);

    //
    //  Delete control device object
    //

    if (gControlDeviceObject != NULL)
    {
        IoDeleteDevice(gControlDeviceObject);
        gControlDeviceObject = NULL;
    }
}

NTSTATUS
SFRegistryCallouts(
    __in PDEVICE_OBJECT DeviceObject
    )
{
    NTSTATUS        Status = STATUS_SUCCESS;
    BOOLEAN         EngineOpened = FALSE;
    BOOLEAN         InTransaction = FALSE;
    FWPM_SESSION0   Session = {0};
    FWPM_SUBLAYER0  FirewallSubLayer;

    Session.flags = FWPM_SESSION_FLAG_DYNAMIC;

    Status = FwpmEngineOpen0(NULL,
                             RPC_C_AUTHN_WINNT,
                             NULL,
                             &Session,
                             &gEngineHandle);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    EngineOpened = TRUE;

    Status = FwpmTransactionBegin0(gEngineHandle, 0);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    InTransaction = TRUE;

    RtlZeroMemory(&FirewallSubLayer, sizeof(FWPM_SUBLAYER0)); 

    FirewallSubLayer.subLayerKey                = SF_SUBLAYER;
    FirewallSubLayer.displayData.name           = L"Transport SimpleFirewall Sub-Layer";
    FirewallSubLayer.displayData.description    = L"Sub-Layer for use by Transport SimpleFirewall callouts";
    FirewallSubLayer.flags                      = 0;
    FirewallSubLayer.weight                     = 0; 

    Status = FwpmSubLayerAdd0(gEngineHandle, &FirewallSubLayer, NULL);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    Status = SFRegisterALEClassifyCallouts(&FWPM_LAYER_ALE_AUTH_CONNECT_V4,
                                           &SF_ALE_CONNECT_CALLOUT_V4,
                                           DeviceObject,
                                           &gAleConnectCalloutIdV4);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    Status = SFRegisterALEClassifyCallouts(&FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4,
                                           &SF_ALE_RECV_ACCEPT_CALLOUT_V4,
                                           DeviceObject,
                                           &gAleRecvAcceptCalloutIdV4);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    Status = FwpmTransactionCommit0(gEngineHandle);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    InTransaction = FALSE;

Exit:

    if (!NT_SUCCESS(Status))
    {
        if (InTransaction)
        {
            FwpmTransactionAbort0(gEngineHandle);
        }

        if (EngineOpened)
        {
            FwpmEngineClose0(gEngineHandle);
            gEngineHandle = NULL;
        }
    }

    return Status;
}

void
SFDeregistryCallouts(
    __in PDEVICE_OBJECT DeviceObject
    )
{
    UNREFERENCED_PARAMETER(DeviceObject);

    FwpmEngineClose0(gEngineHandle);
    gEngineHandle = NULL;

    FwpsCalloutUnregisterById0(gAleConnectCalloutIdV4);
    FwpsCalloutUnregisterById0(gAleRecvAcceptCalloutIdV4);
}

NTSTATUS
SFRegisterALEClassifyCallouts(
    __in const GUID* layerKey,
    __in const GUID* calloutKey,
    __in void* DeviceObject,
    __out UINT32* calloutId
    )
{
    NTSTATUS Status = STATUS_SUCCESS;

    FWPS_CALLOUT0 sCallout = {0};
    FWPM_CALLOUT0 mCallout = {0};

    FWPM_DISPLAY_DATA0 DisplayData = {0};

    BOOLEAN calloutRegistered = FALSE;

    sCallout.calloutKey = *calloutKey;

    if (IsEqualGUID(layerKey, &FWPM_LAYER_ALE_AUTH_CONNECT_V4))
    {
        sCallout.classifyFn = SFALEConnectClassify;
        sCallout.notifyFn   = SFALEConnectNotify;
    }
    else
    {
        sCallout.classifyFn = SFALERecvAcceptClassify;
        sCallout.notifyFn   = SFALERecvAcceptNotify;
    }

    Status = FwpsCalloutRegister0(DeviceObject,
                                  &sCallout,
                                  calloutId);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    calloutRegistered = TRUE;

    DisplayData.name = L"Transport SimpleFirewall ALE Classify Callout";
    DisplayData.description = L"Intercepts inbound or outbound connect attempts";

    mCallout.calloutKey = *calloutKey;
    mCallout.displayData = DisplayData;
    mCallout.applicableLayer = *layerKey;

    Status = FwpmCalloutAdd0(gEngineHandle,
                             &mCallout,
                             NULL,
                             NULL);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

    Status = SFAddFilter(L"Transport SimpleFirewall ALE Classify",
                         L"Intercepts inbound or outbound connect attempts",
                         layerKey,
                         calloutKey);

    if (!NT_SUCCESS(Status))
    {
        goto Exit;
    }

Exit:

    if (!NT_SUCCESS(Status))
    {
        if (calloutRegistered)
        {
            FwpsCalloutUnregisterById0(*calloutId);
            *calloutId = 0;
        }
    }

    return Status;
}

NTSTATUS
SFAddFilter(
    __in const wchar_t* filterName,
    __in const wchar_t* filterDesc,
    __in const GUID* layerKey,
    __in const GUID* calloutKey
    )
{
    FWPM_FILTER0 Filter = {0};

    Filter.layerKey                 = *layerKey;
    Filter.displayData.name         = (wchar_t*)filterName;
    Filter.displayData.description  = (wchar_t*)filterDesc;

    Filter.action.type          = FWP_ACTION_CALLOUT_TERMINATING;
    Filter.action.calloutKey    = *calloutKey;
    Filter.subLayerKey          = SF_SUBLAYER;
    Filter.weight.type          = FWP_EMPTY;
    Filter.rawContext           = 0;

    return FwpmFilterAdd0(gEngineHandle, &Filter, NULL, NULL);
}

NTSTATUS
SFALERecvAcceptNotify(
    __in FWPS_CALLOUT_NOTIFY_TYPE notifyType,
    __in const GUID* filterKey,
    __in const FWPS_FILTER0* filter
    )
{
    UNREFERENCED_PARAMETER(notifyType);
    UNREFERENCED_PARAMETER(filterKey);
    UNREFERENCED_PARAMETER(filter);

    return STATUS_SUCCESS;
}

NTSTATUS
SFALEConnectNotify(
    __in FWPS_CALLOUT_NOTIFY_TYPE notifyType,
    __in const GUID* filterKey,
    __in const FWPS_FILTER0* filter
    )
{
    UNREFERENCED_PARAMETER(notifyType);
    UNREFERENCED_PARAMETER(filterKey);
    UNREFERENCED_PARAMETER(filter);

    return STATUS_SUCCESS;
}

VOID
PerformBasicAction(
    _In_ const FWPS_INCOMING_VALUES* pClassifyValues,
    _In_ const FWPS_INCOMING_METADATA_VALUES* pMetadata,
    _Inout_opt_ VOID* pLayerData,
    _In_ const FWPS_FILTER0* pFilter,
    _In_ UINT64 flowContext,
    _Inout_ FWPS_CLASSIFY_OUT* pClassifyOut,
    _In_ FWP_ACTION_TYPE basicAction
    )
{
    UNREFERENCED_PARAMETER(pClassifyValues);
    UNREFERENCED_PARAMETER(pMetadata);
    UNREFERENCED_PARAMETER(flowContext);
    UNREFERENCED_PARAMETER(pLayerData);

    if (pClassifyOut)
    {
        pClassifyOut->actionType  = basicAction;

        // Clear the right to mark as the definitive answer.

        if ((basicAction == FWP_ACTION_BLOCK) ||
            (basicAction == FWP_ACTION_PERMIT && pFilter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT))
        {
            pClassifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE;
        }
    }
}

BOOLEAN
CanIFilterThisRequest(
    __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues
    )
{
    if (inMetaValues->currentMetadataValues & FWPS_METADATA_FIELD_PROCESS_ID)
    {
        return SFFindProcessId((ULONG)inMetaValues->processId);
    }

    return FALSE;
}

void
SFALEConnectClassify(
    __in const FWPS_INCOMING_VALUES0* inFixedValues,
    __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
    __inout void* layerData,
    __in const FWPS_FILTER0* filter,
    __in UINT64 flowContext,
    __in FWPS_CLASSIFY_OUT0* classifyOut
    )
{
    if (classifyOut->rights & FWPS_RIGHT_ACTION_WRITE)
    {
        FWP_ACTION_TYPE Action = (CanIFilterThisRequest(inMetaValues) ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT);

        PerformBasicAction(inFixedValues,
                           inMetaValues,
                           layerData,
                           filter,
                           flowContext,
                           classifyOut,
                           Action);
    }
}

void
SFALERecvAcceptClassify(
    __in const FWPS_INCOMING_VALUES0* inFixedValues,
    __in const FWPS_INCOMING_METADATA_VALUES0* inMetaValues,
    __inout void* layerData,
    __in const FWPS_FILTER0* filter,
    __in UINT64 flowContext,
    __inout FWPS_CLASSIFY_OUT0* classifyOut
    )
{
    if (classifyOut->rights & FWPS_RIGHT_ACTION_WRITE)
    {
        FWP_ACTION_TYPE Action = (CanIFilterThisRequest(inMetaValues) ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT);

        PerformBasicAction(inFixedValues,
                           inMetaValues,
                           layerData,
                           filter,
                           flowContext,
                           classifyOut,
                           Action);
    }
}

VOID
CreateProcessNotifyEx(
    __inout PEPROCESS Process,
    __in HANDLE ProcessId,
    __in_opt PPS_CREATE_NOTIFY_INFO  CreateInfo
    )
{
    UNREFERENCED_PARAMETER(Process);

    if (CreateInfo)
    {
        if (RtlEqualUnicodeString(&gTargetPath, (PUNICODE_STRING)CreateInfo->ImageFileName, TRUE))
        {
            SFInsertProcessId((ULONG)(ULONG_PTR)ProcessId);
        }
        else
        {
            SFInsertChildProcessId((ULONG)(ULONG_PTR)CreateInfo->ParentProcessId,
                                   (ULONG)(ULONG_PTR)ProcessId);
        }
    }
    else
    {
        SFRemoveProcessId((ULONG)(ULONG_PTR)ProcessId);
    }
}

NTSTATUS
SFRegistryProcessCallback(
    )
{
    NTSTATUS Status;

    RtlZeroMemory(gProcessIdTable, sizeof(gProcessIdTable));

    gProcessIdTableLock = 0;

    Status = PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, FALSE);

    return Status;
}

VOID
SFDeregistryProcessCallback(
    )
{
    PsSetCreateProcessNotifyRoutineEx(CreateProcessNotifyEx, TRUE);

    SFEmptyProcessIdTable();
}

void
SFEmptyProcessIdTable(
    )
{
    KIRQL Irql = ExAcquireSpinLockExclusive(&gProcessIdTableLock);

    RtlZeroMemory(gProcessIdTable, sizeof(gProcessIdTable));

    ExReleaseSpinLockExclusive(&gProcessIdTableLock, Irql);
}

void
SFInsertProcessId(
    __in ULONG ProcessId
    )
{
    size_t  idx;
    KIRQL   Irql = ExAcquireSpinLockExclusive(&gProcessIdTableLock);

    for (idx = 0; idx < MAX_MONITOR_PROCESS_ID; ++idx)
    {
        if (gProcessIdTable[idx] == 0)
        {
            gProcessIdTable[idx] = ProcessId;
            break;
        }
    }

    ExReleaseSpinLockExclusive(&gProcessIdTableLock, Irql);
}

VOID
SFInsertChildProcessId(
    __in ULONG ParentProcessId,
    __in ULONG ProcessId
    )
{
    size_t      idx;
    BOOLEAN     FindParent = FALSE;
    KIRQL       Irql;

    Irql = ExAcquireSpinLockShared(&gProcessIdTableLock);

    for (idx = 0; idx < MAX_MONITOR_PROCESS_ID; ++idx)
    {
        if (gProcessIdTable[idx] == ParentProcessId)
        {
            FindParent = TRUE;
            break;
        }
    }

    ExReleaseSpinLockShared(&gProcessIdTableLock, Irql);

    if (FindParent)
    {
        Irql = ExAcquireSpinLockExclusive(&gProcessIdTableLock);

        for (idx = 0; idx < MAX_MONITOR_PROCESS_ID; ++idx)
        {
            if (gProcessIdTable[idx] == 0)
            {
                gProcessIdTable[idx] = ProcessId;
                break;
            }
        }

        ExReleaseSpinLockExclusive(&gProcessIdTableLock, Irql);
    }
}

VOID
SFRemoveProcessId(
    __in ULONG ProcessId
    )
{
    size_t  idx;
    KIRQL   Irql = ExAcquireSpinLockExclusive(&gProcessIdTableLock);

    for (idx = 0; idx < MAX_MONITOR_PROCESS_ID; ++idx)
    {
        if (gProcessIdTable[idx] == ProcessId)
        {
            gProcessIdTable[idx] = 0;
        }
    }

    ExReleaseSpinLockExclusive(&gProcessIdTableLock, Irql);
}

BOOLEAN
SFFindProcessId(
    __in ULONG ProcessId
    )
{
    size_t  Idx;
    BOOLEAN Result = FALSE;
    KIRQL   Irql = ExAcquireSpinLockShared(&gProcessIdTableLock);

    for (Idx = 0; Idx < MAX_MONITOR_PROCESS_ID; ++Idx)
    {
        if (gProcessIdTable[Idx] == ProcessId)
        {
            Result = TRUE;
            break;
        }
    }

    ExReleaseSpinLockShared(&gProcessIdTableLock, Irql);

    return Result;
}
复制代码

可以看到,WFP比起TDI来说,相当简洁,下面看看代码逻辑:

1. 初始化时,注册进程回调和WFP callout,我们只注册ALE层,再往下可能就没进程信息了。

2. 当有进程启动时,根据进程路径判断是否需要过滤:

    a) : 需要过滤,则把进程ID保留起来,存放到数组中。

    b) : 不需要过滤,判断该进程ID是否已经数组中,如果是,则说明是需要禁止网络的进程在创建子进程,也把子进程ID也加到数组中。

3. 当有网络访问时,获取出当期进程ID,在数组中查找,如果命中数组,则拒绝之。

4. 因为场景比较简单,因此我使用数组来保存进程ID,实现简单,速度也快。

在开发过程中,遇到的几个比较有趣的事件记录一下:

1. 注册进程回调时,我们需要获取进程路径,因此需要使用新的PsSetCreateProcessNotifyRoutineEx接口,这个接口调用时会检查驱动签名情况,下面的代码就是用来绕过检查签名代码:

#ifdef _AMD64_
    *((PCHAR)DriverObject->DriverSection + 0x68) |= 0x20;
#else
    *((PCHAR)DriverObject->DriverSection + 0x34) |= 0x20;
#endif // _AMD64_

2. 有第二种方案可以实现这个效果,在调用SFAddFilter时,可设置FWPM_FILTER_CONDITION0来达到过滤效果,能否根据文件来我没研究,有兴趣的同学可参考下面的链接:http://msdn.microsoft.com/en-us/library/windows/desktop/aa364268(v=vs.85).aspx

3. 在处理ping xxxxxx时,发现从FWPS_INCOMING_METADATA_VALUES0中获取的进程ID是4,也就是说是系统进程,有点奇怪,google一把后发现,微软说this is by design http://social.msdn.microsoft.com/Forums/en-US/wfp/thread/a524907d-af41-41a4-ac88-a181f8644aec/

4. WFP的callout回调是可能运行在Dispatch Level的,因此像EX_PUSH_LOCK\ERESOURCE\FAST_MUTEX都是不能用的,我搜了一把OSR,发现在vista+以后,微软已经实现了read write spin lock,正好在这里可以用。

代码编译环境:

vs2012+wdk8.0

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值