В данных уроках хочу описать процесс реального взлома программы на примере MiniTool Photo Recovery 3.0
Реальный взлом мне кажется наиболее интересном, так как вы сталкиваетесь с проблемами которые пытаетесь решать в процессе реверса. К таким проблемам можно отнести:- большое количество кода в котором нужно найти критические участки данных
- использование сторонних библиотек в том числе статически скомпилированных, это может как облегчить так и затруднить анализ программы
- восстановление различных структур
Постараюсь объяснить ход мыслей при поиске кода отвечающего за регистрацию, при реверсе, и написании кода для шифровки и расшифровки структур данных. Результатом работы будет написание генератора серийных номеров и небольшой патч программы.
Мы будем использовать следующие инструменты:- IDA PRO 7.0 (последняя утекшая версия, ее можно найти на этом форуме)
- Far+Hiew
- Python
- Wireshark
Программу можно скачать по ссылкам, или взять в аттаче на случай если изменится версия или чего-то авторы поменяют.
https://www.minitool.com/download-ce...-download.html
https://www.partitionwizard.com/download/mpr3.exe
https://www.sendspace.com/file/tb2kf1
Устанавливаем программу mpr3.exe я использую Win7 x64, соотвественно у меня будет установлена x64 версия программы в C:\Program Files\MiniToolPhotoRecovery\
запускаем, переходим к регистрации программы вводим произвольный серийный номер и получаем сообщение

пытаемся найти бинарник который содержит сообщение "Invalid license key", для этого в Far выполняем поиск Alt+F7

и видим результат

открываем в IDA x64 файл MiniToolPhotoRecovery.exe и ищем эту строку
Shift+F12, Ctrl+F пишем "Invalid license key" тем самым отфильтровываем лишние строки

переходим на строку и ищем перекрестные ссылки жмем клавишу "x"

ссылка будет всего одна переходим по ней и видим такой вот код

перед этим кодом имеется условие, ставим точку останова F2 на условие, в меню Debugger->Switch Debugger->Local Windows Debugger и запускаем отладку F9, кликаем на регистрацию и вводим произвольный серийный номер "1234567890"
срабатывает точка останова, и если навести курсор на rax можно увидеть наш серийный номер

программа написана с использованием Qt, строка в формате QString
выглядит так если отформатировать ее используя клавишу "d"

тут видно что по смещению
+ 4*1 длинна
+ 4*6 строка
кроме того имеется такой код
Code:
.text:000000013F2DE1B4 83 78 04 1B cmp dword ptr [rax+4], 1Bh
.text:000000013F2DE1B8 74 69 jz short is_ok
проверяется длинна и она должна быть 27 знаков, пробуем ввести "123456789012345678901234567" и проходим это условие.
Далее F9 программа пытается выполнить онлайн регистрацию, нам нужно заблокировать эту возможность, что бы регистрация стала оффлайн.
я делал так: запускал wireshark и смотрел в момент регистрации какие резолвятся домены, потом их занес в C:\Windows\System32\drivers\etc\hosts
Code:
127.0.0.1 www.minitool.com
127.0.0.1 minitool.com
127.0.0.1 pas2.partitionwizard.com
127.0.0.1 pas2.eofsoft.com
127.0.0.1 pas2.minitool.com
после этого появляться окно, вводим произвольные данные в activation code и получим сообщение об ошибке

Ищем текст этого сообщения в ида как в прошлый раз

перекрестных ссылок будет две, ставим точки останова и выполняем регистрацию еще раз, остановка произойдет тут

в ebx проверяется некоторый код от которого зависит сообщение об ошибке
Code:
.text:000000013F2CF000 85 DB test ebx, ebx
.text:000000013F2CF002 74 66 jz short loc_13F2CF06A
поднимаемся в верх по ссылкам где проверяется ebx и видим такой вызов

где регистр r10 связан с
Code:
Dll_PAS_getInterface

Dll_PAS_getInterface ведет нас в библиотеку Pas2.dll, ее мы загружаем в IDA
переходим на экспорт Dll_PAS_getInterface и видим что эта функция возвращает несколько функций

эти функции выглядят так, их 10 штук

создадим структуру Shift+F9, Insert задаем имя pas_interface_t, и создаем 10 полей dq клавишей "d", и применяем структуру к r10, клавиша "t" и выбираем нашу структуру из списка

в Pas2.dll даем такое же имя для функции как в структуре

настраиваем отладку для Pas2.dll как показано тут, теперь мы сможем ее отладить

и запускаем отладку, вводим регистрационные данные, срабатывает точка останова на функции

пытаемся понять что передается на вход функции в одном из аргументов в rcx мы видим введенный наш activation code

а после функции он уже в формате QString

тут расшифровка activation code

операции расшифровки

можно выделить следующие операции на псевдокоде base64_decode(json_decode([base64_decode(data)]))
Пишем скрипт для формирования activation code используя питон
Code:
import struct
import base64
import json
if __name__=="__main__":
activation_code=base64.b64encode(json.dumps([base64.b64encode("test")]))
print activation_code
результат исполнения скрипта
введем его в activation code и проверим корректность расшифровок
тут видим что base64 расшифровался


а тут количество элементов в массиве равно 1

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

немного возвращаемся назад и видим функцию которая расшифровывает этот самый ключик

немного поперемещавшиcь по коду видим что в качестве ключа расшифровки используется "7FB45FAB"

зашифрован он DES ECB

сама функция des ecb

расшифрованный ключ в PEM формате

дампим зашифрованный ключ на диск такой командой
предварительно нужно задать имя g_encodedRSAKey (можно просто указать адрес вместо конструкции LocByName("g_encodedRSAKey"))
размер ключа 0x1c8
Code:
open('pk.enc','wb+').write(GetManyBytes(LocByName("g_encodedRSAKey"),0x1c8))

напишем скрипт для расшифровки ключа что бы проверить все наши предположения об алгоритмах
Code:
import struct
import base64
import json
from Crypto.Cipher import DES
def encodeActivationCode(txt):
activation_code=base64.b64encode(json.dumps([base64.b64encode(txt)]))
return activation_code
def decodeDES(key,data):
d=DES.new(key,mode=DES.MODE_ECB)
return d.decrypt(data)
def decodeRSAKey(enc):
#open('pk.enc','wb+').write(GetManyBytes(LocByName("g_encodedRSAKey"),0x1c8))
return decodeDES('7FB45FAB', enc)
if __name__=="__main__":
enc=open('pk.enc','rb').read()
print decodeDES('7FB45FAB', enc)
результат будет таким
Code:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7265MFWJrq1On6XRqa2p
EM0Zrqz+7mGDAx93i9oxajdFbYmmEiaZg8pLog6GuLeOm2NY0XxcKOHaRcFR2siE
UIwegD2eGNgO2hd2rnWU1AfvCQaOg1JcCbEW0FLoOPoDaZMyh8IhCWf6AyeAs5U0
YH/TLEmAvCaDxdU6TEg8zy2C23GoAoUvcZ3SSQJNWz0NqRSNnV78wZmHyoN0uQ8y
yMgrMYQ12+DZ/Jhe/2wSOPIOZwDkCwLtztcvEsJnJ4i24/o9JUD1nMpNQ84yATPE
s3WxmABmrMLyQLoW4BuFwXxmDBH698SSLG8B+zfZNThCyFssWT2+a3aoQRBE8LgI
NQIDAQAB
-----END PUBLIC KEY-----
\000\000\000\000\000
подведем итог проделанной работы. На этом уроке, мы узнали:- длину лицензионного ключа
- формат сообщения activation code base64(json([base64(rsa(publicKey,enc_data))]))
- используется rsa, алгоритм несимметричный, без знания приватного ключа мы не сможем сформировать activation_code
- ключ des "7FB45FAB" для расшифровки публичного ключа
Итак для формирования activation code, нам нужен приватный ключ, но у нас его нет. Решением этой проблемы может служить генерация свой ключевой пары, и встраивание ее в Pas2.dll, вот этим то мы и займемся на следующем уроке.
Всем кто осилил чтение, спасибо, пишем комментарии. Надеюсь было интересно