В продолжении цикла статей, Реверс-инжиниринг для начинающих, представляю Вам - 3 часть. В нашем реверс Меню сегодня это программа Http Master 5.6.1 (Актуальная версия, на момент написания этой статьи). Вы можете сами убедится посмотрев на историю версий (Обновлений с официального сайта): https://www.httpmaster.net/history
И сегодня рассмотрим не только алгоритм действий защиты программы, делая различные манипуляции, правки с кодом а и рассмотрим принцип локализации программы на нужный, Вам, язык. При работе, бывает, что есть хорошая программа но вот нужного Вам языка для неё не существует - по стандарту, что для большинства пользователей есть не очень удобно. И тогда, мы начинаем искать аналог или же соглашаемся с реальностью и работает с чем есть. Способов перевода (локализации программ) есть не мало, сегодня проделаем, как раз, один из них. Но Всему свое время, что бы не затягивать тему, стартуем !
Обычно я делаю так, если есть возможность посмотреть, вкратце, что собой представляет защита, на официальном ресурсе (хоть какое то описание или упоминание о ограничениях) то я так и делаю, смотрю (чтобы облегчить себе задачу). Заходим по ссылке, на оф. сайт программы https://www.httpmaster.net/compare . Тут нам разработчик, любезно, предоставляет сравнение различий между Бесплатной и Профессиональной версией. Уже что то есть и Мы, как минимум, понимаем
что сумма в 99$ (для одного пользователя) за лицензию, для такой программы, это очень много.
И уже по стандартному принципу, сначала определим на чем написана программа (перетащив на утилиту ExeInfo PE (Принцип работы утилиты можно прочитать в предыдущих моих статьях), её разрядность и есть ли какой упаковщик (протектор) что усложнит нам анализ программы. Как бы не так, условно говоря и не такое видели. И вот тут как обычно, по стандарту, Видим:
- Тут у нас исполняемый файл, его имя: HttpMaster.Windows.exe
- Разрядность (ВНИМАНИЕ!): А вот разрядность системы не смогла определить программа ExeInfo PE.
- На сем написана программа( то есть Язык программирования) и есть ли обфускация или упаковка (сжатие): Язык С# а вот упаковщика или дополнительной защиты нет, но это еще ни о чем не говорит.
- Рекомендации: Для отладки файла утилита предлагает использовать Net Reflector, версии 11. Кому как удобно, дело и свободный выбор каждого.
Запуск в триальном режиме и посмотрев в меню Help - License Data (информацию о лицензии). И сразу становится ясно что нет ключа, нет дополнительных функций. Обидно, досадно но сейчас разберемся
Теперь все становится на свои места и нам уже понятно что для активации программы нам нужна валидная пара в виде регистрационного имени и ключа. Ну что давайте же наконец то приступим к анализу.
А поскольку, вот как 7 дней DnSpy обновился https://github.com/dnSpyEx/dnSpy/releases/tag/v6.3.0-rc1 до версии 6.3.0-rc1 то мы будет использовать актуальную версию отладчика
И наш отладчик перемещается в class Program - метод private static void Main(string[] args) и давайте посмотрим что тут происходит.
Полный код для наглядного примера, чтобы легче было воспринимать статью.
C#:
private static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ApplicationExit += Program.OnApplicationExit;
Application.ThreadException += Program.OnThreadException;
SettingsManager settingsManager = new SettingsManager();
int num = 1;
do
{
try
{
settingsManager.CreateSettings();
AppData.ApplicationSettings = settingsManager.GetSettings();
num += 2;
}
catch (Exception)
{
settingsManager.DeleteSettingsFile();
num++;
}
}
while (num <= 2);
if (AppData.ApplicationSettings == null)
{
MessageBox.Show(string.Format(Messages.readSettingsError, settingsManager.GetSettingsFolder()), "HttpMaster", MessageBoxButtons.OK, MessageBoxIcon.Hand);
Application.Exit();
return;
}
if (AppData.ApplicationSettings.SupportInternationalDomainNames)
{
try
{
OtherUtils.EnableInternationalSupport(true);
}
catch (Exception ex)
{
if (AppData.ApplicationSettings.LogUnhandledError)
{
Program.WriteToLog(ex.GetType().ToString(), ExceptionUtils.CombineExceptionMessage(ex), ex.StackTrace);
}
}
}
if (AppData.ApplicationSettings.DefaultViewer == GlobalValues.PrettyViewEnum.Browser)
{
AppData.ApplicationSettings.DefaultViewer = GlobalValues.PrettyViewEnum.Auto;
}
AppProcedures.SetScalingValues();
RegistrationManager registrationManager = new RegistrationManager();
bool flag = false;
if (registrationManager.NewLicenseKeyExists())
{
flag = true;
registrationManager.DeleteOldLicense();
}
else if (registrationManager.OldLicenseKeyExists() && registrationManager.ConvertLicense())
{
flag = true;
registrationManager.DeleteOldLicense();
}
if (!flag)
{
using (RegistrationForm registrationForm = new RegistrationForm())
{
registrationForm.HeaderTitle = TextManager.GetFormHeaderTitle(registrationForm.Name);
registrationForm.ShowDialog();
if (!registrationForm.AllowToContinue)
{
Application.Exit();
return;
}
AppData.Licensed = registrationForm.LicenseSuccessful;
goto IL_175;
}
}
AppData.Licensed = true;
IL_175:
string text = string.Empty;
string text2 = string.Empty;
bool flag2 = true;
foreach (string text3 in args)
{
if (File.Exists(text3))
{
string extension = Path.GetExtension(text3);
string text4 = ".hmpr";
string text5 = ".hmex";
if (string.Compare(extension, text4, StringComparison.OrdinalIgnoreCase) == 0)
{
text = text3;
flag2 = true;
break;
}
if (string.Compare(extension, text5, StringComparison.OrdinalIgnoreCase) == 0)
{
text2 = text3;
flag2 = true;
break;
}
}
flag2 = false;
}
if (!flag2)
{
MessageBox.Show(Messages.invalidStartArgument, "HttpMaster", MessageBoxButtons.OK, MessageBoxIcon.Hand);
Application.Exit();
return;
}
MainForm mainForm = new MainForm();
if (!AppProcedures.SetFormSize(mainForm))
{
mainForm.WindowState = FormWindowState.Maximized;
}
if (string.IsNullOrEmpty(text) && string.IsNullOrEmpty(text2))
{
if (!AppData.ApplicationSettings.ShowWelcomeForm && AppData.ApplicationSettings.OpenLastProject)
{
string text6 = AppProcedures.DetermineProjectFilePath(0);
if (!string.IsNullOrEmpty(text6))
{
mainForm.OpenProjectFileArgument(text6);
}
}
}
else
{
mainForm.HmprArgument = text;
mainForm.HmexArgument = text2;
if (!string.IsNullOrEmpty(text))
{
mainForm.OpenProjectFileArgument(text);
}
}
Application.Run(mainForm);
}
}
}
Пролистав немного ниже мы видим кусок кода, своего рода конструкцию. И в нем методы NewLicenseKeyExists и DeleteOldLicense; Понимаете направление мысли
Кусок кода, что очевидно наст интересует.
Код:
RegistrationManager registrationManager = new RegistrationManager();
bool flag = false;
if (registrationManager.NewLicenseKeyExists())
{
flag = true;
registrationManager.DeleteOldLicense();
}
else if (registrationManager.OldLicenseKeyExists() && registrationManager.ConvertLicense())
{
flag = true;
registrationManager.DeleteOldLicense();
}
Давайте же перейдем в NewLicenseKeyExists и посмотрим что у нас там спрятаны за сюрпризы, а может какой то универсальный метод проверок или не ломаемый алгоритм
И после этого нам становится доступный вот такой кусок кода. В котором проверяются значения в реестре Windows и если их там нет то результат нам возвращается как False - то есть запретить (нет) а если даже есть какое-то значение то тут уже идут криптографические вычисления (Магия одним словом от разработчика
C#:
public bool NewLicenseKeyExists()
{
string text = RegistryUtils.GetRegistryKey("Software\\Borvid\\HttpMaster", "Id");
if (string.IsNullOrEmpty(text))
{
return false;
}
text = EncryptionUtils.Decrypt(text);
this.LicenseKeyUsed = text;
return new EncryptedLicenseProvider("<EncryptedLicenseParameters>\r\n\t <ProductName>HttpMaster Professional 2</ProductName>\r\n\t <RSAKeyValue>\r\n\t <Modulus>hQQnn6qzLcZdZPFiCcodmOnnEY/OWaM9DQUFKB8EMBndDF/TvgPduwO2vpClJ7qrf7f8sUuEGcGTjyZfcbverN2JaUynGPCXT1/lBDMMoRnbQmbr2qwVWS/cZBPR4s/4OyAM+mbKfc+OsUoUhHY4jYqf00in2XJQoVc8fXtbdic=</Modulus>\r\n\t <Exponent>AQAB</Exponent>\r\n\t </RSAKeyValue>\r\n\t <DesignSignature>CHiUADfZeCEbMXEd87J+eusIYl8UyoMp9X7Fw17BwVwMCGyjarSCF66NwqOmxI6VWJR3lSRR1BOU8Z92PFE1OdApmhyE70dOuFNtEHfy69oH1zB2fHyy+S5fx3w2WHczExSVIGq43MJaJ06D6t4sz7ZL790PbviuLZIyDfAlBYQ=</DesignSignature>\r\n\t <RuntimeSignature>SV7vYU99/VR0R/rfj5eDdiM96V1OKyMrkqUS962XiC33Y+JPi6GGLU9XDcMiq37CWV8Pi+pR+buNbbOq9tSF6Z8+xGHyYU+VKdIDfkpmxKsgVMQAJ8vfPZoVOAceB/sid6KQyxdZlAKMX1mMo+/FXHwZ2anHHbjDarQUWPGCG4A=</RuntimeSignature>\r\n\t <KeyStrength>7</KeyStrength>\r\n\t </EncryptedLicenseParameters>", string.Empty).ValidateLicenseKey(text) != null;
}
Обратите ВНИМАНИЕ: Что вызов NewLicenseKeyExists идет из динамической библиотеки HttpMaster.Engine.dll что расположена рядом с основным файлом программы HttpMaster.Windows.exe. Это можно легко понять посмотрев в отладчике. То есть своего рода конструкция где из основного файла программы идет загрузка проверочных функций из dll, если уже просто говорить.
Что тут можно сделать ? Да все очень просто, можно сделать так что бы функция постоянно возвращала True, при любом исходе. А всю криптографическую проверку вырезать и на этом точка
В итоге весь код проверки превратится в одну строку return true; и выглядеть будет вот так:
И теперь сохраняем наши изменения уже в HttpMaster.Engine.dll (Файл динамической библиотеки). Давайте запустим программу и посмотрим что у нас получилось. Окно с просьбой активировать программу пропало, программа запустилась в профессиональном режиме. Но вот есть одно но, если зайти в меню Help - License Data что бы посмотреть информацию о лицензии. То тут картина отображается не очень красивая, у нас возникло исключение в виде ошибки и оно отображается и режет глаз.
Вернувшись в начало и воспользовавшись или поиском к примеру Licenseinfo или визуально, мы находим функцию, место где именно Вызывается NewLicenseKeyExists
и в зависимости от результата отображается в Информации о лицензии. Вот это место
То есть:
C#:
public LicenseInfoForm()
{
this.InitializeComponent();
RegistrationManager registrationManager = new RegistrationManager();
if (registrationManager.NewLicenseKeyExists())
{
this.licenseKeyLabel.Text = string.Format(this.licenseKeyLabel.Text, registrationManager.LicenseKeyUsed.Substring(0, 5) + " ...");
this.deleteLicensePanel.Visible = true;
this.purchaseButton.Visible = false;
this.okButton.Visible = false;
this.cancelButton.Text = FormStrings.closeButton;
return;
}
this.addLicensePanel.Visible = true;
}
Что же делать далее ? Да ничего такого особо сложного а именно поле this.licenseKeyLabel.Text = и отвечает за Вывод данных в программу в меню Help - License Data.
А поскольку программу мы уже вылечили то это поле мы можем заполнить любыми произвольными данными и на работу программы уже это никак не повлияет. Давайте туда впишем, к примеру, вот такой текст "Зарегистрировано на -EXE- что на форуме xss.pro";. То есть полный код будет выглядеть вот так:
C#:
public LicenseInfoForm()
{
this.InitializeComponent();
if (new RegistrationManager().NewLicenseKeyExists())
{
// То есть вот тут добавили вывод наших данных, авторства.
this.licenseKeyLabel.Text = "Зарегистрировано на -EXE- что на форуме xss.pro";
this.deleteLicensePanel.Visible = true;
this.purchaseButton.Visible = false;
this.okButton.Visible = false;
this.cancelButton.Text = FormStrings.closeButton;
return;
}
this.addLicensePanel.Visible = true;
}
Сохраняем результат и смотрим что у нас получилось. Запускаем программу и заходим в меню Help - License Data и видим как у нас красуется наш надпись. Это уже более солидней смотрится, согласитесь ?
После чего редактируем название программы как Вам угодно, можете добавить любой текст. К примеру давайте допишем туда вот такой текст (Статья для форума xss.pro от -EXE-). Нажимаем правой кнопкой мышки и выбираем изменить IL инструкцию, добавляем текст и сохраняем.
Ну и как Вам такой вариант, смотрится тоже ничего. Пользователь запустив программу сражу же увидит кто её отреверсил
Для этого нажимаем правой кнопкой мышки и выбираем изменить метод. Правим нужный нам текст, переводим на русский и нажимаем Компилировать.
После чего у нас получается уже версия программы с изменениями. И нам осталось только сохранить результат в готовый exe файл и посмотреть на результат, запустив программу.
Сохраняем программу, в отладчике файл - сохранить все и запускаем программу. И смотрим что у нас получилось. А получилось у нас то что мы перевели строки на русский с английского, что есть очень хорошо, разве я не прав ?. Ну и так при надобности можно перевести (локализировать) программу на нужный Вам язык.
В этой статье мы рассмотрели защиту очередной программы. Из статьи освоили некие нюансы, а именно что защита софта может вызываться (проверятся) не только из основного файла а также из вспомогательных файлов, динамических библиотек. Может и подход от части правильный, но создавая какие проверки, разработчик, прежде всего должен подумать, запрограммировать не стандартный способ (алгоритм) защиты. Мыльные пузырки раздувать не будем
На этом я не останавливаюсь, каждая статья будет равна поломанной душе программы
Что в архиве, скриншот:
Наш пациент (Http Master 5.6.1): https://www.virustotal.com/gui/file/a7b972fc34f3ad02d4d3409096c2049e29cdf732718ee63ce1fdab2f19ac8baa
dnSpy x32 / virustotal: https://www.virustotal.com/gui/file/7ce05f1aafaaa87d046bbad1b07801777e724251b084bc7f70cef71c98b08105
dnSpy x64 / virustotal: https://www.virustotal.com/gui/file/cc15f3f7836f5c976e058aabdd55af8635b484a6b9a5e94a2cb048856965f9e2
ExeinfoPe / virustotal: https://www.virustotal.com/gui/file/32e6df44a529d3bd543aae01365852990c0e7f3b4f84b5a7f9d7dfc18d6a46fd
Файлы к статье:
Скрытый контент для зарегистрированных пользователей.