AI Agent Codex 实现 Switch 2 软件越狱与代码执行
Jailbreaking the Switch 2
展示 AI Agent 在复杂逆向工程与漏洞挖掘中的实战能力,技术细节详实且可复现思路,值得安全研究者关注。
jailbreaking the switch 2
越狱 Switch 2
ns2jb is a software-only jailbreak of the switch 2 for the current latest firmware 23.0.1
ns2jb 是针对 Switch 2 当前最新固件 23.0.1 的纯软件越狱
October 18, 2026
2026年10月18日
cs
Introduction
简介
You can get a copy of the exploit from the ns2jb repository.
你可以从 ns2jb 仓库获取漏洞利用程序的副本。
ns2jb is a software-only, tethered jailbreak for the Switch 2. It allows running arbitrary native code as any ordinary sysmodule, and as the built-in fs sysmodule. This lets us run homebrew, reverse engineer and mod Switch 2 games, back up save files off the Switch, and access most of the Switch 2’s peripherals through MMIO. To my knowledge, it’s the first such exploit for the Switch 2.
ns2jb 是 Switch 2 的纯软件、需连接(tethered)越狱工具。它允许以任意普通 sysmodule 以及内置的 fs sysmodule 身份运行任意原生代码。这使得我们能够运行自制软件、逆向工程和修改 Switch 2 游戏、将存档文件备份到 Switch 之外,并通过 MMIO 访问 Switch 2 的大部分外设。据我所知,这是首个针对 Switch 2 的此类漏洞利用。
To demonstrate the capabilities of this jailbreak, Codex modded a Switch 2 exclusive, Mario Kart World. Thanks to my friends in Dabney House who helped me choose characters to add and playtest the game.
为了展示该越狱工具的能力,Codex 对 Switch 2 独占游戏《马里奥赛车世界》进行了模组修改。感谢 Dabney House 的朋友们帮助我选择要添加的角色并参与游戏测试。
Watch the first gameplay video
观看第一段 gameplay 视频
Watch the second gameplay video
观看第二段 gameplay 视频
▶ 0:09 ▶ 0:05
cinnamoroll, red, and tole tole racing
肉桂狗、红色角色和 Tole Tole 竞速
Setup
设置
The very first thing I did was set up a harness around the Switch 1 and 2 so Codex could, ideally, jailbreak the Switch 2 without me:
我做的第一件事是为 Switch 1 和 Switch 2 搭建了一个测试平台,以便 Codex 理想情况下能在没有我协助的情况下越狱 Switch 2:
“set a goal to jailbreak the switch 2. make no mistakes”
“设定目标:越狱 Switch 2。绝不犯错。”
Edit: Many people have been asking me if I actually used this prompt. No, it’s just a silly caption. I set a /goal for userland code execution, but that didn’t succeed. The jailbreak itself took substantial effort for ~2 weeks. The Mario Kart World, however, was entirely vibecoded in a day, and was just for fun.
编辑:许多人问我是否真的使用了这个提示词。不,这只是一个愚蠢的标题。我为用户态代码执行设定了一个 /goal,但并未成功。越狱本身花了大约两周时间才完成。然而,《马里奥赛车世界》完全是在一天内通过 vibecoding 完成的,纯属娱乐。
The JetKVMs allow Codex to send controller inputs to the Switches over USB and receive video output over HDMI. We also need a way to press the power button—when the Switch 2 crashes, it needs a button press to restart.
JetKVM 允许 Codex 通过 USB 向 Switch 发送控制器输入,并通过 HDMI 接收视频输出。我们还需要一种方式来按下电源键——当 Switch 2 崩溃时,需要按键才能重启。
That’s what the Bluetooth light switch flicker from Amazon is for. When the lever comes down, it presses the power button. Once the power button is fully pressed, the rest of the lever’s torque lifts the mechanism.
这就是亚马逊上那个蓝牙灯光开关拨杆的作用。当杠杆向下压时,它会按下电源键。一旦电源键被完全按下,杠杆剩余的扭矩会抬起整个机构。
watch the power button get pressed
观看电源键被按下的视频
Getting code execution
获取代码执行权限
WebKit is a popular target for console jailbreaks, so it’s unsurprising that Codex has picked it for initial code execution.
WebKit 是主机越狱的热门目标,因此 Codex 选择它作为初始代码执行的突破口也就不足为奇了。
Code execution through the browser
通过浏览器执行代码
It claims to have “native control-flow hijack” by taking advantage of a WebKit n-day loaded via a custom Wi-Fi captive portal.
它声称通过自定义 Wi-Fi 强制门户加载的 WebKit n-day 漏洞实现了“原生控制流劫持”。
We’d like to get code execution in microkernel services (or “sysmodules” on the Switch), so we can run homebrew, mod games, download saves, etc. Unfortunately, this approach of replacing one JavaScript function object with another doesn’t generalize beyond existing functions—we’d need a new way to make arbitrary IPC calls. That looks difficult.
我们希望在微内核服务(在 Switch 上称为“sysmodules”)中实现代码执行,以便运行自制软件、修改游戏、下载存档等。不幸的是,这种用一个 JavaScript 函数对象替换另一个的方法无法泛化到现有函数之外——我们需要一种新的方式来发起任意的 IPC 调用。这看起来很难。
Why?
为什么?
- WebKit has execute-only memory, or XOM, so even with arbitrary memory RW, we don’t know where a gadget that could perform arbitrary IPC calls is.
- Even if we did, perhaps by parsing relocation data, it’s not clear how we’d construct a signed pointer for it because of PAC.
- And even if we could, with some signing gadget, it’s not clear how we’d call this pointer, since CFI type-checks most call sites.
- WebKit 具有仅执行内存(XOM),因此即使拥有任意内存的读写权限,我们也无法确定哪里存在能够执行任意 IPC 调用的 gadget。
- 即使我们通过解析重定位数据找到了它,由于 PAC(指针认证码)的存在,也不清楚如何为其构造一个签名后的指针。
- 即使我们能够借助某个签名 gadget 做到这一点,由于 CFI(控制流完整性)会对大多数调用点进行类型检查,也不清楚如何调用这个指针。
Codex has reset the goal to complete these steps. It’s a side bet; it may succeed1, but let’s be real: the accessibility of WebKit and the Nintendo BBP reward have probably incentivized many before me to look here2.
Codex 将目标重置为完成这些步骤。这是一个旁注;它可能会成功¹,但让我们面对现实:WebKit 的可访问性以及任天堂 BBP 奖励可能已经激励了在我之前的许多人来研究这里²。
While that runs, let’s explore other options.
在此期间,让我们探索其他选项。
Code execution through smilehax2
通过 smilehax2 实现代码执行
My idea was to target Switch 1 games, for two reasons:
我的想法是瞄准 Switch 1 的游戏,原因有二:
- They’re built by developers. It’s less likely we’ll need to deal with PAC and CFI, especially because the Switch 1 does not support PAC.
- Since the Switch 1 root of trust is owned, we can decrypt the code and read it.
- 它们是由开发者构建的。我们不太可能需要处理 PAC 和 CFI,尤其是因为 Switch 1 不支持 PAC。
- 由于 Switch 1 的信任根已被掌控,我们可以解密代码并读取它。
Maybe we can still get the upsides of the browser attack surface—orchestrating from JavaScript, using existing n-days, etc—while avoiding the downsides of WebKit mitigations by finding a Switch 1 game that embeds a browser? Something analogous to Electron, but for games, so we could pull off an exploit like this Discord RCE. I can’t disclose this approach’s findings publicly at the moment, but luckily, there are other Switch 1 games.
也许我们仍然可以获得浏览器攻击面的优势——从 JavaScript 进行编排、利用现有的 n-days 漏洞等——同时通过寻找嵌入浏览器的 Switch 1 游戏来避免 WebKit 缓解措施的劣势。类似于 Electron,但是用于游戏的版本,这样我们就可以实施类似 Discord RCE 这样的漏洞利用。我目前不能公开披露这种方法的研究结果,但幸运的是,还有其他 Switch 1 游戏。
In particular, I’m interested in SmileBASIC. This is because it’s been pwned before, in smilehax, giving userspace code execution on the 3DS. It’s a giant C++ codebase; surely, if there was an exploitable memory corruption in the BASIC interpreter, there can be another.
特别是,我对 SmileBASIC 感兴趣。这是因为它在 smilehax 中曾被攻破过,从而在 3DS 上实现了用户态代码执行。它是一个庞大的 C++ 代码库;毫无疑问,如果 BASIC 解释器中存在可利用的内存损坏漏洞,那么很可能还存在另一个。
Codex found a useful 32-bit integer overflow in the interpreter. When removing items from an array, the interpreter checked position + count <= array.length, but the sum could wrap and become negative. To fill the gap created by removing elements, it’s supposed to shift the remaining elements left, like so:
Codex 在解释器中发现了一个有用的 32 位整数溢出。当从数组中移除元素时,解释器会检查 position + count <= array.length,但总和可能会回绕并变为负数。为了填补移除元素后留下的空隙,本应将剩余元素向左移动,如下所示:
memmove(
backing + position * 8, // start of the gap
backing + (position + count) * 8, // elements after the gap
(length - position) * 8 // bytes to copy
);Say we have a string array R of length 4, and we run REMOVE R,&H60000000,&H3FFFFFFF. position + count wraps to 0x9fffffff. As a negative signed 32-bit integer, it’s less than array.length. The multiplications by 8 also wrap, so we end up performing memmove(backing, backing - 8, 32), pulling the previous 8 bytes into the array. The command does report an Out of memory error, but it doesn’t matter, since the copy has happened already.
假设我们有一个长度为 4 的字符串数组 R,并执行 REMOVE R, &H60000000, &H3FFFFFFF。position + count 回绕到 0x9fffffff。作为有符号 32 位整数,它小于 array.length。乘以 8 的操作也会回绕,因此最终执行的是 memmove(backing, backing - 8, 32),将之前的 8 字节拉入数组中。该命令确实报告了内存不足错误,但这并不重要,因为复制操作已经发生。
This is useful because the 8 bytes preceding any array in SmileBASIC contain a pointer to the array itself! After the REMOVE, operations on R[0] will treat the array backing as if it were a string.
这很有用,因为在 SmileBASIC 中,任何数组前面的 8 个字节都包含一个指向数组自身的指针!在 REMOVE 之后,对 R[0] 的操作会将数组后备存储视为字符串。
Since a pointer occupies what would be the length field of R[0], the string length is enormous, and we can perform reads and writes out of bounds. We read and write to memory by copying to and from substrings of R[0].
由于指针占据了 R[0] 的长度字段位置,字符串长度变得巨大,我们可以进行越界读写。我们通过向 R[0] 的子串复制和从子串复制来读写内存。
This was a Switch 1 game, so the SmileBASIC agent could work on the exploit on my Switch 1, while the WebKit agent worked on the Switch 2. After some time, it got ROP.
这是一个 Switch 1 游戏,因此 SmileBASIC 代理可以在我的 Switch 1 上工作以利用漏洞,而 WebKit 代理则在 Switch 2 上工作。经过一段时间,它获得了 ROP(返回导向编程)能力。
Getting sysmodule RW
获取 sysmodule 的读写权限
Okay, now we need to compromise a more privileged process. We’re interested in ordinary sysmodules and built-in sysmodules.
好的,现在我们需要攻破一个权限更高的进程。我们关注普通的 sysmodule 和内置 sysmodule。
Which sysmodule should we pursue? We don’t have their code, so it’s unclear which sysmodules have which privileges.
我们应该追求哪个 sysmodule?我们没有它们的代码,因此不清楚哪些 sysmodule 拥有哪些权限。
I gave a few Codex instances the goal of getting sysmodule code execution. Most chose to focus on audio and ldn. I think that’s because audio and ldn have had bugs on the Switch 2. Codex also looked at the NV service, probably because GPU drivers are generally fruitful?
我让几个 Codex 实例的目标是获取 sysmodule 的代码执行权限。大多数选择专注于 audio 和 ldn。我认为这是因为 audio 和 ldn 在 Switch 2 上存在过漏洞。Codex 还查看了 NV 服务,可能是因为 GPU 驱动程序通常容易发现漏洞?
The fs sysmodule
fs sysmodule
In the meantime, I started looking at the built-in fs sysmodule, because:
与此同时,我开始研究内置的 fs sysmodule,原因如下:
- The Switch 1 code shows that it’s able to serve decrypted code.
- There’s a lot of code.
- It’s probably similar to the fs module in the Switch 1, since the filesystem API didn’t change very much.
- Switch 1 的代码显示它能够提供解密后的代码。
- 代码量很大。
- 它可能与 Switch 1 中的 fs 模块相似,因为文件系统 API 并没有发生太大变化。
The first reason is the most attractive to me. Even if we can’t get a useful jailbreak from fs, we can at least decrypt some of the other sysmodules so Codex can find bugs in them. Other sysmodules like ldr and ro look enticing because they have permission to map executable memory3, but they don’t have large attack surfaces, especially from SmileBASIC. We’ll come back to them later.
第一个原因对我最有吸引力。即使我们无法通过 fs 获得有用的越狱,我们至少可以解密其他一些 sysmodule,以便 Codex 能在其中找到漏洞。ldr 和 ro 等其他 sysmodule 看起来很诱人,因为它们有权映射可执行内存3,但它们没有大的攻击面,尤其是从 SmileBASIC 的角度来看。我们稍后会再回到它们身上。
Buffer overflow with path normalization
路径归一化导致的缓冲区溢出
An IFileSystem handle has an IPC command, GetEntryType. This takes a path string, normalized with Path::Normalize.
IFileSystem 句柄有一个 IPC 命令 GetEntryType。它接受一个由 Path::Normalize 归一化的路径字符串。
Paths containing a parent-directory component with a backslash, like /..\, call into a helper that allocates a temporary heap buffer of 0x301 bytes. Our path is copied in, but it can be larger than the buffer, causing a buffer overflow. lol
包含带有反斜杠的父目录组件的路径(例如 /..\)会调用一个辅助函数,该函数分配一个大小为 0x301 字节的临时堆缓冲区。我们的路径会被复制进去,但它可能比缓冲区更大,从而导致缓冲区溢出。lol
We’ll need a good victim allocation to overflow into. Codex found that the BufferedStorage object allocates a cache entry for its files. If that entry has its dirty bit set, reads and writes to the file follow a pointer to cached data. We can hijack that for arbitrary read/write4.
我们需要一个合适的目标分配来溢出其中。Codex 发现 BufferedStorage 对象为其文件分配了一个缓存条目。如果该条目的脏位被设置,对文件的读写操作将遵循指向缓存数据的指针。我们可以利用这一点实现任意读/写4。
更进一步:量化金融体系
看懂新闻只是起点——沿量化金融路径,把它变成能交付的工程能力