libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
hooks.cpp
Go to the documentation of this file.
1#include "peconv/hooks.h"
2
3#include "peconv/logger.h"
5#include "peconv/peb_lookup.h"
6#include "peconv/util.h"
7
8#include <algorithm>
9#include <cctype>
10
11using namespace peconv;
12
13namespace peconv {
14
15 bool is_pointer_in_ntdll(LPVOID lpAddress)
16 {
17 HMODULE mod = peconv::get_module_via_peb(L"ntdll.dll");
18 size_t module_size = peconv::get_module_size_via_peb(mod);
19 if (peconv::validate_ptr(mod, module_size, lpAddress, sizeof(BYTE))) {
20 return true; //this address lies within NTDLL
21 }
22 return false;
23 }
24
25 BOOL nt_protect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
26 {
27 FARPROC proc = peconv::get_exported_func(
28 peconv::get_module_via_peb(L"ntdll.dll"),
29 "NtProtectVirtualMemory"
30 );
31 if (!proc) {
32 return FALSE;
33 }
34 NTSTATUS(NTAPI *_NtProtectVirtualMemory)(
35 IN HANDLE,
36 IN OUT PVOID*,
37 IN OUT PSIZE_T,
38 IN DWORD,
39 OUT PDWORD) =
40 (NTSTATUS(NTAPI *)(
41 IN HANDLE,
42 IN OUT PVOID*,
43 IN OUT PSIZE_T,
44 IN DWORD,
45 OUT PDWORD)) proc;
46
47 SIZE_T protect_size = dwSize;
48 NTSTATUS status = _NtProtectVirtualMemory(GetCurrentProcess(), &lpAddress, &protect_size, flNewProtect, lpflOldProtect);
49 if (status != S_OK) {
50 return FALSE;
51 }
52 return TRUE;
53 }
54};
55
56bool PatchBackup::makeBackup(BYTE *patch_ptr, size_t patch_size)
57{
58 if (!patch_ptr) {
59 return false;
60 }
62 this->sourcePtr = patch_ptr;
63 this->buffer = new BYTE[patch_size];
64 this->bufferSize = patch_size;
65
66 ::memcpy(buffer, patch_ptr, patch_size);
67 return true;
68}
69
71{
72 if (!isBackup()) {
73 return false;
74 }
75 DWORD oldProtect = 0;
76 if (!nt_protect((LPVOID)sourcePtr, bufferSize, PAGE_EXECUTE_READWRITE, &oldProtect)) {
77 return false;
78 }
79 ::memcpy(sourcePtr, buffer, bufferSize);
80 if (!nt_protect((LPVOID)sourcePtr, bufferSize, oldProtect, &oldProtect)) {
81 LOG_WARNING("Failed to restore protection of region: %p", sourcePtr);
82 }
83
84 //flush cache:
85 FlushInstructionCache(GetCurrentProcess(), sourcePtr, bufferSize);
86 return true;
87}
88
89void peconv::hooking_func_resolver::replace_dll(std::string dll_name, std::string new_dll)
90{
91 std::transform(new_dll.begin(), new_dll.end(), new_dll.begin(), ::tolower);
92 std::transform(dll_name.begin(), dll_name.end(), dll_name.begin(), ::tolower);
93 dll_replacements_map[dll_name] = new_dll;
94}
95
96FARPROC peconv::hooking_func_resolver::resolve_func(LPCSTR lib_name, LPCSTR func_name)
97{
98 //the name may be ordinal rather than string, so check if it is a valid pointer:
99 if (!peconv::is_bad_read_ptr(func_name, 1)) {
100 std::map<std::string, FARPROC>::const_iterator itr = hooks_map.find(func_name);
101 if (itr != hooks_map.end()) {
102 FARPROC hook = itr->second;
103 LOG_DEBUG("Replacing: %s by: %p.", func_name, hook);
104 return hook;
105 }
106 }
107 // resolve replacement DLLs
108 std::string lib_name_str = peconv::is_bad_read_ptr(lib_name, 1) ? "": lib_name;
109 std::transform(lib_name_str.begin(), lib_name_str.end(), lib_name_str.begin(), ::tolower);
110 std::map<std::string, std::string>::const_iterator itr2 = this->dll_replacements_map.find(lib_name_str);
111 if (itr2 != dll_replacements_map.end()) {
112 const std::string &name = itr2->second;
113 LOG_DEBUG("Replacing DLL: %s by: %s.", lib_name_str.c_str(), name.c_str());
114 lib_name_str = name;
115 }
116 return peconv::default_func_resolver::resolve_func(lib_name_str.c_str(), func_name);
117}
118
119size_t peconv::redirect_to_local64(void *ptr, ULONGLONG new_offset, PatchBackup* backup)
120{
121 if (!ptr) return 0;
122
123 BYTE hook_64[] = {
124 0x48, 0xB8, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xEE, 0xFF, //movabs rax,FFEE998877665544
125 0xFF, 0xE0 //jmp rax
126 };
127 const size_t hook64_size = sizeof(hook_64);
128 if (is_pointer_in_ntdll(ptr)) {
129 LOG_WARNING("Patching NTDLL is not allowed because of possible stability issues.");
130 return 0;
131 }
132 DWORD oldProtect = 0;
133 if (!nt_protect((LPVOID)ptr,
134 hook64_size,
135 PAGE_EXECUTE_READWRITE, //this must be executable if we are hooking kernel32.dll, because we are using VirtualProtect from kernel32 at the same time
136 &oldProtect))
137 {
138 return 0;
139 }
140
141 if (backup != nullptr) {
142 backup->makeBackup((BYTE*)ptr, hook64_size);
143 }
144 ::memcpy(hook_64 + 2, &new_offset, sizeof(ULONGLONG));
145 ::memcpy(ptr, hook_64, hook64_size);
146
147 if (!nt_protect((LPVOID)ptr, hook64_size, oldProtect, &oldProtect)) {
148 LOG_WARNING("Failed to restore protection of region: %p", ptr);
149 }
150
151 //flush cache:
152 FlushInstructionCache(GetCurrentProcess(), ptr, hook64_size);
153 return hook64_size;
154}
155
156size_t peconv::redirect_to_local32(void *ptr, DWORD new_offset, PatchBackup* backup)
157{
158 if (!ptr) return 0;
159
160 BYTE hook_32[] = {
161 0xB8, 0xCC, 0xDD, 0xEE, 0xFF, // mov eax,FFEEDDCC
162 0xFF, 0xE0 //jmp eax
163 };
164 const size_t hook32_size = sizeof(hook_32);
165 if (is_pointer_in_ntdll(ptr)) {
166 LOG_WARNING("Patching NTDLL is not allowed because of possible stability issues.");
167 return 0;
168 }
169 DWORD oldProtect = 0;
170 if (!nt_protect((LPVOID)ptr,
171 hook32_size,
172 PAGE_EXECUTE_READWRITE, //this must be executable if we are hooking kernel32.dll, because we are using VirtualProtect from kernel32 at the same time
173 &oldProtect))
174 {
175 return 0;
176 }
177
178 if (backup != nullptr) {
179 backup->makeBackup((BYTE*)ptr, hook32_size);
180 }
181 ::memcpy(hook_32 + 1, &new_offset, sizeof(DWORD));
182 ::memcpy(ptr, hook_32, hook32_size);
183
184 if (!nt_protect((LPVOID)ptr, hook32_size, oldProtect, &oldProtect)) {
185 LOG_WARNING("Failed to restore protection of region: %p", ptr);
186 }
187
188 //flush cache:
189 FlushInstructionCache(GetCurrentProcess(), ptr, hook32_size);
190 return hook32_size;
191}
192
193size_t peconv::redirect_to_local(void *ptr, void* new_function_ptr, PatchBackup* backup)
194{
195#ifdef _WIN64
196 return peconv::redirect_to_local64(ptr, (ULONGLONG)new_function_ptr, backup);
197#else
198 return peconv::redirect_to_local32(ptr, (DWORD)new_function_ptr, backup);
199#endif
200}
201
202inline long long int get_jmp_delta(ULONGLONG currVA, int instrLen, ULONGLONG destVA)
203{
204 long long int diff = destVA - (currVA + instrLen);
205 return diff;
206}
207
208inline bool is_valid_delta(long long int delta)
209{
210 DWORD first_dw = delta >> sizeof(DWORD) * 8;
211 if (first_dw == 0) {
212 return true;
213 }
214 const DWORD max_dword = DWORD(-1);
215 if (first_dw != max_dword) {
216 return false;
217 }
218 DWORD delta_dw = DWORD(delta);
219 if (delta_dw & 0x80000000) {
220 return true;
221 }
222 //invalid, sign bit is missing
223 return false;
224}
225
226bool peconv::replace_target(BYTE *patch_ptr, ULONGLONG dest_addr)
227{
228 typedef enum {
229 OP_JMP = 0xE9,
230 OP_CALL_DWORD = 0xE8
231 } t_opcode;
232
233 if (patch_ptr[0] == OP_JMP || patch_ptr[0] == OP_CALL_DWORD) {
234 ULONGLONG delta = get_jmp_delta(ULONGLONG(patch_ptr), 5, dest_addr);
235 if (!is_valid_delta(delta)) {
236 LOG_WARNING("Cannot replace the target: delta 0x%llx is too large for a DWORD.", (unsigned long long)delta);
237 //too big delta, cannot be saved in a DWORD
238 return false;
239 }
240 DWORD delta_dw = DWORD(delta);
241 ::memcpy(patch_ptr + 1, &delta_dw, sizeof(DWORD));
242
243 //flush cache:
244 FlushInstructionCache(GetCurrentProcess(), patch_ptr + 1, sizeof(DWORD));
245 return true;
246 }
247 return false;
248}
size_t bufferSize
Definition: hooks.h:69
bool applyBackup()
Definition: hooks.cpp:70
BYTE * sourcePtr
Definition: hooks.h:70
BYTE * buffer
Definition: hooks.h:68
void deleteBackup()
Definition: hooks.h:39
bool makeBackup(BYTE *patch_ptr, size_t patch_size)
Definition: hooks.cpp:56
bool isBackup()
Definition: hooks.h:62
virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name)
void replace_dll(std::string dll_name, std::string new_dll)
Definition: hooks.cpp:89
virtual FARPROC resolve_func(LPCSTR lib_name, LPCSTR func_name)
Definition: hooks.cpp:96
Searching specific functions in PE's Exports Table.
long long int get_jmp_delta(ULONGLONG currVA, int instrLen, ULONGLONG destVA)
Definition: hooks.cpp:202
bool is_valid_delta(long long int delta)
Definition: hooks.cpp:208
Functions related to hooking the loaded PE. Reditecting/replacing a functions with another.
Compile-time configurable logging macros for peconv.
#define LOG_DEBUG(fmt,...)
Definition: logger.h:80
#define LOG_WARNING(fmt,...)
Definition: logger.h:68
HMODULE get_module_via_peb(IN OPTIONAL LPCWSTR module_name=nullptr)
Definition: peb_lookup.cpp:108
BOOL nt_protect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect)
Definition: hooks.cpp:25
bool validate_ptr(IN const void *buffer_bgn, IN size_t buffer_size, IN const void *field_bgn, IN size_t field_size)
Definition: buffer_util.cpp:9
bool is_pointer_in_ntdll(LPVOID lpAddress)
Definition: hooks.cpp:15
size_t get_module_size_via_peb(IN OPTIONAL HMODULE hModule=nullptr)
Definition: peb_lookup.cpp:138
FARPROC get_exported_func(LPVOID modulePtr, LPCSTR wanted_name)
bool is_bad_read_ptr(LPCVOID areaStart, SIZE_T areaSize)
Definition: util.cpp:156
bool replace_target(BYTE *ptr, ULONGLONG dest_addr)
Definition: hooks.cpp:226
size_t redirect_to_local32(void *ptr, DWORD new_offset, PatchBackup *backup=nullptr)
Definition: hooks.cpp:156
size_t redirect_to_local64(void *ptr, ULONGLONG new_offset, PatchBackup *backup=nullptr)
Definition: hooks.cpp:119
size_t redirect_to_local(void *ptr, void *new_function_ptr, PatchBackup *backup=nullptr)
Definition: hooks.cpp:193
Functions for retrieving process information from PEB.
Miscellaneous utility functions.