Rating:

Original writeups: https://lkmidas.github.io/posts/20210719-ggctf2021-writeups/
TL;DR:
1. Analyze the main function -> pretty normal, can notice some unfamiliar printf formats.
2. Analyze the init function -> learn that this program registers a bunch of new different printf formats.
3. Analyze each handler function -> learn that most of them are used to create a register based VM.
4. Write an emulator + disassembler to analyze it -> learn that the first block of VM code tries to decode a larger block of code using the first byte of our input.
5. Because this is a format string VM, the first byte of the block of code must be % -> find out the first correct byte of the input is T.
6. Use the emulator + disassembler to analyze the decrypted code block -> reach the check instruction.
7. Insert z3 variables into the input bytes, change the emulator a bit -> let z3 solve the correct input automatically.
8. Run the original program, pass in the correct input -> get flag

Original writeup (https://lkmidas.github.io/posts/20210719-ggctf2021-writeups/).