#!/usr/bin/env python3 # -*- coding: utf-8 -*- """LZMA2 7z quine 生成器,仅依赖 Python 标准库。 用法:python blogquine.py 源目录 输出.7z [--quine-name 名称] [--src-dir 前缀目录] [--quine-dir 自身所在目录] 解压得到源文件及输出归档自身;内容原样存储,辅助信息位于归档内部。 """ import argparse import binascii from dataclasses import dataclass import lzma import os from pathlib import Path import struct import time SIG = b"7z\xbc\xaf'\x1c" VER = b"\x00\x04" CHUNK = 1 << 16 # LZMA2 未压缩 chunk 上限 MAX_CHUNK_OUTPUT = 1 << 21 # LZMA2 压缩 chunk 解压上限 MAX_MATCH = 273 PROPS_BYTE = 0x5D # lc=3, lp=0, pb=2 FILETIME_EPOCH_OFFSET = 116444736000000000 PROB_INIT = 1024 class RangeEncoder: """LZMA 区间编码器;所有整数截断与 SDK 的 UInt32 运算一致。""" def __init__(self): self.low = 0 self.range = 0xFFFFFFFF self.cache = 0 self.cache_size = 1 self.buf = bytearray() def _shift_low(self): carry = self.low >> 32 if (self.low & 0xFFFFFFFF) < 0xFF000000 or carry: temp = self.cache while self.cache_size: self.buf.append((temp + carry) & 0xFF) temp = 0xFF self.cache_size -= 1 self.cache = (self.low >> 24) & 0xFF self.cache_size += 1 self.low = (self.low << 8) & 0xFFFFFFFF def _normalize(self): while self.range < 1 << 24: self.range = (self.range << 8) & 0xFFFFFFFF self._shift_low() def encode_bit(self, probs, index, bit): probability = probs[index] bound = (self.range >> 11) * probability if bit: self.low += bound self.range -= bound probs[index] -= probability >> 5 else: self.range = bound probs[index] += (2048 - probability) >> 5 self._normalize() def encode_direct_bits(self, value, bits): for shift in range(bits - 1, -1, -1): self.range >>= 1 if value >> shift & 1: self.low += self.range self._normalize() def bit_tree(self, probs, offset, bits, symbol, reverse=False): node = 1 shifts = range(bits) if reverse else range(bits - 1, -1, -1) for shift in shifts: bit = symbol >> shift & 1 self.encode_bit(probs, offset + node, bit) node = (node << 1) | bit def finish(self): for _ in range(5): self._shift_low() return bytes(self.buf) class LengthEncoder: """普通 match 和 rep0 使用独立概率,但共享长度编码逻辑。""" def __init__(self): self.choice = [PROB_INIT] * 2 self.low = [PROB_INIT] * 32 self.mid = [PROB_INIT] * 32 self.high = [PROB_INIT] * 256 def encode(self, rc, pos_state, length): value = length - 2 rc.encode_bit(self.choice, 0, value >= 8) if value < 8: rc.bit_tree(self.low, pos_state << 3, 3, value) else: rc.encode_bit(self.choice, 1, value >= 16) if value < 16: rc.bit_tree(self.mid, pos_state << 3, 3, value - 8) else: rc.bit_tree(self.high, 0, 8, value - 16) class LzmaEncoder: """只编码 match/rep0,编码结果不依赖历史字节,供布局后填充数据。""" def __init__(self, out_pos=0): self.rc = RangeEncoder() self.pos = out_pos self.state = 0 self.is_match = [PROB_INIT] * (12 << 4) self.is_rep = [PROB_INIT] * 12 self.is_rep_g0 = [PROB_INIT] * 12 self.rep0_long = [PROB_INIT] * (12 << 4) self.pos_slot = [PROB_INIT] * (4 << 6) self.spec_pos = [PROB_INIT] * 114 self.align = [PROB_INIT] * 16 self.match_len = LengthEncoder() self.rep_len = LengthEncoder() def _write_dist(self, dist, length): if not 1 <= dist <= 0xFFFFFFFF: raise ValueError("LZMA distance must be 1..2^32-1") value = dist - 1 bits = max(0, value.bit_length() - 2) slot = value if value < 4 else 2 * (bits + 1) + (value >> bits & 1) self.rc.bit_tree(self.pos_slot, min(length - 2, 3) << 6, 6, slot) if slot >= 4: base = (2 | (slot & 1)) << bits extra = value - base if slot < 14: self.rc.bit_tree(self.spec_pos, base - slot - 1, bits, extra, True) else: self.rc.encode_direct_bits(extra >> 4, bits - 4) self.rc.bit_tree(self.align, 0, 4, extra & 15, True) def match(self, dist, length): if not 2 <= length <= MAX_MATCH: raise ValueError("LZMA match length must be 2..273") self.rc.encode_bit(self.is_match, (self.state << 4) + (self.pos & 3), 1) self.rc.encode_bit(self.is_rep, self.state, 0) self.match_len.encode(self.rc, self.pos & 3, length) self._write_dist(dist, length) self.state = 7 if self.state < 7 else 10 self.pos += length def rep_match(self, length): if not 2 <= length <= MAX_MATCH: raise ValueError("LZMA rep0 length must be 2..273") index = (self.state << 4) + (self.pos & 3) self.rc.encode_bit(self.is_match, index, 1) self.rc.encode_bit(self.is_rep, self.state, 1) self.rc.encode_bit(self.is_rep_g0, self.state, 0) self.rc.encode_bit(self.rep0_long, index, 1) self.rep_len.encode(self.rc, self.pos & 3, length) self.state = 8 if self.state < 7 else 11 self.pos += length def finish(self): return self.rc.finish() def crc32(data, value=0): return binascii.crc32(data, value) & 0xFFFFFFFF def varint(value): """7z UINT64:首字节给出后续小端字节数,最多九字节。""" if not 0 <= value < 1 << 64: raise ValueError("7z UINT64 out of range") for size in range(8): if value < 1 << (7 * (size + 1)): first = ((0xFF << (8 - size)) & 0xFF) | (value >> (8 * size)) return bytes([first]) + (value & ((1 << (8 * size)) - 1)).to_bytes(size, "little") return b"\xff" + value.to_bytes(8, "little") def store_hdr(payload_len, first=False): if not 1 <= payload_len <= CHUNK: raise ValueError("LZMA2 stored chunk must contain 1..65536 bytes") return bytes([1 if first else 2]) + (payload_len - 1).to_bytes(2, "big") def matches_for(dist, total): """固定距离的首个 match 加 rep0;分段时不留下单字节。""" if total < 2: raise ValueError("match sequence must output at least two bytes") tokens = [] while total: length = min(MAX_MATCH, total) if total - length == 1: length -= 1 tokens.append(("r0", length) if tokens else ("m", dist, length)) total -= length return tokens def dict_prop_for(maxdist): for prop in range(41): size = 0xFFFFFFFF if prop == 40 else (2 | (prop & 1)) << (prop // 2 + 11) if size >= maxdist: return prop, size raise ValueError("LZMA2 dictionary distance exceeds 2^32-1") def lzma_chunk(tokens, out_pos): """重置状态和概率,保留字典;21-bit 解压长度,不写结束标记。""" enc = LzmaEncoder(out_pos) for token in tokens: if token[0] == "m": enc.match(token[1], token[2]) elif token[0] == "r0": enc.rep_match(token[1]) else: raise ValueError("unsupported LZMA token: %r" % (token[0],)) data = enc.finish() total = enc.pos - out_pos if not 1 <= total <= MAX_CHUNK_OUTPUT or not 1 <= len(data) <= CHUNK: raise ValueError("LZMA2 compressed chunk size out of range") unpacked, packed = total - 1, len(data) - 1 header = (bytes([0xC0 | (unpacked >> 16)]) + (unpacked & 0xFFFF).to_bytes(2, "big") + packed.to_bytes(2, "big") + bytes([PROPS_BYTE])) return header + data, total @dataclass(frozen=True) class Entry: name: str is_dir: bool data: bytes = b"" @dataclass(frozen=True) class Layout: prefix_size: int chunks: tuple samples: bytes padding: int matches: bytes header: bytes crc_offset: int dict_size: int @property def packed_size(self): return sum(self.chunks) + 3 * len(self.chunks) + len(self.matches) + 1 class LayoutError(RuntimeError): """当前填充量没有满足布局或 CRC 定点,换一个填充量重试。""" def split_sizes(size, limit): """每片至少两字节,避免 match 无法表达的一字节尾片。""" if size < 2: raise ValueError("分片总长度必须至少为 2") sizes = [limit] * (size // limit) tail = size % limit if tail == 1: sizes[-1] -= 1 tail = 2 if tail: sizes.append(tail) return tuple(sizes) def bitfield(flags): data = bytearray((len(flags) + 7) // 8) for i, flag in enumerate(flags): if flag: data[i // 8] |= 1 << (7 - i % 8) return bytes(data) def archive_path(value, allow_empty=False): value = value.replace("\\", "/") if not value and allow_empty: return "" parts = value.split("/") if any(part in ("", ".", "..") for part in parts) or any(c in value for c in ("\0", ":")): raise ValueError("归档内路径必须是相对路径,不能包含空段、. 或 ..:%r" % value) return "/".join(parts) def encode_segments(segments, out_pos): """把 (历史起点, 长度) 转成 match 流;跨 chunk 时保留字典。""" result, tokens = bytearray(), [] used, max_distance = 0, 0 for source, length in segments: for size in split_sizes(length, MAX_CHUNK_OUTPUT): if used + size > MAX_CHUNK_OUTPUT: block, _ = lzma_chunk(tokens, out_pos) result += block out_pos += used tokens, used = [], 0 distance = out_pos + used - source if distance <= 0: raise LayoutError("回引源尚未输出") max_distance = max(max_distance, distance) tokens.extend(matches_for(distance, size)) used += size source += size if tokens: block, _ = lzma_chunk(tokens, out_pos) result += block return bytes(result), max_distance def solve_fixed_crc(data, groups, targets): """CRC 是 GF(2) 仿射函数;同步所有镜像,解三个 CRC 的 96-bit 定点。""" def vector(values): return sum(value << (32 * i) for i, value in enumerate(values)) base = vector(targets()) columns = [] for copies in groups: for bit in range(32): positions = [copy[bit // 8] for copy in copies] for pos in positions: data[pos] ^= 1 << (bit % 8) columns.append(vector(targets()) ^ base) for pos in positions: data[pos] ^= 1 << (bit % 8) rows = [] for bit in range(96): coeff = 1 << bit for j, column in enumerate(columns): coeff ^= ((column >> bit) & 1) << j rows.append([coeff, (base >> bit) & 1]) pivots = [] for column in range(96): rank = len(pivots) pivot = next((i for i in range(rank, 96) if rows[i][0] >> column & 1), None) if pivot is None: continue rows[rank], rows[pivot] = rows[pivot], rows[rank] for i in range(96): if i != rank and rows[i][0] >> column & 1: rows[i][0] ^= rows[rank][0] rows[i][1] ^= rows[rank][1] pivots.append(column) if any(not coeff and rhs for coeff, rhs in rows): raise LayoutError("CRC 定点方程无解") solution = sum(rows[i][1] << column for i, column in enumerate(pivots)) values = [(solution >> (32 * i)) & 0xFFFFFFFF for i in range(3)] for copies, value in zip(groups, values): packed = struct.pack("