mirror of
https://github.com/grub4dos/ntloader.git
synced 2025-05-08 19:51:14 +08:00
add bmtool
This commit is contained in:
parent
521dd72ac7
commit
1ba67d92ac
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -47,6 +47,9 @@ jobs:
|
||||
- name: Build fsuuid.exe
|
||||
run: |
|
||||
make fsuuid.exe
|
||||
- name: Build bmtool.exe
|
||||
run: |
|
||||
make bmtool.exe
|
||||
- name: Copy files
|
||||
run: |
|
||||
mkdir -p build
|
||||
@ -55,6 +58,7 @@ jobs:
|
||||
cp ntloader.arm64 build/
|
||||
cp initrd.cpio build/
|
||||
cp fsuuid.exe build/
|
||||
cp bmtool.exe build/
|
||||
cp mkinitrd.exe build/
|
||||
cp utils/bcd.bat build/
|
||||
- name: Download README.pdf
|
||||
|
@ -298,6 +298,9 @@ mkinitrd.exe rootfs initrd.cpio
|
||||
find * | cpio -o -H newc > ../initrd.cpio
|
||||
```
|
||||
|
||||
### bmtool
|
||||
`bmtool` is a program for extracting bootmgr.exe from bootmgr.
|
||||
|
||||
### bcd.bat
|
||||
`bcd.bat` is a batch script to create the BCD file.
|
||||
Do not edit it unless you know how NTloader works.
|
||||
@ -323,6 +326,12 @@ make fsuuid
|
||||
make fsuuid.exe
|
||||
```
|
||||
|
||||
### Compile bmtool
|
||||
```
|
||||
make bmtool
|
||||
make bmtool.exe
|
||||
```
|
||||
|
||||
### Compile mkinitrd
|
||||
```
|
||||
make mkinitrd
|
||||
|
@ -32,7 +32,7 @@
|
||||
#define assert(x) \
|
||||
do \
|
||||
{ \
|
||||
if (DEBUG && ! (x)) \
|
||||
if (! (x)) \
|
||||
{ \
|
||||
die ("Assertion failed at %s line %d: %s\n", \
|
||||
__FILE__, __LINE__, #x); \
|
||||
|
115
include/huffman.h
Normal file
115
include/huffman.h
Normal 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
48
include/lznt1.h
Normal 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
96
include/xca.h
Normal 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 */
|
@ -7,5 +7,9 @@ OBJECTS += libnt/vdisk.o
|
||||
|
||||
OBJECTS += libnt/peloader.o
|
||||
|
||||
# OBJECTS += libnt/huffman.c
|
||||
# OBJECTS += libnt/lznt1.c
|
||||
# OBJECTS += libnt/xca.c
|
||||
|
||||
RM_FILES += libnt/*.s libnt/*.o
|
||||
|
||||
|
234
libnt/huffman.c
Normal file
234
libnt/huffman.c
Normal 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
199
libnt/lznt1.c
Normal 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
182
libnt/xca.c
Normal 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
233
utils/bmtool.c
Normal 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;
|
||||
}
|
@ -51,3 +51,16 @@ regview : $(REG_FILES)
|
||||
$(HOST_CC) $(HOST_CFLAGS) -iquote include/ $(REG_FILES) -o $@
|
||||
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user