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