Skip to content

Instantly share code, notes, and snippets.

@Kyle-Kyle
Last active August 31, 2020 05:03
Show Gist options
  • Select an option

  • Save Kyle-Kyle/61a4c9747f85888ce0487f00d32af1ce to your computer and use it in GitHub Desktop.

Select an option

Save Kyle-Kyle/61a4c9747f85888ce0487f00d32af1ce to your computer and use it in GitHub Desktop.
googlectf2020_sprint
---- 0 ----
A3 = 0x26
A9 = 0x7000
-------------
---- 1 ----
A3 = 0x4a
A7 = max(A8, A1)
-------------
---- 2 ----
A3 = 0x6c
A6 = 0x1
-------------
---- 3 ----
A3 = 0x95
A7 = max(A8, A1) + 0x2
-------------
---- 4 ----
A3 = 0xb7
A6 = 0x1
-------------
---- 5 ----
A3 = 0xda
A11 = 0x2
-------------
---- 6 ----
A3 = 0x108
A17 = max(A10, A1) + max(A10, A1)
-------------
---- 7 ----
A3 = 0x136
A7 = 0x7000 + max(A16, A1)
-------------
---- 8 ----
A3 = 0x15b
A15 = max(A5, A1)
-------------
---- 9 ----
A3 = COND_JUMP_A14 + 0x1a3 + 1 + A4 + 0xffdb
-------------
---- 10 ----
A3 = 0x1ae
A13 = max(A10, A1) + max(A10, A1)
-------------
---- 11 ----
A3 = 0x1d4
A7 = 0xffef
-------------
---- 12 ----
A3 = 0x1f9
A6 = max(A12, A1)
-------------
---- 13 ----
A3 = 0x21f
A7 = 0xfff0
-------------
---- 14 ----
A3 = 0x244
A15 = max(A5, A1)
-------------
---- 15 ----
A3 = COND_JUMP_A14 + 0xba + 1 + A4 + 0x1ad
-------------
---- 16 ----
A3 = 0x297
A17 = max(A12, A1) + max(A12, A1)
-------------
---- 17 ----
A3 = 0x2c5
A7 = 0x7000 + max(A16, A1)
-------------
---- 18 ----
A3 = 0x2e7
A6 = 0x1
-------------
---- 19 ----
A3 = 0x315
A13 = max(A12, A1) + max(A10, A1)
-------------
---- 20 ----
A3 = 0x1ae
-------------
---- 21 ----
A3 = 0x34f
A11 = max(A10, A1) + 0x1
-------------
---- 22 ----
A3 = COND_JUMP_A10 + 0xfd65 + 1 + A4 + 0x60d
-------------
---- 23 ----
A3 = 0x39a
A9 = 0xe000
-------------
---- 24 ----
A3 = 0x3bd
A11 = A1
-------------
---- 25 ----
A3 = 0x3e1
A7 = max(A8, A1)
-------------
---- 26 ----
A3 = 0x406
A13 = max(A5, A1)
-------------
---- 27 ----
A3 = COND_JUMP_A12 + 0xe + 1 + A4 + 0x41b
-------------
---- 28 ----
A3 = 0x4a1
-------------
---- 29 ----
A3 = 0x469
A11 = max(A10, A1) + 0xffff
-------------
---- 30 ----
A3 = 0x492
A9 = max(A8, A1) + 0x1
-------------
---- 31 ----
A3 = 0x3bd
-------------
---- 32 ----
A3 = 0x4d0
A17 = max(A10, A1) + 0xfe
-------------
---- 33 ----
A3 = COND_JUMP_A16 + 0xe + 1 + A4 + 0x4e5
-------------
---- 34 ----
A3 = 0x536
-------------
---- 35 ----
A3 = 0x527
A23 = 0x5
-------------
---- 36 ----
A3 = 0x13d9
-------------
---- 37 ----
A3 = 0x558
A9 = A1
-------------
---- 38 ----
A3 = 0x57b
A11 = A1
-------------
---- 39 ----
A3 = 0x5a1
A7 = 0xf100
-------------
---- 40 ----
A3 = 0x5c6
A13 = max(A5, A1)
-------------
---- 41 ----
A3 = 0x5e9
A15 = 0x1
-------------
---- 42 ----
A3 = 0x60c
A23 = A1
-------------
---- 43 ----
A3 = 0x639
A7 = 0xe000 + max(A8, A1)
-------------
---- 44 ----
A3 = 0x65e
A17 = max(A5, A1)
-------------
---- 45 ----
A3 = COND_JUMP_A16 + 0xe + 1 + A4 + 0x673
-------------
---- 46 ----
A3 = 0xd97
-------------
---- 47 ----
A3 = 0x6bb
A9 = max(A8, A1) + 0x1
-------------
---- 48 ----
A3 = 0x6ea
A19 = max(A16, A1) + 0xff8b
-------------
---- 49 ----
A3 = COND_JUMP_A18 + 0x35 + 1 + A4 + 0x6d8
-------------
---- 50 ----
A3 = 0x736
A17 = 0xfff0
-------------
---- 51 ----
A3 = 0x945
-------------
---- 52 ----
A3 = 0x774
A19 = max(A16, A1) + 0xff8e
-------------
---- 53 ----
A3 = COND_JUMP_A18 + 0x31 + 1 + A4 + 0x766
-------------
---- 54 ----
A3 = 0x7bc
A17 = 0x1
-------------
---- 55 ----
A3 = 0x945
-------------
---- 56 ----
A3 = 0x7fa
A19 = max(A16, A1) + 0xff9c
-------------
---- 57 ----
A3 = COND_JUMP_A18 + 0x32 + 1 + A4 + 0x7eb
-------------
---- 58 ----
A3 = 0x843
A17 = 0x10
-------------
---- 59 ----
A3 = 0x945
-------------
---- 60 ----
A3 = 0x881
A19 = max(A16, A1) + 0xff94
-------------
---- 61 ----
A3 = COND_JUMP_A18 + 0x35 + 1 + A4 + 0x86f
-------------
---- 62 ----
A3 = 0x8cd
A17 = 0xffff
-------------
---- 63 ----
A3 = 0x945
-------------
---- 64 ----
A3 = 0x8ff
A15 = A1
-------------
---- 65 ----
A3 = 0x922
A17 = A1
-------------
---- 66 ----
A3 = 0x945
A23 = 0x1
-------------
---- 67 ----
A3 = 0x973
A13 = max(A12, A1) + max(A16, A1)
-------------
---- 68 ----
A3 = 0x999
A7 = 0xffef
-------------
---- 69 ----
A3 = 0x9be
A6 = max(A12, A1)
-------------
---- 70 ----
A3 = 0x9e4
A7 = 0xfff0
-------------
---- 71 ----
A3 = 0xa09
A17 = max(A5, A1)
-------------
---- 72 ----
A3 = COND_JUMP_A16 + 0x336 + 1 + A4 + 0x6f6
-------------
---- 73 ----
A3 = 0xa5c
A7 = 0xf000 + max(A12, A1)
-------------
---- 74 ----
A3 = 0xa81
A17 = max(A5, A1)
-------------
---- 75 ----
A3 = 0xaa7
A7 = 0xffef
-------------
---- 76 ----
A3 = 0xacc
A6 = max(A16, A1)
-------------
---- 77 ----
A3 = 0xaf2
A7 = 0xfff0
-------------
---- 78 ----
A3 = 0xb14
A6 = A1
-------------
---- 79 ----
A3 = 0xb3a
A7 = 0xffef
-------------
---- 80 ----
A3 = 0xb5f
A17 = max(A5, A1)
-------------
---- 81 ----
A3 = 0xb8d
A17 = max(A16, A1) + max(A16, A1)
-------------
---- 82 ----
A3 = 0xbbb
A7 = 0x7000 + max(A16, A1)
-------------
---- 83 ----
A3 = 0xbe0
A17 = max(A5, A1)
-------------
---- 84 ----
A3 = COND_JUMP_A16 + 0x10a + 1 + A4 + 0xaf9
-------------
---- 85 ----
A3 = 0xc30
A17 = max(A10, A1) + 0x1
-------------
---- 86 ----
A3 = 0xc5e
A7 = 0xf102 + max(A16, A1)
-------------
---- 87 ----
A3 = 0xc83
A17 = max(A5, A1)
-------------
---- 88 ----
A3 = 0xcb1
A17 = max(A16, A1) + max(A12, A1)
-------------
---- 89 ----
A3 = COND_JUMP_A16 + 0x2a + 1 + A4 + 0xcaa
-------------
---- 90 ----
A3 = 0xd01
A11 = max(A10, A1) + 0x1
-------------
---- 91 ----
A3 = 0x60c
-------------
---- 92 ----
A3 = 0xd33
A15 = A1
-------------
---- 93 ----
A3 = 0xd56
A23 = 0x2
-------------
---- 94 ----
A3 = 0x60c
-------------
---- 95 ----
A3 = 0xd88
A23 = 0x4
-------------
---- 96 ----
A3 = 0xfffe
-------------
---- 97 ----
A3 = COND_JUMP_A14 + 0xe + 1 + A4 + 0xdac
-------------
---- 98 ----
A3 = 0x13d9
-------------
---- 99 ----
A3 = 0xdfa
A17 = max(A10, A1) + 0xfff7
-------------
---- 100 ----
A3 = COND_JUMP_A16 + 0xe + 1 + A4 + 0xe0f
-------------
---- 101 ----
A3 = 0xe60
-------------
---- 102 ----
A3 = 0xe51
A23 = 0x3
-------------
---- 103 ----
A3 = 0x13d9
-------------
---- 104 ----
A3 = 0xe82
A9 = A1
-------------
---- 105 ----
A3 = 0xea5
A11 = A1
-------------
---- 106 ----
A3 = 0xed3
A13 = max(A8, A1) + 0xffd9
-------------
---- 107 ----
A3 = COND_JUMP_A12 + 0xe + 1 + A4 + 0xee8
-------------
---- 108 ----
A3 = 0x137b
-------------
---- 109 ----
A3 = 0xf2a
A15 = 0x4
-------------
---- 110 ----
A3 = 0xf4d
A13 = A1
-------------
---- 111 ----
A3 = 0xf7b
A13 = max(A12, A1) + max(A12, A1)
-------------
---- 112 ----
A3 = 0xfa9
A13 = max(A12, A1) + max(A12, A1)
-------------
---- 113 ----
A3 = 0xfd7
A7 = 0xe000 + max(A10, A1)
-------------
---- 114 ----
A3 = 0xffc
A17 = max(A5, A1)
-------------
---- 115 ----
A3 = 0x102b
A19 = max(A16, A1) + 0xff8b
-------------
---- 116 ----
A3 = COND_JUMP_A18 + 0xe + 1 + A4 + 0x1040
-------------
---- 117 ----
A3 = 0x1218
-------------
---- 118 ----
A3 = 0x108e
A19 = max(A16, A1) + 0xff8e
-------------
---- 119 ----
A3 = COND_JUMP_A18 + 0x39 + 1 + A4 + 0x1078
-------------
---- 120 ----
A3 = 0x10de
A13 = max(A12, A1) + 0x1
-------------
---- 121 ----
A3 = 0x1218
-------------
---- 122 ----
A3 = 0x111c
A19 = max(A16, A1) + 0xff9c
-------------
---- 123 ----
A3 = COND_JUMP_A18 + 0x39 + 1 + A4 + 0x1106
-------------
---- 124 ----
A3 = 0x116c
A13 = max(A12, A1) + 0x2
-------------
---- 125 ----
A3 = 0x1218
-------------
---- 126 ----
A3 = 0x11aa
A19 = max(A16, A1) + 0xff94
-------------
---- 127 ----
A3 = COND_JUMP_A18 + 0x39 + 1 + A4 + 0x1194
-------------
---- 128 ----
A3 = 0x11fa
A13 = max(A12, A1) + 0x3
-------------
---- 129 ----
A3 = 0x1218
-------------
---- 130 ----
A3 = 0x13d9
-------------
---- 131 ----
A3 = 0x1243
A11 = max(A10, A1) + 0x1
-------------
---- 132 ----
A3 = 0x1272
A15 = max(A14, A1) + 0xffff
-------------
---- 133 ----
A3 = COND_JUMP_A14 + 0xfcb5 + 1 + A4 + 0x15e0
-------------
---- 134 ----
A3 = 0x12c4
A7 = 0xf10c + max(A8, A1)
-------------
---- 135 ----
A3 = 0x12e9
A15 = max(A5, A1)
-------------
---- 136 ----
A3 = 0x1316
A7 = 0xe800 + max(A8, A1)
-------------
---- 137 ----
A3 = 0x1343
A6 = max(A14, A1) + max(A12, A1)
-------------
---- 138 ----
A3 = 0x136c
A9 = max(A8, A1) + 0x1
-------------
---- 139 ----
A3 = 0xea5
-------------
---- 140 ----
A3 = 0x13a8
A7 = 0xe800 + max(A8, A1)
-------------
---- 141 ----
A3 = 0x13ca
A6 = A1
-------------
---- 142 ----
A3 = 0xfffe
-------------
---- 143 ----
A3 = 0x13ff
A7 = 0xe800
-------------
---- 144 ----
A3 = 0x1421
A6 = A1
-------------
---- 145 ----
A3 = 0xfffe
-------------
['%1$00038s%3$hn%1$65498s%1$28672s%9$hn', '%1$00074s%3$hn%1$65462s%1$*8$s%7$hn', '%1$00108s%3$hn%1$65428s%1$1s%6$hn', '%1$00149s%3$hn%1$65387s%1$*8$s%1$2s%7$hn', '%1$00183s%3$hn%1$65353s%1$1s%6$hn', '%1$00218s%3$hn%1$65318s%1$2s%11$hn', '%1$00264s%3$hn%1$65272s%1$*10$s%1$*10$s%17$hn', '%1$00310s%3$hn%1$65226s%1$28672s%1$*16$s%7$hn', '%1$00347s%3$hn%1$65189s%1$*5$s%15$hn', '%14$c%1$00419s%2$c%4$s%1$65499s%3$hn', '%1$00430s%3$hn%1$65106s%1$*10$s%1$*10$s%13$hn', '%1$00468s%3$hn%1$65068s%1$65519s%7$hn', '%1$00505s%3$hn%1$65031s%1$*12$s%6$hn', '%1$00543s%3$hn%1$64993s%1$65520s%7$hn', '%1$00580s%3$hn%1$64956s%1$*5$s%15$hn', '%14$c%1$00186s%2$c%4$s%1$00429s%3$hn', '%1$00663s%3$hn%1$64873s%1$*12$s%1$*12$s%17$hn', '%1$00709s%3$hn%1$64827s%1$28672s%1$*16$s%7$hn', '%1$00743s%3$hn%1$64793s%1$1s%6$hn', '%1$00789s%3$hn%1$64747s%1$*12$s%1$*10$s%13$hn', '%1$00430s%3$hn', '%1$00847s%3$hn%1$64689s%1$*10$s%1$1s%11$hn', '%10$c%1$64869s%2$c%4$s%1$01549s%3$hn', '%1$00922s%3$hn%1$64614s%1$57344s%9$hn', '%1$00957s%3$hn%1$64579s%1$0s%11$hn', '%1$00993s%3$hn%1$64543s%1$*8$s%7$hn', '%1$01030s%3$hn%1$64506s%1$*5$s%13$hn', '%12$c%1$00014s%2$c%4$s%1$01051s%3$hn', '%1$01185s%3$hn', '%1$01129s%3$hn%1$64407s%1$*10$s%1$65535s%11$hn', '%1$01170s%3$hn%1$64366s%1$*8$s%1$1s%9$hn', '%1$00957s%3$hn', '%1$01232s%3$hn%1$64304s%1$*10$s%1$00254s%17$hn', '%16$c%1$00014s%2$c%4$s%1$01253s%3$hn', '%1$01334s%3$hn', '%1$01319s%3$hn%1$64217s%1$5s%23$hn', '%1$05081s%3$hn', '%1$01368s%3$hn%1$64168s%1$0s%9$hn', '%1$01403s%3$hn%1$64133s%1$0s%11$hn', '%1$01441s%3$hn%1$64095s%1$61696s%7$hn', '%1$01478s%3$hn%1$64058s%1$*5$s%13$hn', '%1$01513s%3$hn%1$64023s%1$1s%15$hn', '%1$01548s%3$hn%1$63988s%1$0s%23$hn', '%1$01593s%3$hn%1$63943s%1$57344s%1$*8$s%7$hn', '%1$01630s%3$hn%1$63906s%1$*5$s%17$hn', '%16$c%1$00014s%2$c%4$s%1$01651s%3$hn', '%1$03479s%3$hn', '%1$01723s%3$hn%1$63813s%1$*8$s%1$1s%9$hn', '%1$01770s%3$hn%1$63766s%1$*16$s%1$65419s%19$hn', '%18$c%1$00053s%2$c%4$s%1$01752s%3$hn', '%1$01846s%3$hn%1$63690s%1$65520s%17$hn', '%1$02373s%3$hn', '%1$01908s%3$hn%1$63628s%1$*16$s%1$65422s%19$hn', '%18$c%1$00049s%2$c%4$s%1$01894s%3$hn', '%1$01980s%3$hn%1$63556s%1$1s%17$hn', '%1$02373s%3$hn', '%1$02042s%3$hn%1$63494s%1$*16$s%1$65436s%19$hn', '%18$c%1$00050s%2$c%4$s%1$02027s%3$hn', '%1$02115s%3$hn%1$63421s%1$16s%17$hn', '%1$02373s%3$hn', '%1$02177s%3$hn%1$63359s%1$*16$s%1$65428s%19$hn', '%18$c%1$00053s%2$c%4$s%1$02159s%3$hn', '%1$02253s%3$hn%1$63283s%1$65535s%17$hn', '%1$02373s%3$hn', '%1$02303s%3$hn%1$63233s%1$0s%15$hn', '%1$02338s%3$hn%1$63198s%1$0s%17$hn', '%1$02373s%3$hn%1$63163s%1$1s%23$hn', '%1$02419s%3$hn%1$63117s%1$*12$s%1$*16$s%13$hn', '%1$02457s%3$hn%1$63079s%1$65519s%7$hn', '%1$02494s%3$hn%1$63042s%1$*12$s%6$hn', '%1$02532s%3$hn%1$63004s%1$65520s%7$hn', '%1$02569s%3$hn%1$62967s%1$*5$s%17$hn', '%16$c%1$00822s%2$c%4$s%1$01782s%3$hn', '%1$02652s%3$hn%1$62884s%1$61440s%1$*12$s%7$hn', '%1$02689s%3$hn%1$62847s%1$*5$s%17$hn', '%1$02727s%3$hn%1$62809s%1$65519s%7$hn', '%1$02764s%3$hn%1$62772s%1$*16$s%6$hn', '%1$02802s%3$hn%1$62734s%1$65520s%7$hn', '%1$02836s%3$hn%1$62700s%1$0s%6$hn', '%1$02874s%3$hn%1$62662s%1$65519s%7$hn', '%1$02911s%3$hn%1$62625s%1$*5$s%17$hn', '%1$02957s%3$hn%1$62579s%1$*16$s%1$*16$s%17$hn', '%1$03003s%3$hn%1$62533s%1$28672s%1$*16$s%7$hn', '%1$03040s%3$hn%1$62496s%1$*5$s%17$hn', '%16$c%1$00266s%2$c%4$s%1$02809s%3$hn', '%1$03120s%3$hn%1$62416s%1$*10$s%1$1s%17$hn', '%1$03166s%3$hn%1$62370s%1$61698s%1$*16$s%7$hn', '%1$03203s%3$hn%1$62333s%1$*5$s%17$hn', '%1$03249s%3$hn%1$62287s%1$*16$s%1$*12$s%17$hn', '%16$c%1$00042s%2$c%4$s%1$03242s%3$hn', '%1$03329s%3$hn%1$62207s%1$*10$s%1$1s%11$hn', '%1$01548s%3$hn', '%1$03379s%3$hn%1$62157s%1$0s%15$hn', '%1$03414s%3$hn%1$62122s%1$2s%23$hn', '%1$01548s%3$hn', '%1$03464s%3$hn%1$62072s%1$4s%23$hn', '%1$65534s%3$hn', '%14$c%1$00014s%2$c%4$s%1$03500s%3$hn', '%1$05081s%3$hn', '%1$03578s%3$hn%1$61958s%1$*10$s%1$65527s%17$hn', '%16$c%1$00014s%2$c%4$s%1$03599s%3$hn', '%1$03680s%3$hn', '%1$03665s%3$hn%1$61871s%1$3s%23$hn', '%1$05081s%3$hn', '%1$03714s%3$hn%1$61822s%1$0s%9$hn', '%1$03749s%3$hn%1$61787s%1$0s%11$hn', '%1$03795s%3$hn%1$61741s%1$*8$s%1$65497s%13$hn', '%12$c%1$00014s%2$c%4$s%1$03816s%3$hn', '%1$04987s%3$hn', '%1$03882s%3$hn%1$61654s%1$4s%15$hn', '%1$03917s%3$hn%1$61619s%1$0s%13$hn', '%1$03963s%3$hn%1$61573s%1$*12$s%1$*12$s%13$hn', '%1$04009s%3$hn%1$61527s%1$*12$s%1$*12$s%13$hn', '%1$04055s%3$hn%1$61481s%1$57344s%1$*10$s%7$hn', '%1$04092s%3$hn%1$61444s%1$*5$s%17$hn', '%1$04139s%3$hn%1$61397s%1$*16$s%1$65419s%19$hn', '%18$c%1$00014s%2$c%4$s%1$04160s%3$hn', '%1$04632s%3$hn', '%1$04238s%3$hn%1$61298s%1$*16$s%1$65422s%19$hn', '%18$c%1$00057s%2$c%4$s%1$04216s%3$hn', '%1$04318s%3$hn%1$61218s%1$*12$s%1$1s%13$hn', '%1$04632s%3$hn', '%1$04380s%3$hn%1$61156s%1$*16$s%1$65436s%19$hn', '%18$c%1$00057s%2$c%4$s%1$04358s%3$hn', '%1$04460s%3$hn%1$61076s%1$*12$s%1$2s%13$hn', '%1$04632s%3$hn', '%1$04522s%3$hn%1$61014s%1$*16$s%1$65428s%19$hn', '%18$c%1$00057s%2$c%4$s%1$04500s%3$hn', '%1$04602s%3$hn%1$60934s%1$*12$s%1$3s%13$hn', '%1$04632s%3$hn', '%1$05081s%3$hn', '%1$04675s%3$hn%1$60861s%1$*10$s%1$1s%11$hn', '%1$04722s%3$hn%1$60814s%1$*14$s%1$65535s%15$hn', '%14$c%1$64693s%2$c%4$s%1$05600s%3$hn', '%1$04804s%3$hn%1$60732s%1$61708s%1$*8$s%7$hn', '%1$04841s%3$hn%1$60695s%1$*5$s%15$hn', '%1$04886s%3$hn%1$60650s%1$59392s%1$*8$s%7$hn', '%1$04931s%3$hn%1$60605s%1$*14$s%1$*12$s%6$hn', '%1$04972s%3$hn%1$60564s%1$*8$s%1$1s%9$hn', '%1$03749s%3$hn', '%1$05032s%3$hn%1$60504s%1$59392s%1$*8$s%7$hn', '%1$05066s%3$hn%1$60470s%1$0s%6$hn', '%1$65534s%3$hn', '%1$05119s%3$hn%1$60417s%1$59392s%7$hn', '%1$05153s%3$hn%1$60383s%1$0s%6$hn', '%1$65534s%3$hn']
import re
# load formats
with open("formats.txt") as f:
formats = eval(f.read())
# len-pc mapping
s = 0
mapping = {}
for i in range(len(formats)):
mapping[s] = i
f = formats[i]
s += len(f) + 1
class Format:
def __init__(self, string):
arg_num = None
len_arg_num = None
con_len = None
formatter = None
self._parse(string)
def _parse(self, string):
res = re.match(f"(\d+)\$(.+\$)?(\d*)(\w+)", string)
assert res is not None, string
arg_num = int(res.group(1))
var_len = res.group(2)
con_len = res.group(3)
formatter = res.group(4)
len_arg_num = None
# transform the things a little bit
if not con_len or int(con_len) == 0: con_len = None
if con_len: con_len = int(con_len)
# sanity checks
assert con_len is None or var_len is None
# parse variable length argument number
if var_len:
res = re.match(f"\*(\d+)\$", var_len)
assert res is not None, var_len
len_arg_num = int(res.group(1))
self.arg_num = arg_num
self.len_arg_num = len_arg_num
self.con_len = con_len
self.formatter = formatter
def f2action(sub_fs):
prev_idx = 0
for i in range(len(sub_fs)):
sub_f = sub_fs[i]
# hn indicates the end of an action
if sub_f.formatter != 'hn':
continue
# take out all formats that belongs to one action
action_fs = sub_fs[prev_idx:i+2]
prev_idx = i+2
if action_fs[-1].con_len and action_fs[-1].con_len > 60000:
action_fs = action_fs[:-1]
assert action_fs[-1].formatter == 'hn'
#print([x.__dict__ for x in action_fs])
output = 'A%d =' % (action_fs[-1].arg_num)
first = 1
for action_f in action_fs[:-1]:
if first:
first = 0
else:
output += ' +'
if action_f.formatter == 's' and action_f.con_len:
#val = min(action_f.con_len, 0x10000 - action_f.con_len)
#sign = '+' if val == action_f.con_len else '-'
#if sign == '-':
# output += ' %s%d' % (sign, val)
#else:
# output += ' %d' % (val)
output += ' %#x' % (action_f.con_len)
continue
if action_f.formatter == 's' and not action_f.len_arg_num:
output += ' A%d' % action_f.arg_num
continue
if action_f.formatter == 's' and action_f.len_arg_num:
output += ' max(A%d, A%d)' % (action_f.len_arg_num, action_f.arg_num)
continue
if action_f.formatter == 'c' and action_f.len_arg_num is None:
#output += ' (1 if A%d else 0)' % (action_f.arg_num)
if action_f.arg_num == 2:
output += ' 1'
else:
output += ' COND_JUMP_A%d' % (action_f.arg_num)
continue
print(action_f.__dict__)
print(output)
continue
print([x.__dict__ for x in action_fs])
# try to parse formats
for i in range(len(formats)):
f = formats[i]
stuff = [x for x in f.split('%') if x]
sub_fs = [Format(sub_f_s) for sub_f_s in stuff]
print('---- %d ----' % i)
actions = f2action(sub_fs)
print('-------------')
print('')
---- 0 ----
PC = 1 # A3 = 38
A8 = 0x7000
-------------
---- 1 ----
PC = 2 # A3 = 74
ptr = A8
-------------
---- 2 ----
PC = 3 # A3 = 108
*ptr = 0x1
-------------
---- 3 ----
PC = 4 # A3 = 149
ptr = A8 + 0x2
-------------
---- 4 ----
PC = 5 # A3 = 183
*ptr = 0x1
-------------
---- 5 ----
PC = 6 # A3 = 218
A10 = 0x2
-------------
---- 6 ----
PC = 7 # A3 = 264
A16 = A10 + A10
-------------
---- 7 ----
PC = 8 # A3 = 310
ptr = 0x7000 + A16
-------------
---- 8 ----
PC = 9 # A3 = 347
A14 = *ptr
-------------
---- 9 ----
PC = 10 if A14 == 0 else 21 # A3 = 384 if A14 == 0 else 804
-------------
---- 10 ----
PC = 11 # A3 = 430
A12 = A10 + A10
-------------
---- 11 ----
PC = 12 # A3 = 468
ptr = 0xffef
-------------
---- 12 ----
PC = 13 # A3 = 505
*ptr = A12
-------------
---- 13 ----
PC = 14 # A3 = 543
ptr = 0xfff0
-------------
---- 14 ----
PC = 15 # A3 = 580
A14 = *ptr
-------------
---- 15 ----
PC = 16 if A14 == 0 else 21 # A3 = 617 if A14 == 0 else 804
-------------
---- 16 ----
PC = 17 # A3 = 663
A16 = A12 + A12
-------------
---- 17 ----
PC = 18 # A3 = 709
ptr = 0x7000 + A16
-------------
---- 18 ----
PC = 19 # A3 = 743
*ptr = 0x1
-------------
---- 19 ----
PC = 20 # A3 = 789
A12 = A12 + A10
-------------
---- 20 ----
PC = 11 # A3 = 430
-------------
---- 21 ----
PC = 22 # A3 = 847
A10 = A10 + 0x1
-------------
---- 22 ----
PC = 23 if A10 == 0 else 6 # A3 = 884 if A10 == 0 else 218
-------------
---- 23 ----
PC = 24 # A3 = 922
A8 = 0xe000
-------------
---- 24 ----
PC = 25 # A3 = 957
A10 = 0x0
-------------
---- 25 ----
PC = 26 # A3 = 993
ptr = A8
-------------
---- 26 ----
PC = 27 # A3 = 1030
A12 = *ptr
-------------
---- 27 ----
PC = 28 if A12 == 0 else 29 # A3 = 1067 if A12 == 0 else 1082
-------------
---- 28 ----
PC = 32 # A3 = 1185
-------------
---- 29 ----
PC = 30 # A3 = 1129
A10 = A10 + 0xffff
-------------
---- 30 ----
PC = 31 # A3 = 1170
A8 = A8 + 0x1
-------------
---- 31 ----
PC = 25 # A3 = 957
-------------
---- 32 ----
PC = 33 # A3 = 1232
A16 = A10 + 0xfe
-------------
---- 33 ----
PC = 34 if A16 == 0 else 35 # A3 = 1269 if A16 == 0 else 1284
-------------
---- 34 ----
PC = 37 # A3 = 1334
-------------
---- 35 ----
PC = 36 # A3 = 1319
A22 = 0x5
-------------
---- 36 ----
PC = 143 # A3 = 5081
-------------
---- 37 ----
PC = 38 # A3 = 1368
A8 = 0x0
-------------
---- 38 ----
PC = 39 # A3 = 1403
A10 = 0x0
-------------
---- 39 ----
PC = 40 # A3 = 1441
ptr = 0xf100
-------------
---- 40 ----
PC = 41 # A3 = 1478
A12 = *ptr
-------------
---- 41 ----
PC = 42 # A3 = 1513
A14 = 0x1
-------------
---- 42 ----
PC = 43 # A3 = 1548
A22 = 0x0
-------------
---- 43 ----
PC = 44 # A3 = 1593
ptr = 0xe000 + A8
-------------
---- 44 ----
PC = 45 # A3 = 1630
A16 = *ptr
-------------
---- 45 ----
PC = 46 if A16 == 0 else 47 # A3 = 1667 if A16 == 0 else 1682
-------------
---- 46 ----
PC = 97 # A3 = 3479
-------------
---- 47 ----
PC = 48 # A3 = 1723
A8 = A8 + 0x1
-------------
---- 48 ----
PC = 49 # A3 = 1770
A18 = A16 + 0xff8b
-------------
---- 49 ----
PC = 50 if A18 == 0 else 52 # A3 = 1807 if A18 == 0 else 1861
-------------
---- 50 ----
PC = 51 # A3 = 1846
A16 = 0xfff0
-------------
---- 51 ----
PC = 67 # A3 = 2373
-------------
---- 52 ----
PC = 53 # A3 = 1908
A18 = A16 + 0xff8e
-------------
---- 53 ----
PC = 54 if A18 == 0 else 56 # A3 = 1945 if A18 == 0 else 1995
-------------
---- 54 ----
PC = 55 # A3 = 1980
A16 = 0x1
-------------
---- 55 ----
PC = 67 # A3 = 2373
-------------
---- 56 ----
PC = 57 # A3 = 2042
A18 = A16 + 0xff9c
-------------
---- 57 ----
PC = 58 if A18 == 0 else 60 # A3 = 2079 if A18 == 0 else 2130
-------------
---- 58 ----
PC = 59 # A3 = 2115
A16 = 0x10
-------------
---- 59 ----
PC = 67 # A3 = 2373
-------------
---- 60 ----
PC = 61 # A3 = 2177
A18 = A16 + 0xff94
-------------
---- 61 ----
PC = 62 if A18 == 0 else 64 # A3 = 2214 if A18 == 0 else 2268
-------------
---- 62 ----
PC = 63 # A3 = 2253
A16 = 0xffff
-------------
---- 63 ----
PC = 67 # A3 = 2373
-------------
---- 64 ----
PC = 65 # A3 = 2303
A14 = 0x0
-------------
---- 65 ----
PC = 66 # A3 = 2338
A16 = 0x0
-------------
---- 66 ----
PC = 67 # A3 = 2373
A22 = 0x1
-------------
---- 67 ----
PC = 68 # A3 = 2419
A12 = A12 + A16
-------------
---- 68 ----
PC = 69 # A3 = 2457
ptr = 0xffef
-------------
---- 69 ----
PC = 70 # A3 = 2494
*ptr = A12
-------------
---- 70 ----
PC = 71 # A3 = 2532
ptr = 0xfff0
-------------
---- 71 ----
PC = 72 # A3 = 2569
A16 = *ptr
-------------
---- 72 ----
PC = 73 if A16 == 0 else 95 # A3 = 2606 if A16 == 0 else 3429
-------------
---- 73 ----
PC = 74 # A3 = 2652
ptr = 0xf000 + A12
-------------
---- 74 ----
PC = 75 # A3 = 2689
A16 = *ptr
-------------
---- 75 ----
PC = 76 # A3 = 2727
ptr = 0xffef
-------------
---- 76 ----
PC = 77 # A3 = 2764
*ptr = A16
-------------
---- 77 ----
PC = 78 # A3 = 2802
ptr = 0xfff0
-------------
---- 78 ----
PC = 79 # A3 = 2836
*ptr = 0x0
-------------
---- 79 ----
PC = 80 # A3 = 2874
ptr = 0xffef
-------------
---- 80 ----
PC = 81 # A3 = 2911
A16 = *ptr
-------------
---- 81 ----
PC = 82 # A3 = 2957
A16 = A16 + A16
-------------
---- 82 ----
PC = 83 # A3 = 3003
ptr = 0x7000 + A16
-------------
---- 83 ----
PC = 84 # A3 = 3040
A16 = *ptr
-------------
---- 84 ----
PC = 85 if A16 == 0 else 92 # A3 = 3077 if A16 == 0 else 3344
-------------
---- 85 ----
PC = 86 # A3 = 3120
A16 = A10 + 0x1
-------------
---- 86 ----
PC = 87 # A3 = 3166
ptr = 0xf102 + A16
-------------
---- 87 ----
PC = 88 # A3 = 3203
A16 = *ptr
-------------
---- 88 ----
PC = 89 # A3 = 3249
A16 = A16 + A12
-------------
---- 89 ----
PC = 90 if A16 == 0 else 91 # A3 = 3286 if A16 == 0 else 3329
-------------
---- 90 ----
PC = 91 # A3 = 3329
A10 = A10 + 0x1
-------------
---- 91 ----
PC = 43 # A3 = 1548
-------------
---- 92 ----
PC = 93 # A3 = 3379
A14 = 0x0
-------------
---- 93 ----
PC = 94 # A3 = 3414
A22 = 0x2
-------------
---- 94 ----
PC = 43 # A3 = 1548
-------------
---- 95 ----
PC = 96 # A3 = 3464
A22 = 0x4
-------------
---- 96 ----
PC = 65534 # A3 = 65534
-------------
---- 97 ----
PC = 98 if A14 == 0 else 99 # A3 = 3516 if A14 == 0 else 3531
-------------
---- 98 ----
PC = 143 # A3 = 5081
-------------
---- 99 ----
PC = 100 # A3 = 3578
A16 = A10 + 0xfff7
-------------
---- 100 ----
PC = 101 if A16 == 0 else 102 # A3 = 3615 if A16 == 0 else 3630
-------------
---- 101 ----
PC = 104 # A3 = 3680
-------------
---- 102 ----
PC = 103 # A3 = 3665
A22 = 0x3
-------------
---- 103 ----
PC = 143 # A3 = 5081
-------------
---- 104 ----
PC = 105 # A3 = 3714
A8 = 0x0
-------------
---- 105 ----
PC = 106 # A3 = 3749
A10 = 0x0
-------------
---- 106 ----
PC = 107 # A3 = 3795
A12 = A8 + 0xffd9
-------------
---- 107 ----
PC = 108 if A12 == 0 else 109 # A3 = 3832 if A12 == 0 else 3847
-------------
---- 108 ----
PC = 140 # A3 = 4987
-------------
---- 109 ----
PC = 110 # A3 = 3882
A14 = 0x4
-------------
---- 110 ----
PC = 111 # A3 = 3917
A12 = 0x0
-------------
---- 111 ----
PC = 112 # A3 = 3963
A12 = A12 + A12
-------------
---- 112 ----
PC = 113 # A3 = 4009
A12 = A12 + A12
-------------
---- 113 ----
PC = 114 # A3 = 4055
ptr = 0xe000 + A10
-------------
---- 114 ----
PC = 115 # A3 = 4092
A16 = *ptr
-------------
---- 115 ----
PC = 116 # A3 = 4139
A18 = A16 + 0xff8b
-------------
---- 116 ----
PC = 117 if A18 == 0 else 118 # A3 = 4176 if A18 == 0 else 4191
-------------
---- 117 ----
PC = 131 # A3 = 4632
-------------
---- 118 ----
PC = 119 # A3 = 4238
A18 = A16 + 0xff8e
-------------
---- 119 ----
PC = 120 if A18 == 0 else 122 # A3 = 4275 if A18 == 0 else 4333
-------------
---- 120 ----
PC = 121 # A3 = 4318
A12 = A12 + 0x1
-------------
---- 121 ----
PC = 131 # A3 = 4632
-------------
---- 122 ----
PC = 123 # A3 = 4380
A18 = A16 + 0xff9c
-------------
---- 123 ----
PC = 124 if A18 == 0 else 126 # A3 = 4417 if A18 == 0 else 4475
-------------
---- 124 ----
PC = 125 # A3 = 4460
A12 = A12 + 0x2
-------------
---- 125 ----
PC = 131 # A3 = 4632
-------------
---- 126 ----
PC = 127 # A3 = 4522
A18 = A16 + 0xff94
-------------
---- 127 ----
PC = 128 if A18 == 0 else 130 # A3 = 4559 if A18 == 0 else 4617
-------------
---- 128 ----
PC = 129 # A3 = 4602
A12 = A12 + 0x3
-------------
---- 129 ----
PC = 131 # A3 = 4632
-------------
---- 130 ----
PC = 143 # A3 = 5081
-------------
---- 131 ----
PC = 132 # A3 = 4675
A10 = A10 + 0x1
-------------
---- 132 ----
PC = 133 # A3 = 4722
A14 = A14 + 0xffff
-------------
---- 133 ----
PC = 134 if A14 == 0 else 111 # A3 = 4759 if A14 == 0 else 3917
-------------
---- 134 ----
PC = 135 # A3 = 4804
ptr = 0xf10c + A8
-------------
---- 135 ----
PC = 136 # A3 = 4841
A14 = *ptr
-------------
---- 136 ----
PC = 137 # A3 = 4886
ptr = 0xe800 + A8
-------------
---- 137 ----
PC = 138 # A3 = 4931
*ptr = A14 + A12
-------------
---- 138 ----
PC = 139 # A3 = 4972
A8 = A8 + 0x1
-------------
---- 139 ----
PC = 106 # A3 = 3749
-------------
---- 140 ----
PC = 141 # A3 = 5032
ptr = 0xe800 + A8
-------------
---- 141 ----
PC = 142 # A3 = 5066
*ptr = 0x0
-------------
---- 142 ----
PC = 65534 # A3 = 65534
-------------
---- 143 ----
PC = 144 # A3 = 5119
ptr = 0xe800
-------------
---- 144 ----
PC = 145 # A3 = 5153
*ptr = 0x0
-------------
---- 145 ----
PC = 65534 # A3 = 65534
-------------
import re
# load formats
with open("formats.txt") as f:
formats = eval(f.read())
# len-pc mapping
s = 0
mapping = {}
for i in range(len(formats)):
mapping[s] = i
f = formats[i]
s += len(f) + 1
mapping[0xfffe] = 0xfffe
#print(mapping)
def pass1(content):
# A2 is always 0
new_content = content.replace(' + (1 if A2 else 0)', '')
return new_content
def pass2(content):
# translate "set A3" to PC
lines = content.splitlines()
new_lines = []
for line in lines:
res = re.match(f"A3 = (0x\w+)", line)
if res:
l = int(res.group(1), 16)
pc = (mapping[l])
line = "PC = %d" % pc
line = line.ljust(40)
comment = "# A3 = %d" % l
line += comment
new_lines.append(line)
return '\n'.join(new_lines)
def pass3(content):
# replace duplicates
# e.g. A7 is the write and A6 is the read to the same location
for i in range(8, 24, 2):
read_tag = "A%d" % i
write_tag = "A%d" % (i+1)
content = content.replace(write_tag, read_tag)
return content
def pass4(content):
# in fact, max(x, A1) is actually just Ax
lines = content.splitlines()
new_lines = []
for line in lines:
while True:
res = re.search(f"max\((A\d+), A1\)", line)
if not res:
break
line = line.replace(res.group(0), res.group(1))
new_lines.append(line)
return '\n'.join(new_lines)
def pass5(content):
new_content = content.replace("A6", "*ptr")
new_content = new_content.replace("A5", "*ptr")
new_content = new_content.replace("A7", "ptr")
return new_content
def pass6(content):
# handle conditional jump
lines = content.splitlines()
new_lines = []
for line in lines:
res = re.match(f"A3 = COND_JUMP_(A\d+) \+ (0x\w+) \+ 1 \+ A4 \+ (0x\w+)", line)
if res:
guard = res.group(1)
base = int(res.group(3), 16) + 1
switcher = int(res.group(2), 16) + 1
br1 = (base + switcher) & 0xffff
br2 = (base + switcher*2) & 0xffff
assert br1 in mapping and br2 in mapping
line = "PC = %d if %s == 0 else %d" % (mapping[br1], guard, mapping[br2])
line = line.ljust(40)
comment = "# A3 = %d if %s == 0 else %d" % (br1, guard, br2)
line += comment
new_lines.append(line)
return '\n'.join(new_lines)
def pass7(content):
# A1 is just 0
lines = content.splitlines()
new_lines = []
for line in lines:
res = re.search(f"A1$", line)
if res:
line = '0x0'.join(line.rsplit('A1', 1))
new_lines.append(line)
return '\n'.join(new_lines)
with open("assembly") as f:
content = f.read()
content = pass1(content)
content = pass2(content)
content = pass3(content)
content = pass4(content)
content = pass5(content)
content = pass6(content)
content = pass7(content)
print(content)
import time
#CTF{n0w_ev3n_pr1n7f_1s_7ur1ng_c0mpl3te}
s = """0x7000: 0x0001 0x0001 0x0000 0x0000 0x0001 0x0000 0x0001 0x0000
0x7010: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001
0x7020: 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001 0x0001 0x0000
0x7030: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000
0x7040: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001
0x7050: 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001 0x0001 0x0000
0x7060: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001
0x7070: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001
0x7080: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001 0x0001 0x0000
0x7090: 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000
0x70a0: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001
0x70b0: 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001
0x70c0: 0x0001 0x0000 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000
0x70d0: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001
0x70e0: 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001
0x70f0: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000
0x7100: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001
0x7110: 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001
0x7120: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000
0x7130: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001
0x7140: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001 0x0001 0x0000
0x7150: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001
0x7160: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001
0x7170: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000
0x7180: 0x0001 0x0000 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000
0x7190: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001
0x71a0: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001
0x71b0: 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000
0x71c0: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0000 0x0001 0x0001
0x71d0: 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001 0x0001 0x0000
0x71e0: 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001 0x0001 0x0001
0x71f0: 0x0001 0x0001 0x0001 0x0000 0x0001 0x0001 0x0001 0x0001"""
red_mapping = {}
lines = s.splitlines()
addr = 0x7000
for line in lines:
for val in line.split()[1:]:
red_mapping[hex(addr)] = int(val, 16)
addr += 2
byte_array = [0xCC, 0xB0, 0xE7, 0x7B, 0xBC, 0xC0, 0xEE, 0x3A, 0xFC, 0x73, 0x81, 0xD0, 0x7A, 0x69, 0x84, 0xE2, 0x48, 0xE3, 0xD7, 0x59, 0x11, 0x6B, 0xF1, 0xB3, 0x86, 0x0B, 0x89, 0xC5, 0xBF, 0x53, 0x65, 0x65, 0xF0, 0xEF, 0x6A, 0xBF, 0x08, 0x78, 0xC4, 0x2C, 0x99, 0x35, 0x3C, 0x6C, 0xDC, 0xE0, 0xC8, 0x99, 0xC8, 0x3B, 0xEF, 0x29, 0x97, 0x0B, 0xB3, 0x8B, 0xCC, 0x9D, 0xFC, 0x05, 0x1B, 0x67, 0xB5, 0xAD, 0x15, 0xC1, 0x08, 0xD0, 0x45, 0x45, 0x26, 0x43, 0x45, 0x6D, 0xF4, 0xEF, 0xBB, 0x49, 0x06, 0xCA, 0x73, 0x6B, 0xBC, 0xE9, 0x50, 0x97, 0x05, 0xE5, 0x97, 0xD3, 0xB5, 0x47, 0x2B, 0xAD, 0x25, 0x8B, 0xAE, 0xAF, 0x41, 0xE5, 0xD8, 0x14, 0xF4, 0x83, 0xE6, 0xF0, 0xC0, 0x98, 0x0A, 0xAC, 0xA1, 0x95, 0xF5, 0xB5, 0xD3, 0x53, 0xF0, 0x97, 0xEF, 0x9D, 0xD4, 0x3B, 0x3B, 0x0B, 0xE7, 0x17, 0x07, 0x1F, 0x6C, 0xF1, 0x1E, 0x44, 0x92, 0xB2, 0x57, 0x07, 0xB7, 0x36, 0x8F, 0x53, 0xC9, 0xEA, 0x10, 0x90, 0x62, 0xDF, 0x1D, 0x07, 0xB3, 0x71, 0x53, 0x61, 0x1A, 0x2B, 0x78, 0xBF, 0xC1, 0xB5, 0xC6, 0x3B, 0xEA, 0x2B, 0x44, 0x17, 0xA0, 0x84, 0xCA, 0x8F, 0xB7, 0x3B, 0x38, 0x2F, 0xE8, 0x73, 0x84, 0xAD, 0x44, 0xEF, 0xF8, 0xAD, 0x8C, 0x1F, 0xEA, 0x7F, 0xCD, 0xC5, 0xB3, 0x49, 0x05, 0x03, 0x95, 0xA7, 0x44, 0xB5, 0x91, 0x69, 0xF8, 0x95, 0x6C, 0xE5, 0x87, 0x53, 0x4E, 0x47, 0x92, 0xBE, 0x80, 0xD0, 0x80, 0x1D, 0xAD, 0xF1, 0x3D, 0xE3, 0xDF, 0x35, 0x61, 0xF1, 0xE7, 0x0D, 0x71, 0xC5, 0x02, 0x4F, 0x20, 0x5E, 0xA2, 0x8B, 0xC4, 0x61, 0x32, 0x0F, 0xA8, 0xBE, 0x7E, 0x29, 0xD1, 0x6D, 0x2A, 0xD9, 0x55, 0x47, 0x07, 0x83, 0xEA, 0x2B, 0x79, 0x95, 0x4F, 0x3D, 0xA3, 0x11, 0xDD, 0xC1, 0x1D, 0x89]
count_ref = [0x83, 0x01, 0xAF, 0x49, 0xAD, 0xC1, 0x0F, 0x8B, 0xE1]
good_pos = [0x100-x for x in count_ref]
grid = []
def init_grid():
for i in range(0x10):
line = [" "]*0x11
line[-1] = '*'
grid.append(line)
line = ["*"]*0x11
grid.append(line)
def draw_layout():
for x in range(0x10):
for y in range(0x10):
pos = (x << 4) | y
is_bad = red_mapping[hex(0x7000+2*byte_array[pos])]
grid[x][y] = '*' if is_bad else " "
def clear_console():
print(chr(27)+'[2j')
print('\033c')
print('\x1bc')
def display_grid():
clear_console()
for x in range(0x11):
print('\t\t'+''.join(grid[x]))
def draw_char(x, y):
grid[x][y] = 'X'
def erase_char(x, y):
grid[x][y] = ' '
def draw_good():
for i in range(len(good_pos)):
good = good_pos[i]
x = (good >> 4)
y = (good & 0x0f)
assert grid[x][y] == ' '
grid[x][y] = str(i+1)
X = 1
Y = 1
def interpret(ir):
global X, Y
display_grid()
erase_char(X, Y)
for op in ir:
if op == 'u':
X -= 1
assert X > 0
elif op == 'r':
Y += 1
assert Y < 0x10
elif op == 'd':
X += 1
assert X < 0x10
elif op == 'l':
Y -= 1
assert Y > 0
else:
raise ValueError("unknown op %s" % op)
assert grid[X][Y] != '*'
draw_char(X, Y)
display_grid()
erase_char(X, Y)
time.sleep(0.2)
draw_char(X, Y)
# initialize the grid
init_grid()
draw_layout()
draw_char(X, Y)
draw_good()
interpret("ddrrrrrrddrrrrrrrrddllrruullllllllddddllllllddddrrrrrrrruurrddrrddrrlluulluullddlllllllluuuurrrrrruuuuuulllllldduurrrrrrddddddllllllddddrrrrrruuddlllllluuuuuurruuddllddrrrrrruuuurrrrrruurrllddllllllddddllllllddddrrddllrruulluuuurrrrrruullrruurruuuurrrrrr")
display_grid()

Sprint writeup

Solved by kylebot, mahaloz, hippwndon from Shellphish

An overview

In this challenge, Google introduced us to a new type of instruction set, which in turn allowed us to play a video game completely virtualized in the C language's sprintf format strings. Through some reverse engineering, and IDA finagling, we created a disassembler for the sprint arch, which is the name we are declaring for this challenges ISA.

We break this writeup into sections:

  1. Investigation
  2. Building an Instruction Lifter
  3. Making readable assembly: optimizations
  4. Execution & Decompilation
  5. Solving a Maze

Investigating the Program

Let's start by investigating the program in our disassembler of choice. We are lucky enough to have IDA Pro, but this should be doable in Ghidra.

investigate all the binaries

The challenge gives us a very "simple" program that only does four things:

  1. mmap a region and copy some data into it
  2. read user input to the mmaped region
  3. run sprintf in a loop with a weird exit condition
  4. if part of the mmaped region satisfies some constraints, output that region as the flag

By some close examination, we found out that the "some data" in step 1 is actually a huge array of format strings. At this point, we realized that this is a VM based on sprintf. And each format string is an "instruction". To clarify, we believed that based on the normal operations of sprintf and format strings, one could achieve a Turing Complete machine that allowed you to perform all the normal operations in a computer like addition, storing data, and accessing data.

Here is a sample of the aforementioned format strings that we found:

.rodata:0000000000002020                 public data
.rodata:0000000000002020 ; char data[61748]
.rodata:0000000000002020 data            db '%1$00038s%3$hn%1$65498s%1$28672s%9$hn',0
.rodata:0000000000002020                                         ; DATA XREF: main+46↑o
.rodata:0000000000002046 a100074s3Hn1654 db '%1$00074s%3$hn%1$65462s%1$*8$s%7$hn',0
.rodata:000000000000206A a100108s3Hn1654 db '%1$00108s%3$hn%1$65428s%1$1s%6$hn',0
.rodata:000000000000208C a100149s3Hn1653 db '%1$00149s%3$hn%1$65387s%1$*8$s%1$2s%7$hn',0
.rodata:00000000000020B5 a100183s3Hn1653 db '%1$00183s%3$hn%1$65353s%1$1s%6$hn',0
.rodata:00000000000020D7 a100218s3Hn1653 db '%1$00218s%3$hn%1$65318s%1$2s%11$hn',0
.rodata:00000000000020FA a100264s3Hn1652 db '%1$00264s%3$hn%1$65272s%1$*10$s%1$*10$s%17$hn',0
.rodata:0000000000002128 a100310s3Hn1652 db '%1$00310s%3$hn%1$65226s%1$28672s%1$*16$s%7$hn',0

The sprintf loop looks like this:

  while ( format != &ws[1].formats[6137] )      // A3 != 0xfffe
    sprintf(
      buffer,
      format,
      &nullptr,                                 // A1
      0LL,                                      // A2 == 0
      &format,                                  // A3
      buffer,                                   // A4
      (unsigned __int16)*A6,                    // A5
      A6,                                       // A6
      &A6,                                      // A7
      A8,                                       // A8
      &A8,                                      // A9
      A10,                                      // A10
      &A10,                                     // A11
      A12,                                      // A12
      &A12,                                     // A13
      A14,                                      // A14
      &A14,                                     // A15
      A16,                                      // A16
      &A16,                                     // A17
      A18,                                      // A18
      &A18,                                     // A19
      A20,                                      // A20
      &A20,                                     // A21
      A22,                                      // A22
      &A22);                                    // A23

When taking a look at the variables accessed and used by the initial sprint we realized that these variables are probably used as registers for this architecture. The comments on the right of the variables is the standard names we used to reference the variables, and eventually translate into higher level registers -- like the program counter.

Since the remainder of this writeup will be concerning format strings, it is useful to do a quick overview of the format specifiers relevant to this challenge.

A brief review of format strings:

The relevant specifiers for this challenge are %n, $, %h, %c, and *:

  • %c specifies to print a character, or a single byte. This includes null bytes.
  • %h specifies the size of the print of the buffer to 2 bytes. This means everything with this specifier will print 2 bytes (a cunning way to always move 2 bytes).
  • $ is a specifier to get the nth argument of the printf. For instance 3$ gets the third buffer relative to the printf.
  • %n is a specifier that allows you to write to a buffer that is used in the printf. It will write to the buffer the number of charcters printed before the %n. An example can be found here.
  • * is the trickest specifier for this challenge. The * is a dynamic specifier that determines the size of printing based on the next argument with an integer. For instance, if the next argument is 5, it will print something with a size of 5 -- or 5 charcter slots. An example can be found here also.

With this knowledge we can parse the first part of the first format string: %1$00038s%3$hn. The 1$ accesses the first argument; the 00038s prints a string that is of length 38, right formatted. The %3$hn then writes the value 38 formatted to 2 bytes to the third argument to sprintf. In conclusion, this instruction moved the value 0x0026 to register A3.

Instruction Lifting

Although we still had no idea how the VM worked at this point, we decided to write a lifter for the format string instructions and translate it into a human readable assembly. We needed to do some lifting!

lifffffffffft

As noobs in reversing, we decided to do this in two overarching steps: a syntax parser and a semantic parser -- a similar path that a normal compiler takes.

Syntax Parser

The syntax parser simply parses the format strings and gets information like what formatter it uses, which argument it takes, etc. For example, %1$00038s will be parsed as: arg_num=1, con_len=38, formatter='s', len_arg_num=None where con_num is the concrete length. len_arg_num is used for format string like %1$*16$s which takes the 16th argument as the length parameter.

During writing the syntax parser, we realized that the program constantly writes to the third parameter (we named it A3 as shown above). We guessed it was something like a program counter (PC). It turned out we were right.

Semantic Parser

The semantic parser is built upon the syntax parser. It takes the output from the syntax parser and then translates it into an assembly style Intermediate Representation (IR). As explained earlier, %1$00038s%3$hn writes 38 to A3. So, the semantic parser outputs A3 = 38.

During writing the semantic parser. We found a lot of interesting things:

  1. each "instruction" may consist of 1 or 2 "micro-ops". For example, %1$00038s%3$hn%1$65498s%1$28672s%9$hn will be translated into A3 = 0x26, A9 = 0x7000
  2. there is an amazing conditional jump instruction. Basically, it uses that fact that %c can output \x00. If the argument is equal to 0, it outputs \x00 to the internal buffer, the internal buffer contains a shorter string. If the argument is not equal to 0, it outputs a valid character and the internal buffer contains a longer string. And then it writes the length of the internal string to A3. By carefully controlling the lengths of different strings, this mechanism can behave like if-else statement.
  3. each general "register" has two handles: a read handle and a write handle. This has nothing to do with the VM architecture. It's just an easy way to perform read/write in sprintf. As shown above, A11 actually is the write pointer to A10.
  4. loop exists in the VM. What's worse, by reading the IR, we quickly identified the existence of nested loops. oh no!

A sample IR looks like this:

---- 0 ----
A3 = 0x26
A9 = 0x7000
-------------

---- 1 ----
A3 = 0x4a
A7 = max(A8, A1)
-------------

---- 2 ----
A3 = 0x6c
A6 = 0x1
-------------

---- 3 ----
A3 = 0x95
A7 = max(A8, A1) + 0x2
-------------

---- 4 ----
A3 = 0xb7
A6 = 0x1
-------------

---- 5 ----
A3 = 0xda
A11 = 0x2
-------------

---- 6 ----
A3 = 0x108
A17 = max(A10, A1) + max(A10, A1)
-------------

---- 7 ----
A3 = 0x136
A7 = 0x7000 + max(A16, A1)
-------------

---- 8 ----
A3 = 0x15b
A15 = max(A5, A1)
-------------

---- 9 ----
A3 = COND_JUMP_A14 + 0x1a3 + 1 + A4 + 0xffdb
-------------

IR Optimization: Making Readable Assembly

Now that we have a relatively readable IR, what's next? Optimization of course.

This IR is far from optimized. For example, it shows A3 = <num>; however, what does it mean? In fact, it overwrites the last two bytes of the format string pointer so in the next sprintf loop, another format string will be processed. Basically, A3 is the program counter! We realized it would be nice if we could show it like PC = 5 for basic blocks instead of weird A3 = 0xb7.

There are many ways to achieve this. The most obvious way is to modify how we emit this IR. But personally, we hate this method, it just makes thing dirty. we chose to do an LLVM-pass style optimization. It takes the raw IR and perform optimization pass-by-pass.

We implemented several passes. After the optimization, the optimized IR looks like this:

---- 0 ----
PC = 1                                  # A3 = 38
A8 = 0x7000
-------------

---- 1 ----
PC = 2                                  # A3 = 74
ptr = A8
-------------

---- 2 ----
PC = 3                                  # A3 = 108
*ptr = 0x1
-------------

---- 3 ----
PC = 4                                  # A3 = 149
ptr = A8 + 0x2
-------------

---- 4 ----
PC = 5                                  # A3 = 183
*ptr = 0x1
-------------

---- 5 ----
PC = 6                                  # A3 = 218
A10 = 0x2
-------------

---- 6 ----
PC = 7                                  # A3 = 264
A16 = A10 + A10
-------------

---- 7 ----
PC = 8                                  # A3 = 310
ptr = 0x7000 + A16
-------------

---- 8 ----
PC = 9                                  # A3 = 347
A14 = *ptr
-------------

---- 9 ----
PC = 10 if A14 == 0 else 21             # A3 = 384 if A14 == 0 else 804
-------------

Decompilation, Execution, and Luck

At this stage, we had a highly readable IR. Hardcore reversers would be happy enough with it and figure out the logic pretty quickly. However, as reversing noobs, we chose another approach: translate it to x86_64 assembly, open it in IDA and get it's IDA decompilation. The idea is based on the fact that control flow transition is done by setting PC. We can simply add a label for each basic block and translate PC = 1; A8 = 0X7000 to di = 0x7000; jmp label1. The idea is nasty, but it worked pretty well. IDA is smart enough to understand the function even if there are many jmp instructions in it.

The moment we pressed F5 (decompile), our hearts stopped... It worked! The outputted decompilation was pretty readable for the most part:

  v0 = sys_mmap(0LL, 0x10000uLL, 3uLL, 0x32uLL, 0xFFFFFFFFFFFFFFFFLL, 0LL);
  v1 = sys_read(0, (char *)0xE000, 0x100uLL);
  HIWORD(v2) = 0;
  v3 = 28674LL;
  MEMORY[0x7000] = 1;
  MEMORY[0x7002] = 1;
  v4 = 2;
  do
  {
    LOWORD(v3) = 2 * v4 + 28672;
    if ( !*(_WORD *)v3 )
    {
      for ( i = 2 * v4; ; i += v4 )
      {
        LOWORD(v3) = -17;
        *(_WORD *)v3 = i;
        LOWORD(v3) = -16;
        if ( *(_WORD *)v3 )
          break;
        LOWORD(v3) = 2 * i + 28672;
        *(_WORD *)v3 = 1;
      }
    }
    ++v4;
  }
  [truncated]

Most amazingly, with some prologue, epilogue and most importantly some luck, this binary actually runs with mmap_min_addr set to 0(due to the fact that the VM uses address 0).

Finding a Game and Beating It

Video Game!

At this stage, the flag is not far from reach. We had extracted the program on the inside of this sprintf VM and we now had a way to execute, debug, and understand it. Quickly, we noticed that this was a game based on this code:

if ( j != (__int16)0xFF02 )
    goto label143;
  LOWORD(v2) = 0;
  v7 = 0;
  LOWORD(v3) = -3840;
  v8 = *(_WORD *)v3;
  v9 = 1;
  while ( 1 )
  {
    LOWORD(v3) = v2 - 0x2000;
    v10 = *(_WORD *)v3;
    if ( !*(_WORD *)v3 )
      break;
    LOWORD(v2) = v2 + 1;
    switch ( v10 )
    {
      case 'u':
        v11 = -16;
        break;
      case 'r':
        v11 = 1;
        break;
      case 'd':
        v11 = 16;
        break;
      case 'l':
        v11 = -1;
        break;
      default:
        v9 = 0;
        v11 = 0;
        break;
    }
    [truncated]

We took special notice that it was interpreting up, down, left, right and changing our coordinates with each move. We realized the coordinates were in the form of a byte. So 0x12 translated to (2,1) on a normal (x,y) plane where the top right of the gird is (0,0). You can verify this based on the subtraction of 16 when moving up, and the addition of 1 when moving right -- this is to access the high and low components of the byte.

Continuing our examination, we realized that this game was a maze. Each byte of user input is interpreted as a move. We need to play the game and reach 9 checkpoints in a specified sequence within 254 steps. We came to this conclusion based on the final check in the game:

if ( v9 && v7 == 9 )

Which does a check to see if you made it to 9 checkpoints. It then confirms this by making sure you hit each check point in the right order. The map of the maze is generated during runtime. To get it, hardcore reversers may read the nested loop logic and figure it out. We noobs with a binary in hand can attach to a gdb and dump the memory to figure it out. The map dumped to this:

****************
* *     *       
* * ***** ******
*       * * *   
* ***** * * * **
* * *    
*** *** *******
*   *   *   * 
* ***** *** ****
*       * *   *
* * ***** * ***
* * * * *
* *** * * * ****
*         *
*** * ***** * **
*   * *     *   

The positions that we need to visit is actually embedded in the data section of the raw sprint binary at 0x11020. After extracting that, we were left with a maze that had checkpoints and our starting location (marked with an 'X').

****************
*X*     *      9
* * ***** ******
*       * * *  6
* ***** * * * **
*3*5*
*** *** *******
*   *8  *   *1
* ***** *** ****
*       * *   *
* * ***** * ***
* * * *4*
* *** * * * ****
*         *
*** * ***** * **
*7  * *     *  2

Putting everything together, we wrote a python script to play the game. Clever reversers may write a DFS algorithm to solve the maze automatically. However, we are noobs, noobs know nothing about automation. We wrote an interpreter for the game and solved it by hand.

Our winning movements looked something like this:

Winning!

Conclusion

This was a very very fun challenge to do. Now we know even printf is turing complete. The most impressive part to us is the if-else instruction. That was mind-blowing to us when we figured it out. I don't have any complaints about this challenge. The experience was flawless and the maze was quick enough to be solvable. If you too are reversing noob, know that even you can solve a hard challenge if you put in the time :).

Love,

Shellphish

import re
from pwn import *
context.arch = 'amd64'
vr_map ={}
vr_map['A8'] = "di"
vr_map['A10'] = "si"
vr_map['A22'] = "dx"
vr_map['A14'] = "r8w"
vr_map['A16'] = "r9w"
vr_map['ptr'] = "rax"
vr_map['A12'] = "r10w"
vr_map['A18'] = "r11w"
vr_map['tmp'] = "rbx"
def val_to_op(var):
# if it is a number, just return it
if re.match(r'0x[0-f]+', var):
return var
if re.match(r'(ptr)|(A\d+)', var):
return vr_map[var]
if re.match(r'\*ptr', var):
return "word ptr [%s]" % vr_map[var[1:]]
raise
def pass1(content):
# emit x64 assembly code
pc_line = None
output = ""
# process basic blocks
for bb in content.split('-------------'):
lines = bb.splitlines()
output += '\n'
for line in lines:
if not line:
continue
res = re.match(r'^---- (\d+) ----$', line)
if res:
label = "label%s" % res.group(1)
output += label+":\n"
continue
if 'PC' in line:
pc_line = line.split('#')[0].strip()
continue
res = re.match(r'^([\*\w]+) = ([\*\w]+)$', line)
if res:
op1 = val_to_op(res.group(1))
op2 = val_to_op(res.group(2))
output += "mov %s, %s\n" % (op1, op2)
continue
res = re.match(r'^([\*\w]+) = ([\*\w]+) \+ ([\*\w]+)$', line)
if res:
op1 = val_to_op(res.group(1))
op2 = val_to_op(res.group(2))
op3 = val_to_op(res.group(3))
#print(op1, op2, op3)
output += "xor rbx, rbx\nmov bx, %s\nadd bx, %s\nmov %s, bx\n" % (op3, op2, op1)
continue
print(line)
raise
# process control flow
res = re.match(r'^PC = (\d+)$', pc_line)
if res:
output += "jmp label%s\n" % res.group(1)
continue
res = re.match(r'^PC = (\d+) if (A\d+) == 0 else (\d+)$', pc_line)
if res:
guard = res.group(2)
br1 = res.group(1)
br2 = res.group(3)
op = val_to_op(guard)
# fix:
if 'w' in op:
op = op.replace('w', 'b')
else:
op += 'l'
output += "test %s, %s\njz label%s\njmp label%s\n" % (op, op, br1, br2)
#import IPython;IPython.embed()
#print('------')
continue
print([pc_line])
raise
return output
def pass2(content):
# fix the broken assembly
lines = content.splitlines()
new_lines = []
for line in lines:
if 'rax' in line and 'word ptr' not in line:
line = line.replace('rax', 'ax')
new_lines.append(line)
return '\n'.join(new_lines)
def pass3(content):
# add exit label
content += "\n\nlabel65534:\nmov rax, 60\nsyscall\nret\n"
return content
def pass4(content):
# strip uncessary label so hopefully IDA can analyze it
# FIXME: this pass is broken
# gather necessary labels first
good_labels = set()
lines = content.splitlines()
for i in range(len(lines)):
line = lines[i]
if 'jz' in line:
good_labels.add(line.split()[1])
good_labels.add(lines[i+1].split()[1])
good_labels.add('label65534')
print(good_labels)
# strip labels that are unncessary
lines = content.splitlines()
new_lines = []
for line in lines:
if 'label' in line:
res = re.match('^(label\d+):$', line)
if res:
label = res.group(1)
if label not in good_labels:
continue
res = re.match('^jmp (label\d+)$', line)
if res:
label = res.group(1)
if label not in good_labels:
continue
new_lines.append(line)
#return None
return '\n'.join(new_lines)
def pass5(content):
# add init code
content = shellcraft.linux.mmap(0, 0x10000, 3, 50, -1, 0) + shellcraft.linux.read(0, 0xe000, 0x100) + shellcraft.setregs({'rax':1, 'rbx':'0', 'rdi': '0', 'rsi': '0', 'rdx': '0', 'r8': '0', 'r9':'0', 'r10':'0'}) + content
return content
with open("opt_assembly") as f:
content = f.read()
content = pass1(content)
content = pass2(content)
content = pass3(content)
#content = pass4(content)
content = pass5(content)
print(content)
e = ELF.from_assembly(content)
e.save('unpacked')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment