├── .gitignore ├── Makefile ├── README.md ├── cpukeys.c ├── crypto.c ├── crypto.h ├── dump_patch.c ├── file_io.c ├── filefmt.c ├── fprom.c ├── fprom_data.c ├── opt_cipher.s ├── patchfile.c ├── patchfile.h ├── patchtools.c ├── patchtools.h └── rotate.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | patchtools 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRCS_C =\ 2 | patchfile.c \ 3 | crypto.c \ 4 | fprom.c \ 5 | cpukeys.c \ 6 | dump_patch.c \ 7 | file_io.c \ 8 | filefmt.c 9 | CFLAGS +=-g 10 | 11 | patchtools: $(SRCS_C) opt_cipher.o 12 | 13 | opt_cipher.o: opt_cipher.s 14 | nasm -felf64 opt_cipher.s 15 | 16 | clean: 17 | rm *.o patchtools 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Program for encrypting and decrypting Pentium II microcode patches. 2 | Written by Peter Bosch . 3 | 4 | # Disclaimer 5 | Patchfiles produced by this program might crash or damage the system they are 6 | loaded onto and the author takes no responsibility for any damages resulting 7 | from use of the software. 8 | 9 | Only public resources and publically available hardware were used by the author 10 | to produce this program. 11 | 12 | # Key material 13 | The program needs a 32 bit base key to work, the file cpukeys.c lists the 14 | various unique keys used by certain CPU models. Some keys were recovered, 15 | some are still missing, likely due to unknown microcode update structure, 16 | changes in FPROM (constants used as encryption keys). 17 | 18 | # MSRAM contents 19 | The MSRAM contents are scrambled, and to edit them you need to descramble them. 20 | An example implementation of this can be found at 21 | https://github.com/peterbjornx/p6tools 22 | 23 | # Usage 24 | patchtools [-dec] [-p ] [-i ] 25 | 26 | 27 | -h Print this message and exit 28 | 29 | -e Extract a patch to a configuration and 30 | MSRAM hexdump file 31 | 32 | -c Create a patch from a configuration and 33 | MSRAM hexdump file 34 | 35 | -d Dump the patch contents and keys to the 36 | console after encrypting or decrypting. 37 | 38 | -p Specifies the path of the patchfile to 39 | create or decrypt. When encrypting this 40 | option is not required as the program 41 | will use the path of the configuration 42 | file to generate the output path. 43 | 44 | -i Specifies the path of the config file 45 | to use or extract. When extracting this 46 | option is not required as the program 47 | will use the path of the patch file to 48 | generate the output path. 49 | 50 | 51 | # More information 52 | More information about the patch format can be found at 53 | https://twitter.com/peterbjornx/status/1321653489899081728 54 | -------------------------------------------------------------------------------- /cpukeys.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define CPU_KEY_KLAMATH_A 0x30000000 6 | #define CPU_KEY_KLAMATH_B 0x3a000000 7 | #define CPU_KEY_DESCHUTES_A 0x3b021ce0 8 | #define CPU_KEY_DESCHUTES_B 0x17ae63a2 9 | #define CPU_KEY_MOBILE_A 0x33c949f6 10 | #define CPU_KEY_MOBILE_B 0x2eba562a 11 | #define CPU_KEY_KATMAI_A 0x2d753ea0 12 | #define CPU_KEY_KATMAI_B 0x72b97882 13 | #define CPU_KEY_KATMAI_C 0x7bd10552 14 | #define CPU_KEY_COPPERMINE_A 0x3942095e 15 | #define CPU_KEY_COPPERMINE_B 0x28121d58 16 | #define CPU_KEY_COPPERMINE_C 0x2cb473c4 17 | #define CPU_KEY_BANIAS_A 0x1c514c40 18 | #define CPU_KEY_CASCADES_A 0x6b8a374e 19 | #define CPU_KEY_CASCADES_B 0x44d5346c 20 | #define CPU_KEY_MENDOCINO_A 0x4ef83ad6 21 | #define CPU_KEY_PARTLY_WORKS 0x41af33f6 22 | 23 | uint32_t cpukeys_get_base( uint32_t cpu_sig ) { 24 | 25 | switch ( cpu_sig & 0xFFF ) { 26 | /* Probably different ucode patch format */ 27 | // case 0x611: 28 | // case 0x612: 29 | // case 0x616: 30 | // case 0x617: 31 | // case 0x619: 32 | /* Obtaining keys for KLAMATH failed, likely microcode update has different format or FPROM is different */ 33 | // case 0x630: /* Klamath */ 34 | // case 0x632: /* Klamath */ 35 | // return CPU_KEY_KLAMATH_A; 36 | // case 0x633: /* Klamath */ 37 | // case 0x634: /* Klamath */ 38 | // return CPU_KEY_KLAMATH_B; 39 | case 0x650: /* Deschutes */ 40 | case 0x651: /* Deschutes */ 41 | return CPU_KEY_DESCHUTES_A; 42 | case 0x652: /* Deschutes */ 43 | case 0x653: /* Deschutes */ 44 | return CPU_KEY_DESCHUTES_B; 45 | case 0x660: /* Dixon */ 46 | case 0x66A: /* Dixon */ 47 | case 0x66D: /* Dixon */ 48 | return CPU_KEY_MOBILE_A; 49 | case 0x665: 50 | return CPU_KEY_MOBILE_B; 51 | case 0x670: 52 | case 0x671: /* katmai, tanner */ 53 | return CPU_KEY_KATMAI_A; 54 | case 0x672: /* katmai, tanner */ 55 | return CPU_KEY_KATMAI_B; 56 | case 0x673: /* katmai, tanner */ 57 | return CPU_KEY_KATMAI_C; 58 | // case 0x683: //unknown keycoppermine B0 59 | case 0x680: /* coppermine A2 */ 60 | case 0x681: /* coppermine C0 */ 61 | return CPU_KEY_COPPERMINE_A; 62 | case 0x686: /* coppermine C0 */ 63 | return CPU_KEY_COPPERMINE_B; 64 | case 0x68a: /* coppermine D0 */ 65 | return CPU_KEY_COPPERMINE_C; 66 | // case 0x690: /*unknown (works only for cpu00690_plat01_ver00000004_2000-02-06_PRD_BE8FFBD9.bin) */ 67 | // case 0x691: /* Timna (works only for cpu00691_plat01_ver00000002_2000-02-07_PRD_E6DA1028.bin) */ 68 | // case 0x692: /* Timna (works only for cpu00692_plat10_ver00000003_2000-03-22_PRD_B675019E.bin) */ 69 | // return CPU_KEY_PARTLY_WORKS; 70 | case 0x694: /*Banias */ 71 | case 0x695: /*Banias */ 72 | return CPU_KEY_BANIAS_A; 73 | // case 0x696: /* works only for cpu00696_plat10_ver00000001_2000-07-07_PRD_99C6BF9B.bin */ 74 | // return CPU_KEY_PARTLY_WORKS; 75 | case 0x6a0: /* Cascades PIII xeon A0 */ 76 | case 0x6a1: /* Cascades PIII xeon A1 */ 77 | return CPU_KEY_CASCADES_A; 78 | // case 0x6a4: unknown /* Cascades PIII xeon B0 */ 79 | case 0x6b0: 80 | case 0x6b1: 81 | return CPU_KEY_CASCADES_B; 82 | case 0x6b4: /* CPUID 0665H Medocino with PPGA/370 package */ 83 | return CPU_KEY_MENDOCINO_A; 84 | // case 0x6d0: unknown /* Dothan Processor A0 */ 85 | // case 0x6d1: unknown /* Dothan Processor A1 */ 86 | // case 0x6d6: unknown /* Dothan Processor B1 */ 87 | // case 0x6d8: unknown /* Dothan Processor C0 */ 88 | default: 89 | fprintf( stderr, "Unknown cpu key for CPUID: %03X\n", 90 | cpu_sig & 0xFFF ); 91 | exit( EXIT_FAILURE ); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /crypto.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "rotate.h" 3 | #include "crypto.h" 4 | 5 | uint32_t crypto_key; 6 | uint32_t crypto_LastCWord; 7 | uint32_t crypto_state; 8 | 9 | #ifdef USE_C_BLOCKFUNC 10 | /** 11 | * The 'block cipher' used as the basis for the update encryption. 12 | * Basically a Galois LFSR. 13 | * Because earlier versions of the program contained bruteforce key search 14 | * functionality, the program also comes with an assembly language version of 15 | * this function that avoids branches and as such is much faster. 16 | */ 17 | uint32_t crypto_c_blockfunc( uint32_t plain, uint32_t key ) { 18 | uint32_t lfsr; 19 | int iter; 20 | 21 | /* Seed the LFSR with the plaintext */ 22 | lfsr = plain; 23 | 24 | /* Run the LFSR for 37 clocks */ 25 | for ( iter = 0; iter < 37; iter++ ) { 26 | lfsr = rotr32( lfsr, 0x1 ); 27 | lfsr ^= (lfsr & 0x80000000) ? key : 0; 28 | } 29 | 30 | /* Return the LFSR state XOR the plaintext */ 31 | return lfsr ^ plain; 32 | } 33 | #define crypto_blockfunc crypto_c_blockfunc 34 | #endif 35 | 36 | void crypto_init( uint32_t key, uint32_t iv ) { 37 | crypto_LastCWord = crypto_key = key; 38 | crypto_state = iv; 39 | } 40 | 41 | uint32_t crypto_getstate( void ) { 42 | return crypto_state; 43 | } 44 | 45 | /** 46 | * Decrypt a block using the mode used by Pentium II patches. 47 | * See https://twitter.com/peterbjornx/status/1321653489899081728 48 | */ 49 | uint32_t crypto_decrypt(uint32_t ciphertext) { 50 | uint32_t state; 51 | uint32_t plaintext; 52 | 53 | state = crypto_blockfunc( crypto_state, crypto_key ) ^ ciphertext; 54 | 55 | plaintext = state ^ crypto_LastCWord; 56 | 57 | /* Keep track of the previous ciphertext block and state */ 58 | crypto_LastCWord = ciphertext; 59 | crypto_state = state; 60 | 61 | return plaintext; 62 | 63 | } 64 | 65 | uint32_t crypto_encrypt(uint32_t plaintext) { 66 | uint32_t ciphertext; 67 | uint32_t subkey; 68 | 69 | subkey = crypto_blockfunc( crypto_state, crypto_key ); 70 | 71 | crypto_state = plaintext ^ crypto_LastCWord; 72 | 73 | ciphertext = subkey ^ crypto_state; 74 | 75 | crypto_LastCWord = ciphertext; 76 | 77 | return ciphertext; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /crypto.h: -------------------------------------------------------------------------------- 1 | #ifndef __crypto_h__ 2 | #define __crypto_h__ 3 | 4 | uint32_t crypto_blockfunc( uint32_t state, uint32_t key ); 5 | void crypto_init( uint32_t tmp4, uint32_t r34 ); 6 | uint32_t crypto_getstate( void ); 7 | uint32_t crypto_decrypt( uint32_t ciphertext ); 8 | uint32_t crypto_encrypt( uint32_t plaintext ); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /dump_patch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "patchfile.h" 4 | 5 | void dump_patch_header( const patch_hdr_t *hdr ) { 6 | printf("Header version: %08X\n", hdr->header_ver); 7 | printf("Update revision: %08X\n", hdr->update_rev); 8 | printf("Date: %08X\n", hdr->date_bcd); 9 | printf("Processor sign.: %08X\n", hdr->proc_sig); 10 | printf("Checksum: %08X\n", hdr->checksum); 11 | printf("Loader revision: %08X\n", hdr->loader_ver); 12 | printf("Processor flags: %08X\n", hdr->proc_flags); 13 | printf("Data size: %08X\n", hdr->data_size); 14 | printf("Total size: %08X\n", hdr->total_size); 15 | } 16 | 17 | void dump_patch_body( const patch_body_t *body ) { 18 | const uint32_t *groupbase; 19 | uint32_t grp_or[MSRAM_GROUP_SIZE]; 20 | int i,j; 21 | printf("MSRAM: \n"); 22 | memset( grp_or, 0, sizeof grp_or ); 23 | for ( i = 0; i < MSRAM_GROUP_COUNT; i++ ) { 24 | groupbase = body->msram + MSRAM_GROUP_SIZE * i; 25 | printf("\t%04X: %08X %08X %08X %08X %08X %08X %08X %08X\n", 26 | i * 8, 27 | groupbase[0], groupbase[1], groupbase[2], groupbase[3], 28 | groupbase[4], groupbase[5], groupbase[6], groupbase[7]); 29 | for ( j = 0; j < MSRAM_GROUP_SIZE; j++ ) 30 | grp_or[j] |= groupbase[j]; 31 | } 32 | groupbase = grp_or; 33 | printf("\n\tOR : %08X %08X %08X %08X %08X %08X %08X %08X\n", 34 | 35 | groupbase[0], groupbase[1], groupbase[2], groupbase[3], 36 | groupbase[4], groupbase[5], groupbase[6], groupbase[7]); 37 | printf("Control register ops: \n"); 38 | for ( i = 0; i < PATCH_CR_OP_COUNT; i++ ) { 39 | printf("\tAddr: %08X Mask: %08X Value: %08X\n", 40 | body->cr_ops[i].address, 41 | body->cr_ops[i].mask, 42 | body->cr_ops[i].value); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /file_io.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void read_file(const char *path, void *data, size_t size) { 6 | int fd = open( path, O_RDONLY ); 7 | int nr = read( fd, data, size ); 8 | close( fd );//TODO: error checking 9 | } 10 | 11 | void write_file(const char *path, const void *data, size_t size) { 12 | int fd = open( path, O_WRONLY | O_CREAT ); 13 | int nr = write( fd, data, size ); 14 | close( fd );//TODO: error checking 15 | } 16 | -------------------------------------------------------------------------------- /filefmt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "patchfile.h" 5 | 6 | void write_patch_config( 7 | const patch_hdr_t *hdr, 8 | const patch_body_t *body, 9 | const char *filename, 10 | const char *msram_fn, 11 | uint32_t key_seed ) { 12 | FILE *file; 13 | int i; 14 | 15 | file = fopen(filename, "w"); 16 | if ( !file ) { 17 | perror( "Could not open patch config output file" ); 18 | exit( EXIT_FAILURE ); 19 | } 20 | 21 | fprintf( file, "header_ver 0x%08X\n", hdr->header_ver ); 22 | fprintf( file, "update_rev 0x%08X\n", hdr->update_rev ); 23 | fprintf( file, "date_bcd 0x%08X\n", hdr->date_bcd ); 24 | fprintf( file, "proc_sig 0x%08X\n", hdr->proc_sig ); 25 | fprintf( file, "checksum 0x%08X\n", hdr->checksum ); 26 | fprintf( file, "loader_rev 0x%08X\n", hdr->loader_ver ); 27 | fprintf( file, "proc_flags 0x%08X\n", hdr->proc_flags ); 28 | fprintf( file, "data_size 0x%08X\n", hdr->data_size ); 29 | fprintf( file, "total_size 0x%08X\n", hdr->total_size ); 30 | fprintf( file, "key_seed 0x%08X\n", key_seed ); 31 | fprintf( file, "msram_file %s\n" , msram_fn ); 32 | 33 | for ( i = 0; i < PATCH_CR_OP_COUNT; i++ ) { 34 | fprintf( file, 35 | "write_creg 0x%03X 0x%08X 0x%08X\n", 36 | body->cr_ops[i].address, 37 | body->cr_ops[i].mask, 38 | body->cr_ops[i].value); 39 | } 40 | 41 | fclose( file ); 42 | } 43 | 44 | char line_buf[4096]; 45 | 46 | void read_patch_config( 47 | patch_hdr_t *hdr, 48 | patch_body_t *body, 49 | const char *filename, 50 | char **msram_fnp, 51 | uint32_t *key_seed ) { 52 | 53 | int i; 54 | char *par_n, *par_v, *par_v2, *par_v3; 55 | char *msram_fn; 56 | uint32_t addr, mask, data; 57 | FILE *file; 58 | msram_fn = NULL; 59 | 60 | file = fopen(filename, "r"); 61 | if ( !file ) { 62 | perror( "Could not open patch config input file" ); 63 | exit( EXIT_FAILURE ); 64 | } 65 | 66 | i = 0; 67 | 68 | while ( fgets( line_buf, sizeof line_buf, file ) ) { 69 | par_n = strtok(line_buf, " "); 70 | if ( !par_n ) 71 | continue; 72 | par_v = strtok(NULL, " \n"); 73 | if ( !par_v ) { 74 | fprintf( stderr, 75 | "Config key without value: \"%s\"\n", 76 | par_n ); 77 | exit( EXIT_FAILURE ); 78 | } 79 | 80 | if ( strcmp( par_n, "header_ver" ) == 0 ) { 81 | hdr->header_ver = strtol( par_v, NULL, 0 ); 82 | } else if ( strcmp( par_n, "update_rev" ) == 0 ) { 83 | hdr->update_rev = strtol( par_v, NULL, 0 ); 84 | } else if ( strcmp( par_n, "date_bcd" ) == 0 ) { 85 | hdr->date_bcd = strtol( par_v, NULL, 0 ); 86 | } else if ( strcmp( par_n, "proc_sig" ) == 0 ) { 87 | hdr->proc_sig = strtol( par_v, NULL, 0 ); 88 | } else if ( strcmp( par_n, "checksum" ) == 0 ) { 89 | hdr->checksum = strtol( par_v, NULL, 0 ); 90 | } else if ( strcmp( par_n, "loader_rev" ) == 0 ) { 91 | hdr->loader_ver = strtol( par_v, NULL, 0 ); 92 | } else if ( strcmp( par_n, "proc_flags" ) == 0 ) { 93 | hdr->proc_flags = strtol( par_v, NULL, 0 ); 94 | } else if ( strcmp( par_n, "data_size" ) == 0 ) { 95 | hdr->data_size = strtol( par_v, NULL, 0 ); 96 | } else if ( strcmp( par_n, "total_size" ) == 0 ) { 97 | hdr->total_size = strtol( par_v, NULL, 0 ); 98 | } else if ( strcmp( par_n, "key_seed" ) == 0 ) { 99 | *key_seed = strtol( par_v, NULL, 0 ); 100 | } else if ( strcmp( par_n, "msram_file" ) == 0 ) { 101 | msram_fn = strdup( par_v ); 102 | } else if ( strcmp( par_n, "write_creg" ) == 0 ) { 103 | par_v2 = strtok(NULL, " \n"); 104 | par_v3 = strtok(NULL, " \n"); 105 | if ( !(par_v2 || par_v3) ){ 106 | fprintf( stderr, "Incomplete write_creg\n" ); 107 | exit( EXIT_FAILURE ); 108 | } 109 | addr = strtol( par_v, NULL, 0 ); 110 | mask = strtol( par_v2, NULL, 0 ); 111 | data = strtol( par_v3, NULL, 0 ); 112 | if ( addr & ~0x1FF ) { 113 | fprintf( stderr, 114 | "Invalid creg address: 0x%03X\n", 115 | addr ); 116 | exit( EXIT_FAILURE ); 117 | } 118 | if ( i >= PATCH_CR_OP_COUNT ) { 119 | fprintf( stderr, 120 | "Too many write_creg statements\n"); 121 | exit( EXIT_FAILURE ); 122 | } 123 | body->cr_ops[i].address = addr; 124 | body->cr_ops[i].mask = mask; 125 | body->cr_ops[i].value = data; 126 | i++; 127 | } else { 128 | fprintf( stderr, "Unknown config key \"%s\"\n", par_n ); 129 | exit( EXIT_FAILURE ); 130 | } 131 | } 132 | 133 | fclose( file ); 134 | 135 | *msram_fnp = msram_fn; 136 | 137 | } 138 | 139 | void write_msram_file( const patch_body_t *body, const char *filename ) { 140 | FILE *file; 141 | const uint32_t *groupbase; 142 | uint32_t grp_or[MSRAM_GROUP_SIZE]; 143 | int i,j, base; 144 | 145 | file = fopen(filename, "w"); 146 | if ( !file ) { 147 | perror( "Could not open MSRAM output file" ); 148 | exit( EXIT_FAILURE ); 149 | } 150 | 151 | base = MSRAM_BASE_ADDRESS * 8; 152 | 153 | memset( grp_or, 0, sizeof grp_or ); 154 | for ( i = 0; i < MSRAM_GROUP_COUNT; i++ ) { 155 | groupbase = body->msram + MSRAM_GROUP_SIZE * i; 156 | fprintf(file, 157 | "%04X: %08X %08X %08X %08X %08X %08X %08X %08X\n", 158 | base + i * 8, 159 | groupbase[0], groupbase[1], groupbase[2], groupbase[3], 160 | groupbase[4], groupbase[5], groupbase[6], groupbase[7]); 161 | for ( j = 0; j < MSRAM_GROUP_SIZE; j++ ) 162 | grp_or[j] |= groupbase[j]; 163 | } 164 | 165 | fclose( file ); 166 | 167 | } 168 | 169 | void read_msram_file( patch_body_t *body, const char *filename ) { 170 | char *ts; 171 | FILE *file; 172 | int addr, raddr; 173 | int g; 174 | uint32_t *groupbase; 175 | 176 | 177 | file = fopen(filename, "r"); 178 | if ( !file ) { 179 | perror( "Could not open MSRAM input file" ); 180 | exit( EXIT_FAILURE ); 181 | } 182 | 183 | while ( fgets( line_buf, sizeof line_buf, file ) ) { 184 | ts = strtok(line_buf, ": "); 185 | if ( !ts ) 186 | continue; 187 | addr = strtol( ts, NULL, 16 ); 188 | if ( addr % 8 ) { 189 | fprintf( stderr, "Misaligned address in input :%08X\n", 190 | addr ); 191 | exit( EXIT_FAILURE ); 192 | } 193 | if ( addr < MSRAM_BASE_ADDRESS * 8 ) { 194 | fprintf( stderr, 195 | "Address not in MSRAM range :%08X\n", 196 | addr ); 197 | exit( EXIT_FAILURE ); 198 | } 199 | raddr = ( addr / 8 ) - MSRAM_BASE_ADDRESS; 200 | if ( raddr >= MSRAM_GROUP_COUNT ) { 201 | fprintf( stderr, 202 | "Address not in MSRAM range :%08X\n", addr ); 203 | exit( EXIT_FAILURE ); 204 | } 205 | groupbase = body->msram + MSRAM_GROUP_SIZE * raddr; 206 | for ( g = 0; g < MSRAM_GROUP_SIZE; g++ ) { 207 | ts = strtok(NULL, " "); 208 | if ( !ts ) { 209 | fprintf( stderr, 210 | "Incomplete data for address %04X", 211 | raddr ); 212 | exit( EXIT_FAILURE ); 213 | } 214 | groupbase[g] = strtol( ts, NULL, 16 ); 215 | } 216 | 217 | } 218 | 219 | fclose( file ); 220 | 221 | } 222 | 223 | -------------------------------------------------------------------------------- /fprom.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define ROM_FLAG_VAL (0x13371337) 6 | 7 | static uint32_t FPROM[512]; 8 | 9 | __attribute__((constructor)) 10 | static void init_fprom() { 11 | int i; 12 | /* Fill the FPROM table with a flag value so we know what addresses are 13 | * unimplemented */ 14 | for ( i = 0; i < 512; i++ ) 15 | FPROM[i] = ROM_FLAG_VAL; 16 | 17 | /* Load the FPROM table with the data provided in fprom_data.c */ 18 | #include "fprom_data.c" 19 | ; 20 | } 21 | 22 | /** 23 | * Checks if a given FPROM address is implemented in this program 24 | * @param addr Address into the FPROM to check (truncated to 9 bits) 25 | * @return Non-zero if FPROM[addr mod 512] is implemented. 26 | */ 27 | int fprom_exists( uint32_t addr ) { 28 | uint32_t v = FPROM[ addr & 0x1FF ]; 29 | return v != ROM_FLAG_VAL; 30 | } 31 | 32 | /** 33 | * Gets the low 32 bits of the floating point constant ROM entry with address 34 | * addr mod 512. 35 | * @param addr Address into the FPROM to check (truncated to 9 bits) 36 | * @return The low 32 bits of FPROM[addr mod 512]. 37 | */ 38 | uint32_t fprom_get( uint32_t addr ) { 39 | assert ( fprom_exists( addr ) ); 40 | return FPROM[ addr & 0x1FF ]; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /fprom_data.c: -------------------------------------------------------------------------------- 1 | FPROM[0x00] = 0x8E7BCD5E; 2 | FPROM[0x01] = 0x00000000; 3 | FPROM[0x02] = 0x5555584F; 4 | FPROM[0x03] = 0x397FFFD4; 5 | FPROM[0x04] = 0x250CED0C; 6 | FPROM[0x05] = 0x901CEA50; 7 | FPROM[0x06] = 0x8F4F2318; 8 | FPROM[0x07] = 0x00000000; 9 | FPROM[0x08] = 0x5555558E; 10 | FPROM[0x09] = 0x5555558B; 11 | FPROM[0x0A] = 0x443DB621; 12 | FPROM[0x0B] = 0x5AFD42F4; 13 | FPROM[0x0C] = 0x63B44194; 14 | FPROM[0x0D] = 0x5D1B6D8A; 15 | FPROM[0x0E] = 0x384C73AB;//?6 16 | FPROM[0x0F] = 0x41D6086B; 17 | FPROM[0x10] = 0x0F9C0CE8;//? 18 | FPROM[0x11] = 0x0D1289A8; 19 | FPROM[0x12] = 0x555535F0; 20 | FPROM[0x13] = 0x4208B016; 21 | FPROM[0x14] = 0x53AC37B8; 22 | FPROM[0x15] = 0x3889B2F0; 23 | FPROM[0x16] = 0x55555543;//?6 24 | FPROM[0x17] = 0x66616B73;//3 25 | FPROM[0x18] = 0x0FCA4493; 26 | FPROM[0x19] = 0x6F662C91; 27 | FPROM[0x1A] = 0x0B12EEE8;//?6 28 | FPROM[0x1B] = 0x3C725AA0; 29 | FPROM[0x1C] = 0x55555555;//?6? 30 | FPROM[0x1D] = 0x44443E35;//?6 31 | FPROM[0x1E] = 0x6773C774;//?? 32 | FPROM[0x1F] = 0x50956D70;//??6 33 | FPROM[0x20] = 0xFA0532F0; 34 | FPROM[0x21] = 0x14D5E4D8; 35 | FPROM[0x22] = 0xFFFFFFFE; 36 | FPROM[0x23] = 0x55554277; 37 | FPROM[0x24] = 0x5A18A1BA;//?6 6? 38 | FPROM[0x25] = 0xB559F2CF;//?6 39 | FPROM[0x26] = 0xF5349300; 40 | FPROM[0x27] = 0x514C1AF8; 41 | FPROM[0x28] = 0x55555445; 42 | FPROM[0x29] = 0x43A3FDB6; 43 | FPROM[0x2A] = 0x2044E9AE; 44 | FPROM[0x2B] = 0x0F321240;//6? 45 | FPROM[0x2C] = 0xFFFFFA28; 46 | FPROM[0x2D] = 0x539CFAE6;//3? 47 | FPROM[0x2E] = 0x31B2E713; 48 | FPROM[0x2F] = 0x6E3BFF10; 49 | FPROM[0x30] = 0x00000000; 50 | FPROM[0x31] = 0x00000000; 51 | FPROM[0x32] = 0xE0BF85DE;//?6? 52 | FPROM[0x33] = 0xE0BF85DE; 53 | FPROM[0x34] = 0x81287C11; 54 | FPROM[0x35] = 0x4A5D30BD; 55 | FPROM[0x36] = 0x934F12E0; 56 | FPROM[0x37] = 0x20ED1749; 57 | FPROM[0x38] = 0x1827434A; 58 | FPROM[0x39] = 0x26DD6DC2;//?6 6? 59 | FPROM[0x3A] = 0x27C92FB9; 60 | FPROM[0x3B] = 0x287A2468;//?6? 61 | FPROM[0x3C] = 0xC3FEE620;//?6 6? 62 | FPROM[0x3D] = 0xDE7FBCC5; 63 | FPROM[0x3E] = 0x68DC57F2; 64 | FPROM[0x3F] = 0xE0BF85DE; 65 | FPROM[0x40] = 0x0B4611A6; 66 | FPROM[0x41] = 0x00000000; 67 | FPROM[0x42] = 0x00000000; 68 | FPROM[0x43] = 0x0B4611A6; 69 | FPROM[0x44] = 0x0B4611A6; 70 | FPROM[0x45] = 0x00000000;//?6 6? 71 | FPROM[0x46] = 0xFFFFFFFF; 72 | FPROM[0x47] = 0x00003FFF; 73 | FPROM[0x48] = 0x00000000; 74 | FPROM[0x49] = 0x00000000;//? 75 | FPROM[0x4A] = 0x00000000; 76 | FPROM[0x4B] = 0x78787878; 77 | FPROM[0x4C] = 0x07F807F8; 78 | FPROM[0x4D] = 0x0007FFF8; 79 | FPROM[0x4E] = 0xFFFFFFF8; 80 | FPROM[0x4F] = 0x00000000; 81 | FPROM[0x50] = 0x00000000;//? 82 | FPROM[0x51] = 0x00000000;//6? 83 | FPROM[0x52] = 0x00000000;//6? 84 | FPROM[0x53] = 0x00000000; 85 | FPROM[0x54] = 0x20000000;//6? 86 | FPROM[0x55] = 0xB525A249; 87 | FPROM[0x56] = 0x00000007; 88 | FPROM[0x57] = 0x00000000; 89 | FPROM[0x58] = 0x00000000; 90 | FPROM[0x59] = 0x00000000; 91 | FPROM[0x5A] = 0xFFFFFFF8; 92 | FPROM[0x5B] = 0x00000000; 93 | FPROM[0x5C] = 0x0B4611A6; 94 | FPROM[0x5D] = 0x0000000A; 95 | FPROM[0x5E] = 0x00000000; 96 | FPROM[0x5F] = 0xFFFFFFFF; 97 | FPROM[0x60] = 0x55555555; 98 | FPROM[0x61] = 0xAAAAAAAA; 99 | FPROM[0x62] = 0x00000000; 100 | FPROM[0x63] = 0x00000000;//3? 101 | FPROM[0x64] = 0x00000000;//?6?? 102 | FPROM[0x65] = 0x00000000; 103 | FPROM[0x66] = 0x00004000; 104 | FPROM[0x67] = 0x00000008;//?6 105 | FPROM[0x68] = 0x00000000; 106 | FPROM[0x69] = 0x00000000; 107 | FPROM[0x6A] = 0x00002000;//3? 108 | FPROM[0x6B] = 0x00000004; 109 | FPROM[0x6C] = 0x00000000; 110 | FPROM[0x6D] = 0x00000000; 111 | FPROM[0x6E] = 0xFFFFC000; 112 | FPROM[0x6F] = 0xFFFFFFF8; 113 | FPROM[0x70] = 0xA70EDD92; 114 | FPROM[0x71] = 0xCE1AAC13; 115 | FPROM[0x72] = 0xA95DF6EE; 116 | FPROM[0x73] = 0x3992B586; 117 | FPROM[0x74] = 0xAE1D9460; 118 | FPROM[0x75] = 0xBD65CBE4; 119 | FPROM[0x76] = 0x51B12963;//?6 120 | FPROM[0x77] = 0x2947B682;//?6 121 | FPROM[0x78] = 0xA93188EF;//?6 122 | FPROM[0x79] = 0x72401864; 123 | FPROM[0x7A] = 0x58F7D46D; 124 | FPROM[0x7B] = 0x08390C72; 125 | FPROM[0x7C] = 0xBBE9EFD0; 126 | FPROM[0x7D] = 0x35B03D34;//6? 127 | FPROM[0x7E] = 0x900FE89E; 128 | FPROM[0x7F] = 0x86A10D5A; 129 | FPROM[0x80] = 0x1A2EF221; 130 | FPROM[0x81] = 0x67C8BF6F; 131 | FPROM[0x82] = 0x4E62105D; 132 | FPROM[0x83] = 0x49764072; 133 | FPROM[0x84] = 0xBB9B15CC; 134 | FPROM[0x85] = 0x2727C5CF;//6? 135 | FPROM[0x86] = 0xF2B2594D; 136 | FPROM[0x87] = 0xFEDFA1F6;//6? 137 | FPROM[0x88] = 0x8AFD7B60; 138 | FPROM[0x89] = 0xE0E9123E; 139 | FPROM[0x8A] = 0xE8A5A511;//?6 140 | FPROM[0x8B] = 0xC4BDC688; 141 | FPROM[0x8C] = 0x9942B846; 142 | FPROM[0x8D] = 0x9FFB139F;//?6 143 | FPROM[0x8E] = 0xAD13BB90;//?3 144 | FPROM[0x8F] = 0x486C1748; 145 | FPROM[0x90] = 0xB0626A74; 146 | FPROM[0x91] = 0x3FE1928C;//?3 147 | FPROM[0x92] = 0xB0CB0B54; 148 | FPROM[0x93] = 0x51C90800;//?3 149 | FPROM[0x94] = 0x0037417F; 150 | FPROM[0x95] = 0xA896DC70; 151 | FPROM[0x96] = 0x00577251; 152 | FPROM[0x97] = 0xD3DE672E; 153 | FPROM[0x98] = 0xDD0D63B3; 154 | FPROM[0x99] = 0x6641c113; 155 | FPROM[0x9A] = 0x920EC52F; 156 | FPROM[0x9B] = 0xC7FD252C; 157 | FPROM[0x9C] = 0x46B701C5; 158 | FPROM[0x9D] = 0xDC08B077; 159 | FPROM[0x9E] = 0xC31FC770; 160 | FPROM[0x9F] = 0xA973081C; 161 | FPROM[0xA0] = 0x6EEDB76C; 162 | FPROM[0xA1] = 0x6F5BC8B2; 163 | FPROM[0xA2] = 0x6C12CC8A; 164 | FPROM[0xA3] = 0x69ACA966; 165 | FPROM[0xA4] = 0x6197F61F; 166 | FPROM[0xA5] = 0x68EB03DE; 167 | FPROM[0xA6] = 0x30048AF2; 168 | FPROM[0xA7] = 0xF48059B2; 169 | FPROM[0xA8] = 0xFE7A5FFB; 170 | FPROM[0xA9] = 0xC1916D43; 171 | FPROM[0xAA] = 0xB994E239; 172 | FPROM[0xAB] = 0x1A4561A5; 173 | FPROM[0xAC] = 0x6910265F; 174 | FPROM[0xAD] = 0x172EFEFC; 175 | FPROM[0xAE] = 0x321BFB9E; 176 | FPROM[0xAF] = 0x3FCF88C8; 177 | FPROM[0xB0] = 0x0C863F24; 178 | FPROM[0xB1] = 0x4CA790D2; 179 | FPROM[0xB2] = 0xD2CCA73F; 180 | FPROM[0xB3] = 0xDCDCE7DB;//? 181 | FPROM[0xB4] = 0xF84645CF; 182 | FPROM[0xB5] = 0xADF1164B; 183 | FPROM[0xB6] = 0x003A775A; 184 | FPROM[0xB7] = 0xBB7410D5;//? 185 | FPROM[0xB8] = 0x996699ad; 186 | FPROM[0xB9] = 0x385331AD; 187 | FPROM[0xBA] = 0xE5EE49F2; 188 | FPROM[0xBB] = 0x9F66C1DB;//?6? 189 | FPROM[0xBC] = 0xD1604F33;//?3 190 | FPROM[0xBD] = 0x3F462152; 191 | FPROM[0xBE] = 0x35BE183C; 192 | FPROM[0xBF] = 0x12A08945; 193 | FPROM[0xC0] = 0xB8000000; 194 | FPROM[0xC1] = 0xA8000000; 195 | FPROM[0xC2] = 0x68000000; 196 | FPROM[0xC3] = 0x28000000;//3? 197 | FPROM[0xC4] = 0xC0000000; 198 | FPROM[0xC5] = 0x98000000; 199 | FPROM[0xC6] = 0xC0000000; 200 | FPROM[0xC7] = 0x30000000; 201 | FPROM[0xC8] = 0xF8000000; 202 | FPROM[0xC9] = 0x88000000; 203 | FPROM[0xCA] = 0xD8000000; 204 | FPROM[0xCB] = 0x58000000; 205 | FPROM[0xCC] = 0xD8000000; 206 | FPROM[0xCD] = 0xE8000000; 207 | FPROM[0xCE] = 0x78000000; 208 | FPROM[0xCF] = 0xC8000000;//6 209 | FPROM[0xD0] = 0x28000000; 210 | FPROM[0xD1] = 0x18000000; 211 | FPROM[0xD2] = 0x28000000; 212 | FPROM[0xD3] = 0x80000000; 213 | FPROM[0xD4] = 0x88000000; 214 | FPROM[0xD5] = 0x40000000; 215 | FPROM[0xD6] = 0xB8000000; 216 | FPROM[0xD7] = 0x30000000; 217 | FPROM[0xD8] = 0x88000000;//??6 218 | FPROM[0xD9] = 0x30000000; 219 | FPROM[0xDA] = 0xA0000000; 220 | FPROM[0xDB] = 0x50000000; 221 | FPROM[0xDC] = 0xB8000000; 222 | FPROM[0xDD] = 0x80000000;//?? 223 | FPROM[0xDE] = 0xE8000000; 224 | FPROM[0xDF] = 0xF0000000; 225 | FPROM[0xE0] = 0x0EF73F7B; 226 | FPROM[0xE1] = 0x54666DE9; 227 | FPROM[0xE2] = 0xD3728CBD; 228 | FPROM[0xE3] = 0x603AD3A6; 229 | FPROM[0xE4] = 0x1DCEC753; 230 | FPROM[0xE5] = 0x9AFDC5FB; 231 | FPROM[0xE6] = 0x2D3CAD27; 232 | FPROM[0xE7] = 0xB64B03F7; 233 | FPROM[0xE8] = 0x76FAFCBA; 234 | FPROM[0xE9] = 0xEC16410A; 235 | FPROM[0xEA] = 0x7979EC5B; 236 | FPROM[0xEB] = 0xB5110CCF; 237 | FPROM[0xEC] = 0xF685C776; 238 | FPROM[0xED] = 0x9BFA9853; 239 | FPROM[0xEE] = 0x2A31604A; 240 | FPROM[0xEF] = 0x61A98814; 241 | FPROM[0xF0] = 0xF40B7BC0; 242 | FPROM[0xF1] = 0xE8A55627; 243 | FPROM[0xF2] = 0x9BC59135; 244 | FPROM[0xF3] = 0x602E66B0; 245 | FPROM[0xF4] = 0x81CA95DA; 246 | FPROM[0xF5] = 0xFE4338FE; 247 | FPROM[0xF6] = 0x63EC8D56; 248 | FPROM[0xF7] = 0x75675C90; 249 | FPROM[0xF8] = 0xE55DE96C; 250 | FPROM[0xF9] = 0xA70D849B; 251 | FPROM[0xFA] = 0xDFB264B3; 252 | FPROM[0xFB] = 0x379FAA7C; 253 | FPROM[0xFC] = 0x0CC82AAB; 254 | FPROM[0xFD] = 0x9F8EF14D; 255 | FPROM[0xFE] = 0xF93F7A44; 256 | FPROM[0xFF] = 0x3DAE9CF5; 257 | -------------------------------------------------------------------------------- /opt_cipher.s: -------------------------------------------------------------------------------- 1 | global crypto_blockfunc 2 | section .text 3 | 4 | crypto_blockfunc: 5 | mov eax, edi ; IV = IV0 6 | xor edx, edx ; Zero register 7 | %rep 37 8 | mov ecx, esi ; T = KEY 9 | ror eax, 1 ; IV = ROR( IV, 1 ) 10 | cmovnc ecx, edx ; if ( IV.NC ) T = 0 11 | xor eax, ecx ; IV ^= T 12 | %endrep 13 | xor eax, edi ; IV ^= IV0 14 | ret 15 | -------------------------------------------------------------------------------- /patchfile.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "rotate.h" 6 | #include "crypto.h" 7 | #include "patchtools.h" 8 | #include "patchfile.h" 9 | 10 | #define ENCRYPT_MISSING_FPROM (1) 11 | #define ENCRYPT_OK (0) 12 | #define IV_KEY_INDEX_MASK (0x9C) 13 | #define INTEGRITY_INDEX_MASK (0xFF) 14 | #define CPUID_STEPPING_MASK (0xF) 15 | 16 | /** 17 | * Decrypts and validates an integrity check word based on the current 18 | * encryption state, and exits with an error if it was unsuccessful. 19 | * 20 | * This function does a FPROM lookup and as such, might fail if the FPROM table 21 | * is not complete. 22 | * 23 | * @param ct_integ The encrypted ICV to validate 24 | */ 25 | void decrypt_verify_integrity( uint32_t ct_integ ) { 26 | uint32_t integrity_idx, pt_integ, exp_integ; 27 | 28 | /* The ICV is derived from the crypto state before it is encrypted, so 29 | * compute it first. The current state of the ciphermode is masked and 30 | * indexed into the FPROM to get the check value */ 31 | integrity_idx = crypto_getstate() & INTEGRITY_INDEX_MASK; 32 | 33 | /* Decrypt the ICV from the input */ 34 | pt_integ = crypto_decrypt( ct_integ ); 35 | 36 | /* Check that the FPROM entry used to derive the ICV is mapped in the 37 | * program's table */ 38 | if ( !fprom_exists( integrity_idx ) ) { 39 | /* Assuming correct decryption, this tells us a new FPROM table 40 | * entry */ 41 | fprintf( stderr, 42 | "Integrity check uses unknown FPROM[0x%02X] = 0x%08X\n", 43 | integrity_idx, 44 | pt_integ ); 45 | return; 46 | } 47 | 48 | /* Compute the expected ICV */ 49 | exp_integ = fprom_get( integrity_idx ); 50 | 51 | /* Compare it against the stored, decrypted ICV */ 52 | if ( pt_integ != exp_integ ) { 53 | /* Assuming correct decryption, this means our table was wrong*/ 54 | fprintf( stderr, 55 | "Integrity check failed, got 0x%08X expected 0x%08X\n", 56 | pt_integ, 57 | exp_integ ); 58 | exit( EXIT_FAILURE ); 59 | } 60 | 61 | } 62 | 63 | /** 64 | * Generates an integrity check word based on the current encryption state, and 65 | * encrypts it. 66 | * 67 | * This function does a FPROM lookup and as such, might fail if the FPROM table 68 | * is not complete. 69 | * 70 | * @param status Output parameter, ENCRYPT_OK when successful 71 | * @error ENCRYPT_MISSING_FPROM : A location in the FPROM was ref'd 72 | * @return The encrypted integrity check word 73 | */ 74 | uint32_t encrypt_generate_integrity( int *status ) { 75 | uint32_t integrity_idx, pt_integ; 76 | 77 | /* The ICV is derived from the crypto state before it is encrypted, so 78 | * compute it first. The current state of the ciphermode is masked and 79 | * indexed into the FPROM to get the check value */ 80 | integrity_idx = crypto_getstate() & INTEGRITY_INDEX_MASK; 81 | 82 | /* Ensure that the FPROM table in the program contains this index */ 83 | if ( !fprom_exists( integrity_idx ) ) { 84 | *status = ENCRYPT_MISSING_FPROM; 85 | return 0xFFFFFFFF; 86 | } 87 | 88 | /* Generate and encrypt the ICV */ 89 | pt_integ = fprom_get( integrity_idx ); 90 | 91 | *status = ENCRYPT_OK; 92 | return crypto_encrypt( pt_integ ); 93 | 94 | } 95 | 96 | /** 97 | * Key derivation routine 98 | * @param iv Output parameter for the derived Initialization Vector 99 | * @param key Output parameter for the derived key. 100 | * @param proc_sig The CPUID/processor signature to derive the key for. 101 | * @param seed The key seed used to derive an unique IV. 102 | * @return ENCRYPT_OK when successful 103 | * @error ENCRYPT_MISSING_FPROM : A location in the FPROM was ref'd 104 | * that was not correctly set in the 105 | */ 106 | int derive_key( 107 | uint32_t *iv, 108 | uint32_t *key, 109 | uint32_t proc_sig, 110 | uint32_t seed ) { 111 | 112 | uint32_t _iv, key_idx; 113 | 114 | /* The CPU base key is rotated by the stepping */ 115 | _iv = rotl32( 116 | cpukeys_get_base( proc_sig ), 117 | proc_sig & CPUID_STEPPING_MASK ); 118 | 119 | /* and has 6 plus the key seed added to it to form the IV */ 120 | _iv += 6 + seed; 121 | 122 | *iv = _iv; 123 | 124 | /* The low 32 bits of the floating point constant ROM at the index 125 | * given by masking the IV is used as the key/polynomial for the LFSR */ 126 | key_idx = _iv & IV_KEY_INDEX_MASK; 127 | 128 | /* Ensure that the FPROM table in the program contains this index */ 129 | if ( !fprom_exists( key_idx ) ) { 130 | return ENCRYPT_MISSING_FPROM; 131 | } 132 | 133 | *key = fprom_get( key_idx ); 134 | 135 | return ENCRYPT_OK; 136 | } 137 | 138 | /** 139 | * Decrypts an encrypted microcode patch using a given IV and key 140 | * @param out The buffer to write the decrypted patch body to. 141 | * @param in The encrypted patch body to decrypt. 142 | * @param iv The initialization vector to use. 143 | * @param key The key to use. 144 | */ 145 | void _decrypt_patch( 146 | patch_body_t *out, 147 | const epatch_body_t *in, 148 | uint32_t iv, 149 | uint32_t key ) { 150 | 151 | uint32_t integrity_idx; 152 | int i; 153 | 154 | /* Zero out the output buffer to prevent leaking memory contents */ 155 | memset( out, 0, sizeof(patch_body_t) ); 156 | 157 | /* Load the IV and key into the cipher implementation */ 158 | crypto_init( key, iv ); 159 | 160 | /* Decrypt the patch MSRAM contents */ 161 | for ( i = 0; i < MSRAM_DWORD_COUNT; i++ ) { 162 | out->msram[i] = crypto_decrypt( in->msram[i] ); 163 | } 164 | 165 | /* Validate the patch MSRAM contents */ 166 | decrypt_verify_integrity( in->msram_integrity ); 167 | 168 | /* Decrypt the patch control register operations */ 169 | for ( i = 0; i < PATCH_CR_OP_COUNT; i++ ) { 170 | /* Decrypt operation fields */ 171 | out->cr_ops[i].address = 172 | crypto_decrypt( in->cr_ops[i].address ); 173 | out->cr_ops[i].mask = 174 | crypto_decrypt( in->cr_ops[i].mask ); 175 | out->cr_ops[i].value = 176 | crypto_decrypt( in->cr_ops[i].value ); 177 | 178 | /* Validate operation */ 179 | decrypt_verify_integrity( in->cr_ops[i].integrity ); 180 | } 181 | 182 | } 183 | 184 | /** 185 | * Encrypts a patch body using a given processor signature and key seed. 186 | * Due to a possibly incomplete FPROM table not all seeds may be usable. If 187 | * the chosen seeds results in an unknown FPROM entry being used, this function 188 | * will report an error. 189 | * 190 | * @param out The buffer to write the encrypted patch body to 191 | * @param in The plaintext patch body 192 | * @param proc_sig The CPUID/processor signature to encrypt for 193 | * @param seed The seed to be tried 194 | * @return ENCRYPT_OK when successful 195 | * @error ENCRYPT_MISSING_FPROM : A location in the FPROM was ref'd 196 | * that was not correctly set in the 197 | */ 198 | int _encrypt_patch( 199 | epatch_body_t *out, 200 | const patch_body_t *in, 201 | uint32_t proc_sig, 202 | uint32_t seed ) { 203 | 204 | uint32_t integrity_idx, iv, key_idx, key; 205 | int i, status; 206 | 207 | /* Zero out the output buffer to prevent leaking memory contents */ 208 | memset( out, 0, sizeof(epatch_body_t) ); 209 | out->key_seed = seed; 210 | 211 | /* Derive the IV and key */ 212 | status = derive_key( &iv, &key, proc_sig, seed ); 213 | if ( status != ENCRYPT_OK ) 214 | return status; 215 | 216 | /* Load the IV and key into the cipher implementation */ 217 | crypto_init( key, iv ); 218 | 219 | /* Encrypt the MSRAM contents */ 220 | for ( i = 0; i < MSRAM_DWORD_COUNT; i++ ) { 221 | out->msram[i] = crypto_encrypt( in->msram[i] ); 222 | } 223 | 224 | /* Try to calculate ICV for the MSRAM */ 225 | out->msram_integrity = encrypt_generate_integrity( &status ); 226 | if ( status != ENCRYPT_OK ) 227 | return status; 228 | 229 | /* Encrypt the control register operations */ 230 | for ( i = 0; i < PATCH_CR_OP_COUNT; i++ ) { 231 | /* Encrypt operation fields */ 232 | out->cr_ops[i].address = 233 | crypto_encrypt( in->cr_ops[i].address ); 234 | out->cr_ops[i].mask = 235 | crypto_encrypt( in->cr_ops[i].mask ); 236 | out->cr_ops[i].value = 237 | crypto_encrypt( in->cr_ops[i].value ); 238 | 239 | /* Try to generate control register op ICV */ 240 | out->cr_ops[i].integrity = 241 | encrypt_generate_integrity( &status ); 242 | if ( status != ENCRYPT_OK ) 243 | return status; 244 | } 245 | 246 | return ENCRYPT_OK; 247 | } 248 | 249 | /** 250 | * Encrypts a patch body using a given processor signature and key seed. 251 | * Due to a possibly incomplete FPROM table not all seeds may be usable, to 252 | * be able to encrypt arbitrary plaintext the program will search until it finds 253 | * a seed that does not result in any unknown FPROM locations being used for 254 | * integrity check or key generation. 255 | * @param out The buffer to write the encrypted patch body to 256 | * @param in The plaintext patch body 257 | * @param proc_sig The CPUID/processor signature to encrypt for 258 | * @param seed The initial key seed to be tried 259 | */ 260 | void encrypt_patch_body( 261 | epatch_body_t *out, 262 | const patch_body_t *in, 263 | uint32_t proc_sig, 264 | uint32_t seed ) 265 | { 266 | while( _encrypt_patch( out, in, proc_sig, seed ) != ENCRYPT_OK ) { 267 | seed++; 268 | } 269 | } 270 | 271 | /** 272 | * Decrypts an encrypted microcode patch using a given proc. sig and key seed. 273 | * @param out The buffer to write the decrypted patch body to. 274 | * @param in The encrypted patch body to decrypt 275 | * @param proc_sig The CPUID/processor signature to decrypt for 276 | */ 277 | void decrypt_patch_body( 278 | patch_body_t *out, 279 | const epatch_body_t *in, 280 | uint32_t proc_sig ) { 281 | 282 | uint32_t iv, key_idx, key; 283 | 284 | /* Derive the IV and key */ 285 | if ( derive_key( &iv, &key, proc_sig, in->key_seed ) != ENCRYPT_OK ) { 286 | fprintf( stderr, 287 | "Patch file uses unknown FPROM[0x%02X] as key.", 288 | key_idx ); 289 | return; 290 | } 291 | 292 | /* Actually decrypt the patch */ 293 | _decrypt_patch( out, in, iv, key ); 294 | 295 | } 296 | -------------------------------------------------------------------------------- /patchfile.h: -------------------------------------------------------------------------------- 1 | #ifndef __patchfile_h__ 2 | #define __patchfile_h__ 3 | #include 4 | 5 | #define MSRAM_QWORD_COUNT (0x54) 6 | #define MSRAM_DWORD_COUNT (MSRAM_QWORD_COUNT * 2) 7 | #define MSRAM_GROUP_SIZE (0x8) 8 | #define MSRAM_GROUP_COUNT (MSRAM_DWORD_COUNT/8) 9 | #define PATCH_CR_OP_COUNT (0x10) 10 | #define MSRAM_BASE_ADDRESS (0xFEB) 11 | 12 | typedef struct __attribute__((packed)) { 13 | uint32_t header_ver; 14 | uint32_t update_rev; 15 | uint32_t date_bcd; 16 | uint32_t proc_sig; 17 | uint32_t checksum; 18 | uint32_t loader_ver; 19 | uint32_t proc_flags; 20 | uint32_t data_size; 21 | uint32_t total_size; 22 | uint8_t reserved[12]; 23 | } patch_hdr_t; 24 | 25 | typedef struct __attribute__((packed)) { 26 | uint32_t address; 27 | uint32_t mask; 28 | uint32_t value; 29 | uint32_t integrity; 30 | } patch_cr_op_t; 31 | 32 | typedef struct __attribute__((packed)) { 33 | uint32_t key_seed; 34 | uint32_t resvd_0; 35 | uint32_t msram[ MSRAM_DWORD_COUNT ]; 36 | uint32_t msram_integrity; 37 | uint32_t resvd_1; 38 | patch_cr_op_t cr_ops[ PATCH_CR_OP_COUNT ]; 39 | } epatch_body_t; 40 | 41 | typedef struct { 42 | uint32_t msram[ MSRAM_DWORD_COUNT ]; 43 | patch_cr_op_t cr_ops[ PATCH_CR_OP_COUNT ]; 44 | } patch_body_t; 45 | 46 | typedef struct __attribute__((packed)) { 47 | patch_hdr_t header; 48 | epatch_body_t body; 49 | } epatch_file_t; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /patchtools.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include // for uint32_t 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "patchtools.h" 11 | 12 | char fmt_buf[4096]; 13 | char *patch_filename; 14 | char *patch_name; 15 | epatch_file_t *patch_in; 16 | patch_body_t patch_body; 17 | epatch_file_t epatch_out; 18 | uint8_t data_in[2048]; 19 | 20 | /* Command line flags */ 21 | int extract_patch_flag, dump_patch_flag, create_patch_flag, help_flag; 22 | 23 | /* Command line arguments */ 24 | char *patch_path; 25 | char *config_path; 26 | char *msram_path; 27 | uint32_t patch_seed; 28 | 29 | void usage( const char *reason ) { 30 | fprintf( stderr, "%s\n", reason ); 31 | fprintf( stderr, 32 | "\tpatchtools -h\n" ); 33 | fprintf( stderr, 34 | "\tpatchtools [-dec] [-p ] [-i ]\n\n" ); 35 | 36 | if ( !help_flag ) 37 | exit( EXIT_FAILURE ); 38 | 39 | fprintf( stderr, 40 | "\t\n" 41 | "\tProgram for encrypting and decrypting Pentium II microcode patches\n" 42 | "\twritten by Peter Bosch . Patchfiles produced by \n" 43 | "\tthis program might crash or damage the system they are loaded onto\n" 44 | "\tand the author takes no responsibility for any damages resulting \n" 45 | "\tfrom use of the software.\n" 46 | "\t\t-h Print this message and exit\n" 47 | "\t\t\n" 48 | "\t\t-e Extract a patch to a configuration and \n" 49 | "\t\t MSRAM hexdump file\n" 50 | "\t\t\n" 51 | "\t\t-c Create a patch from a configuration and\n" 52 | "\t\t MSRAM hexdump file\n" 53 | "\t\t\n" 54 | "\t\t-d Dump the patch contents and keys to the\n" 55 | "\t\t console after encrypting or decrypting.\n" 56 | "\t\t\n" 57 | "\t\t-p Specifies the path of the patchfile to \n" 58 | "\t\t create or decrypt. When encrypting this\n" 59 | "\t\t option is not required as the program \n" 60 | "\t\t will use the path of the configuration \n" 61 | "\t\t file to generate the output path.\n" 62 | "\t\t\n" 63 | "\t\t-i Specifies the path of the config file \n" 64 | "\t\t to use or extract. When extracting this\n" 65 | "\t\t option is not required as the program \n" 66 | "\t\t will use the path of the patch file to \n" 67 | "\t\t generate the output path.\n"); 68 | } 69 | 70 | void parse_args( int argc, char *const *argv ) { 71 | char opt; 72 | while ( (opt = getopt( argc, argv, ":p:i:dech" )) != -1 ) { 73 | switch( opt ) { 74 | case 'p': 75 | patch_path = strdup( optarg ); 76 | break; 77 | case 'i': 78 | config_path = strdup( optarg ); 79 | break; 80 | case 'd': 81 | dump_patch_flag = 1; 82 | break; 83 | case 'e': 84 | extract_patch_flag = 1; 85 | break; 86 | case 'c': 87 | create_patch_flag = 1; 88 | break; 89 | case 'h': 90 | help_flag = 1; 91 | break; 92 | case ':': 93 | usage("missing argument"); 94 | break; 95 | default: 96 | case '?': 97 | usage("unknown argument"); 98 | break; 99 | } 100 | } 101 | } 102 | 103 | 104 | void load_input_patch( void ) { 105 | char *patch_fn; 106 | 107 | /* Ensure we have a path */ 108 | if ( !patch_path ) 109 | usage("missing patch path"); 110 | 111 | /* Load the patch */ 112 | read_file( patch_path, data_in, sizeof data_in ); 113 | patch_in = (epatch_file_t *) data_in; 114 | 115 | /* Get the patch filename */ 116 | strncpy( fmt_buf, patch_path, sizeof fmt_buf ); 117 | patch_fn = basename( fmt_buf ); 118 | patch_filename = strdup( patch_fn ); 119 | 120 | /* Get the patch name */ 121 | strncpy( fmt_buf, patch_filename, sizeof fmt_buf ); 122 | patch_name = strtok( patch_filename, "." ); 123 | patch_name = strdup( patch_name ); 124 | 125 | /* Decrypt the patch */ 126 | decrypt_patch_body( 127 | &patch_body, 128 | &patch_in->body, 129 | patch_in->header.proc_sig); 130 | //TODO: Header only mode? 131 | 132 | patch_seed = patch_in->body.key_seed; 133 | 134 | } 135 | 136 | void write_output_patch( void ) { 137 | 138 | /* Ensure we have a path */ 139 | if ( !patch_path ) 140 | usage("missing patch path"); 141 | 142 | /* Encrypt the patch */ 143 | encrypt_patch_body( 144 | &epatch_out.body, 145 | &patch_body, 146 | patch_in->header.proc_sig, 147 | patch_seed); 148 | 149 | /* Assemble the header */ 150 | memcpy( &epatch_out.header, &patch_in->header, sizeof(patch_hdr_t) ); 151 | 152 | /* Write the file */ 153 | write_file( patch_path, &epatch_out, sizeof(epatch_file_t) ); 154 | 155 | } 156 | 157 | void cleanup( void ) { 158 | if ( patch_path ) 159 | free( patch_path ); 160 | if ( patch_filename ) 161 | free( patch_filename ); 162 | if ( patch_name ) 163 | free( patch_name ); 164 | if ( config_path ) 165 | free( config_path ); 166 | if ( msram_path ) 167 | free( msram_path ); 168 | } 169 | 170 | void dump_patch( void ) { 171 | dump_patch_header( &patch_in->header ); 172 | printf("Key seed: 0x%08X\n", patch_seed); 173 | dump_patch_body( &patch_body ); 174 | } 175 | 176 | void extract_patch( void ) { 177 | size_t s; 178 | 179 | if ( !config_path ) { 180 | s = snprintf( fmt_buf, sizeof fmt_buf, "%s.txt", patch_name ); 181 | if ( s < 0 ) { 182 | fprintf( stderr, "Could not generate output path!\n" ); 183 | exit( EXIT_FAILURE ); 184 | } 185 | config_path = strdup( fmt_buf ); 186 | } 187 | 188 | if ( !msram_path ) { 189 | s = snprintf( fmt_buf, sizeof fmt_buf, "%s.hex", patch_name ); 190 | if ( s < 0 ) { 191 | fprintf( stderr, "Could not generate output path!\n" ); 192 | exit( EXIT_FAILURE ); 193 | } 194 | msram_path = strdup( fmt_buf ); 195 | } 196 | 197 | write_patch_config( 198 | &patch_in->header, 199 | &patch_body, 200 | config_path, 201 | msram_path, 202 | patch_seed ); 203 | 204 | write_msram_file( 205 | &patch_body, 206 | msram_path ); 207 | 208 | } 209 | 210 | /** current directory buffer for use by create_patch */ 211 | char current_dir[4096]; 212 | 213 | /** 214 | * Creates a new patch 215 | */ 216 | void create_patch( void ) { 217 | size_t s; 218 | char *config_fn, *config_dir; 219 | 220 | /* Ensure we have a path */ 221 | if ( !config_path ) 222 | usage("missing config path"); 223 | 224 | /* Get the config filename */ 225 | strncpy( fmt_buf, config_path, sizeof fmt_buf ); 226 | config_fn = basename( fmt_buf ); 227 | config_fn = strdup(config_fn); 228 | 229 | /* Get the patch name */ 230 | patch_name = strtok( config_fn, "." ); 231 | patch_name = strdup( patch_name ); 232 | 233 | /* Get the config directory */ 234 | strncpy( fmt_buf, config_path, sizeof fmt_buf ); 235 | config_dir = dirname( fmt_buf ); 236 | config_dir = strdup( config_dir ); 237 | 238 | /* Determine patchfile path */ 239 | if ( !patch_path ) { 240 | s = snprintf( fmt_buf, sizeof fmt_buf, "%s.dat", patch_name ); 241 | if ( s < 0 ) { 242 | fprintf( stderr, "Could not generate output path!\n" ); 243 | exit( EXIT_FAILURE ); 244 | } 245 | patch_path = strdup( fmt_buf ); 246 | } 247 | 248 | /* Set the patch data pointer */ 249 | patch_in = (epatch_file_t *) data_in; 250 | 251 | /* Parse the configuration file */ 252 | read_patch_config( 253 | &patch_in->header, 254 | &patch_body, 255 | config_path, 256 | &msram_path, 257 | &patch_seed ); 258 | 259 | if ( !msram_path ) 260 | usage("missing data path"); 261 | 262 | /* Switching directories to allow the data path to be relative to the 263 | config file path */ 264 | getcwd( current_dir, sizeof current_dir ); 265 | chdir( config_dir ); 266 | 267 | /* Read the MSRAM input data */ 268 | read_msram_file( 269 | &patch_body, 270 | msram_path ); 271 | 272 | /* Restore the working directory */ 273 | chdir( current_dir ); 274 | 275 | free( config_dir ); 276 | 277 | /* Encode and encrypt the patch */ 278 | write_output_patch(); 279 | 280 | } 281 | 282 | int main( int argc, char * const *argv ) { 283 | /* Parse the command line arguments */ 284 | parse_args( argc, argv ); 285 | 286 | if ( help_flag ) { 287 | /* The user requested the built in documentation */ 288 | usage(""); 289 | 290 | } else if ( create_patch_flag && !extract_patch_flag ) { 291 | /* We are to create a new patch */ 292 | 293 | /* Load the input and encode it */ 294 | create_patch(); 295 | 296 | /* If the user requested a dump of the newly created patch, 297 | produce it. */ 298 | if ( dump_patch_flag ) 299 | dump_patch(); 300 | 301 | } else if ( dump_patch_flag || extract_patch_flag ) { 302 | /* The user requested to load and decrypt a patch */ 303 | 304 | /* Creating and decrypting patches are mutually exclusive ops */ 305 | if ( create_patch_flag ) 306 | usage("invalid combination of modes"); 307 | 308 | /* Load and decrypt the patch */ 309 | load_input_patch(); 310 | 311 | /* Dump the patch if requested */ 312 | if ( dump_patch_flag ) 313 | dump_patch(); 314 | 315 | /* Extract the patch if requested */ 316 | if ( extract_patch_flag ) 317 | extract_patch(); 318 | 319 | } else 320 | usage("no mode specified"); 321 | 322 | /* Cleanup dynamically allocated memory */ 323 | cleanup(); 324 | 325 | return EXIT_SUCCESS; 326 | } 327 | -------------------------------------------------------------------------------- /patchtools.h: -------------------------------------------------------------------------------- 1 | #ifndef __patchtools_h__ 2 | #define __patchtools_h__ 3 | #include "patchfile.h" 4 | 5 | int fprom_exists( uint32_t addr ); 6 | 7 | uint32_t fprom_get( uint32_t addr ); 8 | 9 | uint32_t cpukeys_get_base( uint32_t proc_sig ); 10 | 11 | void encrypt_patch_body( 12 | epatch_body_t *out, 13 | const patch_body_t *in, 14 | uint32_t proc_sig, 15 | uint32_t seed ); 16 | 17 | void decrypt_patch_body( 18 | patch_body_t *out, 19 | const epatch_body_t *in, 20 | uint32_t proc_sig ); 21 | 22 | void dump_patch_header( const patch_hdr_t *hdr ); 23 | 24 | void dump_patch_body( const patch_body_t *body ); 25 | 26 | void read_file(const char *path, void *data, size_t size); 27 | 28 | void write_file(const char *path, const void *data, size_t size); 29 | 30 | void write_patch_config( 31 | const patch_hdr_t *hdr, 32 | const patch_body_t *body, 33 | const char *filename, 34 | const char *msram_fn, 35 | uint32_t key_seed ); 36 | 37 | void write_msram_file( const patch_body_t *body, const char *filename ); 38 | 39 | void read_patch_config( 40 | patch_hdr_t *hdr, 41 | patch_body_t *body, 42 | const char *filename, 43 | char **msram_fnp, 44 | uint32_t *key_seed ); 45 | 46 | void read_msram_file( patch_body_t *body, const char *filename ); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /rotate.h: -------------------------------------------------------------------------------- 1 | #ifndef __rotate_h__ 2 | #define __rotate_h__ 3 | 4 | /* Source: https://blog.regehr.org/archives/1063 */ 5 | #include 6 | #include // for CHAR_BIT 7 | 8 | static inline uint32_t rotl32 (uint32_t n, unsigned int c) 9 | { 10 | const unsigned int mask = (CHAR_BIT*sizeof(n) - 1); // assumes width is a power of 2. 11 | 12 | // assert ( (c<=mask) &&"rotate by type width or more"); 13 | c &= mask; 14 | return (n<>( (-c)&mask )); 15 | } 16 | 17 | static inline uint32_t rotr32 (uint32_t n, unsigned int c) 18 | { 19 | const unsigned int mask = (CHAR_BIT*sizeof(n) - 1); 20 | 21 | // assert ( (c<=mask) &&"rotate by type width or more"); 22 | c &= mask; 23 | return (n>>c) | (n<<( (-c)&mask )); 24 | } 25 | 26 | #endif 27 | --------------------------------------------------------------------------------