Хочешь решать самые сложные задания на Codewars, но лень или тупой? Не проблема. Этот brand нахрен new способ got you covered!!
Дисклеймер
Это нарушение правил Codewars, вас могут за это забанить.
В чём соль
Рассматривать будем только нативные компилируемые языки, потому что только для них моя темка работает. Решать будем https://www.codewars.com/kata/51b62bf6a9c58071c600001b/train/c
Вспомним некоторые нюансы:
Все мы знаем (или теперь знаем) что код на нативных компилируемых языках (например, C, Rust) компилируется в инструкции, понятные процессору, которые, в свою очередь, всего лишь байты
Нам доступны исходники проверяющей программы

Пример проверки из задания
По итогу можно попытаться сложить эти 2 знания: в нашей проверяемой функции (solution из картинки выше) получить адрес возврата, сдампить некоторое количество байт оттуда, чтобы получить байты инструкций функции do_test после вызова solution, найти где используется expected и использовать его в нашей функции.
Вот наглядно:
Дампим код:
#include <stdio.h> #include <stdlib.h> static void dump_bytes(const void *address, size_t size) { const unsigned char *p = (const unsigned char *)address; for (size_t offset = 0; offset < size; offset += 16) { size_t line_size = size - offset; if (line_size > 16) line_size = 16; for (size_t i = 0; i < line_size; ++i) fprintf(stderr, "%02x ", (unsigned)p[offset + i]); fputc('\n', stderr); } fflush(stderr); } char *solution(int n) { // return a heap-allocated string: const unsigned char *return_address = (const unsigned char *)__builtin_return_address(0); dump_bytes(return_address, 512); return calloc(1, 1); }
Далее хекс дамп нужно превратить в настоящие байты. Для этого я использовал https://hexed.it/. Загружаем полученный дамп в декомпилятор (я использовал Binary Ninja) как x86-64, видим:

Мы можем смотреть дамп дальше, пытаться понять где expected, но можно просто вывести содержимое arg2 - 0x18 и arg2-0x10 и посмотреть, где лежит то, что от нас просят. Обратите внимание, что arg1 это rax, а arg2 это rbp. По этим переменным, скорее всего, лежит expected из-за того, что перед вызовом функции переменные обязаны куда-то сохраняться и почти всегда это стек (тут не rsp, а rbp, так что это формально стековый фрейм, но это неважные детали), а это очень похоже на восстановление переменной со стека.
Получить стек фрейм в clang, который используется в codewars, можно через __builtin_frame_address(1) (тут 1 значит фрейм какой функции "вверх нам надо", типа 0 - текущая функция, 1 - вызывающая функция, 2 - вызывающая вызывающей и тд). Получается можем написать это:
#include <stdio.h> char *solution(int n) { char *frame = __builtin_frame_address(1); fprintf(stderr, "%s\n", *(char**)(frame - 0x10)); fprintf(stderr, "%s\n", *(char**)(frame - 0x18)); return 0; }
Получаем:
II (null) I (null)
У нас 4 строки, а не 2 из-за того что тест 2 раза выполняется, как я понимаю. II - ровно то, что от нас просят в первом тесте, значит frame - 0x10 - наша переменная. Пишем:
#include <stdio.h> char *solution(int n) { char *frame = __builtin_frame_address(1); return strdup(*(char **)(frame - 0x10)); }
И всё, это решение!
Заключение
Таким образом я, например, смог "решить" задание https://www.codewars.com/kata/59f9cad032b8b91e12000035 - компилятор (или типа того) Brainfuck. Моё решение занимает 34 строки, в то время как чужие занимают более тысячи.
Наверное, в большинстве случаев можно вообще пропустить шаг со снятием дампа и его декомпиляцией и просто сразу подбирать смещения для __builtin_frame_address(1)

