From bfaa242c7cda6c277d3c457cc7b790acabff579a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Thu, 17 Oct 2024 14:55:15 +0200 Subject: [PATCH] tracer: Add support for muting handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Håvard Sørbø --- agents/tracer/agent.ts | 16 +++++++-- apps/tracer/src/App.css | 5 ++- apps/tracer/src/App.tsx | 28 ++++++++++----- apps/tracer/src/HandlerList.css | 4 +++ apps/tracer/src/HandlerList.tsx | 3 +- apps/tracer/src/model.ts | 60 +++++++++++++++++++++++++++------ frida_tools/tracer.py | 13 +++++-- 7 files changed, 102 insertions(+), 27 deletions(-) diff --git a/agents/tracer/agent.ts b/agents/tracer/agent.ts index 59bba212..7184b9c8 100644 --- a/agents/tracer/agent.ts +++ b/agents/tracer/agent.ts @@ -528,9 +528,14 @@ class Agent { private invokeNativeHandler(id: TraceTargetId, callback: TraceEnterHandler | TraceLeaveHandler | TraceProbeHandler, config: HandlerConfig, context: InvocationContext, param: any, cutPoint: CutPoint) { - const timestamp = Date.now() - this.started; const threadId = context.threadId; const depth = this.updateDepth(threadId, cutPoint); + + if (config.muted) { + return; + } + + const timestamp = Date.now() - this.started; const caller = context.returnAddress.toString(); const backtrace = config.capture_backtraces ? Thread.backtrace(context.context).map(p => p.toString()) : null; @@ -543,10 +548,15 @@ class Agent { private invokeJavaHandler(id: TraceTargetId, callback: TraceEnterHandler | TraceLeaveHandler, config: HandlerConfig, instance: Java.Wrapper, param: any, cutPoint: CutPoint) { - const timestamp = Date.now() - this.started; const threadId = Process.getCurrentThreadId(); const depth = this.updateDepth(threadId, cutPoint); + if (config.muted) { + return; + } + + const timestamp = Date.now() - this.started; + const log = (...message: string[]) => { this.emit([id, timestamp, threadId, depth, null, null, message.join(" ")]); }; @@ -901,6 +911,7 @@ async function getHandlers(request: HandlerRequest): Promise { function makeDefaultHandlerConfig(): HandlerConfig { return { + muted: false, capture_backtraces: false, }; } @@ -1115,6 +1126,7 @@ interface HandlerResponse { } type HandlerScript = string; interface HandlerConfig { + muted: boolean; capture_backtraces: boolean; } diff --git a/apps/tracer/src/App.css b/apps/tracer/src/App.css index 831bbf88..0e3e97fd 100644 --- a/apps/tracer/src/App.css +++ b/apps/tracer/src/App.css @@ -49,13 +49,16 @@ display: flex; justify-content: space-between; flex: 0; - padding-right: 10px; } .editor-toolbar .bp5-switch { padding-top: 10px; } +.handler-muted input:checked ~ .bp5-control-indicator { + background: #cd4246 !important; +} + .monaco-editor { position: absolute !important; } diff --git a/apps/tracer/src/App.tsx b/apps/tracer/src/App.tsx index bbbddb20..3506a4a8 100644 --- a/apps/tracer/src/App.tsx +++ b/apps/tracer/src/App.tsx @@ -15,7 +15,6 @@ import { Tabs, Tab, } from "@blueprintjs/core"; -import { useRef } from "react"; import { Resplit } from "react-resplit"; export default function App() { @@ -34,6 +33,8 @@ export default function App() { draftedCode, setDraftedCode, deployCode, + handlerMuted, + setHandlerMuted, captureBacktraces, setCaptureBacktraces, @@ -62,7 +63,6 @@ export default function App() { symbolicate, } = useModel(); - const captureBacktracesSwitchRef = useRef(null); const connectionError = lostConnection ? - setCaptureBacktraces(captureBacktracesSwitchRef.current!.checked)} - > - Capture Backtraces - +
+ setHandlerMuted(e.target.checked)} + > + Muted + + setCaptureBacktraces(e.target.checked)} + > + Capture Backtraces + +
h.scope === scope) - .map(({ id, display_name }) => { + .map(({ id, display_name, config }) => { return { id, label: display_name, isSelected: id === selectedHandler, icon: "code-block", + className: config.muted ? "handler-node-muted" : "", }; }), }; diff --git a/apps/tracer/src/model.ts b/apps/tracer/src/model.ts index b48770e8..ba716df0 100644 --- a/apps/tracer/src/model.ts +++ b/apps/tracer/src/model.ts @@ -25,6 +25,7 @@ export function useModel() { const [selectedHandler, setSelectedHandler] = useState(null); const [handlerCode, setHandlerCode] = useState(""); const [draftedCode, setDraftedCode] = useState(""); + const [handlerMuted, _setHandlerMuted] = useState(false); const [captureBacktraces, _setCaptureBacktraces] = useState(false); const [selectedTab, _setSelectedTab] = useState("events"); @@ -95,8 +96,7 @@ export function useModel() { if (id === null) { setSelectedScope(""); setSelectedHandler(null); - setHandlerCode(""); - setDraftedCode(""); + _setHandlerMuted(false); _setCaptureBacktraces(false); return; } @@ -104,25 +104,36 @@ export function useModel() { const handler = handlers.find(h => h.id === id)!; setSelectedScope(handler.scope); setSelectedHandler(handler); - _setCaptureBacktraces(false); + const { config } = handler; + _setHandlerMuted(config.muted); + _setCaptureBacktraces(config.capture_backtraces); + }, [selectedHandlerId, handlers, request]); + + useEffect(() => { + setHandlerCode(""); + setDraftedCode(""); + + const id = selectedHandlerId; + if (id === null) { + return; + } let ignore = false; - async function loadCodeAndConfig() { - const { code, config } = await request("handler:load", { id: id! }); + async function loadCode() { + const { code } = await request("handler:load", { id: id! }); if (!ignore) { setHandlerCode(code); setDraftedCode(code); - _setCaptureBacktraces(config.capture_backtraces); } } - loadCodeAndConfig(); + loadCode(); return () => { ignore = true; }; - }, [selectedHandlerId, handlers, request]); + }, [selectedHandlerId, request]); const deployCode = useCallback(async (code: string) => { setHandlerCode(code); @@ -135,13 +146,21 @@ export function useModel() { pushState({ handler }); }, [pushState]); + const setHandlerMuted = useCallback(async (muted: boolean) => { + setHandlers(updateHandlerConfig(selectedHandlerId!, { muted }, handlers)); + _setHandlerMuted(muted); + await request("handler:configure", { + id: selectedHandlerId!, + parameters: { muted } + }); + }, [request, selectedHandler]); + const setCaptureBacktraces = useCallback(async (enabled: boolean) => { + setHandlers(updateHandlerConfig(selectedHandlerId!, { capture_backtraces: enabled }, handlers)); _setCaptureBacktraces(enabled); await request("handler:configure", { id: selectedHandler!.id, - parameters: { - capture_backtraces: enabled - } + parameters: { capture_backtraces: enabled } }); }, [request, selectedHandler]); @@ -343,6 +362,8 @@ export function useModel() { draftedCode, setDraftedCode, deployCode, + handlerMuted, + setHandlerMuted, captureBacktraces, setCaptureBacktraces, @@ -373,6 +394,21 @@ export function useModel() { }; } +function updateHandlerConfig(id: HandlerId, updates: Partial, handlers: Handler[]): Handler[] { + return handlers.map(h => { + if (h.id === id) { + return { + ...h, + config: { + ...h.config, + ...updates + } + }; + } + return h; + }); +} + type TraceSpec = TraceSpecItem[]; type TraceSpecItem = [TraceSpecOperation, TraceSpecScope, TraceSpecPattern]; type TraceSpecOperation = "include" | "exclude"; @@ -395,12 +431,14 @@ export interface Handler { scope: ScopeId; display_name: string; address: string | null; + config: HandlerConfig; } export type HandlerId = number; export type TargetFlavor = "insn" | "c" | "objc" | "swift" | "java"; export type ScopeId = string; interface HandlerConfig { + muted: boolean; capture_backtraces: boolean; } diff --git a/frida_tools/tracer.py b/frida_tools/tracer.py index 48f5618d..b5ded91e 100644 --- a/frida_tools/tracer.py +++ b/frida_tools/tracer.py @@ -287,7 +287,7 @@ def on_trace_handler_load(self, target: TraceTarget, handler: str, source: Path) self._print(f'{target}: Loaded handler at "{source}"') def _register_handler(self, target: TraceTarget, source: str) -> None: - config = {"capture_backtraces": False} + config = {"muted": False, "capture_backtraces": False} self._handlers[target.identifier] = (target, source, config) def _get_style(self, thread_id): @@ -397,7 +397,7 @@ async def process_messages(self) -> None: "type": "tracer:sync", "spawned_program": app._spawned_argv[0] if app._spawned_argv is not None else None, "process": app._tracer.process, - "handlers": [target.to_json() for target, _, _ in app._handlers.values()], + "handlers": [self._handler_entry_to_json(entry) for entry in app._handlers.values()], } ) @@ -462,7 +462,9 @@ async def _on_targets_commit(self, payload: dict) -> None: await self.post( { "type": "handlers:add", - "handlers": [self.app._handlers[target_id][0].to_json() for target_id in target_ids], + "handlers": [ + self._handler_entry_to_json(self.app._handlers[target_id]) for target_id in target_ids + ], } ) @@ -476,6 +478,11 @@ async def _on_symbols_resolve_addresses(self, payload: dict) -> None: names = self.app._tracer.resolve_addresses(payload["addresses"]) return {"names": names} + @staticmethod + def _handler_entry_to_json(entry: tuple[str, str, str]) -> dict: + target, _source, config = entry + return {**target.to_json(), "config": config} + app = TracerApplication() app.run()