add bmtool

This commit is contained in:
a1ive 2025-02-17 20:29:28 +09:00
parent 521dd72ac7
commit 1ba67d92ac
No known key found for this signature in database
GPG Key ID: DA9BACF4F462B55D
12 changed files with 1138 additions and 1 deletions

View File

@ -47,6 +47,9 @@ jobs:
- name: Build fsuuid.exe - name: Build fsuuid.exe
run: | run: |
make fsuuid.exe make fsuuid.exe
- name: Build bmtool.exe
run: |
make bmtool.exe
- name: Copy files - name: Copy files
run: | run: |
mkdir -p build mkdir -p build
@ -55,6 +58,7 @@ jobs:
cp ntloader.arm64 build/ cp ntloader.arm64 build/
cp initrd.cpio build/ cp initrd.cpio build/
cp fsuuid.exe build/ cp fsuuid.exe build/
cp bmtool.exe build/
cp mkinitrd.exe build/ cp mkinitrd.exe build/
cp utils/bcd.bat build/ cp utils/bcd.bat build/
- name: Download README.pdf - name: Download README.pdf

View File

@ -298,6 +298,9 @@ mkinitrd.exe rootfs initrd.cpio
find * | cpio -o -H newc > ../initrd.cpio find * | cpio -o -H newc > ../initrd.cpio
``` ```
### bmtool
`bmtool` is a program for extracting bootmgr.exe from bootmgr.
### bcd.bat ### bcd.bat
`bcd.bat` is a batch script to create the BCD file. `bcd.bat` is a batch script to create the BCD file.
Do not edit it unless you know how NTloader works. Do not edit it unless you know how NTloader works.
@ -323,6 +326,12 @@ make fsuuid
make fsuuid.exe make fsuuid.exe
``` ```
### Compile bmtool
```
make bmtool
make bmtool.exe
```
### Compile mkinitrd ### Compile mkinitrd
``` ```
make mkinitrd make mkinitrd

View File

@ -32,7 +32,7 @@
#define assert(x) \ #define assert(x) \
do \ do \
{ \ { \
if (DEBUG && ! (x)) \ if (! (x)) \
{ \ { \
die ("Assertion failed at %s line %d: %s\n", \ die ("Assertion failed at %s line %d: %s\n", \
__FILE__, __LINE__, #x); \ __FILE__, __LINE__, #x); \

115
include/huffman.h Normal file
View File

@ -0,0 +1,115 @@
#ifndef _HUFFMAN_H
#define _HUFFMAN_H
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Huffman alphabets
*
*/
#include <stdint.h>
/** Maximum length of a Huffman symbol (in bits) */
#define HUFFMAN_BITS 16
/** Raw huffman symbol */
typedef uint16_t huffman_raw_symbol_t;
/** Quick lookup length for a Huffman symbol (in bits)
*
* This is a policy decision.
*/
#define HUFFMAN_QL_BITS 7
/** Quick lookup shift */
#define HUFFMAN_QL_SHIFT (HUFFMAN_BITS - HUFFMAN_QL_BITS)
/** A Huffman-coded set of symbols of a given length */
struct huffman_symbols
{
/** Length of Huffman-coded symbols (in bits) */
uint8_t bits;
/** Shift to normalise symbols of this length to HUFFMAN_BITS bits */
uint8_t shift;
/** Number of Huffman-coded symbols having this length */
uint16_t freq;
/** First symbol of this length (normalised to HUFFMAN_BITS bits)
*
* Stored as a 32-bit value to allow the value
* (1<<HUFFMAN_BITS) to be used for empty sets of symbols
* longer than the maximum utilised length.
*/
uint32_t start;
/** Raw symbols having this length */
huffman_raw_symbol_t *raw;
};
/** A Huffman-coded alphabet */
struct huffman_alphabet
{
/** Huffman-coded symbol set for each length */
struct huffman_symbols huf[HUFFMAN_BITS];
/** Quick lookup table */
uint8_t lookup[1 << HUFFMAN_QL_BITS];
/** Raw symbols
*
* Ordered by Huffman-coded symbol length, then by symbol
* value. This field has a variable length.
*/
huffman_raw_symbol_t raw[0];
};
/**
* Get Huffman symbol length
*
* @v sym Huffman symbol set
* @ret len Length (in bits)
*/
static inline __attribute__ ((always_inline)) unsigned int
huffman_len (struct huffman_symbols *sym)
{
return sym->bits;
}
/**
* Get Huffman symbol value
*
* @v sym Huffman symbol set
* @v huf Raw input value (normalised to HUFFMAN_BITS bits)
* @ret raw Raw symbol value
*/
static inline __attribute__ ((always_inline)) huffman_raw_symbol_t
huffman_raw (struct huffman_symbols *sym, unsigned int huf)
{
return sym->raw[ huf >> sym->shift ];
}
extern int
huffman_alphabet (struct huffman_alphabet *alphabet,
uint8_t *lengths, unsigned int count);
extern struct huffman_symbols *
huffman_sym (struct huffman_alphabet *alphabet, unsigned int huf);
#endif /* _HUFFMAN_H */

48
include/lznt1.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef _LZNT1_H
#define _LZNT1_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* LZNT1 decompression
*
*/
#include <stdint.h>
/** Extract LZNT1 block length */
#define LZNT1_BLOCK_LEN(header) (((header) & 0x0fff) + 1)
/** Determine if LZNT1 block is compressed */
#define LZNT1_BLOCK_COMPRESSED(header) ((header) & 0x8000)
/** Extract LZNT1 compressed value length */
#define LZNT1_VALUE_LEN(tuple, split) \
(((tuple) & ((1 << (split)) - 1)) + 3)
/** Extract LZNT1 compressed value offset */
#define LZNT1_VALUE_OFFSET(tuple, split) (((tuple) >> split) + 1)
extern ssize_t
lznt1_decompress (const void *data, size_t len, void *buf);
#endif /* _LZNT1_H */

96
include/xca.h Normal file
View File

@ -0,0 +1,96 @@
#ifndef _XCA_H
#define _XCA_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Xpress Compression Algorithm (MS-XCA) decompression
*
*/
#include <stdint.h>
#include "huffman.h"
/** Number of XCA codes */
#define XCA_CODES 512
/** XCA decompressor */
struct xca
{
/** Huffman alphabet */
struct huffman_alphabet alphabet;
/** Raw symbols
*
* Must immediately follow the Huffman alphabet.
*/
huffman_raw_symbol_t raw[XCA_CODES];
/** Code lengths */
uint8_t lengths[XCA_CODES];
};
/** XCA symbol Huffman lengths table */
struct xca_huf_len
{
/** Lengths of each symbol */
uint8_t nibbles[XCA_CODES / 2];
} __attribute__ ((packed));
/**
* Extract Huffman-coded length of a raw symbol
*
* @v lengths Huffman lengths table
* @v symbol Raw symbol
* @ret len Huffman-coded length
*/
static inline unsigned int
xca_huf_len (const struct xca_huf_len *lengths, unsigned int symbol)
{
return (((lengths->nibbles[ symbol / 2 ]) >>
(4 * (symbol % 2))) & 0x0f);
}
/** Get word from source data stream */
#define XCA_GET16(src) \
({ \
const uint16_t *src16 = src; \
src += sizeof (*src16); \
*src16; \
})
/** Get byte from source data stream */
#define XCA_GET8(src) \
({ \
const uint8_t *src8 = src; \
src += sizeof (*src8); \
*src8; \
})
/** XCA source data stream end marker */
#define XCA_END_MARKER 256
/** XCA block size */
#define XCA_BLOCK_SIZE (64 * 1024)
extern ssize_t
xca_decompress (const void *data, size_t len, void *buf);
#endif /* _XCA_H */

View File

@ -7,5 +7,9 @@ OBJECTS += libnt/vdisk.o
OBJECTS += libnt/peloader.o OBJECTS += libnt/peloader.o
# OBJECTS += libnt/huffman.c
# OBJECTS += libnt/lznt1.c
# OBJECTS += libnt/xca.c
RM_FILES += libnt/*.s libnt/*.o RM_FILES += libnt/*.s libnt/*.o

234
libnt/huffman.c Normal file
View File

@ -0,0 +1,234 @@
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Huffman alphabets
*
*/
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "huffman.h"
#ifdef NTLOADER_UTIL
#define DBG(...) \
do \
{ \
fprintf (stderr, __VA_ARGS__); \
} while (0)
#endif
/**
* Transcribe binary value (for debugging)
*
* @v value Value
* @v bits Length of value (in bits)
* @ret string Transcribed value
*/
static const char *
huffman_bin (unsigned long value, unsigned int bits)
{
static char buf[(8 * sizeof (value)) + 1 /* NUL */];
char *out = buf;
/* Sanity check */
assert (bits < sizeof (buf));
/* Transcribe value */
while (bits--)
*(out++) = ((value & (1 << bits)) ? '1' : '0');
*out = '\0';
return buf;
}
/**
* Dump Huffman alphabet (for debugging)
*
* @v alphabet Huffman alphabet
*/
static void __attribute__ ((unused))
huffman_dump_alphabet (struct huffman_alphabet *alphabet)
{
struct huffman_symbols *sym;
unsigned int bits;
unsigned int huf;
unsigned int i;
/* Dump symbol table for each utilised length */
for (bits = 1; bits <= (sizeof (alphabet->huf) /
sizeof (alphabet->huf[0])); bits++)
{
sym = &alphabet->huf[bits - 1];
if (sym->freq == 0)
continue;
huf = (sym->start >> sym->shift);
DBG ("Huffman length %d start \"%s\" freq %d:", bits,
huffman_bin (huf, sym->bits), sym->freq);
for (i = 0; i < sym->freq; i++)
{
DBG (" %03x", sym->raw[huf + i]);
}
DBG ("\n");
}
/* Dump quick lookup table */
DBG ("Huffman quick lookup:");
for (i = 0; i < (sizeof (alphabet->lookup) /
sizeof (alphabet->lookup[0])); i++)
{
DBG (" %d", (alphabet->lookup[i] + 1));
}
DBG ("\n");
}
/**
* Construct Huffman alphabet
*
* @v alphabet Huffman alphabet
* @v lengths Symbol length table
* @v count Number of symbols
* @ret rc Return status code
*/
int huffman_alphabet (struct huffman_alphabet *alphabet,
uint8_t *lengths, unsigned int count)
{
struct huffman_symbols *sym;
unsigned int huf;
unsigned int cum_freq;
unsigned int bits;
unsigned int raw;
unsigned int adjustment;
unsigned int prefix;
int empty;
int complete;
/* Clear symbol table */
memset (alphabet->huf, 0, sizeof (alphabet->huf));
/* Count number of symbols with each Huffman-coded length */
empty = 1;
for (raw = 0; raw < count; raw++)
{
bits = lengths[raw];
if (bits)
{
alphabet->huf[bits - 1].freq++;
empty = 0;
}
}
/* In the degenerate case of having no symbols (i.e. an unused
* alphabet), generate a trivial alphabet with exactly two
* single-bit codes. This allows callers to avoid having to
* check for this special case.
*/
if (empty)
alphabet->huf[0].freq = 2;
/* Populate Huffman-coded symbol table */
huf = 0;
cum_freq = 0;
for (bits = 1; bits <= (sizeof (alphabet->huf) /
sizeof (alphabet->huf[0])); bits++)
{
sym = &alphabet->huf[ bits - 1 ];
sym->bits = bits;
sym->shift = (HUFFMAN_BITS - bits);
sym->start = (huf << sym->shift);
sym->raw = &alphabet->raw[cum_freq];
huf += sym->freq;
if (huf > (1U << bits))
{
DBG ("Huffman alphabet has too many symbols with "
"lengths <=%d\n", bits);
return -1;
}
huf <<= 1;
cum_freq += sym->freq;
}
complete = (huf == (1U << bits));
/* Populate raw symbol table */
for (raw = 0; raw < count; raw++)
{
bits = lengths[raw];
if (bits)
{
sym = &alphabet->huf[ bits - 1 ];
*(sym->raw++) = raw;
}
}
/* Adjust Huffman-coded symbol table raw pointers and populate
* quick lookup table.
*/
for (bits = 1; bits <= (sizeof (alphabet->huf) /
sizeof (alphabet->huf[0])); bits++)
{
sym = &alphabet->huf[ bits - 1 ];
/* Adjust raw pointer */
sym->raw -= sym->freq; /* Reset to first symbol */
adjustment = (sym->start >> sym->shift);
sym->raw -= adjustment; /* Adjust for quick indexing */
/* Populate quick lookup table */
for (prefix = (sym->start >> HUFFMAN_QL_SHIFT);
prefix < (1 << HUFFMAN_QL_BITS); prefix++)
{
alphabet->lookup[prefix] = (bits - 1);
}
}
/* Check that there are no invalid codes */
if (! complete)
{
DBG ("Huffman alphabet is incomplete\n");
return -1;
}
return 0;
}
/**
* Get Huffman symbol set
*
* @v alphabet Huffman alphabet
* @v huf Raw input value (normalised to HUFFMAN_BITS bits)
* @ret sym Huffman symbol set
*/
struct huffman_symbols *
huffman_sym (struct huffman_alphabet *alphabet,
unsigned int huf)
{
struct huffman_symbols *sym;
unsigned int lookup_index;
/* Find symbol set for this length */
lookup_index = (huf >> HUFFMAN_QL_SHIFT);
sym = &alphabet->huf[alphabet->lookup[lookup_index]];
while (huf < sym->start)
sym--;
return sym;
}

199
libnt/lznt1.c Normal file
View File

@ -0,0 +1,199 @@
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* LZNT1 decompression
*
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "lznt1.h"
#ifdef NTLOADER_UTIL
#define DBG(...) \
do \
{ \
fprintf (stderr, __VA_ARGS__); \
} while (0)
#define DBG2(...)
#endif
/**
* Decompress LZNT1-compressed data block
*
* @v data Compressed data
* @v limit Length of compressed data up to end of block
* @v offset Starting offset within compressed data
* @v block Decompression buffer for this block, or NULL
* @ret out_len Length of decompressed block, or negative error
*/
static ssize_t
lznt1_block (const void *data, size_t limit, size_t offset,
void *block)
{
const uint16_t *tuple;
const uint8_t *copy_src;
uint8_t *copy_dest = block;
size_t copy_len;
size_t block_out_len = 0;
unsigned int split = 12;
unsigned int next_threshold = 16;
unsigned int tag_bit = 0;
unsigned int tag = 0;
while (offset != limit)
{
/* Extract tag */
if (tag_bit == 0)
{
tag = *((uint8_t *) (data + offset));
offset++;
if (offset == limit)
break;
}
/* Calculate copy source and length */
if (tag & 1)
{
/* Compressed value */
if (offset + sizeof (*tuple) > limit)
{
DBG ("LZNT1 compressed value overrun at %#zx\n",
offset);
return -1;
}
tuple = (data + offset);
offset += sizeof (*tuple);
copy_len = LZNT1_VALUE_LEN (*tuple, split);
block_out_len += copy_len;
if (copy_dest)
{
copy_src = (copy_dest -
LZNT1_VALUE_OFFSET (*tuple, split));
while (copy_len--)
*(copy_dest++) = *(copy_src++);
}
}
else
{
/* Uncompressed value */
copy_src = (data + offset);
if (copy_dest)
*(copy_dest++) = *copy_src;
offset++;
block_out_len++;
}
/* Update split, if applicable */
while (block_out_len > next_threshold)
{
split--;
next_threshold <<= 1;
}
/* Move to next value */
tag >>= 1;
tag_bit = ((tag_bit + 1) % 8);
}
return block_out_len;
}
/**
* Decompress LZNT1-compressed data
*
* @v data Compressed data
* @v len Length of compressed data
* @v buf Decompression buffer, or NULL
* @ret out_len Length of decompressed data, or negative error
*/
ssize_t lznt1_decompress (const void *data, size_t len, void *buf)
{
const uint16_t *header;
const uint8_t *end;
size_t offset = 0;
ssize_t out_len = 0;
size_t block_len;
size_t limit;
void *block;
ssize_t block_out_len;
while (offset != len)
{
/* Check for end marker */
if ((offset + sizeof (*end)) == len)
{
end = (data + offset);
if (*end == 0)
break;
}
/* Extract block header */
if ((offset + sizeof (*header)) > len)
{
DBG ("LZNT1 block header overrun at %#zx\n", offset);
return -1;
}
header = (data + offset);
offset += sizeof (*header);
/* Process block */
block_len = LZNT1_BLOCK_LEN (*header);
if (LZNT1_BLOCK_COMPRESSED (*header))
{
/* Compressed block */
DBG2 ("LZNT1 compressed block %#zx+%#zx\n",
offset, block_len);
limit = (offset + block_len);
block = (buf ? (buf + out_len) : NULL);
block_out_len = lznt1_block (data, limit, offset,
block);
if (block_out_len < 0)
return block_out_len;
offset += block_len;
out_len += block_out_len;
}
else
{
/* Uncompressed block */
if ((offset + block_len) > len)
{
DBG ("LZNT1 uncompressed block overrun at "
"%#zx+%#zx\n", offset, block_len);
return -1;
}
DBG2 ("LZNT1 uncompressed block %#zx+%#zx\n",
offset, block_len);
if (buf)
{
memcpy (buf + out_len, data + offset, block_len);
}
offset += block_len;
out_len += block_len;
}
}
return out_len;
}

182
libnt/xca.c Normal file
View File

@ -0,0 +1,182 @@
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Xpress Compression Algorithm (MS-XCA) decompression
*
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "huffman.h"
#include "xca.h"
#ifdef NTLOADER_UTIL
#define DBG(...) \
do \
{ \
fprintf (stderr, __VA_ARGS__); \
} while (0)
#endif
/**
* Decompress XCA-compressed data
*
* @v data Compressed data
* @v len Length of compressed data
* @v buf Decompression buffer, or NULL
* @ret out_len Length of decompressed data, or negative error
*/
ssize_t xca_decompress (const void *data, size_t len, void *buf)
{
const void *src = data;
const void *end = (src + len);
uint8_t *out = buf;
size_t out_len = 0;
size_t out_len_threshold = 0;
const struct xca_huf_len *lengths;
struct xca xca;
uint32_t accum = 0;
int extra_bits = 0;
unsigned int huf;
struct huffman_symbols *sym;
unsigned int raw;
unsigned int match_len;
unsigned int match_offset_bits;
unsigned int match_offset;
const uint8_t *copy;
int rc;
/* Process data stream */
while (src < end)
{
/* (Re)initialise decompressor if applicable */
if (out_len >= out_len_threshold)
{
/* Construct symbol lengths */
lengths = src;
src += sizeof (*lengths);
if (src > end)
{
DBG ("XCA too short to hold Huffman lengths "
"table at input offset %#zx\n", src - data);
return -1;
}
for (raw = 0; raw < XCA_CODES; raw++)
xca.lengths[raw] = xca_huf_len (lengths, raw);
/* Construct Huffman alphabet */
if ((rc = huffman_alphabet (&xca.alphabet,
xca.lengths,
XCA_CODES)) != 0)
return rc;
/* Initialise state */
accum = XCA_GET16 (src);
accum <<= 16;
accum |= XCA_GET16 (src);
extra_bits = 16;
/* Determine next threshold */
out_len_threshold = (out_len + XCA_BLOCK_SIZE);
}
/* Determine symbol */
huf = (accum >> (32 - HUFFMAN_BITS));
sym = huffman_sym (&xca.alphabet, huf);
raw = huffman_raw (sym, huf);
accum <<= huffman_len (sym);
extra_bits -= huffman_len (sym);
if (extra_bits < 0)
{
accum |= (XCA_GET16 (src) << (-extra_bits));
extra_bits += 16;
}
/* Process symbol */
if (raw < XCA_END_MARKER)
{
/* Literal symbol - add to output stream */
if (buf)
*(out++) = raw;
out_len++;
}
else if ((raw == XCA_END_MARKER) && (src >= (end - 1)))
{
/* End marker symbol */
return out_len;
}
else
{
/* LZ77 match symbol */
raw -= XCA_END_MARKER;
match_offset_bits = (raw >> 4);
match_len = (raw & 0x0f);
if (match_len == 0x0f)
{
match_len = XCA_GET8 (src);
if (match_len == 0xff)
{
match_len = XCA_GET16 (src);
}
else
{
match_len += 0x0f;
}
}
match_len += 3;
if (match_offset_bits)
{
match_offset = ((accum >> (32 - match_offset_bits))
+ (1 << match_offset_bits));
}
else
{
match_offset = 1;
}
accum <<= match_offset_bits;
extra_bits -= match_offset_bits;
if (extra_bits < 0)
{
accum |= (XCA_GET16 (src) << (-extra_bits));
extra_bits += 16;
}
/* Copy data */
out_len += match_len;
if (buf)
{
copy = (out - match_offset);
while (match_len--)
*(out++) = *(copy++);
}
}
}
/* Allow for termination with no explicit end marker symbol */
if (src == end)
return out_len;
DBG ("XCA input overrun at output length %#zx\n", out_len);
return -1;
}

233
utils/bmtool.c Normal file
View File

@ -0,0 +1,233 @@
/*
* ntloader -- Microsoft Windows NT6+ loader
* Copyright (C) 2025 A1ive.
*
* ntloader is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* ntloader is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ntloader. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include "lznt1.h"
#include "xca.h"
#define BOOTMGR_MIN_LEN 16384
static void *
load_bootmgr (const char *path, size_t *len)
{
FILE *fp = fopen (path, "rb");
if (!fp)
{
fprintf (stderr, "Error opening file '%s': %s\n",
path, strerror (errno));
return NULL;
}
if (fseek (fp, 0, SEEK_END) != 0)
{
fprintf (stderr, "Error seeking file '%s': %s\n",
path, strerror(errno));
fclose (fp);
return NULL;
}
long fsize = ftell (fp);
if (fsize <= 0)
{
fprintf (stderr, "Error getting file size '%s': %s\n",
path, strerror (errno));
fclose (fp);
return NULL;
}
rewind (fp);
char *buffer = malloc (fsize);
if (!buffer)
{
fprintf (stderr, "Memory allocation failed\n");
fclose (fp);
return NULL;
}
size_t read_size = fread (buffer, 1, fsize, fp);
if (read_size != (size_t) fsize)
{
fprintf (stderr, "Error reading file '%s'\n", path);
free (buffer);
fclose (fp);
return NULL;
}
fclose (fp);
*len = (size_t) fsize;
return buffer;
}
static int
write_data (const char *filename, const void *data, size_t size)
{
FILE *fp = fopen(filename, "wb");
if (fp == NULL)
{
fprintf (stderr, "cannot open %s\n", filename);
return -1;
}
size_t bytes_written = fwrite (data, 1, size, fp);
if (bytes_written != size)
{
fprintf (stderr, "cannot write to %s\n", filename);
fclose (fp);
return -1;
}
fclose (fp);
return 0;
}
static int is_empty_pgh (const void *pgh)
{
const uint32_t *dwords = pgh;
return ((dwords[0] | dwords[1] | dwords[2] | dwords[3]) == 0);
}
static int
decompress_bootmgr (const char *out, void *data, size_t len)
{
const uint8_t *cdata;
size_t offset;
size_t cdata_len;
ssize_t (* decompress) (const void *, size_t, void *);
ssize_t udata_len;
void *udata = NULL;
fprintf (stdout, "bootmgr @%p [%zu]\n", data, len);
/* Look for an embedded compressed bootmgr.exe on an
* eight-byte boundary.
*/
for (offset = BOOTMGR_MIN_LEN;
offset < (len - BOOTMGR_MIN_LEN);
offset += 0x08)
{
/* Initialise checks */
decompress = NULL;
cdata = (uint8_t *) data + offset;
cdata_len = len - offset;
/* Check for an embedded LZNT1-compressed bootmgr.exe.
* Since there is no way for LZNT1 to compress the
* initial "MZ" bytes of bootmgr.exe, we look for this
* signature starting three bytes after a paragraph
* boundary, with a preceding tag byte indicating that
* these two bytes would indeed be uncompressed.
*/
if (((offset & 0x0f) == 0x00) &&
((cdata[0x02] & 0x03) == 0x00) &&
(cdata[0x03] == 'M') &&
(cdata[0x04] == 'Z'))
{
fprintf (stdout,
"checking for LZNT1 bootmgr.exe at +0x%zx\n",
offset);
decompress = lznt1_decompress;
}
/* Check for an embedded XCA-compressed bootmgr.exe.
* The bytes 0x00, 'M', and 'Z' will always be
* present, and so the corresponding symbols must have
* a non-zero Huffman length. The embedded image
* tends to have a large block of zeroes immediately
* beforehand, which we check for. It's implausible
* that the compressed data could contain substantial
* runs of zeroes, so we check for that too, in order
* to eliminate some common false positive matches.
*/
if (((cdata[0x00] & 0x0f) != 0x00) &&
((cdata[0x26] & 0xf0) != 0x00) &&
((cdata[0x2d] & 0x0f) != 0x00) &&
(is_empty_pgh (cdata - 0x10)) &&
(! is_empty_pgh ((cdata + 0x400))) &&
(! is_empty_pgh ((cdata + 0x800))) &&
(! is_empty_pgh ((cdata + 0xc00))))
{
fprintf (stdout,
"checking for XCA bootmgr.exe at +0x%zx\n",
offset);
decompress = xca_decompress;
}
/* If we have not found a possible bootmgr.exe, skip
* to the next offset.
*/
if (! decompress)
continue;
/* Find length of decompressed image */
udata_len = decompress (cdata, cdata_len, NULL);
if (udata_len < 0)
{
/* May be a false positive signature match */
continue;
}
/* Extract decompressed image to memory */
fprintf (stdout, "extracting embedded bootmgr.exe\n");
udata = malloc (udata_len);
if (! udata)
{
fprintf (stderr, "out of memory\n");
return -1;
}
decompress (cdata, cdata_len, udata);
break;
}
if (udata == NULL)
{
fprintf (stderr, "no embedded bootmgr.exe found\n");
return -1;
}
int rc = write_data (out, udata, udata_len);
free (udata);
return rc;
}
int main (int argc, char *argv[])
{
if (argc < 2)
{
fprintf (stderr, "Usage: %s BOOTMGR [BOOTMGR.EXE]\n",
argv[0]);
return EXIT_FAILURE;
}
size_t len;
const char *out = argc >= 3 ? argv[2] : "bootmgr.exe";
void *data = load_bootmgr (argv[1], &len);
if (data == NULL)
return EXIT_FAILURE;
int rc = decompress_bootmgr (out, data, len);
free (data);
return rc;
}

View File

@ -51,3 +51,16 @@ regview : $(REG_FILES)
$(HOST_CC) $(HOST_CFLAGS) -iquote include/ $(REG_FILES) -o $@ $(HOST_CC) $(HOST_CFLAGS) -iquote include/ $(REG_FILES) -o $@
RM_FILES += regview regview.exe RM_FILES += regview regview.exe
# bmtool
#
BMTOOL_FILES := libnt/huffman.c libnt/lznt1.c libnt/xca.c
BMTOOL_FILES += utils/bmtool.c
bmtool.exe : $(BMTOOL_FILES)
$(MINGW_CC) $(HOST_CFLAGS) -iquote include/ $(BMTOOL_FILES) -o $@
bmtool : $(BMTOOL_FILES)
$(HOST_CC) $(HOST_CFLAGS) -iquote include/ $(BMTOOL_FILES) -o $@
RM_FILES += bmtool bmtool.exe