Renifleur pour réseau local avec constructeur. Nous écrivons un simple renifleur pour Windows. Conversion d'un en-tête IP en chaîne

Dans cet article, nous examinerons la création d'un simple renifleur pour le système d'exploitation Windows.
Toute personne intéressée, bienvenue sur Cat.

Introduction

Cible:écrire un programme qui capturera le trafic réseau (Ethernet, WiFi) transmis via le protocole IP.
Installations: Visual Studio 2005 ou supérieur.
L'approche décrite ici n'appartient pas personnellement à l'auteur et est utilisée avec succès dans de nombreux programmes commerciaux ainsi que dans des programmes entièrement gratuits (bonjour, GPL).
Cet ouvrage s'adresse en priorité aux débutants en programmation réseau, qui possèdent cependant au moins des connaissances de base dans le domaine des sockets en général, et des sockets Windows en particulier. Ici, j'écrirai souvent des choses connues, car le domaine est spécifique, si je manque quelque chose, j'aurai la tête en désordre.

J'espère que vous trouverez cela intéressant.

Théorie (la lecture n'est pas obligatoire, mais recommandée)

À l’heure actuelle, la grande majorité des réseaux d’information modernes reposent sur la pile de protocoles TCP/IP. La pile de protocoles TCP/IP (Transmission Control Protocol/Internet Protocol) est un nom collectif désignant les protocoles réseau de différents niveaux utilisés dans les réseaux. Dans cet article, nous nous intéresserons principalement au protocole IP - un protocole réseau routé utilisé pour la livraison non garantie de données divisées en soi-disant paquets (un terme plus correct est datagramme) d'un nœud du réseau à un autre.
Les paquets IP conçus pour transmettre des informations nous intéressent particulièrement. Il s'agit d'un niveau assez élevé du modèle de données réseau OSI, où vous pouvez vous isoler de l'appareil et du support de transmission de données, en fonctionnant uniquement avec une représentation logique.
Il est tout à fait logique que tôt ou tard des outils d'interception, de surveillance, d'enregistrement et d'analyse du trafic réseau soient apparus. De tels outils sont généralement appelés analyseurs de trafic, analyseurs de paquets ou renifleurs (de l'anglais sniff - sniff). Il s'agit d'un analyseur de trafic réseau, d'un programme ou d'un dispositif matériel-logiciel conçu pour intercepter et ensuite analyser, ou uniquement analyser, le trafic réseau destiné à d'autres nœuds.

Pratique (conversation de fond)

À l'heure actuelle, de nombreux logiciels ont été créés pour écouter le trafic. Le plus célèbre d’entre eux : Wireshark. Bien entendu, le but n'est pas de récolter ses lauriers - nous nous intéressons à la tâche d'intercepter le trafic par une simple « écoute » d'une interface réseau. Il est important de comprendre que nous n'allons pas pirater et intercepter étranger trafic. Il nous suffit de visualiser et d'analyser le trafic qui transite par notre hébergeur.

Pourquoi cela peut être nécessaire :

  1. Affichez le flux de trafic actuel via la connexion réseau (entrant/sortant/total).
  2. Redirigez le trafic pour une analyse ultérieure vers un autre hôte.
  3. Théoriquement, vous pouvez essayer de l'utiliser pour pirater un réseau WiFi (on ne va pas faire ça, n'est-ce pas ?).
Contrairement à Wireshark, qui repose sur la bibliothèque libpcap/WinPcap, notre analyseur n'utilisera pas ce pilote. De plus, nous n’aurons pas de pilote du tout et nous n’allons pas écrire notre propre NDIS (oh quelle horreur !). Vous pouvez lire à ce sujet dans. Il sera simplement un observateur passif, utilisant seulement Bibliothèque WinSock. L'utilisation d'un pilote dans ce cas est redondante.

Comment ça? Très simple.
L'étape clé pour transformer une simple application réseau en analyseur de réseau consiste à faire passer l'interface réseau en mode promiscuité, ce qui lui permettra de recevoir des paquets adressés à d'autres interfaces du réseau. Ce mode force la carte réseau à accepter toutes les trames, quelle que soit la personne à qui elles sont adressées sur le réseau.

À partir de Windows 2000 (NT 5.0), il est devenu très simple de créer un programme pour écouter un segment de réseau, car son pilote réseau vous permet de configurer le socket pour recevoir tous les paquets.

Activation du mode promiscuité
drapeau long = 1 ; Prise de courant ; #define SIO_RCVALL 0x98000001 ioctlsocket(socket, SIO_RCVALL, &RS_Flag);
Notre programme fonctionne sur des paquets IP et utilise la bibliothèque Windows Sockets version 2.2 et les sockets bruts. Afin d'accéder directement à un paquet IP, la socket doit être créée comme suit :
Création d'un socket brut
s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
Ici au lieu d'une constante SOCK_STREAM(protocole TCP) ou SOCK_DGRAM(protocole UDP), on utilise la valeur SOCK_RAW. D'une manière générale, travailler avec des sockets bruts n'est pas seulement intéressant du point de vue de la capture de trafic. En fait, nous obtenons un contrôle total sur la formation du package. Ou plutôt, on le forme manuellement, ce qui permet par exemple d'envoyer un paquet ICMP spécifique...

Poursuivre. On sait qu'un paquet IP est constitué d'un en-tête, d'informations de service et, en fait, de données. Je vous conseille de regarder ici pour rafraîchir vos connaissances. Décrivons l'entête IP sous forme de structure (grâce à l'excellent article sur RSDN) :

Description de la structure du paquet IP
typedef struct _IPHeader ( unsigned char ver_len; // version et longueur de l'en-tête non signé char tos; // type de service non signé longueur courte; // longueur du paquet entier non signé short id; // Id non signé court flgs_offset; // drapeaux et décalage non signés char ttl ; // protocole char non signé à vie ; // protocole non signé short xsum ; // somme de contrôle non signée long src ; // adresse IP de l'expéditeur non signée longue dest ; // adresse IP de destination non signée courte *params ; // paramètres (jusqu'à 320 bits) char non signé *data; // données (jusqu'à 65 535 octets) )IPHeader;
La fonction principale de l’algorithme d’écoute ressemblera à ceci :
Fonction de capture de paquet unique
IPHeader* RS_Sniff() ( IPHeader *hdr; int count = 0; count = recv(RS_SSocket, (char*)&RS_Buffer, sizeof(RS_Buffer), 0); if (count >= sizeof(IPHeader)) ( hdr = (LPIPHeader )malloc(MAX_PACKET_SIZE); memcpy(hdr, RS_Buffer, MAX_PACKET_SIZE); RS_UpdateNetStat(count, hdr); return hdr; ) sinon return 0; )
Tout est simple ici : on reçoit une donnée en utilisant la fonction socket standard recv, puis copiez-les dans une structure comme En-tête IP.
Et enfin, nous démarrons une boucle sans fin de capture de paquets :
Capturons tous les paquets qui atteignent notre interface réseau
while (true) ( ​​​​​IPHeader* hdr = RS_Sniff(); // traitement du paquet IP if (hdr) ( // imprimer l'en-tête dans la console ) )
Un peu hors sujet
Ici et ci-dessous, l'auteur a créé le préfixe RS_ (de Raw Sockets) pour certaines fonctions et variables importantes. J'ai réalisé le projet il y a 3-4 ans et j'ai eu l'idée folle d'écrire une bibliothèque à part entière pour travailler avec des sockets bruts. Comme cela arrive souvent, après avoir obtenu des résultats significatifs (pour l'auteur), l'enthousiasme s'est estompé et l'affaire n'est pas allée plus loin qu'un exemple pédagogique.

En principe, vous pouvez aller plus loin et décrire les en-têtes de tous les protocoles ultérieurs situés ci-dessus. Pour ce faire, vous devez analyser le terrain protocole dans la structure En-tête IP. Regardez l'exemple de code (oui, il devrait y avoir un commutateur, bon sang !), où l'en-tête est coloré en fonction du protocole que le paquet a encapsulé en IP :

/* * Mise en surbrillance d'un package avec une couleur */ void ColorPacket(const IPHeader *h, const u_long haddr, const u_long whost = 0) ( if (h->xsum) SetConsoleTextColor(0x17); // si le package n'est pas vide else SetConsoleTextColor(0x07) ; // package vide if (haddr == h->src) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_RED | FOREGROUND_INTENSITY); // package "natif" pour le retour) else if (haddr == h->dest ) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_GREEN | FOREGROUND_INTENSITY); // paquet de réception "natif" ) if (h->protocol == PROT_ICMP || h->protocol == PROT_IGMP) ( SetConsoleTextColor (0x70) ; // Paquet ICMP ) else if(h->protocol == PROT_IP || h->protocol == 115) ( SetConsoleTextColor(0x4F); // Paquet IP-in-IP, L2TP ) else if(h ->protocol == 53 || h->protocol == 56) ( SetConsoleTextColor(0x4C); // TLS, IP avec cryptage) if(whost == h->dest || whost == h->src) ( SetConsoleTextColor (0x0A); ) )

Cependant, cela dépasse largement le cadre de cet article. Pour notre exemple de formation, il suffira de regarder les adresses IP des hôtes d'où et vers lesquels provient le trafic, et de calculer sa quantité par unité de temps (le programme fini est dans l'archive en fin d'article) .

Afin d'afficher les données d'en-tête IP, vous devez implémenter une fonction pour convertir l'en-tête (mais pas les données) du datagramme en chaîne. A titre d'exemple de mise en œuvre, nous pouvons proposer l'option suivante :

Conversion d'un en-tête IP en chaîne
inline char* iph2str(IPHeader *iph) ( const int BUF_SIZE = 1024; char *r = (char*)malloc(BUF_SIZE); memset((void*)r, 0, BUF_SIZE); sprintf(r, "ver=% d hlen=%d tos=%d len=%d id=%d flags=0x%X offset=%d ttl=%dms prot=%d crc=0x%X src=%s dest=%s", BYTE_H (iph->ver_len), BYTE_L(iph->ver_len)*4, iph->tos, ntohs(iph->longueur), ntohs(iph->id), IP_FLAGS(ntohs(iph->flgs_offset)), IP_OFFSET (ntohs(iph->flgs_offset)), iph->ttl, iph->protocole, ntohs(iph->xsum), nethost2str(iph->src), nethost2str(iph->dest)); return r; )
Sur la base des informations de base données ci-dessus, nous obtenons ce petit programme (nom effrayant ss, abréviation de simple sniffer), qui implémente l'écoute locale du trafic IP. Son interface est illustrée ci-dessous dans la figure.

Je fournis le code source et binaire tel quel, comme c'était le cas il y a plusieurs années. Maintenant, j'ai peur de le regarder, et pourtant, c'est assez lisible (bien sûr, on ne peut pas avoir autant confiance en soi). Même Visual Studio Express 2005 suffira pour la compilation.

Ce que nous avons obtenu :

  • Le renifleur fonctionne en mode utilisateur, mais nécessite des privilèges d'administrateur.
  • Les paquets ne sont pas filtrés et sont affichés tels quels (vous pouvez ajouter des filtres personnalisés - je suggère d'examiner ce sujet en détail dans le prochain article si vous êtes intéressé).
  • Le trafic WiFi est également capturé (tout dépend du modèle de puce spécifique, cela peut ne pas fonctionner pour vous, comme cela l'a fait pour moi il y a plusieurs années), bien qu'il existe AirPcap, qui peut le faire à merveille, mais coûte de l'argent.
  • L'intégralité du flux de datagramme est enregistré dans un fichier (voir l'archive jointe à la fin de l'article).
  • Le programme fonctionne comme un serveur sur le port 2000. Vous pouvez vous connecter à l'hôte à l'aide de l'utilitaire telnet et surveiller les flux de trafic. Le nombre de connexions est limité à vingt (le code n'est pas le mien, je l'ai trouvé sur Internet et je l'ai utilisé pour des expériences ; je ne l'ai pas supprimé - c'est dommage)
Merci pour votre attention, je félicite les habitants de Khabrovsk et les habitants de Khabrovka et à tous, Joyeux Noël !

Dans cet article, nous examinerons la création d'un simple renifleur pour le système d'exploitation Windows.
Toute personne intéressée, bienvenue sur Cat.

Introduction

Cible:écrire un programme qui capturera le trafic réseau (Ethernet, WiFi) transmis via le protocole IP.
Installations: Visual Studio 2005 ou supérieur.
L'approche décrite ici n'appartient pas personnellement à l'auteur et est utilisée avec succès dans de nombreux programmes commerciaux ainsi que dans des programmes entièrement gratuits (bonjour, GPL).
Cet ouvrage s'adresse en priorité aux débutants en programmation réseau, qui possèdent cependant au moins des connaissances de base dans le domaine des sockets en général, et des sockets Windows en particulier. Ici, j'écrirai souvent des choses connues, car le domaine est spécifique, si je manque quelque chose, j'aurai la tête en désordre.

J'espère que vous trouverez cela intéressant.

Théorie (la lecture n'est pas obligatoire, mais recommandée)

À l’heure actuelle, la grande majorité des réseaux d’information modernes reposent sur la pile de protocoles TCP/IP. La pile de protocoles TCP/IP (Transmission Control Protocol/Internet Protocol) est un nom collectif désignant les protocoles réseau de différents niveaux utilisés dans les réseaux. Dans cet article, nous nous intéresserons principalement au protocole IP - un protocole réseau routé utilisé pour la livraison non garantie de données divisées en soi-disant paquets (un terme plus correct est datagramme) d'un nœud du réseau à un autre.
Les paquets IP conçus pour transmettre des informations nous intéressent particulièrement. Il s'agit d'un niveau assez élevé du modèle de données réseau OSI, où vous pouvez vous isoler de l'appareil et du support de transmission de données, en fonctionnant uniquement avec une représentation logique.
Il est tout à fait logique que tôt ou tard des outils d'interception, de surveillance, d'enregistrement et d'analyse du trafic réseau soient apparus. De tels outils sont généralement appelés analyseurs de trafic, analyseurs de paquets ou renifleurs (de l'anglais sniff - sniff). Il s'agit d'un analyseur de trafic réseau, d'un programme ou d'un dispositif matériel-logiciel conçu pour intercepter et ensuite analyser, ou uniquement analyser, le trafic réseau destiné à d'autres nœuds.

Pratique (conversation de fond)

À l'heure actuelle, de nombreux logiciels ont été créés pour écouter le trafic. Le plus célèbre d’entre eux : Wireshark. Bien entendu, le but n'est pas de récolter ses lauriers - nous nous intéressons à la tâche d'intercepter le trafic par une simple « écoute » d'une interface réseau. Il est important de comprendre que nous n'allons pas pirater et intercepter étranger trafic. Il nous suffit de visualiser et d'analyser le trafic qui transite par notre hébergeur.

Pourquoi cela peut être nécessaire :

  1. Affichez le flux de trafic actuel via la connexion réseau (entrant/sortant/total).
  2. Redirigez le trafic pour une analyse ultérieure vers un autre hôte.
  3. Théoriquement, vous pouvez essayer de l'utiliser pour pirater un réseau WiFi (on ne va pas faire ça, n'est-ce pas ?).

Contrairement à Wireshark, qui repose sur la bibliothèque libpcap/WinPcap, notre analyseur n'utilisera pas ce pilote. De plus, nous n’aurons pas de pilote du tout et nous n’allons pas écrire notre propre NDIS (oh quelle horreur !). Vous pouvez lire à ce sujet dans ce sujet. Il sera simplement un observateur passif, utilisant seulement Bibliothèque WinSock. L'utilisation d'un pilote dans ce cas est redondante.

Comment ça? Très simple.
L'étape clé pour transformer une simple application réseau en analyseur de réseau consiste à faire passer l'interface réseau en mode promiscuité, ce qui lui permettra de recevoir des paquets adressés à d'autres interfaces du réseau. Ce mode force la carte réseau à accepter toutes les trames, quelle que soit la personne à qui elles sont adressées sur le réseau.

À partir de Windows 2000 (NT 5.0), il est devenu très simple de créer un programme pour écouter un segment de réseau, car son pilote réseau vous permet de configurer le socket pour recevoir tous les paquets.

Activation du mode promiscuité
drapeau long = 1 ; Prise de courant ; #define SIO_RCVALL 0x98000001 ioctlsocket(socket, SIO_RCVALL, &RS_Flag);

Notre programme fonctionne sur des paquets IP et utilise la bibliothèque Windows Sockets version 2.2 et les sockets bruts. Afin d'accéder directement à un paquet IP, la socket doit être créée comme suit :

Création d'un socket brut
s = socket(AF_INET, SOCK_RAW, IPPROTO_IP);

Ici au lieu d'une constante SOCK_STREAM(protocole TCP) ou SOCK_DGRAM(protocole UDP), on utilise la valeur SOCK_RAW. D'une manière générale, travailler avec des sockets bruts n'est pas seulement intéressant du point de vue de la capture de trafic. En fait, nous obtenons un contrôle total sur la formation du package. Ou plutôt, on le forme manuellement, ce qui permet par exemple d'envoyer un paquet ICMP spécifique...

Poursuivre. On sait qu'un paquet IP est constitué d'un en-tête, d'informations de service et, en fait, de données. Je vous conseille de regarder ici pour rafraîchir vos connaissances. Décrivons l'entête IP sous forme de structure (grâce à l'excellent article sur RSDN) :

Description de la structure du paquet IP
typedef struct _IPHeader ( unsigned char ver_len; // version et longueur de l'en-tête non signé char tos; // type de service non signé longueur courte; // longueur du paquet entier non signé short id; // Id non signé court flgs_offset; // drapeaux et décalage non signés char ttl ; // protocole char non signé à vie ; // protocole non signé short xsum ; // somme de contrôle non signée long src ; // adresse IP de l'expéditeur non signée longue dest ; // adresse IP de destination non signée courte *params ; // paramètres (jusqu'à 320 bits) char non signé *data; // données (jusqu'à 65 535 octets) )IPHeader;

La fonction principale de l’algorithme d’écoute ressemblera à ceci :

Fonction de capture de paquet unique
IPHeader* RS_Sniff() ( IPHeader *hdr; int count = 0; count = recv(RS_SSocket, (char*)&RS_Buffer, sizeof(RS_Buffer), 0); if (count >= sizeof(IPHeader)) ( hdr = (LPIPHeader )malloc(MAX_PACKET_SIZE); memcpy(hdr, RS_Buffer, MAX_PACKET_SIZE); RS_UpdateNetStat(count, hdr); return hdr; ) sinon return 0; )

Tout est simple ici : on reçoit une donnée en utilisant la fonction socket standard recv, puis copiez-les dans une structure comme En-tête IP.
Et enfin, nous démarrons une boucle sans fin de capture de paquets :

Capturons tous les paquets qui atteignent notre interface réseau
while (true) ( ​​​​​IPHeader* hdr = RS_Sniff(); // traitement du paquet IP if (hdr) ( // imprimer l'en-tête dans la console ) )
Un peu hors sujet

Ici et ci-dessous, l'auteur a créé le préfixe RS_ (de Raw Sockets) pour certaines fonctions et variables importantes. J'ai réalisé le projet il y a 3-4 ans et j'ai eu l'idée folle d'écrire une bibliothèque à part entière pour travailler avec des sockets bruts. Comme cela arrive souvent, après avoir obtenu des résultats significatifs (pour l'auteur), l'enthousiasme s'est estompé et l'affaire n'est pas allée plus loin qu'un exemple pédagogique.

En principe, vous pouvez aller plus loin et décrire les en-têtes de tous les protocoles ultérieurs situés ci-dessus. Pour ce faire, vous devez analyser le terrain protocole dans la structure En-tête IP. Regardez l'exemple de code (oui, il devrait y avoir un commutateur, bon sang !), où l'en-tête est coloré en fonction du protocole que le paquet a encapsulé en IP :

/* * Mise en surbrillance d'un package avec une couleur */ void ColorPacket(const IPHeader *h, const u_long haddr, const u_long whost = 0) ( if (h->xsum) SetConsoleTextColor(0x17); // si le package n'est pas vide else SetConsoleTextColor(0x07) ; // package vide if (haddr == h->src) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_RED | FOREGROUND_INTENSITY); // package "natif" pour le retour) else if (haddr == h->dest ) ( SetConsoleTextColor(BACKGROUND_BLUE | /*BACKGROUND_INTENSITY |*/ FOREGROUND_GREEN | FOREGROUND_INTENSITY); // paquet de réception "natif" ) if (h->protocol == PROT_ICMP || h->protocol == PROT_IGMP) ( SetConsoleTextColor (0x70) ; // Paquet ICMP ) else if(h->protocol == PROT_IP || h->protocol == 115) ( SetConsoleTextColor(0x4F); // Paquet IP-in-IP, L2TP ) else if(h ->protocol == 53 || h->protocol == 56) ( SetConsoleTextColor(0x4C); // TLS, IP avec cryptage) if(whost == h->dest || whost == h->src) ( SetConsoleTextColor (0x0A); ) )

Cependant, cela dépasse largement le cadre de cet article. Pour notre exemple de formation, il suffira de regarder les adresses IP des hôtes d'où et vers lesquels provient le trafic, et de calculer sa quantité par unité de temps (le programme fini est dans l'archive en fin d'article) .

Afin d'afficher les données d'en-tête IP, vous devez implémenter une fonction pour convertir l'en-tête (mais pas les données) du datagramme en chaîne. A titre d'exemple de mise en œuvre, nous pouvons proposer l'option suivante :

Conversion d'un en-tête IP en chaîne
inline char* iph2str(IPHeader *iph) ( const int BUF_SIZE = 1024; char *r = (char*)malloc(BUF_SIZE); memset((void*)r, 0, BUF_SIZE); sprintf(r, "ver=% d hlen=%d tos=%d len=%d id=%d flags=0x%X offset=%d ttl=%dms prot=%d crc=0x%X src=%s dest=%s", BYTE_H (iph->ver_len), BYTE_L(iph->ver_len)*4, iph->tos, ntohs(iph->longueur), ntohs(iph->id), IP_FLAGS(ntohs(iph->flgs_offset)), IP_OFFSET (ntohs(iph->flgs_offset)), iph->ttl, iph->protocole, ntohs(iph->xsum), nethost2str(iph->src), nethost2str(iph->dest)); return r; )

Sur la base des informations de base données ci-dessus, nous obtenons ce petit programme (nom effrayant ss, abréviation de simple sniffer), qui implémente l'écoute locale du trafic IP. Son interface est illustrée ci-dessous dans la figure.

Je fournis le code source et binaire tel quel, comme c'était le cas il y a plusieurs années. Maintenant, j'ai peur de le regarder, et pourtant, c'est assez lisible (bien sûr, on ne peut pas avoir autant confiance en soi). Même Visual Studio Express 2005 suffira pour la compilation.

Ce que nous avons obtenu :

  • Le renifleur fonctionne en mode utilisateur, mais nécessite des privilèges d'administrateur.
  • Les paquets ne sont pas filtrés et sont affichés tels quels (vous pouvez ajouter des filtres personnalisés - je suggère d'examiner ce sujet en détail dans le prochain article si vous êtes intéressé).
  • Le trafic WiFi est également capturé (tout dépend du modèle de puce spécifique, cela peut ne pas fonctionner pour vous, comme cela l'a fait pour moi il y a plusieurs années), bien qu'il existe AirPcap, qui peut le faire à merveille, mais coûte de l'argent.
  • L'intégralité du flux de datagramme est enregistré dans un fichier (voir l'archive jointe à la fin de l'article).
  • Le programme fonctionne comme un serveur sur le port 2000. Vous pouvez vous connecter à l'hôte à l'aide de l'utilitaire telnet et surveiller les flux de trafic. Le nombre de connexions est limité à vingt (le code n'est pas le mien, je l'ai trouvé sur Internet et je l'ai utilisé pour des expériences ; je ne l'ai pas supprimé - c'est dommage)

Merci de votre attention, je vous souhaite, ainsi qu'à tous, un joyeux Noël !

Bon après-midi D'une manière ou d'une autre, un problème est survenu au travail - il y avait un appareil qui fonctionnait via I2C et dont il fallait comprendre le protocole. Par conséquent, nous avons besoin d'un renifleur pour l'interface I2C, qui transmettrait tout ce qui va et vient via I2C vers le port UART, puis via le convertisseur vers le port COM de l'ordinateur.

Commencer

Je n'avais que quelques Atmeg8 qui traînaient et j'ai décidé pourquoi ne pas les utiliser. Se pose ensuite la question du circuit renifleur.

Il y avait 2 options : allumer le renifleur en parallèle ou en circuit ouvert. Évidemment, la première option semble beaucoup plus simple, ce qui s'est en réalité révélé complètement faux. Mais tout d’abord.

En bref, à propos de l'interface elle-même. I2C (TWI dans Atmel) utilise deux fils – SCL et SDA. Le premier est chargé de synchroniser le signal, le second est chargé de transmettre directement les informations. L'interface dispose également d'états START et STOP.

Donc, ma première pensée a été de prendre la sonde et, d'une part, de la connecter à la jambe d'interruption externe sur atmega8, de l'autre, à la ligne SDA et d'attraper le bord d'attaque, et de déterminer 0 ou 1 par le temps écoulé. Évidemment, cela aurait dû fonctionner très mal, puisque le signal STOP n'a pas été traité correctement.

La deuxième idée était de faire la même chose, mais de détecter l'interruption sur la ligne SCL et de lire la ligne SDA connectée à un tronçon numérique ordinaire en utilisant l'interruption. Ici, tout semblait plus viable, à l'exception du même état STOP, mais j'ai décidé d'essayer de l'assembler sur une maquette et de voir ce qui se passe.

Je m'excuse par avance si vous trouvez des erreurs évidentes dans le code ci-dessous, puisque je reconstruis de mémoire à genoux les versions de code rejetées.

Le code du gestionnaire d'interruption ressemblait à ceci :

ISR(INT0_vect) ( cli(); if (bitIsHigh(PINB, 0)) uart_send_char("1"); else uart_send_char("0"); sei(); )
Des zéros et des uns affluaient dans le port, mais il est immédiatement devenu évident que les données étaient incorrectes - il y en avait beaucoup moins que prévu et elles changeaient lorsque la même demande était répétée. Dans le processus de recherche des raisons, tout se résumait au fait que des données avaient été perdues en raison de l'accès à l'interface UART, qui, soit dit en passant, fonctionnait à une vitesse maximale stable de 38 kbit/s, tandis qu'I2C lui-même fonctionnait. à 100 kbit/s. Il n'a pas été possible d'augmenter la vitesse de l'UART en raison du manque de cristal de la fréquence requise pour amener l'UART à une vitesse acceptable. Par conséquent, il était nécessaire de supprimer le travail avec l'uart de l'interruption. J'ai quelque chose comme ça :

Données statiques uint8_t = 0 ; statique uint8_t idx = 7 ; ISR(INT0_vect) ( cli(); données |= bitIsHigh(PINB, 0)<< (idx--); if (!idx) { uart_send_char(data); data = 0; idx = 7; } sei(); }
Tout est devenu plus stable, mais les données n’avaient toujours aucun sens. Après plusieurs heures de travail sur l'algorithme, d'activation du traitement STOP, etc., il a été décidé d'emprunter une voie différente.

Sur la bonne voie

Peu importe combien j'ai essayé de mettre en œuvre le renifleur à l'aide d'un circuit parallèle, rien n'en est sorti. Sur cette base, il ne restait qu'une seule option: il était nécessaire d'inclure le microcontrôleur dans l'espace, c'est-à-dire qu'il devait être à la fois un maître pour le dispositif de réponse et un esclave pour le maître d'origine. Cela semble probablement déroutant, mais en réalité ce n’est pas le cas.

Étant donné qu'atmega8 n'a qu'un seul I2C matériel à bord, il est évident que pour fonctionner, vous devez écrire un support logiciel pour le protocole.

Le résultat était le code suivant :

ISR(TWI_vect) ( cli(); uint8_t status = TWSR; uint8_t b; char s; s = 0; _delay_ms(1); switch (status & I2C_STATUS_MASK) ( case I2C_STATUS_SR_RX_ADR_ACK:/* case I2C_STATUS_SR_RX_ADR_NACK:*/ uart_send_str("- AW:"); uart_send_int(TWDR); i2csoft_start(); i2csoft_open_write(I2C_ADDRESS); break; cas I2C_STATUS_SR_RX_DATA_ACK:/* cas I2C_STATUS_SR_RX_DATA_NACK:*/ b = TWDR; sprintf(s, " %.2X", b); uart_send_str( s); i2csoft_write_byte(b); break; cas I2C_STATUS_SR_RX_STOP_RESTART: uart_send_str("E\n"); _delay_ms(10); do ( _delay_us(5); i2csoft_start(); ) while (!i2csoft_open_read(I2C_ADDRESS)); break; cas I2C_STATUS_BUS_ERROR : uart_send_str("B\n"); break; cas TW_ST_SLA_ACK : uart_send_str("-AR:"); uart_send_int(TWDR); b = i2csoft_read_byte(); sprintf(s, " %.2X", b); uart_send_str(s); TWDR = b; break; cas TW_ST_DATA_ACK: b = i2csoft_read_byte(); sprintf(s, " %.2X", b); uart_send_str(s); TWDR = b; break; cas TW_ST_DATA_NACK : cas TW_ST_LAST_DATA : b = i2csoft_read_byte(faux); uart_send_str("L\n"); casser; par défaut : uart_send_char("U"); uart_send_int(statut); uart_send_char(" "); casser; ) TWCR |= (1<Le périphérique maître est connecté au matériel I2C de l'atmega, le périphérique d'exécution est connecté à 2 pattes numériques quelconques, dont l'une fonctionne en mode SCL, l'autre en SDA. Tout le code ci-dessus ne fait que recevoir une interruption via I2C et provoquer un état similaire sur l'interface logicielle, tandis que les informations de service sont écrites dans l'uart pour aider à comprendre ce qui se passe. Les délais définis ont été sélectionnés pour un appareil spécifique et peuvent différer légèrement pour d'autres. En conséquence, nous obtenons un très bon renifleur.

Si quelqu'un est intéressé, les sources peuvent être extraites de