open online files zip file-format

online - ¿Cómo se encuentra el inicio del "Directorio Central" en los archivos zip?



zipfile c# (4)

Wikipedia tiene una excelente descripción del formato de archivo ZIP , pero la estructura del "directorio central" me resulta confusa. Específicamente esto:

Este ordenamiento permite crear un archivo ZIP en una sola pasada, pero generalmente se descomprime leyendo primero el directorio central al final.

El problema es que incluso el encabezado final del directorio central es de longitud variable. ¿Cómo, entonces, alguien puede obtener el inicio del directorio central para analizar?

(Ah, y pasé un tiempo mirando APPNOTE.TXT en vano antes de venir aquí y preguntar: P)


Aquí hay una solución que acabo de tener que implementar en caso de que alguien la necesite. Esto implica agarrar el directorio central.

En mi caso, no quería ninguna de las funciones de compresión que se ofrecen en ninguna de las soluciones zip. Solo quería saber sobre los contenidos. El siguiente código devolverá un ZipArchive de una lista de cada entrada en el zip.

También utiliza una cantidad mínima de acceso a archivos y asignación de memoria.

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

Uso:

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 } }


En caso de que alguien todavía esté luchando con este problema, eche un vistazo al repositorio que hospedé en GitHub que contiene mi proyecto y que podría responder sus preguntas.

Lector de archivos zip Básicamente, lo que hace es descargar la parte del central directory del archivo .zip que se encuentra al final del archivo. Luego leerá cada nombre de archivo y carpeta con su ruta desde los bytes y lo imprimirá en la consola.

He hecho comentarios sobre los pasos más complicados en mi código fuente.

El programa solo puede funcionar hasta aproximadamente 4 GB de archivos .zip. Después de eso, tendrá que hacer algunos cambios en el tamaño de la máquina virtual y tal vez más.

Disfrutar :)


Hace un tiempo estaba implementando la compatibilidad con el archivo zip, y busco en los últimos kilobytes un extremo de la firma del directorio central (4 bytes). Eso funciona bastante bien, hasta que alguien ponga un texto de 50 kb en el comentario (lo cual es poco probable que ocurra. Para estar absolutamente seguro, puede buscar los últimos 64 kb + algunos bytes, ya que el tamaño del comentario es de 16 bits). Después de eso, busco el final del localizador central de direcciones zip64, eso es más fácil ya que tiene una estructura fija.


Mis condolencias, leer la descripción de wikipedia me da la impresión muy fuerte de que necesitas hacer una buena cantidad de conjetura + verificación de trabajo:

Busque hacia atrás desde el final para la etiqueta de final de directorio 0x06054b50, mire hacia adelante 16 bytes para encontrar el desplazamiento para la etiqueta de inicio de directorio 0x02014b50, y espero que así sea. Podría hacer algunas comprobaciones de cordura como buscar la longitud del comentario y las etiquetas de cadena de comentario después de la etiqueta de fin de directorio, pero parece que los decodificadores Zip funcionan porque la gente no pone caracteres divertidos en sus comentarios zip, nombres de archivo y demás. adelante. Basado enteramente en la página de wikipedia, de todos modos.