Skip to content

Commit c8b30ff

Browse files
author
Your Name
committed
Initial replace implementation
1 parent 26edc66 commit c8b30ff

5 files changed

Lines changed: 115 additions & 3 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.6.5",
3+
"version": "1.6.6",
44
"description": "Frida's CShell",
55
"scripts": {
66
"prepare": "npm run build && npm run version && npm run package && npm run copy",

src/cmdlets/data/replace.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { CmdLetBase } from '../../commands/cmdlet.js';
2+
import { Output } from '../../io/output.js';
3+
import { Format } from '../../misc/format.js';
4+
import { Token } from '../../io/token.js';
5+
import { Var } from '../../vars/var.js';
6+
7+
interface Replacement {
8+
dest: Var;
9+
src: Var;
10+
}
11+
12+
export class ReplaceCmdLet extends CmdLetBase {
13+
name = 'replace';
14+
category = 'data';
15+
help =
16+
'replace a function with another implementation (returns the address of the trampoline)';
17+
18+
private byIndex: Map<number, Replacement> = new Map<number, Replacement>();
19+
20+
private getNextFreeIndex(): number {
21+
let idx = 1;
22+
while (true) {
23+
if (!this.byIndex.has(idx)) return idx;
24+
idx++;
25+
}
26+
}
27+
28+
private static readonly USAGE: string = `Usage: replace
29+
30+
replace dest src - replace function
31+
dest the address/symbol to replace
32+
src the address/symbol to replace with`;
33+
34+
public runSync(tokens: Token[]): Var {
35+
const retDelete = this.runDelete(tokens);
36+
if (retDelete !== null) return retDelete;
37+
38+
const retCreate = this.runCreate(tokens);
39+
if (retCreate !== null) return retCreate;
40+
41+
const retShow = this.runShow(tokens);
42+
if (retShow !== null) return retShow;
43+
44+
return this.usage();
45+
}
46+
47+
public runDelete(tokens: Token[]): Var | null {
48+
const vars = this.transform(tokens, [this.parseIndex, this.parseDelete]);
49+
if (vars === null) return null;
50+
const [index, _] = vars as [number, string];
51+
const replacement = this.byIndex.get(index);
52+
if (replacement === undefined) {
53+
Output.writeln(`replacement #${index} not found`);
54+
return Var.ZERO;
55+
}
56+
57+
Output.writeln(Output.blue('deleting replacement:'));
58+
this.printReplacement(index, replacement);
59+
this.byIndex.delete(index);
60+
Interceptor.revert(replacement.dest.toPointer());
61+
return Var.ZERO;
62+
}
63+
64+
public runCreate(tokens: Token[]): Var | null {
65+
const vars = this.transform(tokens, [this.parseVar, this.parseVar]);
66+
if (vars === null) return null;
67+
const [destVar, srcVar] = vars as [Var, Var];
68+
69+
const dest = destVar.toPointer();
70+
const src = srcVar.toPointer();
71+
72+
try {
73+
const index = this.getNextFreeIndex();
74+
const replacement = { dest: destVar, src: srcVar };
75+
this.printReplacement(index, replacement);
76+
this.byIndex.set(index, replacement);
77+
const trampoline = Interceptor.replaceFast(dest, src);
78+
Output.writeln(Output.blue('created replacement:'));
79+
return new Var(uint64(trampoline.toString()));
80+
} catch (error) {
81+
throw new Error(
82+
`failed to replace ${Format.toHexString(dest)} with ${Format.toHexString(src)}, ${error}`,
83+
);
84+
}
85+
}
86+
87+
public runShow(tokens: Token[]): Var | null {
88+
if (tokens.length !== 0) return null;
89+
90+
Output.writeln(Output.blue('replacements:'));
91+
this.byIndex.forEach((replacement, index) => {
92+
this.printReplacement(index, replacement);
93+
});
94+
return Var.ZERO;
95+
}
96+
97+
private printReplacement(index: number, replacement: Replacement) {
98+
const idxString = Output.green(`#${index.toString()}.`.padEnd(4, ' '));
99+
const destString = `dest: ${Output.blue(replacement.dest.getLiteral())}`;
100+
const srcString = `src: ${Output.blue(replacement.src.getLiteral())}`;
101+
Output.writeln(`${idxString} ${destString} -> ${srcString}`);
102+
}
103+
104+
public usage(): Var {
105+
Output.writeln(ReplaceCmdLet.USAGE);
106+
return Var.ZERO;
107+
}
108+
}

src/cmdlets/development/js.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ import {
7474
TraceCoverageCmdLet,
7575
} from '../trace/trace.js';
7676
import { MacroCmdLet } from '../misc/macro.js';
77+
import { ReplaceCmdLet } from '../data/replace.js';
7778

7879
export class JsCmdLet extends CmdLetBase {
7980
name = 'js';
@@ -148,6 +149,7 @@ js path - load commandlet JS script
148149
ReadCmdLet: ReadCmdLet,
149150
RegCmdLet: RegCmdLet,
150151
Regs: Regs,
152+
ReplaceCmdLet: ReplaceCmdLet,
151153
ShlCmdLet: ShlCmdLet,
152154
ShrCmdLet: ShrCmdLet,
153155
SrcCmdLet: JsCmdLet,

src/commands/cmdlets.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ import {
6565
TraceCoverageCmdLet,
6666
} from '../cmdlets/trace/trace.js';
6767
import { MacroCmdLet } from '../cmdlets/misc/macro.js';
68+
import { ReplaceCmdLet } from '../cmdlets/data/replace.js';
6869

6970
export class CmdLets {
7071
private static byName: Map<string, CmdLet> = new Map<string, CmdLet>();
@@ -112,6 +113,7 @@ export class CmdLets {
112113
this.registerCmdletType(PrintCmdLet);
113114
this.registerCmdletType(ReadBpCmdLet);
114115
this.registerCmdletType(RegCmdLet);
116+
this.registerCmdletType(ReplaceCmdLet);
115117
this.registerCmdletType(ShCmdLet);
116118
this.registerCmdletType(ShlCmdLet);
117119
this.registerCmdletType(ShrCmdLet);

0 commit comments

Comments
 (0)