feat: typing

This commit is contained in:
Helloyunho
2025-12-31 16:09:47 +09:00
parent 65fb9671e4
commit 69f4a63b43
5 changed files with 260 additions and 39 deletions
+8 -1
View File
@@ -9,7 +9,13 @@ export default defineConfig([
files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
plugins: { js },
extends: ['js/recommended'],
languageOptions: { globals: globals.browser },
languageOptions: {
globals: {
...globals.browser,
jsmaf: 'readonly',
log: 'readonly',
}
},
},
{ files: ['**/*.js'], languageOptions: { sourceType: 'script' } },
tseslint.configs.recommended,
@@ -27,6 +33,7 @@ export default defineConfig([
'no-unused-expressions': 'off',
'no-fallthrough': 'off',
'no-new-native-nonconstructor': 'off', // we use our own BigInt
'no-extend-native': 'off', // we extend native for better usage
// TS duplicates
'@typescript-eslint/no-unused-vars': 'off',
+34 -37
View File
@@ -443,8 +443,11 @@ DataView.prototype.setBigInt = function (byteOffset, value, littleEndian) {
this.setUint32(byteOffset + 4, value.hi(), littleEndian)
}
/**
* @type {struct}
*/
var struct = {
register: function (name, fields) {
create: function (name, fields) {
if (name in this) {
throw new Error(`${name} already registered in struct !!`)
}
@@ -457,8 +460,6 @@ var struct = {
}
}
this[name] = cls
cls.tname = name
cls.sizeof = sizeof
cls.fields = fields
@@ -466,15 +467,8 @@ var struct = {
for (var info of infos) {
struct.define_property(cls, info)
}
},
unregister: function (name) {
if (!(name in this)) {
throw new Error(`${name} not registered in struct !!`)
}
delete this[name]
return true
return cls
},
parse: function (fields) {
var infos = []
@@ -663,7 +657,7 @@ var struct = {
}
}
struct.register('NotificationRequest', [
var NotificationRequest = struct.create('NotificationRequest', [
{ type: 'Int32', name: 'type' },
{ type: 'Int32', name: 'reqId' },
{ type: 'Int32', name: 'priority' },
@@ -804,6 +798,9 @@ master_addr = new BigInt(master[5], master[4])
debug(`master_addr: ${master_addr}`)
log('Achieved ARW !!')
/**
* @type {mem}
*/
var mem = {
allocs: new Map(),
read8: function (addr) {
@@ -875,6 +872,9 @@ var mem = {
}
}
/**
* @type {utils}
*/
var utils = {
base_addr: function (func_addr) {
var module_info_addr = mem.malloc(0x130)
@@ -892,9 +892,9 @@ var utils = {
return base_addr
},
notify: function (msg) {
var notify_addr = mem.malloc(struct.NotificationRequest.sizeof)
var notify_addr = mem.malloc(NotificationRequest.sizeof)
var notify = new struct.NotificationRequest(notify_addr)
var notify = new NotificationRequest(notify_addr)
for (var i = 0; i < msg.length; i++) {
notify.message[i] = msg.charCodeAt(i) & 0xFF
@@ -902,13 +902,13 @@ var utils = {
notify.message[msg.length] = 0
var fd = fn.open('/dev/notification0', 1, 0)
var fd = open('/dev/notification0', 1, 0)
if (fd.lt(0)) {
throw new Error('Unable to open /dev/notification0 !!')
}
fn.write(fd, notify.addr, struct.NotificationRequest.sizeof)
fn.close(fd)
write(fd, notify.addr, NotificationRequest.sizeof)
close(fd)
mem.free(notify_addr)
},
@@ -1019,6 +1019,9 @@ var gadgets = {
PUSH_RAX_POP_RBP_RET: jsc_addr.add(0x4E82B9)
}
/**
* @type {rop}
*/
var rop = {
idx: 0,
base: 0x2500,
@@ -1132,8 +1135,12 @@ var rop = {
}
}
/**
* @type {fn}
*/
var fn = {
register: function (input, name, ret) {
// args is just for typing, not used
create: function (input, _args, ret) {
if (name in this) {
throw new Error(`${name} already registered in fn !!`)
}
@@ -1243,23 +1250,13 @@ var fn = {
Object.defineProperty(f, 'addr', { value: addr })
fn[name] = f
},
unregister (name) {
if (!(name in this)) {
log(`${name} not registered in fn !!`)
return false
}
delete fn[name]
return true
return f
}
}
rop.init()
fn.register(libc_addr.add(0x5F0), 'sceKernelGetModuleInfoForUnwind', 'bigint')
var sceKernelGetModuleInfoForUnwind = fn.create(libc_addr.add(0x5F0), ['bigint'], 'bigint')
var libkernel_addr = utils.base_addr(_error_addr)
@@ -1314,12 +1311,12 @@ syscalls.init(libkernel_addr)
debug(`Found ${syscalls.map.size} syscalls`)
fn.register(_error_addr, '_error', 'bigint')
fn.register(strerror_addr, 'strerror', 'string')
fn.register(0x14, 'getpid', 'bigint')
fn.register(0x29, 'dup', 'bigint')
fn.register(0x4, 'write', 'bigint')
fn.register(0x5, 'open', 'bigint')
fn.register(0x6, 'close', 'bigint')
var _error = fn.create(_error_addr, [], 'bigint')
var strerror = fn.create(strerror_addr, ['bigint'], 'string')
var getpid = fn.create(0x14, [], 'bigint')
var dup = fn.create(0x29, ['bigint'], 'bigint')
var write = fn.create(0x4, ['bigint', 'bigint', 'number'], 'bigint')
var open = fn.create(0x5, ['string', 'number', 'number'], 'bigint')
var close = fn.create(0x6, ['bigint'], 'bigint')
utils.notify('UwU')
+2 -1
View File
@@ -23,5 +23,6 @@
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
},
"include": ["**/*.ts", "**/*.js", "**/*.js.aes"],
}
+189
View File
@@ -0,0 +1,189 @@
declare namespace vue {
declare class BigInt {
buf: ArrayBuffer
i8: Int8Array
u8: Uint8Array
i16: Int16Array
u16: Uint16Array
i32: Int32Array
u32: Uint32Array
f32: Float32Array
f64: Float64Array
static readonly Zero: BigInt
static readonly One: BigInt
static readonly TYPE_MAP: {
Int8Array: 'i8',
Uint8Array: 'u8',
Int16Array: 'i16',
Uint16Array: 'u16',
Int32Array: 'i32',
Uint32Array: 'u32',
Float32Array: 'f32',
Float64Array: 'f64',
}
constructor ()
constructor (value: number | string | BigInt | ArrayLike<number>)
constructor (hi: number, lo: number)
toString (): string
endian (): void
lo (): number
hi (): number
d (): number
jsv (): BigInt
cmp (val: BigInt): -1 | 0 | 1
eq (val: BigInt): boolean
neq (val: BigInt): boolean
gt (val: BigInt): boolean
gte (val: BigInt): boolean
lt (val: BigInt): boolean
lte (val: BigInt): boolean
add (val: BigInt): BigInt
sub (val: BigInt): BigInt
mul (val: BigInt): BigInt
divmod (val: BigInt): { q: BigInt, r: BigInt }
div (val: BigInt): BigInt
mod (val: BigInt): BigInt
xor (val: BigInt): BigInt
and (val: BigInt): BigInt
or (val: BigInt): BigInt
neg (): BigInt
shl (count: number): BigInt
shr (count: number): BigInt
}
}
declare global {
interface DataView {
getBigInt (byteOffset: number, littleEndian?: boolean): vue.BigInt
setBigInt (byteOffset: number, value: vue.BigInt, littleEndian?: boolean): void
}
function log (msg: string): void
function debug (msg: string): void
}
type NonPointerTypes = `${'Int' | 'Uint'}${8 | 16 | 32 | 64}`
type PointerTypes = `${NonPointerTypes}*`
type StructName<T> = T extends `${infer Name}[${string}]` ? Name : T
type StructCount<T> = T extends { count: infer C extends number } ? C :
T extends { name: `${string}[${infer C extends number}]` } ? C :
1
interface BaseStructField {
size?: number
offset?: number
}
interface PointerStructField extends BaseStructField {
name: string
type: PointerTypes
pointer?: true
count?: never
}
interface NonPointerStructFieldWithCountName extends BaseStructField {
name: `${string}[${number}]`
type: NonPointerTypes
pointer?: false
count?: number
}
interface NonPointerStructFieldWithCount extends BaseStructField {
name: name
type: NonPointerTypes
pointer?: false
count: number
}
interface NonPointerStructField extends BaseStructField {
name: name
type: NonPointerTypes
pointer?: false
count?: 1
}
type StructField = PointerStructField | NonPointerStructFieldWithCount | NonPointerStructFieldWithCountName | NonPointerStructField
type StructReturnType<const T extends StructField> =
StructCount<T> extends (1 | undefined) ? (
T['pointer'] extends true ? vue.BigInt :
T['type'] extends 'Int64' ? vue.BigInt :
T['type'] extends 'Uint64' ? vue.BigInt :
number
) : (
T['type'] extends 'Int8' ? Int8Array :
T['type'] extends 'Uint8' ? Uint8Array :
T['type'] extends 'Int16' ? Int16Array :
T['type'] extends 'Uint16' ? Uint16Array :
T['type'] extends 'Int32' ? Int32Array :
T['type'] extends 'Uint32' ? Uint32Array :
T['type'] extends 'Int64' ? Int32Array :
T['type'] extends 'Uint64' ? Uint32Array :
never
)
type StructInstance<const T extends readonly StructField[]> = {
[K in T[number] as StructName<K['name']>]: StructReturnType<K>
} & {
addr: vue.BigInt
}
interface StructConstructor<const Name extends string, const T extends readonly StructField[]> {
new (addr: vue.BigInt): StructInstance<T>
readonly tname: Name
readonly sizeof: number
readonly fields: T
}
declare type struct = {
create<const Name extends string, const T extends StructField[]>(name: Name, fields: T): StructConstructor<Name, T>
}
declare type mem = {
allocs: Map<vue.BigInt, ArrayBufferLike>
read8 (addr: vue.BigInt): vue.BigInt
read4 (addr: vue.BigInt): vue.BigInt
write8 (addr: vue.BigInt, value: vue.BigInt): void
write4 (addr: vue.BigInt, value: vue.BigInt): void
write1 (addr: vue.BigInt, value: vue.BigInt): void
addrof (obj: unknown): vue.BigInt
fakeobj (addr: vue.BigInt): unknown
malloc (size: number): vue.BigInt
free (addr: vue.BigInt): void
free_all (): void
}
declare type utils = {
base_addr (func_addr: vue.BigInt): vue.BigInt
notify (msg: string): void
str (addr: vue.BigInt): string
cstr (str: string): vue.BigInt
get_backing (view: ArrayBufferLike): vue.BigInt
set_backing (view: ArrayBufferLike, addr: vue.BigInt): void
}
declare type rop = {
init (): void
free (): void
reset (): void
push (val: vue.BigInt): void
execute (insts: vue.BigInt[], store_addr: vue.BigInt, store_size: number): void
fake_builtin (addr: vue.BigInt): (useless1: number, useless2: number, useless3: number, addr: vue.BigInt) => void
store (insts: vue.BigInt[], addr: vue.BigInt, index: number): void
load (insts: vue.BigInt[], addr: vue.BigInt, index: number): void
}
type ArgTypeToRealType<T> = T extends 'bigint' ? vue.BigInt :
T extends 'boolean' ? boolean :
T extends 'number' ? number :
T extends 'string' ? string :
never
declare type fn = {
create <const Args extends ('bigint' | 'number' | 'boolean' | 'string')[], Return extends ('bigint' | 'boolean' | 'string')>(addr: vue.BigInt | number, args: Args, ret: Return): (...func_args: { [K in keyof Args]: ArgTypeToRealType<Args[K]> }) => ArgTypeToRealType<Return>
}
+27
View File
@@ -0,0 +1,27 @@
declare function include (path: string): unknown
declare namespace jsmaf {
declare class Text {
x: number
y: number
background: string
url: string
text: string
constructor ()
}
declare class WebSocketServer {
port: number
onmessage: (clientID: number, data: string) => void
constructor ()
broadcast (data: string): void
}
declare namespace root {
declare const children: Text[]
}
declare function eval (code: string): unknown
}