diff --git a/.gitignore b/.gitignore index d2ddc1b..76c1d47 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules frida-cshell.js frida-cshell src/version.ts +core diff --git a/package-lock.json b/package-lock.json index 4b0c840..8c2de0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "frida-cshell", - "version": "1.7.8", + "version": "1.7.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frida-cshell", - "version": "1.7.8", + "version": "1.7.9", "devDependencies": { "@eslint/js": "^9.10.0", "@types/frida-gum": "^18.7", diff --git a/package.json b/package.json index aafc584..09f47dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "frida-cshell", - "version": "1.7.8", + "version": "1.7.9", "description": "Frida's CShell", "scripts": { "prepare": "npm run version && npm run build && npm run package && npm run copy", diff --git a/src/cmdlets/misc/corpse/corpse.ts b/src/cmdlets/misc/corpse/corpse.ts index 93b9a62..24ce6ef 100644 --- a/src/cmdlets/misc/corpse/corpse.ts +++ b/src/cmdlets/misc/corpse/corpse.ts @@ -11,6 +11,11 @@ import { Dumpable } from './dumpable.js'; import { Proc } from './proc.js'; import { Rlimit } from './rlimit.js'; import { SeLinux } from './selinux.js'; +import { Mem, MemProtection } from './mem.js'; +import { APP_VERSION, GIT_COMMIT_HASH } from '../../../version.js'; +import { Overlay } from '../../../memory/overlay.js'; +import { Regs } from '../../../breakpoints/regs.js'; +import { Vars } from '../../../vars/vars.js'; export class CorpseCmdLet extends CmdLetBase { name = 'corpse'; @@ -28,6 +33,7 @@ corpse - create a corpse file`; private dumpable: Dumpable | null = null; private clone: Fork | null = null; private proc: Proc | null = null; + private mem: Mem | null = null; public runSync(tokens: Token[]): Var { if (tokens.length != 0) return this.usage(); @@ -126,6 +132,13 @@ corpse - create a corpse file`; debug(`Restoring default signal action using rt_sigaction`); proc.rt_sigaction(Proc.SIGABRT, Proc.SIG_DFL); + for (const overlay of Overlay.all()) { + debug(`Reverting overlay: ${overlay.toString()}`); + overlay.revert(); + } + + this.writeMetadata(debug); + debug(`Suicide`); proc.kill(pid, Proc.SIGABRT); } catch (error) { @@ -141,6 +154,78 @@ corpse - create a corpse file`; } } + private writeMetadata(debug: (msg: string) => void) { + const magicLen = 8; + /* 43 4f 52 50 53 45 20 33 |CORPSE 3| */ + const magics: [Uint8Array, Uint8Array] = [ + new Uint8Array([0xde, 0xad, 0xfa, 0xce, 0xde, 0xad, 0xfa, 0xce]), + new Uint8Array([0x9d, 0xe2, 0xa8, 0x9e, 0x8d, 0xe8, 0xda, 0xfd]), + ]; + const ranges = Process.enumerateRanges('---').map(r => { + return { + base: `0x${r.base.toString(16)}`, + size: r.size, + protection: r.protection, + file_path: r.file?.path ?? null, + file_offset: r.file?.offset ?? null, + file_size: r.file?.size ?? null, + }; + }); + const modules = Process.enumerateModules().map(m => { + return { + name: m.name, + base: `0x${m.base.toString(16)}`, + size: m.size, + path: m.path, + }; + }); + const regs = Regs.all().map(([name, value]) => { + return { + name: name, + addr: value.toPointer(), + value: value.getLiteral(), + }; + }); + const vars = Vars.all().map(([name, value]) => { + return { + name: name, + addr: value.toPointer(), + value: value.getLiteral(), + }; + }); + const metatdata = { + version: APP_VERSION, + hash: GIT_COMMIT_HASH, + ranges, + modules, + regs, + vars, + }; + + const data = JSON.stringify(metatdata, null, 2); + debug(`data: ${data}`); + debug(`metadata size: ${Format.toSize(data.length)}`); + + const alignedSize = Mem.pageAlign(magicLen + data.length); + const totalSize = Process.pageSize * 2 + alignedSize; + const mem = this.mem as Mem; + const buffer = mem.map_anonymous(totalSize); + mem.protect(buffer, Process.pageSize, MemProtection.PROT_NONE); + mem.protect( + buffer.add(Process.pageSize + alignedSize), + Process.pageSize, + MemProtection.PROT_NONE, + ); + let cursor = buffer.add(Process.pageSize); + for (let i = 0; i < magicLen; i++) { + const x = magics[0][i] as number; + const y = magics[1][i] as number; + cursor.writeU8(x ^ y); + cursor = cursor.add(1); + } + cursor.writeUtf8String(data); + } + private checkCorpse(corePattern: string, pid: number) { const corePath = CorePattern.appendPid() ? `${corePattern}.${pid}` @@ -208,6 +293,7 @@ corpse - create a corpse file`; this.dumpable = new Dumpable(); this.clone = new Fork(); this.proc = new Proc(); + this.mem = new Mem(); } catch { return false; } diff --git a/src/cmdlets/misc/corpse/mem.ts b/src/cmdlets/misc/corpse/mem.ts new file mode 100644 index 0000000..0ba8ae7 --- /dev/null +++ b/src/cmdlets/misc/corpse/mem.ts @@ -0,0 +1,78 @@ +export enum MemProtection { + PROT_NONE = 0, + PROT_READ = 0x1, + PROT_WRITE = 0x2, + PROT_READ_WRITE = PROT_READ | PROT_WRITE, +} + +export class Mem { + private static readonly MAP_PRIVATE: number = 0x2; + private static readonly MAP_ANONYMOUS: number = 0x20; + + private static readonly MAP_FAILED: NativePointer = ptr(-1); + + /* void *mmap(void addr[.length], size_t length, int prot, int flags, + int fd, off_t offset); */ + private fnMmap: SystemFunction< + NativePointer, + [NativePointer, number | UInt64, number, number, number, number | UInt64] + >; + + /* int mprotect(void addr[.len], size_t len, int prot); */ + private fnMprotect: SystemFunction< + number, + [NativePointer, number | UInt64, number] + >; + + public constructor() { + const pMmap = Module.findExportByName(null, 'mmap'); + if (pMmap === null) throw new Error('failed to find mmap'); + + this.fnMmap = new SystemFunction(pMmap, 'pointer', [ + 'pointer', + 'size_t', + 'int', + 'int', + 'int', + 'size_t', + ]); + + const pMprotect = Module.findExportByName(null, 'mprotect'); + if (pMprotect === null) throw new Error('failed to find mprotect'); + + this.fnMprotect = new SystemFunction(pMprotect, 'int', [ + 'pointer', + 'size_t', + 'int', + ]); + } + + public map_anonymous(size: number): NativePointer { + const ret = this.fnMmap( + ptr(0), + size, + MemProtection.PROT_READ | MemProtection.PROT_WRITE, + Mem.MAP_ANONYMOUS | Mem.MAP_PRIVATE, + -1, + 0, + ) as UnixSystemFunctionResult; + if (ret.value.equals(Mem.MAP_FAILED)) + throw new Error(`failed to mmap, errno: ${ret.errno}`); + return ret.value; + } + + public protect(addr: NativePointer, size: number, prot: MemProtection) { + const ret = this.fnMprotect( + addr, + size, + prot, + ) as UnixSystemFunctionResult; + if (ret.value === -1) + throw new Error(`failed to mprotect, errno: ${ret.errno}`); + } + + public static pageAlign(size: number): number { + const mask = Process.pageSize - 1; + return (size + mask) & ~mask; + } +} diff --git a/src/memory/mem.ts b/src/memory/mem.ts index 3f3e28d..58b5031 100644 --- a/src/memory/mem.ts +++ b/src/memory/mem.ts @@ -33,7 +33,7 @@ export class Mem { } } - private static modifyMemory(address: NativePointer, data: Uint8Array) { + public static modifyMemory(address: NativePointer, data: Uint8Array) { const alignStart = this.pageAlignDown(address); const alignEnd = this.pageAlignUp(address.add(data.length)); const pageShift = Math.log2(Process.pageSize); diff --git a/src/memory/overlay.ts b/src/memory/overlay.ts index e29cb71..6a317b3 100644 --- a/src/memory/overlay.ts +++ b/src/memory/overlay.ts @@ -81,4 +81,16 @@ export class Overlay { if (this.address.compare(addr.add(length)) >= 0) return false; return true; } + + public static all(): Overlay[] { + return Overlay.overlays.map(([_, overlay]) => overlay); + } + + public revert() { + Mem.modifyMemory(this.address, this.data); + } + + public toString(): string { + return `addr: ${this.address}, length: ${this.data.length}`; + } }