原文发表于百度空间,2008-7-28
==========================================================================
前面介绍了修改PEB中已加载模块的双链,来隐藏指定的模块.
不过要对付这样隐藏,一个暴力搜索内存就够了.
实现思路如下:
地址以一个页的大小为单位从0x00000000到0x7FFFFFFF遍历,检查是否具有PE特征.
页的大小可以通过GetSystemInfo()得到,结果在SYSTEM_INFO结构的DWORD dwPageSize一项中,通常是0x1000.遍历时,并不是所有地址都是可以访问的,我们还需要先得到该地址是否可以访问的信息,否则将会引发内存访问错误.获取该信息可以使用函数VirturlQuery(),在得到的MEMORY_BASIC_INFORMATION结构中有足够多的信息.
MEMORY_BASIC_INFORMATION在WinNT.h中定义如下:
typedef struct _MEMORY_BASIC_INFORMATION { PVOID BaseAddress; // 区域基地址。 PVOID AllocationBase; // 分配基地址。 DWORD AllocationProtect; // 区域被初次保留时赋予的保护属性。 SIZE_T RegionSize; // 区域大小(以字节为计量单位)。 DWORD State; // 状态(MEM_FREE、MEM_RESERVE或 MEM_COMMIT)。 DWORD Protect; // 保护属性。 DWORD Type; // 类型。 } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
其中的State指明了该地址所在页的状态,分为MEM_FREE,MEM_COMMIT和MEM_RESERVE三种情况.
仅当页状态为MEM_COMMIT时才可以访问,否则将引发访问错误.
然后就可以检验是否是PE文件了,这个通常检查MZ头和PE头即可.
实现代码如下:
void SearchMemoryForPE(void) { IMAGE_DOS_HEADER *dhead; MEMORY_BASIC_INFORMATION mbi; DWORD StartAddr=0; for (;StartAddr<0x7FFF0000;StartAddr+=dwpagesize) { VirtualQuery((PVOID)StartAddr,&mbi,sizeof(MEMORY_BASIC_INFORMATION)); if (mbi.State==MEM_COMMIT) { if ((*(char*)StartAddr=='M')&&(*((char*)StartAddr+1)=='Z')) { dhead=(IMAGE_DOS_HEADER*)StartAddr; if (!lstrcmp((char*)dhead+dhead->e_lfanew,"PE")) { printf("Found PE at 0x%08x! ",StartAddr); } } } } }
对付内核中的隐藏驱动也可以使用类似的方法,关键只在于验证地址是否可以访问.
不过对付这种暴力搜索,只需要抹去相应的特征,使检测者无法匹配即可~
附本例结果: