# 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