3ByteBadVM/ctf-challenge-src/rom.asm

454 lines
12 KiB
NASM

# 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