Last active
July 30, 2024 02:32
-
-
Save NeKzor/6560f254cc397c75df1e061919b29cb4 to your computer and use it in GitHub Desktop.
Deno FFI feat. libc.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const libc = Deno.dlopen( | |
"libc.so.6", | |
{ | |
dl_iterate_phdr: { | |
parameters: ["function", "pointer"], | |
result: "i32", | |
}, | |
readlink: { | |
parameters: ["buffer", "buffer", "isize"], | |
result: "isize", | |
}, | |
dlopen: { | |
parameters: ["buffer", "i32"], | |
result: "pointer", | |
}, | |
dlclose: { | |
parameters: ["pointer"], | |
result: "i32", | |
}, | |
dlsym: { | |
parameters: ["pointer", "buffer"], | |
result: "function", | |
}, | |
dlerror: { | |
parameters: [], | |
result: "buffer", | |
}, | |
} as const, | |
); | |
export class dl_phdr_info { | |
/** Base address of object */ | |
get dlpi_addr() { | |
return this.ptr.getBigUint64(0); | |
} | |
/** (Null-terminated) name of object */ | |
get dlpi_name() { | |
return new Deno.UnsafePointerView(this.ptr.getPointer(8)!).getCString(); | |
} | |
/** Pointer to array of ELF program headers for this object */ | |
get dlpi_phdr() { | |
return this.ptr.getPointer(16); | |
} | |
/** # of items in dlpi_phdr */ | |
get dlpi_phnum() { | |
return this.ptr.getUint16(24); | |
} | |
/** | |
* The following fields were added in glibc 2.4, after the first version of this structure was available. | |
* Check the size argument passed to the dl_iterate_phdr callback to determine whether or not each later member is | |
* available. | |
*/ | |
/** Incremented when a new object may have been added */ | |
get dlpi_adds() { | |
return this.isGlibc2_4 ? this.ptr.getBigUint64(32) : undefined; | |
} | |
/** Incremented when an object may have been removed */ | |
get dlpi_subs() { | |
return this.isGlibc2_4 ? this.ptr.getBigUint64(40) : undefined; | |
} | |
/** If there is a PT_TLS segment, its module ID as used in TLS relocations, else zero */ | |
get dlpi_tls_modid() { | |
return this.isGlibc2_4 ? this.ptr.getBigUint64(48) : undefined; | |
} | |
/** | |
* The address of the calling thread's instance of this module's PT_TLS segment, if it has one and it has been | |
* allocated in the calling thread, otherwise a null pointer */ | |
get dlpi_tls_data() { | |
return this.isGlibc2_4 ? this.ptr.getBigUint64(56) : undefined; | |
} | |
private ptr: Deno.UnsafePointerView; | |
private isGlibc2_4: boolean; | |
constructor(value: Deno.PointerValue, size: number | bigint) { | |
this.ptr = new Deno.UnsafePointerView(value!); | |
this.isGlibc2_4 = size > 32; | |
} | |
} | |
export enum SegmentType { | |
/* Loadable program segment */ | |
LOAD = 1, | |
/* Dynamic linking information */ | |
DYNAMIC = 2, | |
/* Program interpreter */ | |
INTERP = 3, | |
/* Auxiliary information */ | |
NOTE = 4, | |
/* Reserved */ | |
SHLIB = 5, | |
/* Entry for header table itself */ | |
PHDR = 6, | |
/* Thread-local storage segment */ | |
TLS = 7, | |
/* GCC .eh_frame_hdr segment */ | |
GNU_EH_FRAME = 0x6474e550, | |
/* Indicates stack executability */ | |
GNU_STACK = 0x6474e551, | |
/* Read-only after relocation */ | |
GNU_RELRO = 0x6474e552, | |
/** GNU property */ | |
GNU_PROPERTY = 0x6474e553, | |
} | |
export const segmentTypeToString = (value: SegmentType) => { | |
switch (value) { | |
case SegmentType.LOAD: | |
return "PT_LOAD"; | |
case SegmentType.DYNAMIC: | |
return "PT_DYNAMIC"; | |
case SegmentType.INTERP: | |
return "PT_INTERP"; | |
case SegmentType.NOTE: | |
return "PT_NOTE"; | |
case SegmentType.SHLIB: | |
return "PT_SHLIB"; | |
case SegmentType.PHDR: | |
return "PT_PHDR"; | |
case SegmentType.TLS: | |
return "PT_TLS"; | |
case SegmentType.GNU_EH_FRAME: | |
return "PT_GNU_EH_FRAME"; | |
case SegmentType.GNU_STACK: | |
return "PT_GNU_STACK"; | |
case SegmentType.GNU_RELRO: | |
return "PT_GNU_RELRO"; | |
case SegmentType.GNU_PROPERTY: | |
return "PT_GNU_PROPERTY"; | |
default: | |
return "[other (0x" + (value as number).toString(16) + ")]"; | |
} | |
}; | |
export class Elf32_Phdr { | |
/** Segment type */ | |
get p_type() { | |
return this.ptr.getUint32(0) as SegmentType; | |
} | |
/** Segment file offset */ | |
get p_offset() { | |
return this.ptr.getUint32(4); | |
} | |
/** Segment virtual address */ | |
get p_vaddr() { | |
return this.ptr.getUint32(8); | |
} | |
/** Segment physical address */ | |
get p_paddr() { | |
return this.ptr.getUint32(12); | |
} | |
/** Segment size in file */ | |
get p_filesz() { | |
return this.ptr.getUint32(16); | |
} | |
/** Segment size in memory */ | |
get p_memsz() { | |
return this.ptr.getUint32(20); | |
} | |
/** Segment flags */ | |
get p_flags() { | |
return this.ptr.getUint32(24); | |
} | |
/** Segment alignment */ | |
get p_align() { | |
return this.ptr.getUint32(28); | |
} | |
static sizeOf() { | |
return 32; | |
} | |
private ptr: Deno.UnsafePointerView; | |
constructor(value: Deno.PointerValue) { | |
this.ptr = new Deno.UnsafePointerView(value!); | |
} | |
getVirtualAddress(info: dl_phdr_info) { | |
return BigInt(info.dlpi_addr) + BigInt(this.p_vaddr); | |
} | |
} | |
export class Elf64_Phdr { | |
/** Segment type */ | |
get p_type() { | |
return this.ptr.getUint32(0) as SegmentType; | |
} | |
/** Segment flags */ | |
get p_flags() { | |
return this.ptr.getUint32(4); | |
} | |
/** Segment file offset */ | |
get p_offset() { | |
return this.ptr.getBigUint64(8); | |
} | |
/** Segment virtual address */ | |
get p_vaddr() { | |
return this.ptr.getBigUint64(16); | |
} | |
/** Segment physical address */ | |
get p_paddr() { | |
return this.ptr.getBigUint64(24); | |
} | |
/** Segment size in file */ | |
get p_filesz() { | |
return this.ptr.getBigUint64(32); | |
} | |
/** Segment size in memory */ | |
get p_memsz() { | |
return this.ptr.getBigUint64(40); | |
} | |
/** Segment alignment */ | |
get p_align() { | |
return this.ptr.getBigUint64(48); | |
} | |
static sizeOf() { | |
return 56; | |
} | |
private ptr: Deno.UnsafePointerView; | |
constructor(value: Deno.PointerValue) { | |
this.ptr = new Deno.UnsafePointerView(value!); | |
} | |
getVirtualAddress(info: dl_phdr_info) { | |
return BigInt(info.dlpi_addr) + BigInt(this.p_vaddr); | |
} | |
} | |
const encoder = new TextEncoder(); | |
const decoder = new TextDecoder(); | |
export const cstr = (value: string) => encoder.encode(value + "\0"); | |
export const cstrFrom = (value: Uint8Array) => decoder.decode(value); | |
export const getProcessName = (bufferSize = 256) => { | |
const buffer = new Uint8Array(bufferSize); | |
libc.symbols.readlink( | |
cstr(`/proc/${Deno.pid}/exe`), | |
buffer.buffer, | |
buffer.byteLength, | |
); | |
return cstrFrom(buffer); | |
}; | |
export enum DlOpenMode { | |
/** Lazy function call binding. */ | |
LAZY = 0x00001, | |
/** Immediate function call binding. */ | |
NOW = 0x00002, | |
/** Mask of binding time value. */ | |
BINDING_MASK = 0x3, | |
/** Do not load the object. */ | |
NOLOAD = 0x00004, | |
/** Use deep binding. */ | |
DEEPBIND = 0x00008, | |
/** | |
* If the following bit is set in the MODE argument to `dlopen', the symbols of the loaded object and its dependencies | |
* are made visible as if the object were linked directly into the program. */ | |
GLOBAL = 0x00100, | |
/** | |
* Unix98 demands the following flag which is the inverse to RTLD_GLOBAL. The implementation does this by default and | |
* so we can define the value to zero. */ | |
LOCAL = 0, | |
/** Do not delete object when closed. */ | |
NODELETE = 0x01000, | |
} | |
const callback = new Deno.UnsafeCallback( | |
{ | |
parameters: ["pointer", "usize", "pointer"], | |
result: "i32", | |
} as const, | |
( | |
infoPtr: Deno.PointerValue, | |
size: number | bigint, | |
_data: Deno.PointerValue<unknown>, | |
) => { | |
if (infoPtr) { | |
const info = new dl_phdr_info(infoPtr, size); | |
console.log( | |
"Name: ", | |
'"' + info.dlpi_name + '"', | |
"(" + info.dlpi_phnum + " segments)", | |
); | |
for (let i = 0; i < info.dlpi_phnum; i += 1) { | |
const entry = new Elf64_Phdr( | |
Deno.UnsafePointer.offset(info.dlpi_phdr!, i * Elf64_Phdr.sizeOf()), | |
); | |
const addr = entry.getVirtualAddress(info); | |
console.log( | |
" ", | |
i.toString().padStart(2) + ": [" + | |
("0x" + addr.toString(16)).padStart(14) + | |
"; memsz:", | |
entry.p_memsz.toString(16).padStart(7) + "]", | |
"flags: 0x" + entry.p_flags.toString(16) + ";", | |
segmentTypeToString(entry.p_type), | |
); | |
} | |
} | |
return 0; | |
}, | |
); | |
libc.symbols.dl_iterate_phdr(callback.pointer, null); | |
callback.close(); | |
console.log(getProcessName()); | |
// const mod = libc.symbols.dlopen( | |
// cstr("libgcc_s.so.1"), | |
// DlOpenMode.NOLOAD | DlOpenMode.NOW, | |
// ); | |
// if (!mod) { | |
// const err = libc.symbols.dlerror(); | |
// console.log(new Deno.UnsafePointerView(err!).getCString()); | |
// Deno.exit(1); | |
// } | |
// libc.symbols.dlclose(mod); | |
libc.close(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment