Subversion Repositories filter_foundry

Rev

Rev 293 | Rev 311 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  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. #include <assert.h>
  24. #include <time.h>
  25.  
  26. #include "ff.h"
  27.  
  28. const volatile uint32_t cObfuscV4Seed = 0x52830517; // this value will be manipulated during the building of each individual filter (see make_win.c)
  29.  
  30. int rand_msvcc(unsigned int* seed) {
  31.         *seed = *seed * 214013L + 2531011L;
  32.         return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */
  33. }
  34.  
  35. int rand_openwatcom(unsigned int* seed) {
  36.         // https://github.com/open-watcom/open-watcom-v2/blob/master/bld/clib/math/c/rand.c
  37.         *seed = *seed * 1103515245L + 12345L;
  38.         return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */
  39. }
  40.  
  41. void xorshift(unsigned char** p, uint32_t* x32, size_t num) {
  42.         size_t i;
  43.         unsigned char* x = *p;
  44.         for (i = 0; i < num; i++) {
  45.                 // https://de.wikipedia.org/wiki/Xorshift
  46.                 *x32 ^= *x32 << 13;
  47.                 *x32 ^= *x32 >> 17;
  48.                 *x32 ^= *x32 << 5;
  49.                 *x++ ^= *x32;
  50.         }
  51.         *p = x;
  52. }
  53.  
  54. int obfuscation_version(PARM_T* pparm) {
  55.         uint32_t obfusc_info = pparm->unknown2;
  56.  
  57.         if (obfusc_info == 0x00000000) { // 00 00 00 00
  58.                 // Photoshop FilterFactory default initialization
  59.                 // (no obfuscation)
  60.                 return 0;
  61.         }
  62.         else if (obfusc_info == 0x00000001) { // 01 00 00 00
  63.                 // Premiere FilterFactory default initialization
  64.                 // (no obfuscation)
  65.                 return 0;
  66.         }
  67.         else if (obfusc_info == 0x90E364A3) { // A3 64 E3 90
  68.                 // Version 1 obfuscation (Filter Foundry 1.4b8,9,10)
  69.                 return 1;
  70.         }
  71.         else if (obfusc_info == 0xE2CFCA34) { // 34 CA CF E2
  72.                 // Version 2 obfuscation (Filter Foundry 1.7b1)
  73.                 return 2;
  74.         }
  75.         else if ((obfusc_info >= 4) && (obfusc_info <= 0xFF)) { // xx 00 00 00
  76.                 // Version 4 obfuscation (Filter Foundry 1.7.0.7)
  77.                 // Version 5 obfuscation (Filter Foundry 1.7.0.8)
  78.                 // Future: Version 6, 7, 8, ... 255
  79.                 return obfusc_info;
  80.         }
  81.         else {
  82.                 // Version 3 obfuscation (Filter Foundry 1.7.0.5)
  83.                 // obfusc_info is the srand() seed and is equal to the time(0) build timestamp
  84.                 return 3;
  85.         }
  86. }
  87.  
  88. uint32_t obfusc(PARM_T* pparm) {
  89.         // Windows:   Version 5 obfuscation (Introduced in Filter Foundry 1.7.0.8)
  90.         // Macintosh: Version 4 obfuscation (Introduced in Filter Foundry 1.7.0.7)
  91.  
  92.         unsigned char* p;
  93.         size_t size, seed_position;
  94.         uint32_t seed, initial_seed;
  95.         uint32_t obfusc_version;
  96.        
  97. #ifdef MAC_ENV
  98.         // For Mac, we use obfuscation version 4, because the placing the seed into the produced executable code is not implemented in Mac!
  99.         // (It needs to be implemented in make_mac.c)
  100.         initial_seed = OBFUSC_V4_DEFAULT_SEED;
  101.         obfusc_version = 4;
  102. #else
  103.         // In obfuscation version 5, the seed is also the checksum. It will be verified at deobfusc()!
  104.         initial_seed = (uint32_t)get_parm_hash(pparm);
  105.         obfusc_version = 5;
  106. #endif
  107.  
  108.         seed_position = offsetof(PARM_T, unknown2);
  109.         size = sizeof(PARM_T);
  110.         seed = initial_seed;
  111.  
  112.         if (obfusc_version == 5) {
  113.                 // make v4 and v5 intentionally incompatible to avoid a downgrade-attack
  114.                 seed ^= 0xFFFFFFFF;
  115.         }
  116.  
  117.         p = (unsigned char*)pparm;
  118.         xorshift(&p, &seed, seed_position);
  119.         *((uint32_t*)p) = obfusc_version;
  120.         p += 4;
  121.         xorshift(&p, &seed, size - seed_position - 4);
  122.  
  123.         return initial_seed;
  124. }
  125.  
  126. void deobfusc(PARM_T* pparm) {
  127.         uint32_t obfusc_version;
  128.         size_t size = sizeof(PARM_T);
  129.  
  130.         obfusc_version = obfuscation_version(pparm);
  131.  
  132.         switch (obfusc_version) {
  133.                 case 0:
  134.                         // no obfuscation
  135.                         return;
  136.                 case 1: {
  137.                         // Version 1 obfuscation (Filter Foundry 1.4b8,9,10)
  138.                         // Filter built with VC++ (official release by Toby Thain)
  139.  
  140.                         unsigned char* p;
  141.                         size_t i;
  142.                         uint32_t seed;
  143.  
  144.                         seed = 0xdc43df3c;
  145.  
  146.                         for (i = size, p = (unsigned char*)pparm; i--;) {
  147.                                 *p++ ^= rand_msvcc(&seed);
  148.                         }
  149.                         break;
  150.                 }
  151.                 case 2: {
  152.                         // Version 2 obfuscation (Filter Foundry 1.7b1)
  153.                         // Compiler independent
  154.  
  155.                         unsigned char* p;
  156.                         size_t i;
  157.                         uint32_t seed;
  158.  
  159.                         seed = 0x95d4a68f;
  160.  
  161.                         for (i = size, p = (unsigned char*)pparm; i--;) {
  162.                                 seed ^= seed << 13;
  163.                                 seed ^= seed >> 17;
  164.                                 seed ^= seed << 5;
  165.                                 *p++ ^= seed;
  166.                         }
  167.                         break;
  168.                 }
  169.                 case 3: {
  170.                         // Version 3 obfuscation (Filter Foundry 1.7.0.5)
  171.                         // NO loading of other implementation supported, but that doesn't matter since
  172.                         // obfuscation and protection is combined in Filter Factory >= 1.7.0.5.
  173.                         // Using rand() is more secure, because it differs from compiler to compiler, so
  174.                         // it is harder to read a protected 8BF plugin.
  175.                         // Note that rand() in combination with srand() is deterministic, so it is safe
  176.                         // to use it: https://stackoverflow.com/questions/55438293/does-rand-function-in-c-follows-non-determinstc-algorithm
  177.                         // Note: 32-Bit FF is built using OpenWatcom (to support Win95), while 64-Bit FF is built using Microsoft Visual C++
  178.  
  179.                         unsigned char* p;
  180.                         size_t i;
  181.                         uint32_t seed;
  182.                         size_t seed_position;
  183.  
  184.                         seed = pparm->unknown2;
  185.                         seed_position = offsetof(PARM_T, unknown2); // = offsetof(PARM_T_PREMIERE, unknown1)
  186.  
  187.                         srand(seed);
  188.                         p = (unsigned char*)pparm;
  189.                         for (i = 0; i < seed_position; i++) *p++ ^= rand();
  190.                         *((uint32_t*)p) = 0; // here was the seed. Fill it with 0x00000000
  191.                         p += 4;
  192.                         for (i = 0; i < size - seed_position - 4; i++) *p++ ^= rand();
  193.                         break;
  194.                 }
  195.                 case 4:
  196.                 case 5: {
  197.                         // Version 4 obfuscation (Filter Foundry 1.7.0.7)
  198.                         // Version 5 obfuscation (Filter Foundry 1.7.0.8)
  199.                         // Not compiler dependent, but individual for each build
  200.                         // It is important that this code works for both x86 and x64 indepdently from the used compiler,
  201.                         // otherwise, the cross-make x86/x64 won't work!
  202.                         // Version 5 contains a seed requirement (checksum).
  203.  
  204.                         unsigned char* p;
  205.                         size_t seed_position;
  206.                         uint32_t seed, initial_seed;
  207.  
  208.                         initial_seed = cObfuscV4Seed; // this value will be manipulated during the building of each individual filter (see make_win.c)
  209.  
  210.                         seed = initial_seed;
  211.                         seed_position = offsetof(PARM_T, unknown2); // = offsetof(PARM_T_PREMIERE, unknown1)
  212.  
  213.                         if (obfusc_version == 5) {
  214.                                 // make v4 and v5 intentionally incompatible to avoid a downgrade-attack
  215.                                 seed ^= 0xFFFFFFFF;
  216.                         }
  217.  
  218.                         p = (unsigned char*)pparm;
  219.                         xorshift(&p, &seed, seed_position);
  220.                         p += 4; // obfusc info == 4
  221.                         xorshift(&p, &seed, size - seed_position - 4);
  222.  
  223.                         if (obfusc_version == 5) {
  224.                                 if ((uint32_t)get_parm_hash(pparm) != initial_seed) {
  225.                                         // Integrity check failed!
  226.                                         memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
  227.                                 }
  228.                         }
  229.  
  230.                         break;
  231.                 }
  232.                 default: {
  233.                         // Obfuscation version unexpected!
  234.                         memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
  235.                 }
  236.         }
  237.  
  238.         if ((pparm->cbSize != PARM_SIZE) &&
  239.                 //(pparm->cbSize != PARM_SIZE_PREMIERE) &&
  240.                 (pparm->cbSize != PARM_SIG_MAC)) {
  241.                 memset(pparm, 0, sizeof(PARM_T)); // invalidate everything
  242.         }
  243.                
  244.         if (obfusc_version >= 3) {
  245.                 // Filter Foundry >= 1.7.0.5 builds combines obfuscation and protection
  246.                 // when a standalone filter is built. Theoretically, you can un-protect a
  247.                 // plugin, even if it is obfuscated, just by bit-flipping the LSB of byte 0x164.
  248.                 // Therefore, we enforce that the plugin is protected!
  249.                 pparm->iProtected = 1;
  250.  
  251.                 // Furthermore, if obfuscation 3+ failed (since the seed is individual for each 8BF file),
  252.                 // we still want that load_*.c is able to detect pparm->iProtected instead
  253.                 // of throwing the error "Incompatible obfuscation".
  254.                 pparm->cbSize = PARM_SIZE;
  255.         }
  256. }
  257.