├── LICENSE ├── README.md ├── Test.cpp ├── images ├── preview.png └── preview2.png ├── single_header └── HideImport.hpp └── src ├── HideImport.cpp ├── HideImport.hpp └── syscall_shellcode.s /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Reveny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android-Native-Import-Hide 2 | A library for hiding and retrieving imports in ELF binaries. 3 | 4 | [![Stars](https://img.shields.io/github/stars/reveny/Android-Native-Import-Hide?label=Stars)](https://github.com/reveny) 5 | [![Channel](https://img.shields.io/badge/Telegram-Channel-blue.svg?logo=telegram)](https://t.me/reveny1) 6 | 7 | ## Features 8 | - [x] Hide and retrieve symbols in ELF binaries 9 | - [x] Support for multiple architectures (x86, x86_64, ARM, ARM64) 10 | - [x] Cache resolved symbols for performance 11 | - [x] Thread-safe symbol resolution 12 | - [x] Detailed logging for debugging purposes 13 | - [x] Check hooking before calling (ARM and ARM64 only) 14 | - [ ] Prevent hooking completely 15 | 16 | ## Build and Installation 17 | Compatible Compilers 18 | - GCC or Clang 19 | - Make 20 | - CMake 21 | 22 | Clone the repository: 23 | ```sh 24 | git clone https://github.com/reveny/Android-Native-Import-Hide.git 25 | cd Android-Native-Import-Hide 26 | ``` 27 | 28 | To include the library in your project, add the following line to your source code: 29 | 30 | ```cpp 31 | #include "HideImport.hpp" 32 | ``` 33 | ## Usage 34 | Here is a simple example demonstrating how to use the library and make sure to include HideImport.cpp in the source file list: 35 | 36 | ## Using `HI_GET` 37 | ```cpp 38 | #include 39 | #include "HideImport.hpp" 40 | 41 | int main() { 42 | HI_FUNCTION_POINTER(my_malloc, void*, size_t size) = HI_GET("libc.so", "malloc"); 43 | 44 | void *testMemory = my_malloc(20); 45 | printf("my_malloc test returned: %p\n", testMemory); 46 | free(testMemory); 47 | 48 | return 0; 49 | } 50 | ``` 51 | 52 | ## Using `HI_CALL` 53 | ```cpp 54 | #include 55 | #include "HideImport.hpp" 56 | 57 | int main() { 58 | void *testMemory2 = HI_CALL("libc.so", malloc, void*, size_t)(15); 59 | printf("malloc test 2 returned: %p\n", testMemory2); 60 | free(testMemory2); 61 | 62 | return 0; 63 | } 64 | ``` 65 | 66 | ## Using `HI_SAFE` 67 | ```cpp 68 | #include 69 | #include "HideImport.hpp" 70 | 71 | int main() { 72 | void *testMemory2 = HI_CALL_SAFE("libc.so", malloc, void*, size_t)(15); 73 | printf("malloc test 2 returned: %p\n", testMemory2); 74 | free(testMemory2); 75 | 76 | return 0; 77 | } 78 | ``` 79 | The SAFE version will check if the function is hooked before calling. If the function happens to be hooked, the call will not be executed and return NULL. 80 | 81 | ## Single Header Library 82 | A single header version of the library is available for convenience. Simply include single_header/HideImport.hpp in your project. 83 | 84 | ## Preview 85 | Disassembly without string encryption:
86 | ![Preview](https://github.com/reveny/Android-Native-Import-Hide/blob/main/images/preview.png) 87 | 88 | Disassembly with string encryption:
89 | ![Preview](https://github.com/reveny/Android-Native-Import-Hide/blob/main/images/preview2.png) 90 | 91 | ## Credits 92 | Special thanks to: 93 | - [ARandomPerson](https://github.com/ARandomPerson7) for doing a lot of the work and the significant contribution and collaboration on this project. 94 | - [Ac3ss0r](https://github.com/ac3ss0r) for some inspiration from [ShellcodeLab](https://github.com/ac3ss0r/ShellcodeLab) 95 | - LSPlt for inspiration from their module listing implementation: [LSPlt](https://github.com/LSPosed/LSPlt) 96 | 97 | ## Contact 98 | Feel free to reach out via: 99 | - Telegram Group: [Join Group](https://t.me/reveny1) 100 | - Telegram Contact: [Contact](https://t.me/revenyy) 101 | 102 | ## License 103 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 104 | -------------------------------------------------------------------------------- /Test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define USE_SINGLE_HEADER 1 4 | 5 | #if USE_SINGLE_HEADER 6 | #include "single_header/HideImport.hpp" 7 | #else 8 | #include "src/HideImport.hpp" 9 | #endif 10 | 11 | int main() { 12 | printf("Hello World!\n"); 13 | 14 | HI_FUNCTION_POINTER(my_malloc, void*, size_t size) = HI_GET_SAFE("libc.so", "malloc"); 15 | 16 | void *testMemory = my_malloc(20); 17 | printf("my_malloc test returned: %p\n", testMemory); 18 | free(testMemory); 19 | 20 | void *testMemory2 = HI_CALL_SAFE("libc.so", malloc, void*, size_t)(15); 21 | printf("malloc test 2 returned: %p\n", testMemory2); 22 | free(testMemory2); 23 | 24 | return 0; 25 | } -------------------------------------------------------------------------------- /images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-Native-Import-Hide/67f9490e2804ba4df539304cc8a459b97ee322ef/images/preview.png -------------------------------------------------------------------------------- /images/preview2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/reveny/Android-Native-Import-Hide/67f9490e2804ba4df539304cc8a459b97ee322ef/images/preview2.png -------------------------------------------------------------------------------- /single_header/HideImport.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HideImport.hpp - A single header library for hiding and retrieving symbols in ELF binaries. 3 | // Created by reveny and ARandomPerson on 6/6/24. 4 | // Copyright (c) 2024. All rights reserved. 5 | // 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define HI_INLINE __attribute__((always_inline)) 39 | 40 | #define HI_ENABLE_DEBUG 0 41 | #if HI_ENABLE_DEBUG 42 | #define HI_TAG "HideImport" 43 | #if defined(__ANDROID__) 44 | #include 45 | #define HI_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, HI_TAG, __VA_ARGS__)) 46 | #define HI_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, HI_TAG, __VA_ARGS__)) 47 | #else 48 | #include 49 | #define HI_LOGE(fmt, ...) printf("ERROR: [%s] " fmt "\n", HI_TAG, ##__VA_ARGS__) 50 | #define HI_LOGI(fmt, ...) printf("INFO: [%s] " fmt "\n", HI_TAG, ##__VA_ARGS__) 51 | #endif 52 | #else 53 | #define HI_LOGE(fmt, ...) 54 | #define HI_LOGI(fmt, ...) 55 | #endif 56 | 57 | // Template to simplify function pointer assignment 58 | template 59 | class SimpleFunctionPointer; 60 | 61 | template 62 | class SimpleFunctionPointer { 63 | public: 64 | using Type = R (*)(Args...); 65 | 66 | // Constructor from uintptr_t 67 | SimpleFunctionPointer(uintptr_t address) : ptr(reinterpret_cast(address)) {} 68 | 69 | // Overload the function call operator 70 | R operator()(Args... args) const { 71 | if (ptr) { 72 | return ptr(args...); 73 | } else { 74 | throw std::runtime_error("Function pointer is null"); 75 | } 76 | } 77 | 78 | // Assignment operator for function pointer 79 | SimpleFunctionPointer& operator=(Type p) { 80 | ptr = p; 81 | return *this; 82 | } 83 | 84 | // Assignment operator for uintptr_t 85 | SimpleFunctionPointer& operator=(uintptr_t address) { 86 | ptr = reinterpret_cast(address); 87 | return *this; 88 | } 89 | private: 90 | Type ptr; 91 | }; 92 | 93 | #define HI_FUNCTION_POINTER(func_name, ret_type, ...) \ 94 | SimpleFunctionPointer func_name 95 | 96 | #define HI_GET(library, symbol) \ 97 | HideImport::GetSymbol(library, symbol) 98 | 99 | #define HI_CALL(library, symbol, ret_type, ...) \ 100 | reinterpret_cast::Type>(HI_GET(library, #symbol)) 101 | 102 | #define HI_GET_SAFE(library, symbol) \ 103 | ({ \ 104 | auto func = HI_GET(library, symbol); \ 105 | IS_FUNCTION_HOOKED(func) ? NULL : func; \ 106 | }) 107 | 108 | #define HI_CALL_SAFE(library, symbol, ret_type, ...) \ 109 | reinterpret_cast::Type>(HI_GET_SAFE(library, #symbol)) 110 | 111 | #if defined(__x86_64) || defined(__aarch64__) 112 | #define Elf_Ehdr Elf64_Ehdr 113 | #define Elf_Shdr Elf64_Shdr 114 | #define Elf_Sym Elf64_Sym 115 | #define _ELF_ST_BIND(x) ELF64_ST_BIND(x) 116 | #else 117 | #define Elf_Ehdr Elf32_Ehdr 118 | #define Elf_Shdr Elf32_Shdr 119 | #define Elf_Sym Elf32_Sym 120 | #define _ELF_ST_BIND(x) ELF32_ST_BIND(x) 121 | #endif 122 | 123 | // arm and arm64 only for now. 124 | // Detected on shadowhook, dobbyhook and and64inlinehook 125 | // TODO: Arm has not been fully tested on all hooking frameworks. 126 | #if defined(__aarch64__) 127 | #define IS_LDR_X17(instr) (((instr) & 0xFF000000) == 0x58000000) 128 | #define IS_BR_X17(instr) ((instr) == 0xd61f0220) 129 | #define IS_HOOKED_CONDITION (IS_LDR_X17(instr1) && IS_BR_X17(instr2)) 130 | #elif defined(__arm__) 131 | #define IS_LDR_PC(instr) (((instr) & 0x0F7FF000) == 0x051FF000) 132 | #define IS_BLX_R3(instr) ((instr) == 0xE12FFF33) 133 | #define IS_HOOKED_CONDITION (IS_LDR_PC(instr1) && IS_BLX_R3(instr2)) 134 | #else 135 | #define IS_HOOKED_CONDITION 0 136 | #endif 137 | 138 | #define IS_FUNCTION_HOOKED(function) ({ \ 139 | uint32_t *addr = (uint32_t *)(function); \ 140 | uint32_t instr1 = *addr; \ 141 | uint32_t instr2 = *(addr + 1); \ 142 | int result = 0; \ 143 | if (IS_HOOKED_CONDITION) { \ 144 | uintptr_t *hook_addr_ptr = (uintptr_t *)(addr + 2); \ 145 | result = 1; \ 146 | } \ 147 | result; \ 148 | }) 149 | 150 | template 151 | constexpr long to_long(const T& arg) { 152 | if constexpr (std::is_pointer_v) { 153 | return reinterpret_cast(arg); // Cast pointers to long 154 | } else if constexpr (std::is_integral_v) { 155 | return static_cast(arg); // Convert integral types to long 156 | } else if constexpr (std::is_same_v) { 157 | return 0; // Convert nullptr to 0 158 | } else { 159 | static_assert(!std::is_same_v, "Unsupported argument type for syscall"); 160 | } 161 | } 162 | 163 | #define SYSCALL(...) inline_syscall(__VA_ARGS__) 164 | template 165 | inline long inline_syscall(long syscall_number, Args... args) { 166 | long ret; 167 | 168 | long syscall_args[] = {to_long(args)...}; 169 | constexpr size_t num_args = sizeof...(Args); 170 | 171 | #if defined(__x86_64__) 172 | __asm__ volatile ( 173 | "mov %1, %%rax;" 174 | "mov %2, %%rdi;" 175 | "mov %3, %%rsi;" 176 | "mov %4, %%rdx;" 177 | "mov %5, %%r10;" 178 | "mov %6, %%r8;" 179 | "mov %7, %%r9;" 180 | "syscall;" 181 | "mov %%rax, %0;" 182 | : "=r" (ret) 183 | : "r" (syscall_number), 184 | "r" (num_args > 0 ? syscall_args[0] : 0), 185 | "r" (num_args > 1 ? syscall_args[1] : 0), 186 | "r" (num_args > 2 ? syscall_args[2] : 0), 187 | "r" (num_args > 3 ? syscall_args[3] : 0), 188 | "r" (num_args > 4 ? syscall_args[4] : 0), 189 | "r" (num_args > 5 ? syscall_args[5] : 0) 190 | : "%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9" 191 | ); 192 | #elif defined(__i386__) 193 | __asm__ volatile ( 194 | "mov %1, %%eax;" 195 | "mov %2, %%ebx;" 196 | "mov %3, %%ecx;" 197 | "mov %4, %%edx;" 198 | "mov %5, %%esi;" 199 | "mov %6, %%edi;" 200 | "int $0x80;" 201 | "mov %%eax, %0;" 202 | : "=r" (ret) 203 | : "r" (syscall_number), 204 | "r" (num_args > 0 ? syscall_args[0] : 0), 205 | "r" (num_args > 1 ? syscall_args[1] : 0), 206 | "r" (num_args > 2 ? syscall_args[2] : 0), 207 | "r" (num_args > 3 ? syscall_args[3] : 0), 208 | "r" (num_args > 4 ? syscall_args[4] : 0) 209 | : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi" 210 | ); 211 | #elif defined(__arm__) 212 | __asm__ volatile ( 213 | "mov r7, %1;" 214 | "mov r0, %2;" 215 | "mov r1, %3;" 216 | "mov r2, %4;" 217 | "mov r3, %5;" 218 | "mov r4, %6;" 219 | "mov r5, %7;" 220 | "swi 0;" 221 | "mov %0, r0;" 222 | : "=r" (ret) 223 | : "r" (syscall_number), 224 | "r" (num_args > 0 ? syscall_args[0] : 0), 225 | "r" (num_args > 1 ? syscall_args[1] : 0), 226 | "r" (num_args > 2 ? syscall_args[2] : 0), 227 | "r" (num_args > 3 ? syscall_args[3] : 0), 228 | "r" (num_args > 4 ? syscall_args[4] : 0), 229 | "r" (num_args > 5 ? syscall_args[5] : 0) 230 | : "r0", "r1", "r2", "r3", "r4", "r5", "r7" 231 | ); 232 | #elif defined(__aarch64__) 233 | __asm__ volatile ( 234 | "mov x8, %1;" 235 | "mov x0, %2;" 236 | "mov x1, %3;" 237 | "mov x2, %4;" 238 | "mov x3, %5;" 239 | "mov x4, %6;" 240 | "mov x5, %7;" 241 | "svc 0;" 242 | "mov %0, x0;" 243 | : "=r" (ret) 244 | : "r" (syscall_number), 245 | "r" (num_args > 0 ? syscall_args[0] : 0), 246 | "r" (num_args > 1 ? syscall_args[1] : 0), 247 | "r" (num_args > 2 ? syscall_args[2] : 0), 248 | "r" (num_args > 3 ? syscall_args[3] : 0), 249 | "r" (num_args > 4 ? syscall_args[4] : 0), 250 | "r" (num_args > 5 ? syscall_args[5] : 0) 251 | : "x0", "x1", "x2", "x3", "x4", "x5", "x8" 252 | ); 253 | #else 254 | #error "Unsupported architecture" 255 | #endif 256 | 257 | return ret; 258 | } 259 | 260 | namespace HideImport { 261 | std::unordered_map symbolCache; 262 | std::mutex cacheMutex; // Mutex for thread safe access 263 | 264 | enum class MachineType : uint16_t { 265 | ELF_EM_NONE = EM_NONE, // No machine 266 | ELF_EM_386 = EM_386, // Intel 80386 267 | ELF_EM_ARM = EM_ARM, // ARM 268 | ELF_EM_X86_64 = EM_X86_64, // x86_64 269 | ELF_EM_AARCH64 = EM_AARCH64 // ARM64 270 | }; 271 | 272 | namespace Memory { 273 | constexpr static auto K_PERM_LENGTH = 5; 274 | constexpr static auto K_MAP_ENTRY = 7; 275 | struct MapInfo { 276 | std::string name; 277 | uintptr_t start; 278 | uintptr_t end; 279 | int perms; 280 | bool private_map; 281 | uintptr_t offset; 282 | dev_t dev; 283 | ino_t inode; 284 | std::string path; 285 | }; 286 | 287 | // Inspired by LSPlt's implementation of module listing. 288 | // Reference: https://github.com/LSPosed/LSPlt/blob/a674793be6bc060b6d695c0cb481e8262c763885/lsplt/src/main/jni/lsplt.cc#L245 289 | /** 290 | * ListModulesNew - Lists memory mappings of the current process by reading /proc/self/maps. 291 | * Parses each line of the maps file and extracts memory region information. 292 | * 293 | * @return A vector of MapInfo structures containing details about each memory region. 294 | */ 295 | std::vector ListModulesNew() { 296 | std::vector info; 297 | int fd = SYSCALL(__NR_openat, AT_FDCWD, "/proc/self/maps", O_RDONLY); 298 | if (fd == -1) { 299 | HI_LOGE("Failed to open /proc/self/maps with error: %s", strerror(errno)); 300 | return info; 301 | } 302 | 303 | char buffer[4096]; 304 | ssize_t bytesRead; 305 | std::string line; 306 | while ((bytesRead = SYSCALL(__NR_read, fd, buffer, sizeof(buffer) - 1)) > 0) { 307 | buffer[bytesRead] = '\0'; 308 | line += buffer; 309 | 310 | size_t pos; 311 | while ((pos = line.find('\n')) != std::string::npos) { 312 | std::string entry = line.substr(0, pos); 313 | line.erase(0, pos + 1); 314 | 315 | uintptr_t start = 0; 316 | uintptr_t end = 0; 317 | uintptr_t offset = 0; 318 | ino_t inode = 0; 319 | unsigned int devMajor = 0; 320 | unsigned int devMinor = 0; 321 | std::array perm{'\0'}; 322 | int pathOff; 323 | 324 | // Extract fields from the entry line 325 | if (sscanf(entry.c_str(), "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n", &start, &end, perm.data(), &offset, &devMajor, &devMinor, &inode, &pathOff) != K_MAP_ENTRY) { 326 | continue; 327 | } 328 | 329 | // Skip spaces to find the path offset 330 | while (pathOff < entry.size() && isspace(entry[pathOff])) { 331 | pathOff++; 332 | } 333 | 334 | auto &ref = info.emplace_back(MapInfo{ 335 | entry, 336 | start, 337 | end, 338 | 0, 339 | perm[3] == 'p', 340 | offset, 341 | makedev(devMajor, devMinor), 342 | inode, 343 | entry.substr(pathOff) 344 | }); 345 | 346 | if (perm[0] == 'r') ref.perms |= PROT_READ; 347 | if (perm[1] == 'w') ref.perms |= PROT_WRITE; 348 | if (perm[2] == 'x') ref.perms |= PROT_EXEC; 349 | } 350 | } 351 | 352 | if (bytesRead == -1) { 353 | HI_LOGE("Failed to read /proc/self/maps file: %s", strerror(errno)); 354 | perror("read"); 355 | } 356 | 357 | close(fd); 358 | return info; 359 | } 360 | 361 | /** 362 | * GetLibraryPath - Retrieves the full path of a loaded library by matching the provided substring. 363 | * 364 | * @param path The substring to match against the library paths. 365 | * @return The full path of the matching library, or an empty string if not found. 366 | */ 367 | std::string GetLibraryPath(const std::string &path) { 368 | std::vector modules = ListModulesNew(); 369 | for (const auto& module : modules) { 370 | if (module.path.find(path) != std::string::npos) { 371 | return module.path; 372 | } 373 | } 374 | return ""; 375 | } 376 | 377 | /** 378 | * FindLibraryBase - Finds the base address of a loaded library by matching the provided substring. 379 | * 380 | * @param path The substring to match against the library paths. 381 | * @return The base address of the matching library, or 0 if not found. 382 | */ 383 | uintptr_t FindLibraryBase(const std::string &path) { 384 | auto modules = ListModulesNew(); 385 | for (const auto& module : modules) { 386 | if (module.path.find(path) != std::string::npos) { 387 | return module.start; 388 | } 389 | } 390 | return 0; 391 | } 392 | }; 393 | 394 | /** 395 | * GetELFSymbolOffset - Retrieves the offset of a symbol within an ELF binary. 396 | * 397 | * @param entryAddr The base address of the ELF binary in memory. 398 | * @param entryElf The ELF header of the binary. 399 | * @param symbolName The name of the symbol to find. 400 | * @return The offset of the symbol within the ELF binary, or -1 if not found. 401 | */ 402 | uintptr_t GetELFSymbolOffset(uintptr_t entryAddr, Elf_Ehdr *entryElf, const char* symbolName) { 403 | uintptr_t result = static_cast(-1); 404 | 405 | Elf_Shdr* sections = reinterpret_cast(entryAddr + static_cast(entryElf->e_shoff)); 406 | Elf_Shdr* symtab = nullptr; 407 | 408 | // Find the symbol table section 409 | for (int i = 0; i < entryElf->e_shnum; i++) { 410 | if (sections[i].sh_type == SHT_SYMTAB || sections[i].sh_type == SHT_DYNSYM) { 411 | symtab = sections + i; 412 | break; 413 | } 414 | } 415 | 416 | if (!symtab) { 417 | return result; 418 | } 419 | 420 | const char* strSecAddr = reinterpret_cast(entryAddr + static_cast(sections[symtab->sh_link].sh_offset)); 421 | Elf_Sym* symSec = reinterpret_cast(entryAddr + static_cast(symtab->sh_offset)); 422 | int nSymbols = symtab->sh_size / sizeof(Elf_Sym); 423 | 424 | // Search for the symbol by name 425 | for (int i = 0; i < nSymbols; i++) { 426 | if (!(_ELF_ST_BIND(symSec[i].st_info) & (STT_FUNC | STB_GLOBAL))) { 427 | continue; 428 | } 429 | 430 | const char* currSymbolName = strSecAddr + symSec[i].st_name; 431 | if (strcmp(currSymbolName, symbolName) == 0) { 432 | result = symSec[i].st_value; 433 | break; 434 | } 435 | } 436 | 437 | HI_LOGI("Found offset (%p) of %s.", result, symbolName); 438 | return result; 439 | } 440 | 441 | /** 442 | * MapELFFile - Maps an ELF file into memory and retrieves the address of a specified symbol. 443 | * 444 | * @param baseAddr The base address of the ELF library in memory. 445 | * @param path The file path of the ELF library. 446 | * @param symbolName The name of the symbol to find. 447 | * @return The absolute address of the symbol, or -1 if not found. 448 | */ 449 | uintptr_t MapELFFile(uintptr_t baseAddr, std::string path, std::string symbolName) { 450 | uintptr_t result = static_cast(-1); 451 | 452 | int fd = SYSCALL(__NR_openat, AT_FDCWD, path.c_str(), O_RDONLY); 453 | if (fd < 0) { 454 | return result; 455 | } 456 | 457 | struct stat elfStat; 458 | if (fstat(fd, &elfStat) < 0) { 459 | close(fd); 460 | return result; 461 | } 462 | 463 | void *entryRaw = (void *)SYSCALL(__NR_mmap, NULL, static_cast(elfStat.st_size), PROT_READ, MAP_SHARED, fd, 0); 464 | if (entryRaw == MAP_FAILED) { 465 | close(fd); 466 | return result; 467 | } 468 | 469 | // Get the symbol offset and calculate the absolute address 470 | auto* elfEntry = static_cast(entryRaw); 471 | uintptr_t offset = GetELFSymbolOffset(reinterpret_cast(entryRaw), elfEntry, symbolName.c_str()); 472 | 473 | if (offset != static_cast(-1)) { 474 | result = (uintptr_t)baseAddr + offset; 475 | } else { 476 | result = 0; 477 | } 478 | 479 | HI_LOGI("Found absolute address %p of symbol %s in %s", result, symbolName.c_str(), path.c_str()); 480 | 481 | // Clean up 482 | SYSCALL(__NR_munmap, entryRaw, static_cast(elfStat.st_size)); 483 | close(fd); 484 | 485 | return result; 486 | } 487 | 488 | /** 489 | * GetSymbol - Retrieves the address of a symbol within a specified ELF library. 490 | * 491 | * @param elfName The name of the ELF library. 492 | * @param symbolName The name of the symbol to find. 493 | * @return The address of the symbol, or 0 if not found. 494 | */ 495 | uintptr_t GetSymbol(std::string elfName, std::string symbolName) { 496 | std::string key = elfName + ":" + symbolName; 497 | { 498 | // Check the cache first 499 | std::lock_guard lock(cacheMutex); 500 | if (symbolCache.find(key) != symbolCache.end()) { 501 | HI_LOGI("Cache hit for symbol %s in %s", symbolName.c_str(), elfName.c_str()); 502 | return symbolCache[key]; 503 | } 504 | } 505 | 506 | uintptr_t base = Memory::FindLibraryBase(elfName); 507 | if (base == 0) { 508 | return 0; 509 | } 510 | HI_LOGI("Found memory base: %p", base); 511 | 512 | std::string path = Memory::GetLibraryPath(elfName); 513 | uintptr_t result = MapELFFile(base, path, symbolName); 514 | 515 | // Cache the result 516 | { 517 | std::lock_guard lock(cacheMutex); 518 | symbolCache[key] = result; 519 | } 520 | 521 | return result; 522 | } 523 | } 524 | -------------------------------------------------------------------------------- /src/HideImport.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // HideImport.cpp - A library for hiding and retrieving symbols in ELF binaries. 3 | // Created by reveny and ARandomPerson on 6/6/24. 4 | // Copyright (c) 2024. All rights reserved. 5 | // 6 | #include "HideImport.hpp" 7 | 8 | namespace HideImport { 9 | std::unordered_map symbolCache; 10 | std::mutex cacheMutex; 11 | } 12 | 13 | // Inspired by LSPlt's implementation of module listing. 14 | // Reference: https://github.com/LSPosed/LSPlt/blob/a674793be6bc060b6d695c0cb481e8262c763885/lsplt/src/main/jni/lsplt.cc#L245 15 | /** 16 | * ListModulesNew - Lists memory mappings of the current process by reading /proc/self/maps. 17 | * Parses each line of the maps file and extracts memory region information. 18 | * 19 | * @return A vector of MapInfo structures containing details about each memory region. 20 | */ 21 | std::vector HideImport::Memory::ListModulesNew() { 22 | std::vector info; 23 | int fd = SYSCALL(__NR_openat, AT_FDCWD, "/proc/self/maps", O_RDONLY); 24 | if (fd == -1) { 25 | HI_LOGE("Failed to open /proc/self/maps with error: %s", strerror(errno)); 26 | return info; 27 | } 28 | 29 | char buffer[4096]; 30 | ssize_t bytesRead; 31 | std::string line; 32 | while ((bytesRead = SYSCALL(__NR_read, fd, buffer, sizeof(buffer) - 1)) > 0) { 33 | buffer[bytesRead] = '\0'; 34 | line += buffer; 35 | 36 | size_t pos; 37 | while ((pos = line.find('\n')) != std::string::npos) { 38 | std::string entry = line.substr(0, pos); 39 | line.erase(0, pos + 1); 40 | 41 | uintptr_t start = 0; 42 | uintptr_t end = 0; 43 | uintptr_t offset = 0; 44 | ino_t inode = 0; 45 | unsigned int devMajor = 0; 46 | unsigned int devMinor = 0; 47 | std::array perm{'\0'}; 48 | int pathOff; 49 | 50 | // Extract fields from the entry line 51 | if (sscanf(entry.c_str(), "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %x:%x %lu %n", &start, &end, perm.data(), &offset, &devMajor, &devMinor, &inode, &pathOff) != K_MAP_ENTRY) { 52 | continue; 53 | } 54 | 55 | // Skip spaces to find the path offset 56 | while (pathOff < entry.size() && isspace(entry[pathOff])) { 57 | pathOff++; 58 | } 59 | 60 | auto &ref = info.emplace_back(MapInfo{ 61 | entry, 62 | start, 63 | end, 64 | 0, 65 | perm[3] == 'p', 66 | offset, 67 | makedev(devMajor, devMinor), 68 | inode, 69 | entry.substr(pathOff) 70 | }); 71 | 72 | if (perm[0] == 'r') ref.perms |= PROT_READ; 73 | if (perm[1] == 'w') ref.perms |= PROT_WRITE; 74 | if (perm[2] == 'x') ref.perms |= PROT_EXEC; 75 | } 76 | } 77 | 78 | if (bytesRead == -1) { 79 | HI_LOGE("Failed to read /proc/self/maps file: %s", strerror(errno)); 80 | perror("read"); 81 | } 82 | 83 | close(fd); 84 | return info; 85 | } 86 | 87 | /** 88 | * GetLibraryPath - Retrieves the full path of a loaded library by matching the provided substring. 89 | * 90 | * @param path The substring to match against the library paths. 91 | * @return The full path of the matching library, or an empty string if not found. 92 | */ 93 | std::string HideImport::Memory::GetLibraryPath(const std::string &path) { 94 | std::vector modules = ListModulesNew(); 95 | for (const auto& module : modules) { 96 | if (module.path.find(path) != std::string::npos) { 97 | return module.path; 98 | } 99 | } 100 | return ""; 101 | } 102 | 103 | /** 104 | * FindLibraryBase - Finds the base address of a loaded library by matching the provided substring. 105 | * 106 | * @param path The substring to match against the library paths. 107 | * @return The base address of the matching library, or 0 if not found. 108 | */ 109 | uintptr_t HideImport::Memory::FindLibraryBase(const std::string &path) { 110 | auto modules = ListModulesNew(); 111 | for (const auto& module : modules) { 112 | if (module.path.find(path) != std::string::npos) { 113 | return module.start; 114 | } 115 | } 116 | return 0; 117 | } 118 | 119 | /** 120 | * GetELFSymbolOffset - Retrieves the offset of a symbol within an ELF binary. 121 | * 122 | * @param entryAddr The base address of the ELF binary in memory. 123 | * @param entryElf The ELF header of the binary. 124 | * @param symbolName The name of the symbol to find. 125 | * @return The offset of the symbol within the ELF binary, or -1 if not found. 126 | */ 127 | uintptr_t HideImport::GetELFSymbolOffset(uintptr_t entryAddr, Elf_Ehdr *entryElf, const char* symbolName) { 128 | uintptr_t result = static_cast(-1); 129 | Elf_Shdr* sections = reinterpret_cast(entryAddr + static_cast(entryElf->e_shoff)); 130 | Elf_Shdr* symtab = nullptr; 131 | 132 | // Find the symbol table section 133 | for (int i = 0; i < entryElf->e_shnum; i++) { 134 | if (sections[i].sh_type == SHT_SYMTAB || sections[i].sh_type == SHT_DYNSYM) { 135 | // symtab = §ions[i]; 136 | symtab = sections + i; 137 | break; 138 | } 139 | } 140 | 141 | if (!symtab) { 142 | HI_LOGE("Failed to resolve symtab."); 143 | return result; 144 | } 145 | 146 | const char* strSecAddr = reinterpret_cast(entryAddr + static_cast(sections[symtab->sh_link].sh_offset)); 147 | Elf_Sym* symSec = reinterpret_cast(entryAddr + static_cast(symtab->sh_offset)); 148 | int nSymbols = symtab->sh_size / sizeof(Elf_Sym); 149 | 150 | // Search for the symbol by name 151 | for (int i = 0; i < nSymbols; i++) { 152 | if (!(_ELF_ST_BIND(symSec[i].st_info) & (STT_FUNC | STB_GLOBAL))) { 153 | continue; 154 | } 155 | 156 | const char* currSymbolName = strSecAddr + symSec[i].st_name; 157 | if (strcmp(currSymbolName, symbolName) == 0) { 158 | result = symSec[i].st_value; 159 | break; 160 | } 161 | } 162 | 163 | HI_LOGI("Found offset (%p) of %s.", result, symbolName); 164 | return result; 165 | } 166 | 167 | /** 168 | * MapELFFile - Maps an ELF file into memory and retrieves the address of a specified symbol. 169 | * 170 | * @param baseAddr The base address of the ELF library in memory. 171 | * @param path The file path of the ELF library. 172 | * @param symbolName The name of the symbol to find. 173 | * @return The absolute address of the symbol, or -1 if not found. 174 | */ 175 | uintptr_t HideImport::MapELFFile(uintptr_t baseAddr, std::string path, std::string symbolName) { 176 | uintptr_t result = static_cast(-1); 177 | 178 | int fd = SYSCALL(__NR_openat, AT_FDCWD, path.c_str(), O_RDONLY); 179 | if (fd < 0) { 180 | return result; 181 | } 182 | 183 | struct stat elfStat; 184 | if (fstat(fd, &elfStat) < 0) { 185 | close(fd); 186 | return result; 187 | } 188 | 189 | void *entryRaw = (void *)SYSCALL(__NR_mmap, NULL, static_cast(elfStat.st_size), PROT_READ, MAP_SHARED, fd, 0); 190 | if (entryRaw == MAP_FAILED) { 191 | close(fd); 192 | return result; 193 | } 194 | 195 | // Get the symbol offset and calculate the absolute address 196 | auto* elfEntry = static_cast(entryRaw); 197 | uintptr_t offset = GetELFSymbolOffset(reinterpret_cast(entryRaw), elfEntry, symbolName.c_str()); 198 | 199 | if (offset != static_cast(-1)) { 200 | result = (uintptr_t)baseAddr + offset; 201 | } else { 202 | result = 0; 203 | } 204 | 205 | HI_LOGI("Found absolute address %p of symbol %s in %s", result, symbolName.c_str(), path.c_str()); 206 | 207 | // Clean up 208 | SYSCALL(__NR_munmap, entryRaw, static_cast(elfStat.st_size)); 209 | close(fd); 210 | 211 | return result; 212 | } 213 | 214 | /** 215 | * GetSymbol - Retrieves the address of a symbol within a specified ELF library. 216 | * 217 | * @param elfName The name of the ELF library. 218 | * @param symbolName The name of the symbol to find. 219 | * @return The address of the symbol, or 0 if not found. 220 | */ 221 | HI_INLINE uintptr_t HideImport::GetSymbol(std::string elfName, std::string symbolName) { 222 | std::string key = elfName + ":" + symbolName; 223 | { 224 | // Check the cache first 225 | std::lock_guard lock(cacheMutex); 226 | if (symbolCache.find(key) != symbolCache.end()) { 227 | HI_LOGI("Cache hit for symbol %s in %s", symbolName.c_str(), elfName.c_str()); 228 | return symbolCache[key]; 229 | } 230 | } 231 | 232 | uintptr_t base = Memory::FindLibraryBase(elfName); 233 | if (base == 0) { 234 | return 0; 235 | } 236 | HI_LOGI("Found memory base: %p", base); 237 | 238 | std::string path = Memory::GetLibraryPath(elfName); 239 | uintptr_t result = MapELFFile(base, path, symbolName); 240 | 241 | // Cache the result 242 | { 243 | std::lock_guard lock(cacheMutex); 244 | symbolCache[key] = result; 245 | } 246 | 247 | return result; 248 | } -------------------------------------------------------------------------------- /src/HideImport.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // HideImport.hpp - A library for hiding and retrieving symbols in ELF binaries. 3 | // Created by reveny and ARandomPerson on 6/6/24. 4 | // Copyright (c) 2024. All rights reserved. 5 | // 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #define HI_INLINE __attribute__((always_inline)) 39 | 40 | #define HI_ENABLE_DEBUG 1 41 | #if HI_ENABLE_DEBUG 42 | #define HI_TAG "HideImport" 43 | #if defined(__ANDROID__) 44 | #include 45 | #define HI_LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, HI_TAG, __VA_ARGS__)) 46 | #define HI_LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, HI_TAG, __VA_ARGS__)) 47 | #else 48 | #include 49 | #define HI_LOGE(fmt, ...) printf("ERROR: [%s] " fmt "\n", HI_TAG, ##__VA_ARGS__) 50 | #define HI_LOGI(fmt, ...) printf("INFO: [%s] " fmt "\n", HI_TAG, ##__VA_ARGS__) 51 | #endif 52 | #else 53 | #define HI_LOGE(fmt, ...) 54 | #define HI_LOGI(fmt, ...) 55 | #endif 56 | 57 | // Template to simplify function pointer assignment 58 | template 59 | class SimpleFunctionPointer; 60 | 61 | template 62 | class SimpleFunctionPointer { 63 | public: 64 | using Type = R (*)(Args...); 65 | 66 | // Constructor from uintptr_t 67 | SimpleFunctionPointer(uintptr_t address) : ptr(reinterpret_cast(address)) {} 68 | 69 | // Overload the function call operator 70 | R operator()(Args... args) const { 71 | if (ptr) { 72 | return ptr(args...); 73 | } else { 74 | throw std::runtime_error("Function pointer is null"); 75 | } 76 | } 77 | 78 | // Assignment operator for function pointer 79 | SimpleFunctionPointer& operator=(Type p) { 80 | ptr = p; 81 | return *this; 82 | } 83 | 84 | // Assignment operator for uintptr_t 85 | SimpleFunctionPointer& operator=(uintptr_t address) { 86 | ptr = reinterpret_cast(address); 87 | return *this; 88 | } 89 | private: 90 | Type ptr; 91 | }; 92 | 93 | #define HI_FUNCTION_POINTER(func_name, ret_type, ...) \ 94 | SimpleFunctionPointer func_name 95 | 96 | #define HI_GET(library, symbol) \ 97 | HideImport::GetSymbol(library, symbol) 98 | 99 | #define HI_CALL(library, symbol, ret_type, ...) \ 100 | reinterpret_cast::Type>(HI_GET(library, #symbol)) 101 | 102 | #define HI_GET_SAFE(library, symbol) \ 103 | ({ \ 104 | auto func = HI_GET(library, symbol); \ 105 | IS_FUNCTION_HOOKED(func) ? NULL : func; \ 106 | }) 107 | 108 | #define HI_CALL_SAFE(library, symbol, ret_type, ...) \ 109 | reinterpret_cast::Type>(HI_GET_SAFE(library, #symbol)) 110 | 111 | #if defined(__x86_64) || defined(__aarch64__) 112 | #define Elf_Ehdr Elf64_Ehdr 113 | #define Elf_Shdr Elf64_Shdr 114 | #define Elf_Sym Elf64_Sym 115 | #define _ELF_ST_BIND(x) ELF64_ST_BIND(x) 116 | #else 117 | #define Elf_Ehdr Elf32_Ehdr 118 | #define Elf_Shdr Elf32_Shdr 119 | #define Elf_Sym Elf32_Sym 120 | #define _ELF_ST_BIND(x) ELF32_ST_BIND(x) 121 | #endif 122 | 123 | // arm and arm64 only for now. 124 | #if defined(__aarch64__) 125 | #define IS_LDR_X17(instr) (((instr) & 0xFF000000) == 0x58000000) 126 | #define IS_BR_X17(instr) ((instr) == 0xd61f0220) 127 | #define IS_HOOKED_CONDITION (IS_LDR_X17(instr1) && IS_BR_X17(instr2)) 128 | #elif defined(__arm__) 129 | #define IS_LDR_PC(instr) (((instr) & 0x0F7FF000) == 0x051FF000) 130 | #define IS_BLX_R3(instr) ((instr) == 0xE12FFF33) 131 | #define IS_HOOKED_CONDITION (IS_LDR_PC(instr1) && IS_BLX_R3(instr2)) 132 | #else 133 | #define IS_HOOKED_CONDITION 0 134 | #endif 135 | 136 | #define IS_FUNCTION_HOOKED(function) ({ \ 137 | uint32_t *addr = (uint32_t *)(function); \ 138 | uint32_t instr1 = *addr; \ 139 | uint32_t instr2 = *(addr + 1); \ 140 | int result = 0; \ 141 | if (IS_HOOKED_CONDITION) { \ 142 | uintptr_t *hook_addr_ptr = (uintptr_t *)(addr + 2); \ 143 | result = 1; \ 144 | } \ 145 | result; \ 146 | }) 147 | 148 | 149 | template 150 | constexpr long to_long(const T& arg) { 151 | if constexpr (std::is_pointer_v) { 152 | return reinterpret_cast(arg); // Cast pointers to long 153 | } else if constexpr (std::is_integral_v) { 154 | return static_cast(arg); // Convert integral types to long 155 | } else if constexpr (std::is_same_v) { 156 | return 0; // Convert nullptr to 0 157 | } else { 158 | static_assert(!std::is_same_v, "Unsupported argument type for syscall"); 159 | } 160 | } 161 | 162 | #define SYSCALL(...) inline_syscall(__VA_ARGS__) 163 | template 164 | inline long inline_syscall(long syscall_number, Args... args) { 165 | long ret; 166 | 167 | long syscall_args[] = {to_long(args)...}; 168 | constexpr size_t num_args = sizeof...(Args); 169 | 170 | #if defined(__x86_64__) 171 | __asm__ volatile ( 172 | "mov %1, %%rax;" 173 | "mov %2, %%rdi;" 174 | "mov %3, %%rsi;" 175 | "mov %4, %%rdx;" 176 | "mov %5, %%r10;" 177 | "mov %6, %%r8;" 178 | "mov %7, %%r9;" 179 | "syscall;" 180 | "mov %%rax, %0;" 181 | : "=r" (ret) 182 | : "r" (syscall_number), 183 | "r" (num_args > 0 ? syscall_args[0] : 0), 184 | "r" (num_args > 1 ? syscall_args[1] : 0), 185 | "r" (num_args > 2 ? syscall_args[2] : 0), 186 | "r" (num_args > 3 ? syscall_args[3] : 0), 187 | "r" (num_args > 4 ? syscall_args[4] : 0), 188 | "r" (num_args > 5 ? syscall_args[5] : 0) 189 | : "%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9" 190 | ); 191 | #elif defined(__i386__) 192 | __asm__ volatile ( 193 | "mov %1, %%eax;" 194 | "mov %2, %%ebx;" 195 | "mov %3, %%ecx;" 196 | "mov %4, %%edx;" 197 | "mov %5, %%esi;" 198 | "mov %6, %%edi;" 199 | "int $0x80;" 200 | "mov %%eax, %0;" 201 | : "=r" (ret) 202 | : "r" (syscall_number), 203 | "r" (num_args > 0 ? syscall_args[0] : 0), 204 | "r" (num_args > 1 ? syscall_args[1] : 0), 205 | "r" (num_args > 2 ? syscall_args[2] : 0), 206 | "r" (num_args > 3 ? syscall_args[3] : 0), 207 | "r" (num_args > 4 ? syscall_args[4] : 0) 208 | : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi" 209 | ); 210 | #elif defined(__arm__) 211 | __asm__ volatile ( 212 | "mov r7, %1;" 213 | "mov r0, %2;" 214 | "mov r1, %3;" 215 | "mov r2, %4;" 216 | "mov r3, %5;" 217 | "mov r4, %6;" 218 | "mov r5, %7;" 219 | "swi 0;" 220 | "mov %0, r0;" 221 | : "=r" (ret) 222 | : "r" (syscall_number), 223 | "r" (num_args > 0 ? syscall_args[0] : 0), 224 | "r" (num_args > 1 ? syscall_args[1] : 0), 225 | "r" (num_args > 2 ? syscall_args[2] : 0), 226 | "r" (num_args > 3 ? syscall_args[3] : 0), 227 | "r" (num_args > 4 ? syscall_args[4] : 0), 228 | "r" (num_args > 5 ? syscall_args[5] : 0) 229 | : "r0", "r1", "r2", "r3", "r4", "r5", "r7" 230 | ); 231 | #elif defined(__aarch64__) 232 | __asm__ volatile ( 233 | "mov x8, %1;" 234 | "mov x0, %2;" 235 | "mov x1, %3;" 236 | "mov x2, %4;" 237 | "mov x3, %5;" 238 | "mov x4, %6;" 239 | "mov x5, %7;" 240 | "svc 0;" 241 | "mov %0, x0;" 242 | : "=r" (ret) 243 | : "r" (syscall_number), 244 | "r" (num_args > 0 ? syscall_args[0] : 0), 245 | "r" (num_args > 1 ? syscall_args[1] : 0), 246 | "r" (num_args > 2 ? syscall_args[2] : 0), 247 | "r" (num_args > 3 ? syscall_args[3] : 0), 248 | "r" (num_args > 4 ? syscall_args[4] : 0), 249 | "r" (num_args > 5 ? syscall_args[5] : 0) 250 | : "x0", "x1", "x2", "x3", "x4", "x5", "x8" 251 | ); 252 | #else 253 | #error "Unsupported architecture" 254 | #endif 255 | 256 | return ret; 257 | } 258 | 259 | namespace HideImport { 260 | extern std::unordered_map symbolCache; 261 | extern std::mutex cacheMutex; // Mutex for thread safe access 262 | 263 | enum class MachineType : uint16_t { 264 | ELF_EM_NONE = EM_NONE, // No machine 265 | ELF_EM_386 = EM_386, // Intel 80386 266 | ELF_EM_ARM = EM_ARM, // ARM 267 | ELF_EM_X86_64 = EM_X86_64, // x86_64 268 | ELF_EM_AARCH64 = EM_AARCH64 // ARM64 269 | }; 270 | 271 | namespace Memory { 272 | constexpr static auto K_PERM_LENGTH = 5; 273 | constexpr static auto K_MAP_ENTRY = 7; 274 | struct MapInfo { 275 | std::string name; 276 | uintptr_t start; 277 | uintptr_t end; 278 | int perms; 279 | bool private_map; 280 | uintptr_t offset; 281 | dev_t dev; 282 | ino_t inode; 283 | std::string path; 284 | }; 285 | 286 | std::vector ListModulesNew(); 287 | std::string GetLibraryPath(const std::string &path); 288 | uintptr_t FindLibraryBase(const std::string &path); 289 | }; 290 | 291 | uintptr_t GetELFSymbolOffset(uintptr_t entryAddr, Elf_Ehdr *elfEntry, const char *symbolName); 292 | uintptr_t MapELFFile(uintptr_t baseAddr, std::string path, std::string symbolName); 293 | uintptr_t GetSymbol(std::string elfName, std::string symbolName); 294 | }; -------------------------------------------------------------------------------- /src/syscall_shellcode.s: -------------------------------------------------------------------------------- 1 | .text 2 | .global perform_syscall 3 | .type perform_syscall,@function 4 | 5 | perform_syscall: 6 | MOV X8, X0 7 | MOV X0, X1 8 | MOV X1, X2 9 | MOV X2, X3 10 | MOV X3, X4 11 | MOV X4, X5 12 | MOV X5, X6 13 | SVC 0 14 | RET --------------------------------------------------------------------------------