• [ Регистрация ]Открытая и бесплатная
  • Tg admin@ALPHV_Admin (обязательно подтверждение в ЛС форума)

Статья Реверсим винтажный Wise Installer и обходим его проверки

stihl

bot
Moderator
Регистрация
09.02.2012
Сообщения
1,351
Розыгрыши
0
Реакции
640
Deposit
0.228 BTC
stihl не предоставил(а) никакой дополнительной информации.
Сегодня мы разберемся, как устроены дистрибутивы на базе Wise Installer — того самого винтажного инсталлятора родом из девяностых. Мы возьмем старую утилиту, которая отказывается устанавливаться без «главной программы», и шаг за шагом исследуем скрипты и функции инсталлятора с использованием отладчиков и IDA, чтобы понять логику проверки и заставить утилиту устанавливаться без дополнительных условий.
Сегодня мы окунемся в историю и прильнем к ее анналам. Те, кто застал конец прошлого века, когда компьютеры были большими, а трава зеленой, прекрасно помнят, как устанавливали себе на компьютер игры и приложения с купленных на рынке компакт‑дисков: для этого использовался инсталлятор Wise Installer.

Эта разработка команды Для просмотра ссылки Войди или Зарегистрируйся (что в переводе означает «Мудрые решения») в те времена была практически монополистом среди инсталляторов всевозможного софта. При всем богатстве зоопарка современных аналогов актуальных дистрибутивов на этой платформе практически не осталось, но мы попробуем вспомнить прошлое и поковыряться во внутренностях такого дистрибутива.


Итак, постановка задачи. Предположим, мы нашли на старом диске дистрибутив некоей жизненно важной утилиты, но она категорически отказывается устанавливаться, пока не установлена другая, основная программа (отсутствующая у тебя и совершенно тебе ненужная). В пакет той самой «большой программы» эта утилита изначально входила. Сообщение об ошибке, после которого инсталлятор закрывается, выглядит вот так.

Для просмотра ссылки Войди или Зарегистрируйся
Требуется найти способ, как инсталлировать утилиту без основной программы.

Идентифицировать защиту даже и не нужно — она сама при запуске корявыми синими буквами выдает, что это Wise Installer Wizard. Detect It Easy тоже соглашается с этим.

Для просмотра ссылки Войди или Зарегистрируйся
Запускаем инсталлятор из отладчика x32dbg и при появлении окна сообщения прерываем программу. Смотрим стек вызовов.

Для просмотра ссылки Войди или Зарегистрируйся
Видно, что окно сообщения реализовано с использованием функции DialogBoxParamA, которая вызывается из загадочной библиотеки glcb36c.tmp. А она, в свою очередь, находится во временном подкаталоге системы.

Легко убедиться, что при каждом новом запуске инсталлятора название у нее разное, но файл один и тот же. Вытаскиваем его из временного каталога наружу и препарируем при помощи IDA. Самый нижний адрес возврата 10001EA8 принадлежит основной функции WiseMain, из нее вызывается функция sub_10003C7C, в свою очередь вызывающая диалоговое окно сообщения. И этот вызов выглядит весьма интересно.

Для просмотра ссылки Войди или Зарегистрируйся
Очень похоже на обработчик опкодов интерпретатора. Указатель на текущую команду которого IDA почему‑то называет nLength. Попробуем потрассировать шитый код покомандно в нашем любимом отладчике x64dbg.

Нас немного огорчает то обстоятельство, что библиотека glcb36c.tmp мало того что не загружается сразу, так вдобавок материализуется в процессе работы во временном каталоге под разными именами, а это затрудняет установку на нее точек останова. Но мы уже опытные хакеры и знаем, как с этим бороться.

В параметрах x64dbg на вкладке «События» ставим галку на «Прерываться на точке входа DLL», после чего запускаем инсталлятор. Пока что нам сильно везет — первая же остановка выводит нас на точку входа в библиотеку со знакомым названием glc60fb.tmp, у которой вдобавок и экспорты идентичны требуемым.

Для просмотра ссылки Войди или Зарегистрируйся
Теперь и точку останова можно ставить на выборку команд, благо библиотека еще и на фиксированные адреса садится. Остановившись на ней, посмотрим в окне дампа, откуда считывается текущая команда.

Для просмотра ссылки Войди или Зарегистрируйся
На первый взгляд все выглядит как типичный откомпилированный шитый код инсталляционного скрипта. Если в свойствах нашей точки останова поставить в поле условия 0, а в поле текста журнала {eax}: {cl}, то в журнале можно получить лог последовательности выполнения команд с их адресами следующего вида:

Код:
...
460BE1: 0
460C29: 9
460C8C: 9
460CDB: 9
460D5C: 9
460DDD: 9
460E31: 9
460EDA: 1B
460EDB: 9
460F3E: 9
460F92: 9
460FF0: 9
46104E: 9
461169: 9
461203: C
46121B: 9
46126F: 9
4612DC: D
461918: 9
461AB1: 9
461C81: 9
| ...
Все это очень замечательно, но пока что мы не знаем ни названий команд, ни синтаксиса, ни их реального действия. Мы и сам скрипт видели только в окне отладчика, поэтому начнем с его извлечения.

Погуглив, мы к своему удивлению обнаруживаем, что, несмотря на свою винтажность, формат Wise Installer практически не документирован и очень беден на утилиты для реверса. Существует несколько практически однотипных утилит‑распаковщиков: Для просмотра ссылки Войди или Зарегистрируйся, Для просмотра ссылки Войди или Зарегистрируйся, HWUN и E_WISE (к сожалению, за давностью лет эти проекты, похоже, канули в Лету, я их смог найти лишь Для просмотра ссылки Войди или Зарегистрируйся).

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

Создатели утилиты Для просмотра ссылки Войди или Зарегистрируйся попытались пойти чуть дальше и реверсировать инсталляционный скрипт. К сожалению, похоже, им надоело это безблагодатное дело еще на самом первоначальном этапе, поэтому опция декомпиляции скрипта -z сохранилась только в отладочном варианте, который по умолчанию отключен. Попробуем воспользоваться их наработками для лучшего понимания структуры и функционирования интерпретатора инсталляционного скрипта.

Итак, натравив REWize на наш инсталляционный модуль, получаем в консоль огромную простыню даже не декомпилированного кода, а, скорее, отладочного лога покомандной разборки инсталляционного скрипта. Он имеет следующий вид:

Код:
...
0x16 'W32INST_PATH_'
0x00 2440 0x00000000 0x000011B4 1999-01-21 14:40:08        9728 0000000000000000000000000000000000000000 20CC89C5 'TEMP/W32INST_PATH_' "(null)" "(null)" "(null)" "(null)" "(null)" "(null)"
0x09 09 '(null)' 'f16' '(null)' '(null)'
"0_x7FBB_FILEVERSION_x7F"
"0_x7FBB_FILEVERSION_x7F"
"0_x7FBB_FILEVERSION_x7F"
"0_x7FBB_FILEVERSION_x7F"
"0_x7FBB_FILEVERSION_x7F"

0x09 09 '(null)' 'f16' '(null)' '(null)'
"0_x7FBB_VERSION_x7F"
"0_x7FBB_VERSION_x7F"
"0_x7FBB_VERSION_x7F"
"0_x7FBB_VERSION_x7F"
"0_x7FBB_VERSION_x7F"

0x09 09 '(null)' 'f16' '(null)' '(null)'
...
По‑прежнему ни фига не понятно, кроме того, что первое слева Hex-число — это код операции, а следующая за ним мешанина строк и чисел — ее параметры.

Ну что ж, по крайней мере это хотя бы выглядит более структурированно, чем наш исходный лог. Как минимум из него видна взаимосвязь между командами (смысл которых по‑прежнему остается для нас загадкой) и константами, текстовыми и числовыми. Впрочем, покопавшись в исходниках декомпилятора и в комментариях файла wisescript.h, мы находим предположительные наброски системы команд интерпретатора:

Operation codes:

Код:
0x00  // Custom deflate file header
  0x03  // ?
  0x04  // Form data?
  0x05  // .ini file, section-name and values for that section
  0x06  // Deflated file just used by the installer? (No filename)
  0x07
  0x08  // end branch
  0x09  // function call ?
  0x0A
  0x0B
  0x0C  // if statement (new branch)
  0x0D  // else/default statement (inside if statement branch)
  0x0F  // Start form data?
  0x10  // End form data?
  0x11
  0x12  // File on install medium (CD/DVD), to copy?
  0x14  // Deflated file just used by the installer? (No filename)
  0x15
  0x16  // Temp filename?
  0x17
  0x18  // Skip this byte? On some installers also skip next 6 bytes FIXME
  0x19
  0x23  // else if statement (inside if statement branch)
  0x24  // Skip this byte? Only seen in RTCW
  0x25  // Skip this byte? Only seen in RTCW
  0x1A
  0x1B  // Skip this byte
  0x1C
  0x1D
  0x1E
  0x30  // read 1 byte and 2 strings, only seen in cuteftp.exe, same
        // as 0x15? or maybe even 0x23?
С этим можно уже если не идти, но начинать собираться в банк! Неизвестный авторам опкод 0x03 мы уже сами определили, это вызов окна сообщения. Что самое главное, в этом списке есть коды условных операторов, при помощи которых можно постичь логику ветвления кода. Причем авторы явно позаботились о нас и в комментариях к команде 0x0C (IF) даже привели примеры ее синтаксиса:

Код:
/* WiseScriptUnknown0x0C - aka the 'if' struct */
typedef struct {
  // examples:
  //   0x0C 18 'DELAY' '3000'
  //   0x0C 00 'LANG' 'Deutsch'
  //   0x0C 00 'BRANDING' '1'
  //   0x0C 00 'NAME' '(null)'
  //   0x0C 07 'WOLF_VERSION' '1.32'
  //   0x0C 00 'WOLF_VERSION' '(null)'
  //   0x0C 00 'PATCH_INSTALLED' '1'
Собственно, для выполнения поставленной задачи большего и не требуется, ведь мы и изначально не замахивались на полный реверс инсталляционного скрипта. Рассмотрим место в «декомпилированном скрипте» от REWise, которое вызывает окно сообщения, более подробно, в свете открывшейся информации:

Код:
...
0x09 09 '(null)' 'f8' '(null)' '(null)'      // «Вызов функции» f8
"0_x7FBB_TMPATYPE_x7F%SYS32%\bbaptype.dll_x7FApplicationType_x7FApplicationType_x7F-1"
"0_x7FBB_TMPATYPE_x7F%SYS32%\bbaptype.dll_x7FApplicationType_x7FApplicationType_x7F-1"
"0_x7FBB_TMPATYPE_x7F%SYS32%\bbaptype.dll_x7FApplicationType_x7FApplicationType_x7F-1"
"0_x7FBB_TMPATYPE_x7F%SYS32%\bbaptype.dll_x7FApplicationType_x7FApplicationType_x7F-1"
"0_x7FBB_TMPATYPE_x7F%SYS32%\bbaptype.dll_x7FApplicationType_x7FApplicationType_x7F-1"

0x0C 00 'BB_TMPATYPE' '-1'                   // IF BB_TMPATYPE==-1
        0x03 0x26 "MSG_5002" "%MSG_5002%" "MSG_5002" "%MSG_5002%" "MSG_5002" "%MSG_5002%" "MSG_5002" "%MSG_5002%" "MSG_5002" "%MSG_5002%"          // Выдать сообщение MSG_5002 и завершить скрипт
        0x18 - Skipped 0 * 0x00
0x0D                                         // ELSE
...
Проверить, что это действительно нужный нам код, достаточно просто. Если приостановить работу инсталлятора в отладчике и подправить в памяти распакованный скрипт, написав в аргументах команды вместо 0x0C '-1', к примеру, 0, то окна с сообщением о завершении работы мы не получим и инсталляция продолжится до успешного окончания, что формально позволяет считать поставленную задачу решенной.

Но, как ты, вероятно, уже привык, мы чужды халтуре и формализму, поэтому подумаем над тем, как все‑таки заставить программу инсталлироваться, не прибегая к загрузке в отладчик. Самый простой выход — подправить в памяти распакованный скрипт прямо на лету, используя прокси, лоадер или даже патч кода самого инсталлятора. Мы уже многократно им пользовались в предыдущих статьях, поэтому такой способ выглядит малоинтересным.

Можно (и это было бы хорошим решением) пропатчить инсталляционный скрипт и перепаковать его. Но предоставлю тебе разобраться, как сделать это, самостоятельно: приведенные выше ссылки содержат достаточно информации для реализации подобного плана, а мое повествование и так достаточно затянулось. Поэтому поищем пути попроще.

Для начала попробуем разобраться, почему же все‑таки программа не хочет инсталлироваться, почему на входе в условие внутренняя псевдопеременная BB_TMPATYPE равна -1? Кэп подсказывает, что эта переменная заполняется в предыдущем вызове «функции f8», аргументы которой содержат и имя BB_TMPATYPE, и значение -1.

Чтобы понять, как именно это происходит, снова открываем IDA и смотрим реализацию команды 0x09. Судя по коду, в зависимости от значения самого первого параметра способы вызова функций этот код реализует самые разнообразные. В нашем конкретном случае (первый параметр тоже равен 9) код реализации примерно такой.

Для просмотра ссылки Войди или Зарегистрируйся
То есть вызывается ординал с именем f8 этой же самой библиотеки. Надо отметить, таких номерных ординалов в ней достаточно много: похоже, они реализуют самые ходовые команды (вплоть до присваивания значений переменным), на которые, видимо, не хватило опкодов. Реализация конкретно функции f8 выглядит вот так:

Код:
int __stdcall f8(int a1)  // На входе в a1[32] строка "0_x7FBB_TMPATYPE_x7F%SYS32%\bbaptype.dll_x7FApplicationType_x7FApplicationType_x7F-1" разделитель 0x7F
{
  _BYTE *v1; // eax
  _BYTE *v2; // eax
  _BYTE *v3; // eax
  _BYTE *v4; // eax
  _BYTE *v5; // eax
  CHAR ReturnedString[256]; // [esp+Ch] [ebp-140h] BYREF
  char v8[32]; // [esp+10Ch] [ebp-40h] BYREF
  CHAR String2[32]; // [esp+12Ch] [ebp-20h] BYREF
// Далее следует разбор строки параметров
  v1 = sub_1001182E(v8, (_BYTE *)(a1 + 32));  // v8="0"
  v2 = sub_1001182E(String2, v1);               // String2="BB_TMPATYPE"
  v3 = sub_1001182E(String, v2);                // String="%SYS32%\bbaptype.dll"
  v4 = sub_1001182E(&byte_10024200, v3);        // byte_10024200="ApplicationType"
  v5 = sub_1001182E(byte_10023F00, v4);         // byte_10023F00="ApplicationType"
  sub_1001182E(&byte_10024000, v5);             // byte_10024000="-1"
  GetPrivateProfileStringA(&byte_10024200, byte_10023F00, Default, ReturnedString, 0x100u, String); // GetPrivateProfileStringA("ApplicationType", "ApplicationType", Default, ReturnedString, 0x100u, "C:\Windows\System32\bbaptype.dll")
  if ( ReturnedString[0] ) // Если строка успешно извлечена
  {
if ( v8[0] != 48 )     // Если первый параметр не 0, то проделать с извлеченной строкой еще какие-то действия. Не заморачиваемся, у нас она 0
      sub_10011C76(ReturnedString);
  }
  else
  {
    lstrcpyA(ReturnedString, &byte_10024000); // Если файл C:\Windows\System32\bbaptype.dll отсутствует или внутри него нет нужных секций и ключей, заполняем ReturnedString значением по умолчанию, в нашем случае это -1
  }
  sub_100114B9(String2, ReturnedString);  // BB_TMPATYPE=ReturnedString
  return 0;
}
Итак, из этого кода видно, что функция f8 извлекает строковое значение ключа ApplicationType одноименного раздела из текстового ini-файла, который хитрые разработчики для конспирации назвали bbaptype.dll и поместили в системный каталог Windows. Выходит, для решения нашей задачи не нужно никакого патчинга и перепаковки инсталлятора, достаточно создать простенький текстовый файл, содержащий вот такой раздел:

Код:
[ApplicationType]
ApplicationType=1
Назовем этот файл bbaptype.dll и поместим его в каталог C:\Windows\System32\, все гениальное просто! Разумеется, не стоит сильно обольщаться, я специально выбрал такой простой пример для наглядности. В реальной жизни тебе наверняка могут попасться гораздо более сложные примеры инсталляторов, для победы над которыми придется и патчить инсталляционные скрипты, и полностью перепаковывать их, и даже, возможно, реверсировать скрипт вплоть до разбора всех‑всех‑всех команд интерпретатора и встроенных функций. Хотя, если учесть винтажность самого инсталлятора, который уже много лет не поддерживается, вероятность того, что он вообще когда‑либо тебе попадется, откровенно говоря, близка к нулю. Тем не менее нужно всегда быть готовым к встрече с неизведанным, и я изо всех сил стараюсь помогать тебе в этом.
 
Activity
So far there's no one here
Сверху Снизу