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

    技术2022-05-11  59

    作者: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:x/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:s
    转载请注明原文地址: https://ibbs.8miu.com/read-25960.html

    最新回复(0)