├── .gitignore ├── modulerules ├── nthread_rules.h └── 20_ntutils.h ├── .gitmodules ├── CMakeLists.txt ├── tests ├── CMakeLists.txt └── inject.c ├── LICENSE.md ├── README.md ├── .clang-format ├── include ├── ntucc.h ├── nttunnel.h ├── ntmem.h ├── nthread.h └── ntutils.h └── src ├── ntmem.c ├── nttunnel.c ├── nthread.c └── ntutils.c /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .cache/ 3 | .vscode/ 4 | .clangd 5 | -------------------------------------------------------------------------------- /modulerules/nthread_rules.h: -------------------------------------------------------------------------------- 1 | #include "10_neptune.h" 2 | #include "20_ntutils.h" 3 | #include "50_neptune.h" 4 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 LOG_ON_STDOUT=1 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | void thread_func(void) 37 | { 38 | while (1) 39 | Sleep(10); 40 | } 41 | 42 | int main(int argc, char *argv[]) 43 | { 44 | if (HAS_ERR(neptune_init())) 45 | return EXIT_FAILURE; 46 | 47 | LOG_INFO("Neptune initilaized!"); 48 | 49 | DWORD tid; 50 | HANDLE create_thread = CreateThread( 51 | NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, NULL, 0, &tid); 52 | if (create_thread == NULL) { 53 | LOG_ERROR("Thread creation failed"); 54 | neptune_destroy(); 55 | return 0x01; 56 | } 57 | 58 | #ifdef _WIN32 59 | 60 | HANDLE thread = OpenThread(THREAD_ALL_ACCESS, FALSE, tid); 61 | if (thread == NULL) { 62 | LOG_ERROR("Thread not found"); 63 | neptune_destroy(); 64 | CloseHandle(create_thread); 65 | return 0x30; 66 | } 67 | 68 | DWORD pid = GetProcessIdOfThread(thread); 69 | HANDLE proc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); 70 | if (proc == NULL) { 71 | LOG_ERROR("Process not found"); 72 | neptune_destroy(); 73 | CloseHandle(create_thread); 74 | return 0x31; 75 | } 76 | 77 | void *push_sleep_addr = VirtualAllocEx(proc, NULL, sizeof(push_sleep), 78 | MEM_RESERVE | MEM_COMMIT, 79 | PAGE_EXECUTE_READWRITE); 80 | 81 | if (push_sleep_addr == NULL) { 82 | LOG_ERROR("Memory allocation failed"); 83 | neptune_destroy(); 84 | CloseHandle(create_thread); 85 | return 0x32; 86 | } 87 | 88 | SIZE_T write_len; 89 | if (!WriteProcessMemory(proc, push_sleep_addr, push_sleep, 90 | sizeof(push_sleep), &write_len)) { 91 | neptune_destroy(); 92 | CloseHandle(create_thread); 93 | return 0x33; 94 | } 95 | 96 | #endif /* ifdef _WIN32 */ 97 | 98 | LOG_INFO("%lld bytes writed to %ld", write_len, pid); 99 | 100 | if (HAS_ERR(ntu_attach(tid, push_sleep_addr, 101 | (void *)((int8_t *)push_sleep_addr + 2)))) { 102 | LOG_INFO("ntu_init failed"); 103 | neptune_destroy(); 104 | CloseHandle(create_thread); 105 | return 0x06; 106 | } 107 | 108 | LOG_INFO("ntutils initilaized"); 109 | 110 | char test_str[] = "test string"; 111 | void *str_addr = ntu_alloc_str(test_str); 112 | if (str_addr == NULL) { 113 | ntu_destroy(); 114 | neptune_destroy(); 115 | CloseHandle(create_thread); 116 | return 0x34; 117 | } 118 | 119 | int8_t buffer[64] = { 0 }; 120 | if (HAS_ERR(ntu_read_memory(str_addr, buffer, sizeof(test_str)))) { 121 | ntu_destroy(); 122 | neptune_destroy(); 123 | CloseHandle(create_thread); 124 | return 0x35; 125 | } 126 | 127 | ntu_free(str_addr); 128 | LOG_INFO("Buffer: %s", buffer); 129 | if (strcmp((void *)buffer, test_str) != 0) { 130 | ntu_destroy(); 131 | neptune_destroy(); 132 | CloseHandle(create_thread); 133 | return 0x36; 134 | } 135 | 136 | LOG_INFO("String readed from target process"); 137 | 138 | ntu_destroy(); 139 | LOG_INFO("ntutils destroyed"); 140 | LOG_INFO("Test successful"); 141 | 142 | neptune_destroy(); 143 | CloseHandle(create_thread); 144 | return EXIT_SUCCESS; 145 | } 146 | -------------------------------------------------------------------------------- /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 __NTUCC_H__ 34 | #define __NTUCC_H__ 35 | 36 | #include "neptune.h" 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | typedef int64_t ntucc_t; 43 | 44 | #define NTUCC_RBX NTHREAD_RBX_INDEX 45 | #define NTUCC_RCX NTHREAD_RCX_INDEX 46 | #define NTUCC_RDX NTHREAD_RDX_INDEX 47 | #define NTUCC_RDI NTHREAD_RDI_INDEX 48 | #define NTUCC_RSI NTHREAD_RSI_INDEX 49 | #define NTUCC_RSP NTHREAD_RSP_INDEX 50 | #define NTUCC_RBP NTHREAD_RSP_INDEX 51 | #define NTUCC_R8 NTHREAD_R8_INDEX 52 | #define NTUCC_R9 NTHREAD_R9_INDEX 53 | #define NTUCC_R10 NTHREAD_R10_INDEX 54 | #define NTUCC_R11 NTHREAD_R11_INDEX 55 | #define NTUCC_R12 NTHREAD_R12_INDEX 56 | #define NTUCC_R13 NTHREAD_R13_INDEX 57 | #define NTUCC_R14 NTHREAD_R14_INDEX 58 | #define NTUCC_R15 NTHREAD_R15_INDEX 59 | 60 | #define NTUCC_CALC_ARG(reg_index, arg_pos) \ 61 | (((int64_t)(reg_index)) << (((arg_pos) * 4) + 0x20)) 62 | 63 | #define NTUCC_CREATE_ARG_1(i1) (NTUCC_CALC_ARG(i1, 0)) 64 | #define NTUCC_CREATE_ARG_2(i1, i2) \ 65 | (NTUCC_CREATE_ARG_1(i1) + NTUCC_CALC_ARG(i2, 1)) 66 | 67 | #define NTUCC_CREATE_ARG_3(i1, i2, i3) \ 68 | (NTUCC_CREATE_ARG_2(i1, i2) + NTUCC_CALC_ARG(i3, 2)) 69 | 70 | #define NTUCC_CREATE_ARG_4(i1, i2, i3, i4) \ 71 | (NTUCC_CREATE_ARG_3(i1, i2, i3) + NTUCC_CALC_ARG(i4, 3)) 72 | 73 | #define NTUCC_CREATE_ARG_5(i1, i2, i3, i4, i5) \ 74 | (NTUCC_CREATE_ARG_4(i1, i2, i3, i4) + NTUCC_CALC_ARG(i5, 4)) 75 | 76 | #define NTUCC_CREATE_ARG_6(i1, i2, i3, i4, i5, i6) \ 77 | (NTUCC_CREATE_ARG_5(i1, i2, i3, i4, i5) + NTUCC_CALC_ARG(i6, 5)) 78 | 79 | #define NTUCC_CREATE_ARG_7(i1, i2, i3, i4, i5, i6, i7) \ 80 | (NTUCC_CREATE_ARG_6(i1, i2, i3, i4, i5, i6) + NTUCC_CALC_ARG(i7, 6)) 81 | 82 | #define NTUCC_CREATE_ARG_8(i1, i2, i3, i4, i5, i6, i7, i8) \ 83 | (NTUCC_CREATE_ARG_6(i1, i2, i3, i4, i5, i6, i7) + NTUCC_CALC_ARG(i8, 7)) 84 | 85 | #define NTUCC_GET_ARG_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, NAME, ...) NAME 86 | #define NTUCC_CREATE_ARG_MASK(...) \ 87 | NTUCC_GET_ARG_MACRO(__VA_ARGS__, NTUCC_CREATE_ARG_8, \ 88 | NTUCC_CREATE_ARG_7, NTUCC_CREATE_ARG_6, \ 89 | NTUCC_CREATE_ARG_5, NTUCC_CREATE_ARG_4, \ 90 | NTUCC_CREATE_ARG_3, NTUCC_CREATE_ARG_2, \ 91 | NTUCC_CREATE_ARG_1)(__VA_ARGS__) 92 | 93 | #define NTUCC_GET_ARG(cc, arg_pos) \ 94 | (((int8_t)(cc >> (((arg_pos) * 4) + 0x20))) & 0x0F) 95 | 96 | #define NTUCC_REVERSE_OP (0x10000) 97 | #define NTUCC_AUTO_CLEAN (0x20000) 98 | 99 | #define NTUCC_GET_STACK_ADD(cc) ((uint16_t)(cc & 0xFFFF)) 100 | 101 | #define NTUCC_WINDOWS_X64 \ 102 | (NTUCC_CREATE_ARG_MASK(NTUCC_RCX, NTUCC_RDX, NTUCC_R8, NTUCC_R9) | \ 103 | NTUCC_AUTO_CLEAN + sizeof(DWORD64) * 4) 104 | 105 | #ifdef NTUCC_WINDOWS_X64 106 | 107 | #define NTUCC_WINDOWS_X64_PASS_RCX \ 108 | ((NTUCC_CREATE_ARG_MASK(NTUCC_RDX, NTUCC_R8, NTUCC_R9) | \ 109 | NTUCC_AUTO_CLEAN) + \ 110 | sizeof(DWORD64) * 4) 111 | 112 | #endif // NTUCC_WINDOWS_X64 113 | 114 | #define NTUCC_MAX_REGARG_COUNT 4 115 | 116 | #define NTUCC_GET_REGARG_COUNT(ntu_cc) (ntu_cc & NTUCC_HAS_REGARG_MASK) 117 | #define NTUCC_HAS_REGARG(ntu_cc) ((ntu_cc & NTUCC_HAS_REGARG_MASK) != 0) 118 | 119 | #ifdef _WIN32 120 | #define NTUCC_DEFAULT NTUCC_WINDOWS_X64 121 | #endif // _WIN32 122 | 123 | #ifdef __cplusplus 124 | } 125 | #endif 126 | 127 | #endif // !__NTUCC_H__ 128 | -------------------------------------------------------------------------------- /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 "nthread.h" 43 | #include "nfile.h" 44 | 45 | #ifdef __cplusplus 46 | extern "C" { 47 | #endif 48 | 49 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_256 0x00 50 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_1024 0x01 51 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_4096 0x02 52 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_16384 0x03 53 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_65536 0x04 54 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_262144 0x05 55 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_1048576 0x06 56 | #define NTTUNNEL_FSCHAN_MAX_TRANSFER_4194304 0x07 57 | #define NTTUNNEL_FSCHAN_DONT_SAVE_PATH 0x08 58 | #define NTTUNNEL_FSCHAN_WRITE_MODE 0x10 59 | #define NTTUNNEL_FSCHAN_CREATE_TEMP_PATH 0x20 60 | 61 | #define NTTUNNEL_FSCHAN_DEFAULT_FLAGS \ 62 | NTTUNNEL_FSCHAN_MAX_TRANSFER_262144 | NTTUNNEL_FSCHAN_CREATE_TEMP_PATH 63 | 64 | #define NTTUNNEL_FSCHAN_MAX_MODE_SIZE 6 65 | 66 | #define NTTUNNEL_FSCHAN_CALC_MAX_TRANSFER(flags) \ 67 | (((size_t)4) << (6 + (flags & 0x07) * 2)) 68 | 69 | typedef int8_t nttunnel_fschan_flags_t; 70 | 71 | struct nttunnel_fschan { 72 | void *remote_file; 73 | nfile_t local_file; 74 | 75 | nfile_path_t path; 76 | }; 77 | 78 | typedef struct nttunnel_fschan nttunnel_fschan_t; 79 | 80 | typedef struct ntmem ntmem_t; 81 | 82 | struct nttunnel { 83 | ntmem_t *ntmem; 84 | 85 | nttunnel_fschan_t read; 86 | size_t read_transfer; 87 | 88 | nttunnel_fschan_t write; 89 | size_t write_transfer; 90 | 91 | size_t max_transfer; 92 | }; 93 | 94 | typedef struct nttunnel nttunnel_t; 95 | 96 | #define NTTUNNEL_ERROR 0x7600 97 | 98 | #define NTTUNNEL_GET_TEMP_PATH_ERROR 0x7601 99 | #define NTTUNNEL_GET_TEMP_FILE_ERROR 0x7602 100 | #define NTTUNNEL_ALLOC_ERROR 0x7603 101 | #define NTTUNNEL_NFILE_OPEN_ERROR 0x7604 102 | #define NTTUNNEL_NTM_PUSH_ERROR 0x7605 103 | #define NTTUNNEL_NTU_FOPEN_ERROR 0x7606 104 | #define NTTUNNEL_NFILE_WRITE_ERROR 0x7607 105 | #define NTTUNNEL_NFILE_READ_ERROR 0x7608 106 | #define NTTUNNEL_NTU_FREAD_ERROR 0x7609 107 | #define NTTUNNEL_NTU_FWRITE_ERROR 0x760A 108 | #define NTTUNNEL_NTU_FFLUSH_ERROR 0x760B 109 | #define NTTUNNEL_INIT_FSCHAN_ERROR 0x760C 110 | #define NTTUNNEL_CREATE_TEMP_PATH_ERROR 0x760D 111 | #define NTTUNNEL_NTM_CREATE_WITH_ALLOC_EX_ERROR 0x760E 112 | 113 | #define NTTUNNEL_ERROR_E NTTUNNEL_NTM_CREATE_ERROR 114 | 115 | /** 116 | * @brief Check if the tunnel is ready for reading. 117 | * 118 | * @param nttunnel Pointer to the tunnel structure. 119 | * @return true if reading is available; false otherwise. 120 | */ 121 | NTHREAD_API bool ntt_can_read(nttunnel_t *nttunnel); 122 | 123 | /** 124 | * @brief Check if the tunnel is ready for writing. 125 | * 126 | * @param nttunnel Pointer to the tunnel structure. 127 | * @return true if writing is available; false otherwise. 128 | */ 129 | NTHREAD_API bool ntt_can_write(nttunnel_t *nttunnel); 130 | 131 | /** 132 | * @brief Initialize the tunnel with specific FSCHAN flags. 133 | * 134 | * @param nttunnel Pointer to the tunnel structure. 135 | * @param flags Flags controlling tunnel behavior. 136 | * @return Error code. 137 | */ 138 | NTHREAD_API nerror_t ntt_init_ex(nttunnel_t *nttunnel, 139 | nttunnel_fschan_flags_t flags); 140 | 141 | /** 142 | * @brief Initialize the tunnel with default settings. 143 | * 144 | * @param nttunnel Pointer to the tunnel structure. 145 | * @return Error. 146 | */ 147 | NTHREAD_API nerror_t ntt_init(nttunnel_t *nttunnel); 148 | 149 | /** 150 | * @brief Clean up and release resources associated with the tunnel. 151 | * 152 | * @param nttunnel Pointer to the tunnel structure. 153 | */ 154 | NTHREAD_API void ntt_destroy(nttunnel_t *nttunnel); 155 | 156 | /** 157 | * @brief Read memory through the tunnel. 158 | * 159 | * @param nttunnel Pointer to the tunnel structure. 160 | * @param dest Address in local memory to copy data into. 161 | * @param source Source address in the remote or target memory. 162 | * @param length Number of bytes to read. 163 | * @return Error code. 164 | */ 165 | NTHREAD_API nerror_t ntt_read(nttunnel_t *nttunnel, const void *dest, 166 | void *source, size_t length); 167 | 168 | /** 169 | * @brief Write memory through the tunnel. 170 | * 171 | * This function uses `ntu_write_with_memset` internally to perform the memory write. 172 | * 173 | * @param nttunnel Pointer to the tunnel structure. 174 | * @param dest Destination address in the remote or target memory. 175 | * @param source Local buffer to write from. 176 | * @param length Number of bytes to write. 177 | * @return Error code. 178 | */ 179 | NTHREAD_API nerror_t ntt_write(nttunnel_t *nttunnel, void *dest, 180 | const void *source, size_t length); 181 | 182 | #ifdef __cplusplus 183 | } 184 | #endif 185 | 186 | #endif // !__NTTUNNEL_H__ 187 | -------------------------------------------------------------------------------- /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 | #ifdef __cplusplus 44 | extern "C" { 45 | #endif 46 | #define NTMEM_SAFE_WRITE 0x10 47 | 48 | typedef int8_t ntmem_flags_t; 49 | 50 | typedef struct nttunnel nttunnel_t; 51 | 52 | struct ntmem { 53 | ntmem_flags_t flags; 54 | 55 | size_t length; 56 | void *remote_mem; 57 | }; 58 | 59 | #define NTM_LENGTH(ntmem) (ntmem->length) 60 | #define NTM_SET_LENGTH(ntmem, set_length) (ntmem->length = (set_length)) 61 | 62 | #define NTM_CALC_LOCALS_SIZE(length) (length * 2 * sizeof(int8_t)) 63 | #define NTM_CALC_STRUCT_SIZE(length) \ 64 | (sizeof(ntmem_t) + NTM_CALC_LOCALS_SIZE(length)) 65 | #define NTM_STRUCT_SIZE(ntmem) (NTM_CALC_STRUCT_SIZE(ntmem->length)) 66 | 67 | #define NTM_SET_REMOTE(ntmem, set_remote_mem) \ 68 | (ntmem->remote_mem = (set_remote_mem)) 69 | #define NTM_REMOTE(ntmem) (ntmem->remote_mem) 70 | #define NTM_LOCAL(ntmem) ((void *)(ntmem + 1)) 71 | #define NTM_LOCAL_CPY(ntmem) \ 72 | ((void *)(((int8_t *)NTM_LOCAL(ntmem)) + NTM_LENGTH(ntmem))) 73 | 74 | typedef struct ntmem ntmem_t; 75 | 76 | #define NTMEM_ERROR 0x9200 77 | 78 | #define NTMEM_ALLOC_ERROR 0x9201 79 | #define NTMEM_NTU_MALLOC_ERROR 0x9202 80 | #define NTMEM_NTM_RESET_REMOTE_ERROR 0x9203 81 | #define NTM_ALLOC_REMOTE_ERROR 0x9204 82 | 83 | #define NTMEM_ERROR_E NTMEM_PUSH_WITH_MEMSET_ERROR 84 | 85 | #include "ntutils.h" 86 | 87 | /** 88 | * @brief Enable safe write mode for memory synchronization. 89 | * 90 | * @param ntmem Pointer to memory structure. 91 | */ 92 | NTHREAD_API void ntm_enable_safe_write(ntmem_t *ntmem); 93 | 94 | /** 95 | * @brief Disable safe write mode for memory synchronization. 96 | * 97 | * @param ntmem Pointer to memory structure. 98 | */ 99 | NTHREAD_API void ntm_disable_safe_write(ntmem_t *ntmem); 100 | 101 | /** 102 | * @brief Check if safe write mode is enabled. 103 | * 104 | * @param ntmem Pointer to memory structure. 105 | * @return true if safe write mode is enabled, false otherwise. 106 | */ 107 | NTHREAD_API bool ntm_is_safe_write(ntmem_t *ntmem); 108 | 109 | NTHREAD_API void *ntm_reset_locals(ntmem_t *ntmem); 110 | 111 | NTHREAD_API void *ntm_reset_remote_ex(ntmem_t *ntmem, size_t length); 112 | 113 | NTHREAD_API void *ntm_reset_remote(ntmem_t *ntmem); 114 | 115 | NTHREAD_API nerror_t ntm_reset(ntmem_t *ntmem); 116 | 117 | NTHREAD_API void *ntm_alloc_remote(ntmem_t *ntmem); 118 | 119 | NTHREAD_API void ntm_free_remote(ntmem_t *ntmem); 120 | 121 | NTHREAD_API void *ntm_alloc_remote_and_reset(ntmem_t *ntmem); 122 | 123 | /** 124 | * @brief Create and initialize a new memory structure with specific length. 125 | * 126 | * @param length Memory size in bytes. 127 | * @return Pointer to newly created memory structure. 128 | */ 129 | NTHREAD_API ntmem_t *ntm_create_ex(size_t length); 130 | 131 | /** 132 | * @brief Create and initialize a memory structure with default size. 133 | * 134 | * @return Pointer to newly created memory structure. 135 | */ 136 | NTHREAD_API ntmem_t *ntm_create(); 137 | 138 | NTHREAD_API ntmem_t *ntm_create_with_alloc_ex(size_t length); 139 | 140 | NTHREAD_API ntmem_t *ntm_create_with_alloc(); 141 | 142 | NTHREAD_API ntmem_t *ntm_create_from_remote(void *remote, size_t length); 143 | 144 | /** 145 | * @brief Delete memory structure. 146 | * 147 | * @param ntmem Pointer to memory structure. 148 | */ 149 | NTHREAD_API void ntm_delete(ntmem_t *ntmem); 150 | 151 | NTHREAD_API void ntm_delete_and_free(ntmem_t *ntmem); 152 | 153 | /** 154 | * @brief Delete the ntmem structure and detach remote memory pointer. 155 | * 156 | * Frees the local `ntmem_t` structure and its local memory buffer, 157 | * but returns the remote memory address (in target process) to the caller. 158 | * 159 | * This allows the caller to continue interacting with the remote memory 160 | * even after the local structure is destroyed. 161 | * 162 | * @param ntmem Pointer to memory structure. 163 | * @return Pointer to remote memory address in target process. 164 | */ 165 | NTHREAD_API void *ntm_delete_and_detach(ntmem_t *ntmem); 166 | 167 | NTHREAD_API void *ntm_pull_with_tunnel_ex(ntmem_t *ntmem, nttunnel_t *nttunnel, 168 | size_t len); 169 | 170 | /** 171 | * @brief Pull data from tunnel into the memory buffer. 172 | * 173 | * @param ntmem Pointer to memory structure. 174 | * @param nttunnel Associated tunnel structure. 175 | * @return Pointer to updated buffer. 176 | */ 177 | NTHREAD_API void *ntm_pull_with_tunnel(ntmem_t *ntmem, nttunnel_t *nttunnel); 178 | 179 | /** 180 | * @brief Push memory buffer into target process using tunnel. 181 | * 182 | * @param ntmem Pointer to memory structure. 183 | * @param nttunnel Associated tunnel structure. 184 | * @return Pointer to pushed data location. 185 | */ 186 | NTHREAD_API void *ntm_push_with_tunnel(ntmem_t *ntmem, nttunnel_t *nttunnel); 187 | 188 | /** 189 | * @brief Push memory buffer using memset as a writing method. 190 | * 191 | * @param ntmem Pointer to memory structure. 192 | * @return Pointer to pushed data location. 193 | */ 194 | NTHREAD_API void *ntm_push_with_memset(ntmem_t *ntmem); 195 | 196 | /** 197 | * @brief Push memory buffer into target using default method (tunnel or memset). 198 | * 199 | * @param ntmem Pointer to memory structure. 200 | * @param nttunnel Associated tunnel structure. 201 | * @return Pointer to pushed data location. 202 | */ 203 | NTHREAD_API void *ntm_push_ex(ntmem_t *ntmem, nttunnel_t *nttunnel); 204 | 205 | /** 206 | * @brief Push memory buffer into target using default method. 207 | * 208 | * Wrapper for `ntm_push_ex` that uses the default tunnel from `ntu_nttunnel`. 209 | * 210 | * @param ntmem Pointer to memory structure. 211 | * @return Pointer to pushed data location. 212 | */ 213 | NTHREAD_API void *ntm_push(ntmem_t *ntmem); 214 | 215 | #ifdef __cplusplus 216 | } 217 | #endif 218 | 219 | #endif // !__NTMEM_H__ 220 | -------------------------------------------------------------------------------- /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 | NTHREAD_API void ntm_enable_safe_write(ntmem_t *ntmem) 30 | { 31 | ntmem->flags |= NTMEM_SAFE_WRITE; 32 | } 33 | 34 | NTHREAD_API void ntm_disable_safe_write(ntmem_t *ntmem) 35 | { 36 | ntmem->flags &= ~(NTMEM_SAFE_WRITE); 37 | } 38 | 39 | NTHREAD_API bool ntm_is_safe_write(ntmem_t *ntmem) 40 | { 41 | return (ntmem->flags & NTMEM_SAFE_WRITE) != 0; 42 | } 43 | 44 | NTHREAD_API void *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 | NTHREAD_API void *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 | NTHREAD_API void *ntm_reset_remote(ntmem_t *ntmem) 61 | { 62 | return ntm_reset_remote_ex(ntmem, NTM_LENGTH(ntmem)); 63 | } 64 | 65 | NTHREAD_API nerror_t 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 | NTHREAD_API void *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 | NTHREAD_API void 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 | NTHREAD_API void *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 | NTHREAD_API ntmem_t *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 | NTHREAD_API ntmem_t *ntm_create() 115 | { 116 | return ntm_create_ex(NTMEM_DEFAULT_LENGTH); 117 | } 118 | 119 | NTHREAD_API ntmem_t *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 | NTHREAD_API ntmem_t *ntm_create_with_alloc() 133 | { 134 | return ntm_create_with_alloc_ex(NTMEM_DEFAULT_LENGTH); 135 | } 136 | 137 | NTHREAD_API ntmem_t *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 | NTHREAD_API void ntm_delete(ntmem_t *ntmem) 153 | { 154 | N_FREE(ntmem); 155 | } 156 | 157 | NTHREAD_API void ntm_delete_and_free(ntmem_t *ntmem) 158 | { 159 | ntm_free_remote(ntmem); 160 | ntm_delete(ntmem); 161 | } 162 | 163 | NTHREAD_API void *ntm_delete_and_detach(ntmem_t *ntmem) 164 | { 165 | void *remote = NTM_REMOTE(ntmem); 166 | ntm_delete(ntmem); 167 | return remote; 168 | } 169 | 170 | NTHREAD_API void *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 | NTHREAD_API void *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 = (void *)((int8_t *)NTM_REMOTE(ntmem) + begin); 209 | void *local = (void *)((int8_t *)NTM_LOCAL(ntmem) + begin); 210 | void *local_cpy = (void *)((int8_t *)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 | NTHREAD_API void *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 = (void *)((int8_t *)NTM_REMOTE(ntmem) + begin); 232 | void *local = (void *)((int8_t *)NTM_LOCAL(ntmem) + begin); 233 | void *local_cpy = (void *)((int8_t *)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 | NTHREAD_API void *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 | if (len == 0) 256 | return 0; 257 | 258 | size_t i = len - 1; 259 | while (true) { 260 | if (((char *)mem1)[i] != ((char *)mem2)[i]) 261 | return i; 262 | 263 | if (i == 0) 264 | break; 265 | 266 | i--; 267 | } 268 | 269 | return len; 270 | } 271 | 272 | size_t _find_diff(void *mem1, void *mem2, size_t len) 273 | { 274 | size_t i; 275 | for (i = 0; i < len; i++) { 276 | if (((char *)mem1)[i] != ((char *)mem2)[i]) 277 | return i; 278 | } 279 | 280 | return len; 281 | } 282 | 283 | NTHREAD_API void *ntm_push_ex(ntmem_t *ntmem, nttunnel_t *nttunnel) 284 | { 285 | void *remote = NTM_REMOTE(ntmem); 286 | bool b = !ntt_can_write(nttunnel); 287 | 288 | if (ntm_is_safe_write(ntmem)) { 289 | if (b) { 290 | if (ntm_push_with_memset(ntmem) == NULL) 291 | return NULL; 292 | } else if (ntm_push_with_tunnel(ntmem, nttunnel) == NULL) 293 | return NULL; 294 | } else { 295 | void *local = NTM_LOCAL(ntmem); 296 | void *local_cpy = NTM_LOCAL_CPY(ntmem); 297 | 298 | size_t sl = _find_diff(local, local_cpy, NTM_LENGTH(ntmem)); 299 | if (sl == NTM_LENGTH(ntmem)) 300 | return remote; 301 | 302 | size_t el = _find_diff_rev(local, local_cpy, NTM_LENGTH(ntmem)); 303 | size_t l = el - sl + 1; 304 | 305 | if (b || l < 3) { 306 | if (ntm_push_with_memset_ex(ntmem, sl, l) == NULL) 307 | return NULL; 308 | } else if (ntm_push_with_tunnel_ex(ntmem, nttunnel, sl, l) == 309 | NULL) 310 | return NULL; 311 | } 312 | 313 | return remote; 314 | } 315 | 316 | NTHREAD_API void *ntm_push(ntmem_t *ntmem) 317 | { 318 | nttunnel_t *nttunnel = ntu_nttunnel(); 319 | return ntm_push_ex(ntmem, nttunnel); 320 | } 321 | -------------------------------------------------------------------------------- /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 "ntutils.h" 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((void *)((int8_t *)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 | (void *)((int8_t *)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 = 129 | ntu_fopen(remote, (void *)((int8_t *)remote + temp_path_size)); 130 | if (fschan->remote_file == NULL) 131 | return GET_ERR(NTTUNNEL_NTU_FOPEN_ERROR); 132 | 133 | return N_OK; 134 | } 135 | 136 | void _ntt_destroy_fschan(nttunnel_fschan_t *fschan) 137 | { 138 | if (fschan == NULL) 139 | return; 140 | 141 | if (fschan->remote_file != NULL) { 142 | ntu_fclose(fschan->remote_file); 143 | fschan->remote_file = NULL; 144 | } 145 | 146 | if (fschan->local_file != NULL) { 147 | NFILE_CLOSE(fschan->local_file); 148 | fschan->local_file = NULL; 149 | } 150 | 151 | _ntt_delete_path(fschan); 152 | } 153 | 154 | NTHREAD_API bool ntt_can_read(nttunnel_t *nttunnel) 155 | { 156 | if (nttunnel == NULL) 157 | return false; 158 | 159 | return nttunnel->read.remote_file != NULL; 160 | } 161 | 162 | NTHREAD_API bool ntt_can_write(nttunnel_t *nttunnel) 163 | { 164 | if (nttunnel == NULL) 165 | return false; 166 | 167 | return nttunnel->write.remote_file != NULL; 168 | } 169 | 170 | NTHREAD_API nerror_t ntt_init_ex(nttunnel_t *nttunnel, 171 | nttunnel_fschan_flags_t flags) 172 | { 173 | #ifdef LOG_LEVEL_2 174 | LOG_INFO("ntt_init_ex(nttunnel=%p, flags=%02x)", nttunnel, flags); 175 | #endif /* ifdef LOG_LEVEL_2 */ 176 | 177 | memset(nttunnel, 0, sizeof(nttunnel_t)); 178 | 179 | nttunnel->ntmem = ntm_create_with_alloc_ex( 180 | NFILE_MAX_PATH_SIZE + NTTUNNEL_FSCHAN_MAX_MODE_SIZE); 181 | 182 | if (nttunnel->ntmem == NULL) 183 | return GET_ERR(NTTUNNEL_NTM_CREATE_WITH_ALLOC_EX_ERROR); 184 | 185 | nttunnel->max_transfer = NTTUNNEL_FSCHAN_CALC_MAX_TRANSFER(flags); 186 | 187 | if (HAS_ERR(_ntt_init_fschan(nttunnel, &nttunnel->write, 188 | flags | NTTUNNEL_FSCHAN_WRITE_MODE))) 189 | goto ntt_init_ex_init_fschan_error; 190 | 191 | if (HAS_ERR(_ntt_init_fschan(nttunnel, &nttunnel->read, flags))) { 192 | ntt_init_ex_init_fschan_error: 193 | ntt_destroy(nttunnel); 194 | return GET_ERR(NTTUNNEL_INIT_FSCHAN_ERROR); 195 | } 196 | 197 | return N_OK; 198 | } 199 | 200 | NTHREAD_API nerror_t ntt_init(nttunnel_t *nttunnel) 201 | { 202 | return ntt_init_ex(nttunnel, NTTUNNEL_FSCHAN_DEFAULT_FLAGS); 203 | } 204 | 205 | NTHREAD_API void ntt_destroy(nttunnel_t *nttunnel) 206 | { 207 | if (nttunnel == NULL) 208 | return; 209 | 210 | if (nttunnel->ntmem != NULL) { 211 | ntm_delete_and_free(nttunnel->ntmem); 212 | nttunnel->ntmem = NULL; 213 | } 214 | 215 | if (ntt_can_read(nttunnel)) 216 | _ntt_destroy_fschan(&nttunnel->read); 217 | 218 | if (ntt_can_write(nttunnel)) 219 | _ntt_destroy_fschan(&nttunnel->write); 220 | } 221 | 222 | nerror_t _ntt_fschan_read(nttunnel_fschan_t *fschan, const void *dest, 223 | void *source, size_t length) 224 | { 225 | size_t len_1 = ntu_fwrite(dest, 1, length, fschan->remote_file); 226 | if (len_1 != length) 227 | return GET_ERR(NTTUNNEL_NFILE_READ_ERROR); 228 | 229 | if (ntu_fflush(fschan->remote_file)) 230 | return GET_ERR(NTTUNNEL_NTU_FFLUSH_ERROR); 231 | 232 | size_t len_2 = NFILE_READ(fschan->local_file, source, length); 233 | if (len_2 != length) 234 | return GET_ERR(NTTUNNEL_NTU_FWRITE_ERROR); 235 | 236 | return N_OK; 237 | } 238 | 239 | nerror_t _ntt_fschan_write(nttunnel_fschan_t *fschan, void *dest, 240 | const void *source, size_t length) 241 | { 242 | size_t len_1 = NFILE_WRITE(fschan->local_file, source, length); 243 | if (len_1 != length) 244 | return GET_ERR(NTTUNNEL_NFILE_WRITE_ERROR); 245 | 246 | NFILE_FLUSH(fschan->local_file); 247 | 248 | size_t len_2 = ntu_fread(dest, 1, length, fschan->remote_file); 249 | if (len_2 != length) 250 | return GET_ERR(NTTUNNEL_NTU_FREAD_ERROR); 251 | 252 | return N_OK; 253 | } 254 | 255 | nerror_t _ntt_fschan_reset(nttunnel_t *nttunnel, nttunnel_fschan_t *fschan, 256 | nttunnel_fschan_flags_t flags) 257 | { 258 | nfile_path_t path = fschan->path; 259 | bool has_path = path != NULL; 260 | if ((flags & NTTUNNEL_FSCHAN_CREATE_TEMP_PATH) != 0) { 261 | nttunnel_fschan_t new_fschan; 262 | if (HAS_ERR(_ntt_init_fschan(nttunnel, &new_fschan, flags))) 263 | return GET_ERR(NTTUNNEL_INIT_FSCHAN_ERROR); 264 | 265 | _ntt_destroy_fschan(fschan); 266 | memcpy(fschan, &new_fschan, sizeof(nttunnel_fschan_t)); 267 | } else { 268 | if (has_path) 269 | _ntt_create_path(nttunnel->ntmem, path); 270 | else { 271 | flags |= NTTUNNEL_FSCHAN_CREATE_TEMP_PATH; 272 | flags |= NTTUNNEL_FSCHAN_DONT_SAVE_PATH; 273 | } 274 | 275 | _ntt_destroy_fschan(fschan); 276 | if (HAS_ERR(_ntt_init_fschan(nttunnel, fschan, flags))) 277 | return GET_ERR(NTTUNNEL_INIT_FSCHAN_ERROR); 278 | } 279 | 280 | return N_OK; 281 | } 282 | 283 | NTHREAD_API nerror_t ntt_read(nttunnel_t *nttunnel, const void *dest, 284 | void *source, size_t length) 285 | { 286 | nerror_t ret = _ntt_fschan_read(&nttunnel->read, dest, source, length); 287 | if (HAS_ERR(ret)) 288 | goto ntt_read_return; 289 | 290 | nttunnel->read_transfer += length; 291 | if (nttunnel->read_transfer >= nttunnel->max_transfer) { 292 | nttunnel->read_transfer = 0; 293 | ret = _ntt_fschan_reset(nttunnel, &nttunnel->read, 294 | NTTUNNEL_FSCHAN_CREATE_TEMP_PATH); 295 | } 296 | 297 | ntt_read_return: 298 | return ret; 299 | } 300 | 301 | NTHREAD_API nerror_t ntt_write(nttunnel_t *nttunnel, void *dest, 302 | const void *source, size_t length) 303 | { 304 | nerror_t ret = 305 | _ntt_fschan_write(&nttunnel->write, dest, source, length); 306 | if (HAS_ERR(ret)) 307 | goto ntt_write_return; 308 | 309 | nttunnel->write_transfer += length; 310 | if (nttunnel->write_transfer >= nttunnel->max_transfer) { 311 | nttunnel->write_transfer = 0; 312 | ret = _ntt_fschan_reset(nttunnel, &nttunnel->write, 313 | NTTUNNEL_FSCHAN_CREATE_TEMP_PATH | 314 | NTTUNNEL_FSCHAN_WRITE_MODE); 315 | } 316 | 317 | ntt_write_return: 318 | return ret; 319 | } 320 | -------------------------------------------------------------------------------- /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 | NTHREAD_API ntid_t nthread_get_id(nthread_t *nthread) 33 | { 34 | return GetThreadId(nthread->thread); 35 | } 36 | 37 | NTHREAD_API bool 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 ((int8_t *)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 | NTHREAD_API nerror_t 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 = 123 | nthread_calc_stack((void *)((int8_t *)rsp + NTHREAD_STACK_ADD)); 124 | NTHREAD_SET_REG(nthread, NTHREAD_RSP, new_rsp); 125 | NTHREAD_SET_REG(nthread, push_reg_offset, sleep_addr); 126 | 127 | #endif /* ifdef _WIN32 */ 128 | 129 | error_helper = nthread_set_regs(nthread); 130 | if (HAS_ERR(error_helper)) { 131 | nthread_init_resume_and_ret: 132 | if ((flags & NTHREAD_FLAG_DONT_RESUME) == 0) 133 | nthread_resume(nthread); 134 | nthread_init_destroy_and_ret: 135 | nthread_destroy(nthread); 136 | return error_helper; 137 | } 138 | 139 | if ((flags & NTHREAD_FLAG_DONT_RESUME) == 0) { 140 | error_helper = nthread_resume(nthread); 141 | if (HAS_ERR(error_helper)) 142 | goto nthread_init_destroy_and_ret; 143 | } 144 | 145 | error_helper = nthread_wait(nthread); 146 | if (HAS_ERR(error_helper)) { 147 | goto nthread_init_destroy_and_ret; 148 | } 149 | 150 | nthread_copy_ncontext(nthread); 151 | 152 | NTHREAD_SET_OREG(nthread, push_reg_offset, rvp); 153 | NTHREAD_SET_OREG(nthread, NTHREAD_RIP, rip); 154 | NTHREAD_SET_OREG(nthread, NTHREAD_RSP, rsp); 155 | 156 | nthread->n_ctx.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL; 157 | 158 | #ifdef LOG_LEVEL_2 159 | LOG_INFO("NThread(%ld) succesfully created", thread_id); 160 | #endif /* ifdef LOG_LEVEL_2 */ 161 | 162 | return N_OK; 163 | } 164 | 165 | NTHREAD_API nerror_t nthread_init(nthread_t *nthread, ntid_t thread_id, 166 | nthread_reg_offset_t push_reg_offset, 167 | void *push_addr, void *sleep_addr) 168 | { 169 | return nthread_init_ex(nthread, thread_id, push_reg_offset, push_addr, 170 | sleep_addr, NTHREAD_DEFAULT_TIMEOUT); 171 | } 172 | 173 | NTHREAD_API void nthread_destroy(nthread_t *nthread) 174 | { 175 | #ifdef _WIN32 176 | 177 | if (nthread->o_ctx.ContextFlags != 0) { 178 | memcpy((void *)&nthread->n_ctx, (void *)&nthread->o_ctx, 179 | sizeof(CONTEXT)); 180 | 181 | nthread_set_regs(nthread); 182 | } 183 | 184 | if (NTHREAD_IS_VALID(nthread)) { 185 | CloseHandle(nthread->thread); 186 | NTHREAD_SET_INVALID(nthread); 187 | } 188 | 189 | #endif /* ifdef _WIN32 */ 190 | } 191 | 192 | NTHREAD_API void *nthread_stack_begin(nthread_t *nthread) 193 | { 194 | void *rsp = 195 | (void *)(((int8_t *)NTHREAD_GET_OREG(nthread, NTHREAD_RSP)) + 196 | NTHREAD_STACK_ADD); 197 | 198 | return nthread_calc_stack(rsp); 199 | } 200 | 201 | NTHREAD_API nerror_t nthread_suspend(nthread_t *nthread) 202 | { 203 | #ifdef LOG_LEVEL_2 204 | LOG_INFO("nthread_suspend(nthread_id=%ld)", NTHREAD_GET_ID(nthread)); 205 | #endif /* ifdef LOG_LEVEL_2 */ 206 | 207 | #ifdef _WIN32 208 | 209 | DWORD count = SuspendThread(nthread->thread); 210 | 211 | #ifdef LOG_LEVEL_1 212 | 213 | if (count == (DWORD)(-1)) { 214 | LOG_WARN("nthread_suspend(nthread_id=%ld) failed", 215 | NTHREAD_GET_ID(nthread)); 216 | 217 | return GET_ERR(NTHREAD_SUSPEND_ERROR); 218 | } 219 | 220 | #else /* ifndef LOG_LEVEL_1 */ 221 | 222 | if (count == (DWORD)(-1)) 223 | return GET_ERR(NTHREAD_SUSPEND_ERROR); 224 | 225 | #endif /* infdef LOG_LEVEL_1 */ 226 | #endif /* ifdef _WIN32 */ 227 | 228 | return N_OK; 229 | } 230 | 231 | NTHREAD_API nerror_t nthread_resume(nthread_t *nthread) 232 | { 233 | #ifdef LOG_LEVEL_2 234 | LOG_INFO( 235 | "nthread_resume(nthread_id=%ld) called. If this hangs, see: https://github.com/woldann/nthread/wiki/nthread_resume-troubleshooting", 236 | NTHREAD_GET_ID(nthread)); 237 | #endif /* ifdef LOG_LEVEL_2 */ 238 | 239 | #ifdef _WIN32 240 | 241 | DWORD count = ResumeThread(nthread->thread); 242 | 243 | #ifdef LOG_LEVEL_1 244 | 245 | if (count == (DWORD)(-1)) { 246 | LOG_WARN("nthread_resume(nthread_id=%ld) failed", 247 | NTHREAD_GET_ID(nthread)); 248 | 249 | return GET_ERR(NTHREAD_RESUME_ERROR); 250 | } 251 | 252 | #else /* ifndef LOG_LEVEL_1 */ 253 | 254 | if (count == (DWORD)(-1)) 255 | return GET_ERR(NTHREAD_RESUME_ERROR); 256 | 257 | #endif /* infdef LOG_LEVEL_1 */ 258 | #endif /* ifdef _WIN32 */ 259 | 260 | return N_OK; 261 | } 262 | 263 | NTHREAD_API nerror_t nthread_get_regs(nthread_t *nthread) 264 | { 265 | #ifdef _WIN32 266 | 267 | if (!GetThreadContext(nthread->thread, &nthread->n_ctx)) 268 | return GET_ERR(NTHREAD_GET_CONTEXT_ERROR); 269 | 270 | #endif /* ifdef _WIN32 */ 271 | 272 | return N_OK; 273 | } 274 | 275 | NTHREAD_API nerror_t nthread_set_regs(nthread_t *nthread) 276 | { 277 | #ifdef _WIN32 278 | 279 | if (!SetThreadContext(nthread->thread, &nthread->n_ctx)) 280 | return GET_ERR(NTHREAD_SET_CONTEXT_ERROR); 281 | 282 | #endif /* ifdef _WIN32 */ 283 | 284 | return N_OK; 285 | } 286 | 287 | NTHREAD_API void nthread_set_timeout(nthread_t *nthread, uint8_t timeout_sec) 288 | { 289 | nthread->timeout = timeout_sec; 290 | } 291 | 292 | NTHREAD_API nerror_t nthread_wait_ex(nthread_t *nthread, uint32_t sleep) 293 | { 294 | ntime_t end; 295 | uint32_t timeout_sec = nthread->timeout; 296 | if (timeout_sec != 0) { 297 | end = ntime_get_unix() + timeout_sec; 298 | uint32_t timeout_ms = timeout_sec * 1000; 299 | if (sleep >= timeout_ms) 300 | sleep = timeout_ms + 1; 301 | } else 302 | end = 0; 303 | 304 | while (true) { 305 | #ifdef _WIN32 306 | Sleep((DWORD)sleep); 307 | #endif /* ifdef _WIN32 */ 308 | 309 | RET_ERR(nthread_get_regs(nthread)); 310 | 311 | void *rip = NTHREAD_GET_REG(nthread, NTHREAD_RIP); 312 | if (rip == nthread->sleep_addr) 313 | return N_OK; 314 | 315 | if (end > 0) { 316 | ntime_t cur = ntime_get_unix(); 317 | if (cur >= end) 318 | return GET_ERR(NTHREAD_TIMEOUT_ERROR); 319 | } 320 | } 321 | } 322 | 323 | NTHREAD_API nerror_t nthread_wait(nthread_t *nthread) 324 | { 325 | return nthread_wait_ex(nthread, NTHREAD_DEFAULT_WAIT_MS); 326 | } 327 | 328 | NTHREAD_API nerror_t nthread_call(nthread_t *nthread, void *fun_addr, 329 | void **return_value) 330 | { 331 | #ifdef LOG_LEVEL_3 332 | 333 | LOG_INFO("nthread_call(nthread_id=%ld, fun_addr=%p, return_value=%p)", 334 | NTHREAD_GET_ID(nthread), fun_addr, return_value); 335 | 336 | #endif /* ifdef LOG_LEVEL_3 */ 337 | 338 | NTHREAD_SET_REG(nthread, NTHREAD_RIP, fun_addr); 339 | 340 | void *rsp = nthread_stack_begin(nthread); 341 | NTHREAD_SET_REG(nthread, NTHREAD_RSP, 342 | (void *)((int8_t *)rsp - sizeof(fun_addr))); 343 | 344 | RET_ERR(nthread_set_regs(nthread)); 345 | RET_ERR(nthread_wait(nthread)); 346 | 347 | *return_value = NTHREAD_GET_REG(nthread, NTHREAD_RAX); 348 | return N_OK; 349 | } 350 | -------------------------------------------------------------------------------- /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 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | #include "neptune.h" 43 | #include "nerror.h" 44 | 45 | #if !(defined(__x86_64__) || defined(__amd64__) || defined(__LP64__) || \ 46 | defined(_LP64) || defined(_WIN64)) 47 | #error "NThread only works on x86_64 systems." 48 | #endif 49 | 50 | #if !(defined(_WIN32) || defined(__linux__)) 51 | #error "NThread unsupported os." 52 | #endif // OS Check 53 | 54 | #ifdef _WIN32 55 | 56 | #include 57 | 58 | typedef int16_t nthread_reg_offset_t; 59 | 60 | #define WINDOWS_RAX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rax)) 61 | #define WINDOWS_RBX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rbx)) 62 | #define WINDOWS_RCX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rcx)) 63 | #define WINDOWS_RDX_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rdx)) 64 | #define WINDOWS_RSI_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rsi)) 65 | #define WINDOWS_RDI_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rdi)) 66 | #define WINDOWS_R8_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R8)) 67 | #define WINDOWS_R9_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R9)) 68 | #define WINDOWS_R10_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R10)) 69 | #define WINDOWS_R11_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R11)) 70 | #define WINDOWS_R12_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R12)) 71 | #define WINDOWS_R13_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R13)) 72 | #define WINDOWS_R14_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R14)) 73 | #define WINDOWS_R15_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, R15)) 74 | #define WINDOWS_RIP_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rip)) 75 | #define WINDOWS_RSP_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rsp)) 76 | #define WINDOWS_RBP_OFFSET ((nthread_reg_offset_t)offsetof(CONTEXT, Rbp)) 77 | 78 | #define WINDOWS_BEST_PUSH_REG WINDOWS_RBP_OFFSET 79 | #define NTHREAD_BEST_PUSH_REG WINDOWS_BEST_PUSH_REG 80 | 81 | #define NTHREAD_RAX WINDOWS_RAX_OFFSET 82 | #define NTHREAD_RBX WINDOWS_RBX_OFFSET 83 | #define NTHREAD_RCX WINDOWS_RCX_OFFSET 84 | #define NTHREAD_RDX WINDOWS_RDX_OFFSET 85 | #define NTHREAD_RSI WINDOWS_RSI_OFFSET 86 | #define NTHREAD_RDI WINDOWS_RDI_OFFSET 87 | #define NTHREAD_R8 WINDOWS_R8_OFFSET 88 | #define NTHREAD_R9 WINDOWS_R9_OFFSET 89 | #define NTHREAD_R10 WINDOWS_R10_OFFSET 90 | #define NTHREAD_R11 WINDOWS_R11_OFFSET 91 | #define NTHREAD_R12 WINDOWS_R12_OFFSET 92 | #define NTHREAD_R13 WINDOWS_R13_OFFSET 93 | #define NTHREAD_R14 WINDOWS_R14_OFFSET 94 | #define NTHREAD_R15 WINDOWS_R15_OFFSET 95 | #define NTHREAD_RIP WINDOWS_RIP_OFFSET 96 | #define NTHREAD_RSP WINDOWS_RSP_OFFSET 97 | #define NTHREAD_RBP WINDOWS_RBP_OFFSET 98 | 99 | #define NTHREAD_RAX_INDEX 0x00 100 | #define NTHREAD_RCX_INDEX 0x01 101 | #define NTHREAD_RDX_INDEX 0x02 102 | #define NTHREAD_RBX_INDEX 0x03 103 | #define NTHREAD_RSP_INDEX 0x04 104 | #define NTHREAD_RBP_INDEX 0x05 105 | #define NTHREAD_RSI_INDEX 0x06 106 | #define NTHREAD_RDI_INDEX 0x07 107 | #define NTHREAD_R8_INDEX 0x08 108 | #define NTHREAD_R9_INDEX 0x09 109 | #define NTHREAD_R10_INDEX 0x0A 110 | #define NTHREAD_R11_INDEX 0x0B 111 | #define NTHREAD_R12_INDEX 0x0C 112 | #define NTHREAD_R13_INDEX 0x0D 113 | #define NTHREAD_R14_INDEX 0x0E 114 | #define NTHREAD_R15_INDEX 0x0F 115 | #define NTHREAD_RIP_INDEX 0x10 116 | 117 | typedef uint8_t nthread_flags_t; 118 | 119 | #define NTHREAD_FLAG_DONT_SUSPEND 0x10 120 | #define NTHREAD_FLAG_DONT_RESUME 0x20 121 | #define NTHREAD_FLAG_DISABLE_GET_ID 0x40 122 | 123 | #define NTHREAD_REG_INDEX_TO_OFFSET(reg_index) \ 124 | ((nthread_reg_offset_t)(NTHREAD_RAX + ((sizeof(DWORD64)) * reg_index))) 125 | 126 | #define NTHREAD_ACCESS \ 127 | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | \ 128 | THREAD_QUERY_INFORMATION 129 | 130 | typedef DWORD ntid_t; 131 | typedef HANDLE nthread_handle_t; 132 | typedef CONTEXT nthread_regs_t; 133 | 134 | #else // !_WIN32 135 | 136 | #include 137 | #include 138 | #include 139 | 140 | typedef pid_t ntid_t; 141 | typedef ntid_t nthread_handle_t; 142 | typedef struct user_regs_struct nthread_regs_t; 143 | 144 | #define NTHREAD_GET_ID(nthread) (((nthread_t *)nthread)->thread) 145 | 146 | #endif // !_WIN32 147 | 148 | #define NTHREAD_DEFAULT_WAIT_MS 0x00 149 | #define NTHREAD_DEFAULT_TIMEOUT 0x00 150 | 151 | #define NTHREAD_ERROR 0x4760 152 | 153 | #define NTHREAD_SUSPEND_ERROR 0x4761 154 | #define NTHREAD_RESUME_ERROR 0x4762 155 | #define NTHREAD_OPEN_THREAD_ERROR 0x4763 156 | #define NTHREAD_GET_CONTEXT_ERROR 0x4764 157 | #define NTHREAD_SET_CONTEXT_ERROR 0x4765 158 | #define NTHREAD_ERROR_INVALID_ARGS 0x4766 159 | #define NTHREAD_TIMEOUT_ERROR 0x4767 160 | 161 | #define NTHREAD_ERROR_E NTHREAD_ERROR_INVALID_ARGS 162 | 163 | #define NTHREAD_STACK_ADD (-8192) 164 | 165 | typedef struct { 166 | nthread_handle_t thread; 167 | void *sleep_addr; 168 | uint8_t timeout; 169 | 170 | nthread_regs_t n_ctx; 171 | nthread_regs_t o_ctx; 172 | } nthread_t; 173 | 174 | #ifdef _WIN32 175 | 176 | #ifndef NTHREAD_API 177 | #define NTHREAD_API NEPTUNE_API 178 | #endif // !NTHREAD_API 179 | 180 | #define NTHREAD_IS_VALID(nthread) ((nthread)->thread != NULL) 181 | #define NTHREAD_SET_INVALID(nthread) ((nthread)->thread = NULL) 182 | 183 | NTHREAD_API ntid_t nthread_get_id(nthread_t *nthread); 184 | 185 | #define NTHREAD_GET_ID(nthread) nthread_get_id(nthread) 186 | 187 | #else // !_WIN32 188 | 189 | #define NTHREAD_IS_VALID(nthread) ((nthread)->thread != 0) 190 | #define NTHREAD_SET_INVALID(nthread) ((nthread)->thread = 0) 191 | 192 | #define NTHREAD_GET_ID(nthread) ((nthread)->thread) 193 | 194 | #endif // !_WIN32 195 | 196 | #define NTHREAD_GET_REG(nthread, reg) \ 197 | (((void **)(((int8_t *)&(nthread)->n_ctx) + reg))[0]) 198 | 199 | #define NTHREAD_SET_REG(nthread, reg, set) \ 200 | (((void **)(((int8_t *)&(nthread)->n_ctx) + (reg)))[0] = (void *)(set)) 201 | 202 | #define NTHREAD_GET_OREG(nthread, reg) \ 203 | (((void **)(((int8_t *)&(nthread)->o_ctx) + reg))[0]) 204 | 205 | #define NTHREAD_SET_OREG(nthread, reg, set) \ 206 | (((void **)(((int8_t *)&(nthread)->o_ctx) + (reg)))[0] = (void *)(set)) 207 | 208 | NTHREAD_API bool nthread_is_waiting(nthread_t *nthread); 209 | 210 | NTHREAD_API nerror_t nthread_init_ex(nthread_t *nthread, ntid_t ntid, 211 | nthread_reg_offset_t push_reg_offset, 212 | void *push_addr, void *sleep_addr, 213 | nthread_flags_t flags); 214 | 215 | NTHREAD_API nerror_t nthread_init(nthread_t *nthread, ntid_t ntid, 216 | nthread_reg_offset_t push_reg_offset, 217 | void *push_addr, void *sleep_addr); 218 | 219 | /** 220 | * @brief Release resources and restore context related to an NThread instance. 221 | * 222 | * Should be called after operations are completed to clean up state. 223 | * 224 | * @param nthread Pointer to the NThread structure to destroy. 225 | */ 226 | NTHREAD_API void nthread_destroy(nthread_t *nthread); 227 | 228 | /** 229 | * @brief Retrieve the base address of the thread's stack. 230 | * 231 | * @param nthread Pointer to the NThread structure. 232 | * @return Pointer to the beginning of the thread's stack. 233 | */ 234 | NTHREAD_API void *nthread_stack_begin(nthread_t *nthread); 235 | 236 | /** 237 | * @brief Suspend execution of the target thread. 238 | * 239 | * @param nthread Pointer to the NThread structure. 240 | * @return Error code indicating success or failure. 241 | */ 242 | NTHREAD_API nerror_t nthread_suspend(nthread_t *nthread); 243 | 244 | /** 245 | * @brief Resume execution of a previously suspended thread. 246 | * 247 | * @param nthread Pointer to the NThread structure. 248 | * @return Error code indicating success or failure. 249 | */ 250 | NTHREAD_API nerror_t nthread_resume(nthread_t *nthread); 251 | 252 | /** 253 | * @brief Read the current CPU context (registers) of the target thread. 254 | * 255 | * @param nthread Pointer to the NThread structure. 256 | * @return Error code indicating success or failure. 257 | */ 258 | NTHREAD_API nerror_t nthread_get_regs(nthread_t *nthread); 259 | 260 | /** 261 | * @brief Set the CPU context (registers) of the target thread. 262 | * 263 | * Applies previously modified or constructed context to the thread. 264 | * 265 | * @param nthread Pointer to the NThread structure. 266 | * @return Error code indicating success or failure. 267 | */ 268 | NTHREAD_API nerror_t nthread_set_regs(nthread_t *nthread); 269 | 270 | NTHREAD_API void nthread_set_timeout(nthread_t *nthread, uint8_t timeout_sec); 271 | 272 | /** 273 | * @brief Wait for the thread to return from a function call, with timeout control. 274 | * 275 | * Repeatedly checks if the thread has returned from execution within the specified time. 276 | * 277 | * @param nthread Pointer to the NThread structure. 278 | * @param sleep Timeout in milliseconds between each status check. 279 | * @return Error code indicating success, timeout, or failure. 280 | */ 281 | NTHREAD_API nerror_t nthread_wait_ex(nthread_t *nthread, uint32_t sleep); 282 | 283 | /** 284 | * @brief Wait indefinitely until the thread returns from a function call. 285 | * 286 | * @param nthread Pointer to the NThread structure. 287 | * @return Error code indicating success or failure. 288 | */ 289 | NTHREAD_API nerror_t nthread_wait(nthread_t *nthread); 290 | 291 | /** 292 | * @brief Execute a function inside the target process using the hijacked thread. 293 | * 294 | * This manipulates the thread's instruction pointer to call the provided function address. 295 | * 296 | * @param nthread Pointer to the NThread structure. 297 | * @param function_address Address of the target function to call. 298 | * @param return_value Pointer to store the return value of the function, if any. 299 | * 300 | * @return Error code indicating success or failure. 301 | */ 302 | NTHREAD_API nerror_t nthread_call(nthread_t *nthread, void *function_address, 303 | void **return_value); 304 | 305 | #ifdef __cplusplus 306 | } 307 | #endif 308 | 309 | #endif // !__NSHELL_H__ 310 | -------------------------------------------------------------------------------- /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 | 43 | #define NTU_DEFAULT_CC NTUCC_DEFAULT 44 | 45 | #ifdef _WIN32 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_PFA_CC NTUCC_CDECL_PASS_ 50 | #define NTU_DEFAULT_CC_FA NTHREAD_RCX 51 | #endif // !_WIN32 52 | 53 | #define NTUTILS_ERROR 0x7340 54 | 55 | #define NTUTILS_GET_LIBC_BASE_ERROR 0x7341 56 | #define NTUTILS_UNKNOWN_CC_ERROR 0x7342 57 | #define NTUTILS_ALLOC_ERROR 0x7343 58 | #define NTUTILS_TLS_SET_VALUE_ERROR 0x7344 59 | #define NTUTILS_TLS_ALLOC_ERROR 0x7345 60 | #define NTUTILS_NTU_MEMSET_ERROR 0x7346 61 | #define NTUTILS_NTU_WRITE_WITH_MEMSET_ERROR 0x7347 62 | #define NTUTILS_NTU_WRITE_WITH_MEMSET_DEST_ERROR 0x7348 63 | #define NTUTILS_NTM_CREATE_EX_ERROR 0x7349 64 | #define NTUTILS_NTM_RESET_REMOTE_EX_ERROR 0x734A 65 | #define NTUTILS_NTM_PUSH_ERROR 0x734B 66 | #define NTUTILS_NTHREAD_ERROR 0x734C 67 | #define NTUTILS_NTU_RESIZE_ERROR 0x734D 68 | #define NTUTILS_NTHREAD_INIT_ERROR 0x734E 69 | #define NTUTILS_NTT_INIT_ERROR 0x734F 70 | #define NTUTILS_READ_MEMORY_ERROR 0x7350 71 | #define NTUTILS_WRITE_MEMORY_ERROR 0x7351 72 | #define NTUTILS_NTU_GET_ERROR 0x7352 73 | 74 | #define NTUTILS_FUNC_INIT_ERROR 0x7360 75 | #define NTUTILS_FUNC_INIT_ERROR_E (NTUTILS_FUNC_INIT_ERROR + 7) 76 | 77 | #define NTUTILS_ERROR_E NTUTILS_FUNC_INIT_ERROR_E 78 | 79 | #include "nttunnel.h" 80 | 81 | #ifdef __cplusplus 82 | extern "C" { 83 | #endif 84 | 85 | #if !defined(NTUTILS_DISABLE_GLOBAL_CC) || NTUTILS_DISABLE_GLOBAL_CC != 1 86 | 87 | #ifdef _WIN32 88 | #define NTU_GLOBAL_CC NTU_DEFAULT_CC 89 | #endif // _WIN32 90 | 91 | #endif // !defined(NTUTILS_DISABLE_GLOBAL_CC) || NTUTILS_DISABLE_GLOBAL_CC != 1 92 | 93 | struct ntutils { 94 | void *ret_value; 95 | ntucc_t sel_cc; 96 | 97 | nttunnel_t nttunnel; 98 | int8_t stack_helper[255 * sizeof(void *)]; 99 | 100 | nthread_t nthread; 101 | }; 102 | 103 | typedef struct ntutils ntutils_t; 104 | 105 | NTHREAD_API ntutils_t *_ntu_get(void); 106 | 107 | NTHREAD_API nerror_t ntu_set(ntutils_t *ntutils); 108 | 109 | NTHREAD_API nerror_t ntu_resize(size_t new_size); 110 | 111 | #define ntu_get() _ntu_get() 112 | 113 | #define ntu_set_cc_ex(ntutils, cc) (ntutils->sel_cc = cc) 114 | 115 | #ifndef NTU_GLOBAL_CC 116 | 117 | NTHREAD_API void _ntu_set_cc(ntucc_t cc); 118 | 119 | #define ntu_set_cc(cc) _ntu_set_cc(cc) 120 | 121 | NTHREAD_API void _ntu_set_default_cc(); 122 | 123 | #define ntu_set_default_cc() _ntu_set_default_cc() 124 | 125 | NTHREAD_API ntutils_t *_ntu_o(ntucc_t cc); 126 | 127 | #define ntu_o(cc) _ntu_o(cc) 128 | 129 | #else // NTU_GLOBAL_CC 130 | 131 | #define ntu_set_cc(cc) \ 132 | do { \ 133 | } while (0) 134 | 135 | #define ntu_set_default_cc() \ 136 | do { \ 137 | } while (0) 138 | 139 | #define ntu_o(cc) ntu_get() 140 | 141 | #endif // NTU_GLOBAL_CC 142 | 143 | NTHREAD_API void *ntu_get_libc_base(); 144 | 145 | /** 146 | * @brief Initialize global state for ntutils subsystem. 147 | * 148 | * @return Error code. 149 | */ 150 | NTHREAD_API nerror_t ntu_global_init(void); 151 | 152 | /** 153 | * @brief Clean up global ntutils resources. 154 | */ 155 | NTHREAD_API void ntu_global_destroy(void); 156 | 157 | NTHREAD_API nerror_t ntu_upgrade(nthread_t *nthread); 158 | 159 | NTHREAD_API nerror_t ntu_attach_ex(ntid_t thread_id, 160 | nthread_reg_offset_t push_reg_offset, 161 | void *push_addr, void *sleep_addr); 162 | 163 | NTHREAD_API nerror_t ntu_attach(ntid_t thread_id, void *push_addr, 164 | void *sleep_addr); 165 | 166 | /** 167 | * @brief Destroy the current ntutils instance and release resources. 168 | */ 169 | NTHREAD_API void ntu_destroy(); 170 | 171 | NTHREAD_API void ntu_set_reg_args_ex(uint8_t arg_count, void **args, 172 | ntucc_t sel_cc); 173 | 174 | /** 175 | * @brief Set register arguments directly using an array of values 176 | * 177 | * @param arg_count Number of arguments to set 178 | * @param args Array of argument values to put in registers 179 | */ 180 | NTHREAD_API void ntu_set_reg_args(uint8_t arg_count, void **args); 181 | 182 | /** 183 | * @brief Set arguments using a va_list 184 | * 185 | * @param arg_count Number of arguments in the va_list 186 | * @param args Variable argument list containing the arguments 187 | * @return nerror_t error code 188 | */ 189 | NTHREAD_API nerror_t ntu_set_args_v(uint8_t arg_count, va_list args); 190 | 191 | /** 192 | * @brief Set arguments using variadic parameters 193 | * 194 | * @param arg_count Number of arguments to set 195 | * @param ... Variable arguments to set 196 | * @return nerror_t error code 197 | */ 198 | NTHREAD_API nerror_t ntu_set_args(int arg_count, ...); 199 | 200 | NTHREAD_API void ntu_get_reg_args_ex(uint8_t arg_count, void **args, 201 | ntucc_t sel_cc); 202 | 203 | /** 204 | * @brief Get current register argument values 205 | * 206 | * @param arg_count Number of arguments to retrieve 207 | * @param args Array to store retrieved argument values 208 | */ 209 | NTHREAD_API void ntu_get_reg_args(uint8_t arg_count, void **args); 210 | 211 | /** 212 | * @brief Get current argument values 213 | * 214 | * @param arg_count Number of arguments to retrieve 215 | * @param args Array to store retrieved argument values 216 | * @return nerror_t error code 217 | */ 218 | NTHREAD_API nerror_t ntu_get_args(uint8_t arg_count, void **args); 219 | 220 | /** 221 | * @brief Call a function inside the target thread with variable arguments (va_list). 222 | * 223 | * @param func_addr Address of the target function to call. 224 | * @param arg_count Number of arguments to pass. 225 | * @param args Variable argument list. 226 | * @return Error code. 227 | */ 228 | NTHREAD_API nerror_t ntu_call_v(void *func_addr, uint8_t arg_count, 229 | va_list args); 230 | 231 | /** 232 | * @brief Call a function inside the target thread with variadic arguments. 233 | * 234 | * @param ntutils Pointer to the ntutils instance. 235 | * @param func_addr Address of the function to call. 236 | * @param arg_count Number of arguments to pass. 237 | * @param ... Arguments to be passed to the function. 238 | * @return Error code. 239 | */ 240 | NTHREAD_API nerror_t ntu_call(void *func_addr, int arg_count, ...); 241 | 242 | /** 243 | * @brief Call a function with variable arguments and retrieve a return value. 244 | * 245 | * @param func_addr Address of the function to call. 246 | * @param arg_count Number of arguments to pass. 247 | * @param args Variable argument list. 248 | * @return Pointer returned by the called function 249 | */ 250 | NTHREAD_API void *ntu_ucall_v(void *func_addr, int arg_count, va_list args); 251 | 252 | /** 253 | * @brief Call a function with variadic arguments and retrieve a return value. 254 | * 255 | * @param func_addr Address of the function to call. 256 | * @param arg_count Number of arguments to pass. 257 | * @param ... Arguments to be passed. 258 | * @return Pointer returned by the called function 259 | */ 260 | NTHREAD_API void *_ntu_ucall(void *func_addr, int arg_count, ...); 261 | 262 | #define ntu_ucall(func_addr, ...) \ 263 | _ntu_ucall(func_addr, NEPTUNE_GET_ARG_COUNT(__VA_ARGS__), __VA_ARGS__) 264 | 265 | /** 266 | * @brief Fill a block of memory in the target process with a specified value. 267 | * 268 | * @param dest Destination address. 269 | * @param fill Value to fill with. 270 | * @param length Number of bytes to fill. 271 | * @return Pointer to the destination. 272 | */ 273 | NTHREAD_API void *ntu_memset(void *dest, int fill, size_t length); 274 | 275 | /** 276 | * @brief Allocate memory inside the target process. 277 | * 278 | * @param size Number of bytes to allocate. 279 | * @return Pointer to allocated memory. 280 | */ 281 | NTHREAD_API void *ntu_malloc(size_t size); 282 | 283 | /** 284 | * @brief Free previously allocated memory inside the target process. 285 | * 286 | * @param address Pointer to memory to free. 287 | */ 288 | NTHREAD_API void ntu_free(void *address); 289 | 290 | /** 291 | * @brief Open a file stream inside the target process. 292 | * 293 | * @param filename File path in the target process's memory. 294 | * @param mode Mode string in the target process's memory. 295 | * @return FILE pointer representing the opened file stream. 296 | * 297 | * @note Strings must exist in the target process memory prior to calling this function. 298 | */ 299 | NTHREAD_API FILE *ntu_fopen(const nfile_path_t filename, 300 | const nfile_path_t mode); 301 | 302 | /** 303 | * @brief Read from a file stream into a buffer. 304 | * 305 | * @param buffer Destination buffer. 306 | * @param size Size of each element. 307 | * @param count Number of elements to read. 308 | * @param fstream FILE pointer of the opened file stream. 309 | * @return Number of elements successfully read. 310 | */ 311 | NTHREAD_API size_t ntu_fread(void *buffer, size_t size, size_t count, 312 | FILE *fstream); 313 | 314 | /** 315 | * @brief Write data from a buffer into a file stream. 316 | * 317 | * @param buffer Source buffer. 318 | * @param size Size of each element. 319 | * @param count Number of elements to write. 320 | * @param fstream FILE pointer of the opened file stream. 321 | * @return Number of elements successfully written. 322 | */ 323 | NTHREAD_API size_t ntu_fwrite(const void *buffer, size_t size, size_t count, 324 | FILE *fstream); 325 | 326 | /** 327 | * @brief Flush the file stream buffers. 328 | * 329 | * @param fstream FILE pointer of the opened file stream. 330 | * @return 0 on success. 331 | */ 332 | NTHREAD_API int ntu_fflush(FILE *fstream); 333 | 334 | /** 335 | * @brief Close the opened file stream. 336 | * 337 | * @param fstream FILE pointer to close. 338 | * @return 0 on success. 339 | */ 340 | NTHREAD_API int ntu_fclose(FILE *fstream); 341 | 342 | /** 343 | * @brief Allocate and copy a string into the target process memory. 344 | * 345 | * @param str Source null-terminated string. 346 | * @return Pointer to the allocated string in target memory. 347 | */ 348 | NTHREAD_API void *ntu_alloc_str(const char *str); 349 | 350 | /** 351 | * @brief Write data to remote memory, skipping bytes equal to the given value. 352 | * 353 | * This function writes only the bytes from `source` that are not equal to `last_value`. 354 | * It avoids writing bytes with the same value to reduce memory operations. 355 | * 356 | * @param dest Destination address in remote memory. 357 | * @param source Source buffer to write from. 358 | * @param length Number of bytes to write. 359 | * @param last_value Value to skip while writing (e.g., 0x00 or 0xFF). 360 | * @return Error code indicating success or failure. 361 | */ 362 | NTHREAD_API nerror_t ntu_write_with_memset_value(void *dest, const void *source, 363 | size_t length, 364 | int8_t last_value); 365 | 366 | /** 367 | * @brief Write data to remote memory, skipping identical bytes by comparing with last written destination. 368 | * 369 | * This function compares the `source` buffer with the memory at `last_dest` and only writes bytes that differ. 370 | * Both `source` and `last_dest` are expected to be in local (current process) memory. 371 | * 372 | * @param dest Destination address in remote memory. 373 | * @param source Source buffer to write from (local memory). 374 | * @param length Number of bytes to write. 375 | * @param last_dest Last written destination buffer for comparison (local memory, required). 376 | * @return Error code indicating success or failure. 377 | */ 378 | NTHREAD_API nerror_t ntu_write_with_memset_dest(void *dest, const void *source, 379 | size_t length, 380 | const void *last_dest); 381 | 382 | /** 383 | * @brief Write data to remote memory using memset-based operations. 384 | * 385 | * This function writes the entire source buffer to the destination address 386 | * using memory write techniques based on memset. 387 | * 388 | * @param dest Destination address in remote memory. 389 | * @param source Source buffer to write from. 390 | * @param length Number of bytes to write. 391 | * @return Error code indicating success or failure. 392 | */ 393 | NTHREAD_API nerror_t ntu_write_with_memset(void *dest, const void *source, 394 | size_t length); 395 | 396 | #define NTU_NTTUNNEL_EX(ntutils) (&ntutils->nttunnel) 397 | #define NTU_NTTUNNEL() (ntu_nttunnel()) 398 | 399 | NTHREAD_API nttunnel_t *ntu_nttunnel(); 400 | 401 | NTHREAD_API bool ntu_tunnel_can_read(); 402 | 403 | NTHREAD_API bool ntu_tunnel_can_write(); 404 | 405 | NTHREAD_API nerror_t ntu_tunnel_read(const void *dest, void *source, 406 | size_t length); 407 | 408 | NTHREAD_API nerror_t ntu_tunnel_write(void *dest, const void *source, 409 | size_t length); 410 | 411 | #define ntu_read(...) ntu_tunnel_read(__VA_ARGS__) 412 | #define ntu_write(...) ntu_tunnel_write(__VA_ARGS__) 413 | 414 | #define ntu_read_memory(...) ntu_tunnel_read(__VA_ARGS__) 415 | #define ntu_write_memory(...) ntu_tunnel_write(__VA_ARGS__) 416 | 417 | #ifdef __cplusplus 418 | } 419 | #endif 420 | 421 | #endif // !__NTUTILS_H__ 422 | -------------------------------------------------------------------------------- /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 | 55 | #ifdef NTU_USE_TLS 56 | DWORD ntu_tls_index = 0; 57 | #else /* ifndef NTU_USE_TLS */ 58 | ntutils_t *ntu_ntutils = NULL; 59 | #endif /* ifndef NTU_USE_TLS */ 60 | 61 | #endif /* ifdef _WIN32 */ 62 | 63 | NTHREAD_API ntutils_t *_ntu_get(void) 64 | { 65 | #ifdef _WIN32 66 | #ifdef NTU_USE_TLS 67 | return (ntutils_t *)TlsGetValue(ntu_tls_index); 68 | #else /* ifndef NTU_USE_TLS */ 69 | return ntu_ntutils; 70 | #endif /* ifndef NTU_USE_TLS */ 71 | #endif /* ifdef _WIN32 */ 72 | 73 | return NULL; 74 | } 75 | 76 | NTHREAD_API nerror_t ntu_set(ntutils_t *ntutils) 77 | { 78 | #ifdef _WIN32 79 | #ifdef NTU_USE_TLS 80 | 81 | if (!TlsSetValue(ntu_tls_index, (void *)ntutils)) 82 | return GET_ERR(NTUTILS_TLS_SET_VALUE_ERROR); 83 | 84 | #else /* ifndef NTU_USE_TLS */ 85 | 86 | ntu_ntutils = ntutils; 87 | 88 | #endif /* ifndef NTU_USE_TLS */ 89 | #endif /* ifdef _WIN32 */ 90 | 91 | return N_OK; 92 | } 93 | 94 | NTHREAD_API nerror_t ntu_resize(size_t new_size) 95 | { 96 | ntutils_t *o_ntutils = ntu_get(); 97 | if (new_size == 0) { 98 | if (o_ntutils != NULL) 99 | N_FREE(o_ntutils); 100 | 101 | ntu_set(NULL); 102 | } else { 103 | ntutils_t *ntutils; 104 | if (o_ntutils == NULL) 105 | ntutils = N_ALLOC(new_size); 106 | else 107 | ntutils = N_REALLOC(o_ntutils, new_size); 108 | 109 | if (ntutils == NULL) 110 | return GET_ERR(NTUTILS_ALLOC_ERROR); 111 | 112 | ntu_set(ntutils); 113 | } 114 | 115 | return N_OK; 116 | } 117 | 118 | #ifndef NTU_GLOBAL_CC 119 | 120 | NTHREAD_API void _ntu_set_cc(ntucc_t cc) 121 | { 122 | ntutils_t *ntutils = ntu_get(); 123 | if (ntutils != NULL) 124 | ntu_set_cc_ex(ntutils, cc); 125 | } 126 | 127 | NTHREAD_API void _ntu_set_default_cc() 128 | { 129 | _ntu_set_cc(NTU_DEFAULT_CC); 130 | } 131 | 132 | NTHREAD_API ntutils_t *_ntu_o(ntucc_t cc) 133 | { 134 | ntutils_t *ntutils = ntu_get(); 135 | if (ntutils != NULL) 136 | ntu_set_cc_ex(ntutils, cc); 137 | 138 | return ntutils; 139 | } 140 | 141 | #endif // !NTU_GLOBAL_CC 142 | 143 | NTHREAD_API void *ntu_get_libc_base() 144 | { 145 | void *ret; 146 | 147 | #ifdef _WIN32 148 | 149 | ret = (void *)GetModuleHandleA("msvcrt"); 150 | if (ret == NULL) { 151 | #ifdef LOG_LEVEL_1 152 | LOG_INFO("msvcrt.dll not found, loading dynamically..."); 153 | #endif /* ifdef LOG_LEVEL_1 */ 154 | 155 | LoadLibraryA("msvcrt"); 156 | ret = (void *)GetModuleHandleA("msvcrt"); 157 | } 158 | 159 | #endif /* ifdef _WIN32 */ 160 | 161 | return ret; 162 | } 163 | 164 | NTHREAD_API nerror_t ntu_global_init(void) 165 | { 166 | #ifdef _WIN32 167 | #ifdef NTU_USE_TLS 168 | 169 | ntu_tls_index = TlsAlloc(); 170 | if (ntu_tls_index == 0) 171 | return GET_ERR(NTUTILS_TLS_ALLOC_ERROR); 172 | 173 | #endif /* ifdef NTU_USE_TLS */ 174 | #endif /* ifdef _WIN32 */ 175 | 176 | void *libc_base = ntu_get_libc_base(); 177 | if (libc_base == NULL) 178 | return GET_ERR(NTUTILS_GET_LIBC_BASE_ERROR); 179 | 180 | #ifdef _WIN32 181 | 182 | const char func_names[8][8] = { "_wfopen", "memset", "malloc", "fwrite", 183 | "fflush", "fclose", "fread", "free" }; 184 | 185 | #endif /* ifdef _WIN32 */ 186 | 187 | int8_t i; 188 | for (i = 0; i < 8; i++) { 189 | const char *func_name = func_names[i]; 190 | 191 | #ifdef _WIN32 192 | void *func = GetProcAddress((void *)libc_base, func_name); 193 | #endif /* ifdef _WIN32 */ 194 | 195 | if (func == NULL) 196 | return GET_ERR(NTUTILS_FUNC_INIT_ERROR + i); 197 | 198 | #ifdef LOG_LEVEL_3 199 | LOG_INFO("ntutils function(%s): %p", func_name, func); 200 | #endif /* ifdef LOG_LEVEL_3 */ 201 | 202 | ((void **)&ntu_funcs)[i] = func; 203 | } 204 | 205 | return N_OK; 206 | } 207 | 208 | NTHREAD_API void ntu_global_destroy(void) 209 | { 210 | ntu_resize(0); 211 | 212 | #ifdef _WIN32 213 | #ifdef NTU_USE_TLS 214 | 215 | if (ntu_tls_index != 0) { 216 | TlsFree(ntu_tls_index); 217 | 218 | ntu_tls_index = 0; 219 | } 220 | 221 | #endif /* ifdef NTU_USE_TLS */ 222 | #endif /* ifdef _WIN32 */ 223 | } 224 | 225 | NTHREAD_API nerror_t ntu_upgrade(nthread_t *nthread) 226 | { 227 | if (!NTHREAD_IS_VALID(nthread)) 228 | return GET_ERR(NTUTILS_NTHREAD_ERROR); 229 | 230 | if (HAS_ERR(ntu_resize(sizeof(ntutils_t)))) 231 | return GET_ERR(NTUTILS_NTU_RESIZE_ERROR); 232 | 233 | ntutils_t *ntutils = ntu_get(); 234 | if (ntutils == NULL) 235 | return GET_ERR(NTUTILS_NTU_GET_ERROR); 236 | 237 | memcpy(&ntutils->nthread, nthread, sizeof(nthread_t)); 238 | nttunnel_t *nttunnel = NTU_NTTUNNEL_EX(ntutils); 239 | 240 | memset(nttunnel, 0, sizeof(nttunnel_t)); 241 | if (HAS_ERR(ntt_init(nttunnel))) { 242 | ntu_destroy(); 243 | return GET_ERR(NTUTILS_NTT_INIT_ERROR); 244 | } 245 | 246 | return N_OK; 247 | } 248 | 249 | NTHREAD_API nerror_t ntu_attach_ex(ntid_t thread_id, 250 | nthread_reg_offset_t push_reg_offset, 251 | void *push_addr, void *sleep_addr) 252 | { 253 | nthread_t nthread; 254 | if (HAS_ERR(nthread_init(&nthread, thread_id, push_reg_offset, 255 | push_addr, sleep_addr))) { 256 | return GET_ERR(NTUTILS_NTHREAD_INIT_ERROR); 257 | } 258 | 259 | return ntu_upgrade(&nthread); 260 | } 261 | 262 | NTHREAD_API nerror_t ntu_attach(ntid_t thread_id, void *push_addr, 263 | void *sleep_addr) 264 | { 265 | return ntu_attach_ex(thread_id, NTHREAD_BEST_PUSH_REG, push_addr, 266 | sleep_addr); 267 | } 268 | 269 | NTHREAD_API void ntu_destroy() 270 | { 271 | ntutils_t *ntutils = ntu_get(); 272 | if (ntutils == NULL) 273 | return; 274 | 275 | if (NTHREAD_IS_VALID(&ntutils->nthread)) { 276 | ntt_destroy(NTU_NTTUNNEL_EX(ntutils)); 277 | nthread_destroy(&ntutils->nthread); 278 | } 279 | 280 | ntu_resize(0); 281 | } 282 | 283 | NTHREAD_API nerror_t ntu_write_with_memset_value(void *dest, const void *source, 284 | size_t length, 285 | int8_t last_value) 286 | { 287 | size_t i = 0, j; 288 | while (i < length) { 289 | while (last_value == ((int8_t *)source)[i]) { 290 | i++; 291 | if (i >= length) 292 | break; 293 | } 294 | 295 | int8_t ms_value = ((int8_t *)source)[i]; 296 | for (j = i + 1; j < length; j++) { 297 | if (((int8_t *)source)[j] != ms_value) 298 | break; 299 | } 300 | 301 | void *addr = ntu_memset((void *)((int8_t *)dest + i), ms_value, 302 | j - i); 303 | if (addr == NULL) 304 | return GET_ERR(NTUTILS_NTU_MEMSET_ERROR); 305 | 306 | i = j; 307 | } 308 | 309 | return N_OK; 310 | } 311 | 312 | NTHREAD_API nerror_t ntu_write_with_memset_dest(void *dest, const void *source, 313 | size_t length, 314 | const void *last_dest) 315 | { 316 | size_t i = 0, j; 317 | while (i < length) { 318 | if (last_dest != NULL) { 319 | while (((int8_t *)last_dest)[i] == 320 | ((int8_t *)source)[i]) { 321 | i++; 322 | if (i >= length) 323 | break; 324 | } 325 | } 326 | 327 | int8_t ms_value = ((int8_t *)source)[i]; 328 | for (j = i + 1; j < length; j++) { 329 | if (((int8_t *)source)[j] != ms_value) 330 | break; 331 | } 332 | 333 | void *addr = ntu_memset((void *)((int8_t *)dest + i), ms_value, 334 | 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 | NTHREAD_API nerror_t 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((void *)((int8_t *)dest + i), ms_value, 356 | j - i); 357 | if (addr == NULL) 358 | return GET_ERR(NTUTILS_NTU_MEMSET_ERROR); 359 | 360 | i = j; 361 | } 362 | 363 | return N_OK; 364 | } 365 | 366 | NTHREAD_API void ntu_set_reg_args_ex(uint8_t arg_count, void **args, 367 | ntucc_t sel_cc) 368 | { 369 | ntutils_t *ntutils = ntu_get(); 370 | nthread_t *nthread = &ntutils->nthread; 371 | 372 | int8_t i; 373 | for (i = 0; i < 8 && i < arg_count; i++) { 374 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 375 | if (reg_index == 0) 376 | continue; 377 | 378 | nthread_reg_offset_t off = 379 | NTHREAD_REG_INDEX_TO_OFFSET(reg_index); 380 | 381 | NTHREAD_SET_REG(nthread, off, args[i]); 382 | } 383 | } 384 | 385 | NTHREAD_API void ntu_set_reg_args(uint8_t arg_count, void **args) 386 | { 387 | #ifdef NTU_GLOBAL_CC 388 | ntucc_t sel_cc = NTU_GLOBAL_CC; 389 | #else /* ifdnef NTU_GLOBAL_CC */ 390 | 391 | ntutils_t *ntutils = ntu_get(); 392 | ntucc_t sel_cc = ntutils->sel_cc; 393 | 394 | #endif /* ifndef NTU_GLOBAL_CC */ 395 | 396 | ntu_set_reg_args_ex(arg_count, args, sel_cc); 397 | } 398 | 399 | NTHREAD_API nerror_t ntu_set_args_v(uint8_t arg_count, va_list args) 400 | { 401 | ntutils_t *ntutils = ntu_get(); 402 | nthread_t *nthread = &ntutils->nthread; 403 | 404 | #ifdef NTU_GLOBAL_CC 405 | ntucc_t sel_cc = NTU_GLOBAL_CC; 406 | #else /* ifdnef NTU_GLOBAL_CC */ 407 | ntucc_t sel_cc = ntutils->sel_cc; 408 | #endif /* ifndef NTU_GLOBAL_CC */ 409 | 410 | #ifdef LOG_LEVEL_3 411 | LOG_INFO("ntu_set_args_v(cc=%p, nthread_id=%ld, arg_count=%d, args=%p)", 412 | sel_cc, NTHREAD_GET_ID(nthread), arg_count, args); 413 | #endif /* ifdef LOG_LEVEL_3 */ 414 | 415 | int8_t reg_arg_count = 0; 416 | 417 | uint8_t i; 418 | for (i = 0; i < 8; i++) { 419 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 420 | if (reg_index != 0) 421 | reg_arg_count++; 422 | } 423 | 424 | if (reg_arg_count > arg_count) 425 | reg_arg_count = arg_count; 426 | 427 | uint8_t push_arg_count = arg_count - reg_arg_count; 428 | bool need_push = push_arg_count > 0; 429 | 430 | void *rsp = nthread_stack_begin(nthread); 431 | void *wpos = (void *)((int8_t *)rsp + NTUCC_GET_STACK_ADD(sel_cc)); 432 | 433 | size_t push_args_size; 434 | void **push_args; 435 | if (need_push) { 436 | push_args_size = push_arg_count * sizeof(void *); 437 | push_args = (void **)ntutils->stack_helper; 438 | } 439 | 440 | uint8_t push_arg_pos; 441 | 442 | void *reg_args[8]; 443 | bool reverse = (sel_cc & NTUCC_REVERSE_OP) != 0; 444 | if (reverse) 445 | push_arg_pos = push_arg_count - 1; 446 | else 447 | push_arg_pos = 0; 448 | 449 | for (i = 0; i < arg_count; i++) { 450 | void *arg = va_arg(args, void *); 451 | if (i < 8) { 452 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 453 | if (reg_index != 0) { 454 | reg_args[i] = arg; 455 | continue; 456 | } 457 | } 458 | 459 | push_args[push_arg_pos] = arg; 460 | if (reverse) 461 | push_arg_pos--; 462 | else 463 | push_arg_pos++; 464 | } 465 | 466 | if (need_push) { 467 | if (HAS_ERR(ntu_write_memory(wpos, push_args, push_args_size))) 468 | return GET_ERR(NTUTILS_WRITE_MEMORY_ERROR); 469 | } 470 | 471 | ntu_set_reg_args_ex(arg_count, reg_args, sel_cc); 472 | return N_OK; 473 | } 474 | 475 | NTHREAD_API nerror_t ntu_set_args(int arg_count, ...) 476 | { 477 | va_list args; 478 | va_start(args, arg_count); 479 | 480 | nerror_t ret = ntu_set_args_v(arg_count, args); 481 | 482 | va_end(args); 483 | return ret; 484 | } 485 | 486 | NTHREAD_API void ntu_get_reg_args_ex(uint8_t arg_count, void **args, 487 | ntucc_t sel_cc) 488 | { 489 | ntutils_t *ntutils = ntu_get(); 490 | nthread_t *nthread = &ntutils->nthread; 491 | 492 | int8_t i; 493 | for (i = 0; i < 8 && i < arg_count; i++) { 494 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 495 | if (reg_index == 0) 496 | continue; 497 | 498 | nthread_reg_offset_t off = 499 | NTHREAD_REG_INDEX_TO_OFFSET(reg_index); 500 | 501 | args[i] = NTHREAD_GET_OREG(nthread, off); 502 | } 503 | } 504 | 505 | NTHREAD_API void ntu_get_reg_args(uint8_t arg_count, void **args) 506 | { 507 | #ifdef NTU_GLOBAL_CC 508 | ntucc_t sel_cc = NTU_GLOBAL_CC; 509 | #else /* ifdnef NTU_GLOBAL_CC */ 510 | 511 | ntutils_t *ntutils = ntu_get(); 512 | ntucc_t sel_cc = ntutils->sel_cc; 513 | 514 | #endif /* ifndef NTU_GLOBAL_CC */ 515 | 516 | ntu_get_reg_args_ex(arg_count, args, sel_cc); 517 | } 518 | 519 | NTHREAD_API nerror_t ntu_get_args(uint8_t arg_count, void **args) 520 | { 521 | ntutils_t *ntutils = ntu_get(); 522 | nthread_t *nthread = &ntutils->nthread; 523 | 524 | RET_ERR(nthread_get_regs(nthread)); 525 | 526 | #ifdef NTU_GLOBAL_CC 527 | ntucc_t sel_cc = NTU_GLOBAL_CC; 528 | #else /* ifdnef NTU_GLOBAL_CC */ 529 | ntucc_t sel_cc = ntutils->sel_cc; 530 | #endif /* ifndef NTU_GLOBAL_CC */ 531 | 532 | #ifdef LOG_LEVEL_3 533 | LOG_INFO("ntu_get_args(cc=%p, nthread_id=%ld, arg_count=%d, args=%p)", 534 | sel_cc, NTHREAD_GET_ID(nthread), arg_count, args); 535 | #endif /* ifdef LOG_LEVEL_3 */ 536 | 537 | int8_t reg_arg_count = 0; 538 | 539 | int8_t i; 540 | for (i = 0; i < 8; i++) { 541 | int8_t reg_index = NTUCC_GET_ARG(sel_cc, i); 542 | if (reg_index != 0) 543 | reg_arg_count++; 544 | } 545 | 546 | if (reg_arg_count > arg_count) 547 | reg_arg_count = arg_count; 548 | 549 | ntu_get_reg_args_ex(reg_arg_count, args, sel_cc); 550 | 551 | uint8_t push_arg_count = arg_count - reg_arg_count; 552 | if (push_arg_count > 0) { 553 | void *rsp = NTHREAD_GET_OREG(nthread, NTHREAD_RSP); 554 | void *wpos = 555 | (void *)((int8_t *)rsp + NTUCC_GET_STACK_ADD(sel_cc)); 556 | 557 | size_t push_args_size = sizeof(void *) * push_arg_count; 558 | void **push_args = args + reg_arg_count; 559 | 560 | if (HAS_ERR(ntu_read_memory(wpos, (void *)push_args, 561 | push_args_size))) 562 | return GET_ERR(NTUTILS_READ_MEMORY_ERROR); 563 | 564 | if ((sel_cc & NTUCC_REVERSE_OP) != 0) { 565 | void **push_args_copy = (void **)ntutils->stack_helper; 566 | memcpy(push_args_copy, push_args, push_args_size); 567 | 568 | uint8_t i; 569 | uint8_t helper = push_arg_count - 1; 570 | for (i = 0; i < push_arg_count; i++) { 571 | push_args[i] = push_args_copy[helper - i]; 572 | } 573 | } 574 | } 575 | 576 | return N_OK; 577 | } 578 | 579 | NTHREAD_API nerror_t ntu_call_v(void *func_addr, uint8_t arg_count, 580 | va_list args) 581 | { 582 | ntutils_t *ntutils = ntu_get(); 583 | 584 | RET_ERR(ntu_set_args_v(arg_count, args)); 585 | return nthread_call(&ntutils->nthread, func_addr, &ntutils->ret_value); 586 | } 587 | 588 | NTHREAD_API nerror_t ntu_call(void *func_addr, int arg_count, ...) 589 | { 590 | va_list args; 591 | va_start(args, arg_count); 592 | 593 | nerror_t ret = ntu_call_v(func_addr, arg_count, args); 594 | 595 | va_end(args); 596 | return ret; 597 | } 598 | 599 | NTHREAD_API void *ntu_ucall_v(void *func_addr, int arg_count, va_list args) 600 | { 601 | ntutils_t *ntutils = ntu_get(); 602 | if (HAS_ERR(ntu_call_v(func_addr, arg_count, args))) 603 | return NULL; 604 | 605 | return ntutils->ret_value; 606 | } 607 | 608 | NTHREAD_API void *_ntu_ucall(void *func_addr, int arg_count, ...) 609 | { 610 | va_list args; 611 | va_start(args, arg_count); 612 | 613 | nerror_t *ret = ntu_ucall_v(func_addr, arg_count, args); 614 | 615 | va_end(args); 616 | return ret; 617 | } 618 | 619 | NTHREAD_API void *ntu_memset(void *dest, int fill, size_t length) 620 | { 621 | ntu_set_default_cc(); 622 | return ntu_ucall(ntu_funcs.memset, dest, fill, length); 623 | } 624 | 625 | NTHREAD_API void *ntu_malloc(size_t size) 626 | { 627 | ntu_set_default_cc(); 628 | return ntu_ucall(ntu_funcs.malloc, size); 629 | } 630 | 631 | NTHREAD_API void ntu_free(void *address) 632 | { 633 | ntu_set_default_cc(); 634 | ntu_ucall(ntu_funcs.free, address); 635 | } 636 | 637 | NTHREAD_API FILE *ntu_fopen(const nfile_path_t filename, 638 | const nfile_path_t mode) 639 | { 640 | ntu_set_default_cc(); 641 | return (FILE *)ntu_ucall(ntu_funcs.fopen, filename, mode); 642 | } 643 | 644 | NTHREAD_API size_t ntu_fread(void *buffer, size_t size, size_t count, 645 | FILE *fstream) 646 | { 647 | ntu_set_default_cc(); 648 | return (size_t)ntu_ucall(ntu_funcs.fread, buffer, size, count, fstream); 649 | } 650 | 651 | NTHREAD_API size_t ntu_fwrite(const void *buffer, size_t size, size_t count, 652 | FILE *fstream) 653 | { 654 | ntu_set_default_cc(); 655 | return (size_t)ntu_ucall(ntu_funcs.fwrite, buffer, size, count, 656 | fstream); 657 | } 658 | 659 | NTHREAD_API int ntu_fflush(FILE *fstream) 660 | { 661 | ntutils_t *ntutils = ntu_o(NTU_DEFAULT_CC); 662 | if (HAS_ERR(ntu_call(ntu_funcs.fflush, 1, fstream))) 663 | return -1; 664 | 665 | return (int)(int64_t)ntutils->ret_value; 666 | } 667 | 668 | NTHREAD_API int ntu_fclose(FILE *fstream) 669 | { 670 | ntutils_t *ntutils = ntu_o(NTU_DEFAULT_CC); 671 | if (HAS_ERR(ntu_call(ntu_funcs.fclose, 1, fstream))) 672 | return -1; 673 | 674 | return (int)(int64_t)ntutils->ret_value; 675 | } 676 | 677 | NTHREAD_API void *ntu_alloc_str(const char *str) 678 | { 679 | size_t str_len = (strlen(str) + 1) * sizeof(*str); 680 | void *addr = ntu_malloc(str_len); 681 | if (addr == NULL) 682 | return NULL; 683 | 684 | if (HAS_ERR(ntu_write(addr, str, str_len))) { 685 | ntu_free(addr); 686 | return NULL; 687 | } 688 | 689 | return addr; 690 | } 691 | 692 | NTHREAD_API nttunnel_t *ntu_nttunnel() 693 | { 694 | ntutils_t *ntutils = ntu_get(); 695 | return NTU_NTTUNNEL_EX(ntutils); 696 | } 697 | 698 | NTHREAD_API bool ntu_tunnel_can_read() 699 | { 700 | nttunnel_t *nttunnel = ntu_nttunnel(); 701 | return ntt_can_read(nttunnel); 702 | } 703 | 704 | NTHREAD_API bool ntu_tunnel_can_write() 705 | { 706 | nttunnel_t *nttunnel = ntu_nttunnel(); 707 | return ntt_can_write(nttunnel); 708 | } 709 | 710 | NTHREAD_API nerror_t ntu_tunnel_read(const void *dest, void *source, 711 | size_t length) 712 | { 713 | nttunnel_t *tunnel = ntu_nttunnel(); 714 | return ntt_read(tunnel, dest, source, length); 715 | } 716 | 717 | NTHREAD_API nerror_t ntu_tunnel_write(void *dest, const void *source, 718 | size_t length) 719 | { 720 | nttunnel_t *tunnel = ntu_nttunnel(); 721 | return ntt_write(tunnel, dest, source, length); 722 | } 723 | --------------------------------------------------------------------------------