Skip to content

Commit

Permalink
Add conditional breakpoint support
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Oct 1, 2024
1 parent 31f9dc4 commit b870996
Show file tree
Hide file tree
Showing 13 changed files with 525 additions and 172 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "frida-cshell",
"version": "1.5.3",
"version": "1.6.0",
"description": "Frida's CShell",
"scripts": {
"prepare": "npm run build && npm run version && npm run package && npm run copy",
Expand Down
176 changes: 134 additions & 42 deletions src/breakpoints/bp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ export class Bp {
private _addr: Var | null;
private _length: number;
private _depth: number;
private _conditional: boolean;

private _lines: string[] = [];
private _commands: string[] = [];
private _conditions: string[] = [];
private _listener: InvocationListener | null;
private _overlay: string | null = null;
private _trace: Trace | null = null;
Expand All @@ -52,13 +54,15 @@ export class Bp {
addr: Var | null,
length: number = 0,
depth: number = 0,
conditional: boolean = false,
) {
this._type = type;
this._idx = idx;
this._hits = hits;
this._addr = addr;
this._length = length;
this._depth = depth;
this._conditional = conditional;
this._listener = null;
}

Expand Down Expand Up @@ -264,39 +268,81 @@ export class Bp {
retVal: InvocationReturnValue | null = null,
) {
if (this._hits === 0) return;
else if (this._hits > 0) this._hits--;
Output.clearLine();
Output.writeln(Output.yellow('-'.repeat(80)));
Output.writeln(
[
`${Output.yellow('|')} Break`,
Output.green(`#${this._idx}`),
`[${this._type}]`,
Output.yellow(this.literal),
`@ $pc=${Output.blue(Format.toHexString(ctx.pc))}`,
`$tid=${threadId}`,
].join(' '),
);
Output.writeln(Output.yellow('-'.repeat(80)));

Regs.setThreadId(threadId);
Regs.setContext(ctx);
Regs.setReturnAddress(returnAddress);

if (retVal !== null) Regs.setRetVal(retVal);

this.runCommands();
try {
if (this.runConditions()) {
if (this._hits > 0) this._hits--;
Output.clearLine();
Output.writeln(Output.yellow('-'.repeat(80)));
Output.writeln(
[
`${Output.yellow('|')} Break`,
Output.green(`#${this._idx}`),
`[${this._type}]`,
Output.yellow(this.literal),
`@ $pc=${Output.blue(Format.toHexString(ctx.pc))}`,
`$tid=${threadId}`,
].join(' '),
);
Output.writeln(Output.yellow('-'.repeat(80)));
this.runCommands();
}
} finally {
Regs.clear();
}
}

private runConditions(): boolean {
if (!this._conditional) return true;
if (this._conditions.length === 0) return true;

if (!Output.getDebugging()) {
Output.suppress(true);
}
Input.suppressIntercept(true);
Output.setIndent(true);
Output.writeln();
try {
for (const condition of this._conditions) {
if (condition.length === 0) continue;
if (condition.charAt(0) === '#') continue;
Output.writeln(`${Output.bold(Input.PROMPT)}${condition}`);
const parser = new Parser(condition.toString());
const tokens = parser.tokenize();
const ret = Command.runSync(tokens);
Vars.setRet(ret);
Output.writeRet();
Output.writeln();
}
} finally {
Output.setIndent(false);
Input.suppressIntercept(false);
Input.prompt();
Output.suppress(false);
}

if (Vars.getRet().compare(Var.ZERO) === 0) {
return false;
} else {
return true;
}
}

private runCommands() {
Input.suppressIntercept(true);
Output.setIndent(true);
Output.writeln();
try {
for (const line of this._lines) {
if (line.length === 0) continue;
if (line.charAt(0) === '#') continue;
Output.writeln(`${Output.bold(Input.PROMPT)}${line}`);
const parser = new Parser(line.toString());
for (const command of this._commands) {
if (command.length === 0) continue;
if (command.charAt(0) === '#') continue;
Output.writeln(`${Output.bold(Input.PROMPT)}${command}`);
const parser = new Parser(command.toString());
const tokens = parser.tokenize();
const ret = Command.runSync(tokens);
Vars.setRet(ret);
Expand All @@ -315,7 +361,6 @@ export class Bp {
Input.suppressIntercept(false);
Output.writeln(Output.yellow('-'.repeat(80)));
Input.prompt();
Regs.clear();
}
}

Expand Down Expand Up @@ -352,25 +397,33 @@ export class Bp {
}

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

Output.clearLine();
Output.writeln(Output.yellow('-'.repeat(80)));
Output.writeln(
[
`${Output.yellow('|')} Break`,
Output.green(`#${this._idx}`),
`[${this._type}]`,
Output.yellow(this.literal),
`@ $pc=${Output.blue(Format.toHexString(details.from))}`,
`$addr=${Output.blue(Format.toHexString(details.address))}`,
].join(' '),
);
Output.writeln(Output.yellow('-'.repeat(80)));
Regs.setAddress(details.address);
Regs.setPc(details.from);

this.runCommands();
try {
if (this.runConditions()) {
if (this._hits > 0) this._hits--;

Output.clearLine();
Output.writeln(Output.yellow('-'.repeat(80)));
Output.writeln(
[
`${Output.yellow('|')} Break`,
Output.green(`#${this._idx}`),
`[${this._type}]`,
Output.yellow(this.literal),
`@ $pc=${Output.blue(Format.toHexString(details.from))}`,
`$addr=${Output.blue(Format.toHexString(details.address))}`,
].join(' '),
);
Output.writeln(Output.yellow('-'.repeat(80)));

this.runCommands();
}
} finally {
Regs.clear();
}
}

public overlaps(
Expand Down Expand Up @@ -400,17 +453,35 @@ export class Bp {
const addString = `@ $pc=${Output.blue(this.addrString)}`;
const hitsString = `[hits:${this.hitsString}]`;
const lengthString = this.lengthString;
const conditionalString = Output.blue(
this._conditional ? 'conditional' : 'unconditional',
);
const header = [
idxString,
typeString,
literalString,
addString,
hitsString,
lengthString,
conditionalString,
].join(' ');

const lines = this._lines.map(l => ` - ${Output.yellow(l)}`);
lines.unshift(header);
const lines = [header];

if (this._conditional && this._conditions.length !== 0) {
lines.push(Output.green('Conditions:'));
this._conditions.forEach(c => {
lines.push(` - ${Output.yellow(c)}`);
});
}

if (this._commands.length !== 0) {
lines.push(Output.green('Commands:'));
this._commands.forEach(c => {
lines.push(` - ${Output.yellow(c)}`);
});
}

return `${lines.join('\n')}\n`;
}

Expand Down Expand Up @@ -464,6 +535,18 @@ export class Bp {
return this._hits;
}

public get conditional(): boolean {
return this._conditional;
}

public get conditions(): string[] {
return this._conditions;
}

public get commands(): string[] {
return this._commands;
}

public set address(addr: Var | null) {
if (addr === null) return;
this._addr = addr;
Expand All @@ -479,11 +562,20 @@ export class Bp {
this._depth = depth;
}

public set conditional(conditional: boolean | null) {
if (conditional === null) return;
this._conditional = conditional;
}

public set hits(hits: number) {
this._hits = hits;
}

public set lines(lines: string[]) {
this._lines = lines;
public set commands(commands: string[]) {
this._commands = commands;
}

public set conditions(conditions: string[]) {
this._conditions = conditions;
}
}
39 changes: 4 additions & 35 deletions src/breakpoints/bps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { Var } from '../vars/var.js';

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

private constructor() {}

Expand All @@ -14,6 +12,7 @@ export class Bps {
addr: Var | null = null,
length: number = 0,
depth: number = 0,
conditional: boolean = false,
): Bp {
const idx = this.getNextFreeIndex(type);
const key = this.buildKey(type, idx);
Expand All @@ -22,10 +21,8 @@ export class Bps {
this.checkOverlaps(type, addr.toPointer(), length);
}

const bp = new Bp(type, idx, hits, addr, length, depth);
const bp = new Bp(type, idx, hits, addr, length, depth, conditional);
this.byIndex.set(key, bp);
this.last = bp;
this.lines = [];
return bp;
}

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

Expand All @@ -88,8 +86,7 @@ export class Bps {
bp.address = addr;
bp.length = length;
bp.depth = depth;
this.last = bp;
this.lines = [];
bp.conditional = conditional;
return bp;
}

Expand All @@ -110,34 +107,6 @@ export class Bps {
return bp;
}

public static addCommandLine(line: string) {
this.lines.push(line);
}

private static exitEdit(callback: (bp: Bp) => void) {
if (this.last === null) throw new Error('no breakpoint to modify');
callback(this.last);
this.last.enable();
this.last = null;
this.lines = [];
}

public static done() {
this.exitEdit(bp => {
bp.lines = this.lines;
});
}

public static clear() {
this.exitEdit(bp => {
bp.lines = [];
});
}

public static abort() {
this.exitEdit(_bp => {});
}

public static all(): Bp[] {
const items: Bp[] = Array.from(this.byIndex.values()).sort((b1, b2) =>
b1.compare(b2),
Expand Down
Loading

0 comments on commit b870996

Please sign in to comment.