Can you recover text hidden by colored strokes in a screenshot? The answer depends on what remains in the file. An editable annotation, an embedded preview, a partially transparent stroke, and an opaque stroke exported into a JPEG present different recovery opportunities.
This investigation examined two overlapping screenshots received as email attachments. Both contained concealed text. The task was to look for recoverable information in their metadata, internal file structure, and remaining pixels.
No concealed name or fully covered word was recovered reliably. The files contained flattened redacted images, with no embedded alternative image found. Channel separation and contrast analysis exposed stroke boundaries and compression artifacts, but no readable lettering inside the solid covered regions.
The workflow below explains how we reached that result and what the evidence supports.
Privacy note: Original filenames, hashes, conversation text, exact evidence timestamps, local paths, and source identifiers are omitted. Commands use generic filenames. The original screenshots and derived crops are not included because their visible content could identify people.
Platform note: The investigation used macOS.
xattrandmdlsexamples are macOS-specific. The pixel analysis uses Python and Pillow. The setup commands below describe a reproducible environment; they are not an acquisition log.
Minimal Toolkit
The examination used ordinary file utilities, ExifTool, and two small Python scripts. It did not require a decompiler or a generative image model.
# macOS: install ExifTool if needed
brew install exiftool
# Isolated Python environment for image analysis
python3 -m venv .venv
source .venv/bin/activate
python -m pip install Pillow
mkdir -p analysis
A JPEG is compressed image data organized into marker segments. For this task, examining those segments was more useful than treating the attachments as executable programs to disassemble.
1. Identify the Files and Record Integrity
The first checks established what the attachments actually contained:
file sample-a.jpg sample-b.jpg
shasum -a 256 sample-a.jpg sample-b.jpg > analysis/before.sha256
Both files were baseline JPEG images, approximately 90 KB each, with three color components and 4:2:0 chroma subsampling. Their dimensions differed slightly. The extension matched the detected format.
Hashes were recorded before the deeper inspection and checked again at the end. Both file-content hashes matched. Derived images and reports were written separately; the original JPEG contents were not rewritten.
shasum -a 256 -c analysis/before.sha256
This verifies file bytes. It does not prove that filesystem metadata remained unchanged: opening a file may update its access timestamp. For an evidentiary acquisition, preserve the source and its filesystem metadata separately and work on analysis copies. This examination concerned supplied attachments, rather than a forensic disk acquisition.
2. Inspect Embedded Metadata
The metadata pass looked for capture dates, camera information, GPS coordinates, authorship, editing software, comments, and embedded previews.
exiftool -a -u -G1 -s sample-a.jpg sample-b.jpg \
> analysis/metadata.txt
The options make the output easier to audit:
-aincludes duplicate tags.-uincludes unknown tags that ExifTool recognizes as unknown.-G1identifies the metadata group for each field.-sdisplays tag names in a compact form.
The output showed basic JPEG and JFIF properties. It did not expose EXIF capture information, GPS coordinates, author details, editing software, a transcript, or an embedded thumbnail.
One distinction matters when reading this output: ExifTool also reports local filesystem properties. A FileModifyDate under the System group is not an embedded original capture date. A filename containing a date is another clue, but it does not authenticate when an image was created.
The absence of embedded metadata limited provenance analysis. It did not prove who created the screenshots or which service originally hosted them.
3. Inspect Local Download Metadata
On macOS, useful information can exist outside the JPEG bytes, in extended attributes and the Spotlight index.
# List extended attribute names
xattr sample-a.jpg
xattr sample-b.jpg
# Inspect an attribute in hexadecimal
xattr -px com.apple.quarantine sample-a.jpg
# Inspect selected Spotlight fields
mdls \
-name kMDItemWhereFroms \
-name kMDItemAuthors \
-name kMDItemContentCreationDate \
-name kMDItemDateAdded \
-name kMDItemAcquisitionModel \
sample-a.jpg sample-b.jpg
The quarantine records identified an email application as the recorded local source. They also contained local timestamps and event identifiers. Those values described the files' handling on this machine; they did not establish the original screenshot date or sender identity.
Spotlight returned no origin URL, author, or acquisition model. Other opaque extended attributes were recorded, but no recovered conversation text or useful provenance was established from them.
These checks support a narrow finding: the local records were consistent with attachments saved from email. They did not verify the claimed upstream cloud-storage origin.
Raw metadata logs were kept out of the public post. Paths, URLs, timestamps, and identifiers can expose private information even when the image itself has been redacted.
4. Examine the JPEG Structure
Metadata inspection alone does not answer whether another image or an extra payload remains in a file. We also walked each JPEG's marker structure with a small Python parser.
The parser recorded segment offsets and lengths, skipped the compressed scan while accounting for JPEG byte stuffing and restart markers, located the end-of-image marker, and checked for trailing bytes.
Both files followed this structure:
SOI Start of image
APP0 JFIF header
DQT Quantization tables
SOF0 Baseline frame header
DHT Huffman tables
SOS Start of scan
Compressed image data
EOI End of image
The relevant observations were:
| Check | Finding in both files |
|---|---|
| JFIF thumbnail | None |
| APP1 segment, commonly used for EXIF/XMP | Absent |
| APP2 segment | Absent |
| APP13 segment, commonly used for IPTC/Photoshop data | Absent |
| JPEG comment segment | Absent |
| Image scan | One baseline scan |
| Bytes after EOI | Zero |
No embedded alternate image, preview, annotation layer, or appended payload was found in the inspected structure. The decoded image already included the red strokes.
This matters because there was no separate object to remove or undo. Any pixel-based recovery would have to come from information that survived in the rendered image.
It is also a bounded result. A normal-looking JPEG structure does not rule out every possible steganographic encoding. This was a targeted check for practical recovery opportunities, not an exhaustive steganalysis exercise.
5. Compare the Overlapping Screenshots
The two images showed overlapping portions of the same conversation. Repeated content can be useful when an area covered in one screenshot is exposed in another.
We visually compared the shared message areas and their redactions. The concealment strokes differed, but both images covered the same names. Neither supplied a readable replacement for the other's concealed text.
One screenshot showed additional ordinary content beyond the other's viewport. That was already-visible information, rather than recovery beneath a redaction.
This step was a visual comparison. We did not perform automated registration, subtract aligned frames, or reconstruct hidden text by combining them.
6. Separate the Color Channels
Colored strokes can affect the red, green, and blue channels differently. Looking at each channel separately can reveal text that remains visible through a translucent overlay or near its edges.
We generated contact sheets containing the original redacted crops alongside contrast-stretched versions of each channel. The following example reproduces that operation for a selected region:
from pathlib import Path
from PIL import Image, ImageOps
def inspect_channels(image_path, box, output_prefix):
with Image.open(image_path) as source:
crop = source.convert("RGB").crop(box)
crop.save(f"{output_prefix}-original.png")
for name, channel in zip(("red", "green", "blue"), crop.split()):
enhanced = ImageOps.autocontrast(channel)
enhanced.save(f"{output_prefix}-{name}.png")
# Illustrative coordinates, not coordinates from the evidence.
# Pillow boxes use (left, upper, right, lower).
inspect_channels(
"sample-a.jpg",
(0, 0, 300, 120),
Path("analysis/region-a"),
)
The investigation inspected crops around every concealed region in both files. The solid stroke interiors did not reveal readable text in any channel.
Some fragments at the stroke boundaries remained visible. They were insufficient to identify a complete covered name or word. A partial shape is not a reliable basis for filling in the missing string.
7. Amplify Local Residuals
A second pass examined subtle local variation in the blue channel. We applied a contrast stretch, then a high-pass filter to highlight changes that might be difficult to see in the original crop.
The filter subtracts a blurred version of the channel, centers the residual around mid-gray, and amplifies it. The parameters below match the residual inspection used in this examination:
from PIL import Image, ImageChops, ImageFilter, ImageOps
with Image.open("analysis/region-a-original.png") as source:
blue = source.convert("RGB").getchannel("B")
ImageOps.autocontrast(blue, cutoff=1).save(
"analysis/region-a-blue-contrast.png"
)
blurred = blue.filter(ImageFilter.GaussianBlur(2))
residual = ImageChops.subtract(blue, blurred, scale=1, offset=128)
amplified = residual.point(
lambda value: max(0, min(255, (value - 128) * 12 + 128))
)
amplified.save("analysis/region-a-blue-residual.png")
The resulting crops showed strong stroke edges, local gradients, and compression patterns. They did not produce reliable lettering inside the covered areas.
JPEG artifacts become more obvious under aggressive enhancement. Blocks and ringing can resemble character fragments. We checked the enhanced output against the original crops and the other screenshot before assigning meaning to a pattern. No complete concealed string met that threshold.
No generative reconstruction was used. The derived images transformed existing pixels; they did not synthesize replacement text.
Findings and Limits
The examination produced several concrete results:
- The attachments were flattened baseline JPEGs containing the visible redactions.
- Embedded metadata supplied no useful identity or original capture information.
- Local metadata supported email handling, without identifying the original sender or cloud source.
- The inspected JPEG structures contained no alternate image or trailing payload.
- Overlap comparison did not expose the concealed names.
- Channel separation and residual enhancement did not recover a readable covered name or word.
- Before-and-after SHA-256 checks confirmed that the source file contents were unchanged.
The private examination package retained the metadata listing, marker offsets, hashes, analysis scripts, channel comparisons, residual images, and a report. Those artifacts support review of the methods and findings. They are not attached here because they contain evidence-specific content and identifiers.
The result applies to these supplied JPEGs. It does not establish whether an earlier unredacted screenshot, an editable project, a retained cloud version, or a chat export exists elsewhere. Those sources would be the most useful next recovery paths. Downloading the same flattened JPEG bytes again would not restore overwritten pixels.
What This Examination Demonstrates
Redaction analysis starts by determining what information survived export. Metadata and file structure may provide something to extract. Overlapping images may expose missing regions. Pixel analysis may reveal text beneath incomplete or translucent concealment.
In this case, none of those paths produced a reliable recovery. The useful outcome was a documented explanation of that limit: we identified the file format, inspected the available metadata and structure, tested the remaining pixels, and preserved the distinction between readable evidence and speculation.