По долгу службы пришлось мне иметь дело с битриксом. Так как раньше с ним я никогда не работал то сразу побрёл на официальный сайт за демо-версией. Из всех
редакций для скачки оказалась доступна версия "Старт". Кстати, как показала истина, распространяется сие благо с открытым исходником, что и натолкнуло меня на коварные мысли. На ознакомление разработчики дают 30 дней. Мне это показалось несправедливым, исправлением чего мы и займёмся :)
Начнём с того, что-же произойдёт по истечении триального периода. Выставляем системную дату на месяц вперёд, открываем сайт и видим вверху страницы сообщение слещующего содержания: "Срок работы пробной версии продукта истек. Через две недели этот сайт полностью прекратит свою работу... бла бла бла". Ну ok, смотрим что будет через две недели. Переводим дату на две недели вперёд и уже видим, что на сообщении "Срок работы пробной версии продукта истек... бу бу бу" работа скрипта обрывается. Непорядок. Информация должна быть доступна (c). С этого момента наше расследование причин сего инцидента и начнётся.
Делаем прыжок во времени на две недели назад, жмём в браузере ctrl+u и смотрим HTML-код сообщения. Любоваться тут особенно нечем:
<font class="tablebodytext"><font color="#FF0000">
Срок работы пробной версии продукта истек.
Через две недели этот сайт полностью прекратит свою работу.
Вы можете купить полнофункциональную версию продукта на сайте <a href="http://www.1c-bitrix.ru/?r1=bsm7trial&r2=expiried">www.1c-bitrix.ru</a>.
</font></font>
Здесь первым делом мой взгляд привлёк класс tablebodytext. Как настоящие хакеры вы немедленно грепнете php-файлы содержащие этот класс, ну а мы виндузятники просто заюзаем для поиска старый добрый FAR-менеджер. Поиск даст нам ~15 файлов среди которых найдутся и языковые файлы tools.php. По искомому слову (tablebodytext) находим строку локализации $MESS["expire_mess1"] и рядом с ней expire_mess2 в которых мы и увидим сообщение об истёкшем сроке. Идём по следу дальше. Теперь нам нужно найти PHP-код который выводит это сообщение и конечно-же выпилить его. Ищем expire_mess1 во всех php-файлах и находим prolong_after.php на каких-то сто строк кода.
<?
define("START_EXEC_PROLOG_AFTER_1", microtime());
$GLOBALS["BX_STATE"] = "PA";
if(!headers_sent())
header("Content-type: text/html; charset=".LANG_CHARSET);
if(defined("DEMO") && DEMO=="Y")
{
if(OLDSITEEXPIREDATE != SITEEXPIREDATE) die(GetMessage("expire_mess2"));
//wizard customization file
$bxProductConfig = array();
if(file_exists($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/.config.php"))
include($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/.config.php");
$delta = $SiteExpireDate-time();
$daysToExpire = ($delta < 0? 0 : ceil($delta/86400));
$bSaas = (COption::GetOptionString('main', '~SAAS_MODE', "N") == "Y");
if(isset($bxProductConfig["saas"]))
{
if($bSaas)
{
if($daysToExpire > 0)
{
if($daysToExpire <= $bxProductConfig["saas"]["days_before_warning"])
{
$sWarn = $bxProductConfig["saas"]["public_warning"];
$sWarn = str_replace("#RENT_DATE#", COption::GetOptionString('main', '~support_finish_date'), $sWarn);
$sWarn = str_replace("#DAYS#", $daysToExpire, $sWarn);
echo $sWarn;
}
}
else
{
echo str_replace("#RENT_DATE#", COption::GetOptionString('main', '~support_finish_date'), $bxProductConfig["saas"]["public_warning_expired"]);
}
}
else
{
if($daysToExpire == 0)
echo $bxProductConfig["saas"]["public_trial_expired"];
}
}
elseif($daysToExpire == 0)
{
echo GetMessage("expire_mess1");
}
}
if(COption::GetOptionString("main", "site_stopped", "N")=="Y" && !$GLOBALS["USER"]->CanDoOperation('edit_other_settings'))
{
if(file_exists($_SERVER["DOCUMENT_ROOT"].BX_PERSONAL_ROOT."/php_interface/".LANG."/site_closed.php"))
include($_SERVER["DOCUMENT_ROOT"].BX_PERSONAL_ROOT."/php_interface/".LANG."/site_closed.php");
elseif(file_exists($_SERVER["DOCUMENT_ROOT"].BX_PERSONAL_ROOT."/php_interface/include/site_closed.php"))
include($_SERVER["DOCUMENT_ROOT"].BX_PERSONAL_ROOT."/php_interface/include/site_closed.php");
else
include($_SERVER["DOCUMENT_ROOT"]."/bitrix/modules/main/include/site_closed.php");
die();
}
$sPreviewFile = $_SERVER["DOCUMENT_ROOT"].BX_PERSONAL_ROOT."/tmp/templates/__bx_preview/header.php";
if($_GET['bx_template_preview_mode'] == 'Y' && $USER->CanDoOperation('edit_other_settings') && file_exists($sPreviewFile))
include_once($sPreviewFile);
else
include_once($_SERVER["DOCUMENT_ROOT"].BX_PERSONAL_ROOT."/templates/".SITE_TEMPLATE_ID."/header.php");
/* Draw edit menu for whole content */
global $BX_GLOBAL_AREA_EDIT_ICON;
$BX_GLOBAL_AREA_EDIT_ICON = false;
if($GLOBALS['APPLICATION']->GetShowIncludeAreas())
{
require_once($_SERVER["DOCUMENT_ROOT"].BX_ROOT."/modules/main/interface/init_admin.php");
$aUserOpt = CUserOptions::GetOption("global", "settings", array());
if ($aUserOpt["page_edit_control_enable"] != "N")
{
$documentRoot = CSite::GetSiteDocRoot(SITE_ID);
if(isset($_SERVER["REAL_FILE_PATH"]) && $_SERVER["REAL_FILE_PATH"] != "")
$currentFilePath = $_SERVER["REAL_FILE_PATH"];
else
$currentFilePath = $GLOBALS['APPLICATION']->GetCurPage(true);
$bCanEdit = true;
if(!is_file($documentRoot.$currentFilePath) || !$GLOBALS["USER"]->CanDoFileOperation("fm_edit_existent_file", array(SITE_ID, $currentFilePath)))
$bCanEdit = false;
//need fm_lpa for every .php file, even with no php code inside
if($bCanEdit && !$GLOBALS["USER"]->CanDoOperation('edit_php') && in_array(GetFileExtension($currentFilePath), GetScriptFileExt()) && !$GLOBALS["USER"]->CanDoFileOperation('fm_lpa', array(SITE_ID, $currentFilePath)))
$bCanEdit = false;
if($bCanEdit && IsModuleInstalled("fileman") && !($GLOBALS["USER"]->CanDoOperation("fileman_admin_files") && $GLOBALS["USER"]->CanDoOperation("fileman_edit_existent_files")))
$bCanEdit = false;
if($bCanEdit)
{
echo $GLOBALS['APPLICATION']->IncludeStringBefore();
$BX_GLOBAL_AREA_EDIT_ICON = true;
}
}
}
define("START_EXEC_PROLOG_AFTER_2", microtime());
$GLOBALS["BX_STATE"] = "WA";
?>
С первого взгляда может показаться, что дело в шляпе и остаётся лишь закомментировать места вывода сообщений. А лучше просто поменять if(defined("DEMO") && DEMO=="Y") на if(defined("OLOLO") && DEMO=="Y"). Правда всёравно это не поможет :) Разработчики битрикса не такие лохи как вы себе представили.
Но всёравно лохи. Но на этом и начинается самое интересное. Как быть дальше? Похоже что дело зашло в тупик. Но не очкуйте и включите мозг. Давайте ещё раз повнимательнее посмотрим на prolong_after.php и увидим в нём строчку if(OLDSITEEXPIREDATE != SITEEXPIREDATE) которая нам как-бэ намекает, что разработчики битрикса заранее позаботились о сроках годности демо-версии. Ура. Мы снова напали на след и теперь ищем SITEEXPIREDATE везде где только можно. Поиск одарит нас несколькими файлами, самым вкусным на мой беглый взгляд оказался include.php, и моя интуиция снова меня не подвела. От увиденного в этом файле меня чуть не стошнило. Код скрипта маниакально обфусцирован, но зато это значит что разработчикам есть что скрывать. А раз есть что скрывать - значит надо раскопать и наверняка собака зарыта именно в этом месте. Как проверить, что это нужный нам файл? Да очень просто. Вставляем в начало скрипта такой код , сохраняем и заходим на сайт. Видим что оно сработало. Теперь убираем этот код и дописываем его в самый конец файла. Снова заходим на сайт и видем до боли нам знакомое сообщение. Это значит, что код отвечающий за вывод сообщения находится именно в этом файле. Но вот как его найти среди всей этой бредятины? Ну, первым делом нам надо привести файл к более-менее читабельному виду.
Гуглим PHP-деобфускатор и находим чудесную утилиту
phpCodeBeautifier. Качаем, распаковываем и открываем командную строку. Кстати программка эта есть как в никсовом так и в виндовом виде. Теперь несём файл include.php в папочку с phpCB и выполняем команду phpcb include.php > include.norm.php
Получившийся файл я бы не сказал что Beautiful но зато более менее Readable, чего нам вполне достаточно. Этим файлом (include.norm.php) без проблем можно заменить старый обфусцированный файл include.php. Теперь методом научного тыка подставляем (ну и убираем тоже) в разные строки файла die('all right!'); и постепенно сужая диапазон строк находим строчку на которой происходит вывод ненавистного сообщения. Для тех кто в танке объясню как найти нужную строку более подробно. Подставляем например в строку 12 die('all right');, сохраняем файл и заходим на сайт. Видим all right. Далее убираем all right с 12 строки и вставляем на строку 200. Снова заходим на сайт и видим сообщение об истёкоем сроке. Потом идём на строку 70, потом на 150 и постепенно сужаем поиск до нужной строки.
По иронии судьбы, а может разработчиков битрикса номер этой строки оказался 123 :) Пытаемся разобраться что-же тут написано но в конце концов побеждает лень и принято решение просто закомментировать эту строку. Ура - сайт снова работает, правда сообщение всёравно выводится. Но ничего, теперь возвращаемся к файлу prolong_after.php и чего нибудь там меняем (или удаляем) чтобы оно не выводилось. Ну, например мняем defined("DEMO") на defined("NULLED") и на этом цель достигнута. Правда в админке всёравно вылетает сообщение о том, что осталось 0 дней, но уж это я любезно предоставлю читателю :)