Skip to content

Commit

Permalink
Add support for macros
Browse files Browse the repository at this point in the history
  • Loading branch information
Your Name committed Sep 30, 2024
1 parent a1c8472 commit 4b4fb36
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 6 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.1",
"version": "1.5.2",
"description": "Frida's CShell",
"scripts": {
"prepare": "npm run build && npm run version && npm run package && npm run copy",
Expand Down
2 changes: 2 additions & 0 deletions src/cmdlets/development/js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import {
TraceUniqueBlockCmdLet,
TraceCoverageCmdLet,
} from '../trace/trace.js';
import { MacroCmdLet } from '../misc/macro.js';

export class JsCmdLet extends CmdLet {
name = 'js';
Expand Down Expand Up @@ -117,6 +118,7 @@ js path - load commandlet JS script
LdCmdLet: LdCmdLet,
Line: Line,
LogCmdLet: LogCmdLet,
MacroCmdLet: MacroCmdLet,
Mem: Mem,
MemoryBps: MemoryBps,
ModCmdLet: ModCmdLet,
Expand Down
128 changes: 128 additions & 0 deletions src/cmdlets/misc/macro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { CmdLet } from '../../commands/cmdlet.js';
import { Command } from '../../commands/command.js';
import { Input, InputInterceptLine } from '../../io/input.js';
import { Output } from '../../io/output.js';
import { Parser } from '../../io/parser.js';
import { Token } from '../../io/token.js';
import { Macro, Macros } from '../../macros/macros.js';
import { Var } from '../../vars/var.js';
import { Vars } from '../../vars/vars.js';

export class MacroCmdLet extends CmdLet implements InputInterceptLine {
name = 'm';
category = 'misc';
help = 'manage macros';
private static readonly USAGE: string = `Usage: m
m - show all macros
m name - create, modify or display a macro
name the name of the macro
m name ${CmdLet.DELETE_CHAR} - delete a macro
name the name of the macro to delete`;

private current: string | null = null;
private commands: string[] = [];

public runSync(tokens: Token[]): Var {
const retWithNameAndHash = this.runDelete(tokens);
if (retWithNameAndHash !== null) return retWithNameAndHash;

const retWithNameAndPointer = this.runSet(tokens);
if (retWithNameAndPointer !== null) return retWithNameAndPointer;

const retWithName = this.runShow(tokens);
if (retWithName !== null) return retWithName;

return this.usage();
}

private runDelete(tokens: Token[]): Var | null {
const vars = this.transform(tokens, [this.parseLiteral, this.parseDelete]);
if (vars === null) return null;
const [name, _] = vars as [string, string];

const macro = Macros.delete(name);
if (macro === null) {
Output.writeln(`macro ${Output.green(name)} not set`);
} else {
Output.writeln(`deleted macro ${Output.green(name)}`);
}
return Var.ZERO;
}

private runSet(tokens: Token[]): Var | null {
const vars = this.transform(tokens, [this.parseLiteral]);
if (vars === null) return null;
const [name] = vars as [string];
this.commands = [];
this.current = name;
const macro = Macros.get(name);
if (macro === null) {
Output.writeln(`Creating macro '${Output.green(name)}'`);
} else {
Output.writeln(`Modifying macro '${Output.green(name)}'`);
Output.writeln(macro.toString());
}
Input.setInterceptLine(this);
return Var.ZERO;
}

addLine(line: string): void {
this.commands.push(line);
}

clear(): void {
if (this.current != null) {
const macro = new Macro(this.current, []);
Macros.set(macro);
}
}

done(): void {
if (this.current != null) {
const macro = new Macro(this.current, this.commands);
Macros.set(macro);
}
}

abort(): void {}

private runShow(tokens: Token[]): Var | null {
if (tokens.length !== 0) return null;
Output.writeln(Output.blue('Macros:'));
for (const macro of Macros.all()) {
Output.writeln(`${Output.green(macro.name)}:`, true);

Output.writeln(macro.toString(), true);

Output.writeln();
}
return Var.ZERO;
}

public usage(): Var {
Output.writeln(MacroCmdLet.USAGE);
return Var.ZERO;
}

public static run(macro: Macro): Var {
let ret = Var.ZERO;
for (const command of macro.commands) {
if (command.length === 0) continue;
if (command.charAt(0) === '#') continue;

Output.writeln(`${Output.bold(Input.PROMPT)}${command}`);

if (command.trim().length === 0) continue;

const parser = new Parser(command.toString());
const tokens = parser.tokenize();
ret = Command.runSync(tokens);
Vars.setRet(ret);
Output.writeRet();
Output.writeln();
}
return ret;
}
}
2 changes: 2 additions & 0 deletions src/commands/cmdlets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
TraceUniqueBlockCmdLet,
TraceCoverageCmdLet,
} from '../cmdlets/trace/trace.js';
import { MacroCmdLet } from '../cmdlets/misc/macro.js';

export class CmdLets {
private static byName: Map<string, CmdLet> = new Map<string, CmdLet>();
Expand Down Expand Up @@ -89,6 +90,7 @@ export class CmdLets {
this.registerCmdletType(LogCmdLet);
this.registerCmdletType(OrCmdLet);
this.registerCmdletType(ReadCmdLet);
this.registerCmdletType(MacroCmdLet);
this.registerCmdletType(ModCmdLet);
this.registerCmdletType(MulCmdLet);
this.registerCmdletType(NotCmdLet);
Expand Down
25 changes: 22 additions & 3 deletions src/commands/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ import { Format } from '../misc/format.js';
import { Var } from '../vars/var.js';
import { Token } from '../io/token.js';
import { CmdLet } from './cmdlet.js';
import { Macro, Macros } from '../macros/macros.js';
import { MacroCmdLet } from '../cmdlets/misc/macro.js';

export class Command {
private static readonly MACRO_PREFIX: string = '!';
public static async run(tokens: Token[]): Promise<Var> {
const cmdlet = this.getCmdlet(tokens);
if (cmdlet === null) {
return this.runFunction(tokens);
} else {
if (cmdlet !== null) {
return cmdlet.run(tokens.slice(1));
}

const macro = this.getMacro(tokens);
if (macro !== null) {
return MacroCmdLet.run(macro);
}

return this.runFunction(tokens);
}

public static runSync(tokens: Token[]): Var {
Expand All @@ -30,6 +38,17 @@ export class Command {
return CmdLets.getByName(t0.getLiteral());
}

private static getMacro(tokens: Token[]): Macro | null {
if (tokens.length === 0) throw new Error('failed to tokenize macro');
const t0 = tokens[0] as Token;
const name = t0.getLiteral();
if (!name.startsWith(Command.MACRO_PREFIX)) return null;
if (name.length === 1) throw new Error('macro name not supplied');
const macro = Macros.get(name.slice(1));
if (macro === null) throw new Error(`failed to recognozie macro ${Output.green(name)}`);
return macro;
}

private static runFunction(tokens: Token[]): Var {
if (tokens.length === 0) throw new Error('failed to tokenize command');
const t0 = tokens[0] as Token;
Expand Down
50 changes: 50 additions & 0 deletions src/macros/macros.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Output } from '../io/output.js';

export class Macro {
private readonly _name: string;
private readonly _commands: string[] = [];

constructor(name: string, commands: string[]) {
this._name = name;
this._commands = commands;
}

public get name(): string {
return this._name;
}

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

public toString(): string {
return this._commands
.map(l => Output.writeln(` - ${Output.yellow(l)}`))
.join('\n');
}
}

export class Macros {
private static map: Map<string, Macro> = new Map<string, Macro>();

public static get(name: string): Macro | null {
return this.map.get(name) ?? null;
}

public static set(macro: Macro) {
this.map.set(macro.name, macro);
}

public static delete(name: string): Macro | null {
const macro = this.map.get(name);
if (macro === undefined) return null;
this.map.delete(name);
return macro;
}

public static all(): Macro[] {
return Array.from(this.map.entries())
.sort(([k1, _v1], [k2, _v2]) => k1.localeCompare(k2))
.map(([k, v]) => v);
}
}

0 comments on commit 4b4fb36

Please sign in to comment.