Nous retirons le logiciel du microcontrôleur protégé par mot de passe Renesas M16C



J'ai un ami qui s'occupe de la réparation de fer à repasser pour automobiles. Il m'a en quelque sorte apporté un microcontrôleur soudé à partir d'une unité de commande de chauffage autonome. Il a dit que son programmeur ne le supportait pas, et il aimerait pouvoir transférer le firmware d'avant en arrière, car il y a beaucoup de blocs, en fer ce sont souvent les mêmes, mais les unités qu'ils contrôlent sont différentes. Et il semble qu'il y ait un bloc au lieu d'un défectueux, mais le logiciel est différent et vous ne pouvez tout simplement pas le remplacer. La tâche étant intéressante, j'ai décidé de farfouiller. Si le sujet vous intéresse, s'il vous plaît, sous le chat ...

Le sujet était M306N5FCTFP. Il s'agit d'un microcontrôleur du groupe M16C / 6N5. Le noyau M16C / 60 a été développé par Mitsubishi, et comme Depuis 2003, le successeur de cette société en termes de MK est Renesas, maintenant ces microcontrôleurs sont connus sous cette marque.

Un peu sur le microcontrôleur lui-même


Le caillou est un microcontrôleur 16 bits dans un boîtier QFP à 100 broches. Le noyau possède 1 Mo d'espace d'adressage, une fréquence d'horloge de 20 MHz pour les performances automobiles. L'ensemble des périphériques est également très étendu: deux temporisations 16 bits et la possibilité de générer un PWM triphasé pour contrôler les moteurs, toutes sortes d'UART, SPI, I2C naturellement, 2 canaux DMA, il y a un contrôleur CAN2.0B intégré, ainsi qu'une PLL. À mon avis, c'est très bon pour le vieil homme. Voici un tableau récapitulatif de la documentation:



Étant donné que ma tâche consiste à arracher le logiciel, il s'intéresse également beaucoup à la mémoire. Ce MK a été produit en deux versions: masqué et Flash. J'ai obtenu, comme mentionné ci-dessus, le M306N5FCTFP. À son sujet, la description dit ce qui suit:

  • Version mémoire flash
  • 128 Ko + 4K (4K supplémentaire - le soi-disant bloc A comme cadeau à l'utilisateur pour le stockage des données, mais peut également stocker le programme)
  • V-ver. (version automobile avec une plage de + 125 ° C)

Comment extraire de l'appareil ce que les développeurs ont fait glisser


Il est naturel que vous commenciez à essayer d'obtenir quelque chose du microcontrôleur en étudiant les mécanismes intégrés par le développeur de puces pour les tâches de programmation de la mémoire. Le manuel indique que le fabricant a gentiment placé un chargeur de démarrage dans la mémoire pour les besoins de programmation en circuit de l'appareil.



Comme vous pouvez le voir sur l'image ci-dessus, la mémoire est divisée en 2 parties: la zone utilisateur et la zone du chargeur de démarrage. Dans le second, un chargeur de démarrage par défaut est chargé en usine, qui peut écrire, lire, effacer la mémoire utilisateur et communique via une interface asynchrone, synchrone ou CAN. Il est indiqué qu'il peut être réécrit sur le vôtre ou non. En fin de compte, cela est facilement vérifiable en essayant de frapper le chargeur de démarrage standard au moins via UART ... Pour l'avenir: le fabricant de chauffage n'a pas pris la peine avec son chargeur de démarrage, vous pouvez donc creuser plus loin dans cette direction. Faites immédiatement une réserve qu'il existe toujours une méthode de programmation parallèle, mais puisque Je n'avais pas de programmeur pour ça, je n'ai pas envisagé cette option.

L'entrée en mode de fonctionnement du chargeur de démarrage est assurée par une certaine combinaison aux entrées CNVSS, P5_0, P5_5 lors d'une réinitialisation matérielle. Ensuite, écrivez votre propre utilitaire pour copier le contenu de la mémoire ou utilisez celui fini. Renesas fournit son propre utilitaire, appelé «M16C Flash Starter», mais il a une fonction de lecture réduite. Il n'enregistre pas ce qu'il lit sur le disque, mais le compare avec un fichier du disque. C'est-à-dire en fait, ce n'est pas de la lecture, mais de la vérification. Cependant, il existe un utilitaire gratuit allemand appelé M16C-Flasher, qui peut lire le firmware. En général, la boîte à outils initiale a été récupérée.

À propos de la protection en lecture




Tout serait assez simple si le chargeur de démarrage ne fournissait pas de protection contre les accès non autorisés. Je vais juste donner une traduction très gratuite du manuel.

Fonction de vérification d'identité

Utilisé en modes d'échange série et CAN. L'identifiant transmis par le programmeur est comparé à l'identifiant enregistré en mémoire flash. Si les identifiants ne correspondent pas, les commandes envoyées par le programmeur ne sont pas acceptées. Cependant, si 4 octets du vecteur de réinitialisation sont FFFFFFFFh, les identifiants ne sont pas comparés, ce qui permet d'exécuter toutes les commandes. L'identifiant est de 7 octets stockés séquentiellement, à partir du premier octet, aux adresses 0FFFDFh, 0FFFE3h, 0FFFEBh, 0FFFEFh, 0FFFF3h, 0FFFF7h et 0FFFFBh.

Ainsi, pour accéder au programme, vous devez connaître les 7 octets chéris. Encore une fois, pour l'avenir, je me suis connecté à MK en utilisant le même «M16C Flash Starter» et je me suis assuré que les combinaisons de zéros et de FF ne fonctionnaient pas et que ce problème devait être résolu d'une manière ou d'une autre. Une idée avec une attaque via des canaux tiers a immédiatement fait surface ici. Déjà, je commençais à faire semblant d'être un foulard dans ma tête, ce qui me permet de mesurer le courant dans le circuit de puissance, mais j'ai décidé que l'Internet est grand et que la plupart des vélos ont déjà été inventés. Après avoir effectué quelques recherches, j'ai rapidement trouvé sur hackaday.io le projet Serge 'q3k' Bazanski, intitulé "Reverse engineering Toshiba R100 BIOS". Et dans le cadre de ce projet, l'auteur a résolu essentiellement le même problème: extraire le firmware du MK M306K9FCLR. De plus - à cette époque, la tâche avait déjà été résolue avec succès. D'une part, j'étais un peu contrarié - une énigme intéressante n'a pas été résolue par moi. D'un autre côté, la tâche est passée d'une recherche de vulnérabilité à son exploitation qui promettait une solution beaucoup plus rapide.

En résumé, q3k, par exactement la même logique, a commencé l'étude par une analyse de la consommation actuelle, à cet égard, elle était dans des conditions beaucoup plus favorables, car il avait ChipWhisperer, je n'ai toujours pas ce truc. Mais depuis sa première sonde pour supprimer le courant de consommation s'est avérée inappropriée et il n'a pas pu isoler quelque chose d'utile du bruit, il a décidé de tenter une simple attaque sur le temps de réponse. Le fait est que le chargeur de démarrage extrait la sortie BUSY pendant l'exécution de la commande afin d'informer l'hôte qu'il est occupé ou prêt à exécuter la commande suivante. Selon l'hypothèse de q3k, la mesure du temps entre la transmission du dernier bit de l'identifiant et la suppression du drapeau occupé pourrait servir de source d'informations pendant l'énumération. Lors de la vérification de cette hypothèse en énumérant le premier octet de la clé, une déviation temporelle a vraiment été trouvée dans un seul cas - lorsque le premier octet était égal à FFh. Pour la commodité de la mesure du temps, l'auteur a même ralenti le MK en éteignant le résonateur à quartz et en appliquant une onde carrée de 666 kHz à l'entrée d'horloge pour simplifier la procédure de mesure. Après cela, l'identifiant a été sélectionné avec succès et le logiciel a été récupéré.

La première crêpe - un râteau


Ha! Je pensais ... Maintenant, je rivets rapidement le programme sur mon STM32VLDiscovery c STM32F100 à bord, qui enverra le code et mesurera le temps de réponse, et crachera les résultats de mesure dans le terminal. Parce que La planche à pain avec le contrôleur cible était auparavant connectée au PC via l'adaptateur USB-UART, afin de ne rien changer sur la planche à pain, nous travaillerons en mode asynchrone.



Quand au démarrage du chargeur de démarrage, l'entrée CLK1 est tirée au sol, il se rend compte qu'ils veulent de lui une communication asynchrone. C'est pourquoi je l'ai utilisé - la bretelle était déjà soudée et je viens de connecter les deux cartes avec des fils: Discovery et la planche à pain avec la cible M306.

Remarque sur l'harmonisation des niveaux:

Parce que Étant donné que M16 a des niveaux TTL sur les terminaux et STM32 a LVTTL (simplifié, voir la fiche technique pour plus de détails), une correspondance de niveau est nécessaire. Parce que ce n'est pas un appareil qui, comme une batterie bien connue, devrait fonctionner, fonctionner et fonctionner, mais en fait il se connecte une fois sur la table, alors je ne me suis pas soucié des traducteurs de niveau: les niveaux de sortie digérés MK à cinq volts de STM32, dans le sens de 3 volts, il perçoit comme "1" , les sorties de M16 sont alimentées vers les entrées STM32 tolérantes 5V afin que cela ne se sente pas mal, et nous n'oublions pas de mettre la jambe qui tire RESET M16 en mode drain ouvert. J'ai oublié, et c'est + 2 heures à la tirelire du temps perdu.
Ce minimum est suffisant pour comprendre les glandes des autres.

La logique du logiciel attaquant est la suivante:

  1. Nous établissons une connexion avec le contrôleur. Pour ce faire, vous devez attendre la fin de la réinitialisation, puis transmettre 16 caractères zéro avec un intervalle de plus de 20 ms. Ceci afin d'élaborer l'algorithme de détermination automatique du taux de change, car l'interface est asynchrone et MK ne sait rien de sa fréquence. La vitesse de démarrage de l'émetteur doit être de 9600 bauds, c'est à cette vitesse que le chargeur calcule. Après cela, si vous le souhaitez, vous pouvez demander un taux de change différent sur cinq disponible dans la gamme 9600-115200 (bien que dans mon cas, le chargeur ait refusé de travailler sur 115200). Je n'ai pas besoin de modifier la vitesse, j'ai donc simplement demandé la version du chargeur de démarrage pour contrôler la synchronisation. Nous passons FBh, le chargeur répond avec une ligne comme "VER.1.01".
  2. Nous envoyons la commande "unlock", qui contient l'itération actuelle de la clé, et mesurons le temps jusqu'à ce que le drapeau occupé soit effacé.

    La commande se compose du code F5h, de trois octets de l'adresse où commence la zone d'identifiant (dans mon cas, pour le noyau M16C, c'est 0FFFDFh), de la longueur (07h) et de l'identifiant lui-même.
  3. Nous mesurons le temps entre la transmission du dernier bit de l'identifiant et la suppression du drapeau occupé.
  4. Nous augmentons l'octet de clé en cours de tri (KEY1 à l'étape initiale), nous revenons à l'étape 2 jusqu'à ce que nous triions les 255 valeurs de l'octet actuel.
  5. Nous réinitialisons les statistiques sur le terminal (enfin, ou nous effectuons l'analyse «à bord»).

Pour communiquer avec le MK cible, j'ai utilisé USART dans STM32, pour mesurer le temps - une minuterie en mode Capture d'entrée. La seule chose, pour simplifier, je n'ai pas mesuré le temps entre le dernier bit de la clé et la suppression du drapeau, mais entre le début de la transmission et le drapeau. La raison en était que le dernier bit pouvait changer, et en mode asynchrone, il n'y avait rien à attacher à l'entrée de capture. Dans le même temps, l'UART est matériel et le temps de transmission est fondamentalement identique et il ne devrait pas y avoir d'erreurs tangibles.

Par conséquent, pour toutes les valeurs, les résultats étaient identiques. Complètement identique. La fréquence d'horloge de la minuterie était de 24 MHz, respectivement, la résolution temporelle est de 41,6 ns. Bon, ok, j'ai essayé de ralentir la cible MK. Rien n'a changé. Ici, la question s'est posée dans ma tête: qu'est-ce que je fais mal, comme l'a fait q3k? Après comparaison, la différence a été trouvée: il utilise une interface d'échange synchrone (SPI), et je suis asynchrone (UART). Et quelque part ici, j'ai attiré l'attention sur le moment que j'ai manqué au début. Même sur les schémas de câblage pour les modes de chargeur de démarrage synchrones et asynchrones, la sortie prête est nommée différemment:



En synchrone c'est "BUSY", en asynchrone c'est "Monitor". Nous regardons le tableau «Fonctions de sortie en mode d'E / S série standard»:


"Semyon Semenych ..."

La bagatelle, qui a été manquée au début, a apporté le mauvais endroit. En fait, si en mode synchrone, c'est exactement l'indicateur occupé du chargeur de démarrage, alors en mode asynchrone (celui qui mode E / S série 2) est juste un «clignotant» pour indiquer le fonctionnement. Peut-être, en général, le signal matériel de disponibilité de l'émetteur-récepteur, et donc la précision étonnante de son élévation.

En général, nous soudons la résistance sur la broche SCLK du sol au VCC, soudons le fil là-bas, le raccordons tous au SPI et recommençons ...

Succès!




En mode synchrone, tout est presque le même, seule aucune procédure préliminaire pour établir une connexion n'est requise, la synchronisation est simplifiée et la capture de l'heure peut être effectuée avec plus de précision. Si je choisissais immédiatement ce mode, je gagnerais du temps ... Encore une fois, je n'ai pas compliqué et mesuré le temps à partir du dernier bit, mais j'ai démarré le chronomètre avant de commencer le transfert du dernier octet de la clé, c'est-à-dire nous allumons la minuterie et l'envoyons à l'émetteur KEY7 (dans la capture d'écran ci-dessus, à partir de l'analyseur logique, vous pouvez voir la distance entre les curseurs. C'est l'intervalle de temps mesuré).

C'était plus que suffisant pour une identification réussie. Voici l'énumération d'un octet:



Sur l'axe des x, nous avons le nombre de comptages discrets, sur l'axe des y, respectivement, la valeur de clé transmise. Le rapport signal / bruit est tel que même aucun filtre n'est nécessaire, tout comme à l'école dans une leçon d'informatique: nous trouvons le maximum dans le tableau et passons à la sélection de l'octet suivant. Les 6 premiers octets sont sélectionnés facilement et rapidement, un peu plus difficile avec le dernier: là c'est juste que la recherche arrogante ne passe pas, il faut réinitialiser la "victime" avant chaque tentative. En conséquence, cela prend environ 400 ms pour chaque tentative, et la recherche est dans le pire des cas, de l'ordre d'une minute et demie. Mais c'est le pire. Après chaque tentative, nous demandons un statut et, dès que nous le supposons, nous nous arrêtons. Au début, je passais généralement rapidement en revue l'identifiant avec des stylos, en insérant la sortie de la console dans Excel et en traçant le graphique, d'autant plus que c'était une tâche ponctuelle, mais pour l'article, j'ai décidé d'ajouter une itération automatique, pour une belle console ...



Bien sûr, si le développeur effaçait le chargeur de démarrage (remplacé par le sien), il ne serait pas si facile de sortir, mais dans l'électronique automobile, les MK ne sont souvent pas fermés du tout. En particulier, dans l'unité de commande d'un autre appareil de chauffage, dans laquelle le V850 du même Renesas a été installé, tout a été décidé en soudant une paire de fils et en copiant le firmware avec un utilitaire standard. Il s'agit de l'ensemble du moteur de crypto-monnaie dans le monde des calculateurs. Apparemment, les fabricants n'aiment pas le phénomène de réglage des puces et d'autres types d'interférences ... Bien que cela ressemble à une course d'armures et d'obus - les glandes sont plus raides, plus chères, mais il n'y a pas de gagnant ...

Références:

  1. https://www.dataman.com/media/datasheet/Renesas/M16C6N5Group.pdf
  2. https://hackaday.io/project/723-reverse-engineering-toshiba-r100-bios/log/51302-ec-firmware-dumped
  3. https://q3k.org/slides-recon-2018.pdf

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


All Articles