手搓Nt*或Zw*函数,避免被hook 支持x86_x64
本帖最后由 wtujoxk 于 2024-11-14 13:48 编辑## 仅学习,如有错误请指出…………
在加壳软件中,ntdll.dll里的有些API会被加壳软件hook,导致我们在做补丁无法正常执行
如:有的程序会hook NtGetContextThread和NtSetContextThread,导致无法下硬件断点,也有的会hook NtProtectVirtualMemory,无法修改内存属性,就不能修改汇编代码等……
Nt或Zw开头的函数执行的代码是一样的,他们所指向的函数地址都是同一个,具体有什么不同,请自行搜索查看,我这里使用Nt函数进行编写。
# 代码:
代码是这篇文章代码的优化版:DLL巧妙的绕过被VMP壳HOOK的ZwProtectVirtualMemory
```
#include <iostream>
#include <windows.h>
static void* lpNtdllBuffer = NULL;
ULONG_PTR CustomNtFunction(const char* functionName)
{
ULONG_PTR functionAddress = 0;
char dllPath;
GetSystemDirectoryA(dllPath, MAX_PATH);
strcat_s(dllPath, MAX_PATH, "\\ntdll.dll");//拼接系统目录ntdll.dll路径
HMODULE dllHandle = LoadLibraryA(dllPath);
ULONG_PTR apiAddress = (ULONG_PTR)GetProcAddress(dllHandle, functionName);
//读取ntdll.dll到内存,程序运行时只读一次
if (lpNtdllBuffer == NULL)
{
HANDLE hFile = CreateFileA(dllPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
DWORD dwBytesRead = 0;
DWORD dwSize = GetFileSize(hFile, NULL);
if (dwSize == INVALID_FILE_SIZE || dwSize == 0) return functionAddress;
lpNtdllBuffer = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ReadFile(hFile, lpNtdllBuffer, dwSize, &dwBytesRead, NULL);
CloseHandle(hFile);
}
}
//通过apiAddress地址获取函数的foa地址
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpNtdllBuffer;
//取出PE头结构
PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + ((ULONG_PTR)pDosHeader));
//取出节头结构
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
ULONG_PTR foaAddress = 0;
//Nt函数地址 - ntdll基址 = rva
ULONG_PTR rva = apiAddress - (ULONG_PTR)dllHandle;
for (WORD i = 0; i < pNtHeaders->FileHeader.NumberOfSections; ++i)
{
if (rva >= pSectionHeader.VirtualAddress && rva <= pSectionHeader.VirtualAddress + pSectionHeader.SizeOfRawData)
{
//找到foa地址
foaAddress = rva - pSectionHeader.VirtualAddress + pSectionHeader.PointerToRawData;
break;
}
}
#ifndef _WIN64// x86位8、9位foa值与va值不同,要特殊处理
memcpy((PVOID)((ULONG_PTR)lpNtdllBuffer + foaAddress + 6), (PVOID)(apiAddress + 6), 6);
#endif
functionAddress = (ULONG_PTR)lpNtdllBuffer + foaAddress;
printf("函数名称: %s, 地址:%Ix, 偏移:%Ix\n", functionName, functionAddress, foaAddress);
//VirtualFree(lpNtdllBuffer, 0, MEM_RELEASE); lpNtdllBuffer = NULL;
return functionAddress;
}
//NtGetContextThread
typedef BOOL(NTAPI* PNtGetContextThread)(HANDLE hThread, LPCONTEXT lpContext);
PNtGetContextThread pNtGetContextThread;
//NtSetContextThread
typedef BOOL(CALLBACK* PNtSetContextThread)(HANDLE hThread, LPCONTEXT lpContext);
PNtSetContextThread pNtSetContextThread;
//NtProtectVirtualMemory
typedef BOOL(NTAPI* PNtProtectVirtualMemory)(HANDLE hProcess, PVOID* lpAddress, PSIZE_T dwSize, ULONG flNewProtect, PULONG lpflOldProtect);
PNtProtectVirtualMemory pNtProtectVirtualMemory;
int main()
{
CustomNtFunction("ZwResumeThread");
CustomNtFunction("NtSuspendThread");
HANDLE hThread = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
//NtGetContextThread
pNtGetContextThread = (PNtGetContextThread)CustomNtFunction("NtGetContextThread");
pNtGetContextThread(hThread, &context);
//NtSetContextThread
pNtSetContextThread = (PNtSetContextThread)CustomNtFunction("ZwSetContextThread");
pNtSetContextThread(hThread, &context);
//NtProtectVirtualMemory
SIZE_T size = 1;
ULONG OldProtect = 0;
PVOID addr = (PVOID)GetModuleHandle(nullptr);
pNtProtectVirtualMemory = (PNtProtectVirtualMemory)CustomNtFunction("NtProtectVirtualMemory");
pNtProtectVirtualMemory((HANDLE)-1, &addr, &size, PAGE_EXECUTE_READWRITE, &OldProtect);
system("pause");
return 0;
}
```
## 最后,实战一个VMP加壳的程序,此程序正常情况下无法修改内存属性
程序下载地址:https://nns.lanzouu.com/iiMLu2ee6sxi
## 效果演示
goodluckwxl 发表于 2024-11-6 22:02
愿闻其详,我没有明白
正常的 VirtualProtect(a,b,c,d)里面是调用的 VirtualProtectEx 句柄 传递的-1,类似VirtualProtectEx(-1,a,b,c,d),然后调用NtProtectVirtualMemory
而VMP 会在 NtProtectVirtualMemory
挂上HOOK 做一些检测 ,猜测估计就是判断句柄是否为 -1 ,如果是 -1 则检测 内存是否为 被保护的exe或者dll 的内存地址。如果 是则返回失败。
但是如果 我们openprocess一下 自己的进程。 HANDLE hProc =OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());获取 进程句柄,不传-1。 然后手动调用 VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
就可以过掉这个检测。封装一下就是这样。
static BOOL WINAPI _VirtualProtect(
LPVOID lpAddress,
DWORD dwSize,
DWORD flNewProtect,
PDWORD lpflOldProtect)
{
static HANDLE hProcess = NULL;
if (hProcess==NULL) {
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
}
return VirtualProtectEx(hProcess, lpAddress, dwSize, flNewProtect, lpflOldProtect);
} 感谢分享。之前也被VMP的保护坑过。做过这个。后来发现只要 openprocess 一下 ,句柄不用 -1 就可以了 哇卡哇卡哇咔咔 多谢分享,下载试试看 {:1_921:}感谢分享 收藏学习,谢谢 shadow api??
感谢分享。 牛啊,牛啊 还得是你啊