├── README.md ├── Usage.md ├── main.c ├── CmakeLists.txt └── base.c /README.md: -------------------------------------------------------------------------------- 1 | this injector is between kernel and usermode, Hooking etc , discord : @kay133769 this project is in beta state, soon changes, 2 | games tested on : brawl stars, (more powerful than ptrace but less powerful than an LKM) soon updates 3 | -------------------------------------------------------------------------------- /Usage.md: -------------------------------------------------------------------------------- 1 | put project in C: after that open cmd from the folder and type build(needed builders = Ninja,Android-NDK) after its built push bs_inject and libbs_hook.so to /data/local/tmp 2 | or manually move it, open termux after commands: su(U NEED ROOT), cd /data/local/tmp, ./bs_inject and after that follow the steps asked by the injector 3 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | typedef int (*inject_func)(const char*, const char*); 8 | 9 | int main() { 10 | char pkg_name[256]; 11 | char lib_name[256]; 12 | 13 | printf("Enter target package name: "); 14 | fgets(pkg_name, sizeof(pkg_name), stdin); 15 | pkg_name[strcspn(pkg_name, "\n")] = 0; 16 | 17 | printf("Enter target library name: "); 18 | fgets(lib_name, sizeof(lib_name), stdin); 19 | lib_name[strcspn(lib_name, "\n")] = 0; 20 | 21 | 22 | void *handle = dlopen("./libbs_hook.so", RTLD_LAZY); 23 | if (!handle) { 24 | fprintf(stderr, "Error loading library: %s\n", dlerror()); 25 | return 1; 26 | } 27 | 28 | 29 | inject_func inject = (inject_func)dlsym(handle, "inject"); 30 | if (!inject) { 31 | fprintf(stderr, "Error finding symbol: %s\n", dlerror()); 32 | dlclose(handle); 33 | return 1; 34 | } 35 | 36 | 37 | printf("[+] Injecting into %s (%s)\n", pkg_name, lib_name); 38 | int result = inject(pkg_name, lib_name); 39 | 40 | 41 | dlclose(handle); 42 | return result; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /CmakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.4.1) 2 | project(BSHook LANGUAGES C) 3 | 4 | # Set common compile flags for both targets 5 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -Wall -Wextra") 6 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -g -O0") 7 | set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3") 8 | 9 | # Build shared library 10 | add_library(bs_hook SHARED base.c) 11 | 12 | # Set library properties 13 | set_target_properties(bs_hook PROPERTIES 14 | C_STANDARD 11 15 | C_STANDARD_REQUIRED ON 16 | POSITION_INDEPENDENT_CODE ON 17 | VERSION 1.0.0 18 | SOVERSION 1 19 | ) 20 | 21 | # Build main executable 22 | add_executable(bs_inject main.c) 23 | 24 | # Common link libraries for both targets 25 | foreach(TARGET bs_hook bs_inject) 26 | target_link_libraries(${TARGET} PRIVATE 27 | dl 28 | log 29 | ) 30 | 31 | # Android-specific settings 32 | if(ANDROID) 33 | target_compile_definitions(${TARGET} PRIVATE -DANDROID) 34 | 35 | # NDK-specific settings 36 | set_target_properties(${TARGET} PROPERTIES 37 | ANDROID_ARM_NEON TRUE 38 | ANDROID_TOOLCHAIN clang 39 | ) 40 | endif() 41 | endforeach() 42 | 43 | # Additional Android NDK settings 44 | if(ANDROID) 45 | # Enable C++ exceptions and RTTI 46 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -frtti") 47 | 48 | # Security hardening flags 49 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fstack-protector-strong -D_FORTIFY_SOURCE=2") 50 | endif() -------------------------------------------------------------------------------- /base.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifndef __NR_process_vm_readv 15 | #define __NR_process_vm_readv 310 16 | #endif 17 | #ifndef __NR_process_vm_writev 18 | #define __NR_process_vm_writev 311 19 | #endif 20 | 21 | #define TARGET_OFFSET 0x4588BC 22 | #define MAX_ATTEMPTS 3 23 | #define RETRY_DELAY_US 100000 24 | 25 | typedef struct { 26 | void* original_addr; 27 | void* hooked_addr; 28 | uint8_t original_bytes[16]; 29 | void* trampoline; 30 | int is_active; 31 | pid_t target_pid; 32 | } FunctionHook; 33 | 34 | static FunctionHook g_hooks[10]; // Support multiple hooks 35 | static int hook_count = 0; 36 | static pthread_mutex_t hook_mutex = PTHREAD_MUTEX_INITIALIZER; 37 | 38 | // Global flag for conditional hooking 39 | static int haha = 1; // Your condition variable 40 | 41 | // Custom syscall wrappers 42 | #ifndef SYS_process_vm_readv 43 | ssize_t process_vm_readv(pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, 44 | const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags) { 45 | return syscall(__NR_process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); 46 | } 47 | #endif 48 | 49 | #ifndef SYS_process_vm_writev 50 | ssize_t process_vm_writev(pid_t pid, const struct iovec *local_iov, unsigned long liovcnt, 51 | const struct iovec *remote_iov, unsigned long riovcnt, unsigned long flags) { 52 | return syscall(__NR_process_vm_writev, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); 53 | } 54 | #endif 55 | 56 | // Memory operations 57 | int read_memory(pid_t pid, void *remote_addr, void *local_addr, size_t size) { 58 | struct iovec local_iov = { .iov_base = local_addr, .iov_len = size }; 59 | struct iovec remote_iov = { .iov_base = remote_addr, .iov_len = size }; 60 | 61 | ssize_t bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0); 62 | if (bytes_read == (ssize_t)size) { 63 | return 1; 64 | } 65 | 66 | // Fallback to /proc/pid/mem 67 | char mem_path[64]; 68 | sprintf(mem_path, "/proc/%d/mem", pid); 69 | 70 | int mem_fd = open(mem_path, O_RDONLY); 71 | if (mem_fd == -1) return 0; 72 | 73 | if (lseek(mem_fd, (off_t)remote_addr, SEEK_SET) == -1) { 74 | close(mem_fd); 75 | return 0; 76 | } 77 | 78 | ssize_t result = read(mem_fd, local_addr, size); 79 | close(mem_fd); 80 | 81 | return (result == (ssize_t)size); 82 | } 83 | 84 | int write_memory(pid_t pid, void *remote_addr, void *local_addr, size_t size) { 85 | struct iovec local_iov = { .iov_base = local_addr, .iov_len = size }; 86 | struct iovec remote_iov = { .iov_base = remote_addr, .iov_len = size }; 87 | 88 | ssize_t bytes_written = process_vm_writev(pid, &local_iov, 1, &remote_iov, 1, 0); 89 | if (bytes_written == (ssize_t)size) { 90 | return 1; 91 | } 92 | 93 | // Fallback to /proc/pid/mem 94 | char mem_path[64]; 95 | sprintf(mem_path, "/proc/%d/mem", pid); 96 | 97 | int mem_fd = open(mem_path, O_WRONLY); 98 | if (mem_fd == -1) return 0; 99 | 100 | if (lseek(mem_fd, (off_t)remote_addr, SEEK_SET) == -1) { 101 | close(mem_fd); 102 | return 0; 103 | } 104 | 105 | ssize_t result = write(mem_fd, local_addr, size); 106 | close(mem_fd); 107 | 108 | return (result == (ssize_t)size); 109 | } 110 | 111 | void* create_trampoline(pid_t pid, void* original_addr) { 112 | // Allocate executable memory for trampoline 113 | void *trampoline = mmap(NULL, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, 114 | MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); 115 | if (trampoline == MAP_FAILED) { 116 | return NULL; 117 | } 118 | 119 | uint8_t original_bytes[16]; 120 | if (!read_memory(pid, original_addr, original_bytes, 16)) { 121 | munmap(trampoline, 4096); 122 | return NULL; 123 | } 124 | 125 | uint8_t *tramp_ptr = (uint8_t*)trampoline; 126 | 127 | // Copy original instructions 128 | memcpy(tramp_ptr, original_bytes, 16); 129 | tramp_ptr += 16; 130 | 131 | // Add jump back to original function + 16 132 | uint64_t return_addr = (uint64_t)original_addr + 16; 133 | uint32_t *jump_code = (uint32_t*)tramp_ptr; 134 | 135 | // ARM64: ldr x17, #8; br x17; .quad return_addr 136 | jump_code[0] = 0x58000051; // ldr x17, #8 137 | jump_code[1] = 0xD61F0220; // br x17 138 | *((uint64_t*)(jump_code + 2)) = return_addr; 139 | 140 | // Clear instruction cache 141 | __builtin___clear_cache(trampoline, (char*)trampoline + 4096); 142 | 143 | return trampoline; 144 | } 145 | 146 | int find_pid(const char *process_name) { 147 | char command[256]; 148 | snprintf(command, sizeof(command), "pidof %s", process_name); 149 | FILE *fp = popen(command, "r"); 150 | if (!fp) return -1; 151 | 152 | int pid; 153 | if (fscanf(fp, "%d", &pid) != 1) { 154 | pclose(fp); 155 | return -1; 156 | } 157 | pclose(fp); 158 | return pid; 159 | } 160 | 161 | unsigned long find_lib_base(int pid, const char *lib_name) { 162 | char path[256]; 163 | snprintf(path, sizeof(path), "/proc/%d/maps", pid); 164 | 165 | FILE *fp = fopen(path, "r"); 166 | if (!fp) return 0; 167 | 168 | char line[1024]; 169 | unsigned long base = 0; 170 | 171 | while (fgets(line, sizeof(line), fp)) { 172 | if (strstr(line, lib_name) && strstr(line, "r-xp")) { 173 | unsigned long current_base; 174 | sscanf(line, "%lx-", ¤t_base); 175 | 176 | if (base == 0 || current_base < base) { 177 | base = current_base; 178 | } 179 | } 180 | } 181 | fclose(fp); 182 | 183 | return base; 184 | } 185 | 186 | typedef float (*getCam_t)(void* instance, float s); 187 | static getCam_t original_getCam = NULL; 188 | 189 | float new_getCam(void* instance, float s) { 190 | printf("[HOOK] getCam called with instance=%p, s=%f\n", instance, s); 191 | 192 | if (haha) { 193 | printf("[HOOK] Modifying parameter s from %f to 10.0f\n", s); 194 | s = 10.0f; 195 | } 196 | 197 | float result = original_getCam(instance, s); 198 | 199 | printf("[HOOK] getCam returning %f\n", result); 200 | return result; 201 | } 202 | 203 | int hook_function(pid_t pid, void* target_addr, void* hook_func) { 204 | pthread_mutex_lock(&hook_mutex); 205 | 206 | if (hook_count >= 10) { 207 | printf("[-] Maximum number of hooks reached\n"); 208 | pthread_mutex_unlock(&hook_mutex); 209 | return 0; 210 | } 211 | 212 | printf("[+] Hooking function at %p\n", target_addr); 213 | 214 | FunctionHook *hook = &g_hooks[hook_count]; 215 | 216 | if (!read_memory(pid, target_addr, hook->original_bytes, 16)) { 217 | printf("[-] Failed to read original bytes\n"); 218 | pthread_mutex_unlock(&hook_mutex); 219 | return 0; 220 | } 221 | 222 | printf("[+] Original bytes: "); 223 | for (int i = 0; i < 16; i++) { 224 | printf("%02x ", hook->original_bytes[i]); 225 | } 226 | printf("\n"); 227 | 228 | hook->trampoline = create_trampoline(pid, target_addr); 229 | if (!hook->trampoline) { 230 | printf("[-] Failed to create trampoline\n"); 231 | pthread_mutex_unlock(&hook_mutex); 232 | return 0; 233 | } 234 | 235 | original_getCam = (getCam_t)hook->trampoline; 236 | 237 | uint64_t hook_addr = (uint64_t)hook_func; 238 | uint32_t jump_code[] = { 239 | 0x58000051, // ldr x17, #8 240 | 0xD61F0220, // br x17 241 | (uint32_t)(hook_addr & 0xFFFFFFFF), // low 32 bits 242 | (uint32_t)((hook_addr >> 32) & 0xFFFFFFFF) // high 32 bits 243 | }; 244 | 245 | if (!write_memory(pid, target_addr, jump_code, sizeof(jump_code))) { 246 | printf("[-] Failed to install hook\n"); 247 | munmap(hook->trampoline, 4096); 248 | pthread_mutex_unlock(&hook_mutex); 249 | return 0; 250 | } 251 | 252 | hook->original_addr = target_addr; 253 | hook->hooked_addr = hook_func; 254 | hook->target_pid = pid; 255 | hook->is_active = 1; 256 | hook_count++; 257 | 258 | printf("[+] Hook installed successfully!\n"); 259 | printf("[+] Original function can be called via trampoline at %p\n", hook->trampoline); 260 | 261 | pthread_mutex_unlock(&hook_mutex); 262 | return 1; 263 | } 264 | 265 | int unhook_function(void* target_addr) { 266 | pthread_mutex_lock(&hook_mutex); 267 | 268 | for (int i = 0; i < hook_count; i++) { 269 | FunctionHook *hook = &g_hooks[i]; 270 | if (hook->original_addr == target_addr && hook->is_active) { 271 | 272 | printf("[+] Restoring function at %p\n", target_addr); 273 | 274 | if (!write_memory(hook->target_pid, target_addr, hook->original_bytes, 16)) { 275 | printf("[-] Failed to restore original bytes\n"); 276 | pthread_mutex_unlock(&hook_mutex); 277 | return 0; 278 | } 279 | 280 | if (hook->trampoline) { 281 | munmap(hook->trampoline, 4096); 282 | } 283 | 284 | hook->is_active = 0; 285 | printf("[+] Function restored successfully\n"); 286 | 287 | pthread_mutex_unlock(&hook_mutex); 288 | return 1; 289 | } 290 | } 291 | 292 | printf("[-] Hook not found for address %p\n", target_addr); 293 | pthread_mutex_unlock(&hook_mutex); 294 | return 0; 295 | } 296 | 297 | #define HOOK_FUNCTION(pid, addr, hook_func) hook_function(pid, addr, hook_func) 298 | #define UNHOOK_FUNCTION(addr) unhook_function(addr) 299 | 300 | void enable_hook() { 301 | haha = 1; 302 | printf("[+] Hook condition enabled\n"); 303 | } 304 | 305 | void disable_hook() { 306 | haha = 0; 307 | printf("[+] Hook condition disabled\n"); 308 | } 309 | 310 | int is_hook_enabled() { 311 | return haha; 312 | } 313 | 314 | void prompt_for_hook_status() { 315 | char input; 316 | printf("\n[+] Do you want to enable the hook at startup? (Y/N): "); 317 | scanf(" %c", &input); 318 | 319 | if (input == 'Y' || input == 'y') { 320 | enable_hook(); 321 | } else if (input == 'N' || input == 'n') { 322 | disable_hook(); 323 | } else { 324 | printf("[-] Invalid input. Defaulting to enabled.\n"); 325 | enable_hook(); 326 | } 327 | } 328 | 329 | __attribute__((visibility("default"))) 330 | int inject(const char *pkg_name, const char *lib_name) { 331 | printf("Function Hook Injector by @kay133769\n"); 332 | printf("[+] Targeting %s (%s)\n", pkg_name, lib_name); 333 | 334 | // Ask user whether to enable hook at startup 335 | prompt_for_hook_status(); 336 | 337 | int target_pid = find_pid(pkg_name); 338 | if (target_pid == -1) { 339 | printf("[-] Process not found: %s\n", pkg_name); 340 | return 1; 341 | } 342 | 343 | printf("[+] Found process PID: %d\n", target_pid); 344 | 345 | unsigned long base = find_lib_base(target_pid, lib_name); 346 | if (!base) { 347 | printf("[-] Library not found: %s\n", lib_name); 348 | return 1; 349 | } 350 | 351 | void *target = (void*)(base + TARGET_OFFSET); 352 | printf("[+] Target address: %p (base: 0x%lx + offset: 0x%x)\n", target, base, TARGET_OFFSET); 353 | 354 | if (HOOK_FUNCTION(target_pid, target, new_getCam)) { 355 | printf("[+] getCam hook installed successfully!\n"); 356 | printf("[+] Hook is currently %s\n", is_hook_enabled() ? "ENABLED" : "DISABLED"); 357 | printf("[+] You can now call enable_hook()/disable_hook() to control behavior\n"); 358 | 359 | printf("[+] Hook is active.\n"); 360 | while (1) { 361 | sleep(1); 362 | } 363 | 364 | UNHOOK_FUNCTION(target); 365 | } 366 | 367 | return 0; 368 | } 369 | 370 | __attribute__((visibility("default"))) 371 | void toggle_hook() { 372 | haha = !haha; 373 | printf("[+] Hook condition toggled to %s\n", haha ? "ENABLED" : "DISABLED"); 374 | } 375 | 376 | __attribute__((visibility("default"))) 377 | void set_hook_condition(int enabled) { 378 | haha = enabled; 379 | printf("[+] Hook condition set to %s\n", haha ? "ENABLED" : "DISABLED"); 380 | } 381 | --------------------------------------------------------------------------------