Starting from total's link, I did some tracing myself to see how the space-time beam glitched copy loop works. I think it does this, basically (in a C-like notation) (The beam is a bit-mask, where each bit represents charge (c), plasma (p), spazer (s), ice (i) and wave (w)):
*beam_table = 0x9383c1
beam = 000cpsiw (p=plasma, etc.) (RAM address 0x9a6)
dir = {U UR R DR D D DL L UL U} (0..9)
*target = 0x7ec1c0
y0 = *(beam_table[(beam&0xf)<<1]+(dir+1)<<1)
x0 = 0?
for(y=y0+2,x=x0+2;y<0x20;y+=2,x+=2)
target[x] = (*direct_page[0:3])[y]
For the space-time beam, plasma, spazer and ice are active, giving beam=0xe. The index into the beam table will be 0x1c, which I think goes out of bounds, since the highest possible beam combination is supposed to be plasma, ice, wave, which would give an index of 0x16. The value that is read from this table is used as a pointer after adding a number which depends on which direction you fire the beam. The location this points to is then where we read the loop counter from.
During the loop, A, X and Y are all 16 bit registers, so no bytes are skipped in the memcpy. By examining the beam_table, it should be easy to find all the values y0 could take at this point. This can be used to choose the range of memory overwritten by the glitch, and coupled with the firing direction, one has quite a bit of flexibility.
I didn't use the charge beam in this test, so that gives another set of possibilities.
I don't think it's obvious that this couldn't eventually be used to gain control. We write lots of garbage to memory, and that is clearly capable of affecting the instruction pointer in some way, since it can make the game crash.
Cpadolf: How much did you test for rooms in which to activate the space-time beam? Did you try the murder beam too? Charged vs. uncharged and direction one fires in, and whether the projectile materializes in a door are all things that I think matter for this.
(Cpadolf: I agree that a space-time TAS should be in the same category as an X-ray TAS, and having a space-time tas obsolete the less-glitched TAS would be pretty crazy. I spent a lot of time making that argument in the thread I linked to. I was just saying that some people involved with category management thought otherwise)
Edit: The beam table is (little endian; left column indicates which beam combinations lead there):
9383c1 9383d9
00000: 31 84 c0000: 39 85
0000w: B5 84 c000w: D3 85
000i0: 9F 84 c00i0: A7 85
000iw: E1 84 c00iw: E9 85
00s00: 47 84 c0s00: 4F 85
00s0w: F7 84 c0s0w: FF 85
00si0: 5D 84 c0si0: 65 85
00siw: 73 84 c0siw: 7B 85
0p000: CB 84 cp000: BD 85
0p00w: 0D 85 cp00w: 2B 86
0p0i0: 23 85 cp0i0: 15 86
0p0iw: 89 84 cp0iw: 91 85
0ps00: 39 85 cps00: 41 86
0ps0w: D3 85 cps0w: 41 86
0psi0: A7 85 cpsi0: 57 86
0psiw: E9 85 cpsiw: 71 86
Coupled with the possible directions, this means that y0 will be read from these locations in bank 93:
U UR R DR D D DL L UL U
00000: 8433 8435 8437 8439 843b 843d 843f 8441 8443 8445
0000w: 84b7 84b9 84bb 84bd 84bf 84c1 84c3 84c5 84c7 84c9
000i0: 84a1 84a3 84a5 84a7 84a9 84ab 84ad 84af 84b1 84b3
000iw: 84e3 84e5 84e7 84e9 84eb 84ed 84ef 84f1 84f3 84f5
00s00: 8449 844b 844d 844f 8451 8453 8455 8457 8459 845b
00s0w: 84f9 84fb 84fd 84ff 8501 8503 8505 8507 8509 850b
00si0: 845f 8461 8463 8465 8467 8469 846b 846d 846f 8471
00siw: 8475 8477 8479 847b 847d 847f 8481 8483 8485 8487
0p000: 84cd 84cf 84d1 84d3 84d5 84d7 84d9 84db 84dd 84df
0p00w: 850f 8511 8513 8515 8517 8519 851b 851d 851f 8521
0p0i0: 8525 8527 8529 852b 852d 852f 8531 8533 8535 8537
0p0iw: 848b 848d 848f 8491 8493 8495 8497 8499 849b 849d
0ps00: 853b 853d 853f 8541 8543 8545 8547 8549 854b 854d :c0000
0ps0w: 85d5 85d7 85d9 85db 85dd 85df 85e1 85e3 85e5 85e7 :c000w
0psi0: 85a9 85ab 85ad 85af 85b1 85b3 85b5 85b7 85b9 85bb :c00i0
0psiw: 85eb 85ed 85ef 85f1 85f3 85f5 85f7 85f9 85fb 85fd :c00iw
8551 8553 8555 8557 8559 855b 855d 855f 8561 8563 :c0s00
8601 8603 8605 8607 8609 860b 860d 860f 8611 8613 :c0s0w
8567 8569 856b 856d 856f 8571 8573 8575 8577 8579 :c0si0
857d 857f 8581 8583 8585 8587 8589 858b 858d 858f :c0siw
85bf 85c1 85c3 85c5 85c7 85c9 85cb 85cd 85cf 85d1 :cp000
862d 862f 8631 8633 8635 8637 8639 863b 863d 863f :cp00w
8617 8619 861b 861d 861f 8621 8623 8625 8627 8629 :cp0i0
8593 8595 8597 8599 859b 859d 859f 85a1 85a3 85a5 :cp0iw
8643 8645 8647 8649 864b 864d 864f 8651 8653 8655 :cps00
8643 8645 8647 8649 864b 864d 864f 8651 8653 8655 :cps0w
8659 865b 865d 865f 8661 8663 8665 8667 8669 866b :cpsi0
8673 8675 8677 8679 867b 867d 867f 8681 8683 8685 :cpsiw
This leads to the y0 values:
U UR R DR D D DL L UL U
00000: 86db 86e7 86f3 86ff 870b 870b 8717 8723 872f 86db
0000w: 873b 87c7 884b 88cf 8743 8743 87c7 884b 88cf 873b
000i0: 8953 8953 8953 8953 8953 8953 8953 8953 8953 8953
000iw: 873b 87c7 884b 88cf 8743 8743 87c7 884b 88cf 873b
00s00: 8977 8993 89af 89cb 89e7 89e7 8a03 8a1f 8a3b 8977
00s0w: 8a57 8aab 8aff 8b53 8ba7 8ba7 8bfb 8c4f 8ca3 8a57
00si0: 8977 8993 89af 89cb 89e7 89e7 8a03 8a1f 8a3b 8977
00siw: 8a57 8aab 8aff 8b53 8ba7 8ba7 8bfb 8c4f 8ca3 8a57
0p000: 8cf7 8d0b 8d1f 8d33 8cf7 8cf7 8d0b 8d1f 8d33 8cf7
0p00w: 8d4f 8d9b 8ddf 8e33 8d4f 8d4f 8d9b 8ddf 8e33 8d4f
0p0i0: 8cf7 8d0b 8d1f 8d33 8cf7 8cf7 8d0b 8d1f 8d33 8cf7
0p0iw: 8d47 8d93 8ddf 8e2b 8d47 8d47 8d93 8ddf 8e2b 8d47
0ps00: 8e77 8e8b 8e9f 8eb3 8ec7 8ec7 8edb 8eef 8f03 8e77 :c0000
0ps0w: 8f17 8fa3 9027 90ab 8f1f 8f1f 8fa3 9027 90ab 8f17 :c000w
0psi0: 912f 912f 912f 912f 912f 912f 912f 912f 912f 912f :c00i0
0psiw: 9153 91df 9263 92e7 915b 915b 91df 9263 92e7 9153 :c00iw
936b 93bf 9413 9467 936b 936b 93bf 9413 9467 936b :c0s00
94bb 957f 9643 9707 97cb 97cb 988f 9953 9a17 94bb :c0s0w
936b 93bf 9413 9467 936b 936b 93bf 9413 9467 936b :c0si0
94bb 957f 9643 9707 97cb 97cb 988f 9953 9a17 94bb :c0siw
9adb 9b1f 9b63 9ba7 9adb 9adb 9b1f 9b63 9ba7 9adb :cp000
9beb 9c9f 9d53 9e07 9beb 9beb 9c9f 9d53 9e07 9beb :cp00w
9adb 9b1f 9b63 9ba7 9adb 9adb 9b1f 9b63 9ba7 9adb :cp0i0
9beb 9c9f 9d53 9e07 9beb 9beb 9c9f 9d53 9e07 9beb :cp0iw
9ebb 9ec7 9ed3 9edf 9eeb 9eeb 9ef7 9f03 9f0f 9ebb :cps00
9ebb 9ec7 9ed3 9edf 9eeb 9eeb 9ef7 9f03 9f0f 9ebb :cps0w
9f1b 9f27 9f33 9f3f 9f4b 9f4b 9f57 9f63 9f6f 9f1b :cpsi0
9f87 001e 9fbf 0008 a007 0008 a039 0000 a06b 012c :cpsiw
These are almost all negative, even for normal beam combinations, so if all the beams performed the same glitched jump as the spacetime beam, they would all have much the same effects. But they don't. Where to jump is determined by $0c68,x. This seems to be given by another table with 2*beam as the lookup, located at 90b96e or 90ba3e depending on whether the beam is charged (unless firing into a door or similar, when the constant value 69 B1 is used):
90b963 90ba3e
00000: F3 AE c0000: F3 AE
0000w: E4 B0 c000w: C3 B0
000i0: F3 AE c00i0: F3 AE
000iw: E4 B0 c00iw: C3 B0
00s00: F3 AE c0s00: F3 AE
00s0w: C3 B0 c0s0w: C3 B0
00si0: F3 AE c0si0: F3 AE
00siw: C3 B0 c0siw: C3 B0
0p000: F3 AE cp000: F3 AE
0p00w: C3 B0 cp00w: C3 B0
0p0i0: F3 AE cp0i0: F3 AE
0p0iw: C3 B0 cp0iw: C3 B0
0ps00: 20 39 cps00: AD 1C
0ps0w: AC B0 cps0w: 0A 0A
0psi0: 16 AD cpsi0: 0A 0A
0psiw: C2 0D cpsiw: AA A4
So for the spacetime beam, y0 will always be 0x912f.
For the other beams, we get:
- 0ps00: Jumps to 903920, which RAM. In my trace, this was a BRK instruction, hanging the game
- 0ps0w: This is the chainsaw beam. Jumps to 90b0ac (ROM), which is close to the beginning of the function for some other shot type. Ends up executing the code for that shot, plus one other side effect, a write of the current Y register value to address 0x60. This has little potential for doing anything useful.
- 0psi0: This is the space-time beam, described above. Jumps to 90ad16 (ROM).
- 0psiw: Jumps to 900dc2, which is RAM, but a BRK instruction in my tests, hanging the game.
- cps00: Jumps to 901cad, which is RAM, but in my test it lead to PLY, ROL A, BRK, hanging the game.
- cpsi0: Jumps to 900a0a, which is RAM. In my tests, this memory region contained AND, AND, BRK, hanging the game.
- cps0w: Same as above.
- cpsiw: The murder beam. Usually jumps to 90a4aa. This is ROM. It leads to the incredibly glitched function below. However, if y0 is zero (which it is when facing left), the jump is skipped. This avoids a crash, and is needed for the normal, useful behavior of the murder beam (which happens elsewhere). For all other directions, the jump will happen.
As HHS points out,
half of the beams jump to RAM, and are therefore promising for arbitrary code execution, if some way can be found to control those memory locations.
Wow, the murder beam is more messed up than the spacetime beam. It completely breaks the stack.
Much of its shenanigans are sadly blocked by most writes being redirected to ROM, where they have no effect. But not all. And some logic depends on possibly manipulatable RAM values. I think this beam has much higher potential for eventually leading to arbitrary code execution than the spacetime beam.
A is set using the same logic as for the spacetime beam, i.e.
y0 with the forum post terminology. Depending on the direction
it is fired in, A can take the values
0000 (L), 0008 (DR DDL), 001e (UR), 012c (UUL)
9f97 (UUR), 9fbf (R), a007 (DDR), a039 (DL), a06b (UL)
I.e. both positive and negative values are possible. The value of
A only affects the initial test. So the cases that matter are
A positive but less than the value in $9e99=500, and A outside this range.
I.e. [L DR DDL UR UUL] vs. [UUR R DDR DL UL].
if(A>=0&&A<$9e99) {
·$0dfa = $dfa & #ff00 | #0001
·if($8f & $09b4) call $9098bc
}
$90/A4AA 30 1C BMI $1C [$A4C8]
$90/A4AC CD 99 9E CMP $9E99 =0500
$90/A4AF 10 17 BPL $17 [$A4C8]
$90/A4B1 AD FA 0D LDA $0DFA
$90/A4B4 29 00 FF AND #$FF00
$90/A4B7 09 01 00 ORA #$0001
$90/A4BA 8D FA 0D STA $0DFA
$90/A4BD A5 8F LDA $8F Controller button pressed this frame!
$90/A4BF 2D B4 09 AND $09B4
$90/A4C2 F0 04 BEQ $04 [$A4C8]
$90/A4C4 22 BC 98 90 JSL $9098BC
The intial value of A no longer matters at this point.
$90/A4C8 AD 1C 0A LDA $0A1C
$90/A4CB C9 81 00 CMP #$0081
$90/A4CE F0 39 BEQ $39 [$A509]
$90/A4D0 C9 82 00 CMP #$0082
$90/A4D3 F0 34 BEQ $34 [$A509]
$90/A4D5 80 00 BRA $00 [$A4D7]
$90/A4D7 A5 12 LDA $12
$90/A4D9 F0 1A BEQ $1A [$A4F5] Perhaps
$90/A4DB AE 96 0A LDX $0A96
$90/A4DE AD 94 0A LDA $0A94
$90/A4E1 C9 01 00 CMP #$0001
$90/A4E4 D0 0F BNE $0F [$A4F5]
$90/A4E6 BD 14 A5 LDA $A514,x
$90/A4E9 29 FF 00 AND #$00FF
$90/A4EC F0 07 BEQ $07 [$A4F5]
$90/A4EE A9 2F 00 LDA #$002F
$90/A4F1 22 49 90 80 JSL $809049
$90/A4F5 AD D0 0C LDA $0CD0 =0000
$90/A4F8 C9 3C 00 CMP #$003C
$90/A4FB 30 12 BMI $12 [$A50F]
$90/A4FD A5 12 LDA $12
$90/A4FF D0 0E BNE $0E [$A50F]
$90/A501 A9 04 00 LDA #$0004
$90/A504 8D 6E 0A STA $0A6E
$90/A507 80 06 BRA $06 [$A50F]
$90/A509 A9 03 00 LDA #$0003
$90/A50C 8D 6E 0A STA $0A6E
$90/A50F 20 40 90 JSR $9040
$90/A512 28 PLP This messes up the stack!
$90/A513 60 RTS Will return somewhere crazy
In my test, game eventually reached a BRK instruction after a long series of jumps after this broken RTS, and halted. But exactly what happens could depend on RAM contents. I really should explore this further.