-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathcomextract.c
executable file
·358 lines (309 loc) · 8.08 KB
/
comextract.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
// Toshiba COM file header
#pragma pack(push, 1)
typedef struct TOSHIBA_COM_HEADER_ {
uint16_t Zero;
uint8_t HeaderVersion;
uint32_t Signature; // 'BIOS'
uint16_t Unk0;
uint16_t Unk1;
uint8_t BiosVersion[16];
uint8_t Compressed; // 0 if not compressed, 1 if compressed
uint32_t Unk2;
uint32_t Unk3;
uint32_t CompressedSize;
uint16_t DecompressedSizeShifted; // Stored as uint32_t shifted left by 10 bits, limited to 4 Mb
// The rest of the header is not used and can be ignored
} TOSHIBA_COM_HEADER;
#pragma pack(pop)
#define TOSHIBA_COM_HEADER_SIGNATURE 0x534F4942 // 'BIOS'
#define TOSHIBA_COM_HEADER_VERSION_0_HEADER_SIZE 0x100
#define TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE 0x200
// Global state
static unsigned char* gCurrentInput;
static unsigned char* gCurrentOutput;
static size_t gNumDecodedBytes;
static uint16_t gBigTable[1024];
static uint16_t gSmallTable[1024];
uint8_t check(uint8_t *input, uint8_t *output)
{
uint8_t result;
result = (output[1] & 0x80u) != 0;
*(uint16_t *)output *= 2;
if ((++input[1] & 8) == 8) {
input[1] = 0;
*output = *gCurrentInput++;
}
return result;
}
void apply(uint8_t *input, uint8_t *output, uint16_t *index)
{
*index = output[1];
*(uint16_t *)output <<= 8 - input[1];
*output = *gCurrentInput++;
*(uint16_t *)output = *(uint16_t *)output << input[1];
}
uint8_t build_table(uint8_t *input, uint16_t *value, uint16_t *index, uint8_t *output)
{
uint8_t result;
uint16_t local_index;
if (check(input, output))
{
if ((signed int)++*index < 511)
{
local_index = 2 * *index;
result = build_table(input, value, index, output);
if (!result)
{
gBigTable[local_index] = *value;
result = build_table(input, value, index, output);
if (!result)
{
gSmallTable[local_index] = *value;
*value = local_index;
*value >>= 1;
result = 0;
}
}
}
else
{
result = 1;
}
}
else
{
apply(input, output, value);
result = 0;
}
return result;
}
uint8_t decode_block()
{
uint8_t result;
uint32_t i = 0;
uint32_t stored_i = 0;
uint16_t index = 0;
uint8_t first[4] = { 0,0,0,0 };
uint8_t second[4] = { 0,0,0,0 };
*(uint16_t *)first = *gCurrentInput;
*(uint32_t *)first = *(uint16_t *)first << 16;
first[1] = gCurrentInput[1];
first[0] = gCurrentInput[2];
gCurrentInput += 3;
stored_i = *(uint32_t *)first;
first[1] = 0;
*(uint16_t *)&second[1] = *gCurrentInput;
second[0] = gCurrentInput[1];
gCurrentInput += 2;
i = 0xFF;
result = build_table(first, &index, (uint16_t*)&i, second);
uint16_t original_index = index;
if (!result)
{
i = stored_i;
do
{
index = original_index;
while (index >= 0x100)
{
index *= 2;
if (check(first, second))
{
index = gSmallTable[index];
}
else {
index = gBigTable[index];
}
}
*gCurrentOutput++ = (uint8_t)index;
++gNumDecodedBytes;
--i;
} while (i);
--gCurrentInput;
result = 0;
if (!first[1])
--gCurrentInput;
}
return result;
}
uint8_t decompress(uint8_t *input, uint8_t *output)
{
int result;
char currentByte;
// Reset global state
gNumDecodedBytes = 0;
gCurrentInput = input;
gCurrentOutput = output;
memset(gBigTable, 0, sizeof(gBigTable));
memset(gSmallTable, 0, sizeof(gSmallTable));
while (1)
{
currentByte = *gCurrentInput++;
if (currentByte != 1)
break;
result = decode_block();
if (result)
return result;
}
return currentByte != 0;
}
uint8_t comextract(uint8_t* input_buffer, size_t input_size, uint8_t** output_buffer, size_t* output_size) {
uint8_t* output = NULL;
size_t size = 0;
size_t rest = 0;
if (input_size < TOSHIBA_COM_HEADER_VERSION_0_HEADER_SIZE) {
return 1;
}
for (size_t i = 0; i < input_size - sizeof(TOSHIBA_COM_HEADER); i++) {
// Search input file for BIOS signature
if (*(uint32_t*)(input_buffer + i + 3) == TOSHIBA_COM_HEADER_SIGNATURE) {
size_t header_size;
size_t compressed_size;
size_t decompressed_size;
uint8_t result;
rest = input_size - i + 3;
if (rest < sizeof(TOSHIBA_COM_HEADER)) {
break;
}
// Map this part of file as a candidate for header
const TOSHIBA_COM_HEADER* header = (const TOSHIBA_COM_HEADER*)(input_buffer + i);
// Check first 2 bytes to be zero
if (header->Zero != 0) {
continue;
}
printf("Toshiba COM header candidate found at offset 0x%X\n", (unsigned) i);
// Determine header size based on header version
if (header->HeaderVersion == 0) {
header_size = TOSHIBA_COM_HEADER_VERSION_0_HEADER_SIZE;
}
else if (header->HeaderVersion == 2) {
header_size = TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE;
}
else {
printf("Unknown header version 0x%X, assuming header size 0x%X\n", header->HeaderVersion, TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE);
header_size = TOSHIBA_COM_HEADER_VERSION_2_HEADER_SIZE;
}
if (rest < header_size + sizeof(uint32_t)) {
continue;
}
// Check sanity of compression byte
if (header->Compressed > 1) {
printf("Candidate skipped, compression state is unknown (0x%X)\n", header->Compressed);
continue;
}
// Get data sizes
compressed_size = header->CompressedSize;
decompressed_size = ((size_t)header->DecompressedSizeShifted) << 10;
// Check sanity of both sizes
if (compressed_size > decompressed_size) {
printf("Candidate skipped, compressed size is larger than decompressed size\n");
continue;
}
if (decompressed_size > 0x400000) {
printf("Candidate skipped, decompressed size is larger than 4 Mb\n");
continue;
}
if (rest < header_size + compressed_size) {
continue;
}
// Show BIOS version
{
uint8_t version[sizeof(header->BiosVersion) + 1];
memcpy(version, header->BiosVersion, sizeof(header->BiosVersion));
version[sizeof(header->BiosVersion)] = 0;
printf("Toshiba COM header appears valid, BIOS version: %s\n", version);
}
// Perform decompression
if (header->Compressed == 0) {
printf("File is not compressed, data start is at offset 0x%X\n", (unsigned) (i + header_size));
return 1;
}
else if (header->Compressed == 1) {
printf("File is compressed, decompressing...\n");
// (Re)allocate output buffer
size += decompressed_size;
output = (uint8_t*)realloc(output, size);
// Call decompression fuction
result = decompress((uint8_t*)header + header_size, output + size - decompressed_size);
if (result) {
printf("Decompression failed, bailing\n");
return 1;
}
printf("Decompressed 0x%X bytes\n", (unsigned) decompressed_size);
// Advance position
i += header_size + compressed_size;
}
}
}
if (size == 0) {
// Nothing was found
return 1;
}
*output_buffer = output;
*output_size = size;
return 0;
}
// main
int main(int argc, char* argv[])
{
FILE* file;
uint8_t* buffer;
uint8_t* image;
size_t filesize;
size_t imagesize;
size_t read;
uint8_t status;
// Check arguments count
if (argc != 3) {
// Print usage and exit
printf("Toshiba COM Extractor v0.1.0 - extracts payload from compressed COM file used in Toshiba BIOS updates\n\n"
"Usage: comextract infile.com outfile.bin\n");
return 7;
}
// Read input file
file = fopen(argv[1], "rb");
if (!file) {
printf("Can't open input file\n");
return 2;
}
// Get file size
fseek(file, 0, SEEK_END);
filesize = ftell(file);
fseek(file, 0, SEEK_SET);
// Allocate buffer
buffer = (uint8_t*)malloc(filesize);
if (!buffer) {
printf("Can't allocate memory for input file\n");
return 3;
}
// Read the whole file into buffer
read = fread((void*)buffer, 1, filesize, file);
if (read != filesize) {
printf("Can't read input file\n");
return 4;
}
// Close input file
fclose(file);
// Call extraction routine
status = comextract(buffer, filesize, &image, &imagesize);
if (status)
return status;
// Create output file
file = fopen(argv[2], "wb");
if (!file) {
printf("Can't create output file\n");
return 5;
}
// Write extracted image
if (fwrite(image, 1, imagesize, file) != imagesize) {
printf("Can't write to output file\n");
return 6;
}
// Close output file
fclose(file);
return 0;
}