wtujoxk 发表于 2024-11-6 12:09

手搓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

## 效果演示



hunfeifei 发表于 2024-11-7 08:12

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

hunfeifei 发表于 2024-11-6 15:51

感谢分享。之前也被VMP的保护坑过。做过这个。后来发现只要 openprocess 一下 ,句柄不用 -1 就可以了

lc2006 发表于 2024-11-6 12:59

哇卡哇卡哇咔咔

yaoguen 发表于 2024-11-6 13:07

多谢分享,下载试试看

wshq 发表于 2024-11-6 13:51

{:1_921:}感谢分享

hbfp 发表于 2024-11-6 14:07

收藏学习,谢谢

影风 发表于 2024-11-6 15:54

shadow api??

wuai52001 发表于 2024-11-6 16:53


感谢分享。

sherlock0209 发表于 2024-11-6 17:24

牛啊,牛啊

910jqkAAAA 发表于 2024-11-6 17:37

还得是你啊
页: [1] 2 3 4 5
查看完整版本: 手搓Nt*或Zw*函数,避免被hook 支持x86_x64