all repos — 3ByteBadVM @ 1c8605952f985718532c998f9b042a08bf011628

3ByteBadVM

src/rom.asm

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 215
 216
 217
 218
 219
 220
 221
 222
 223
 224
 225
 226
 227
 228
 229
 230
 231
 232
 233
 234
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 253
 254
 255
 256
 257
 258
 259
 260
 261
 262
 263
 264
 265
 266
 267
 268
 269
 270
 271
 272
 273
 274
 275
 276
 277
 278
 279
 280
 281
 282
 283
 284
 285
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
# rom.asm

# Flag for this challenge: 
# B4bys_1st_VMPr0tect

# Note: as you go deeper in the file, it just gets worse and worse. You can
# see my mental endurance draining to the point of not wanting to think despite
# being forced to because I'm writing really tedious, hardcoded assembly.
# MAJOR lesson learned: if I ever have to write another assembler, I WILL
# add support for labels, as hardcoding EVERY address for every jump/reference
# ended up being even worse than I originally thought.

# This was the result of 50-60+ hours of hard work (mostly Wednesday-Saturday),
# two all-nighters, and a complete and total rejection of my schoolwork.
# On the schoolwork: let the record show that I learned FAR more writing this
# in a week than I have so far learned in all of my "technical" degree-related
# classes this semester, which I unfortunately suspect will not have changed by
# the time I graduate.

# program start: 0x0100
jmp   0x8d01    # jump to main-like function

#####################################################################
# This subroutine implements the world's sketchiest string reversal
# (using my favorite: self-modifying code!)

# 0x0103:
push  0x8000    # do push instruction (to be modified)
push  0x0401    # push 1st operand of previous instruction to stack
pushi 0x0100    # push 0x01 to stack
add             # add items to stack
dup             # duplicate sum
pop   0x0401    # pop one of the sums to overwrite 1st operand of first 'push'
pushi 0x9400    # push 0x94 to top of stack (Note: because of self-modifying
                # code, this 94 actually also determines the size of the buffer
                # to be copied)
beq   0x2401    # jump ahead to 'pushed' if stack vals are equal
pop   0xffff    # dispose at high memory
pop   0xffff    # dispose at high memory
jmp   0x0301    # jump back


# pushed: (0x0124)
pop   0xffff    # dispose at high memory
pop   0xffff    # dispose at high memory


# current position: 0x012a
pop   0xa010    # do pop instruction (to be modified)
push  0x2b01    # push 1st operand of previous instruction to stack
pushi 0x0100    # push 0x01 to stack
add             # add items to stack
dup             # duplicate sum
pop   0x2b01    # pop one of the sums to overwrite 1st operand of first 'push'
pushi 0xb400    # push 0xb4 to top of stack (Note: because of self-modifying
                # code, this b4 actually also determines the size of the buffer
                # to be copied)
# 0x013f
beq   0x4b01    # jump ahead to 'pushed' if stack vals are equal
pop   0xffff    # dispose at high memory
pop   0xffff    # dispose at high memory
# 0x0148
jmp   0x2a01    # jump back

# popped: (0x014b)
pop   0xffff    # dispose at high memory
pop   0xffff    # dispose at high memory
ret
#####################################################################


#####################################################################
# This subroutine simply subtracts 27 from each of the bytes in the
# previously reversed string

# 0x0154
push  0xa010
pushi 0x1b00    # 0x1b = 27dec
sub
pop   0xa010    # pop subtracted value back into zero page
push  0x5501    # push 1st operand of instruction at 0x154 to stack
pushi 0x0100
add
dup
dup
pop   0x5501    # pop incremented 1st operand back to code
pop   0x5e01    # pop incremented 1st operand back to code
pushi 0xb400    # push 'pointer' (0xb4) to byte after last byte in string
beq   0x8401    # 
pop   0xffff
pop   0xffff
jmp   0x5401
pop   0xffff
pop   0xffff
ret

# By this point, the reversed, subtracted flag looks like the following:
# e5 59 48 4a 59 15 57 35 32 3b 44 59 58 16 44 58 5e 47 19 27
# ... and it is located at address 0x10a0

#####################################################################


#####################################################################
#####################################################################
# main-ish: this pretty calls everything else
0x41 0x41 0x41
0x42 0x42 0x42
0x43 0x43 0x43
0x44 0x44 0x44
0x45 0x45 0x45
0x46 0x46 0x46
0x47 0x47 0x47
0x48 0x48 0x48
0x49 0x49 0x49
# 0x01a8
nop
# 0x01ab
call 0x0301
# 0x01ae
call 0x5401
# 0x01b1
call 0xba01
0x61 0x62 0x63      # TODO: replace with call to flag_cmp
halt
# end main-ish
#####################################################################
#####################################################################


#####################################################################
# block_setup:
#
# Note: a "block" is a 4-byte section of the overall 20-byte flag (so
# there are 5 blocks in total)
# blocks get placed at 0x08d0, 0x09d0, 0x0ad0, 0x0bd0

# 0x01ba
stlr  0x1000            # store link-register at address 0x10 (16)
# 0x01bd
pushi 0x0800
pushi 0xd000            # stack has 0x08d0 (->buffer) in little-endian (idk)
# 0x01c3
pop   0x3c02            # write next block address to scramble_block scratchpad
pop   0x3b02            # write next block address to scramble_block scratchpad
push  0xa010            # push block[0] (character)
push  0xa110            # push block[1] (character)
push  0xa210            # push block[2] (character)
push  0xa310            # push block[3] (character)
# 0x01d5
call  0x3802   # scramble_block:  Note: <- need discard 4 pushed vals off stack

# 0x01d8
push  0xca01    # modify 1st push instruction
pushi 0x0400
add
pop   0xca01

# 0x01e4
push  0xcd01    # modify 2nd push instruction
pushi 0x0400
add
pop   0xcd01

# 0x01f0
push  0xd001    # modify 3rd push instruction
pushi 0x0400
add
pop   0xd001

# 0x01fc
push  0xd301    # modify 4th push instruction
pushi 0x0400
add
pop   0xd301

# 0x0208
push  0xca01        # get 1st operand of 1st push instruction
pushi 0xb400        # push value to compare it to (1st operand + 20)
beq   0x2602        # if value matches limit, branch, else
pop   0xffff        # discard stack byte
pop   0xffff        # discard stack byte

# 0x0217
push  0xbe01        # get old pointer to flag buffer block
pushi 0x0100        # add new offset
add
pop   0xbe01        # update pointer to flag buffer block (self-modifying code)
jmp   0xba01        # loop back to scramble next block

# 0x0226
pop   0xffff        # discard stack byte
pop   0xffff        # discard stack byte

# 0x022c
0x69 0x69 0x69
0x69 0x69 0x69
ldlr 0x1000         # restore link-register from address 0x10
                    # (literally fails, and I'm too lazy to debug it rn)
jmp 0xc802
#####################################################################



#####################################################################
# scramble_block: takes a 4-byte block on the stack, write it to scratchpad,
# scrambles it somewhat, write scratchpad to place in memory, erase scratchpad
# return
#

# 0x0238
jmp 0x4402          # skip ahead (past function scratchpad)

# 0x023b             # scratchpad:
42 42 42            # first two bytes set to pointer to block location in mem
# 0x023e
43 43 43            # first 3 bytes of block
# 0x0241
44 44 44            # 4th byte of block is first here

# 0x0244 (I think)
pushi 0x4500        # push 69dec to stack (Nice)
xor
# 0x024a
pop   0x3e02        # written to by block_setup, points to block location in mem
0x77 0x77 0x77      # junk

# 0x0250
pop   0x4002        # write "2nd" byte to 3rd block position
pop   0x3f02        # write "3rd" byte to 2nd block position
push  0x3e02        # read 69-XORed value to stack
add
pop   0x4102        # add 69-XORed value to 4th value, write to block

# 0x025f
jmp   0x7102        # GOTO set_lsb

# 0x0262: write_block_to_mem
pop   0x0000        # write 1st byte of block
pop   0x0100        # write 2nd byte of block
pop   0x0200        # write 3rd byte of block
pop   0x0300        # write 4th byte of block
# 0x026e
jmp  0xc502         # GOTO END (ret)

# 0x0271: set_lsb:
0x2f 0x2f 0x2f      # junk instruction
# 0x0274
push  0x3b02        # push MSB of pointer to block location in memory
seti  0x6402        # write MSB to code block that will eventually write to it
seti  0x6702        # write MSB to code block that will eventually write to it
seti  0x6a02        # write MSB to code block that will eventually write to it
seti  0x6d02        # write MSB to code block that will eventually write to it

# 0x0283
pop  0xffff

# 0x0286
push 0x6302
push 0x3c02
add
pop 0x6302      # add offset to block pointer LSB to make new LSB
# 0x0292
push 0x6602
push 0x3c02
add
pop 0x6602      # add offset to block pointer LSB to make new LSB
# 0x029e
push 0x6902
push 0x3c02
add
pop 0x6902      # add offset to block pointer LSB to make new LSB
# 0x02aa
push 0x6c02
push 0x3c02
add
pop 0x6c02      # add offset to block pointer LSB to make new LSB

# 0x02b6
push 0x4102     # push 4th block value to stack
push 0x4002     # push 3rd block value to stack
push 0x3f02     # push 2nd block value to stack
push 0x3e02     # push 1st block value to stack

# 0x02c2
jmp 0x6202      # GOTO write_block_to_mem

# 0x02c5: END
jmp 0xd801


                # Oh, the joy of working 80+ hours on writing a difficult CTF
                # challenge and staring at bad assembly and tons of hex for
                # a third of that time. At this point, it is just "getting it
                # to werk!"

# 0x02c8
jmp     0xda02
# 0x02cb
pushi   0x0700    # IMPORTANT!!!
seti    0x3000    # set address 0x30 to the number 7 to indicate correct flag.
# 0x02d1
0x30 0x30 0x30  # junk NOP
0xee 0x07 0x30  # junk NOP
halt

push  0xa009    # check second block
pushi 0x7000
push  0xa109
pushi 0x1500
0x30 0x30 0x30  # junk NOP
push  0xa209
pushi 0x5700
push  0xa309
pushi 0xc900
0x30 0x30 0x30  # junk NOP

# 
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
0x30 0x30 0x30  # junk NOP
0xee 0x07 0x30  # junk NOP
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff

0xee 0x07 0x30  # junk NOP
push  0x700a    # check third block
pushi 0x1c00
push  0x710a
pushi 0x3b00
push  0x720a
pushi 0x4400
push  0x730a
pushi 0x4e00

# 
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
0x30 0x30 0x30  # junk NOP
0xee 0x07 0x30  # junk NOP
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff

push  0xd008    # check first block
pushi 0x0f00
0xee 0x07 0x30  # junk NOP
push  0xd108
0x30 0x30 0x30  # junk NOP
pushi 0x5900
push  0xd208
pushi 0x4800
push  0xd308
pushi 0xf400

bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
0xee 0x07 0x30  # junk NOP
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
0x30 0x30 0x30  # junk NOP
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
0x30 0x30 0x30  # junk NOP
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff



push  0x100c    # check fifth block
pushi 0x6200
push  0x110c
pushi 0x4700
0xee 0x07 0x30  # junk NOP
push  0x120c
pushi 0x1900
0x30 0x30 0x30  # junk NOP
push  0x130c
pushi 0xc000

# 
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
0x30 0x30 0x30  # junk NOP
0x30 0x30 0x30  # junk NOP
0x30 0x30 0x30  # junk NOP
pop   0xffff
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
0xee 0x07 0x30  # junk NOP
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff

push  0x400b    # check fourth block
pushi 0x1d00
push  0x410b
pushi 0x1600
emc   0x0122
0x30 0x30 0x30  # junk NOP
push  0x420b
pushi 0x4400
push  0x430b
pushi 0x7500

# 
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
0xee 0x07 0x30  # junk NOP
emc   0x3344
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
0xee 0x07 0x30  # junk NOP
pop   0xffff
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
pop   0xffff
0xee 0x07 0x30  # junk NOP
bnq   0xd102    # Fail if any two bytes ever not equal
pop   0xffff
0x30 0x30 0x30  # junk NOP
pop   0xffff

jmp   0xcb02
push  0xcb02
0x40 0x77 0x12  # junk NOP
halt
0x30 0x30 0x30  # junk NOP