├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md └── comextract.c /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | cmake_install.cmake 4 | comextract 5 | comextract.exe 6 | Makefile 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(ToshibaCOMExtractor) 2 | 3 | SET(PROJECT_SOURCES 4 | comextract.c 5 | ) 6 | 7 | ADD_EXECUTABLE(comextract ${PROJECT_SOURCES}) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017, LongSoft 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ToshibaComExtractor 2 | =================== 3 | 4 | Quick and dirty tool to extract Toshiba .COM firmware files, released so 5 | it does not get lost. 6 | -------------------------------------------------------------------------------- /comextract.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | // Toshiba COM file header 7 | #pragma pack(push, 1) 8 | typedef struct TOSHIBA_COM_HEADER_ { 9 | uint16_t Zero; 10 | uint8_t HeaderVersion; 11 | uint32_t Signature; // 'BIOS' 12 | uint16_t Unk0; 13 | uint16_t Unk1; 14 | uint8_t BiosVersion[16]; 15 | uint8_t Compressed; // 0 if not compressed, 1 if compressed 16 | uint32_t Unk2; 17 | uint32_t Unk3; 18 | uint32_t CompressedSize; 19 | uint16_t DecompressedSizeShifted; // Stored as uint32_t shifted left by 10 bits, limited to 4 Mb 20 | // The rest of the header is not used and can be ignored 21 | } TOSHIBA_COM_HEADER; 22 | #pragma pack(pop) 23 | 24 | #define TOSHIBA_COM_HEADER_SIGNATURE 0x534F4942 // 'BIOS' 25 | #define TOSHIBA_COM_HEADER_VERSION_0_HEADER_SIZE 0x100 26 | #define TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE 0x200 27 | 28 | // Global state 29 | static unsigned char* gCurrentInput; 30 | static unsigned char* gCurrentOutput; 31 | static size_t gNumDecodedBytes; 32 | static uint16_t gBigTable[1024]; 33 | static uint16_t gSmallTable[1024]; 34 | 35 | uint8_t check(uint8_t *input, uint8_t *output) 36 | { 37 | uint8_t result; 38 | 39 | result = (output[1] & 0x80u) != 0; 40 | *(uint16_t *)output *= 2; 41 | if ((++input[1] & 8) == 8) { 42 | input[1] = 0; 43 | *output = *gCurrentInput++; 44 | } 45 | 46 | return result; 47 | } 48 | 49 | void apply(uint8_t *input, uint8_t *output, uint16_t *index) 50 | { 51 | *index = output[1]; 52 | *(uint16_t *)output <<= 8 - input[1]; 53 | *output = *gCurrentInput++; 54 | *(uint16_t *)output = *(uint16_t *)output << input[1]; 55 | } 56 | 57 | uint8_t build_table(uint8_t *input, uint16_t *value, uint16_t *index, uint8_t *output) 58 | { 59 | uint8_t result; 60 | uint16_t local_index; 61 | 62 | if (check(input, output)) 63 | { 64 | if ((signed int)++*index < 511) 65 | { 66 | local_index = 2 * *index; 67 | result = build_table(input, value, index, output); 68 | if (!result) 69 | { 70 | gBigTable[local_index] = *value; 71 | result = build_table(input, value, index, output); 72 | if (!result) 73 | { 74 | gSmallTable[local_index] = *value; 75 | *value = local_index; 76 | *value >>= 1; 77 | result = 0; 78 | } 79 | } 80 | } 81 | else 82 | { 83 | result = 1; 84 | } 85 | } 86 | else 87 | { 88 | apply(input, output, value); 89 | result = 0; 90 | } 91 | 92 | return result; 93 | } 94 | 95 | uint8_t decode_block() 96 | { 97 | uint8_t result; 98 | 99 | uint32_t i = 0; 100 | uint32_t stored_i = 0; 101 | uint16_t index = 0; 102 | uint8_t first[4] = { 0,0,0,0 }; 103 | uint8_t second[4] = { 0,0,0,0 }; 104 | 105 | *(uint16_t *)first = *gCurrentInput; 106 | *(uint32_t *)first = *(uint16_t *)first << 16; 107 | first[1] = gCurrentInput[1]; 108 | first[0] = gCurrentInput[2]; 109 | gCurrentInput += 3; 110 | 111 | stored_i = *(uint32_t *)first; 112 | first[1] = 0; 113 | *(uint16_t *)&second[1] = *gCurrentInput; 114 | second[0] = gCurrentInput[1]; 115 | gCurrentInput += 2; 116 | 117 | i = 0xFF; 118 | result = build_table(first, &index, (uint16_t*)&i, second); 119 | uint16_t original_index = index; 120 | if (!result) 121 | { 122 | i = stored_i; 123 | do 124 | { 125 | index = original_index; 126 | while (index >= 0x100) 127 | { 128 | index *= 2; 129 | if (check(first, second)) 130 | { 131 | index = gSmallTable[index]; 132 | } 133 | else { 134 | index = gBigTable[index]; 135 | } 136 | } 137 | *gCurrentOutput++ = (uint8_t)index; 138 | ++gNumDecodedBytes; 139 | --i; 140 | } while (i); 141 | --gCurrentInput; 142 | result = 0; 143 | if (!first[1]) 144 | --gCurrentInput; 145 | } 146 | 147 | return result; 148 | } 149 | 150 | uint8_t decompress(uint8_t *input, uint8_t *output) 151 | { 152 | int result; 153 | char currentByte; 154 | 155 | // Reset global state 156 | gNumDecodedBytes = 0; 157 | gCurrentInput = input; 158 | gCurrentOutput = output; 159 | memset(gBigTable, 0, sizeof(gBigTable)); 160 | memset(gSmallTable, 0, sizeof(gSmallTable)); 161 | 162 | while (1) 163 | { 164 | currentByte = *gCurrentInput++; 165 | if (currentByte != 1) 166 | break; 167 | result = decode_block(); 168 | if (result) 169 | return result; 170 | } 171 | return currentByte != 0; 172 | } 173 | 174 | uint8_t comextract(uint8_t* input_buffer, size_t input_size, uint8_t** output_buffer, size_t* output_size) { 175 | uint8_t* output = NULL; 176 | size_t size = 0; 177 | size_t rest = 0; 178 | 179 | if (input_size < TOSHIBA_COM_HEADER_VERSION_0_HEADER_SIZE) { 180 | return 1; 181 | } 182 | 183 | for (size_t i = 0; i < input_size - sizeof(TOSHIBA_COM_HEADER); i++) { 184 | // Search input file for BIOS signature 185 | if (*(uint32_t*)(input_buffer + i + 3) == TOSHIBA_COM_HEADER_SIGNATURE) { 186 | size_t header_size; 187 | size_t compressed_size; 188 | size_t decompressed_size; 189 | uint8_t result; 190 | 191 | rest = input_size - i + 3; 192 | if (rest < sizeof(TOSHIBA_COM_HEADER)) { 193 | break; 194 | } 195 | 196 | // Map this part of file as a candidate for header 197 | const TOSHIBA_COM_HEADER* header = (const TOSHIBA_COM_HEADER*)(input_buffer + i); 198 | 199 | // Check first 2 bytes to be zero 200 | if (header->Zero != 0) { 201 | continue; 202 | } 203 | 204 | printf("Toshiba COM header candidate found at offset 0x%X\n", (unsigned) i); 205 | 206 | // Determine header size based on header version 207 | if (header->HeaderVersion == 0) { 208 | header_size = TOSHIBA_COM_HEADER_VERSION_0_HEADER_SIZE; 209 | } 210 | else if (header->HeaderVersion == 2) { 211 | header_size = TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE; 212 | } 213 | else { 214 | printf("Unknown header version 0x%X, assuming header size 0x%X\n", header->HeaderVersion, TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE); 215 | header_size = TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE; 216 | } 217 | if (rest < header_size + sizeof(uint32_t)) { 218 | continue; 219 | } 220 | 221 | // Check sanity of compression byte 222 | if (header->Compressed > 1) { 223 | printf("Candidate skipped, compression state is unknown (0x%X)\n", header->Compressed); 224 | continue; 225 | } 226 | 227 | // Get data sizes 228 | compressed_size = header->CompressedSize; 229 | decompressed_size = ((size_t)header->DecompressedSizeShifted) << 10; 230 | 231 | // Check sanity of both sizes 232 | if (compressed_size > decompressed_size) { 233 | printf("Candidate skipped, compressed size is larger than decompressed size\n"); 234 | continue; 235 | } 236 | if (decompressed_size > 0x400000) { 237 | printf("Candidate skipped, decompressed size is larger than 4 Mb\n"); 238 | continue; 239 | } 240 | if (rest < header_size + compressed_size) { 241 | continue; 242 | } 243 | 244 | // Show BIOS version 245 | { 246 | uint8_t version[sizeof(header->BiosVersion) + 1]; 247 | memcpy(version, header->BiosVersion, sizeof(header->BiosVersion)); 248 | version[sizeof(header->BiosVersion)] = 0; 249 | printf("Toshiba COM header appears valid, BIOS version: %s\n", version); 250 | } 251 | 252 | // Perform decompression 253 | if (header->Compressed == 0) { 254 | printf("File is not compressed, data start is at offset 0x%X\n", (unsigned) (i + header_size)); 255 | return 1; 256 | } 257 | else if (header->Compressed == 1) { 258 | printf("File is compressed, decompressing...\n"); 259 | 260 | // (Re)allocate output buffer 261 | size += decompressed_size; 262 | output = (uint8_t*)realloc(output, size); 263 | 264 | // Call decompression fuction 265 | result = decompress((uint8_t*)header + header_size, output + size - decompressed_size); 266 | if (result) { 267 | printf("Decompression failed, bailing\n"); 268 | return 1; 269 | } 270 | printf("Decompressed 0x%X bytes\n", (unsigned) decompressed_size); 271 | 272 | // Advance position 273 | i += header_size + compressed_size; 274 | } 275 | } 276 | } 277 | 278 | if (size == 0) { 279 | // Nothing was found 280 | return 1; 281 | } 282 | 283 | *output_buffer = output; 284 | *output_size = size; 285 | return 0; 286 | } 287 | 288 | // main 289 | int main(int argc, char* argv[]) 290 | { 291 | FILE* file; 292 | uint8_t* buffer; 293 | uint8_t* image; 294 | size_t filesize; 295 | size_t imagesize; 296 | size_t read; 297 | uint8_t status; 298 | 299 | // Check arguments count 300 | if (argc != 3) { 301 | // Print usage and exit 302 | printf("Toshiba COM Extractor v0.1.0 - extracts payload from compressed COM file used in Toshiba BIOS updates\n\n" 303 | "Usage: comextract infile.com outfile.bin\n"); 304 | return 7; 305 | } 306 | 307 | // Read input file 308 | file = fopen(argv[1], "rb"); 309 | if (!file) { 310 | printf("Can't open input file\n"); 311 | return 2; 312 | } 313 | 314 | // Get file size 315 | fseek(file, 0, SEEK_END); 316 | filesize = ftell(file); 317 | fseek(file, 0, SEEK_SET); 318 | 319 | // Allocate buffer 320 | buffer = (uint8_t*)malloc(filesize); 321 | if (!buffer) { 322 | printf("Can't allocate memory for input file\n"); 323 | return 3; 324 | } 325 | 326 | // Read the whole file into buffer 327 | read = fread((void*)buffer, 1, filesize, file); 328 | if (read != filesize) { 329 | printf("Can't read input file\n"); 330 | return 4; 331 | } 332 | 333 | // Close input file 334 | fclose(file); 335 | 336 | // Call extraction routine 337 | status = comextract(buffer, filesize, &image, &imagesize); 338 | if (status) 339 | return status; 340 | 341 | // Create output file 342 | file = fopen(argv[2], "wb"); 343 | if (!file) { 344 | printf("Can't create output file\n"); 345 | return 5; 346 | } 347 | 348 | // Write extracted image 349 | if (fwrite(image, 1, imagesize, file) != imagesize) { 350 | printf("Can't write to output file\n"); 351 | return 6; 352 | } 353 | 354 | // Close output file 355 | fclose(file); 356 | 357 | return 0; 358 | } 359 | --------------------------------------------------------------------------------