-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathzsteg_scraper.py
More file actions
107 lines (85 loc) · 3.25 KB
/
zsteg_scraper.py
File metadata and controls
107 lines (85 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import subprocess
import os
import re
from collections import defaultdict
def to_wsl_path(win_path: str) -> str:
r"""
Convert a Windows path (e.g. C:\Users\User\...) to a WSL path (/mnt/c/Users/User/...)
"""
win_path = os.path.abspath(win_path)
drive, path_rest = os.path.splitdrive(win_path)
drive_letter = drive.strip(":").lower()
unix_path = path_rest.replace("\\", "/")
return f"/mnt/{drive_letter}{unix_path}"
def run_zsteg(image_path: str) -> str:
"""
Run zsteg via WSL on the provided image path and return filtered output.
Args:
image_path (str): Full Windows path to the image file (from GUI).
Returns:
str: Filtered output from zsteg or an appropriate error message.
"""
try:
# Locate script
script_path_win = os.path.join(os.path.dirname(os.path.abspath(__file__)), "runzsteg.sh")
# Sanity checks
if not os.path.exists(script_path_win):
return f"❌ Error: Bash script not found:\n{script_path_win}"
if not os.path.exists(image_path):
return f"❌ Error: Image file not found:\n{image_path}"
# Convert both paths to WSL format
script_path_wsl = to_wsl_path(script_path_win)
image_path_wsl = to_wsl_path(image_path)
# Build and run WSL command
cmd = ["wsl", "bash", script_path_wsl, image_path_wsl]
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=120 # Increased timeout
)
if result.returncode != 0:
return f"❌ Error from zsteg:\n{result.stderr.strip() or 'Unknown error'}"
raw_output = result.stdout.strip()
if not raw_output:
return "✅ Zsteg finished but returned no output."
# Parse & group zsteg output
return parse_and_group_zsteg(raw_output)
except subprocess.TimeoutExpired:
return "❌ Error: zsteg analysis timed out. Try a smaller image or check WSL status."
except Exception as e:
return f"❌ Unexpected error: {str(e)}"
def parse_and_group_zsteg(output: str) -> str:
"""
Filter and group duplicate-looking zsteg lines.
Args:
output (str): Raw output from zsteg.
Returns:
str: Grouped summary of findings.
"""
grouped = defaultdict(set)
pattern = re.compile(r"^(.*?)\s+\.\.\s+(file|text):\s*(.+)$")
unparsed_lines = []
for line in output.splitlines():
line = line.strip()
if not line:
continue
match = pattern.match(line)
if match:
channel = match.group(1).strip()
content = match.group(3).strip()
grouped[content].add(channel)
else:
unparsed_lines.append(line)
if not grouped and not unparsed_lines:
return "✅ Zsteg completed. No hidden content detected."
result = []
for content, channels in sorted(grouped.items()):
sorted_channels = sorted(channels)
result.append(f"🔹 Detected: {content}")
result.append(f" ↳ Found in: {', '.join(sorted_channels)}")
result.append("")
if unparsed_lines:
result.append("🔸 Unparsed lines:")
result.extend(unparsed_lines)
return "\n".join(result).strip()