R0 CREW

1. Уроки реального взлома MiniTool Photo Recovery 3.0

В данных уроках хочу описать процесс реального взлома программы на примере MiniTool Photo Recovery 3.0

Реальный взлом мне кажется наиболее интересном, так как вы сталкиваетесь с проблемами которые пытаетесь решать в процессе реверса. К таким проблемам можно отнести:

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

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

Мы будем использовать следующие инструменты:

  1. IDA PRO 7.0 (последняя утекшая версия, ее можно найти на этом форуме)
  2. Far+Hiew
  3. Python
  4. Wireshark

Программу можно скачать по ссылкам, или взять в аттаче на случай если изменится версия или чего-то авторы поменяют.

https://www.minitool.com/download-center/photo-recovery-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 строка

кроме того имеется такой код

.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

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 проверяется некоторый код от которого зависит сообщение об ошибке

.text:000000013F2CF000 85 DB                                         test    ebx, ebx
.text:000000013F2CF002 74 66                                         jz      short loc_13F2CF06A

поднимаемся в верх по ссылкам где проверяется ebx и видим такой вызов

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

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 используя питон

import struct
import base64
import json

if __name__=="__main__":
    activation_code=base64.b64encode(json.dumps([base64.b64encode("test")]))
    print activation_code

результат исполнения скрипта

WyJkR1Z6ZEE9PSJd

введем его в activation code и проверим корректность расшифровок

тут видим что base64 расшифровался

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

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

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

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

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

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

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

дампим зашифрованный ключ на диск такой командой

предварительно нужно задать имя g_encodedRSAKey (можно просто указать адрес вместо конструкции LocByName(“g_encodedRSAKey”))
размер ключа 0x1c8

open('pk.enc','wb+').write(GetManyBytes(LocByName("g_encodedRSAKey"),0x1c8))

напишем скрипт для расшифровки ключа что бы проверить все наши предположения об алгоритмах

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)

результат будет таким

-----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, вот этим то мы и займемся на следующем уроке.

Всем кто осилил чтение, спасибо, пишем комментарии. Надеюсь было интересно 1

Классно, спасибо за урок!