安全稳定的实现进线程监控

进线柜:从能量入口到智能核心的供配电系统关键设备解析 在供配电系统中,断路器、隔离开关等一次设备构成了电能传输与保护的基础架构,其核心原理在于通过可靠的机械与电气结构,实现对电路的通断、隔离与故障切除。这些设备的技术价值在于保障电网与用电设备的安全稳定运行,是电力系统选择性保护与供电连续性的基石。其应用场景广泛覆盖工厂、数据中心、商业建筑等所有从电网取电的场所。随着智能化发展,现代进线柜已集成综合保护装置与多功能电力仪表,演变为系统的数据感知核心,通过实时监测电压、电流、谐波等参数,为能效分析与预防性维护提供支持,实现了从传统“黑盒子”到“能量枢纽”的认知升级 阅读详情
作者:suei8423 (suei8423_at_163.com)
出处:http://www.xfocus.net/articles/200503/788.html
日期:2005-04-04

创建时间:2005-03-24
文章属性:原创
文章提交:suei8423 (suei8423_at_163.com)

安全稳定的实现进线程监控

作者:ZwelL

    用PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine来进行进程线程监控我想大家已经都非常熟练了.sinister在<<编写进程/线程监视器>>一文中已经实现得很好了.前一段时间看到网上有人在研究监视远线程的文章,比较有意思.就写代码玩一玩.这之中就出现了一些问题.比方说直接用sinister的代码的话,是不能动态卸载的,因为他在安装了进线程监视函数后没有进行清除动作,造成在动态卸载时蓝屏,BUGCHECK为0x000000ce,错误码为:DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS.很显然,在驱动退出后,一些进线程操作仍然在访问原来的地址,造成出错.在XP后,微软给出了一个函数PsRemoveCreateThreadNotifyRoutine用来清除线程监视函数(清除进程监视的就是PsSetCreateProcessNotifyRoutine).我一直奇怪ICESWORD在2000中是怎么做到进线程监视的.后来才发现,在运行icesword后释放出一个detport.sys文件,然后一直在系统中存在着没有卸载掉.只是把它隐藏了而已^_^.这不是个好消息,难道我为了测试一个驱动,测试一次就得重启一次吗?呵呵,肯定不是啊,所以想办法搞定它.

我们来看一下进线程监视在底层是如何实现的,在win2000源代码中先找到创建线程的函数实现:
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   /win2k/private/ntos/ps/create.h
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
NTSTATUS
PspCreateThread(
    ...
    ...
    )
{
    ...
        if (PspCreateProcessNotifyRoutineCount != 0) {        //首先调用进程监控函数
            ULONG i;
            for (i=0; i<PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
                if (PspCreateProcessNotifyRoutine[i] != NULL) {
                    (*PspCreateProcessNotifyRoutine[i])( Process->InheritedFromUniqueProcessId,
                                                         Process->UniqueProcessId,
                                                         TRUE
                                                       );
                    }
                }
            }

        }
    ...
    ...
    if (PspCreateThreadNotifyRoutineCount != 0) {
        ULONG i;

        for (i=0; i<PSP_MAX_CREATE_THREAD_NOTIFY; i++) {    //再调用线程监控函数
            if (PspCreateThreadNotifyRoutine[i] != NULL) {
                (*PspCreateThreadNotifyRoutine[i])( Thread->Cid.UniqueProcess,
                                                    Thread->Cid.UniqueThread,
                                                    TRUE
                                                   );
            }
        }
    }
    ...
    ...
}

从上面可以看到,在每创建一个线程后会调用PspCreateProcessNotifyRoutine[i]地址指向的函数.而PsSetCreateThreadNotifyRoutine的作用就是将PspCreateThreadNotifyRoutine[i]数组设置值,该值就是监视函数的地址.

NTSTATUS
PsSetCreateThreadNotifyRoutine(
    IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine
    )
{
    ULONG i;
    NTSTATUS Status;

    Status = STATUS_INSUFFICIENT_RESOURCES;
    for (i = 0; i < PSP_MAX_CREATE_THREAD_NOTIFY; i += 1) {
        if (PspCreateThreadNotifyRoutine[i] == NULL) {
            PspCreateThreadNotifyRoutine[i] = NotifyRoutine;
            PspCreateThreadNotifyRoutineCount += 1;
            Status = STATUS_SUCCESS;
            break;
        }
    }

    return Status;
}
上面的一些结构如下:
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
//   /win2k/private/ntos/ps/psp.h
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
#define PSP_MAX_CREATE_THREAD_NOTIFY 8        //最大监视数目

ULONG PspCreateThreadNotifyRoutineCount;    //用来记数
PCREATE_THREAD_NOTIFY_ROUTINE PspCreateThreadNotifyRoutine[ PSP_MAX_CREATE_THREAD_NOTIFY ];    //函数地址数组

而PCREATE_THREAD_NOTIFY_ROUTINE定义如下:
typedef
VOID
(*PCREATE_THREAD_NOTIFY_ROUTINE)(
    IN HANDLE ProcessId,
    IN HANDLE ThreadId,
    IN BOOLEAN Create
    );

相应的,进程的结构也是一样的.
通过上面,我们可以看到,只要我们找出该函数数组地址,在我们退出驱动时先将其全部清零,清零的大小为PSP_MAX_CREATE_THREAD_NOTIFY,
这样的话下一次的进线程操作就不会调用这个函数指针了.也就让系统回到正常,我们再通过PsSetCreateProcessNotifyRoutine来验证一下:

NTSTATUS
PsSetCreateProcessNotifyRoutine(
    IN PCREATE_PROCESS_NOTIFY_ROUTINE NotifyRoutine,
    IN BOOLEAN Remove
    )
{
    ULONG i;

    for (i=0; i < PSP_MAX_CREATE_PROCESS_NOTIFY; i++) {
        if (Remove) {    
            if (PspCreateProcessNotifyRoutine[i] == NotifyRoutine) {    //清除时就是简单的赋植操作
                PspCreateProcessNotifyRoutine[i] = NULL;
                PspCreateProcessNotifyRoutineCount -= 1;        //将计数器减一
                return STATUS_SUCCESS;
            }
        } else {
            if (PspCreateProcessNotifyRoutine[i] == NULL) {        //设置时也是简单的赋值操作
                PspCreateProcessNotifyRoutine[i] = NotifyRoutine;
                PspCreateProcessNotifyRoutineCount += 1;        //将计数器加一
                return STATUS_SUCCESS;
            }
        }
    }

    return Remove ? STATUS_PROCEDURE_NOT_FOUND : STATUS_INVALID_PARAMETER;
}

好了,方法已经知道了,只要找出地址,我们就能够"全身而退"了.看一下windows2003下面的PsRemoveCreateThreadNotifyRoutine实现:
lkd> u PsRemoveCreateThreadNotifyRoutine l 20
nt!PsRemoveCreateThreadNotifyRoutine:
80651d7b 53               push    ebx
80651d7c 56               push    esi
80651d7d 57               push    edi
80651d7e 33db             xor     ebx,ebx
80651d80 bf400f5780       mov     edi,0x80570f40    //起始地址
80651d85 57               push    edi
80651d86 e8a7500100 call nt!ExWaitForRundownProtectionRelease+0x5cf (80666e32)
80651d8b 8bf0             mov     esi,eax
80651d8d 85f6             test    esi,esi
80651d8f 7420         jz nt!PsRemoveCreateThreadNotifyRoutine+0x36 (80651db1)
80651d91 56               push    esi
80651d92 e8ba1bffff      call nt!IoReportTargetDeviceChange+0x7aa0 (80643951)
80651d97 3b442410         cmp     eax,[esp+0x10]
80651d9b 750d        jnz nt!PsRemoveCreateThreadNotifyRoutine+0x2f (80651daa)
80651d9d 56               push    esi
80651d9e 6a00             push    0x0
80651da0 57               push    edi
80651da1 e8c54f0100 call nt!ExWaitForRundownProtectionRelease+0x508 (80666d6b)
80651da6 84c0             test    al,al
80651da8 751b        jnz nt!PsRemoveCreateThreadNotifyRoutine+0x4a (80651dc5)
80651daa 56               push    esi
80651dab 57               push    edi
80651dac e892510100 call nt!ExWaitForRundownProtectionRelease+0x6e0 (80666f43)
80651db1 43               inc     ebx
80651db2 83c704           add     edi,0x4
80651db5 83fb08           cmp     ebx,0x8    //看是否到了最大数(8)
80651db8 72cb          jb nt!PsRemoveCreateThreadNotifyRoutine+0xa (80651d85)
80651dba b87a0000c0       mov     eax,0xc000007a
80651dbf 5f               pop     edi
80651dc0 5e               pop     esi
80651dc1 5b               pop     ebx
80651dc2 c20400           ret     0x4

lkd> dd 0x80570f40                //设置了监视函数后
80570f40  e316e557 00000000 00000000 00000000
.............................

lkd> dd 0x80570f40                //清除了监视函数后
80570f40  00000000 00000000 00000000 00000000

哈哈.下面是实现代码,代码中实现了进线的的监视,并且实现了远线程的监视:

Drivers.c
/////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Made By ZwelL

#include "ntddk.h"
#include "windef.h"
#include "define.h"

#define SYSNAME "System"
#define VERSIONLEN 100

const WCHAR devLink[]  = L"//??//MyEvent";
const WCHAR devName[]  = L"//Device//MyEvent";
UNICODE_STRING          devNameUnicd;
UNICODE_STRING          devLinkUnicd;    
PVOID                    gpEventObject = NULL;            // 与应用程序通信的 Event 对象
ULONG                    ProcessNameOffset =0;
PVOID                    outBuf[255];
BOOL                    g_bMainThread;
ULONG                    g_dwParentId;
CHECKLIST                CheckList;
ULONG                    BuildNumber;                    //系统版本号                    
ULONG                    SYSTEMID;                    //System进程的ID
PWCHAR                    Version[VERSIONLEN];

NTSTATUS PsLookupProcessByProcessId(IN ULONG ulProcId, OUT PEPROCESS * pEProcess);

ULONG GetProcessNameOffset()
{
    PEPROCESS curproc;
    int i;

    curproc = PsGetCurrentProcess();

    for( i = 0; i < 3*PAGE_SIZE; i++ )
    {
        if( !strncmp( SYSNAME, (PCHAR) curproc + i, strlen(SYSNAME) ))
        {
            return i;
        }
    }

    return 0;
}

NTSTATUS GetRegValue(PCWSTR RegPath,PCWSTR ValueName,PWCHAR Value)
{
    int ReturnValue = 0;
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE KeyHandle;
    PKEY_VALUE_PARTIAL_INFORMATION valueInfoP;
    ULONG valueInfoLength,returnLength;
    UNICODE_STRING UnicodeRegPath;
    UNICODE_STRING UnicodeValueName;

    RtlInitUnicodeString(&UnicodeRegPath, RegPath);
    RtlInitUnicodeString(&UnicodeValueName, ValueName);

    InitializeObjectAttributes(&ObjectAttributes,
        &UnicodeRegPath,
        OBJ_CASE_INSENSITIVE, // Flags
        NULL, // Root directory
        NULL); // Security descriptor

    Status = ZwOpenKey(&KeyHandle,
        KEY_ALL_ACCESS,
        &ObjectAttributes);
    if (Status != STATUS_SUCCESS)
    {
        DbgPrint("ZwOpenKey Wrong/n");
        return 0;
    }

    valueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION)+VERSIONLEN;
    valueInfoP =    (PKEY_VALUE_PARTIAL_INFORMATION) ExAllocatePool
                                                    (NonPagedPool, valueInfoLength);
    Status = ZwQueryValueKey(KeyHandle,
        &UnicodeValueName,
        KeyValuePartialInformation,
        valueInfoP,
        valueInfoLength,
        &returnLength);

    if (!NT_SUCCESS(Status))
    {
        DbgPrint("ZwQueryValueKey Wrong:%08x/n",Status);
        return Status;
    }
    else
    {
        RtlCopyMemory((PCHAR)Value, (PCHAR)valueInfoP->Data, valueInfoP->DataLength);
        ReturnValue = 1;
    }

    if(!valueInfoP);
        ExFreePool(valueInfoP);
    ZwClose(KeyHandle);
    return ReturnValue;
}

VOID MyRemoveCraeteThreadNotifyRoutine(
                                       IN PCREATE_THREAD_NOTIFY_ROUTINE  NotifyRoutine
                                       )
{
    //PsRemoveCreateThreadNotifyRoutine(ThreadCreateMon);
    PVOID ptr=NULL;
    if(BuildNumber==2195)                                    //Windows 2000 Sp4,2195
                                                            //低于sp4的我没有调试
    {
        ptr=0x80484520;
    }
    else if(BuildNumber==2600)        
    {
        if(wcscmp(Version,L"Service Pack 1")==0)            //Windows Xp Sp1,2600
            ptr=0x8054efc0;
        else if(wcscmp(Version,L"Service Pack 2")==0)        //Windows Xp Sp2,2600
            ptr=0x80561d20;
    }
    else if(BuildNumber==3790)                                //Windows 2003 server,3790
    {
        ptr=0x80570f40;
    }
    if(ptr!=NULL)
        memset(ptr, 0, sizeof(ULONG)*8);
}

VOID ThreadCreateMon (IN HANDLE PId, IN HANDLE TId, IN BOOLEAN  bCreate)
{

    PEPROCESS   EProcess,PEProcess;
    NTSTATUS    status;
    HANDLE        dwParentPID;

    status = PsLookupProcessByProcessId( (ULONG)PId, &EProcess);
    if (!NT_SUCCESS( status ))
    {
        DbgPrint("PsLookupProcessByProcessId()/n");
        return ;
    }    

    if ( bCreate )
    {
        dwParentPID=PsGetCurrentProcessId();
        status = PsLookupProcessByProcessId(
            (ULONG)dwParentPID,
            &PEProcess);
        if (!NT_SUCCESS( status ))
        {
            DbgPrint("PsLookupProcessByProcessId()/n");
            return ;
        }
        if(PId==4)    //System进程创建的东东我们不管
                //在2000下是0,在XP后是4
            return;
        if((g_bMainThread==TRUE)
            &&(g_dwParentId != dwParentPID)
            &&(dwParentPID != PId)
            )
        {
            g_bMainThread=FALSE;
            sprintf(outBuf, "=============================="
                "Remote Thread :"
                "=============================="
                "/nT:%18s%9d%9d%25s%9d/n"
                "======================================"
                "======================================/n",
                (char *)((char *)EProcess+ProcessNameOffset),
                PId, TId,
                (char *)((char *)PEProcess+ProcessNameOffset),dwParentPID);
            if(gpEventObject!=NULL)
                KeSetEvent((PRKEVENT)gpEventObject, 0, FALSE);
        }
        if(CheckList.ONLYSHOWREMOTETHREAD)    //只显示远线程
            return;
        DbgPrint( "T:%18s%9d%9d%25s%9d/n",
            (char *)((char *)EProcess+ProcessNameOffset),
            PId, TId,
            (char *)((char *)PEProcess+ProcessNameOffset),dwParentPID);
        sprintf(outBuf, "T:%18s%9d%9d%25s%9d/n",
            (char *)((char *)EProcess+ProcessNameOffset),
            PId, TId,
            (char *)((char *)PEProcess+ProcessNameOffset),dwParentPID);
        if(gpEventObject!=NULL)
            KeSetEvent((PRKEVENT)gpEventObject, 0, FALSE);
    }
    else if(CheckList.SHOWTERMINATETHREAD)
    {
        DbgPrint( "TERMINATED == THREAD ID: %d/n", TId);
        sprintf(outBuf,"TERMINATED == THREAD ID: %d/n", TId);
        if(gpEventObject!=NULL)
            KeSetEvent((PRKEVENT)gpEventObject, 0, FALSE);
    }
}


VOID ProcessCreateMon ( HANDLE hParentId, HANDLE PId, BOOLEAN bCreate )
{

    PEPROCESS        EProcess,PProcess;
    NTSTATUS        status;
    HANDLE            TId;

    g_dwParentId = hParentId;
    status = PsLookupProcessByProcessId((ULONG)PId, &EProcess);
    if (!NT_SUCCESS( status ))
    {
        DbgPrint("PsLookupProcessByProcessId()/n");
        return ;
    }
    status = PsLookupProcessByProcessId((ULONG)hParentId, &PProcess);
    if (!NT_SUCCESS( status ))
    {
        DbgPrint("PsLookupProcessByProcessId()/n");
        return ;
    }

    if ( bCreate )
    {
        g_bMainThread = TRUE;
        DbgPrint( "P:%18s%9d%9d%25s%9d/n",
            (char *)((char *)EProcess+ProcessNameOffset),
            PId,PsGetCurrentThreadId(),
            (char *)((char *)PProcess+ProcessNameOffset),
            hParentId
            );
        sprintf(outBuf, "P:%18s%9d%9d%25s%9d/n",
            (char *)((char *)EProcess+ProcessNameOffset),
            PId,PsGetCurrentThreadId(),
            (char *)((char *)PProcess+ProcessNameOffset),
            hParentId
            );
        if(gpEventObject!=NULL)
            KeSetEvent((PRKEVENT)gpEventObject, 0, FALSE);
    }
    else if(CheckList.SHOWTERMINATEPROCESS)
    {
        DbgPrint( "TERMINATED == PROCESS ID: %d/n", PId);
        sprintf(outBuf,"TERMINATED == PROCESS ID: %d/n", PId);
        if(gpEventObject!=NULL)
            KeSetEvent((PRKEVENT)gpEventObject, 0, FALSE);
    }

}

NTSTATUS OnUnload( IN PDRIVER_OBJECT pDriverObject )
{
    NTSTATUS            status;
    DbgPrint("OnUnload called/n");
    if(gpEventObject)
        ObDereferenceObject(gpEventObject);
    PsSetCreateProcessNotifyRoutine(ProcessCreateMon, TRUE);
    MyRemoveCraeteThreadNotifyRoutine(ThreadCreateMon);
    if(pDriverObject->DeviceObject != NULL)
    {
        status=IoDeleteSymbolicLink( &devLinkUnicd );
        if ( !NT_SUCCESS( status ) )
        {
            DbgPrint((  "IoDeleteSymbolicLink() failed/n" ));
            return status;
        }
        IoDeleteDevice( pDriverObject->DeviceObject );
    }
    return STATUS_SUCCESS;
}

NTSTATUS DeviceIoControlDispatch(
                                 IN  PDEVICE_OBJECT  DeviceObject,
                                 IN  PIRP            pIrp
                                 )
{
    PIO_STACK_LOCATION              irpStack;
    NTSTATUS                        status;
    PVOID                           inputBuffer;
    ULONG                           inputLength;
    PVOID                           outputBuffer;
    ULONG                           outputLength;
    OBJECT_HANDLE_INFORMATION        objHandleInfo;

    status = STATUS_SUCCESS;
    // 取出IOCTL请求代码
    irpStack = IoGetCurrentIrpStackLocation(pIrp);

    switch (irpStack->MajorFunction)
    {
    case IRP_MJ_CREATE :
        DbgPrint("Call IRP_MJ_CREATE/n");
        break;
    case IRP_MJ_CLOSE:
        DbgPrint("Call IRP_MJ_CLOSE/n");
        break;
    case IRP_MJ_DEVICE_CONTROL:
        DbgPrint("IRP_MJ_DEVICE_CONTROL/n");
        inputLength=irpStack->Parameters.DeviceIoControl.InputBufferLength;
        outputLength=irpStack->Parameters.DeviceIoControl.OutputBufferLength;
        switch (irpStack->Parameters.DeviceIoControl.IoControlCode)
        {
        case IOCTL_PASSEVENT:    //用事件做通信
            inputBuffer = pIrp->AssociatedIrp.SystemBuffer;

            DbgPrint("inputBuffer:%08x/n", (HANDLE)inputBuffer);
            status = ObReferenceObjectByHandle(*(HANDLE *)inputBuffer,
                GENERIC_ALL,
                NULL,
                KernelMode,
                &gpEventObject,
                &objHandleInfo);

            if(status!=STATUS_SUCCESS)
            {
                DbgPrint("wrong/n");
                break;
            }
            break;
        case IOCTL_UNPASSEVENT:
            if(gpEventObject)
                ObDereferenceObject(gpEventObject);
            DbgPrint("UNPASSEVENT called/n");
            break;
        case IOCTL_PASSBUF:
            RtlCopyMemory(pIrp->UserBuffer, outBuf, outputLength);
            break;
        case IOCTL_PASSEVSTRUCT:
            inputBuffer = pIrp->AssociatedIrp.SystemBuffer;
            memset(&CheckList, 0, sizeof(CheckList));
            RtlCopyMemory(&CheckList, inputBuffer, sizeof(CheckList));
            DbgPrint("%d:%d/n", CheckList.ONLYSHOWREMOTETHREAD, CheckList.SHOWTHREAD);
            break;
        default:
            break;
        }
        break;
    default:
        DbgPrint("Call IRP_MJ_UNKNOWN/n");
        break;
    }

    pIrp->IoStatus.Status = status;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest (pIrp, IO_NO_INCREMENT);
    return status;
}

NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING theRegistryPath )
{
    NTSTATUS                Status;    
    PDEVICE_OBJECT            pDevice;

    DbgPrint("DriverEntry called!/n");
    g_bMainThread = FALSE;

    if(1!=GetRegValue(L"//Registry//Machine//SOFTWARE//Microsoft//Windows NT//CurrentVersion", L"CSDVersion", Version))
    {
        DbgPrint("GetRegValueDword Wrong/n");
    }
    PsGetVersion(NULL, NULL, &BuildNumber, NULL);
    DbgPrint("[[[%d]]]:[[[%ws]]]", BuildNumber, Version);

    RtlInitUnicodeString (&devNameUnicd, devName );
    RtlInitUnicodeString (&devLinkUnicd, devLink );

    Status = IoCreateDevice ( pDriverObject,
        0,
        &devNameUnicd,
        FILE_DEVICE_UNKNOWN,
        0,
        TRUE,
        &pDevice );
    if( !NT_SUCCESS(Status))
    {
        DbgPrint(("Can not create device./n"));
        return Status;
    }

    Status = IoCreateSymbolicLink (&devLinkUnicd, &devNameUnicd);
    if( !NT_SUCCESS(Status))
    {
        DbgPrint(("Cannot create link./n"));
        return Status;
    }

    ProcessNameOffset = GetProcessNameOffset();

    pDriverObject->DriverUnload  = OnUnload;
    pDriverObject->MajorFunction[IRP_MJ_CREATE] =
        pDriverObject->MajorFunction[IRP_MJ_CLOSE] =
        pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControlDispatch;

    Status = PsSetCreateProcessNotifyRoutine(ProcessCreateMon, FALSE);
    if (!NT_SUCCESS( Status ))
    {
        DbgPrint("PsSetCreateProcessNotifyRoutine()/n");
        return Status;
    }

    Status = PsSetCreateThreadNotifyRoutine(ThreadCreateMon);
    if (!NT_SUCCESS( Status ))
    {
        DbgPrint("PsSetCreateThreadNotifyRoutine()/n");
        return Status;
    }

    return STATUS_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////


main.c,  这里我用事件做为通信驱动
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Made By ZwelL

#include <windows.h>
#include <stdio.h>
#include "define.h"

int main()
{
    HANDLE        hDevice;    
    bool        status;
    HANDLE        m_hCommEvent;
    ULONG        dwReturn;
    char        outbuf[255];
    CHECKLIST    CheckList;

    hDevice = NULL;
    m_hCommEvent = NULL;
    hDevice = CreateFile( "////.//MyEvent",
                    GENERIC_READ|GENERIC_WRITE,
                    FILE_SHARE_READ | FILE_SHARE_WRITE,
                    NULL,
                    OPEN_EXISTING,
                    FILE_ATTRIBUTE_NORMAL,
                    NULL);
    if(hDevice == INVALID_HANDLE_VALUE)
    {
        printf("createfile wrong/n");
        getchar();
        return 0;
    }

    m_hCommEvent = CreateEvent(NULL,
                                  false,
                                  false,
                                  NULL);
    printf("hEvent:%08x/n", m_hCommEvent);

    status =DeviceIoControl(hDevice,
                IOCTL_PASSEVENT,
                &m_hCommEvent,
                sizeof(m_hCommEvent),
                NULL,
                0,
                &dwReturn,
                NULL);
    if( !status)
    {
        printf("IO wrong+%d/n", GetLastError());
        getchar();
        return 0;
    }

    CheckList.ONLYSHOWREMOTETHREAD=TRUE;
    CheckList.SHOWTHREAD=TRUE;
    CheckList.SHOWTERMINATETHREAD=FALSE;
    CheckList.SHOWTERMINATEPROCESS=FALSE;
    status =DeviceIoControl(hDevice,
                IOCTL_PASSEVSTRUCT,
                &CheckList,
                sizeof(CheckList),
                NULL,
                0,
                &dwReturn,
                NULL);
    if( !status)
    {
        printf("IO wrong+%d/n", GetLastError());
        getchar();
        return 0;
    }

    printf("      [Process Name]    [PID]    [TID]    [Parent Process Name]    [PID]    [TID]/n");
    while(1)
    {
        ResetEvent(m_hCommEvent);
        WaitForSingleObject(m_hCommEvent, INFINITE);
        status =DeviceIoControl(hDevice,
                    IOCTL_PASSBUF,
                    NULL,
                    0,
                    &outbuf,
                    sizeof(outbuf),
                    &dwReturn,
                    NULL);
        if( !status)
        {
            printf("IO wrong+%d/n", GetLastError());
            getchar();
            return 0;
        }
        printf("%s", outbuf);
    }

    status =DeviceIoControl(hDevice,
                IOCTL_UNPASSEVENT,
                NULL,
                0,
                NULL,
                0,
                &dwReturn,
                NULL);
    if( !status)
    {
        printf("UNPASSEVENT wrong+%d/n", GetLastError());
        getchar();
        return 0;
    }

    status = CloseHandle( hDevice );
    status = CloseHandle(m_hCommEvent);
    getchar();
    return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////

define.h
/////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stdio.h"

#define FILE_DEVICE_EVENT  0x8000

// Define Interface reference/dereference routines for
// Interfaces exported by IRP_MN_QUERY_INTERFACE

#define EVENT_IOCTL(index) /
    CTL_CODE(FILE_DEVICE_EVENT, index, METHOD_BUFFERED, FILE_READ_DATA)

#define IOCTL_PASSEVENT /
    CTL_CODE(FILE_DEVICE_EVENT, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_PASSBUF /
    CTL_CODE(FILE_DEVICE_EVENT, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_UNPASSEVENT /
    CTL_CODE(FILE_DEVICE_EVENT, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define IOCTL_PASSEVSTRUCT /
    CTL_CODE(FILE_DEVICE_EVENT, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)

typedef struct        //这个结构主要用于调试用
{
    BOOL SHOWTHREAD;
    BOOL ONLYSHOWREMOTETHREAD;
    BOOL SHOWTERMINATEPROCESS;
    BOOL SHOWTERMINATETHREAD;
}CHECKLIST, *PCHECKLIST;


////////////////////////////////////////////////////////////////////////////////////////////////////////////

先用驱动加载工具加载驱动,再运行程序,可以监视到进程线的操作信息,并且可以实现监视远线程的创建.个人认为很完美.
如果您有更好的方法,请告知我一声,谢谢了.  ^_^
下面的运行结果:

hEvent:00000010
      [Process Name]    [PID]    [TID]    [Parent Process Name]    [PID]    [TID]
T:       svchost.exe      940     3540              svchost.exe      940
T:      explorer.exe     1680     3564             explorer.exe     1680
P:       notepad.exe     3568     1684             explorer.exe     1680
T:       notepad.exe     3568     3572             explorer.exe     1680
T:       svchost.exe     1036     3576              svchost.exe     1036
T:           cmd.exe     3580     3084             explorer.exe     1680
P:        doskey.exe     3608     3084                  cmd.exe     3580
T:       taskmgr.exe      352     3752             explorer.exe     1680
T:       svchost.exe     1036     2492              svchost.exe     1036
T:        remote.exe     3824     3828                  cmd.exe     3580
==============================Remote Thread :==============================
T:            hh.exe     3116     3832               remote.exe     3824
============================================================================



参考资料:
1. 编写进程/线程监视器  -sinister
    http://www.xfocus.net/articles/200303/495.html
2. 监视远程线程的创建 -一块三毛钱
    http://www.luocong.com/bbs/dispbbs.asp?boardID=2&ID=6895&page=2
3. Windows 2000源代码
电力监控系统如何在锡林郭勒配电工程中采集相关遥测、遥信和SOE等数据完成监视、控制和预警? 安科瑞电力监控系统和五防系统在锡林郭勒项目的应用 安科瑞 崔远航 摘要:随着电力、计算机、信息和网络等技术的不断发展,推动了电力监控的快速发展,人们对电力系统运行的安全性以及稳定性的要求越来越高。本文针对锡林郭勒供配电系统特点及供配电系统高可靠性的要求,提出了保护类、监测类和防误闭锁产品解决方案。本文介绍的AM5SE系列微机保护装置,可以针对锡林郭勒配电工程中不同保护对象提供对应的保护功能,并且通过电力监控系统采集相关遥测、遥信和SOE等数据完成监视、控制和预警,配合五防系统,大大提高了变电站运行的可靠 阅读详情

相关推荐

机房动力环境集中监控系统

随着信息化程度日益加深,整个社会的业务流程都越来越依赖信息系统,服务器上储存这数量巨大的核心信息,其运行的也都是重要业务,作为信息化载体的计算机、服务器等,其“栖身之地”正式机房。因此机房成为各单位重要的数据处理以及运行中心。 机房动力环境集中监控的适用范围 机房动力环境集中监控同样使用在工厂车间、实验室、药房、库房、仓库、粮仓、酒窖、图书馆、文物室等地方,安装调试灵活便利,降低建设成本和运营成本...

BJ_Yclh的博客 2322

64位下使用回调函数实现监控(下)

线程监控使用到的API相对于进程监控简单,使用到,而这个值并不能像进程操作的API一样进行操作,这里我们首先先使用这个API来进行线程的监控指向的结构如下,第一个参数为PID,第二个参数为TID,第三个参数表示是创建线程还是删除线程,创建线程则为TRUE,删除线程则为FALSE那么我们这里就可以写出函数这里如果要将函数摘除,就需要用到函数,定义如下我们如果想让进行线程的保护该如何操作呢?

星星我啊,已经很努力在学习了, 有人一起学习吗?Ziansec-1 1028

5SX_5SJ系列断路器接线指南:正确极性与进线方向

在现代电气系统中,5SX_5SJ系列断路器扮演着至关重要的角色。本章节将为您提供5SX_5SJ系列断路器的基本概念与特点,使您对这一关键设备有一个全面而深入的了解。5SX_5SJ系列断路器是西门子公司推出的智能型断路器产品。它结合了先进的电子技术和机电一体化设计,具备高可靠性和智能化控制功能。广泛应用于工业、商业以及住宅建筑的配电系统中,用于保护电路免受过载和短路的影响。

weixin_42612804的博客 1288

[原创]通过PsSetCreateProcessNotifyRoutineEx和PsSetCreateThreadNotifyRoutine实现进程与线程监控

PsSetCreateProcessNotifyRoutineEx是Windows提供得一个可以通过设置回调函数实现进程监控的内核API。其在文档的定义如下: 1 2 3 4 5 NTSTATUS PsSetCreateProcessNotifyRoutineEx( INPCREATE_PROCESS_NOTIFY_ROUTINE_EXNotifyRoutine, INBOOLEAN...

追逐光芒,追随内心 2356

扩展内核函数 PsSetCreateProcessNotifyRoutine 等的蹲坑数量

扩展内核函数 PsSetCreateProcessNotifyRoutine 等的蹲坑数量

精选_枚举并删除系统上PsSetCreateThreadNotifyRoutine回调_源码打包

枚举并删除系统上PsSetCreateThreadNotifyRoutine回调

(转载)安全稳定的实现进线程监控

安全稳定的实现进线程监控创建时间:2005-03-24文章属性:原创文章提交:suei8423 (suei8423_at_163.com)安全稳定的实现进线程监控作者:ZwelL    用PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine来进行进程线程监控我想大家已经都非常熟练了.sinister在>一文中已经实现得很好了.

Kendiv's Box 5365

Win11下Edge浏览器CPU占用飙升?试试这5个隐藏设置(附实测对比图)

本文详细解析了Win11下Edge浏览器CPU占用飙升的原因,并提供了5个关键设置优化方案,包括禁用硬件加速、管理扩展程序等,有效降低资源消耗。实测数据显示优化后CPU占用显著下降,适合Win11用户提升浏览器性能。

weixin_29266679的博客 1522

驱动开发:内核监控进程与线程创建

监控进程的启动与退出可以使用 `PsSetCreateProcessNotifyRoutineEx` 来创建回调,当新进程产生时,回调函数会被率先执行,然后执行我们自己的`MyCreateProcessNotifyEx`函数,并在内部进行打印输出。而检测线程操作与检测进程差不多,检测线程需要调用`PsSetCreateThreadNotifyRoutine` 创建回调函数,然后就可以检测线程的创建了。

微软技术分享 1万+

Dify安装使用说明

dify可以说是一个功能不错的LLMOps,可以通过dify集中管理模型,可以通过界面创建AI应用,可以上传文档形成知识库,可以创建自定义工具(API),并可以对外提供API。相关功能类似Openai的GPTs或智谱的智能体,其他大厂的大模型也有类似功能,不过貌似只有dify是开源的。这就为个人本地部署AI应用提供了便利。

龙井茶的Sky 1万+

COMSOL新手必看:锂电池仿真从入门到精通的5个实战案例(附模型文件)

本文为COMSOL新手提供锂电池仿真的5个实战案例,涵盖锂枝晶生长模拟、电池热失控预警、SOC估计等效电路模型、电池组液冷系统热管理及SOH衰退模型构建。通过详细步骤和参数设置,帮助用户快速掌握锂电池仿真技术,提升新能源研发效率。附模型文件可直接应用。

wdx01234567的博客 261

配电自动化中的进线监控技术

进线监控技术是配电网自动化中的一项关键技术,它主要用于对配电网进线变电站的开关状态以及母线电压、电流、有功功率、无功功率及电度量等参数进行实时监测。

贾贾的博客 1022

(头歌作业)—6.1 葡萄酒评论分析报告(project)

请读取文件中的数据,完成以下任务:‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬。请读取文件中的数据,完成以下任务:‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬。请读取文件中的数据,完成以下任务:‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬。

2301_80677920的博客 1246

从高压进线到末端馈线,护航 AIDC 智算中心稳定运行

ATU一体化电力模组智能监控系统主要用在数据中心、智算中心,给核心业务提供稳定看得见的电力保障。

Acrellyj的博客 215

警惕!数字签名伪造的攻防演练:用SigThief克隆证书的真实测试(Windows环境)

本文深入探讨了Windows环境下数字签名伪造的攻防技术,详细介绍了使用SigThief工具进行签名提取与克隆的实战步骤。通过真实测试案例,揭示了数字签名安全机制的潜在漏洞,并提供了有效的防御策略和检测技术,帮助安全人员提升系统防护能力。

weixin_42530732的博客 410

SAP Fiori 会话超时全解析:从 Launchpad 到 Identity Authentication 的统一策略

SAP Fiori会话超时机制解析与配置策略 本文深入探讨SAP Fiori项目中的会话超时机制,分析其对系统安全和用户体验的双重影响。文章系统拆解了SAP Fiori Launchpad与SAP Cloud Identity Services之间的会话超时工作原理,重点介绍了两种超时机制:服务器驱动的超时(通过SESSION_TIMEOUT_STOP_DATA_REFRESH参数控制)和UI驱动的超时(通过SESSION_TIMEOUT_INTERVAL_IN_MINUTES参数控制)。文中结合制造业产线

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 213

第八章:综合布线技术 —— 进线间和建筑群子系统设计

本文系统阐述了综合布线系统中进线间子系统和建筑群子系统的设计要点。进线间设计需遵循GB50311-2007标准,重点考虑位置选择、空间预留、多运营商接入及防护要求。建筑群子系统设计包括线缆选型(推荐多模/单模光缆)、四种敷设方式对比(架空/直埋/管道/隧道)及保护措施(防雷、接地等)。补充知识部分强调了光缆芯数预留30%余量、不同场景敷设方式选择以及验收关键指标。全文为建筑物间通信网络布线提供了全面的技术指导,兼顾功能实现与长期维护需求。

2401_82750246的博客 842

反爬虫:对抗最新反爬技术 Cloudflare详解

本文针对Cloudflare最新反爬机制提供完整解决方案,覆盖TLS指纹、行为轨迹、设备指纹等检测场景。文章首先分析Cloudflare的三重检测体系(TLS指纹、行为指纹、设备指纹)及其拦截表现,随后提出四种绕过方案:1)使用curl_cffi模拟浏览器TLS指纹;2)通过undetected-chromedriver移除自动化特征;3)Playwright结合stealth插件模拟人类行为;4)动态请求调度策略。重点推荐轻量级Python方案curl_cffi和浏览器自动化方案Playwright-st

数据知道的博客 4629

分布式光伏电站运维难?Acrel-1000DP助力安全稳定运行

光伏发电功率预测系统通过采集数值天气预报数据、实时环境气象数据、光伏电站实时输出功率数据、光伏组件运行状态等信息,可按照电网调度技术要求,实现标准格式的中期功率预测、短期功率预测、超短期功率预测,以及光伏电站实时气象数据、装机容量、投运容量、最大出力等信息的上报。根据《光伏发电站接入电力系统的技术规定》GB/T 19964的相关要求,光伏发电站应配置光伏发电功率预测系统,系统具有0h-240h中期光伏发电功率预测、0h-72h短期光伏发电功率预测以及15min-4h超短期光伏发电功率预测功能。

Acrel18701808662的博客 894

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

从隐患频发走向精准防控:配电室电力安全监控系统

在电力供应体系中,配电室作为关键节点,维系着各类场所的电力稳定输送。然而,传统配电室运维管理模式下,高成本与低效益的矛盾日益凸显。为打破这一困局,配电室电力安全监控系统应运而生,凭借智能化、自动化的特性,成为实现降本增效的有力武器。

配电房综合监控 1070

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: 微软计划于下周发布WMF补丁
下一篇: 2005 packetstorm exploit
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值