hookコードでレジスタ書き出し(CTRPF)
概要
きっかけはどぅーむーんさんのツイート。
全レジスタ確認の考えまくったところ、可能性は低いがある。
— どぅーむーん (@domoon_mutex) 2021年3月14日
それも構成自体は超簡単に作れそうと考えれるから後は実現だけ。
内容としては、hookコードでレジスタ確認を行うというもの。実装は少々面倒かもしれないが、面白そうだったので組んでみることにした。(十分なテストを行っていないため、間違い等あればコメント欄まで)
手順
以下の手順で実装する。今回の方法ではlrとpcを参照できないため、確認するレジスタはr0~r13(sp)とする。
- r0~r13, cpsrをスタックに退避。
- スタックから値を読み込み、実行コードの下部に書き込む。
- 上書きしたレジスタを復元し、スタックポインタを戻す。
※hook処理にもスタックは使われる。spはあくまでhookコード開始時の値であり、hookアドレス時点での値ではないため注意。
コード
上記の手順に沿って実装。ちなみに各レジスタの役割は以下のように割り振った。
- r0 ・・・cpsrの格納と復元及び偶数番目のレジスタのロード
- r1・・・奇数番目のレジスタのロード
- r2・・・ロードするレジスタのポインタ
- r3・・・書き込み先のポインタ
- r4・・・push前のスタックポインタの値
E92D3FFF @ push {r0-r13} E10F0000 @ mrs r0, cpsr E52D0004 @ push {r0} E28D2004 @ add r2, sp, #4 E28F3028 @ add r3, pc, #0x28 E28D403C @ add r4, sp, #0x3C E0C200D8 @ ldrd r0, [r2], #8 E0C300F8 @ strd r0, [r3], #8 E1520004 @ cmp r2, r4 BAFFFFFB @ blt #-12 E49D0004 @ pop {r0} E129F000 @ msr cpsr, r0 E8BD001F @ pop {r0-r4} E28DD024 @ add sp, sp, #0x24 E12FFF1E @ bx lr
以下各コード解説。(コードをクリックして詳細確認)
push {r0-r13}
確認対象とするレジスタをスタックに積む。
mrs r0, cpsr
cpsrの値をr0にコピー。
push {r0}
r0(中身はcpsrの値)をスタックに積む。
add r2, sp, #4
r2に読み込みを開始するレジスタの位置を読み込む。この時r2はスタックに退避したr0の位置を指している。
add r3, pc, #0x28
r3に書き込み開始位置を読み込む。今回はhookコード終了命令(bx lr)の8byte先を指している。
add r4, sp, #0x3C
r4にpush前のspの位置を読み込む。
cmp r2, r4
r2とr4を比較。r2は次のロード開始位置、r4は元のスタックポインタの位置を指している。つまり、「ロードすべきレジスタが残っているか」を判断している。
pop {r0}
スタックに積んでいたcpsrの値をr0に読み込む。
msr cpsr, r0
r0の値をcpsrにコピー。これで元のcpsrの値が復元される。
pop {r0-r4}
処理に使用したレジスタ(r0~r4)は復元する必要があるため、スタックから復元する。
add sp, sp, #0x24
スタックポインタを元の位置に戻す。
bx lr
lrに分岐し、hookコード終了。
D3000000 XXXXXXXX // フックするアドレス FD000000 00000078 // フック開始(0x78 byte) E92D3FFF E10F0000 E52D0004 E28D2004 E28F3028 E28D403C E0C200D8 E0C300F8 E1520004 BAFFFFFB E49D0004 E129F000 E8BD001F E28DD024 E12FFF1E 00000000 00000000 00000000 // r0, r1 00000000 00000000 // r2, r3 00000000 00000000 // r4, r5 00000000 00000000 // r6, r7 00000000 00000000 // r8, r9 00000000 00000000 // r10, r11 00000000 00000000 // r12, sp D2000000 00000000
CTRPFのコードに直すと上記の通り。これでr0~r13(sp)のレジスタ値が取得できる。