Post

Switch case using EA in ROP - Ep.2

An example ROP chain using the EA switch case technique

Notes: All addresses mentioned below is for verA

Let’s consider the following example where the keycodes to be checked are:

0401, 0402, 0404, 0201, 0204, 0101, 0102, 0104, 0208, 0210, 0108, 0110

And their corresponding jump addresses are:

D730, D740, D750, D760, D770, D780, D790, D7A0, D7B0, D7C0, D7D0, D7E0

The keycodes are placed first, followed by the jymp addresses. The last two bytes of the keycode table are 0000, and the jump address for no key press is D7F0. At address D800, the table is constructed as follows:

org 0xD800
hex 04 01 30 D7 04 02 40 D7 04 04 50 D7 02 01 60 D7
hex 02 04 70 D7 01 01 80 D7 01 02 90 D7 01 04 A0 D7
hex 02 08 B0 D7 02 10 C0 D7 01 08 D0 D7 01 10 E0 D7
hex 00 00 F0 D7

Program Logic

We will write the program at D700 (skipping the part where keycodes are read):

  1. Setup Link Register (LR) and other registers:

    Use 3:08D0 (far_call_stub) to set LR. To set EA to D800, we can use POP XR12 (2:0730) to set both ER12 and ER14 to D800 - 0xA = D7F6

     setlr
     pop xr12 (D7F6 D7F6)
    
  2. Set EA to D800:

    Call 1:7CA6 to assign EA at *[D800]. Next, add a POP ER0 and pad it with 2 bytes. To optimize the program, we have decided to directly store the keycode into the 2 bytes pad, and later pop them into ER0 to save up some L commands.
    If you use 2:F5EA to read the keycode and no key is pressed, the value will not be written, thus making the keycode: still remains 0000. (Explanation: Because this is a backup loop, the orginal default value will not be overwritten)

     setlr 
     pop xr12 (D7F6 D7F6)
     call 17CA6
        
     pop er0
     keycode:
     hex 00 00
    
  3. Use 0:9C20 Call 0:9C20 to obtain the address of the keycode that matches ER0. After running it, the EA will perfectly points to the address value.

     setlr 
     pop xr12 (D7F6 D7F6)
     call 17CA6
        
     pop er0
     keycode:
     hex 00 00
        
     call 09C20
    
  4. Final Steps

    Finally, call 1:C64A to store the value into ER6, and 2:1F74 to copy the value in ER6 to SP

     setlr 
     pop xr12 (D7F6 D7F6)
     call 17CA6
        
     pop er0
     keycode:
     hex 00 00
        
     call 09C20
     call 1C64A
     call 21F74
    

    (Don’t forget to pad all your jump address by 2 since 2:1F74 ends with POP ER8)


P/s: Issues when using 2:F5EA key detection with EA switcher 0:9C20

Overview

2:F5EA detects key presses and writes the keycode to *[ER0]. It then continues execution without any delay. When you use it in a loop to detect key presses, the function will repeatedly execute while the key is being held down, leading to unexpected behaviours.

To address this issue, a dedicated entry is reserved in the table to store the last keycode and its corresponding jump address. This allows the program to compare the current keycode with the previously detected keycode.

Idea

  • After each traversal, the keycode at the table header in the main program (eg at E9E0) is updated with the newly pressed keycode.
  • During the next traversal, the copied table begins by checking whether the keycode mathces the already backup keycode in the table header. If they match, the program should immediately exits the traversal loop to prevent repeated execution of the same function.

Writing the keycode to the table header

To write the keycode to the table header

  1. First, load the keycode into ER0 (you could use the POP ER0 trick aforementioned)
  2. Set ER2 to point to the table header location
  3. Call 1:3B9E (er0 = [er2],r2 = 9,rt) to write the value in ER0 to *[ER2]

Additionally, you can use 1:0E78 to write the keycode to *[ER4]

  1. Before calling 1:0E78, load the table header address into ER4
  2. 1:0E78 writes the value of ER0 to [ER4]
1:0E78H	9141			ST R1, [ER4]
1:0E7AH	E4FF			ADD ER4, #-1H
1:0E7CH	9041			ST R0, [ER4]
1:0E7EH	E4FF			ADD ER4, #-1H
1:0E80H	F18E			POP EA
1:0E82H	16FF			ADD R6, #-1H
1:0E84H	C8F4			BNE .l_010
1:0E86H	F42E			POP XR4
1:0E88H	F28E			POP PC

1:0E78 is followed by the 1:0E80 (POP EA) which is mentioned in the last post, which assigns a value to EA. This makes 1:0E78 particularly useful for scenarios where both keycode writing and EA assignment need to occur in the sequence.

Very important note

If you are going to do this, your table’s structure should looks like this

hex FFFF ;reserved for last keycode
hex D700 ;address to return when the same key is pressed
hex [key] [value]
hex [key] [value]
hex [key] [value]
    ...
hex 0000 [default value]

However, when you are not pressing anything, the keycode stored in ER0 will be 00 00. Let’s modify the previous code by changing the hex 00 00 after keycode: into a different unique value. I advise you to use hex FF FF, since there shouldn’t be any key matches that value.