# Reverse Engineering Glow.dylib → Build Glow từ source

## Kết quả phân tích (16 May 2026)

### File: Glow.dylib
- **Size**: 16.8MB (16787136 bytes)
- **Content**: ~95% ffmpeg static lib (libavcodec, libavformat, libavutil, libswscale, libx264, libx265, libdav1d), Go runtime, thin ObjC layer
- **Build**: macOS, Xcode 16.2 SDK, iPhoneOS 18.2 SDK, compiled with `--target=arm64-apple-ios12.1`
- **Arch**: arm64 only
- **Link**: Substrate (`MSHookMessageEx`), UIKit, Foundation, AVFoundation, Photos, Security, SystemConfiguration

### Môi trường build (GitHub Actions)
- macOS runner: `macos-latest` → Apple Silicon (ARM64)
- Toolchain: Xcode 16.2, iPhoneOS 18.2 SDK
- Build command: Theos `make package` via `waruhachi/theos-action`

---

## Glow classes (23 classes)

### UI / Presentation
| Class | Chức năng |
|---|---|
| `WelcomeVC` | First-launch welcome screen (UIViewController + tableView) |
| `ChangelogVC` | Changelog after update |
| `SettingsViewController` | Main settings screen |
| `DVNSheetController` | Bottom sheet controller |
| `DVNSheetPresenter` | Bottom sheet presenter |
| `PseudoDetentController` | Custom sheet detent |
| `PseudoDetentTransitioningDelegate` | Sheet transition delegate |
| `DVNLongPressGestureRecognizer` | Custom long press (tab bar) |

### Toast system
| Class | Chức năng |
|---|---|
| `ToastManager` | Toast queue manager |
| `ToastView` | Toast UI view |
| `ToastWindow` | Toast overlay window |

### Download / Media
| Class | Chức năng |
|---|---|
| `Downloader` | Main download manager |
| `DownloaderHelper` | Download utility |
| `MPDParser` | MPEG-DASH manifest parser (NSXMLParser delegate) |
| `FFMpegHelper` | ffmpeg wrapper |
| `FFmpegKit` | ffmpeg kit |
| `FFmpegKitConfig` | ffmpeg config |
| `FFmpegExecution` | ffmpeg command executor |

### Utility
| Class | Chức năng |
|---|---|
| `GlowUserDefaults` | NSUserDefaults wrapper |
| `ArchDetect` | CPU architecture detection |
| `AtomicLong` | Thread-safe counter |
| `CallbackData` | Callback data wrapper |
| `Statistics` | Usage statistics |

---

## Hook mechanism
- **Runtime class resolution**: `NSClassFromString` (class names built dynamically, NOT stored as strings)
- **Runtime selector resolution**: `NSSelectorFromString`
- **Hook method**: `MSHookMessageEx` (Substrate)
- **Dynamic method addition**: `class_addMethod`, `class_addProperty`

## Known hooks (extracted from strings)

### Still valid on FB 560.x
| Selector | Target class (dynamically resolved) | Purpose |
|---|---|---|
| `initWithFBTree:` | `FBMemFeedStory`, `FBVideoChannelPlaylistItem` | Return nil for ads |
| `initWithFBPandoTree:` | Various FBMem* model classes | Return nil for unwanted items |
| `advanceToNextItemWithNavigationAction:` | Story player | Disable auto-advance |
| `tabbarHeightDidChange:` | Tab bar | Override tab bar |
| `handleLongPress:` | Self (gesture recognizer) | Open settings |

### Removed in FB 560.x (keep for compatibility, silently fail)
| Selector | Target class | Purpose |
|---|---|---|
| `_markThreadAsSeen:bucket:session:shouldMarkThreadSeenStateUpdates:` | `FBSnacks*SeenState*` (old) | Mark story seen |
| `_canMarkStoryAsSeen` | Story seen manager (old) | Check if can mark seen |
| `markThreadAsSeen:` | Story seen manager (old) | Mark thread seen |

### NEW hooks for FB 560.x seen fix (add alongside old ones)
| Selector | Target class | Purpose |
|---|---|---|
| `FBSnacksUnifiedSeenStateMutator` | ❌ **KHÔNG TỒN TẠI** trong FB 560.x | — |
| `shouldDeferSeenStateUpdates` | `FBStoryInlineViewerConfiguration` | Property — defer seen updates |
| `FBShortsSeenStateComponentFragmentUpdater` | GraphQL protocol | Seen state updater |
| `FBReuseNuxMarkSeenMutation` | GraphQL mutation | Mark seen |
| `FBInspirationVODPrivacyMergeToastHasSeenMutationMutation` | GraphQL mutation | Has seen mutation |
| `seenState` / `setSeenState:` | Various | Seen state property |
| `FBShortsSeenStateComponentFragment` | GraphQL fragment | Seen state data |

---

## FB 560.x IPA Analysis (16 May 2026) — GitHub Actions + Tailscale Funnel

### Binary info
- **File**: `Facebook` (16MB Mach-O 64-bit ARM64, decrypted)
- **Binary name**: `Facebook` (NOT `Facebook.app/Facebook.app`)
- **Architecture**: ARM64, MH_MAGIC_64, PIE, DYLDLINK
- **Load commands**: 108, LC_SEGMENT_64, LC_DYLD_CHAINED_FIXUPS

### Key findings from strings analysis (2MB strings_all.txt)
| Category | Result |
|---|---|
| `FBSnacksUnifiedSeenStateMutator` | ❌ **KHÔNG TỒN TẠI** — class đã bị xoá hoàn toàn |
| `FBShortsSeenStateComponentFragmentUpdater` | ✅ GraphQL protocol — `asFBShortsSeenStateComponent`, `setSeenState:` |
| `FBReuseNuxMarkSeenMutation` | ✅ GraphQL mutation — `FBReuseNuxMarkSeenMutationBuilder` |
| `FBInspirationVODPrivacyMergeToastHasSeenMutationMutation` | ✅ GraphQL mutation |
| `shouldDeferSeenStateUpdates` | ✅ Property trong `FBStoryInlineViewerConfiguration` |
| `seenState` / `setSeenState:` | ✅ Property trên nhiều class |
| `FBSnacksStoryViewerWindowingEventsListener` | ✅ Story viewer class (thay cho `FBStoryViewerController`) |
| `didTapLike:` | ✅ Like selector (thay cho `performLikeAction:`) |
| `videoURLString`, `playableURLString`, `hdPlayableURLString`, `dashPlayableURL` | ✅ Video URL properties |
| `FBMemFeedStory`, `FBMemStory`, `FBMemVideo` | ✅ Vẫn tồn tại — initWithFBTree:, initWithFBPandoTree: |

### Video URL extraction keys (confirmed from binary strings)
```
videoURLString
playableURLString
hdPlayableURLString
dashPlayableURL
playableURL
mediaURLString
```

### Like mechanism (confirmed from binary strings)
```
-[FBFunFactMultiPlayerLikeButtonComponentController didTapLike:]
asPerformLikeMutationForFunFactPlayerUFI_feedbackFragment
asFBCommentLikeActionGraphQL
```

### Story viewer (confirmed from binary strings)
```
FBSnacksStoryViewerWindowingEventsListener
FBSnacksStandardFooterData (struct with threadID, mediaID, unseenStoryViewers, etc.)
```

---

## Feature set (from Localizable.strings)

| Feature key | UI text | Implementation |
|---|---|---|
| `AnonymousStories` | "Incognito Mode" / "Stay unseen. View stories and mark them as seen only when you want to." | Hook seen mutator |
| `RemoveAds` | "Remove recommendations" / "Removes recommended posts. NOTE: Needs enough follows to work properly" | Return nil in initWithFBTree: |
| `RemovePYMK` | "Remove People you may know" | Filter in initWithFBTree:/PandoTree: |
| `RemoveRecs` | "Remove recommendations" | Filter in model init |
| `RemoveReelsCarousel` | "Remove reels carousel" | Hook + filter |
| `DownloadVideos` | "Download videos from feed and stories with a long press" | Media extraction + download |
| `DownloadStories` | "Download story" | Media extraction + download |
| `DownloadReels` | "Download reels" | Media extraction + download |
| `DownloadVideo` | "Download video" | Media extraction + download |
| `DownloadingAudio` | "Downloading audio" | Audio extraction |
| `PostLikeConfirm` | "Confirm post like" / "Worried about accidental likes?" | Hook like action |
| `ReelsLikeConfirm` | "Confirm reels like" | Hook reel like action |
| `DisableAutoNext` | "Disable auto advancing" | Hook advanceToNextItemWithNavigationAction: |
| `AutoClearCache` | "Clear cache on startup" | NSURLCache removal |
| `EncodingSpeed` | "Encoding speed" (Ultrafast/Fast/Medium) | ffmpeg preset |
| `HideOverlay` | "Hide overlay" | Toggle download button visibility |
| `GlowSettings` | "Glow settings are available via long press on any tab" | Settings VC + gesture |

---

## Build approach

### Prerequisites
- Theos (iPhoneOS 12.4 SDK + iPhoneOS 16.5 SDK)
- macOS GA runner (ARM64, Xcode 16.2)
- CydiaSubstrate (auto-injected by cyan)

### Dependencies
- `/Library/Frameworks/CydiaSubstrate.framework` (auto-bundled)
- UIKit, Foundation, AVFoundation, Photos, CoreGraphics, Security, SystemConfiguration

### Single dylib structure
```
Glow.dylib (16MB, ffmpeg statically linked)
├── ObjC hooks (MSHookMessageEx + runtime class resolution)
├── UI code (WelcomeVC, SettingsVC, Toast, Sheet, Gesture)
├── ffmpeg (libavcodec, libavformat, libavutil, libswscale, libx264, libx265, libdav1d)
├── Download manager (NSURLSession + file save)
└── Utilities (UserDefaults, ArchDetect, Statistics)

Glow.bundle
├── Assets.car (icons)
├── Info.plist (NSPrincipalClass = Glow)
└── Localizable.strings (10 languages)
```

### Build command
```bash
make package
# Output: com.dvntm.glow_1.3.1_iphoneos-arm.deb
```

### Inject
```bash
cyan -i facebook.ipa -o glow.ipa -uwef com.dvntm.glow_1.3.1_iphoneos-arm.deb -n "Facebook" -b com.facebook.Facebook -s
```

---

## Build plan (phases)

### Phase 1: Reverse Engineering (DONE ✓)
- [x] Dump class list (otool -ov)
- [x] Extract all selectors (strings)
- [x] Identify hooks (method names + purpose)
- [x] Identify UI classes and their roles
- [x] Determine fb 560.x api changes (seen)

### Phase 2: Core hooks + UI 
1. SettingsViewController + WelcomeVC (with Glow.bundle assets)
2. DVNLongPressGestureRecognizer (long press tab → settings)
3. DVNSheetController / DVNSheetPresenter (bottom sheet)
4. ToastManager / ToastView / ToastWindow
5. GlowUserDefaults wrapper
6. Substrate hooks: initWithFBTree:, initWithFBPandoTree:, advanceToNextItemWithNavigationAction:
7. ~~Seen fix (FB 560.x): _attemptSendSeenStateAndHandleResponse:bucket:~~ → **CLASS REMOVED**
8. **NEW Seen fix (FB 560.x)**: Hook GraphQL-based seen mechanism (`FBShortsSeenStateComponentFragmentUpdater`, `shouldDeferSeenStateUpdates`)
9. Old seen hooks (keep as-is): _markThreadAsSeen:..., _canMarkStoryAsSeen, markThreadAsSeen:
10. Story viewer: `FBSnacksStoryViewerWindowingEventsListener` (NOT `FBStoryViewerController`)
11. Like: `didTapLike:` (NOT `performLikeAction:`)

### Phase 3: Download
1. Downloader / DownloaderHelper (NSURLSession)
2. MPDParser (DASH manifest)
3. Media overlay UI (download button on stories/videos)
4. AVAssetExportSession encoding (replace ffmpeg to reduce binary size)

### Phase 4: Remaining features
1. ConfirmLike → hook FB like action
2. Remove PYMK / Reels / Recs → filter in model init hooks
3. AutoClearCache → NSURLCache on startup
4. Statistics tracking

---

## Technical notes

### macOS GA runner architecture
- `macos-latest` → Apple Silicon (ARM64) as of 2025+
- Xcode toolchain → produces `LC_DYLD_CHAINED_FIXUPS` (required for iOS 16)
- Linux toolchain (`ld64.lld`) → produces `LC_DYLD_INFO_ONLY` (crashes on iOS 16+)

### Cyan injection notes
- Single `-f` for single dylib (2 dylibs cause crash due to LC_LOAD_DYLIB corruption)
- Flags: `-u -w -e -s` (remove UISupportedDevices, remove watch, remove extensions, fakesign)
- Use `--overwrite` flag

### Glow's classless layout
Most Glow classes have `baseMethods count 0` — methods are added dynamically via `class_addMethod` at runtime, NOT compiled as static ObjC method lists.

---

## FB 560.x IPA Analysis via GitHub Actions (16 May 2026)

### Approach
- **Tailscale Funnel**: Expose `facebook.ipa` (184MB) qua `https://tmy-tuf-1.danio-map.ts.net/`
- **GitHub Actions workflow** (`.github/workflows/analyze-fb-ipa.yml`): Download IPA → extract → class-dump/strings/otool/nm
- **Trigger**: `push` event on `qwen-glow` branch (workflow_dispatch cần default branch)

### Workflow steps
1. Download IPA từ Funnel URL (curl -L)
2. Unzip → find executable binary (NOT assume $APP_DIR/$APP_DIR)
3. otool -hv, -L, -l (architecture, linked libs, load commands)
4. class-dump -H (ObjC headers)
5. strings + grep (seen, video, like, story, FBMem classes)
6. nm -g, -u (global/undefined symbols)
7. Upload artifacts

### Key learnings
- Binary name: `Facebook` (16MB), NOT `Facebook.app/Facebook.app`
- `FBSnacksUnifiedSeenStateMutator` KHÔNG tồn tại → cần GraphQL-based approach
- Video URL properties: `videoURLString`, `playableURLString`, `hdPlayableURLString`, `dashPlayableURL`
- Like selector: `didTapLike:` (NOT `performLikeAction:`)
- Story viewer: `FBSnacksStoryViewerWindowingEventsListener` (NOT `FBStoryViewerController`)
- FBMem classes vẫn tồn tại: `FBMemFeedStory`, `FBMemStory`, `FBMemVideo`

### CRITICAL: Hook mechanism must be 100% runtime-resolved
- **DO NOT use `%hook FBMemFeedStory`** — compile-time hooks crash because FB classes are in frameworks not linked at dylib load time
- **USE `NSClassFromString` + `MSHookMessageEx`** — exactly like original Glow.dylib
- Original Glow resolves ALL FB class names dynamically at runtime via `NSClassFromString`
- Selectors resolved via `NSSelectorFromString` or `@selector()`
- Hooks applied via `MSHookMessageEx` with `imp_implementationWithBlock`
- **Lesson learned**: Build 1 crash ngay sau login do `%hook` compile-time trên class FB không linked → fix bằng runtime hooks

### Build artifacts
- `glowfb.dylib`: 128KB (down from 16.8MB original — replaced ffmpeg with AVAssetExportSession)
- `com.dvntm.glow_1.3.1_iphoneos-arm.deb`: 552KB
- IPA: 176MB (injected via cyan)

---

## Current Process: Frida Debug (16 May 2026)

### Goal
Log exactly which FB classes + selectors the **original** Glow.dylib hooks at runtime on FB 560.x, by using Frida spawn mode to intercept `MSHookMessageEx`, `NSClassFromString`, `NSSelectorFromString`, `class_addMethod`.

### Why not static analysis
- Glow resolves FB class names via `NSClassFromString` (NOT stored in `_OBJC_CLASS_$_FB*` symbols)
- Selectors via `NSSelectorFromString`
- Hooks via `MSHookMessageEx` at runtime
- Can't know exact target classes without runtime observation

### Setup
| File | Source | Purpose |
|---|---|---|
| `facebook.ipa` | Decrypted FB 560.x (184MB) | Base app |
| `Glow.dylib` | `glow.deb` (dayanch96) | Original tweak |
| `Glow.bundle` | `glow.deb` | Tweak assets |
| `FridaGadget.dylib` | GitHub releases v17.9.10 (38.7MB) | Backup hook method |
| `glow_log.js` | Written manually (4.5KB) | Frida logging script |

### How to build debug IPA
```bash
# 1. Inject original Glow.dylib
cyan -i facebook.ipa -o glow_orig_debug.ipa -u -w -e \
    -f Glow.dylib \
    -n "Facebook6" -b "com.facebook.Facebook6" -s --overwrite

# 2. Manually add Glow.bundle (cyan doesn't bundle .bundle)
unzip glow_orig_debug.ipa -d /tmp/glow_patch
cp -r Glow.bundle /tmp/glow_patch/Payload/Facebook.app/
cd /tmp/glow_patch && zip -qr ~/test/glow/glow_orig_debug.ipa Payload
```

### Frida logging script (glow_log.js)
Hooks 7 functions:
1. `MSHookMessageEx` → log exact class + selector Glow hooks
2. `class_addMethod` → detect dynamic methods Glow adds to FB classes
3. `class_addProperty` → detect dynamic properties
4. `NSClassFromString` → log which FB class names Glow resolves at runtime
5. `NSSelectorFromString` → log which selectors Glow builds
6. `method_setImplementation` → detect swizzling
7. `dlopen` → confirm when Glow.dylib loads

Output: `/tmp/glow_hooks_*.txt` on iPhone

### Running on Windows (user has frida)
```cmd
:: Option A: Spawn mode (preferred)
frida -U -f com.facebook.Facebook6 -l glow_log.js --no-pause

:: Option B: FridaGadget mode (if spawn fails)
:: Inject FridaGadget.dylib into IPA → AltStore sign → install
frida -U -n Facebook6 -l glow_log.js
```

### What we expect to learn
- ✅ Which hooks from Glow still fire on FB 560.x (e.g. `initWithFBTree:`, `advanceToNextItemWithNavigationAction:`)
- ❌ Which old seen hooks silently fail (`_markThreadAsSeen:`, `_canMarkStoryAsSeen`, `markThreadAsSeen:`)
- ❓ If Glow already has FB 560.x compatible seen hooks (`_attemptSendSeenStateAndHandleResponse:bucket:`)
- All dynamic FB class lookups Glow performs at init

### Next steps after Frida results
1. Map exact hook → class → selector pairs from Frida output
2. Compare with our clone's Tweak.xm hooks
3. Fix mismatches in class names, selectors, or hook approach
4. Build new clone via GA, test non-crash first
5. Then add download + remaining features
