Open through the r2 command so what it opens is mapped: the
disassembler skips whatever falls outside a map, and reading is not
enough. Drop the bin load, which only sniffed formats at address zero.
Let a failed read say so instead of returning null, remember the pages
that have none, and resolve an anchor live or not at all -- a stale
address lands on whatever occupies it now.
Analysis pulled every export, import and symbol across the wire
and then kept the handful that name code -- 87k rows for a
kernel, each carrying a type and an address as text.
Ask for what it uses instead: the offset, name and source of each
function the module knows about, chosen where the rows already
are.
Every lookup built all three categories, gave each row a lowered
key to search by, and sorted each with a callback -- 14 seconds
before the first row of a kernel's 87k exports reached the view,
and most of it for rows nobody asked to see.
Keep what the runtime hands back, put it in order with the
engine's own sort, and report a row only when it goes out. Build
the text to search by when a query first arrives.
The encoder walked a value by calling itself through map(), and a
patch pass walked the tree again the same way. Inside a kernel,
where a script runs on a 16 KiB stack, that ran out after three
levels: each map() level costs a native frame plus two JS frames,
where a plain loop costs one.
Walk from an explicit list of what is left to encode, and let the
byte chunks patch themselves once their offsets are known, so the
second pass is gone.
Drop the Monaco webview. Edit in a GtkSourceView on GTK and in a
native text view on Apple, both speaking to Frida.Compiler's
in-process language server for completions, signature help,
hovers and diagnostics.
Color, complete and indent code natively, matching the GitHub
theme in light and dark.
Serve symbol pages by offset and step through them with prev/next
controls showing the current range. Reset to the first page when
the filter or category changes. Clamp the page index at the
mutation site and floor the offset in the agent so hammering
prev/next can't run past the ends into a negative range.
Bind Cmd+F (Ctrl+F on GTK) to focus the filter input.
Modules like YouTube Kids export 100k+ symbols. Enumerating and
marshalling the whole set per open stalled the UI, and both
frontends re-filtered the full arrays on the main thread on every
keystroke.
Build a cached, pre-sorted, pre-lowercased index in the agent and
serve filtered, capped pages. Add queryModuleSymbols returning a
window plus match and category counts. Wire both frontends to it
with a debounced filter; drop the client-side filtering.
Run each submission through indirect eval so declarations land
on the global object and persist. Rewrite a leading let/const to
var so it survives too, matching the Node REPL.
Have the agent classify each member as callable via its property
descriptor (no getter side effects), then order properties ahead
of functions, alphabetically within each group.
Return no members when the base expression fails to resolve instead
of falling back to the global scope.
Walk the prototype chain so resolved objects surface inherited
members.
Consume a capture index only when a trace is actually recorded,
so raising the limit or re-arming resumes capture instead of
staying stuck at the old invocation count.
Drop the shared-buffer rendezvous. Each trace now owns its
buffer, created before Stalker is armed and announced to Luma
via a token-keyed start/ack handshake. The ack carries the
drain verdict: Luma remaps the buffer out-of-process while the
agent blocks, so an early crash still yields data; on
task_for_pid denial (cached per target) the agent drains and
pushes chunks itself.
Fold all itrace into the tracer instrument. Function-arm traces
and thread traces share one Trace registry; thread traces become
threadTraces config entries on a find-or-created tracer instance.
Core no longer exports itrace, and the drain agent keys its
readers by session so concurrent traces stay separate.
Re-attaching the interceptor on an itrace_arming change relocated
its own live redirect as the original prologue, so the trampoline
dereferenced the hooked function's bytes as a pointer and crashed.
A hook's target never moves via update, so reconcile in place when
code and state are unchanged. Snapshot the pristine prologue at
first attach so arming a live hook still has the original bytes.
Console widgets get an appendImage / respond.image API on the
agent. read_widget_state surfaces image entries as attachments
scaled to at most max_dimension pixels (default 1568) on the
longest edge. Anthropic nests image blocks inside tool_result;
OpenAI gets a synthetic follow-up user message. The host scales
via CoreImage on macOS and GdkPixbuf on GTK.
Identify a process instance by a deterministic identity returned
from the agent (per-OS, with a Process.mainModule + pid fallback)
and stash it on ProcessSession.processInfo. Rekey memory_page
into memory_page_module (module UUID + offset) so cached bytes
survive ASLR, and memory_page_anon (process identity + address)
for JIT / heap regions that only survive within one process
instance. Purge stale anon pages on every fresh attach.
Add updateProcessInfo, updateModuleAnalysis and updateInsight
SessionOps plus a side-channel publish-memory-page op and a
+fetch-memory-page request. The driver write-throughs every
page it fetches; non-drivers pull on miss and persist locally.
Module-analysis rows flow the same way so non-drivers can key
their cache by module UUID. The lab-join snapshot now carries
processInfo and module_analyses too.
Refresh actions on the insight detail view: Reread Bytes drops
the pages overlapping the visible range; Reanalyze Module drops
the module's analysis and pages, re-running r2 on next access.
The macOS detail view now reads the insight live from
insightsBySession via observation; GTK forwards .insightUpdated
into the detail and reschedules a refresh when the resolved
address changes.
Persist per-session module analysis (executable ranges + known
functions, keyed by module name + LC_UUID) so on-demand
analysis runs once per binary and rebases on reattach. The
agent gains enumerateModuleRanges and getModuleIdentity; the
store gains a module_analysis table.
Disassembler queries $FB/$FE for the request address; when it
hits a function start, fetch via pDJ bounded to the function's
byte range. Otherwise fall back to pdJ N. Filter pdJ
decoration entries (xrefs, fcn headers, var summaries) that
share the addr of an instruction.
DisassemblyPage carries a scope (.function/.span) so the
SwiftUI and GTK insight views can disable loadMore when the
page is function-bounded. The disassemble AI tool surfaces the
scope too, so the model knows whether it has the whole
function or just an N-instruction span.
Go-to-Function-Start now opens (or selects) the insight rooted
at the function start, which then renders via the bounded
path.
Hook packs and custom instruments can declare a console widget for
REPL-style input. Adds a WidgetConsoleEntry update kind, per-session
persistence, an agent-side appendConsole/onConsoleInput API, and a
ConsoleView-backed renderer card.
Regroups all widget kinds across the codebase as
counter, histogram, graph, list, table, hex, console
(aggregates, collections, raw, interactive).
Bundle compiler reads Agent/bundle.json describing outputs,
bundles, typings, and externals. Package install is driven by
Agent/package.json + Agent/package-lock.json (managed via
frida-pm), so the lockfile is the source of truth for resolved
versions.
@types/frida-gum and @types/node are embedded into
LumaTypings.swift at build time; the Resources d.ts is no
longer checked in. Editor profiles append both as extra libs.
summarize_recent_events and wait_for_event embed each
event's payload as JSON with $type sentinels for
NativePointer, BigInt, Date, typed arrays, Map, Set, and
friends. Tracer events decode into named fields with
caller and backtrace pre-symbolicated.
Add read_event(event_id) for the full uncapped payload.
SymbolicateResult collapses to one struct with optional
offset and source. Agent symbolicate returns objects with
a numeric offset field.
TracerConfig.Hook.State holds .enabled / .disabled, with
room for future cases (e.g. .muted: installed but no-op).
The mission tool's update_tracer_hook accepts a "state"
string; list/read return state as a string. The agent's
HookConfig wire format matches.
Add write_memory, list_threads, list_session_instruments,
arm_session, disarm_session, list_address_insights, unpin_insight,
detach_session, read_widget_state, and invoke_widget_action.
read_memory grows an explicit format hint (hex/utf8/cstring);
eval_repl's description clarifies that the intent string lands
in the audit log.
Failure paths now emit a machine-readable code alongside the
human message (invalid_input, not_found, unavailable, rejected,
failed) so LLM-driven missions can react without parsing prose.
Table mirrors list with multi-column rows + per-row actions and
configurable column alignment. Counter is a single big value
with optional unit and signed delta. Histogram supports both
setHistogram and incrementBucket and renders as a bar chart.
Hex shows a live binary buffer with a base address, capped to a
trailing window.
Agent SDK gains upsertRow/removeRow/setCounter/setHistogram/
incrementBucket/setHex on ctx.widget(id). Per-def TS narrows
table column ids and action ids the same way list actions are
narrowed; restored snapshots get matching shapes.
Engine owns the widget state cache, applies inbound updates from
the agent, persists session-scoped widgets, and broadcasts to
collaborating peers (action invocations route to the host).
Renderers in both UIs read the cache and the unified update
stream, with a Clear button that propagates to peers.
Agent SDK adds ctx.widget(id) with push/upsertItem/removeItem/
clear, plus a typed onAction handler. create() now takes a
restored snapshot seeded from persisted state and the join
snapshot.
Per-def .d.ts narrows the widget map, restored shape, and action
union to the exact ids declared in the editor.
ITraceArming gains maxBytesPerInvocation (default 1 MB).
The itrace core polls each open session every 100 ms,
streams the drained chunk back via itrace:chunk, and stops
the session as soon as cumulative bytes cross the
threshold. The tracer config UI grows a second stepper for
the byte budget alongside the invocation counter, and
update_tracer_hook accepts max_bytes_per_invocation in
itrace_arming.
Replace TracerConfig.Hook.itraceEnabled (Bool) with
itraceArming (ITraceArming?) carrying maxInvocations.
The agent only opens an itrace session while
callIndex < maxInvocations, so a flooding hook can no
longer flood the trace store. The tracer config UI gets a
stepper for the cap and shows captured/cap live, derived
from saved ITrace rows. update_tracer_hook now takes
itrace_arming as an object {max_invocations} or null to
disarm.
Drop the dual ITraceCaptureRecord/CapturedITrace types in favour of
a single ITrace covering both function-call and thread origins.
Promote itrace-capture from an instrument into a core service so
both origins share the same start/stop/drain machinery.
ProcessNode now keys pending traces by an opaque session id, hands
finalized data to a TraceStore, throttles running emits, drains
in-process when no system-drain is available, and skips the
Interceptor-trampoline trim for thread origin. ModuleDelta carries
incremental module updates.
LumaDocument grows sqliteURL and tracesDirectory accessors so the
project file can be a directory holding both the index and
per-trace blobs.
Add agent-side observers for the live module list and thread list
plus an RPC to enumerate a module's exports/imports/symbols. Mirror
in LumaCore as ProcessThread and ModuleSymbolBundle.
- Agent now says "The 'frida-java-bridge' package is required
for Java tracing." when the module can't be imported, and
"No Java runtime detected in this process." when the bridge
is loaded but the target isn't a JVM. Neither conflates the
two cases.
- Surface the actual compiler diagnostics from Engine.loadPackage
when propagating an installed package fails, so "Compilation
failed" doesn't stand alone in the log.
- Replace the inline error row with a rounded banner carrying a
package icon, caption text, and a bordered Install button.
The GTK side already renders a flat button + label in a row
so string matching is the only update there.
Add a javaMethod anchor kind and the matching java-method scope
so the tracer can target JVM methods. Resolution uses
Java.enumerateMethods through the bridge; attachment replaces
each method's implementation and restores it on detach.
The bridge isn't bundled into our agent, since only one copy
can live in a process at a time. Instead it's marked external
so frida-compile leaves the imports as runtime lookups, and our
agent does `await import("frida-java-bridge")` on demand. If
the user hasn't installed the package via the Packages panel,
we surface a message pointing them there.
To let frida-compile honour the external, LumaBundleCompiler
grew a --external <name> flag that forwards to
BuildOptions.externals. Both the Xcode Run Script phase and the
GTK Makefile now pass --package [email protected] (for
TypeScript types) alongside --external frida-java-bridge (so
the runtime code isn't embedded).
Java resolves to no native address, so ProcessNode.resolve and
the instruction annotation map throw / skip rather than trying
to materialise one. The seed code for new Java hooks matches
frida-tools' pattern: log args via JSON.stringify.
Extend the resolver path so traced targets can be described by more
anchor kinds than just module exports. The agent now exposes a
unified resolveTargets RPC on top of Frida's ApiResolver (module,
objc, swift) plus DebugSymbol.findFunctionsMatching, and a
lookupAnchorAddress entry point that resolves a stored anchor back
to an address.
AddressAnchor gains objcMethod, swiftFunc, and debugSymbol cases
with matching Codable round-tripping. ProcessNode.resolve routes
the new cases through the agent; the sync fast-path only handles
anchors that don't require the target runtime.
Both Add-Hook UIs keep using scope:"function" for now so behaviour
is unchanged; Stage 2 will expose a scope picker. The search-result
rows now carry a pre-resolved AddressAnchor instead of reconstructing
module+export pairs, and subtitle switches are exhaustive over the
new cases.
Lay the groundwork for a GTK/Adwaita frontend by extracting
shared infrastructure into a LumaCore Swift package. Agent
sources and LumaBundleCompiler move to top-level locations
so both the Xcode project and SPM can reference them.
New structure:
- Package.swift: root manifest with LumaCore lib + compiler
- Sources/LumaCore/: engine, persistence, models, and
platform-abstracted token store
- Sources/LumaBundleCompiler/: moved from LumaBundleCompiler/
- Agent/: moved from Luma/Agent/
- LumaGtk/: skeleton GTK app package