все заметки

Learning Bear: Strings

2025.03.10 (ред.: 2025.03.12)

Задание Strings (main):

Sometimes just by running strings on binary you can figure out something useful. Too bad that it is not the case

Сразу открываем в дизассемблере (у меня IDA). Стоит сказать, что я рефактнул некоторые моменты для понимания логики.

// есть параметр? ./main FLAG\nif ( count != 2 )\n\tgoto WRONG;\n\t\n// длина 60\t\nif ( strlen(param[1]) != 60 )\n\tgoto WRONG;\n\t\n// начинается с BL\t\nif ( param[1] != 'BL' )\n\tgoto WRONG;\n\t\n// и иметь фигурные скобки\t\nif ( param[1][2] != '{' )\n\tgoto WRONG;\nif ( param[1][59] != '}' )\n\tgoto WRONG;\n// итого: BL{........................................................} \n \nparam_len = strlen(param[1]);\n// дешифруем только то, что в фигурных скобках\ndecrypt(param[1] + 3, param_len - 4, result, 0x3F);\n\n// сравниваем результат\nif ( !strcmp(result, "LB{1_t0old_y0u_5trings_w0ont_h33lp}") )\n{\n\tputs("Correct!");\n\treturn 0;\n}\nelse\n{\n\tWRONG:\n\tputs("Wrong!");\n\treturn 0;\n}

В main нужно предоставить 60 символьный флаг, и тогда пойдет дешифровка:

./main BL{........................................................}

Смотрим decrypt с комментариями:

// цикл на всю длинну флага\nfor ( i = 0; i < length; ++i )\n{\n\tresult = 0;\n\t// берем по 8 символов\n\tfor ( j = 0; j <= 7; ++j )\n\t{\n\t\t// умножение на 32\n\t\t// в ассемблере это shl [rbp+result], 5\n\t\t// что оказалось более понятнее\n\t\t// ибо это смещение на 5 бит влево\n\t\t// v7 = result << 5\n\t\tv7 = 32 * result;\n\t\t// эта функция ниже\n\t\tchar = char_convert((unsigned int)*(char *)(j + 8 * i + a1));\n\t\tif ( char < 0 )\n\t\t\treturn -1;\n\t\t// здесь так же в ассемблере более понятнее\n\t\t// or [rbp+result], rax\n\t\t// добавляем к результату\n\t\t// по сути v7 - это result\n\t\t// result |= char;\n\t\t// оставил оригинал для наглядности\n\t\tresult = char | v7;\n\t}\n\t// перемещаем получившиеся байты к итоговому результату\n\tfor ( k = 0; k <= 4; ++k )\n\t{\n\t\t*(_BYTE *)(5 * i - k + 4 + a3) = result;\n\t\tresult >>= 8;\n\t}\n}

Что такое char_convert?

// ( char > '@' && char <= 'Z' )\nif ( char > 64 && char <= 90 )\n\treturn 96 - a1; // 6-31\n// ( char > '1' && char <= '7' )\nif ( char > 49 && char <= 55 )\n\treturn a1 - 50; // 0-5\nif ( char == 61 )\n\treturn 0;\nreturn -1;

Идет проверка каждого символа, и если подходит по параметрам, то происходит вычисление.

На выходе максимум может быть 31 - это в двоичном представлении: 11111.

Пять бит. Это те самые пять бит, которые присоединяются к результату, сразу после смещения на теже пять бит.

SHL << 5 нагляднее показывает, чем умножение на 32. С другой стороны, это новые знания.

Далее по коду видно, что входные 8 байт (символов) превращаются в 5 байт (символов) на выходе.

Тоесть, в decrypt приходит 56 байт, делим на 8 и умножаем на 5, получаем на выходе 35 байт.

Это как раз длина строки сравнения LB{1_t0old_y0u_5trings_w0ont_h33lp}

Итого:

  1. делим строку сравнения на 7 частей по 5 байт
  2. в каждой части, каждые 5 бит преобразуем в нужный символ
  3. соединяем (конкатенируем)

Для примера первая часть: LB{1_ = 100110001000010011110110011000101011111. Не забываем добавлять слева 0 до размера 40.

  • 01001 = 09 -> 0x87 = W
  • 10001 = 17 -> 0x79 = O
  • 00001 = 01 -> 0x51 = 3
  • 00111 = 07 -> 0x89 = Y
  • 10110 = 22 -> 0x74 = J
  • 01100 = 12 -> 0x84 = T
  • 01010 = 10 -> 0x86 = V
  • 11111 = 31 -> 0x65 = A

Получается WO3YJTVA. И т.д.

Флаг: LB{WO3YJTVARPHZBE56UCDM2CVAZKFY6FURTCGKBCGPSCIYXIEXZTGJHD5C}

P.S. Естественно все это было вскопе с дебаггером. Сейчас в Windows дебажить Linux софт в разы проще. WSL не заменимая вещь в этом. Ставим Ubuntu (или), в нем gdbserver, вешаем его на порт, и погнали...