На проверке Разбор и анализ патчера GenP: безопасный кряк для всех приложений Adobe?

Preview.png


1.0 Введение

Хоть компания Adobe и ушла из России, но потребность в их софте всё ещё остаётся. Решают её по-разному: кто-то использует другие аккаунты, кто-то использует ключи, а кто-то одевает повязку и спускает на воду свой цифровой фрегат.
На последнем мы как раз и остановимся. В этой статье проанализируем Adobe GenP.

Adobe GenP — это патчер, предназначенный для активации продуктов Adobe Creative Cloud, начиная с версий 2019 года и до текущих релизов. Он позволяет устанавливать и использовать приложения Adobe: Photoshop, Illustrator, Premiere Pro и другие, без необходимости приобретения лицензии.
Патчер — это программа, предназначенная для модификации программного обеспечения. Чаще всего используемая для обхода проверки лицензий и других защитных механизмов.
В отличие от многих других патчеров у него есть даже !

1727474570191.png


Кстати, помимо основного сообщества рекомендую обратить внимание на Community bookmarks. Там много полезных туториалов и дополнительной информации.

План статьи:

1.0 Введение
2.0 Файлы
3.0 Установка Creative Cloud и первый запуск

3.1 Про установку Adobe
3.2 Запуск GenP
4.0 Разбираемся в исходном коде
4.1 Начало скрипта
4.2 Основной цикл патчера
4.2.1. Обработчик кнопки поиска
4.2.2. Обработчик кнопки выбора папки
4.2.3. Обработчик кнопки выбора/отмены всех элементов
4.2.4. Обработчик кнопки Pop-up
4.2.5. Обработчик кнопки "Patch CC" и как работает эта магия
4.2.5.1. Весь цикл поиска и патчинга кратко
4.2.5.2. Вернёмся к обработчику кнопки "Patch CC"
4.2.5.3. Сравниваем пропатченные байты с оригинальными и ищем их
4.2.6. Обработчик кнопки "Restore"
4.2.7. Обработчик кнопки "Patch"
5.0 Изучаем патчи из INI-файла
5.1. Секция CustomPatterns
5.2. Секция DefaultPatterns
5.3. Оставшиеся патчи
6.0 Вывод

1727474582204.png


2.0 Файлы

Архив с GenP, который будет разбирать, был загружен с Reddit.

1727474644705.png


Его контрольные суммы:

iz3lne.zip
MD5: 6b104ba9deb749a6b6ce88b9c6997dae
SHA1: 19d9b52477606b78bdce568235c0acb9321c1bc4
SHA256: 14ce93ae01d50b9d2ff3c36c3edd574a9f8bcec56451f3a865fcc210c617a77b

Архив с GenP состоит из следующих частей:

1727474740656.png


Исполняемый файл и исходник со всем необходимым, если мы хотим собрать патчер сами. Исходник написан в виде скрипта для .

1727474755438.png


3.0 Установка Creative Cloud и первый запуск

3.1 Про установку Adobe

Установщик для Creative Cloud (далее CC) загружаем с . На момент написания статьи его MD5: 0011ec2b0f49f83ccf67aa706a638ccc. Но и с более новыми версиями всё работает (дата проверки 27.09.2024).

1727474775016.png


ОБЯЗАТЕЛЬНО снимаем тут галочку, чтобы патчи не слетали. Должно быть так, как на скрине.

3.2 Запуск GenP

После установки видим магазин приложений, где нам предлагают купить софт.

c5e08c2ec78a29824cced1be1be3cd05.png


Попробуем запустить GenP.

15297ceb7ce0915f7d838fc856dbd6a6.png


Он предлагает повысить привилегии GenP, чтобы нормально пропатчить XD и UWP-приложения. Откажемся.
Патчинг происходит таким образом:
  1. Патчим Adobe Creative Cloud, чтобы скачивать приложения - кнопка "Patch CC"
  2. Патчим отдельные приложения - кнопки "Search" и "Patch".
46fb39b7914f22a51dd7fd6d30dd7e63.png

Нажмём "Patch CC".

ca832c6ec2fedbcfee38bfc824469eb8.png


Все приложения Adobe CC закрываются. Выводится лог патча: какой файл патчится и что в нём ищется/заменяется - это первая/вторая строка после названия файла. Попробуем запустить Adobe CC.

1b29a5b9b554fe4e2d216e90dfcaaac5.png


Появилась возможность скачивать любое приложение, как будто у нас есть подписка. Скачаем и запустим Adobe Audition для теста.

d0e0553df889588f7aa8ad08cc228b9e.png


Нас просят купить подписку. Вернёмся в GenP и выполним поиск.

e3884365af291a0bd732be48aa3fc968.png


Пропатчим. Перед этим желательно закрыть Audition.

d5ccb71afe6ac53a60798f74d4414afd.png


Нам вывелся лог патча. Попробуем запустить снова программу.

a9776047bdf5e4dc893a94a9330b070c.png


Теперь можем спокойно использовать софт. Также есть возможность заблокировать всплывающие окна (pop-up's). Adobe Illustrator также успешно патчится.

2459fab84405ae86af2a92028069ef56.png


Более того, мы можем восстановить оригинальные файлы до патча через кнопку "Restore".

7a87bad0d80b7d59fcbeb9a1256a3657.png


a614c3b25d36111fe5b62e8dc7749dd7.png


4.0 Разбираемся в исходном коде

Код представлен в виде скрипта для AutoIt.

0127ba78236ea77afa4e232bf25e87fd.png


Читается как многие скриптовые языки вроде того же Python.

4.1 Начало скрипта

Первые две строчки кода указывают, что в трее не будет иконки от скрипта и то что он выполняется с правами админа.

Visual Basic:
#NoTrayIcon
#RequireAdmin

Затем идут настройки конкретно для AutoIT.

Visual Basic:
#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Icon=ICONS/Logo.ico
#AutoIt3Wrapper_Outfile_x64=GenP-3.4.14.1.exe
#AutoIt3Wrapper_Res_Comment=GenP v3.4.14.1
#AutoIt3Wrapper_Res_Description=GenP v3.4.14.1
#AutoIt3Wrapper_Res_Fileversion=3.4.14.1
#AutoIt3Wrapper_Res_ProductName=GenP v3.4.14.1
#AutoIt3Wrapper_Res_ProductVersion=3.4.14.1
#AutoIt3Wrapper_Res_CompanyName=GenP
#AutoIt3Wrapper_Res_LegalCopyright=GenP
#AutoIt3Wrapper_Res_LegalTradeMarks=GenP
#AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker
#AutoIt3Wrapper_Run_Au3Stripper=y
#Au3Stripper_Parameters=/pe /sf /sv /rm
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
  • #NoTrayIcon - отключает иконку в системном трее для этого скрипта.
  • #RequireAdmin - требует права администратора для выполнения скрипта.
  • #AutoIt3Wrapper_Icon=ICONS/Logo.ico - задает иконку для исполняемого файла.
  • #AutoIt3Wrapper_Outfile_x64=GenP-3.4.14.1.exe - указывает имя выходного файла для 64-битной системы.
  • #AutoIt3Wrapper_Res_* - различные настройки ресурсов, такие как комментарии, описание, версия файла и продукта, имя компании, авторские права и торговые марки.
  • #AutoIt3Wrapper_Res_requestedExecutionLevel=asInvoker - устанавливает уровень выполнения для исполняемого файла (в данном случае, как вызывающий).
  • #AutoIt3Wrapper_Run_Au3Stripper=y - включает использование Au3Stripper, инструмента для уменьшения размера скрипта.
  • #Au3Stripper_Parameters=/pe /sf /sv /rm - параметры для Au3Stripper, которые включают удаление комментариев, пустых строк и других ненужных элементов.
Затем идут include'ы.

Visual Basic:
#include <ProgressConstants.au3>
#include <WindowsConstants.au3>
#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
#include <GuiListView.au3>
#include <WinAPIProc.au3>
#include <Constants.au3>
#include <String.au3>
#include <WinAPI.au3>
#include <Misc.au3>

Далее небольшой блок кода:

Visual Basic:
AutoItSetOption("GUICloseOnESC", 0)  ;1=ESC closes, 0=ESC won't close
Global Const $g_AppWndTitle = "GenP v3.4.14.1", $g_AppVersion = "Original version by uncia/CGP - GenP Community Edition - v3.4.14.1"

If _Singleton($g_AppWndTitle, 1) = 0 Then
    Exit
EndIf

Первая строка указывает, что при нажатии ESC окно программы не будет закрываться. Вторая строка указывает заголовок приложения и дополнительную информацию о приложении.
Блок if служит для запуска только одного экземпляра приложения.

Затем объявляются глобальные переменные:

Visual Basic:
Global $MyLVGroupIsExpanded = True
Global $fInterrupt = 0
Global $FilesToPatch[0][1], $FilesToPatchNull[0][1]
Global $FilesToRestore[0][1], $fFilesListed = 0
Global $MyhGUI, $hTab, $hMainTab, $hLogTab, $idMsg, $idListview, $g_idListview, $idButtonSearch, $idButtonStop
Global $idButtonCustomFolder, $idBtnCure, $idBtnDeselectAll, $ListViewSelectFlag = 1
Global $idBtnBlockPopUp, $idBtnPatchCC, $idMemo, $timestamp, $idLog, $idBtnRestore, $idBtnCopyLog

Далее идёт проверка наличия config.ini. Если его нет, то он создаётся по указанному пути.

Visual Basic:
Global $sINIPath = @ScriptDir & "\config.ini"
If Not FileExists($sINIPath) Then
    FileInstall("config.ini", @ScriptDir & "\config.ini")
EndIf

Этот конфиг-файл выглядит так:

660dde4571b98ffe808d522a775e36cd.png


Часть контента скрыл, чтобы скрин поместился на экран. Этот файл важен для работы патчера, так как содержит названия файлов, что будет искать патчер, сами патчи и указания, какие конкретно патчи к чему применяются.
  1. Секция [Default]:
    • Содержит ключ Path, который указывает путь по умолчанию до папки с программами Adobe.
  2. Секция [TargetFiles]:
    • Содержит список файлов, которые будет искать патчер.
  3. Секция [DefaultPatterns]:
    • Содержит ключ Values с набором имён применяемых патчей. Используется для тех программ, что нет в [CustomPatterns].
  4. Секция [CustomPatterns]:
    • Содержит ключи для различных файлов и их соответствующие имена патчей.
  5. Секция [Patches]:
    • Содержит ключи (имена патчей) и значения (сами патчи в виде искомых и заменяемых байт).
Затем мы читаем из INI-файла путь до папки с программами Adobe по умолчанию. Если указанного пути нет, то используем путь по умолчанию - это "C:\Program Files\Adobe". Также мы пишем этот путь в INI-файл.

Visual Basic:
Global $MyDefPath = IniRead($sINIPath, "Default", "Path", "C:\Program Files\Adobe")
If Not FileExists($MyDefPath) Or Not StringInStr(FileGetAttrib($MyDefPath), "D") Then
    IniWrite($sINIPath, "Default", "Path", "C:\Program Files\Adobe")
    $MyDefPath = "C:\Program Files\Adobe"
EndIf

Далее идёт работа с Nsudo - инструмент, позволяющий запускать процессы с правами System или TrustedInstaller. По описанию она нужна для корректной работы Adobe XD и приложений UWP.

Вот краткое объяснение функции:

Сначала происходит копирование файла NSudoLG.exe во временную директорию (обычно C:\Users\<Имя_Пользователя>\AppData\Local\Temp). Далее проверка.

Если скрипт не запущен от имени SYSTEM и файл NSudoLG.exe существует:
Выводится диалоговое окно с вопросом о повышении привилегий до TrustedInstaller.​
Если пользователь соглашается, скрипт перезапускается с повышенными привилегиями.​
Если условия не выполнены:
Проверяется существование директории "C:\Windows\System32\config\systemprofile\Desktop". Нужна для нормальной работы приложений, чьи привилегии были повышены.​
Если директория не существует, она создаётся.​
В конце удаление временного файла NSudoLG.exe.

Затем снова объявляются глобальные переменные:

Visual Basic:
Global $MyRegExpGlobalPatternSearchCount = 0, $Count = 0, $idProgressBar
Global $aOutHexGlobalArray[0], $aNullArray[0], $aInHexArray[0]
Global $MyFileToParse = "", $MyFileToParsSweatPea = "", $MyFileToParseEaclient = ""
Global $sz_type, $bFoundAcro32 = False, $bFoundLrARM = False, $bFoundCCARM = False, $bFoundPsARM = False, $bFoundGenericARM = False, $aSpecialFiles, $sSpecialFiles = "|"
Global $ProgressFileCountScale, $FileSearchedCount

Далее идёт код, что читает содержимое секций TargetFiles и CustomPatterns из config.ini.

Visual Basic:
Local $tTargetFileList_Adobe = IniReadSection($sINIPath, "TargetFiles")
Global $TargetFileList_Adobe[0]
If Not @error Then
    ReDim $TargetFileList_Adobe[$tTargetFileList_Adobe[0][0]]
    For $i = 1 To $tTargetFileList_Adobe[0][0]
        $TargetFileList_Adobe[$i - 1] = StringReplace($tTargetFileList_Adobe[$i][1], '"', "")
    Next
EndIf
;_ArrayDisplay($TargetFileList_Adobe, "TargetFileList_Adobe")

$aSpecialFiles = IniReadSection($sINIPath, "CustomPatterns")
;_ArrayDisplay($aSpecialFiles)
For $i = 1 To UBound($aSpecialFiles) - 1
    $sSpecialFiles = $sSpecialFiles & $aSpecialFiles[$i][0] & "|"
Next
;MsgBox(0, "", $sSpecialFiles)

Тут есть даже отладочный код. Можно убрать комментарии и запустить скрипт с ним. Для дальнейшего понимания патчера важно запомнить, что:

$TargetFileList_Adobe - TargetFiles из конфига.
$sSpecialFiles - CustomPatterns из конфига.

fb3de2f9964eedc9f949cda82737ff57.png


0be475b2e398edd21de295380c9fd68b.png


Затем идёт блок кода для настройки GUI-части:

Visual Basic:
GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")

MainGui()

Функция MainGui находится в самом скрипте и создает графический интерфейс для приложения: табы, ListView, кнопки для выбора пути, поиска, патчинга, блокировки всплывающих окон и восстановления файлов, а также прогресс-бар и лог активности.
После этого идёт главный код в цикле While 1.

4.2 Основной цикл патчера

Visual Basic:
$idMsg = GUIGetMsg()

    Select
        Case $idMsg = $GUI_EVENT_CLOSE
            GUIDelete($MyhGUI)
            Exit
        Case $idMsg = $GUI_EVENT_RESIZED
            ContinueCase
        Case $idMsg = $GUI_EVENT_RESTORE
            ContinueCase
        Case $idMsg = $GUI_EVENT_MAXIMIZE
            Local $iWidth
            Local $aGui = WinGetPos($MyhGUI)
            Local $aRect = _GUICtrlListView_GetViewRect($g_idListview)
            If ($aRect[2] > $aGui[2]) Then
                $iWidth = $aGui[2] - 75
            Else
                $iWidth = $aRect[2] - 25
            EndIf
            GUICtrlSendMsg($idListview, $LVM_SETCOLUMNWIDTH, 1, $iWidth)

Этот фрагмент настраивает закрытие окна, изменение его размера и максимизацию. Далее идёт обработка кнопки "Stop" в поиске.

af92e7152ac25c9017c6895427fd17cb.png


Далее в коде будет обработка событий для кнопки "Search".

e0e53195b58560225c7b406b4149a845.png


4.2.1. Обработчик кнопки поиска

В самом начале идёт настройка списка файлов (в ListView) под вывод и появление надписи, что нам нужно подождать:

Visual Basic:
Case $idMsg = $idButtonSearch
            $fInterrupt = 0
            GUICtrlSetState($idButtonSearch, $GUI_HIDE)
            GUICtrlSetState($idButtonStop, $GUI_SHOW)
            ToggleLog(0)
            GUICtrlSetState($idBtnDeselectAll, 128)
            GUICtrlSetState($idBtnBlockPopUp, 128)
            GUICtrlSetState($idListview, 128)
            GUICtrlSetState($idBtnCure, 128)
            GUICtrlSetState($idButtonCustomFolder, 128)
            GUICtrlSetState($idBtnPatchCC, 128)
            ;Search through all files and folders in directory and fill ListView
            _GUICtrlListView_DeleteAllItems($g_idListview)
            _GUICtrlListView_SetExtendedListViewStyle($idListview, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER))
            _GUICtrlListView_AddItem($idListview, "", 0)
            _GUICtrlListView_AddItem($idListview, "", 1)
            _GUICtrlListView_AddItem($idListview, "", 2)
            _GUICtrlListView_AddItem($idListview, "", 2)

            _GUICtrlListView_RemoveAllGroups($idListview)
            _GUICtrlListView_InsertGroup($idListview, -1, 1, "", 1)    ; Group 1
            _GUICtrlListView_SetGroupInfo($idListview, 1, "Info", 1, $LVGS_COLLAPSIBLE)

            _GUICtrlListView_AddSubItem($idListview, 0, "", 1)
            _GUICtrlListView_AddSubItem($idListview, 1, "Preparing...", 1)
            _GUICtrlListView_AddSubItem($idListview, 2, "", 1)
            _GUICtrlListView_AddSubItem($idListview, 3, "Be patient, please.", 1)
            _GUICtrlListView_SetItemGroupID($idListview, 0, 1)
            _GUICtrlListView_SetItemGroupID($idListview, 1, 1)
            _GUICtrlListView_SetItemGroupID($idListview, 2, 1)
            _GUICtrlListView_SetItemGroupID($idListview, 3, 1)

            _Expand_All_Click()
            _GUICtrlListView_SetGroupInfo($idListview, 1, "Info", 1, $LVGS_COLLAPSIBLE)

Функция _Expand_All_Click самописная:

Visual Basic:
Func _Expand_All_Click()
    Local $aInfo, $aCount = _GUICtrlListView_GetGroupCount($idListview) ; Group Count
    If $aCount > 0 Then
        ; Change group information
        For $i = 1 To 28
            $aInfo = _GUICtrlListView_GetGroupInfo($idListview, $i)
            _GUICtrlListView_SetGroupInfo($idListview, $i, $aInfo[0], $aInfo[1], $LVGS_NORMAL)
            _GUICtrlListView_SetGroupInfo($idListview, $i, $aInfo[0], $aInfo[1], $LVGS_COLLAPSIBLE)
        Next
    EndIf

EndFunc   ;==>_Expand_All_Click

Также есть обратная функция:

Visual Basic:
Func _Collapse_All_Click()
    Local $aInfo, $aCount = _GUICtrlListView_GetGroupCount($idListview) ; Group Count
    If $aCount > 0 Then
        If $MyLVGroupIsExpanded = 1 Then
            ; Change group information
            For $i = 1 To 28
                $aInfo = _GUICtrlListView_GetGroupInfo($idListview, $i)
                _GUICtrlListView_SetGroupInfo($idListview, $i, $aInfo[0], $aInfo[1], $LVGS_COLLAPSED)
            Next
        Else
            _Expand_All_Click()
        EndIf
        $MyLVGroupIsExpanded = Not $MyLVGroupIsExpanded
    EndIf

EndFunc   ;==>_Collapse_All_Click

_Expand_All_Click: Раскрывает все группы в ListView, делая их видимыми и сворачиваемыми.
_Collapse_All_Click: Сворачивает все группы, если они были раскрыты, и наоборот, раскрывает все группы, если они были свернуты.

04202a3cf6e4fd524df07e642ac9f61d.png


Далее идут подготовительные блоки:

Visual Basic:
            ; Clear previous results
            $FilesToPatch = $FilesToPatchNull
            $FilesToRestore = $FilesToPatchNull

            $timestamp = TimerInit()

Очищаем предыдущие результаты и настраиваем таймер. Затем идёт блок, что ищет файлы, считает их количество, настраивает прогресс-бар и некоторые переменные.

Visual Basic:
            Local $FileCount
            Local $aSize = DirGetSize($MyDefPath, $DIR_EXTENDED)     ; extended mode
            If UBound($aSize) >= 2 Then
                $FileCount = $aSize[1]
                $ProgressFileCountScale = 100 / $FileCount
                $FileSearchedCount = 0
                ProgressWrite(0)
                RecursiveFileSearch($MyDefPath, 0, $FileCount)   ;Search through all files and folders
                Sleep(100)
                ProgressWrite(0)
            EndIf

Параметр $MyDefPath в этот момент содержит содержимое секции [Default] из INI-файла:

INI:
[Default]
Path=C:\Program Files\Adobe

Функция RecursiveFileSearch самописная:

Visual Basic:
Func RecursiveFileSearch($INSTARTDIR, $DEPTH, $FileCount)
    ;_FileListToArrayEx
    _GUICtrlListView_SetItemText($idListview, 1, "Searching for files.", 1)
    ;_GUICtrlListView_SetItemGroupID($idListview, 0, 1)

    Local $RecursiveFileSearch_MaxDeep = 6
    ; Local $RecursiveFileSearch_WhenFoundRaiseToLevel = 0 ;0 to disable raising
    If $DEPTH > $RecursiveFileSearch_MaxDeep Then Return

    Local $STARTDIR = $INSTARTDIR & "\"
    $FileSearchedCount += 1

    Local $HSEARCH = FileFindFirstFile($STARTDIR & "*.*")
    If @error Then Return

    Local $NEXT, $IPATH, $isDir

    While $fInterrupt = 0

        $NEXT = FileFindNextFile($HSEARCH)
        $FileSearchedCount += 1

        If @error Then ExitLoop
        $isDir = StringInStr(FileGetAttrib($STARTDIR & $NEXT), "D")

        If $isDir Then
            Local $targetDepth
            $targetDepth = RecursiveFileSearch($STARTDIR & $NEXT, $DEPTH + 1, $FileCount)
            ; raise up in recursion to wanted level
;~             if ( $targetDepth > 0 ) and _
;~              ( $targetDepth < $DEPTH ) Then _
;~                 Return $targetDepth
        Else
            $IPATH = $STARTDIR & $NEXT
            Local $FileNameCropped
            If (IsArray($TargetFileList_Adobe)) Then
                For $AdobeFileTarget In $TargetFileList_Adobe
                    $FileNameCropped = StringSplit(StringLower($IPATH), StringLower($AdobeFileTarget), $STR_ENTIRESPLIT)
                    If @error <> 1 Then
                        If Not StringInStr($IPATH, ".bak") Then
                            ;_ArrayAdd( $FilesToPatch, $DEPTH & " - " & $IPATH )
                            If StringInStr($IPATH, "Adobe") Or StringInStr($IPATH, "Acrobat") Then
                                If StringInStr($IPATH, "4.js") And Not StringInStr($IPATH, "UXP\com.adobe.ccx.start\js\4.js") Then
                                    Return
                                EndIf
                                If StringInStr($IPATH, "manifest.json") And Not StringInStr($IPATH, "UXP\com.adobe.ccx.start\manifest.json") Then
                                    Return
                                EndIf
                                _ArrayAdd($FilesToPatch, $IPATH)
                            EndIf
                        Else
                            _ArrayAdd($FilesToRestore, $IPATH)
                        EndIf

                        ; File Found and stored - Quit search in current dir
;~                     return $RecursiveFileSearch_WhenFoundRaiseToLevel
                    EndIf
                Next
            EndIf
        EndIf
    WEnd

    ;Lazy screenupdates
    If 1 = Random(0, 10, 1) Then
        MemoWrite(@CRLF & "Searching in " & $FileCount & " files" & @TAB & @TAB & "Found : " & UBound($FilesToPatch) & @CRLF & _
                "---" & @CRLF & _
                "Level: " & $DEPTH & " Time elapsed : " & Round(TimerDiff($timestamp) / 1000, 0) & " second(s)" & @TAB & @TAB & "Excluded because of *.bak: " & UBound($FilesToRestore) & @CRLF & _
                "---" & @CRLF & _
                $INSTARTDIR _
                )
        ProgressWrite($ProgressFileCountScale * $FileSearchedCount)
    EndIf

    FileClose($HSEARCH)
EndFunc   ;==>RecursiveFileSearch

Она выполняет поиск файлов из $TargetFileList_Adobe (секция [TargetFiles] в INI) и добавляет пути до них в массив $FilesToPatch - файла для патчинга, которые нашлись из [TargetFiles]. Также если где-то нашлись бекапы оригинальных файлов (с постфиксом .bak), то они добавляются в $FilesToRestore.
Для теста можно добавить вывод массива в процессе работы.

e0ee180fe82e77ff941bebe3b0622291.png


Или так:

9cfeb0f68191118050dd6588e7758180.png


Также в логах выводится дополнительная информация.
Далее в обработчике кнопки поиска идёт блок, который также ищет файлы, но в заранее определённых директориях Adobe (по умолчанию). Оба блока нужны, чтобы обработать случаи, когда путь до файлов Adobe стандартный (по умолчанию) и кастомный.

Visual Basic:
            If $MyDefPath = "C:\Program Files" Or $MyDefPath = "C:\Program Files\Adobe" Then
                Local $sProgramFiles = EnvGet('ProgramFiles(x86)') & "\Common Files\Adobe"
                $aSize = DirGetSize($sProgramFiles, $DIR_EXTENDED)     ; extended mode
                If UBound($aSize) >= 2 Then
                    $FileCount = $aSize[1]
                    RecursiveFileSearch($sProgramFiles, 0, $FileCount)   ;Search through all files and folders
                    ProgressWrite(0)
                EndIf
            EndIf

Затем идёт вывод найденных файлов через самописную функцию FillListViewWithFiles:

Visual Basic:
Func FillListViewWithFiles()

    _GUICtrlListView_DeleteAllItems($g_idListview)
    _GUICtrlListView_SetExtendedListViewStyle($idListview, BitOR($LVS_EX_FULLROWSELECT, $LVS_EX_GRIDLINES, $LVS_EX_DOUBLEBUFFER, $LVS_EX_CHECKBOXES))

    ; Two column load
    If UBound($FilesToPatch) > 0 Then
        Global $aItems[UBound($FilesToPatch)][2]
        For $i = 0 To UBound($aItems) - 1
            $aItems[$i][0] = $i
            $aItems[$i][1] = $FilesToPatch[$i][0]

        Next
        _GUICtrlListView_AddArray($idListview, $aItems)

        MemoWrite(@CRLF & UBound($FilesToPatch) & " File(s) were found in " & Round(TimerDiff($timestamp) / 1000, 0) & " second(s) at:" & @CRLF & "---" & @CRLF & $MyDefPath & @CRLF & "---" & @CRLF & "Press the 'Patch Files'")
        LogWrite(1, UBound($FilesToPatch) & " File(s) were found in " & Round(TimerDiff($timestamp) / 1000, 0) & " second(s)" & @CRLF)
        ;_ArrayDisplay($FilesToPatch)
        $fFilesListed = 1
    Else
        MemoWrite(@CRLF & "Nothing was found in" & @CRLF & "---" & @CRLF & $MyDefPath & @CRLF & "---" & @CRLF & "waiting for user action")
        LogWrite(1, "Nothing was found in " & $MyDefPath)
        $fFilesListed = 0
    EndIf

EndFunc   ;==>FillListViewWithFiles

Информация выводится в логах. В конце обработчика кнопки будет настройка GUI-частей.
Также есть такие небольшие функции:

Visual Basic:
; Write a line to the memo control
Func MemoWrite($sMessage)
    GUICtrlSetData($idMemo, $sMessage)
EndFunc   ;==>MemoWrite

Func LogWrite($bTS, $sMessage)
    GUICtrlSetDataEx($idLog, $sMessage, $bTS)
EndFunc   ;==>LogWrite

Func ToggleLog($bShow)
    If $bShow = 1 Then
        GUICtrlSetState($idMemo, $GUI_HIDE)
        GUICtrlSetState($idLog, $GUI_SHOW)
    Else
        GUICtrlSetState($idLog, $GUI_HIDE)
        GUICtrlSetState($idMemo, $GUI_SHOW)
    EndIf
EndFunc   ;==>ToggleLog

Func SendToClipBoard()
    If BitAND(GUICtrlGetState($idMemo), $GUI_HIDE) = $GUI_HIDE Then
        ClipPut(GUICtrlRead($idLog))
    Else
        ClipPut(GUICtrlRead($idMemo))
    EndIf
EndFunc   ;==>SendToClipBoard

Краткое описание этих функций:
  1. MemoWrite записывает строку текста в элемент управления Memo (текстовое поле для многострочного ввода).
  2. LogWrite записывает строку текста в элемент управления Log (журнал) со временной меткой к сообщению или без неё.
  3. ToggleLog переключает видимость между элементами управления Memo и Log.
  4. SendToClipBoard копирует текст из видимого элемента управления (либо Memo, либо Log) в буфер обмена. Если элемент Memo скрыт, копируется текст из Log, иначе копируется текст из Memo.
Тут можно увидеть использование функции GUICtrlSetDataEx. Её код прилагается:

Visual Basic:
Func GUICtrlSetDataEx($hWnd, $sText, $bTS)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    Local $iLength = DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", 0x000E, "wparam", 0, "lparam", 0)
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", 0xB1, "wparam", $iLength[0], "lparam", $iLength[0]) ; $EM_SETSEL
    If $bTS = 1 Then
        Local $iData = @CRLF & @YEAR & "-" & @MON & "-" & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & "." & @MSEC & " " & $sText
    Else
        Local $iData = $sText
    EndIf
    DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $hWnd, "uint", 0xC2, "wparam", True, "wstr", $iData) ; $EM_REPLACESEL
EndFunc   ;==>GUICtrlSetDataEx

Она выполняет запись текста в элемент управления (например, текстовое поле) с возможностью добавления временной метки.

4.2.2. Обработчик кнопки выбора папки

Далее идёт обработчик выбора своей папки с программами Adobe вместо стандартной.

Visual Basic:
Case $idMsg = $idButtonCustomFolder     ; Select Custom Path

            ToggleLog(0)

            MyFileOpenDialog()
            _Expand_All_Click()

            If $fFilesListed = 0 Then
                GUICtrlSetState($idBtnCure, 128)
                GUICtrlSetState($idBtnDeselectAll, 128)
                GUICtrlSetState($idButtonSearch, 64)
                GUICtrlSetState($idButtonSearch, 256)     ; Set focus
            Else
                GUICtrlSetState($idButtonSearch, 128)
                GUICtrlSetState($idBtnDeselectAll, 64)
                GUICtrlSetState($idBtnCure, 64)
                GUICtrlSetState($idBtnCure, 256)     ; Set focus
            EndIf

Функция MyFileOpenDialog самописная.

Visual Basic:
Func MyFileOpenDialog()
    ; Create a constant variable in Local scope of the message to display in FileOpenDialog.
    Local Const $sMessage = "Select a Path"

    ; Display an open dialog to select a file.
    FileSetAttrib("C:\Program Files\WindowsApps", "-H")
    Local $MyTempPath = FileSelectFolder($sMessage, $MyDefPath, 0, $MyDefPath, $MyhGUI)


    If @error Then
        ; Display the error message.
        ;MsgBox($MB_SYSTEMMODAL, "", "No folder was selected.")
        FileSetAttrib("C:\Program Files\WindowsApps", "+H")
        MemoWrite(@CRLF & "Path" & @CRLF & "---" & @CRLF & $MyDefPath & @CRLF & "---" & @CRLF & "waiting for user action")

    Else
        GUICtrlSetState($idBtnCure, 128)
        $MyDefPath = $MyTempPath
        IniWrite($sINIPath, "Default", "Path", $MyDefPath)
        _GUICtrlListView_DeleteAllItems($g_idListview)
        _GUICtrlListView_SetExtendedListViewStyle($idListview, BitOR($LVS_EX_GRIDLINES, $LVS_EX_FULLROWSELECT, $LVS_EX_SUBITEMIMAGES))
        _GUICtrlListView_AddItem($idListview, "", 0)
        _GUICtrlListView_AddItem($idListview, "", 1)
        _GUICtrlListView_AddItem($idListview, "", 2)
        _GUICtrlListView_AddItem($idListview, "", 3)
        _GUICtrlListView_AddItem($idListview, "", 4)
        _GUICtrlListView_AddItem($idListview, "", 5)
        _GUICtrlListView_AddItem($idListview, "", 6)
        _GUICtrlListView_AddSubItem($idListview, 0, "", 1)
        _GUICtrlListView_AddSubItem($idListview, 1, "Path:", 1)
        _GUICtrlListView_AddSubItem($idListview, 2, " " & $MyDefPath, 1)
        _GUICtrlListView_AddSubItem($idListview, 3, "Step 1:", 1)
        _GUICtrlListView_AddSubItem($idListview, 4, " Press 'Search Files' - wait until GenP finds all files", 1)
        _GUICtrlListView_AddSubItem($idListview, 5, "Step 2:", 1)
        _GUICtrlListView_AddSubItem($idListview, 6, " Press 'Patch Files' - wait until GenP will do it's job", 1)
        _GUICtrlListView_SetItemGroupID($idListview, 0, 1)
        _GUICtrlListView_SetItemGroupID($idListview, 1, 1)
        _GUICtrlListView_SetItemGroupID($idListview, 2, 1)
        _GUICtrlListView_SetItemGroupID($idListview, 3, 1)
        _GUICtrlListView_SetItemGroupID($idListview, 4, 1)
        _GUICtrlListView_SetItemGroupID($idListview, 5, 1)
        _GUICtrlListView_SetItemGroupID($idListview, 6, 1)
        _GUICtrlListView_SetGroupInfo($idListview, 1, "Info", 1, $LVGS_COLLAPSIBLE)

        FileSetAttrib("C:\Program Files\WindowsApps", "+H")
        MemoWrite(@CRLF & "Path" & @CRLF & "---" & @CRLF & $MyDefPath & @CRLF & "---" & @CRLF & "Press the Search button")
        ; Display the selected folder.
        ;MsgBox($MB_SYSTEMMODAL, "", "You chose the following folder:" & @CRLF & $MyDefPath)
        GUICtrlSetState($idBtnBlockPopUp, $GUI_SHOW)
        GUICtrlSetState($idBtnRestore, $GUI_HIDE)
        $fFilesListed = 0

    EndIf

EndFunc   ;==>MyFileOpenDialog

Она отображает диалоговое окно для выбора папки и снимает скрытый атрибут с папки C:\Program Files\WindowsApps. Если папка выбрана, обновляет путь, сохраняет его в INI-файле, очищает и обновляет ListView с новыми элементами, восстанавливает скрытый атрибут и выводит сообщение с инструкцией.

4.2.3. Обработчик кнопки выбора/отмены всех элементов

Затем идёт обработчик для выбора или отмены выбора всех элементов:

Visual Basic:
        Case $idMsg = $idBtnDeselectAll     ; Deselect-Select All
            ToggleLog(0)
            If $ListViewSelectFlag = 1 Then
                For $i = 0 To _GUICtrlListView_GetItemCount($idListview) - 1
                    _GUICtrlListView_SetItemChecked($idListview, $i, 0)
                Next
                $ListViewSelectFlag = 0   ; Set Flag to Deselected State
            Else
                For $i = 0 To _GUICtrlListView_GetItemCount($idListview) - 1
                    _GUICtrlListView_SetItemChecked($idListview, $i, 1)
                Next
                $ListViewSelectFlag = 1   ; Set Flag to Selected State
            EndIf

c12fdb69b20b50398f6d26de0d9d46c1.png


4.2.4. Обработчик кнопки Pop-up

Следующий обработчик для кнопки "Pop-up":

Visual Basic:
        Case $idMsg = $idBtnBlockPopUp     ; Pop-up button
            ToggleLog(0)
            BlockPopUp()

Функцию BlockPopUp нам нужно разобрать.

Visual Basic:
Func BlockPopUp()
    GUICtrlSetState($hLogTab, $GUI_SHOW)
    GUICtrlSetState($idBtnBlockPopUp, 128)
    MemoWrite(@CRLF & "Checking for an active internet connection..." & @CRLF & "" & @CRLF & "")
    Local $sCmdInfo = """C:\Windows\System32\WindowsPowerShell\v1.0\PowerShell.exe"" -Command ""Test-Connection 8.8.8.8 -Count 1 -Quiet"""
    Local $iPID = Run($sCmdInfo, "", @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
    Local $sOutput = ""
    While 1
        $sOutput &= StdoutRead($iPID)
        If @error Then ExitLoop
    WEnd
    ProcessWaitClose($iPID)
    If StringReplace($sOutput, @CRLF, "") = "True" Then

Тут происходит настройка GUI и проверка наличия интернет-соединения через Powershell-команду:

Bash:
Test-Connection 8.8.8.8 -Count 1 -Quiet

Она вернёт нам True, если интернет есть.
Затем идёт блок кода для разрешения IP-адресов доменов adobe.io и 3u6k9as4bj.adobestats.io.

Visual Basic:
    If StringReplace($sOutput, @CRLF, "") = "True" Then
        MemoWrite(@CRLF & "Resolving ip-addresses..." & @CRLF & "" & @CRLF & "")
        $sCmdInfo = ; Powershell-скрипт
        $iPID = Run($sCmdInfo, "", @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
        $sOutput = ""
        While 1
            $sOutput &= StdoutRead($iPID)
            If @error Then ExitLoop
        WEnd
        ProcessWaitClose($iPID)
        If StringInStr($sOutput, "False") Then
            MemoWrite(@CRLF & "Failed to resolve ip-addresses, try using a VPN..." & @CRLF & "" & @CRLF & "")
            Sleep(2000)
        Else
        ...

Разрешение IP-адресов (или "резолвинг" IP-адресов) — это процесс преобразования доменного имени (например, example.com) в его соответствующий IP-адрес (например, 101.34.200.8).
В функции выполняется PowerShell-скрипт для разрешения IP-адресов доменов adobe.io и 3u6k9as4bj.adobestats.io:
  • Читается вывод скрипта в переменную $sOutput.
  • Если в выводе содержится "False", записывает сообщение о неудаче в журнал и предлагает использовать VPN.
  • Иначе добавляется правило в брандмауэр Windows для блокировки IP-адресов. Это разберём далее.
Пока перейдём к самому скрипту. Изначально он был в однострочном виде, но для нашего удобства представим его так:

Bash:
$currentDate = Get-Date
$ipAddresses = @()

try {
    $SOA = (Resolve-DnsName -Name adobe.io -Type SOA -ErrorAction Stop).PrimaryServer
} catch {
    $SOA = $null
}

if ($SOA) {
    Do {
        if ((New-TimeSpan -Start $currentDate -End (Get-Date)).TotalSeconds -gt 5) {
            if ($ipAddresses.Count -eq 0) {
                $ipAddresses += 'False'
            }
            break
        }
 
        try {
            $ipAddress = (Resolve-DnsName -Name adobe.io -Server $SOA -ErrorAction Stop).IPAddress
        } catch {
            $ipAddress = $null
        }
 
        if ($ipAddress) {
            $ipAddresses += $ipAddress
        }
 
        $ipAddresses = $ipAddresses | Select -Unique | Sort-Object
    } While ($ipAddresses.Count -lt 8)
} else {
    $ipAddresses += 'False'
}

Do {
    if ((New-TimeSpan -Start $currentDate -End (Get-Date)).TotalSeconds -gt 5 -or $ipAddresses[0] -eq 'False') {
        break
    }
 
    try {
        $ipAddress = (Resolve-DnsName -Name 3u6k9as4bj.adobestats.io -ErrorAction Stop).IPAddress
    } catch {
        $ipAddress = $null
    }
 
    if ($ipAddress) {
        $ipAddresses += $ipAddress
    }
 
    $ipAddresses = $ipAddresses | Select -Unique | Sort-Object
} While ($ipAddresses.Count -lt 12 -and $ipAddresses[0] -ne 'False')

$ipAddresses = $ipAddresses -ne 'False' | Select -Unique | Sort-Object
$ipAddressList = if ($ipAddresses.Count -eq 0) { 'False' } else { $ipAddresses -join ',' }

$ipAddressList

Скрипт определяет авторитетный сервер (SOA) для домена adobe.io. Затем собирает уникальные IP-адреса для доменов adobe.io и 3u6k9as4bj.adobestats.io.
В итоге мы получим список уникальных IP-адресов такого вида:

Код:
107.22.247.231,18.207.85.246,23.22.254.206...

Затем идёт блок Else, что добавляет правило в брандмауэр Windows для блокировки IP-адресов:

Visual Basic:
        If StringInStr($sOutput, "False") Then
            MemoWrite(@CRLF & "Failed to resolve ip-addresses, try using a VPN..." & @CRLF & "" & @CRLF & "")
            Sleep(2000)
        Else
            MemoWrite(@CRLF & "Adding Windows Firewall rule..." & @CRLF & "" & @CRLF & "")
            $sCmdInfo = "netsh advfirewall firewall delete rule name=""Adobe Unlicensed Pop-up"""
            $iPID = Run($sCmdInfo, "", @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
            ProcessWaitClose($iPID)
            $sCmdInfo = "netsh advfirewall firewall add rule name=""Adobe Unlicensed Pop-up"" dir=out action=block remoteip=""" & StringReplace($sOutput, @CRLF, "") & """"
            $iPID = Run($sCmdInfo, "", @SW_HIDE, BitOR($STDERR_CHILD, $STDOUT_CHILD))
            ProcessWaitClose($iPID)
        EndIf

Данный код удаляет старое правило в брандмауэре, если оно было:

Bash:
netsh advfirewall firewall delete rule name="Adobe Unlicensed Pop-up"

Затем уже добавляет своё:

Bash:
netsh advfirewall firewall add rule name="Adobe Unlicensed Pop-up" dir=out action=block remoteip="..."

Вместо "..." подставятся IP из прошлого ps-скрипта. Получим команду подобного вида:

Bash:
netsh advfirewall firewall add rule name="Adobe Unlicensed Pop-up" dir=out action=block
remoteip="107.22.247.231,18.207.85.246,23.22.254.206..."

Далее идёт блок кода, что работает с файлом hosts.

Visual Basic:
MemoWrite(@CRLF & "Blocking Hosts..." & @CRLF & "" & @CRLF & "")
        $sCmdInfo = ; Powershell-скрипт
        $iPID = Run($sCmdInfo, "", @SW_HIDE, $STDOUT_CHILD)
        $sOutput = ""
        While 1
            $sLine = StdoutRead($iPID)
            If @error Then ExitLoop
            $sOutput &= $sLine
        WEnd
        If StringInStr($sOutput, "Script execution complete.") Then
            MemoWrite(@CRLF & "All Hosts blocked." & @CRLF & "" & @CRLF & "")
        Else
            MemoWrite(@CRLF & "Failed to block Hosts, try using a VPN..." & @CRLF & "" & @CRLF & "")
        EndIf

Тут снова используется Powershell-скрипт:

Bash:
try {
    if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        Write-Host 'Script execution failed...'
        return
    }

    $hostsPath = 'C:\Windows\System32\drivers\etc\hosts'
    $bakPath = $hostsPath + '.bak'

    if (-not (Test-Path $bakPath)) {
        Copy-Item -Path $hostsPath -Destination $bakPath
    }

    $webContent = (Invoke-RestMethod -Uri 'https://a.dove.isdumb.one/list.txt' -UseBasicParsing).Split($([char]0x0A)) | ForEach-Object { $_.Trim() }
    $currentHostsContent = Get-Content -Path $hostsPath
    $startMarker = '# <GenP>'
    $endMarker = '# </GenP>'
    $blockStart = $currentHostsContent.IndexOf($startMarker)
    $blockEnd = $currentHostsContent.IndexOf($endMarker)

    if ($blockStart -eq -1 -or $blockEnd -eq -1) {
        $currentHostsContent += $startMarker
        $currentHostsContent += $endMarker
        $blockStart = $currentHostsContent.IndexOf($startMarker)
        $blockEnd = $currentHostsContent.IndexOf($endMarker)
    }

    $newBlock = @($startMarker) + $webContent + $endMarker
    $newHostsContent = $currentHostsContent[0..($blockStart - 1)] + $newBlock + $currentHostsContent[($blockEnd + 1)..$currentHostsContent.Length]

    Set-Content -Path $hostsPath -Value $newHostsContent
    Write-Host 'Script execution complete.'
} catch {
    Write-Host 'Script execution failed...'
}

Данный скрипт выполняет предварительные действия:
  1. Проверка наличие прав администратора. Если нет, то завершает выполнение.
  2. Создание резервной копии файла hosts: C:\Windows\System32\drivers\etc\hosts.bak
  3. Загрузка файла https://a.dove.isdumb.one/list.txt и разделяет загруженный контент по строкам и удаляет пробелы в начале и конце каждой строки.
Файл list.txt можно взять из этого репозитория: GitHub - ignaciocastro/a-dove-is-dumb: Easily block Adobe telemetry checking domains. Continuously Updated. Useful for HostsMan / SwitchHosts / Pi-hole users ✨
А далее происходит работа с файлом hosts. Мы записываем содержимое list.txt между строками <GenP>.

До выполнения скрипта:

Код:
127.0.0.1 localhost

После:

Код:
127.0.0.1 localhost
# <GenP>
# Данные из list.txt
# </GenP>

3ef8747ef7ba6bbe8dc21ec122f456b7.png


Таким образом, функция BlockPopUp просто добавляет правила в брандмауэр и записи в файл hosts. Теперь настало время самого интересного - начинаем разбор патчинга Creative Cloud и программ Adobe.

2d0df42a309ed28bee9a6d54faad8675.png


4.2.5. Обработчик кнопки "Patch CC" и как работает эта магия

9b5e4a9dc7c76d67d8016a058cf3d59c.png


Обработкой кнопки "Patch CC" занимается вот этот обработчик:

Visual Basic:
Case $idMsg = $idBtnPatchCC     ; Patch Creative Cloud button
            Global $appsPanelFile
            Global $containerBLFile
            Global $adobeDesktopServiceFile
            ...

Первое, что патчит код - это AppsPanelBL.dll.

Visual Basic:
If FileExists("C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll") Then
                $appsPanelFile = "C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll"
            ElseIf FileExists("C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll") Then
                $appsPanelFile = "C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll"
            Else
                $appsPanelFile = FileOpenDialog("Select a File", @ScriptDir, "AppsPanelBL.dll (AppsPanelBL.dll)")
            EndIf
            ProgressWrite(0)
            If FileExists($appsPanelFile) Then
                MyGlobalPatternSearch($appsPanelFile)
                Sleep(100)
                MemoWrite(@CRLF & "File Path:" & @CRLF & "" & @CRLF & $appsPanelFile & @CRLF & "" & @CRLF & "")
                Sleep(100)
                MyGlobalPatternPatch($appsPanelFile, $aOutHexGlobalArray)
                Sleep(500)
            EndIf

Выполняется проверка на существование файла. А затем в нём ищутся паттерны, которые будут взяты из INI-файла через MyGlobalPatternSearch.

Visual Basic:
Func MyGlobalPatternSearch($MyFileToParse)
    ;ConsoleWrite($MyFileToParse & @CRLF)
    $aInHexArray = $aNullArray   ; Nullifay Array that will contain Hex later
    $aOutHexGlobalArray = $aNullArray     ; Nullifay Array that will contain Hex later

    ProgressWrite(0)
    $MyRegExpGlobalPatternSearchCount = 0
    $Count = 15

    Local $sFileName = StringRegExpReplace($MyFileToParse, "^.*\\", "")
    Local $sExt = StringRegExpReplace($sFileName, "^.*\.", "")

    MemoWrite(@CRLF & $MyFileToParse & @CRLF & "---" & @CRLF & "Preparing to Analyze" & @CRLF & "---" & @CRLF & "*****")
    LogWrite(1, "Checking File: " & $sFileName & " ")
    ;MsgBox($MB_SYSTEMMODAL,"","$sFileName = " & $sFileName & @CRLF & "$sExt = " & $sExt)

    If $sExt = "exe" Then
        _ProcessCloseEx("""" & $sFileName & """")
    EndIf

    If $sFileName = "AppsPanelBL.dll" Or $sFileName = "ContainerBL.dll" Or $sFileName = "Adobe Desktop Service.exe" Then
        _ProcessCloseEx("""Creative Cloud.exe""")
        _ProcessCloseEx("""Adobe Desktop Service.exe""")
        Sleep(100)
    EndIf

    If StringInStr($sSpecialFiles, $sFileName) Then
        ;MsgBox($MB_SYSTEMMODAL, "", "Special File: " & $sFileName)
        LogWrite(0, " - using Custom Patterns")
        ExecuteSearchPatterns($sFileName, 0, $MyFileToParse)
    Else
        LogWrite(0, " - using Default Patterns")
        ExecuteSearchPatterns($sFileName, 1, $MyFileToParse)
        ;MsgBox($MB_SYSTEMMODAL, "", "File: " & $sFileName & @CRLF & "Not in Special Files")
    EndIf
    Sleep(100)
EndFunc   ;==>MyGlobalPatternSearch

Функции передаётся путь до файла. Затем из него извлекается:
  1. Имя файла вместе с расширением;
  2. Отдельно расширение файла.
Visual Basic:
    If $sExt = "exe" Then
        _ProcessCloseEx("""" & $sFileName & """")
    EndIf

    If $sFileName = "AppsPanelBL.dll" Or $sFileName = "ContainerBL.dll" Or $sFileName = "Adobe Desktop Service.exe" Then
        _ProcessCloseEx("""Creative Cloud.exe""")
        _ProcessCloseEx("""Adobe Desktop Service.exe""")
        Sleep(100)
    EndIf

Затем закрываются процессы Creative Cloud, чтобы успешно пропатчить нужный файл. В конце функции вызывается ExecuteSearchPatterns в зависимости от того, есть ли искомый файл в $sSpecialFiles - это секция [CustomPatterns] в INI-файле.

Для дальнейшего понимания патчера важно запомнить, что:
$TargetFileList_Adobe - TargetFiles из конфига.
$sSpecialFiles - CustomPatterns из конфига.

Если такой файл есть, то вызывается ExecuteSearchPatterns($sFileName, 0, $MyFileToParse).
Иначе - ExecuteSearchPatterns($sFileName, 1, $MyFileToParse).

Сейчас как раз разберём её.

Visual Basic:
Func ExecuteSearchPatterns($FileName, $DefaultPatterns, $MyFileToParse)

    Local $aPatterns, $sPattern, $sData, $aArray, $sSearch, $sReplace, $iPatternLength

    If $DefaultPatterns = 0 Then
        $aPatterns = IniReadArray($sINIPath, "CustomPatterns", $FileName, "")
    Else
        $aPatterns = IniReadArray($sINIPath, "DefaultPatterns", "Values", "")
    EndIf

    ;_ArrayDisplay($aPatterns, "Patterns for " & $FileName)

    For $i = 0 To UBound($aPatterns) - 1
        $sPattern = $aPatterns[$i]
        $sData = IniRead($sINIPath, "Patches", $sPattern, "")

        If StringInStr($sData, "|") Then
            $aArray = StringSplit($sData, "|")
            If UBound($aArray) = 3 Then

                $sSearch = StringReplace($aArray[1], '"', '')
                $sReplace = StringReplace($aArray[2], '"', '')

                $iPatternLength = StringLen($sSearch)
                If $iPatternLength <> StringLen($sReplace) Or Mod($iPatternLength, 2) <> 0 Then
                    MsgBox($MB_SYSTEMMODAL, "Error", "Pattern Error in config.ini:" & $sPattern & @CRLF & $sSearch & @CRLF & $sReplace)
                    Exit
                EndIf

                ;MsgBox(0,0, $MyFileToParse & @CRLF & $sSearch & @CRLF  & $aReplace & @CRLF  & $sPattern )
                LogWrite(1, "Searching for: " & $sPattern & ": " & $sSearch)

                MyRegExpGlobalPatternSearch($MyFileToParse, $sSearch, $sReplace, $sPattern)

                ;Exit ; STOP AT FIRST VALUE - COMMENT OUT TO CONTINUE
            EndIf
            ;Exit
        EndIf

    Next

EndFunc   ;==>ExecuteSearchPatterns

$FileName - имя файла
$DefaultPatterns - 1 или 0 (True или False)
$MyFileToParse - путь до файла

Если передаётся 0, то используется [CustomPatterns]. Иначе - [DefaultPatterns]. Затем начинается основной цикл, где происходит обработка данных из INI-файла и передача в MyRegExpGlobalPatternSearch.

4.2.5.1. Весь цикл поиска и патчинга кратко

Рассмотрим работу всего цикла поиска и патчинга, забегая немного вперёд. Посмотрим на INI-файл:

INI:
...
[DefaultPatterns]
Values="ProfileExpired1","ProfileExpired3","ProfileExpired4","ProfileExpired5","ProfileExpired6","ValidateLicense1","ValidateLicense2","ValidateLicense3","CmpEax61","CmpEax62","CmpEax63","CmpEax64","Profile1","Profile2","Banner1","Banner2","InstantShutdown1"

[CustomPatterns]
Acrobat.dll="Acrobat3","Acrobat5"
acrodistdll.dll="Acrodist2","Acrodist3","Acrodist4","Acrodist5","AcroRegistry1","AcroNew1"
acrotray.exe="AcroTray2","AcroTray3","AcroTray4","AcroTray5","AcroRegistry1","AcroNew1"
AppsPanelBL.dll="CreativeCloud1","CreativeCloud2"
ContainerBL.dll="CreativeCloud3"
...

[Patches]
CreativeCloud1="8378????0F84????????8378????0F84????????8378????0F84????????33C0"|"C640????0F84????????C640????0F84????????C640????0F84????????33C0"
CreativeCloud2="E8????????85C00F85????????83EC??8BCC89"|"E8????????FEC00F85????????83EC??8BCC89"
CreativeCloud3="??????0F85890300"|"??????E98A030000"
Acrobat1="488BCFE8????????85C00F84????????488D??????????488BCFE8????????85C075??8D"|"488BCFE8????????FFC00F84????????488D??????????488BCFE8????????31C075??8D"
Acrobat2="6685C0740FE8????????6685C07405BB01000000"|"6685C07400E8????????6685C07400BB01000000"
Acrobat3="6685C0741A6685??0F85??020000"|"6685C090906685??0F85??020000"
...

В секции [CustomPatterns] есть ключи, имена которых - это имена файлов для патчинга. У имён-файлов (ключей) есть значения. Значения используются в секции [Patches], которая содержит байты для поиска и патчинга.
Например, если патчится файл AppsPanelBL.dll, то используются патчи "CreativeCloud1","CreativeCloud2" (из-за прямого указания в строке AppsPanelBL.dll="CreativeCloud1","CreativeCloud2"):

В секции патчей это будет:

INI:
CreativeCloud1="8378????0F84????????8378????0F84????????8378????0F84????????33C0"|"C640????0F84????????C640????0F84????????C640????0F84????????33C0"
CreativeCloud2="E8????????85C00F85????????83EC??8BCC89"|"E8????????FEC00F85????????83EC??8BCC89"

Байты для поиска и замены разделены символов |. Для CreativeCloud1 будут искаться байты "8378????0F84????????8378????0F84????????8378????0F84????????33C0", а заменой им будет "C640????0F84????????C640????0F84????????C640????0F84????????33C0".
Затем поиск и замена будут выполнены для CreativeCloud2. До знаков вопроса в патчах мы сейчас дойдём. Для понимания нам нужно изучить MyRegExpGlobalPatternSearch. Вот так она вызывается в MyRegExpGlobalPatternSearch:

Visual Basic:
MyRegExpGlobalPatternSearch($MyFileToParse, $sSearch, $sReplace, $sPattern)

$MyFileToParse - путь до файла
$sSearch - байты, которые ищутся (до символа | в секции с патчами)
$sReplace - байты, на которые произойдёт замена (после символа | в секции с патчами)
$sPattern - значение ключа из набора в секции [CustomPatterns] (CreativeCloud1, CreativeCloud2 и другие).

Функция MyRegExpGlobalPatternSearch:

Visual Basic:
Func MyRegExpGlobalPatternSearch($FileToParse, $PatternToSearch, $PatternToReplace, $PatternName)  ; Path to a file to parse
    ;MsgBox($MB_SYSTEMMODAL, "Path", $FileToParse)
    ;ConsoleWrite($FileToParse & @CRLF)
    Local $hFileOpen = FileOpen($FileToParse, $FO_READ + $FO_BINARY)

    FileSetPos($hFileOpen, 60, 0)

    $sz_type = FileRead($hFileOpen, 4)
    FileSetPos($hFileOpen, Number($sz_type) + 4, 0)

    $sz_type = FileRead($hFileOpen, 2)

    If $sz_type = "0x4C01" And StringInStr($FileToParse, "Acrobat", 2) > 0 Then ; Acrobat x86 won't work with this script

        MemoWrite(@CRLF & $FileToParse & @CRLF & "---" & @CRLF & "File is 32bit. Aborting..." & @CRLF & "---")
        FileClose($hFileOpen)
        Sleep(100)
        $bFoundAcro32 = True

    ElseIf $sz_type = "0x64AA" Then ; AArch64 (ARM64) and ~~AArch32 (ARM32) architectures~~ (big-endian). only exist as photoshop, lightroom, and ccdesktop at time of writing

        If StringInStr($FileToParse, "Lightroom", 2) > 0 Then ; Lightroom ARM
            MemoWrite(@CRLF & $FileToParse & @CRLF & "---" & @CRLF & "Lightroom is ARM. Aborting..." & @CRLF & "---")
            FileClose($hFileOpen)
            Sleep(100)
            $bFoundLrARM = True

        ElseIf StringInStr($FileToParse, "AppsPanelBL.dll", 2) Or StringInStr($FileToParse, "ContainerBL.dll", 2) Or StringInStr($FileToParse, "Adobe Desktop Service.exe", 2) > 0 Then ; CC Desktop ARM
            MemoWrite(@CRLF & $FileToParse & @CRLF & "---" & @CRLF & "Creative Cloud is ARM. Aborting..." & @CRLF & "---")
            FileClose($hFileOpen)
            Sleep(100)
            $bFoundCCARM = True

        ElseIf StringInStr($FileToParse, "Photoshop", 2) > 0 Then ; Photoshop ARM
            MemoWrite(@CRLF & $FileToParse & @CRLF & "---" & @CRLF & "Photoshop is ARM. Aborting..." & @CRLF & "---")
            FileClose($hFileOpen)
            Sleep(100)
            $bFoundPsARM = True

        Else ; Other ARM
            MemoWrite(@CRLF & $FileToParse & @CRLF & "---" & @CRLF & "File is ARM. Aborting..." & @CRLF & "---")
            FileClose($hFileOpen)
            Sleep(100)
            $bFoundGenericARM = True

        EndIf

    Else

        FileSetPos($hFileOpen, 0, 0)

        Local $sFileRead = FileRead($hFileOpen)

        Local $GeneQuestionMark, $AnyNumOfBytes, $OutStringForRegExp
        For $i = 256 To 1 Step -2 ; limiting to 256 -?-
            $GeneQuestionMark = _StringRepeat("??", $i / 2) ; Repeat the string -??- $i/2 times.
            $AnyNumOfBytes = "(.{" & $i & "})"
            $OutStringForRegExp = StringReplace($PatternToSearch, $GeneQuestionMark, $AnyNumOfBytes)
            $PatternToSearch = $OutStringForRegExp
        Next

        Local $sSearchPattern = $OutStringForRegExp     ;string
        Local $aReplacePattern = $PatternToReplace     ;string
        Local $sWildcardSearchPattern = "", $sWildcardReplacePattern = "", $sFinalReplacePattern = ""
        Local $aInHexTempArray[0]
        Local $sSearchCharacter = "", $sReplaceCharacter = ""

        $aInHexTempArray = $aNullArray
        $aInHexTempArray = StringRegExp($sFileRead, $sSearchPattern, $STR_REGEXPARRAYGLOBALFULLMATCH, 1)

        For $i = 0 To UBound($aInHexTempArray) - 1

            $aInHexArray = $aNullArray
            $sSearchCharacter = ""
            $sReplaceCharacter = ""
            $sWildcardSearchPattern = ""
            $sWildcardReplacePattern = ""
            $sFinalReplacePattern = ""


            $aInHexArray = $aInHexTempArray[$i]
            ;_ArrayDisplay($aInHexArray)

            If @error = 0 Then
                $sWildcardSearchPattern = $aInHexArray[0]   ; full founded Search Pattern index 0
                $sWildcardReplacePattern = $aReplacePattern

                ;MsgBox(-1,"",$sWildcardSearchPattern & @CRLF & $sWildcardReplacePattern) ; full search and full patch with ?? symbols

                If StringInStr($sWildcardReplacePattern, "?") Then
                    ;MsgBox($MB_SYSTEMMODAL, "Found ? symbol", "Constructing new Replace string")
                    For $j = 1 To StringLen($sWildcardReplacePattern) + 1
                        ; Retrieve a characters from the $jth position in each string.
                        $sSearchCharacter = StringMid($sWildcardSearchPattern, $j, 1)
                        $sReplaceCharacter = StringMid($sWildcardReplacePattern, $j, 1)

                        If $sReplaceCharacter <> "?" Then
                            $sFinalReplacePattern &= $sReplaceCharacter
                        Else
                            $sFinalReplacePattern &= $sSearchCharacter
                        EndIf

                    Next
                Else
                    $sFinalReplacePattern = $sWildcardReplacePattern
                EndIf

                _ArrayAdd($aOutHexGlobalArray, $sWildcardSearchPattern)
                _ArrayAdd($aOutHexGlobalArray, $sFinalReplacePattern)

                ConsoleWrite($PatternName & "---" & @TAB & $sWildcardSearchPattern & "    " & @CRLF)
                ConsoleWrite($PatternName & "R" & "--" & @TAB & $sFinalReplacePattern & "    " & @CRLF)
                MemoWrite(@CRLF & $FileToParse & @CRLF & "---" & @CRLF & $PatternName & @CRLF & "---" & @CRLF & $sWildcardSearchPattern & @CRLF & $sFinalReplacePattern)
                LogWrite(1, "Replacing with: " & $sFinalReplacePattern)

            Else
                ConsoleWrite($PatternName & "---" & @TAB & "No" & "    " & @CRLF)
                MemoWrite(@CRLF & $FileToParse & @CRLF & "---" & @CRLF & $PatternName & "---" & "No")
            EndIf
            $MyRegExpGlobalPatternSearchCount += 1

        Next
        FileClose($hFileOpen)
        $sFileRead = ""
        ProgressWrite(Round($MyRegExpGlobalPatternSearchCount / $Count * 100))
        Sleep(100)

    EndIf      ;==>If $sz_type = "0x4C01"

EndFunc   ;==>MyRegExpGlobalPatternSearch

Первое, что проверяет функция - патчим ли мы Adobe Acrobat с архитектурой x32. Если да, то функция завершается, так как скрипт не может обработать такой случай. Далее проверяется, используем ли мы файл с архитектурой ARM. Если да, то снова завершение функции. GenP рассчитан в основном на x64.

Далее функция читает файл, путь до которого был ей передан, и находит точные байты в нём (без знаков ?), добавляя их в массив $aOutHexGlobalArray:

Visual Basic:
                _ArrayAdd($aOutHexGlobalArray, $sWildcardSearchPattern)
                _ArrayAdd($aOutHexGlobalArray, $sFinalReplacePattern)

В первый элемент массива будут добавлены точные байты, которые были найдены в файле.
Во второй элемент массива будут добавлены точные байты, на которые произойдёт замена.

Функция заменяет знаки ?? на байты из самого файла, чтобы получились "точные байты". Забегая вперёд, потом благодаря массиву $aOutHexGlobalArray функция MyGlobalPatternPatch просто пропатчит точные искомые байты на точные заменяемые. Но перед этим создаст резервную копию.

Рассмотрим схематично замену байтов в файле. Значения CreativeCloud1 для файла AppsPanelBL.dll:

INI:
CreativeCloud1="8378????0F84????????8378????0F84????????8378????0F84????????33C0"|"C640????0F84????????C640????0F84????????C640????0F84????????33C0"

Более понятная запись:

INI:
"8378????0F84????????8378????0F84????????8378????0F84????????33C0" ; байты, которые ищем
"C640????0F84????????C640????0F84????????C640????0F84????????33C0" ; байты, на которые заменяем

2 знака вопроса выступают в роли одного любого байта, который не будет изменён. Или по-другому это часть регулярного выражения. Отсюда и Reg в названии функции.

INI:
"83 78 ?? ?? 0F 84 ?? ?? ?? ?? 83 78 ?? ?? 0F 84 ..." ; байты, которые ищем
"C6 40 ?? ?? 0F 84 ?? ?? ?? ?? C6 40 ?? ?? 0F 84..." ; байты, на которые заменяем

Вот так записано более понятно. Функция MyRegExpGlobalPatternSearch сделает из вопросов точные байты:

INI:
"83 78 FF FF 0F 84 FF FF FF FF 83 78 FF FF 0F 84 ..." ; байты, которые ищем
"C6 40 FF FF 0F 84 FF FF FF FF C6 40 FF FF 0F 84..." ; байты, на которые заменяем

Для примера представим, что в самом файле на месте вопросов были байты 0xFF.
Байт 0x83 заменится на 0xC6,
Байт 0x78 заменится на 0x40,
Байт 0xFF заменится на 0xFF,
Байт 0xFF заменится на 0xFF,
Байт 0x0F заменится на 0x0F,
Байт 0x84 заменится на 0x84,
и так далее

c96f6b4582fac9b597867579a4df1d19.png


4.2.5.2. Вернёмся к обработчику кнопки "Patch CC"

Теперь снова вернёмся к обработчику нажатия кнопки "Patch CC".

Visual Basic:
If FileExists("C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll") Then
                $appsPanelFile = "C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll"
            ElseIf FileExists("C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll") Then
                $appsPanelFile = "C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll"
            Else
                $appsPanelFile = FileOpenDialog("Select a File", @ScriptDir, "AppsPanelBL.dll (AppsPanelBL.dll)")
            EndIf
            ProgressWrite(0)
            If FileExists($appsPanelFile) Then
                MyGlobalPatternSearch($appsPanelFile)
                Sleep(100)
                MemoWrite(@CRLF & "File Path:" & @CRLF & "" & @CRLF & $appsPanelFile & @CRLF & "" & @CRLF & "")
                Sleep(100)
                MyGlobalPatternPatch($appsPanelFile, $aOutHexGlobalArray)
                Sleep(500)
            EndIf

После того как вызов MyGlobalPatternSearch пропатчит файл, запустится функция MyGlobalPatternPatch.

Visual Basic:
Func MyGlobalPatternPatch($MyFileToPatch, $MyArrayToPatch)
    ;MsgBox($MB_SYSTEMMODAL, "", $MyFileToPatch)
    ;_ArrayDisplay($MyArrayToPatch)
    ProgressWrite(0)
    ;MemoWrite("Current path" & @CRLF & "---" & @CRLF & $MyFileToPatch & @CRLF & "---" & @CRLF & "medication :)")
    Local $iRows = UBound($MyArrayToPatch) ; Total number of rows

    MsgBox(0, "q", $MyArrayToPatch)
    MsgBox(0, "q", $iRows)

    If $iRows > 0 Then
        MemoWrite(@CRLF & "Path" & @CRLF & "---" & @CRLF & $MyFileToPatch & @CRLF & "---" & @CRLF & "medication :)")
        Local $hFileOpen = FileOpen($MyFileToPatch, $FO_READ + $FO_BINARY)
        Local $sFileRead = FileRead($hFileOpen)
        Local $sStringOut

        For $i = 0 To $iRows - 1 Step 2
                MsgBox(0, "q", $MyArrayToPatch[$i])
                MsgBox(0, "q", $MyArrayToPatch[$i+1])
            $sStringOut = StringReplace($sFileRead, $MyArrayToPatch[$i], $MyArrayToPatch[$i + 1], 0, 1)
            $sFileRead = $sStringOut
            $sStringOut = $sFileRead
            ProgressWrite(Round($i / $iRows * 100))
        Next

        ;MsgBox($MB_SYSTEMMODAL, "", "binary: " & Binary($sStringOut))
        FileClose($hFileOpen)
        FileMove($MyFileToPatch, $MyFileToPatch & ".bak", $FC_OVERWRITE)
        Local $hFileOpen1 = FileOpen($MyFileToPatch, $FO_OVERWRITE + $FO_BINARY)
        FileWrite($hFileOpen1, Binary($sStringOut))
        FileClose($hFileOpen1)
        ProgressWrite(0)
        Sleep(100)
        ;MemoWrite1(@CRLF & "---" & @CRLF & "Waitng for your command :)" & @CRLF & "---")

        LogWrite(1, "File patched." & @CRLF)

    Else
        ;Empty array - > no search-replace patterns
        ;File is already patched or no patterns were found .
        MemoWrite(@CRLF & "No patterns were found" & @CRLF & "---" & @CRLF & "or" & @CRLF & "---" & @CRLF & "file is already patched.")
        Sleep(100)

        LogWrite(1, "No patterns were found or file already patched." & @CRLF)

    EndIf
    ;Sleep(100)
    ;MemoWrite2("***")
EndFunc   ;==>MyGlobalPatternPatch

Данная функция открывает файл и благодаря глобальному массиву $aOutHexGlobalArray ищет точные байты в файле и заменяет их на точные нужные. Передачу этого массива, как аргумента, можно найти в обработчике. Перед записью изменения создаётся бекап файла с расширением .bak, чтобы в случае чего его можно было бы восстановить. Таким образом, MyGlobalPatternSearch и все функции, что вызываются внутри неё, ищут точные байты в файле для замены и на основе них создают точные байты, на которые произойдёт замена. Саму замену байтов выполняет MyGlobalPatternPatch. Связаны 2 эти функции через $aOutHexGlobalArray.

Таким образом, во всём обработчике "Patch CC" прослеживается общий шаблон:

Код:
1. Проверка, существует ли обрабатываемый файл в Program Files (x86) или в Program Files. Иначе выбираем сами.
2. Функцией MyGlobalPatternSearch ищутся точные исходные и заменяемые байты и записываются в глобальный массив $aOutHexGlobalArray.
3. Функция MyGlobalPatternPatch использует $aOutHexGlobalArray и заменяет точные искомые байты на точные заменяемые. Также создаёт бекап исходного файла.

В коде это:

Visual Basic:
If FileExists("C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll") Then
                $appsPanelFile = "C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll"
            ElseIf FileExists("C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll") Then
                $appsPanelFile = "C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll"
            Else
                $appsPanelFile = FileOpenDialog("Select a File", @ScriptDir, "AppsPanelBL.dll (AppsPanelBL.dll)")
            EndIf
            ProgressWrite(0)
            If FileExists($appsPanelFile) Then
                MyGlobalPatternSearch($appsPanelFile)
                Sleep(100)
                MemoWrite(@CRLF & "File Path:" & @CRLF & "" & @CRLF & $appsPanelFile & @CRLF & "" & @CRLF & "")
                Sleep(100)
                MyGlobalPatternPatch($appsPanelFile, $aOutHexGlobalArray)
                Sleep(500)
            EndIf
            ProgressWrite(0)
            If $bFoundCCARM = False Then
                If FileExists("C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\ContainerBL.dll") Then
                    $containerBLFile = "C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\ContainerBL.dll"
                ElseIf FileExists("C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\ADS\ContainerBL.dll") Then
                    $containerBLFile = "C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\ADS\ContainerBL.dll"
                Else
                    $containerBLFile = FileOpenDialog("Select a File", @ScriptDir, "ContainerBL.dll (ContainerBL.dll)")
                EndIf
                ProgressWrite(0)
                If FileExists($containerBLFile) Then
                    MyGlobalPatternSearch($containerBLFile)
                    Sleep(100)
                    MemoWrite(@CRLF & "File Path:" & @CRLF & "" & @CRLF & $containerBLFile & @CRLF & "" & @CRLF & "")
                    Sleep(100)
                    MyGlobalPatternPatch($containerBLFile, $aOutHexGlobalArray)
                    Sleep(500)
                EndIf
                ProgressWrite(0)
                If FileExists("C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe") Then
                    $adobeDesktopServiceFile = "C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe"
                ElseIf FileExists("C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe") Then
                    $adobeDesktopServiceFile = "C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe"
                Else
                    $adobeDesktopServiceFile = FileOpenDialog("Select a File", @ScriptDir, "Adobe Desktop Service.exe (Adobe Desktop Service.exe)")
                EndIf
                If FileExists($adobeDesktopServiceFile) Then
                    MyGlobalPatternSearch($adobeDesktopServiceFile)
                    Sleep(100)
                    MemoWrite(@CRLF & "File Path:" & @CRLF & "" & @CRLF & $adobeDesktopServiceFile & @CRLF & "" & @CRLF & "")
                    Sleep(100)
                    MyGlobalPatternPatch($adobeDesktopServiceFile, $aOutHexGlobalArray)
                    Sleep(500)
                EndIf
                ProgressWrite(0)
                MemoWrite(@CRLF & "All files patched." & @CRLF & "" & @CRLF & "")

Кроме этого проверяется, что мы не используем ARM-версии файлов. Ну и всё пишется в лог :)
Теперь посмотрим, какие именно изменения вносятся. Как мы узнали, меняются эти файлы:

Код:
C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll
C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\ContainerBL.dll
C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe

Вместо Program Files может быть Program Files (x86). У меня как раз этот случай. Патчинг этих файлов позволяет скачивать платные программы Adobe из Creative Cloud.

До патча:

f775f62634dc6508df201ccebfd98761.png


После патча:

4a287edbce79862c35ea03eda748fd69.png


Экспериментально у себя я выяснил, что для достижения такого результата обязательно нужно пропатчить ContainerBL.dll и AppsPanelBL.dll. Вероятно, патчинг Adobe Desktop Service.exe нужен для подстраховки.

4.2.5.3. Сравниваем пропатченные байты с оригинальными и ищем их

Мы сравним файл до патча и после патча. Я буду делать это через IDA. Но сначала нужно найти точные байты.

Это можно сделать этой утилитой на C++:

C++:
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <iomanip>
#include <exception>
#include <locale>

struct Pattern {
    std::string name;
    std::vector<std::pair<std::string, std::string>> patterns;
};

// Функция для парсинга паттернов
std::vector<Pattern> parsePatterns() {
    std::vector<Pattern> patterns = {
        {"CreativeCloud1", {{"8378????0F84????????8378????0F84????????8378????0F84????????33C0", "C640????0F84????????C640????0F84????????C640????0F84????????33C0"}}},
        {"CreativeCloud2", {{"E8????????85C00F85????????83EC??8BCC89", "E8????????FEC00F85????????83EC??8BCC89"}}},
        {"CreativeCloud3", {{"??????0F85890300", "??????E98A030000"}}},
        {"Acrobat1", {{"488BCFE8????????85C00F84????????488D??????????488BCFE8????????85C075??8D", "488BCFE8????????FFC00F84????????488D??????????488BCFE8????????31C075??8D"}}},
        {"Acrobat2", {{"6685C0740FE8????????6685C07405BB01000000", "6685C07400E8????????6685C07400BB01000000"}}},
        {"Acrobat3", {{"6685C0741A6685??0F85??020000", "6685C090906685??0F85??020000"}}},
        {"Acrobat4", {{"00E8????E7FF66893D??????03381D??????03751F4533C0488D15??????02488D0D??????02E8????E7FF6685C00F84BAFEFFFF8BDFE9B3FEFFFFB801000000", "00E8????E7FF66893D??????03381D??????03EB1F4533C0488D15??????02488D0D??????02E8????E7FF6685C00F84BAFEFFFF8BDFE9B3FEFFFFB801000000"}}},
        {"Acrobat5", {{"753FE8????????6685C0740933C9E8????????EB25488D05????????48894424204C8D0D????????33D2448D423C488D0D??????00E8????????66893D????????381D????????75", "EB3FE8????????6685C0740933C9E8????????EB25488D05????????48894424204C8D0D????????33D2448D423C488D0D??????00E8????????66893D????????381D????????EB"}}},
        {"Acrodist1", {{"85C00F84????????E8????????85C00F85????????E8", "85C00F84????????E8????????FFC00F85????????E8"}}},
        {"Acrodist2", {{"00908BC8E8??????00CCCCCCCCCCCC48895C24104889", "00908BC8E8??????00CCCCCCCCCCCC31C0C324104889"}}},
        {"Acrodist3", {{"5E5B5DC3CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC4055535657415441554156415748", "5E5B5DC3CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC31C0C35657415441554156415748"}}},
        {"Acrodist4", {{"00005072696E745F447269766572", "0000B001906E745F447269766572"}}},
        {"Acrodist5", {{"51??????????????????????????????48895C????????????????????????55", "51??????????????????????????????33C0C3????????????????????????55"}}},
        {"AcroNew1", {{"C3488BC1C3CC48895C24084889??2410", "C3488BC1C3CCC3895C24084889??2410"}}},
        {"AcroRegistry1", {{"0FB6C3488B5C2458????????????????895C", "B801000000909090????????????????895C"}}},
        {"AcroTray1", {{"E8????????84C074??E8????????84C075??33DB", "E8????????FEC074??E8????????FEC075??33DB"}}},
        {"AcroTray2", {{"0F8459030000803D??????00000F8545030000", "E95A03000090803D??????0000E94603000090"}}},
        {"AcroTray3", {{"0F85A302000048397C24400F84", "E9A40200009048397C24400F85"}}},
        {"AcroTray4", {{"244884DB0F84C70100", "244884DB0F85C70100"}}},
        {"AcroTray5", {{"????????E86791D9FFE862B2D8FF????", "????????9090909090B801000000????"}}},
        {"DesktopService1", {{"68008B40148985CCFEFFFF85C0", "68008B40148985CCFEFFFF33C0"}}},
        {"Banner1", {{"72656C6174696F6E7368697050726F66696C65", "78656C6174696F6E7368697050726F66696C65"}}},
        {"Banner2", {{"000000000000000072656C6174696F6E", "000000000000000078656C6174696F6E"}}},
        {"BridgeCamRaw1", {{"84C074??8B??83??0174??83??0174??83??01", "84C074??8B??83??01EB??83??0174??83??01"}}},
        {"BridgeCamRaw2", {{"4084??0F85????????4084??0F84", "4084??0F85????????40FEC60F85"}}},
        {"CmpEax61", {{"8B??85C074??83F80674????83????007D", "C7??0300000083F8067400??83????00EB"}}},
        {"CmpEax62", {{"8B??85C074??83F80674????83??????007D", "C7??0300000083F8067400??83??????00EB"}}},
        {"CmpEax63", {{"8B????85C074??83F80674????83????007D", "C7????0300000083F8067400??83????00EB"}}},
        {"CmpEax64", {{"8B????85C074??83F80674????83??????007D", "C7????0300000083F8067400??83??????00EB"}}},
        {"HevcMpegEnabler1", {{"FF50100FB6", "FFC0900FB6"}}},
        {"HevcMpegEnabler2", {{"FF5010??0FB6", "FFC090??0FB6"}}},
        {"HevcMpegEnabler3", {{"FF50??0FB6", "FFC0900FB6"}}},
        {"HevcMpegEnabler4", {{"FF50????0FB6", "FFC090??0FB6"}}},
        {"Profile1", {{"00007504488D4850", "00007500488D4850"}}},
        {"Profile2", {{"00007504488D5050", "00007500488D5050"}}},
        {"ProfileExpired1", {{"85C075??????????75??B892010000E9", "31C075004883FF0F7500B800000000E9"}}},
        {"ProfileExpired2", {{"85C075??B892010000E9", "85C07500B800000000E9"}}},
        {"ProfileExpired3", {{"85C075????????75??B892010000E9", "31C075????????7500B800000000E9"}}},
        {"ProfileExpired4", {{"488D4D??483B??0F??????000048????4889??4885C9", "488D4D??483B??C700000000004831C94889??4885C9"}}},
        {"ProfileExpired5", {{"488B0B4889034885C974??BA04000000E8????????B00148", "C7000000000048890348C7C1000000000F1F440000B00148"}}},
        {"ProfileExpired6", {{"4885C974??BA04000000E8????????4C8D5C", "C700000000004889034831C90F1F004C8D5C"}}},
        {"TeamProjectEnabler1", {{"488379????740A488379????7403B001C332C0C3", "488379????740A488379????7403B001C3B001C3"}}},
        {"ValidateLicense1", {{"83F80175??BA94010000", "83F80175??BA00000000"}}},
        {"ValidateLicense2", {{"83F8040F95C281C293010000", "83F8040F95C2BA0000000090"}}},
        {"ValidateLicense3", {{"83F8040F95C181C193010000", "83F8040F95C1B90000000090"}}},
        {"InstantShutdown1", {{"00??????????E875000000????????C0", "00??????????9090909090????????C0"}}},
        {"PluginVerification1", {{"5F5E????????????????????????????48895C2410??8974", "5F5E????????????????????????????C390909090??8974"}}},
        {"LoginVerification1", {{"4C8B??????????????????????????????????????742C48", "4C8B??????????????????????????????????????EB2C48"}}},
        {"JS1", {{"52656C6174696F6E7368697050726F66696C653A612E??2E6F7074696F6E616C", "58656C6174696F6E7368697050726F66696C653A612E??2E6F7074696F6E616C"}}},
        {"JS2", {{"52656C6174696F6E7368697050726F66696C652E66696E6428653D3E652E75736564466F724C656761637950726F66696C65293B72657475726E20692E6D61696E53746F7265", "58656C6174696F6E7368697050726F66696C652E66696E6428653D3E652E75736564466F724C656761637950726F66696C65293B72657475726E20692E6D61696E53746F7265"}}},
        {"JS3", {{"52656C6174696F6E7368697050726F66696C652E66696E6428653D3E652E75736564466F724C656761637950726F66696C65297D7D2929", "58656C6174696F6E7368697050726F66696C652E66696E6428653D3E652E75736564466F724C656761637950726F66696C65297D7D2929"}}},
        {"JS4", {{"52656C6174696F6E7368697050726F66696C65297C7C766F696420303D3D3D69", "58656C6174696F6E7368697050726F66696C65297C7C766F696420303D3D3D69"}}},
        {"Version", {{"6363782E7374617274222C0A20202276657273696F6E223A2022372E", "6363782E7374617274222C0A20202276657273696F6E223A2022392E"}}}
    };
    return patterns;
}

// Функция для преобразования паттерна в байтовый массив и маску
void parseHexPattern(const std::string& pattern, std::vector<uint8_t>& bytes, std::vector<uint8_t>& mask) {
    for (size_t i = 0; i < pattern.size(); i += 2) {
        if (pattern[i] == '?' || pattern[i+1] == '?') {
            bytes.push_back(0);
            mask.push_back(0);
        } else {
            std::string byteString = pattern.substr(i, 2);
            try {
                uint8_t byte = static_cast<uint8_t>(std::stoi(byteString, nullptr, 16));
                bytes.push_back(byte);
                mask.push_back(0xFF);
            } catch (const std::invalid_argument& e) {
                std::cerr << "Invalid pattern: " << byteString << std::endl;
                throw;
            }
        }
    }
}

// Функция для поиска паттерна в файле
bool matchPattern(const std::vector<uint8_t>& data, const std::vector<uint8_t>& pattern, const std::vector<uint8_t>& mask, size_t pos) {
    for (size_t i = 0; i < pattern.size(); ++i) {
        if ((data[pos + i] & mask[i]) != (pattern[i] & mask[i])) {
            return false;
        }
    }
    return true;
}

bool searchAndPrintPattern(const std::vector<uint8_t>& data, const std::vector<uint8_t>& pattern, const std::vector<uint8_t>& mask, const std::string& patternType, bool& headerPrinted, const std::string& patternName) {
    bool found = false;
    for (size_t i = 0; i <= data.size() - pattern.size(); ++i) {
        if (matchPattern(data, pattern, mask, i)) {
            if (!headerPrinted) {
                std::cout << "<========= " << patternName << " =========>" << std::endl;
                headerPrinted = true;
            }
            found = true;
            std::cout << patternType << ": ";
            std::cout << std::hex << std::setfill('0');
            for (size_t j = 0; j < pattern.size(); ++j) {
                std::cout << std::setw(2) << static_cast<int>(data[i + j]) << " ";
            }
            std::cout << std::endl;
        }
    }
    return found;
}

int main(int argc, char* argv[]) {
    setlocale(LC_ALL, "Rus");

    if (argc < 2) {
        std::cerr << "Использование: " << argv[0] << " <файл>" << std::endl;
        return 1;
    }

    std::ifstream file(argv[1], std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "Не получается открыть файл: " << argv[1] << std::endl;
        return 1;
    }

    std::vector<uint8_t> data((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());

    auto patterns = parsePatterns();

    for (const auto& pattern : patterns) {
        bool anyFound = false;
        bool headerPrinted = false;

        for (const auto& [searchPattern, replacePattern] : pattern.patterns) {
            std::vector<uint8_t> searchBytes, searchMask;
            parseHexPattern(searchPattern, searchBytes, searchMask);

            std::vector<uint8_t> replaceBytes, replaceMask;
            parseHexPattern(replacePattern, replaceBytes, replaceMask);

            if (searchAndPrintPattern(data, searchBytes, searchMask, "Искомый   ", headerPrinted, pattern.name)) {
                anyFound = true;
            }
            if (searchAndPrintPattern(data, replaceBytes, replaceMask, "Заменяемый", headerPrinted, pattern.name)) {
                anyFound = true;
            }
        }

        if (anyFound) {
            std::cout << std::endl;
        }
    }

    return 0;
}

Пример запуска:

Код:
C:\Users\fff\Desktop>search.exe AppsPanelBL.dll
<========= CreativeCloud1 =========>
Искомый   : 83 78 2c 00 0f 84 95 01 00 00 83 78 44 00 0f 84 8b 01 00 00 83 78 5c 00 0f 84 81 01 00 00 33 c0

<========= CreativeCloud2 =========>
Искомый   : e8 76 47 10 00 85 c0 0f 85 9d 00 00 00 83 ec 18 8b cc 89
Искомый   : e8 01 a9 0e 00 85 c0 0f 85 8f 02 00 00 83 ec 18 8b cc 89
Искомый   : e8 09 b7 02 00 85 c0 0f 85 1f 02 00 00 83 ec 18 8b cc 89
Искомый   : e8 83 fd ff ff 85 c0 0f 85 bd 04 00 00 83 ec 18 8b cc 89
Искомый   : e8 f2 f7 ff ff 85 c0 0f 85 74 01 00 00 83 ec 18 8b cc 89
Искомый   : e8 ef f5 ff ff 85 c0 0f 85 76 02 00 00 83 ec 18 8b cc 89
Искомый   : e8 cf f2 ff ff 85 c0 0f 85 76 02 00 00 83 ec 18 8b cc 89

<========= CreativeCloud3 =========>
Искомый   : ff 84 c0 0f 85 89 03 00
Искомый   : ff 84 c0 0f 85 89 03 00
Искомый   : 74 24 38 0f 85 89 03 00
Заменяемый: ff 32 c0 e9 8a 03 00 00
Заменяемый: 0a 01 00 e9 8a 03 00 00

Только иногда она находит лишние байты. Но нам нужно искать только те, что патчатся. Для AppsPanelBL.dll это будет CreativeCloud1 и CreativeCloud2. На CreativeCloud3 просто не смотрим.

INI:
AppsPanelBL.dll="CreativeCloud1","CreativeCloud2"

Далее файл нужно открыть в IDA, нажать ALT+B и ввести первые 5-10 найденных байт:

9e9d79787fc9fe90762b054754342803.png


ce0208547e731f9ad4ba7ed012a68adc.png


Нажимаем 2 раза на запись.

6068fec75a8f8fd28152094da08583c8.png


Попали сюда. Теперь отобразим опкоды инструкций.

dd7c5f4676b313276112bf5ecda13b64.png


3f9c2ca73ef7adeb6210966c6f382171.png


f10fa4ab0cd28379a4dbae7d8a5ca260.png


Таким образом можно сравнивать файлы до патча и после. Так мы посмотрим, как поменяются инструкции.
Так как GenP делает бекапы (.bak), то можно сравнивать бекапы с пропатченными файлами.

C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe
DesktopService1

2b22ecd956cc97731cc1a14037722c83.png


a58a9db5e3857214e854f06ee12fe68a.png


Слева я буду располагать файл до патча, а справа после. Если хотите перейти в режим листинга IDA (как на скрине), нажмите пробел. И снова в настройках включите отображение опкодов.

C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll
CreativeCloud1

162210b372c83fc12c5d9f6d7e865b55.png


CreativeCloud2

446f17de3e855f6d48595683fdacd456.png


C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\ContainerBL.dll
CreativeCloud3

aa4f1319ad8035c000d4414c89118b0a.png


Проанализировав различия между этими файлами, видим, что ничего вредоносного в них он не добавляет. Просто меняет ход выполнения программы. Где-то через замену инструкций условных переходов, где-то через замену инструкций перед ними. Можно даже сделать всё это вручную и получить тот же результат.

Далее нам нужно будет рассмотреть, как работает обработчик патчинга файлов самих программ Adobe. Но перед этим рассмотрим обработчик кнопки восстановления оригинального файла - "Restore".

c05ac91a794f3adb885e00f8f457b325.png


4.2.6. Обработчик кнопки "Restore"

Она появляется, если после поиска файлов для патча (кнопка "Search") мы выберем какой-либо или какие-либо из них, и есть файл .bak для выбранного.

25e74781e4209b395b154b66ef64d1eb.png


Начало его обработчика:

Visual Basic:
        Case $idMsg = $idBtnRestore
            GUICtrlSetData($idLog, "Activity Log" & @CRLF)
            ToggleLog(0)
            GUICtrlSetState($idListview, 128)
            GUICtrlSetState($idBtnDeselectAll, 128)
            GUICtrlSetState($idButtonSearch, 128)
            GUICtrlSetState($idBtnCure, 128)

Основная функция там - это RestoreFile:

Visual Basic:
                If _GUICtrlListView_GetItemChecked($idListview, $i) = True Then

                    _GUICtrlListView_SetItemSelected($idListview, $i)

                    $ItemFromList = _GUICtrlListView_GetItemText($idListview, $i, 1)
                    $iCheckedItems = _GUICtrlListView_GetSelectedCount($idListview)
                    $iProgress = 100 / $iCheckedItems
                    ProgressWrite(0)
                    RestoreFile($ItemFromList)

                    ProgressWrite($iProgress)
                    Sleep(100)
                    MemoWrite(@CRLF & "Path" & @CRLF & "---" & @CRLF & $ItemFromList & @CRLF & "---" & @CRLF & "restoring :)")
                    Sleep(100)

                    ; Scroll control 10 pixels - 1 line
                    _GUICtrlListView_Scroll($idListview, 0, 10)
                    _GUICtrlListView_EnsureVisible($idListview, $i, 0)
                    Sleep(100)

                EndIf

Этой функции передаётся путь до файла. Её код:

Visual Basic:
Func RestoreFile($MyFileToDelete)
    If FileExists($MyFileToDelete & ".bak") Then
        FileDelete($MyFileToDelete)
        FileMove($MyFileToDelete & ".bak", $MyFileToDelete, $FC_OVERWRITE)
        Sleep(100)
        MemoWrite(@CRLF & "File restored" & @CRLF & "---" & @CRLF & $MyFileToDelete)
        LogWrite(1, $MyFileToDelete)
        LogWrite(1, "File restored.")
    Else
        Sleep(100)
        MemoWrite(@CRLF & "No backup file found" & @CRLF & "---" & @CRLF & $MyFileToDelete)
        LogWrite(1, $MyFileToDelete)
        LogWrite(1, "No backup file found.")
    EndIf
EndFunc   ;==>RestoreFile

Тут всё просто :)
Удаляем пропатченный файл и переименовываем .bak-файл в файл без постфикса .bak. Конечно же, если бекап этого файла существует.

4.2.7. Обработчик кнопки "Patch"

Ну а теперь переходим к обработчику кнопки "Patch", что доступна после "Search".

47b7c0b5c5249d10aaebc28998e905ae.png


Visual Basic:
Case $idMsg = $idBtnCure
            ToggleLog(0)
            GUICtrlSetState($idListview, 128)
            GUICtrlSetState($idBtnDeselectAll, 128)
            GUICtrlSetState($idButtonSearch, 128)
            GUICtrlSetState($idBtnCure, 128)
            GUICtrlSetState($idBtnBlockPopUp, 128)
            GUICtrlSetState($idBtnRestore, 128)
            GUICtrlSetState($idButtonCustomFolder, 128)
            GUICtrlSetState($idBtnPatchCC, 128)
            _Expand_All_Click()
            _GUICtrlListView_EnsureVisible($idListview, 0, 0)
            ...

Это начало кода функции.

Visual Basic:
For $i = 0 To _GUICtrlListView_GetItemCount($idListview) - 1

                If _GUICtrlListView_GetItemChecked($idListview, $i) = True Then

                    _GUICtrlListView_SetItemSelected($idListview, $i)
                    $ItemFromList = _GUICtrlListView_GetItemText($idListview, $i, 1)

                    MyGlobalPatternSearch($ItemFromList)
                    ProgressWrite(0)
                    Sleep(100)
                    MemoWrite(@CRLF & "Path" & @CRLF & "---" & @CRLF & $ItemFromList & @CRLF & "---" & @CRLF & "medication :)")
                    LogWrite(1, $ItemFromList)
                    Sleep(100)

                    MyGlobalPatternPatch($ItemFromList, $aOutHexGlobalArray)


                    ; Scroll control 10 pixels - 1 line
                    _GUICtrlListView_Scroll($idListview, 0, 10)
                    _GUICtrlListView_EnsureVisible($idListview, $i, 0)
                    Sleep(100)

                EndIf

При выборе какого-либо элемента и при нажатии кнопки "Patch" в функцию MyGlobalPatternSearch передаётся путь до файла. В MyGlobalPatternSearch, как мы помним, решается, что брать из INI-файла для этого искомого файла, затем проверяется сам файл и уже далее ищутся байты в нём.
После поиска нужных байт и занесения их в $aOutHexGlobalArray, вызывается MyGlobalPatternPatch. Тут всё, как в патчинге самого Creative Cloud. Но отличие в том, что при патчинге Creative Cloud искомые файлы были в секции [CustomPatterns]:

INI:
[CustomPatterns]
...
AppsPanelBL.dll="CreativeCloud1","CreativeCloud2"
ContainerBL.dll="CreativeCloud3"
Adobe Desktop Service.exe="DesktopService1"
...

Но теперь нам нужно пропатчить сами программы, а не Creative Cloud. Вот полный список файлов, что будет искать GenP:

INI:
[TargetFiles]
1="acrobat.dll"
2="acrodistdll.dll"
3="acrotray.exe"
4="aero.exe"
5="afterfxlib.dll"
6="animate.exe"
7="animator.exe"
8="animator (beta).exe"
9="auui.dll"
10="adobe bridge.exe"
11="designer.exe"
12="dreamweaver.exe"
13="dvaappsupport.dll"
14="encoder.exe"
15="encoder (beta).exe"
16="euclid-core"
17="gemini_uwp_bridge.dll"
18="illustrator.exe"
19="lightroom.exe"
20="lightroomcc.exe"
21="modeler.exe"
22="modeler beta.exe"
23="ngl-lib.dll"
24="painter.exe"
25="photoshop.exe"
26="public.dll"
27="registration.dll"
28="sampler.exe"
29="sampler beta.exe"
30="stager.exe"
31="sweetpeasupport.dll"
32="xd.exe"
33="appframework.rpln"
34="objectmodel.dll"
35="4.js"
36="manifest.json"

А вот список специальных файлов:

INI:
[CustomPatterns]
Acrobat.dll="Acrobat3","Acrobat5"
acrodistdll.dll="Acrodist2","Acrodist3","Acrodist4","Acrodist5","AcroRegistry1","AcroNew1"
acrotray.exe="AcroTray2","AcroTray3","AcroTray4","AcroTray5","AcroRegistry1","AcroNew1"
AppsPanelBL.dll="CreativeCloud1","CreativeCloud2"
ContainerBL.dll="CreativeCloud3"
Adobe Bridge.exe="ProfileExpired1","ValidateLicense1","ValidateLicense2","ValidateLicense3","CmpEax61","CmpEax62","CmpEax63","CmpEax64","Profile1","Profile2","Banner1","BridgeCamRaw1","BridgeCamRaw2","InstantShutdown1"
dvaappsupport.dll="TeamProjectEnabler1"
SweetPeaSupport.dll="HevcMpegEnabler3","HevcMpegEnabler4"
AppFramework.rpln="LoginVerification1"
ObjectModel.dll="PluginVerification1"
Adobe Desktop Service.exe="DesktopService1"
4.js="JS1","JS2","JS3","JS4"
manifest.json="Version"

Файлов из TargetFiles явно больше. Как быть с теми, что не в секции со специальными файлами?
В таком случае используется набор патчей из секции [DefaultPatterns]:

INI:
[DefaultPatterns]
Values="ProfileExpired1","ProfileExpired3","ProfileExpired4","ProfileExpired5","ProfileExpired6","ValidateLicense1","ValidateLicense2","ValidateLicense3","CmpEax61","CmpEax62","CmpEax63","CmpEax64","Profile1","Profile2","Banner1","Banner2","InstantShutdown1"

Патчи по умолчанию применяются для всех файлов, что не находятся в секции [CustomPatterns]. В функции MyGlobalPatternSearch и ExecuteSearchPatterns как раз можно это увидеть:

Visual Basic:
Func MyGlobalPatternSearch($MyFileToParse)
    ...
    If StringInStr($sSpecialFiles, $sFileName) Then
        ;MsgBox($MB_SYSTEMMODAL, "", "Special File: " & $sFileName)
        LogWrite(0, " - using Custom Patterns")
        ExecuteSearchPatterns($sFileName, 0, $MyFileToParse)
    Else
        LogWrite(0, " - using Default Patterns")
        ExecuteSearchPatterns($sFileName, 1, $MyFileToParse)
        ;MsgBox($MB_SYSTEMMODAL, "", "File: " & $sFileName & @CRLF & "Not in Special Files")
    EndIf
    ...

Visual Basic:
Func ExecuteSearchPatterns($FileName, $DefaultPatterns, $MyFileToParse)
    Local $aPatterns, $sPattern, $sData, $aArray, $sSearch, $sReplace, $iPatternLength

    If $DefaultPatterns = 0 Then
        $aPatterns = IniReadArray($sINIPath, "CustomPatterns", $FileName, "")
    Else
        $aPatterns = IniReadArray($sINIPath, "DefaultPatterns", "Values", "")
    EndIf
    ...

Явно видно, что в случае если $DefaultPatterns равен 1, то читается значение ключа Values из секции [DefaultPatterns]. А если же файл будет из секции CustomPatterns, то и использоваться будут те байты, что указаны в этой секции. Например, как с Acrobat.dll:

INI:
[CustomPatterns]
Acrobat.dll="Acrobat3","Acrobat5"

5.0 Изучаем патчи из INI-файла

Теперь рассмотрим патчи из секции [Patches]. Как и с файлами Creative Cloud, слева на скринах будет файл до патча, а справа - после. Сначала рассмотрим оставшиеся значения для ключей из [CustomPatterns], а затем из [DefaultPatterns]. Важно учесть то, что не все значения для ключей будут найдены и применены. Некоторые останутся ненайденными и байты из них не будут использоваться для патча. Скорее всего, они использовались у некоторых старых версий приложений. Я покажу в IDA только то, что нашлось у меня. Также в файлах могут встречаться байты из других. Дубликаты я буду пропускать.

5f9ec721035821b07e0f486767015957.png


5.1. Секция CustomPatterns

C:\Program Files\Adobe\Acrobat DC\Acrobat\Acrobat.dll
Acrobat3

19029fbb4ae44f497a438f80eb741f00.png


Acrobat5

a3125ddc853aa5f801e9f5a792504094.png


82397071fbf85d645a6e352b29fdeb25.png


C:\Program Files\Adobe\Acrobat DC\Acrobat\acrodistdll.dll
Acrodist4

c21cfd7a7af8fa9bc1ffe698e7ff0ff0.png


AcroRegistry1

8dba75d48b1051ac36c4902d247308ee.png


AcroNew1

404405e8738ad787fa7f75f2eb839a56.png


C:\Program Files\Adobe\Acrobat DC\Acrobat\acrotray.exe
Новых нет.

C:\Program Files\Adobe\Adobe Bridge 2024\Adobe Bridge.exe
Banner1

381435478ae9fb5712cce2668f79d11d.png


BridgeCamRaw2

0b96d0c908cb6154946abf3b2077a340.png


CmpEax63

ce2bd886e02a0dd188a4dba122fbf456.png


Profile2

ca4b3c113ac5f6d8aafbf5695eac9afa.png


ProfileExpired1

eb70038d71dff15377f99a6b9885f930.png


ValidateLicense2

e5db56a74f45dfc1a5f878f346f91c14.png


C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\dvaappsupport.dll
TeamProjectEnabler1

35d08f18f1f12db65f112717cba10e5c.png


C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\SweetPeaSupport.dll
HevcMpegEnabler3

508ddfb1688c595c9fd7a77897c7951f.png


HevcMpegEnabler4

96ee17bb4d76e431ef78aaeb0cd354c7.png


C:\Program Files\Adobe\Adobe Photoshop 2024\Required\UXP\com.adobe.ccx.start\js\4.js
JS1

7d5fb14cf31faf1a84e988370f579d3c.png


JS2

808e4ec69be14128f8c01c400694b786.png


JS3

0b757e0f0cebabedfe1e085138564222.png


JS4

5225b778cf0e0b2c8ba77bf25b43ac43.png


C:\Program Files\Adobe\Adobe Photoshop 2024\Required\UXP\com.adobe.ccx.start\manifest.json
Version

4f8a05d210504705048637d0a79116b2.png


5.2. Секция DefaultPatterns

C:\Program Files\Adobe\Adobe Photoshop 2024\Photoshop.exe
Banner2

7e62341d9a1ffaf81147d2fb26c29449.png


Profile1

bc42e8cf38dea0df2c2efae3f0fcd1ad.png


ProfileExpired4

d7d0a1cd27ed31731b82d9f355498275.png


C:\Program Files\Adobe\Adobe Illustrator 2024\Support Files\Contents\Windows\Illustrator.exe
ProfileExpired6

be8c9d075f9bbcadfe6b1860823be1f7.png


C:\Program Files\Adobe\Adobe Substance 3D Designer\Adobe Substance 3D Designer.exe
ProfileExpired5

025bd32bd52901ab7671779136ff946f.png


C:\Program Files\Adobe\Adobe Dreamweaver 2021\Dreamweaver.exe
ValidateLicense3

c59f3ae496aeaded94a57534b9bef86d.png


d92a968e44fec44d22ca55b84953a58d.png



5.3. Оставшиеся патчи

После анализа всех программ у меня остались нетронутыми такие байты:

INI:
Acrobat1="488BCFE8????????85C00F84????????488D??????????488BCFE8????????85C075??8D"|"488BCFE8????????FFC00F84????????488D??????????488BCFE8????????31C075??8D"
Acrobat2="6685C0740FE8????????6685C07405BB01000000"|"6685C07400E8????????6685C07400BB01000000"

Acrobat4="00E8????E7FF66893D??????03381D??????03751F4533C0488D15??????02488D0D??????02E8????E7FF6685C00F84BAFEFFFF8BDFE9B3FEFFFFB801000000"|"00E8????E7FF66893D??????03381D??????03EB1F4533C0488D15??????02488D0D??????02E8????E7FF6685C00F84BAFEFFFF8BDFE9B3FEFFFFB801000000"

Acrodist1="85C00F84????????E8????????85C00F85????????E8"|"85C00F84????????E8????????FFC00F85????????E8"
Acrodist2="00908BC8E8??????00CCCCCCCCCCCC48895C24104889"|"00908BC8E8??????00CCCCCCCCCCCC31C0C324104889"
Acrodist3="5E5B5DC3CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC4055535657415441554156415748"|"5E5B5DC3CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC31C0C35657415441554156415748"

Acrodist5="51??????????????????????????????48895C????????????????????????55"|"51??????????????????????????????33C0C3????????????????????????55"

AcroTray1="E8????????84C074??E8????????84C075??33DB"|"E8????????FEC074??E8????????FEC075??33DB"
AcroTray2="0F8459030000803D??????00000F8545030000"|"E95A03000090803D??????0000E94603000090"
AcroTray3="0F85A302000048397C24400F84"|"E9A40200009048397C24400F85"
AcroTray4="244884DB0F84C70100"|"244884DB0F85C70100"
AcroTray5="????????E86791D9FFE862B2D8FF????"|"????????9090909090B801000000????"

BridgeCamRaw1="84C074??8B??83??0174??83??0174??83??01"|"84C074??8B??83??01EB??83??0174??83??01"

CmpEax61="8B??85C074??83F80674????83????007D"|"C7??0300000083F8067400??83????00EB"
CmpEax62="8B??85C074??83F80674????83??????007D"|"C7??0300000083F8067400??83??????00EB"

CmpEax64="8B????85C074??83F80674????83??????007D"|"C7????0300000083F8067400??83??????00EB"
HevcMpegEnabler1="FF50100FB6"|"FFC0900FB6"
HevcMpegEnabler2="FF5010??0FB6"|"FFC090??0FB6"

ProfileExpired2="85C075??B892010000E9"|"85C07500B800000000E9"
ProfileExpired3="85C075????????75??B892010000E9"|"31C075????????7500B800000000E9"

ValidateLicense1="83F80175??BA94010000"|"83F80175??BA00000000"

InstantShutdown1="00??????????E875000000????????C0"|"00??????????9090909090????????C0"
PluginVerification1="5F5E????????????????????????????48895C2410??8974"|"5F5E????????????????????????????C390909090??8974"
LoginVerification1="4C8B??????????????????????????????????????742C48"|"4C8B??????????????????????????????????????EB2C48"

В этом случае можно поступить по-другому: мы используем онлайн-дизассемблер ( ). Передадим в него искомые и заменяемые байты, чтобы сравнить. Знаки ?? я заменю 00 вот так:

Этот способ даст не на 100% точный вывод дизассемблера, а приблизительный. Но точный нам тут и не нужен. Для проверки того, вредоносен ли патч, хватит и приблизительного вывода. К тому же изменения в патчах маленькие, обычно меняют инструкции перед условным переходом, сам условный переход или что-то заменяют чем-то. Это всё не будет вредоносным.

Acrobat1

39914d38da683ea9a68693894e812b56.png


Acrobat2

87e3d986fb13187c93ab321875035b1d.png


Acrobat4

ad7b0649bc82b2a406d0ebf935649bda.png


Acrodist1

4263c9ab0aac46bce76ead5327834d75.png


Acrodist2

f9e298ca9d749bf36654d27b1eaf2d7c.png


Acrodist3

17ef0feaab4d099ac9ccd098ed678a60.png


Acrodist5

f2bcd7fb17935197bad1e9ef7e4de1a6.png


AcroTray1

8a28fe6002108f5ab2fe6a6e443749ae.png


AcroTray2

1350407ad56fd174cfbafbc37e4e47c8.png


AcroTray3

3f4b8699a4fdbe46da0650c96e84c701.png


AcroTray4

7f3b151061249728106fcb94dd534f4a.png


AcroTray5

f5a283837cd0f6a2715556c98c6219d1.png


BridgeCamRaw1

8c4d6ecaf6315c42301171f52d4cf34f.png


CmpEax61

5abb61f74d0a252827fb1b5eb6a0ad3d.png


CmpEax62

b4201a9a8a30d9da33033bd9de3e4f76.png


CmpEax64

1f47527badeb232eb39796ef67a652ce.png


HevcMpegEnabler1

33f4d2c3cec085b94c8473bc7bb2a99c.png


HevcMpegEnabler2

4cc93d113e9c22b9bb57a6bb43fe189d.png


ProfileExpired2

56d8ac7c520f21c4e8eca95ddbfdb232.png


ProfileExpired3

66785ef021a4e43bf07a003365e58865.png


ValidateLicense1

90d0183a12ea5143472a45ae9606cc9c.png


InstantShutdown1

a96bd32923f177df86e519ffa5710b93.png


PluginVerification1

4c9d3705176a87bc64b5699bab063f5e.png


LoginVerification1

b3b9a65d3e020b416e71f2878d7e9c21.png


6.0 Вывод

Мы вместе с вами изучили этот патчер и не нашли ничего особо вредоносного внутри него. Даже рассмотрели все патчи и все функции. Но важный момент - мы работали с исходником GenP. Помимо исходника в архиве идёт ещё и EXE-файл. Насчёт его ничего сказать не могу, так как ещё не ревёрсил его. Поэтому если вы хотите тоже его изучить, для безопасности ЗАГРУЖАЙТЕ AutoIt С ОФИЦИАЛЬНОГО САЙТА И СОБИРАЙТЕ СВОЙ EXE ИЗ ИСХОДНИКА! Также обращайте внимание на версию GenP. Ниже я прикреплю контрольные суммы файлов, что были рассмотрены в статье.

Архив с GenP:

iz3lne.zip
MD5: 6b104ba9deb749a6b6ce88b9c6997dae
SHA1: 19d9b52477606b78bdce568235c0acb9321c1bc4
SHA256: 14ce93ae01d50b9d2ff3c36c3edd574a9f8bcec56451f3a865fcc210c617a77b

GenP-3.4.14.1.au3
MD5: 42c434f0a040132e37eddb5b1d886f8e
SHA1: 3e94d309d3c1dbd4dd9077082e9caf5926bc0fde
SHA256: 3dd6cf96e38768110c8f0e64ae8c698e43931ff9fb57b4a1476b63f4e5d45554

config.ini
MD5: add427035968bc6f8bcdf0c5d7580495
SHA1: 7c1d13771b0546c31b87b36d1f158665ba9f793b
SHA256: 66232a4d8677cd50612eaebc664b2f2f3556b497d5bf8657967c259ef4723b68

ebd087d6171497f5268581134d6952fd.jpg
 
Последнее редактирование:
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!