SECCON 2020 OnlineCTFに参加しました。welcome, surveyの他にはrev1問だけ解けたので、その1問のwrite-up記事です。
コンテスト概要
Rulesページより引用:
Game Format: Jeopardy Date: Sat, 10 Oct 2020, 06:00 (UTC) to Sun, 11 Oct 2020, 06:00 (UTC) Duration: 24 hours Language: English
問題の説明
rev SCSBX:Reversing SecCon SandBoX is an open-sourced virtual machine! ./scsbx seccon.bin
問題文中のリンクのtar.gzファイルから、c++ソースコード・バイナリ(ELF)・seccon.bin(入力用プログラム)・docs.md等を入手できました。 docs.mdの内容を以下に記述します。
# Brief Documentation for SCSBX ## SCSBX ### What is SCSBX? SCSBX is a virtual machine for SCArch-32, a virtual architecture designed for SECCON Online CTF 2020. SCArch-32 is a stack machine with 32-bit address space. ### Direct Memory Access SCSBX allows the program to directly access to the host memory. This may sound dangerous but the program can only use the 32-bit address space. Since SCSBX runs on a 64-bit machine with PIE enabled, there's no way the user can read/write the VM memory directly. ## Design ### Instruction Every instruction except for `push` is 1-byte long. `push` accepts a 4-byte value to push into the stack. **SCSBX is proudly open-sourced!** Check the source code for more details. ### Memory Layout (略)
seccon.binの逆コンパイル
docs.mdの説明より、push以外は1byte命令、pushのみは1byte+データ4byteだと分かります。 ソースコード中のscsbx.hppにて各種命令のオペコードが定義されていたため、テキストエディアの矩形選択やマクロを使って以下のPythonスクリプトを記述しました:
#!/usr/bin/env python3 with open("seccon.bin", "rb") as f: pc = 0 while True: d = f.read(1) pc += 1 if len(d) == 0: break ins = d[0] print(f"{pc-1}\t: ", end="") # どうやら調整が必要みたいです if ins == 0x20: value = f.read(4) if len(value) < 4: print("Wrong len in push!") exit pc += 4 v = int.from_bytes(value, byteorder="little") print(f"push {v}(={hex(v)})") elif ins == 0x21: print("POP") elif ins == 0x22: print("DUP") elif ins == 0x23: print("XCHG") elif ins == 0x24: print("LOAD32") elif ins == 0x25: print("LOAD64") elif ins == 0x26: print("STORE8") elif ins == 0x27: print("STORE16") elif ins == 0x28: print("STORE32") elif ins == 0x70: print("SHOW") elif ins == 0x30: print("JMP") elif ins == 0x31: print("JEQ") elif ins == 0x32: print("JGT") elif ins == 0x33: print("JGE") elif ins == 0x34: print("CALL") elif ins == 0x40: print("ADD") elif ins == 0x41: print("SUB") elif ins == 0x42: print("MUL") elif ins == 0x43: print("DIV") elif ins == 0x44: print("MOD") elif ins == 0x50: print("NOT") elif ins == 0x51: print("AND") elif ins == 0x52: print("OR") elif ins == 0x53: print("XOR") elif ins == 0x54: print("SHL") elif ins == 0x55: print("SHR") elif ins == 0x56: print("ROL") elif ins == 0x57: print("ROR") elif ins == 0x60: print("READ") elif ins == 0x61: print("WRITE") elif ins == 0x62: print("MAP") elif ins == 0x63: print("UNMAP") elif ins == 0x64: print("EXIT") else: print(f"unknown ins! {ins}") exit
実行結果は248行になりました(詳細はメモ含めて本ブログ最後に記載)。後は、scsbxにデバッグ出力を追加したりしながら、ひたすら逆コンパイル結果にスタックの処理内容をメモしながら読み解いていきました。
処理内容概略
メモリ関連の使い方を読み解いた内容は以下になります:
- 最初に
0xdead0000
からメモリを確保する - その後のメモリマップは以下の通り
[0xdead0000, 0xdead0004)
:: 522のブロックで 0x06d35bcd を初期値として書き込み、その後534のブロックでLOAD,STOREで更新する[0xdead000a, 0xdead004a)
:: 66でのREAD結果を書き込み、その後ループで更新する[0xdead004a, 0xdead008a)
:: 598のブロックで書き込んだ後は固定、最後に[0xdead000a, 0xdead004a)
と一致するか比較される
JMP命令等によるフロー制御を読み解いた内容は以下になります:
main: 0~77 (10でのmapで0xdead0000メモリを確保) (11でcall 598) (17でcall 522) (23~55で"FLAG:"出力) (55~66で[0xdead000a, 0xdead004a)へREAD結果格納) (62でのpush 8がループ変数) iループ(8→1): 270~291 78~162 (114で [0xdead000a+8*(8-i)] のLOAD) (151で [0xdead000e+8*(8-i)] のLOAD) (152のpush 3がループ変数) jループ(3→1): 221~242 163~220 (186で534へのcallを含む、つまり[0xdead0000]を更新する) 243~291 (249で [0xdead000e+8*(8-i)] = 計算結果1 なSTORE) (256で [0xdead000a+8*(8-i)] = 計算結果2 なSTORE) 292~308 (292で一旦スタックは空になる、298で8をpush, 293で0をpush) iループ(8→1): 405~426 309~426 (全体的に: [0xdead000a, 0xdead004a)と[0xdead004a, 0xdead008a)の一致判定、後者の[0xdead004a, 0xdead008a)は初期化時から不変なのでそれ依存で分かりそう) 427~437 if 一致: 477~515 else: 438~476 516~521(exit) def 522: [0xdead0000]の初期値設定 def 534: 534~597 (539で[0xdead0000]をLOAD) (581で[0xdead0000]へ ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea) としてSTORE) (597で スタックトップに「~((呼び出し時のtop) xor (更新後の[0xdead0000]))」をpushした状態でreturn) def 598: 598~774 ([0xdead004a, 0xdead008a)の設定、これらの値は以降変わらない)
フラグの計算
上記の読み解いた結果から、以下のことが分かります。
- 入力を8byte単位で処理
- 計算結果が特定8byteに一致すること必須
- それを0x40byte分繰り返す
今回の場合、フラグ形式がSECCON{
の7文字から始まる事がわかっています。それを利用して順方向処理の動作確認をして、その後に逆方向の処理を実装しました:
#!/usr/bin/env python3 # 0xdead0000の値の推移 dead0000 = [0x6d35bcd,0x2a5d2289,0x2f6875f9,0x2fad9e73,0x5b9550f,0x26c9c89f,0x11c0d0f,0x20e72c5d,0x13c96e3b,0x22929531,0x28cc5725,0x12466b89,0xc06913b,0x253aa61b,0x12a3213b,0x23b9fa5d,0xda0ec51,0x294b7005,0x2bbf4a7d,0x2d748c31,0x2b8ac467,0x478d51b,0x25080a67,0x629db31,0x3635d3b,] print(len(dead0000)) # 25 # [0xdead004a, 0xdead008a)の設定値 expected = [0x46761223,0x54bea5c5,0x7a22e8f6,0x5db493c9,0x055d175e,0x022fcd33,0x42c46be6,0x6d10a0e8,0x53f4c278,0x7279ec2a,0x5491fb39,0x49ac421f,0x49ab3a37,0x47855812,0x5718bb05,0x0540fb5b,] print(len(expected)) # 16 # loopCount: iループの回数、iは8→1のデクリメントだけど、ここでは0~7を前提 def convert(lower, upper, loopCount): for i in range(3): dead = dead0000[(3*loopCount)+i+1] xor1 = upper ^ dead tilde = ~xor1 # (((~(xor1))+0x100000000)&0xFFFFFFFF) # print(f"dead={hex(dead)}") # print(f"xor1={hex(xor1)}") # print(f"tilde={hex(tilde)}") [lower, upper] = [upper, lower ^ tilde] # print(f"lower={hex(lower)}") # print(f"upper={hex(upper)}") # print() return [lower, upper] # loopCount: iループの回数、iは8→1のデクリメントだけど、ここでは0~7を前提 def invert(lower, upper, loopCount): for i in range(3): tilde = upper ^ lower xor1 = ~tilde [lower, upper] = [xor1 ^ dead0000[(3*loopCount)+(2-i)+1], lower] return [lower, upper] def get_bytes(s): return int.from_bytes(s.encode("utf-8"), byteorder="little") def get_str(n): return n.to_bytes(length=4, byteorder="little").decode("utf-8") # for c in range(0x20, 0x7f): # [lower, upper] = convert(get_bytes("SECC"), get_bytes("ON{"+chr(c)), 0) # if lower==expected[0] and upper==expected[1]: # print(c, chr(c)) # "T" for i in range(8): [lower, upper] = invert(expected[2*i], expected[2*i+1], i) print(f"{get_str(lower)}{get_str(upper)}", end="") print()
上記を実行してフラグが手に入りました: SECCON{TfuRYYVaz8Us696t3JWNxZZPsXEmdL7cCmgzpgxXKarUOnIwhSj9tQ}
感想
- スタックマシンの処理を追うのは難しかったです
- とくにDUPとXCHGを追うのが勘違いが頻発してとても難しかったです
- それでもこういった処理を追うのはまさにリバーシングで楽しめました
- だいぶ難しく感じたのですが、それでもwelcome/surveyを除いたら、3番目に解かれている問題なんですね、皆様凄い
- SECCONはとても難しい、他の問題は手も足も出ませんでした
ここまでを導出するために使用した逆コンパイルコードとメモ結果
この記事はこのセクションで終わりです。 自分用メモなので他の方が理解できるかどうかは保証できません。スタック箇所のメモは左がtopです。
0 : push 4096(=0x1000) // map size 5 : push 3735879680(=0xdead0000) // map address 10 : MAP 11 : push 598(=0x256) 16 : CALL 17 : push 522(=0x20a) 22 : CALL // ここから書き込み文字列構築 23 : push 1195461702(=0x47414c46) 28 : push 3735879684(=0xdead0004) // store address 33 : STORE32 34 : push 8250(=0x203a) 39 : push 3735879688(=0xdead0008) // store address 44 : STORE16 45 : push 6(=0x6) // write size 50 : push 3735879684(=0xdead0004) // write size address 55 : WRITE // ここまで書き込み 56 : push 64(=0x40) // read size!!!!!! 61 : push 3735879690(=0xdead000a) // read address!!!! 66 : READ 67 : push 8(=0x8) // カウンター 72 : push 270(=0x10e) 77 : JMP // ここまで最初の処理 // pc291で不一致に来る(多分8..1(inclusive)でデクリメントするカウンターiが入っている) 78 : push 0(=0x0) // [0, i] 83 : DUP // [i, i] 84 : push 8(=0x8) // [8, i, i] 89 : SUB // [8-i, i, ...] 90 : push 8(=0x8) 95 : MUL // [8*(8-i), i, ...] 96 : push 0(=0x0) 101 : DUP // [8*(8-i), 8*(8-i), i, ...] 102 : push 3735879690(=0xdead000a) 107 : ADD // [0xdead000a+8*(8-i), 8*(8-i), i] 108 : push 0(=0x0) 113 : DUP // [0xdead000a+8*(8-i), 0xdead000a+8*(8-i), 8*(8-i), i] 114 : LOAD32 // {[0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 8*(8-i), i} 115 : push 0(=0x0) 120 : XCHG // {[0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 8*(8-i), i} 121 : push 2(=0x2) 126 : XCHG // {8*(8-i), 0xdead000a+8*(8-i), [0xdead000a+8*(8-i)], i} 127 : push 3735879694(=0xdead000e) 132 : ADD // {0xdead000e+8*(8-i), 0xdead000a+8*(8-i), [0xdead000a+8*(8-i)], i} 133 : push 0(=0x0) 138 : DUP // {0xdead000e+8*(8-i), 0xdead000e+8*(8-i), 0xdead000a+8*(8-i), [0xdead000a+8*(8-i)], i} 139 : push 3(=0x3) 144 : XCHG // {[0xdead000a+8*(8-i)], 0xdead000e+8*(8-i), 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 145 : push 1(=0x1) 150 : XCHG // {0xdead000e+8*(8-i), [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 151 : LOAD32 // {[0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 152 : push 3(=0x3) // {j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 157 : push 221(=0xdd) 162 : JMP // cp242のjのデクリメントループ(初期値3、1含めてここまで来る) {j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} // 2番めは、最初は「次の4文字」、2回目以降は (4文字 xor ~(次の4文字 xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))) (つまり4文字と次の4文字をベースに色々計算した結果、[0xdead0000]は毎回更新されるので注意) 163 : push 2(=0x2) 168 : DUP // {[0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 169 : push 2(=0x2) 174 : DUP // {[0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 175 : push 0(=0x0) 180 : DUP // {[0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 181 : push 534(=0x216) 186 : CALL // 中で0xdead0000へのload, storestoreしている、 {~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)), [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 187 : push 2(=0x2) 192 : DUP // {[0xdead000a+8*(8-i)], ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)), [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 193 : XOR // { ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 194 : push 4(=0x4) 199 : XCHG // { [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 200 : POP // { [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 201 : push 4(=0x4) 206 : XCHG // { [0xdead000a+8*(8-i)], [0xdead000a+8*(8-i)], j, ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000e+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 207 : POP 208 : POP 209 : push 1(=0x1) // {1, j, ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000e+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 214 : push 1(=0x1) 219 : XCHG // {j, 1, ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000e+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 220 : SUB // 実質j--しての {j, ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000e+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} // cp157からjmpして来た時: {j(=3), [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} // cp163から滑ってきた時: {j, ([0xdead000a+8*(8-i)] xor ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea))), [0xdead000e+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} // つまりjが1減って、元2番めが新3番目にずれて、元2番めの値を元に新2番めの値が決まる、4~6番目の値は不変 // 実質jが0ならcp243へ、1以上ならcp163へ、って分岐 221 : push 0(=0x0) 226 : DUP // {j, j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 227 : push 0(=0x0) 232 : push 163(=0xa3) // f 237 : push 243(=0xf3) // t 242 : JEQ // ここまでjループ(ここはもう0にあったあと、つまり3回回るはず) // {0(==j), (計算結果3), (計算結果2), 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 243 : push 4(=0x4) 248 : XCHG // {0xdead000e+8*(8-i), (計算結果3), (計算結果2), 0xdead000a+8*(8-i), 0, i} 249 : STORE32 // {(計算結果2), 0xdead000a+8*(8-i), 0, i} 250 : push 1(=0x1) 255 : XCHG // {0xdead000a+8*(8-i), (計算結果2), 0, i} 256 : STORE32 // {0, i} 257 : POP // {i} 258 : push 1(=0x1) 263 : push 1(=0x1) 268 : XCHG 269 : SUB // 実質i--からの{i} // ここへpc72からjmpしてくる(事前にpush 8付き)→8はcounteでは?4*8=32文字? // 上から滑ってくる場合でも{i},だけ // iが0なら292へ[i]残して, それ以外なら[i]残して78へ 270 : push 0(=0x0) 275 : DUP // {i, i} 276 : push 0(=0x0) // b 281 : push 78(=0x4e) // if NOT 286 : push 292(=0x124) // if equal 291 : JEQ // 291のpush結果が一致している場合にここに来る 292 : POP // {} 293 : push 0(=0x0) 298 : push 8(=0x8) [8, 0] 303 : push 405(=0x195) 308 : JMP // // pc426のJEQのfalse branch 309 : push 0(=0x0) 314 : DUP // {i, i, 0} 315 : push 1(=0x1) 320 : push 1(=0x1) 325 : XCHG // {i, 1, i, 0} 326 : SUB // {i-1, i, 0} 327 : push 8(=0x8) 332 : MUL // {8*(i-1), i, 0} 333 : push 3735879690(=0xdead000a) 338 : ADD // {0xdead000a+8*(i-1), i, 0} 339 : push 0(=0x0) 344 : DUP // {0xdead000a+8*(i-1), 0xdead000a+8*(i-1), i, 0} 345 : push 64(=0x40) 350 : ADD // {0xdead004a+8*(i-1), 0xdead000a+8*(i-1), i, 0} // LOAD64なので2byte分積まれる // iがデクリメントするループなので後ろの方から積まれることに注意!(つまり、i=8時は {0x5718bb05, 0x0540fb5b, ...}が積まれる) 351 : LOAD64 // {[0xdead004a+8*(i-1)], [0xdead004e+8*(i-1)], 0xdead000a+8*(i-1), i, 0} 352 : push 2(=0x2) 357 : XCHG // {0xdead000a+8*(i-1), [0xdead004e+8*(i-1)], [0xdead004a+8*(i-1)], i, 0} 358 : LOAD64 // {[0xdead000a+8*(i-1)], [0xdead000e+8*(i-1)], [0xdead004e+8*(i-1)], [0xdead004a+8*(i-1)], i, 0} 359 : push 2(=0x2) 364 : XCHG // {[0xdead004e+8*(i-1)], [0xdead000e+8*(i-1)], [0xdead000a+8*(i-1)], [0xdead004a+8*(i-1)], i, 0} 365 : SUB // { ([0xdead004e+8*(i-1)]-[0xdead000e+8*(i-1)]), [0xdead000a+8*(i-1)], [0xdead004a+8*(i-1)], i, 0} 366 : push 2(=0x2) 371 : XCHG // {[0xdead004a+8*(i-1)], [0xdead000a+8*(i-1)], ([0xdead004e+8*(i-1)]-[0xdead000e+8*(i-1)]), i, 0} 372 : SUB // { ([0xdead004a+8*(i-1)]-[0xdead000a+8*(i-1)]), ([0xdead004e+8*(i-1)]-[0xdead000e+8*(i-1)]), i, 0} 373 : OR // { (つまり8バイトが一致していたら0), i, 0} 374 : push 1(=0x1) 379 : XCHG // { i, (正解なら0), 0} 380 : push 2(=0x2) 385 : XCHG // { 0, (正解なら0), i} 386 : OR // { (正解なら0), i } 387 : push 1(=0x1) 392 : XCHG // { i, (正解なら0) } 393 : push 1(=0x1) 398 : push 1(=0x1) 403 : XCHG 404 : SUB // 実質i--, { i, (正解なら0) } // pc308から {i, 0}で来る、スタックトップが0かどうかで分岐(→これもカウンターでは?) // 上から流れてくることもある { i, (正解なら0) } 405 : push 0(=0x0) // 410 : DUP // {i, i, 0} 411 : push 0(=0x0) // b 416 : push 309(=0x135) // if NOT 421 : push 427(=0x1ab) // if eq, ここに飛ぶときは(正解なら0)が0のままであってほしい! 426 : JEQ // pc426のJEQのtrue branch {i, (正解なら0) } 427 : push 438(=0x1b6) // if not 432 : push 477(=0x1dd) // if eq, 一致してほしい!!!!!! 437 : JEQ // pc437のfalse branch, 来てほしくない 438 : push 1852797527(=0x6e6f7257) // "Wron" 443 : push 3735879680(=0xdead0000) 448 : STORE32 449 : push 169943399(=0xa212167) // "g!!\n" 454 : push 3735879684(=0xdead0004) 459 : STORE32 460 : push 8(=0x8) 465 : push 3735879680(=0xdead0000) 470 : WRITE 471 : push 516(=0x204) 476 : JMP // exit 0 // pc437のJEQのfalse branch、ここに来てほしい! 477 : push 1920102211(=0x72726f43) // "Corr" 482 : push 3735879680(=0xdead0000) 487 : STORE32 488 : push 175399781(=0xa746365) // "ect\n" 493 : push 3735879684(=0xdead0004) 498 : STORE32 499 : push 8(=0x8) 504 : push 3735879680(=0xdead0000) 509 : WRITE 510 : push 516(=0x204) 515 : JMP // exit 0 // pc476 or 515からのjmp(そしてここで終わる) 516 : push 0(=0x0) 521 : EXIT // pc16→pc522へcall // storeのみの関数 522 : push 114514893(=0x6d35bcd) // value 527 : push 3735879680(=0xdead0000) // address 532 : STORE32 533 : JMP // [0xdead0000]の内容を ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea) で更新する // スタックトップに ~((呼び出し時のtop) xor (更新後の[0xdead0000])) をpushした状態でreturn // pc186→pc534、多分入力結果を加工する奴, {(ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 534 : push 3735879680(=0xdead0000) //最初は0x6d35bcdだけどこの関数末尾で更新してるね 539 : LOAD32 // {[0xdead0000], (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 540 : push 1919(=0x77f) 545 : MUL // {0x77f*[0xdead0000], (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 546 : push 810(=0x32a) // {0x32a, 0x77f*[0xdead0000], (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 551 : push 1(=0x1) 556 : XCHG // {0x77f*[0xdead0000], 0x32a, (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 557 : SUB // {(0x77f*[0xdead0000]-0x32a), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 558 : push 811512810(=0x305eb3ea) // {0x305eb3ea, (0x77f*[0xdead0000]-0x32a), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 563 : push 1(=0x1) 568 : XCHG // {(0x77f*[0xdead0000]-0x32a), 0x305eb3ea, (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 569 : MOD // {((0x77f*[0xdead0000]-0x32a)%0x305eb3ea), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 570 : push 0(=0x0) 575 : DUP // {((0x77f*[0xdead0000]-0x32a)%0x305eb3ea), ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} // ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)へ更新 // 初回は0x6d35bcd→2a5d2289へ更新 576 : push 3735879680(=0xdead0000) 581 : STORE32 // {((0x77f*[0xdead0000]-0x32a)%0x305eb3ea), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} // return address以下がかかわるのはここからだけ 582 : push 2(=0x2) 587 : DUP // {[0xdead000e+8*(8-i)], ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 588 : XOR // {([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 589 : NOT // { ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)), (ra), [0xdead000e+8*(8-i)], [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 590 : push 2(=0x2) 595 : XCHG // { [0xdead000e+8*(8-i)], (ra), ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)), [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 596 : POP // {(ra), ~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)), [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} 597 : JMP // {~([0xdead000e+8*(8-i)] xor ((0x77f*[0xdead0000]-0x32a)%0x305eb3ea)), [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], j, [0xdead000e+8*(8-i)], [0xdead000a+8*(8-i)], 0xdead000a+8*(8-i), 0xdead000e+8*(8-i), i} // pc11→pc598へcallする // このブロックはstore32だけして終わり 598 : push 1182143011(=0x46761223) // value 603 : push 3735879754(=0xdead004a) // address 608 : STORE32 609 : push 1421780421(=0x54bea5c5) 614 : push 3735879758(=0xdead004e) 619 : STORE32 620 : push 2049108214(=0x7a22e8f6) 625 : push 3735879762(=0xdead0052) 630 : STORE32 631 : push 1572115401(=0x5db493c9) 636 : push 3735879766(=0xdead0056) 641 : STORE32 642 : push 89986910(=0x55d175e) 647 : push 3735879770(=0xdead005a) 652 : STORE32 653 : push 36687155(=0x22fcd33) 658 : push 3735879774(=0xdead005e) 663 : STORE32 664 : push 1120168934(=0x42c46be6) 669 : push 3735879778(=0xdead0062) 674 : STORE32 675 : push 1829806312(=0x6d10a0e8) 680 : push 3735879782(=0xdead0066) 685 : STORE32 686 : push 1408549496(=0x53f4c278) 691 : push 3735879786(=0xdead006a) 696 : STORE32 697 : push 1920592938(=0x7279ec2a) 702 : push 3735879790(=0xdead006e) 707 : STORE32 708 : push 1418853177(=0x5491fb39) 713 : push 3735879794(=0xdead0072) 718 : STORE32 719 : push 1236025887(=0x49ac421f) 724 : push 3735879798(=0xdead0076) 729 : STORE32 730 : push 1235958327(=0x49ab3a37) 735 : push 3735879802(=0xdead007a) 740 : STORE32 741 : push 1199921170(=0x47855812) 746 : push 3735879806(=0xdead007e) 751 : STORE32 752 : push 1461238533(=0x5718bb05) 757 : push 3735879810(=0xdead0082) 762 : STORE32 763 : push 88144731(=0x540fb5b) 768 : push 3735879814(=0xdead0086) 773 : STORE32 774 : JMP