В один прекрасный день разные каналы в телеграмме начали кидать связь на кряке от ЛК, Успешно справившиеся с заданием будут приглашены на собеседование! .
После такого громкого заявления я задумался, насколько трудным будет обратный процесс.
О том, как я решил эту проблему, вы можете прочитать под катом (много картинок).
Придя домой, я еще раз внимательно перечитал задание, скачал архив и начал смотреть, что внутри.
А внутри было вот это:
Запускаем x64dbg, сбрасываем его после распаковки и смотрим, что на самом деле внутри:
Имя файла берем из аргументов командной строки -> открыть, прочитать -> зашифровать первым этапом -> зашифровать вторым этапом -> записать в новый файл.
Все просто, пришло время взглянуть на шифрование.
Начнем с этапа 1
По адресу 0x4033f4 есть функция, которую я назвал crypt_64bit_up (позже вы поймете почему), она вызывается из цикла где-то внутри stage1И немного кривоватый результат декомпиляции
Сначала я попробовал переписать тот же алгоритм на Python, потратил на это несколько часов и получилось примерно так (что делают get_dword и byteswap должно быть понятно из названий)
Но потом я решил обратить внимание на константы 0x12, 0x112, 0x212, 0x312 (без шестнадцатеричных 18, 274, 536. не выглядит чем-то необычным).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))
Пробуем их погуглить и найти целый репозиторий (подсказка: 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 печатных символов из созданного ранее массива.
Поскольку при создании упомянутого выше массива нет случайности, этот массив будет одинаковым при каждом запуске программы, мы дамп его после инициализации и можем распаковать 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()
И мы получаем результат
Теги: #python #криптография #обратное проектирование #обратное проектирование #Ассемблер
-
Поведенческие Технологии В Сети Рлэ.
19 Oct, 24 -
Риа Новости Запустило Портал Открытых Данных
19 Oct, 24