App-bouwers opgelet! Zo automatiseerde ik de Kamergotchi-app
Naar aanleiding van de aankomende verkiezingen op 15 maart, lanceerde het satirisch televisieprogramma Zondag met Lubach de app Kamergotchi. Met dit spelletje kun je de aan jou toegewezen lijsttrekker in leven houden of juist laten sterven. Als je je Kamergotchi goed verzorgt, word je beloond met punten. Inmiddels is ook bekend dat Jesse Klaver (GroenLinks) de best verzorgde lijsttrekker in het spel is.
Ruim 750.000 mensen hebben de app gedownload. Tijd om te kijken hoe de achterkant van Kamergotchi in elkaar steekt, vonden wij. Na wat graafwerk kwamen we erachter dat het niet zo moeilijk is om de app te automatiseren.
Op maandagochtend 20 februari, de ochtend na de aankondiging van Kamergotchi, hebben we drie accounts tegelijk automatisch aangezet. Dinsdag hadden we positie 1, 2 en 3 in de dag top 50 te pakken, en klommen alle drie de accounts in de totale top 50.
In dit artikel leg ik uit hoe het reverse engineeren van een app in zijn werk gaat, hoe je zo’n app vervolgens automatiseert en wat de makers van de Kamergotchi app hadden kunnen doen (en nog steeds zouden kunnen doen) om mij tegen te houden.
Hoe je een app reverse engineert
Voordat je de functionaliteit van een app kunt automatiseren, moet je eerst precies in kaart brengen wat deze doet. Voor veel applicaties is het een kwestie van uitzoeken wat voor communicatie de app met de buitenwereld heeft. Dit contact gaat bijna altijd via internet. Wat we dus eigenlijk willen weten, is welke websites de app bezoekt als jij op een knop drukt.
Je kunt in twee stappen een app reverse engineeren.
1. Netwerkverkeer van je telefoon via een proxy sturen
De makkelijkste manier om erachter te komen welke websites de app bezoekt, is door al het netwerkverkeer van je telefoon via een proxy te sturen. Dat is een programma dat namens een gebruiker (in dit geval je telefoon) verzoeken doet naar het internet. Als je de proxy zo instelt om bij te houden wat voor netwerkverzoeken worden gedaan, kun je uitlezen wat je telefoon allemaal doet.
Een voorbeeld van een proxy-programma dat dit kan is Charles. Je kunt het programma op je desktop of laptop aanzetten. Vervolgens zet je in de netwerkinstellingen van je telefoon (bij zowel Android als iOS onder WiFi-instellingen) dat hij je desktop/laptop moet gebruiken als proxy. Vanaf dat moment zie je elk verzoek dat je telefoon naar buiten toe doet over internet verschijnen in het scherm.
Ik raad je aan dit experiment eens te doen met een aantal van je favoriete apps. Let er eens op hoeveel van deze apps data over het internet sturen terwijl je niets doet. Zie bijvoorbeeld de verzoeken van Kamergotchi naar Facebook in het screenshot hierboven.
2. Kamergotchi spelen
De volgende stap is: Kamergotchi spelen! Nu we alles zo hebben ingesteld dat we kunnen zien wat de applicatie doet, willen we weten wat elke knop in de app precíes doet.
Voordat we daarmee beginnen, eerst even een kort intermezzo over het internet. Alle verzoeken die we doen op het web (denk aan een website laden, een bericht plaatsen op social media, bestanden uploaden) hebben een ‘werkwoord’. Het internet heeft een aantal werkwoorden, maar voor nu beperken we ons tot twee daarvan: GET en POST. We gebruiken GET over het algemeen als we een webpagina willen opvragen, POST als we data willen versturen (zoals het invullen van een formulier).
Als we de app Kamergotchi openen is het eerste dat we zien een verzoek naar GET:
https://api.kamergotchi.nl/game
Dit verzoek wordt vervolgens iedere seconde herhaald. Aan de inhoud van de pagina te zien, is dit de game state. Deze pagina geeft de app informatie over hoe het spel op dat moment loopt. Interessante onderdelen daarvan zijn voor ons bijvoorbeeld careLeft en careReset.
Het onderdeel careLeft lijkt aan te geven hoe vaak we onze Kamergotchi nog eten, kennis en aandacht kunnen geven, voordat deze wil schorsen. Het andere, careReset, is de tijd waarop we weer verder kunnen spelen, nadat careLeft op is. De tijd achter claimReset lijkt aan te geven over hoeveel tijd we weer op de claim-knop kunnen klikken in het spel (die eens in de twee uur een bonus geeft).
Als we op één van de verzorg-knoppen klikken in de app, zien we een verzoek naar POST:
https://api.kamergotchi.nl/game/care
Met deze link kunnen we onze Kamergotchi dus verzorgen. Met het verzoek wordt (zoals bij een webformulier) meegegeven wat voor type verzorging we willen geven.
Als we op de claim-knop drukken, zien we (zonder extra data) een verzoek naar POST:
https://api.kamergotchi.nl/game/claim
Hoe je een app automatiseert
Nu we weten wat de Kamergotchi-app precies doet, kunnen we een eigen app maken die op dezelfde manier met de Kamergotchi-website praat.
Het belangrijke verschil met de officiële Kamergotchi-app is dat je in onze versie niet zelf hoeft te klikken om jouw lijsttrekker te verzorgen. Als ik hier ‘app’ zeg, moet je niet denken aan een polished geheel met een mooie grafische voorkant, zoals de officiële app dat is. Omdat het ons alleen gaat om punten scoren, hebben we dat niet nodig. We kunnen onze voortgang altijd volgen via de echte Kamergotchi-app.
Om zo snel mogelijk punten te verzamelen, gaan we onze versie van de Kamergotchi-app als volgt laten werken: als we starten, vragen we de huidige status van het spel op (met GET /game) en zien we hoe vaak we nog mogen verzorgen. Standaard is dit acht keer tot de volgende reset. Dan doen we zo snel mogelijk dat aantal verzoeken naar POST /game/care, waarbij we elke keer de categorie (food, attention, knowledge) kiezen die op dat moment de laagste score heeft. Zo zorgen we ervoor dat de tevredenheid van onze lijsttrekker gelijkmatig oploopt.
Elke ronde het maximaal aantal punten
Na deze verzorgingsronde wachten we aan de hand van de waarde careReset een aantal minuten (standaard zeven minuten), en beginnen we weer helemaal opnieuw. Op die manier halen we dus elke zeven minuten het maximaal aantal punten.
Elke twee uur (als de claimReset bereikt is) doen we een verzoek naar POST /game/claim om de bonuspunten te claimen.
Mocht je na deze uitleg benieuwd zijn hoe dit automatiseren nu precíes in zijn werk gaat, kun je mijn volledige script hier vinden.
De resultaten bleken vrij snel zichtbaar te zijn. Onze accounts hadden een aardige achterstand op de mensen die zondagavond al begonnen waren met spelen (met name omdat de claim ‘bonus’ afhankelijk is van het aantal dagen dat je Kamergotchi in leven is). Desondanks waren we dinsdagochtend al nummer 2, 3 en 4 in de ranglijst. Hoewel niet gelijk duidelijk, hoort het account Wouter ook bij ons – je had erbij moeten zijn. 😉
Wat later op de dag waren we zelfs nummer 1, 2 en 3, maar in de opwinding ben ik vergeten daar een screenshot van te nemen. Ons code-orange.nl-account met Tunahan Kuzu (als eerste gestart) haalde inmiddels ook al de top 20 van de totale ranglijst en klom uiteindelijk tot in de top 10.
Onze domeinnaam in de ranglijst begon ook op Twitter op te vallen.
Overigens waren we zeker niet de enigen die met een automatisch script meededen aan Kamergotchi. Aan de hoge scores in de dag top 50 te zien, waren er veel meer mensen die dit deden. Aan onze toppositie in de top 50 van de dag te zien, was ons script nét iets beter in punten verzamelen en groeide onze score dus sneller.
De shutdown
Maar, aan alles komt een eind. We hebben nooit geprobeerd om Kamergotchi voor de gek te houden en te doen alsof we wel echte spelers waren (sterker nog, in elk verzoek naar de Kamergotchi-website had ik mijn naam en e-mailadres staan).
Dinsdagavond, bijna twee volle dagen na het uitkomen van onze Kamergotchi’s, kwam deze notificatie:
Op dat moment kon ik de app nog gebruiken, maar werden mijn verzoeken naar POST /game/care geweigerd door de Kamergotchi-server. Niet veel later werden mijn verzoeken naar GET /game ook geweigerd en kon mijn app dus ook de game state niet meer ophalen. Sindsdien ziet de Kamergotchi-app er voor mij zo uit:
Na het blokkeren van onze accounts hebben we niet meer geprobeerd om de blokkade te omzeilen – de lol was eraf. Wel heb ik nog een e-mail gestuurd naar [email protected] met mijn ideeën over het tegengaan van automatisering.
Hoe je automatiseren ontmoedigt
Vóór de lancering van de app hadden de makers een aantal maatregelen kunnen nemen om automatisering te ontmoedigen. Zo hadden ze bijvoorbeeld elk verzoek naar de server kunnen signen met een digitale sleutel en de huidige tijd. Op die manier wordt het veel moeilijker om automatisch de verzoeken te doen, omdat je dan eerst de sleutel uit de app moet zien te krijgen. Met die extra moeilijkheidsgraad haken mensen zoals ik veel sneller af. Als Kamergotchi signed verzoeken had gebruikt, had ik niet de moeite gedaan om het te automatiseren. Het is maar een spelletje.
Na de lancering van de app heb je echter niets aan het advies om deze functionaliteit toe te voegen aan de server. Voordat je de oude (onbeveiligde) versie offline haalt, moet namelijk iedereen ook over naar een nieuwe versie van de app. Anders werkt de app niet meer bij mensen die niet geüpdatet hebben. Het probleem daarmee is dat voordat je een update kan doen op iOS, Apple je update eerst moet goedkeuren. Dat proces is vrij onvoorspelbaar en kan soms wel meer dan twee weken duren. Niet heel handig voor een app die minder dan een maand te gebruiken is (tot vlak voor de verkiezingen).
Om zonder update toch automatisering tegen te gaan, moet je dus een manier verzinnen die werkt binnen het huidige protocol (de manier waarop de website en de app communiceren).
Schorsingstijd verlengen
Gelukkig is het Kamergotchi-protocol daar best geschikt voor. De ‘schorsingstijd’ tussen twee rondes van verzorgen ligt namelijk niet vast, maar wordt elke keer door de website teruggegeven als we een verzoek doen naar GET /game. Door deze tijd langer te maken bij mensen die je verdenkt van automatisering, zorg je ervoor dat deze spelers minder punten kunnen scoren, omdat ze langer moeten wachten.
Dit wordt ook wel exponential backoff genoemd in de informatica. Als een speler direct na de ‘schorsingstijd’ binnen 1 seconde acht keer zijn Kamergotchi verzorgt, vermenigvuldig je de standaard zeven minuten wachttijd met een bepaalde factor, zeg 1,5. Die speler moet dan de volgende keer 10,5 minuut wachten totdat er weer verzorgd kan worden. Wordt er dan weer gelijk acht keer te eten gegeven, dan wordt de tijd nogmaals met die factor vermenigvuldigd. Dan zit die speler dus al op 15,75 minuten.
De factor laat je afhangen van hoe erg een speler geautomatiseerd lijkt te zijn (door dus bijvoorbeeld te kijken naar hoe lang er tussen ‘mogen verzorgen’ en de eerste verzorging zit). Daardoor moeten geautomatiseerde spelers veel langer wachten tot ze weer kunnen spelen en hebben echte spelers geen last van de maatregelen.
Ontmoedigen, niet voorkomen
Natuurlijk kun je, als je écht wil, dit ook omzeilen, door je script aan te passen om ‘menselijker’ voor te komen. Je kunt als app-bouwer nooit helemaal voorkomen dat iemand je app automatiseert. Uiteindelijk wil je namelijk dat je app gebruikt kan worden. Het kernwoord is dan ook ontmoedigen. Door het de zogenaamde valsspelers moeilijker te maken, hoop je dat niemand het probeert.
Helaas heb ik geen reactie gekregen op mijn mail naar Kamergotchi.