├── .gitignore ├── include ├── frong.h └── frong │ ├── helpers.h │ ├── debug.h │ ├── handles.h │ ├── thread.h │ ├── memscan.h │ ├── nt.h │ └── process.h ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # visual studio files 2 | .vs/ 3 | *.sln 4 | *.vcxproj* 5 | 6 | # output 7 | bin/ 8 | int/ 9 | 10 | # no one cares about da cpp files :( 11 | *.cpp 12 | 13 | # unit tests (should probably include this in the project later on) 14 | tests.h -------------------------------------------------------------------------------- /include/frong.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | namespace frg { 5 | // everything is in the frg namespace 6 | } 7 | 8 | // allow debug messages 9 | //#define FRONG_DEBUG 10 | 11 | // allow classes to inherit from frg::process and override virtual functions 12 | //#define FRONG_VIRTUAL_PROCESS 13 | 14 | #include "frong/process.h" 15 | #include "frong/memscan.h" 16 | -------------------------------------------------------------------------------- /include/frong/helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | namespace frg { 8 | 9 | // uint32_t if 4 10 | // uint64_t if 8 11 | template 12 | using ptr_from_size = std::enable_if_t>; 14 | 15 | // cast to void* (to stop annoying warnings about casting to larger size) 16 | template 17 | void* cast_ptr(T const ptr) { 18 | if constexpr (sizeof(ptr) < sizeof(void*)) 19 | return (void*)(uint64_t)ptr; 20 | else 21 | return (void*)ptr; 22 | } 23 | 24 | } // namespace frg -------------------------------------------------------------------------------- /include/frong/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef FRONG_DEBUG 4 | 5 | // for assert() 6 | #undef NDEBUG 7 | 8 | // debug messages 9 | #include 10 | #define FRONG_DEBUG_RAW(format, ...) printf(format, __VA_ARGS__) 11 | 12 | #else 13 | 14 | // do nothing looool 15 | #define FRONG_DEBUG_RAW(format, ...) ((void)0) 16 | 17 | #endif 18 | 19 | // assert() 20 | #include 21 | #define FRONG_ASSERT(x) assert(x) 22 | 23 | // pretty print debug messages 24 | #define FRONG_DEBUG_ERROR(format, ...) FRONG_DEBUG_RAW("[frong-error]: " format "\n", __VA_ARGS__) 25 | #define FRONG_DEBUG_WARNING(format, ...) FRONG_DEBUG_RAW("[frong-warning]: " format "\n", __VA_ARGS__) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 jono 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /include/frong/handles.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nt.h" 4 | 5 | #include 6 | 7 | namespace frg { 8 | 9 | struct handle_info { 10 | uint32_t pid; 11 | HANDLE handle; 12 | ACCESS_MASK access; 13 | uint8_t type; 14 | void* object; 15 | }; 16 | 17 | // iterate over every open handle in the system 18 | template 19 | void iterate_handles(Callback&& callback); 20 | 21 | 22 | // 23 | // 24 | // implementation below 25 | // 26 | // 27 | 28 | 29 | // iterate over every open handle in the system 30 | template 31 | inline void iterate_handles(Callback&& callback) { 32 | DWORD size = 0; 33 | nt::SYSTEM_HANDLE_INFORMATION tmp; 34 | 35 | // STATUS_INFO_LENGTH_MISMATCH 36 | if (0xC0000004 != nt::NtQuerySystemInformation( 37 | nt::SystemHandleInformation, &tmp, sizeof(tmp), &size)) { 38 | FRONG_DEBUG_ERROR("Failed to query handle information."); 39 | return; 40 | } 41 | 42 | auto const buffer = std::make_unique(size + 0x1000); 43 | auto const info = (nt::SYSTEM_HANDLE_INFORMATION*)buffer.get(); 44 | 45 | // query again now that we have our buffer big enough 46 | if (!NT_SUCCESS(nt::NtQuerySystemInformation( 47 | nt::SystemHandleInformation, buffer.get(), size + 0x1000, nullptr))) { 48 | FRONG_DEBUG_ERROR("Failed to query handle information."); 49 | return; 50 | } 51 | 52 | for (size_t i = 0; i < info->HandleCount; ++i) { 53 | auto const& entry = info->Handles[i]; 54 | 55 | // call the callback 56 | if (!callback(handle_info{ entry.ProcessId, (HANDLE)entry.Handle, 57 | entry.GrantedAccess, entry.ObjectTypeNumber, entry.ObjectAddress })) 58 | break; 59 | } 60 | } 61 | 62 | } // namespace frg 63 | 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frong 2 | 3 | ![made with c++17](https://img.shields.io/static/v1?label=made+with&message=c%2B%2B17&color=blue&logo=c%2B%2B&logoColor=blue&style=for-the-badge) 4 | ![mit license](https://img.shields.io/static/v1?label=license&message=MIT&color=blue&style=for-the-badge) 5 | 6 | A ***header-only*** memory library written in modern c++. Only supports Windows. 7 | 8 | --- 9 | 10 | ## Example Usage: 11 | 12 | ```cpp 13 | // print the pid of every process with the name "Discord.exe" 14 | for (auto const pid : frg::pids_from_name(L"Discord.exe")) 15 | printf("%u\n", pid); 16 | 17 | frg::process const process(L"Discord.exe", true); 18 | if (!process) 19 | return 0; 20 | 21 | // allocate 4 bytes of read/write virtual memory 22 | auto const address = process.allocate(4); 23 | 24 | // write to the newly allocated memory 25 | assert(4 == process.write(address, 69)); 26 | 27 | // prints "69" 28 | printf("%i\n", process.read(address)); 29 | 30 | // print the base address and path of every loaded module 31 | for (auto const& [path, m] : process.modules()) 32 | printf("0x%p %S\n", m.base(), path.c_str()); 33 | 34 | // print the id, start address, and ethread address, of every thread in the process 35 | for (auto const& t : process.threads()) 36 | printf("%u 0x%p 0x%p\n", t.tid(), t.start_address(), t.ethread()); 37 | 38 | // print the value and access rights of every handle in the process 39 | for (auto const& h : process.handles()) 40 | printf("0x%p 0x%X\n", h.handle, h.access); 41 | 42 | // search for the specified pattern in the module "kernel32.dll" 43 | auto const results = frg::memscan(process, 44 | frg::pattern("AA ? ? BB CC"), L"kernel32.dll"); 45 | 46 | // get the address of an exported routine 47 | auto const load_library_a = process.get_proc_addr(L"kernel32.dll", "LoadLibraryA"); 48 | printf("LoadLibraryA: 0x%p\n", load_library_a); 49 | 50 | // get the address of the process's native PEB (on x64 machines) 51 | printf("PEB64: 0x%p\n", process.peb_addr<8>()); 52 | 53 | // get the address of the process's WOW64 PEB (on x64 machines) 54 | printf("PEB32: 0x%p\n", process.peb_addr<4>()); 55 | 56 | // get the address of the process's kernel EPROCESS structure 57 | printf("EPROCESS: 0x%p\n", process.eprocess()); 58 | ``` 59 | 60 | ## Custom Memory Functions 61 | 62 | It is possible to override virtual methods in `frg::process` for manipulating 63 | memory if `FRONG_VIRTUAL_PROCESS` is defined before including `frong.h`. 64 | Specifically, the following methods: 65 | 66 | ```cpp 67 | // read from memory and return the number of bytes read 68 | size_t read(void const* address, void* buffer, size_t size) const; 69 | 70 | // write to memory and return the number of bytes written 71 | size_t write(void* address, void const* buffer, size_t size) const; 72 | 73 | // allocate memory in the process 74 | void* allocate(size_t size, uint32_t protection) const; 75 | 76 | // free memory returned from allocate() 77 | void free(void* address) const; 78 | ``` 79 | 80 | ## Example Usage: 81 | ```cpp 82 | #define FRONG_VIRTUAL_PROCESS 83 | #include 84 | 85 | class custom_process : public frg::process { 86 | public: 87 | // this lets us inherit every base constructor 88 | using frg::process::process; 89 | 90 | // this unhides the overloaded read() function (that isn't virtual) 91 | using frg::process::read; 92 | 93 | // override the read() function to use our own method 94 | virtual size_t read(void const* address, void* buffer, size_t size) const override { 95 | // custom implementation here... 96 | } 97 | }; 98 | ``` 99 | -------------------------------------------------------------------------------- /include/frong/thread.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "debug.h" 4 | #include "nt.h" 5 | #include "handles.h" 6 | 7 | 8 | namespace frg { 9 | 10 | enum class hwbp_type { 11 | execute = 0b00, 12 | write = 0b01, 13 | readwrite = 0b11 14 | }; 15 | 16 | enum class hwbp_size { 17 | one = 0b00, 18 | two = 0b01, 19 | four = 0b11, 20 | eight = 0b10 21 | }; 22 | 23 | // represents a running thread 24 | class thread { 25 | public: 26 | thread() = default; 27 | 28 | // use an existing thread handle 29 | thread(HANDLE handle); 30 | 31 | // opens a handle to the thread 32 | explicit thread(uint32_t tid); 33 | 34 | // we cant copy but we can move 35 | thread(thread&& other) noexcept; 36 | thread& operator=(thread&& other) noexcept; 37 | 38 | // close our handle if we were the one that opened it 39 | ~thread(); 40 | 41 | // is this process class initialized? 42 | bool valid() const noexcept; 43 | explicit operator bool() const noexcept; 44 | 45 | // get the underlying handle 46 | HANDLE handle() const noexcept; 47 | 48 | // get the thread id 49 | uint32_t tid() const noexcept; 50 | 51 | // this is where the thread started execution at 52 | void* start_address() const; 53 | 54 | // get the address of the kernel ethread structure for this thread 55 | void* ethread() const; 56 | 57 | // enable or disable a debug breakpoint on the specified address 58 | bool hwbp(void const* address, bool enable, hwbp_type type = 59 | hwbp_type::execute, hwbp_size size = hwbp_size::one) const; 60 | 61 | // suspend the current thread 62 | void suspend() const; 63 | 64 | // resume the suspended thread 65 | void resume() const; 66 | 67 | private: 68 | // ***** 69 | // WARNING: any new instance variable must also be added in the move 70 | // assignment operator or else bad things will happen! 71 | // ***** 72 | 73 | HANDLE handle_ = nullptr; 74 | 75 | // did we create this handle? 76 | bool close_handle_ = false; 77 | 78 | // the thread id 79 | uint32_t tid_ = 0; 80 | 81 | private: 82 | // no copying -_- 83 | thread(thread const&) = delete; 84 | thread& operator=(thread const&) = delete; 85 | }; 86 | 87 | 88 | // 89 | // 90 | // implementation below 91 | // 92 | // 93 | 94 | 95 | // use an existing thread handle 96 | inline thread::thread(HANDLE const handle) 97 | : handle_(handle), close_handle_(false), tid_(GetThreadId(handle)) {} 98 | 99 | // open a handle to the thread 100 | inline thread::thread(uint32_t const tid) 101 | : close_handle_(true), tid_(tid) { 102 | static auto constexpr access = THREAD_ALL_ACCESS; 103 | 104 | handle_ = OpenThread(access, FALSE, tid); 105 | 106 | if (!handle_) { 107 | FRONG_DEBUG_ERROR("Failed to open thread with tid %u", tid); 108 | return; 109 | } 110 | } 111 | 112 | // we cant copy but we can move 113 | inline thread::thread(thread&& other) noexcept { 114 | *this = std::forward(other); 115 | } 116 | inline thread& thread::operator=(thread&& other) noexcept { 117 | // we can just swap all our instance variables since other's destructor 118 | // will cleanup our (old) thread instance for us :) 119 | std::swap(handle_, other.handle_); 120 | std::swap(close_handle_, other.close_handle_); 121 | std::swap(tid_, other.tid_); 122 | 123 | return *this; 124 | } 125 | 126 | // close our handle if we were the one that opened it 127 | inline thread::~thread() { 128 | if (valid() && close_handle_) 129 | CloseHandle(handle_); 130 | } 131 | 132 | // is this process class initialized? 133 | inline bool thread::valid() const noexcept { 134 | return handle_ != nullptr; 135 | } 136 | inline thread::operator bool() const noexcept { 137 | return valid(); 138 | } 139 | 140 | // get the underlying handle 141 | inline HANDLE thread::handle() const noexcept { 142 | return handle_; 143 | } 144 | 145 | // get the thread id 146 | inline uint32_t thread::tid() const noexcept { 147 | return tid_; 148 | } 149 | 150 | // this is where the thread started execution at 151 | inline void* thread::start_address() const { 152 | void* address = nullptr; 153 | 154 | // query the address 155 | auto const status = nt::NtQueryInformationThread(handle_, 156 | nt::ThreadQuerySetWin32StartAddress, &address, sizeof(address), nullptr); 157 | 158 | if (NT_ERROR(status)) { 159 | FRONG_DEBUG_WARNING("Failed to query thread's start address."); 160 | return nullptr; 161 | } 162 | 163 | return address; 164 | } 165 | 166 | // get the address of the kernel ethread structure for this thread 167 | inline void* thread::ethread() const { 168 | void* address = nullptr; 169 | 170 | // the handle we're searching for 171 | auto search_handle = handle_; 172 | 173 | // this wont work with pseudo handles, so we need to create a real one 174 | if (handle_ == GetCurrentThread()) { 175 | DuplicateHandle(handle_, handle_, handle_, &search_handle, 176 | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, 0); 177 | } 178 | 179 | auto const current_process_id = GetCurrentProcessId(); 180 | 181 | iterate_handles([&](handle_info const& info) { 182 | // we only care about handles that WE own 183 | if (info.pid != current_process_id) 184 | return true; 185 | 186 | // we're searching for the open handle to the thread 187 | if (info.handle != search_handle) 188 | return true; 189 | 190 | // we found the target handle 191 | address = info.object; 192 | return false; 193 | }); 194 | 195 | // free the handle, if we opened it 196 | if (search_handle != handle_) 197 | CloseHandle(search_handle); 198 | 199 | return address; 200 | } 201 | 202 | // enable or disable a debug breakpoint on the specified address 203 | inline bool thread::hwbp(void const* const address, bool const enable, 204 | hwbp_type const type, hwbp_size const size) const { 205 | 206 | CONTEXT ctx{}; 207 | ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS; 208 | 209 | // get the current values of Dr0-Dr7 210 | if (!GetThreadContext(handle_, &ctx)) { 211 | FRONG_DEBUG_ERROR("Failed to get thread context."); 212 | return false; 213 | } 214 | 215 | bool success = false; 216 | 217 | if (enable) { 218 | for (size_t i = 0; i < 4; ++i) { 219 | // is this bp already being used 220 | if (ctx.Dr7 & (size_t(1) << (i * 2))) 221 | continue; 222 | 223 | // set the address 224 | (&ctx.Dr0)[i] = (uintptr_t)address; 225 | 226 | // enable the dr7 flag 227 | ctx.Dr7 |= size_t(1) << (i * 2); 228 | 229 | // specify the breakpoint size and when should it trigger 230 | const auto type_size_mask((size_t(size) << 2) | size_t(type)); 231 | ctx.Dr7 &= ~(0b1111 << (16 + i * 4)); // clear old value 232 | ctx.Dr7 |= type_size_mask << (16 + i * 4); 233 | 234 | success = true; 235 | break; 236 | } 237 | } else { 238 | for (size_t i = 0; i < 4; ++i) { 239 | // matching address? 240 | if (cast_ptr((&ctx.Dr0)[i]) != address) 241 | continue; 242 | 243 | // clear the debug register 244 | (&ctx.Dr0)[i] = 0; 245 | 246 | // disable the dr7 flag 247 | ctx.Dr7 &= ~(1 << (i * 2)); 248 | 249 | // clear out the size/type as well cuz we're nice people 250 | ctx.Dr7 &= ~(0b1111 << (16 + i * 4)); 251 | 252 | success = true; 253 | break; 254 | } 255 | } 256 | 257 | if (!success) 258 | return false; 259 | 260 | // set the new debug register values 261 | if (!SetThreadContext(handle_, &ctx)) { 262 | FRONG_DEBUG_ERROR("Failed to set thread context."); 263 | return false; 264 | } 265 | 266 | return success; 267 | } 268 | 269 | // suspend the current thread 270 | inline void thread::suspend() const { 271 | SuspendThread(handle_); 272 | } 273 | 274 | // resume the suspended thread 275 | inline void thread::resume() const { 276 | ResumeThread(handle_); 277 | } 278 | 279 | } // namespace frg -------------------------------------------------------------------------------- /include/frong/memscan.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nt.h" 4 | #include "debug.h" 5 | #include "process.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | namespace frg { 15 | 16 | // defines a region of memory 17 | struct region { 18 | void* base; 19 | size_t size; 20 | }; 21 | 22 | // doesn't filter any region out 23 | struct filter_none { 24 | constexpr size_t operator()(MEMORY_BASIC_INFORMATION const& info) const { return info.RegionSize; } 25 | }; 26 | 27 | // search for a value 28 | template 29 | struct simple_compare { 30 | constexpr simple_compare(T const& value) 31 | : value_to_search_for(value) {} 32 | 33 | // look for da value 34 | bool operator()(void* address, void* const buffer, size_t const size) const; 35 | 36 | private: 37 | T value_to_search_for; 38 | }; 39 | 40 | // search for a specific pattern 41 | template 42 | class pattern { 43 | public: 44 | // construct a pattern from an ida style sig 45 | constexpr pattern(char const (&ptrn)[Size]); 46 | 47 | // does the pattern match? 48 | bool operator()(void*, void* buffer, size_t size) const; 49 | 50 | // return the pattern in a pretty string 51 | std::string display() const; 52 | 53 | private: 54 | // parse an ascii hex character as a number 55 | static constexpr uint8_t parse_hex(char letter); 56 | 57 | private: 58 | // the number of bytes in the pattern/mask 59 | size_t size_ = 0; 60 | 61 | // only set bits in the mask are checked 62 | std::array mask_; 63 | 64 | // bytes to search for 65 | std::array pattern_; 66 | }; 67 | 68 | // get memory regions that pass the Filter test 69 | template 70 | size_t regions(process const& proc, OutIt dest, Filter const& filter); 71 | 72 | // returns an std::vector of memory ranges 73 | template 74 | std::vector regions(process const& proc, Filter const& filter); 75 | 76 | // scan memory for stuffs 77 | template 78 | size_t memscan(process const& proc, OutIt dest, Compare const& compare, Regions const& regions); 79 | 80 | // returns the results in an std::vector 81 | template 82 | std::vector memscan(process const& proc, Compare const& compare, Regions const& regions); 83 | 84 | // scan memory for stuffs (only memory inside of the specified module will be scanned) 85 | template 86 | size_t memscan(process const& proc, OutIt dest, Compare const& compare, std::wstring_view module_name); 87 | 88 | // returns the results in an std::vector (only memory inside of the specified module will be scanned) 89 | template 90 | std::vector memscan(process const& proc, Compare const& compare, std::wstring_view module_name); 91 | 92 | 93 | // 94 | // 95 | // implementation below 96 | // 97 | // 98 | 99 | 100 | namespace impl { 101 | 102 | // checks whether this container is suitable for memscan() 103 | // note: this check is not perfect or even close to it-- it doesn't check 104 | // whether the container has begin() and end() and lots of more stuff 105 | // but it is suitable enough to distiniguish between std::wstring_view 106 | // and a region container 107 | template 108 | static constexpr bool valid_scan_container = std::is_same_v< 109 | std::remove_reference_t()[0])>, region>; 110 | 111 | } // namespace impl 112 | 113 | // look for da value 114 | template 115 | inline bool simple_compare::operator()(void* const address, void* const buffer, size_t const size) const { 116 | if constexpr (alignof(T) != 1) 117 | // make sure address is aligned correctly 118 | if ((uintptr_t)address & (alignof(T) - 1)) 119 | return false; 120 | 121 | // too smol 122 | if (size < sizeof(T)) 123 | return false; 124 | 125 | return *(T*)buffer == value_to_search_for; 126 | } 127 | 128 | // construct a pattern from an ida style sig 129 | template 130 | inline constexpr pattern::pattern(char const (&ptrn)[Size]) { 131 | size_ = 0; 132 | 133 | // parse the pattern 134 | for (size_t i = 0; i < Size - 1; ++i) { 135 | // ignore whitespace 136 | if (ptrn[i] == ' ') 137 | continue; 138 | 139 | size_ += 1; 140 | 141 | // wildcard 142 | if (ptrn[i] == '?') { 143 | mask_[size_ - 1] = false; 144 | continue; 145 | } 146 | 147 | // enable this byte 148 | mask_[size_ - 1] = true; 149 | 150 | // parse the next two letters as a byte 151 | pattern_[size_ - 1] = parse_hex(ptrn[i + 1]) + 152 | parse_hex(ptrn[i]) * 0x10; 153 | 154 | // skip the next letter since we will be consuming two letters 155 | i += 1; 156 | } 157 | } 158 | 159 | // does the pattern match? 160 | template 161 | inline bool pattern::operator()(void*, void* const buffer, size_t const size) const { 162 | if (size < size_) 163 | return false; 164 | 165 | for (size_t i = 0; i < size_; ++i) { 166 | // wildcard 167 | if (!mask_[i]) 168 | continue; 169 | 170 | if (pattern_[i] != *((uint8_t*)buffer + i)) 171 | return false; 172 | } 173 | 174 | return true; 175 | } 176 | 177 | // return the pattern in a pretty string 178 | template 179 | inline std::string pattern::display() const { 180 | std::string str = ""; 181 | 182 | for (size_t i = 0; i < size_; ++i) { 183 | if (!str.empty()) 184 | str += ' '; 185 | 186 | if (!mask_[i]) 187 | str += '?'; 188 | else { 189 | str += pattern_[i] / 0x10; 190 | str.back() += str.back() <= 9 ? '0' : 'A' - 10; 191 | 192 | str += pattern_[i] % 0x10; 193 | str.back() += str.back() <= 9 ? '0' : 'A' - 10; 194 | } 195 | } 196 | 197 | return str; 198 | } 199 | 200 | // parse an ascii hex character as a number 201 | template 202 | inline constexpr uint8_t pattern::parse_hex(char const letter) { 203 | if (letter >= '0' && letter <= '9') 204 | return letter - '0'; 205 | else if (letter >= 'A' && letter <= 'F') 206 | return letter - 'A' + 0xA; 207 | else if (letter >= 'a' && letter <= 'f') 208 | return letter - 'a' + 0xA; 209 | return 0; 210 | } 211 | 212 | // get memory regions that pass the Filter test 213 | template 214 | inline size_t regions(process const& proc, OutIt dest, Filter const& filter) { 215 | // number of regions added 216 | size_t count = 0; 217 | 218 | MEMORY_BASIC_INFORMATION mbi{}; 219 | mbi.BaseAddress = nullptr; 220 | 221 | for (; true; mbi.BaseAddress = (uint8_t*)mbi.BaseAddress + mbi.RegionSize) { 222 | // query the next memory region 223 | if (VirtualQueryEx(proc.handle(), mbi.BaseAddress, &mbi, sizeof(mbi)) != sizeof(mbi)) 224 | break; 225 | 226 | // not backed by physical memory 227 | if (mbi.State != MEM_COMMIT) 228 | continue; 229 | 230 | // we cant read this memory :( 231 | if (mbi.Protect & PAGE_GUARD || mbi.Protect & PAGE_NOACCESS) 232 | continue; 233 | 234 | // ignore this region 235 | auto const size = filter(mbi); 236 | if (size == 0) 237 | continue; 238 | 239 | // add this region 240 | (count++, dest++) = { 241 | mbi.BaseAddress, 242 | size 243 | }; 244 | } 245 | 246 | return count; 247 | } 248 | 249 | // returns an std::vector of memory ranges 250 | template 251 | inline std::vector regions(process const& proc, Filter const& filter) { 252 | std::vector r; 253 | regions(proc, back_inserter(r), filter); 254 | return r; 255 | } 256 | 257 | // scan memory for stuffs 258 | template >* = nullptr> 261 | inline size_t memscan(process const& proc, OutIt dest, Compare const& compare, Regions const& regions) { 262 | // memory regions to search in 263 | if (regions.empty()) 264 | return 0; 265 | 266 | // get the biggest region size 267 | auto const buffer_size = max_element(std::begin(regions), std::end(regions), 268 | [](region const& a, region const& b) { 269 | return a.size < b.size; 270 | })->size; 271 | 272 | // allocate a buffer to read memory into 273 | auto const buffer = std::make_unique(buffer_size); 274 | 275 | // number of matches 276 | size_t count = 0; 277 | 278 | // iterate over every region 279 | for (auto [base, size] : regions) { 280 | // read 281 | size = proc.read(base, buffer.get(), size); 282 | 283 | if (size == 0) { 284 | FRONG_DEBUG_WARNING("Failed to read memory: <%p>", base); 285 | continue; 286 | } 287 | 288 | for (size_t offset = 0; offset < size; ++offset) { 289 | // does this match? 290 | if (!compare((uint8_t*)base + offset, (uint8_t*)buffer.get() + offset, size - offset)) 291 | continue; 292 | 293 | (count++, dest++) = (uint8_t*)base + offset; 294 | } 295 | } 296 | 297 | return count; 298 | } 299 | 300 | // returns the results in an std::vector 301 | template >* = nullptr> 304 | inline std::vector memscan(process const& proc, Compare const& compare, Regions const& regions) { 305 | std::vector r; 306 | memscan(proc, back_inserter(r), compare, regions); 307 | return r; 308 | } 309 | 310 | // scan memory for stuffs (only memory inside of the specified module will be scanned) 311 | template 312 | inline size_t memscan(process const& proc, OutIt dest, Compare const& compare, std::wstring_view const module_name) { 313 | auto const m = proc.module(module_name); 314 | 315 | if (!m) 316 | // module not found... 317 | return 0; 318 | 319 | return memscan(proc, dest, compare, regions(proc, 320 | [&m](MEMORY_BASIC_INFORMATION const& info) -> size_t { 321 | // address is not within module 322 | if (info.BaseAddress < m->base() || info.BaseAddress >= (uint8_t*)m->base() + m->size()) 323 | return 0; 324 | 325 | // TODO: not thoroughly tested 326 | return min((uint8_t*)info.BaseAddress + info.RegionSize, 327 | (uint8_t*)m->base() + m->size()) - (uint8_t*)info.BaseAddress; 328 | })); 329 | } 330 | 331 | // returns the results in an std::vector (only memory inside of the specified module will be scanned) 332 | template 333 | inline std::vector memscan(process const& proc, Compare const& compare, std::wstring_view const module_name) { 334 | std::vector r; 335 | memscan(proc, back_inserter(r), compare, module_name); 336 | return r; 337 | } 338 | 339 | } // namespace frg -------------------------------------------------------------------------------- /include/frong/nt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "debug.h" 4 | #include "helpers.h" 5 | 6 | #include 7 | #include 8 | 9 | // yes i know, i know, macros are bad... 10 | #define FRONG_CALL_NTDLL_EXPORT(name, ...)\ 11 | static auto const export_func_##name = (decltype(&name))\ 12 | GetProcAddress(GetModuleHandleA("ntdll.dll"), #name);\ 13 | FRONG_ASSERT(export_func_##name != nullptr);\ 14 | return export_func_##name(__VA_ARGS__) 15 | 16 | #pragma warning(push) 17 | #pragma warning(disable: 4201) // warning C4201: nonstandard extension used: nameless struct/union 18 | 19 | namespace frg::nt { 20 | 21 | // SYSTEM_INFORMATION_CLASS 22 | static constexpr auto SystemBasicInformation = (SYSTEM_INFORMATION_CLASS)0x00; 23 | static constexpr auto SystemProcessorInformation = (SYSTEM_INFORMATION_CLASS)0x01; 24 | static constexpr auto SystemPerformanceInformation = (SYSTEM_INFORMATION_CLASS)0x02; 25 | static constexpr auto SystemTimeOfDayInformation = (SYSTEM_INFORMATION_CLASS)0x03; 26 | static constexpr auto SystemPathInformation = (SYSTEM_INFORMATION_CLASS)0x04; 27 | static constexpr auto SystemProcessInformation = (SYSTEM_INFORMATION_CLASS)0x05; 28 | static constexpr auto SystemCallCountInformation = (SYSTEM_INFORMATION_CLASS)0x06; 29 | static constexpr auto SystemDeviceInformation = (SYSTEM_INFORMATION_CLASS)0x07; 30 | static constexpr auto SystemProcessorPerformanceInformation = (SYSTEM_INFORMATION_CLASS)0x08; 31 | static constexpr auto SystemFlagsInformation = (SYSTEM_INFORMATION_CLASS)0x09; 32 | static constexpr auto SystemCallTimeInformation = (SYSTEM_INFORMATION_CLASS)0x0A; 33 | static constexpr auto SystemModuleInformation = (SYSTEM_INFORMATION_CLASS)0x0B; 34 | static constexpr auto SystemLocksInformation = (SYSTEM_INFORMATION_CLASS)0x0C; 35 | static constexpr auto SystemStackTraceInformation = (SYSTEM_INFORMATION_CLASS)0x0D; 36 | static constexpr auto SystemPagedPoolInformation = (SYSTEM_INFORMATION_CLASS)0x0E; 37 | static constexpr auto SystemNonPagedPoolInformation = (SYSTEM_INFORMATION_CLASS)0x0F; 38 | static constexpr auto SystemHandleInformation = (SYSTEM_INFORMATION_CLASS)0x10; 39 | static constexpr auto SystemObjectInformation = (SYSTEM_INFORMATION_CLASS)0x11; 40 | static constexpr auto SystemPageFileInformation = (SYSTEM_INFORMATION_CLASS)0x12; 41 | static constexpr auto SystemVdmInstemulInformation = (SYSTEM_INFORMATION_CLASS)0x13; 42 | static constexpr auto SystemVdmBopInformation = (SYSTEM_INFORMATION_CLASS)0x14; 43 | static constexpr auto SystemFileCacheInformation = (SYSTEM_INFORMATION_CLASS)0x15; 44 | static constexpr auto SystemPoolTagInformation = (SYSTEM_INFORMATION_CLASS)0x16; 45 | static constexpr auto SystemInterruptInformation = (SYSTEM_INFORMATION_CLASS)0x17; 46 | static constexpr auto SystemDpcBehaviorInformation = (SYSTEM_INFORMATION_CLASS)0x18; 47 | static constexpr auto SystemFullMemoryInformation = (SYSTEM_INFORMATION_CLASS)0x19; 48 | static constexpr auto SystemLoadGdiDriverInformation = (SYSTEM_INFORMATION_CLASS)0x1A; 49 | static constexpr auto SystemUnloadGdiDriverInformation = (SYSTEM_INFORMATION_CLASS)0x1B; 50 | static constexpr auto SystemTimeAdjustmentInformation = (SYSTEM_INFORMATION_CLASS)0x1C; 51 | static constexpr auto SystemSummaryMemoryInformation = (SYSTEM_INFORMATION_CLASS)0x1D; 52 | static constexpr auto SystemNextEventIdInformation = (SYSTEM_INFORMATION_CLASS)0x1E; 53 | static constexpr auto SystemMirrorMemoryInformation = (SYSTEM_INFORMATION_CLASS)0x1E; 54 | static constexpr auto SystemEventIdsInformation = (SYSTEM_INFORMATION_CLASS)0x1F; 55 | static constexpr auto SystemPerformanceTraceInformation = (SYSTEM_INFORMATION_CLASS)0x1F; 56 | static constexpr auto SystemCrashDumpInformation = (SYSTEM_INFORMATION_CLASS)0x20; 57 | static constexpr auto SystemExceptionInformation = (SYSTEM_INFORMATION_CLASS)0x21; 58 | static constexpr auto SystemCrashDumpStateInformation = (SYSTEM_INFORMATION_CLASS)0x22; 59 | static constexpr auto SystemKernelDebuggerInformation = (SYSTEM_INFORMATION_CLASS)0x23; 60 | static constexpr auto SystemContextSwitchInformation = (SYSTEM_INFORMATION_CLASS)0x24; 61 | static constexpr auto SystemRegistryQuotaInformation = (SYSTEM_INFORMATION_CLASS)0x25; 62 | static constexpr auto SystemExtendServiceTableInformation = (SYSTEM_INFORMATION_CLASS)0x26; 63 | static constexpr auto SystemPrioritySeperation = (SYSTEM_INFORMATION_CLASS)0x27; 64 | static constexpr auto SystemPlugPlayBusInformation = (SYSTEM_INFORMATION_CLASS)0x28; 65 | static constexpr auto SystemVerifierAddDriverInformation = (SYSTEM_INFORMATION_CLASS)0x28; 66 | static constexpr auto SystemDockInformation = (SYSTEM_INFORMATION_CLASS)0x29; 67 | static constexpr auto SystemVerifierRemoveDriverInformation = (SYSTEM_INFORMATION_CLASS)0x29; 68 | static constexpr auto SystemPowerInformation = (SYSTEM_INFORMATION_CLASS)0x2A; 69 | static constexpr auto SystemProcessorIdleInformation = (SYSTEM_INFORMATION_CLASS)0x2A; 70 | static constexpr auto SystemProcessorSpeedInformation = (SYSTEM_INFORMATION_CLASS)0x2B; 71 | static constexpr auto SystemLegacyDriverInformation = (SYSTEM_INFORMATION_CLASS)0x2B; 72 | static constexpr auto SystemCurrentTimeZoneInformation = (SYSTEM_INFORMATION_CLASS)0x2C; 73 | static constexpr auto SystemLookasideInformation = (SYSTEM_INFORMATION_CLASS)0x2D; 74 | static constexpr auto SystemTimeSlipNotification = (SYSTEM_INFORMATION_CLASS)0x2E; 75 | static constexpr auto SystemSessionCreate = (SYSTEM_INFORMATION_CLASS)0x2F; 76 | static constexpr auto SystemSessionDetach = (SYSTEM_INFORMATION_CLASS)0x30; 77 | static constexpr auto SystemSessionInformation = (SYSTEM_INFORMATION_CLASS)0x31; 78 | static constexpr auto SystemRangeStartInformation = (SYSTEM_INFORMATION_CLASS)0x32; 79 | static constexpr auto SystemVerifierInformation = (SYSTEM_INFORMATION_CLASS)0x33; 80 | static constexpr auto SystemVerifierThunkExtend = (SYSTEM_INFORMATION_CLASS)0x34; 81 | static constexpr auto SystemSessionProcessInformation = (SYSTEM_INFORMATION_CLASS)0x35; 82 | //static constexpr auto SystemObjectSecurityMode = (SYSTEM_INFORMATION_CLASS)0x36; 83 | static constexpr auto SystemLoadGdiDriverInSystemSpace = (SYSTEM_INFORMATION_CLASS)0x36; 84 | static constexpr auto SystemNumaProcessorMap = (SYSTEM_INFORMATION_CLASS)0x37; 85 | static constexpr auto SystemPrefetcherInformation = (SYSTEM_INFORMATION_CLASS)0x38; 86 | static constexpr auto SystemExtendedProcessInformation = (SYSTEM_INFORMATION_CLASS)0x39; 87 | static constexpr auto SystemRecommendedSharedDataAlignment = (SYSTEM_INFORMATION_CLASS)0x3A; 88 | static constexpr auto SystemComPlusPackage = (SYSTEM_INFORMATION_CLASS)0x3B; 89 | static constexpr auto SystemNumaAvailableMemory = (SYSTEM_INFORMATION_CLASS)0x3C; 90 | static constexpr auto SystemProcessorPowerInformation = (SYSTEM_INFORMATION_CLASS)0x3D; 91 | static constexpr auto SystemEmulationBasicInformation = (SYSTEM_INFORMATION_CLASS)0x3E; 92 | static constexpr auto SystemEmulationProcessorInformation = (SYSTEM_INFORMATION_CLASS)0x3F; 93 | static constexpr auto SystemExtendedHandleInformation = (SYSTEM_INFORMATION_CLASS)0x40; 94 | static constexpr auto SystemLostDelayedWriteInformation = (SYSTEM_INFORMATION_CLASS)0x41; 95 | static constexpr auto SystemBigPoolInformation = (SYSTEM_INFORMATION_CLASS)0x42; 96 | static constexpr auto SystemSessionPoolTagInformation = (SYSTEM_INFORMATION_CLASS)0x43; 97 | static constexpr auto SystemSessionMappedViewInformation = (SYSTEM_INFORMATION_CLASS)0x44; 98 | static constexpr auto SystemHotpatchInformation = (SYSTEM_INFORMATION_CLASS)0x45; 99 | static constexpr auto SystemObjectSecurityMode = (SYSTEM_INFORMATION_CLASS)0x46; 100 | static constexpr auto SystemWatchdogTimerHandler = (SYSTEM_INFORMATION_CLASS)0x47; 101 | static constexpr auto SystemWatchdogTimerInformation = (SYSTEM_INFORMATION_CLASS)0x48; 102 | static constexpr auto SystemLogicalProcessorInformation = (SYSTEM_INFORMATION_CLASS)0x49; 103 | static constexpr auto SystemWow = (SYSTEM_INFORMATION_CLASS)0x4A; 104 | static constexpr auto SystemRegisterFirmwareTableInformationHandler = (SYSTEM_INFORMATION_CLASS)0x4B; 105 | static constexpr auto SystemFirmwareTableInformation = (SYSTEM_INFORMATION_CLASS)0x4C; 106 | static constexpr auto SystemModuleInformationEx = (SYSTEM_INFORMATION_CLASS)0x4D; 107 | static constexpr auto SystemVerifierTriageInformation = (SYSTEM_INFORMATION_CLASS)0x4E; 108 | static constexpr auto SystemSuperfetchInformation = (SYSTEM_INFORMATION_CLASS)0x4F; 109 | static constexpr auto SystemMemoryListInformation = (SYSTEM_INFORMATION_CLASS)0x50; 110 | static constexpr auto SystemFileCacheInformationEx = (SYSTEM_INFORMATION_CLASS)0x51; 111 | static constexpr auto SystemThreadPriorityClientIdInformation = (SYSTEM_INFORMATION_CLASS)0x52; 112 | static constexpr auto SystemProcessorIdleCycleTimeInformation = (SYSTEM_INFORMATION_CLASS)0x53; 113 | static constexpr auto SystemVerifierCancellationInformation = (SYSTEM_INFORMATION_CLASS)0x54; 114 | static constexpr auto SystemProcessorPowerInformationEx = (SYSTEM_INFORMATION_CLASS)0x55; 115 | static constexpr auto SystemRefTraceInformation = (SYSTEM_INFORMATION_CLASS)0x56; 116 | static constexpr auto SystemSpecialPoolInformation = (SYSTEM_INFORMATION_CLASS)0x57; 117 | static constexpr auto SystemProcessIdInformation = (SYSTEM_INFORMATION_CLASS)0x58; 118 | static constexpr auto SystemErrorPortInformation = (SYSTEM_INFORMATION_CLASS)0x59; 119 | static constexpr auto SystemBootEnvironmentInformation = (SYSTEM_INFORMATION_CLASS)0x5A; 120 | static constexpr auto SystemHypervisorInformation = (SYSTEM_INFORMATION_CLASS)0x5B; 121 | static constexpr auto SystemVerifierInformationEx = (SYSTEM_INFORMATION_CLASS)0x5C; 122 | static constexpr auto SystemTimeZoneInformation = (SYSTEM_INFORMATION_CLASS)0x5D; 123 | static constexpr auto SystemImageFileExecutionOptionsInformation = (SYSTEM_INFORMATION_CLASS)0x5E; 124 | static constexpr auto SystemCoverageInformation = (SYSTEM_INFORMATION_CLASS)0x5F; 125 | static constexpr auto SystemPrefetchPatchInformation = (SYSTEM_INFORMATION_CLASS)0x60; 126 | static constexpr auto SystemVerifierFaultsInformation = (SYSTEM_INFORMATION_CLASS)0x61; 127 | static constexpr auto SystemSystemPartitionInformation = (SYSTEM_INFORMATION_CLASS)0x62; 128 | static constexpr auto SystemSystemDiskInformation = (SYSTEM_INFORMATION_CLASS)0x63; 129 | static constexpr auto SystemProcessorPerformanceDistribution = (SYSTEM_INFORMATION_CLASS)0x64; 130 | static constexpr auto SystemNumaProximityNodeInformation = (SYSTEM_INFORMATION_CLASS)0x65; 131 | static constexpr auto SystemDynamicTimeZoneInformation = (SYSTEM_INFORMATION_CLASS)0x66; 132 | static constexpr auto SystemCodeIntegrityInformation = (SYSTEM_INFORMATION_CLASS)0x67; 133 | static constexpr auto SystemProcessorMicrocodeUpdateInformation = (SYSTEM_INFORMATION_CLASS)0x68; 134 | static constexpr auto SystemProcessorBrandString = (SYSTEM_INFORMATION_CLASS)0x69; 135 | static constexpr auto SystemVirtualAddressInformation = (SYSTEM_INFORMATION_CLASS)0x6A; 136 | static constexpr auto SystemLogicalProcessorAndGroupInformation = (SYSTEM_INFORMATION_CLASS)0x6B; 137 | static constexpr auto SystemProcessorCycleTimeInformation = (SYSTEM_INFORMATION_CLASS)0x6C; 138 | static constexpr auto SystemStoreInformation = (SYSTEM_INFORMATION_CLASS)0x6D; 139 | static constexpr auto SystemRegistryAppendString = (SYSTEM_INFORMATION_CLASS)0x6E; 140 | static constexpr auto SystemAitSamplingValue = (SYSTEM_INFORMATION_CLASS)0x6F; 141 | static constexpr auto SystemVhdBootInformation = (SYSTEM_INFORMATION_CLASS)0x70; 142 | static constexpr auto SystemCpuQuotaInformation = (SYSTEM_INFORMATION_CLASS)0x71; 143 | static constexpr auto SystemNativeBasicInformation = (SYSTEM_INFORMATION_CLASS)0x72; 144 | static constexpr auto SystemErrorPortTimeouts = (SYSTEM_INFORMATION_CLASS)0x73; 145 | static constexpr auto SystemLowPriorityIoInformation = (SYSTEM_INFORMATION_CLASS)0x74; 146 | static constexpr auto SystemBootEntropyInformation = (SYSTEM_INFORMATION_CLASS)0x75; 147 | static constexpr auto SystemVerifierCountersInformation = (SYSTEM_INFORMATION_CLASS)0x76; 148 | static constexpr auto SystemPagedPoolInformationEx = (SYSTEM_INFORMATION_CLASS)0x77; 149 | static constexpr auto SystemSystemPtesInformationEx = (SYSTEM_INFORMATION_CLASS)0x78; 150 | static constexpr auto SystemNodeDistanceInformation = (SYSTEM_INFORMATION_CLASS)0x79; 151 | static constexpr auto SystemAcpiAuditInformation = (SYSTEM_INFORMATION_CLASS)0x7A; 152 | static constexpr auto SystemBasicPerformanceInformation = (SYSTEM_INFORMATION_CLASS)0x7B; 153 | static constexpr auto SystemQueryPerformanceCounterInformation = (SYSTEM_INFORMATION_CLASS)0x7C; 154 | static constexpr auto SystemSessionBigPoolInformation = (SYSTEM_INFORMATION_CLASS)0x7D; 155 | static constexpr auto SystemBootGraphicsInformation = (SYSTEM_INFORMATION_CLASS)0x7E; 156 | static constexpr auto SystemScrubPhysicalMemoryInformation = (SYSTEM_INFORMATION_CLASS)0x7F; 157 | static constexpr auto SystemBadPageInformation = (SYSTEM_INFORMATION_CLASS)0x80; 158 | static constexpr auto SystemProcessorProfileControlArea = (SYSTEM_INFORMATION_CLASS)0x81; 159 | static constexpr auto SystemCombinePhysicalMemoryInformation = (SYSTEM_INFORMATION_CLASS)0x82; 160 | static constexpr auto SystemEntropyInterruptTimingInformation = (SYSTEM_INFORMATION_CLASS)0x83; 161 | static constexpr auto SystemConsoleInformation = (SYSTEM_INFORMATION_CLASS)0x84; 162 | static constexpr auto SystemPlatformBinaryInformation = (SYSTEM_INFORMATION_CLASS)0x85; 163 | static constexpr auto SystemThrottleNotificationInformation = (SYSTEM_INFORMATION_CLASS)0x86; 164 | static constexpr auto SystemPolicyInformation = (SYSTEM_INFORMATION_CLASS)0x86; 165 | static constexpr auto SystemHypervisorProcessorCountInformation = (SYSTEM_INFORMATION_CLASS)0x87; 166 | static constexpr auto SystemDeviceDataInformation = (SYSTEM_INFORMATION_CLASS)0x88; 167 | static constexpr auto SystemDeviceDataEnumerationInformation = (SYSTEM_INFORMATION_CLASS)0x89; 168 | static constexpr auto SystemMemoryTopologyInformation = (SYSTEM_INFORMATION_CLASS)0x8A; 169 | static constexpr auto SystemMemoryChannelInformation = (SYSTEM_INFORMATION_CLASS)0x8B; 170 | static constexpr auto SystemBootLogoInformation = (SYSTEM_INFORMATION_CLASS)0x8C; 171 | static constexpr auto SystemProcessorPerformanceInformationEx = (SYSTEM_INFORMATION_CLASS)0x8D; 172 | static constexpr auto SystemCriticalProcessErrorLogInformation = (SYSTEM_INFORMATION_CLASS)0x8E; 173 | static constexpr auto SystemSecureBootPolicyInformation = (SYSTEM_INFORMATION_CLASS)0x8F; 174 | static constexpr auto SystemPageFileInformationEx = (SYSTEM_INFORMATION_CLASS)0x90; 175 | static constexpr auto SystemSecureBootInformation = (SYSTEM_INFORMATION_CLASS)0x91; 176 | static constexpr auto SystemEntropyInterruptTimingRawInformation = (SYSTEM_INFORMATION_CLASS)0x92; 177 | static constexpr auto SystemPortableWorkspaceEfiLauncherInformation = (SYSTEM_INFORMATION_CLASS)0x93; 178 | static constexpr auto SystemFullProcessInformation = (SYSTEM_INFORMATION_CLASS)0x94; 179 | static constexpr auto SystemKernelDebuggerInformationEx = (SYSTEM_INFORMATION_CLASS)0x95; 180 | static constexpr auto SystemBootMetadataInformation = (SYSTEM_INFORMATION_CLASS)0x96; 181 | static constexpr auto SystemSoftRebootInformation = (SYSTEM_INFORMATION_CLASS)0x97; 182 | static constexpr auto SystemElamCertificateInformation = (SYSTEM_INFORMATION_CLASS)0x98; 183 | static constexpr auto SystemOfflineDumpConfigInformation = (SYSTEM_INFORMATION_CLASS)0x99; 184 | static constexpr auto SystemProcessorFeaturesInformation = (SYSTEM_INFORMATION_CLASS)0x9A; 185 | static constexpr auto SystemRegistryReconciliationInformation = (SYSTEM_INFORMATION_CLASS)0x9B; 186 | static constexpr auto SystemEdidInformation = (SYSTEM_INFORMATION_CLASS)0x9C; 187 | static constexpr auto SystemManufacturingInformation = (SYSTEM_INFORMATION_CLASS)0x9D; 188 | static constexpr auto SystemEnergyEstimationConfigInformation = (SYSTEM_INFORMATION_CLASS)0x9E; 189 | static constexpr auto SystemHypervisorDetailInformation = (SYSTEM_INFORMATION_CLASS)0x9F; 190 | static constexpr auto SystemProcessorCycleStatsInformation = (SYSTEM_INFORMATION_CLASS)0xA0; 191 | static constexpr auto SystemVmGenerationCountInformation = (SYSTEM_INFORMATION_CLASS)0xA1; 192 | static constexpr auto SystemTrustedPlatformModuleInformation = (SYSTEM_INFORMATION_CLASS)0xA2; 193 | static constexpr auto SystemKernelDebuggerFlags = (SYSTEM_INFORMATION_CLASS)0xA3; 194 | static constexpr auto SystemCodeIntegrityPolicyInformation = (SYSTEM_INFORMATION_CLASS)0xA4; 195 | static constexpr auto SystemIsolatedUserModeInformation = (SYSTEM_INFORMATION_CLASS)0xA5; 196 | static constexpr auto SystemHardwareSecurityTestInterfaceResultsInformation = (SYSTEM_INFORMATION_CLASS)0xA6; 197 | static constexpr auto SystemSingleModuleInformation = (SYSTEM_INFORMATION_CLASS)0xA7; 198 | static constexpr auto SystemAllowedCpuSetsInformation = (SYSTEM_INFORMATION_CLASS)0xA8; 199 | static constexpr auto SystemDmaProtectionInformation = (SYSTEM_INFORMATION_CLASS)0xA9; 200 | static constexpr auto SystemInterruptCpuSetsInformation = (SYSTEM_INFORMATION_CLASS)0xAA; 201 | static constexpr auto SystemSecureBootPolicyFullInformation = (SYSTEM_INFORMATION_CLASS)0xAB; 202 | static constexpr auto SystemCodeIntegrityPolicyFullInformation = (SYSTEM_INFORMATION_CLASS)0xAC; 203 | static constexpr auto SystemAffinitizedInterruptProcessorInformation = (SYSTEM_INFORMATION_CLASS)0xAD; 204 | static constexpr auto SystemRootSiloInformation = (SYSTEM_INFORMATION_CLASS)0xAE; 205 | static constexpr auto SystemCpuSetInformation = (SYSTEM_INFORMATION_CLASS)0xAF; 206 | static constexpr auto SystemCpuSetTagInformation = (SYSTEM_INFORMATION_CLASS)0xB0; 207 | static constexpr auto SystemWin = (SYSTEM_INFORMATION_CLASS)0xB1; 208 | static constexpr auto SystemSecureKernelProfileInformation = (SYSTEM_INFORMATION_CLASS)0xB2; 209 | static constexpr auto SystemCodeIntegrityPlatformManifestInformation = (SYSTEM_INFORMATION_CLASS)0xB3; 210 | static constexpr auto SystemInterruptSteeringInformation = (SYSTEM_INFORMATION_CLASS)0xB4; 211 | static constexpr auto SystemSuppportedProcessorArchitectures = (SYSTEM_INFORMATION_CLASS)0xB5; 212 | static constexpr auto SystemMemoryUsageInformation = (SYSTEM_INFORMATION_CLASS)0xB6; 213 | static constexpr auto SystemCodeIntegrityCertificateInformation = (SYSTEM_INFORMATION_CLASS)0xB7; 214 | static constexpr auto SystemPhysicalMemoryInformation = (SYSTEM_INFORMATION_CLASS)0xB8; 215 | static constexpr auto SystemControlFlowTransition = (SYSTEM_INFORMATION_CLASS)0xB9; 216 | static constexpr auto SystemKernelDebuggingAllowed = (SYSTEM_INFORMATION_CLASS)0xBA; 217 | static constexpr auto SystemActivityModerationExeState = (SYSTEM_INFORMATION_CLASS)0xBB; 218 | static constexpr auto SystemActivityModerationUserSettings = (SYSTEM_INFORMATION_CLASS)0xBC; 219 | static constexpr auto SystemCodeIntegrityPoliciesFullInformation = (SYSTEM_INFORMATION_CLASS)0xBD; 220 | static constexpr auto SystemCodeIntegrityUnlockInformation = (SYSTEM_INFORMATION_CLASS)0xBE; 221 | static constexpr auto SystemIntegrityQuotaInformation = (SYSTEM_INFORMATION_CLASS)0xBF; 222 | static constexpr auto SystemFlushInformation = (SYSTEM_INFORMATION_CLASS)0xC0; 223 | static constexpr auto SystemProcessorIdleMaskInformation = (SYSTEM_INFORMATION_CLASS)0xC1; 224 | static constexpr auto SystemSecureDumpEncryptionInformation = (SYSTEM_INFORMATION_CLASS)0xC2; 225 | static constexpr auto SystemWriteConstraintInformation = (SYSTEM_INFORMATION_CLASS)0xC3; 226 | static constexpr auto SystemKernelVaShadowInformation = (SYSTEM_INFORMATION_CLASS)0xC4; 227 | static constexpr auto SystemHypervisorSharedPageInformation = (SYSTEM_INFORMATION_CLASS)0xC5; 228 | static constexpr auto SystemFirmwareBootPerformanceInformation = (SYSTEM_INFORMATION_CLASS)0xC6; 229 | static constexpr auto SystemCodeIntegrityVerificationInformation = (SYSTEM_INFORMATION_CLASS)0xC7; 230 | static constexpr auto SystemFirmwarePartitionInformation = (SYSTEM_INFORMATION_CLASS)0xC8; 231 | static constexpr auto SystemSpeculationControlInformation = (SYSTEM_INFORMATION_CLASS)0xC9; 232 | static constexpr auto SystemDmaGuardPolicyInformation = (SYSTEM_INFORMATION_CLASS)0xCA; 233 | static constexpr auto SystemEnclaveLaunchControlInformation = (SYSTEM_INFORMATION_CLASS)0xCB; 234 | 235 | // OBJECT_INFORMATION_CLASS 236 | static constexpr auto ObjectNameInformation = OBJECT_INFORMATION_CLASS(1); 237 | 238 | #pragma warning(push) 239 | #pragma warning(disable : 26812) 240 | enum THREADINFOCLASS { 241 | ThreadBasicInformation, 242 | ThreadTimes, 243 | ThreadPriority, 244 | ThreadBasePriority, 245 | ThreadAffinityMask, 246 | ThreadImpersonationToken, 247 | ThreadDescriptorTableEntry, 248 | ThreadEnableAlignmentFaultFixup, 249 | ThreadEventPair, 250 | ThreadQuerySetWin32StartAddress, 251 | ThreadZeroTlsCell, 252 | ThreadPerformanceCount, 253 | ThreadAmILastThread, 254 | ThreadIdealProcessor, 255 | ThreadPriorityBoost, 256 | ThreadSetTlsArrayAddress, 257 | ThreadIsIoPending, 258 | ThreadHideFromDebugger 259 | }; 260 | #pragma warning(pop) 261 | 262 | template 263 | struct UNICODE_STRING { 264 | using ptr = ptr_from_size; 265 | 266 | uint16_t Length; 267 | uint16_t MaximumLength; 268 | ptr Buffer; 269 | }; 270 | 271 | // doubly linked list entry 272 | template 273 | struct LIST_ENTRY { 274 | using ptr = ptr_from_size; 275 | 276 | ptr Flink; 277 | ptr Blink; 278 | }; 279 | 280 | // PEB_LDR_DATA::InMemoryOrderModuleList 281 | template 282 | struct LDR_DATA_TABLE_ENTRY { 283 | using ptr = ptr_from_size; 284 | private: 285 | ptr Padding1[2]; 286 | public: 287 | LIST_ENTRY InMemoryOrderLinks; 288 | private: 289 | ptr Padding2[2]; 290 | public: 291 | ptr DllBase; 292 | private: 293 | ptr Padding3[2]; 294 | public: 295 | UNICODE_STRING FullDllName; 296 | private: 297 | uint8_t Padding4[8]; 298 | ptr Padding5[3]; 299 | public: 300 | union { 301 | ptr Alignment1; 302 | uint32_t CheckSum; 303 | }; 304 | uint32_t TimeDateStamp; 305 | }; 306 | 307 | // PEB::Ldr 308 | template 309 | struct PEB_LDR_DATA { 310 | using ptr = ptr_from_size; 311 | private: 312 | uint8_t Padding1[8]; 313 | ptr Padding2[3]; 314 | public: 315 | LIST_ENTRY InMemoryOrderModuleList; // LDR_DATA_TABLE_ENTRY 316 | }; 317 | 318 | // ApiSet structs are taken from https://lucasg.github.io/2017/10/15/Api-set-resolution/ 319 | struct API_SET_NAMESPACE { 320 | ULONG Version; // v2 on Windows 7, v4 on Windows 8.1 and v6 on Windows 10 321 | ULONG Size; // apiset map size (usually the .apiset section virtual size) 322 | ULONG Flags; // according to Geoff Chappell, tells if the map is sealed or not. 323 | ULONG Count; // hash table entry count 324 | ULONG EntryOffset; // Offset to the api set entries values 325 | ULONG HashOffset; // Offset to the api set entries hash indexes 326 | ULONG HashFactor; // multiplier to use when computing hash 327 | }; 328 | 329 | // Hash table value 330 | struct API_SET_NAMESPACE_ENTRY { 331 | ULONG Flags; // sealed flag in bit 0 332 | ULONG NameOffset; // Offset to the ApiSet library name PWCHAR (e.g. "api-ms-win-core-job-l2-1-1") 333 | ULONG NameLength; // Ignored 334 | ULONG HashedLength; // Apiset library name length 335 | ULONG ValueOffset; // Offset the list of hosts library implement the apiset contract (points to API_SET_VALUE_ENTRY array) 336 | ULONG ValueCount; // Number of hosts libraries 337 | }; 338 | 339 | // Host Library entry 340 | struct API_SET_VALUE_ENTRY { 341 | ULONG Flags; // sealed flag in bit 0 342 | ULONG NameOffset; // Offset to the ApiSet library name PWCHAR (e.g. "api-ms-win-core-job-l2-1-1") 343 | ULONG NameLength; // Apiset library name length 344 | ULONG ValueOffset; // Offset to the Host library name PWCHAR (e.g. "ucrtbase.dll") 345 | ULONG ValueLength; // Host library name length 346 | }; 347 | 348 | // process environment block 349 | template 350 | struct PEB { 351 | using ptr = ptr_from_size; 352 | 353 | union { 354 | ptr Alignment1; 355 | struct { 356 | uint8_t InheritedAddressSpace; 357 | uint8_t ReadImageFileExecOptions; 358 | uint8_t BeingDebugged; 359 | uint8_t BitField; 360 | }; 361 | }; 362 | 363 | ptr Mutant; 364 | ptr ImageBaseAddress; 365 | ptr Ldr; // PEB_LDR_DATA 366 | 367 | private: 368 | ptr Padding1[8]; 369 | uint8_t Padding2[8]; 370 | 371 | public: 372 | ptr ApiSetMap; // API_SET_NAMESPACE 373 | }; 374 | 375 | struct SYSTEM_HANDLE_TABLE_ENTRY_INFO { 376 | DWORD ProcessId; 377 | BYTE ObjectTypeNumber; 378 | BYTE Flags; 379 | WORD Handle; 380 | PVOID ObjectAddress; 381 | ACCESS_MASK GrantedAccess; 382 | }; 383 | 384 | struct SYSTEM_HANDLE_INFORMATION { 385 | DWORD HandleCount; 386 | SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1]; 387 | }; 388 | 389 | struct OBJECT_NAME_INFORMATION { 390 | ::UNICODE_STRING Name; 391 | WCHAR NameBuffer[1]; 392 | }; 393 | 394 | struct SYSTEM_BIGPOOL_ENTRY { 395 | union { 396 | PVOID VirtualAddress; 397 | ULONG_PTR NonPaged : 1; 398 | }; 399 | 400 | ULONG_PTR SizeInBytes; 401 | 402 | union { 403 | UCHAR Tag[4]; 404 | ULONG TagUlong; 405 | }; 406 | }; 407 | 408 | struct SYSTEM_BIGPOOL_INFORMATION { 409 | ULONG Count; 410 | SYSTEM_BIGPOOL_ENTRY AllocatedInfo[1]; 411 | }; 412 | 413 | // ntdll!NtQueryInformationProcess 414 | inline NTSTATUS NTAPI 415 | NtQueryInformationProcess( 416 | HANDLE ProcessHandle, 417 | PROCESSINFOCLASS ProcessInformationClass, 418 | PVOID ProcessInformation, 419 | ULONG ProcessInformationLength, 420 | PULONG ReturnLength) { 421 | FRONG_CALL_NTDLL_EXPORT(NtQueryInformationProcess, 422 | ProcessHandle, 423 | ProcessInformationClass, 424 | ProcessInformation, 425 | ProcessInformationLength, 426 | ReturnLength); 427 | } 428 | 429 | // ntdll!NtQueryInformationThread 430 | inline NTSTATUS NTAPI 431 | NtQueryInformationThread( 432 | HANDLE ThreadHandle, 433 | THREADINFOCLASS ThreadInformationClass, 434 | PVOID ThreadInformation, 435 | ULONG ThreadInformationLength, 436 | PULONG ReturnLength) { 437 | FRONG_CALL_NTDLL_EXPORT(NtQueryInformationThread, 438 | ThreadHandle, 439 | ThreadInformationClass, 440 | ThreadInformation, 441 | ThreadInformationLength, 442 | ReturnLength); 443 | } 444 | 445 | // ntdll!NtQuerySystemInformation 446 | inline NTSTATUS NTAPI 447 | NtQuerySystemInformation( 448 | SYSTEM_INFORMATION_CLASS SystemInformationClass, 449 | PVOID SystemInformation, 450 | ULONG SystemInformationLength, 451 | PULONG ReturnLength) { 452 | FRONG_CALL_NTDLL_EXPORT(NtQuerySystemInformation, 453 | SystemInformationClass, 454 | SystemInformation, 455 | SystemInformationLength, 456 | ReturnLength); 457 | } 458 | 459 | // ntdll!NtQueryObject 460 | inline NTSTATUS NTAPI 461 | NtQueryObject( 462 | HANDLE ObjectHandle, 463 | OBJECT_INFORMATION_CLASS ObjectInformationClass, 464 | PVOID ObjectInformation, 465 | ULONG Length, 466 | PULONG ResultLength) { 467 | FRONG_CALL_NTDLL_EXPORT(NtQueryObject, 468 | ObjectHandle, 469 | ObjectInformationClass, 470 | ObjectInformation, 471 | Length, 472 | ResultLength); 473 | } 474 | 475 | } // namespace frg::nt 476 | 477 | #pragma warning(pop) -------------------------------------------------------------------------------- /include/frong/process.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "debug.h" 4 | #include "nt.h" 5 | #include "thread.h" 6 | #include "handles.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // for WTS* functions 19 | #include 20 | #pragma comment(lib, "Wtsapi32.lib") 21 | 22 | namespace frg { 23 | 24 | struct module_export { 25 | std::string name; 26 | void* address; 27 | uint16_t ordinal; 28 | }; 29 | 30 | // represents a module loaded in memory 31 | class module { 32 | public: 33 | module() = default; 34 | module(void* base, class process const& proc); 35 | 36 | // the base address of the module in memory 37 | void* base() const; 38 | 39 | // the size of the module 40 | size_t size() const; 41 | 42 | // name of the module 43 | std::wstring name(class process const& proc) const; 44 | 45 | // 32 or 64 bit 46 | // note: 47 | // 32 bit programs running under wow64 will still have 64bit modules loaded 48 | // in memory, e.g. ntdll 49 | bool x86() const; 50 | bool x64() const; 51 | 52 | // get all of the module's exports 53 | template 54 | size_t exports(class process const& proc, OutIt dest) const; 55 | 56 | // returns a vector of exports 57 | std::vector exports(class process const& proc) const; 58 | 59 | // get the address of an export 60 | void* get_proc_addr(class process const& proc, char const* name) const; 61 | 62 | private: 63 | void* base_ = nullptr; 64 | size_t size_ = 0; 65 | bool x64_ = false; 66 | 67 | private: 68 | // the address of the IMAGE_NT_HEADERS 69 | uint8_t* ntheader(class process const& proc) const; 70 | 71 | // iterate over every export in a loaded module 72 | template 73 | void iterate_exports(class process const& proc, Callback&& callback) const; 74 | 75 | // finds a forwarder export 76 | void* resolve_forwarder(class process const& proc, std::wstring_view parent, std::string_view forwarder) const; 77 | }; 78 | 79 | #ifdef FRONG_VIRTUAL_PROCESS 80 | #define FRONG_VIRTUAL virtual 81 | #else 82 | #define FRONG_VIRTUAL 83 | #endif 84 | 85 | // wrapper over a process handle 86 | class process { 87 | public: 88 | process() = default; 89 | 90 | // this is explicitly not explicit: 91 | // we want to be able to pass a HANDLE where a process is expected 92 | // note: handle requires atleast PROCESS_QUERY_LIMITED_INFORMATION 93 | // but without PROCESS_VM_READ most functions wont work 94 | process(HANDLE handle); 95 | 96 | // opens a handle to a process 97 | explicit process(uint32_t pid); 98 | 99 | // get a process with it's name 100 | // note: an invalid process will be returned if 0 or more 101 | // than 1 processes are found with a matching name 102 | // if force is true, it will return the first process found 103 | // when multiple processes match the provided name 104 | explicit process(std::wstring_view name, bool force = false); 105 | 106 | // we cant copy but we can move 107 | process(process&& other) noexcept; 108 | process& operator=(process&& other) noexcept; 109 | 110 | // close our handle if we were the one that opened it 111 | FRONG_VIRTUAL ~process(); 112 | 113 | // is this process class initialized? 114 | bool valid() const noexcept; 115 | explicit operator bool() const noexcept; 116 | 117 | // 32 or 64 bit code 118 | bool x86() const noexcept; 119 | bool x64() const noexcept; 120 | 121 | // is this process running under wow64? 122 | bool wow64() const noexcept; 123 | 124 | // return the underlying handle 125 | HANDLE handle() const noexcept; 126 | 127 | // get the process's pid 128 | uint32_t pid() const noexcept; 129 | 130 | // get the name of the process 131 | std::wstring name() const; 132 | 133 | // get the address of the peb 134 | template 135 | void* peb_addr() const noexcept; 136 | 137 | // read the peb 138 | template 139 | auto peb() const; 140 | 141 | // get the address of the kernel eprocess structure for this process 142 | void* eprocess() const; 143 | 144 | // get a single module 145 | template 146 | std::optional module(std::wstring_view name, std::wstring_view parent = L"") const; 147 | 148 | // calls module() with PtrSize set to 4 if x86() or 8 if x64() 149 | std::optional module(std::wstring_view name, std::wstring_view parent = L"") const; 150 | 151 | // get a list of loaded modules 152 | template 153 | size_t modules(OutIt dest) const; 154 | 155 | // calls modules() with PtrSize set to 4 if x86() or 8 if x64() 156 | template 157 | size_t modules(OutIt dest) const; 158 | 159 | // returns an std::vector of loaded modules 160 | template 161 | std::vector> modules() const; 162 | 163 | // calls modules() with PtrSize set to 4 if x86() or 8 if x64() 164 | std::vector> modules() const; 165 | 166 | // get a list of threads running under the process 167 | template 168 | size_t threads(OutIt dest) const; 169 | 170 | // returns an std::vector of threads 171 | std::vector threads() const; 172 | 173 | // get a list of handles that this process has open 174 | template 175 | size_t handles(OutIt dest) const; 176 | 177 | // returns an std::vector of handles 178 | std::vector handles() const; 179 | 180 | // external GetProcAddress() 181 | template 182 | void* get_proc_addr(std::wstring_view mod_name, char const* name) const; 183 | 184 | // calls get_proc_addr() with PtrSize set to 4 if x86() or 8 if x64() 185 | void* get_proc_addr(std::wstring_view mod_name, char const* name) const; 186 | 187 | // read/write memory (returns the number of bytes read/written) 188 | FRONG_VIRTUAL size_t read(void const* address, void* buffer, size_t size) const; 189 | FRONG_VIRTUAL size_t write(void* address, void const* buffer, size_t size) const; 190 | 191 | // allocate virtual memory in the process 192 | FRONG_VIRTUAL void* allocate(size_t size, uint32_t protection) const; 193 | 194 | // free memory returned from allocate() 195 | FRONG_VIRTUAL void free(void* address) const; 196 | 197 | // wrapper for read() 198 | template 199 | T read(void const* address, size_t* bytes_read = nullptr) const; 200 | 201 | // wrapper for write() 202 | template 203 | size_t write(void* address, T const& value) const; 204 | 205 | // allocate read/write memory in the process 206 | void* allocate(size_t size) const; 207 | 208 | private: 209 | // ***** 210 | // WARNING: any new instance variable must also be added in the move 211 | // assignment operator or else bad things will happen! 212 | // ***** 213 | 214 | HANDLE handle_ = nullptr; 215 | 216 | // the process id 217 | uint32_t pid_ = 0; 218 | 219 | // process env block 220 | void* peb32_address_ = nullptr, 221 | *peb64_address_ = nullptr; 222 | 223 | // should we close the handle ourselves? 224 | bool close_handle_ = false; 225 | 226 | // x64 vs x86 227 | bool x64_ = false; 228 | 229 | // a 32-bit process running on a 64-bit machine 230 | bool wow64_ = false; 231 | 232 | private: 233 | // no copying -_- 234 | process(process const&) = delete; 235 | process& operator=(process const&) = delete; 236 | 237 | // cache some stuff 238 | void initialize(); 239 | 240 | // call callback() for every module loaded 241 | template 242 | void iterate_modules(Callback&& callback) const; 243 | 244 | // is this an api set schema dll? 245 | static bool is_api_set_schema(std::wstring_view name); 246 | 247 | // resolve an api stub dll to it's real dll 248 | static std::wstring resolve_api_name(std::wstring_view name, std::wstring_view parent = L""); 249 | 250 | // if constexpr is so fucking shit pls fix 251 | template 252 | static nt::API_SET_NAMESPACE* get_api_set_map(); 253 | }; 254 | 255 | // iterate over every process 256 | template 257 | void iterate_processes(Callback&& callback); 258 | 259 | // get the pids that have the target process name 260 | template 261 | size_t pids_from_name(std::wstring_view name, OutIt dest); 262 | 263 | // returns a vector of pids 264 | std::vector pids_from_name(std::wstring_view name); 265 | 266 | 267 | // 268 | // 269 | // implementation below 270 | // 271 | // 272 | 273 | 274 | // iterate over every process 275 | template 276 | inline void iterate_processes(Callback&& callback) { 277 | DWORD count = 0; 278 | PWTS_PROCESS_INFOW processes = nullptr; 279 | 280 | // query every active process 281 | if (!WTSEnumerateProcessesW(WTS_CURRENT_SERVER_HANDLE, 0, 1, &processes, &count)) { 282 | FRONG_DEBUG_ERROR("Call to WTSEnumerateProcessesA failed."); 283 | return; 284 | } 285 | 286 | for (size_t i = 0; i < count; ++i) { 287 | if (!callback(processes[i].pProcessName, processes[i].ProcessId)) 288 | break; 289 | } 290 | 291 | WTSFreeMemory(processes); 292 | } 293 | 294 | // get the pids that have the target process name 295 | template 296 | inline size_t pids_from_name(std::wstring_view const name, OutIt dest) { 297 | size_t count = 0; 298 | 299 | iterate_processes([&](wchar_t const* const n, uint32_t const pid) { 300 | // does the name match? 301 | if (name.empty() || 0 == _wcsncoll(n, name.data(), name.size())) 302 | (count++, dest) = pid; 303 | 304 | return true; 305 | }); 306 | 307 | return count; 308 | } 309 | 310 | // returns a vector of pids 311 | inline std::vector pids_from_name(std::wstring_view const name) { 312 | std::vector pids; 313 | pids_from_name(name, back_inserter(pids)); 314 | return pids; 315 | } 316 | 317 | // constructor 318 | inline module::module(void* base, process const& proc) : base_(base) { 319 | auto const nth = ntheader(proc); 320 | 321 | // image size 322 | size_ = proc.read(nth + 323 | offsetof(IMAGE_NT_HEADERS, OptionalHeader) + 324 | offsetof(IMAGE_OPTIONAL_HEADER, SizeOfImage)); 325 | 326 | // 64 or 32 bit 327 | x64_ = IMAGE_FILE_MACHINE_AMD64 == 328 | proc.read(nth + 329 | offsetof(IMAGE_NT_HEADERS, FileHeader) + 330 | offsetof(IMAGE_FILE_HEADER, Machine)); 331 | } 332 | 333 | // the base address of the module in memory 334 | inline void* module::base() const { 335 | return base_; 336 | } 337 | 338 | // the size of the module 339 | inline size_t module::size() const { 340 | return size_; 341 | } 342 | 343 | // name of the module 344 | inline std::wstring module::name(process const& proc) const { 345 | // cancer cuz im sick of templates 346 | auto const dir = proc.read(cast_ptr(ntheader(proc) + 347 | offsetof(IMAGE_NT_HEADERS, OptionalHeader) + (x64() ? 348 | offsetof(IMAGE_OPTIONAL_HEADER64, DataDirectory) : 349 | offsetof(IMAGE_OPTIONAL_HEADER32, DataDirectory)))); 350 | 351 | if (!dir.VirtualAddress) { 352 | FRONG_DEBUG_WARNING("Module has no export directory: 0x%p", base_); 353 | return L""; 354 | } 355 | 356 | auto const data = proc.read((uint8_t*)base_ + dir.VirtualAddress); 357 | 358 | if (!data.Name) { 359 | FRONG_DEBUG_WARNING("Module has no export name: 0x%p", base_); 360 | return L""; 361 | } 362 | 363 | char buffer[256] = { 0 }; 364 | proc.read((uint8_t*)base_ + data.Name, buffer, sizeof(buffer) - 1); 365 | 366 | // convert to wstring for convenience 367 | return std::wstring(std::begin(buffer), std::begin(buffer) + strlen(buffer)); 368 | } 369 | 370 | // 32 or 64 bit 371 | inline bool module::x86() const { 372 | return !x64_; 373 | } 374 | inline bool module::x64() const { 375 | return x64_; 376 | } 377 | 378 | // get all of the module's exports 379 | template 380 | inline size_t module::exports(process const& proc, OutIt dest) const { 381 | size_t count = 0; 382 | 383 | // name of this module, for resolving forwarded exports 384 | auto const module_name = name(proc); 385 | 386 | auto const callback = [&](char const* name, 387 | uint16_t ordinal, void* address, char const* forwarder) { 388 | 389 | if (forwarder) 390 | address = resolve_forwarder(proc, module_name, forwarder); 391 | 392 | (count++, dest++) = module_export{ 393 | name, 394 | address, 395 | ordinal 396 | }; 397 | 398 | return true; 399 | }; 400 | 401 | x86() ? 402 | iterate_exports<4>(proc, callback) : 403 | iterate_exports<8>(proc, callback); 404 | 405 | return count; 406 | } 407 | 408 | // returns a vector of exports 409 | inline std::vector module::exports(process const& proc) const { 410 | std::vector vec; 411 | exports(proc, back_inserter(vec)); 412 | return vec; 413 | } 414 | 415 | // get the address of an export 416 | inline void* module::get_proc_addr(process const& proc, char const* name) const { 417 | void* proc_addr = nullptr; 418 | 419 | // search for matching export 420 | auto const callback = [&](char const* routine, 421 | uint16_t ordinal, void* address, char const* forwarder) { 422 | if (((uintptr_t)name & ~0xFFFF) == 0) { 423 | // search by ordinal 424 | if (name != (void*)ordinal) 425 | return true; 426 | } else { 427 | // search by name 428 | if (strcmp(routine, name)) 429 | return true; 430 | } 431 | 432 | proc_addr = forwarder ? resolve_forwarder( 433 | proc, this->name(proc), forwarder) : address; 434 | 435 | return false; 436 | }; 437 | 438 | x86() ? 439 | iterate_exports<4>(proc, callback) : 440 | iterate_exports<8>(proc, callback); 441 | 442 | return proc_addr; 443 | } 444 | 445 | // the address of the IMAGE_NT_HEADERS 446 | inline uint8_t* module::ntheader(process const& proc) const { 447 | FRONG_ASSERT(base_ != nullptr); 448 | 449 | // base + IMAGE_DOS_HEADER::e_lfanew 450 | return (uint8_t*)base_ + proc.read( 451 | (uint8_t*)base_ + offsetof(IMAGE_DOS_HEADER, e_lfanew)); 452 | } 453 | 454 | // iterate over every export in a loaded module 455 | template 456 | inline void module::iterate_exports(process const& proc, Callback&& callback) const { 457 | // read the nt header 458 | auto const nth = proc.read>(ntheader(proc)); 460 | 461 | auto const export_data = nth.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; 462 | 463 | // cmon bruh 464 | if (export_data.Size <= 0) 465 | return; 466 | 467 | auto const export_buffer = std::make_unique(export_data.Size); 468 | 469 | // read everything at once for huge performance gains 470 | proc.read((uint8_t*)base_ + export_data.VirtualAddress, 471 | export_buffer.get(), export_data.Size); 472 | 473 | auto const export_dir = (PIMAGE_EXPORT_DIRECTORY)export_buffer.get(); 474 | 475 | // parallel arrays 476 | auto const ordinals = (uint16_t*)(&export_buffer[ 477 | export_dir->AddressOfNameOrdinals - export_data.VirtualAddress]); 478 | auto const name_rvas = (uint32_t*)(&export_buffer[ 479 | export_dir->AddressOfNames - export_data.VirtualAddress]); 480 | auto const function_rvas = (uint32_t*)(&export_buffer[ 481 | export_dir->AddressOfFunctions - export_data.VirtualAddress]); 482 | 483 | // iterate over every export 484 | for (size_t i = 0; i < export_dir->NumberOfNames; ++i) { 485 | // the exported function's name 486 | auto const name = (char const*)( 487 | &export_buffer[name_rvas[i] - export_data.VirtualAddress]); 488 | 489 | // the index into the address array 490 | auto const ordinal = ordinals[i]; 491 | 492 | // rva to the exported function 493 | auto const func_rva = function_rvas[ordinal]; 494 | 495 | // does func_rva point to an import name instead 496 | auto const is_forwarder = func_rva >= export_data.VirtualAddress && 497 | func_rva < export_data.VirtualAddress + export_data.Size; 498 | 499 | // the string in our memory space 500 | auto const forwarder_name = is_forwarder ? (char const*)&export_buffer[ 501 | func_rva - export_data.VirtualAddress] : nullptr; 502 | 503 | // call the callback 504 | if (!callback(name, (uint16_t)export_dir->Base + 505 | ordinal, (uint8_t*)base_ + func_rva, forwarder_name)) 506 | break; 507 | } 508 | } 509 | 510 | // finds a forwarder export 511 | inline void* module::resolve_forwarder(process const& proc, 512 | std::wstring_view parent, std::string_view forwarder) const { 513 | auto const split = forwarder.find_first_of('.'); 514 | 515 | // the name of the module (without the .dll extension) 516 | std::wstring module_name(begin(forwarder), begin(forwarder) + split); 517 | module_name += L".dll"; 518 | 519 | // the name of the import 520 | auto const import_name = forwarder.data() + split + 1; 521 | 522 | auto const mod = x86() ? 523 | proc.module<4>(module_name, parent) : 524 | proc.module<8>(module_name, parent); 525 | 526 | // rip 527 | if (!mod) 528 | return nullptr; 529 | 530 | if (import_name[0] == '#') 531 | // resolve by ordinal 532 | return mod->get_proc_addr(proc, (char const*)(uintptr_t)atoi(import_name + 1)); 533 | 534 | // resolve by name 535 | return mod->get_proc_addr(proc, import_name); 536 | } 537 | 538 | // this is explicitly not explicit: 539 | // we want to be able to pass a HANDLE where a process is expected 540 | // note: handle requires atleast PROCESS_QUERY_LIMITED_INFORMATION 541 | // but without PROCESS_VM_READ most functions wont work 542 | inline process::process(HANDLE const handle) 543 | : handle_(handle), close_handle_(false) { 544 | initialize(); 545 | } 546 | 547 | // opens a handle to a process 548 | inline process::process(uint32_t const pid) { 549 | static auto constexpr access = 550 | PROCESS_VM_READ | 551 | PROCESS_VM_WRITE | 552 | PROCESS_VM_OPERATION | 553 | PROCESS_QUERY_LIMITED_INFORMATION; 554 | 555 | handle_ = OpenProcess(access, FALSE, pid); 556 | 557 | // rip 558 | if (!handle_) { 559 | FRONG_DEBUG_ERROR("Failed to open process with pid %u", pid); 560 | return; 561 | } 562 | 563 | close_handle_ = true; 564 | 565 | initialize(); 566 | } 567 | 568 | // get a process with it's name 569 | // note: an invalid process will be returned if 0 or more 570 | // than 1 processes are found with a matching name 571 | // if force is true, it will return the first process found 572 | // when multiple processes match the provided name 573 | inline process::process(std::wstring_view const name, bool const force) { 574 | auto const pids = pids_from_name(name); 575 | 576 | // process not found 577 | if (pids.empty()) { 578 | FRONG_DEBUG_WARNING("No processes found with the name \"%.*ws\"", 579 | (int)name.size(), name.data()); 580 | return; 581 | } 582 | 583 | // nore than one matching process found 584 | if (pids.size() > 1 && !force) { 585 | FRONG_DEBUG_WARNING("Multiple processes found with the name \"%.*ws\"", 586 | (int)name.size(), name.data()); 587 | return; 588 | } 589 | 590 | *this = process(pids.front()); 591 | } 592 | 593 | // we cant copy but we can move 594 | inline process::process(process&& other) noexcept { 595 | *this = std::forward(other); 596 | } 597 | inline process& process::operator=(process&& other) noexcept { 598 | // we can just swap all our instance variables since other's destructor 599 | // will cleanup our (old) process instance for us :) 600 | std::swap(handle_, other.handle_); 601 | std::swap(pid_, other.pid_); 602 | std::swap(peb32_address_, other.peb32_address_); 603 | std::swap(peb64_address_, other.peb64_address_); 604 | std::swap(close_handle_, other.close_handle_); 605 | std::swap(x64_, other.x64_); 606 | std::swap(wow64_, other.wow64_); 607 | 608 | return *this; 609 | } 610 | 611 | // close our handle if we were the one that opened it 612 | inline process::~process() { 613 | if (valid() && close_handle_) 614 | CloseHandle(handle_); 615 | } 616 | 617 | // is this process class initialized? 618 | inline bool process::valid() const noexcept { 619 | return handle_ != nullptr; 620 | } 621 | inline process::operator bool() const noexcept { 622 | return valid(); 623 | } 624 | 625 | // 32 or 64 bit code 626 | inline bool process::x86() const noexcept { 627 | return !x64_; 628 | } 629 | inline bool process::x64() const noexcept { 630 | return x64_; 631 | } 632 | 633 | // is this process running under wow64? 634 | inline bool process::wow64() const noexcept { 635 | return wow64_; 636 | } 637 | 638 | // return the underlying handle 639 | inline HANDLE process::handle() const noexcept { 640 | return handle_; 641 | } 642 | 643 | // get the process's pid 644 | inline uint32_t process::pid() const noexcept { 645 | return pid_; 646 | } 647 | 648 | // get the name of the process 649 | inline std::wstring process::name() const { 650 | wchar_t buffer[512] = { L'\0' }; 651 | unsigned long size = 512; 652 | 653 | // query the process path 654 | if (!QueryFullProcessImageNameW(handle_, 0, buffer, &size)) 655 | return L""; 656 | 657 | // trim the start of the path 658 | for (int i = size - 2; i >= 0; --i) { 659 | if (buffer[i] == L'\\' || buffer[i] == L'/') 660 | return &buffer[i + 1]; 661 | } 662 | 663 | return buffer; 664 | } 665 | 666 | // get the address of the peb 667 | template 668 | inline void* process::peb_addr() const noexcept { 669 | FRONG_ASSERT(PtrSize == 4 || PtrSize == 8); 670 | 671 | if constexpr (PtrSize == 8) { 672 | FRONG_ASSERT(sizeof(void*) == 8); 673 | return peb64_address_; 674 | } else { 675 | FRONG_ASSERT(x86()); 676 | return peb32_address_; 677 | } 678 | } 679 | 680 | // read the peb 681 | template 682 | inline auto process::peb() const { 683 | return read>(peb_addr()); 684 | } 685 | 686 | // get the address of the kernel eprocess structure for this process 687 | inline void* process::eprocess() const { 688 | void* address = nullptr; 689 | 690 | // the handle we're searching for 691 | auto search_handle = handle_; 692 | 693 | // this wont work with pseudo handles, so we need to create a real one 694 | if (handle_ == GetCurrentProcess()) { 695 | DuplicateHandle(handle_, handle_, handle_, &search_handle, 696 | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, 0); 697 | } 698 | 699 | auto const current_process_id = GetCurrentProcessId(); 700 | 701 | iterate_handles([&](handle_info const& info) { 702 | // we only care about handles that WE own 703 | if (info.pid != current_process_id) 704 | return true; 705 | 706 | // we're searching for the open handle to the process 707 | if (info.handle != search_handle) 708 | return true; 709 | 710 | // we found the target handle 711 | address = info.object; 712 | return false; 713 | }); 714 | 715 | // free the handle, if we opened it 716 | if (search_handle != handle_) 717 | CloseHandle(search_handle); 718 | 719 | return address; 720 | } 721 | 722 | // get a single module 723 | template 724 | inline std::optional process::module( 725 | std::wstring_view const name, std::wstring_view const parent) const { 726 | FRONG_ASSERT(!name.empty()); 727 | 728 | std::wstring real_name(name); 729 | 730 | // api redirection 731 | if (is_api_set_schema(name)) 732 | real_name = resolve_api_name(name, parent); 733 | 734 | std::optional found_module = {}; 735 | 736 | iterate_modules([&](std::wstring&& path, void* base) { 737 | // not big enough 738 | if (path.size() < real_name.size()) 739 | return true; 740 | 741 | auto const bslash = path.find_last_of(L'\\'); 742 | auto const fslash = path.find_last_of(L'/'); 743 | 744 | auto n = std::wstring_view(path); 745 | 746 | // we only want the name 747 | if (bslash != n.npos || fslash != n.npos) { 748 | if (bslash != n.npos && fslash != n.npos) 749 | n = n.substr(max(fslash, bslash) + 1); 750 | else if (fslash != n.npos) 751 | n = n.substr(fslash + 1); 752 | else 753 | n = n.substr(bslash + 1); 754 | } 755 | 756 | if (n.size() != real_name.size()) 757 | return true; 758 | 759 | // does the name match? 760 | if (0 == _wcsnicmp(n.data(), real_name.data(), real_name.size())) { 761 | found_module = frg::module(base, *this); 762 | return false; 763 | } 764 | 765 | return true; 766 | }); 767 | 768 | // rip 769 | if (!found_module) { 770 | FRONG_DEBUG_WARNING("Failed to find module: %.*ws", 771 | (int)name.size(), name.data()); 772 | } 773 | 774 | return found_module; 775 | } 776 | 777 | // calls module() with PtrSize set to 4 if x86() or 8 if x64() 778 | inline std::optional process::module( 779 | std::wstring_view const name, std::wstring_view const parent) const { 780 | return x64() ? module<8>(name, parent) : module<4>(name, parent); 781 | } 782 | 783 | // get a list of loaded modules 784 | template 785 | inline size_t process::modules(OutIt dest) const { 786 | size_t count = 0; 787 | 788 | // iterate over every module and add it to the list 789 | iterate_modules([&](std::wstring&& path, void* base) { 790 | (count++, dest++) = std::pair(std::move(path), frg::module(base, *this)); 791 | return true; 792 | }); 793 | 794 | return count; 795 | } 796 | 797 | // calls modules() with PtrSize set to 4 if x86() or 8 if x64() 798 | template 799 | inline size_t process::modules(OutIt dest) const { 800 | return x64() ? modules<8>(dest) : modules<4>(dest); 801 | } 802 | 803 | // returns an std::vector of loaded modules 804 | template 805 | inline std::vector> process::modules() const { 807 | std::vector> vec; 808 | modules(back_inserter(vec)); 809 | return vec; 810 | } 811 | 812 | // calls modules() with PtrSize set to 4 if x86() or 8 if x64() 813 | inline std::vector> process::modules() const { 815 | return x64() ? modules<8>() : modules<4>(); 816 | } 817 | 818 | // get a list of threads running under the process 819 | template 820 | inline size_t process::threads(OutIt dest) const { 821 | size_t count = 0; 822 | 823 | // create a snapshot of all current threads 824 | const auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); 825 | 826 | if (snapshot == INVALID_HANDLE_VALUE) { 827 | FRONG_DEBUG_WARNING("Invalid thread snapshot handle."); 828 | return 0; 829 | } 830 | 831 | THREADENTRY32 entry{ sizeof(THREADENTRY32) }; 832 | 833 | // get the first thread 834 | if (!Thread32First(snapshot, &entry)) { 835 | FRONG_DEBUG_WARNING("Failed to get first thread entry."); 836 | return 0; 837 | } 838 | 839 | // iterate through the rest of the threads 840 | do { 841 | if (entry.th32OwnerProcessID != pid_) 842 | continue; 843 | 844 | (count++, dest++) = thread(entry.th32ThreadID); 845 | } while (Thread32Next(snapshot, &entry)); 846 | 847 | return count; 848 | } 849 | 850 | // returns an std::vector of threads 851 | inline std::vector process::threads() const { 852 | std::vector vec; 853 | threads(back_inserter(vec)); 854 | return vec; 855 | } 856 | 857 | // get a list of handles that this process has open 858 | template 859 | inline size_t process::handles(OutIt dest) const { 860 | size_t count = 0; 861 | 862 | iterate_handles([&](handle_info const& info) { 863 | if (info.pid == pid_) 864 | (count++, dest++) = info; 865 | 866 | return true; 867 | }); 868 | 869 | return count; 870 | } 871 | 872 | // returns an std::vector of handles 873 | inline std::vector process::handles() const { 874 | std::vector vec; 875 | handles(back_inserter(vec)); 876 | return vec; 877 | } 878 | 879 | // external GetProcAddress() 880 | template 881 | inline void* process::get_proc_addr(std::wstring_view const mod_name, char const* const name) const { 882 | auto const mod = module(mod_name); 883 | 884 | if (!mod) { 885 | FRONG_DEBUG_WARNING("Failed to find module: %.*ws", 886 | (int)mod_name.size(), mod_name.data()); 887 | return nullptr; 888 | } 889 | 890 | return mod->get_proc_addr(*this, name); 891 | } 892 | 893 | // calls get_proc_addr() with PtrSize set to 4 if x86() or 8 if x64() 894 | inline void* process::get_proc_addr(std::wstring_view const mod_name, char const* const name) const { 895 | return x86() ? 896 | get_proc_addr<4>(mod_name, name) : 897 | get_proc_addr<8>(mod_name, name); 898 | } 899 | 900 | // read/write memory (returns the number of bytes read/written) 901 | inline size_t process::read(void const* const address, 902 | void* const buffer, size_t const size) const { 903 | FRONG_ASSERT(buffer != nullptr); 904 | FRONG_ASSERT(address != nullptr); 905 | 906 | if (size <= 0) 907 | return 0; 908 | 909 | SIZE_T bytes_read = 0; 910 | if (!ReadProcessMemory(handle_, address, buffer, size, &bytes_read) 911 | && GetLastError() != ERROR_PARTIAL_COPY) { 912 | FRONG_DEBUG_ERROR("Failed to read memory at address %p.", address); 913 | return 0; 914 | } 915 | 916 | return bytes_read; 917 | } 918 | inline size_t process::write(void* const address, 919 | void const* const buffer, size_t const size) const { 920 | FRONG_ASSERT(buffer != nullptr); 921 | FRONG_ASSERT(address != nullptr); 922 | 923 | if (size <= 0) 924 | return 0; 925 | 926 | SIZE_T bytes_written = 0; 927 | if (!WriteProcessMemory(handle_, address, buffer, size, &bytes_written) 928 | && GetLastError() != ERROR_PARTIAL_COPY) { 929 | FRONG_DEBUG_ERROR("Failed to write memory at address %p.", address); 930 | return 0; 931 | } 932 | 933 | return bytes_written; 934 | } 935 | 936 | // allocate virtual memory in the process 937 | inline void* process::allocate(size_t const size, uint32_t const protection) const { 938 | FRONG_ASSERT(size > 0); 939 | 940 | return VirtualAllocEx(handle_, nullptr, size, MEM_COMMIT | MEM_RESERVE, protection); 941 | } 942 | 943 | // free memory returned from allocate() 944 | inline void process::free(void* const address) const { 945 | FRONG_ASSERT(address != nullptr); 946 | 947 | VirtualFreeEx(handle_, address, 0, MEM_RELEASE); 948 | } 949 | 950 | // wrapper for read() 951 | template 952 | inline T process::read(void const* address, size_t* bytes_read) const { 953 | T buffer{}; 954 | 955 | if (bytes_read) 956 | *bytes_read = read(address, &buffer, sizeof(buffer)); 957 | else 958 | read(address, &buffer, sizeof(buffer)); 959 | 960 | return buffer; 961 | } 962 | 963 | // wrapper for write() 964 | template 965 | inline size_t process::write(void* const address, T const& value) const { 966 | return write(address, &value, sizeof(value)); 967 | } 968 | 969 | // allocate read/write memory in the process 970 | inline void* process::allocate(size_t const size) const { 971 | return allocate(size, PAGE_READWRITE); 972 | } 973 | 974 | // cache some stuff 975 | inline void process::initialize() { 976 | pid_ = GetProcessId(handle_); 977 | 978 | // is this process running under wow64? 979 | BOOL wow64 = FALSE; 980 | if (!IsWow64Process(handle_, &wow64)) 981 | FRONG_DEBUG_WARNING("Call to IsWow64Process failed."); 982 | 983 | wow64_ = wow64 != FALSE; 984 | 985 | if (wow64_) { 986 | // a 32-bit program on a 64-bit machine 987 | x64_ = false; 988 | } else { 989 | // what architecture is this machine? 990 | SYSTEM_INFO info; 991 | GetNativeSystemInfo(&info); 992 | x64_ = (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64); 993 | } 994 | 995 | // dont "attach" to a 64-bit process from a 32-bit process 996 | FRONG_ASSERT(sizeof(void*) == 8 || !x64()); 997 | 998 | PROCESS_BASIC_INFORMATION info{}; 999 | 1000 | // for the peb 1001 | if (NT_ERROR(nt::NtQueryInformationProcess(handle_, 1002 | ProcessBasicInformation, &info, sizeof(info), nullptr))) 1003 | FRONG_DEBUG_ERROR("Failed to query basic process information."); 1004 | 1005 | if constexpr (sizeof(void*) == 8) { 1006 | peb64_address_ = info.PebBaseAddress; 1007 | 1008 | if (wow64_) { 1009 | // get the 32bit peb as well 1010 | if (NT_ERROR(nt::NtQueryInformationProcess(handle_, 1011 | ProcessWow64Information, &peb32_address_, sizeof(peb32_address_), nullptr))) 1012 | FRONG_DEBUG_ERROR("Failed to query wow64 information."); 1013 | } 1014 | } else { 1015 | peb32_address_ = info.PebBaseAddress; 1016 | } 1017 | } 1018 | 1019 | // call callback() for every module loaded 1020 | template 1021 | inline void process::iterate_modules(Callback&& callback) const { 1022 | using ldr_data = nt::PEB_LDR_DATA; 1023 | using ldr_entry = nt::LDR_DATA_TABLE_ENTRY; 1024 | using list_entry = nt::LIST_ENTRY; 1025 | 1026 | // the address of PEB::Ldr::InMemoryOrderModuleList 1027 | auto const list_head = peb().Ldr + 1028 | offsetof(ldr_data, InMemoryOrderModuleList); 1029 | 1030 | // first entry 1031 | auto current = read(cast_ptr(list_head)).Flink; 1032 | 1033 | // iterate over the linked list 1034 | while (current != list_head) { 1035 | // basically just CONTAINING_RECORD(current, ldr_entry, InMemoryOrderLinks) 1036 | auto const entry = read(cast_ptr( 1037 | current - offsetof(ldr_entry, InMemoryOrderLinks))); 1038 | 1039 | // create a std::wstring object big enough to hold the dll name 1040 | std::wstring fullpath(entry.FullDllName.Length / 2, L' '); 1041 | 1042 | // read the full dll path 1043 | auto const bytes_read = read( 1044 | cast_ptr(entry.FullDllName.Buffer), 1045 | fullpath.data(), 1046 | entry.FullDllName.Length); 1047 | 1048 | // error reading memory 1049 | if (bytes_read != entry.FullDllName.Length) { 1050 | FRONG_DEBUG_WARNING("Failed to read module's FullDllName."); 1051 | continue; 1052 | } 1053 | 1054 | // process this module 1055 | if (!callback(std::move(fullpath), cast_ptr(entry.DllBase))) 1056 | break; 1057 | 1058 | // go to the next node 1059 | current = read(cast_ptr(current)).Flink; 1060 | } 1061 | } 1062 | 1063 | // is this an api set schema dll? 1064 | inline bool process::is_api_set_schema(std::wstring_view const name) { 1065 | // cmon bruh 1066 | if (name.size() < 4) 1067 | return false; 1068 | 1069 | return (0 == _wcsnicmp(name.data(), L"api-", 4)) || 1070 | (0 == _wcsnicmp(name.data(), L"ext-", 4)); 1071 | } 1072 | 1073 | // resolve an api stub dll to it's real dll 1074 | inline std::wstring process::resolve_api_name(std::wstring_view name, std::wstring_view const parent) { 1075 | FRONG_ASSERT(is_api_set_schema(name)); 1076 | 1077 | // remove everything past the last version number 1078 | // e.g. api-ms-win-core-apiquery-l1-1-0.dll -> api-ms-win-core-apiquery-l1-1 1079 | if (auto const end = name.find_last_of(L'-'); end != name.npos) 1080 | name = name.substr(0, end); 1081 | 1082 | // should be the same for every process 1083 | auto const map = get_api_set_map(); 1084 | FRONG_ASSERT(map->Version == 6); 1085 | 1086 | auto const entries = (nt::API_SET_NAMESPACE_ENTRY*)( 1087 | (uint8_t*)map + map->EntryOffset); 1088 | 1089 | for (size_t i = 0; i < map->Count; ++i) { 1090 | auto const& entry = entries[i]; 1091 | 1092 | // cmon bruh 1093 | if (entry.ValueCount <= 0) 1094 | continue; 1095 | 1096 | // prob not null terminated idfk 1097 | auto const entry_name = (wchar_t*)((uint8_t*)map + entry.NameOffset); 1098 | size_t name_size = entry.NameLength / 2; 1099 | 1100 | // find the real size 1101 | for (size_t j = 0; j < name_size; ++j) { 1102 | if (entry_name[name_size - j - 1] != L'-') 1103 | continue; 1104 | 1105 | name_size = name_size - j - 1; 1106 | break; 1107 | } 1108 | 1109 | // name doesn't match 1110 | if (name.size() != name_size || _wcsnicmp(entry_name, name.data(), name.size())) 1111 | continue; 1112 | 1113 | auto const values = (nt::API_SET_VALUE_ENTRY*)( 1114 | (uint8_t*)map + entry.ValueOffset); 1115 | 1116 | // default host 1117 | auto value = &values[0]; 1118 | 1119 | // maybe we don't want the default host 1120 | if (entry.ValueCount > 1 && !parent.empty()) { 1121 | // search for matching parents 1122 | for (size_t j = 1; j < entry.ValueCount; ++j) { 1123 | std::wstring_view const pname((wchar_t const*)((uint8_t*)map + 1124 | values[j].NameOffset), values[j].NameLength / 2); 1125 | 1126 | // name doesn't match 1127 | if (pname.size() != parent.size() || _wcsnicmp( 1128 | pname.data(), parent.data(), parent.size())) 1129 | continue; 1130 | 1131 | value = &values[j]; 1132 | break; 1133 | } 1134 | } 1135 | 1136 | return std::wstring((wchar_t*)((uint8_t*)map + 1137 | value->ValueOffset), value->ValueLength / 2); 1138 | } 1139 | 1140 | FRONG_DEBUG_WARNING("Failed to resolve api: %.*ws", 1141 | (int)name.size(), name.data()); 1142 | return L""; 1143 | } 1144 | 1145 | // if constexpr is so fucking shit pls fix 1146 | template 1147 | inline nt::API_SET_NAMESPACE* process::get_api_set_map() { 1148 | if constexpr (PtrSize == 8) { 1149 | return (nt::API_SET_NAMESPACE*)((nt::PEB*) 1150 | __readgsqword(0x60 + PtrSize - PtrSize))->ApiSetMap; 1151 | } else { 1152 | return (nt::API_SET_NAMESPACE*)((nt::PEB*) 1153 | __readfsdword(0x30 + PtrSize - PtrSize))->ApiSetMap; 1154 | } 1155 | } 1156 | 1157 | } // namespace frg --------------------------------------------------------------------------------