├── .gitignore ├── LICENSE ├── README.md ├── ff_tool.ppj └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | *.ppx 3 | *.tag 4 | *.exe 5 | *.zip 6 | *.intro 7 | *.obj 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlashForge firmware tool 2 | Supported printers: 3 | * FlashForge Dreamer 4 | * FlashForge Dreamer NX 5 | * FlashForge Finder 6 | * FlashForge Creator Max 7 | * FlashForge Guider ( not linux based firmware like in Guider II, II S ) 8 | * FlashForge Inventor ( not linux based firmware like FlashForge Inventor II ) 9 | * Dremel 3D20 10 | * PowerSpec Ultra 3D 11 | ------------ 12 | ### Building 13 | **Linux:** 14 | 15 | `gcc main.c -o ff_fw_tool` 16 | 17 | **Windows:** 18 | 19 | Pelles C project included 20 | 21 | **Firmware encryption keys:** 22 | | Key | Vendor | 23 | | ---------- | ---------- | 24 | | flashforge790315 | FlashForge/PowerSpec | 25 | | flashforge123456 | Dremel | 26 | ### Usage 27 | **Decrypt firmware:** 28 | 29 | `ff_fw_tool -k key -i firmware.bin -o decrypted_firmware.bin` 30 | 31 | **Encrypt firmware:** 32 | 33 | `ff_fw_tool -k key -e -i firmware.bin -o encrypted_firmware.bin` 34 | -------------------------------------------------------------------------------- /ff_tool.ppj: -------------------------------------------------------------------------------- 1 | # 2 | # PROJECT FILE generated by "Pelles C for Windows, version 8.00". 3 | # WARNING! DO NOT EDIT THIS FILE. 4 | # 5 | 6 | POC_PROJECT_VERSION = 7.00# 7 | POC_PROJECT_TYPE = 3# 8 | POC_PROJECT_OUTPUTDIR = output# 9 | POC_PROJECT_RESULTDIR = .# 10 | POC_PROJECT_ARGUMENTS = # 11 | POC_PROJECT_WORKPATH = .# 12 | POC_PROJECT_EXECUTOR = # 13 | POC_PROJECT_ZIPEXTRA = # 14 | CC = pocc.exe# 15 | AS = poasm.exe# 16 | RC = porc.exe# 17 | LINK = polink.exe# 18 | SIGN = posign.exe# 19 | CCFLAGS = -std:C11 -Tx86-coff -Zi -Ob1 -fp:precise -W1 -Gd# 20 | ASFLAGS = -AIA32 -Zi -Gd# 21 | RCFLAGS = # 22 | LINKFLAGS = -debug -debugtype:both -subsystem:console -machine:x86 kernel32.lib advapi32.lib delayimp.lib# 23 | SIGNFLAGS = -location:CU -store:MY -timeurl:http://timestamp.verisign.com/scripts/timstamp.dll -errkill# 24 | INCLUDE = $(PellesCDir)\Include\Win;$(PellesCDir)\Include# 25 | LIB = $(PellesCDir)\Lib\Win;$(PellesCDir)\Lib# 26 | WizCreator = Pelle Orinius# 27 | 28 | # 29 | # Build ff_fw_tool.exe. 30 | # 31 | ff_fw_tool.exe: \ 32 | output\main.obj 33 | $(LINK) $(LINKFLAGS) -out:"$@" $** 34 | 35 | # 36 | # Build main.obj. 37 | # 38 | output\main.obj: \ 39 | main.c 40 | $(CC) $(CCFLAGS) "$!" -Fo"$@" 41 | 42 | .EXCLUDEDFILES: 43 | 44 | .SILENT: 45 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | const uint8_t swap_table_encode[256] = 7 | { 8 | 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, 9 | 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 10 | 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, 11 | 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, 12 | 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 13 | 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, 14 | 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, 15 | 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 16 | 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, 17 | 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, 18 | 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 19 | 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, 20 | 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, 21 | 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 22 | 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, 23 | 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16, 24 | }; 25 | 26 | const uint8_t swap_table_decode[256] = { 27 | 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, 28 | 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 29 | 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, 30 | 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, 31 | 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 32 | 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, 33 | 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, 34 | 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 35 | 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, 36 | 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, 37 | 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 38 | 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, 39 | 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, 40 | 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 41 | 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, 42 | 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D, 43 | }; 44 | 45 | uint8_t xor_key_table[11][16] = { 0 }; 46 | 47 | int xor_data_buffer(uint8_t *pbuf, const uint8_t *pkey, int size) 48 | { 49 | for (int i = 0; i < size; i++) 50 | { 51 | pbuf[i] ^= pkey[i]; 52 | } 53 | return size; 54 | } 55 | 56 | void swap_data_buffer(uint8_t *pdata, int size, int decode) 57 | { 58 | const uint8_t *p_swap = 0; 59 | 60 | p_swap = decode ? swap_table_decode : swap_table_encode; 61 | 62 | for (int i = 0; i < size; i++) 63 | { 64 | pdata[i] = p_swap[pdata[i]]; 65 | } 66 | } 67 | 68 | void shuffle_data_buffer(uint8_t *pdata, int decode) 69 | { 70 | uint8_t tmp[4] = { 0 }; 71 | 72 | for (int i = 1; i < 4; i++) 73 | { 74 | for (int j = 0; j < 4; j++) 75 | { 76 | tmp[j] = pdata[4 * j + i]; 77 | } 78 | int pos = decode ? (4 - i) : i; 79 | 80 | for (int n = 0; n < 4; n++) 81 | { 82 | pdata[4 * n + i] = tmp[(pos + n) % 4]; 83 | } 84 | } 85 | } 86 | 87 | uint8_t xor_byte(uint8_t data) 88 | { 89 | if (data & 0x80) 90 | return ((data << 1) ^ 0x1B); 91 | else 92 | return (data << 1) & 0xFF; 93 | } 94 | 95 | int xor_data_block(uint8_t *pdata, int decode) 96 | { 97 | uint8_t xor_array[4] = { 0 }; 98 | 99 | for (int i = 0; i < 4; i++) 100 | { 101 | uint8_t xor_key = pdata[0] ^ pdata[1] ^ pdata[2] ^ pdata[3]; 102 | 103 | xor_array[0] = xor_byte(pdata[0] ^ pdata[1]) ^ (pdata[0] ^ xor_key); 104 | xor_array[1] = xor_byte(pdata[1] ^ pdata[2]) ^ (pdata[1] ^ xor_key); 105 | xor_array[2] = xor_byte(pdata[2] ^ pdata[3]) ^ (pdata[2] ^ xor_key); 106 | xor_array[3] = xor_byte(pdata[3] ^ pdata[0]) ^ (pdata[3] ^ xor_key); 107 | 108 | if (decode) 109 | { 110 | uint8_t a, b, c; 111 | a = xor_byte(xor_byte(pdata[0] ^ pdata[2])); 112 | b = xor_byte(xor_byte(pdata[1] ^ pdata[3])); 113 | c = xor_byte(a ^ b); 114 | 115 | xor_array[0] ^= c ^ a; 116 | xor_array[1] ^= c ^ b; 117 | xor_array[2] ^= c ^ a; 118 | xor_array[3] ^= c ^ b; 119 | } 120 | 121 | memcpy(pdata, xor_array, 4); 122 | pdata += 4; 123 | } 124 | 125 | return 0; 126 | } 127 | 128 | int decrypt_data_block(uint8_t *pdata) 129 | { 130 | xor_data_buffer(pdata, xor_key_table[10], 16); 131 | 132 | for (int i = 9; i >= 0; i--) 133 | { 134 | shuffle_data_buffer(pdata, 1); 135 | swap_data_buffer(pdata, 16, 1); 136 | xor_data_buffer(pdata, xor_key_table[i], 16); 137 | 138 | if (i != 0) 139 | { 140 | xor_data_block(pdata, 1); 141 | } 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | int encrypt_data_block(uint8_t *pdata) 148 | { 149 | xor_data_buffer(pdata, xor_key_table[0], 16); 150 | 151 | for (int i = 1; i <= 10; i++) 152 | { 153 | swap_data_buffer(pdata, 16, 0); 154 | shuffle_data_buffer(pdata, 0); 155 | if (i != 10) 156 | { 157 | xor_data_block(pdata, 0); 158 | } 159 | xor_data_buffer(pdata, xor_key_table[i], 16); 160 | } 161 | 162 | return 0; 163 | } 164 | 165 | int decrypt_data_buffer(uint8_t *pdata, int size, uint8_t *pkey) 166 | { 167 | int n = size / 16; 168 | uint8_t *p = &pdata[size - 16]; 169 | 170 | while (n) 171 | { 172 | decrypt_data_block(p); 173 | if (n == 1) /* last one ? */ 174 | { 175 | xor_data_buffer(p, pkey, 16); 176 | } 177 | else 178 | { 179 | xor_data_buffer(p, p - 16, 16); 180 | } 181 | --n; 182 | p -= 16; 183 | } 184 | 185 | return size; 186 | } 187 | 188 | int encrypt_data_buffer(uint8_t *pdata, int size, uint8_t *pkey) 189 | { 190 | int n = size / 16; 191 | uint8_t *p = pdata; 192 | 193 | for (int i = 0; i < n; i++) 194 | { 195 | if (i == 0) 196 | { 197 | xor_data_buffer(p, pkey, 16); 198 | } 199 | else 200 | { 201 | xor_data_buffer(p, p - 16, 16); 202 | } 203 | encrypt_data_block(p); 204 | p += 16; 205 | } 206 | 207 | return size; 208 | } 209 | 210 | void rol_bytes(uint8_t *p) 211 | { 212 | uint8_t t; 213 | 214 | t = p[0]; 215 | p[0] = p[1]; 216 | p[1] = p[2]; 217 | p[2] = p[3]; 218 | p[3] = t; 219 | } 220 | 221 | int generate_xor_table(char *psz_key) 222 | { 223 | int key_len = strlen(psz_key); 224 | if (key_len != 16) 225 | return -1; 226 | /* first entry it is key itself */ 227 | memcpy(xor_key_table[0], psz_key, key_len); 228 | uint32_t xor_key = 1; 229 | 230 | uint32_t *p_table = (void *)xor_key_table[1]; 231 | for (int i = 4; i < 44; i++) 232 | { 233 | p_table[0] = p_table[-1]; 234 | if ((i % 4) == 0) 235 | { 236 | rol_bytes((uint8_t *)p_table); 237 | swap_data_buffer((uint8_t *)p_table, 4, 0); 238 | xor_data_buffer((uint8_t *)p_table, (uint8_t *) & xor_key, 4); 239 | xor_key = xor_byte(xor_key & 0xFF); 240 | } 241 | xor_data_buffer((uint8_t *)p_table, (uint8_t *) & p_table[-4], 16); 242 | ++p_table; 243 | } 244 | 245 | return 0; 246 | } 247 | 248 | #include 249 | int main(int argc, char *argv[]) 250 | { 251 | FILE *f = 0, *o = 0; 252 | uint8_t buffer[2048]; 253 | uint8_t xor_key[16] = { 0 }; 254 | char sz_key[16+1] = "flashforge790315"; 255 | int c, encode = 0; 256 | 257 | #if defined(__linux__) || defined(__APPLE__) 258 | while ((c = getopt(argc, argv, "ei:o:k:")) != -1) 259 | #else 260 | while ((c = _getopt(argc, argv, "ei:o:k:")) != -1) 261 | #endif 262 | { 263 | switch (c) 264 | { 265 | case 'e': 266 | encode = 1; 267 | break; 268 | case 'i': 269 | f = fopen(optarg, "rb"); 270 | printf("intput: %s\n", optarg); 271 | break; 272 | case 'o': 273 | o = fopen(optarg, "wb"); 274 | printf("output: %s\n", optarg); 275 | break; 276 | case 'k': 277 | if( strlen( optarg ) != 16 ) 278 | { 279 | printf( "invalid key size\n" ); 280 | return -1; 281 | } 282 | strcpy( sz_key, optarg ); 283 | break; 284 | } 285 | } 286 | 287 | if (!f || !o) 288 | { 289 | printf("input/output path does not exist\n"); 290 | exit(-1); 291 | } 292 | 293 | printf("%s firmware... ", encode ? "encode" : "decode"); 294 | 295 | generate_xor_table( sz_key ); 296 | 297 | while (feof(f) == 0) 298 | { 299 | int align_r, w, r = fread(buffer, 1, sizeof(buffer), f); 300 | if (r < 16) 301 | break; 302 | /* if firmware has unaligned data chunk it will not be decoded/encoded */ 303 | align_r = r & (~15); 304 | if (encode) 305 | { 306 | encrypt_data_buffer(buffer, align_r, xor_key); 307 | } 308 | else 309 | { 310 | decrypt_data_buffer(buffer, align_r, xor_key); 311 | } 312 | w = fwrite(buffer, 1, r, o); 313 | if (w != r) 314 | { 315 | printf("i/o problem...\n"); 316 | exit(-3); 317 | } 318 | } 319 | fclose(f); 320 | fclose(o); 321 | 322 | printf("done\n"); 323 | return 0; 324 | } 325 | --------------------------------------------------------------------------------