Byte Babies
SwampCTF 2025: You Shall Not Passsss
Mar 31, 2025 - yoshixi - writeup, revauthor: @yoshixi
Challenge Info:
Category: Rev
Challenge Resources
Solution
We first decompile with binary ninja
In the main function, We allocate 3 pointers and 1 variable, change a variable until its non-zero, do a lot more assignments to variables from constants loaded within memory, and perform a hashing function for each of them (there are like 20 of thes variables). Another loop of a pointer, and then we run the 2nd function
The second function is a lot more interesting. It constructs a list, with the first element as a function. Then, it calls that function. 
Debugging
So, the function data is dynamicaly loaded. We debug to see whats going on.
- Enter libc_start_main
- Continue until we enter main which is here:

- Step into that call, then keep moving until we call rax. Our entry is at
0x5555555550c0. This is what it looks like:
- It iterates through a loop, and it runs in it for 20 times.
- Afterwards, it gradually pushes the word
Incorrect\nonto the memory locations
- Then, it writes
Correct!\nto the memory space afterwards
- Afterwards, save some data to the heap

- Enter a loop that repeats 181 times,
- Write the data, then jump into the next call.

- Then, it tries to mmap, and it will jump out if it fails.
- It will then move some string contents into registers

- Moves some more things around
- Moves in some constants, then calls rbp

- Inside rbp: string constants and data is being loaded

- Enters a loop that runs 38 times

- It should have a conditional move that occurs when dil is equal to r8b
This is the key. - Initially, before the start of the loop, rbx is
Correct!\n
- After the loop, if esi is 0, then the value at r14 (which is
Incorrect) is moved into rbx, then it prints rbx.


- If this comparison is not equal, then we will have to cmov
- so, this comparison must be equal all 36 times
- Setting the first character to
spasses the first check, so we know that it is decoding now character by character. Also, changing characters does not modify ther8bvalues, so these are constant. - Now, we know that it loads the character into
dil, xors it withaland compares it to ber8b. So, we should be able to find the character from the expectedr8bvalues andalvalues. - I have a script to help me calculate the next character, since I did not want to reverse engineer the encryption process
def find_a(b_hex, c_hex):
b_int = int(b_hex, 16) # Convert b from hex to integer
c_int = int(c_hex, 16) # Convert c from hex to integer
a_int = c_int ^ b_int # Perform the XOR operation
a_hex = hex(a_int) # Convert a back to hexadecimal
return a_hex
b_decimal = int(input('b_decimal: '))
b_hex = hex(b_decimal & 0xFF) #ensure 8 bit representation.
c_hex = input("c_hex: ")
result_hex = int(find_a(b_hex, c_hex), 16)
print(f"a = {result_hex}, chr(a) = {chr(result_hex)}")
Kept on running the script until i got the flag.