仅学习,如有错误请指出…………

在加壳软件中,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;
}
  
//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;
}

方式二:

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

最后,实战一个VMP加壳的程序,此程序正常情况下无法修改内存属性

上传的附件 Test.vmp.zip
ntdll.h.zip

最新回复:
天山飞雪 2024-11-06 15:19:53
太强大了 谢谢分享
superlover 2024-11-06 15:33:30
很强大,感谢分享
tomtory 2024-11-06 16:25:47
很好狠强大,感谢分享
穗之影 2024-11-06 17:42:03
看看
樂樂 2024-11-06 18:44:32
感谢分享  学习学习
hqfdsyj 2024-11-06 19:25:58
感谢分享
X小浪X 2024-11-06 19:30:08
很好狠强大,感谢分享
haogl 2024-11-06 20:16:47
回复来看看,先谢过楼主!
熊趴趴来 2024-11-06 20:31:27
1