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
|
#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);
|
||||||
|
|||||||
Reference in new issue
Block a user