#!/usr/bin/env python3 """从“郑州情趣王一手科技.exe”提取的 JPEG 图片处理逻辑。 处理步骤与原程序一致: 1. 裁掉 JPEG 底部两行并以 quality=95 重新编码; 2. 把 SOF 段中的图片高度改回原始高度; 3. 删除最后一个 JPEG EOI(FF D9)结束标志; 4. 按指定大小追加随机字节,输出为同名 .file 文件。 注意:输出是刻意构造的非标准 JPEG,部分看图软件可能拒绝打开。 """ from __future__ import annotations import argparse import random import sys from pathlib import Path from typing import Iterable try: from PIL import Image except ImportError as exc: raise SystemExit("缺少 Pillow,请先执行:python3 -m pip install pillow") from exc JPEG_EXTENSIONS = {".jpg", ".jpeg", ".jpe", ".jfif"} SOF_MARKERS = { 0xC0, 0xC1, 0xC2, 0xC3, 0xC5, 0xC6, 0xC7, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF, } def find_and_modify_height(data: bytearray, original_height: int) -> bytearray | None: """找到第一个 JPEG SOF 段,把高度字段恢复为裁剪前的高度。""" index = 0 length = len(data) while index < length - 1: if data[index] != 0xFF: index += 1 continue marker = data[index + 1] if marker in SOF_MARKERS: height_offset = index + 5 if height_offset + 1 >= length: return None data[height_offset] = (original_height >> 8) & 0xFF data[height_offset + 1] = original_height & 0xFF return data # SOI 和 EOI 标记没有长度字段。 if marker in (0xD8, 0xD9): index += 2 continue if index + 3 < length: segment_length = (data[index + 2] << 8) | data[index + 3] index += 2 + segment_length else: index += 2 return None def remove_eoi(data: bytearray) -> tuple[bytearray, bool]: """从末尾向前查找并删除最后一个 FF D9 结束标志。""" for index in range(len(data) - 1, 0, -1): if data[index - 1] == 0xFF and data[index] == 0xD9: return data[: index - 1] + data[index + 1 :], True return data, False def process_single_image( input_file: Path | str, output_file: Path | str, junk_size_mb: float = 1.0, ) -> Path: """按原程序逻辑处理一张图片,并返回输出路径。""" input_path = Path(input_file) output_path = Path(output_file) temp_path = Path(f"{output_path}.tmp") if not 0 <= junk_size_mb <= 100: raise ValueError("junk_size_mb 必须在 0 到 100 之间") output_path.parent.mkdir(parents=True, exist_ok=True) try: with Image.open(input_path) as image: width, height = image.size if height <= 2: raise ValueError(f"图片高度不足 3 行:{input_path}") cropped_image = image.crop((0, 0, width, height - 2)) try: cropped_image.save(temp_path, "JPEG", quality=95) finally: cropped_image.close() image_data = bytearray(temp_path.read_bytes()) modified_data = find_and_modify_height(image_data, height) or image_data modified_data, _ = remove_eoi(modified_data) junk_size_bytes = int(junk_size_mb * 1024 * 1024) if junk_size_bytes > 0: # 保留原程序的逐字节伪随机生成方式。 modified_data += bytearray( random.getrandbits(8) for _ in range(junk_size_bytes) ) output_path.write_bytes(modified_data) return output_path finally: if temp_path.exists(): temp_path.unlink() def collect_images(paths: Iterable[Path]) -> list[Path]: """收集输入文件,并按原程序支持的扩展名递归扫描目录。""" images: list[Path] = [] for path in paths: if path.is_file() and path.suffix.lower() in JPEG_EXTENSIONS: images.append(path) elif path.is_dir(): images.extend( candidate for candidate in path.rglob("*") if candidate.is_file() and candidate.suffix.lower() in JPEG_EXTENSIONS ) else: print(f"跳过无效路径或非 JPEG 文件:{path}", file=sys.stderr) return images def build_output_path(input_path: Path, output_dir: Path | None) -> Path: """原程序使用图片主文件名加 .file 作为输出名。""" directory = output_dir if output_dir is not None else input_path.parent return directory / f"{input_path.stem}.file" def main() -> int: parser = argparse.ArgumentParser( description="裁掉 JPEG 底部两行、伪造原高度、删除 EOI 并追加随机数据。" ) parser.add_argument("inputs", nargs="+", type=Path, help="JPEG 文件或目录") parser.add_argument("-o", "--output-dir", type=Path, help="统一输出目录") parser.add_argument( "--junk-mb", type=float, default=1.0, help="每张图片追加的随机数据大小,范围 0–100 MB,默认 1 MB", ) args = parser.parse_args() if not 0 <= args.junk_mb <= 100: parser.error("--junk-mb 必须在 0 到 100 之间") images = collect_images(args.inputs) if not images: print("没有找到可处理的 JPEG 图片。", file=sys.stderr) return 1 succeeded = 0 for index, input_path in enumerate(images, start=1): output_path = build_output_path(input_path, args.output_dir) try: process_single_image(input_path, output_path, args.junk_mb) succeeded += 1 print(f"[{index}/{len(images)}] 完成:{input_path} -> {output_path}") except Exception as exc: print(f"[{index}/{len(images)}] 失败:{input_path} - {exc}", file=sys.stderr) print(f"处理完成:总计 {len(images)},成功 {succeeded},失败 {len(images) - succeeded}") return 0 if succeeded == len(images) else 2 if __name__ == "__main__": raise SystemExit(main())