隐藏进程.docx
- 文档编号:30455843
- 上传时间:2023-08-15
- 格式:DOCX
- 页数:60
- 大小:34.35KB
隐藏进程.docx
《隐藏进程.docx》由会员分享,可在线阅读,更多相关《隐藏进程.docx(60页珍藏版)》请在冰豆网上搜索。
隐藏进程
Windows2000内核级进程隐藏、侦测技术
信息对抗是目前计算机发展的一个重要的方向,为了更好的防御,必须去深入的了解敌人进攻的招式。
信息对抗促使信息技术飞速的发展。
下面我选取了信息对抗技术的中一个很小一角关于windows内核级病毒隐藏技术和反病毒侦测技术作为议题详细讨论。
目录
1.驱动程序简介
1.1为什么选驱动程序
1.2入口例程DriverEntry
1.3Unload例程
1.4派遣例程
1.5驱动程序的安装
2.通过HookSSDT(SystemServiceDispathTable)隐藏进程
2.1原理介绍
2.2Hook
2.3对NtQuerySystemInformation返回的数据进行删改
2.4核心实现
3.枚举和修改活动进程链表来检测和隐藏进程
3.1介绍EPROCESS块(进程执行块)
3.2查看EPROCESS结构
3.3什么是活动进程链表
3.4进程枚举检测HookSSDT隐藏的进程
3.5解决硬编码问题
3.6删除活动进程链表实现进程隐藏
4.基于线程调度链表的检测和隐藏技术
4.1什么是ETHREAD和KTHREAD块
4.2线程调度
4.3通过线程调度链表进行隐藏进程的检测
4.4绕过内核调度链表隐藏进程
4.5检测绕过内核调度链表隐藏进程
5.Hook内核函数(KiReadyThread)检测进程
5.1介绍通用Hook内核函数的方法
5.2检测隐藏进程
6.结论
驱动程序简介
1.为什么选驱动程序
驱动程序是运行在系统信任的Ring0环境下在代码,她拥有对系统任何软件和硬件的访问权限。
这意味着内核驱动可以访问所有的系统资源,可以读取所有的内存空间,而且也被允许执行CPU的特权指令,如,读取CPU控制寄存器的当前值等。
而处于用户模式下的程序如果试图从内核空间中读取一个字节或者试图执行像MOVEAX,CR3这样的汇编指令都会被立即终止掉。
不过,这种强大的底线是驱动程序的一个很小的错误就会让整个系统崩溃。
所以对隐藏和反隐藏技术来说都提供了一个极好的环境。
但是又对攻击者和反查杀者提出了更高的技术要求。
2.入口例程DriverEntry
DriverEntry是内核模式驱动程序主入口点常用的名字,她的作用和main,WinMain,是一样的。
extern"C"NTSTATUSDriverEntry(INPDRIVER_OBJECTDriverObject,INPUNICODE_STRINGRegistryPath){...}DriverEntry的第一个参数是一个指针,指向一个刚被初始化的驱动程序对象,该对象就代表你的驱动程序,DriverEntry的第二个参数是设备服务键的键名。
DriverEntry函数返回一个NTSTATUS值。
NTSTATUS实际就是一个长整型,但你应该使用NTSTATUS定义该函数的返回值而不是LONG,这样代码的可读性会更好。
大部分内核模式支持例程都返回NTSTATUS状态代码,你可以在DDK头文件NTSTATUS.H中找到NTSTATUS的代码列表。
DriverEntry的作用主要就是创建设备对象,建立设备对象的符号链接,设置好各个类型的回调函数等。
例如:
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中通过设置AddDevice回调函数来创建设备对象。
在NT驱动中在DriverEntry例程中创建设备对象和符号链接。
例如:
RtlInitUnicodeString(&deviceNameUnicodeString,deviceNameBuffer);//初始化设备名字
//创建设备
ntStatus=IoCreateDevice(DriverObject,
0,
&deviceNameUnicodeString,
##DeviceId,
0,
FALSE,
&deviceObject
);
if(NT_SUCCESS(ntStatus)){
RtlInitUnicodeString(&deviceLinkUnicodeString,deviceLinkBuffer);//初始化符号链接名字
//创建符号链接
ntStatus=IoCreateSymbolicLink(&deviceLinkUnicodeString,&deviceNameUnicodeString);
if(!
NT_SUCCESS(ntStatus)){
IoDeleteDevice(deviceObject);//如果创建符号链接失败,删除设备
returnntStatus;
}
}
建立符号链接的作用就是暴露一个给应用程序的接口,应用程序可以通过CreateFileAPI打开链接符号,得到一个语柄,和我们的驱动程序进行交互操作。
3.Unload例程
虽然各个驱动程序的Unload例程不尽相同,但是它大致执行下列工作:
释放属于驱动程序的任何硬件。
从Win32的名字空间移除符号连接名。
这个动作可以调用IoDeleteSymbolicLink来实现。
使用IoDeleteDevice移除设备对象。
释放驱动程序持有的任何缓冲池等。
VOIDDriverUnload(INPDRIVER_OBJECTpDriverObject)
{
PDEVICE_OBJECTpNextObj;
//循环每一个驱动过程控制的设备
pNextObj=pDriverObject->DeviceObject;
while(pNextObj!
=NULL)
{
//从设备对象中取出设备Extension
PDEVICE_EXTENSIONpDevExt=(PDEVICE_EXTENSION)extObj->DeviceExtension;
//取出符号连接名
UNICODE_STRINGpLinkName=pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName);//删除符号连接名
oDeleteDevice(pNextObj);//删除设备
pNextObj=pNextObj->NextDevice;
}
}4.派遣例程
Win2000的I/O请求是包驱动的,当一个I/O请求开始,I/O管理器先创建一个IRP去跟踪这个请求,另外,它存储一个功能代码在IRP的I/O堆栈区的MajorField域中来唯一的标识请求的类型。
MajorField域是被I/O管理器用来索引驱动程序对象的MajorFunction表,这个表包含一个指向一个特殊I/O请求的派遣例程的功能指针,如果驱动程序不支持这个请求,MajorFunction表就会指向I/O管理器函数_IopInvalidDeviceRequest,该函数返回一个错误给原始的调用者。
驱动程序的作者有责任提供所有的驱动程序支持的派遣例程。
所有的驱动程序必须支持IRP_MJ_CREATE功能代码,因为这个功能代码是用来响应Win32用户模式的CreateFile调用,如果不支持这功能代码,Win32程序就没有办法获得设备的句柄,类似的,驱动程序必须支持IRP_MJ_CLOSE功能代码,因为它用来响应Win32用户模式的CloseHandle调用。
顺便提一下,系统自动调用CloseHandle函数,因为在程序退出的时候,所有的句柄都没有被关闭。
staticNTSTATUSMydrvDispatch(INPDEVICE_OBJECTDeviceObject,INPIRPIrp)
{
NTSTATUSstatus;
PIO_STACK_LOCATIONirpSp;
//得到当前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管理器的操作支持一个标准的读写提取,IRP_MJ_DEVICE_CONTROL允许扩展的I/O请求,使用用户模式的DeviceIoControl函数来调用,I/O管理器创建一个IRP,这个IRP的MajorFunction和IoControlCode是被DeviceIoControl函数指定其内容。
传递给驱动程序的IOCTL遵循一个特殊的结构,它有32-bit大小,DDK包含一个方便的产生IOCTL值的机制的宏,CTL_CODE。
可以使用CTL_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;//堆栈区域存储了用户缓冲区信息
pIrpStack=IoGetCurrentIrpStackLocation(pIrp);
//取出IOCTL请求代码
controlCode=pIrpStack->Parameters.DeviceIoControl.IoControlCode;
//得到请求缓冲区大小
inSize=pIrpStack->Parameters.DeviceIoControl.InputBufferLength;
OutSize=pIrpStack->Parameters.DeivceIoControl.OutputBufferLength;
//现在执行二次派遣
switch(controlCode)
{
caseIOCTL_MISSLEDEVICEAIM:
......
caseIOCTL_DEVICE_LAUNCH:
......
default:
//驱动程序收到了未被承认的控制代码
status=STATUS_INVALID_DEVICE_REQUEST;
}
pIrp->IoStatus.Information=0;//数据没有传输
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
returnstatus;
}
5.驱动程序的安装
SC管理器(即服务控制管理器)可以控制服务和驱动程序。
加载和运行一个服务需要执行的典型操作步骤:
1.调用OpenSCManager()以获取一个管理器句柄
2.调用CreateService()来向系统中添加一个服务
3.调用StartService()来运行一个服务
4.调用CloseServiceHandle()来释放管理器或服务句柄
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;
}
以上对驱动程序大致框架做了一个非常简单的介绍,这仅仅是驱动程序中的一个”HelloWorld!
”。
驱动程序是相当复杂的,由于我们只是利用驱动程序的特权,对windows内核进行修改,所以就不对驱动驱动程序进行深入讨论了。
通过HookSSDT(SystemServiceDispathTable)隐藏进程
1.原理介绍:
Windows操作系统是一种分层的架构体系。
应用层的程序是通过API来访问操作系统。
而API又是通过ntdll里面的核心API来进行系统服务的查询。
核心API通过对int2e的切换,从用户模式转换到内核模式。
2Eh中断的功能是通过NTOSKRNL.EXE的一个函数KiSystemService()来实现的。
在你使用了一个系统调用时,必须首先装载要调用的函数索引号到EAX寄存器中。
把指向参数区的指针被保存在EDX寄存器中。
中断调用后,EAX寄存器保存了返回的结果。
KiSystemService()是根据EAX的值来决定哪个函数将被调用。
而系统在SSDT中维持了一个数组,专门用来索引特定的函数服务地址。
在Windows2000中有一个未公开的由ntoskrnl.exe导出的KeServiceDescriptorTable变量,我们可以通过它来完成对SSDT的访问与修改。
KeServiceDescriptorTable对应于一个数据结构,定义如下:
typedefstructSystemServiceDescriptorTable
{
UINT*ServiceTableBase;
UINT*ServiceCounterTableBase;
UINTNumberOfService;
UCHAR*ParameterTableBase;
}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;
其中ServiceTableBase指向系统服务程序的地址(SSDT),ParameterTableBase则指向SSPT中的参数地址,它们都包含了NumberOfService这么多个数组单元。
在windows2000sp4中NumberOfService的数目是248个。
我们的任务管理器,是通过用户层的API来枚举当前的进程的。
Ring3级枚举的方法:
?
PSAPI
–EnumProcesses()
?
ToolHelp32
–Process32First()
-Process32Next()
来对进程进行枚举。
而她们最后都是通过NtQuerySystemInformation来进行查询的。
所以我们只需要Hook掉NtQuerySystemInformation,把真实NtQuerySystemInformation返回的数进行添加或者是删改,就能有效的欺骗上层API。
从而达到隐藏特定进程的目的。
2.Hook
Windows2000中NtQuerySystemInformation在SSDT里面的索引号是0x97,所以只需要把SSDT中偏移0x97*4处把原来的一个DWORD类型的读出来保存一个全局变量中然后再把她重新赋值成一个新的Hook函数的地址,就完成了Hook。
OldFuncAddress=KeServiceDescriptorTable->ServiceCounterTableBase[0x97];
KeServiceDescriptorTable->ServiceCounterTableBase[0x97]=NewFuncAddress;
在其他系统中这个号就不一定一样。
所以必须找一种通用的办法来得到这个索引号。
在《UndocumentNt》中介绍了一种办法可以解决这个通用问题,从未有效的避免了使用硬编码。
在ntoskrnl导出的ZwQuerySystemInformation中包含有索引号的硬编码:
kd>uZwQuerySystemInformation
804011aab897000000moveax,0x97
804011af8d542404leaedx,[esp+0x4]
804011b3cd2eint2e
804011b5c21000ret0x10
所以只需要把ZwQuerySystemInformation入口处的第二个字节取出来就能得到相应的索引号了。
例如:
ID=*(PULONG)((PUCHAR)ZwQuerySystemInformation+1);
RealZwQuerySystemInformation=((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID]);
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID]=HookZwQuerySystemInformation;
3.对NtQuerySystemInformation返回的数据进行删改
NtQuerySystemInformation的原型:
NtQuerySystemInformation(
INULONGSystemInformationClass,//查询系统服务类型
INPVOIDSystemInformation,//接收系统信息缓冲区
INULONGSystemInformationLength,//接收信息缓冲区大小
OUTPULONGReturnLength);//实际接收到的大小
NtQuerySystemInformation可以对系统的很多状态进行查询,不仅仅是对进程的查询,通过SystemInformationClass号来区分功能,当SystemInformationClass等于5的时候是在进行进程的查询。
此时返回的Sy
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 隐藏 进程