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

手搓Nt*或Zw*函数,避免被hook 支持x86_x64

本帖最后由 wtujoxk 于 2024-11-14 13:57 编辑

仅学习,如有错误请指出…………
在加壳软件中,ntdll.dll里的有些API会被加壳软件hook,导致我们在做补丁无法正常执行
如:有的程序会hook NtGetContextThread和NtSetContextThread,导致无法下硬件断点,也有的会hook NtProtectVirtualMemory,无法修改内存属性,就不能修改汇编代码等……
Nt或Zw开头的函数执行的代码是一样的,他们所指向的函数地址都是同一个,具体有什么不同,请自行搜索查看,我这里使用Nt函数进行编写。
代码:#include <iostream>
#include <windows.h>

static void* lpNtdllBuffer = NULL;
ULONG_PTR GetFunctionAddressByName(const char* functionName)
{
        ULONG_PTR functionAddress = 0;
       
        //读取ntdll.dll到内存,程序运行时只读一次
        if (lpNtdllBuffer == NULL)
        {
      char dllPath;
          GetSystemDirectoryA(dllPath, MAX_PATH);
          strcat_s(dllPath, MAX_PATH, "\\ntdll.dll");//拼接系统目录ntdll.dll路径
                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);
      }
        }

        //取出导出表
        //DLL内存数据转成DOS头结构
        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpNtdllBuffer;
        //取出PE头结构
        PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)lpNtdllBuffer + pDosHeader->e_lfanew);
        //判断PE头导出表表是否为空
        if (pNtHeaders->OptionalHeader.DataDirectory.VirtualAddress == 0) return functionAddress;

        //取出导出表偏移
        ULONG_PTR FileOffset = pNtHeaders->OptionalHeader.DataDirectory.VirtualAddress;

        //取出节头结构
        PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((ULONG_PTR)pNtHeaders + sizeof(IMAGE_NT_HEADERS));
        PIMAGE_SECTION_HEADER pOldSectionHeader = pSectionHeader;
        //遍历节结构进行地址运算
        for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
        {
                if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
                {
                        FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
                        break;
                }
        }

        //导出表地址
        PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)lpNtdllBuffer + FileOffset);
        //取出导出表函数地址
        FileOffset = pExportDirectory->AddressOfFunctions;
        //遍历节结构进行地址运算
        pSectionHeader = pOldSectionHeader;
        for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
        {
                if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
                {
                        FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
                        break;
                }
        }
        PLONG AddressOfFunctions = (PLONG)((ULONG_PTR)lpNtdllBuffer + FileOffset);//这里注意一下foa和rva

        //取出导出表函数名字
        FileOffset = pExportDirectory->AddressOfNameOrdinals;

        //遍历节结构进行地址运算
        pSectionHeader = pOldSectionHeader;
        for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
        {
                if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
                {
                        FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
                        break;
                }
        }
        PUSHORT AddressOfNameOrdinals = (PUSHORT)((ULONG_PTR)lpNtdllBuffer + FileOffset);//注意一下foa和rva

        //取出导出表函数序号
        FileOffset = pExportDirectory->AddressOfNames;

        //遍历节结构进行地址运算
        pSectionHeader = pOldSectionHeader;
        for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
        {
                if (pSectionHeader->VirtualAddress <= FileOffset && FileOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
                {
                        FileOffset = FileOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
                        break;
                }
        }
        PULONG AddressOfNames = (PULONG)((ULONG_PTR)lpNtdllBuffer + FileOffset);//注意一下foa和rva

        //分析导出表
        ULONG uNameOffset;
        ULONG uOffset;
        LPSTR FunName;
        ULONG uAddressOfNames;
        //获取所有导出函数名
        for (DWORD uIndex = 0; uIndex < pExportDirectory->NumberOfNames; uIndex++, AddressOfNames++, AddressOfNameOrdinals++)
        {
                uAddressOfNames = *AddressOfNames;
                pSectionHeader = pOldSectionHeader;
                for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
                {
                        if (pSectionHeader->VirtualAddress <= uAddressOfNames && uAddressOfNames <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
                        {
                                uOffset = uAddressOfNames - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
                                break;
                        }
                }
                FunName = (LPSTR)((ULONG_PTR)lpNtdllBuffer + uOffset);

                //得到指定的函数地址
                if (!_stricmp(FunName, functionName))
                {
                        pSectionHeader = pOldSectionHeader;
                        uOffset = (ULONG)AddressOfFunctions[*AddressOfNameOrdinals];
                        for (WORD Index = 0; Index < pNtHeaders->FileHeader.NumberOfSections; Index++, pSectionHeader++)
                        {
                                //计算函数偏移地址
                                if (pSectionHeader->VirtualAddress <= uOffset && uOffset <= pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData)
                                {
                                        uNameOffset = uOffset - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
                                        break;
                                }
                        }
                        functionAddress = (ULONG_PTR)lpNtdllBuffer + uNameOffset;
                        printf("函数名称: %s, 地址:%Ix, 偏移:%Ix\n", functionName, functionAddress, uNameOffset);

                        //VirtualFree(lpNtdllBuffer, 0, MEM_RELEASE); lpNtdllBuffer = NULL;
                        return functionAddress;
                }
        }
        //VirtualFree(lpNtdllBuffer, 0, MEM_RELEASE); lpNtdllBuffer = NULL;
        return functionAddress;
}
static BYTE* lpJmpWow64TransitionBuffer = NULL;
ULONG_PTR CustomNtFunction2(const char* functionName)
{
#ifdef _WIN64
        return GetFunctionAddressByName(functionName);
#else
        if (lpJmpWow64TransitionBuffer == NULL)
        {
                // 只处理一次Wow64Transition的va地址
                char dllPath;
                GetSystemDirectoryA(dllPath, MAX_PATH);
                strcat_s(dllPath, MAX_PATH, "\\ntdll.dll");//拼接系统目录ntdll.dll路径
                //jmp ntdll.Wow64Transition 写到内存
                lpJmpWow64TransitionBuffer = (BYTE*)VirtualAlloc(NULL, sizeof(ULONG_PTR) + 2, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                lpJmpWow64TransitionBuffer = 0xFF;// jmp ntdll.Wow64Transition地址
                lpJmpWow64TransitionBuffer = 0x25;
                ULONG_PTR Wow64TransitionAddress = (ULONG_PTR)GetProcAddress(LoadLibraryA(dllPath), "Wow64Transition");
                memcpy(lpJmpWow64TransitionBuffer + 2, &Wow64TransitionAddress, sizeof(ULONG_PTR));
        }
        //写jmpWow64Transition地址的内存到functionNameAddress+6的位置
        ULONG_PTR functionNameAddress = GetFunctionAddressByName(functionName);
        memcpy((void*)(functionNameAddress + 6), &lpJmpWow64TransitionBuffer, sizeof(ULONG_PTR));

        //printf("函数名称: %s, 地址:%Ix,地址2:%Ix\n", functionName, functionNameAddress, lpJmpWow64TransitionBuffer);
        return functionNameAddress;
#endif
}
//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;

//extern "C" NTSTATUS NTAPI NtGetContextThread(HANDLE hThread, LPCONTEXT lpContext);
int main()
{
        CustomNtFunction2("ZwResumeThread");
        CustomNtFunction2("NtSuspendThread");

        HANDLE hThread = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
        CONTEXT context;
        memset(&context, 0, sizeof(CONTEXT));
        context.ContextFlags = CONTEXT_DEBUG_REGISTERS;
        //NtGetContextThread
        pNtGetContextThread = (PNtGetContextThread)CustomNtFunction2("NtGetContextThread");
        pNtGetContextThread(hThread, &context);

        //NtSetContextThread
        pNtSetContextThread = (PNtSetContextThread)CustomNtFunction2("ZwSetContextThread");
        pNtSetContextThread(hThread, &context);

        NtProtectVirtualMemory
        SIZE_T size = 1;
        ULONG OldProtect = 0;
        PVOID addr = (PVOID)GetModuleHandle(nullptr);
        pNtProtectVirtualMemory = (PNtProtectVirtualMemory)CustomNtFunction2("NtProtectVirtualMemory");
        pNtProtectVirtualMemory((HANDLE)-1, &addr, &size, PAGE_EXECUTE_READWRITE, &OldProtect);

        system("pause");
        return 0;
}

杨林 发表于 2024-11-13 09:09:17

感谢分享,学习了!

飞天梦 发表于 2024-11-13 09:29:12

谢谢分享

hahayzl 发表于 2024-11-16 23:08:20

拥抱大大大大大佬!

雪很冷 发表于 前天 08:54

                           
感谢分享
页: [1]
查看完整版本: 手搓Nt*或Zw*函数,避免被hook 支持x86_x64