NSEC21 Rare Metal Sequencer, pt. 2 - Mon, May 24, 2021 - Lucas Bajolet Barberousse
Rare Metal Sequencer 2
As mentioned in part 1, solving the first step makes a second challenge available.
We’ll open it in ZSNES, as with the first rom:
Alright, the output is a bit garbled, but the UI looks similar enough.
No need to guess here, we’ll go straight into the code so we can see how it’s made.
Note: We’ll go through the main differences between the two (the binaries are quite similar in their handling).
First, let’s head to the input management function, @0x81e5!
Input function
This time, we’ve got a bit more data to keep track of:
We can see a similar handling for both the bytes pertaining to the inputs of controller one, i.e.:
- 0x4219 -> 0x1414
- 0x4218 -> 0x1418
So that part hasn’t changed, the mapped addresses are the same, however, two more repeats are visible:
- 0x421b -> 0x1424
- 0x421a -> 0x1428
These bytes belong to the inputs of controller 2.
Remainder for the mapped buttons/addresses:
Controller 1: Controller 2:
So we will need to map both if we plan to successfully input the right sequence to get our flag.
Moving on, we can take a look at the first input check:
OK, we have a bit more to consider this time.
Let’s go step by step, shall we:
- LDA 0x1428 -> load the
AXLR
button line for controller 2 into A - CLC -> clear carry flag, since we do ADC afterwards, that’ll ensure it’s not garbage
- ADC 0x1414 -> add the values of the
BYsSUDLR
button line for controller 1 - ADC 0x1418 -> add the values of the
AXLR
button line for controller 1 - AND 0xff -> keep only the last byte
- BEQ 0x00826c -> so after all ADC are done, A must have value 0 in order to proceed
After that, we’ve got the final button check:
So in this case, we must input button Y on controller 2, which leads to the famed:
Alright, so by the same logic, we should be able to get all the combinations:
- Input 2:
- None of the buttons in lines 0x1414, 0x1418 and 0x1428
- 0x80 in line 0x1414, so
B
on controller 1
- Input 3:
- None of the buttons in lines 0x1418 or 0x1428
(@0x1418 + @0x1428) ^ 0xA0 == 0
, soAL
on either controller
- Input 4: 0x1414 » 1 => 0x20, so 0x1414 must be 0x40, which is effectively button Y on controller 1.
Anyway, that’s the theory.
Note: for proof that it’s working
What we actually did
But, we misunderstood some part of the pre-conditions, and thought we had to effectively press 9 buttons at the same time on some occasion.
So that was not an option.
In its stead, we used wits (which should also perfectly work for part 1).
See, the flag is actually pre-stored in the binary, however, we do need to get the right parts.
This is actually done in the same routine as the remainder of the operations, in case you press the start button on controller 1.
We can follow the flow to the end here:
We’ll focus on this part, the logic is very similar for the remainder of the binary.
So here, we load the base address of the FLAG string (at 0x88a9), and we add to it the value at address 0x1400.
However, what we have at 0x1400 is actually the value of the buttons from 0x1424, when input 1 is successfully entered.
So, by that logic, we can go back to each input, and infer the value of each address we depend upon for decoding our flag:
- 0x1400 -> 0x40
- 0x1404 -> 0xF1 (0x80 ^ 0x71)
- 0x1408 -> 0xA0
- 0x140c -> 0x20
We can then read the amount specified for the loop counter, and append it to the FLAG-
prefix.
- I1: 0x88A9 + 0x40 -> 0x88E9, 6 bytes to read: QYR88W
- I2: 0x88A9 + 0xF1 -> 0x899A, 5 bytes to read: NDGBW
- I3: 0x88A9 + 0xA0 -> 0x8949, 5 bytes to read: 7HP3G
- I4: 0x88A9 + 0x20 -> 0x89C9, 10 bytes to read: XRGW49GE5W
We then got the expected output: FLAG-QYR88WNDGBW7HP3GXRGW49GE5W
.