Skip to content

Commit

Permalink
Wire up some more
Browse files Browse the repository at this point in the history
  • Loading branch information
oleavr committed Sep 23, 2024
1 parent dfa8a7a commit f2fa697
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 36 deletions.
8 changes: 5 additions & 3 deletions agents/tracer/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,10 @@ class Agent {
const timestamp = Date.now() - this.started;
const threadId = context.threadId;
const depth = this.updateDepth(threadId, cutPoint);
const caller = context.returnAddress.toString();

const log = (...message: string[]) => {
this.emit([id, timestamp, threadId, depth, message.join(" ")]);
this.emit([id, timestamp, threadId, depth, caller, message.join(" ")]);
};

callback.call(context, log, param, this.traceState);
Expand All @@ -464,7 +465,7 @@ class Agent {
const depth = this.updateDepth(threadId, cutPoint);

const log = (...message: string[]) => {
this.emit([id, timestamp, threadId, depth, message.join(" ")]);
this.emit([id, timestamp, threadId, depth, null, message.join(" ")]);
};

try {
Expand Down Expand Up @@ -981,10 +982,11 @@ interface HandlerResponse {
type HandlerScript = string;

type TraceTargetId = number;
type TraceEvent = [TraceTargetId, Timestamp, ThreadId, Depth, Message];
type TraceEvent = [TraceTargetId, Timestamp, ThreadId, Depth, Caller, Message];

type Timestamp = number;
type Depth = number;
type Caller = string | null;
type Message = string;

type TraceHandler = [TraceEnterHandler, TraceLeaveHandler];
Expand Down
26 changes: 20 additions & 6 deletions apps/tracer/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import "./App.css";
import AddTargetsDialog from "./AddTargetsDialog.tsx";
import DisassemblyView from "./DisassemblyView.tsx";
import DisassemblyView, { type DisassemblyTarget } from "./DisassemblyView.tsx";
import EventView from "./EventView.tsx";
import HandlerEditor from "./HandlerEditor.tsx";
import HandlerList from "./HandlerList.tsx";
Expand Down Expand Up @@ -46,7 +46,7 @@ export default function App() {
commitItems,
} = useModel();
const [selectedTabId, setSelectedTabId] = useState("events");
const [disassemblyAddress, setDisassemblyAddress] = useState<string | null>(null);
const [disassemblyTarget, setDisassemblyTarget] = useState<DisassemblyTarget>();

const connectionError = lostConnection
? <Callout
Expand All @@ -59,12 +59,22 @@ export default function App() {
<EventView
events={events}
highlightedIndex={highlightedEventIndex}
onActivate={selectHandler}
onActivate={(handlerId, eventIndex) => {
selectHandler(handlerId);
setHighlightedEventIndex(eventIndex);
}}
onDeactivate={() => {
setHighlightedEventIndex(null);
}}
onDisassemble={address => {
setSelectedTabId("disassembly");
setDisassemblyTarget({ type: "instruction", address });
}}
/>
);

const disassemblyView = (
<DisassemblyView address={disassemblyAddress} />
<DisassemblyView target={disassemblyTarget} />
);

return (
Expand Down Expand Up @@ -106,10 +116,14 @@ export default function App() {
</Button>
<Button
icon="code"
disabled={lostConnection || selectedHandler === null}
disabled={lostConnection || selectedHandler === null || selectedHandler.address === null}
onClick={() => {
setSelectedTabId("disassembly");
setDisassemblyAddress(selectedHandler!.address);
setDisassemblyTarget({
type: "function",
name: selectedHandler!.display_name,
address: selectedHandler!.address!
});
}}
>
Disassemble
Expand Down
36 changes: 28 additions & 8 deletions apps/tracer/src/DisassemblyView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,47 @@ import { Spinner } from "@blueprintjs/core";
import { useEffect, useState } from "react";

export interface DisassemblyViewProps {
address?: string | null;
name?: string | null;
target?: DisassemblyTarget;
}

export default function DisassemblyView({ address = null, name = null }: DisassemblyViewProps = {}) {
export type DisassemblyTarget = FunctionTarget | InstructionTarget;

export interface FunctionTarget {
type: "function";
name: string;
address: string;
}

export interface InstructionTarget {
type: "instruction";
address: string;
}

export default function DisassemblyView({ target }: DisassemblyViewProps = {}) {
const [r2Output, setR2Output] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { executeR2Command } = useR2();

useEffect(() => {
if (address === null) {
if (target === undefined) {
return;
}

let ignore = false;
setIsLoading(true);

const t = target;

async function start() {
const command = (name !== null)
? `af+ ${address} ${name}; s ${address}; pdf`
: `s ${address}; pd`;
const command = (t.type === "function")
? [
`s ${target!.address}`,
`af-`,
`af`,
`afn base64:${btoa(t.name)}`,
`pdf`,
].join("; ")
: `s ${t.address}; pd`;
const result = await executeR2Command(command);
if (!ignore) {
setR2Output(result);
Expand All @@ -37,7 +57,7 @@ export default function DisassemblyView({ address = null, name = null }: Disasse
return () => {
ignore = true;
};
}, [address]);
}, [target]);

if (isLoading) {
return (
Expand Down
43 changes: 38 additions & 5 deletions apps/tracer/src/EventView.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,40 @@
padding: 5px;
overflow: auto;
font-family: monospace;
font-size: 10px;
font-size: 12px;
background-color: #1e1e1e;
color: #e4e4e4;
user-select: text;
}

.event-heading {
margin-left: 65px;
margin-left: 93px;
}

.event-item {
padding-top: 2px;
}

.event-timestamp {
color: #555;
margin-left: 5px;
margin-right: 5px;
vertical-align: top;
vertical-align: middle;
text-align: right;
}

.event-message {
min-height: 0;
padding: 0 5px;
text-align: left;
font-size: 10px;
font-size: 12px;
font-weight: bold;
white-space: pre-line;
}

.event-message:focus {
outline: 0;
}

.event-highlighted {
background-color: #EF6456;
}
Expand All @@ -39,3 +47,28 @@
.event-highlighted .event-message {
color: white !important;
}

.event-item .bp5-card {
color: #1e1e1e;
}

.event-details td:nth-child(1) {
font-weight: bold;
}

.event-details td:nth-child(2) {
padding-left: 6px;
}

.event-details td:nth-child(3) {
padding-left: 10px;
}

.event-details .bp5-button {
padding: 5px 10px;
font-size: 10px;
}

.event-dismiss {
margin-top: 10px;
}
57 changes: 49 additions & 8 deletions apps/tracer/src/EventView.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import "./EventView.css";
import { Event, HandlerId } from "./model.js";
import { Button } from "@blueprintjs/core";
import { Button, Card } from "@blueprintjs/core";
import { useEffect, useRef } from "react";
import { useStayAtBottom } from "react-stay-at-bottom";

export interface EventViewProps {
events: Event[];
highlightedIndex: number | null;
onActivate: ActivateEventHandler;
onActivate: EventActionHandler;
onDeactivate: EventActionHandler;
onDisassemble: EventDisassembleHandler;
}

export type ActivateEventHandler = (id: HandlerId) => void;
export type EventActionHandler = (handlerId: HandlerId, eventIndex: number) => void;
export type EventDisassembleHandler = (address: string) => void;

const NON_BLOCKING_SPACE = "\u00A0";
const INDENT = NON_BLOCKING_SPACE.repeat(3) + "|" + NON_BLOCKING_SPACE;

export default function EventView({ events, highlightedIndex = null, onActivate }: EventViewProps) {
export default function EventView({ events, highlightedIndex = null, onActivate, onDeactivate, onDisassemble }: EventViewProps) {
const containerRef = useRef<HTMLDivElement>(null);
const highlightedRef = useRef<HTMLDivElement>(null);
let lastTid: number | null = null;
Expand All @@ -26,13 +29,22 @@ export default function EventView({ events, highlightedIndex = null, onActivate
});

useEffect(() => {
highlightedRef.current?.scrollIntoView({ block: "center" });
const item = highlightedRef.current;
if (item === null) {
return;
}
const itemRect = item.getBoundingClientRect();
const containerRect = containerRef.current!.getBoundingClientRect();
if (itemRect.top >= containerRect.top && itemRect.bottom <= containerRect.bottom) {
return;
}
item.scrollIntoView({ block: "center" });
}, [highlightedRef, highlightedIndex]);

return (
<div ref={containerRef} className="event-view">
{
events.reduce((result, [targetId, timestamp, threadId, depth, message, style], i) => {
events.reduce((result, [targetId, timestamp, threadId, depth, caller, message, style], i) => {
let timestampStr = timestamp.toString();
const timestampPaddingNeeded = Math.max(6 - timestampStr.length, 0);
for (let i = 0; i !== timestampPaddingNeeded; i++) {
Expand All @@ -51,23 +63,52 @@ export default function EventView({ events, highlightedIndex = null, onActivate
}

const isHighlighted = i === highlightedIndex;
const eventClasses = ["event-item"];
if (isHighlighted) {
eventClasses.push("event-highlighted");
}

result.push(
<div
key={i}
ref={isHighlighted ? highlightedRef : undefined}
className={isHighlighted ? "event-highlighted" : ""}
className={eventClasses.join(" ")}
>
<span className="event-timestamp">{timestampStr} ms</span>
<span className={"event-indent " + colorClass}>{INDENT.repeat(depth)}</span>
<Button
className={"event-message " + colorClass}
minimal={true}
alignText="left"
onClick={() => onActivate(targetId)}
onClick={() => onActivate(targetId, i)}
>
{message}
</Button>
{isHighlighted ? (
<Card className="event-details" interactive={true} compact={true}>
<table>
<tbody>
<tr>
<td>Thread ID</td>
<td>0x{threadId.toString(16)}</td>
<td>
</td>
</tr>
{(caller !== null) ? (
<tr>
<td>Caller</td>
<td>{caller}</td>
<td>
<Button icon="code" onClick={() => onDisassemble(caller)}>Disassemble</Button>
</td>
</tr>
) : null
}
</tbody>
</table>
<Button className="event-dismiss" onClick={() => onDeactivate(targetId, i)}>Dismiss</Button>
</Card>
) : null}
</div>
);

Expand Down
10 changes: 9 additions & 1 deletion apps/tracer/src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,15 @@ export type StagedItemId = number;
export type ScopeName = string;
export type MemberName = string | [string, string];

export type Event = [targetId: HandlerId, timestamp: number, threadId: number, depth: number, message: string, style: string[]];
export type Event = [
targetId: HandlerId,
timestamp: number,
threadId: number,
depth: number,
caller: string | null,
message: string,
style: string[]
];

export interface ProcessDetails {
id: number;
Expand Down
10 changes: 5 additions & 5 deletions frida_tools/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,15 +251,15 @@ def on_trace_error(self, message: str) -> None:

def on_trace_events(self, raw_events) -> None:
events = [
(target_id, timestamp, thread_id, depth, message, self._get_style(thread_id))
for target_id, timestamp, thread_id, depth, message in raw_events
(target_id, timestamp, thread_id, depth, caller, message, self._get_style(thread_id))
for target_id, timestamp, thread_id, depth, caller, message in raw_events
]
self._asyncio_loop.call_soon_threadsafe(
lambda: self._asyncio_loop.create_task(self._broadcast_trace_events(events))
)

no_attributes = Style.RESET_ALL
for target_id, timestamp, thread_id, depth, message, style in events:
for target_id, timestamp, thread_id, depth, caller, message, style in events:
if self._output is not None:
self._output.append(message + "\n")
elif self._quiet:
Expand Down Expand Up @@ -615,8 +615,8 @@ def _on_message(self, message, data, ui) -> None:
def _try_handle_message(self, mtype, params, data, ui) -> False:
if mtype == "events:add":
events = [
(target_id, timestamp, thread_id, depth, message)
for target_id, timestamp, thread_id, depth, message in params["events"]
(target_id, timestamp, thread_id, depth, caller, message)
for target_id, timestamp, thread_id, depth, caller, message in params["events"]
]
ui.on_trace_events(events)
return True
Expand Down

0 comments on commit f2fa697

Please sign in to comment.