2011-01-26 13 views
17

Wikipedia has an excellent description of the ZIP file format, ale struktura "katalogu centralnego" jest dla mnie myląca. Konkretnie:Jak znaleźć początek "katalogu centralnego" w plikach zip?

To zamówienie pozwala na utworzenie pliku ZIP w jednym przebiegu, ale zazwyczaj jest dekompresowane po pierwszym przeczytaniu katalogu centralnego na końcu.

Problem polega na tym, że nawet końcowy nagłówek katalogu centralnego ma zmienną długość. Jak zatem ktoś może uzyskać początek katalogu centralnego do przeanalizowania?

(Oh, i ja spędzić trochę czasu patrząc na APPNOTE.TXT bezskutecznie przed przyjazdem tutaj i pytać: P)

Odpowiedz

9

Moje kondolencje, czytając opis wikipedia daje mi bardzo silne wrażenie, że trzeba zrobić spora liczba zgadywania + praca kontrolna:

Poluję do tyłu od końca dla znacznika końca 0x06054b50, spodziewamy się 16 bajtów, aby znaleźć przesunięcie dla znacznika początkowego katalogu 0x02014b50, i mam nadzieję, że jest to. Możesz wykonać kilka testów poprawności, takich jak wyszukiwanie długości komentarza i znaczników komentarzy po znaczniku końca katalogu, ale na pewno działa jak dekoder Zip, ponieważ ludzie nie umieszczają zabawnych znaków w swoich komentarzach zip, nazwach plików i tak naprzód. Opierając się w całości na stronie wikipedii.

+1

Mam nadzieję, że nikt nie używa '0x06054b50' w sekcji komentarzy: P – Tower

+1

Wygląda na to, że będzie to dużo łatwiejsze jeśli był mały nagłówek z przodu z tagiem i 8 bajtów dla przesunięcia CD. Oprogramowanie ZIP będzie musiało powrócić i ustawić te bajty na przedniej stronie pliku, gdy tylko zorientuje się, na czym polega przesunięcie (część piękna CD znajduje się na końcu pliku, zbieramy informacje potrzebne podczas archiwizowania) ale potem znalezienie go byłoby dziecinnie proste. Inną wadą jest to, że byłby on odporny tylko na przyszłą liczbę zarezerwowanych bajtów, chyba że inne nagłówki zostały użyte (i oczekiwano) do odróżnienia ZIP32 od ZIP64 (i ZIP128 itd.). – KeithS

6

Jakiś czas temu wdrażałem obsługę archiwów ZIP, a ostatnie kilka kilobajtów szukam na koniec sygnatury katalogu centralnego (4 bajty). Działa to całkiem dobrze, dopóki ktoś nie wprowadzi 50kb tekstu do komentarza (co jest mało prawdopodobne, aby być absolutnie pewnym, możesz przeszukać ostatnie 64kb + kilka bajtów, ponieważ rozmiar komentarza wynosi 16 bitów). Po tym, szukam zip64 koniec centralnego lokalizatora dir, to łatwiejsze, ponieważ ma ustaloną strukturę.

0

Jeśli ktoś wciąż zmaga się z tym problemem - zajrzyj do repozytorium hostowanego na GitHub zawierającego mój projekt, który może odpowiedzieć na twoje pytania.

Zip file reader Zasadniczo co robi to pobrać central directory część pliku .zip który zamieszkuje na końcu pliku. Następnie odczyta każdy plik i nazwę folderu wraz z jego ścieżką z bajtów i wydrukuje na konsoli.

Dodałem komentarze na temat bardziej skomplikowanych kroków w moim kodzie źródłowym.

Program może działać tylko do około 4 GB plików .zip. Potem będziesz musiał dokonać pewnych zmian w wielkości maszyny wirtualnej i może więcej.

Enjoy :)

1

Oto rozwiązanie właśnie musiał rozciągnąć okrywać ktoś tego potrzebuje. Obejmuje to pobranie katalogu centralnego.

W moim przypadku nie chciałem żadnej funkcji kompresji, które są oferowane w każdym rozwiązaniu zip. Chciałem tylko wiedzieć o zawartości. Poniższy kod zwróci ZipArchive z listy wszystkich pozycji w pliku ZIP.

Używa również minimalnej ilości dostępu do plików i alokacji pamięci.

TinyZip.CPP

#include "TinyZip.h" 
#include <cstdio> 

namespace TinyZip 
{ 
#define VALID_ZIP_SIGNATURE 0x04034b50 
#define CENTRAL_DIRECTORY_EOCD 0x06054b50 //signature 
#define CENTRAL_DIRECTORY_ENTRY_SIGNATURE 0x02014b50 
#define PTR_OFFS(type, mem, offs) *((type*)(mem + offs)) //SHOULD BE OK 

    typedef struct { 
     unsigned int signature : 32; 
     unsigned int number_of_disk : 16; 
     unsigned int disk_where_cd_starts : 16; 
     unsigned int number_of_cd_records : 16; 
     unsigned int total_number_of_cd_records : 16; 
     unsigned int size_of_cd : 32; 
     unsigned int offset_of_start : 32; 
     unsigned int comment_length : 16; 
    } ZipEOCD; 

    ZipArchive* ZipArchive::GetArchive(const char *filepath) 
    { 
     FILE *pFile = nullptr; 
#ifdef WIN32 
     errno_t err; 
     if ((err = fopen_s(&pFile, filepath, "rb")) == 0) 
#else 
     if ((pFile = fopen(filepath, "rb")) == NULL) 
#endif 
     { 
      int fileSignature = 0; 
      //Seek to start and read zip header 
      fread(&fileSignature, sizeof(int), 1, pFile); 
      if (fileSignature != VALID_ZIP_SIGNATURE) return false; 

      //Grab the file size 
      long fileSize = 0; 
      long currPos = 0; 

      fseek(pFile, 0L, SEEK_END); 
      fileSize = ftell(pFile); 
      fseek(pFile, 0L, SEEK_SET); 

      //Step back the size of the ZipEOCD 
      //If it doesn't have any comments, should get an instant signature match 
      currPos = fileSize; 
      int signature = 0; 
      while (currPos > 0) 
      { 
       fseek(pFile, currPos, SEEK_SET); 
       fread(&signature, sizeof(int), 1, pFile); 
       if (signature == CENTRAL_DIRECTORY_EOCD) 
       { 
        break; 
       } 
       currPos -= sizeof(char); //step back one byte 
      } 

      if (currPos != 0) 
      { 
       ZipEOCD zipOECD; 
       fseek(pFile, currPos, SEEK_SET); 
       fread(&zipOECD, sizeof(ZipEOCD), 1, pFile); 

       long memBlockSize = fileSize - zipOECD.offset_of_start; 

       //Allocate zip archive of size 
       ZipArchive *pArchive = new ZipArchive(memBlockSize); 

       //Read in the whole central directory (also includes the ZipEOCD...) 
       fseek(pFile, zipOECD.offset_of_start, SEEK_SET); 
       fread((void*)pArchive->m_MemBlock, memBlockSize - 10, 1, pFile); 
       long currMemBlockPos = 0; 
       long currNullTerminatorPos = -1; 
       while (currMemBlockPos < memBlockSize) 
       { 
        int sig = PTR_OFFS(int, pArchive->m_MemBlock, currMemBlockPos); 
        if (sig != CENTRAL_DIRECTORY_ENTRY_SIGNATURE) 
        { 
         if (sig == CENTRAL_DIRECTORY_EOCD) return pArchive; 
         return nullptr; //something went wrong 
        } 

        if (currNullTerminatorPos > 0) 
        { 
         pArchive->m_MemBlock[currNullTerminatorPos] = '\0'; 
         currNullTerminatorPos = -1; 
        } 

        const long offsToFilenameLen = 28; 
        const long offsToFieldLen = 30; 
        const long offsetToFilename = 46; 

        int filenameLength = PTR_OFFS(int, pArchive->m_MemBlock, currMemBlockPos + offsToFilenameLen); 
        int extraFieldLen = PTR_OFFS(int, pArchive->m_MemBlock, currMemBlockPos + offsToFieldLen); 
        const char *pFilepath = &pArchive->m_MemBlock[currMemBlockPos + offsetToFilename]; 
        currNullTerminatorPos = (currMemBlockPos + offsetToFilename) + filenameLength; 
        pArchive->m_Entries.push_back(pFilepath); 

        currMemBlockPos += (offsetToFilename + filenameLength + extraFieldLen); 
       } 

       return pArchive; 
      } 
     } 
     return nullptr; 
    } 

    ZipArchive::ZipArchive(long size) 
    { 
     m_MemBlock = new char[size]; 
    } 

    ZipArchive::~ZipArchive() 
    { 
     delete[] m_MemBlock; 
    } 

    const std::vector<const char*> &ZipArchive::GetEntries() 
    { 
     return m_Entries; 
    } 
} 

TinyZip.h

#ifndef __TinyZip__ 
#define __TinyZip__ 

#include <vector> 
#include <string> 

namespace TinyZip 
{ 
    class ZipArchive 
    { 
    public: 
     ZipArchive(long memBlockSize); 
     ~ZipArchive(); 

     static ZipArchive* GetArchive(const char *filepath); 

     const std::vector<const char*> &GetEntries(); 

    private: 
     std::vector<const char*> m_Entries; 
     char *m_MemBlock; 
    }; 

} 


#endif 

Zastosowanie:

TinyZip::ZipArchive *pArchive = TinyZip::ZipArchive::GetArchive("Scripts_unencrypt.pak"); 
if (pArchive != nullptr) 
{ 
    const std::vector<const char*> entries = pArchive->GetEntries(); 
    for (auto entry : entries) 
    { 
     //do stuff 
    } 
} 
+1

1. Gdzie w instrukcji jest napisane? 2. Przepraszam, że nie przeczytałem w pełni książki o standardach kompilatora. Czy to zbyt trudne, aby to zmienić? Czy masz złamane ręce? 3. Czy chcesz, żebym wytłumaczył ci użycie wskaźników? 4. Tak, nie rozumiesz mojego kodu? 5. Czy jesteś głupi? – Asheh

Powiązane problemy