public void TestParseRC4CryptoAPIFilePassRecord()
        {
            byte[] rc4CryptoApiFilePassRecord = new byte[]
            {
                0x2F, 0x00, 0xC8, 0x00, 0x01, 0x00, 0x04, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00,
                0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00, 0x04, 0x80, 0x00, 0x00,
                0x80, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D,
                0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x73, 0x00, 0x6F, 0x00, 0x66, 0x00, 0x74, 0x00,
                0x20, 0x00, 0x45, 0x00, 0x6E, 0x00, 0x68, 0x00, 0x61, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, 0x64,
                0x00, 0x20, 0x00, 0x43, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70, 0x00, 0x74, 0x00, 0x6F, 0x00, 0x67, 0x00,
                0x72, 0x00, 0x61, 0x00, 0x70, 0x00, 0x68, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72,
                0x00, 0x6F, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72, 0x00, 0x20, 0x00, 0x76, 0x00,
                0x31, 0x00, 0x2E, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x98, 0xC3, 0xA1, 0xA0, 0x9A,
                0xC8, 0x92, 0x3D, 0x7A, 0x5D, 0x03, 0x30, 0x34, 0xE8, 0x67, 0x64, 0xA8, 0xA9, 0x29, 0xF0, 0xA6, 0xA8,
                0xB6, 0xA1, 0x1F, 0x8C, 0x6F, 0x27, 0xB6, 0xA8, 0x82, 0xEB, 0x14, 0x00, 0x00, 0x00, 0xE8, 0xAF, 0x8B,
                0x67, 0x4E, 0x3F, 0xA7, 0xA1, 0x39, 0x9D, 0x9F, 0x2D, 0xDC, 0xB5, 0x1A, 0x33, 0x5D, 0x8A, 0xB2, 0x69
            };

            VirtualStreamReader vsr            = new VirtualStreamReader(new MemoryStream(rc4CryptoApiFilePassRecord));
            FilePass            filePassRecord = new FilePass(vsr, (RecordType)vsr.ReadUInt16(), vsr.ReadUInt16());

            byte[] filePassBytes = filePassRecord.GetBytes();

            Assert.AreEqual(rc4CryptoApiFilePassRecord.Length, filePassBytes.Length);

            Assert.IsFalse(filePassRecord.encryptionHeader.fDocProps);
            Assert.IsFalse(filePassRecord.encryptionHeader.fCryptoAPI);
            Assert.IsTrue(filePassRecord.encryptionHeader.fExternal);
            Assert.IsTrue(filePassRecord.encryptionHeader.fAES);

            for (int offset = 0; offset < rc4CryptoApiFilePassRecord.Length; offset += 1)
            {
                Assert.AreEqual(rc4CryptoApiFilePassRecord[offset], filePassBytes[offset]);
            }
        }
Esempio n. 2
0
        public byte[] TransformWorkbookBytes(byte[] bytes, ObfuscationMode mode, string password = XorObfuscation.DefaultPassword)
        {
            VirtualStreamReader vsr = new VirtualStreamReader(new MemoryStream(bytes));
            MemoryStream        ms  = new MemoryStream();
            BinaryWriter        bw  = new BinaryWriter(ms);

            while (vsr.BaseStream.Position < vsr.BaseStream.Length)
            {
                BiffHeader bh;
                bh.id = (RecordType)vsr.ReadUInt16();

                //Handle case where RecordId is empty
                if (bh.id == 0)
                {
                    break;
                }

                bh.length = vsr.ReadUInt16();

                //Taken from https://social.msdn.microsoft.com/Forums/en-US/3dadbed3-0e68-4f11-8b43-3a2328d9ebd5/xls-xor-data-transformation-method-1


                byte XorArrayIndex = (byte)((vsr.BaseStream.Position + bh.length) % 16);


                //We remove the FilePass Record for the decrypted document
                if (mode == ObfuscationMode.Decrypt && bh.id == RecordType.FilePass)
                {
                    //Skip the remaining FilePass bytes
                    ushort encryptionMode = vsr.ReadUInt16();

                    if (encryptionMode != 0)
                    {
                        throw new NotImplementedException("FilePass EncryptionMode of " + encryptionMode +
                                                          " is unsupported.");
                    }

                    ushort key    = vsr.ReadUInt16();
                    ushort verify = vsr.ReadUInt16();

                    ushort passwordVerify = CreatePasswordVerifier_Method1(password);

                    if (verify != passwordVerify)
                    {
                        throw new ArgumentException(
                                  "Incorrect decryption password. Try bruteforcing the password with another tool.");
                    }

                    continue;
                }
                ;

                bw.Write(Convert.ToUInt16(bh.id));
                bw.Write(Convert.ToUInt16(bh.length));

                //If we're encrypting, then use the byte writer for our current position rather than the read stream
                if (mode == ObfuscationMode.Encrypt)
                {
                    XorArrayIndex = (byte)((bw.BaseStream.Position + bh.length) % 16);
                }

                //Nothing to decrypt for 0 length
                if (bh.length == 0)
                {
                    continue;
                }

                switch (bh.id)
                {
                case RecordType.BOF:
                case RecordType.FilePass:
                case RecordType.UsrExcl:
                case RecordType.FileLock:
                case RecordType.InterfaceHdr:
                case RecordType.RRDInfo:
                case RecordType.RRDHead:
                    byte[] recordBytes = vsr.ReadBytes(bh.length);
                    bw.Write(recordBytes);

                    //If this is the first BOF record, we inject the appropriate FilePass record
                    if (mode == ObfuscationMode.Encrypt &&
                        bh.id == RecordType.BOF &&
                        vsr.BaseStream.Position == (bh.length + 4))
                    {
                        ushort   key           = CreateXorKey_Method1(password);
                        ushort   verify        = CreatePasswordVerifier_Method1(password);
                        FilePass filePass      = new FilePass(key, verify);
                        byte[]   filePassBytes = filePass.GetBytes();
                        bw.Write(filePassBytes);
                    }

                    continue;

                case RecordType.BoundSheet8:
                    //Special Case - don't encrypt/decrypt the lbPlyPos Field
                    uint lbPlyPos = vsr.ReadUInt32();

                    //For encryption we need to adjust this by the added FilePass record length
                    //Decryption we auto-fix afterwards, but encrypted entries don't auto-fix well
                    // if (mode == ObfuscationMode.Encrypt)
                    // {
                    //     lbPlyPos += 10;
                    // }

                    bw.Write(lbPlyPos);
                    //Since we are skipping lbPlyPos, we need to update the XorArrayIndex offset as well
                    XorArrayIndex = (byte)((XorArrayIndex + 4) % 16);
                    byte[] remainingBytes = vsr.ReadBytes(bh.length - 4);
                    if (mode == ObfuscationMode.Decrypt)
                    {
                        byte[] decryptedBytes = DecryptData_Method1(password, remainingBytes, XorArrayIndex);
                        bw.Write(decryptedBytes);
                    }
                    else
                    {
                        byte[] encryptedBytes = EncryptData_Method1(password, remainingBytes, XorArrayIndex);
                        bw.Write(encryptedBytes);
                    }
                    continue;

                default:
                    byte[] preTransformBytes = vsr.ReadBytes(bh.length);
                    if (mode == ObfuscationMode.Decrypt)
                    {
                        byte[] decBytes = DecryptData_Method1(password, preTransformBytes, XorArrayIndex);
                        bw.Write(decBytes);
                    }
                    else
                    {
                        byte[] encBytes = EncryptData_Method1(password, preTransformBytes, XorArrayIndex);
                        bw.Write(encBytes);
                    }
                    continue;
                }
            }

            return(bw.GetBytesWritten());
        }