GreyMecha/Army v2

I did some stuff for GreyMecha/Army and GreyCTF2026. Most of it is in the slides, and I'll add additional things here and there

1. GreyCTF 2026 Context

Anyway the context this year is that we provided everyone a GreyFlag PCB + a GreyMecha/Army v2. The GreyMecha/Army v2 mainly added PSRAM + Fixed the Flash + an extra PMOD.

GreyFlag was meant to pair with the plushie AND/OR the GreyMecha/Army v2. The firmware wasn't done in time

I came up with the idea but GreyFlag was designed by Restia.

Pasted image 20261008211148.png

Pasted image 20261008211202.png|284 Pasted image 20261008211210.png|393

Pasted image 20261008211401.png|297

1.1 Production Issues

Hackin7's Tinkering Log: Btw note to self for greymecha production delays

For greymecha I pulled the 2 layer stunt because I believed that it'll still work, and it'll be cheaper
I had valid reason too, if you look at other ECP5 Schematics, there weren't dedicated planes for grounding high speed signals, it's just all signal planes, so if you don't need so many signals you don't really need so many planes, other than for a vague reason like signal integrity

But in hindsight that cost us both a slightly worse board, and in production difficulties, because bga PCBA production capabilities are designed for multilayer boards, such as via in pad, it'll still work on 2 layer, but the manufacturer is just not as used to it I guess.

I guess next time for complex parts at large scales, just use 4 layer, it's not that much more expensive, it fulfils the signal integrity better and it also just better for manufacturers

Hackin7's Tinkering Log: Also my rough rule of thumb is that anything less than 100MHz I can get away with suboptimal routing

GreyMecha was a 50MHz PCB, but in hindsight if I handled the oscillators properly it could have been 400MHz with the PLL
I think with secure memory, one of the reasons we needed some histogram nonsense in the solution is due to the routing not being optimal

1.2 PIO

Did a deep dive in PIO during GreyCTF itself for my PIO challenge (turns out vibecoding the whole thing led to me not understanding🤔)

Some useful Resources
https://rp2040pio-docs.readthedocs.io/en/latest/architecture.html (cool emulator thingy)

Interesting notes

  1. Because PIO is so fast, you gotta switch it multiple times and sample multiple times, then use statistics to figure out what is the most likely value
  1. PIO reads pins in sequentially, no exceptions, so gotta proxcess in the CPU code, and focus on PIO for basic data input output
  2. There are basic synchronization primitives to pass data into and out of the PIO, which would be useful

In terms of setting challenges

  1. its always useful to provide more template code, especially in this AI Age
  2. Need to handhold on hardware connections, ctf players lowkey quite blur

From Restia

pio_input.py

  1. I think that goes for most analog reads tbh, iirc ADC is 13x oversampling and your UART has oversampling as well iirc

  2. When i was working with PIO, I remmeber for each state machine, you can only set 1 base pin, it you need to tell it how many pins to read beyond that also, and that it reads like pin Base then pin Base+1... up to 5 iirc but I couldn't get that to work so I just work with 1 input and 1 output lol. But there are 12 statemachines on rp2350 soo

  3. The FIFO was quite useful for me, I remember to make my set up work, I used the fact that it was 32 bytes iirc and a blocking function call on my main loop so that the FIFO is always full and hence the pull from the PIO is accurate. Also learnt about OSR and scratch registers etc, all made me confused for a while quite cool

1.3 Watchdog KOTH Solutions

https://github.com/NUSGreyhats/greybadge26/tree/greymechaarmy_v2/greymechaarmy_v2/firmware/challs/watchdog-solutions

Yes in the watchdog challenge the watchdog is implemented on the FPGA in hardware.
We had a picorv32 core and I've added a watchdog timer that resets when it exceeds a certain value

Intented solution was to look for the Watchdog address by fuzzing, but tbh you could guess (uart is 0x1000000, led is 0x10000100, so judging by the spacing the next peripheral would be 0x10000200, which is where the watchdog would be)

Levels of Solution

  1. read character by character
  2. disable watchdog through fuzzing
  3. optimise by minimising loops, hardcoding address values, minimising operations,
  4. GCC O3 Compiler flag
  5. Assembly level optimisations (using lb or sb instructions)

Other solutions

  1. hardcoding the flag would provide better performance, because it takes less clock cycles to load a hardcoded value than to read from memory mapped IO

smallest possible solutions are 0x150 for hardcoded flag, and 0x1a5 for non hardcoded solution

You can thank low level learning video series on hacking his baby monitor for this challenge, he had to code a bitcoin miner that bypassed the baby monitor's watchdog

2. Emulators

I did an Arduboy Emulator
https://github.com/Hackin7/GreyMechaArmy_Tests/tree/main/emulators/arduboy
Pasted image 20261008232921.png|312

I did a Gameboy Emulator also

Pasted image 20261008232745.png|300

Just got Codex to port an FPGA emulators and softcores over

These are just for fun tbh