Een schokhalsband gebruiken om doomscrolling te stoppen

Ik droom van een gezond dieet wat betreft sociale media (en meer zelfbeheersing). Instagram staat vol met geweldige makers die me motiveren en Facebook heeft interessante dingen op de Marketplace. De gebruikelijke wijsheid zou suggereren om de verleiding volledig weg te nemen door deze apps te verwijderen, maar ik vind ze marginaal nuttig voor werk en het kopen van meubels. Daarom wil ik liever mijn consumptie verminderen.

Eerdere pogingen tot zelfbeheersing

Dit is niets nieuws; in de loop der jaren heb ik verschillende methoden geprobeerd:

  • Het instellen van tijdslimieten voor mijn apps.
  • Het verplaatsen van app-iconen om de reflex om ze aan te raken te doorbreken.
  • Het gebruik van toegankelijkheidsinstellingen om de kleuren om te keren, in de hoop dat het minder aantrekkelijk zou worden (een methode die Meta inmiddels voor de rechter moet verdedigen).

Onlangs had ik succes met een vorm van gedragsmodificatie. Ik besloot dat het simpelweg uitschakelen van de app niet genoeg was, omdat ik deze gemakkelijk weer zou inschakelen voor "slechts 5 minuten" noodzakelijk gebruik. Ik had een motiverende factor nodig. Met behulp van MacroDroid stelde ik een macro in die een afbeelding van een verdrietige mier opende telkens wanneer ik een bepaalde zwarte lijst van apps startte. Dit was een poging om mezelf te laten zien wat er zou gebeuren als ik al mijn vrije energie besteedde aan sociale media: ik zou een verdrietige, arme mier worden.

Maar zoals dat gaat, moest ik de macro op een dag uitschakelen en heb ik hem nooit meer geactiveerd. Sisyphus gleed opnieuw het feed terug. Ik concludeerde dat ik simpelweg niet genoeg bang was om een verdrietige mier te zijn. Ik had iets nodig als een klassiek geval van operante conditionering.

De overstap naar operante conditionering

Geïnspireerd door cartoons uit de late jaren '90 en vroege jaren 2000 wist ik dat er een oplossing bestond: hondentrainingshalsbanden. Conceptueel ben ik er totaal tegen en geloof ik dat pijn alleen gedeeld moet worden met jezelf of tussen twee instemmende volwassenen. Echter, ze voldeden precies aan mijn technische eisen.

Ik deed onderzoek en ontdekte dat moderne apparaten meestal in twee varianten komen: radio-gestuurd op het 433 MHz-spectrum of Bluetooth-gestuurd, beide via een afstandsbediening. Omdat veel advertenties dit niet specificeren, besteedde ik tijd aan FCC ID om generieke productnamen te koppelen aan apparaten, in de hoop een ideale module te vinden. Een trigger via een WiFi-knopdrukker gekoppeld aan de afstandsbediening zou te onhandig zijn en zou geen fijnmazige controle mogelijk maken. Als snelle test kocht ik een 433 MHz-model bij een lokale leverancier, maar dit bleek de "Fisher-Price-oven" onder de halsbanden; ik had waarschijnlijk meer trilling ervaren door een oude Nokia tegen mijn gezicht te drukken.

Tegelijkertijd herontdekte ik de vreugden van Alibaba. Met een eenvoudige zoekopdracht vond ik wat ik zocht: een halsband genaamd de S900. Deze beschikte over Bluetooth-appbediening (een zeldzamere derde variant) en beloofde een "dagboek" (wat waarschijnlijk een handig spoor is voor een audit door de dierenbescherming). Ik kocht een sample en deze arriveerde. De kwaliteit was veel beter dan de vorige testband; goed genoeg dat een hond er niet op zou stikken.

Inhoud van de doos:

  • Eén trainingshalsband.
  • 4 metalen contactpunten (twee lange en twee korte).
  • Een USB-C oplaadkabel.
  • Een kleine gloeilamp-tester om te controleren of de schokfunctie werkt.
  • Een "uitnodigingskaart" met de beveiligingscode P2026.

Reverse engineering van het protocol

De volgende vraag was: hoe krijg ik de halsband te reageren op het openen van een applicatie? Omdat ik onlangs met CTF's (Capture The Flag) bezig was, was mijn eerste impuls om de Android-applicatie te decompileren, maar ik besefte dat dit het proces onnodig zou compliceren.

Ik opende de Pet Diary app (met de tagline "Pet and Human Joy, Always"). Ik maakte een gastaccount aan met een fictief e-mailadres en gebruikte de uitnodigingscode P2026. De app vond de halsband automatisch en toonde informatie zoals het batterijniveau, het dagboek, automatische blafmonitoring en een handmatige trainingsmodus. Dat laatste was precies wat ik zocht. In tegenstelling tot goedkopere halsbanden had deze drie soorten "versterking": geluid, trilling en statische schokken. Elke modus had een intensiteitsregelaar met een handmatige trigger.

Omdat de app op Android draait, kon ik binnen tien seconden Bluetooth HCI-sniffing inschakelen. De commando's die op OS-niveau worden verzonden, zouden me precies laten zien wat er gebeurde. Om de interpretatie te vergemakkelijken, bedacht ik een test: ik voerde elk commando uit over het volledige bereik:

  • Geluid: niveau 1-8.
  • Trilling: niveau 1-8.
  • Statische schok: niveau 1-16.

Ik verzond deze in volgorde, maakte een ADB-bugreport, unzipte dit, zocht naar de Bluetooth-logs en importeerde deze in Wireshark.

Analyse van de datapakketten

Hieronder volgen de write-requests die door de applicatie werden verzonden:

Sound Commands 407 34.979279 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:07:00:18:85:1e:00:06:03:01:00:00:01:00:00:00:05 411 41.236774 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:08:00:18:85:1e:00:06:03:02:00:00:01:00:00:00:06

Vibration Commands 443 67.865725 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:0f:00:18:85:1e:00:06:03:00:01:00:01:00:00:00:05 447 71.483347 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:10:00:18:85:1e:00:06:03:00:02:00:01:00:00:00:06

Static Commands 512 93.757535 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:17:00:18:85:1e:00:06:03:00:00:01:01:00:00:00:05 516 96.702175 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:18:00:18:85:1e:00:06:03:00:00:02:01:00:00:00:06 ... 554 118.667453 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:21:00:18:85:1e:00:06:03:00:00:0b:01:00:00:00:0f 560 120.874215 Googleb4:9b:d9 (Pixel 7a) Espressif34:27:f2 () ATT 40 Sent Write Request, Handle: 0x001c (Unknown: Unknown) 55:55:55:39:41:42:43:61:62:63:0a:00:22:00:18:85:1e:00:06:03:00:00:0c:01:00:00:00:08

Door het statische begin van het pakket (55:55:55:39:41:42:43:61:62:63:0a:00:) te verwijderen, kon ik het volgende analyseren:

  • Byte 12 (uitgaande van index 0) startte bij 7 en liep op; dit was een interne tracker van de app voor verzonden gebeurtenissen.
  • Byte 20 in de geluidssectie correspondeerde met het gekozen niveau.
  • Byte 21 deed hetzelfde voor trillingen.
  • Byte 22 deed hetzelfde voor statische schokken.

Het nauwkeurig testen van elke sequentie betaalde zich uit. Omdat de statische schokken tot 16 gaan, verandert de hex-waarde vanaf 10 naar 0a, 0b, etc. De laatste byte leek op te lopen, maar bleek dat niet altijd het geval was.

Met hulp van Claude Opus 5 werd vastgesteld dat de laatste byte een eenvoudige XOR-checksum was. De halsband berekent bij ontvangst dezelfde XOR-waarde om te controleren of het frame geldig is, wat waarschijnlijk bedoeld is om te voorkomen dat een corrupt pakket de drager onbedoeld shocks geeft.

Een ander belangrijk inzicht was dat er geen pairing nodig was. De Bluetooth-logs onthulden dat men simpelweg on-demand verbinding kan maken met het apparaat; pogingen om een formele binding (bond) aan te gaan veroorzaakten alleen maar problemen.

De implementatie

Met deze informatie was het proces simpel: het apparaat ontdekken, verbinden en een geldig pakket sturen met de juiste byte-positie.

Ik verpakte de Bluetooth-code in een Python-webserver die op mijn laptop draaide en maakte endpoints aan voor elk commando in het formaat: http://0.0.0.0:42069/shock?level=2. In MacroDroid verving ik de actie van het tonen van de verdrietige mier door een "Get Request" naar dit endpoint. Tijdens het testen merkte ik dat de verbinding soms wegviel of dat het apparaat in "slaapstand" ging; ik ontdekte dat het verzenden van 00-bytes op de plek van de instellingen werd gezien als een geldig pakket, waardoor de verbinding actief bleef zonder de halsband te activeren.

De resultaten

Het was tijd voor een echte test. Ik bevestigde de halsband aan mijn been, omdat ik uit televisieseries weet dat het een slecht idee is om hem om je nek te dragen. Vervolgens ging ik aan het werk, met de halsband om mijn been en mijn telefoon binnen handbereik.

Werkte het? Iets té goed. Ik had het niveau op 2 gezet en ik gooide mijn telefoon bijna tegen de muur toen hij afging. Dit gebeurde twee keer. De pijn was vergelijkbaar met een kramp, maar dan met een veel suddenere onset. Ik vermoed dat niveau 16 me volledig zou resetten naar fabrieksinstellingen.

Ik was nu te bang om mijn telefoon te gebruiken, maar ook te bang om de halsband te blijven gebruiken. Als initiële onderzoeksproject werkte het, maar ik zal moeten overstappen op de instelling voor irritante, hoge pieptonen in plaats van de "misbruik"-instellingen. Ik laat het jullie weten als ik een middenweg vind tussen deze extremen.

Toekomstige verbeteringen

  • Dedicated server: Het hosten op een aparte server (misschien een ESP32, waar de halsband zelf ook op draait), zodat de status van mijn laptop de loop niet beïnvloedt.
  • Robuuste authenticatie: Ik heb een token op de server gezet als basis-auth, maar ik zou me prettiger voelen met iets robuusters. Gezien de tijd dat mijn katten op mijn toetsenbord lopen, kan ik niet uitsluiten dat een van hen in staat is tot portmapping of domain enumeration.
  • Comfortabelere bevestiging: Een halsband die niet losjes bungelt als een ongestrikte schoen. Ik zou een beugel kunnen 3D-printen die om de metalen pinnen past, zodat deze strakker tegen mijn been zit.
  • Integratie: Dit omzetten naar buttplug.io voor masochisten.