ninjhax is the name I gave to the solution I released back in November 2014 to allow users to run homebrew software on their 3DS/2DS/New 3DS with the (at the time) latest firmware version. Because the 9.3 firmware update patched a critical exploit used in this solution and Gateway recently made available a warez-ready solution exploiting the other critical vulnerability used by ninjhax, I’ve decided now’s as good a time as any to share the technical details.

The full source code can be found here.

Let’s get started. ninjhax works in 4 stages and almost as many exploits : we first get ROP execution, then we get code execution proper, then we get access to new services by launching and taking over another process, and then we get higher privileges by exploiting a system module.

Stage 1 : Getting ROP under Cubic Ninja (aka ninjhax proper)

Description : Cubic Ninja (a game published by Ubisoft) contains a level editor. User-made levels can be saved to the gamecard’s save partition and shared with other users through QR codes (and, in the japanese version, streetpass). Once the encryption and compression (blowfish and some lz variant, respectively) schemes are understood, it is possible to generate your own custom level QR codes which the game will read.
Problem : the format that the levels are stored in contains variable-length elements. When parsed, those elements are routinely loaded to fixed-size buffers located on the stack, with the length of the elements in question left unchecked. Of course, this leads to a trivially-exploitable stack smash, which gives us ROP capabilities. It should be noted that the entire level file is loaded to the heap beforehand and that there is no limit to level file size, making it extremely convenient. Worse still, it is possible to trigger the exact same crash with a forged QR code.
Limitations : this gives us ROP capabilities, not code execution, as the 3DS’s OS strictly enforces NX (ie executable memory pages are never writable).Additionally, QR codes can’t have an unlimited size, and the stack isn’t huge either. We overcome the first limitation with stage 2 and the second one by downloading a secondary payload through the http:C service, and then saving that payload to the savegame. Another thing to take into account is that Cubic Ninja does not have either SDMC or ldr:ro access (see stage 4 for why the latter matters).
Fix : fixing this with the game’s source code should be trivial; just add a few well placed size checks. Fixing it without the source code (which might be necessary given how the studio went under ?) would be more difficult. It shouldn’t be hard to write a piece of code that will scan the savegame to check that the .map files located under the /edit/ directory are sane; however, that would not be enough as the QR code reading would still be exploitable. With that in mind, it might be possible to hack up the game’s compiled code to prevent the crash, though the solution won’t be pretty.
To be more precise, the problem seems to lie mainly with the function located at 0x001F5F38 (in EU/US version, haven’t checked JPN). This function seems designed to copy data a packed structure to some external buffer. It takes as arguments a pointer to the source-data struct (R0), a pointer to the destination buffer (R1) and the size of the destination buffer (R2); it fails if the input data is bigger than the output buffer. This makes it seem safe ! However, it includes a “backdoor” : when R2 is 0, there are no checks made on the input data size. Unfortunately, this R2 = 0 backdoor is used throughout the game’s code, often with stack buffers, making it very unsafe. The good news is that it’s possible that by patching the code to specify a proper R2, the crash will be fixed. Unfortunately the code usually does not seem to check for errors output by function 0x001F5F38, so that might not quite be enough, but it’d be a start.

Stage 2 : Getting code execution through unsafe GPU DMA (aka gspwn, gpuhax)

Description : the 3DS’s GPU is capable of accessing certain sections of memory with reading and writing capabilities. These regions include VRAM and parts of FCRAM. This is used for a number of things, including commands which let us use the GPU to copy data across memory regions through the GSP module, reading texture/vertex object data, or writing to the framebuffer when rendering.
Problem : while the GPU having access to these regions of memory isn’t necessarily an issue, the fact that GSP doesn’t do *any* checks on input/output addresses for GPU commands it processes definitely is. The GPU has read/write access to the entire 0×20000000-0×26800000 memory range (on old 3DS; iirc the new 3DS range is 0×20000000-0x2D000000 which basically gives the same results), and it is therefore possible to access those regions by using GX commands 2, 3 and 4. This “thankfully” leaves out the BASE memory region at the end of FCRAM (intentional ?), but still opens the door to easily obtainable code execution as well as other types of attacks (time of use/time of check comes to mind).
Limitations : while this lets us execute native code by overwriting the currently running application’s .text region, it doesn’t immediately let us do much more. Every other currently running application has its .text hidden away in the 0×26800000+ memory region, including home menu.
Fix : the fix for this is, interestingly enough, both trivial and fairly elaborate. The thing to see here is that there are (at least) three ways of exploiting this :
- GX commands : this is the easiest one to fix; it can be done by simply adding checks to the handlers for commands 2, 3 and 4. I’m not entirely sure if the kernel provides an easy way to validate that an address is within a given process’s LINEAR heap, but if not something for that should be added.
- WriteHWRegs/WriteHWRegsWithMask : it’s possible to manually mimick the sequence of register reads/writes used by GX command handlers from a usermode application. This means that the above fixes can be bypassed fairly easily. Therefore, similar fixes should be added to the handlers for those service commands when writing to certain registers.
- Rendering : this is the tricky one. The first time I exploited this vulnerability, I did so by rendering a scene with the framebuffer set to output to my application’s .text region. To prevent this, it would be necessary to scan GPU command buffers sent with GX commands 1 and 5 for framebuffer setting GPU commands. This can be tricky to do for a number of reasons I won’t be going into here.
Relevant snippet :
;copy code to GSP heap (for VA -> PA translation and dcache flushing)
	.word 0x001bbeb8 ; pop {r3, pc}
		.word 0x002c9628 ; r3 (pop {r0, pc})
	.word 0x00106eb8 ; pop {r4, lr} | bx r3
		.word 0xDEADC0DE ; r4 (garbage)
		.word 0x002c9628 ; lr (pop	{r0, pc})
	;equivalent to .word 0x002c9628 ; pop {r0, pc}
		.word CN_CODELOCATIONGSP    ; r0 (dst)
	.word 0x00226734 ; pop	{r1, pc}
		.word CN_CODELOCATIONVA ; r1 (src)
	.word 0x0020b8e8 ; pop	{r2, r3, r4, pc}
		.word codePatchEnd-codePatch ; r2 (size)
		.word 0xDEADC0DE ; r3 (garbage)
		.word 0xDEADC0DE ; r4 (garbage)
	.word 0x00224FB0 ; memcpy (ends in BX	LR)

;flush data cache
	;equivalent to .word 0x002c9628 ; pop {r0, pc}
		.word CN_GSPHANDLE_ADR ; r0 (handle ptr)
	.word 0x00226734 ; pop	{r1, pc}
		.word 0xFFFF8001 ; r1 (kprocess handle)
	.word 0x0020b8e8 ; pop	{r2, r3, r4, pc}
		.word CN_CODELOCATIONGSP  ; r2 (address)
		.word 0x00010000 ; r3 (size)
		.word 0xDEADC0DE ; r4 (garbage)
	.word CN_GSPGPU_FlushDataCache_ADR+4 ; GSPGPU_FlushDataCache (ends in LDMFD   SP!, {R4-R6,PC})
		.word 0xDEADC0DE ; r4 (garbage)
		.word 0xDEADC0DE ; r5 (garbage)
		.word 0xDEADC0DE ; r6 (garbage)

;send GX command
	.word 0x002c9628 ; pop	{r0, pc}
		.word 0x356208+0x58 ; r0
	.word 0x00226734 ; pop	{r1, pc}
		.word CN_HEAPPAYLOADADR+gxCommand-ROP ; r1 (cmd addr)
	.word CN_nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue+4 ; nn__gxlow__CTR__CmdReqQueueTx__TryEnqueue (ends in LDMFD   SP!, {R4-R8,PC})
	; got this method name from symbols left over in a game btw, i do not have the SDK
		.word 0xDEADC0DE ; r4 (garbage)
		.word 0xDEADC0DE ; r5 (garbage)
		.word 0xDEADC0DE ; r6 (garbage)
		.word 0xDEADC0DE ; r7 (garbage)
		.word 0xDEADC0DE ; r8 (garbage)

;sleep for a second and jump to code
	.word 0x00226734 ; pop {r3, pc}
		.word 0x002c9628 ; r1 (pop {r0, pc})
	.word 0x0012ec64 ; pop {r4, lr} | bx r1
		.word 0xDEADC0DE ; r4 (garbage)
		.word 0x002c9628 ; lr (pop {r0, pc})
	;equivalent to .word 0x002c9628 ; pop {r0, pc}
		.word 0x3B9ACA00 ; r0 = 1 second
	.word 0x00226734 ; pop	{r1, pc}
		.word 0x00000000 ; r1
	.word 0x00293D14 ; svcSleepThread (ends in BX	LR)
	;equivalent to .word 0x002c9628 ; pop {r0, pc}
		.word 0x00000000 ; r0 (time_low)
	.word 0x00226734 ; pop	{r1, pc}
		.word 0x00000000 ; r1 (time_high)
	.word 0x002D9700 ;jump to code

	.word 0xBEEF0000

.align 4
	.word 0x00000004 ; SetTextureCopy
	.word CN_CODELOCATIONGSP ; source
	.word CN_GSPHEAP+CN_TEXTPAOFFSET+0x001D9700 ; destination
	.word 0x00010000 ; size
	.word 0xFFFFFFFF ; dim in
	.word 0xFFFFFFFF ; dim out
	.word 0x00000008 ; flags
	.word 0x00000000 ; unused

.align 4
	.incbin "cn_initial/cn_initial.bin" ; our initial code
	.word 0xDEADDEAD
	.word 0xDEADDEAD
	.word 0xDEADDEAD
	.word 0xDEADDEAD


Stage 3 : getting access to more services and SDMC by taking over spider/SKATER (aka spiderto/SKATERto)

Description : the 3DS runs a fully multitasking OS which is able to execute applets simultaneously to the main application. These applets include the home menu and the web browser (aka spider on the 3DS, and SKATER on the New 3DS). It is possible to launch such an applet from any usermode app through APT commands.
Problem : while system applets such as spider usually keep their .text in the 0×26800000+ region, they use a heap to store information, including stacks for secondary threads. This heap is below the cutoff for FCRAM GPU DMA access. This allows us to takeover that thread using GPU DMA and some neat timing tricks. From there we can takeover spider’s main thread, and use that to run the next stage, all through ROP. From there, we use NS:SendParameter to give Cubic Ninja spider’s ro and fs session handles.
Limitations : this only gives us ROP execution under spider/SKATER, not actual code execution.
Fix : N/A
Relevant snippet : 
// we use GSP access rights as a synchronization method
// we know home menu will wait until it has the rights to do anything, so we first release the rights
// BUT we also know that spider will do the same, so we get in line to grab the rights right afterwards
// this way, we'll have spider hanging at a known point in code and we can take it over more easily
_GSPGPU_ReleaseRight(*gspHandle); //disable GSP module access

	ret=_APT_PrepareToStartSystemApplet(aptuHandle, APPID_WEB);
drawTitleScreen("running exploit... 000%");

	ret=_APT_StartSystemApplet(aptuHandle, APPID_WEB, &buf, 0, 0);
drawTitleScreen("running exploit... 020%");

svc_sleepThread(100000000); //sleep just long enough for menu to grab rights

_GSPGPU_AcquireRight(*gspHandle, 0x0); //get in line for gsp rights

//need to sleep longer on 4.x ?
svc_sleepThread(1000000000); //sleep long enough for spider to startup

//read spider memory
	_GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)0x14100000, 0x00000200);
	doGspwn((u32*)(SPIDER_HOOKROP_PADR-0x0C000000), (u32*)0x14100000, 0x00000200);

svc_sleepThread(1000000); //sleep long enough for memory to be read

//patch memdump and write it
	memcpy(((u8*)(0x14100000+SPIDER_HOOKROP_OFFSET)), spider_hook_rop_bin, 0xC);
	_GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)0x14100000, 0x00000200);

	doGspwn((u32*)0x14100000, (u32*)(SPIDER_HOOKROP_PADR-0x0C000000), 0x00000200);


	memset((u8*)0x14100000, 0x00, 0x2000);
	memcpy((u8*)0x14100000, spider_initial_rop_bin, spider_initial_rop_bin_size);
	_GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)0x14100000, 0x1000);

	doGspwn((u32*)0x14100000, (u32*)(SPIDER_INITIALROP_PADR-0x0C000000), 0x1000);


	memset((u8*)0x14100000, 0x00, 0x2000);
	memcpy((u8*)0x14100000, spider_thread0_rop_bin, spider_thread0_rop_bin_size);
	_GSPGPU_FlushDataCache(gspHandle, 0xFFFF8001, (u32*)0x14100000, 0x2000);

	doGspwn((u32*)0x14100000, (u32*)(SPIDER_THREAD0ROP_PADR-0x0C000000), 0x2000);

svc_sleepThread(100000000);//sleep long enough for memory to be written

	_APT_CancelParameter(aptuHandle, APPID_WEB);


drawTitleScreen("running exploit... 040%");

_GSPGPU_ReleaseRight(*gspHandle); //disable GSP module access

svc_sleepThread(100000000); //sleep just long enough for spider to grab rights

_GSPGPU_AcquireRight(*gspHandle, 0x0); //get in line for gsp rights

drawTitleScreen("running exploit... 070%"); //getting rights means spiderto was successful


Stage 4 : getting access to privileged syscalls by taking over ro, a sysmodule, from spider/SKATER (aka rohax)

Description : the 3DS runs a system module named “ro” (for relocatable object probably ?) which allows users to load and run dynamically linked code (“CROs”). These CROs are most commonly stored directly in romfs and are checked with a central CRR file which is signed and contains hashes for each CRO file in the application. The CRR’s signature is checked once when that file is loaded, and then each CRO’s integrity is checked by comparing it against the corresponding hash stored in the CRR file. CRO files contain a list of patches which must be applied to the code being loaded so that it can be relocated. The ro system module has access to a number of high-privilege system calls which, among other things, allow it to give regions of memory executable status within its own virtual memory space as well as other processes’.
Problem : using GPU DMA, we are able to modify the loaded CRR file’s hash list *after* the signature check has occured. This, in turn, lets us load arbitrary unsigned CRO files. It is then possible for us to manufacture a custom CRO which will use the relocation patch list to patch ro’s stack, give us ROP capabilities within ro and use that to get ro code execution using SVC 0×70. This is not supposed to be possible as relocation patches can only be within CRO segments, and CRO segments are checked to be within CRO memory when first parsed, which is great. However, what we can do is extend one of the CRO segments to include the CRO segment table (which is within the CRO itself, not copied elsewhere for actual use), then use our first relocation patch to extend a CRO segment to include the stack, and then use subsequent CRO patches to write our ROP chain to the stack.
Limitations : getting code execution in ro is great because it gives us access to svcControlProcessMemory, which lets us map memory as executable within other processes. It also allows us to modify other processes’ memory using svcMapProcessMemory. That being said, we can’t do that with just any other process (like, ideally, loader…) as we need a handle for that other process, and ro does not have access to svcOpenProcess. Therefore, we only use our code execution capabilities within ro to remap memory within Cubic Ninja.
Fix : this was fixed in 9.3 by adding a bunch of (apparently) proper bound checks to ro’s various commands. I haven’t looked into it myself but I’ve been assured it’s pretty much airtight.


- ninjhax => Cubic Ninja ROP
- Cubic Ninja ROP => gpuhax => Cubic Ninja code execution
- Cubic Ninja code execution => gpuhax => spiderto => spider ROP
- spider ROP => rohax => ro code execution (ro becomes “hb” custom service handler)
- ro code execution => spider code execution => Cubic Ninja code execution with memory remapping and basic cache management capabilities, as well as access to sdmc and a bunch of new services

Unfortunately I haven’t taken the time to document the work I’ve been doing on the 3DS lately here, even though it’s been pretty extensive. Normally, I’d try to cover things chronologically, but since I decided to reveal a new exploit today, it kind of takes priority as there’s a lot of stuff I need to clear up. To start off, here’s a video showing ssspwn in action :

What it is, what it isn’t

If you’ve read my (now really old and outdated) article on 3DS hacking, you’ll recall that for a number of reasons, hacking the console happened by chaining multiple exploits with one another. The most widely used hack (used by flashcart teams, myself and a number of other people) reliies on not one but two completely distinct exploits : the mset DS user settings exploit, which gives us arm11 usermode ROP capabilities, through which a FIRM vuln is exploited to obtain arm9 code exec. This last part was fixed with firmware version 5.0, and it’s the real critical part : while there’s a pretty high number of games that could potentially be exploited through saves to do usermode ROP, it’s useless if you don’t have another exploit to chain that gives you code exec capabilities. This is where ssspwn comes in; it essentially replaces the FIRM exploit we had on 4.5 and lets us execute arbitrary code. That’s why the video looks similar to the one I’d done when I got 4.5 code exec : the first stage exploit used is the same, just fine tuned to work on 6.3.

What does that mean ? Simply that because the two exploits are completely separate, there’s no reason to believe that just because the mset bug was fixed in 7.0, so was ssspwn. That’s right; ssspwn has yet to be plugged by Nintendo, and could in theory give us code exex on latest firmware version. This isn’t the case yet because we haven’t really looked for a new entrypoint, but that’s the next step.

To release or not to release

Generally speaking, the thing that’s been stopping me (and others) from releasing working exploits has been the fact that they might be used for piracy. Fortunately, that should not be a factor in this case, as by its very nature, ssspwn can not by itself allow piracy. That’s right, it’s the sweet spot that gives us just enough to get awesome homebrew code running in arm11 user mode, but not enough to break the system bad enough to let anyone do whatever the hell they want. As such, I personally have no qualms with releasing the exploit into the wild.

You might be wondering why there isn’t a download link available yet. The reason for that is that, as I mentioned, ssspwn has yet to be fixed. In my opinion, it would be dumb to burn such a nice vuln on just 6.3 when we know full well that we should be able to use this on 7.x, and possibly even 8.x+ with some work.

Plan of action

Now, while I don’t think it’s a good idea to release this publicly just yet, I do think it would be a good idea to get it into the hands of devs with consoles still on 4.5-6.3 so we can make progress creating 3DS homebrew development tools. We’ve been making tremendous progress as it is, but we could do much more with some more talented and motivated developers. As such, I want to share this with as many reputable and available devs as possible so that they can work on making things ready for the (hopefully) upcoming 7.1+ release.

Do note that I don’t have a developer-friendly version ready just yet, but I will let everyone know as soon as I do.

Other thoughts

This is, in my opinion, the best shot we have at making a successful and accessible 3DS homebrew scene happen. I’m going to try not to fuck it up. That means that unfortunately the number of devs I’ll feel comfortable sharing the current iteration of ssspwn with will be rather limited, in an effort to avoid premature leaks. Even then, there’s a good chance this whole thing is a bad idea and that it’ll lead to the vuln being plugged before we ever get a chance to exploit it on latest system version. I’m choosing to trust people, and I sincerely hope it’s not something that will backfire.

On another, more personal note, this is my first own big boy exploit I unveil so I think that’s pretty cool.

Sorry it’s been a while since my last post; I was really busy in November and December and basically got no 3DS work done back then. Fortunately though my schedule’s cleared up quite a bit since then and I’m happy to say that I’m back on track and making some fairly good progress. Let’s start with a little video I uploaded last night :

For those of you too lazy to watch the video (you know who you are…), it shows me booting into redNAND mode on 7.1 from 4.2 (works on 4.1-4.5 ofc) and running a homebrew game contained within its own little channel, complete with custom icon and banner. It also gives some other stuff.

This video is a glimpse at what I want for the up and coming 3DS homebrew scene, ie a way for people to make their own homebrew applications and install so that they’re directly accessible from home menu. This has a number of advantages over running code “on the bare metal” as some are already doing. For one thing, it means that homebrew code will be strictly limited to user mode code, the same way commercial games and applications are, which drastically lowers the likelihood of anyone’s (*cough*GW*cough*) code accidentally bricking your console. For another, it means that our code will be able to interface with every service provided by the 3DS’s OS; it’ll make stuff like FS, wifi and GPU access much easier. And of course, it just looks cool having your own channel in the menu, and being able to return to menu and switch between games instantly is a nice plus.

For that goal to become a reality, we basically need two things : a way to create new channels and a way to install them. I’m proud to say that I’m taking steps to make creating channels possible, by starting ctrulib (whose code is freely available on github). The idea is to make interfacing with 3DS services easier, by providing functions designed to do so and example code to understand how they’re used. Of course it’s not much at the moment; very few services are implemented and the examples don’t necessarily use them in exactly the way they were meant to be used. Nevertheless, it already provides the basics; enough to do basic interactions with NS, the HID module for user input and the GSP module for VRAM and later on GPU access. It’s very much a work in progress and will only keep growing. yeti3DS is an example of what can be achieved with ctrulib at the moment; not much, but a pretty cool start if you ask me. yeti3DS’s code is also available on github.

Now the thing is, there is at the moment no public way to install new channels, which means that even though you can just clone the ctrulib repo right now and compile it, you probably won’t be able to run what it produces. The reason for that is, basically, that I don’t have an installer ready. That’s the next big step for me and I’ll have to ask you to be patient. There is a fair bit of work involved and while I do expect to have an installer POC ready within the next couple weeks, there’s no telling how long it’ll take to get a safe package ready for mass consumption; users have already suffered through enough bricks, I’d rather my software didn’t add to the list.

So sit tight ! We’ll have nice 3DS homebrew soon enough. Feel free to ask any questions you may have (other than ETA requests), I’m not sure how clear this post was. (I’m pretty tired…)

Unfortunately, I have not had any time to work on 3DS stuff in the past couple weeks. That’s ok though, as I did get a lot done before starting my hiatus (and I started writing an article about my 3DS work). Mostly, I was able to get NAND redirection working on my 3DS. Getting that done was not actually that hard a task. Really, what it took to get it running was a bunch of code analysis and reverse engineering. Not necessarily an easy thing to do, but since we did already have some information on how NAND and SD are accessed thanks to the (light) documentation present on 3Dbrew and the fact that it works in ways very similar to how it did on the DSi, it was really little more than a matter of time until we got it working.

NAND redirection in action. I’m calling it redNAND because I like silly names, but you don’t have to.

If you follow me on twitter, maybe you know that I bricked a 3DS while working on this. As far as I can tell, what happened is that while I’d gotten my NAND redirection code working for reading (as the 3DS did boot by loading all its data from my SD), I had not actually located and hooked the NAND writing code properly. Because of that, I guess the 3DS overwrote something it shouldn’t have on NAND, and somehow that broke everything. I don’t know for sure exactly what happened as I haven’t repaired that 3DS yet (simply reflashing an old dump to NAND *should* be enough to get it going again), but I think it has to do with game notes. My theory goes that because I had never gone into game notes before dumping my NAND (I’d just done a system reset to undo the potential side effects of a previous bad NAND write), the files which were supposed to contain its data had not been created. Because of that, going into game notes would create those files, which would normally be fine, but because it would be using the FAT table from my NAND dump while writing to my actual NAND, obviously things would go wrong and it would and up messing stuff up. Not 100% sure about this as there are some discrepancies in my theory (iirc the console crashed before it got to the point where it would show the game notes initialization message), but so far that’s the best explanation I’ve got for what happened. Either way, redNAND seems to be running very well at the moment; obviously we’d need more than just the few of us who have it running atm to test it before an actual release, but I’ve used it on my 3DS for a number of hours while playing pokemon and so far no problem.

One of the less alarming side effects of messing with the 3DS’s NAND like a reckless idiot.

Which brings me to pokémon, and pokéhacking (yes, that’s a word now). I’d like to start by saying that I’m not in any way a “pokéhacker”; I did what I did for fun and because people were curious, and I was all too glad to be able to help out in finding out some secrets. Overall I’d say it was a positive experience. I wasn’t expecting for something so simple to make such a big impact, and frankly it’s a little frustrating that now a large chunk of the messages I get are from people who want to buy hacked pokémon from me. I was also disappointed in the reaction of some “hardcore pokéhackers” (because apparently, I was expected to share the game’s full decrypted code… which obviously wasn’t going to happen). But what can you do, live and learn I suppose.

For the unitiated : shortly after I got redNAND up and running on my 3DS, I got ahold of a copy of pokemon Y. As I was now able to run it on my console while running my own unsigned code in the background, which meant I could not only take in game screenshots, but also make full ram dumps. Being able to dump RAM in game meant being able to see the game’s code, some of its ressources, but also of course it meant being able to analyze it to create cheats. Now, I *really* wanted to capture a mew for no appropriate reason so I decided to make a cheat that would allow me to do so. That wasn’t actually very hard. It stood to reason that the possibly encounterable pokémon in a given area would have to be stored somewhere in memory. With that in mind, I started asking around to see if anyone knew of such structures in previous games. That’s how I ran across Kaphotics, who confirmed my intuition by graciously providing encounter tables from previous pokémon games. From there, I listed the pokemon I’d encountered in my area, wrote a python script to search through my ram dump to find an adequate-looking structure, and that’s how I found the encounter tables. Nothing too exciting in and of itself, but it allowed me to spawn unobtainable pokémon, and the rest is history.

Now, here’s to hoping I’ll have time to further my 3DS plans soon !

As you may have noticed, my web page has been through a number of changes during the last couple of weeks. First change you’ll notice is the website is now in english. I had been wanting to put up a couple of articles related to my work for some time, and I figured it would be better to write them in english for them to reach as broad an audience as possible. Because of that, it only made sense to switch the entire devblog to english. Rest assured, you can still write comments in french if you want to, but future posts and articles will all be written in english.
You’ll also notice I changed the theme for something a little more modern. Not overly so, it’s still a very simple layout. I’m not happy with everything about it at the moment so there will still be a few changes here and there in the coming days/weeks, but nothing major. (not a fan of how article titles are rendered at the moment for instance).
Finally, as I mentioned above, I’ve started writing a number of short articles related to the work I’ve been doing for the last couple years. They’re all very much WIP at the moment, but more complete versions should be up soon enough, so stay tuned !