Pour ce projet, j'ai procédé à l'ingénierie inverse de l'application "C by GE" pour contrôler les ampoules intelligentes GE connectées au WiFi. Pour ce faire, j'ai commencé par décompiler l'application Android, puis j'ai procédé à une ingénierie inverse du protocole binaire que l'application utilise pour communiquer avec un serveur. Pour plus de détails, voir Reverse Engineering C par GE.
Les produits finaux de ce projet sont :
Avertissement : ce code est le résultat d'une ingénierie inverse et n'a pas été informé par une spécification de protocole. Par conséquent, rien ne garantit qu’il continuera à fonctionner ou qu’il fonctionnera pour chaque réseau ou appareil intelligent. Bien que d'autres aient utilisé avec succès cette API dans certains cas, il est possible que le code fasse des hypothèses incorrectes qui ne tiennent pas dans tous les cas d'utilisation.
Le répertoire du serveur est une application Web autonome et un point de terminaison d'API JSON pour les ampoules C by GE. Le site Web ressemble à ceci :
Si vous exécutez le site Web avec un argument -email
et -password
, le site Web affichera une page d'authentification à deux facteurs la première fois que vous le chargerez. Vous cliquerez sur un bouton et saisirez le code de vérification envoyé à votre adresse e-mail. Vous pouvez également vous connecter à l'avance en exécutant la commande login_2fa avec les indicateurs -email
et -password
définis sur les informations de votre compte. La commande vous demandera le code de vérification 2FA. Une fois que vous avez entré ce code, la commande crachera les informations de session sous forme de blob JSON. Vous pouvez ensuite transmettre ce JSON à l'argument -sessinfo
du serveur, par exemple en tant que -sessinfo 'JSON HERE'
. Notez qu'une partie de la session expire après une semaine, mais une instance de serveur en cours d'exécution continuera à fonctionner après ce délai puisque la partie expirable de la session n'est utilisée qu'une seule fois pour énumérer les appareils.
Les comptes plus récents nécessitent l'utilisation d'une authentification à deux facteurs. Vous pouvez effectuer une poignée de main 2FA pour créer une session comme ceci :
callback , err := cbyge . Login2FA ( "my_email" , "my_password" , "" )
// Handle error...
sessionInfo , err := callback ( "2FA code from email" )
// Handle error...
session , err := cbyge . NewController ( sessionInfo , 0 )
// Handle error...
Pour les comptes plus anciens qui n’ont jamais utilisé 2FA auparavant, vous pourrez peut-être vous connecter directement :
session , err := cbyge . NewControllerLogin ( "my_email" , "my_password" )
// Handle error...
Une fois que vous avez une session, vous pouvez énumérer les appareils comme suit :
devs , err := session . Devices ()
// Handle error...
for _ , x := range devs {
fmt . Println ( x . Name ())
}
Vous pouvez contrôler les ampoules comme ceci :
x := devs [ 0 ]
session . SetDeviceStatus ( x , true ) // turn on
session . SetDeviceLum ( x , 50 ) // set brightness
session . SetDeviceCT ( x , 100 ) // set color tone (100=blue, 0=orange)
Vous pouvez également interroger les paramètres actuels d'une ampoule :
status , err := session . DeviceStatus ( x )
// Handle error...
fmt . Println ( status . IsOn )
fmt . Println ( status . ColorTone )
Dans cette section, je vais vous expliquer comment j'ai procédé à la rétro-ingénierie de certaines parties du protocole C by GE.
La première étape consistait à démonter l'application Android avec Apktool. Cela produit un démontage Smali pour l'application. En fouillant, j'ai recherché des URL et des noms de domaine. Au départ, j'ai trouvé ça :
.field public static final API_VERSION : L java/lang/String ; = " v2/ "
.field public static final BASE_URL : L java/lang/String ; = " https://api-ge.xlink.cn:443/ "
Voir où ce point de terminaison d'API était utilisé m'a rapidement conduit à un ensemble d'appels HTTP basés sur JSON pour me connecter, répertorier les appareils, etc. Cependant, ce point de terminaison ne semble pas fournir un moyen 1) d'obtenir l'état des appareils, ou 2) mettre à jour la couleur ou la luminosité des appareils.
Il devait y avoir un autre moyen pour l'application de communiquer avec les ampoules intelligentes. Cependant, le démontage était truffé de code pour la communication Bluetooth et LAN, et j'étais un peu inquiet qu'il n'y ait pas de point final API global pour contrôler les ampoules. Pire encore, l'application C by GE se plaignait chaque fois que je désactivais Bluetooth et que j'essayais de l'utiliser. Cependant, j'ai finalement découvert que je pouvais ouvrir l'application, la laisser faire son travail, puis désactiver le Bluetooth et le WiFi tout en gardant le contrôle des ampoules. Tout ce que j'avais à faire était d'appuyer sur le bouton "retour" d'Android chaque fois que l'application ouvrait une fenêtre contextuelle me demandant "Activer le suivi de localisation" (un nom étrange pour Bluetooth et WiFi, remarquez).
À ce stade, j'étais presque sûr que l'application n'établissait pas d'autres connexions HTTP(S) mystérieuses. Fait intéressant, cependant, j'ai trouvé le domaine « xlink.cn » ailleurs dans le code Smali :
.field public static final CM_SERVER_ADDRESS : L java/lang/String ; = " cm-ge.xlink.cn "
.field public static final CM_SERVER_PORT : I = 0x5ce2
Sainte vache, cela pourrait-il être un protocole brut basé sur des sockets ? J'ai essayé, et bien sûr, j'ai pu ouvrir une connexion TCP à cm-ge.xlink.cn:23778
. Cependant, le Smali était également truffé de logique pour les paquets UDP , donc je ne savais pas quel protocole l'application utiliserait. Dans cet esprit, j'ai créé un proxy de paquets et l'ai configuré en écoute sur le port 23778. Ensuite, j'ai remplacé le domaine cm-ge.xlink.cn
par mon adresse IP dans le code Smali, j'ai recompilé l'application en APK et je l'ai installée sur mon téléphone.
Effectivement, mon application C by GE corrigée s'est immédiatement connectée à mon instance de proxy de paquets et a commencé à discuter. Notamment, cela ne l’a fait que lorsque Bluetooth et WiFi étaient désactivés. Sinon, il semblait en préférer un pour communiquer localement avec les ampoules intelligentes.
Le protocole que l'application a choisi d'utiliser était de loin le résultat le plus simple à gérer : 1) il s'agissait de TCP plutôt que d'UDP, 2) il était totalement non crypté. L'absence de cryptage est plutôt alarmante avec le recul, puisque le premier message comprend un jeton d'autorisation qui ne semble jamais changer pour mon compte.
J'ai trouvé que les messages de l'application vers le serveur pouvaient être "rejoués" efficacement. Une fois que j'ai compris quels octets (ou "paquets", grâce au proxy de paquets) étaient destinés à allumer et éteindre les lumières, je pouvais simplement ouvrir un nouveau socket et envoyer ces mêmes paquets et obtenir les mêmes résultats. C'était un bon signe. Dans le pire des cas, j'avais déjà une manière de mettre en œuvre ce que je voulais pour moi-même, même si ce n'était pas très général.
À ce stade, il était temps d’approfondir le protocole. Après une combinaison d'expérimentations avec le proxy de paquets et de fouilles dans le démontage de Smali, j'ai eu une compréhension assez générale de la nature de la communication. La première chose que j'ai remarquée, c'est que la communication se faisait sous forme de "messages", qui commençaient par un champ de type et un champ de longueur (en big endian). La prochaine étape consistait à déterminer quels types de paquets, où, et finalement comment les paquets spécifiques eux-mêmes étaient codés. Voici un exemple de paquet du serveur contenant les statuts de mes trois appareils :
73 00 00 00 60 47 e2 be ab 00 37 00 7e 00 01 00 00 f9 52 4e
00 03 00 00 00 03 00 03 00 81 01 00 00 81 01 00 00 00 00 35
00 00 00 27 00 00 00 00 00 00 00 02 00 00 01 00 00 00 01 00
00 00 00 35 00 00 00 27 00 00 00 00 00 00 00 01 00 00 01 00
00 00 01 00 00 00 00 35 00 00 00 27 00 00 00 00 00 00 00 c8
7e
Une fois que j’en ai eu assez du protocole élaboré, j’ai créé une API pour celui-ci. Cette API peut répertorier les appareils, obtenir leurs statuts et mettre à jour diverses propriétés des appareils (par exemple, luminosité et tonalité de couleur). Étonnamment, j’ai trouvé mon API beaucoup plus rapide et fiable que l’application elle-même. Il semble qu'essayer d'utiliser Bluetooth ou WiFi avant de recourir à un serveur distant rend l'application beaucoup plus fragile et moins fiable qu'elle ne pourrait l'être.
Pour terminer, je ne possède pas tous les appareils pris en charge par cette application, je n'étais donc pas motivé (ou facilement capable) de procéder à une ingénierie inverse du fonctionnement de ces appareils. Par exemple, la même entreprise produit des prises intelligentes, des capteurs et des interrupteurs.