Subversion Repositories filter_foundry

Rev

Rev 329 | Rev 348 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
292 daniel-mar 1
/*
2
    This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
3
    Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.com.au
4
    Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
5
 
6
    This program is free software; you can redistribute it and/or modify
7
    it under the terms of the GNU General Public License as published by
8
    the Free Software Foundation; either version 2 of the License, or
9
    (at your option) any later version.
10
 
11
    This program is distributed in the hope that it will be useful,
12
    but WITHOUT ANY WARRANTY; without even the implied warranty of
13
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
    GNU General Public License for more details.
15
 
16
    You should have received a copy of the GNU General Public License
17
    along with this program; if not, write to the Free Software
18
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
*/
20
 
21
#include <stddef.h>
22
#include <stdint.h>
23
 
24
#include "ff.h"
25
 
312 daniel-mar 26
// this value will be manipulated during the building of each individual filter (see make_win.c)
347 daniel-mar 27
const volatile uint64_t cObfuscSeed = 0x38AD972A52830517L;
292 daniel-mar 28
 
29
int rand_msvcc(unsigned int* seed) {
30
        *seed = *seed * 214013L + 2531011L;
31
        return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */
32
}
33
 
34
int rand_openwatcom(unsigned int* seed) {
35
        // https://github.com/open-watcom/open-watcom-v2/blob/master/bld/clib/math/c/rand.c
36
        *seed = *seed * 1103515245L + 12345L;
37
        return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */
38
}
39
 
293 daniel-mar 40
void xorshift(unsigned char** p, uint32_t* x32, size_t num) {
292 daniel-mar 41
        size_t i;
42
        unsigned char* x = *p;
43
        for (i = 0; i < num; i++) {
44
                // https://de.wikipedia.org/wiki/Xorshift
45
                *x32 ^= *x32 << 13;
46
                *x32 ^= *x32 >> 17;
47
                *x32 ^= *x32 << 5;
48
                *x++ ^= *x32;
49
        }
50
        *p = x;
51
}
52
 
293 daniel-mar 53
int obfuscation_version(PARM_T* pparm) {
309 daniel-mar 54
        uint32_t obfusc_info = pparm->unknown2;
292 daniel-mar 55
 
56
        if (obfusc_info == 0x00000000) { // 00 00 00 00
57
                // Photoshop FilterFactory default initialization
58
                // (no obfuscation)
59
                return 0;
60
        }
61
        else if (obfusc_info == 0x00000001) { // 01 00 00 00
62
                // Premiere FilterFactory default initialization
63
                // (no obfuscation)
64
                return 0;
65
        }
66
        else if (obfusc_info == 0x90E364A3) { // A3 64 E3 90
67
                // Version 1 obfuscation (Filter Foundry 1.4b8,9,10)
68
                return 1;
69
        }
70
        else if (obfusc_info == 0xE2CFCA34) { // 34 CA CF E2
71
                // Version 2 obfuscation (Filter Foundry 1.7b1)
72
                return 2;
73
        }
293 daniel-mar 74
        else if ((obfusc_info >= 4) && (obfusc_info <= 0xFF)) { // xx 00 00 00
292 daniel-mar 75
                // Version 4 obfuscation (Filter Foundry 1.7.0.7)
76
                // Version 5 obfuscation (Filter Foundry 1.7.0.8)
293 daniel-mar 77
                // Future: Version 6, 7, 8, ... 255
292 daniel-mar 78
                return obfusc_info;
79
        }
80
        else {
81
                // Version 3 obfuscation (Filter Foundry 1.7.0.5)
82
                // obfusc_info is the srand() seed and is equal to the time(0) build timestamp
83
                return 3;
84
        }
85
}
86
 
311 daniel-mar 87
uint32_t crc32b(char *data, int nLength) {
88
        int i, j, k;
318 daniel-mar 89
        uint32_t crc, mask;
90
        char byte;
311 daniel-mar 91
 
92
        i = 0;
93
        crc = 0xFFFFFFFF;
94
 
95
        for(k=0;k<nLength;k++) {
96
                byte = data[k];
97
                crc = crc ^ byte;
98
                for (j = 7; j >= 0; j--) {
318 daniel-mar 99
                        mask = (-1) * (crc & 1);
311 daniel-mar 100
                        crc = (crc >> 1) ^ (0xEDB88320 & mask);
101
                }
102
                i++;
103
        }
104
        return ~crc;
105
}
106
 
347 daniel-mar 107
uint64_t obfusc(PARM_T* pparm) {
108
        // Version 6 obfuscation (Introduced in Filter Foundry 1.7.0.10)
292 daniel-mar 109
 
110
        unsigned char* p;
347 daniel-mar 111
        uint32_t seed1, seed2;
112
 
292 daniel-mar 113
#ifdef MAC_ENV
347 daniel-mar 114
        // Currently, make_mac.c does not implement modifying the executable code (TODO),
115
        // so we will use the default initial_seed!
116
        uint64_t initial_seed = cObfuscSeed;
117
        seed1 = cObfuscSeed & 0xFFFFFFFF;
118
        seed2 = cObfuscSeed >> 32;
292 daniel-mar 119
#else
347 daniel-mar 120
        // Give always the same seed if the parameters are the same. No random values.
121
        // This initial seed will be returned and built into the executable code by make_win.c
122
        seed1 = get_parm_hash(pparm);
123
        seed2 = crc32b((char*)pparm, sizeof(PARM_T));
124
        uint64_t initial_seed = ((uint64_t)seed2 << 32) + seed1;
292 daniel-mar 125
#endif
126
 
347 daniel-mar 127
        pparm->unknown1 = 0;
128
        pparm->unknown2 = 0;
129
        pparm->unknown3 = 0;
292 daniel-mar 130
 
347 daniel-mar 131
        // AFTER unknown1-3 have been set to 0, calculate the checksum!
132
        pparm->unknown1 = crc32b((char*)pparm, sizeof(PARM_T));
309 daniel-mar 133
 
347 daniel-mar 134
        seed1 = initial_seed & 0xFFFFFFFF;
292 daniel-mar 135
        p = (unsigned char*)pparm;
347 daniel-mar 136
        xorshift(&p, &seed1, sizeof(PARM_T));
292 daniel-mar 137
 
347 daniel-mar 138
        seed2 = initial_seed >> 32;
139
        p = (unsigned char*)pparm;
140
        xorshift(&p, &seed2, sizeof(PARM_T));
141
 
142
        pparm->unknown2 = 6; // obfusc version
143
 
292 daniel-mar 144
        return initial_seed;
145
}
146
 
147
void deobfusc(PARM_T* pparm) {
309 daniel-mar 148
        uint32_t obfusc_version;
292 daniel-mar 149
        size_t size = sizeof(PARM_T);
150
 
293 daniel-mar 151
        obfusc_version = obfuscation_version(pparm);
292 daniel-mar 152
 
153
        switch (obfusc_version) {
154
                case 0:
155
                        // no obfuscation
156
                        return;
157
                case 1: {
158
                        // Version 1 obfuscation (Filter Foundry 1.4b8,9,10)
159
                        // Filter built with VC++ (official release by Toby Thain)
160
 
161
                        unsigned char* p;
162
                        size_t i;
309 daniel-mar 163
                        uint32_t seed;
292 daniel-mar 164
 
165
                        seed = 0xdc43df3c;
166
 
167
                        for (i = size, p = (unsigned char*)pparm; i--;) {
168
                                *p++ ^= rand_msvcc(&seed);
169
                        }
170
                        break;
171
                }
172
                case 2: {
173
                        // Version 2 obfuscation (Filter Foundry 1.7b1)
174
                        // Compiler independent
175
 
176
                        unsigned char* p;
177
                        size_t i;
309 daniel-mar 178
                        uint32_t seed;
292 daniel-mar 179
 
180
                        seed = 0x95d4a68f;
181
 
182
                        for (i = size, p = (unsigned char*)pparm; i--;) {
183
                                seed ^= seed << 13;
184
                                seed ^= seed >> 17;
185
                                seed ^= seed << 5;
186
                                *p++ ^= seed;
187
                        }
188
                        break;
189
                }
190
                case 3: {
191
                        // Version 3 obfuscation (Filter Foundry 1.7.0.5)
192
                        // NO loading of other implementation supported, but that doesn't matter since
193
                        // obfuscation and protection is combined in Filter Factory >= 1.7.0.5.
194
                        // Using rand() is more secure, because it differs from compiler to compiler, so
195
                        // it is harder to read a protected 8BF plugin.
196
                        // Note that rand() in combination with srand() is deterministic, so it is safe
197
                        // to use it: https://stackoverflow.com/questions/55438293/does-rand-function-in-c-follows-non-determinstc-algorithm
198
                        // Note: 32-Bit FF is built using OpenWatcom (to support Win95), while 64-Bit FF is built using Microsoft Visual C++
199
 
200
                        unsigned char* p;
201
                        size_t i;
309 daniel-mar 202
                        uint32_t seed;
292 daniel-mar 203
                        size_t seed_position;
204
 
205
                        seed = pparm->unknown2;
206
                        seed_position = offsetof(PARM_T, unknown2); // = offsetof(PARM_T_PREMIERE, unknown1)
207
 
208
                        srand(seed);
329 daniel-mar 209
 
292 daniel-mar 210
                        p = (unsigned char*)pparm;
329 daniel-mar 211
                        for (i = 0; i < seed_position; i++) *p++ ^= (int)(rand() * 1.0 / ((double)RAND_MAX + 1) * 256);
309 daniel-mar 212
                        *((uint32_t*)p) = 0; // here was the seed. Fill it with 0x00000000
292 daniel-mar 213
                        p += 4;
329 daniel-mar 214
                        for (i = 0; i < size - seed_position - 4; i++) *p++ ^= (int)(rand() * 1.0 / ((double)RAND_MAX + 1) * 256);
215
 
292 daniel-mar 216
                        break;
217
                }
218
                case 4:
219
                case 5: {
220
                        // Version 4 obfuscation (Filter Foundry 1.7.0.7)
221
                        // Version 5 obfuscation (Filter Foundry 1.7.0.8)
222
                        // Not compiler dependent, but individual for each build
223
                        // It is important that this code works for both x86 and x64 indepdently from the used compiler,
224
                        // otherwise, the cross-make x86/x64 won't work!
225
                        // Version 5 contains a seed requirement (checksum).
226
 
227
                        unsigned char* p;
228
                        size_t seed_position;
309 daniel-mar 229
                        uint32_t seed, initial_seed;
292 daniel-mar 230
 
347 daniel-mar 231
                        initial_seed = cObfuscSeed & 0xFFFFFFFF; // this value will be manipulated during the building of each individual filter (see make_win.c)
309 daniel-mar 232
 
233
                        seed = initial_seed;
292 daniel-mar 234
                        seed_position = offsetof(PARM_T, unknown2); // = offsetof(PARM_T_PREMIERE, unknown1)
235
 
309 daniel-mar 236
                        if (obfusc_version == 5) {
237
                                // make v4 and v5 intentionally incompatible to avoid a downgrade-attack
238
                                seed ^= 0xFFFFFFFF;
239
                        }
240
 
292 daniel-mar 241
                        p = (unsigned char*)pparm;
293 daniel-mar 242
                        xorshift(&p, &seed, seed_position);
329 daniel-mar 243
                        *((uint32_t*)p) = 0; // here was the version info. Fill it with 0x00000000
244
                        p += 4; // obfusc info was 4 or 5
293 daniel-mar 245
                        xorshift(&p, &seed, size - seed_position - 4);
292 daniel-mar 246
 
247
                        if (obfusc_version == 5) {
318 daniel-mar 248
                                pparm->unknown2 = 0; // make sure crc32b matches always
347 daniel-mar 249
                                if (crc32b((char*)pparm, sizeof(PARM_T)) != initial_seed) {
292 daniel-mar 250
                                        // Integrity check failed!
309 daniel-mar 251
                                        memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
292 daniel-mar 252
                                }
253
                        }
254
 
255
                        break;
256
                }
347 daniel-mar 257
                case 6: {
258
                        // Version 6 obfuscation (Filter Foundry 1.7.0.10)
259
                        // Not compiler dependent, but individual for each build
260
                        // It is important that this code works for both x86 and x64 indepdently from the used compiler,
261
                        // otherwise, the cross-make x86/x64 won't work!
262
 
263
                        unsigned char* p;
264
                        uint32_t seed1, seed2, checksum;
265
                        uint64_t initial_seed = cObfuscSeed; // this value will be manipulated during the building of each individual filter (see make_win.c)
266
 
267
                        seed2 = initial_seed >> 32;
268
                        p = (unsigned char*)pparm;
269
                        xorshift(&p, &seed2, sizeof(PARM_T));
270
 
271
                        seed1 = initial_seed & 0xFFFFFFFF;
272
                        p = (unsigned char*)pparm;
273
                        xorshift(&p, &seed1, sizeof(PARM_T));
274
 
275
                        checksum = pparm->unknown1;
276
 
277
                        pparm->unknown1 = 0;
278
                        pparm->unknown2 = 0;
279
                        pparm->unknown3 = 0;
280
 
281
                        if (checksum != crc32b((char*)pparm, sizeof(PARM_T))) {
282
                                // Integrity check failed!
283
                                memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
284
                        }
285
 
286
                        break;
287
                }
292 daniel-mar 288
                default: {
289
                        // Obfuscation version unexpected!
309 daniel-mar 290
                        memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
311 daniel-mar 291
 
292
                        // If "return" is present: Calling function will receive an invalid cbSize value, hence showing "incompatible obfuscation"
293
                        // If "return" is not present: Then the code below will set a correct cbSize and iProtect flag if obfusc_version>=3, which will raise the error "filter is protected"
294
                        return;
292 daniel-mar 295
                }
296
        }
297
 
309 daniel-mar 298
        if ((pparm->cbSize != PARM_SIZE) &&
299
                //(pparm->cbSize != PARM_SIZE_PREMIERE) &&
300
                (pparm->cbSize != PARM_SIG_MAC)) {
301
                memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
302
        }
303
 
292 daniel-mar 304
        if (obfusc_version >= 3) {
305
                // Filter Foundry >= 1.7.0.5 builds combines obfuscation and protection
306
                // when a standalone filter is built. Theoretically, you can un-protect a
307
                // plugin, even if it is obfuscated, just by bit-flipping the LSB of byte 0x164.
308
                // Therefore, we enforce that the plugin is protected!
309
                pparm->iProtected = 1;
310
 
311
                // Furthermore, if obfuscation 3+ failed (since the seed is individual for each 8BF file),
293 daniel-mar 312
                // we still want that load_*.c is able to detect pparm->iProtected instead
313
                // of throwing the error "Incompatible obfuscation".
292 daniel-mar 314
                pparm->cbSize = PARM_SIZE;
315
        }
312 daniel-mar 316
 
317
        if (obfusc_version >= 1) {
318
                // information was lost due to obfuscation. Make sure it is zero.
319
                pparm->unknown2 = 0;
320
        }
292 daniel-mar 321
}