feat(bt): intercept LE_SET_SCAN_PARAM via shadow vtable to inject random address on the fly

This commit is contained in:
nawrhehe committed 2026-10-15 23:00:43 +02:00
1 parent 88c0dccd79
commit 820154e54b
1 file changed
+108
+108
View File
@@ -9,6 +9,88 @@
#define super IOService #define super IOService
OSDefineMetaClassAndStructors(QualcommWireless, IOService) OSDefineMetaClassAndStructors(QualcommWireless, IOService)
struct HciCommandHdr {
uint16_t opcode;
uint8_t len;
} __attribute__((packed));
#define MAX_HCI_BUF_LEN 255
#define HCI_OP_RESET 0x0c03
#define HCI_OP_LE_SET_SCAN_PARAM 0x200B
#define HCI_OP_LE_SET_SCAN_ENABLE 0x200C
static const uint8_t sRandomAddressHci[9] = {0x05, 0x20, 0x06, 0x48, 0x1B, 0x7F, 0x3C, 0xF2, 0xEC};
typedef IOReturn (*DeviceRequestInternalFn)(IOUSBHostDevice *that, IOService *forClient, StandardUSB::DeviceRequest &request, void *rawBuffer, IOMemoryDescriptor *descriptorBuffer, uint32_t &bytesTransferred, IOUSBHostCompletion *completion, uint32_t completionTimeoutMs);
static DeviceRequestInternalFn sOrigDeviceRequest = nullptr;
static void **sOrigVtablePtr = nullptr;
static bool sRandomAddressInit = false;
static void *sShadowVtable[512] = {0};
static size_t sDeviceRequestVtableIndex = 0;
struct DeviceRequestAccessor : public IOUSBHostDevice {
static size_t getVtableIndex() {
typedef IOReturn (IOUSBHostDevice::*Fn)(IOService*, StandardUSB::DeviceRequest&, void*, IOMemoryDescriptor*, uint32_t&, IOUSBHostCompletion*, uint32_t);
Fn fn = &DeviceRequestAccessor::deviceRequest;
uintptr_t ptrVal = *(uintptr_t *)&fn;
if (ptrVal & 1) {
return (ptrVal - 1) / sizeof(void *);
}
return 0;
}
};
static IOReturn hookDeviceRequest(IOUSBHostDevice *that, IOService *forClient, StandardUSB::DeviceRequest &request, void *rawBuffer, IOMemoryDescriptor *descriptorBuffer, uint32_t &bytesTransferred, IOUSBHostCompletion *completion, uint32_t completionTimeoutMs) {
HciCommandHdr *hdr = nullptr;
char hciBuf[MAX_HCI_BUF_LEN] = {0};
if (rawBuffer == nullptr) {
if (descriptorBuffer != nullptr) {
descriptorBuffer->prepare(kIODirectionOut);
if (descriptorBuffer->getLength() > 0) {
descriptorBuffer->readBytes(0, hciBuf, (descriptorBuffer->getLength() < MAX_HCI_BUF_LEN) ? descriptorBuffer->getLength() : MAX_HCI_BUF_LEN);
}
descriptorBuffer->complete(kIODirectionOut);
}
hdr = (HciCommandHdr *)hciBuf;
} else {
hdr = (HciCommandHdr *)rawBuffer;
}
if (hdr) {
if (hdr->opcode == HCI_OP_RESET) {
sRandomAddressInit = false;
} else if (hdr->opcode == HCI_OP_LE_SET_SCAN_PARAM) {
if (!sRandomAddressInit && sOrigDeviceRequest) {
StandardUSB::DeviceRequest randReq;
randReq.bmRequestType = makeDeviceRequestbmRequestType(kRequestDirectionOut, kRequestTypeClass, kRequestRecipientInterface);
randReq.bRequest = 0;
randReq.wIndex = 0;
randReq.wValue = 0;
randReq.wLength = sizeof(sRandomAddressHci);
uint32_t randLen = sizeof(sRandomAddressHci);
IOBufferMemoryDescriptor *randDesc = IOBufferMemoryDescriptor::withBytes(sRandomAddressHci, sizeof(sRandomAddressHci), kIODirectionOut);
if (randDesc) {
randDesc->prepare(kIODirectionOut);
sOrigDeviceRequest(that, forClient, randReq, nullptr, randDesc, randLen, nullptr, 2000);
randDesc->complete(kIODirectionOut);
randDesc->release();
}
sRandomAddressInit = true;
IOLog("QualcommWireless: [PATCH] Intercepte LE_SET_SCAN_PARAM -> adresse aleatoire LE injectee au vol !\n");
}
}
}
if (!sOrigDeviceRequest) {
return kIOReturnNotReady;
}
return sOrigDeviceRequest(that, forClient, request, rawBuffer, descriptorBuffer, bytesTransferred, completion, completionTimeoutMs);
}
bool QualcommWireless::init(OSDictionary *dictionary) { bool QualcommWireless::init(OSDictionary *dictionary) {
if (!super::init(dictionary)) return false; if (!super::init(dictionary)) return false;
fDevice = nullptr; fDevice = nullptr;
@@ -406,6 +488,23 @@ bool QualcommWireless::start(IOService *provider) {
OSSafeReleaseNULL(intfIter); OSSafeReleaseNULL(intfIter);
} }
// 4. Installation du hook shadow vtable sur fDevice pour intercepter les requêtes HCI au vol (ex: scan LE)
sDeviceRequestVtableIndex = DeviceRequestAccessor::getVtableIndex();
if (sDeviceRequestVtableIndex > 0 && sDeviceRequestVtableIndex < 512) {
void **origVtable = *(void ***)fDevice;
if (origVtable && !sOrigDeviceRequest) {
memcpy(sShadowVtable, origVtable, sizeof(sShadowVtable));
sOrigVtablePtr = origVtable;
sOrigDeviceRequest = (DeviceRequestInternalFn)origVtable[sDeviceRequestVtableIndex];
sShadowVtable[sDeviceRequestVtableIndex] = (void *)hookDeviceRequest;
*(void ***)fDevice = sShadowVtable;
IOLog("QualcommWireless: Hook shadow vtable installe avec succes (index=%zu, fn=%p) !\n",
sDeviceRequestVtableIndex, (void *)sOrigDeviceRequest);
}
} else {
IOLog("QualcommWireless: Index vtable invalide (%zu).\n", sDeviceRequestVtableIndex);
}
// Délai de 150ms avant de relâcher l'accès exclusif pour garantir que bluetoothd // Délai de 150ms avant de relâcher l'accès exclusif pour garantir que bluetoothd
// trouve le contrôleur dans un état parfaitement calme et stabilisé au démarrage. // trouve le contrôleur dans un état parfaitement calme et stabilisé au démarrage.
IOSleep(150); IOSleep(150);
@@ -421,6 +520,15 @@ bool QualcommWireless::start(IOService *provider) {
void QualcommWireless::stop(IOService *provider) { void QualcommWireless::stop(IOService *provider) {
IOLog("QualcommWireless: Arret du service.\n"); IOLog("QualcommWireless: Arret du service.\n");
if (fDevice && sOrigVtablePtr) {
void **currentVtable = *(void ***)fDevice;
if (currentVtable == sShadowVtable) {
*(void ***)fDevice = sOrigVtablePtr;
sOrigVtablePtr = nullptr;
sOrigDeviceRequest = nullptr;
}
}
if (fInterface) { if (fInterface) {
if (fInterface->isOpen(this)) { if (fInterface->isOpen(this)) {
fInterface->close(this); fInterface->close(this);