.include "instr.inc" .text mem_e000: .2byte 0 mem_e002: .fill 0x0efe mem_ef00: .fill 0xb0 mem_efb0: .fill 0x30 mem_efe0: .fill 0x20 .global _start _start: ld_sp stack # Set up the entrypoint for the breakpoint instruction ld_r0 entry ld_r1 0x98 # 'call' strb_r1_r0 inc_r0 ld_r1 main str_r1_r0 # Explain the debugger ld_r2 str_intro call PrintStr auth_user: # Gather the username ld_r2 str_prompt_user call PrintStr ld_r3 0x10 ld_r2 mem_input_user call ReadStr ld_r2 database auth_user_loop: # Check if we've reached the end of the database ldrb_r0_r2 cp_r0 0 jp_eq error_incorrect_user # Check if the username matches the current entry ld_r3 mem_input_user push_r2 call StrCmp pop_r2 cp_r0 0 jp_eq auth_pass # Next entry add_r2 0x50 jp auth_user_loop auth_pass: # Grab the password field add_r2 0x10 push_r2 call delay_input # Gather the password ld_r2 str_prompt_pass call PrintStr ld_r3 0x30 ld_r2 mem_input_pass call ReadStr call delay_input # Load the ENCTABLE.BIN file ld_r0 1 ld_r1 0xc000 copr_readblk # r2 = encoded pass # r3 = input pass pop_r2 ld_r3 mem_input_pass auth_pass_loop: push_r3 # b = *(0xc000 + *input) ld_r1 0xc000 ldrb_r0_r3 add_r1_r0 ld_r3 0 ldrb_r0_r1 add_r3_r0 # b += *(0xc100 + *input) add_r1 0x100 ldrb_r0_r1 add_r3_r0 # b += *(0xc200 + *input) add_r1 0x100 ldrb_r0_r1 add_r3_r0 # b += *(0xc300 + *input) add_r1 0x100 ldrb_r0_r1 add_r3_r0 # b &= 0xff ld_r0_r3 and_r0 0xff ldrb_r1_r2 cp_r0_r1 jp_ne error_incorrect_pass pop_r3 call rotate_enctable inc_r2 inc_r3 ldrb_r0_r2 cp_r0 0 jp_eq auth_success jp auth_pass_loop auth_success: ld_r2 str_login_success call PrintStr # Execute the breakpoint, jump to main brk # If breakpoint returns here, we've ran out of stack ld_r2 str_dry_stack call PrintStr copr_halt die: jp die error_incorrect_user: call delay_input ld_r2 str_incorrect_user call PrintStr jp auth_user error_incorrect_pass: ld_r2 str_incorrect_pass call PrintStr jp auth_user rotate_enctable: ldrb_r0 0xc000 strb_r0 rotate_enctable_temp + 0 ldrb_r0 0xc100 strb_r0 rotate_enctable_temp + 1 ldrb_r0 0xc200 strb_r0 rotate_enctable_temp + 2 ldrb_r0 0xc300 strb_r0 rotate_enctable_temp + 3 ld_r1 0xc000 rotate_enctable_loop: inc_r1 ldrb_r0_r1 dec_r1 strb_r0_r1 inc_r1 cp_r1 0xc3ff jp_ne rotate_enctable_loop ldrb_r0 rotate_enctable_temp + 0 strb_r0 0xc0ff ldrb_r0 rotate_enctable_temp + 1 strb_r0 0xc1ff ldrb_r0 rotate_enctable_temp + 2 strb_r0 0xc2ff ldrb_r0 rotate_enctable_temp + 3 strb_r0 0xc3ff ret rotate_enctable_temp: .fill 4 delay_input: push_r2 ld_r2 str_please_wait call PrintStr pop_r2 ld_r0 0x3fff delay_input_loop: nop nop nop nop nop nop nop dec_r0 cp_r0 0 jp_ne delay_input_loop ret database: .fill 0x00 - (. - database) .ascii "ax.arwen\0" .fill 0x10 - (. - database) .byte 0xc6, 0x44, 0x99, 0xe3, 0xe9, 0x19, 0x0d, 0x07, 0x0d, 0x12, 0x79 .fill 0x50 - (. - database) .ascii "sbw.shadow\0" .fill 0x60 - (. - database) .byte 0xe9, 0x22, 0xd8, 0x7c, 0x3c, 0x07, 0x54, 0x2d .byte 0x5e, 0x53, 0x6a, 0xff, 0x80, 0x5e, 0xcd, 0xc8 .byte 0xcf, 0xff, 0x44, 0x74, 0xc8, 0xd8, 0x4b .fill 0xbd - (. - database) mem_input_user: .ascii "________________\0" mem_input_pass: .ascii "________________________________________________________________\0" str_prompt_user: .ascii "Username: \0" str_prompt_pass: .ascii ">> Username OK. Password required\n" .ascii "Password: \0" str_please_wait: .ascii ">> Please wait...\n\0" str_incorrect_user: .ascii ">> User not found in database.\n\0" str_incorrect_pass: .ascii ">> Password is incorrect.\n\0" str_login_success: .ascii ">> Login successful.\n" .ascii "======================================================================\n" .ascii "Running machine language monitor now.\n" .ascii "======================================================================\n" .ascii "\n\0" main: # Save all the registers push_sp str_r0 mem_save_r0 str_r1 mem_save_r1 str_r2 mem_save_r2 str_r3 mem_save_r3 # Store the stack pointer pop_r0 str_r0 mem_info_sp # Read the return address ld_r0_sp ldr_r0_r0 add_r0 -1 str_r0 mem_info_pc # Read some more stack values ld_r1 (6 * 2) ld_r2 mem_info_stack ldr_r3 mem_info_sp call MemCpy # Print the processor state ld_r2 str_breakpoint call PrintStr main_cmd_loop: ld_r0 0 strb_r0 mem_e002 ld_r2 str_ready call PrintStr # Read input ld_r2 mem_e000 ld_r3 8 call ReadStr cp_r3 0 jp_eq error_too_long ld_r2 mem_e000 call StrTrim # Make sure the input is at most 2 characters ldrb_r0 mem_e002 cp_r0 0 jp_ne error_bad_command # Parse all the commands ldr_r0 mem_e000 cp_r0 ('h' + '\n' * 0x100) jp_eq cmd_help cp_r0 'h' jp_eq cmd_help cp_r0 'r' jp_eq cmd_read cp_r0 ('r' + '\n' * 0x100) jp_eq cmd_read cp_r0 'p' jp_eq cmd_print cp_r0 ('p' + '\n' * 0x100) jp_eq cmd_print cp_r0 'x' jp_eq cmd_exec cp_r0 ('x' + '\n' * 0x100) jp_eq cmd_exec cp_r0 'c' jp_eq cmd_cont cp_r0 ('c' + '\n' * 0x100) jp_eq cmd_cont cp_r0 ('U' + 'C' * 0x100) jp_eq cmd_undoc cp_r0 ('l' + 's' * 0x100) jp_eq cmd_list cp_r0 ('r' + 'f' * 0x100) jp_eq cmd_readfile cp_r0 'w' jp_eq cmd_write cp_r0 ('w' + '\n' * 0x100) jp_eq cmd_write error_bad_command: ld_r2 str_error_bad_command call PrintStr jp main_cmd_loop error_too_long: ld_r2 str_error_too_long call PrintStr jp main_cmd_loop cmd_help: ld_r2 str_help call PrintStr jp main_cmd_loop cmd_read: # Get the address to read ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex cp_r0 -1 jp_eq cmd_read_invalid push_r0 # Get the amount of lines to read ld_r2 str_prompt_lines call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex cp_r0 -1 jp_eq cmd_read_invalid cp_r0 0 jp_eq cmd_read_invalid # r2 = address, r3 = lines ld_r3_r0 pop_r2 cmd_read_loop: # Read 8 bytes push_r2 push_r3 ld_r1 8 ld_r3_r2 ld_r2 mem_data call MemCpy pop_r3 pop_r2 # Store the current read address str_r2 mem_addr # Write it out push_r2 push_r3 ld_r2 str_hexdump call PrintStr pop_r3 pop_r2 # Check if we're done add_r2 8 dec_r3 cp_r3 0 jp_ne cmd_read_loop jp main_cmd_loop cmd_read_invalid: ld_r2 str_error_invalid_input call PrintStr jp main_cmd_loop cmd_write: # Get the address to write ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex cp_r0 -1 jp_eq cmd_write_invalid str_r0 mem_addr ld_r2 str_prompt_hex call PrintStr cmd_write_loop: # Clear the buffer ld_r3 mem_e000 + 2 ld_r0 0 strb_r0_r3 dec_r3 strb_r0_r3 dec_r3 cmd_write_loop_first: # Get the first nybble copr_getc strb_r0_r3 ld_r2_r3 # Check if it's the end or invalid cp_r0 '.' jp_eq cmd_write_end cp_r0 '\n' jp_eq cmd_write_loop_first # Try to convert the nybble push_r3 call ConvertHex pop_r3 cp_r0 -1 jp_eq cmd_write_loop_first inc_r3 cmd_write_loop_second: # Get the second nybble copr_getc strb_r0_r3 ld_r2_r3 # Check if it's the end or invalid cp_r0 '.' jp_eq cmd_write_end cp_r0 '\n' jp_eq cmd_write_loop_second # Try to convert the nybble push_r3 call ConvertHex pop_r3 cp_r0 -1 jp_eq cmd_write_loop_second # Convert and store the full byte dec_r3 ld_r2_r3 call ConvertHex ldr_r1 mem_addr strb_r0_r1 inc_r1 str_r1 mem_addr jp cmd_write_loop # 0xf1d0 cmd_write_end: # Read until newline copr_getc cp_r0 '\n' jp_ne cmd_write_end ld_r2 str_loaded call PrintStr jp main_cmd_loop cmd_write_invalid: ld_r2 str_error_invalid_input call PrintStr jp main_cmd_loop cmd_print: # Get the string to print ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex ld_r2_r0 call PrintStr jp main_cmd_loop cmd_exec: # Get the address to execute ld_r2 str_prompt_address call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ld_r2 mem_e000 call ConvertHex # Prompt the user to execute the address push_r0 str_r0 mem_addr ld_r2 str_prompt_exec call PrintStr ld_r2 mem_e000 ld_r3 8 call ReadStr ldrb_r0 mem_e000 cp_r0 'Y' jp_eq cmd_exec_go cp_r0 'y' jp_eq cmd_exec_go # Cancel the call ld_r2 str_error_cancelled call PrintStr pop_r0 jp main_cmd_loop cmd_exec_go: # Call the function pop_r3 call_r3 jp main_cmd_loop cmd_cont: ld_r2 str_continuing call PrintStr # Restore register state and unpause ldr_r0 mem_save_r0 ldr_r1 mem_save_r1 ldr_r2 mem_save_r2 ldr_r3 mem_save_r3 ret cmd_list: ld_r2 str_file_list_header call PrintStr # Read directory ld_r0 0 ld_r1 mem_e000 copr_readblk ld_r3 mem_e000 cmd_list_loop: ldrb_r0_r3 cp_r0 0 jp_eq cmd_list_next # Store block push_r3 strb_r0 mem_data inc_r3 ldr_r0_r3 # Store size push_r3 str_r0 mem_addr # Print block and size ld_r2 str_file_list_entry call PrintStr pop_r3 # Print filename inc_r3 inc_r3 ld_r2_r3 call PrintStr ld_r0 '\n' copr_putc pop_r3 cmd_list_next: add_r3 0x10 cp_r3 mem_ef00 jp_ne cmd_list_loop jp main_cmd_loop cmd_undoc: ld_r2 str_undoc call PrintStr jp main_cmd_loop cmd_readfile: ld_r2 str_prompt_filename call PrintStr # Read directory ld_r0 0 ld_r1 mem_e000 copr_readblk # Get filename to read ld_r2 mem_efb0 ld_r3 0x10 call ReadStr cp_r3 0 jp_eq cmd_readfile_error_too_long ld_r2 mem_efb0 call StrTrim ld_r3 mem_e000 + 3 cmd_readfile_loop: # Check if the filename matches ld_r2 mem_efb0 push_r3 call StrCmp pop_r3 cp_r0 0 jp_eq cmd_readfile_match add_r3 0x10 cp_r3 mem_ef00 + 3 jp_eq cmd_readfile_error_not_found jp cmd_readfile_loop cmd_readfile_match: # Read file block dec_r3 dec_r3 dec_r3 ldrb_r0_r3 # Read file size inc_r3 ldr_r1_r3 # Read file into memory push_r1 ld_r1 mem_e000 copr_readblk # Prompt for address to write to ld_r2 str_prompt_address call PrintStr ld_r2 mem_efe0 ld_r3 0x10 call ReadStr ld_r2 mem_efe0 call ConvertHex pop_r1 cp_r0 -1 jp_eq cmd_readfile_error_invalid_input # Copy the data ld_r2_r0 ld_r3 mem_e000 call MemCpy ld_r2 str_loaded call PrintStr jp main_cmd_loop cmd_readfile_error_not_found: ld_r2 str_error_not_found call PrintStr jp main_cmd_loop cmd_readfile_error_too_long: ld_r2 str_error_too_long call PrintStr jp main_cmd_loop cmd_readfile_error_invalid_input: ld_r2 str_error_invalid_input call PrintStr jp main_cmd_loop str_intro: .ascii "======================================================================\r\n" .ascii "Welcome to Glitch Research Laboratory Network: Test Server 2 (GRLTS02)\r\n" .ascii "======================================================================\r\n" .ascii "This machine requires authentication.\r\n\0" str_error_too_long: .ascii "! Input too long error.\n\0" str_error_bad_command: .ascii "! Bad command error (h for help).\n\0" str_error_invalid_input: .ascii "! Invalid input error.\n\0" str_prompt_address: .ascii "> Which address? \0" str_prompt_lines: .ascii "> How many lines? \0" str_prompt_exec: .ascii "> Really exec at " text_ram_word mem_addr .ascii "? Type Y if so: \0" str_error_cancelled: .ascii "! Cancelled action error.\n\0" str_continuing: .ascii "Continuing.\n\0" str_loaded: .ascii "Loaded.\n\0" str_dry_stack: .ascii "! Dry stack. Halting machine.\n\0" str_prompt_filename: .ascii "> Filename? \0" str_error_not_found: .ascii "! File not found error.\n\0" str_prompt_hex: .ascii "> Enter hex data. End with dot \".\" + newline:\n\0" str_undoc: .ascii "Wow, undocumented monitor command! FOOLS2023_{Secret" text_ram_word 0xe000 .ascii "x" text_ram_word 0xe001 .ascii "Command}\n\0" str_help: .ascii "Available commands:\n" .ascii "r :: print memory as hex\n" .ascii "p :: print memory as text\n" .ascii "w :: write hex data to memory\n" .ascii "x :: execute memory\n" .ascii "rf :: load memory from file\n" .ascii "ls :: print file index\n" .ascii "h :: print this help message\n" .ascii "c :: exit monitor and continue\n" .ascii "Please enter hex numbers when prompted.\n" .ascii "If necessary for debugging, you can break into monitor\n" .ascii "with instruction BRK (0x00) and continue with 'c'\n" .ascii "Note: memory region E000-FFFF is used by monitor\n\0" str_file_list_header: .ascii "BLK SIZE NAME\n" .ascii "=======================\n\0" str_file_list_entry: text_ram_byte mem_data .ascii " " text_ram_word mem_addr .ascii " \0" # 0xfa97 str_ready: .ascii "Ready.\n" .ascii "> \0" str_breakpoint: .ascii "*** BREAK INTO MONITOR AT $" text_ram_word mem_info_pc .ascii " ***\n" .ascii "R0=$" text_ram_word mem_save_r0 .ascii " R1=$" text_ram_word mem_save_r1 .ascii " R2=$" text_ram_word mem_save_r2 .ascii " R3=$" text_ram_word mem_save_r3 .ascii "\n" .ascii "SP=$" text_ram_word mem_info_sp .ascii " [" text_ram_byte mem_info_stack + 0 text_ram_byte mem_info_stack + 1 text_ram_byte mem_info_stack + 2 text_ram_byte mem_info_stack + 3 text_ram_byte mem_info_stack + 4 text_ram_byte mem_info_stack + 5 text_ram_byte mem_info_stack + 6 text_ram_byte mem_info_stack + 7 text_ram_byte mem_info_stack + 8 text_ram_byte mem_info_stack + 9 text_ram_byte mem_info_stack + 10 text_ram_byte mem_info_stack + 11 .ascii "]\n\0" str_hexdump: text_ram_word mem_addr .ascii " | " text_ram_byte mem_data + 0 .ascii " " text_ram_byte mem_data + 1 .ascii " " text_ram_byte mem_data + 2 .ascii " " text_ram_byte mem_data + 3 .ascii " " text_ram_byte mem_data + 4 .ascii " " text_ram_byte mem_data + 5 .ascii " " text_ram_byte mem_data + 6 .ascii " " text_ram_byte mem_data + 7 .ascii "\n\0" mem_save_r0: .2byte 0 mem_save_r1: .2byte 0 mem_save_r2: .2byte 0 mem_save_r3: .2byte 0 mem_info_sp: .2byte 0 mem_info_pc: .2byte 0 mem_info_stack: .fill 12 mem_data: .fill 8 mem_addr: .2byte 0 .fill 0xf00 - (. - _start) stack: .fill 0xf0 entry: .fill 0x10