Subversion Repositories filter_foundry

Rev

Rev 347 | Rev 349 | 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)
348 daniel-mar 27
const volatile uint64_t cObfuscSeed = 0x38AD972A52830517ull;
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
 
348 daniel-mar 53
#ifndef CHAR_BIT
54
#define CHAR_BIT 8
55
#endif
56
 
57
uint64_t rol_u64(uint64_t value, uint64_t by) {
58
        return value << by | value >> (sizeof(uint64_t) * CHAR_BIT - by);
59
}
60
 
61
void rolshift(unsigned char** p, uint64_t* x64, size_t num) {
62
        size_t i;
63
        unsigned char* x = *p;
64
        for (i = 0; i < num; i++) {
65
                *x++ ^= *x64;
66
                *x64 = rol_u64(*x64, 1);
67
        }
68
        *p = x;
69
}
70
 
293 daniel-mar 71
int obfuscation_version(PARM_T* pparm) {
309 daniel-mar 72
        uint32_t obfusc_info = pparm->unknown2;
292 daniel-mar 73
 
74
        if (obfusc_info == 0x00000000) { // 00 00 00 00
75
                // Photoshop FilterFactory default initialization
76
                // (no obfuscation)
77
                return 0;
78
        }
79
        else if (obfusc_info == 0x00000001) { // 01 00 00 00
80
                // Premiere FilterFactory default initialization
81
                // (no obfuscation)
82
                return 0;
83
        }
84
        else if (obfusc_info == 0x90E364A3) { // A3 64 E3 90
85
                // Version 1 obfuscation (Filter Foundry 1.4b8,9,10)
86
                return 1;
87
        }
88
        else if (obfusc_info == 0xE2CFCA34) { // 34 CA CF E2
89
                // Version 2 obfuscation (Filter Foundry 1.7b1)
90
                return 2;
91
        }
293 daniel-mar 92
        else if ((obfusc_info >= 4) && (obfusc_info <= 0xFF)) { // xx 00 00 00
292 daniel-mar 93
                // Version 4 obfuscation (Filter Foundry 1.7.0.7)
94
                // Version 5 obfuscation (Filter Foundry 1.7.0.8)
293 daniel-mar 95
                // Future: Version 6, 7, 8, ... 255
292 daniel-mar 96
                return obfusc_info;
97
        }
98
        else {
99
                // Version 3 obfuscation (Filter Foundry 1.7.0.5)
100
                // obfusc_info is the srand() seed and is equal to the time(0) build timestamp
101
                return 3;
102
        }
103
}
104
 
311 daniel-mar 105
uint32_t crc32b(char *data, int nLength) {
106
        int i, j, k;
318 daniel-mar 107
        uint32_t crc, mask;
108
        char byte;
311 daniel-mar 109
 
110
        i = 0;
111
        crc = 0xFFFFFFFF;
112
 
113
        for(k=0;k<nLength;k++) {
114
                byte = data[k];
115
                crc = crc ^ byte;
116
                for (j = 7; j >= 0; j--) {
318 daniel-mar 117
                        mask = (-1) * (crc & 1);
311 daniel-mar 118
                        crc = (crc >> 1) ^ (0xEDB88320 & mask);
119
                }
120
                i++;
121
        }
122
        return ~crc;
123
}
124
 
347 daniel-mar 125
uint64_t obfusc(PARM_T* pparm) {
126
        // Version 6 obfuscation (Introduced in Filter Foundry 1.7.0.10)
292 daniel-mar 127
 
128
        unsigned char* p;
347 daniel-mar 129
        uint32_t seed1, seed2;
348 daniel-mar 130
        uint64_t initial_seed, rolseed;
347 daniel-mar 131
 
292 daniel-mar 132
#ifdef MAC_ENV
347 daniel-mar 133
        // Currently, make_mac.c does not implement modifying the executable code (TODO),
134
        // so we will use the default initial_seed!
348 daniel-mar 135
        initial_seed = cObfuscSeed;
136
        seed1 = initial_seed & 0xFFFFFFFF;
137
        seed2 = initial_seed >> 32;
292 daniel-mar 138
#else
347 daniel-mar 139
        // Give always the same seed if the parameters are the same. No random values.
140
        // This initial seed will be returned and built into the executable code by make_win.c
141
        seed1 = get_parm_hash(pparm);
142
        seed2 = crc32b((char*)pparm, sizeof(PARM_T));
348 daniel-mar 143
        initial_seed = ((uint64_t)seed2 << 32) + seed1;
292 daniel-mar 144
#endif
145
 
347 daniel-mar 146
        pparm->unknown1 = 0;
147
        pparm->unknown2 = 0;
148
        pparm->unknown3 = 0;
292 daniel-mar 149
 
347 daniel-mar 150
        // AFTER unknown1-3 have been set to 0, calculate the checksum!
151
        pparm->unknown1 = crc32b((char*)pparm, sizeof(PARM_T));
309 daniel-mar 152
 
347 daniel-mar 153
        seed1 = initial_seed & 0xFFFFFFFF;
292 daniel-mar 154
        p = (unsigned char*)pparm;
347 daniel-mar 155
        xorshift(&p, &seed1, sizeof(PARM_T));
292 daniel-mar 156
 
348 daniel-mar 157
        rolseed = initial_seed;
347 daniel-mar 158
        p = (unsigned char*)pparm;
348 daniel-mar 159
        rolshift(&p, &rolseed, sizeof(PARM_T));
347 daniel-mar 160
 
161
        pparm->unknown2 = 6; // obfusc version
162
 
292 daniel-mar 163
        return initial_seed;
164
}
165
 
166
void deobfusc(PARM_T* pparm) {
309 daniel-mar 167
        uint32_t obfusc_version;
292 daniel-mar 168
        size_t size = sizeof(PARM_T);
169
 
293 daniel-mar 170
        obfusc_version = obfuscation_version(pparm);
292 daniel-mar 171
 
172
        switch (obfusc_version) {
173
                case 0:
174
                        // no obfuscation
175
                        return;
176
                case 1: {
177
                        // Version 1 obfuscation (Filter Foundry 1.4b8,9,10)
178
                        // Filter built with VC++ (official release by Toby Thain)
179
 
180
                        unsigned char* p;
181
                        size_t i;
309 daniel-mar 182
                        uint32_t seed;
292 daniel-mar 183
 
184
                        seed = 0xdc43df3c;
185
 
186
                        for (i = size, p = (unsigned char*)pparm; i--;) {
187
                                *p++ ^= rand_msvcc(&seed);
188
                        }
189
                        break;
190
                }
191
                case 2: {
192
                        // Version 2 obfuscation (Filter Foundry 1.7b1)
193
                        // Compiler independent
194
 
195
                        unsigned char* p;
196
                        size_t i;
309 daniel-mar 197
                        uint32_t seed;
292 daniel-mar 198
 
199
                        seed = 0x95d4a68f;
200
 
201
                        for (i = size, p = (unsigned char*)pparm; i--;) {
202
                                seed ^= seed << 13;
203
                                seed ^= seed >> 17;
204
                                seed ^= seed << 5;
205
                                *p++ ^= seed;
206
                        }
207
                        break;
208
                }
209
                case 3: {
210
                        // Version 3 obfuscation (Filter Foundry 1.7.0.5)
211
                        // NO loading of other implementation supported, but that doesn't matter since
212
                        // obfuscation and protection is combined in Filter Factory >= 1.7.0.5.
213
                        // Using rand() is more secure, because it differs from compiler to compiler, so
214
                        // it is harder to read a protected 8BF plugin.
215
                        // Note that rand() in combination with srand() is deterministic, so it is safe
216
                        // to use it: https://stackoverflow.com/questions/55438293/does-rand-function-in-c-follows-non-determinstc-algorithm
217
                        // Note: 32-Bit FF is built using OpenWatcom (to support Win95), while 64-Bit FF is built using Microsoft Visual C++
218
 
219
                        unsigned char* p;
220
                        size_t i;
309 daniel-mar 221
                        uint32_t seed;
292 daniel-mar 222
                        size_t seed_position;
223
 
224
                        seed = pparm->unknown2;
225
                        seed_position = offsetof(PARM_T, unknown2); // = offsetof(PARM_T_PREMIERE, unknown1)
226
 
227
                        srand(seed);
329 daniel-mar 228
 
292 daniel-mar 229
                        p = (unsigned char*)pparm;
329 daniel-mar 230
                        for (i = 0; i < seed_position; i++) *p++ ^= (int)(rand() * 1.0 / ((double)RAND_MAX + 1) * 256);
309 daniel-mar 231
                        *((uint32_t*)p) = 0; // here was the seed. Fill it with 0x00000000
292 daniel-mar 232
                        p += 4;
329 daniel-mar 233
                        for (i = 0; i < size - seed_position - 4; i++) *p++ ^= (int)(rand() * 1.0 / ((double)RAND_MAX + 1) * 256);
234
 
292 daniel-mar 235
                        break;
236
                }
237
                case 4:
238
                case 5: {
239
                        // Version 4 obfuscation (Filter Foundry 1.7.0.7)
240
                        // Version 5 obfuscation (Filter Foundry 1.7.0.8)
241
                        // Not compiler dependent, but individual for each build
242
                        // It is important that this code works for both x86 and x64 indepdently from the used compiler,
243
                        // otherwise, the cross-make x86/x64 won't work!
244
                        // Version 5 contains a seed requirement (checksum).
245
 
246
                        unsigned char* p;
247
                        size_t seed_position;
309 daniel-mar 248
                        uint32_t seed, initial_seed;
292 daniel-mar 249
 
347 daniel-mar 250
                        initial_seed = cObfuscSeed & 0xFFFFFFFF; // this value will be manipulated during the building of each individual filter (see make_win.c)
309 daniel-mar 251
 
252
                        seed = initial_seed;
292 daniel-mar 253
                        seed_position = offsetof(PARM_T, unknown2); // = offsetof(PARM_T_PREMIERE, unknown1)
254
 
309 daniel-mar 255
                        if (obfusc_version == 5) {
256
                                // make v4 and v5 intentionally incompatible to avoid a downgrade-attack
257
                                seed ^= 0xFFFFFFFF;
258
                        }
259
 
292 daniel-mar 260
                        p = (unsigned char*)pparm;
293 daniel-mar 261
                        xorshift(&p, &seed, seed_position);
329 daniel-mar 262
                        *((uint32_t*)p) = 0; // here was the version info. Fill it with 0x00000000
263
                        p += 4; // obfusc info was 4 or 5
293 daniel-mar 264
                        xorshift(&p, &seed, size - seed_position - 4);
292 daniel-mar 265
 
266
                        if (obfusc_version == 5) {
318 daniel-mar 267
                                pparm->unknown2 = 0; // make sure crc32b matches always
347 daniel-mar 268
                                if (crc32b((char*)pparm, sizeof(PARM_T)) != initial_seed) {
292 daniel-mar 269
                                        // Integrity check failed!
309 daniel-mar 270
                                        memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
292 daniel-mar 271
                                }
272
                        }
273
 
274
                        break;
275
                }
347 daniel-mar 276
                case 6: {
277
                        // Version 6 obfuscation (Filter Foundry 1.7.0.10)
278
                        // Not compiler dependent, but individual for each build
279
                        // It is important that this code works for both x86 and x64 indepdently from the used compiler,
280
                        // otherwise, the cross-make x86/x64 won't work!
281
 
282
                        unsigned char* p;
348 daniel-mar 283
                        uint32_t seed1, checksum;
284
                        uint64_t initial_seed, rolseed;
285
 
286
                        initial_seed = cObfuscSeed; // this value will be manipulated during the building of each individual filter (see make_win.c)
347 daniel-mar 287
 
348 daniel-mar 288
                        rolseed = initial_seed;
347 daniel-mar 289
                        p = (unsigned char*)pparm;
348 daniel-mar 290
                        rolshift(&p, &rolseed, sizeof(PARM_T));
347 daniel-mar 291
 
292
                        seed1 = initial_seed & 0xFFFFFFFF;
293
                        p = (unsigned char*)pparm;
294
                        xorshift(&p, &seed1, sizeof(PARM_T));
295
 
296
                        checksum = pparm->unknown1;
297
 
298
                        pparm->unknown1 = 0;
299
                        pparm->unknown2 = 0;
300
                        pparm->unknown3 = 0;
301
 
302
                        if (checksum != crc32b((char*)pparm, sizeof(PARM_T))) {
303
                                // Integrity check failed!
304
                                memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
305
                        }
306
 
307
                        break;
308
                }
292 daniel-mar 309
                default: {
310
                        // Obfuscation version unexpected!
309 daniel-mar 311
                        memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
311 daniel-mar 312
 
313
                        // If "return" is present: Calling function will receive an invalid cbSize value, hence showing "incompatible obfuscation"
314
                        // 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"
315
                        return;
292 daniel-mar 316
                }
317
        }
318
 
309 daniel-mar 319
        if ((pparm->cbSize != PARM_SIZE) &&
320
                //(pparm->cbSize != PARM_SIZE_PREMIERE) &&
321
                (pparm->cbSize != PARM_SIG_MAC)) {
322
                memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
323
        }
324
 
292 daniel-mar 325
        if (obfusc_version >= 3) {
326
                // Filter Foundry >= 1.7.0.5 builds combines obfuscation and protection
327
                // when a standalone filter is built. Theoretically, you can un-protect a
328
                // plugin, even if it is obfuscated, just by bit-flipping the LSB of byte 0x164.
329
                // Therefore, we enforce that the plugin is protected!
330
                pparm->iProtected = 1;
331
 
332
                // Furthermore, if obfuscation 3+ failed (since the seed is individual for each 8BF file),
293 daniel-mar 333
                // we still want that load_*.c is able to detect pparm->iProtected instead
334
                // of throwing the error "Incompatible obfuscation".
292 daniel-mar 335
                pparm->cbSize = PARM_SIZE;
336
        }
312 daniel-mar 337
 
338
        if (obfusc_version >= 1) {
339
                // information was lost due to obfuscation. Make sure it is zero.
340
                pparm->unknown2 = 0;
341
        }
292 daniel-mar 342
}