Skip to content

Commit 9fe6b6a

Browse files
author
Your Name
committed
Improved tracing support
1 parent 234feda commit 9fe6b6a

File tree

9 files changed

+395
-165
lines changed

9 files changed

+395
-165
lines changed

assets/target/target.c

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,16 @@ void my_b(uint i)
9393

9494
void my_a(uint i)
9595
{
96-
if ((i % 2) == 0)
97-
{
98-
my_b(1);
99-
}
100-
else
96+
for (uint n = 0; n < 3; n++)
10197
{
102-
my_b(2);
98+
if ((i % 2) == 0)
99+
{
100+
my_b(1);
101+
}
102+
else
103+
{
104+
my_b(2);
105+
}
103106
}
104107
}
105108

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "frida-cshell",
3-
"version": "1.2.0",
3+
"version": "1.2.1",
44
"description": "Frida's CShell",
55
"scripts": {
66
"prepare": "npm run build && npm run version && npm run package && npm run copy",

src/breakpoints/bp.ts

Lines changed: 51 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import { Overlay } from '../memory/overlay.js';
66
import { Parser } from '../io/parser.js';
77
import { Regs } from './regs.js';
88
import { Format } from '../misc/format.js';
9+
import { BlockTrace } from '../traces/block.js';
10+
import { Trace, Traces } from '../traces/trace.js';
911
import { Var } from '../vars/var.js';
1012
import { Vars } from '../vars/vars.js';
13+
import { CallTrace } from '../traces/call.js';
1114

1215
export const BP_LENGTH: number = 16;
1316

@@ -20,7 +23,9 @@ export enum BpType {
2023
Instruction = 'instruction',
2124
FunctionEntry = 'function entry',
2225
FunctionExit = 'function exit',
23-
FunctionTrace = 'function trace',
26+
BlockTrace = 'block trace',
27+
CallTrace = 'call trace',
28+
UniqueBlockTrace = 'unique block trace',
2429
MemoryRead = 'memory read',
2530
MemoryWrite = 'memory write',
2631
}
@@ -38,7 +43,7 @@ export class Bp {
3843
private _lines: string[] = [];
3944
private _listener: InvocationListener | null;
4045
private _overlay: string | null = null;
41-
private _trace: ArrayBuffer = new ArrayBuffer(0);
46+
private _trace: Trace | null = null;
4247

4348
public constructor(
4449
type: BpType,
@@ -79,7 +84,9 @@ export class Bp {
7984
case BpType.Instruction:
8085
case BpType.FunctionEntry:
8186
case BpType.FunctionExit:
82-
case BpType.FunctionTrace:
87+
case BpType.BlockTrace:
88+
case BpType.CallTrace:
89+
case BpType.UniqueBlockTrace:
8390
return BpKind.Code;
8491
case BpType.MemoryRead:
8592
case BpType.MemoryWrite:
@@ -121,100 +128,53 @@ export class Bp {
121128
},
122129
});
123130
break;
124-
case BpType.FunctionTrace:
131+
case BpType.BlockTrace:
125132
this._listener = Interceptor.attach(addr.toPointer(), {
126133
onEnter() {
134+
if (bp._hits === 0) return;
135+
bp._trace = BlockTrace.create(this.threadId, bp._depth);
127136
bp.startCoverage(this.threadId, this.context);
128137
},
129138
onLeave(_retVal) {
139+
if (bp._hits === 0) return;
130140
bp.stopCoverage(this.threadId, this.context);
131141
},
132142
});
143+
break;
144+
case BpType.CallTrace:
145+
this._listener = Interceptor.attach(addr.toPointer(), {
146+
onEnter() {
147+
if (bp._hits === 0) return;
148+
bp._trace = CallTrace.create(this.threadId, bp._depth);
149+
bp.startCoverage(this.threadId, this.context);
150+
},
151+
onLeave(_retVal) {
152+
if (bp._hits === 0) return;
153+
bp.stopCoverage(this.threadId, this.context);
154+
},
155+
});
156+
break;
157+
case BpType.UniqueBlockTrace:
158+
this._listener = Interceptor.attach(addr.toPointer(), {
159+
onEnter() {
160+
if (bp._hits === 0) return;
161+
bp._trace = BlockTrace.create(this.threadId, bp._depth, true);
162+
bp.startCoverage(this.threadId, this.context);
163+
},
164+
onLeave(_retVal) {
165+
if (bp._hits === 0) return;
166+
bp.stopCoverage(this.threadId, this.context);
167+
},
168+
});
169+
break;
170+
default:
171+
throw new Error(`unknown code breakpoint type: ${this._type}`);
133172
}
134173

135174
Interceptor.flush();
136175
}
137176

138-
private displayEvents() {
139-
const events = Stalker.parse(this._trace, {
140-
annotate: true,
141-
stringify: false,
142-
}) as StalkerEventFull[];
143-
144-
let currentDepth = 0;
145-
let first = true;
146-
for (const e of events) {
147-
switch (e.length) {
148-
case 3: {
149-
const [kind, start, _end] = e;
150-
if (currentDepth >= this._depth) break;
151-
if (kind !== 'block') break;
152-
const name = this.getAddressString(start as NativePointer);
153-
if (name === null) break;
154-
if (first) {
155-
currentDepth = 0;
156-
first = false;
157-
}
158-
if (currentDepth > 0) {
159-
Output.write('\t'.repeat(currentDepth));
160-
}
161-
Output.writeln(name);
162-
break;
163-
}
164-
case 4: {
165-
const [kind, _from, _to, _depth] = e;
166-
if (kind === 'call') {
167-
currentDepth += 1;
168-
} else if (kind === 'ret') {
169-
if (currentDepth > 0) {
170-
currentDepth -= 1;
171-
}
172-
}
173-
break;
174-
}
175-
default:
176-
break;
177-
}
178-
}
179-
}
180-
181-
private getAddressString(address: NativePointer): string | null {
182-
const debug = DebugSymbol.fromAddress(address);
183-
if (debug === null || debug.name === null) {
184-
const module = Process.findModuleByAddress(address);
185-
if (module === null) {
186-
return null;
187-
}
188-
189-
const offset = address.sub(module.base);
190-
const prefix = `${module.name}+0x${offset.toString(16)}`;
191-
return `${Output.green(prefix.padEnd(40, '.'))} ${Output.yellow(Format.toHexString(address))}`;
192-
}
193-
194-
const lookup = DebugSymbol.fromName(debug.name);
195-
let offset = ptr(0);
196-
if (lookup !== null && lookup.address.compare(debug.address) < 0) {
197-
offset = debug.address.sub(lookup.address);
198-
}
199-
const OFFSET_MAX = 1024;
200-
const prefix = debug.moduleName === null ? '' : `${debug.moduleName}!`;
201-
202-
let name = `${prefix}${debug.name}`;
203-
if (offset !== ptr(0) || offset.compare(OFFSET_MAX) < 0) {
204-
name = `${prefix}${debug.name}+0x${offset.toString(16)}`;
205-
}
206-
207-
const symbol = `${Output.green(name.padEnd(40, '.'))} ${Output.yellow(Format.toHexString(debug.address))}`;
208-
if (debug.fileName !== null && debug.lineNumber !== null) {
209-
if (debug.fileName.length !== 0 && debug.lineNumber !== 0) {
210-
return `${symbol} ${Output.blue(debug.fileName)}:${Output.blue(debug.lineNumber.toString())}`;
211-
}
212-
}
213-
return symbol;
214-
}
215-
216177
private startCoverage(threadId: ThreadId, ctx: CpuContext) {
217-
if (this._hits === 0) return;
218178
Output.clearLine();
219179
Output.writeln(Output.yellow('-'.repeat(80)));
220180
Output.write(`${Output.yellow('|')} Start Trace `);
@@ -225,36 +185,19 @@ export class Bp {
225185
Output.write(`$tid=${threadId}, depth=${this._depth}`);
226186
Output.writeln();
227187
Output.writeln(Output.yellow('-'.repeat(80)));
228-
229-
this._trace = new ArrayBuffer(0);
230-
231-
Stalker.follow(threadId, {
232-
events: {
233-
call: true,
234-
ret: true,
235-
block: true,
236-
},
237-
onReceive: (events: ArrayBuffer) => {
238-
const newBuffer = new Uint8Array(
239-
this._trace.byteLength + events.byteLength,
240-
);
241-
newBuffer.set(new Uint8Array(this._trace), 0);
242-
newBuffer.set(new Uint8Array(events), this._trace.byteLength);
243-
this._trace = newBuffer.buffer as ArrayBuffer;
244-
},
245-
});
246188
}
247189

248190
private stopCoverage(threadId: ThreadId, ctx: CpuContext) {
249-
if (this._hits === 0) return;
250-
else if (this._hits > 0) this._hits--;
191+
this._hits--;
251192
try {
252-
Stalker.unfollow(threadId);
253-
Stalker.flush();
193+
if (this._trace === null) return;
194+
this._trace.stop();
195+
254196
Output.writeln(Output.blue('-'.repeat(80)));
255-
this.displayEvents();
197+
this._trace.display();
256198
Output.writeln(Output.blue('-'.repeat(80)));
257199
Output.clearLine();
200+
258201
Output.writeln(Output.yellow('-'.repeat(80)));
259202
Output.write(`${Output.yellow('|')} Stop Trace `);
260203
Output.write(`${Output.green(`#${this._idx}`)} `);
@@ -264,6 +207,8 @@ export class Bp {
264207
Output.write(`$tid=${threadId}`);
265208
Output.writeln();
266209
Output.writeln(Output.yellow('-'.repeat(80)));
210+
211+
Traces.delete(threadId);
267212
} finally {
268213
Input.prompt();
269214
Regs.clear();

0 commit comments

Comments
 (0)