用PsSetCreateProcessNotifyRoutine,PsSetCreateThreadNotifyRoutine來進行進程線程監控我想大家已經都非常熟練了.sinister在<<編寫進程/線程監視器>>一文中已經實現得很好了.前一段時間看到網上有人在研究監視遠線程的文章,比較有意思.就寫代碼玩一玩.這之中就出現了一些問題.比方說直接用sinister的代碼的話,是不能動態卸載的,因為他在安裝了進線程監視函數后沒有進行清除動作,造成在動態卸載時藍屏,BUGCHECK為0x000000ce,錯誤碼為:DRIVER_UNLOADED_WITHOUT_CANCELLING_PENDING_OPERATIONS.很顯然,在驅動退出后,一些進線程操作仍然在訪問原來的地址,造成出錯.在XP后,微軟給出了一個函數PsRemoveCreateThreadNotifyRoutine用來清除線程監視函數(清除進程監視的就是PsSetCreateProcessNotifyRoutine).我一直奇怪ICESWORD在2000中是怎么做到進線程監視的.后來才發現,在運行icesword后釋放出一個detport.sys文件,然后一直在系統中存在著沒有卸載掉.只是把它隱藏了而已^_^.這不是個好消息,難道我為了測試一個驅動,測試一次就得重啟一次嗎?呵呵,肯定不是啊,所以想辦法搞定它.
我們來看一下進線程監視在底層是如何實現的,在win2000源代碼中先找到創建線程的函數實現:
從上面可以看到,在每創建一個線程后會調用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來驗證一下:
好了,方法已經知道了,只要找出地址,我們就能夠"全身而退"了.看一下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] &, nbsp; [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源代碼


