Hack de ATAG One Android App

PlayStore-AtagOneApp
De ATAG One app, gemaakt door Applied Micro Electronics AME B.V.

De ATAG One app (voor iPhone en Android) benadert rechtstreeks via het locale netwerk de ATAG One thermostaat. Atag stelt helaas geen technische documentatie online beschikbaar hoe de thermostaat aan te sturen is. De enige manier om uit te vissen hoe de app “praat” met de thermostaat, is door de app te hacken 🙂

De Android app ontleden

Het installatie pakket voor Android is een Android PacKage bestand, kortweg APK. Dit is een ge-zipt archief en daarom eenvoudig uit te pakken met unzip:

$ mv io\ cordova\ AtagUser_apkpure.com.apk io\ cordova\ AtagUser_apkpure.com.apk.zip 
$ unzip io\ cordova\ AtagUser_apkpure.com.apk.zip 
Archive:  io cordova AtagUser_apkpure.com.apk.zip
  inflating: META-INF/MANIFEST.MF    
  inflating: META-INF/ATAG_TES.SF    
  inflating: META-INF/ATAG_TES.RSA   
  inflating: AndroidManifest.xml     
  inflating: assets/_where-is-www.txt  
  inflating: assets/www/AnalyticsEvents.js  
  inflating: assets/www/Controllers/ActivateScheduleC.js  
  inflating: assets/www/Controllers/AddDeviceC.js  
  inflating: assets/www/Controllers/AdjustDayScheduleC.js  
  etc...

De App maakt gebruik van Apache Cordova. Apache Cordova is een populair framework voor het maken van mobiele applicaties. De applicatie is ontwikkeld in de talen JavaScript, HTML en CSS. Voor het uitlezen van UDP netwerk berichten, is de applicatie aangevuld met plugins die geschreven zijn in Java en rechtstreeks met het Android besturingssysteem praten.

Zoek thermostaat in netwerk

/assets/www/index.html lijkt een belangrijk bestand. Na wat gebladerd te hebben in dit bestand kom ik de volgende JavaScript voor het zoeken naar de thermostaat tegen:

cordova.exec(null, null, "UdpReceive", "close", ["close"]);
$controller('searchForOneDiv')._fire();

Het belangrijkste deel wordt blijkbaar uitgevoerd door een externe plugin met de naam UdpReceive.

Helaas bevat het Android PacKage alleen de gecompileerde Dalvik bytecode. De code is samengevoegd in het bestand classes.dex. Gelukkig zijn er tools om deze gecompileerde code weer om te zetten in enigszins leesbare code.

Eén zo’n tool is dexdump. Een andere handige tool is dex2jar. Beide tools heb ik gebruikt om de werking van de plugin te achterhalen.

$ dexdump -d classes.dex > dump.txt
$ d2j-dex2jar.sh classes.dex

dexdump geeft de volgende output:

0d51ac:                 |[0d51ac] io.cordova.AtagUser.UdpReceive.startListening:(Lorg/apache/cordova/CallbackContext;Ljava/lang/String;)V
0d51bc: 1a16 4503       |0000: const-string v22, "AtagUser" // string@0345
0d51c0: 1a17 f00e       |0002: const-string v23, "Listening" // string@0ef0
0d51c4: 7702 7412 1600  |0004: invoke-static/range {v22, v23}, Landroid/util/Log;.d:(Ljava/lang/String;Ljava/lang/String;)I // method@1274
0d51ca: 2213 7405       |0007: new-instance v19, Ljava/net/DatagramSocket; // class@0574
0d51ce: 1316 f82a       |0009: const/16 v22, #int 11000 // #2af8
0d51d2: 0800 1300       |000b: move-object/from16 v0, v19
0d51d6: 0201 1600       |000d: move/from16 v1, v22
0d51da: 7020 a024 1000  |000f: invoke-direct {v0, v1}, Ljava/net/DatagramSocket;.<init>:(I)V // method@24a0
0d51e0: 1316 0100       |0012: const/16 v22, #int 1 // #1
0d51e4: 0800 1300       |0014: move-object/from16 v0, v19
0d51e8: 0201 1600       |0016: move/from16 v1, v22
0d51ec: 6e20 a324 1000  |0018: invoke-virtual {v0, v1}, Ljava/net/DatagramSocket;.setBroadcast:(Z)V // method@24a3
0d51f2: 1316 3075       |001b: const/16 v22, #int 30000 // #7530
0d51f6: 0800 1300       |001d: move-object/from16 v0, v19
0d51fa: 0201 1600       |001f: move/from16 v1, v22
0d51fe: 6e20 a424 1000  |0021: invoke-virtual {v0, v1}, Ljava/net/DatagramSocket;.setSoTimeout:(I)V // method@24a4
0d5204: 1316 

Bovenstaande code maakt een DatagramSocket aan om naar UDP berichten op poort 11.000 te luisteren. Blijkbaar verstuurt de ATAG One berichten naar het lokale netwerk. Een andere tool met de naam Wireshark luistert naar alle berichten op het lokale netwerk. Dit gebruik ik om te zien wat voor soort berichten de thermostaat verstuurt en wat de inhoud van dit bericht is.

De thermostaat heeft IP 10.0.1.45 binnen het locale netwerk. Onderstaande Wireshark screenshot toont verkeer van en naar IP 10.0.1.45. Elke 10 seconden stuurt de thermostaat een bericht van 37 tekens, met daarin het unieke device id van de thermostaat:

Wireshark netwerk sniffer. Toon alle verkeer van en naar IP 10.0.1.43

Vind thermostaat in netwerk

De code voor het zoeken naar de thermostaat in het locale netwerk ziet er dan vervolgens in Java als volgt uit:

datagramSocket = new DatagramSocket(11000, InetAddress.getByName("0.0.0.0"));
datagramSocket.setBroadcast(true);
datagramSocket.setSoTimeout(30000);

byte[] receiveData = new byte[37];
final DatagramPacket datagramPacket = new DatagramPacket(receiveData, receiveData.length);
datagramSocket.receive(datagramPacket);

final InetAddress oneInetAddress = datagramPacket.getAddress();
final String receivedMessage = new String(datagramPacket.getData(), "UTF-8");

if (receivedMessage.startsWith("ONE ")) {
    String deviceId = receivedMessage.split(" ")[1];
    System.out.println("Found at IP: " + oneInetAddress + " device id: " + deviceId);
}

Bovenstaande code toont bij uitvoer na maximaal 10 seconden het volgende:

Found at IP: 10.0.1.45 device id: 6808-1401-3109_15-30-001-544 

Conclusie

De werking van de ATAG One thermostaat is via de Android App en met de hulp van een aantal tools eenvoudig via reverse engineering technieken te achterhalen.

Een volgende stap is onderzoeken hoe de app rechtstreeks de temperatuur van de thermostaat uitleest en instelt.

One thought on “Hack de ATAG One Android App”

Leave a Reply

Your email address will not be published.