Исходный код бинарного файла:

#include <stdio.h>
#include <string.h>
#include <ctype.h>
 
void toLowerCase(char* str) {
    for (size_t i = 0; str[i]; i++) {
        str[i] = tolower((unsigned char)str[i]);
    }
}
 
void algorithm(char* input, char digit, char* output) {
    size_t length = strlen(input);
    for (size_t i = 0; i < length; i += 2) {
        int hexValue;
        sscanf(&input[i], "%2x", &hexValue);
        hexValue ^= (digit - '0');
        sprintf(&output[i], "%02x", hexValue);
    }
}
 
int main() {
    char input[64];
    char output[64];
    const char digit = '4';
    const char* predefinedString = "630c5b3c9a313fdc9a3a1e7625be6f04bee0d380"; // уже заксоренная строка
 
    printf("Введите строку в шестнадцатеричном формате: ");
    fgets(input, sizeof(input), stdin);
    
    input[strcspn(input, "\n")] = '\0';
 
    toLowerCase(input);
 
    memset(output, 0, sizeof(output));
 
    algorithm(input, digit, output);
 
    if (strcmp(output, predefinedString) == 0) {
        printf("ptech2024{%s}\n", input);
    } else {
        printf("Упс, не то...\n");
    }
 
    return 0;
}

Для ревёрса откроем бинарный файл в программе Ghidra:

В списке функций увидим “main”, посмотрим её содержание:

Заметим, что флага в явном виде здесь нет, но мы видим, что он формируется из ввода пользователя т.к внутренняя часть флага это переменная local_98, а она, в сою очередь, получается через функцию fgets, которая считывает символы до тех пор, пока не встретится символ “/n” - новая строка

Также, заметим функцию “algorithm” в которую передаётся большая строка и какое то число, если навести на него, то можно увидеть его в ASCII

Теперь, посмотрим функцию “algorithm”. Проанализировав её, можно понять, что это XOR, в частности, стоит посмотреть на строку:

local_24 = local_24 ^ (int)param_2 - 0x30U;

Также, можно заметить, что XOR выполняется не над всей строкой разом, а над каждым символом отдельно, это можно понять по циклу:

for (local_20 = 0; local_20 < local_18; local_20 = local_20 + 2) {
    __isoc99_sscanf(param_1 + local_20, &DAT_00102008, &local_24);
    local_24 = local_24 ^ (int)param_2 - 0x30U;
    sprintf((char *)(param_3 + local_20), "%02x", (ulong)local_24);
}

Зная все эти данные, напишем программу, которая восстановит нам исходную строку:

def reverse_algorithm(input_str, digit):
    output = ""
    for i in range(0, len(input_str), 2):
        hex_value = int(input_str[i:i+2], 16)
        hex_value ^= int(digit)
        output += f"{hex_value:02x}"
    return output
 
encoded_str = "630c5b3c9a313fdc9a3a1e7625be6f04bee0d380"
digit = '4'
 
decoded_str = reverse_algorithm(encoded_str, digit)
print(f"Decoded: {decoded_str}")
 

Результат работы программы: Decoded: 67085f389e353bd89e3e1a7221ba6b00bae4d784

Для проверки на валидность, скормим программе эту строку:

Получаем заветный флаг!