作者:fleshwound(fleshwound@smatrix.org) http://www.smatrix.org 来源:安全焦点
1 引言 近些年来,恶意代码依赖一些特殊的Native API函数和内核系统函数进行感染、传播、隐藏的这种趋势愈加明显代码,并大量的使用了多重加密壳、驱动关联壳、变形壳等代码保护机制和多态和变形等新的技术。传统的恶意代码查杀技术遭到了严重的挑战。 恶意代码开发者想尽了各种办法,对进程、文件、注册表、系统服务、网络服务等各方面信息进行了控制,内核级的恶意代码做得更加巧妙和隐蔽。从技术上进行分类,恶意代码使用的技术手段可以分为:(1)用户模式系统调用劫持;(2)核心模式系统调用劫持;(3)核心模式数据篡改;(4)核心模式中断处理程序劫持。 2 对进程信息的控制 大家知道,windows操作系统给我们提供了两套公开的API函数来获得进程信息:一套是PSAPI,通过EnumProcesses()函数来枚举进程,另一套是ToolHelp32,通过Process32First()和Process32Next()函数来获得整个进程列表.现在运行在用户层的恶意代码一般都会让自己在系统进程列表中消失,原理是HOOK上述进程相关API函数,将自身进程从最后的进程列表中去除,但这样做一般很容易发现。如图2所示,PSAPI和ToolHelp32都是通过调用位于ntdll.dll中的一个native API函数NtQuerySystemInformation()获取进程信息,而函数NtQuerySystemInformation()是依靠内核函数ExpGetProcessInformation()遍历ActiveProcessLinks,通过EPROCESS结构(其中包含 ActiveProcessLinks项,类型为LIST_ENTRY)获得真实进程列表信息。整个环节中,内核态的恶意代码可以试图通过HOOK函数 NtQuerySystemInformation()和函数ExpGetProcessInformation(),将自己的相关进程从返回结果中去除或者直接从ActiveProcessLinks摘除自身相关进程信息, 即要把要隐藏的进程的EPROCESS从LIST_ENTRY中摘除。另外几乎所有的反病毒查杀软件和系统进程软件使用 PsSetCreateProcessNotifyRoutine()来监视进程创建和销毁,但是令人遗憾的是该函数最多只能设置8个回调函数,因此有不少内核级的恶意代码被动隐藏自身相关进程的时候,还主动得销毁掉其它进程监控软件的使用的回调函数,让进程监控失效。 3 对文件和注册表信息的控制 内核恶意代码往往要对自身宿主文件和所在目录进行隐藏,达到保护自身的目地。在NT内核操作系统中,与文件和目录相关的API函数基本上都是调用 ntdll.dll中的几个Native API函数NtQueryVolumeInformationFile、NtQueryDirectoryFile、NtCreateFile、 NtVdmControl(对应内核态下为ZwQueryVolumeInformationFile、ZwQueryDirectoryFile、 ZwCreateFile、ZwVdmControl),因此许多内核级的恶意代码基本上都会HOOK上面的这些函数。我们以HOOK函数 ZwQueryDirectoryFile为例来阐明实现的基本原理:自己定义一个HookZwQueryDirectoryFile函数来处理一个名为 alice的文件名,调用老的ZwQueryDirectoryFile获取真实文件和目录信息列表,然后在这个文件和目录信息列表中删除我们要隐藏的 “alice”相关信息,最后将加工后的结果返回。对注册表的信息的控制也是采用同样的方法,对ntdll.dll中的NtEnumerateKey 、NtEnumerateValueKey或者它们的内核版本ZwEnumerateKey 、ZwEnumerateValueKey进行HOOK实现隐藏注册表。 //示例程序:自定义HOOK函数 ZWQUERYDIRECTORYFILE pOldZwQueryDirectoryFile;//定义指向ZwQueryDirectoryFile函数的指针 NTSTATUS NTAPI HookZwQueryDirectoryFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID FileInformation, IN ULONG FileInformationLength, IN FILE_INFORMATION_CLASS FileInformationClass, IN BOOLEAN ReturnSingleEntry, IN PUNICODE_STRING FileName OPTIONAL, IN BOOLEAN RestartScan) { NTSTATUS dwStAtus; //返回状态 PFILE_BOTH_DIRECTORY_INFORMATION lpInfo; //定义一个文件目录信息结构 //调用老的ZwQueryDirectoryFile获取文件和目录信息列表 dwStAtus=pOldZwQueryDirectoryFile( FileHandle, Event, ApcRoutine, ApcContext , IoStatusBlock, FileInformation, FileInformationLength, FileInformationClass, ReturnSingleEntry, FileName, RestartScan); //以下在文件和目录信息列表中“过滤”我们要隐藏的“alice”,将加工后的结果返回 if (FileInformationClass == FileBothDirectoryInformation) { if (dwStAtus == STATUS_SUCCESS) { lpInfo = (PFILE_BOTH_DIRECTORY_INFORMATION)FileInformation; //文件信息存在lpInfo结构 if (RtlCompareMemory(lpInfo->FileName,L"alice",10) == 10) { if ((ULONG)(lpInfo->NextEntryOffset) != 0) { FileInformationLength -= (ULONG)(lpInfo->NextEntryOffset); RtlCopyMemory(lpInfo, (PFILE_BOTH_DIRECTORY_INFORMATION)((ULONG)lpInfo+(ULONG)(lpInfo->NextEntryOffset)), FileInformationLength-(((ULONG)lpInfo+(ULONG)(lpInfo->NextEntryOffset))-(ULONG)FileInformation)); } else { FileInformationLength -= (ULONG)(lpInfo->NextEntryOffset); return dwStAtus; } //此时返回状态码STATUS_NO_SUCH_FILE; }
[1] [2] 下一页 |