/* * AirPods like Normal (ALN) - Bringing Apple-only features to Linux and Android for seamless AirPods functionality! * * Copyright (C) 2024 Kavish Devar * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, either version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include "l2c_fcr_hook.h" #define LOG_TAG "AirPodsHook" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) static HookFunType hook_func = nullptr; static uint8_t (*original_l2c_fcr_chk_chan_modes)(void* p_ccb) = nullptr; uint8_t fake_l2c_fcr_chk_chan_modes([[maybe_unused]] void* p_ccb) { LOGI("l2c_fcr_chk_chan_modes hooked! Always returning true"); return 1; } uintptr_t loadHookOffset([[maybe_unused]] const char* package_name) { const char* property_name = "persist.aln.hook_offset"; char value[PROP_VALUE_MAX] = {0}; int len = __system_property_get(property_name, value); if (len > 0) { LOGI("Read hook offset from property: %s", value); uintptr_t offset = 0; if (value[0] == '0' && (value[1] == 'x' || value[1] == 'X')) { sscanf(value + 2, "%x", &offset); } else { sscanf(value, "%x", &offset); } if (offset > 0) { LOGI("Parsed offset: 0x%x", offset); return offset; } } LOGI("Failed to read offset from property, using hardcoded fallback"); return 0x00a55e30; } uintptr_t getModuleBase(const char *module_name) { FILE *fp; char line[1024]; uintptr_t base_addr = 0; fp = fopen("/proc/self/maps", "r"); if (!fp) { LOGE("Failed to open /proc/self/maps"); return 0; } while (fgets(line, sizeof(line), fp)) { if (strstr(line, module_name)) { char *start_addr_str = line; char *end_addr_str = strchr(line, '-'); if (end_addr_str) { *end_addr_str = '\0'; base_addr = strtoull(start_addr_str, nullptr, 16); break; } } } fclose(fp); return base_addr; } bool findAndHookFunction([[maybe_unused]] const char *library_path) { if (!hook_func) { LOGE("Hook function not initialized"); return false; } uintptr_t base_addr = getModuleBase("libbluetooth_jni.so"); if (!base_addr) { LOGE("Failed to get base address of libbluetooth_jni.so"); return false; } uintptr_t offset = loadHookOffset(nullptr); void* target = reinterpret_cast(base_addr + offset); LOGI("Using offset: 0x%x, base: %p, target: %p", offset, (void*)base_addr, target); int result = hook_func(target, (void*)fake_l2c_fcr_chk_chan_modes, (void**)&original_l2c_fcr_chk_chan_modes); if (result == 0) { LOGI("Successfully hooked l2c_fcr_chk_chan_modes"); return true; } else { LOGE("Failed to hook function, error: %d", result); return false; } } void on_library_loaded(const char *name, [[maybe_unused]] void *handle) { if (strstr(name, "libbluetooth_jni.so")) { LOGI("Detected Bluetooth library: %s", name); bool hooked = findAndHookFunction(name); if (!hooked) { LOGE("Failed to hook Bluetooth library function"); } } } extern "C" [[gnu::visibility("default")]] [[gnu::used]] NativeOnModuleLoaded native_init(const NativeAPIEntries* entries) { LOGI("L2C FCR Hook module initialized"); hook_func = entries->hook_func; return on_library_loaded; }