Rev 507 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
267 | daniel-mar | 1 | # Obfuscated filters |
2 | |||
272 | daniel-mar | 3 | ## Resource location |
267 | daniel-mar | 4 | |
5 | Obfuscated standalone filters: |
||
375 | daniel-mar | 6 | - Windows resource: OBFS\16\0 (previously RCDATA\16001\0) |
7 | - MacOS resource: 'obFS' 16 (previously 'DATA' 16001) |
||
267 | daniel-mar | 8 | |
9 | Normal standalone filters: |
||
375 | daniel-mar | 10 | - Windows resource: PARM\16\0 (previously PARM\16000\0) |
11 | - MacOS resource: 'PARM' 16 (previously 'PARM' 16000) |
||
267 | daniel-mar | 12 | |
271 | daniel-mar | 13 | ## Implementation |
270 | daniel-mar | 14 | |
292 | daniel-mar | 15 | Defined in **ff.h**, implemented in **obfusc.c**: |
267 | daniel-mar | 16 | |
508 | daniel-mar | 17 | // Implements Obfusc V7. |
18 | // Returns a seed1 and seed2 which need to be stored in the executable code. |
||
19 | obfusc(PARM_T* pparm, uint64_t* out_initial_seed, uint64_t* out_initial_seed2); |
||
282 | daniel-mar | 20 | |
21 | // In V1+V2: Seed is hardcoded |
||
317 | daniel-mar | 22 | // In V3: Seed is in PARM (field "unknown2") |
508 | daniel-mar | 23 | // In V4-V7: Seed is in the program code and will me modified with a binary search+replace |
271 | daniel-mar | 24 | void deobfusc(PARM_T* pparm); |
25 | |||
508 | daniel-mar | 26 | ### Obfuscation "Version 7" |
27 | |||
28 | Introduced in **Filter Foundry 1.7.0.17** |
||
29 | |||
30 | Now, there are two 64-bit seeds: |
||
31 | |||
32 | Initial seed 1: `0x7416972a52830517` (is in the code segment) |
||
33 | Initial seed 2: `0xEF87A2F13E1F2186` (is in the data segment) |
||
34 | |||
35 | First, XOR-Shift64 using seed 2, then ROL shift, then XOR-Shift32 like in Obfusc V6. |
||
36 | |||
347 | daniel-mar | 37 | ### Obfuscation "Version 6" |
38 | |||
39 | Introduced in **Filter Foundry 1.7.0.10** |
||
40 | |||
441 | daniel-mar | 41 | First, the fields `unknown1`, `unknown2`, aned `unknown3` are set to 0. |
347 | daniel-mar | 42 | |
348 | daniel-mar | 43 | A 64 bit seed will be generated. |
349 | daniel-mar | 44 | On Windows, the seed is the ECMA 182 CRC64 checksum of the PARM. |
507 | daniel-mar | 45 | On Macintosh, it stays at the default value `0x38AD972A52830517` (before 1.7.0.17) or |
46 | `0x7416972a52830517` (beginning with 1.7.0.17). |
||
349 | daniel-mar | 47 | (because the manipulation of the binary code is not implemented). |
348 | daniel-mar | 48 | |
349 | daniel-mar | 49 | Then, the CRC32b checksum of the PARM will be written to `unknown1`. |
50 | |||
51 | The PARM will then be XORed with a random data stream of the lower 32 bits of the 64 bit seed. |
||
347 | daniel-mar | 52 | The algorithm is the XORshift which was introcuced in obfuscation version 2. |
53 | Unlike obfuscation version 3-5, while generating and applying the random data |
||
54 | stream, no bytes are skipped. |
||
55 | |||
348 | daniel-mar | 56 | After this, PARM will be XORed with the 64 bit seed, |
57 | which will be ROLed by 1 bit after each byte: |
||
347 | daniel-mar | 58 | |
348 | daniel-mar | 59 | uint64_t rol_u64(uint64_t value, uint64_t by) { |
60 | return value << by | value >> (sizeof(uint64_t) * 8 - by); |
||
61 | } |
||
62 | |||
63 | The 64 bit seed is stored in the executable. |
||
64 | |||
347 | daniel-mar | 65 | The DWORD value `0x00000006` will be stored at field `unknown2` |
66 | (byte 0x30..0x33; the field is not used in the `PARM` resource). |
||
67 | |||
68 | During de-obfuscation, the program will check if the checksum in `unknown1` |
||
69 | matches. If it does not match, the data will be discarded. |
||
70 | |||
292 | daniel-mar | 71 | ### Obfuscation "Version 5" |
72 | |||
73 | Introduced in **Filter Foundry 1.7.0.8** |
||
74 | |||
75 | Obfuscation version 5 is the same as version 4, but there is a constraint |
||
311 | daniel-mar | 76 | that the seed must be equal to the CRC32b checksum of the deobfuscated PARM. |
292 | daniel-mar | 77 | This is done to check the integrity of the deobfuscation. |
311 | daniel-mar | 78 | |
309 | daniel-mar | 79 | Also, the xor-shifting is intentionally incompatible with version 4 |
311 | daniel-mar | 80 | (to avoid downgrade-attacks) by XORing the initial seed with 0xFFFFFFFF. |
292 | daniel-mar | 81 | |
329 | daniel-mar | 82 | The DWORD value `0x00000005` will be stored at field `unknown2` |
83 | (byte 0x30..0x33; the field is not used in the `PARM` resource). |
||
84 | |||
85 | While generating and applying the random data stream, the bytes |
||
86 | 0x30..0x33 (the location where the version info is stored) are skipped, |
||
87 | like in version 3. |
||
88 | |||
276 | daniel-mar | 89 | ### Obfuscation "Version 4" |
90 | |||
292 | daniel-mar | 91 | Introduced in **Filter Foundry 1.7.0.7** |
276 | daniel-mar | 92 | |
93 | It is not compiler-dependant, but different between every standalone filter. |
||
94 | |||
277 | daniel-mar | 95 | Windows version: |
276 | daniel-mar | 96 | The binary code of the 8BF file will be manipulated during building |
282 | daniel-mar | 97 | to store the seed into the `deobfusc()` function. |
98 | The placeholder value is `OBFUSC_V4_DEFAULT_SEED 0x52830517` |
||
99 | This allows that 32-bit and 64-bit filters are "cross built". |
||
276 | daniel-mar | 100 | |
277 | daniel-mar | 101 | (Theoretical) Macintosh version: |
329 | daniel-mar | 102 | Obfuscation and deobfuscation has the seed `0x52830517`, since the |
277 | daniel-mar | 103 | manipulation of the binary code is not implemented. |
104 | |||
282 | daniel-mar | 105 | Algorithm: XOR-Shift like in version 2, but the seed is individual for |
276 | daniel-mar | 106 | each individual built standalone filter. |
107 | |||
329 | daniel-mar | 108 | The DWORD value `0x00000004` will be stored at field `unknown2` |
109 | (byte 0x30..0x33; the field is not used in the `PARM` resource). |
||
276 | daniel-mar | 110 | |
329 | daniel-mar | 111 | While generating and applying the random data stream, the bytes |
112 | 0x30..0x33 (the location where the version info is stored) are skipped, |
||
113 | like in version 3. |
||
114 | |||
271 | daniel-mar | 115 | ### Obfuscation "Version 3" |
116 | |||
292 | daniel-mar | 117 | Introduced in **Filter Foundry 1.7.0.5** |
267 | daniel-mar | 118 | |
329 | daniel-mar | 119 | A random seed is chosen and written to field `unknown2` (byte 0x30..0x33). |
267 | daniel-mar | 120 | |
329 | daniel-mar | 121 | Then, the `PARM` resource will be obfuscated by applying an XOR operation to a random data stream: |
267 | daniel-mar | 122 | |
329 | daniel-mar | 123 | unsigned char *p; |
124 | *p++ ^= (int)(rand() * 1.0 / (RAND_MAX + 1) * 256); |
||
125 | |||
126 | Bytes 0x30..0x33 (the location where the seed is stored) are skipped. |
||
127 | |||
128 | The `rand()` operation is compiler-dependant, and therefore the resource cannot be exchanged between plugins. |
||
129 | |||
270 | daniel-mar | 130 | 32 bit plugin is built with OpenWatcom (for Win95 compatibility) which has following formula: |
131 | |||
132 | int rand_openwatcom(unsigned int* seed) { |
||
133 | *seed = *seed * 1103515245L + 12345L; |
||
134 | return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */ |
||
135 | } |
||
136 | |||
271 | daniel-mar | 137 | 64 bit plugin is built with Visual C++ which has following formula: |
270 | daniel-mar | 138 | |
139 | int rand_msvcc(unsigned int* seed) { |
||
272 | daniel-mar | 140 | *seed = *seed * 214013L + 2531011L; |
141 | return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */ |
||
270 | daniel-mar | 142 | } |
143 | |||
271 | daniel-mar | 144 | ### Obfuscation "Version 2" |
267 | daniel-mar | 145 | |
292 | daniel-mar | 146 | Introduced in **Filter Foundry 1.7b1** |
267 | daniel-mar | 147 | |
329 | daniel-mar | 148 | It is compiler-independent! |
267 | daniel-mar | 149 | |
150 | Algorithm: [XOR-Shift](https://de.wikipedia.org/wiki/Xorshift "XOR-Shift") with hardcoded seed `0x95d4a68f`. |
||
151 | |||
152 | x32 = 0x95d4a68f; |
||
153 | for(i = size, p = pparm; i--;) { |
||
272 | daniel-mar | 154 | x32 ^= x32 << 13; |
155 | x32 ^= x32 >> 17; |
||
156 | x32 ^= x32 << 5; |
||
157 | *p++ ^= x32; |
||
267 | daniel-mar | 158 | } |
159 | |||
271 | daniel-mar | 160 | ### Obfuscation "Version 1" |
267 | daniel-mar | 161 | |
162 | Introduced in **Filter Foundry 1.4b8,9,10** |
||
163 | |||
329 | daniel-mar | 164 | It is compiler-dependant, and therefore the resource cannot be exchanged between plugins! |
267 | daniel-mar | 165 | |
166 | Algorithm: XOR with `rand()`-stream with hardcoded seed `0xdc43df3c`. |
||
167 | |||
168 | srand(0xdc43df3c); |
||
169 | for(i = size, p = pparm; i--;) { |
||
272 | daniel-mar | 170 | *p++ ^= rand(); |
271 | daniel-mar | 171 | } |
172 | |||
173 | The plugin is built with Visual C++ which has following formula: |
||
174 | |||
175 | int rand_msvcc(unsigned int* seed) { |
||
272 | daniel-mar | 176 | *seed = *seed * 214013L + 2531011L; |
177 | return (*seed >> 16) & 0x7fff; /* Scale between 0 and RAND_MAX */ |
||
271 | daniel-mar | 178 | } |
179 |