mirror of
https://github.com/Kudaes/Puzzle
synced 2026-06-06 16:04:33 +00:00
148 lines
4.0 KiB
Python
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()
|