La rilevazione di un documento protetto da password

C’è un modo per sapere se un doc/ppt/xls file è protetto da password, anche prima di aprire il file?

InformationsquelleAutor logeeks | 2011-04-25

 

3 Replies
  1. 12

    Ho creato un metodo di utilità che tenta di rilevare se un determinato ufficio documento è protetto da una password. Ecco una lista di vantaggi:

    • supporta Word, Excel e PowerPoint, sia legacy (doc, xls, ppt) e nuovo OpenXml versione (docx, xlsx, pptx)
    • non dipende da COM o qualsiasi altra libreria
    • richiede solo Sistema, Sistema.IO e Sistema.Testo di spazi dei nomi
    • abbastanza veloce e affidabile di rilevamento (rispetta legacy .doc, .ppt e .formati file xls)
    • basso utilizzo della memoria (massimo 64KB)

    Qui c’è il codice, spero che qualcuno lo troverà utile:

    public static class MsOfficeHelper
    {
        ///<summary>
        ///Detects if a given office document is protected by a password or not.
        ///Supported formats: Word, Excel and PowerPoint (both legacy and OpenXml).
        ///</summary>
        ///<param name="fileName">Path to an office document.</param>
        ///<returns>True if document is protected by a password, false otherwise.</returns>
        public static bool IsPasswordProtected(string fileName)
        {
            using (var stream = File.OpenRead(fileName))
                return IsPasswordProtected(stream);
        }
    
        ///<summary>
        ///Detects if a given office document is protected by a password or not.
        ///Supported formats: Word, Excel and PowerPoint (both legacy and OpenXml).
        ///</summary>
        ///<param name="stream">Office document stream.</param>
        ///<returns>True if document is protected by a password, false otherwise.</returns>
        public static bool IsPasswordProtected(Stream stream)
        {
            //minimum file size for office file is 4k
            if (stream.Length < 4096)
                return false;
    
            //read file header
            stream.Seek(0, SeekOrigin.Begin);
            var compObjHeader = new byte[0x20];
            ReadFromStream(stream, compObjHeader);
    
            //check if we have plain zip file
            if (compObjHeader[0] == 'P' && compObjHeader[1] == 'K')
            {
                //this is a plain OpenXml document (not encrypted)
                return false;
            }
    
            //check compound object magic bytes
            if (compObjHeader[0] != 0xD0 || compObjHeader[1] != 0xCF)
            {
                //unknown document format
                return false;
            }
    
            int sectionSizePower = compObjHeader[0x1E];
            if (sectionSizePower < 8 || sectionSizePower > 16)
            {
                //invalid section size
                return false;
            }
            int sectionSize = 2 << (sectionSizePower - 1);
    
            const int defaultScanLength = 32768;
            long scanLength = Math.Min(defaultScanLength, stream.Length);
    
            //read header part for scan
            stream.Seek(0, SeekOrigin.Begin);
            var header = new byte[scanLength];
            ReadFromStream(stream, header);
    
            //check if we detected password protection
            if (ScanForPassword(stream, header, sectionSize))
                return true;
    
            //if not, try to scan footer as well
    
            //read footer part for scan
            stream.Seek(-scanLength, SeekOrigin.End);
            var footer = new byte[scanLength];
            ReadFromStream(stream, footer);
    
            //finally return the result
            return ScanForPassword(stream, footer, sectionSize);
        }
    
        static void ReadFromStream(Stream stream, byte[] buffer)
        {
            int bytesRead, count = buffer.Length;
            while (count > 0 && (bytesRead = stream.Read(buffer, 0, count)) > 0)
                count -= bytesRead;
            if (count > 0) throw new EndOfStreamException();
        }
    
        static bool ScanForPassword(Stream stream, byte[] buffer, int sectionSize)
        {
            const string afterNamePadding = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
    
            try
            {
                string bufferString = Encoding.ASCII.GetString(buffer, 0, buffer.Length);
    
                //try to detect password protection used in new OpenXml documents
                //by searching for "EncryptedPackage" or "EncryptedSummary" streams
                const string encryptedPackageName = "E\0n\0c\0r\0y\0p\0t\0e\0d\0P\0a\0c\0k\0a\0g\0e" + afterNamePadding;
                const string encryptedSummaryName = "E\0n\0c\0r\0y\0p\0t\0e\0d\0S\0u\0m\0m\0a\0r\0y" + afterNamePadding;
                if (bufferString.Contains(encryptedPackageName) ||
                    bufferString.Contains(encryptedSummaryName))
                    return true;
    
                //try to detect password protection for legacy Office documents
                const int coBaseOffset = 0x200;
                const int sectionIdOffset = 0x74;
    
                //check for Word header
                const string wordDocumentName = "W\0o\0r\0d\0D\0o\0c\0u\0m\0e\0n\0t" + afterNamePadding;
                int headerOffset = bufferString.IndexOf(wordDocumentName, StringComparison.InvariantCulture);
                int sectionId;
                if (headerOffset >= 0)
                {
                    sectionId = BitConverter.ToInt32(buffer, headerOffset + sectionIdOffset);
                    int sectionOffset = coBaseOffset + sectionId * sectionSize;
                    const int fibScanSize = 0x10;
                    if (sectionOffset < 0 || sectionOffset + fibScanSize > stream.Length)
                        return false; //invalid document
                    var fibHeader = new byte[fibScanSize];
                    stream.Seek(sectionOffset, SeekOrigin.Begin);
                    ReadFromStream(stream, fibHeader);
                    short properties = BitConverter.ToInt16(fibHeader, 0x0A);
                    //check for fEncrypted FIB bit
                    const short fEncryptedBit = 0x0100;
                    return (properties & fEncryptedBit) == fEncryptedBit;
                }
    
                //check for Excel header
                const string workbookName = "W\0o\0r\0k\0b\0o\0o\0k" + afterNamePadding;
                headerOffset = bufferString.IndexOf(workbookName, StringComparison.InvariantCulture);
                if (headerOffset >= 0)
                {
                    sectionId = BitConverter.ToInt32(buffer, headerOffset + sectionIdOffset);
                    int sectionOffset = coBaseOffset + sectionId * sectionSize;
                    const int streamScanSize = 0x100;
                    if (sectionOffset < 0 || sectionOffset + streamScanSize > stream.Length)
                        return false; //invalid document
                    var workbookStream = new byte[streamScanSize];
                    stream.Seek(sectionOffset, SeekOrigin.Begin);
                    ReadFromStream(stream, workbookStream);
                    short record = BitConverter.ToInt16(workbookStream, 0);
                    short recordSize = BitConverter.ToInt16(workbookStream, sizeof(short));
                    const short bofMagic = 0x0809;
                    const short eofMagic = 0x000A;
                    const short filePassMagic = 0x002F;
                    if (record != bofMagic)
                        return false; //invalid BOF
                    //scan for FILEPASS record until the end of the buffer
                    int offset = sizeof(short) * 2 + recordSize;
                    int recordsLeft = 16; //simple infinite loop check just in case
                    do
                    {
                        record = BitConverter.ToInt16(workbookStream, offset);
                        if (record == filePassMagic)
                            return true;
                        recordSize = BitConverter.ToInt16(workbookStream, sizeof(short) + offset);
                        offset += sizeof(short) * 2 + recordSize;
                        recordsLeft--;
                    } while (record != eofMagic && recordsLeft > 0);
                }
    
                //check for PowerPoint user header
                const string currentUserName = "C\0u\0r\0r\0e\0n\0t\0 \0U\0s\0e\0r" + afterNamePadding;
                headerOffset = bufferString.IndexOf(currentUserName, StringComparison.InvariantCulture);
                if (headerOffset >= 0)
                {
                    sectionId = BitConverter.ToInt32(buffer, headerOffset + sectionIdOffset);
                    int sectionOffset = coBaseOffset + sectionId * sectionSize;
                    const int userAtomScanSize = 0x10;
                    if (sectionOffset < 0 || sectionOffset + userAtomScanSize > stream.Length)
                        return false; //invalid document
                    var userAtom = new byte[userAtomScanSize];
                    stream.Seek(sectionOffset, SeekOrigin.Begin);
                    ReadFromStream(stream, userAtom);
                    const int headerTokenOffset = 0x0C;
                    uint headerToken = BitConverter.ToUInt32(userAtom, headerTokenOffset);
                    //check for headerToken
                    const uint encryptedToken = 0xF3D1C4DF;
                    return headerToken == encryptedToken;
                }
            }
            catch (Exception ex)
            {
                //BitConverter exceptions may be related to document format problems
                //so we just treat them as "password not detected" result
                if (ex is ArgumentException)
                    return false;
                //respect all the rest exceptions
                throw;
            }
    
            return false;
        }
    }
    • Che bello speleologia c’è!!!
    • Grande lavoro! Ha confermato che funziona su word, excel sia per il 2013 & 97 formati.
    • Sembra che non riesce a rilevare correttamente il 97 versioni di Powerpoint. Se stai cercando di esito negativo, anziché bloccarsi. Suggerisco di passare immediatamente la password seguendo il percorso su come aprire così. @”c:\path\to\file.ppt” + “::BadPassword::”
    • Non ho ben capito il tuo suggerimento su bad password. Potrebbe, per favore, carica che non è riuscito 97 del file PPT? Cercherò di risolvere la funzione. Grazie
    • Impressionante classe, grazie! Per me funziona, protetto da password doc/x, xlsx, pptx, MA NON è protetto da password xls, ppt.
    • Sei il benvenuto. Ci sono un sacco di diversi XLS versioni, quindi c’è una possibilità che alcuni di loro non sono supportati. Tuttavia, la funzione è stato testato su più di 10000 file di Excel con il 99% di rilevamento corretto almeno.. Sarebbe bello se si può condividere il tuo file XLS.
    • Ho creato doc, ppt, xls file con “salva con nome” con un file docx, pptx, e xlsx, rispettivamente. Può essere che non è supportato. Ma la maggior parte dei file in formato precedente sono stati creati da vecchia app di Office, quindi non dovrebbe essere un problema reale. Sarei felice di condividere il file, ma come?
    • lavoro favoloso, ma simile a quello che Ofer ha detto, questo non funziona per i file ppt salvato l’utilizzo di salvare come nel più recente vestito di office (office 16). Sembra che la sezione di codice per il controllo password di PPT manca in ScanForPassword metodo di cui sopra.
    • Ho aggiornato il codice per il supporto legacy documenti PPT, si prega di provare, ora dovrebbe funzionare (almeno con i documenti salvati dopo Office 2002).
    • Ho aggiornato il codice per il supporto legacy documenti PPT, si prega di provare.
    • Ho aggiornato il codice per il supporto legacy documenti PPT, si prega di provare.
    • Grazie mille per i vostri sforzi. Io sono già in un’altra società che fa altre cose, ma sono sicuro che sarebbe utile a molti altri.
    • Ho provato la tua soluzione per file di word è sempre ritornare false, anche se il documento di word è protetto da password. per favore mi potete aiutare con queste.
    • la soluzione funziona se è impostata una password per aprire il documento o excel, ma se è impostata una password per modificare solo quindi restituirà false. Potete per favore aiutarmi come rilevare se è stata impostata la password solo per modificare.
    • Mi dispiace, io non sono sicuro di come si modifica password vengono applicate (lo scopo di questo codice è stato quello di rilevare se il documento può essere aperto per la visualizzazione). È possibile controllare il formato di file XLS (download.microsoft.com/download/1/A/9/…), probabilmente c’è solo un altro tipo di record o di un flag di controllo modifica password.

  2. 10

    Qui è una versione grezza di una password detecter che ho fatto. Non è necessario aprire un qualsiasi Ufficio oggetti.

        public static bool IsPassworded(string file) {
            var bytes = File.ReadAllBytes(file);
            return IsPassworded(bytes);
            return false;
        }
        public static bool IsPassworded(byte[] bytes) {
            var prefix = Encoding.Default.GetString(bytes.Take(2).ToArray());
            if (prefix == "PK") {
                //ZIP and not password protected
                return false;
            }
            if (prefix == "ÐÏ") {
                //Office format.
    
                //Flagged with password
                if (bytes.Skip(0x20c).Take(1).ToArray()[0] == 0x2f) return true; //XLS 2003
                if (bytes.Skip(0x214).Take(1).ToArray()[0] == 0x2f) return true; //XLS 2005
                if (bytes.Skip(0x20B).Take(1).ToArray()[0] == 0x13) return true; //DOC 2005
    
                if (bytes.Length < 2000) return false; //Guessing false
                var start = Encoding.Default.GetString(bytes.Take(2000).ToArray()); //DOC/XLS 2007+
                start = start.Replace("\0", " ");
                if (start.Contains("E n c r y p t e d P a c k a g e")) return true;
                return false;
            }
    
            //Unknown.
            return false;
        }

    Potrebbe non essere al 100%. Le bandiere che ho trovato confrontando diversi Excel e Word con e senza password. Per aggiungere per PowerPoint fare lo stesso.

    • Bello. @Wolf5 +1
    • grande. fa questo lavoro solo per i documenti di office? come circa i Pdf?
    • Il codice di cui sopra è solo per i documenti di office (Microsoft). I file pdf sono un prodotto Adobe e probabilmente hanno un modo diverso per farlo. Ma potrebbe essere facile come paragonare un documento PDF prima e dopo il suo stato protetto per trovare una bandiera (posizione) che ha indicato di essere protetto. Quindi basta creare un codice che reagisce a, il valore a tale posizione.

Lascia un commento