Comment déboguez-vous un format binaire?

11

J'aimerais pouvoir déboguer la construction d'un générateur binaire. En ce moment, je suis en train d'imprimer les données d'entrée sur l'analyseur binaire, puis d'aller en profondeur dans le code et d'imprimer le mappage de l'entrée sur la sortie, puis de prendre le mappage de sortie (entiers) et de l'utiliser pour localiser l'entier correspondant dans le binaire. Assez maladroit, et nécessite que je modifie profondément le code source pour obtenir le mappage entre l'entrée et la sortie.

Il semble que vous puissiez voir le binaire dans différentes variantes (dans mon cas, je voudrais le voir en morceaux de 8 bits sous forme de nombres décimaux, car c'est assez proche de l'entrée). En fait, certains nombres sont 16 bits, certains 8, certains 32, etc. Donc, il y aurait peut-être un moyen de visualiser le binaire avec chacun de ces différents nombres mis en évidence en mémoire d'une manière ou d'une autre.

La seule façon dont je pouvais voir que cela était possible était de créer un visualiseur spécifique au format / à la disposition binaire réel. Il sait donc où dans la séquence les numéros de 32 bits devraient être, et où les numéros de 8 bits devraient être, etc. C'est beaucoup de travail et un peu délicat dans certaines situations. Je me demande donc s'il y a une façon générale de le faire.

Je me demande également quelle est la façon générale de déboguer ce type de chose actuellement, alors peut-être que je peux avoir des idées sur ce qu'il faut essayer.

Lance Pollard
la source
75
Vous avez obtenu une réponse disant "utilisez directement l'hexdump, et faites ceci et cela en plus" - et cette réponse a reçu beaucoup de votes positifs. Et une deuxième réponse, 5 heures plus tard (!), Disant seulement "utiliser un hexdump". Vous avez alors accepté le second en faveur du premier? Sérieusement?
Doc Brown
4
Bien que vous ayez peut-être une bonne raison d'utiliser un format binaire, considérez si vous pouvez simplement utiliser un format de texte existant comme JSON à la place. La lisibilité humaine compte beaucoup, et les machines et les réseaux sont généralement assez rapides pour que l'utilisation d'un format personnalisé pour réduire la taille soit inutile de nos jours.
jpmc26
4
@ jpmc26 il y a encore beaucoup d'utilisation pour les formats binaires et le sera toujours. La lisibilité humaine est généralement secondaire aux performances, aux exigences de stockage et aux performances du réseau. Et il existe encore de nombreux domaines où les performances du réseau sont particulièrement médiocres et le stockage limité. N'oubliez pas non plus que tous les systèmes doivent s'interfacer avec les anciens systèmes (matériels et logiciels) et prendre en charge leurs formats de données.
jwenting
4
@jwenting Non, en fait, le temps de développement est généralement l'élément le plus cher d'une application. Bien sûr, cela peut ne pas être le cas si vous travaillez chez Google ou Facebook, mais la plupart des applications ne fonctionnent pas à cette échelle. Et lorsque vos développeurs passent du temps sur des trucs est la ressource la plus chère, la lisibilité humaine compte pour beaucoup plus que les 100 millisecondes supplémentaires pour que le programme les analyse.
jpmc26
3
@ jpmc26 Je ne vois rien dans la question qui me suggère que l'OP est celui qui définit le format.
JimmyJames

Réponses:

76

Pour les vérifications ad hoc, utilisez simplement un hexdump standard et apprenez à le regarder.

Si vous voulez vous préparer pour une enquête appropriée, j'écris généralement un décodeur séparé dans quelque chose comme Python - idéalement, il sera piloté directement à partir d'un document de spécification de message ou IDL, et sera aussi automatisé que possible (il n'y a donc aucune chance d'introduire manuellement le même bug dans les deux décodeurs).

Enfin, n'oubliez pas que vous devez écrire des tests unitaires pour votre décodeur, en utilisant une entrée fixe connue.

Inutile
la source
2
"utilisez simplement un hexdump standard et apprenez à le regarder." Ouaip. D'après mon expérience, plusieurs sections de quelque chose jusqu'à 200 bits peuvent être écrites sur un tableau blanc pour une comparaison groupée, ce qui aide parfois à ce genre de chose pour commencer.
Mât
1
Je trouve un décodeur séparé qui en vaut la peine si les données binaires jouent un rôle important dans l'application (ou le système en général). Cela est particulièrement vrai si le format des données est variable: les données dans des mises en page fixes peuvent être repérées dans un hexdump avec un peu de pratique mais heurtent rapidement un mur de praticabilité. Nous avons débogué le trafic USB et CAN avec des décodeurs de paquets commerciaux, et j'ai écrit un décodeur PROFIBus (où les variables se répartissent sur plusieurs octets, complètement illisibles dans un vidage hexadécimal), et les ai trouvées toutes les trois extrêmement utiles.
Peter - Réintègre Monica le
10

La première étape pour ce faire est que vous avez besoin d'un moyen de trouver ou de définir une grammaire qui décrit la structure des données, c'est-à-dire un schéma.

Un exemple de ceci est une fonction de langage de COBOL qui est officieusement connue sous le nom de copybook. Dans les programmes COBOL, vous définiriez la structure des données en mémoire. Cette structure correspondait directement à la façon dont les octets étaient stockés. Ceci est commun aux langages de cette époque par opposition aux langages contemporains courants où la disposition physique de la mémoire est une préoccupation d'implémentation qui est éloignée du développeur.

Une recherche google pour le langage de schéma de données binaires fait apparaître un certain nombre d'outils. Un exemple est Apache DFDL . Il peut également y avoir une interface utilisateur pour cela.

JimmyJames
la source
2
Cette fonctionnalité n'est pas réservée aux langues de l'ère «ancienne». Les structures et les unions C et C ++ peuvent être alignées en mémoire. C # a StructLayoutAttribute, que j'ai utilisé pour transmettre des données binaires.
Kasper van den Berg
1
@KaspervandenBerg À moins que vous ne disiez que C et C ++ les ont ajoutés récemment, je considère que c'est la même époque. Le fait est que ces formats n'étaient pas simplement destinés à la transmission de données, bien qu'ils aient été utilisés pour cela, ils étaient directement liés à la façon dont le code fonctionnait avec les données en mémoire et sur disque. Ce n'est pas, en général, comment les nouvelles langues ont tendance à fonctionner, bien qu'elles puissent avoir de telles fonctionnalités.
JimmyJames
@KaspervandenBerg C ++ ne fait pas cela autant que vous le pensez. Il est possible d'utiliser des outils spécifiques à l'implémentation pour aligner et éliminer le remplissage (et, certes, de plus en plus la norme ajoute des fonctionnalités pour ce genre de chose) et l'ordre des membres est déterministe (mais pas nécessairement le même qu'en mémoire!).
Courses de légèreté en orbite du
6

ASN.1 , Abstract Syntax Notation One, fournit un moyen de spécifier un format binaire.

  • DDT - Développez en utilisant des exemples de données et des tests unitaires.
  • Un vidage textuel peut être utile. Si en XML, vous pouvez réduire / développer les sous-hiérarchies.
  • ASN.1 n'est pas vraiment nécessaire mais une spécification de fichier basée sur la grammaire et plus déclarative est plus facile.
Joop Eggen
la source
6
Si le défilé sans fin des vulnérabilités de sécurité dans les analyseurs ASN.1 est une indication, son adoption fournirait certainement un bon exercice de débogage des formats binaires.
Mark
1
@Mark de nombreux tableaux de petits octets (et que dans des arborescences hiérarchiques variables) ne sont souvent pas traités correctement (en toute sécurité) en C (par exemple, sans utiliser d'exceptions). Ne sous-estimez jamais le faible niveau, l'insécurité inhérente de C. ASN.1 dans - par exemple - java n'expose pas ce problème. Comme une analyse dirigée par la grammaire ASN.1 peut être effectuée en toute sécurité, même C peut être effectué avec une base de code petite et sûre. Et une partie des vulnérabilités est inhérente au format binaire lui-même: on peut exploiter des constructions "légales" de la grammaire du format, qui ont une sémantique désastreuse.
Joop Eggen
3

D'autres réponses ont décrit la visualisation d'un vidage hexadécimal ou l'écriture de structures d'objets dans JSON. Je pense que combiner les deux est très utile.

L'utilisation d'un outil qui peut rendre le JSON au-dessus du vidage hexadécimal est vraiment utile; J'ai écrit un outil open source qui analysait les binaires .NET appelé dotNetBytes , voici une vue d'un exemple de DLL .

Exemple dotNetBytes

Carl Walsh
la source
1

Je ne suis pas sûr de bien comprendre, mais il semble que vous ayez un analyseur pour ce format binaire et que vous en contrôliez le code. Donc, cette réponse est construite sur cette hypothèse.

Un analyseur remplira en quelque sorte les structures, les classes ou toute autre structure de données de votre langage. Si vous implémentez un ToStringpour tout ce qui est analysé, vous vous retrouvez avec une méthode très facile à utiliser et facilement maintenue pour afficher ces données binaires dans un format lisible par l'homme.

Vous auriez essentiellement:

byte[] arrayOfBytes; // initialized somehow
Object obj = Parser.parse(arrayOfBytes);
Logger.log(obj.ToString());

Et c'est tout, du point de vue de son utilisation. Bien sûr, cela vous oblige à implémenter / remplacer la ToStringfonction pour votre Objectclasse / struct / quoi que ce soit, et vous devrez également le faire pour toutes les classes / structs / whatevers imbriquées.

Vous pouvez en outre utiliser une instruction conditionnelle pour empêcher la ToStringfonction d'être appelée dans le code de version afin de ne pas perdre de temps sur quelque chose qui ne sera pas enregistré en dehors du mode débogage.

Votre ToStringpourrait ressembler à ceci:

return String.Format("%d,%d,%d,%d", int32var, int16var, int8var, int32var2);

// OR

return String.Format("%s:%d,%s:%d,%s:%d,%s:%d", varName1, int32var, varName2, int16var, varName3, int8var, varName4, int32var2);

Votre question d'origine donne l'impression que vous avez quelque peu tenté de le faire et que vous pensez que cette méthode est lourde, mais vous avez également à un moment donné implémenté l'analyse syntaxique d'un format binaire et créé des variables pour stocker ces données. Il vous suffit donc d'imprimer ces variables existantes au niveau d'abstraction approprié (la classe / structure dans laquelle se trouve la variable).

C'est quelque chose que vous ne devriez faire qu'une seule fois, et vous pouvez le faire tout en construisant l'analyseur. Et cela ne changera que lorsque le format binaire changera (ce qui entraînera déjà une modification de votre analyseur).

Dans la même veine: certains langages ont des fonctionnalités robustes pour transformer des classes en XML ou JSON. C # est particulièrement bon dans ce domaine. Vous n'avez pas à abandonner votre format binaire, vous faites simplement le XML ou JSON dans une instruction de journalisation de débogage et laissez votre code de version seul.

Je recommanderais personnellement de ne pas emprunter la route de vidage hexadécimal, car elle est sujette aux erreurs (avez-vous commencé sur le bon octet, êtes-vous sûr lorsque vous lisez de gauche à droite que vous "voyez" la bonne endianness, etc.) .

Exemple: dites vos ToStringsvariables crachées a,b,c,d,e,f,g,h. Vous exécutez votre programme et remarquez un bogue avec g, mais le problème a vraiment commencé avec c(mais vous déboguez, donc vous ne l'avez pas encore compris). Si vous connaissez les valeurs d'entrée (et vous devriez), vous verrez instantanément que cc'est là que les problèmes commencent.

Comparé à un vidage hexadécimal qui vous dit juste 338E 8455 0000 FF76 0000 E444 ....; si vos champs sont de taille variable, où ccommence et quelle est la valeur - un éditeur hexadécimal vous le dira, mais mon point est que cela est sujet aux erreurs et prend du temps. Non seulement cela, mais vous ne pouvez pas automatiser facilement / rapidement un test via une visionneuse hexadécimale. L'impression d'une chaîne après avoir analysé les données vous dira exactement ce que votre programme «pense» et sera une étape sur la voie des tests automatisés.

Shaz
la source