Repository navigation
Expand file tree
/
Copy pathexternal_process.hpp
More file actions
540 lines (501 loc) · 21 KB
/
Copy pathexternal_process.hpp
File metadata and controls
540 lines (501 loc) · 21 KB
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
/**
* @file external_process.hpp
*
* @brief Declaration of ExternalProcess class. This class provides
* functionality for interacting with external Win32 processess.
*
*/
#ifndef EXTERNAL_PROCESS_HPP
#define EXTERNAL_PROCESS_HPP
#include <cstdint>
#include <unordered_map>
namespace P1ExternalProcess
{
enum class enInjectionType
{
EIT_JMP = 1,
EIT_PUSHRET,
};
enum enVirtualProtect
{
NOACCESS = 0b0001,
READ = 0b0010,
WRITE = 0b0100,
EXECUTE = 0b1000,
READ_EXECUTE = READ | EXECUTE,
READ_WRITE = READ | WRITE,
READ_WRITE_EXECUTE = READ | WRITE | EXECUTE
};
class ExternalProcess
{
public:
/**
* @brief This constructor takes a process id @process_id, gets the process
* handle using OpenProcess method and assigns it to @_handle field.
*
* @param process_id Process id.
*/
ExternalProcess(uint32_t process_id);
/**
* @brief This constructor takes a process name @process_name, gets the
* process id using @get_process_id_by_process_name method and
* delegates object creation to the constructor that takes a process
* id.
*
* @param process_name Process name.
*/
ExternalProcess(const char *process_name);
/**
* @brief Removes all created external callers and injected code from remote
* process memory. Frees all memory allocated in remote process.
* Closes process handle.
*/
~ExternalProcess();
/**
* @brief Reads @size bytes at @address address of the external process.
* Writes the result to @out_result.
*
* @param address Address to read from.
* @param size Number of bytes to read.
* @param out_result Buffer to write the result to.
*/
void read_buf(uintptr_t address, size_t size, void *out_result) const;
/**
* @brief Writes @size bytes from @data buffer to the external process at
* @address address.
*
* @param address Address to write to.
* @param size Number of bytes to write.
* @param data Buffer to read data from.
*/
void write_buf(uintptr_t address, size_t size, const void *data) const;
/**
* @brief Allocates @size bytes in the external process.
*
* If allocation is successful, adds an entry to the @_allocated_memory map,
* where the key is the address where the memory was allocated, the value is
* the number of allocated bytes.
*
* @param size Number of bytes to allocate.
*
* @return
* Address of the allocated memory if success.
* NULL if fail.
*/
uintptr_t alloc(const size_t size);
/**
* @brief Frees allocated region located at @address address in the external
* process address space.
*
* Also, removes an entry from the @_allocated_memory map, where the key is
* @address.
*
* @param address Address of the region to free.
*/
void free(uintptr_t address);
/**
* @brief Changes the protection on a region of committed pages in the
* virtual address space of the calling process.
*
* @param address Region address.
* @param size Region size.
* @param type Protection type.
*/
void set_virtual_protect(uintptr_t address, size_t size,
enVirtualProtect type);
/**
* @brief Restores the original protection on a region of committed pages in
* the virtual address space of the calling process.
*
* @param address Region address.
*/
void restore_virtual_protect(uintptr_t address);
/**
* @brief Return an address of the module named @module_name (if exists).
*
* @param module_name Module name.
*
* @return
* Non-zero module address if success.
* 0 if failire.
*/
uintptr_t get_module_address(const char *module_name);
/**
* @brief Calls a function at @address address with @args arguments using
* 'cdecl' call convention.
*
* Allocates a buffer with execution rights in the remote process.
* Writes to this buffer a set of x86 instructions that call a function at
* @address with @args arguments using 'cdecl' call convention.
* Creates a thread that executes the code in this buffer.
* Waits for this thread to finish executing.
* Returns a value returned by the function at the @address address (or
* value of EAX register for void functions).
*
* @param address An address of the function to be called.
* @param argc A number of arguments that the function takes.
* @param args Function arguments (if any).
*
* @return
* A value returned by the called function (or a value of EAX register for
* functions of 'void' type).
*/
uintptr_t call_cdecl_function(uintptr_t address, size_t argc, ...);
/**
* @brief Calls a function at @address address with @args arguments using
* 'stdcall' call convention.
*
* Allocates a buffer with execution rights in the remote process.
* Writes to this buffer a set of x86 instructions that call a function at
* @address with @args arguments using 'stdcall' call convention.
* Creates a thread that executes the code in this buffer.
* Waits for this thread to finish executing.
* Returns a value returned by the function at the @address address (or
* value of EAX register for void functions).
*
* @param address An address of the function to be called.
* @param argc A number of arguments that the function takes.
* @param args Function arguments (if any).
*
* @return
* A value returned by the called function (or a value of EAX register
* for functions of 'void' type).
*/
uintptr_t call_stdcall_function(uintptr_t address, size_t argc, ...);
/**
* @brief Calls a function at @address address with @args arguments using
* 'thiscall' call convention.
*
* Allocates a buffer with execution rights in the remote process.
* Writes to this buffer a set of x86 instructions that call a function at
* @address with @args arguments using 'thiscall' call convention.
* Creates a thread that executes the code in this buffer.
* Waits for this thread to finish executing.
* Returns a value returned by the function at the @address address (or
* value of EAX register for void functions).
*
* @param address An address of the function to be called.
* @param this_ptr A pointer to an object for which the @ec caller will
* call method at @ec.function_address address.
* @param argc A number of arguments that the function takes.
* @param args Function arguments (if any).
*
* @return A value returned by the called function (or a value of EAX
* register for functions of 'void' type).
*/
uintptr_t call_thiscall_function(uintptr_t address, uintptr_t this_ptr,
size_t argc, ...);
/**
* @brief Injects @bytes_size bytes of code into the external process at
* @address address.
*
* @param address An address where code should be injected.
* @param bytes Code bytes to be injected at @address
* address.
* @param bytes_size Number of the bytes to be injected.
* @param overwrite_bytes_size Number of original bytes to be overwritten
* and executed after injected code.
* @param it Type of transition to the injected code.
*/
void inject_code(uintptr_t address, const uint8_t *bytes, size_t bytes_size,
size_t overwrite_bytes_size, enInjectionType it);
/**
* @brief Removes code injected at @address address (if any), restores the
* original bytes overwritten by the injector.
*
* @param address An address where the code was injected.
*/
void uninject_code(uintptr_t address);
/**
* @brief Patches the external process memory with @size bytes of @bytes
* code at @address address.
*
* @param address An address where the code should be patched.
* @param bytes Code bytes to be patched at @address address.
* @param size Number of the bytes to be patched.
*/
void patch(uintptr_t address, const uint8_t *bytes, size_t size);
/**
* @brief Searches for a sequence of bytes in the external process.memory.
*
* @param address The address from which to start the search.
* @param size The size of the memory area in which to search for the
* signature.
* @param signature The byte sequence to be found.
* @param mask The mask to search by. The character 'x' is a complete
* match. Characters other than 'x' ignore the
* corresponding byte.
*
* @return The address of the first occurrence of @signature by mask @mask
* in memory area [@address ... @address + @size), or zero on unsuccessful
* search.
*/
uintptr_t find_signature(uintptr_t address, size_t size,
const uint8_t *signature, const char *mask) const;
/** @brief Reads a value of type T from the specified address in the
* external process' memory.
*
* @tparam T A type of the value to be read.
* @param address An address to read the value from.
*
* @return A value read from the specified address.
*/
template <typename T> T read(uintptr_t address) const;
/** @brief Writes a value of type T to the specified address in the
* external process' memory.
*
* @tparam T A type of the value to be written.
* @param address An address to write the value to.
* @param data A value to be written.
*/
template <typename T> void write(uintptr_t address, const T &data) const;
private:
enum class enCallConvention
{
ECC_CDECL = 1,
ECC_STDCALL,
ECC_THISCALL
};
struct VirtualProtect
{
size_t size;
uint32_t original_protect;
};
struct ExternalCaller
{
uintptr_t function_address;
uintptr_t caller_address;
size_t argc;
enCallConvention cc;
};
struct InjectedCodeInfo
{
size_t injected_bytes_number;
size_t overwritten_bytes_number;
uintptr_t allocated_buffer;
};
/**
* @brief Return process id of process named @process_name (if one is
* running).
*
* @param process_name Process name.
*
* @return
* Process id if success.
* 0 if failire.
*/
uint32_t get_process_id_by_process_name(const char *process_name) const;
/**
* @brief Adds debug access rights to the external process.
*
* // TODO: Test.
*/
void provide_debug_access(void);
/**
* @brief Allocates and initializes a buffer in a remote process for calling
* functions using'cdecl' call convention.
*
* Creates a buffer with execution rights in the external process. Writes to
* this buffer a set of i686 instructions that:
* - push arguments in the amount of @argc onto the stack
* - call function at @address using 'cdecl' call convention
* - restore stack
* - return
*
* cdecl-function caller structure:
* BYTES INSTRUCTION SIZE COMMENT
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the last arg. value
* ...
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the 2-nd arg. value
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the 1-st arg. value
* E8 XXXXXXXX call XXXXXXXX 5 XXXXXXXX = function address
* 83 C4 XX add esp, XX 3 Restore stack. XX = 4 * @argc
* C3 ret 1 Return (terminate thread)
*
* @param address An address of a function to be called.
* @param argc A number of arguments that the function takes.
*
* @return Address where the caller is placed in the external process.
*/
uintptr_t build_cdecl_caller(uintptr_t address, size_t argc);
/**
* @brief Allocates and initializes a buffer in a remote process for calling
* functions using 'stdcall' call convention.
*
* Creates a buffer with execution rights in the external process. Writes to
* this buffer a set of i686 instructions that:
* - push arguments in the amount of @argc onto the stack
* - call function at @address using 'stdcall' call convention
* - return
*
* stdcall-function caller structure:
* BYTES INSTRUCTION SIZE COMMENT
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the last arg. value
* ...
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the 2-nd arg. value
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the 1-st arg. value
* E8 XXXXXXXX call XXXXXXXX 5 XXXXXXXX = function address
* C3 ret 1 Return (terminate the thread)
*
* @param address An address of the function to be called.
* @param argc A number of arguments that the function takes.
*
* @return Address where the caller is placed in the external process.
*/
uintptr_t build_stdcall_caller(uintptr_t address, size_t argc);
/**
* @brief Allocates and initializes a buffer in a remote process for calling
* functions using MSVC 'thiscall' call convention.
*
* Creates a buffer with execution rights in the external process. Writes
* to this buffer a set of i686 instructions that:
* - push arguments in the amount of @argc onto the stack
* - put 'this' for which a method at @address is called into EAX register
* - call class method at @address using 'stdcall' call convention
* - return
*
* thiscall-function caller structure:
* BYTES INSTRUCTION SIZE COMMENT
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the last arg. value
* ...
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the 2-nd arg. value
* 68 XXXXXXXX push XXXXXXXX 5 XXXXXXXX = the 1-st arg. value
* B9 XXXXXXXX mov ecx, XXXXXXXX 5 XXXXXXXX = 'this'
* E8 XXXXXXXX call XXXXXXXX 5 XXXXXXXX = function address
* C3 ret 1 Return (terminate the thread)
*
* @param address An address of the function to be called.
* @param argc A number of arguments that the function takes.
*
* @return Address where the caller is placed in the external process.
*/
uintptr_t build_thiscall_caller(uintptr_t address, size_t argc);
/**
* @brief
* Writes arguments @args with which the function should be called. Since
* instructions for pushing arguments on stack for all implemented callers
* (cdecl, thiscall, stdcall) are at the very beginning, this function is
* universal and applicable to callers of all call supported call
* conventions.
*
* @param ec A caller to specify arguments for.
* @param args Arguments.
*/
void send_external_caller_arguments(const ExternalCaller &ec,
uintptr_t args, ...);
/**
* @brief
* Puts @this_ptr in caller @ec. The ffset is calculated as follows:
* @ec.argc * 5 is a push args instructions size. +1 is a MOV ECX
* instruction size. It must be followed by @this_ptr.
*
* @param ec A caller to specify class object ptr for.
* @param this_ptr A pointer to an object for which the @ec caller will
* call method at @ec.function_address address.
*/
void send_thiscall_this_ptr(const ExternalCaller &ec, uintptr_t this_ptr);
/**
* @brief Calls a function using information from a caller.
*
* Creates a thread in remote process. This thread executes the code located
* in @ec.caller_address buffer. Waits for this thread to finish executing.
* Returns a value returned by the function at the @ec.function+address
* address (or a value of EAX register for void functions).
*
* @param eс Caller whose function is to be called.
*
* @return A value returned by the called function (or a value of EAX
* register for void functions.
*/
uintptr_t call_external_function(const ExternalCaller &ec) const;
/**
* @brief Injects @bytes_size bytes of code from the @bytes argument into
* remote process at @address address.
*
* Logic:
* Allocates a @allocated_buf buffer in a remote process with execute
* permissions.
* Writes @bytes injected code to it. Writes an unconditional jump
* instruction 'JMP'to @allocated_buf at @address. This JMP instruction
* overwrites 5 bytes at @address. These 5 bytes are recovered in the
* @allocated_buf buffer after the injected code.
* There are cases where the fifth byte (@address+4) is not the last byte of
* the instruction. In this case, the number of overwritten bytes must be
* specified explicitly (argument @overwrite_bytes_size) and all of them
* will be executed in the @allocated_buf buffer after the injected code.
* If @overwrite_bytes_size is greater than 5, then to save addressing, the
* first (@overwrite_bytes_size - 5) bytes at @address will be replaced with
* nops (0x90).
* In the example below, there is a situation when it is impossible to write
* a JMP instruction at @address without explicitly specifying
* @overwrite_bytes_size.
*
* Original bytes:
* ADDRESS INSTRUCTION(S) INSTRUCTION(S) LEN
* @address + 00 instruction #1 4
* @address + 04 instruction #2 2
* @address + 06 instruction #3 4
*
* After injection with @overwrite_bytes_size = 6:
* ADDRESS INSTRUCTION(S) INSTRUCTION(S) LEN
* @address + 00 nop 1
* @address + 01 jmp @allocated_buf 5
* @address + 06 instruction #3 4
*
* Allocated buffer:
* ADDRESS INSTRUCTION(S) INSTRUCTION(S) LEN
* @allocated_buf + 00 injected code @size
* @allocated_buf + @size instruction #1 4
* @allocated_buf + @size + 4 instruction #2 2
* @allocated_buf + @size + 4 + 2 jmp @address + 06 5
*
* @param address An address where code should be injected.
* @param bytes Code bytes to be injected at @address
* address.
* @param bytes_size Number of the bytes to be injected.
* @param overwrite_bytes_size The number of original bytes to be
* overwritten and executed after injected code
*
* @return allocated buffer address @allocated_buf
*/
uintptr_t inject_code_using_jmp(uintptr_t address, const uint8_t *bytes,
size_t bytes_size,
size_t overwrite_bytes_size);
/**
* @brief Injects @bytes_size bytes of code from the @bytes argument into
* remote process at @address address.
*
* @param address An address where code should be injected.
* @param bytes Code bytes to be injected at @address
* address.
* @param bytes_size Number of the bytes to be injected.
* @param overwrite_bytes_size The number of original bytes to be
* overwritten and executed after injected code
*
* @return allocated buffer address @allocated_buf
*/
uintptr_t inject_code_using_push_ret(uintptr_t address,
const uint8_t *bytes,
size_t bytes_size,
size_t overwrite_bytes_size);
void *_handle;
void *_dbg_handle;
uint32_t _process_id;
std::unordered_map<uintptr_t, size_t> _allocated_memory;
std::unordered_map<uintptr_t, VirtualProtect> _virtual_protect;
std::unordered_map<uintptr_t, ExternalCaller> _callers;
std::unordered_map<uintptr_t, InjectedCodeInfo> _injected_code;
};
template <typename T> inline T ExternalProcess::read(uintptr_t address) const
{
T result;
read_buf(address, sizeof(T), &result);
return result;
}
template <typename T>
inline void ExternalProcess::write(uintptr_t address, const T &data) const
{
write_buf(address, sizeof(data), &data);
}
} /* namespace P1ExternalProcess */
#endif /* EXTERNAL_PROCESS_HPP */