Rétro-ingénierie MCU

Déverrouillage de microcontrôleur - MikaTech

Nos valeurs et objectifs

À propos de MikaTech

Le temps a passé vite, du jour où nous avons réalisé notre premier projet de rétro-ingénierie sur MCU 8051 en 1998, jusqu'à l'installation de notre laboratoire de rétro-ingénierie d'un million de dollars en 2012, 14 années se sont écoulées. Aujourd'hui, nous démarrons notre nouvelle activité de développement de systèmes visuels embarqués, avec l'espoir de servir encore 10 ans.

signature Peter Lee Co-fondateur & CEO

Rétro-ingénierie pour émuler des cartouches d'encre

    • Depuis deux ans, je prévois de construire une imprimante 3D à partir de vieilles imprimantes à jet d'encre que j'ai collectionnées au fil des ans. Mais ce n'est que depuis deux semaines que j'ai réellement commencé à travailler dessus.

      L'imprimante 3D que je souhaite réaliser utilise des jets d'encre pour pulvériser un produit chimique sur une plateforme de construction. La plateforme descend à mesure qu'une nouvelle couche de poudre est étalée. Ensuite, un produit chimique est pulvérisé par la tête d'impression, ce qui fait durcir la poudre. Si vous voulez plus d'informations sur ce type d'imprimantes, regardez ici.

      Mon imprimante de choix était une vieille Epson Stylus C63 pour ces raisons :
      1. Les cartouches d'encre fournissent uniquement l'encre, elles n'ont pas de buses d'impression elles-mêmes.
      2. Je peux fixer des tubes en caoutchouc ou un autre type de tuyauterie qui me permet d'imprimer un autre produit chimique de mon choix.
      3. Les buses de la tête d'impression utilisent un matériau piézoélectrique pour produire des gouttelettes d'encre (dure beaucoup plus longtemps que les têtes thermiques).
      4. La plupart des manuels d'entretien des imprimantes Epson sont accompagnés d'un schéma détaillé de la carte principale.

      Remarque : En raison de certaines circonstances, cette imprimante avait déjà été démontée.

      La première chose à faire était de faire fonctionner mon imprimante pour pouvoir brancher mon analyseur logique et observer les signaux. Le problème était qu'il me manquait une cartouche d'encre, donc l'imprimante refusait d'imprimer ou de faire quoi que ce soit.
      En raison de mon budget (sans emploi et sur le point de commencer l'université), j'ai décidé d'essayer d'émuler la cartouche manquante, ou toutes, à l'aide d'un microcontrôleur, ce qui me permettrait d'avancer et d'utiliser de l'eau pour la tête d'impression au lieu d'encre qui tache.


    • Donc, pour émuler les cartouches d'encre, je dois d'abord comprendre leur fonctionnement. Heureusement, les cartouches de mon imprimante ont de simples EEPROM qui contiennent l'utilisation de l'encre et d'autres informations importantes sur la cartouche, telles que la date d'expiration, la couleur, le numéro de série, etc.

      À l'intérieur de la tête d'impression, il y a une carte de circuit imprimé avec des pads qui sont le miroir exact des pads des cartouches d'encre et un câble nappe qui relie cette carte à la carte principale sur les broches 1 à 6.

      Un coup d'œil rapide au schéma de la carte principale révèle que les broches 1 à 6 sont nommées CVDD, CSDA, CRST, GND, CSCK et COI.

      Avec ces noms de signaux, j'ai décidé qu'il serait préférable de dessiner un schéma de la carte de connexion car il n'était pas présent dans le manuel d'entretien. Cela a révélé que CSDA, CSCK, CRST ont des résistances de tirage vers le bas de 100 kOhms, un peu inhabituel à mon avis car ce n'est pas comme l'I2C (qui utilise des résistances de tirage vers le haut, généralement de 1,8 kOhms à 47 kOhms). Le signal COI se connecte en série aux cartouches d'encre mais à l'extrémité est court-circuité à GND. Curieusement, j'ai testé la continuité des pads sur les cartouches d'encre et j'ai constaté qu'elles sont court-circuitées, ce qui signifie que lorsque COI est à la masse, toutes les cartouches d'encre sont présentes.

    • Maintenant que je connais les noms des signaux, je voulais voir à quoi ressemblent les signaux eux-mêmes.
      J'ai sorti mon fer à souder, un câble nappe (qui était un câble IDE d'un vieil ordinateur) et un cutter. J'ai coupé un câble nappe de 6 fils d'environ 30 cm de long, j'ai dénudé les extrémités soigneusement avec le cutter (beaucoup plus facile d'utiliser un cutter pour dénuder un câble nappe qu'une pince coupante ou une pince), et je les ai soudés sur la carte de circuit imprimé qui s'accouple avec les cartouches d'encre.
      J'ai également dénudé les autres extrémités et soudé quelques broches pour pouvoir les brancher sur mon analyseur logique.

      Après avoir branché l'analyseur logique sur la carte de circuit imprimé et observé les signaux, j'ai constaté que immédiatement SCK, SDA ne ressemblent en rien à I2C. Je suis presque sûr qu'Epson a dû inventer son propre protocole série pour communiquer avec les EEPROM à l'intérieur des cartouches d'encre.

      Ce que j'ai trouvé, c'est que CVDD est alimenté et peu après, CRST et CSDA montent en même temps, suivis de près par CSCK qui pulse. CSDA change sur le front montant de CSCK et je suppose qu'il est lu sur le front descendant.
      CSCK est Serial ClocK (horloge série)
      CSDA est Serial Data (données série)
      CRST est Reset
      et CVDD est l'alimentation
      Cela semble assez simple, mais je voulais plus d'informations sur ce protocole : y avait-il des adresses envoyées ?

      J'ai augmenté la fréquence d'échantillonnage de mon analyseur logique et j'ai trouvé qu'il y a 4 cycles d'horloge, une pause, puis 8 cycles, pause, 8 de plus, etc. Les 4 cycles d'horloge au début m'ont donné l'idée que les quatre premiers pourraient être une adresse de 4 bits de la cartouche d'encre. Beaucoup d'informations étaient encore inconnues sur ce protocole. J'ai donc fait des recherches !

      En cherchant sur Google, je n'ai rien trouvé sur ce protocole ; le meilleur endroit suivant pour chercher était les brevets.
      Oui, les brevets. J'ai recherché "Epson SDA SCK RST memory eeprom" sur http://www.freepatentsonline.com/ et l'un des brevets que j'ai trouvés (US7660008) contenait exactement ce que je cherchais. Il décrivait le SCK, SDA, RST et VDD du protocole.
      Je vous épargne les tracas et vous indique ce que j'ai trouvé :
      1. Des organigrammes décrivant ce que font l'hôte et l'esclave.
      2. Un diagramme de timing du protocole.
      3. Les 3 premiers bits sont une adresse de 3 bits suivie d'un bit Lecture/Écriture (Lecture = 0, Écriture = 1).
      4. Le compteur d'adresse commence toujours à 0x00 et s'incrémente de 1 à chaque cycle d'horloge (cela signifie qu'il écrit en bits, pas en octets).
      5. Dès que RST passe à l'état bas, l'EEPROM arrête tout et se réinitialise.
      6. Il y a 252 bits à lire, les 3 derniers bits (en fait ils sont au début) sont l'adresse de la cartouche d'encre.

      En utilisant ce que j'avais trouvé, j'ai décidé de commencer à écrire du code.

      Ce que je voulais faire, c'était lire les ROM des cartouches d'encre, puis les utiliser pour essayer d'émuler la cartouche d'encre manquante que je n'ai pas. Cela signifie créer une bibliothèque qui agira comme un hôte pour lire les données, mais qui pourra également être utilisée pour émuler une cartouche d'encre.
      En utilisant les connaissances acquises en créant des protocoles logiciels, j'ai créé un fichier d'en-tête avec ces fonctions :
      • extern void epsnCartInit(void);
      • extern void epsnCartStart(unsigned char addr, unsigned char write);
      • extern void epsnCartStop(void);
      • extern void epsnCartWrite(unsigned char data);
      • extern unsigned char epsnCartRead(void);

      J'ai également créé les définitions suivantes pour aider à se souvenir de quelle broche correspond à quoi :
      Pour votre microcontrôleur, vous devrez modifier ces valeurs pour les adapter à votre application.

      #define _ecSCKTRIS TRISCbits.TRISC0
      #define _ecSDATRIS TRISCbits.TRISC1
      #define _ecRSTTRIS TRISCbits.TRISC2
      #define _ecVDDTRIS TRISBbits.TRISB1

    • #define _ecSCKLat LATCbits.LATC0
      #define _ecSDALat LATCbits.LATC1
      #define _ecRSTLat LATCbits.LATC2
      #define _ecVDDLat LATBbits.LATB0

      #define _ecSCK PORTCbits.RC0
      #define _ecSDA PORTCbits.RC1
      #define _ecRST PORTCbits.RC2
      #define _ecVDD PORTBbits.RB1

      #define wait() DelayUS(50)

      Remarque : j'ai utilisé un PIC18F et j'ai utilisé des verrous pour définir les sorties, c'est juste ma préférence. Vous pouvez utiliser n'importe quel microcontrôleur que vous connaissez.

      void epsnCartInit(){
      _epsnCartSCKTRIS = 0;
      _epsnCartSDATRIS = 0;
      _epsnCartRSTTRIS = 0;
      _epsnCartVDDTRIS = 0;

      _epsnCartSCK = 0;
      _epsnCartSDA = 0;
      _epsnCartRST = 0;
      _epsnCartVDD = 0;

      _epsnCartSCKLat = 0;
      _epsnCartSCKLat = 0;
      _epsnCartSCKLat = 0;
      _epsnCartSCKLat = 0;
      }

      • Liste de rétro-ingénierie des MCU d'imprimantes Atmel par Mikatech :
    • Série AT89xx complète : craquage de microcontrôleur : AT89C51 AT89C52 AT89S52 AT89S53 AT89S54 AT89S58 AT89S64 AT89C1051 AT89C2051 AT89C4051 AT89C55 AT89C55WD AT89C5131A AT89C51WD AT89C51ED2 AT89C51CC01 AT89S51 AT89C51CC02 AT89C51CC03 AT89C51RB2 AT89C51RC AT89C51RD2 AT89C51RD-CM AT89C51RC2 AT89C51ID2 AT87C5101 AT89C1051U AT89C2051X2 AT89C5130AM AT89C5130A AT89C5131AL AT89C5131AM AT89C51AC3 AT89C5132 AT89C51AC2 AT89C51CC03C AT89C51SND1C AT89C51CC03U AT89C51IC2 AT89C51RE2 AT89C51SND2 AT89LP2051 AT89LP2052 AT89LP213 AT89LP214 AT89LP216 AT89LP4051 AT89LP4052 AT89LP828 AT89LP428 AT89LS51 AT89LS52 AT89LV51 AT89LS53 AT89LS8252 AT89LV52 AT89LV55 AT89S2051 AT89S4051 AT89S8252 AT89S8253 ...

    • Série AT90xx complète : craquage de microcontrôleur : AT90S1200 AT90S2323 AT90S2343 AT90S2331 AT90S4433 AT90S8515 AT90S8535 AT90S4414 AT90S4434 AT90S2313 90S1200 90S2323 90S2343 90S2331 90S4433 90S8515 90S8535 90S4414 90S4434 90S2313 ...

    • Série AT90CAN/PWM/USB/xx complète : lecture de microcontrôleur : AT90CAN32 AT90CAN64 AT90CAN128 AT90PWM2 AT90PWM216 AT90PWM2B AT90PWM3 AT90PWM316 AT90PWM3B AT90USB1286 AT90USB1287 AT90USB162 AT90USB646 AT90USB647 AT90USB82 ...
    • Série AT91SAMxx complète : craquage de firmware MCU : AT91SAM9XE512 AT91SAM9XE256 AT91SAM9XE128 AT91SAM7S64B AT91SAM7S32B AT91SAM7SE512 AT91SAM7SE256 AT91SAM7SE32 AT91SAM7XC512 AT91SAM7XC256 AT91SAM7XC128 AT91SAM7X512 AT91SAM7X256 AT91SAM7X128 AT91SAM7S161 AT91SAM7S512 AT91SAM7S256 AT91SAM7S128 AT91SAM7S64 AT91SAM7S321 ...

    • Série ATTinyxx complète : craquage de firmware MCU : ATtiny4 ATtiny5 ATtiny10 ATtiny11 ATtiny12 ATtiny13 ATtiny15 ATtiny20 ATtiny22 ATtiny24 ATtiny25 ATtiny26 ATtiny261 ATtiny28 ATtiny2313 ATtiny40 ATtiny4313 ATtiny43 ATtiny44 ATtiny45 ATtiny461 ATtiny48 ATtiny84 ATtiny85 ATtiny861 ATtiny87 ATtiny88 ATtiny4A ATtiny5A ATtiny10A ATtiny11A ATtiny12A ATtiny13A ATtiny15A ATtiny20A ATtiny22A ATtiny24A ATtiny25A ATtiny26A ATtiny261A ATtiny28A ATtiny2313A ATtiny40A ATtiny4313A ATtiny43A ATtiny44A ATtiny45A ATtiny461A ATtiny48A ATtiny84A ATtiny85A ATtiny861A ATtiny87A ATtiny88A ATtiny4V ATtiny5V ATtiny10V ATtiny11V ATtiny12V ATtiny13V ATtiny15V ATtiny20V ATtiny22V ATtiny24V ATtiny25V ATtiny26V ATtiny261V ATtiny28V ATtiny2313V ATtiny40V ATtiny4313V ATtiny43V ATtiny44V ATtiny45V ATtiny461V ATtiny48V ATtiny84V ATtiny85V ATtiny861V ATtiny87V ATtiny88V ...

    • Série ATMegaxx complète : craquage de microcontrôleur : ATmega16 ATmega162 ATmega164 ATmega165 ATmega168 ATmega169 ATmega128 ATmega1280 ATmega1281 ATmega2560 ATmega2561 ATmega328 ATmega48 ATmega32 ATmega324 ATmega325 ATmega3250 ATmega329 ATmega3290 ATmega64 ATmega640 ATmega645 ATmega6450 ATmega649 ATmega6490 ATmega8 ATmega88 ATmega8515 ATmega8535 ATmega16L ATmega162L ATmega164L ATmega165L ATmega168L ATmega169L ATmega128L ATmega1280L ATmega1281L ATmega2560L ATmega2561L ATmega328L ATmega48L ATmega32L ATmega324L ATmega325L ATmega3250L ATmega329L ATmega3290L ATmega64L ATmega640L ATmega645L ATmega6450L ATmega649L ATmega6490L ATmega8L ATmega88L ATmega8515L ATmega8535L ATmega16P ATmega162P ATmega164P ATmega165P ATmega168P ATmega169P ATmega128P ATmega1280P ATmega1281P ATmega2560P ATmega2561P ATmega328P ATmega48P ATmega32P ATmega324P ATmega325P ATmega3250P ATmega329P ATmega3290P ATmega64P ATmega640P ATmega645P ATmega6450P ATmega649P ATmega6490P ATmega8P ATmega88P ATmega16A ATmega162A ATmega164A ATmega165A ATmega168A ATmega169A ATmega128A ATmega1280A ATmega1281A ATmega2560A ATmega2561A ATmega328A ATmega48A ATmega32A ATmega324A ATmega325A ATmega3250A ATmega329A ATmega3290A ATmega64A ATmega640A ATmega645A ATmega6450A ATmega649A ATmega6490A ATmega8A ATmega88A ATmega8515A ATmega8535A ...

    • Série ATFxx : craquage de microcontrôleur : ATF16V8B ATF16V8BL ATF16V8BQ ATF16V8BQL ATF16LV8C ATF16LV8CEXT ATF16V8C ATF16V8CEXT ATF16V8CZ ATF20V8B ATF20V8BL ATF20V8BQ ATF20V8BQL ATF22LV10C ATF22LV10CEXT ATF22LV10CUES ATF22LV10CZ ATF22LV10CQZ ATV22V10 ATF22V10B ATF22V10BQ ATF22V10BL ATF22V10BQL ATF22V10C ATF22V10CEXT ATF22V10CUES ATF22V10CZ ATF22V10CQZ ATF22V10CZUES ATF22V10CQZUES ATF1500A ATF1500ABV ATF1500ABVL ATF1500 ATF1500L ATF1502AS ATF1502ASL ATF1502ASV ATF1502ASVL ATF1504AS ATF1504ASVL ATF1508 ATF1508AS ATF1508ASV ATF2500C ATF2500CL ATF2500CQ ATF2500CQL ATF750C ATF750CEXT ATF750CL ATF750LVC ATF750LVCCEXT ATF750LVCEXT ATF750LVCL ATV2500 ATV2500H ATV2500L ATV2500B ATV2500BL ATV2500BQL ATV5000 ATV5000L ATV750 ATV750B ATV750BL ATV750L ...

    • Série AT88scxx/90scxx : craquage de microcontrôleur : AT88SC0104 AT88SC0104C AT88SC0204 AT88SC0204C AT88SC0404 AT88SC0404C AT88SC0808 AT88SC0808C AT88SC1003 AT88SC101 AT88SC102 AT88SC1281 AT88SC12816C AT88SC150 AT88SC153 ...

    La fonction de démarrage met VDD à un niveau haut, attend, puis définit SDA en fonction du bit d'adresse ainsi que SCK. Attend, abaisse SCK, attend encore, puis définit SDA en fonction de l'adresse. Cela se répète 2 fois de plus, sauf qu'à la 4ème fois, SDA est mis à un niveau bas car nous voulons lire les données dans les EEPROM, pas écrire.

    void epsnCartStart(unsigned char addr, unsigned char write){
    char i = 0; // Utilisé pour le comptage
    char tmp = 0; // Variable de stockage temporaire

    _ecVDDLat = 1; // Met VDD à l'état haut
    DelayMS(1); // Attend que les EEPROM soient prêtes
    _ecRSTLat = 1; // Active les EEPROM pour la communication

    tmp = addr; // Copie l'adresse dans la variable temporaire
    if(write){ // Si nous voulons écrire
    temp |= 0x08; // Met le 4ème bit à 1 ('|' signifie OU)
    }

    while(i<4){
    _ecSDALat = (tmp&0x01); // Met SDA à un niveau haut ou bas selon le LSB

    wait(); // Attend environ 50 µs
    _ecSCKLat = 1; // Met l'horloge à l'état haut
    wait(); // Attend encore 50 µs
    _ecSCKLat = 0; // Met l'horloge à l'état bas
    wait(); // Attend encore 50 µs
    tmp>>=1; // Décale tous les bits de tmp d'un cran vers la droite
    i++; // Incrémente le compteur de un
    }

    }

    La fonction suivante à implémenter est stop :
    void epsnCartStop(){
    _ecRSTLat = 0; // Met RST à l'état bas
    DelayMS(1); // Attend un peu
    _ecVDDLat = 0; // Éteint les EEPROM
    }

    Et enfin la fonction de lecture :
    unsigned char epsnCartRead(){
    char i=0;
    char temp = 0x00;

    while(i<8){
    temp>>=1; // Décale la variable temporaire d'un cran vers la droite
    _ecSCKLat = 1; // Met SCK à l'état haut
    wait(); // Attend environ 50 µs
    if(_ecSDA){ // Si SDA est à l'état haut
    temp |= 0x80; // Alors met le bit 7 à 1
    }
    _ecSCKLat = 0; // Remet SCK à l'état bas
    wait(); // Attend encore 50 µs

    i++; // Incrémente le compteur de 1
    }
    return temp; // Retourne ce qui a été lu
    }

    Nous avons maintenant les fonctions de base nécessaires pour lire les données des cartouches d'encre. Configurons notre microcontrôleur et affichons les résultats.

  • Démarrez votre IDE, dans mon cas c'est MPLab, créez un nouveau projet pour votre puce et créez un programme "Hello World" basique pour vous assurer que votre puce fonctionne.

    Remarque : Mon programme "Hello World" envoie "Hello World!\r\n" en série vers mon ordinateur à 38400 bps.

    #include <p18f2550.h> // Fichier include spécifique à cette puce
    #include <usart.h> // Fonctions USART
    #include <stdio.h> // printf(format,...), sprintf(string, format, ...), etc.

    #include "delay.h"

    void main(){

    PORTA = 0x00;
    PORTB = 0x00;
    PORTC = 0x00;

    TRISA = 0x00;
    TRISB = 0x00;
    TRISC = 0x00;

    ADCON1 = 0x0F; // C'est un vrai fauteur de troubles, n'oubliez jamais de repasser les broches analogiques en numériques ! :P

    TRISCbits.TRISC7 = 1; // Transforme la broche RX en entrée (consultez votre fiche technique pour votre puce)

    OpenUSART(USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ADDEN_OFF & USART_BRGH_HIGH & USART_CONT_RX & USART_EIGHT_BIT & USART_ASYNCH_MODE,78);

    while(1){
    putrsUSART("Hello World!\r\n"); // Envoie un message via Serial

    LATAbits.LATA0 = ~LATAbits.LATA0; // Fait clignoter une LED en la basculant

    DelayMS(500); // Une fonction de délai que j'ai créée
    }
    }

    Ceci a été configuré avec une alimentation 3,3 V car les cartouches d'encre fonctionnent en 3,3 Volts.

    Pourquoi un programme "Hello World" ? Eh bien, à chaque fois que je configure un nouveau projet sur une plaque d'essai, je veux toujours m'assurer que les bits de configuration sont corrects et que je ne reçois pas de données inintelligibles dans le terminal.

    Je pense que c'est une bonne pratique et tout le monde devrait le faire ! :P

    Si votre puce fait quelque chose de simple comme la mienne, super !! Passons à l'étape suivante.

    Implémentation de notre bibliothèque
    Nous voulons maintenant inclure la bibliothèque que nous avons créée pour interfacer le microcontrôleur avec la cartouche d'encre.

    En haut du fichier, ajoutez :
    #include "epsnCart.h" // le nom du fichier d'en-tête que nous avons créé précédemment

    Comme ce microcontrôleur se fait passer pour l'hôte, il contrôle les signaux SCK, SDA et RST. Assurez-vous donc qu'ils sont en sortie, ajoutez ceci avec les autres registres TRIS dans main() :

    epsnCartInit();

    Le morceau de code suivant est ce que j'ai utilisé à l'intérieur de while(1) {
    Il demande une adresse à partir de 0x00 puis l'incrémente de un après 32 octets lus :

    void main(){
    char addr = 0, i = 0;
    char string[40];

    ...
    epsnCartInit(); // Initialise les broches utilisées

    while(1){ // Boucle infinie !

    sprintf(string, "Lecture de la cartouche avec l'adresse : 0x%02X\r\n",addr);
    putsUSART(string); // Affiche un message comme : Lecture de la cartouche avec l'adresse : 0x03

    epsnCartStart(addr,0); // Démarre en envoyant l'adresse en mode lecture

    i = 0;
    while(i<32){ // Boucle tant que i est inférieur à 32
    sprintf(string,"0x%02X,",epsnCartRead());
    putsUSART(string);
    i++;
    }

    epsnCartStop(); // Met RST à l'état bas

    putrsUSART("\r\n");

    addr++
    if(addr>7){
    addr = 0;
    }
    DelayMS(500);
    }
    }

    Vous vous demandez probablement à propos de la ligne "sprintf(string,"0x%02X,",epsnCartRead());" et vous dites "Hein ?"
    sprintf est une fonction de formatage de chaîne, un peu comme printf, sauf qu'elle enregistre la chaîne formatée dans la variable string.
    "0x%02X," retournera une chaîne avec une valeur hexadécimale lisible, par exemple : 0xFE, et epsnCartRead() retourne une valeur qui a été lue depuis la cartouche d'encre.

    Ceci a été configuré avec une alimentation 3,3 V car les cartouches d'encre fonctionnent en 3,3 Volts.

    J'ai programmé ceci dans mon microcontrôleur, j'ai déconnecté la tête d'impression de l'imprimante pour éviter les interférences.
    J'ai ensuite branché les 3 cartouches d'encre que j'avais et j'ai mis le tout sous tension.

    Remarque : À ce stade, si vous exécutez ce code pour la première fois, je m'attends à ce que vous ayez des problèmes. Comme moi, j'ai passé en revue le code des dizaines de fois, en le modifiant ici et là pour le faire fonctionner. Il est normal que cela ne fonctionne pas du premier coup pour vous. C'est une excellente expérience d'apprentissage que de comprendre ce qui n'a pas fonctionné ! :P

  • Ouf, après tout ce travail acharné, ça vit !!!

    Voici ce que j'ai obtenu sur mon terminal

    Addr: 0x01
    57,07,02,F2,00,00,00,00,00,50,98,6A,EB,D3,54,A3,39,50,14,15,31,93,E4,B4,24,96,57,04,35,F5,E4,14,

    Addr: 0x02
    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,

    Addr: 0x03
    03,00,02,F2,00,00,00,00,00,50,C8,1C,2A,A5,82,00,3B,50,16,17,31,93,E4,B4,24,96,57,04,35,F5,E4,34,

    Addr: 0x04
    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,

    Addr: 0x05
    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,

    Addr: 0x06
    00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,

    Addr: 0x07
    03,00,02,F2,00,00,00,00,00,50,30,9F,74,81,D1,40,3B,50,1A,1B,31,93,E4,B4,24,96,57,04,35,F5,E4,74,

    Maintenant, voyez ici comment seules les adresses impaires ont retourné des données, sauf 0x05 ?
    C'est parce que 0x05 est l'adresse de la cartouche d'encre magenta, que je n'ai malheureusement pas :(

  • Routines de code d'émulation
    Maintenant, la partie amusante !

    J'ai besoin que l'imprimante pense qu'il y a quatre cartouches d'encre installées. Pour ce faire, nous devons d'abord implémenter quelques fonctions d'émulation. Encore une fois, à partir de connaissances antérieures, je les ai prédéfinies dans l'en-tête :

    • #define epsnCartReady() (_ecRST)
    • extern unsigned char espnCartGetAddr(void);
    • extern void epsnCartOut(unsigned char data);
    • extern unsigned char epsnCartIn(unsigned char data);

    epsnCartReady est une macro utilisée pour vérifier si l'imprimante a RST à l'état haut.
    epsnCartGetAddr retourne l'adresse demandée par la tête d'impression.
    epsnCartOut est utilisé pour envoyer des données au contrôleur hôte.
    epsnCartIn est utilisé pour recevoir des données de l'hôte lorsqu'il écrit.

    Donc, cette fois, au lieu de contrôler SCK pour qu'il passe à l'état haut ou bas, nous devons attendre qu'il passe à l'état haut ou bas, mais si la réinitialisation passe à l'état bas, alors arrêtez tout et revenez :

    unsigned char espnCartGetAddr(void){
    char i=0;
    char temp = 0x00;

    while((i<4) && _ecRST){ // Boucle tant que i < 4 et RST est à l'état haut
    temp>>=1; // Décale Temp d'un cran vers la droite
    while((!_ecSCK) && (_ecRST)); // Attend que l'horloge passe à l'état haut
    if(_ecSDA){// seulement si SDA est à l'état haut
    temp |= 0x08; // alors met le 4ème bit à 1
    }
    while((_ecSCK) && (_ecRST));// attend que l'horloge passe à l'état bas
    i++; // incrémente le compteur
    }

    if(_ecRST == 0){
    return 0xFF;//si RST est passé à l'état bas, cela signifie que l'hôte a coupé le signal
    }
    return temp; //retourne l'adresse et le bit de lecture/écriture
    }

    Et la fonction Out :

    void epsnCartOut(unsigned char data){
    char i = 0;
    char temp = data;

    _ecSDATRIS = 0; // définit SDA en sortie
    _ecSDALat = 0; // met SDA à l'état bas

    while((i<8) && _ecRST){ // Boucle tant que i < 8 et RST est à l'état haut
    while(!_ecSCK && _ecRST); // Attend que SCK passe à l'état haut
    _ecSDALat = temp&0x01; // Met SDA à l'état haut ou bas selon le premier bit

    while(_ecSCK && _ecRST); // Attend que SCK passe à l'état bas
    temp>>=1; // Décale les données d'un cran vers la droite
    i++; // incrémente le compteur de un
    }
    _ecSDATRIS = 1; // Remet SDA en entrée
    _ecSDA = 1; // Pas sûr que ce soit nécessaire
    }

    Et enfin la fonction In :

    unsigned char epsnCartIn(void){
    unsigned char i=0;
    char temp = 0x00;

    while((i<8) && _ecRST){ // Boucle tant que i < 8 et RST est à l'état haut
    temp>>=1; // Décale Temp d'un cran vers la droite
    while(!_ecSCK && _ecRST); // Attend que SCK passe à l'état haut
    if(_ecSDA){ // seulement si SDA est à l'état haut
    temp |= 0x80; // Met le 8ème bit à 1
    }

    while(_ecSCK && _ecRST);// Attend que SCK passe à l'état bas
    i++; // Incrémente le compteur de un
    }
    return temp; // retourne ce qui a été reçu
    }

    Ce qui se passe maintenant, c'est que nous devons configurer notre microcontrôleur pour qu'il contienne les données que nous avons extraites. Ensuite, permettre à l'hôte de l'imprimante de lire et d'écrire dessus.

    Le problème avec cela est que je ne veux pas que mes fausses cartouches d'encre soient jamais épuisées. Pour ce faire, nous avons un dump fixe que le microcontrôleur chargera dans la RAM à chaque démarrage. Cela permettra à l'imprimante non seulement de lire, mais aussi d'écrire dessus. Lorsque le microcontrôleur est éteint puis rallumé, les données précédentes sont restaurées. (Double bonus)

    Stockage des tableaux de données dans le microcontrôleur, placez ceci quelque part sous l'inclusion, mais avant void main()

    Remarque : Cela peut varier selon les différentes familles de microcontrôleurs, mais cela fonctionne bien avec mon PIC18F

    ram unsigned char black[32] = {0x10,0x00,0x02,0xF2,0x00,0x00,0x00,0x00,0x00,0x50,0x90,0x6A,0x17,0xE3,0x54,0xA3,
    0x39,0x50,0x14,0x15,0x31,0x93,0xE4,0xB4,0x24,0x96,0x57,0x04,0x35,0xF5,0xE4,0x14};
    ram unsigned char cyan[32] = {0x4C,0x04,0x01,0xF2,0x00,0x00,0x00,0x00,0x00,0x50,0x90,0x5A,0x19,0xF0,0x94,0x03,
    0x3B,0x50,0x16,0x17,0x31,0x93,0xE4,0xB4,0x24,0x96,0x57,0x04,0x35,0xF5,0xE4,0x34};
    ram unsigned char yellow[32] = {0x4A,0x04,0x01,0xF2,0x00,0x00,0x00,0x00,0x00,0x50,0x90,0x6A,0x8B,0xC9,0xA4,0x41,
    0x3B,0x50,0x1A,0x1B,0x31,0x93,0xE4,0xB4,0x24,0x96,0x57,0x04,0x35,0xF5,0xE4,0x74};

    Remarque : Vous avez remarqué que je n'ai pas de magenta ? C'est parce que c'est la cartouche d'encre que je n'ai pas.

    L'étape suivante consiste à écrire une routine qui vérifie quelle cartouche d'encre est demandée et permet à l'hôte de lire et d'écrire dessus.
    Voici comment je l'ai fait :

    void main(){
    unsigned char* inkPtr;
    char addr = 0x00;
    ...

    While(1){

    epsnCartStartListen();

    while(epsnCartReady()==1); // Attend que le signal Ready repasse à l'état bas

    while(epsnCartReady()==0);
    addr = espnCartGetAddr();

    if(addr&0x7 == 0x01){ // si les 3 premiers bits sont égaux à 0x01
    inkPtr = &black[0]; // Pointe vers les données noires
    }else if(addr&0x7 == 0x03){ // si les 3 premiers bits sont égaux à 0x03
    inkPtr = &cyan[0]; // Pointe vers les données cyan
    }else if(addr[i]&0x7 == 0x05){
    inkPtr = &cyan[0]; // Ceci est censé être le magenta, mais je ne l'ai pas
    }else if(addr[i]&0x7 == 0x07){
    inkPtr = &yellow[0];
    }else{
    continue: // Retourne au début de la boucle principale car nous ne connaissons pas cette adresse
    }

    if(addr&0x08){//Mode Écriture – Vérifie si l'hôte veut écrire
    while(epsnCartReady()){//continue de boucler jusqu'à ce que RST passe à l'état bas
    *inkPtr = epsnCartIn();
    // Les données pointées par le pointeur
    // sont écrasées par les nouvelles données
    inkPtr++; // Incrémente le pointeur
    }
    }else{//Mode Lecture
    while(epsnCartReady()){
    epsnCartOut(*inkPtr);
    // Sort les données pointées par le pointeur
    inkPtr++; // Incrémente le pointeur
    }
    }
    }
    }

    Succès !
    Après avoir programmé ce nouveau code et allumé mon imprimante, l'imprimante a accepté 4 cartouches imaginaires grâce à mon microcontrôleur. Je suis également assez surpris qu'il ait accepté les données de la cartouche cyan pour le magenta aussi !

    Ce projet est un excellent exemple de la façon dont la rétro-ingénierie peut libérer le potentiel d'un matériel propriétaire. En comprenant le protocole de communication, j'ai effectué une extraction de firmware des contenus EEPROM, bien que les données soient petites. Dans un système plus complexe, il serait nécessaire de dumper la mémoire flash d'un microcontrôleur pour analyser son code. L'EEPROM de ces cartouches agit comme un petit stockage sécurisé, mais sans aucun bit de verrouillage ni chiffrement, il est trivial de lire les données. Certaines cartouches d'imprimante modernes incluent un mécanisme de verrouillage qui empêche la réutilisation, mais ici la seule protection était le protocole obscur. Cette obscurité n'est pas de la sécurité, comme je l'ai prouvé en émulant avec succès la cartouche manquante. La capacité de dupliquer l'identité de la cartouche permettrait des recharges infinies, ce qui est exactement ce que les fabricants d'imprimantes essaient d'empêcher. Ils utilisent souvent des fusibles ou des bits programmables une fois pour marquer une cartouche comme vide, mais cette EEPROM est entièrement réinscriptible. Mon microcontrôleur agit maintenant comme un homme du milieu, interceptant les requêtes de lecture et servant les données stockées. C'est essentiellement une forme simple de récupération de firmware pour l'imprimante, car j'ai restauré la pleine fonctionnalité sans la pièce d'origine. Le code que j'ai écrit est petit mais efficace, et il pourrait être adapté à d'autres dispositifs avec des EEPROM série similaires. Je me suis souvent demandé si les fabricants d'imprimantes laissent intentionnellement ces vulnérabilités pour permettre les cartouches tierces. Quoi qu'il en soit, ce projet m'a beaucoup appris sur les protocoles matériels et les systèmes embarqués. Le MCU que j'ai utilisé, un PIC18F, était plus que capable de gérer les exigences de timing. En fait, j'aurais pu utiliser un microcontrôleur plus petit, mais le PIC m'a donné beaucoup de place pour de futures extensions. L'ensemble fonctionne en 3,3 V, ce qui correspond à la tension interne de la cartouche. Un défi était de s'assurer que la ligne SDA était correctement pilotée dans les deux sens, car l'imprimante et mon MCU alternent le contrôle. J'ai dû gérer soigneusement les registres TRIS pour éviter les conflits de bus. C'est un problème courant dans tout système multi-maîtres, mais ici c'est un seul hôte et un esclave émulé. Les diagrammes de timing du brevet ont été inestimables ; ils m'ont donné les relations exactes des fronts d'horloge. Sans cela, j'aurais passé des jours à deviner. Cette expérience souligne l'importance de la documentation, même si elle est cachée dans les brevets. J'ai aussi appris que parfois la solution la plus simple est la meilleure : stocker le dump dans la RAM et le rafraîchir à la mise sous tension. Cela garantit que les niveaux d'encre ne diminuent jamais, ce qui est un bonus sympa. Cependant, pour un produit réel, je devrais envisager l'usure et la protection contre les pertes de puissance. Mais pour un projet amateur, c'est parfait. Je suis maintenant confiant que je peux aborder des tâches de rétro-ingénierie plus complexes à l'avenir. La prochaine étape pourrait impliquer l'écoute de la communication entre l'imprimante et les pilotes du moteur de la tête d'impression. Cela me permettrait de contrôler précisément le mouvement XY pour ma conversion en imprimante 3D. Mais c'est une histoire pour un autre instructable. Pour l'instant, je suis ravi que mon émulation fonctionne parfaitement. J'ai même testé avec plusieurs cycles d'alimentation et elle conserve les données à chaque fois. Le tableau RAM est réinitialisé avec les valeurs codées en dur à chaque démarrage, donc la cartouche apparaît toujours comme neuve. L'imprimante ne se plaint jamais des cartouches contrefaites car les données sont une copie parfaite d'une cartouche authentique. C'est essentiellement une duplication de l'identité de la cartouche d'origine. Dans un cadre commercial, une telle technique pourrait être utilisée pour un clonage non autorisé, mais ici c'est pour un réemploi légitime. Je fais attention à ne l'utiliser que pour mes propres projets. Les connaissances acquises grâce à cet exercice sont applicables à de nombreux autres appareils électroniques grand public qui utilisent des mémoires série simples. De nombreux appareils ont des EEPROM similaires qui stockent des données d'étalonnage ou des numéros de série. En les lisant, vous pouvez sauvegarder et restaurer des informations critiques. C'est une forme d'extraction de firmware en soi, même si le firmware n'est que des données de configuration. J'ai vu des projets où les gens décryptent le firmware d'un compteur intelligent en utilisant des méthodes similaires. Mais cela implique une cryptographie beaucoup plus complexe et souvent un bit de verrouillage pour empêcher la lecture. Ici, il n'y a pas de tel verrou, donc c'est grand ouvert. Certains pourraient dire que c'est une faille de sécurité, mais pour moi c'est une opportunité. J'ai déjà commencé à planifier une version qui utilise un ATtiny plus petit pour économiser de l'espace. La taille du code est minuscule, donc cela pourrait tenir dans un boîtier 8 broches. Je pourrais même ajouter une fonctionnalité pour simuler différents types de cartouches via un commutateur DIP. Ce serait pratique pour tester diverses encres. Les possibilités sont infinies. J'envisage également de partager mon code sur GitHub pour que d'autres puissent l'adapter. Cette approche open-source encourage la collaboration et l'innovation. De nombreux hackers ont déjà fait des choses similaires avec d'autres marques d'imprimantes. La communauté est très active dans la rétro-ingénierie des consommables pour réduire les déchets et économiser de l'argent. C'est gagnant-gagnant pour l'environnement et l'utilisateur. J'espère que mon instructable incitera d'autres à regarder sous le capot de leurs propres appareils. Avec un analyseur logique et un peu de patience, vous pouvez découvrir des protocoles cachés. La clé est de commencer petit, comme je l'ai fait avec cette simple EEPROM. Une fois que vous maîtrisez les bases, vous pouvez passer à des cibles plus difficiles. J'attends avec impatience le jour où je pourrai imprimer en 3D avec mon imprimante convertie. Ce n'est que la première étape. La prochaine grande étape est d'interfacer les pilotes de la tête d'impression et de construire le cadre mécanique. Mais c'est un projet pour une autre saison. Pour l'instant, je savoure cette victoire. C'est une preuve tangible que la rétro-ingénierie n'est pas réservée aux pirates ; c'est une compétence précieuse pour tout ingénieur. En comprenant comment les choses fonctionnent au niveau le plus bas, vous pouvez vraiment les faire vôtres. C'est l'esprit du "making" et du bricolage. Alors allez-y, prenez une vieille imprimante et commencez à sonder. Vous serez surpris de ce que vous pouvez accomplir. Et rappelez-vous, documentez toujours vos découvertes pour que d'autres puissent apprendre de votre parcours. C'est ainsi que notre communauté grandit et évolue. Sur ce, je vais conclure cette section. Profitez du reste de l'instructable et bon bricolage !

  • Questions générales sur l'extraction de firmware pour microcontrôleur


      • Est-il sûr d'envoyer un paiement à MikaTech ?

        Si MikaTech était une mauvaise entreprise, vous trouveriez des tonnes de mauvaises réputations sur ses services sur internet au cours de ses 28 ans d'histoire

        Donc, la réponse est OUI ! Nous sommes de bonnes personnes.

        Pourquoi choisir Mikatech, veuillez cliquer ici pour le savoir


      • Mikatech peut-il casser des circuits intégrés non listés sur ce site ?

        Différents fabricants de puces ont des numéros de pièces différents, mais le cœur interne de la puce peut être fabriqué avec la même technologie. Il serait tout à fait impossible de lister tous les numéros de pièces auxquels notre technologie peut s'appliquer, comme MYSON, STK, FEELING, ANALOG, FUJITSU, NOVATEK, LG/HYNDAI.

        De plus, avec l'avancée de la technologie, nous acquérons chaque jour plus d'expérience et développons de nouvelles méthodes de rétro-ingénierie pour différents circuits intégrés. La liste complète des numéros de pièces de circuits intégrés relevant de notre champ de compétence ne cesse de s'agrandir, veuillez nous contacter pour en savoir plus.

      • Ma vie privée sera-t-elle protégée ?

        Mikatech Innovative Limited comprend l'importance de la vie privée de ses clients. Dès que vous contactez Mikatech, vos informations personnelles sont placées sous la protection de notre règlement de gestion, développé au fil de nos années de pratique. Mikatech utilise ces informations pour personnaliser ses services à votre égard et ne les divulguera jamais à des tiers, pour quelque raison que ce soit.
        Pour chaque projet que nous réalisons, nous supprimons toutes les données, tous les matériaux et tous les codes 60 jours après la livraison des fichiers, ce qui nous protège et protège votre vie privée.

      • Est-il légal de faire appel aux services de Mikatech ?

        Oui, c'est tout à fait légal.
        Mikatech fournit ses services de rétro-ingénierie à des fins éducatives uniquement. L'utilisation des services mentionnés ci-dessus peut être illégale dans certains pays ou régions, veuillez vérifier les lois locales. Mikatech n'assume aucune responsabilité concernant l'utilisation des services mentionnés ci-dessus qui pourrait être considérée comme illégale.


      • Je vous ai envoyé un email, pourquoi n'y a-t-il pas de réponse ?

        • A. Notre serveur de messagerie est temporairement en panne, votre message n'a pas été livré dans notre boîte aux lettres même si le message d'envoi réussi s'affiche à l'écran, veuillez nous recontacter.
        • B. Notre email est reconnu comme courrier indésirable par votre serveur, donc notre réponse a été rejetée par votre serveur ou elle est redirigée vers votre dossier de courriers indésirables, veuillez supprimer notre compte de la liste des indésirables ou vérifier votre dossier de courriers indésirables, ou utilisez un autre compte email comme gmail.
        • C. Votre email est reconnu comme courrier indésirable par notre serveur, donc votre email a été placé dans notre dossier de courriers indésirables, veuillez utiliser un autre compte email pour nous recontacter.

        temps de piratage microcontrôleur

        Années

        28 +
        pays de piratage microcontrôleur

        Pays

        110 +
        clients attaque microcontrôleur

        Clients

        5000 +
        projets microcontrôleur déverrouillés

        Projets

        60000 +
        extraction de firmware microcontrôleur embarqué