// 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}@e:cfg.bigendian=false`) ); // requires 5.8.9 } readU16be(): number { return parseInt( this.api.cmd(`pv2d@${this.addr}@e: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}@e:cfg.bigendian=false`) ); // requires 5.8.9 } readS16be(): number { return parseInt( this.api.cmd(`pv2d@${this.addr}@e: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}@e:cfg.bigendian=false`) ); // requires 5.8.9 } readU32be(): number { return parseInt( this.api.cmd(`pv4u@${this.addr}@e: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}@e:cfg.bigendian=false`) ); // requires 5.8.9 } readU64be(): number { return parseInt( this.api.cmd(`pv8u@${this.addr}@e: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}@e:cfg.bigendian=true`); return true; } writeU16le(n: number): boolean { this.api.cmd(`wv2 ${n}@${this.addr}@e: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}@e:cfg.bigendian=true`); return true; } writeU32le(n: number): boolean { this.api.cmd(`wv4 ${n}@${this.addr}@e: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}@e:cfg.bigendian=true`); return true; } writeU64le(n: number): boolean { this.api.cmd(`wv8 ${n}@${this.addr}@e: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;