First post, by sander
While looking at some VLSI VL82C483 bios dumps I stumbled upon the Dell OptiPlex 486 MTE motherboard. Within the uncompressed DELL update executable there is a bios image which is exactly 131.072 bytes (For example. 4xxLeA08.exe). As I don't own one of these machines I'm unsure if the bios is uncompressed on boot (is this even possible when the video ROM is compressed as well?) or if this image is written to ROM and is unpacked and written to ROM in a second stage update.
[EDIT]I should have mentioned that although the 'uncompressed' ROM is 131.072 bytes, the data in there is obviously compressed. There is a little section where I found the decompression routine, but to me it currently looks like that's part of the firmware update procedure.[/EDIT]
As I've reverse-engineered the decompression routine (which was cumbersome to find) I'm posting it here for archiving purposes:
/*This routine was reverse-engineered from a compressed Dell Optiplex ROMand is released for public domain.It's used for older style Dell (Optiplex?) ROMs as a second stage?decompression / deobfuscation routine. This routine is used for at leastthe following systems:OptiPlex GN PlusOptiPlex NOptiPlex GL PlusOptiPlex GM PlusOptiPlex GMT PlusOptiPlex GXLOptiPlex GXMOptiPlex GXMTOptiPlex G1OptiPlex 486 LEOptiPlex 486 MTEOptiPlex 486 MXEThis is a quick and dirty implementation, it will possibly crash.Who needs bounds checking..?Note. this only runs on little endian machines (x86).*/#define _CRT_SECURE_NO_DEPRECATE#include <stdint.h>#include <stdio.h>#include <stdlib.h>#include <string.h>int decompress_rom_block(const uint8_t *src, uint8_t *dst, int blk){uint16_t seg_size = 0;const uint8_t *src_end, *src_p = src, *dst_p = dst;for (int i = 0; i < blk; i++) {seg_size = *(uint16_t*)src_p;src_p += 2;if (i + 1 != blk) {src_p += seg_size;}}src_end = src_p + seg_size;while (src_p < src_end) {uint16_t blkofs;uint8_t blksize, instr = *(src_p++);if (instr & 0xf){blksize = (instr & 0xf) + 1;blkofs = (((uint16_t)instr << 4) & 0xff00) | *(src_p++);memcpy(dst_p, dst_p - blkofs - 1, blksize);dst_p += blksize;}
else{blksize = (instr >> 4) + 1;memcpy(dst_p, src_p, blksize);dst_p += blksize;src_p += blksize;}}return dst_p - dst;}int main(int argc, char *argv[]){FILE* f;if (argc != 4){printf("Usage: %s inputfile outputfile blocknr\n", argv[0]);exit(1);}f = fopen(argv[1], "rb");if (!f) {return -1;}fseek(f, 0, SEEK_END);long fsize = ftell(f);fseek(f, 0, SEEK_SET);uint8_t* src = (uint8_t * ) malloc(fsize);if (src != NULL) {fread(src, fsize, 1, f);}fclose(f);uint8_t* dst = (uint8_t * )malloc(32768);if (dst != NULL) {int sz = decompress_rom_block(src, dst, atoi(argv[3]));f = fopen(argv[2], "wb");if (f) {fwrite(dst, 1, sz, f);fclose(f);}}free(dst);free(src);return 0;}
Original listing:
decompress_rom_block proc nearcldloc_F49F3:mov ax, sishr ax, 4mov dx, dsadd dx, axmov ds, dxand si, 0Fhlodswmov dx, axdec bljz short loc_F4A0Cadd si, dxjmp short loc_F49F3loc_F4A0C:add dx, sisub ch, chloc_F4A10:cmp si, dxjnb short locret_F4A3Flodsbtest al, 0Fhjnz short loc_F4A24shr al, 4mov cl, alinc clrep movsbjmp short loc_F4A10loc_F4A24:mov cl, aland cl, 0Fhinc clsub ah, ahshl ax, 4lodsbmov bx, simov si, disub si, axdec sirep movs byte ptr es:[di], byte ptr es:[si]mov si, bxjmp short loc_F4A10locret_F4A3F:retndecompress_rom_block endp