Skip to content

Commit b870996

Browse files
author
Your Name
committed
Add conditional breakpoint support
1 parent 31f9dc4 commit b870996

13 files changed

Lines changed: 525 additions & 172 deletions

File tree

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.5.3",
3+
"version": "1.6.0",
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: 134 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ export class Bp {
3939
private _addr: Var | null;
4040
private _length: number;
4141
private _depth: number;
42+
private _conditional: boolean;
4243

43-
private _lines: string[] = [];
44+
private _commands: string[] = [];
45+
private _conditions: string[] = [];
4446
private _listener: InvocationListener | null;
4547
private _overlay: string | null = null;
4648
private _trace: Trace | null = null;
@@ -52,13 +54,15 @@ export class Bp {
5254
addr: Var | null,
5355
length: number = 0,
5456
depth: number = 0,
57+
conditional: boolean = false,
5558
) {
5659
this._type = type;
5760
this._idx = idx;
5861
this._hits = hits;
5962
this._addr = addr;
6063
this._length = length;
6164
this._depth = depth;
65+
this._conditional = conditional;
6266
this._listener = null;
6367
}
6468

@@ -264,39 +268,81 @@ export class Bp {
264268
retVal: InvocationReturnValue | null = null,
265269
) {
266270
if (this._hits === 0) return;
267-
else if (this._hits > 0) this._hits--;
268-
Output.clearLine();
269-
Output.writeln(Output.yellow('-'.repeat(80)));
270-
Output.writeln(
271-
[
272-
`${Output.yellow('|')} Break`,
273-
Output.green(`#${this._idx}`),
274-
`[${this._type}]`,
275-
Output.yellow(this.literal),
276-
`@ $pc=${Output.blue(Format.toHexString(ctx.pc))}`,
277-
`$tid=${threadId}`,
278-
].join(' '),
279-
);
280-
Output.writeln(Output.yellow('-'.repeat(80)));
271+
281272
Regs.setThreadId(threadId);
282273
Regs.setContext(ctx);
283274
Regs.setReturnAddress(returnAddress);
284-
285275
if (retVal !== null) Regs.setRetVal(retVal);
286276

287-
this.runCommands();
277+
try {
278+
if (this.runConditions()) {
279+
if (this._hits > 0) this._hits--;
280+
Output.clearLine();
281+
Output.writeln(Output.yellow('-'.repeat(80)));
282+
Output.writeln(
283+
[
284+
`${Output.yellow('|')} Break`,
285+
Output.green(`#${this._idx}`),
286+
`[${this._type}]`,
287+
Output.yellow(this.literal),
288+
`@ $pc=${Output.blue(Format.toHexString(ctx.pc))}`,
289+
`$tid=${threadId}`,
290+
].join(' '),
291+
);
292+
Output.writeln(Output.yellow('-'.repeat(80)));
293+
this.runCommands();
294+
}
295+
} finally {
296+
Regs.clear();
297+
}
298+
}
299+
300+
private runConditions(): boolean {
301+
if (!this._conditional) return true;
302+
if (this._conditions.length === 0) return true;
303+
304+
if (!Output.getDebugging()) {
305+
Output.suppress(true);
306+
}
307+
Input.suppressIntercept(true);
308+
Output.setIndent(true);
309+
Output.writeln();
310+
try {
311+
for (const condition of this._conditions) {
312+
if (condition.length === 0) continue;
313+
if (condition.charAt(0) === '#') continue;
314+
Output.writeln(`${Output.bold(Input.PROMPT)}${condition}`);
315+
const parser = new Parser(condition.toString());
316+
const tokens = parser.tokenize();
317+
const ret = Command.runSync(tokens);
318+
Vars.setRet(ret);
319+
Output.writeRet();
320+
Output.writeln();
321+
}
322+
} finally {
323+
Output.setIndent(false);
324+
Input.suppressIntercept(false);
325+
Input.prompt();
326+
Output.suppress(false);
327+
}
328+
329+
if (Vars.getRet().compare(Var.ZERO) === 0) {
330+
return false;
331+
} else {
332+
return true;
333+
}
288334
}
289335

290336
private runCommands() {
291337
Input.suppressIntercept(true);
292338
Output.setIndent(true);
293339
Output.writeln();
294340
try {
295-
for (const line of this._lines) {
296-
if (line.length === 0) continue;
297-
if (line.charAt(0) === '#') continue;
298-
Output.writeln(`${Output.bold(Input.PROMPT)}${line}`);
299-
const parser = new Parser(line.toString());
341+
for (const command of this._commands) {
342+
if (command.length === 0) continue;
343+
if (command.charAt(0) === '#') continue;
344+
Output.writeln(`${Output.bold(Input.PROMPT)}${command}`);
345+
const parser = new Parser(command.toString());
300346
const tokens = parser.tokenize();
301347
const ret = Command.runSync(tokens);
302348
Vars.setRet(ret);
@@ -315,7 +361,6 @@ export class Bp {
315361
Input.suppressIntercept(false);
316362
Output.writeln(Output.yellow('-'.repeat(80)));
317363
Input.prompt();
318-
Regs.clear();
319364
}
320365
}
321366

@@ -352,25 +397,33 @@ export class Bp {
352397
}
353398

354399
if (this._hits === 0) return;
355-
else if (this._hits > 0) this._hits--;
356400

357-
Output.clearLine();
358-
Output.writeln(Output.yellow('-'.repeat(80)));
359-
Output.writeln(
360-
[
361-
`${Output.yellow('|')} Break`,
362-
Output.green(`#${this._idx}`),
363-
`[${this._type}]`,
364-
Output.yellow(this.literal),
365-
`@ $pc=${Output.blue(Format.toHexString(details.from))}`,
366-
`$addr=${Output.blue(Format.toHexString(details.address))}`,
367-
].join(' '),
368-
);
369-
Output.writeln(Output.yellow('-'.repeat(80)));
370401
Regs.setAddress(details.address);
371402
Regs.setPc(details.from);
372403

373-
this.runCommands();
404+
try {
405+
if (this.runConditions()) {
406+
if (this._hits > 0) this._hits--;
407+
408+
Output.clearLine();
409+
Output.writeln(Output.yellow('-'.repeat(80)));
410+
Output.writeln(
411+
[
412+
`${Output.yellow('|')} Break`,
413+
Output.green(`#${this._idx}`),
414+
`[${this._type}]`,
415+
Output.yellow(this.literal),
416+
`@ $pc=${Output.blue(Format.toHexString(details.from))}`,
417+
`$addr=${Output.blue(Format.toHexString(details.address))}`,
418+
].join(' '),
419+
);
420+
Output.writeln(Output.yellow('-'.repeat(80)));
421+
422+
this.runCommands();
423+
}
424+
} finally {
425+
Regs.clear();
426+
}
374427
}
375428

376429
public overlaps(
@@ -400,17 +453,35 @@ export class Bp {
400453
const addString = `@ $pc=${Output.blue(this.addrString)}`;
401454
const hitsString = `[hits:${this.hitsString}]`;
402455
const lengthString = this.lengthString;
456+
const conditionalString = Output.blue(
457+
this._conditional ? 'conditional' : 'unconditional',
458+
);
403459
const header = [
404460
idxString,
405461
typeString,
406462
literalString,
407463
addString,
408464
hitsString,
409465
lengthString,
466+
conditionalString,
410467
].join(' ');
411468

412-
const lines = this._lines.map(l => ` - ${Output.yellow(l)}`);
413-
lines.unshift(header);
469+
const lines = [header];
470+
471+
if (this._conditional && this._conditions.length !== 0) {
472+
lines.push(Output.green('Conditions:'));
473+
this._conditions.forEach(c => {
474+
lines.push(` - ${Output.yellow(c)}`);
475+
});
476+
}
477+
478+
if (this._commands.length !== 0) {
479+
lines.push(Output.green('Commands:'));
480+
this._commands.forEach(c => {
481+
lines.push(` - ${Output.yellow(c)}`);
482+
});
483+
}
484+
414485
return `${lines.join('\n')}\n`;
415486
}
416487

@@ -464,6 +535,18 @@ export class Bp {
464535
return this._hits;
465536
}
466537

538+
public get conditional(): boolean {
539+
return this._conditional;
540+
}
541+
542+
public get conditions(): string[] {
543+
return this._conditions;
544+
}
545+
546+
public get commands(): string[] {
547+
return this._commands;
548+
}
549+
467550
public set address(addr: Var | null) {
468551
if (addr === null) return;
469552
this._addr = addr;
@@ -479,11 +562,20 @@ export class Bp {
479562
this._depth = depth;
480563
}
481564

565+
public set conditional(conditional: boolean | null) {
566+
if (conditional === null) return;
567+
this._conditional = conditional;
568+
}
569+
482570
public set hits(hits: number) {
483571
this._hits = hits;
484572
}
485573

486-
public set lines(lines: string[]) {
487-
this._lines = lines;
574+
public set commands(commands: string[]) {
575+
this._commands = commands;
576+
}
577+
578+
public set conditions(conditions: string[]) {
579+
this._conditions = conditions;
488580
}
489581
}

src/breakpoints/bps.ts

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { Var } from '../vars/var.js';
33

44
export class Bps {
55
private static byIndex: Map<string, Bp> = new Map<string, Bp>();
6-
private static last: Bp | null = null;
7-
private static lines: string[] = [];
86

97
private constructor() {}
108

@@ -14,6 +12,7 @@ export class Bps {
1412
addr: Var | null = null,
1513
length: number = 0,
1614
depth: number = 0,
15+
conditional: boolean = false,
1716
): Bp {
1817
const idx = this.getNextFreeIndex(type);
1918
const key = this.buildKey(type, idx);
@@ -22,10 +21,8 @@ export class Bps {
2221
this.checkOverlaps(type, addr.toPointer(), length);
2322
}
2423

25-
const bp = new Bp(type, idx, hits, addr, length, depth);
24+
const bp = new Bp(type, idx, hits, addr, length, depth, conditional);
2625
this.byIndex.set(key, bp);
27-
this.last = bp;
28-
this.lines = [];
2926
return bp;
3027
}
3128

@@ -71,6 +68,7 @@ export class Bps {
7168
addr: Var | null = null,
7269
length: number = 0,
7370
depth: number = 0,
71+
conditional: boolean = false,
7472
): Bp {
7573
const key = this.buildKey(type, idx);
7674

@@ -88,8 +86,7 @@ export class Bps {
8886
bp.address = addr;
8987
bp.length = length;
9088
bp.depth = depth;
91-
this.last = bp;
92-
this.lines = [];
89+
bp.conditional = conditional;
9390
return bp;
9491
}
9592

@@ -110,34 +107,6 @@ export class Bps {
110107
return bp;
111108
}
112109

113-
public static addCommandLine(line: string) {
114-
this.lines.push(line);
115-
}
116-
117-
private static exitEdit(callback: (bp: Bp) => void) {
118-
if (this.last === null) throw new Error('no breakpoint to modify');
119-
callback(this.last);
120-
this.last.enable();
121-
this.last = null;
122-
this.lines = [];
123-
}
124-
125-
public static done() {
126-
this.exitEdit(bp => {
127-
bp.lines = this.lines;
128-
});
129-
}
130-
131-
public static clear() {
132-
this.exitEdit(bp => {
133-
bp.lines = [];
134-
});
135-
}
136-
137-
public static abort() {
138-
this.exitEdit(_bp => {});
139-
}
140-
141110
public static all(): Bp[] {
142111
const items: Bp[] = Array.from(this.byIndex.values()).sort((b1, b2) =>
143112
b1.compare(b2),

0 commit comments

Comments
 (0)