├── .clang-format ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.md ├── README.md ├── include ├── nthread.h ├── ntmem.h ├── nttunnel.h ├── ntucc.h └── ntutils.h ├── modulerules ├── 20_ntutils.h └── nthread_rules.h ├── src ├── nthread.c ├── ntmem.c ├── nttunnel.c └── ntutils.c └── tests ├── CMakeLists.txt └── inject.c /.clang-format: -------------------------------------------------------------------------------- 1 | 2 | # SPDX-License-Identifier: GPL-2.0 3 | # 4 | # clang-format configuration file. Intended for clang-format >= 11. 5 | # 6 | # For more information, see: 7 | # 8 | # Documentation/dev-tools/clang-format.rst 9 | # https://clang.llvm.org/docs/ClangFormat.html 10 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html 11 | # 12 | --- 13 | AccessModifierOffset: -4 14 | AlignAfterOpenBracket: Align 15 | AlignConsecutiveAssignments: false 16 | AlignConsecutiveDeclarations: false 17 | AlignEscapedNewlines: Left 18 | AlignOperands: true 19 | AlignTrailingComments: false 20 | AllowAllParametersOfDeclarationOnNextLine: false 21 | AllowShortBlocksOnASingleLine: false 22 | AllowShortCaseLabelsOnASingleLine: false 23 | AllowShortFunctionsOnASingleLine: None 24 | AllowShortIfStatementsOnASingleLine: false 25 | AllowShortLoopsOnASingleLine: false 26 | AlwaysBreakAfterDefinitionReturnType: None 27 | AlwaysBreakAfterReturnType: None 28 | AlwaysBreakBeforeMultilineStrings: false 29 | AlwaysBreakTemplateDeclarations: false 30 | BinPackArguments: true 31 | BinPackParameters: true 32 | BraceWrapping: 33 | AfterClass: false 34 | AfterControlStatement: false 35 | AfterEnum: false 36 | AfterFunction: true 37 | AfterNamespace: true 38 | AfterObjCDeclaration: false 39 | AfterStruct: false 40 | AfterUnion: false 41 | AfterExternBlock: false 42 | BeforeCatch: false 43 | BeforeElse: false 44 | IndentBraces: false 45 | SplitEmptyFunction: true 46 | SplitEmptyRecord: true 47 | SplitEmptyNamespace: true 48 | BreakBeforeBinaryOperators: None 49 | BreakBeforeBraces: Custom 50 | BreakBeforeInheritanceComma: false 51 | BreakBeforeTernaryOperators: false 52 | BreakConstructorInitializersBeforeComma: false 53 | BreakConstructorInitializers: BeforeComma 54 | BreakAfterJavaFieldAnnotations: false 55 | BreakStringLiterals: false 56 | ColumnLimit: 80 57 | CommentPragmas: '^ IWYU pragma:' 58 | CompactNamespaces: false 59 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 60 | ConstructorInitializerIndentWidth: 8 61 | ContinuationIndentWidth: 8 62 | Cpp11BracedListStyle: false 63 | DerivePointerAlignment: false 64 | DisableFormat: false 65 | ExperimentalAutoDetectBinPacking: false 66 | FixNamespaceComments: false 67 | IncludeBlocks: Preserve 68 | IncludeCategories: 69 | - Regex: '.*' 70 | Priority: 1 71 | IncludeIsMainRegex: '(Test)?$' 72 | IndentCaseLabels: false 73 | IndentGotoLabels: false 74 | IndentPPDirectives: None 75 | IndentWidth: 8 76 | IndentWrappedFunctionNames: false 77 | JavaScriptQuotes: Leave 78 | JavaScriptWrapImports: true 79 | KeepEmptyLinesAtTheStartOfBlocks: false 80 | MacroBlockBegin: '' 81 | MacroBlockEnd: '' 82 | MaxEmptyLinesToKeep: 1 83 | NamespaceIndentation: None 84 | ObjCBinPackProtocolList: Auto 85 | ObjCBlockIndentWidth: 8 86 | ObjCSpaceAfterProperty: true 87 | ObjCSpaceBeforeProtocolList: true 88 | 89 | # Taken from git's rules 90 | PenaltyBreakAssignment: 10 91 | PenaltyBreakBeforeFirstCallParameter: 30 92 | PenaltyBreakComment: 10 93 | PenaltyBreakFirstLessLess: 0 94 | PenaltyBreakString: 10 95 | PenaltyExcessCharacter: 100 96 | PenaltyReturnTypeOnItsOwnLine: 60 97 | 98 | PointerAlignment: Right 99 | ReflowComments: false 100 | SortIncludes: false 101 | SortUsingDeclarations: false 102 | SpaceAfterCStyleCast: false 103 | SpaceAfterTemplateKeyword: true 104 | SpaceBeforeAssignmentOperators: true 105 | SpaceBeforeCtorInitializerColon: true 106 | SpaceBeforeInheritanceColon: true 107 | SpaceBeforeParens: ControlStatementsExceptForEachMacros 108 | SpaceBeforeRangeBasedForLoopColon: true 109 | SpaceInEmptyParentheses: false 110 | SpacesBeforeTrailingComments: 1 111 | SpacesInAngles: false 112 | SpacesInContainerLiterals: false 113 | SpacesInCStyleCastParentheses: false 114 | SpacesInParentheses: false 115 | SpacesInSquareBrackets: false 116 | Standard: Cpp03 117 | TabWidth: 8 118 | UseTab: Always 119 | ... 120 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .cache/ 3 | .clangd 4 | compile_commands.json 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/Neptune"] 2 | path = tests/Neptune 3 | url = https://github.com/woldann/Neptune 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(NThread C) 3 | 4 | set(NTHREAD_MODULE_RULES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/modulerules) 5 | set(NTHREAD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) 6 | set(NTHREAD_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 7 | 8 | file(GLOB_RECURSE NTHREAD_HEADERS CONFIGURE_DEPENDS ${NTHREAD_INCLUDE_DIR}/*.h) 9 | file(GLOB_RECURSE NTHREAD_SOURCES CONFIGURE_DEPENDS ${NTHREAD_SOURCE_DIR}/*.c) 10 | 11 | add_library(NThread INTERFACE) 12 | 13 | target_include_directories(NThread INTERFACE ${NTHREAD_INCLUDE_DIR}) 14 | target_include_directories(NThread INTERFACE ${NTHREAD_MODULE_RULES_DIR}) 15 | target_sources(NThread INTERFACE ${NTHREAD_SOURCES}) 16 | 17 | add_subdirectory(tests) 18 | 19 | target_link_libraries(NThread INTERFACE Neptune) 20 | 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | 2 | MIT License 3 | 4 | Copyright (c) 2024, 2025 Serkan Aksoy 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NThread 2 | 3 | **NThread** is a powerful, x64-focused thread manipulation library designed to safely call functions inside target processes by leveraging their existing threads. 4 | 5 | > ⚙️ Built for stealth, flexibility, and reliability — no injections, no hooks, just pure thread register control. 6 | 7 | --- 8 | 9 | ## ✨ Features 10 | 11 | - ✅ **x64 Architecture Focused** — Designed specifically for x64 systems, currently supporting Windows x64. 12 | - 🛡️ **Stealthy Operation** — Avoids common AV/EDR triggers by using no remote memory allocation or shellcode. 13 | - 🔄 **Reversible Hijacking** — Temporarily controls target threads and restores them perfectly after use. 14 | - 🔗 **Thread-Local Storage (TLS) or Equivalent** — Maps your control threads safely to target threads for smooth multi-thread management. 15 | - ⚙️ **Flexible & Reliable** — Uses standard libc functions within the target process and supports advanced code reuse techniques. 16 | 17 | --- 18 | 19 | ## 🚫 Code Injection? Not Needed. 20 | 21 | NThread does **not** rely on traditional code injection (e.g. shellcode, `VirtualAllocEx`, `CreateRemoteThread`, etc.). 22 | Instead, it uses pre-existing threads and simple instruction sequences already present in most executables. 23 | 24 | If the target process already contains the following instruction pattern: 25 | 26 | ```assembly 27 | 0x7f0000 0x55 push rbp 28 | 0x7f0001 0xC3 ret 29 | 30 | 0x7f0050 0xEB 0xFE jmp $ 31 | ``` 32 | 33 | You can locate such an address and use it directly with `ntu_attach`: 34 | ```c 35 | ntu_attach(tid, existing_push_addr=0x7f0000, existing_jmp_addr=0x7f0050); 36 | ``` 37 | 38 | Alternatively, as demonstrated in [tests/inject.c](tests/inject.c) you can allocate this code into the target process yourself: 39 | ```c 40 | int8_t push_sleep[] = { 0x55, 0xC3, 0xEB, 0xFE }; 41 | 42 | // Allocate memory in target process and write code 43 | void *push_sleep_addr = VirtualAllocEx(...); 44 | WriteProcessMemory(..., push_sleep_addr, push_sleep, sizeof(push_sleep)); 45 | 46 | // Initialize NThread with known valid instructions 47 | ntu_attach(tid, push_sleep_addr, push_sleep_addr + 2); 48 | ``` 49 | 50 | --- 51 | 52 | ## TODO 53 | 54 | - **nttunnel** 55 | - Separate the currently intertwined `fschan` and `nttunnel` functions 56 | to create a more modular, channel-based architecture. 57 | - Enable adding different types of channels. 58 | 59 | - **ntutils** 60 | - Improve the `init` function to allow 61 | - More flexible and parameterized configurations. 62 | - Advanced initialization options. 63 | 64 | - **Linux support** 65 | - Consider developing a kernel module-based method for Linux. 66 | -------------------------------------------------------------------------------- /include/nthread.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2024, 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file nthread.h 27 | * @brief Stealth thread manipulation library for x64. 28 | * 29 | * NThread provides a safe and stealthy way to call functions inside a target process 30 | * using its own existing threads. It avoids traditional remote memory allocations 31 | * or shellcode injections, using only safe system APIs like OpenThread, 32 | * GetThreadContext, SetThreadContext. 33 | */ 34 | 35 | #ifndef __NTHREAD_H__ 36 | #define __NTHREAD_H__ 37 | 38 | #include "neptune.h" 39 | #include "nerror.h" 40 | 41 | #if !(defined(__x86_64__) || defined(__amd64__) || defined(__LP64__) || \ 42 | defined(_LP64)) 43 | #error "NThread only works on x86_64 systems." 44 | #endif 45 | 46 | #if !(defined(__WIN32) || defined(__linux__)) 47 | #error "NThread unsupported os." 48 | #endif // OS Check 49 | 50 | #ifdef __WIN32 51 | 52 | #include 53 | 54 | typedef int16_t nthread_reg_offset_t; 55 | 56 | #define WINDOWS_RAX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rax)) 57 | #define WINDOWS_RBX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rbx)) 58 | #define WINDOWS_RCX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rcx)) 59 | #define WINDOWS_RDX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rdx)) 60 | #define WINDOWS_RSI_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rsi)) 61 | #define WINDOWS_RDI_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rdi)) 62 | #define WINDOWS_R8_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R8)) 63 | #define WINDOWS_R9_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R9)) 64 | #define WINDOWS_R10_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R10)) 65 | #define WINDOWS_R11_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R11)) 66 | #define WINDOWS_R12_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R12)) 67 | #define WINDOWS_R13_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R13)) 68 | #define WINDOWS_R14_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R14)) 69 | #define WINDOWS_R15_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R15)) 70 | #define WINDOWS_RIP_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rip)) 71 | #define WINDOWS_RSP_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rsp)) 72 | #define WINDOWS_RBP_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rbp)) 73 | 74 | #define WINDOWS_BEST_PUSH_REG WINDOWS_RBP_OFFSET 75 | #define NTHREAD_BEST_PUSH_REG WINDOWS_BEST_PUSH_REG 76 | 77 | #define NTHREAD_RAX WINDOWS_RAX_OFFSET 78 | #define NTHREAD_RBX WINDOWS_RBX_OFFSET 79 | #define NTHREAD_RCX WINDOWS_RCX_OFFSET 80 | #define NTHREAD_RDX WINDOWS_RDX_OFFSET 81 | #define NTHREAD_RSI WINDOWS_RSI_OFFSET 82 | #define NTHREAD_RDI WINDOWS_RDI_OFFSET 83 | #define NTHREAD_R8 WINDOWS_R8_OFFSET 84 | #define NTHREAD_R9 WINDOWS_R9_OFFSET 85 | #define NTHREAD_R10 WINDOWS_R10_OFFSET 86 | #define NTHREAD_R11 WINDOWS_R11_OFFSET 87 | #define NTHREAD_R12 WINDOWS_R12_OFFSET 88 | #define NTHREAD_R13 WINDOWS_R13_OFFSET 89 | #define NTHREAD_R14 WINDOWS_R14_OFFSET 90 | #define NTHREAD_R15 WINDOWS_R15_OFFSET 91 | #define NTHREAD_RIP WINDOWS_RIP_OFFSET 92 | #define NTHREAD_RSP WINDOWS_RSP_OFFSET 93 | #define NTHREAD_RBP WINDOWS_RBP_OFFSET 94 | 95 | #define NTHREAD_RAX_INDEX 0x00 96 | #define NTHREAD_RCX_INDEX 0x01 97 | #define NTHREAD_RDX_INDEX 0x02 98 | #define NTHREAD_RBX_INDEX 0x03 99 | #define NTHREAD_RSP_INDEX 0x04 100 | #define NTHREAD_RBP_INDEX 0x05 101 | #define NTHREAD_RSI_INDEX 0x06 102 | #define NTHREAD_RDI_INDEX 0x07 103 | #define NTHREAD_R8_INDEX 0x08 104 | #define NTHREAD_R9_INDEX 0x09 105 | #define NTHREAD_R10_INDEX 0x0A 106 | #define NTHREAD_R11_INDEX 0x0B 107 | #define NTHREAD_R12_INDEX 0x0C 108 | #define NTHREAD_R13_INDEX 0x0D 109 | #define NTHREAD_R14_INDEX 0x0E 110 | #define NTHREAD_R15_INDEX 0x0F 111 | #define NTHREAD_RIP_INDEX 0x10 112 | 113 | typedef uint8_t nthread_flags_t; 114 | 115 | #define NTHREAD_FLAG_DONT_SUSPEND 0x10 116 | #define NTHREAD_FLAG_DONT_RESUME 0x20 117 | #define NTHREAD_FLAG_DISABLE_GET_ID 0x40 118 | 119 | #define NTHREAD_REG_INDEX_TO_OFFSET(reg_index) \ 120 | ((nthread_reg_offset_t)(NTHREAD_RAX + ((sizeof(DWORD64)) * reg_index))) 121 | 122 | #define NTHREAD_ACCESS \ 123 | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | \ 124 | THREAD_QUERY_INFORMATION 125 | 126 | typedef DWORD ntid_t; 127 | typedef HANDLE nthread_handle_t; 128 | typedef CONTEXT nthread_regs_t; 129 | 130 | #else // !__WIN32 131 | 132 | #include 133 | #include 134 | #include 135 | 136 | typedef pid_t ntid_t; 137 | typedef ntid_t nthread_handle_t; 138 | typedef struct user_regs_struct nthread_regs_t; 139 | 140 | #define NTHREAD_GET_ID(nthread) (((nthread_t *)nthread)->thread) 141 | 142 | #endif // !__WIN32 143 | 144 | #define NTHREAD_DEFAULT_WAIT_MS 0x00 145 | #define NTHREAD_DEFAULT_TIMEOUT 0x00 146 | 147 | #define NTHREAD_ERROR 0x4760 148 | 149 | #define NTHREAD_SUSPEND_ERROR 0x4761 150 | #define NTHREAD_RESUME_ERROR 0x4762 151 | #define NTHREAD_OPEN_THREAD_ERROR 0x4763 152 | #define NTHREAD_GET_CONTEXT_ERROR 0x4764 153 | #define NTHREAD_SET_CONTEXT_ERROR 0x4765 154 | #define NTHREAD_ERROR_INVALID_ARGS 0x4766 155 | #define NTHREAD_TIMEOUT_ERROR 0x4767 156 | 157 | #define NTHREAD_ERROR_E NTHREAD_ERROR_INVALID_ARGS 158 | 159 | #define NTHREAD_STACK_ADD (-8192) 160 | 161 | typedef struct { 162 | nthread_handle_t thread; 163 | void *sleep_addr; 164 | uint8_t timeout; 165 | 166 | nthread_regs_t n_ctx; 167 | nthread_regs_t o_ctx; 168 | } nthread_t; 169 | 170 | #ifdef __WIN32 171 | 172 | #ifndef NTHREAD_API 173 | #define NTHREAD_API NEPTUNE_API 174 | #endif // !NTHREAD_API 175 | 176 | #define NTHREAD_IS_VALID(nthread) ((nthread)->thread != NULL) 177 | #define NTHREAD_SET_INVALID(nthread) ((nthread)->thread = NULL) 178 | 179 | ntid_t NTHREAD_API nthread_get_id(nthread_t *nthread); 180 | 181 | #define NTHREAD_GET_ID(nthread) nthread_get_id(nthread) 182 | 183 | #else // !__WIN32 184 | 185 | #define NTHREAD_IS_VALID(nthread) ((nthread)->thread != 0) 186 | #define NTHREAD_SET_INVALID(nthread) ((nthread)->thread = 0) 187 | 188 | #define NTHREAD_GET_ID(nthread) ((nthread)->thread) 189 | 190 | #endif // !__WIN32 191 | 192 | #define NTHREAD_GET_REG(nthread, reg) \ 193 | (((void **)(((void *)&(nthread)->n_ctx) + reg))[0]) 194 | 195 | #define NTHREAD_SET_REG(nthread, reg, set) \ 196 | (((void **)(((void *)&(nthread)->n_ctx) + (reg)))[0] = (void *)(set)) 197 | 198 | #define NTHREAD_GET_OREG(nthread, reg) \ 199 | (((void **)(((void *)&(nthread)->o_ctx) + reg))[0]) 200 | 201 | #define NTHREAD_SET_OREG(nthread, reg, set) \ 202 | (((void **)(((void *)&(nthread)->o_ctx) + (reg)))[0] = (void *)(set)) 203 | 204 | bool NTHREAD_API nthread_is_waiting(nthread_t *nthread); 205 | 206 | nerror_t NTHREAD_API nthread_init_ex(nthread_t *nthread, ntid_t ntid, 207 | nthread_reg_offset_t push_reg_offset, 208 | void *push_addr, void *sleep_addr, 209 | nthread_flags_t flags); 210 | 211 | nerror_t NTHREAD_API nthread_init(nthread_t *nthread, ntid_t ntid, 212 | nthread_reg_offset_t push_reg_offset, 213 | void *push_addr, void *sleep_addr); 214 | 215 | /** 216 | * @brief Release resources and restore context related to an NThread instance. 217 | * 218 | * Should be called after operations are completed to clean up state. 219 | * 220 | * @param nthread Pointer to the NThread structure to destroy. 221 | */ 222 | void NTHREAD_API nthread_destroy(nthread_t *nthread); 223 | 224 | /** 225 | * @brief Retrieve the base address of the thread's stack. 226 | * 227 | * @param nthread Pointer to the NThread structure. 228 | * @return Pointer to the beginning of the thread's stack. 229 | */ 230 | void *NTHREAD_API nthread_stack_begin(nthread_t *nthread); 231 | 232 | /** 233 | * @brief Suspend execution of the target thread. 234 | * 235 | * @param nthread Pointer to the NThread structure. 236 | * @return Error code indicating success or failure. 237 | */ 238 | nerror_t NTHREAD_API nthread_suspend(nthread_t *nthread); 239 | 240 | /** 241 | * @brief Resume execution of a previously suspended thread. 242 | * 243 | * @param nthread Pointer to the NThread structure. 244 | * @return Error code indicating success or failure. 245 | */ 246 | nerror_t NTHREAD_API nthread_resume(nthread_t *nthread); 247 | 248 | /** 249 | * @brief Read the current CPU context (registers) of the target thread. 250 | * 251 | * @param nthread Pointer to the NThread structure. 252 | * @return Error code indicating success or failure. 253 | */ 254 | nerror_t NTHREAD_API nthread_get_regs(nthread_t *nthread); 255 | 256 | /** 257 | * @brief Set the CPU context (registers) of the target thread. 258 | * 259 | * Applies previously modified or constructed context to the thread. 260 | * 261 | * @param nthread Pointer to the NThread structure. 262 | * @return Error code indicating success or failure. 263 | */ 264 | nerror_t NTHREAD_API nthread_set_regs(nthread_t *nthread); 265 | 266 | void NTHREAD_API nthread_set_timeout(nthread_t *nthread, uint8_t timeout_sec); 267 | 268 | /** 269 | * @brief Wait for the thread to return from a function call, with timeout control. 270 | * 271 | * Repeatedly checks if the thread has returned from execution within the specified time. 272 | * 273 | * @param nthread Pointer to the NThread structure. 274 | * @param sleep Timeout in milliseconds between each status check. 275 | * @return Error code indicating success, timeout, or failure. 276 | */ 277 | nerror_t NTHREAD_API nthread_wait_ex(nthread_t *nthread, uint32_t sleep); 278 | 279 | /** 280 | * @brief Wait indefinitely until the thread returns from a function call. 281 | * 282 | * @param nthread Pointer to the NThread structure. 283 | * @return Error code indicating success or failure. 284 | */ 285 | nerror_t NTHREAD_API nthread_wait(nthread_t *nthread); 286 | 287 | /** 288 | * @brief Execute a function inside the target process using the hijacked thread. 289 | * 290 | * This manipulates the thread's instruction pointer to call the provided function address. 291 | * 292 | * @param nthread Pointer to the NThread structure. 293 | * @param function_address Address of the target function to call. 294 | * @param return_value Pointer to store the return value of the function, if any. 295 | * 296 | * @return Error code indicating success or failure. 297 | */ 298 | nerror_t NTHREAD_API nthread_call(nthread_t *nthread, void *function_address, 299 | void **return_value); 300 | 301 | #endif // !__NSHELL_H__ 302 | -------------------------------------------------------------------------------- /include/ntmem.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file ntmem.h 27 | * @brief Provides a memory synchronization mechanism between processes. 28 | * 29 | * This module facilitates shared memory management for inter-process communication, 30 | * supporting both safe and unsafe write modes. When safe write is disabled, memory 31 | * synchronization is performed using more minimal data transfers. 32 | * 33 | * Writing operations are performed using `nttunnel` or `ntu_write_with_memset`. 34 | */ 35 | 36 | #ifndef __NTMEM_H__ 37 | #define __NTMEM_H__ 38 | 39 | #define NTMEM_DEFAULT_LENGTH 1024 40 | 41 | #include "neptune.h" 42 | 43 | #define NTMEM_SAFE_WRITE 0x10 44 | 45 | typedef int8_t ntmem_flags_t; 46 | 47 | typedef struct nttunnel nttunnel_t; 48 | 49 | struct ntmem { 50 | ntmem_flags_t flags; 51 | 52 | size_t length; 53 | void *remote_mem; 54 | }; 55 | 56 | #define NTM_LENGTH(ntmem) (ntmem->length) 57 | #define NTM_SET_LENGTH(ntmem, set_length) (ntmem->length = (set_length)) 58 | 59 | #define NTM_CALC_LOCALS_SIZE(length) (length * 2 * sizeof(int8_t)) 60 | #define NTM_CALC_STRUCT_SIZE(length) \ 61 | (sizeof(ntmem_t) + NTM_CALC_LOCALS_SIZE(length)) 62 | #define NTM_STRUCT_SIZE(ntmem) (NTM_CALC_STRUCT_SIZE(ntmem->length)) 63 | 64 | #define NTM_SET_REMOTE(ntmem, set_remote_mem) \ 65 | (ntmem->remote_mem = (set_remote_mem)) 66 | #define NTM_REMOTE(ntmem) (ntmem->remote_mem) 67 | #define NTM_LOCAL(ntmem) ((void *)(ntmem + 1)) 68 | #define NTM_LOCAL_CPY(ntmem) (NTM_LOCAL(ntmem) + NTM_LENGTH(ntmem)) 69 | 70 | typedef struct ntmem ntmem_t; 71 | 72 | #define NTMEM_ERROR 0x9200 73 | 74 | #define NTMEM_ALLOC_ERROR 0x9201 75 | #define NTMEM_NTU_MALLOC_ERROR 0x9202 76 | #define NTMEM_NTM_RESET_REMOTE_ERROR 0x9203 77 | #define NTM_ALLOC_REMOTE_ERROR 0x9204 78 | 79 | #define NTMEM_ERROR_E NTMEM_PUSH_WITH_MEMSET_ERROR 80 | 81 | #include "ntutils.h" 82 | 83 | /** 84 | * @brief Enable safe write mode for memory synchronization. 85 | * 86 | * @param ntmem Pointer to memory structure. 87 | */ 88 | void NTHREAD_API ntm_enable_safe_write(ntmem_t *ntmem); 89 | 90 | /** 91 | * @brief Disable safe write mode for memory synchronization. 92 | * 93 | * @param ntmem Pointer to memory structure. 94 | */ 95 | void NTHREAD_API ntm_disable_safe_write(ntmem_t *ntmem); 96 | 97 | /** 98 | * @brief Check if safe write mode is enabled. 99 | * 100 | * @param ntmem Pointer to memory structure. 101 | * @return true if safe write mode is enabled, false otherwise. 102 | */ 103 | bool NTHREAD_API ntm_is_safe_write(ntmem_t *ntmem); 104 | 105 | void *NTHREAD_API ntm_reset_locals(ntmem_t *ntmem); 106 | 107 | void *NTHREAD_API ntm_reset_remote_ex(ntmem_t *ntmem, size_t length); 108 | 109 | void *NTHREAD_API ntm_reset_remote(ntmem_t *ntmem); 110 | 111 | nerror_t NTHREAD_API ntm_reset(ntmem_t *ntmem); 112 | 113 | void *NTHREAD_API NTHREAD_API ntm_alloc_remote(ntmem_t *ntmem); 114 | 115 | void NTHREAD_API ntm_free_remote(ntmem_t *ntmem); 116 | 117 | void *NTHREAD_API ntm_alloc_remote_and_reset(ntmem_t *ntmem); 118 | 119 | /** 120 | * @brief Create and initialize a new memory structure with specific length. 121 | * 122 | * @param length Memory size in bytes. 123 | * @return Pointer to newly created memory structure. 124 | */ 125 | ntmem_t *NTHREAD_API ntm_create_ex(size_t length); 126 | 127 | /** 128 | * @brief Create and initialize a memory structure with default size. 129 | * 130 | * @return Pointer to newly created memory structure. 131 | */ 132 | ntmem_t *NTHREAD_API ntm_create(); 133 | 134 | ntmem_t *NTHREAD_API ntm_create_with_alloc_ex(size_t length); 135 | 136 | ntmem_t *NTHREAD_API ntm_create_with_alloc(); 137 | 138 | ntmem_t *NTHREAD_API ntm_create_from_remote(void *remote, size_t length); 139 | 140 | /** 141 | * @brief Delete memory structure. 142 | * 143 | * @param ntmem Pointer to memory structure. 144 | */ 145 | void NTHREAD_API ntm_delete(ntmem_t *ntmem); 146 | 147 | void NTHREAD_API ntm_delete_and_free(ntmem_t *ntmem); 148 | 149 | /** 150 | * @brief Delete the ntmem structure and detach remote memory pointer. 151 | * 152 | * Frees the local `ntmem_t` structure and its local memory buffer, 153 | * but returns the remote memory address (in target process) to the caller. 154 | * 155 | * This allows the caller to continue interacting with the remote memory 156 | * even after the local structure is destroyed. 157 | * 158 | * @param ntmem Pointer to memory structure. 159 | * @return Pointer to remote memory address in target process. 160 | */ 161 | void *NTHREAD_API ntm_delete_and_detach(ntmem_t *ntmem); 162 | 163 | void *NTHREAD_API ntm_pull_with_tunnel_ex(ntmem_t *ntmem, nttunnel_t *nttunnel, 164 | size_t len); 165 | 166 | /** 167 | * @brief Pull data from tunnel into the memory buffer. 168 | * 169 | * @param ntmem Pointer to memory structure. 170 | * @param nttunnel Associated tunnel structure. 171 | * @return Pointer to updated buffer. 172 | */ 173 | void *NTHREAD_API ntm_pull_with_tunnel(ntmem_t *ntmem, nttunnel_t *nttunnel); 174 | 175 | /** 176 | * @brief Push memory buffer into target process using tunnel. 177 | * 178 | * @param ntmem Pointer to memory structure. 179 | * @param nttunnel Associated tunnel structure. 180 | * @return Pointer to pushed data location. 181 | */ 182 | void *NTHREAD_API ntm_push_with_tunnel(ntmem_t *ntmem, nttunnel_t *nttunnel); 183 | 184 | /** 185 | * @brief Push memory buffer using memset as a writing method. 186 | * 187 | * @param ntmem Pointer to memory structure. 188 | * @return Pointer to pushed data location. 189 | */ 190 | void *NTHREAD_API ntm_push_with_memset(ntmem_t *ntmem); 191 | 192 | /** 193 | * @brief Push memory buffer into target using default method (tunnel or memset). 194 | * 195 | * @param ntmem Pointer to memory structure. 196 | * @param nttunnel Associated tunnel structure. 197 | * @return Pointer to pushed data location. 198 | */ 199 | void *NTHREAD_API ntm_push_ex(ntmem_t *ntmem, nttunnel_t *nttunnel); 200 | 201 | /** 202 | * @brief Push memory buffer into target using default method. 203 | * 204 | * Wrapper for `ntm_push_ex` that uses the default tunnel from `ntu_nttunnel`. 205 | * 206 | * @param ntmem Pointer to memory structure. 207 | * @return Pointer to pushed data location. 208 | */ 209 | void *NTHREAD_API ntm_push(ntmem_t *ntmem); 210 | 211 | #endif // !__NTUTILS_BUFFER_H__ 212 | -------------------------------------------------------------------------------- /include/nttunnel.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file nttunnel.h 27 | * @brief High-level memory tunnel utilities using filesystem channel (FSCHAN). 28 | * 29 | * This module provides advanced memory read/write capabilities by building on top 30 | * of the basic `ntutils` operations. It introduces a read mechanism and improves 31 | * write operations via a virtual tunnel abstraction. 32 | * 33 | * It uses `ntu_write_with_memset` internally for memory writes. 34 | * 35 | * FSCHAN (File System Channel) refers to the logical memory communication channel 36 | * established between processes or within the same process context. 37 | */ 38 | 39 | #ifndef __NTTUNNEL_H__ 40 | #define __NTTUNNEL_H__ 41 | 42 | #include "neptune.h" 43 | #include "nfile.h" 44 | 45 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_256 0x00 46 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_1024 0x01 47 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_4096 0x02 48 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_16384 0x03 49 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_65536 0x04 50 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_262144 0x05 51 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_1048576 0x06 52 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_4194304 0x07 53 | #define NTTUNNEL_FSCHAN_DONT_SAVE_PATH 0x08 54 | #define NTTUNNEL_FSCHAN_WRITE_MODE 0x10 55 | #define NTTUNNEL_FSCHAN_CREATE_TEMP_PATH 0x20 56 | 57 | #define NTTUNNEL_FSCHAN_DEFAULT_FLAGS \ 58 | NTTUNNEL_FSCHAN_MAX_TRANSFER_262144 | NTTUNNEL_FSCHAN_CREATE_TEMP_PATH 59 | 60 | #define NTTUNNEL_FSCHAN_MAX_MODE_SIZE 6 61 | 62 | #define NTTUNNEL_FSCHAN_CALC_MAX_TRANSFER(flags) \ 63 | (((size_t)4) << (6 + (flags & 0x07) * 2)) 64 | 65 | typedef int8_t nttunnel_fschan_flags_t; 66 | 67 | struct nttunnel_fschan { 68 | void *remote_file; 69 | nfile_t local_file; 70 | 71 | nfile_path_t path; 72 | }; 73 | 74 | typedef struct nttunnel_fschan nttunnel_fschan_t; 75 | 76 | typedef struct ntmem ntmem_t; 77 | 78 | struct nttunnel { 79 | ntmem_t *ntmem; 80 | 81 | nttunnel_fschan_t read; 82 | size_t read_transfer; 83 | 84 | nttunnel_fschan_t write; 85 | size_t write_transfer; 86 | 87 | size_t max_transfer; 88 | }; 89 | 90 | typedef struct nttunnel nttunnel_t; 91 | 92 | #define NTTUNNEL_ERROR 0x7600 93 | 94 | #define NTTUNNEL_GET_TEMP_PATH_ERROR 0x7601 95 | #define NTTUNNEL_GET_TEMP_FILE_ERROR 0x7602 96 | #define NTTUNNEL_ALLOC_ERROR 0x7603 97 | #define NTTUNNEL_NFILE_OPEN_ERROR 0x7604 98 | #define NTTUNNEL_NTM_PUSH_ERROR 0x7605 99 | #define NTTUNNEL_NTU_FOPEN_ERROR 0x7606 100 | #define NTTUNNEL_NFILE_WRITE_ERROR 0x7607 101 | #define NTTUNNEL_NFILE_READ_ERROR 0x7608 102 | #define NTTUNNEL_NTU_FREAD_ERROR 0x7609 103 | #define NTTUNNEL_NTU_FWRITE_ERROR 0x760A 104 | #define NTTUNNEL_NTU_FFLUSH_ERROR 0x760B 105 | #define NTTUNNEL_INIT_FSCHAN_ERROR 0x760C 106 | #define NTTUNNEL_CREATE_TEMP_PATH_ERROR 0x760D 107 | #define NTTUNNEL_NTM_CREATE_WITH_ALLOC_EX_ERROR 0x760E 108 | 109 | #define NTTUNNEL_ERROR_E NTTUNNEL_NTM_CREATE_ERROR 110 | 111 | #include "ntutils.h" 112 | 113 | /** 114 | * @brief Check if the tunnel is ready for reading. 115 | * 116 | * @param nttunnel Pointer to the tunnel structure. 117 | * @return true if reading is available; false otherwise. 118 | */ 119 | bool NTHREAD_API ntt_can_read(nttunnel_t *nttunnel); 120 | 121 | /** 122 | * @brief Check if the tunnel is ready for writing. 123 | * 124 | * @param nttunnel Pointer to the tunnel structure. 125 | * @return true if writing is available; false otherwise. 126 | */ 127 | bool NTHREAD_API ntt_can_write(nttunnel_t *nttunnel); 128 | 129 | /** 130 | * @brief Initialize the tunnel with specific FSCHAN flags. 131 | * 132 | * @param nttunnel Pointer to the tunnel structure. 133 | * @param flags Flags controlling tunnel behavior. 134 | * @return Error code. 135 | */ 136 | nerror_t NTHREAD_API ntt_init_ex(nttunnel_t *nttunnel, 137 | nttunnel_fschan_flags_t flags); 138 | 139 | /** 140 | * @brief Initialize the tunnel with default settings. 141 | * 142 | * @param nttunnel Pointer to the tunnel structure. 143 | * @return Error. 144 | */ 145 | nerror_t NTHREAD_API ntt_init(nttunnel_t *nttunnel); 146 | 147 | /** 148 | * @brief Clean up and release resources associated with the tunnel. 149 | * 150 | * @param nttunnel Pointer to the tunnel structure. 151 | */ 152 | void NTHREAD_API ntt_destroy(nttunnel_t *nttunnel); 153 | 154 | /** 155 | * @brief Read memory through the tunnel. 156 | * 157 | * @param nttunnel Pointer to the tunnel structure. 158 | * @param dest Address in local memory to copy data into. 159 | * @param source Source address in the remote or target memory. 160 | * @param length Number of bytes to read. 161 | * @return Error code. 162 | */ 163 | nerror_t NTHREAD_API ntt_read(nttunnel_t *nttunnel, const void *dest, 164 | void *source, size_t length); 165 | 166 | /** 167 | * @brief Write memory through the tunnel. 168 | * 169 | * This function uses `ntu_write_with_memset` internally to perform the memory write. 170 | * 171 | * @param nttunnel Pointer to the tunnel structure. 172 | * @param dest Destination address in the remote or target memory. 173 | * @param source Local buffer to write from. 174 | * @param length Number of bytes to write. 175 | * @return Error code. 176 | */ 177 | nerror_t NTHREAD_API ntt_write(nttunnel_t *nttunnel, void *dest, 178 | const void *source, size_t length); 179 | 180 | #endif // !__NTTUNNEL_H__ 181 | -------------------------------------------------------------------------------- /include/ntucc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2024, 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file ntucc.h 27 | * @brief Calling convention helper macros for ntutils. 28 | * 29 | * Provides simplified and more readable macro definitions to select and apply 30 | * calling conventions when working with ntutils-related function calls. 31 | */ 32 | 33 | #ifndef __NTU_CC__ 34 | #define __NTU_CC__ 35 | 36 | #include "neptune.h" 37 | 38 | typedef int64_t ntucc_t; 39 | 40 | #define NTUCC_RBX NTHREAD_RBX_INDEX 41 | #define NTUCC_RCX NTHREAD_RCX_INDEX 42 | #define NTUCC_RDX NTHREAD_RDX_INDEX 43 | #define NTUCC_RDI NTHREAD_RDI_INDEX 44 | #define NTUCC_RSI NTHREAD_RSI_INDEX 45 | #define NTUCC_RSP NTHREAD_RSP_INDEX 46 | #define NTUCC_RBP NTHREAD_RSP_INDEX 47 | #define NTUCC_R8 NTHREAD_R8_INDEX 48 | #define NTUCC_R9 NTHREAD_R9_INDEX 49 | #define NTUCC_R10 NTHREAD_R10_INDEX 50 | #define NTUCC_R11 NTHREAD_R11_INDEX 51 | #define NTUCC_R12 NTHREAD_R12_INDEX 52 | #define NTUCC_R13 NTHREAD_R13_INDEX 53 | #define NTUCC_R14 NTHREAD_R14_INDEX 54 | #define NTUCC_R15 NTHREAD_R15_INDEX 55 | 56 | #define NTUCC_CALC_ARG(reg_index, arg_pos) \ 57 | (((int64_t)(reg_index)) << (((arg_pos) * 4) + 0x20)) 58 | 59 | #define NTUCC_CREATE_ARG_1(i1) (NTUCC_CALC_ARG(i1, 0)) 60 | #define NTUCC_CREATE_ARG_2(i1, i2) \ 61 | (NTUCC_CREATE_ARG_1(i1) + NTUCC_CALC_ARG(i2, 1)) 62 | 63 | #define NTUCC_CREATE_ARG_3(i1, i2, i3) \ 64 | (NTUCC_CREATE_ARG_2(i1, i2) + NTUCC_CALC_ARG(i3, 2)) 65 | 66 | #define NTUCC_CREATE_ARG_4(i1, i2, i3, i4) \ 67 | (NTUCC_CREATE_ARG_3(i1, i2, i3) + NTUCC_CALC_ARG(i4, 3)) 68 | 69 | #define NTUCC_CREATE_ARG_5(i1, i2, i3, i4, i5) \ 70 | (NTUCC_CREATE_ARG_4(i1, i2, i3, i4) + NTUCC_CALC_ARG(i5, 4)) 71 | 72 | #define NTUCC_CREATE_ARG_6(i1, i2, i3, i4, i5, i6) \ 73 | (NTUCC_CREATE_ARG_5(i1, i2, i3, i4, i5) + NTUCC_CALC_ARG(i6, 5)) 74 | 75 | #define NTUCC_CREATE_ARG_7(i1, i2, i3, i4, i5, i6, i7) \ 76 | (NTUCC_CREATE_ARG_6(i1, i2, i3, i4, i5, i6) + NTUCC_CALC_ARG(i7, 6)) 77 | 78 | #define NTUCC_CREATE_ARG_8(i1, i2, i3, i4, i5, i6, i7, i8) \ 79 | (NTUCC_CREATE_ARG_6(i1, i2, i3, i4, i5, i6, i7) + NTUCC_CALC_ARG(i8, 7)) 80 | 81 | #define NTUCC_GET_ARG_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, NAME, ...) NAME 82 | #define NTUCC_CREATE_ARG_MASK(...) \ 83 | NTUCC_GET_ARG_MACRO(__VA_ARGS__, NTUCC_CREATE_ARG_8, \ 84 | NTUCC_CREATE_ARG_7, NTUCC_CREATE_ARG_6, \ 85 | NTUCC_CREATE_ARG_5, NTUCC_CREATE_ARG_4, \ 86 | NTUCC_CREATE_ARG_3, NTUCC_CREATE_ARG_2, \ 87 | NTUCC_CREATE_ARG_1)(__VA_ARGS__) 88 | 89 | #define NTUCC_GET_ARG(cc, arg_pos) \ 90 | (((int8_t)(cc >> (((arg_pos) * 4) + 0x20))) & 0x0F) 91 | 92 | #define NTUCC_REVERSE_OP (0x10000) 93 | #define NTUCC_AUTO_CLEAN (0x20000) 94 | 95 | #define NTUCC_GET_STACK_ADD(cc) ((uint16_t)(cc & 0xFFFF)) 96 | 97 | #define NTUCC_WINDOWS_X64 \ 98 | (NTUCC_CREATE_ARG_MASK(NTUCC_RCX, NTUCC_RDX, NTUCC_R8, NTUCC_R9) | \ 99 | NTUCC_AUTO_CLEAN + sizeof(DWORD64) * 4) 100 | 101 | #ifdef NTUCC_WINDOWS_X64 102 | #define NTUCC_WINDOWS_X64_PASS_RCX \ 103 | ((NTUCC_CREATE_ARG_MASK(NTUCC_RDX, NTUCC_R8, NTUCC_R9) | \ 104 | NTUCC_AUTO_CLEAN) + \ 105 | sizeof(DWORD64) * 4) 106 | #endif // NTUCC_WINDOWS_X64 107 | 108 | #ifdef NTU_CDECL 109 | 110 | #endif // NTU_CDECL 111 | 112 | #define NTUCC_MAX_REGARG_COUNT 4 113 | 114 | #define NTUCC_GET_REGARG_COUNT(ntu_cc) (ntu_cc & NTUCC_HAS_REGARG_MASK) 115 | #define NTUCC_HAS_REGARG(ntu_cc) ((ntu_cc & NTUCC_HAS_REGARG_MASK) != 0) 116 | 117 | #endif // !__NTU_CC__ 118 | -------------------------------------------------------------------------------- /include/ntutils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2024, 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file ntutils.h 27 | * @brief Extended nthread utilities for advanced function calls and memory manipulation. 28 | * 29 | * Provides enhanced capabilities for invoking functions inside target threads/processes, 30 | * along with safe memory operations such as cross-process memory write, allocation, 31 | * and buffered file I/O emulation. Built on top of NThread's core thread manipulation, 32 | * ntutils adds flexible argument passing, memory memset-based writing, and file stream 33 | * operations tailored for remote contexts. 34 | */ 35 | 36 | #ifndef __NTUTILS_H__ 37 | #define __NTUTILS_H__ 38 | 39 | #include "nerror.h" 40 | #include "nthread.h" 41 | #include "ntucc.h" 42 | #include "nmutex.h" 43 | 44 | #ifdef __WIN32 45 | #define NTU_DEFAULT_CC NTUCC_WINDOWS_X64 46 | #define NTU_DEFAULT_PFA_CC NTUCC_WINDOWS_X64_PASS_RCX 47 | #define NTU_DEFAULT_CC_FA NTHREAD_RCX 48 | #else // !__WIN32 49 | #define NTU_DEFAULT_CC NTUCC_CDECL 50 | #define NTU_DEFAULT_PFA_CC NTUCC_CDECL_PASS_ 51 | #define NTU_DEFAULT_CC_FA NTHREAD_RCX 52 | #endif // !__WIN32 53 | 54 | #define NTUTILS_ERROR 0x7340 55 | 56 | #define NTUTILS_GET_LIBC_BASE_ERROR 0x7341 57 | #define NTUTILS_UNKNOWN_CC_ERROR 0x7342 58 | #define NTUTILS_ALLOC_ERROR 0x7343 59 | #define NTUTILS_TLS_SET_VALUE_ERROR 0x7344 60 | #define NTUTILS_TLS_ALLOC_ERROR 0x7345 61 | #define NTUTILS_NTU_MEMSET_ERROR 0x7346 62 | #define NTUTILS_NTU_WRITE_WITH_MEMSET_ERROR 0x7347 63 | #define NTUTILS_NTU_WRITE_WITH_MEMSET_DEST_ERROR 0x7348 64 | #define NTUTILS_NTM_CREATE_EX_ERROR 0x7349 65 | #define NTUTILS_NTM_RESET_REMOTE_EX_ERROR 0x734A 66 | #define NTUTILS_NTM_PUSH_ERROR 0x734B 67 | #define NTUTILS_NTHREAD_ERROR 0x734C 68 | #define NTUTILS_NTU_RESIZE_ERROR 0x734D 69 | #define NTUTILS_NTHREAD_INIT_ERROR 0x734E 70 | 71 | #define NTUTILS_FUNC_INIT_ERROR 0x7350 72 | #define NTUTILS_FUNC_INIT_ERROR_E (NTUTILS_FUNC_INIT_ERROR + 7) 73 | 74 | #define NTUTILS_ERROR_E NTUTILS_FUNC_INIT_ERROR_E 75 | 76 | #include "nttunnel.h" 77 | 78 | typedef struct ntmem ntmem_t; 79 | 80 | #if !defined(NTUTILS_DISABLE_GLOBAL_CC) || NTUTILS_DISABLE_GLOBAL_CC != 1 81 | 82 | #ifdef __WIN32 83 | #define NTU_GLOBAL_CC NTU_DEFAULT_CC 84 | #endif // __WIN32 85 | 86 | #endif // !defined(NTUTILS_DISABLE_GLOBAL_CC) || NTUTILS_DISABLE_GLOBAL_CC != 1 87 | 88 | struct ntutils { 89 | void *ret_value; 90 | ntucc_t sel_cc; 91 | 92 | nttunnel_t nttunnel; 93 | ntmem_t *stack_helper; 94 | 95 | nthread_t nthread; 96 | }; 97 | 98 | typedef struct ntutils ntutils_t; 99 | 100 | ntutils_t *NTHREAD_API _ntu_get(void); 101 | 102 | nerror_t NTHREAD_API ntu_set(ntutils_t *ntutils); 103 | 104 | ntutils_t *NTHREAD_API ntu_resize(size_t new_size); 105 | 106 | #define ntu_get() _ntu_get() 107 | 108 | #define ntu_set_cc_ex(ntutils, cc) (ntutils->sel_cc = cc) 109 | 110 | #ifndef NTU_GLOBAL_CC 111 | 112 | void NTHREAD_API _ntu_set_cc(ntucc_t cc); 113 | 114 | #define ntu_set_cc(cc) _ntu_set_default_cc(cc) 115 | 116 | void NTHREAD_API _ntu_set_default_cc(); 117 | 118 | #define ntu_set_default_cc() _ntu_set_default_cc() 119 | 120 | ntutils_t *NTHREAD_API _ntu_o(ntucc_t cc); 121 | 122 | #define ntu_o(cc) _ntu_o(cc) 123 | 124 | #else // NTU_GLOBAL_CC 125 | 126 | #define ntu_set_cc(cc) \ 127 | do { \ 128 | } while (0) 129 | 130 | #define ntu_set_default_cc(cc) \ 131 | do { \ 132 | } while (0) 133 | 134 | #define ntu_o(cc) ntu_get() 135 | 136 | #endif // NTU_GLOBAL_CC 137 | 138 | void *NTHREAD_API ntu_get_libc_base(); 139 | 140 | /** 141 | * @brief Initialize global state for ntutils subsystem. 142 | * 143 | * @return Error code. 144 | */ 145 | nerror_t NTHREAD_API ntu_global_init(void); 146 | 147 | /** 148 | * @brief Clean up global ntutils resources. 149 | */ 150 | void NTHREAD_API ntu_global_destroy(void); 151 | 152 | nerror_t NTHREAD_API ntu_upgrade(nthread_t *nthread); 153 | 154 | nerror_t NTHREAD_API ntu_attach_ex(ntid_t thread_id, 155 | nthread_reg_offset_t push_reg_offset, 156 | void *push_addr, void *sleep_addr); 157 | 158 | nerror_t NTHREAD_API ntu_attach(ntid_t thread_id, void *push_addr, 159 | void *sleep_addr); 160 | 161 | /** 162 | * @brief Destroy the current ntutils instance and release resources. 163 | */ 164 | void NTHREAD_API ntu_destroy(); 165 | 166 | void NTHREAD_API ntu_set_reg_args(uint8_t arg_count, void **args); 167 | 168 | nerror_t NTHREAD_API ntu_set_args_v(uint8_t arg_count, va_list args); 169 | 170 | nerror_t NTHREAD_API ntu_set_args(uint8_t arg_count, ...); 171 | 172 | /** 173 | * @brief Call a function inside the target thread with variable arguments (va_list). 174 | * 175 | * @param func_addr Address of the target function to call. 176 | * @param arg_count Number of arguments to pass. 177 | * @param args Variable argument list. 178 | * @return Error code. 179 | */ 180 | nerror_t NTHREAD_API ntu_call_v(void *func_addr, uint8_t arg_count, 181 | va_list args); 182 | 183 | /** 184 | * @brief Call a function inside the target thread with variadic arguments. 185 | * 186 | * @param ntutils Pointer to the ntutils instance. 187 | * @param func_addr Address of the function to call. 188 | * @param arg_count Number of arguments to pass. 189 | * @param ... Arguments to be passed to the function. 190 | * @return Error code. 191 | */ 192 | nerror_t NTHREAD_API ntu_call(void *func_addr, uint8_t arg_count, ...); 193 | 194 | /** 195 | * @brief Call a function with variable arguments and retrieve a return value. 196 | * 197 | * @param func_addr Address of the function to call. 198 | * @param arg_count Number of arguments to pass. 199 | * @param args Variable argument list. 200 | * @return Pointer returned by the called function 201 | */ 202 | void *NTHREAD_API ntu_ucall_v(void *func_addr, uint8_t arg_count, va_list args); 203 | 204 | /** 205 | * @brief Call a function with variadic arguments and retrieve a return value. 206 | * 207 | * @param func_addr Address of the function to call. 208 | * @param arg_count Number of arguments to pass. 209 | * @param ... Arguments to be passed. 210 | * @return Pointer returned by the called function 211 | */ 212 | void *NTHREAD_API _ntu_ucall(void *func_addr, uint8_t arg_count, ...); 213 | 214 | #define ntu_ucall(func_addr, ...) \ 215 | _ntu_ucall(func_addr, NEPTUNE_GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) 216 | 217 | /** 218 | * @brief Fill a block of memory in the target process with a specified value. 219 | * 220 | * @param dest Destination address. 221 | * @param fill Value to fill with. 222 | * @param length Number of bytes to fill. 223 | * @return Pointer to the destination. 224 | */ 225 | void *NTHREAD_API ntu_memset(void *dest, int fill, size_t length); 226 | 227 | /** 228 | * @brief Allocate memory inside the target process. 229 | * 230 | * @param size Number of bytes to allocate. 231 | * @return Pointer to allocated memory. 232 | */ 233 | void *NTHREAD_API ntu_malloc(size_t size); 234 | 235 | /** 236 | * @brief Free previously allocated memory inside the target process. 237 | * 238 | * @param address Pointer to memory to free. 239 | */ 240 | void NTHREAD_API ntu_free(void *address); 241 | 242 | /** 243 | * @brief Open a file stream inside the target process. 244 | * 245 | * @param filename File path in the target process's memory. 246 | * @param mode Mode string in the target process's memory. 247 | * @return FILE pointer representing the opened file stream. 248 | * 249 | * @note Strings must exist in the target process memory prior to calling this function. 250 | */ 251 | FILE *NTHREAD_API ntu_fopen(const nfile_path_t filename, 252 | const nfile_path_t mode); 253 | 254 | /** 255 | * @brief Read from a file stream into a buffer. 256 | * 257 | * @param buffer Destination buffer. 258 | * @param size Size of each element. 259 | * @param count Number of elements to read. 260 | * @param fstream FILE pointer of the opened file stream. 261 | * @return Number of elements successfully read. 262 | */ 263 | size_t NTHREAD_API ntu_fread(void *buffer, size_t size, size_t count, 264 | FILE *fstream); 265 | 266 | /** 267 | * @brief Write data from a buffer into a file stream. 268 | * 269 | * @param buffer Source buffer. 270 | * @param size Size of each element. 271 | * @param count Number of elements to write. 272 | * @param fstream FILE pointer of the opened file stream. 273 | * @return Number of elements successfully written. 274 | */ 275 | size_t NTHREAD_API ntu_fwrite(const void *buffer, size_t size, size_t count, 276 | FILE *fstream); 277 | 278 | /** 279 | * @brief Flush the file stream buffers. 280 | * 281 | * @param fstream FILE pointer of the opened file stream. 282 | * @return 0 on success. 283 | */ 284 | int NTHREAD_API ntu_fflush(FILE *fstream); 285 | 286 | /** 287 | * @brief Close the opened file stream. 288 | * 289 | * @param fstream FILE pointer to close. 290 | * @return 0 on success. 291 | */ 292 | int NTHREAD_API ntu_fclose(FILE *fstream); 293 | 294 | /** 295 | * @brief Allocate and copy a string into the target process memory. 296 | * 297 | * @param str Source null-terminated string. 298 | * @return Pointer to the allocated string in target memory. 299 | */ 300 | void *NTHREAD_API ntu_alloc_str(const char *str); 301 | 302 | /** 303 | * @brief Write data to remote memory, skipping bytes equal to the given value. 304 | * 305 | * This function writes only the bytes from `source` that are not equal to `last_value`. 306 | * It avoids writing bytes with the same value to reduce memory operations. 307 | * 308 | * @param dest Destination address in remote memory. 309 | * @param source Source buffer to write from. 310 | * @param length Number of bytes to write. 311 | * @param last_value Value to skip while writing (e.g., 0x00 or 0xFF). 312 | * @return Error code indicating success or failure. 313 | */ 314 | nerror_t NTHREAD_API ntu_write_with_memset_value(void *dest, const void *source, 315 | size_t length, 316 | int8_t last_value); 317 | 318 | /** 319 | * @brief Write data to remote memory, skipping identical bytes by comparing with last written destination. 320 | * 321 | * This function compares the `source` buffer with the memory at `last_dest` and only writes bytes that differ. 322 | * Both `source` and `last_dest` are expected to be in local (current process) memory. 323 | * 324 | * @param dest Destination address in remote memory. 325 | * @param source Source buffer to write from (local memory). 326 | * @param length Number of bytes to write. 327 | * @param last_dest Last written destination buffer for comparison (local memory, required). 328 | * @return Error code indicating success or failure. 329 | */ 330 | nerror_t NTHREAD_API ntu_write_with_memset_dest(void *dest, const void *source, 331 | size_t length, 332 | const void *last_dest); 333 | 334 | /** 335 | * @brief Write data to remote memory using memset-based operations. 336 | * 337 | * This function writes the entire source buffer to the destination address 338 | * using memory write techniques based on memset. 339 | * 340 | * @param dest Destination address in remote memory. 341 | * @param source Source buffer to write from. 342 | * @param length Number of bytes to write. 343 | * @return Error code indicating success or failure. 344 | */ 345 | nerror_t NTHREAD_API ntu_write_with_memset(void *dest, const void *source, 346 | size_t length); 347 | 348 | #define NTU_NTTUNNEL_EX(ntutils) (&ntutils->nttunnel) 349 | #define NTU_NTTUNNEL() (ntu_nttunnel()) 350 | 351 | nttunnel_t *NTHREAD_API ntu_nttunnel(); 352 | 353 | bool NTHREAD_API ntu_tunnel_can_read(); 354 | 355 | bool NTHREAD_API ntu_tunnel_can_write(); 356 | 357 | nerror_t NTHREAD_API ntu_tunnel_read(const void *dest, void *source, 358 | size_t length); 359 | 360 | nerror_t NTHREAD_API ntu_tunnel_write(void *dest, const void *source, 361 | size_t length); 362 | 363 | #define ntu_read(...) ntu_tunnel_read(__VA_ARGS__) 364 | #define ntu_write(...) ntu_tunnel_write(__VA_ARGS__) 365 | 366 | #define ntu_read_memory(...) ntu_tunnel_read(__VA_ARGS__) 367 | #define ntu_write_memory(...) ntu_tunnel_write(__VA_ARGS__) 368 | 369 | #endif // !__NTUTILS_H__ 370 | -------------------------------------------------------------------------------- /modulerules/20_ntutils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "ntutils.h" 26 | 27 | #ifdef __NTUTILS_H__ 28 | NEPTUNE_MODULE(ntu_global_init, ntu_global_destroy) 29 | #endif // __NTUTILS_H__ 30 | -------------------------------------------------------------------------------- /modulerules/nthread_rules.h: -------------------------------------------------------------------------------- 1 | #include "10_neptune.h" 2 | #include "20_ntutils.h" 3 | #include "50_neptune.h" 4 | -------------------------------------------------------------------------------- /src/nthread.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2024, 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "nthread.h" 26 | #include "nerror.h" 27 | #include "ntime.h" 28 | #include "log.h" 29 | 30 | #ifdef __WIN32 31 | 32 | ntid_t NTHREAD_API nthread_get_id(nthread_t *nthread) 33 | { 34 | return GetThreadId(nthread->thread); 35 | } 36 | 37 | bool NTHREAD_API nthread_is_waiting(nthread_t *nthread) 38 | { 39 | if (!NTHREAD_IS_VALID(nthread)) 40 | return false; 41 | 42 | void *rip = NTHREAD_GET_REG(nthread, NTHREAD_RIP); 43 | if (rip != nthread->sleep_addr) 44 | return false; 45 | 46 | return nthread->n_ctx.ContextFlags == 47 | (CONTEXT_INTEGER | CONTEXT_CONTROL); 48 | } 49 | 50 | #endif /* ifdef __WIN32 */ 51 | 52 | static void *nthread_calc_stack(void *rsp) 53 | { 54 | return rsp + (16 - ((int64_t)(rsp) % 16)); 55 | } 56 | 57 | static void nthread_copy_ncontext(nthread_t *nthread) 58 | { 59 | memcpy((void *)&nthread->o_ctx, (void *)&nthread->n_ctx, 60 | sizeof(CONTEXT)); 61 | } 62 | 63 | nerror_t NTHREAD_API nthread_init_ex(nthread_t *nthread, ntid_t thread_id, 64 | nthread_reg_offset_t push_reg_offset, 65 | void *push_addr, void *sleep_addr, 66 | nthread_flags_t flags) 67 | { 68 | #ifdef LOG_LEVEL_1 69 | LOG_INFO( 70 | "nthread_init(thread_id=%ld, push_reg_offset=%ld, push_addr=%p, sleep_addr=%p)", 71 | thread_id, push_reg_offset, push_addr, sleep_addr); 72 | #endif /* ifdef LOG_LEVEL_1 */ 73 | 74 | if (push_addr == NULL || sleep_addr == NULL) 75 | return NTHREAD_ERROR_INVALID_ARGS; 76 | 77 | #ifdef __WIN32 78 | 79 | DWORD access = THREAD_GET_CONTEXT | THREAD_SET_CONTEXT; 80 | 81 | if ((flags & NTHREAD_FLAG_DONT_SUSPEND) == 0 || 82 | (flags & NTHREAD_FLAG_DONT_RESUME) == 0) 83 | access |= THREAD_SUSPEND_RESUME; 84 | 85 | if ((flags & NTHREAD_FLAG_DISABLE_GET_ID) == 0) 86 | access |= THREAD_QUERY_INFORMATION; 87 | 88 | HANDLE thread = OpenThread(access, false, thread_id); 89 | if (thread == NULL) 90 | return GET_ERR(NTHREAD_OPEN_THREAD_ERROR); 91 | 92 | #endif /* ifdef __WIN32 */ 93 | 94 | nthread->thread = thread; 95 | nthread->sleep_addr = sleep_addr; 96 | nthread->timeout = (flags & 0x0f); 97 | 98 | #ifdef __WIN32 99 | 100 | nthread->o_ctx.ContextFlags = 0; 101 | nthread->n_ctx.ContextFlags = CONTEXT_ALL; 102 | 103 | #endif /* ifdef __WIN32 */ 104 | 105 | if ((flags & NTHREAD_FLAG_DONT_SUSPEND) == 0) 106 | RET_ERR(nthread_suspend(nthread)); 107 | 108 | nerror_t error_helper = nthread_get_regs(nthread); 109 | if (HAS_ERR(error_helper)) 110 | goto nthread_init_resume_and_ret; 111 | 112 | #ifdef __WIN32 113 | 114 | nthread_copy_ncontext(nthread); 115 | 116 | void *rip = NTHREAD_GET_REG(nthread, NTHREAD_RIP); 117 | void *rsp = NTHREAD_GET_REG(nthread, NTHREAD_RSP); 118 | void *rvp = NTHREAD_GET_REG(nthread, push_reg_offset); 119 | 120 | NTHREAD_SET_REG(nthread, NTHREAD_RIP, push_addr); 121 | 122 | void *new_rsp = nthread_calc_stack(rsp + NTHREAD_STACK_ADD); 123 | NTHREAD_SET_REG(nthread, NTHREAD_RSP, new_rsp); 124 | NTHREAD_SET_REG(nthread, push_reg_offset, sleep_addr); 125 | 126 | #endif /* ifdef __WIN32 */ 127 | 128 | error_helper = nthread_set_regs(nthread); 129 | if (HAS_ERR(error_helper)) { 130 | nthread_init_resume_and_ret: 131 | if ((flags & NTHREAD_FLAG_DONT_RESUME) == 0) 132 | nthread_resume(nthread); 133 | nthread_init_destroy_and_ret: 134 | nthread_destroy(nthread); 135 | return error_helper; 136 | } 137 | 138 | if ((flags & NTHREAD_FLAG_DONT_RESUME) == 0) { 139 | error_helper = nthread_resume(nthread); 140 | if (HAS_ERR(error_helper)) 141 | goto nthread_init_destroy_and_ret; 142 | } 143 | 144 | error_helper = nthread_wait(nthread); 145 | if (HAS_ERR(error_helper)) { 146 | goto nthread_init_destroy_and_ret; 147 | } 148 | 149 | nthread_copy_ncontext(nthread); 150 | 151 | NTHREAD_SET_OREG(nthread, push_reg_offset, rvp); 152 | NTHREAD_SET_OREG(nthread, NTHREAD_RIP, rip); 153 | NTHREAD_SET_OREG(nthread, NTHREAD_RSP, rsp); 154 | 155 | nthread->n_ctx.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; 156 | 157 | #ifdef LOG_LEVEL_2 158 | LOG_INFO("NThread(%ld) succesfully created", thread_id); 159 | #endif /* ifdef LOG_LEVEL_2 */ 160 | 161 | return N_OK; 162 | } 163 | 164 | nerror_t NTHREAD_API nthread_init(nthread_t *nthread, ntid_t thread_id, 165 | nthread_reg_offset_t push_reg_offset, 166 | void *push_addr, void *sleep_addr) 167 | { 168 | return nthread_init_ex(nthread, thread_id, push_reg_offset, push_addr, 169 | sleep_addr, NTHREAD_DEFAULT_TIMEOUT); 170 | } 171 | 172 | void NTHREAD_API nthread_destroy(nthread_t *nthread) 173 | { 174 | #ifdef __WIN32 175 | 176 | if (nthread->o_ctx.ContextFlags != 0) { 177 | memcpy((void *)&nthread->n_ctx, (void *)&nthread->o_ctx, 178 | sizeof(CONTEXT)); 179 | 180 | nthread_set_regs(nthread); 181 | } 182 | 183 | if (NTHREAD_IS_VALID(nthread)) { 184 | CloseHandle(nthread->thread); 185 | NTHREAD_SET_INVALID(nthread); 186 | } 187 | 188 | #endif /* ifdef __WIN32 */ 189 | } 190 | 191 | void *NTHREAD_API nthread_stack_begin(nthread_t *nthread) 192 | { 193 | void *rsp = NTHREAD_GET_OREG(nthread, NTHREAD_RSP) + NTHREAD_STACK_ADD; 194 | return nthread_calc_stack(rsp); 195 | } 196 | 197 | nerror_t NTHREAD_API nthread_suspend(nthread_t *nthread) 198 | { 199 | #ifdef LOG_LEVEL_2 200 | LOG_INFO("nthread_suspend(nthread_id=%ld)", NTHREAD_GET_ID(nthread)); 201 | #endif /* ifdef LOG_LEVEL_2 */ 202 | 203 | #ifdef __WIN32 204 | 205 | DWORD count = SuspendThread(nthread->thread); 206 | 207 | #ifdef LOG_LEVEL_1 208 | 209 | if (count == (DWORD)(-1)) { 210 | LOG_WARN("nthread_suspend(nthread_id=%ld) failed", 211 | NTHREAD_GET_ID(nthread)); 212 | 213 | return GET_ERR(NTHREAD_SUSPEND_ERROR); 214 | } 215 | 216 | #else /* ifndef LOG_LEVEL_1 */ 217 | 218 | if (count == (DWORD)(-1)) 219 | return GET_ERR(NTHREAD_SUSPEND_ERROR); 220 | 221 | #endif /* infdef LOG_LEVEL_1 */ 222 | #endif /* ifdef __WIN32 */ 223 | 224 | return N_OK; 225 | } 226 | 227 | nerror_t NTHREAD_API nthread_resume(nthread_t *nthread) 228 | { 229 | #ifdef LOG_LEVEL_2 230 | LOG_INFO( 231 | "nthread_resume(nthread_id=%ld) called. If this hangs, see: https://github.com/woldann/nthread/wiki/nthread_resume-troubleshooting", 232 | NTHREAD_GET_ID(nthread)); 233 | #endif /* ifdef LOG_LEVEL_2 */ 234 | 235 | #ifdef __WIN32 236 | 237 | DWORD count = ResumeThread(nthread->thread); 238 | 239 | #ifdef LOG_LEVEL_1 240 | 241 | if (count == (DWORD)(-1)) { 242 | LOG_WARN("nthread_resume(nthread_id=%ld) failed", 243 | NTHREAD_GET_ID(nthread)); 244 | 245 | return GET_ERR(NTHREAD_RESUME_ERROR); 246 | } 247 | 248 | #else /* ifndef LOG_LEVEL_1 */ 249 | 250 | if (count == (DWORD)(-1)) 251 | return GET_ERR(NTHREAD_RESUME_ERROR); 252 | 253 | #endif /* infdef LOG_LEVEL_1 */ 254 | #endif /* ifdef __WIN32 */ 255 | 256 | return N_OK; 257 | } 258 | 259 | nerror_t NTHREAD_API nthread_get_regs(nthread_t *nthread) 260 | { 261 | #ifdef __WIN32 262 | 263 | if (!GetThreadContext(nthread->thread, &nthread->n_ctx)) 264 | return GET_ERR(NTHREAD_GET_CONTEXT_ERROR); 265 | 266 | #endif /* ifdef __WIN32 */ 267 | 268 | return N_OK; 269 | } 270 | 271 | nerror_t NTHREAD_API nthread_set_regs(nthread_t *nthread) 272 | { 273 | #ifdef __WIN32 274 | 275 | if (!SetThreadContext(nthread->thread, &nthread->n_ctx)) 276 | return GET_ERR(NTHREAD_SET_CONTEXT_ERROR); 277 | 278 | #endif /* ifdef __WIN32 */ 279 | 280 | return N_OK; 281 | } 282 | 283 | void NTHREAD_API nthread_set_timeout(nthread_t *nthread, uint8_t timeout_sec) 284 | { 285 | nthread->timeout = timeout_sec; 286 | } 287 | 288 | nerror_t NTHREAD_API nthread_wait_ex(nthread_t *nthread, uint32_t sleep) 289 | { 290 | ntime_t end; 291 | uint32_t timeout_sec = nthread->timeout; 292 | if (timeout_sec != 0) { 293 | end = ntime_get_unix() + timeout_sec; 294 | uint32_t timeout_ms = timeout_sec * 1000; 295 | if (sleep >= timeout_ms) 296 | sleep = timeout_ms + 1; 297 | } else 298 | end = 0; 299 | 300 | while (true) { 301 | #ifdef __WIN32 302 | Sleep((DWORD)sleep); 303 | #endif /* ifdef __WIN32 */ 304 | 305 | RET_ERR(nthread_get_regs(nthread)); 306 | 307 | void *rip = NTHREAD_GET_REG(nthread, NTHREAD_RIP); 308 | if (rip == nthread->sleep_addr) 309 | return N_OK; 310 | 311 | if (end > 0) { 312 | ntime_t cur = ntime_get_unix(); 313 | if (cur >= end) 314 | return GET_ERR(NTHREAD_TIMEOUT_ERROR); 315 | } 316 | } 317 | } 318 | 319 | nerror_t NTHREAD_API nthread_wait(nthread_t *nthread) 320 | { 321 | return nthread_wait_ex(nthread, NTHREAD_DEFAULT_WAIT_MS); 322 | } 323 | 324 | nerror_t NTHREAD_API nthread_call(nthread_t *nthread, void *fun_addr, 325 | void **return_value) 326 | { 327 | #ifdef LOG_LEVEL_3 328 | 329 | LOG_INFO("nthread_call(nthread_id=%ld, fun_addr=%p, return_value=%p)", 330 | NTHREAD_GET_ID(nthread), fun_addr, return_value); 331 | 332 | #endif /* ifdef LOG_LEVEL_3 */ 333 | 334 | NTHREAD_SET_REG(nthread, NTHREAD_RIP, fun_addr); 335 | 336 | void *rsp = nthread_stack_begin(nthread); 337 | NTHREAD_SET_REG(nthread, NTHREAD_RSP, rsp - sizeof(fun_addr)); 338 | 339 | RET_ERR(nthread_set_regs(nthread)); 340 | RET_ERR(nthread_wait(nthread)); 341 | 342 | *return_value = NTHREAD_GET_REG(nthread, NTHREAD_RAX); 343 | return N_OK; 344 | } 345 | -------------------------------------------------------------------------------- /src/ntmem.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "ntmem.h" 26 | #include "nttunnel.h" 27 | #include "nmem.h" 28 | 29 | void NTHREAD_API ntm_enable_safe_write(ntmem_t *ntmem) 30 | { 31 | ntmem->flags |= NTMEM_SAFE_WRITE; 32 | } 33 | 34 | void NTHREAD_API ntm_disable_safe_write(ntmem_t *ntmem) 35 | { 36 | ntmem->flags &= ~(NTMEM_SAFE_WRITE); 37 | } 38 | 39 | bool NTHREAD_API ntm_is_safe_write(ntmem_t *ntmem) 40 | { 41 | return (ntmem->flags & NTMEM_SAFE_WRITE) != 0; 42 | } 43 | 44 | void *NTHREAD_API ntm_reset_locals(ntmem_t *ntmem) 45 | { 46 | void *local = NTM_LOCAL(ntmem); 47 | memset(local, 0, NTM_CALC_LOCALS_SIZE(NTM_LENGTH(ntmem))); 48 | return local; 49 | } 50 | 51 | void *NTHREAD_API ntm_reset_remote_ex(ntmem_t *ntmem, size_t length) 52 | { 53 | void *remote = NTM_REMOTE(ntmem); 54 | if (ntu_memset(remote, 0, length) == NULL) 55 | return NULL; 56 | 57 | return remote; 58 | } 59 | 60 | void *NTHREAD_API ntm_reset_remote(ntmem_t *ntmem) 61 | { 62 | return ntm_reset_remote_ex(ntmem, NTM_LENGTH(ntmem)); 63 | } 64 | 65 | nerror_t NTHREAD_API ntm_reset(ntmem_t *ntmem) 66 | { 67 | if (ntm_reset_remote(ntmem) == NULL) 68 | return GET_ERR(NTMEM_NTM_RESET_REMOTE_ERROR); 69 | 70 | ntm_reset_locals(ntmem); 71 | return N_OK; 72 | } 73 | 74 | void *NTHREAD_API ntm_alloc_remote(ntmem_t *ntmem) 75 | { 76 | size_t len = NTM_LENGTH(ntmem); 77 | 78 | NTM_SET_REMOTE(ntmem, ntu_malloc(len)); 79 | return NTM_REMOTE(ntmem); 80 | } 81 | 82 | void NTHREAD_API ntm_free_remote(ntmem_t *ntmem) 83 | { 84 | if (NTM_REMOTE(ntmem) != NULL) { 85 | ntu_free(NTM_REMOTE(ntmem)); 86 | NTM_SET_REMOTE(ntmem, NULL); 87 | } 88 | } 89 | 90 | void *NTHREAD_API ntm_alloc_remote_and_reset(ntmem_t *ntmem) 91 | { 92 | void *remote = ntm_alloc_remote(ntmem); 93 | if (remote == NULL) 94 | return NULL; 95 | 96 | if (HAS_ERR(ntm_reset(ntmem))) { 97 | ntm_free_remote(ntmem); 98 | return NULL; 99 | } 100 | 101 | return remote; 102 | } 103 | 104 | ntmem_t *NTHREAD_API ntm_create_ex(size_t length) 105 | { 106 | ntmem_t *ntmem = N_ALLOC(NTM_CALC_STRUCT_SIZE(length)); 107 | if (ntmem == NULL) 108 | return NULL; 109 | 110 | NTM_SET_LENGTH(ntmem, length); 111 | return ntmem; 112 | } 113 | 114 | ntmem_t *NTHREAD_API ntm_create() 115 | { 116 | return ntm_create_ex(NTMEM_DEFAULT_LENGTH); 117 | } 118 | 119 | ntmem_t *NTHREAD_API ntm_create_with_alloc_ex(size_t length) 120 | { 121 | ntmem_t *ntmem = ntm_create_ex(length); 122 | if (ntmem == NULL) 123 | return NULL; 124 | 125 | if (ntm_alloc_remote_and_reset(ntmem) == NULL) { 126 | ntm_delete_and_free(ntmem); 127 | return NULL; 128 | } 129 | return ntmem; 130 | } 131 | 132 | ntmem_t *NTHREAD_API ntm_create_with_alloc() 133 | { 134 | return ntm_create_with_alloc_ex(NTMEM_DEFAULT_LENGTH); 135 | } 136 | 137 | ntmem_t *NTHREAD_API ntm_create_from_remote(void *remote, size_t length) 138 | { 139 | ntmem_t *ntmem = ntm_create_ex(length); 140 | if (ntmem == NULL) 141 | return NULL; 142 | 143 | NTM_SET_REMOTE(ntmem, remote); 144 | if (HAS_ERR(ntm_reset(ntmem))) { 145 | ntm_delete_and_detach(ntmem); 146 | return NULL; 147 | } 148 | 149 | return ntmem; 150 | } 151 | 152 | void NTHREAD_API ntm_delete(ntmem_t *ntmem) 153 | { 154 | N_FREE(ntmem); 155 | } 156 | 157 | void NTHREAD_API ntm_delete_and_free(ntmem_t *ntmem) 158 | { 159 | ntm_free_remote(ntmem); 160 | ntm_delete(ntmem); 161 | } 162 | 163 | void *NTHREAD_API ntm_delete_and_detach(ntmem_t *ntmem) 164 | { 165 | void *remote = NTM_REMOTE(ntmem); 166 | ntm_delete(ntmem); 167 | return remote; 168 | } 169 | 170 | void *NTHREAD_API ntm_pull_with_tunnel_ex(ntmem_t *ntmem, nttunnel_t *nttunnel, 171 | size_t len) 172 | { 173 | #ifdef LOG_LEVEL_3 174 | LOG_INFO("ntm_pull(ntmem=%p, nttunnel=%p)", ntmem, nttunnel); 175 | #endif /* ifdef LOG_LEVEL3 */ 176 | 177 | if (!ntt_can_read(nttunnel)) 178 | return NULL; 179 | 180 | void *remote = NTM_REMOTE(ntmem); 181 | void *local = NTM_LOCAL(ntmem); 182 | void *local_cpy = NTM_LOCAL_CPY(ntmem); 183 | 184 | if (HAS_ERR(ntu_read_memory(remote, local, len))) 185 | return NULL; 186 | 187 | memcpy(local_cpy, local, len); 188 | return local; 189 | } 190 | 191 | void *NTHREAD_API ntm_pull_with_tunnel(ntmem_t *ntmem, nttunnel_t *nttunnel) 192 | { 193 | return ntm_pull_with_tunnel_ex(ntmem, nttunnel, NTM_LENGTH(ntmem)); 194 | } 195 | 196 | static void *ntm_push_with_tunnel_ex(ntmem_t *ntmem, nttunnel_t *nttunnel, 197 | size_t begin, size_t len) 198 | { 199 | #ifdef LOG_LEVEL_3 200 | LOG_INFO( 201 | "ntm_push_with_tunnel(ntmem=%p, nttunnel=%p, begin=%d, len=%d)", 202 | ntmem, nttunnel, begin, len); 203 | #endif /* ifdef LOG_LEVEL3 */ 204 | 205 | if (!ntt_can_write(nttunnel)) 206 | return NULL; 207 | 208 | void *remote = NTM_REMOTE(ntmem) + begin; 209 | void *local = NTM_LOCAL(ntmem) + begin; 210 | void *local_cpy = NTM_LOCAL_CPY(ntmem) + begin; 211 | 212 | if (HAS_ERR(ntt_write(nttunnel, remote, local, len))) 213 | return NULL; 214 | 215 | memcpy(local_cpy, local, len); 216 | return remote; 217 | } 218 | 219 | void *NTHREAD_API ntm_push_with_tunnel(ntmem_t *ntmem, nttunnel_t *nttunnel) 220 | { 221 | return ntm_push_with_tunnel_ex(ntmem, nttunnel, 0, NTM_LENGTH(ntmem)); 222 | } 223 | 224 | static void *ntm_push_with_memset_ex(ntmem_t *ntmem, size_t begin, size_t len) 225 | { 226 | #ifdef LOG_LEVEL_3 227 | LOG_INFO("ntm_push_with_memset(ntmem=%p, begin=%d, len=%d)", ntmem, 228 | begin, len); 229 | #endif /* ifdef LOG_LEVEL3 */ 230 | 231 | void *remote = NTM_REMOTE(ntmem) + begin; 232 | void *local = NTM_LOCAL(ntmem) + begin; 233 | void *local_cpy = NTM_LOCAL_CPY(ntmem) + begin; 234 | 235 | if (ntm_is_safe_write(ntmem)) { 236 | if (HAS_ERR(ntu_write_with_memset(remote, local, len))) 237 | return NULL; 238 | } else { 239 | if (HAS_ERR(ntu_write_with_memset_dest(remote, local, len, 240 | local_cpy))) 241 | return NULL; 242 | } 243 | 244 | memcpy(local_cpy, local, len); 245 | return remote; 246 | } 247 | 248 | void *NTHREAD_API ntm_push_with_memset(ntmem_t *ntmem) 249 | { 250 | return ntm_push_with_memset_ex(ntmem, 0, NTM_LENGTH(ntmem)); 251 | } 252 | 253 | size_t _find_diff_rev(void *mem1, void *mem2, size_t len) 254 | { 255 | size_t i; 256 | for (i = len - 1; i >= 0; i--) { 257 | if (((char *)mem1)[i] != ((char *)mem2)[i]) 258 | return i; 259 | } 260 | 261 | return len; 262 | } 263 | 264 | size_t _find_diff(void *mem1, void *mem2, size_t len) 265 | { 266 | size_t i; 267 | for (i = 0; i < len; i++) { 268 | if (((char *)mem1)[i] != ((char *)mem2)[i]) 269 | return i; 270 | } 271 | 272 | return len; 273 | } 274 | 275 | void *NTHREAD_API ntm_push_ex(ntmem_t *ntmem, nttunnel_t *nttunnel) 276 | { 277 | void *remote = NTM_REMOTE(ntmem); 278 | bool b = !ntt_can_write(nttunnel); 279 | 280 | if (ntm_is_safe_write(ntmem)) { 281 | if (b) { 282 | if (ntm_push_with_memset(ntmem) == NULL) 283 | return NULL; 284 | } else if (ntm_push_with_tunnel(ntmem, nttunnel) == NULL) 285 | return NULL; 286 | } else { 287 | void *local = NTM_LOCAL(ntmem); 288 | void *local_cpy = NTM_LOCAL_CPY(ntmem); 289 | 290 | size_t sl = _find_diff(local, local_cpy, NTM_LENGTH(ntmem)); 291 | if (sl == NTM_LENGTH(ntmem)) 292 | return remote; 293 | 294 | size_t el = _find_diff_rev(local, local_cpy, NTM_LENGTH(ntmem)); 295 | size_t l = el - sl + 1; 296 | 297 | if (b || l < 3) { 298 | if (ntm_push_with_memset_ex(ntmem, sl, l) == NULL) 299 | return NULL; 300 | } else if (ntm_push_with_tunnel_ex(ntmem, nttunnel, sl, l) == 301 | NULL) 302 | return NULL; 303 | } 304 | 305 | return remote; 306 | } 307 | 308 | void *NTHREAD_API ntm_push(ntmem_t *ntmem) 309 | { 310 | nttunnel_t *nttunnel = ntu_nttunnel(); 311 | return ntm_push_ex(ntmem, nttunnel); 312 | } 313 | -------------------------------------------------------------------------------- /src/nttunnel.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "nttunnel.h" 26 | #include "ntmem.h" 27 | #include "nmem.h" 28 | 29 | #include 30 | 31 | void _ntt_delete_path(nttunnel_fschan_t *fschan) 32 | { 33 | if (fschan->path != NULL) { 34 | NFILE_DELETE(fschan->path); 35 | N_FREE(fschan->path); 36 | } 37 | } 38 | 39 | void *_ntt_create_path(ntmem_t *ntmem, nfile_path_t path) 40 | { 41 | void *local = NTM_LOCAL(ntmem); 42 | size_t l = NFILE_PATH_GET_SIZE(path); 43 | 44 | memcpy(local, path, l); 45 | return local; 46 | } 47 | 48 | void *_ntt_create_temp_path(ntmem_t *ntmem) 49 | { 50 | #ifdef __WIN32 51 | 52 | void *local = NTM_LOCAL(ntmem); 53 | DWORD temp_path_len = GetTempPathW(MAX_PATH, (void *)local); 54 | if (temp_path_len <= 0) 55 | return NULL; 56 | 57 | UINT unum = GetTempFileNameW((void *)local, NULL, 0, (void *)local); 58 | if (unum == 0) 59 | return NULL; 60 | 61 | #endif /* ifdef __WIN32 */ 62 | 63 | return local; 64 | } 65 | 66 | nerror_t _ntt_init_fschan(nttunnel_t *nttunnel, nttunnel_fschan_t *fschan, 67 | nttunnel_fschan_flags_t flags) 68 | { 69 | ntmem_t *ntmem = nttunnel->ntmem; 70 | 71 | if ((flags & NTTUNNEL_FSCHAN_CREATE_TEMP_PATH) != 0) { 72 | void *local = _ntt_create_temp_path(ntmem); 73 | if (local == NULL) 74 | return GET_ERR(NTTUNNEL_CREATE_TEMP_PATH_ERROR); 75 | } 76 | 77 | #ifdef __WIN32 78 | 79 | static const wchar_t mode_read[] = L"rb"; 80 | static const wchar_t mode_write[] = L"wb"; 81 | 82 | #endif /* ifdef __WIN32 */ 83 | 84 | int8_t mode_len; 85 | void *mode; 86 | bool read = (flags & NTTUNNEL_FSCHAN_WRITE_MODE) == 0; 87 | if (read) { 88 | mode = (void *)mode_write; 89 | mode_len = sizeof(mode_write); 90 | } else { 91 | mode = (void *)mode_read; 92 | mode_len = sizeof(mode_read); 93 | } 94 | 95 | void *local = NTM_LOCAL(ntmem); 96 | size_t temp_path_size = NFILE_PATH_GET_SIZE(local); 97 | memcpy(local + temp_path_size, mode, mode_len); 98 | 99 | bool save_path = (flags & NTTUNNEL_FSCHAN_DONT_SAVE_PATH) == 0; 100 | if (save_path) { 101 | fschan->path = N_ALLOC(temp_path_size); 102 | if (fschan->path == NULL) 103 | return GET_ERR(NTTUNNEL_ALLOC_ERROR); 104 | 105 | memcpy(fschan->path, local, temp_path_size); 106 | } else 107 | fschan->path = NULL; 108 | 109 | #ifdef LOG_LEVEL_2 110 | #ifdef __WIN32 111 | LOG_INFO("ntt_fschan_init(path=%ls, mode=%ls, flags(%02x)", local, 112 | local + temp_path_size, flags); 113 | #endif /* ifdef __WIN32 */ 114 | #endif /* ifdef LOG_LEVEL_2 */ 115 | 116 | if (read) 117 | fschan->local_file = nfile_open_r(local); 118 | else 119 | fschan->local_file = nfile_open_w(local); 120 | 121 | if (fschan->local_file == NULL) 122 | return GET_ERR(NTTUNNEL_NFILE_OPEN_ERROR); 123 | 124 | void *remote = ntm_push_ex(ntmem, nttunnel); 125 | if (remote == NULL) 126 | return GET_ERR(NTTUNNEL_NTM_PUSH_ERROR); 127 | 128 | fschan->remote_file = ntu_fopen(remote, remote + temp_path_size); 129 | if (fschan->remote_file == NULL) 130 | return GET_ERR(NTTUNNEL_NTU_FOPEN_ERROR); 131 | 132 | return N_OK; 133 | } 134 | 135 | void _ntt_destroy_fschan(nttunnel_fschan_t *fschan) 136 | { 137 | if (fschan == NULL) 138 | return; 139 | 140 | if (fschan->remote_file != NULL) { 141 | ntu_fclose(fschan->remote_file); 142 | fschan->remote_file = NULL; 143 | } 144 | 145 | if (fschan->local_file != NULL) { 146 | NFILE_CLOSE(fschan->local_file); 147 | fschan->local_file = NULL; 148 | } 149 | 150 | _ntt_delete_path(fschan); 151 | } 152 | 153 | bool NTHREAD_API ntt_can_read(nttunnel_t *nttunnel) 154 | { 155 | if (nttunnel == NULL) 156 | return false; 157 | 158 | return nttunnel->read.remote_file != NULL; 159 | } 160 | 161 | bool NTHREAD_API ntt_can_write(nttunnel_t *nttunnel) 162 | { 163 | if (nttunnel == NULL) 164 | return false; 165 | 166 | return nttunnel->write.remote_file != NULL; 167 | } 168 | 169 | nerror_t NTHREAD_API ntt_init_ex(nttunnel_t *nttunnel, 170 | nttunnel_fschan_flags_t flags) 171 | { 172 | #ifdef LOG_LEVEL_2 173 | LOG_INFO("ntt_init_ex(nttunnel=%p, flags=%02x)", nttunnel, flags); 174 | #endif /* ifdef LOG_LEVEL_2 */ 175 | 176 | memset(nttunnel, 0, sizeof(nttunnel_t)); 177 | 178 | nttunnel->ntmem = ntm_create_with_alloc_ex( 179 | NFILE_MAX_PATH_SIZE + NTTUNNEL_FSCHAN_MAX_MODE_SIZE); 180 | 181 | if (nttunnel->ntmem == NULL) 182 | return GET_ERR(NTTUNNEL_NTM_CREATE_WITH_ALLOC_EX_ERROR); 183 | 184 | nttunnel->max_transfer = NTTUNNEL_FSCHAN_CALC_MAX_TRANSFER(flags); 185 | 186 | if (HAS_ERR(_ntt_init_fschan(nttunnel, &nttunnel->write, 187 | flags | NTTUNNEL_FSCHAN_WRITE_MODE))) 188 | goto ntt_init_ex_init_fschan_error; 189 | 190 | if (HAS_ERR(_ntt_init_fschan(nttunnel, &nttunnel->read, flags))) { 191 | ntt_init_ex_init_fschan_error: 192 | ntt_destroy(nttunnel); 193 | return GET_ERR(NTTUNNEL_INIT_FSCHAN_ERROR); 194 | } 195 | 196 | return N_OK; 197 | } 198 | 199 | nerror_t NTHREAD_API ntt_init(nttunnel_t *nttunnel) 200 | { 201 | return ntt_init_ex(nttunnel, NTTUNNEL_FSCHAN_DEFAULT_FLAGS); 202 | } 203 | 204 | void NTHREAD_API ntt_destroy(nttunnel_t *nttunnel) 205 | { 206 | if (nttunnel == NULL) 207 | return; 208 | 209 | if (nttunnel->ntmem != NULL) { 210 | ntm_delete_and_free(nttunnel->ntmem); 211 | nttunnel->ntmem = NULL; 212 | } 213 | 214 | if (ntt_can_read(nttunnel)) 215 | _ntt_destroy_fschan(&nttunnel->read); 216 | 217 | if (ntt_can_write(nttunnel)) 218 | _ntt_destroy_fschan(&nttunnel->write); 219 | } 220 | 221 | nerror_t _ntt_fschan_read(nttunnel_fschan_t *fschan, const void *dest, 222 | void *source, size_t length) 223 | { 224 | size_t len_1 = ntu_fwrite(dest, 1, length, fschan->remote_file); 225 | if (len_1 != length) 226 | return GET_ERR(NTTUNNEL_NFILE_READ_ERROR); 227 | 228 | if (ntu_fflush(fschan->remote_file)) 229 | return GET_ERR(NTTUNNEL_NTU_FFLUSH_ERROR); 230 | 231 | size_t len_2 = NFILE_READ(fschan->local_file, source, length); 232 | if (len_2 != length) 233 | return GET_ERR(NTTUNNEL_NTU_FWRITE_ERROR); 234 | 235 | return N_OK; 236 | } 237 | 238 | nerror_t _ntt_fschan_write(nttunnel_fschan_t *fschan, void *dest, 239 | const void *source, size_t length) 240 | { 241 | size_t len_1 = NFILE_WRITE(fschan->local_file, source, length); 242 | if (len_1 != length) 243 | return GET_ERR(NTTUNNEL_NFILE_WRITE_ERROR); 244 | 245 | NFILE_FLUSH(fschan->local_file); 246 | 247 | size_t len_2 = ntu_fread(dest, 1, length, fschan->remote_file); 248 | if (len_2 != length) 249 | return GET_ERR(NTTUNNEL_NTU_FREAD_ERROR); 250 | 251 | return N_OK; 252 | } 253 | 254 | nerror_t _ntt_fschan_reset(nttunnel_t *nttunnel, nttunnel_fschan_t *fschan, 255 | nttunnel_fschan_flags_t flags) 256 | { 257 | nfile_path_t path = fschan->path; 258 | bool has_path = path != NULL; 259 | if ((flags & NTTUNNEL_FSCHAN_CREATE_TEMP_PATH) != 0) { 260 | nttunnel_fschan_t new_fschan; 261 | if (HAS_ERR(_ntt_init_fschan(nttunnel, &new_fschan, flags))) 262 | return GET_ERR(NTTUNNEL_INIT_FSCHAN_ERROR); 263 | 264 | _ntt_destroy_fschan(fschan); 265 | memcpy(fschan, &new_fschan, sizeof(nttunnel_fschan_t)); 266 | } else { 267 | if (has_path) 268 | _ntt_create_path(nttunnel->ntmem, path); 269 | else { 270 | flags |= NTTUNNEL_FSCHAN_CREATE_TEMP_PATH; 271 | flags |= NTTUNNEL_FSCHAN_DONT_SAVE_PATH; 272 | } 273 | 274 | _ntt_destroy_fschan(fschan); 275 | if (HAS_ERR(_ntt_init_fschan(nttunnel, fschan, flags))) 276 | return GET_ERR(NTTUNNEL_INIT_FSCHAN_ERROR); 277 | } 278 | 279 | return N_OK; 280 | } 281 | 282 | nerror_t NTHREAD_API ntt_read(nttunnel_t *nttunnel, const void *dest, 283 | void *source, size_t length) 284 | { 285 | nerror_t ret = _ntt_fschan_read(&nttunnel->read, dest, source, length); 286 | if (HAS_ERR(ret)) 287 | goto ntt_read_return; 288 | 289 | nttunnel->read_transfer += length; 290 | if (nttunnel->read_transfer >= nttunnel->max_transfer) { 291 | nttunnel->read_transfer = 0; 292 | ret = _ntt_fschan_reset(nttunnel, &nttunnel->read, 293 | NTTUNNEL_FSCHAN_CREATE_TEMP_PATH); 294 | } 295 | 296 | ntt_read_return: 297 | return ret; 298 | } 299 | 300 | nerror_t NTHREAD_API ntt_write(nttunnel_t *nttunnel, void *dest, 301 | const void *source, size_t length) 302 | { 303 | nerror_t ret = 304 | _ntt_fschan_write(&nttunnel->write, dest, source, length); 305 | if (HAS_ERR(ret)) 306 | goto ntt_write_return; 307 | 308 | nttunnel->write_transfer += length; 309 | if (nttunnel->write_transfer >= nttunnel->max_transfer) { 310 | nttunnel->write_transfer = 0; 311 | ret = _ntt_fschan_reset(nttunnel, &nttunnel->write, 312 | NTTUNNEL_FSCHAN_CREATE_TEMP_PATH | 313 | NTTUNNEL_FSCHAN_WRITE_MODE); 314 | } 315 | 316 | ntt_write_return: 317 | return ret; 318 | } 319 | -------------------------------------------------------------------------------- /src/ntutils.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2024, 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "ntutils.h" 26 | #include "neptune.h" 27 | #include "nmem.h" 28 | 29 | #include "nerror.h" 30 | #include "nthread.h" 31 | #include "ntucc.h" 32 | 33 | #include "ntmem.h" 34 | #include "nttunnel.h" 35 | 36 | #include 37 | #include 38 | 39 | struct ntutils_tfunctions { 40 | #ifdef __WIN32 41 | void *fopen; 42 | #endif /* ifdef __WIN32 */ 43 | 44 | void *memset; 45 | void *malloc; 46 | void *fwrite; 47 | void *fflush; 48 | void *fclose; 49 | void *fread; 50 | void *free; 51 | } ntu_funcs; 52 | 53 | #ifdef __WIN32 54 | DWORD ntu_tls_index; 55 | #endif /* ifdef __WIN32 */ 56 | 57 | ntutils_t *NTHREAD_API _ntu_get(void) 58 | { 59 | #ifdef __WIN32 60 | return (ntutils_t *)TlsGetValue(ntu_tls_index); 61 | #endif /* ifdef __WIN32 */ 62 | } 63 | 64 | nerror_t NTHREAD_API ntu_set(ntutils_t *ntutils) 65 | { 66 | #ifdef __WIN32 67 | 68 | if (!TlsSetValue(ntu_tls_index, (void *)ntutils)) 69 | return GET_ERR(NTUTILS_TLS_SET_VALUE_ERROR); 70 | 71 | #endif /* ifdef __WIN32 */ 72 | 73 | return N_OK; 74 | } 75 | 76 | ntutils_t *NTHREAD_API ntu_resize(size_t new_size) 77 | { 78 | if (new_size == 0) { 79 | ntu_set(NULL); 80 | return NULL; 81 | } 82 | 83 | ntutils_t *ntutils; 84 | ntutils_t *o_ntutils = ntu_get(); 85 | if (o_ntutils == NULL) 86 | ntutils = N_ALLOC(new_size); 87 | else 88 | ntutils = N_REALLOC(o_ntutils, new_size); 89 | 90 | if (ntutils != NULL) 91 | ntu_set(ntutils); 92 | 93 | return ntutils; 94 | } 95 | 96 | #ifndef NTU_GLOBAL_CC 97 | 98 | void NTHREAD_API _ntu_set_cc(ntucc_t cc) 99 | { 100 | ntutils_t *ntutils = ntu_get(); 101 | if (ntutils != NULL) 102 | ntu_set_cc_ex(ntutils, cc); 103 | } 104 | 105 | void NTHREAD_API _ntu_set_default_cc() 106 | { 107 | _ntu_set_cc(NTU_DEFAULT_CC); 108 | } 109 | 110 | ntutils_t *NTHREAD_API _ntu_o(ntucc_t cc) 111 | { 112 | ntutils_t *ntutils = ntu_get(); 113 | if (ntutils != NULL) 114 | ntu_set_cc_ex(ntutils, cc); 115 | 116 | return ntutils; 117 | } 118 | 119 | #endif // !NTU_GLOBAL_CC 120 | 121 | void *NTHREAD_API ntu_get_libc_base() 122 | { 123 | void *ret; 124 | 125 | #ifdef __WIN32 126 | 127 | ret = (void *)GetModuleHandleA("msvcrt"); 128 | if (ret == NULL) { 129 | #ifdef LOG_LEVEL_1 130 | LOG_INFO("msvcrt.dll not found, loading dynamically..."); 131 | #endif /* ifdef LOG_LEVEL_1 */ 132 | 133 | LoadLibraryA("msvcrt"); 134 | ret = (void *)GetModuleHandleA("msvcrt"); 135 | } 136 | 137 | #endif /* ifdef __WIN32 */ 138 | 139 | return ret; 140 | } 141 | 142 | nerror_t NTHREAD_API ntu_global_init(void) 143 | { 144 | #ifdef __WIN32 145 | 146 | ntu_tls_index = TlsAlloc(); 147 | if (ntu_tls_index == 0) 148 | return GET_ERR(NTUTILS_TLS_ALLOC_ERROR); 149 | 150 | #endif /* ifdef __WIN32 */ 151 | 152 | void *libc_base = ntu_get_libc_base(); 153 | if (libc_base == NULL) 154 | return GET_ERR(NTUTILS_GET_LIBC_BASE_ERROR); 155 | 156 | #ifdef __WIN32 157 | char func_names[] = 158 | "_wfopen\x05memsetmallocfwritefflushfclosefreadfree"; 159 | #endif /* ifdef __WIN32 */ 160 | 161 | int8_t i = 8; 162 | int8_t pos = 0; 163 | int8_t c = 0; 164 | int8_t func_pos = 0; 165 | 166 | char func_name[i]; 167 | 168 | while (true) { 169 | if (c == 0) { 170 | char fc = (char)func_names[pos]; 171 | if (fc == 0) 172 | break; 173 | 174 | if (fc <= 5) { 175 | c = fc; 176 | pos++; 177 | } else 178 | c = 1; 179 | 180 | i--; 181 | func_name[i] = 0; 182 | } 183 | 184 | memcpy(func_name, func_names + pos, i); 185 | pos += i; 186 | c--; 187 | 188 | #ifdef __WIN32 189 | void *func = GetProcAddress((void *)libc_base, func_name); 190 | #endif /* ifdef __WIN32 */ 191 | 192 | if (func == NULL) 193 | return GET_ERR(NTUTILS_FUNC_INIT_ERROR + func_pos); 194 | 195 | #ifdef LOG_LEVEL_3 196 | LOG_INFO("ntutils function(%s): %p", func_name, func); 197 | #endif /* ifdef LOG_LEVEL_3 */ 198 | 199 | ((void **)&ntu_funcs)[func_pos] = func; 200 | func_pos++; 201 | } 202 | 203 | return N_OK; 204 | } 205 | 206 | void NTHREAD_API ntu_global_destroy(void) 207 | { 208 | if (ntu_tls_index != 0) { 209 | #ifdef __WIN32 210 | TlsFree(ntu_tls_index); 211 | #endif /* ifdef __WIN32 */ 212 | 213 | ntu_tls_index = 0; 214 | } 215 | } 216 | 217 | nerror_t NTHREAD_API ntu_upgrade(nthread_t *nthread) 218 | { 219 | if (!NTHREAD_IS_VALID(nthread)) 220 | return GET_ERR(NTUTILS_NTHREAD_ERROR); 221 | 222 | ntutils_t *ntutils = ntu_resize(sizeof(ntutils_t)); 223 | if (ntutils == NULL) 224 | return GET_ERR(NTUTILS_NTU_RESIZE_ERROR); 225 | 226 | memcpy(&ntutils->nthread, nthread, sizeof(nthread_t)); 227 | nttunnel_t *nttunnel = NTU_NTTUNNEL_EX(ntutils); 228 | 229 | memset(nttunnel, 0, sizeof(nttunnel_t)); 230 | 231 | nerror_t ret; 232 | ntutils->stack_helper = ntm_create_ex(255 * sizeof(void *)); 233 | if (ntutils->stack_helper == NULL) { 234 | ret = GET_ERR(NTUTILS_NTM_CREATE_EX_ERROR); 235 | goto ntu_upgrade_error_ret; 236 | } 237 | 238 | ret = ntt_init(nttunnel); 239 | if (HAS_ERR(ret)) { 240 | ntu_upgrade_error_ret: 241 | ntu_destroy(); 242 | } 243 | 244 | return ret; 245 | } 246 | 247 | nerror_t NTHREAD_API ntu_attach_ex(ntid_t thread_id, 248 | nthread_reg_offset_t push_reg_offset, 249 | void *push_addr, void *sleep_addr) 250 | { 251 | nthread_t nthread; 252 | if (HAS_ERR(nthread_init(&nthread, thread_id, push_reg_offset, 253 | push_addr, sleep_addr))) { 254 | return GET_ERR(NTUTILS_NTHREAD_INIT_ERROR); 255 | } 256 | 257 | return ntu_upgrade(&nthread); 258 | } 259 | 260 | nerror_t NTHREAD_API ntu_attach(ntid_t thread_id, void *push_addr, 261 | void *sleep_addr) 262 | { 263 | return ntu_attach_ex(thread_id, NTHREAD_BEST_PUSH_REG, push_addr, 264 | sleep_addr); 265 | } 266 | 267 | void NTHREAD_API ntu_destroy() 268 | { 269 | ntutils_t *ntutils = ntu_get(); 270 | if (ntutils == NULL) 271 | return; 272 | 273 | if (ntutils->nthread.thread != NULL) { 274 | ntt_destroy(NTU_NTTUNNEL_EX(ntutils)); 275 | 276 | if (ntutils->stack_helper != NULL) 277 | ntm_delete(ntutils->stack_helper); 278 | 279 | nthread_destroy(&ntutils->nthread); 280 | } 281 | 282 | ntu_set(NULL); 283 | } 284 | 285 | nerror_t NTHREAD_API ntu_write_with_memset_value(void *dest, const void *source, 286 | size_t length, 287 | int8_t last_value) 288 | { 289 | size_t i = 0, j; 290 | while (i < length) { 291 | while (last_value == ((int8_t *)source)[i]) { 292 | i++; 293 | if (i >= length) 294 | break; 295 | } 296 | 297 | int8_t ms_value = ((int8_t *)source)[i]; 298 | for (j = i + 1; j < length; j++) { 299 | if (((int8_t *)source)[j] != ms_value) 300 | break; 301 | } 302 | 303 | void *addr = ntu_memset(dest + i, ms_value, j - i); 304 | if (addr == NULL) 305 | return GET_ERR(NTUTILS_NTU_MEMSET_ERROR); 306 | 307 | i = j; 308 | } 309 | 310 | return N_OK; 311 | } 312 | 313 | nerror_t NTHREAD_API ntu_write_with_memset_dest(void *dest, const void *source, 314 | size_t length, 315 | const void *last_dest) 316 | { 317 | size_t i = 0, j; 318 | while (i < length) { 319 | if (last_dest != NULL) { 320 | while (((int8_t *)last_dest)[i] == 321 | ((int8_t *)source)[i]) { 322 | i++; 323 | if (i >= length) 324 | break; 325 | } 326 | } 327 | 328 | int8_t ms_value = ((int8_t *)source)[i]; 329 | for (j = i + 1; j < length; j++) { 330 | if (((int8_t *)source)[j] != ms_value) 331 | break; 332 | } 333 | 334 | void *addr = ntu_memset(dest + i, ms_value, j - i); 335 | if (addr == NULL) 336 | return GET_ERR(NTUTILS_NTU_MEMSET_ERROR); 337 | 338 | i = j; 339 | } 340 | 341 | return N_OK; 342 | } 343 | 344 | nerror_t NTHREAD_API ntu_write_with_memset(void *dest, const void *source, 345 | size_t length) 346 | { 347 | size_t i = 0, j; 348 | while (i < length) { 349 | int8_t ms_value = ((int8_t *)source)[i]; 350 | for (j = i + 1; j < length; j++) { 351 | if (((int8_t *)source)[j] != ms_value) 352 | break; 353 | } 354 | 355 | void *addr = ntu_memset(dest + i, ms_value, j - i); 356 | if (addr == NULL) 357 | return GET_ERR(NTUTILS_NTU_MEMSET_ERROR); 358 | 359 | i = j; 360 | } 361 | 362 | return N_OK; 363 | } 364 | 365 | void NTHREAD_API ntu_set_reg_args(uint8_t arg_count, void **args) 366 | { 367 | ntutils_t *ntutils = ntu_get(); 368 | nthread_t *nthread = &ntutils->nthread; 369 | 370 | #ifdef NTU_GLOBAL_CC 371 | ntucc_t sel_cc = NTU_GLOBAL_CC; 372 | #else /* ifdnef NTU_GLOBAL_CC */ 373 | ntucc_t sel_cc = ntutils->sel_cc; 374 | #endif /* ifndef NTU_GLOBAL_CC */ 375 | 376 | int8_t i; 377 | for (i = 0; i < 8 && i < arg_count; i++) { 378 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 379 | if (reg_index == 0) 380 | continue; 381 | 382 | nthread_reg_offset_t off = 383 | NTHREAD_REG_INDEX_TO_OFFSET(reg_index); 384 | 385 | NTHREAD_SET_REG(nthread, off, args[i]); 386 | } 387 | } 388 | 389 | nerror_t NTHREAD_API ntu_set_args_v(uint8_t arg_count, va_list args) 390 | { 391 | ntutils_t *ntutils = ntu_get(); 392 | nthread_t *nthread = &ntutils->nthread; 393 | 394 | #ifdef NTU_GLOBAL_CC 395 | ntucc_t sel_cc = NTU_GLOBAL_CC; 396 | #else /* ifdnef NTU_GLOBAL_CC */ 397 | ntucc_t sel_cc = ntutils->sel_cc; 398 | #endif /* ifndef NTU_GLOBAL_CC */ 399 | 400 | #ifdef LOG_LEVEL_3 401 | LOG_INFO("ntu_set_args_v(cc=%p, nthread_id=%ld, arg_count=%d, args=%p)", 402 | sel_cc, NTHREAD_GET_ID(nthread), arg_count, args); 403 | #endif /* ifdef LOG_LEVEL_3 */ 404 | 405 | int8_t reg_arg_count = 0; 406 | 407 | uint8_t i; 408 | for (i = 0; i < 8; i++) { 409 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 410 | if (reg_index != 0) 411 | reg_arg_count++; 412 | } 413 | 414 | if (reg_arg_count > arg_count) 415 | reg_arg_count = arg_count; 416 | 417 | uint8_t push_arg_count = arg_count - reg_arg_count; 418 | bool need_push = push_arg_count > 0; 419 | 420 | void *rsp = nthread_stack_begin(nthread); 421 | void *wpos = rsp + NTUCC_GET_STACK_ADD(sel_cc); 422 | 423 | void **push_args; 424 | ntmem_t *ntmem; 425 | if (need_push) { 426 | ntmem = ntutils->stack_helper; 427 | NTM_SET_REMOTE(ntmem, wpos); 428 | 429 | if (ntm_reset_remote_ex(ntmem, push_arg_count * 430 | sizeof(void *)) == NULL) 431 | return GET_ERR(NTUTILS_NTM_RESET_REMOTE_EX_ERROR); 432 | 433 | push_args = (void **)ntm_reset_locals(ntmem); 434 | } 435 | uint8_t push_arg_pos; 436 | 437 | void *reg_args[8]; 438 | nthread_reg_offset_t reg_offsets[8]; 439 | 440 | bool reverse = (sel_cc & NTUCC_REVERSE_OP) != 0; 441 | if (reverse) 442 | push_arg_pos = push_arg_count - 1; 443 | else 444 | push_arg_pos = 0; 445 | 446 | for (i = 0; i < arg_count; i++) { 447 | void *arg = va_arg(args, void *); 448 | if (i < 8) { 449 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 450 | if (reg_index != 0) { 451 | reg_args[i] = arg; 452 | continue; 453 | } 454 | } 455 | 456 | push_args[push_arg_pos] = arg; 457 | if (reverse) 458 | push_arg_pos--; 459 | else 460 | push_arg_pos++; 461 | } 462 | 463 | if (need_push && ntm_push(ntmem) == NULL) 464 | return GET_ERR(NTUTILS_NTM_PUSH_ERROR); 465 | 466 | ntu_set_reg_args(arg_count, reg_args); 467 | return N_OK; 468 | } 469 | 470 | nerror_t NTHREAD_API ntu_set_args(uint8_t arg_count, ...) 471 | { 472 | va_list args; 473 | va_start(args, arg_count); 474 | 475 | nerror_t ret = ntu_set_args_v(arg_count, args); 476 | 477 | va_end(args); 478 | return ret; 479 | } 480 | 481 | nerror_t NTHREAD_API ntu_call_v(void *func_addr, uint8_t arg_count, 482 | va_list args) 483 | { 484 | ntutils_t *ntutils = ntu_get(); 485 | 486 | RET_ERR(ntu_set_args_v(arg_count, args)); 487 | return nthread_call(&ntutils->nthread, func_addr, &ntutils->ret_value); 488 | } 489 | 490 | nerror_t NTHREAD_API ntu_call(void *func_addr, uint8_t arg_count, ...) 491 | { 492 | va_list args; 493 | va_start(args, arg_count); 494 | 495 | nerror_t ret = ntu_call_v(func_addr, arg_count, args); 496 | 497 | va_end(args); 498 | return ret; 499 | } 500 | 501 | void *NTHREAD_API ntu_ucall_v(void *func_addr, uint8_t arg_count, va_list args) 502 | { 503 | ntutils_t *ntutils = ntu_get(); 504 | if (HAS_ERR(ntu_call_v(func_addr, arg_count, args))) 505 | return NULL; 506 | 507 | return ntutils->ret_value; 508 | } 509 | 510 | void *NTHREAD_API _ntu_ucall(void *func_addr, uint8_t arg_count, ...) 511 | { 512 | va_list args; 513 | va_start(args, arg_count); 514 | 515 | nerror_t *ret = ntu_ucall_v(func_addr, arg_count, args); 516 | 517 | va_end(args); 518 | return ret; 519 | } 520 | 521 | void *NTHREAD_API ntu_memset(void *dest, int fill, size_t length) 522 | { 523 | ntu_set_default_cc(); 524 | return ntu_ucall(ntu_funcs.memset, dest, fill, length); 525 | } 526 | 527 | void *NTHREAD_API ntu_malloc(size_t size) 528 | { 529 | ntu_set_default_cc(); 530 | return ntu_ucall(ntu_funcs.malloc, size); 531 | } 532 | 533 | void NTHREAD_API ntu_free(void *address) 534 | { 535 | ntu_set_default_cc(); 536 | ntu_ucall(ntu_funcs.free, address); 537 | } 538 | 539 | FILE *NTHREAD_API ntu_fopen(const nfile_path_t filename, 540 | const nfile_path_t mode) 541 | { 542 | ntu_set_default_cc(); 543 | return (FILE *)ntu_ucall(ntu_funcs.fopen, filename, mode); 544 | } 545 | 546 | size_t NTHREAD_API ntu_fread(void *buffer, size_t size, size_t count, 547 | FILE *fstream) 548 | { 549 | ntu_set_default_cc(); 550 | return (size_t)ntu_ucall(ntu_funcs.fread, buffer, size, count, fstream); 551 | } 552 | 553 | size_t NTHREAD_API ntu_fwrite(const void *buffer, size_t size, size_t count, 554 | FILE *fstream) 555 | { 556 | ntu_set_default_cc(); 557 | return (size_t)ntu_ucall(ntu_funcs.fwrite, buffer, size, count, 558 | fstream); 559 | } 560 | 561 | int NTHREAD_API ntu_fflush(FILE *fstream) 562 | { 563 | ntutils_t *ntutils = ntu_o(NTU_DEFAULT_CC); 564 | if (HAS_ERR(ntu_call(ntu_funcs.fflush, 1, fstream))) 565 | return -1; 566 | 567 | return (size_t)ntutils->ret_value; 568 | } 569 | 570 | int NTHREAD_API ntu_fclose(FILE *fstream) 571 | { 572 | ntutils_t *ntutils = ntu_o(NTU_DEFAULT_CC); 573 | if (HAS_ERR(ntu_call(ntu_funcs.fclose, 1, fstream))) 574 | return -1; 575 | 576 | return (size_t)ntutils->ret_value; 577 | } 578 | 579 | void *NTHREAD_API ntu_alloc_str(const char *str) 580 | { 581 | size_t str_len = (strlen(str) + 1) * sizeof(*str); 582 | void *addr = ntu_malloc(str_len); 583 | if (addr == NULL) 584 | return NULL; 585 | 586 | if (HAS_ERR(ntu_write(addr, str, str_len))) { 587 | ntu_free(addr); 588 | return NULL; 589 | } 590 | 591 | return addr; 592 | } 593 | 594 | nttunnel_t *NTHREAD_API ntu_nttunnel() 595 | { 596 | ntutils_t *ntutils = ntu_get(); 597 | return NTU_NTTUNNEL_EX(ntutils); 598 | } 599 | 600 | bool NTHREAD_API ntu_tunnel_can_read() 601 | { 602 | nttunnel_t *nttunnel = ntu_nttunnel(); 603 | return ntt_can_read(nttunnel); 604 | } 605 | 606 | bool NTHREAD_API ntu_tunnel_can_write() 607 | { 608 | nttunnel_t *nttunnel = ntu_nttunnel(); 609 | return ntt_can_write(nttunnel); 610 | } 611 | 612 | nerror_t NTHREAD_API ntu_tunnel_read(const void *dest, void *source, 613 | size_t length) 614 | { 615 | nttunnel_t *tunnel = ntu_nttunnel(); 616 | return ntt_read(tunnel, dest, source, length); 617 | } 618 | 619 | nerror_t NTHREAD_API ntu_tunnel_write(void *dest, const void *source, 620 | size_t length) 621 | { 622 | nttunnel_t *tunnel = ntu_nttunnel(); 623 | return ntt_write(tunnel, dest, source, length); 624 | } 625 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(NEPTUNE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/Neptune") 4 | 5 | if(NOT TARGET Neptune) 6 | if(EXISTS "${NEPTUNE_DIR}" AND EXISTS "${NEPTUNE_DIR}/CMakeLists.txt") 7 | add_subdirectory(${NEPTUNE_DIR}) 8 | else() 9 | message(STATUS "Neptune directory or CMakeLists.txt not found, skipping add_subdirectory(Neptune).") 10 | endif() 11 | endif() 12 | 13 | if(TARGET Neptune) 14 | add_executable(inject ${CMAKE_CURRENT_SOURCE_DIR}/inject.c) 15 | target_link_libraries(inject PRIVATE NThread Neptune) 16 | target_compile_definitions(inject PRIVATE LOG_LEVEL_3 NEPTUNE_MODULERULES_HEADER="nthread_rules.h") 17 | target_include_directories(inject PRIVATE ${NTHREAD_INCLUDE_DIR}) 18 | else() 19 | message(STATUS "Neptune target not found, skipping tests.") 20 | endif() 21 | -------------------------------------------------------------------------------- /tests/inject.c: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2024, 2025 Serkan Aksoy 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "neptune.h" 26 | #include "nerror.h" 27 | #include "nthread.h" 28 | #include "ntutils.h" 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | int8_t push_sleep[] = { 0x55, 0xc3, 0xeb, 0xfe }; 35 | 36 | int main(int argc, char *argv[]) 37 | { 38 | if (HAS_ERR(neptune_init())) 39 | return EXIT_FAILURE; 40 | 41 | LOG_INFO("Neptune initilaized!"); 42 | 43 | ntid_t tid; 44 | printf("enter tid: "); 45 | scanf("%ld", &tid); 46 | 47 | #ifdef __WIN32 48 | 49 | HANDLE thread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid); 50 | if (thread == NULL) { 51 | LOG_ERROR("Thread not found"); 52 | neptune_destroy(); 53 | return 0x30; 54 | } 55 | 56 | DWORD pid = GetProcessIdOfThread(thread); 57 | HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 58 | if (proc == NULL) { 59 | neptune_destroy(); 60 | return 0x31; 61 | } 62 | 63 | void *push_sleep_addr = VirtualAllocEx(proc, NULL, sizeof(push_sleep), 64 | MEM_RESERVE | MEM_COMMIT, 65 | PAGE_EXECUTE_READWRITE); 66 | 67 | if (push_sleep_addr == NULL) { 68 | neptune_destroy(); 69 | return 0x32; 70 | } 71 | 72 | SIZE_T write_len; 73 | if (!WriteProcessMemory(proc, push_sleep_addr, push_sleep, 74 | sizeof(push_sleep), &write_len)) { 75 | neptune_destroy(); 76 | return 0x33; 77 | } 78 | 79 | #endif /* ifdef __WIN32 */ 80 | 81 | LOG_INFO("%lld bytes writed to %ld", write_len, pid); 82 | 83 | if (HAS_ERR(ntu_attach(tid, push_sleep_addr, push_sleep_addr + 2))) { 84 | LOG_INFO("ntu_init failed"); 85 | neptune_destroy(); 86 | return 0x06; 87 | } 88 | 89 | LOG_INFO("ntutils initilaized"); 90 | 91 | char test_str[] = "test string"; 92 | void *str_addr = ntu_alloc_str(test_str); 93 | if (str_addr == NULL) { 94 | ntu_destroy(); 95 | neptune_destroy(); 96 | return 0x34; 97 | } 98 | 99 | int8_t buffer[64] = { 0 }; 100 | if (HAS_ERR(ntu_read_memory(str_addr, buffer, sizeof(test_str)))) { 101 | ntu_destroy(); 102 | neptune_destroy(); 103 | return 0x35; 104 | } 105 | 106 | ntu_free(str_addr); 107 | LOG_INFO("Buffer: %s", buffer); 108 | if (strcmp((void *)buffer, test_str) != 0) { 109 | ntu_destroy(); 110 | neptune_destroy(); 111 | return 0x36; 112 | } 113 | 114 | ntu_destroy(); 115 | neptune_destroy(); 116 | return EXIT_SUCCESS; 117 | } 118 | --------------------------------------------------------------------------------