xiejun
2023-08-23 205056bc17023dd44e6d262cfe78b6e146ee2f20
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
function _JS2ANSI(str) { if(typeof cptable !== 'undefined') return cptable.utils.encode(1252, str); return str.split("").map(function(x) { return x.charCodeAt(0); }); }
 
/* [MS-OFFCRYPTO] 2.1.4 Version */
function parse_Version(blob, length) {
    var o = {};
    o.Major = blob.read_shift(2);
    o.Minor = blob.read_shift(2);
    return o;
}
/* [MS-OFFCRYPTO] 2.3.2 Encryption Header */
function parse_EncryptionHeader(blob, length) {
    var o = {};
    o.Flags = blob.read_shift(4);
 
    // Check if SizeExtra is 0x00000000
    var tmp = blob.read_shift(4);
    if(tmp !== 0) throw 'Unrecognized SizeExtra: ' + tmp;
 
    o.AlgID = blob.read_shift(4);
    switch(o.AlgID) {
        case 0: case 0x6801: case 0x660E: case 0x660F: case 0x6610: break;
        default: throw 'Unrecognized encryption algorithm: ' + o.AlgID;
    }
    parsenoop(blob, length-12);
    return o;
}
 
/* [MS-OFFCRYPTO] 2.3.3 Encryption Verifier */
function parse_EncryptionVerifier(blob, length) {
    return parsenoop(blob, length);
}
/* [MS-OFFCRYPTO] 2.3.5.1 RC4 CryptoAPI Encryption Header */
function parse_RC4CryptoHeader(blob, length) {
    var o = {};
    var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
    if(vers.Minor != 2) throw 'unrecognized minor version code: ' + vers.Minor;
    if(vers.Major > 4 || vers.Major < 2) throw 'unrecognized major version code: ' + vers.Major;
    o.Flags = blob.read_shift(4); length -= 4;
    var sz = blob.read_shift(4); length -= 4;
    o.EncryptionHeader = parse_EncryptionHeader(blob, sz); length -= sz;
    o.EncryptionVerifier = parse_EncryptionVerifier(blob, length);
    return o;
}
/* [MS-OFFCRYPTO] 2.3.6.1 RC4 Encryption Header */
function parse_RC4Header(blob, length) {
    var o = {};
    var vers = o.EncryptionVersionInfo = parse_Version(blob, 4); length -= 4;
    if(vers.Major != 1 || vers.Minor != 1) throw 'unrecognized version code ' + vers.Major + ' : ' + vers.Minor;
    o.Salt = blob.read_shift(16);
    o.EncryptedVerifier = blob.read_shift(16);
    o.EncryptedVerifierHash = blob.read_shift(16);
    return o;
}
 
/* [MS-OFFCRYPTO] 2.3.7.1 Binary Document Password Verifier Derivation */
function crypto_CreatePasswordVerifier_Method1(Password) {
    var Verifier = 0x0000, PasswordArray;
    var PasswordDecoded = _JS2ANSI(Password);
    var len = PasswordDecoded.length + 1, i, PasswordByte;
    var Intermediate1, Intermediate2, Intermediate3;
    PasswordArray = new_raw_buf(len);
    PasswordArray[0] = PasswordDecoded.length;
    for(i = 1; i != len; ++i) PasswordArray[i] = PasswordDecoded[i-1];
    for(i = len-1; i >= 0; --i) {
        PasswordByte = PasswordArray[i];
        Intermediate1 = ((Verifier & 0x4000) === 0x0000) ? 0 : 1;
        Intermediate2 = (Verifier << 1) & 0x7FFF;
        Intermediate3 = Intermediate1 | Intermediate2;
        Verifier = Intermediate3 ^ PasswordByte;
    }
    return Verifier ^ 0xCE4B;
}
 
/* [MS-OFFCRYPTO] 2.3.7.2 Binary Document XOR Array Initialization */
var crypto_CreateXorArray_Method1 = (function() {
    var PadArray = [0xBB, 0xFF, 0xFF, 0xBA, 0xFF, 0xFF, 0xB9, 0x80, 0x00, 0xBE, 0x0F, 0x00, 0xBF, 0x0F, 0x00];
    var InitialCode = [0xE1F0, 0x1D0F, 0xCC9C, 0x84C0, 0x110C, 0x0E10, 0xF1CE, 0x313E, 0x1872, 0xE139, 0xD40F, 0x84F9, 0x280C, 0xA96A, 0x4EC3];
    var XorMatrix = [0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09, 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF, 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0, 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40, 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5, 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A, 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9, 0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0, 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC, 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10, 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168, 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C, 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD, 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC, 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4];
    var Ror = function(Byte) { return ((Byte/2) | (Byte*128)) & 0xFF; };
    var XorRor = function(byte1, byte2) { return Ror(byte1 ^ byte2); };
    var CreateXorKey_Method1 = function(Password) {
        var XorKey = InitialCode[Password.length - 1];
        var CurrentElement = 0x68;
        for(var i = Password.length-1; i >= 0; --i) {
            var Char = Password[i];
            for(var j = 0; j != 7; ++j) {
                if(Char & 0x40) XorKey ^= XorMatrix[CurrentElement];
                Char *= 2; --CurrentElement;
            }
        }
        return XorKey;
    };
    return function(password) {
        var Password = _JS2ANSI(password);
        var XorKey = CreateXorKey_Method1(Password);
        var Index = Password.length;
        var ObfuscationArray = new_raw_buf(16);
        for(var i = 0; i != 16; ++i) ObfuscationArray[i] = 0x00;
        var Temp, PasswordLastChar, PadIndex;
        if((Index & 1) === 1) {
            Temp = XorKey >> 8;
            ObfuscationArray[Index] = XorRor(PadArray[0], Temp);
            --Index;
            Temp = XorKey & 0xFF;
            PasswordLastChar = Password[Password.length - 1];
            ObfuscationArray[Index] = XorRor(PasswordLastChar, Temp);
        }
        while(Index > 0) {
            --Index;
            Temp = XorKey >> 8;
            ObfuscationArray[Index] = XorRor(Password[Index], Temp);
            --Index;
            Temp = XorKey & 0xFF;
            ObfuscationArray[Index] = XorRor(Password[Index], Temp);
        }
        Index = 15;
        PadIndex = 15 - Password.length;
        while(PadIndex > 0) {
            Temp = XorKey >> 8;
            ObfuscationArray[Index] = XorRor(PadArray[PadIndex], Temp);
            --Index;
            --PadIndex;
            Temp = XorKey & 0xFF;
            ObfuscationArray[Index] = XorRor(Password[Index], Temp);
            --Index;
            --PadIndex;
        }
        return ObfuscationArray;
    };
})();
 
/* [MS-OFFCRYPTO] 2.3.7.3 Binary Document XOR Data Transformation Method 1 */
var crypto_DecryptData_Method1 = function(password, Data, XorArrayIndex, XorArray, O) {
    /* If XorArray is set, use it; if O is not set, make changes in-place */
    if(!O) O = Data;
    if(!XorArray) XorArray = crypto_CreateXorArray_Method1(password);
    var Index, Value;
    for(Index = 0; Index != Data.length; ++Index) {
        Value = Data[Index];
        Value ^= XorArray[XorArrayIndex];
        Value = ((Value>>5) | (Value<<3)) & 0xFF;
        O[Index] = Value;
        ++XorArrayIndex;
    }
    return [O, XorArrayIndex, XorArray];
};
 
var crypto_MakeXorDecryptor = function(password) {
    var XorArrayIndex = 0, XorArray = crypto_CreateXorArray_Method1(password);
    return function(Data) {
        var O = crypto_DecryptData_Method1(null, Data, XorArrayIndex, XorArray);
        XorArrayIndex = O[1];
        return O[0];
    };
};
 
/* 2.5.343 */
function parse_XORObfuscation(blob, length, opts, out) {
    var o = { key: parseuint16(blob), verificationBytes: parseuint16(blob) };
    if(opts.password) o.verifier = crypto_CreatePasswordVerifier_Method1(opts.password);
    out.valid = o.verificationBytes === o.verifier;
    if(out.valid) out.insitu_decrypt = crypto_MakeXorDecryptor(opts.password);
    return o;
}
 
/* 2.4.117 */
function parse_FilePassHeader(blob, length, oo) {
    var o = oo || {}; o.Info = blob.read_shift(2); blob.l -= 2;
    if(o.Info === 1) o.Data = parse_RC4Header(blob, length);
    else o.Data = parse_RC4CryptoHeader(blob, length);
    return o;
}
function parse_FilePass(blob, length, opts) {
    var o = { Type: blob.read_shift(2) }; /* wEncryptionType */
    if(o.Type) parse_FilePassHeader(blob, length-2, o);
    else parse_XORObfuscation(blob, length-2, opts, o);
    return o;
}