Skip to content

Add BeReal Android artifact support - #962

Merged
abrignoni merged 7 commits into
abrignoni:mainfrom
Gear-I:BeReal_Android
Aug 10, 2026
Merged

Add BeReal Android artifact support#962
abrignoni merged 7 commits into
abrignoni:mainfrom
Gear-I:BeReal_Android

Conversation

@Gear-I

@Gear-I Gear-I commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds six evidence-gated artifacts for the BeReal Android client (com.bereal.ft):

Device User — the authenticated account, with a confidence tier (High/Medium/Low) and the specific evidence behind it
Accepted Friends — friends confirmed via /relationships/friends or an explicit accepted-status field
Posts — recovered posts with correlated front/rear photos and behind-the-scenes (BTS) video, and an "Authorship Basis" column explaining why a post is attributed to a given user
Correlated Profile Pictures
Comments
RealMojis

How the schema was confirmed

Field names and API response shapes were not guessed. classes.dex was extracted from the app's APK and its kotlinx.serialization DTOs were read directly — those field names survive R8 minification because they have to match the JSON on the wire. This surfaced the real post schema (id, primaryContent/secondaryContent/btsContent, or the live variant primary/secondary/btsMedia), the feed structure (myPosts/userPosts + friendsPosts), and that comments/RealMojis are embedded arrays on the post object rather than separate records referencing a postId.

The result was then validated end to end against a real UFED extraction (Josh Hickman's publicly documented Android 14 image, thebinaryhick.blog). Every artifact's output matches that image's documented action sheet exactly: 1 device user, 1 friend, 3 posts with captions and capture timestamps matching to the minute, 0 comments, 0 RealMojis.

Design principles

Each of these ties back to a specific over-collection or mis-attribution problem found while validating against real data:

Every row states its evidence. Device-user and friend rows carry the endpoint, explicit flag, or relationship status that justified including them, plus a confidence tier — nothing is reported on a bare guess.
/friends-v1 is a feed endpoint, not the friends roster. Treating it as friend evidence pulled the device owner's own account in as a "friend" of themselves. The roster is /relationships/friends; friend evidence now requires that endpoint or an explicit accepted-status/isFriend field.
Post authorship comes from schema, not guesswork. myPosts/userPosts vs. friendsPosts, or the sibling user on a {user, posts} wrapper, determines authorship structurally.
Own BTS videos are recovered from local temp files. They never round-trip through the network cache, so a URL-based media lookup alone never finds them. They're correlated to their post by matching the epoch-millis capture timestamp embedded in the bereal_my_user_temp_video filename against the post's takenAt.
Friend-search and contact-recommendation results are excluded from both the friends and profile-pictures artifacts — matching a search query isn't a validated user action, and including them re-introduces exactly the kind of noise (unrelated strangers' profile pictures) this rebuild set out to remove.

@abrignoni

Copy link
Copy Markdown
Owner

Really nice work — deriving the schema from the classes.dex kotlinx DTOs (rather than guessing minified names) and validating end-to-end against Hickman's public image is exactly the rigor we want, and the evidence-gating / confidence tiers are a genuinely good forensic pattern. A few changes before this can go green and merge:

  1. Convert the six artifacts to the context signaturedef bereal_x(context): with files_found = context.get_files_found(), instead of (files_found, report_folder, seeker, wrap_text). It's the current standard (iLEAPP/RLEAPP/VLEAPP are 100% on it, and ALEAPP is migrating), and it also removes the 18 W0613 unused-argument warnings that would otherwise fail the lint CI — no # pylint: disable=W0613 needed.
  2. Narrow the two broad except Exception: (≈ lines 175 and 393) to the specific exceptions you expect (W0718 also fails the --disable=C,R gate).
  3. Type the datetime columns. Captured and Posted/Updated (posts) and Created (comments, RealMojis) hold aware-UTC datetimes but their headers are plain strings — make them ('Captured','datetime'), ('Posted/Updated','datetime'), ('Created','datetime'). The UTC conversion in _timestamp() is already correct; this just lets LAVA store them as real timestamps and populate the timeline that output_types:"standard" requests.

No html_columns, and media typed as ('…','media') via check_in_media — nice, that keeps it clear of the HTML-report escaping issue. Once CI runs green with the above, happy to merge.

All six artifacts converted to the single-parameter (context) signature (files_found = context.get_files_found()), matching the current iLEAPP/RLEAPP/VLEAPP standard. This removes the # pylint: disable=W0613 that the old four-parameter signature required.
The two broad except Exception: blocks (_decode_body, _media_ref) narrowed to the specific exceptions actually expected ((OSError, zlib.error, EOFError) and OSError respectively), clearing W0718 under the --disable=C,R lint gate.
Datetime columns typed as ('Column', 'datetime') tuples — Captured/Posted/Updated in bereal_posts, Created in bereal_comments and bereal_realmojis — so LAVA stores them as real timestamps for the timeline.
@Gear-I

Gear-I commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

Addressed in this revision:

All six artifacts converted to the single-parameter (context) signature (files_found = context.get_files_found()), matching the current iLEAPP/RLEAPP/VLEAPP standard. This removes the # pylint: disable=W0613 that the old four-parameter signature required.
The two broad except Exception: blocks (_decode_body, _media_ref) narrowed to the specific exceptions actually expected ((OSError, zlib.error, EOFError) and OSError respectively), clearing W0718 under the --disable=C,R lint gate.
Datetime columns typed as ('Column', 'datetime') tuples — Captured/Posted/Updated in bereal_posts, Created in bereal_comments and bereal_realmojis — so LAVA stores them as real timestamps for the timeline.

abrignoni and others added 2 commits August 10, 2026 06:46
- berealAndroid.py already defines bereal_friends, and the loader raises
  on a duplicate key, so the whole tool died at startup with both modules
  present. The PR's artifact is now bereal_accepted_friends.
- Every path was listed twice, once as */data/data/... and once as
  */data/user/0/...; the repo's */com.bereal.ft/... form matches both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@abrignoni

Copy link
Copy Markdown
Owner

Thank you for this one, real new coverage. Since you opened it the repo gained berealAndroid.py (cached media, friends, API responses), and your module collided with it on the bereal_friends key, which killed the loader outright. I pushed a fixup to your branch: the artifact is now bereal_accepted_friends, the doubled data/data and data/user/0 globs are folded into the repo's single form, and sample_data is recorded from runs against our two BeReal-carrying test images. Verified side by side with the existing module across all 12 images: the existing three artifacts reproduce their counts exactly and yours add posts, device user, accepted friends and profile pictures with no errors. Merging. Good stuff.

@abrignoni
abrignoni merged commit c5aa6d1 into abrignoni:main Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants