Решение Crackme От Лаборатории Касперского

В один прекрасный день разные каналы в телеграмме начали кидать связь на кряке от ЛК, Успешно справившиеся с заданием будут приглашены на собеседование! .

После такого громкого заявления я задумался, насколько трудным будет обратный процесс.

О том, как я решил эту проблему, вы можете прочитать под катом (много картинок).

Придя домой, я еще раз внимательно перечитал задание, скачал архив и начал смотреть, что внутри.

А внутри было вот это:

Решение Crackme от Лаборатории Касперского

Запускаем x64dbg, сбрасываем его после распаковки и смотрим, что на самом деле внутри:

Решение Crackme от Лаборатории Касперского



Решение Crackme от Лаборатории Касперского

Имя файла берем из аргументов командной строки -> открыть, прочитать -> зашифровать первым этапом -> зашифровать вторым этапом -> записать в новый файл.

Все просто, пришло время взглянуть на шифрование.



Начнем с этапа 1

По адресу 0x4033f4 есть функция, которую я назвал crypt_64bit_up (позже вы поймете почему), она вызывается из цикла где-то внутри stage1

Решение Crackme от Лаборатории Касперского

И немного кривоватый результат декомпиляции

Решение Crackme от Лаборатории Касперского

Сначала я попробовал переписать тот же алгоритм на Python, потратил на это несколько часов и получилось примерно так (что делают get_dword и byteswap должно быть понятно из названий)
  
  
  
   

def _add(x1, x2): return (x1+x2) & 0xFFFFFFFF def get_buf_val(t, buffer): t_0 = t & 0xFF t_1 = (t >> 8) & 0xFF t_2 = (t >> 16) & 0xFF t_3 = (t >> 24) & 0xFF res = _add(get_dword(buffer, t_0 + 0x312), (get_dword(buffer, t_1 + 0x212) ^ _add(get_dword(buffer, t_2+0x112), get_dword(buffer, t_3+0x12)))) # print('Got buf val: 0x%X' % res) return res def crypt_64bit_up(initials, buffer): steps = [] steps.append(get_dword(buffer, 0) ^ byteswap(initials[0])) # = z steps.append(get_buf_val(steps[-1], buffer) ^ byteswap(initials[1]) ^ get_dword(buffer, 1)) for i in range(2, 17): steps.append(get_buf_val(steps[-1], buffer) ^ get_dword(buffer, i) ^ steps[i-2]) res_0 = steps[15] ^ get_dword(buffer, 17) res_1 = steps[16] print('Res[0]=0x%X, res[1]=0x%X' % (res_0, res_1))

Но потом я решил обратить внимание на константы 0x12, 0x112, 0x212, 0x312 (без шестнадцатеричных 18, 274, 536. не выглядит чем-то необычным).

Пробуем их погуглить и найти целый репозиторий (подсказка: NTR) с реализацией функций шифрования и расшифровка , это удача.

Пробуем зашифровать тестовый файл со случайным содержимым в исходной программе, сбросить его и зашифровать этот же файл Python-скриптом, все должно работать и результаты должны быть такими же.

После этого пытаемся его расшифровать (я решил не вдаваться в подробности и просто скопировать-вставить функцию расшифровки из исходника)

def crypt_64bit_down(initials, keybuf): x = initials[0] y = initials[1] for i in range(0x11, 1, -1): z = get_dword(keybuf, i) ^ x x = get_buf_val(z, keybuf) x = y ^ x y = z res_0 = x ^ get_dword(keybuf, 0x01) # x - step[i], y - step[i-1] res_1 = y ^ get_dword(keybuf, 0x0) return (res_1, res_0) def stage1_unpack(packed_data, state): res = bytearray() for i in range(0, len(packed_data), 8): ciphered = struct.unpack('>II', packed_data[i:i+8]) res += struct.pack('>II', *crypt_64bit_down(ciphered, state)) return res

Важное замечание: ключ в репозитории отличается от ключа в программе (что вполне логично).

Поэтому после инициализации ключа я просто сбрасывал его в файл, это буфер/keybuf

Перейдем ко второй части

Здесь все гораздо проще: сначала создается массив уникальных символов размером 0x55 байт в диапазоне (33, 118) (печатные символы), затем 32-битное значение упаковывается в 5 печатных символов из созданного ранее массива.



Решение Crackme от Лаборатории Касперского



Решение Crackme от Лаборатории Касперского

Поскольку при создании упомянутого выше массива нет случайности, этот массив будет одинаковым при каждом запуске программы, мы дамп его после инициализации и можем распаковать stage_2 с помощью простой функции

def stage2_unpack(packed_data, state): # checked! res = bytearray() for j in range(0, len(packed_data), 5): mapped = [state.index(packed_data[j+i]) for i in range(5)] res += struct.pack('>I', sum([mapped[4-i]*0x55**i for i in range(5)])) return res

Давайте сделаем что-то вроде этого:

f = open('stage1.state.bin', 'rb') stage1 = f.read() f.close() f = open('stage2.state.bin', 'rb') stage2 = f.read() f.close() f = open('rprotected.dat', 'rb') packed = f.read() f.close() unpacked_from_2 = stage2_unpack(packed, stage2) f = open('unpacked_from_2', 'wb') f.write(unpacked_from_2) f.close() unpacked_from_1 = stage1_unpack(unpacked_from_2, stage1) f = open('unpacked_from_1', 'wb') f.write(unpacked_from_1) f.close()

И мы получаем результат

Решение Crackme от Лаборатории Касперского

Теги: #python #криптография #обратное проектирование #обратное проектирование #Ассемблер

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.