C/C++ Aide sur le parsage d'un SWF compressé avec zlib

  • Auteur de la discussion Anonymous
  • Date de début
A

Anonymous

Invité
#1
Bonjour à tous,

Je suis en train de programmer un petit parseur de fichier SWF pour mon bot.
Pour celà, j'ai récupéré la doc de spécification des fichiers SWF d'Adobe.
J'ai codé la partie lecture de l'entête du fichier (8 premiers octets) et je dois donc ensuite décompresser le reste du fichier à l'aide de la bibliothèque zlib sous Linux. Mais j'arrive à un petit problème, l'initialisation de la zlib donne un "segmentation fault" et comme je pense que je suis un gros boulet, je n'arrive pas à comprendre d'où vient l'erreur.
Voici le code de ma classe SwfFile : https://github.com/wamilou/SwfParser/bl ... wfFile.cpp

Quand je debug mon programme, le segfault apparait à la ligne 76, la plupart du code utilisé vient du site officiel de zlib et aussi les 2 tableaux in et out font la taille du fichier et la taille du swf final respectivement ( ce qui n'est pas très bon je l'avoue si le fichier est très gros x) )

Donc je me demandais, s'il y avait des gens ici qui ont utilisé zlib et s'ils ont eu ce problème comme moi, car là, je suis repassé plusieurs fois sur mon code pour voir si j'avais pas fait d'erreur monstrueuse mais j'ai toujours ce problème de segmentation fault (surement j'ai mal initialisé la structure ?)

Merci d'avance pour votre aide !

PS : Mon code est dégueulasse, mais j'aimerai fixer ce problème avant d'optimiser x)
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#2
Lorsque tu passes un coup de valgrind/gdb, tu as plus d'informations ? La ligne 76 est un cout... tu veux peut-être dire que le segfault apparaît entre les deux lignes de debug ??
 
A

Anonymous

Invité
#3
J'utilise code block qui utilise lui même gdb, mais il semblerait que il arrive pas bien à communiquer avec gdb. Je pense que c'est la ligne au dessous qui plante, je teste ça en rentrant chez moi.

EDIT : En testant, les 2 lignes font un segfault :
- Si je commente 1 des 2 lignes = segfault
- Si je commente les 2 lignes, celà ne plante pas !
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#4
Coucou ! Il semblerait que le problème vient en effet de la ligne 26 : plus tôt dans ton code tu fais :

Code:
    unsigned char in[tailleFichier-8];
    unsigned char out[m_size];
1. C'est une très mauvaise pratique d'allouer statiquement un tableau dont on ne connaît pas la taille.
2. Simplement, ces deux tableaux sont tellement grands qu'ils dépassent la taille limite de ta stack. Lorsqu'à la ligne 76 tu fais ton cout de debug, tu appelles une fonction. Ta stack étant hors limite, tu segfault.

Remplace ces deux lignes par une allocation dynamique :

Code:
    unsigned char *in = new unsigned char[tailleFichier-8];
    unsigned char *out = new unsigned char[m_size];
Et tout devrait rentrer dans l'ordre !

P.S : Ce serait bien de mettre un makefile (ou n'importe quoi) dans ton repo, ça aiderait à compiler !
 
A

Anonymous

Invité
#5
Effectivement, merci !

EDIT : J'arrive pas à trouver la structure RECT à jours en cherchant, si quelqu'un sait comment avoir la taille, vu qu'ils disent que la taille dépend de ce qu'on veut stocker comme valeur.
 
A

Anonymous

Invité
#6
Il semblerait que mon code ne décompresse pas ce qui fait que mon tableau out n'a que des 0.
J'ai pas changé le code de décompression par rapport à celui en ligne sur mon github. Pourtant je n'ai pas d'erreur lors de l'exécution, donc si quelqu'un a une idée du problème, celà m'aiderait largement pour continuer mon code :)

Merci d'avance.

PS : Je continus à chercher de mon côté au cas où si je trouve.
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#7
Les lignes :

Code:
    strm.avail_in = sizeof(in);
    if (strm.avail_in == 0)
        return;
    strm.next_in = in;

    strm.avail_out = sizeof(out);
sont maintenant fausses. Puisque in et out sont des pointeurs, sizeof(in) et sizeof(out) équivaut à sizeof(void*) donc généralement 4 ou 8. zlib n'arrive probablement pas à décompresser pour cette raison. Remplace ça par :

Code:
    strm.avail_in = tailleFichier-8;
    if (strm.avail_in == 0)
        return;
    strm.next_in = in;

    strm.avail_out = m_size;
 
A

Anonymous

Invité
#8
En effet, merci !

Sinon, j'ai du mal à "sauter" la structure RECT du header :
Je lis les 5 premiers bits (dans le premier byte), j'obtiens une taille de 16 ce qui est plutôt correct (il me semble).
Pour la lecture, j'utilise un stringstream et pour "sauter", je fais :
Code:
seekg( rectSize*4/8-3, fileData.cur);
Mais je dois me louper quelque part ou j'ai du mal lire la doc d'Adobe :/

Merci d'avance.
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#9
La ligne que tu cites est certainement incorrecte.
Tu as bien compris que chaque champ prenait rectSize bits à lire. Cependant, tu te trompes sur un point. Prenons l'exemple d'un rect avec 3 bits par champ. Il y a bien 4 champs à lire (AAA représente le champ 1, etc ; chaque champ étant séparé par un -) :

0001 1-AAA- BBB-C CC-DD D000 0000

Ici, on voit bien que la structure RECT prend 2 octets.
Si on essaie de poser le calcul, la taille de la structure RECT donnerait plutôt quelque chose comme :

((rectSize * 4 + 5) / 8) + 1 octets.

La structure fait 5 (nBits) + le nombre de champs multiplié par la taille d'un champ. On ramène le tout en octets puis on arrondit à l'octet supérieur pour le padding. À noter que le padding doit demander plus de calcul que ça.

Cela dit, j'aurais tendance à dire que quitte à faire un parser, autant tout parser correctement, au lieu de simplement ignorer les champs qui ne t'intéressent pas.
 
Inscrit
27 Juin 2012
Messages
238
Reactions
0
#10
Voici la documentation sur la structure du RECT.
 
A

Anonymous

Invité
#11
Je vais tenter de parser cette structure, mais ça risque d'être compliqué vu que je ne sais pas comment gérer les valeurs sur plusieurs octets :/
Example : quand je lis une valeur, je ne peux lire qu'en octet et non en bit avec les fonctions de base.
Il va falloir donc que je fasse une usine à gaz pour lire tout ça :<
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#12
Ton stream doit être capable de lire des bits, le reste se fait plutôt simplement !
 
A

Anonymous

Invité
#13
Donc, pour me "simplifier" un peu la tache, je viens de créer une classe BitReader permettant de lire des bits.
Seulement, voilà, je n'obtiens pas la taille de 16 que j'avais au début :<

Niveau code, j'ai fait ça : https://github.com/wamilou/SwfParser/bl ... Reader.cpp

Niveau algo, j'ai pensé à récupérer, dans un char où on stock l'octet actuel, le bit avec le masque correspondant (0x1, 0x2 ...)
puis on décale le tout vers la droite de n pour avoir le bit tout à droite. (Fonction readBit())
Ensuite, je récupère ce bit et je le mets dans une variable que je décale d'un cran vers la gauche à chaque bit lu (Fonction readBits())

Mon code est toujours améliorable mais je n'arrive pas à trouver mon erreur dans mon code :<
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#14
Il serait intéressant de savoir quelle valeur tu as obtenu.

Très rapidement, dans la fonction readBits() :

Code:
unsigned char BitReader::readBits(uint32_t aLire)
{
    unsigned char retour = 0;

    for(int i = 0; i < aLire; i++)
    {
        retour = retour | readBit();
        retour = retour << 1;
    }

    return retour;
}
devrait plutôt être :

Code:
unsigned char BitReader::readBits(uint32_t aLire)
{
    unsigned char retour = 0;

    for(int i = 0; i < aLire; i++)
    {
        retour = retour | (readBit() << i);
    }

    return retour;
}
 
A

Anonymous

Invité
#15
J'ai repris ton code en modifiant le for comme ceci :
Code:
unsigned char BitReader::readBits(uint32_t aLire)
{
    unsigned char retour = 0;

    for(int i = aLire-1; i >= 0; i--)
    {
        retour = retour | (readBit() << i);
    }

    return retour;
}
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#16
Quelle valeur est-ce que tu obtiens lorsque tu lis tes 5 bits de taille ?
À titre de comparaison, voilà ce que j'ai pour lire un int de 5 bits :

Code:
 73    const int SWFReader::readBInt32(const int n)
 74    {
 75      if (n > sizeof(int) * 8)
 76        throw malformated_swf_exception("bit value's length cannot be > 32");
 77      utils::BitArray *bits = m_stream->readBits(n);
 78      const int value = bits->toInt32();
 79      delete bits;
 80      return value;
 81    }
Code:
154    utils::BitArray *BufferedStream::readBits(const int n)
155    {
156      const int nBytes = (n + m_bitOffset) / 8;
157      if (!m_buffer || m_offset + nBytes > m_buffer->getLen())
158        throw eof_stream_exception();
159
160      const char * const bufPtr = getBufferAtPtr();
161      utils::BitArray *bits = new utils::BitArray(bufPtr, m_bitOffset, n);
162      m_offset += nBytes;
163      m_bitOffset = (m_bitOffset + n) % 8;
164      return bits;
165    }
Une fois que j'ai mon BitArray :

Code:
 84    const int BitArray::toInt32() const
 85    {
 86      if (m_len > sizeof(int) * 8)
 87        throw out_of_bound_exception();
 88
 89      int idx;
 90      int out = 0;
 91      const int limit = m_len + m_offset;
 92      const int highBit = (m_bytes[0] & (1 << (7 - (m_offset % 8)))) != 0;
 93      for(idx = m_offset; idx < limit; ++idx)
 94        {
 95          int bit = (m_bytes[idx / 8] & (1 << (8 - (idx % 8)))) != 0;
 96          out |= bit << (m_len - (idx - m_offset));
 97        }
 98      for ( ; idx < sizeof(int) * 8; ++idx)
 99        out |= highBit << idx;
100      return out;
101    }
J'espère que ça t'aidera, malgré ma flemme :)
 
A

Anonymous

Invité
#17
En lisant les 5 premiers bits, j'obtiens une valeur de 16. Je sais pas si ça semble correct : je fais mes tests sur le swf du client Dofus.

EDIT : Je fais 2-3 modifications, voici les résultats obtenus qui sont un peu faux ;) :


J'ai mis à jour le code sur github.
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#18
Je t'invite à regarder de plus près les valeurs que tu as, les octets qui sont dans le stream au moment où tu lis le RECT.
Quitte à poser le problème sur papier, pour voir ce qui est faux. Utilise un décompilateur fonctionnel (JPEX blabla, par exemple) pour voir les valeurs correctes. Joue avec les bits pour voir où ça fail, etc.

Si tu ne trouves pas poste tes informations ici, mais comme ça, c'est un peu compliqué de trouver le souci, je t'avoue :/
 
A

Anonymous

Invité
#19
J'ai trouvé mes erreurs mais pour les fixer, il faut aussi que j'inverse l'ordre des octets et j'aimerai donc savoir s'il existe une fonction toute faite ou dois je la coder moi même ?
 

Lakh92

Membre Actif
Inscrit
24 Decembre 2009
Messages
118
Reactions
0
#20
Euh... ça n'existe pas (de ce que je sais, en tout cas). Avec une recherche google ça se trouve, avec un peu de réflexion aussi.

Cela dit, si c'est l'ordre des bits qui est inversé, tu devrais peut-être vérifier ton BitReader :)
 
Haut Bas