feat(bt): intercept LE_SET_SCAN_PARAM via shadow vtable to inject random address on the fly
This commit is contained in:
1 parent
88c0dccd79
commit
820154e54b
1 file changed
+108
@@ -9,6 +9,88 @@
|
||||
#define super 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) {
|
||||
if (!super::init(dictionary)) return false;
|
||||
fDevice = nullptr;
|
||||
@@ -406,6 +488,23 @@ bool QualcommWireless::start(IOService *provider) {
|
||||
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
|
||||
// trouve le contrôleur dans un état parfaitement calme et stabilisé au démarrage.
|
||||
IOSleep(150);
|
||||
@@ -421,6 +520,15 @@ bool QualcommWireless::start(IOService *provider) {
|
||||
void QualcommWireless::stop(IOService *provider) {
|
||||
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->isOpen(this)) {
|
||||
fInterface->close(this);
|
||||
|
||||
Reference in new issue
Block a user