/trunk/CHANGELOG.md |
---|
1,10 → 1,11 |
# Changelog |
## 1.7.0.7 [Work-In-Progress] |
## 1.7.0.7 [08-Aug-2021] |
- Standalone filters are now created in 32 and 64 bit. To make this work, you need to install FilterFoundry.8bf and FilterFoundry64.8bf in your plug-in directory. |
- Security improvement: Filters which have been built using obfuscation "V3" or "V4" will now always be treated as protected plugin, even if the obfuscated code has been manipulated. |
- Introduced obfuscation "V4" (more secure) |
- Obfuscated "V1" and obfuscated "V2" filters can now be loaded again (as long as they aren't protected) |
- Security improvement: Filters which have been built using obfuscation "V3" will now always be treated as protected plugin, even if the obfuscated code has been manipulated. |
- Windows message box dialogs are now properly "modal" (they have the correct parent window handle), and therefore, they blink when you click at the parent window. |
- Standalone filters are now created in 32 and 64 bit. To make this work, you need to install FilterFoundry.8bf and FilterFoundry64.8bf in your plug-in directory. |
- The TPLT (template data) and build dialog resources are now deleted from standalone filters to save space. |
## 1.7.0.6 [01-Aug-2021] |
/trunk/Obfuscation.md |
---|
14,9 → 14,24 |
Defined in **ff.h**, implemented in **make.c**: |
void obfusc(PARM_T* pparm); |
void obfusc(PARM_T* pparm, unsigned int seed); |
void deobfusc(PARM_T* pparm); |
### Obfuscation "Version 4" |
Introduced in **Filter Foundry 1.7.0.7** [08-Aug-2021] |
It is not compiler-dependant, but different between every standalone filter. |
The binary code of the 8BF file will be manipulated during building |
in order to store the seed into the `deobfusc()` function. |
This allows that 32 bit and 64 bit filters are "cross built". |
Algorithm: XOR shift like in version 2, but the seed is individual for |
each individual built standalone filter. |
The value "4" will be stored at position 0x30 (this field is not used in the `PARM` resource). |
### Obfuscation "Version 3" |
Introduced in **Filter Foundry 1.7.0.5** [30-Jul-2021] |
/trunk/README.md |
---|
6,7 → 6,7 |
Initially written by [Toby Thain](https://www.telegraphics.com.au/sw/) in 2003 - 2009, the development has been continued by [Daniel Marschall](https://www.daniel-marschall.de/) / [ViaThinkSoft](https://www.viathinksoft.de/) since 2018. Several advancements and improvements have been made, and a 64-bit Windows version was created. |
### Windows version 1.7.0.6 |
### Windows version 1.7.0.7 |
Filter Foundry 1.7 comes with a 32-Bit Windows plugin (FilterFoundry.8bf, [Download here](https://www.viathinksoft.de/download/248/FilterFoundry.8bf)) and a 64-Bit Windows plugin (FilterFoundry64.8bf, [Download here](https://www.viathinksoft.de/download/249/FilterFoundry64.8bf)). |
/trunk/TODO.md |
---|
5,12 → 5,14 |
Known bugs |
---------- |
* 32/64 bit cross creation is not possible if obfuscation is compiler dependent! We need to make something different! |
(Nothing here yet) |
Minor priority stuff or ideas |
----------------------------- |
* When a filter is created obfuscated and you click "Make" again, should then be the "obfuscate" checkbox be checked again? |
* The filter `r*(y&1)` looks horrible when you zoom out! |
* Memory leak: `strdup()` and `my_strdup()` need `free()` ! |
/trunk/ff.h |
---|
88,6 → 88,7 |
//#define DEBUG |
// from main.c |
unsigned long get_parm_hash(PARM_T *parm); |
void DoPrepare (FilterRecordPtr epb); |
void DoStart (FilterRecordPtr epb); |
OSErr DoContinue (FilterRecordPtr epb); |
115,10 → 116,11 |
void evalpixel(unsigned char *outp,unsigned char *inp); |
// from make.c |
#define OBFUSC_V4_DEFAULT_SEED 0x52830517 |
unsigned long printablehash(unsigned long hash); |
size_t fixpipl(PIPropertyList *pipl,size_t origsize,StringPtr title, long *event_id); |
size_t aete_generate(void* aeteptr, PARM_T *pparm, long event_id); |
void obfusc(PARM_T* pparm); |
void obfusc(PARM_T* pparm, unsigned int seed); |
void deobfusc(PARM_T* pparm); |
// from loadfile_*.c |
165,3 → 167,4 |
#endif /* _MSC_VER */ |
#endif /* INCLUDED_FF_H */ |
/trunk/load_mac.c |
---|
79,7 → 79,13 |
// then try plugin formats (Mac first, then Windows .8bf or .prm DLL) |
}else if( (readok = readmacplugin(sfr,reason) || read8bfplugin(sfr,reason)) ){ |
if ((gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) { |
if (gdata->parm.unknown2 == 4) { |
// Obfuscation V4 is protected, because FF>=1.7.0.5 combines protection and obfuscation |
*reason = "The filter is protected."; |
} |
else { |
*reason = "Incompatible obfuscation."; |
} |
return false; // Stop! We know the issue now. |
}else if(gdata->parm.iProtected){ |
*reason = "The filter is protected."; |
/trunk/load_win.c |
---|
91,7 → 91,13 |
if (hm = LoadLibraryEx(myp2cstrcpy(name,sfr->sfFile.name),NULL,LOAD_LIBRARY_AS_DATAFILE)) { |
if (readPARMresource(hm,reason,READ_OBFUSC)) { |
if ((gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) { |
if (gdata->parm.unknown2 == 4) { |
// Obfuscation V4 is protected, because FF>=1.7.0.5 combines protection and obfuscation |
*reason = _strdup("The filter is protected."); |
} |
else { |
*reason = _strdup("Incompatible obfuscation."); |
} |
return false; // Stop! We know the issue now. |
} else if (gdata->parm.iProtected) { |
*reason = _strdup("The filter is protected."); |
/trunk/main.c |
---|
64,17 → 64,17 |
DLLEXPORT MACPASCAL |
void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result); |
unsigned long get_parm_hash(PARM_T parm) { |
unsigned long get_parm_hash(PARM_T *parm) { |
unsigned long hash; |
int i; |
hash = djb2((char*)parm.category); |
hash += djb2((char*)parm.title); |
hash += djb2((char*)parm.copyright); |
hash += djb2((char*)parm.author); |
for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm.map[i]); |
for (i = 0; i < 8; i++) hash += hash += djb2((char*)parm.ctl[i]); |
for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm.formula[i]); |
hash = djb2((char*)parm->category); |
hash += djb2((char*)parm->title); |
hash += djb2((char*)parm->copyright); |
hash += djb2((char*)parm->author); |
for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm->map[i]); |
for (i = 0; i < 8; i++) hash += hash += djb2((char*)parm->ctl[i]); |
for (i = 0; i < 4; i++) hash += hash += djb2((char*)parm->formula[i]); |
return hash; |
} |
247,7 → 247,7 |
if (strlen(tempdir) > 0) strcat(tempdir, "/"); |
#endif |
hash = (gdata->standalone) ? get_parm_hash(gdata->parm) : 0; |
hash = (gdata->standalone) ? get_parm_hash(&gdata->parm) : 0; |
sprintf(outfilename, "%sFilterFoundry%d.afs", tempdir, hash); |
myc2pstrcpy(sfr.sfFile.name, outfilename); |
345,7 → 345,7 |
if (strlen(tempdir) > 0) strcat(tempdir, "/"); |
#endif |
hash = (isStandalone) ? get_parm_hash(gdata->parm) : 0; |
hash = (isStandalone) ? get_parm_hash(&gdata->parm) : 0; |
sprintf(outfilename, "%sFilterFoundry%d.afs", tempdir, hash); |
myc2pstrcpy(sfr.sfFile.name, outfilename); |
/trunk/make.c |
---|
460,30 → 460,34 |
return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */ |
} |
// TODO: obfusc() must be compiler independent again, otherwise the 32/64 cross-create won't work! |
void _xorshift(unsigned char** p, uint32_t* x32, size_t num) { |
size_t i; |
unsigned char* x = *p; |
for (i = 0; i < num; i++) { |
// https://de.wikipedia.org/wiki/Xorshift |
*x32 ^= *x32 << 13; |
*x32 ^= *x32 >> 17; |
*x32 ^= *x32 << 5; |
*x++ ^= *x32; |
} |
*p = x; |
} |
void obfusc(PARM_T* pparm) { |
void obfusc(PARM_T* pparm, unsigned int seed) { |
unsigned char* p; |
size_t i; |
unsigned int seed; |
size_t size, seed_position; |
seed_position = offsetof(PARM_T, unknown2); |
size = sizeof(PARM_T); |
// Version 3 obfuscation |
// Filter Foundry >= 1.7.0.5 |
// Version 4 obfuscation |
// Filter Foundry >= 1.7.0.7 |
do { |
seed = (unsigned int)time(0); |
} while ((seed == 0x90E364A3/*V1*/) || (seed == 0xE2CFCA34/*V2*/)); |
srand(seed); |
p = (unsigned char*)pparm; |
for (i = 0; i < seed_position; i++) *p++ ^= rand(); |
*((unsigned int*)p) = seed; // seed is placed at this position. data will lost! (in deobfusc, it will be set to 0x00000000) |
_xorshift(&p, &seed, seed_position); |
*((unsigned int*)p) = 4; // Obfusc V4 info |
p += 4; |
for (i = 0; i < size - seed_position - 4; i++) *p++ ^= rand(); |
_xorshift(&p, &seed, size - seed_position - 4); |
} |
void deobfusc(PARM_T* pparm) { |
516,6 → 520,26 |
*p++ ^= x32; |
} |
} |
else if (seed == 4) { |
// Version 4 obfuscation |
// Filter Foundry >= 1.7.0.7 |
// Not compiler dependent, but individual for each build |
// It is important that this code works for both x86 and x64 indepdently from the used compiler, |
// otherwise, the cross-make x86/x64 won't work! |
seed = OBFUSC_V4_DEFAULT_SEED; // this value will be manipulated during the building of each individual filter (see make_win.c) |
p = (unsigned char*)pparm; |
_xorshift(&p, &seed, seed_position); |
p += 4; // obfusc info == 4 |
_xorshift(&p, &seed, size - seed_position - 4); |
// Filter Foundry >= 1.7.0.5 builds combines obfuscation and protection |
// when a standalone filter is built. Theoretically, you can un-protect a |
// plugin, even if it is obfuscated, just by bit-flipping the LSB of byte 0x164. |
// Therefore, we enforce that the plugin is protected! |
// Note: We don't need to check PARM_T_PREMIERE, because only PARM_T |
// can be obfuscated by FilterFoundry. |
pparm->iProtected = 1; |
} |
else { |
// Version 3 obfuscation |
// Filter Foundry >= 1.7.0.5 |
/trunk/make_mac.c |
---|
106,7 → 106,7 |
if( !(e = PtrToHand(&gdata->parm,&h,sizeof(PARM_T))) ){ |
if(gdata->obfusc){ |
HLock(h); |
obfusc((PARM_T*)*h); |
obfusc((PARM_T*)*h, OBFUSC_V4_DEFAULT_SEED); |
HUnlock(h); |
parm_type = 'DATA'; |
parm_id = OBFUSCDATA_ID; |
/trunk/make_win.c |
---|
52,7 → 52,7 |
char name[1024]; |
char description[1024]; |
size_t i; |
int iname = 0; |
size_t iname = 0; |
int idescription = 0; |
// Description |
172,6 → 172,31 |
free(changeRequestStr); |
} |
int binary_replace_file(const char* filename, unsigned int search, unsigned int replace) { |
unsigned int srecord = 0; |
int found = 0; |
FILE* fptr = fopen(filename, "rb+"); |
if (fptr == NULL) return -1; |
while ((fread(&srecord, sizeof(srecord), 1, fptr) == 1)) |
{ |
if (srecord == search) { |
srecord = replace; |
fseek(fptr, -1*(long)sizeof(srecord), SEEK_CUR); |
fwrite(&srecord, (int)sizeof(srecord), 1, fptr); |
fseek(fptr, 0, SEEK_CUR); // important! |
found++; |
} |
else { |
fseek(fptr, -1*(long)(sizeof(srecord) - 1), SEEK_CUR); |
} |
} |
fclose(fptr); |
return found; |
} |
Boolean doresources(HMODULE srcmod,char *dstname, int bits){ |
HRSRC datarsrc,aetersrc,manifestsrc; |
HGLOBAL datah,aeteh,hupdate,manifesth; |
183,12 → 208,9 |
LPCTSTR parm_type; |
int i,parm_id; |
Boolean discard = true; |
unsigned int obfuscseed = 0; |
long event_id; |
// if(!EnumResourceLanguages(srcmod,"PiPL",MAKEINTRESOURCE(16000),enumfunc,0)) |
// dbglasterror("EnumResourceLanguages"); |
if( (hupdate = _BeginUpdateResource(dstname,false)) ){ |
DBG("BeginUpdateResource OK"); |
if( (datarsrc = FindResource(srcmod,MAKEINTRESOURCE(16000),"TPLT")) |
229,6 → 251,12 |
/* Generate 'aete' resource (contains names of the parameters for the "Actions" tab in Photoshop) */ |
aetesize = aete_generate(newaete, pparm, event_id); |
if (gdata->obfusc) { |
// Avoid that the same filter can generate with two seeds, |
// otherwise the comparison would be much easier |
obfuscseed = (unsigned int)get_parm_hash(pparm); |
} |
// ====== Change Pascal strings to C-Strings |
/* convert to C strings for Windows PARM resource */ |
253,7 → 281,9 |
if(gdata->obfusc){ |
parm_type = RT_RCDATA; |
parm_id = OBFUSCDATA_ID; |
obfusc(pparm); |
// Note: After we have finished updating the resources, we will write <obfuscseed> into the binary code of the 8BF file |
obfusc(pparm, obfuscseed); |
}else{ |
parm_type = "PARM"; |
parm_id = PARM_ID; |
282,9 → 312,18 |
}else dbglasterror(_strdup("Find-, Load- or LockResource")); |
// Here, the file will be saved |
if(!_EndUpdateResource(hupdate,discard)) |
dbglasterror(_strdup("EndUpdateResource")); |
if (gdata->obfusc) { |
// We modify the binary code to replace the deobfuscate-seed from OBFUSC_V4_DEFAULT_SEED to obfuscseed |
if (binary_replace_file(dstname, OBFUSC_V4_DEFAULT_SEED, obfuscseed) <= 0) { |
dbg("binary_replace_file failed"); |
discard = true; |
} |
} |
if(pparm) free(pparm); |
if(newpipl) free(newpipl); |
if(newaete) free(newaete); |
296,11 → 335,11 |
BOOL remove_64_filename_prefix(char* dstname) { |
// foobar.8bf => foobar.8bf |
// foobar64.8bf => foobar.8bf |
int i; |
size_t i; |
for (i = strlen(dstname); i > 2; i--) { |
if (dstname[i] == '.') { |
if ((dstname[i - 2] == '6') && (dstname[i - 1] == '4')) { |
int tmp = strlen(dstname); |
size_t tmp = strlen(dstname); |
memcpy(&dstname[i - 2], &dstname[i], strlen(dstname) - i + 1); |
dstname[tmp - 2] = 0; |
return true; |
312,10 → 351,10 |
BOOL add_64_filename_prefix(char* dstname) { |
// foobar.8bf => foobar64.8bf |
int i; |
size_t i; |
for (i = strlen(dstname); i > 2; i--) { |
if (dstname[i] == '.') { |
int tmp = strlen(dstname); |
size_t tmp = strlen(dstname); |
memcpy(&dstname[i + 2], &dstname[i], strlen(dstname) - i + 1); |
dstname[i] = '6'; |
dstname[i + 1] = '4'; |
/trunk/manifest32.xml |
---|
5,7 → 5,7 |
<assemblyIdentity |
name="Telegraphics.FilterFoundry" |
processorArchitecture="x86" |
version="1.7.0.6" |
version="1.7.0.7" |
type="win32"/> |
<description>Filter Foundry</description> |
<dependency> |
/trunk/manifest64.xml |
---|
5,7 → 5,7 |
<assemblyIdentity |
name="Telegraphics.FilterFoundry" |
processorArchitecture="amd64" |
version="1.7.0.6" |
version="1.7.0.7" |
type="win32"/> |
<description>Filter Foundry</description> |
<dependency> |
/trunk/testcases/import/foundry17_32_op.8bf |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes: |
Deleted: svn:mime-type |
-application/octet-stream |
\ No newline at end of property |
/trunk/testcases/import/foundry17_64_op.8bf |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes: |
Deleted: svn:mime-type |
-application/octet-stream |
\ No newline at end of property |
/trunk/testcases/import/README.md |
---|
28,10 → 28,12 |
| Import FilterFoundry 1.7 for Windows x86 | .8bf | 0x2086 | foundry17_32.8bf | Watcom | Works | |
| Import FF Obfuscated "V2" 1.7 for Windows x86 | .8bf | n/a | foundry17_32_o.8bf | Watcom | Works | |
| Import FF Protected 1.7 for Windows x86 | .8bf | 0x2086 | foundry17_32_p.8bf | Watcom | Locked OK | |
| Import FF Prot+Obfsc "V2" 1.7 for Windows x86 | .8bf | n/a | foundry17_32_op.8bf | Watcom | Locked OK | |
| Import FF Prot+Obfsc "V2" 1.7 for Windows x86 | .8bf | n/a | foundry17_32_op2.8bf | Watcom | Locked OK | |
| Import FF Prot+Obfsc "V3" 1.7 for Windows x86 | .8bf | n/a | foundry17_32_op3.8bf | Watcom | Locked/Incompat | |
| Import FF Prot+Obfsc "V4" 1.7 for Windows x86 | .8bf | n/a | foundry17_32_op4.8bf | Watcom | Locked OK | |
| Import FilterFoundry 1.7 for Windows x64 | .8bf | 0x2086 | foundry17_64.8bf | MSVC++ | Works | |
| Import FF Obfuscated "V2" 1.7 for Windows x64 | .8bf | n/a | foundry17_64_o.8bf | MSVC++ | Works | |
| Import FF Protected 1.7 for Windows x64 | .8bf | 0x2086 | foundry17_64_p.8bf | MSVC++ | Locked OK | |
| Import FF Prot+Obfsc "V2" 1.7 for Windows x64 | .8bf | n/a | foundry17_64_op.8bf | MSVC++ | Locked OK | |
| Import FF Prot+Obfsc "V2" 1.7 for Windows x64 | .8bf | n/a | foundry17_64_op2.8bf | MSVC++ | Locked OK | |
| Import FF Prot+Obfsc "V3" 1.7 for Windows x64 | .8bf | n/a | foundry17_64_op3.8bf | MSVC++ | Locked/Incompat | |
| Import FF Prot+Obfsc "V4" 1.7 for Windows x64 | .8bf | n/a | foundry17_64_op4.8bf | MSVC++ | Locked OK | |
/trunk/testcases/import/foundry17_32_op2.8bf |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/testcases/import/foundry17_32_op4.8bf |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/testcases/import/foundry17_64_op2.8bf |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/testcases/import/foundry17_64_op4.8bf |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/version.h |
---|
32,8 → 32,8 |
#define plugInName "FilterFoundry" |
#define VERSION_STR "1.7.0.6" |
#define VERSION_NUM 1,7,0,6 |
#define VERSION_STR "1.7.0.7" |
#define VERSION_NUM 1,7,0,7 |
#define VERS_RSRC VERSION_NUM,verUS,VERSION_STR,"Filter Foundry " VERSION_STR |
42,7 → 42,7 |
#define PROJECT_URL "https://github.com/danielmarschall/filter_foundry" |
/* formatted for Win32 VERSIONINFO resource */ |
#define VI_VERS_NUM 1,7,0,6 |
#define VI_VERS_NUM 1,7,0,7 |
#define VI_FLAGS 0 /* 0 for final, or any of VS_FF_DEBUG,VS_FF_PATCHED,VS_FF_PRERELEASE,VS_FF_PRIVATEBUILD,VS_FF_SPECIALBUILD */ |
#define VI_COMMENTS "Download the latest version here: " PROJECT_URL "\0" /* null terminated Comments field */ |
#define VI_COMPANY_NAME "ViaThinkSoft, Telegraphics Pty Ltd\0" |