r2papi
Version:
r2api on top of r2pipe for typescript and js
1,239 lines (1,201 loc) • 33.9 kB
text/typescript
// main r2papi file
import { R2Shell } from "./shell.js";
import { newAsyncR2PipeFromSync, r2, R2PipeSync } from "./r2pipe.js";
export type InstructionType =
| "mov"
| "jmp"
| "cmp"
| "nop"
| "call"
| "add"
| "sub";
export type InstructionFamily = "cpu" | "fpu" | "priv";
export type GraphFormat = "dot" | "json" | "mermaid" | "ascii";
export type Permission = "---" | "r--" | "rw-" | "rwx" | "r-x" | "-wx" | "--x";
export type R2Papi = R2PapiSync;
export interface SearchResult {
offset: number; // TODO: rename to addr
type: string;
data: string;
}
export interface DebugModule {
base: string;
name: string;
path: string;
size: number;
}
export interface Flag {
name: string;
size: number;
offset: number;
}
export type PluginFamily =
| "anal"
| "arch"
| "bin"
| "core"
| "crypto"
| "debug"
| "egg"
| "esil"
| "fs"
| "io"
| "lang";
// XXX not working? export type ThreadState = "waiting" | "running" | "dead" ;
export interface ThreadContext {
context: any;
id: number;
state: string;
selected: boolean;
}
export interface CallRef {
addr: number;
type: string;
at: number;
}
export interface FunctionDetails {
offset: number;
name: string;
size: number;
noreturn: boolean;
stackframe: number;
ebbs: number;
signature: string;
nbbs: number;
callrefs: CallRef[];
codexrefs: CallRef[];
}
export interface BinFile {
arch: string;
static: boolean;
va: boolean;
stripped: boolean;
pic: boolean;
relocs: boolean;
sanitize: boolean;
baddr: number;
binsz: number;
bintype: string;
bits: number;
canary: boolean;
class: string;
compiler: string;
endian: string;
machine: string;
nx: boolean;
os: string;
laddr: number;
linenum: boolean;
havecode: boolean;
intrp: string;
}
export interface Reference {
from: number;
type: string;
perm: string;
opcode: string;
fcn_addr: number;
fcn_name: string;
realname: string;
refname: string;
}
export interface BasicBlock {
addr: number;
size: number;
jump: number;
fail: number;
opaddr: number;
inputs: number;
outputs: number;
ninstr: number;
instrs: number[];
traced: boolean;
}
export class ThreadClass {
api: any = null;
constructor(r2: any) {
this.api = r2;
}
backtrace() {
return r2.call("dbtj");
}
sleep(seconds: number) {
return r2.call("sleep " + seconds);
}
}
export interface Instruction {
type: InstructionType;
addr: number;
opcode: string;
pseudo: string;
mnemonic: string;
sign: boolean;
family: InstructionFamily;
description: string;
esil: string;
opex: any;
size: number;
ptr: number;
bytes: string;
id: number;
refptr: number;
direction: "read" | "write";
stackptr: number;
stack: string; // "inc"|"dec"|"get"|"set"|"nop"|"null";
}
export class ModuleClass {
api: any = null;
constructor(r2: R2PipeSync) {
this.api = r2;
}
fileName(): string {
return this.api.call("dpe").trim();
}
name(): string {
return "Module";
}
findBaseAddress() {
return "TODO";
}
getBaseAddress(name: string) {
return "TODO";
}
getExportByName(name: string) : NativePointer {
const res = r2.call("iE,name/eq/" + name + ",vaddr/cols,:quiet");
return ptr(res);
}
findExportByName(name: string): NativePointer {
return this.getExportByName(name);
}
enumerateExports() : any {
// TODO: adjust to be the same output as Frida
return r2.callj("iEj");
}
enumerateImports() {
// TODO: adjust to be the same output as Frida
return r2.callj("iij");
}
enumerateSymbols() : any {
// TODO: adjust to be the same output as Frida
return r2.callj("isj");
}
enumerateEntrypoints() : any {
// TODO: adjust to be the same output as Frida
return r2.callj("iej");
}
enumerateRanges() : any {
// TODO: adjust to be the same output as Frida
return r2.callj("omj");
}
}
export class ProcessClass {
r2: any = null;
constructor(r2: R2PipeSync) {
this.r2 = r2;
}
enumerateMallocRanges() {}
enumerateSystemRanges() {}
enumerateRanges() {}
enumerateThreads() {
return r2.callj("dptj");
}
enumerateModules(): any {
r2.call("cfg.json.num=string"); // to handle 64bit values properly
if (r2.callj("e cfg.debug")) {
const modules = r2.callj("dmmj");
const res = [];
for (const mod of modules) {
const entry = {
base: new NativePointer(mod.addr),
size: new NativePointer(mod.addr_end).sub(mod.addr),
path: mod.file,
name: mod.name
};
res.push(entry);
}
return res;
} else {
const fname = (x: string) => {
const y = x.split("/");
return y[y.length - 1];
};
const bobjs = r2.callj("obj");
const res = [];
for (const obj of bobjs) {
const entry = {
base: new NativePointer(obj.addr),
size: obj.size,
path: obj.file,
name: fname(obj.file)
};
res.push(entry);
}
const libs = r2.callj("ilj");
for (const lib of libs) {
const entry = {
base: 0,
size: 0,
path: lib,
name: fname(lib)
};
res.push(entry);
}
return res;
}
}
getModuleByAddress(addr: NativePointer | number | string): any {}
getModuleByName(moduleName: string): any {}
codeSigningPolicy(): string {
return "optional";
}
getTmpDir() {
return this.r2.call("e dir.tmp").trim();
}
getHomeDir() {
return this.r2.call("e dir.home").trim();
}
platform() {
return this.r2.call("e asm.os").trim();
}
getCurrentDir() {
return this.r2.call("pwd").trim();
}
getCurrentThreadId(): number {
return +this.r2.call("dpq");
}
pageSize(): number {
if (
this.r2.callj("e asm.bits") === 64 &&
this.r2.call("e asm.arch").startsWith("arm")
) {
return 16384;
}
return 4096;
}
isDebuggerAttached(): boolean {
return this.r2.callj("e cfg.debug");
}
setExceptionHandler() {
// do nothing
}
id() {
//
return this.r2.callj("dpq").trim();
}
pointerSize() {
return r2.callj("e asm.bits") / 8;
}
}
/**
* Assembler and disassembler facilities to decode and encode instructions
*
* @typedef Assembler
*/
export class Assembler {
program: string = "";
labels: any = {};
endian: boolean = false;
pc: NativePointer = ptr(0);
r2: R2PipeSync;
constructor(myr2?: R2PipeSync) {
if (myr2 === undefined) {
this.r2 = newAsyncR2PipeFromSync(r2);
} else {
this.r2 = myr2;
}
this.program = "";
this.labels = {};
}
/**
* Change the address of the program counter, some instructions need to know where
* are they located before being encoded or decoded.
*
* @param {NativePointerValue}
*/
setProgramCounter(pc: NativePointer) {
this.pc = pc;
}
setEndian(big: boolean) {
this.endian = big;
}
toString() {
return this.program;
}
append(x: string) {
// append text
this.pc = this.pc.add(x.length / 2);
this.program += x;
}
// api
label(s: string): NativePointer {
const pos = this.pc; // this.#program.length / 4;
this.labels[s] = this.pc;
return pos;
}
/**
* Encode (assemble) an instruction by taking the string representation.
*
* @param {string} the string representation of the instruction to assemble
* @returns {string} the hexpairs that represent the assembled instruciton
*/
encode(s: string): string {
const output = this.r2.call(`pa ${s}`);
return output.trim();
}
/**
* Decode (disassemble) an instruction by taking the hexpairs string as input.
* TODO: should take an array of bytes too
*
* @param {string} the hexadecimal pairs of bytes to decode as an instruction
* @returns {string} the mnemonic and operands of the resulting decoding
*/
decode(s: string): string {
const output = this.r2.call(`pad ${s}`);
return output.trim();
}
}
/**
* High level abstraction on top of the r2 command interface provided by r2pipe.
*
* @typedef R2Papi
*/
export class R2PapiSync {
/**
* Keep a reference r2pipe instance
*
* @type {R2PipeSync}
*/
public r2: R2PipeSync;
/**
* Create a new instance of the R2Papi class, taking an r2pipe interface as reference.
*
* @param {R2PipeSync} the r2pipe instance to use as backend.
* @returns {R2Papi} instance
*/
constructor(r2: R2PipeSync) {
this.r2 = r2;
}
toString() {
return "[object R2Papi]";
}
toJSON() {
return this.toString();
}
/**
* Get the base address used by the current loaded binary
*
* @returns {NativePointer} address of the base of the binary
*/
getBaseAddress(): NativePointer {
return new NativePointer(this.cmd("e bin.baddr"));
}
jsonToTypescript(name: string, a: any): string {
let str = `interface ${name} {\n`;
if (a.length && a.length > 0) {
a = a[0];
}
for (const k of Object.keys(a)) {
const typ = typeof a[k];
const nam = k;
str += ` ${nam}: ${typ};\n`;
}
return `${str}}\n`;
}
/**
* Get the general purpose register size of the targize architecture in bits
*
* @returns {number} the regsize
*/
getBits(): number {
return +this.cmd("-b");
}
/**
* Get the name of the arch plugin selected, which tends to be the same target architecture.
* Note that on some situations, this info will be stored protected bby the AirForce.
* When using the r2ghidra arch plugin the underlying arch is in `asm.cpu`:
*
* @returns {string} the name of the target architecture.
*/
getArch(): string {
return this.cmdTrim("-a");
}
callTrim(x: string): string {
const res = this.call(x);
return res.trim()
}
cmdTrim(x: string): string {
const res = this.cmd(x);
return res.trim()
}
/**
* Get the name of the selected CPU for the current selected architecture.
*
* @returns {string} the value of asm.cpu
*/
getCpu(): string {
// return this.cmd('-c');
return this.cmdTrim("-e asm.cpu"); // use arch.cpu
}
// TODO: setEndian, setCpu, ...
setArch(arch: string, bits: number | undefined) {
this.cmd("-a " + arch);
if (bits !== undefined) {
this.cmd("-b " + bits);
}
}
setFlagSpace(name: string) {
this.cmd("fs " + name);
}
demangleSymbol(lang: string, mangledName: string) : string {
return this.cmdTrim("iD " + lang + " " + mangledName);
}
setLogLevel(level: number) {
this.cmd("e log.level=" + level);
}
/**
* should return the id for the new map using the given file descriptor
*/
// rename to createMap or mapFile?
newMap(
fd: number,
vaddr: NativePointer,
size: number,
paddr: NativePointer,
perm: Permission,
name: string = ""
): void {
this.cmd(`om ${fd} ${vaddr} ${size} ${paddr} ${perm} ${name}`);
}
at(a: string): NativePointer {
return new NativePointer(a);
}
getShell(): R2Shell {
return new R2Shell(this);
}
// Radare/Frida
version(): string {
const v = this.r2.cmd("?Vq");
return v.trim();
}
// Process
platform(): string {
const output = this.r2.cmd("uname");
return output.trim();
}
arch(): string {
const output = this.r2.cmd("uname -a");
return output.trim();
}
bits(): string {
const output = this.r2.cmd("uname -b");
return output.trim();
}
id(): number {
// getpid();
return +this.r2.cmd("?vi:$p");
}
// Other stuff
printAt(msg: string, x: number, y: number): void {
// see pg, but pg is obrken :D
}
clearScreen(): R2Papi {
this.r2.cmd("!clear");
return this;
}
getConfig(key: string): Error | string {
if (key === "") {
return new Error("Empty key");
}
const exist = this.r2.cmd(`e~^${key} =`);
if (exist.trim() === "") {
return new Error("Config key does not exist");
}
const value = this.r2.call("e " + key);
return value.trim();
}
setConfig(key: string, val: string): R2Papi {
this.r2.call("e " + key + "=" + val);
return this;
}
getRegisterStateForEsil(): string {
const dre = this.cmdj("dre");
return this.cmdj("dre");
}
getRegisters(): any {
// this.r2.log("winrar" + JSON.stringify(JSON.parse(this.r2.cmd("drj")),null, 2) );
return this.cmdj("drj");
}
resizeFile(newSize: number): R2Papi {
this.cmd(`r ${newSize}`);
return this;
}
insertNullBytes(
newSize: number,
at?: NativePointer | number | string
): R2Papi {
if (at === undefined) {
at = "$$";
}
this.cmd(`r+${newSize}@${at}`);
return this;
}
removeBytes(newSize: number, at?: NativePointer | number | string): R2Papi {
if (at === undefined) {
at = "$$";
}
this.cmd(`r-${newSize}@${at}`);
return this;
}
seek(addr: number): R2Papi {
this.cmd(`s ${addr}`);
return this;
}
currentSeek(): NativePointer {
return new NativePointer("$$");
}
seekToRelativeOpcode(nth: number): NativePointer {
this.cmd(`so ${nth}`);
return this.currentSeek();
}
getBlockSize(): number {
return +this.cmd("b");
}
setBlockSize(a: number): R2Papi {
this.cmd(`b ${a}`);
return this;
}
countFlags(): number {
return Number(this.cmd("f~?"));
}
countFunctions(): number {
return Number(this.cmd("aflc"));
}
analyzeFunctionsWithEsil(depth?: number) {
this.cmd("aaef");
}
analyzeProgramWithEsil(depth?: number) {
this.cmd("aae");
}
analyzeProgram(depth?: number) {
if (depth === undefined) {
depth = 0;
}
switch (depth) {
case 0:
this.cmd("aa");
break;
case 1:
this.cmd("aaa");
break;
case 2:
this.cmd("aaaa");
break;
case 3:
this.cmd("aaaaa");
break;
}
return this;
}
enumerateThreads(): ThreadContext[] {
// TODO: use apt/dpt to list threads at iterate over them to get the registers
const regs0 = this.cmdj("drj");
const thread0 = {
context: regs0,
id: 0,
state: "waiting",
selected: true
};
return [thread0];
}
currentThreadId(): number {
if (+this.cmd("e cfg.debug")) {
return +this.cmd("dpt.");
}
return this.id();
}
setRegisters(obj: any) {
for (const r of Object.keys(obj)) {
const v = obj[r];
this.r2.cmd("dr " + r + "=" + v);
}
}
hex(s: number | string): string {
const output = this.r2.cmd("?v " + s);
return output.trim();
}
step(): R2Papi {
this.r2.cmd("ds");
return this;
}
stepOver(): R2Papi {
this.r2.cmd("dso");
return this;
}
math(expr: number | string): number {
return +this.r2.cmd("?v " + expr);
}
stepUntil(dst: NativePointer | string | number): void {
this.cmd(`dsu ${dst}`);
}
enumerateXrefsTo(s: string): string[] {
const output = this.call("axtq " + s);
return output.trim().split(/\n/);
}
// TODO: rename to searchXrefsTo ?
findXrefsTo(s: string, use_esil: boolean) {
if (use_esil) {
this.call("/r " + s);
} else {
this.call("/re " + s);
}
}
analyzeFunctionsFromCalls(): R2Papi {
this.call("aac");
return this;
}
autonameAllFunctions(): R2Papi {
this.call("aan");
return this;
}
analyzeFunctionsWithPreludes(): R2Papi {
this.call("aap");
return this;
}
analyzeObjCReferences(): R2Papi {
this.cmd("aao");
return this;
}
analyzeImports(): R2Papi {
this.cmd("af @ sym.imp.*");
return this;
}
searchDisasm(s: string): SearchResult[] {
const res: SearchResult[] = this.callj("/ad " + s);
return res;
}
searchString(s: string): SearchResult[] {
const res: SearchResult[] = this.cmdj("/j " + s);
return res;
}
searchBytes(data: number[]): SearchResult[] {
function num2hex(data: number): string {
return (data & 0xff).toString(16);
}
const s = data.map(num2hex).join("");
const res: SearchResult[] = this.cmdj("/xj " + s);
return res;
}
binInfo(): BinFile {
try {
return this.cmdj("ij~{bin}");
} catch (e: any) {
return {} as BinFile;
}
}
// TODO: take a BinFile as argument instead of number
selectBinary(id: number): void {
this.call(`ob ${id}`);
}
openFile(name: string): number | Error {
const ofd = this.call("oqq");
this.call(`o ${name}`);
const nfd = this.call("oqq");
if (ofd.trim() === nfd.trim()) {
return new Error("Cannot open file");
}
return parseInt(nfd);
}
openFileNomap(name: string): number | Error {
const ofd = this.call("oqq");
this.call(`of ${name}`);
const nfd = this.call("oqq");
if (ofd.trim() === nfd.trim()) {
return new Error("Cannot open file");
}
return parseInt(nfd);
}
currentFile(name: string): string {
return (this.call("o.")).trim();
}
enumeratePlugins(type: PluginFamily): any {
switch (type) {
case "bin":
return this.callj("Lij");
case "io":
return this.callj("Loj");
case "core":
return this.callj("Lcj");
case "arch":
return this.callj("LAj");
case "anal":
return this.callj("Laj");
case "lang":
return this.callj("Llj");
}
return [];
}
enumerateModules(): DebugModule[] {
return this.callj("dmmj");
}
enumerateFiles(): any {
return this.callj("oj");
}
enumerateBinaries(): any {
return this.callj("obj");
}
enumerateMaps(): any {
return this.callj("omj");
}
enumerateClasses(): any {
return this.callj("icj");
}
enumerateSymbols(): any {
return this.callj("isj");
}
enumerateExports(): any {
return this.callj("iEj");
}
enumerateImports(): any {
return this.callj("iij");
}
enumerateLibraries(): string[] {
return this.callj("ilj");
}
enumerateSections(): any {
return this.callj("iSj");
}
enumerateSegments(): any {
return this.callj("iSSj");
}
enumerateEntrypoints(): any {
return this.callj("iej");
}
enumerateRelocations(): any {
return this.callj("irj");
}
enumerateFunctions(): FunctionDetails[] {
return this.cmdj("aflj");
}
enumerateFlags(): Flag[] {
return this.cmdj("fj");
}
skip() {
this.r2.cmd("dss");
}
ptr(s: string | number): NativePointer {
return new NativePointer(s, this);
}
call(s: string): string {
return this.r2.call(s);
}
callj(s: string): any {
return JSON.parse(this.call(s));
}
cmd(s: string): string {
return this.r2.cmd(s);
}
cmdj(s: string): any {
return JSON.parse(this.cmd(s));
}
log(s: string) {
return this.r2.log(s);
}
clippy(msg: string): void {
this.r2.log(this.r2.cmd("?E " + msg));
}
ascii(msg: string): void {
this.r2.log(this.r2.cmd("?ea " + msg));
}
}
// useful to call functions via dxc and to define and describe function signatures
export class NativeFunction {
constructor() {}
}
// uhm not sure how to map this into r2 yet
export class NativeCallback {
constructor() {}
}
// export const NULL = ptr("0");yet
/**
* Global function that returns a new instance of a NativePointer.
* Saves some typing: `ptr(0)` is the same as `new NativePointer(0)`
*
* @type function
*/
export declare function ptr(v: NativePointerValue): NativePointer;
/**
* A NativePointer can be described using a string that contains a number in any base (hexadecimal and decimal are the most common formats used)
* But it actually supports anything else that could be handled by radare2. You can use symbol names, math operations or special symbols like `$$`.
*
* @type NativePointerValue
*/
export type NativePointerValue = string | number | NativePointer;
/**
* Class providing a way to work with 64bit pointers from Javascript, this API mimics the same
* well-known promitive available in Frida, but it's baked by the current session of r2.
*
* It is also possible to use this class via the global `ptr` function.
*
* @typedef NativePointer
*/
export class NativePointer {
addr: string;
api: R2Papi;
constructor(s: NativePointerValue, api?: R2Papi) {
if (api === undefined) {
this.api = R;
} else {
this.api = api;
}
// this.api.r2.log("NP " + s);
this.addr = ("" + s).trim();
}
/**
* Filter a string to be used as a valid flag name
*
* @param {string} name of the symbol name
* @returns {string} filtered name to be used as a flag
*/
filterFlag(name: string) : string {
return this.api.call(`fD ${name}`)
}
/**
* Set a flag (name) at the offset pointed
*
* @param {string} name of the flag to set
* @returns {string} base64 decoded string
*/
setFlag(name: string) {
this.api.call(`f ${name}=${this.addr}`);
}
/**
* Remove the flag in the current offset
*
*/
unsetFlag() {
this.api.call(`f-${this.addr}`);
}
/**
* Render an hexadecimal dump of the bytes contained in the range starting
* in the current pointer and given length.
*
* @param {number} length optional amount of bytes to dump, using blocksize
* @returns {string} string containing the hexadecimal dump of memory
*/
hexdump(length?: number): string {
const len = length === undefined ? "" : "" + length;
return this.api.cmd(`x${len}@${this.addr}`);
}
functionGraph(format?: GraphFormat): string {
if (format === "dot") {
return this.api.cmd(`agfd@ ${this.addr}`);
}
if (format === "json") {
return this.api.cmd(`agfj@${this.addr}`);
}
if (format === "mermaid") {
return this.api.cmd(`agfm@${this.addr}`);
}
return this.api.cmd(`agf@${this.addr}`);
}
readByteArray(len: number): number[] {
return JSON.parse(this.api.cmd(`p8j ${len}@${this.addr}`));
}
readHexString(len: number): string {
return (this.api.cmd(`p8 ${len}@${this.addr}`)).trim();
}
and(a: number): NativePointer {
const addr = this.api.call(`?v ${this.addr} & ${a}`);
return new NativePointer(addr.trim());
}
or(a: number): NativePointer {
const addr = this.api.call(`?v ${this.addr} | ${a}`);
return new NativePointer(addr.trim());
}
add(a: number): NativePointer {
const addr = this.api.call(`?v ${this.addr}+${a}`);
return new NativePointer(addr);
}
sub(a: number): NativePointer {
const addr = this.api.call(`?v ${this.addr}-${a}`);
return new NativePointer(addr);
}
writeByteArray(data: number[]): NativePointer {
this.api.cmd("wx " + data.join(""));
return this;
}
writeAssembly(instruction: string): NativePointer {
this.api.cmd(`wa ${instruction} @ ${this.addr}`);
return this;
}
writeCString(s: string): NativePointer {
this.api.call("w " + s);
return this;
}
writeWideString(s: string): NativePointer {
this.api.call("ww " + s);
return this;
}
/**
* Check if it's a pointer to the address zero. Also known as null pointer.
*
* @returns {boolean} true if null
*/
isNull(): boolean {
return (this.toNumber()) == 0;
}
/**
* Compare current pointer with the passed one, and return -1, 0 or 1.
*
* * if (this < arg) return -1;
* * if (this > arg) return 1;
* * if (this == arg) return 0;
*
* @returns {number} returns -1, 0 or 1 depending on the comparison of the pointers
*/
compare(a: NativePointerValue): number {
const bv: NativePointer =
typeof a === "string" || typeof a === "number"
? new NativePointer(a)
: a;
const dist = r2.call(`?vi ${this.addr} - ${bv.addr}`);
if (dist[0] === "-") {
return -1;
}
if (dist[0] === "0") {
return 0;
}
return 1;
}
/**
* Check if it's a pointer to the address zero. Also known as null pointer.
*
* @returns {boolean} true if null
*/
pointsToNull(): boolean {
const value = this.readPointer();
return (value.compare(0)) == 0;
}
toJSON(): string {
const output = this.api.cmd("?vi " + this.addr.trim());
return output.trim();
}
toString(): string {
return (this.api.cmd("?v " + this.addr.trim())).trim();
}
toNumber(): number {
return parseInt(this.toString());
}
writePointer(p: NativePointer): void {
this.api.cmd(`wvp ${p}@${this}`); // requires 5.8.2
}
readRelativePointer(): NativePointer {
return this.add(this.readS32());
}
readPointer(): NativePointer {
const address = this.api.call("pvp@" + this.addr);
return new NativePointer(address);
}
readS8(): number {
// requires 5.8.9
return parseInt(this.api.cmd(`pv1d@${this.addr}`));
}
readU8(): number {
return parseInt(this.api.cmd(`pv1u@${this.addr}`));
}
readU16(): number {
return parseInt(this.api.cmd(`pv2d@${this.addr}`));
}
readU16le(): number {
return parseInt(
this.api.cmd(`pv2d@${this.addr}:cfg.bigendian=false`)
); // requires 5.8.9
}
readU16be(): number {
return parseInt(
this.api.cmd(`pv2d@${this.addr}:cfg.bigendian=true`)
); // requires 5.8.9
}
readS16(): number {
return parseInt(this.api.cmd(`pv2d@${this.addr}`)); // requires 5.8.9
}
readS16le(): number {
return parseInt(
this.api.cmd(`pv2d@${this.addr}:cfg.bigendian=false`)
); // requires 5.8.9
}
readS16be(): number {
return parseInt(
this.api.cmd(`pv2d@${this.addr}:cfg.bigendian=true`)
); // requires 5.8.9
}
readS32(): number {
// same as readInt32()
return parseInt(this.api.cmd(`pv4d@${this.addr}`)); // requires 5.8.9
}
readU32(): number {
return parseInt(this.api.cmd(`pv4u@${this.addr}`)); // requires 5.8.9
}
readU32le(): number {
return parseInt(
this.api.cmd(`pv4u@${this.addr}:cfg.bigendian=false`)
); // requires 5.8.9
}
readU32be(): number {
return parseInt(
this.api.cmd(`pv4u@${this.addr}:cfg.bigendian=true`)
); // requires 5.8.9
}
readU64(): number {
// XXX: use bignum or string here
return parseInt(this.api.cmd(`pv8u@${this.addr}`));
}
readU64le(): number {
return parseInt(
this.api.cmd(`pv8u@${this.addr}:cfg.bigendian=false`)
); // requires 5.8.9
}
readU64be(): number {
return parseInt(
this.api.cmd(`pv8u@${this.addr}:cfg.bigendian=true`)
); // requires 5.8.9
}
writeInt(n: number): boolean {
return this.writeU32(n);
}
/**
* Write a byte in the current offset, the value must be between 0 and 255
*
* @param {string} n number to write in the pointed byte in the current address
* @returns {boolean} false if the operation failed
*/
writeU8(n: number): boolean {
this.api.cmd(`wv1 ${n}@${this.addr}`);
return true;
}
writeU16(n: number): boolean {
this.api.cmd(`wv2 ${n}@${this.addr}`);
return true;
}
writeU16be(n: number): boolean {
this.api.cmd(`wv2 ${n}@${this.addr}:cfg.bigendian=true`);
return true;
}
writeU16le(n: number): boolean {
this.api.cmd(`wv2 ${n}@${this.addr}:cfg.bigendian=false`);
return true;
}
writeU32(n: number): boolean {
this.api.cmd(`wv4 ${n}@${this.addr}`);
return true;
}
writeU32be(n: number): boolean {
this.api.cmd(`wv4 ${n}@${this.addr}:cfg.bigendian=true`);
return true;
}
writeU32le(n: number): boolean {
this.api.cmd(`wv4 ${n}@${this.addr}:cfg.bigendian=false`);
return true;
}
writeU64(n: number): boolean {
this.api.cmd(`wv8 ${n}@${this.addr}`);
return true;
}
writeU64be(n: number): boolean {
this.api.cmd(`wv8 ${n}@${this.addr}:cfg.bigendian=true`);
return true;
}
writeU64le(n: number): boolean {
this.api.cmd(`wv8 ${n}@${this.addr}:cfg.bigendian=false`);
return true;
}
readInt32(): number {
return this.readU32();
}
readCString(): string {
const output = this.api.cmd(`pszj@${this.addr}`);
return JSON.parse(output).string;
}
readWideString(): string {
const output = this.api.cmd(`pswj@${this.addr}`);
return JSON.parse(output).string;
}
readPascalString(): string {
const output = this.api.cmd(`pspj@${this.addr}`);
return JSON.parse(output).string;
}
instruction(): Instruction {
const output = this.api.cmdj(`aoj@${this.addr}`);
return output[0] as Instruction;
}
disassemble(length?: number): string {
const len = length === undefined ? "" : "" + length;
return this.api.cmd(`pd ${len}@${this.addr}`);
}
analyzeFunction(): NativePointer {
this.api.cmd("af@" + this.addr);
return this;
}
analyzeFunctionRecursively(): NativePointer {
this.api.cmd("afr@" + this.addr);
return this;
}
name(): string {
return (this.api.cmd("fd " + this.addr)).trim();
}
methodName(): string {
// TODO: @ should be optional here, as addr should be passable as argument imho
return (this.api.cmd("ic.@" + this.addr)).trim();
}
symbolName(): string {
// TODO: @ should be optional here, as addr should be passable as argument imho
const name = this.api.cmd("isj.@" + this.addr);
return name.trim();
}
getFunction(): FunctionDetails {
return this.api.cmdj("afij@" + this.addr);
}
basicBlock(): BasicBlock {
return this.api.cmdj("abj@" + this.addr);
}
functionBasicBlocks(): BasicBlock[] {
return this.api.cmdj("afbj@" + this.addr);
}
xrefs(): Reference[] {
return this.api.cmdj("axtj@" + this.addr);
}
}
/*
// already defined by r2
function ptr(x: string|number) {
return new NativePointer(x);
}
*/
/**
* Global instance of R2Papi based on the current session of radare2.
* Note that `r2` is the global instance of `r2pipe` used by `R`.
*
* @type R2Papi
*/
export declare const R: R2Papi;
/**
* Global instance of the Module class based on the current radare2 session.
* This variable mimics the same APIs shipped by Frida.
*
* @type ModuleClass
*/
export declare const Module: ModuleClass;
/**
* Global instance of the Process class based on the current radare2 session.
* This variable mimics the same APIs shipped by Frida.
*
* @type ProcessClass
*/
export declare const Process: ProcessClass;
/**
* Global instance of the Thread class based on the current radare2 session.
* This variable mimics the same APIs shipped by Frida.
*
* @type ThreadClass
*/
export declare const Thread: ThreadClass;