Skip to content

Commit 279d7d8

Browse files
feat: implement process injection methods and control functions
1 parent e37f2b5 commit 279d7d8

4 files changed

Lines changed: 113 additions & 86 deletions

File tree

include/blook/module.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,6 @@ class Module : public std::enable_shared_from_this<Module> {
5454
Pointer base();
5555

5656
size_t size();
57-
58-
WIN_ONLY(
59-
enum class InjectMethod{CreateRemoteThread, NtCreateThread,
60-
RtlCreateUserThread};
61-
62-
void *inject(const std::string &dll_path,
63-
InjectMethod method = InjectMethod::CreateRemoteThread);)
6457
};
6558

6659
} // namespace blook

include/blook/process.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,19 @@ class Process : public std::enable_shared_from_this<Process> {
109109
return std::shared_ptr<Process>(new Process(argv...));
110110
}
111111

112+
void suspend();
113+
void resume();
112114

115+
WIN_ONLY(
116+
enum class InjectMethod{CreateRemoteThread, NtCreateThread,
117+
RtlCreateUserThread};
118+
119+
void *inject(const std::string &dll_path,
120+
InjectMethod method = InjectMethod::CreateRemoteThread);)
121+
122+
static std::shared_ptr<Process> launch(const std::string &path,
123+
bool suspended = false);
124+
static std::shared_ptr<Process> launch_suspended(const std::string &path);
113125
};
114126

115127
} // namespace blook

src/platform/windows/module.cpp

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3,68 +3,13 @@
33
#include "Windows.h"
44
#include "blook/process.h"
55

6-
#include <print>
7-
86
#include <Psapi.h>
97
#include <cassert>
108
#include <cstdint>
119
#include <libloaderapi.h>
1210
#include <map>
1311
#include <minwindef.h>
1412
#include <utility>
15-
16-
HANDLE RtlCreateUserThread(HANDLE hProcess, LPVOID lpBaseAddress,
17-
LPVOID lpSpace) {
18-
// undocumented.ntinternals.com
19-
typedef DWORD(WINAPI * functypeRtlCreateUserThread)(
20-
HANDLE ProcessHandle, PSECURITY_DESCRIPTOR SecurityDescriptor,
21-
BOOL CreateSuspended, ULONG StackZeroBits, PULONG StackReserved,
22-
PULONG StackCommit, LPVOID StartAddress, LPVOID StartParameter,
23-
HANDLE ThreadHandle, LPVOID ClientID);
24-
HANDLE hRemoteThread = NULL;
25-
HMODULE hNtDllModule = GetModuleHandle("ntdll.dll");
26-
if (hNtDllModule == NULL) {
27-
return NULL;
28-
}
29-
functypeRtlCreateUserThread funcRtlCreateUserThread =
30-
(functypeRtlCreateUserThread)GetProcAddress(hNtDllModule,
31-
"RtlCreateUserThread");
32-
if (!funcRtlCreateUserThread) {
33-
return NULL;
34-
}
35-
funcRtlCreateUserThread(hProcess, NULL, 0, 0, 0, 0, lpBaseAddress, lpSpace,
36-
&hRemoteThread, NULL);
37-
DWORD lastError = GetLastError();
38-
if (lastError)
39-
throw std::runtime_error(std::to_string(lastError));
40-
return hRemoteThread;
41-
}
42-
43-
HANDLE NtCreateThreadEx(HANDLE hProcess, LPVOID lpBaseAddress, LPVOID lpSpace) {
44-
// undocumented.ntinternals.com
45-
typedef DWORD(WINAPI * functypeNtCreateThreadEx)(
46-
PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes,
47-
HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress,
48-
LPVOID lpParameter, BOOL CreateSuspended, DWORD dwStackSize,
49-
DWORD Unknown1, DWORD Unknown2, LPVOID Unknown3);
50-
HANDLE hRemoteThread = NULL;
51-
HMODULE hNtDllModule = NULL;
52-
functypeNtCreateThreadEx funcNtCreateThreadEx = NULL;
53-
hNtDllModule = GetModuleHandle("ntdll.dll");
54-
if (hNtDllModule == NULL) {
55-
return NULL;
56-
}
57-
funcNtCreateThreadEx = (functypeNtCreateThreadEx)GetProcAddress(
58-
hNtDllModule, "NtCreateThreadEx");
59-
if (!funcNtCreateThreadEx) {
60-
return NULL;
61-
}
62-
funcNtCreateThreadEx(&hRemoteThread, GENERIC_ALL, NULL, hProcess,
63-
(LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace, FALSE,
64-
NULL, NULL, NULL, NULL);
65-
return hRemoteThread;
66-
}
67-
6813
namespace blook {
6914
std::optional<Function> Module::exports(const std::string &name) {
7015
if (proc->is_self()) {
@@ -132,30 +77,6 @@ std::unordered_map<std::string, Function> *Module::obtain_exports() {
13277
return &exports_cache;
13378
}
13479

135-
void *Module::inject(const std::string &dll_path, Module::InjectMethod method) {
136-
LPVOID lpSpace =
137-
(LPVOID)VirtualAllocEx(proc->h, NULL, dll_path.length(),
138-
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
139-
if (!lpSpace)
140-
throw std::runtime_error(std::format("Failed to alloc in proc"));
141-
142-
int n = WriteProcessMemory(proc->h, lpSpace, dll_path.c_str(),
143-
dll_path.length(), NULL);
144-
if (n == 0)
145-
throw std::runtime_error(std::format("failed to write into process"));
146-
147-
switch (method) {
148-
case InjectMethod::NtCreateThread:
149-
return NtCreateThreadEx(proc->h, (void *)LoadLibraryA, lpSpace);
150-
case InjectMethod::RtlCreateUserThread:
151-
return RtlCreateUserThread(proc->h, (void *)LoadLibraryA, lpSpace);
152-
default:
153-
return CreateRemoteThread(proc->h, NULL, 0,
154-
(LPTHREAD_START_ROUTINE)(void *)LoadLibraryA,
155-
lpSpace, NULL, NULL);
156-
}
157-
}
158-
15980
std::optional<MemoryRange> Module::section(const std::string &name) {
16081

16182
auto mod = base();

src/platform/windows/process.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,57 @@ static bool acquireDebugPrivilege() {
102102

103103
#endif
104104

105+
static HANDLE RtlCreateUserThread(HANDLE hProcess, LPVOID lpBaseAddress,
106+
LPVOID lpSpace) {
107+
typedef DWORD(WINAPI * functypeRtlCreateUserThread)(
108+
HANDLE ProcessHandle, PSECURITY_DESCRIPTOR SecurityDescriptor,
109+
BOOL CreateSuspended, ULONG StackZeroBits, PULONG StackReserved,
110+
PULONG StackCommit, LPVOID StartAddress, LPVOID StartParameter,
111+
HANDLE ThreadHandle, LPVOID ClientID);
112+
HANDLE hRemoteThread = NULL;
113+
HMODULE hNtDllModule = GetModuleHandle("ntdll.dll");
114+
if (hNtDllModule == NULL) {
115+
return NULL;
116+
}
117+
functypeRtlCreateUserThread funcRtlCreateUserThread =
118+
(functypeRtlCreateUserThread)GetProcAddress(hNtDllModule,
119+
"RtlCreateUserThread");
120+
if (!funcRtlCreateUserThread) {
121+
return NULL;
122+
}
123+
funcRtlCreateUserThread(hProcess, NULL, 0, 0, 0, 0, lpBaseAddress, lpSpace,
124+
&hRemoteThread, NULL);
125+
DWORD lastError = GetLastError();
126+
if (lastError)
127+
throw std::runtime_error(std::to_string(lastError));
128+
return hRemoteThread;
129+
}
130+
131+
static HANDLE NtCreateThreadEx(HANDLE hProcess, LPVOID lpBaseAddress,
132+
LPVOID lpSpace) {
133+
typedef DWORD(WINAPI * functypeNtCreateThreadEx)(
134+
PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes,
135+
HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress,
136+
LPVOID lpParameter, BOOL CreateSuspended, DWORD dwStackSize,
137+
DWORD Unknown1, DWORD Unknown2, LPVOID Unknown3);
138+
HANDLE hRemoteThread = NULL;
139+
HMODULE hNtDllModule = NULL;
140+
functypeNtCreateThreadEx funcNtCreateThreadEx = NULL;
141+
hNtDllModule = GetModuleHandle("ntdll.dll");
142+
if (hNtDllModule == NULL) {
143+
return NULL;
144+
}
145+
funcNtCreateThreadEx = (functypeNtCreateThreadEx)GetProcAddress(
146+
hNtDllModule, "NtCreateThreadEx");
147+
if (!funcNtCreateThreadEx) {
148+
return NULL;
149+
}
150+
funcNtCreateThreadEx(&hRemoteThread, GENERIC_ALL, NULL, hProcess,
151+
(LPTHREAD_START_ROUTINE)lpBaseAddress, lpSpace, FALSE,
152+
NULL, NULL, NULL, NULL);
153+
return hRemoteThread;
154+
}
155+
105156
namespace blook {
106157

107158
static DWORD ProtectToWin(Protect protect) {
@@ -420,4 +471,54 @@ std::vector<Thread> Process::threads() {
420471
}
421472
return threads;
422473
}
474+
475+
void *Process::inject(const std::string &dll_path, Process::InjectMethod method) {
476+
LPVOID lpSpace =
477+
(LPVOID)VirtualAllocEx(h, NULL, dll_path.length(),
478+
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
479+
if (!lpSpace)
480+
throw std::runtime_error(std::format("Failed to alloc in proc"));
481+
482+
int n = WriteProcessMemory(h, lpSpace, dll_path.c_str(),
483+
dll_path.length(), NULL);
484+
if (n == 0)
485+
throw std::runtime_error(std::format("failed to write into process"));
486+
487+
switch (method) {
488+
case InjectMethod::NtCreateThread:
489+
return NtCreateThreadEx(h, (void *)LoadLibraryA, lpSpace);
490+
case InjectMethod::RtlCreateUserThread:
491+
return RtlCreateUserThread(h, (void *)LoadLibraryA, lpSpace);
492+
default:
493+
return CreateRemoteThread(h, NULL, 0,
494+
(LPTHREAD_START_ROUTINE)(void *)LoadLibraryA,
495+
lpSpace, NULL, NULL);
496+
}
497+
}
498+
499+
void Process::suspend() {
500+
for (auto &t : threads())
501+
t.suspend();
502+
}
503+
504+
void Process::resume() {
505+
for (auto &t : threads())
506+
t.resume();
507+
}
508+
509+
std::shared_ptr<Process> Process::launch(const std::string &path,
510+
bool suspended) {
511+
STARTUPINFOA si = {sizeof(si)};
512+
PROCESS_INFORMATION pi = {};
513+
if (!CreateProcessA(path.c_str(), NULL, NULL, NULL, FALSE,
514+
suspended ? CREATE_SUSPENDED : 0, NULL, NULL, &si, &pi))
515+
throw std::runtime_error(std::format("Failed to launch process: {}",
516+
GetLastError()));
517+
CloseHandle(pi.hThread);
518+
return attach(pi.hProcess);
519+
}
520+
521+
std::shared_ptr<Process> Process::launch_suspended(const std::string &path) {
522+
return launch(path, true);
523+
}
423524
} // namespace blook

0 commit comments

Comments
 (0)