Files
2026-04-27 13:34:48 +02:00

148 lines
4.0 KiB
Python

#!/usr/bin/env python3
import subprocess
import sys
import os
import re
import uuid
# Change this if the binary is not named "wimlib-imagex"
WIMLIB_EXE = "wimlib-imagex"
WIM_MAGIC = b"MSWIM\x00\x00\x00"
WIM_GUID_OFFSET = 0x18
WIM_GUID_SIZE = 16
def get_wim_guid(wim_path):
"""
Returns the GUID of the WIM (canonical string) read from the header.
"""
if not os.path.exists(wim_path):
raise FileNotFoundError(f"WIM not found: {wim_path}")
with open(wim_path, "rb") as f:
hdr = f.read(208)
if len(hdr) < 208:
raise RuntimeError("File too small to be a WIM (incomplete header).")
if hdr[:8] != WIM_MAGIC:
raise RuntimeError("Does not appear to be a WIM: magic != MSWIM\\0\\0\\0.")
guid_le = hdr[WIM_GUID_OFFSET:WIM_GUID_OFFSET + WIM_GUID_SIZE]
if len(guid_le) != 16:
raise RuntimeError("Could not read GUID from header (unexpected offset/size).")
return str(uuid.UUID(bytes_le=guid_le))
def get_wim_file_sha1(wim_path, image, internal_path, wimlib_exe=WIMLIB_EXE):
"""
Returns the SHA-1 (40 hex) of the file `internal_path` inside the
image `image` of the WIM `wim_path`, using 'wimlib-imagex dir'.
Parameters:
wim_path -> path to the .wim file (str)
image -> index (int) or image name (str)
internal_path-> path inside the WIM, for example:
'Windows/System32/notepad.exe' or 'hola.txt'
"""
if not os.path.exists(wim_path):
raise FileNotFoundError(f"WIM not found: {wim_path}")
# Normalize internal path to POSIX format used by wimlib
# 'Windows\\hello.txt' -> '/Windows/hello.txt'
internal_path = internal_path.replace("\\", "/")
if not internal_path.startswith("/"):
internal_path = "/" + internal_path
image_str = str(image)
cmd = [
wimlib_exe,
"dir",
wim_path,
image_str,
f"--path={internal_path}",
"--detailed",
"--one-file-only",
]
# Run wimlib-imagex and capture output
try:
proc = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
encoding="utf-8",
errors="replace",
)
except FileNotFoundError:
raise RuntimeError(
f"'{wimlib_exe}' not found. "
"Install wimlib (e.g., 'sudo apt install wimtools') "
"or adjust the path in WIMLIB_EXE."
)
if proc.returncode != 0:
raise RuntimeError(
f"wimlib-imagex dir failed (rc={proc.returncode}).\n"
f"Command: {' '.join(cmd)}\n"
f"STDERR:\n{proc.stderr}"
)
# Search for a 40-character hex token (SHA-1) in the output
sha1 = None
for line in proc.stdout.splitlines():
m = re.search(r"\b([0-9a-fA-F]{40})\b", line)
if m:
sha1 = m.group(1).lower()
break
if not sha1:
raise ValueError(
"No SHA-1 found in wimlib-imagex output.\n"
f"Output was:\n{proc.stdout}"
)
return sha1
def main(argv=None):
if argv is None:
argv = sys.argv[1:]
if len(argv) != 3:
print(
"Usage:\n"
" python3 get_wim_hash.py <wim_path> <image> <internal_path>\n\n"
"Examples:\n"
" python3 get_wim_hash.py /mnt/shared/file.wim 1 hello.txt\n"
" python3 get_wim_hash.py /mnt/shared/file.wim 1 Windows/hello.txt\n"
" python3 get_wim_hash.py /mnt/shared/file.wim 'TestWOF' hello.txt\n",
file=sys.stderr,
)
sys.exit(1)
wim_path = argv[0]
image = argv[1]
internal_path = argv[2]
# if image is a number, use it as an index; otherwise, as a name
try:
image = int(image)
except ValueError:
pass
# Always print the WIM GUID
wim_guid = get_wim_guid(wim_path)
print(wim_guid)
# And then the SHA-1 of the internal file
sha1 = get_wim_file_sha1(wim_path, image, internal_path)
print(sha1)
if __name__ == "__main__":
main()