Piratage automatique du bus CAN pour le contrôle vocal



Une voiture moderne n'est pas seulement un moyen de transport, mais aussi un gadget avancé avec des fonctions multimédias et un système de contrôle électronique pour les unités et un tas de capteurs. De nombreux constructeurs automobiles offrent les fonctions d'assistant de mouvement, d'aide au stationnement, de surveillance et de contrôle de voiture à partir d'un téléphone. Cela est possible grâce à l'utilisation d'un bus CAN dans la voiture à laquelle tous les systèmes sont connectés: moteur, système de freinage, volant, multimédia, climat, etc.

Ma voiture est la Skoda Octavia 2011. Il n'offre pas de capacités de contrôle à partir du téléphone, j'ai donc décidé de corriger cet inconvénient, et en même temps d'ajouter la fonction de contrôle vocal. En tant que passerelle entre le bus CAN et le téléphone, j'utilise le Raspberry Pi avec le blindage CAN BUS et le routeur WiFi TP-Link. Le protocole de communication des agrégats automatiques est fermé, et Volkswagen a répondu à toutes mes lettres avec la documentation du protocole. Par conséquent, la seule façon de savoir comment les appareils communiquent dans les voitures et d'apprendre à les gérer est l'ingénierie inverse du protocole CAN du bus VW.

J'ai agi par étapes:

  1. Développement du bouclier CAN pour Raspberry Pi
  2. Installation d'un logiciel pour travailler avec le bus CAN
  3. Connexion au bus CAN d'une voiture
  4. Développement d'un sniffer et étude du protocole du bus CAN
  5. Développement d'applications téléphoniques
  6. Contrôle vocal avec Homekit et Siri

À la fin de la fenêtre de contrôle vocal vidéo.

Développement du bouclier CAN pour Raspberry Pi


Ici, le schéma de blindage a été pris par lnxpps.de/rpie , il y a aussi une description des conclusions, 2 microcircuits MCP2515 et MCP2551 sont utilisés pour communiquer avec CAN. 2 fils CAN-High et CAN-Low sont connectés au blindage. Dans SprintLayout 6, je déploie la planche, quelqu'un peut-il venir à portée de main CANBoardRPi.lay (dans la photo de titre le prototype du bouclier sur la planche à pain).





Installation d'un logiciel pour travailler avec le bus CAN


Sur Raspbian âgé de 2 ans, j'avais besoin de corriger bcm2708.c pour ajouter la prise en charge CAN (ce n'est peut-être pas nécessaire maintenant). Pour travailler avec le bus CAN, vous devez installer le package d'utilitaires can-utils à partir de github.com/linux-can/can-utils , puis charger les modules et soulever l'interface can:

# initialize
insmod spi-bcm2708
insmod can
insmod can-dev
insmod can-raw
insmod can-bcm
insmod mcp251x
# Maerklin Gleisbox (60112 and 60113) uses 250000
# loopback mode for testing
ip link set can0 type can bitrate 125000 loopback on
ifconfig can0 up

Nous vérifions que l'interface CAN a augmenté avec la commande ifconfig :



Vous pouvez vérifier que tout fonctionne en envoyant et en recevant la commande.

Dans un terminal, nous écoutons:

root@raspberrypi ~ # candump any,0:0,#FFFFFFFF

Dans un autre terminal, nous envoyons:

root@raspberrypi ~ # cansend can0 123#deadbeef

Un processus d'installation plus détaillé est décrit ici lnxpps.de/rpie .

Connexion au bus CAN d'une voiture


Après une petite étude de la documentation ouverte sur le bus VW CAN, j'ai découvert que j'utilisais 2 bus.

Le bus CAN de l'unité de puissance , transmettant des données à une vitesse de 500 kbit / s, relie toutes les unités de contrôle desservant cette unité.

Par exemple, les appareils suivants peuvent être connectés au bus CAN d'une unité d'alimentation:

  • unité de contrôle moteur
  • Unité de commande ABS
  • calculateur de stabilisation de cap,
  • unité de commande de boîte de vitesses,
  • unité de commande d'airbag,
  • combiné d'instruments.

Le bus CAN du système Comfort et du système de commande d' information , qui permet de transférer des données à une vitesse de 100 kbit / s entre les unités de contrôle desservant ces systèmes.

Par exemple, les
appareils suivants peuvent être connectés au bus CAN du système Comfort et au système d'information <commande :

  • Unité de commande Climatronic ou système de climatisation,
  • boîtiers de commande dans les portières,
  • Unité de commande du système de confort,
  • unité de commande avec affichage pour radio et système de navigation.

En ayant accès au premier, vous pouvez contrôler le trafic (dans ma version sur la mécanique, vous pouvez au moins contrôler le régulateur de vitesse), en ayant accès au second, vous pouvez contrôler la radio, la climatisation, le verrouillage central, les vitres électriques, les phares, etc.

Les deux bus sont connectés via la passerelle, qui est située dans la zone sous le volant, le connecteur de diagnostic OBD2 est également connecté à la passerelle, malheureusement le trafic des deux bus ne peut pas être entendu via le connecteur OBD2, vous pouvez uniquement envoyer une commande et demander un statut. J'ai décidé que je ne travaillerais qu'avec le bus Comfort, et le connecteur dans la porte du conducteur s'est avéré être l'endroit le plus pratique pour se connecter au bus.



Maintenant, je peux écouter tout ce qui se passe sur le Comfort CAN et envoyer des commandes.

Développement d'un sniffer et étude du protocole du bus CAN




Après avoir eu accès à l'écoute du bus CAN, je dois déchiffrer qui passe à qui et quoi. Le format de paquet CAN est illustré dans la figure.



Tous les utilitaires de l'ensemble can-utils sont capables d'analyser les paquets CAN et de ne fournir que des informations utiles, à savoir:

  • Identifiant
  • Longueur des données
  • Les données

Les données sont transmises sous une forme non cryptée, ce qui a facilité l'étude du protocole. Sur Raspberry Pi, j'ai écrit un petit serveur qui redirige les données de candump vers TCP / IP pour analyser le flux de données sur l'ordinateur et l'afficher magnifiquement.

Pour macOS, j'ai écrit une application simple qui pour chaque adresse d'appareil ajoute une cellule à la tablette et dans cette cellule je peux déjà voir quelles données changent.



J'appuie sur le bouton de la fenêtre d'alimentation, j'ai trouvé une cellule dans laquelle les données changent, puis j'ai déterminé quelles commandes correspondent à appuyer vers le bas, appuyer vers le haut, maintenir vers le haut, maintenir vers le bas.

Vous pouvez vérifier que la commande fonctionne en l'envoyant depuis le terminal, par exemple, la commande pour relever la vitre gauche:

cansend can0 181#0200

Équipes qui transmettent des appareils via le bus CAN dans des voitures VAG (Skoda Octavia 2011), reçues par rétro-ingénierie:

// Front Left Glass Up
181#0200
// Front Left Glass Down
181#0800
// Front Right Glass Up
181#2000
// Front Right Glass Down
181#8000
// Back Left Glass Up
181#0002
// Back Left Glass Down
181#0008
// Back Right Glass Up
181#0020
// Back Right Glass Down
181#0080
// Central Lock Open
291#09AA020000
// Central Lock Close
291#0955040000
// Update Light status of central lock (   /          ,       ,    )
291#0900000000

J'étais trop paresseux pour étudier tous les autres appareils, donc dans cette liste, seulement ce qui m'intéressait.

Développement d'applications téléphoniques


En utilisant les commandes reçues, j'ai écrit une application pour l'iPhone qui ouvre / ferme les fenêtres et contrôle le verrouillage central.

Sur Raspberry Pi, j'ai lancé 2 petits serveurs, le premier envoie des données de candump vers TCP / IP, le second reçoit des commandes de l'iPhone et les envoie cansend.


Sources d'application de contrôle automatique pour iOS
//
//  FirstViewController.m
//  Car Control
//
//  Created by Vitaliy Yurkin on 17.05.15.
//  Copyright (c) 2015 Vitaliy Yurkin. All rights reserved.
//

#import "FirstViewController.h"
#import "DataConnection.h"
#import "CommandConnection.h"

@interface FirstViewController () <DataConnectionDelegate>
@property (nonatomic, strong) DataConnection *dataConnection;
@property (nonatomic, strong) CommandConnection *commandConnection;
@property (weak, nonatomic) IBOutlet UILabel *Door_1;
@property (weak, nonatomic) IBOutlet UILabel *Door_2;
@property (weak, nonatomic) IBOutlet UILabel *Door_3;
@property (weak, nonatomic) IBOutlet UILabel *Door_4;
@property (weak, nonatomic) IBOutlet UIButton *CentralLock;
- (IBAction)lockUnlock:(UIButton *)sender;
@end

@implementation FirstViewController

- (void)viewDidLoad {
    self.dataConnection = [DataConnection new];
    self.dataConnection.delegate = self;
    [self.dataConnection connectToCanBus];
    
    self.commandConnection = [CommandConnection new];
    [self.commandConnection connectToCanBus];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)doorStatusChanged:(char)value {
    /*
     1 - Front Left Door
     2 - Front Right Door
     4 - Back Left Door
     8 - Back Right Door
     
     3 - Front Left&Right Door = 1 + 3
     5 - Front& Back left Door = 1 + 4
     */
    
    // Front Left Door
    if (value & 1) {
        self.Door_1.backgroundColor = [UIColor yellowColor];
        self.Door_1.text = @"";
        NSLog(@"1");
    }
    else {
        self.Door_1.backgroundColor = [UIColor lightGrayColor];
        self.Door_1.text = @"";
    }
    
    // Front Right Door
    if (value & 2) {
        self.Door_2.backgroundColor = [UIColor yellowColor];
        self.Door_2.text = @"";
        NSLog(@"2");
    }
    else {
        self.Door_2.backgroundColor = [UIColor lightGrayColor];
        self.Door_2.text = @"";
    }
    
    // Back Left Door
    if (value & 4) {
        self.Door_3.backgroundColor = [UIColor yellowColor];
        self.Door_3.text = @"";
        NSLog(@"4");
    }
    else {
        self.Door_3.backgroundColor = [UIColor lightGrayColor];
        self.Door_3.text = @"";
    }
    
    // Back Right Door
    if (value & 8) {
        self.Door_4.backgroundColor = [UIColor yellowColor];
        self.Door_4.text = @"";
        NSLog(@"8");
    }
    else {
        self.Door_4.backgroundColor = [UIColor lightGrayColor];
        self.Door_4.text = @"";
    }
}

BOOL firstStatusChange = YES;
BOOL lastStatus;

-(void) centralLockStatusChanged:(BOOL)status {
    // At first status changes set lastStatus variable
    if (firstStatusChange) {
        firstStatusChange = NO;
        // Invert status, to pass the next test
        lastStatus = !status;
    }
    
    // Change Lock image only if status changed
    if (!(lastStatus == status)) {
        // Check status
        if (status) {
            [self.CentralLock setBackgroundImage:[UIImage imageNamed:@"lock_close"] forState:UIControlStateNormal];
        }
        else {
            [self.CentralLock setBackgroundImage:[UIImage imageNamed:@"lock_open"] forState:UIControlStateNormal];
        }
        lastStatus = status;
    }
}


// Front Left Glass
- (IBAction)frontLeftUp:(UIButton *)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#0200"];
}
- (IBAction)frontLeftDown:(id)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#0800"];
}

// Front Right Glass
- (IBAction)frontRightUp:(UIButton *)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#2000"];
}
- (IBAction)frontRightDown:(id)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#8000"];
}

// Back Left Glass
- (IBAction)backLeftUp:(UIButton *)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#0002"];
}
- (IBAction)backLeftDown:(id)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#0008"];
}

// Back Right Glass
- (IBAction)backRightUp:(UIButton *)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#0020"];
}
- (IBAction)backtRightDown:(id)sender {
    [self.commandConnection sendMessage:@"cansend can0 181#0080"];
}

- (IBAction)lockUnlock:(UIButton *)sender {
    // If central lock closed
    if (lastStatus) {
        // Open
        [self.commandConnection sendMessage:@"cansend can0 291#09AA020000"];

        int64_t delayInSeconds = 1; // 1 sec
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [self.commandConnection sendMessage:@"cansend can0 291#0900000000"];
        });
        
    }
    else {
        // Close
        [self.commandConnection sendMessage:@"cansend can0 291#0955040000"];
        int64_t delayInSeconds = 1; // 1 sec
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
        dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
            [self.commandConnection sendMessage:@"cansend can0 291#0900000000"];
        });
    }
    
}
@end


Il existe un moyen de ne pas écrire votre application pour le téléphone, mais pour tirer parti du ready-made du monde des maisons intelligentes, il vous suffit d'installer le système d'automatisation Z-Way sur la commande Raspberry Pi :

wget -q -O - razberry.z-wave.me/install | sudo bash

Après cela, nous ajoutons nos appareils CAN au système d'automatisation Z-Way


et nous contrôlons le


lève- vitre comme un interrupteur normal: Applications mobiles pour Z-Way: ZWay Home Control et ZWay Control.

Contrôle vocal avec Homekit et Siri


Dans un de mes articles, j'ai décrit le processus d'installation de Homebridge sur un Raspberry Pi pour le contrôle vocal du système domotique Z-Way . Après avoir installé Homebridge, vous pourrez contrôler la voix avec Siri. Je suis sûr que pour Android, il existe de nombreuses applications qui permettent à la voix d'envoyer des requêtes HTTP pour contrôler Z-Way.

J'attache la vidéo à la commande vocale du lève-vitre.

Source: https://habr.com/ru/post/fr399043/


All Articles