仅学习,如有错误请指出…………
在加壳软件中,ntdll.dll里的有些API会被加壳软件hook,导致我们在做补丁无法正常执行
如:有的程序会hook NtGetContextThread和NtSetContextThread,导致无法下硬件断点,也有的会hook NtProtectVirtualMemory,无法修改内存属性,就不能修改汇编代码等……
Nt或Zw开头的函数执行的代码是一样的,他们所指向的函数地址都是同一个,具体有什么不同,请自行搜索查看,我这里使用Nt函数进行编写。
推荐使用方式二
方式一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | #include <iostream>#include <windows.h> static void* lpNtdllBuffer = NULL;ULONG_PTR CustomNtFunction(const char* functionName){ ULONG_PTR functionAddress = 0; char dllPath[MAX_PATH]; 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[i].VirtualAddress && rva <= pSectionHeader[i].VirtualAddress + pSectionHeader[i].SizeOfRawData) { //找到foa地址 foaAddress = rva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].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;} //NtGetContextThreadtypedef BOOL(NTAPI* PNtGetContextThread)(HANDLE hThread, LPCONTEXT lpContext);PNtGetContextThread pNtGetContextThread; //NtSetContextThreadtypedef BOOL(CALLBACK* PNtSetContextThread)(HANDLE hThread, LPCONTEXT lpContext);PNtSetContextThread pNtSetContextThread; //NtProtectVirtualMemorytypedef 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;} |
方式二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 | #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[MAX_PATH]; 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[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) return functionAddress; //取出导出表偏移 ULONG_PTR FileOffset = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].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[MAX_PATH]; 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[0] = 0xFF; // jmp ntdll.Wow64Transition地址 lpJmpWow64TransitionBuffer[1] = 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}//NtGetContextThreadtypedef BOOL(NTAPI* PNtGetContextThread)(HANDLE hThread, LPCONTEXT lpContext);PNtGetContextThread pNtGetContextThread; //NtSetContextThreadtypedef BOOL(CALLBACK* PNtSetContextThread)(HANDLE hThread, LPCONTEXT lpContext);PNtSetContextThread pNtSetContextThread; //NtProtectVirtualMemorytypedef 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;} |
最后,实战一个VMP加壳的程序,此程序正常情况下无法修改内存属性