1.0 Введение
Хоть компания Adobe и ушла из России, но потребность в их софте всё ещё остаётся. Решают её по-разному: кто-то использует другие аккаунты, кто-то использует ключи, а кто-то одевает повязку и спускает на воду свой цифровой фрегат.
На последнем мы как раз и остановимся. В этой статье проанализируем Adobe GenP.
Adobe GenP — это патчер, предназначенный для активации продуктов Adobe Creative Cloud, начиная с версий 2019 года и до текущих релизов. Он позволяет устанавливать и использовать приложения Adobe: Photoshop, Illustrator, Premiere Pro и другие, без необходимости приобретения лицензии.
Патчер — это программа, предназначенная для модификации программного обеспечения. Чаще всего используемая для обхода проверки лицензий и других защитных механизмов.
В отличие от многих других патчеров у него есть даже своё комьюнити на Reddit!
Кстати, помимо основного сообщества рекомендую обратить внимание на 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 Вывод
2.0 Файлы
Архив с GenP, который будет разбирать, был загружен с Reddit.
Его контрольные суммы:
MD5:
Код:
6b104ba9deb749a6b6ce88b9c6997dae
SHA1:
Код:
19d9b52477606b78bdce568235c0acb9321c1bc4
SHA256:
Код:
14ce93ae01d50b9d2ff3c36c3edd574a9f8bcec56451f3a865fcc210c617a77b
Архив с GenP состоит из следующих частей:
Исполняемый файл и исходник со всем необходимым, если мы хотим собрать патчер сами. Исходник написан в виде скрипта для AutoIt.
3.0 Установка Creative Cloud и первый запуск
3.1 Про установку Adobe
Установщик для Creative Cloud (далее CC) загружаем с официального сайта. На момент написания статьи его MD5:
Код:
0011ec2b0f49f83ccf67aa706a638ccc
. Но и с более новыми версиями всё работает (дата проверки 27.09.2024).
ОБЯЗАТЕЛЬНО снимаем тут галочку, чтобы патчи не слетали. Должно быть так, как на скрине.
3.2 Запуск GenP
После установки видим магазин приложений, где нам предлагают купить софт.
Попробуем запустить GenP.
Он предлагает повысить привилегии GenP, чтобы нормально пропатчить XD и UWP-приложения. Откажемся.
Патчинг происходит таким образом:
- Патчим Adobe Creative Cloud, чтобы скачивать приложения - кнопка "Patch CC"
- Патчим отдельные приложения - кнопки "Search" и "Patch".
Нажмём "Patch CC".
Все приложения Adobe CC закрываются. Выводится лог патча: какой файл патчится и что в нём ищется/заменяется - это первая/вторая строка после названия файла. Попробуем запустить Adobe CC.
Появилась возможность скачивать любое приложение, как будто у нас есть подписка. Скачаем и запустим Adobe Audition для теста.
Нас просят купить подписку. Вернёмся в GenP и выполним поиск.
Пропатчим. Перед этим желательно закрыть Audition.
Нам вывелся лог патча. Попробуем запустить снова программу.
Теперь можем спокойно использовать софт. Также есть возможность заблокировать всплывающие окна (pop-up's). Adobe Illustrator также успешно патчится.
Более того, мы можем восстановить оригинальные файлы до патча через кнопку "Restore".
4.0 Разбираемся в исходном коде
Код представлен в виде скрипта для AutoIt.
Читается как многие скриптовые языки вроде того же 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 ****
- - отключает иконку в системном трее для этого скрипта.
- - требует права администратора для выполнения скрипта.
Код:
#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
#
include
#
include
#
include
#
include
#
include
#
include
#
include
#
include
#
include
Далее небольшой блок кода:
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 окно программы не будет закрываться. Вторая строка указывает заголовок приложения и дополнительную информацию о приложении.
Блок
служит для запуска только одного экземпляра приложения.
Затем объявляются глобальные переменные:
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
Далее идёт проверка наличия
. Если его нет, то он создаётся по указанному пути.
Visual Basic:
Код:
Global
$
sINIPath
=
@
ScriptDir
&
"\config.ini"
If
Not
FileExists
(
$
sINIPath
)
Then
FileInstall
(
"config.ini"
,
@
ScriptDir
&
"\config.ini"
)
EndIf
Этот конфиг-файл выглядит так:
Часть контента скрыл, чтобы скрин поместился на экран. Этот файл важен для работы патчера, так как содержит названия файлов, что будет искать патчер, сами патчи и указания, какие конкретно патчи к чему применяются.
- Секция
:- Содержит ключ Path, который указывает путь по умолчанию до папки с программами Adobe.
- Секция
:- Содержит список файлов, которые будет искать патчер.
- Секция
:- Содержит ключ Values с набором имён применяемых патчей. Используется для тех программ, что нет в [CustomPatterns].
- Секция
:- Содержит ключи для различных файлов и их соответствующие имена патчей.
- Секция
:- Содержит ключи (имена патчей) и значения (сами патчи в виде искомых и заменяемых байт).
Затем мы читаем из 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.
Вот краткое объяснение функции:
Сначала происходит копирование файла
во временную директорию (обычно
Код:
C:\Users\\AppData\Local\Temp
). Далее проверка.
Если скрипт не запущен от имени
и файл
существует:
Выводится диалоговое окно с вопросом о повышении привилегий до TrustedInstaller.
Если пользователь соглашается, скрипт перезапускается с повышенными привилегиями.
Если условия не выполнены:
Проверяется существование директории
Код:
"C:\Windows\System32\config\systemprofile\Desktop"
. Нужна для нормальной работы приложений, чьи привилегии были повышены.
Если директория не существует, она создаётся.
В конце удаление временного файла
.
Затем снова объявляются глобальные переменные:
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
Далее идёт код, что читает содержимое секций
и
из
.
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
-
из конфига.
-
из конфига.
Затем идёт блок кода для настройки GUI-части:
Visual Basic:
Код:
GUIRegisterMsg
(
$
WM_COMMAND
,
"WM_COMMAND"
)
MainGui
(
)
Функция
находится в самом скрипте и создает графический интерфейс для приложения: табы, ListView, кнопки для выбора пути, поиска, патчинга, блокировки всплывающих окон и восстановления файлов, а также прогресс-бар и лог активности.
После этого идёт главный код в цикле
.
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" в поиске.
Далее в коде будет обработка событий для кнопки "Search".
4.2.1. Обработчик кнопки поиска
В самом начале идёт настройка списка файлов (в
) под вывод и появление надписи, что нам нужно подождать:
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
)
Функция
самописная:
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
: Раскрывает все группы в ListView, делая их видимыми и сворачиваемыми.
: Сворачивает все группы, если они были раскрыты, и наоборот, раскрывает все группы, если они были свернуты.
Далее идут подготовительные блоки:
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
Параметр
в этот момент содержит содержимое секции
из INI-файла:
INI:
Код:
[Default]
Path
=C:\Program Files\Adobe
Функция
самописная:
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
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
(секция
в INI) и добавляет пути до них в массив
- файла для патчинга, которые нашлись из
. Также если где-то нашлись бекапы оригинальных файлов (с постфиксом
), то они добавляются в
.
Для теста можно добавить вывод массива в процессе работы.
Или так:
Также в логах выводится дополнительная информация.
Далее в обработчике кнопки поиска идёт блок, который также ищет файлы, но в заранее определённых директориях 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
Краткое описание этих функций:
- записывает строку текста в элемент управления
(текстовое поле для многострочного ввода).
- записывает строку текста в элемент управления
(журнал) со временной меткой к сообщению или без неё.
- переключает видимость между элементами управления
и
.
- копирует текст из видимого элемента управления (либо
, либо
) в буфер обмена. Если элемент
скрыт, копируется текст из
, иначе копируется текст из
.
Тут можно увидеть использование функции
. Её код прилагается:
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"
,
0
x000E
,
"wparam"
,
0
,
"lparam"
,
0
)
DllCall
(
"user32.dll"
,
"lresult"
,
"SendMessageW"
,
"hwnd"
,
$
hWnd
,
"uint"
,
0
xB1
,
"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"
,
0
xC2
,
"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
Функция
самописная.
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-файле, очищает и обновляет
с новыми элементами, восстанавливает скрытый атрибут и выводит сообщение с инструкцией.
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
4.2.4. Обработчик кнопки Pop-up
Следующий обработчик для кнопки "Pop-up":
Visual Basic:
Код:
Case
$
idMsg
=
$
idBtnBlockPopUp ; Pop
-
up button
ToggleLog
(
0
)
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-адресов доменов
и
Код:
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-адресов) — это процесс преобразования доменного имени (например,
) в его соответствующий IP-адрес (например,
).
В функции выполняется PowerShell-скрипт для разрешения IP-адресов доменов
и
Код:
3u6k9as4bj.adobestats.io
:
- Читается вывод скрипта в переменную
.
- Если в выводе содержится "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) для домена
. Затем собирает уникальные IP-адреса для доменов
и
Код:
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..."
Далее идёт блок кода, что работает с файлом
.
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
=
'# '
$endMarker
=
'# '
$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...'
}
Данный скрипт выполняет предварительные действия:
- Проверка наличие прав администратора. Если нет, то завершает выполнение.
- Создание резервной копии файла
:
Код:
C:\Windows\System32\drivers\etc\hosts.bak
- Загрузка файла
Код:
https://a.dove.isdumb.one/list.txt
и разделяет загруженный контент по строкам и удаляет пробелы в начале и конце каждой строки.
Файл
можно взять из этого репозитория: GitHub - ignaciocastro/a-dove-is-dumb: Easily block Adobe telemetry checking domains. Continuously Updated. Useful for HostsMan / SwitchHosts / Pi-hole users
А далее происходит работа с файлом
. Мы записываем содержимое
между строками
.
До выполнения скрипта:
Код:
После:
Код:
Код:
127.0.0.1 localhost
#
# Данные из list.txt
#
Таким образом, функция
просто добавляет правила в брандмауэр и записи в файл
. Теперь настало время самого интересного - начинаем разбор патчинга Creative Cloud и программ Adobe.
4.2.5. Обработчик кнопки "Patch CC" и как работает эта магия
Обработкой кнопки "Patch CC" занимается вот этот обработчик:
Visual Basic:
Код:
Case
$
idMsg
=
$
idBtnPatchCC ; Patch Creative Cloud button
Global
$
appsPanelFile
Global
$
containerBLFile
Global
$
adobeDesktopServiceFile
.
.
.
Первое, что патчит код - это
.
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
Функции передаётся путь до файла. Затем из него извлекается:
- Имя файла вместе с расширением;
- Отдельно расширение файла.
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
в зависимости от того, есть ли искомый файл в
- это секция
в INI-файле.
Для дальнейшего понимания патчера важно запомнить, что:
Код:
$TargetFileList_Adobe
-
из конфига.
-
из конфига.
Если такой файл есть, то вызывается
Код:
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
- имя файла
- 1 или 0 (True или False)
- путь до файла
Если передаётся 0, то используется
. Иначе -
. Затем начинается основной цикл, где происходит обработка данных из 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"
...
В секции
есть ключи, имена которых - это имена файлов для патчинга. У имён-файлов (ключей) есть значения. Значения используются в секции
, которая содержит байты для поиска и патчинга.
Например, если патчится файл
, то используются патчи
,
(из-за прямого указания в строке
Код:
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"
Байты для поиска и замены разделены символов
. Для
будут искаться байты
Код:
"8378????0F84????????8378????0F84????????8378????0F84????????33C0"
, а заменой им будет
Код:
"C640????0F84????????C640????0F84????????C640????0F84????????33C0"
.
Затем поиск и замена будут выполнены для
. До знаков вопроса в патчах мы сейчас дойдём. Для понимания нам нужно изучить
Код:
MyRegExpGlobalPatternSearch
. Вот так она вызывается в
Код:
MyRegExpGlobalPatternSearch
:
Visual Basic:
Код:
MyRegExpGlobalPatternSearch
(
$
MyFileToParse
,
$
sSearch
,
$
sReplace
,
$
sPattern
)
- путь до файла
- байты, которые ищутся (до символа | в секции с патчами)
- байты, на которые произойдёт замена (после символа | в секции с патчами)
- значение ключа из набора в секции
(
,
и другие).
Функция
Код:
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.
Далее функция читает файл, путь до которого был ей передан, и находит
точные байты в нём (без знаков ?), добавляя их в массив
:
Visual Basic:
Код:
_ArrayAdd
(
$
aOutHexGlobalArray
,
$
sWildcardSearchPattern
)
_ArrayAdd
(
$
aOutHexGlobalArray
,
$
sFinalReplacePattern
)
В первый элемент массива будут добавлены точные байты, которые были найдены в файле.
Во второй элемент массива будут добавлены точные байты, на которые произойдёт замена.
Функция заменяет знаки
на байты из самого файла, чтобы получились "точные байты". Забегая вперёд, потом благодаря массиву
функция
Код:
MyGlobalPatternPatch
просто пропатчит точные искомые байты на точные заменяемые. Но перед этим создаст резервную копию.
Рассмотрим схематично замену байтов в файле. Значения
для файла
:
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,
и так далее
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
Данная функция открывает файл и благодаря глобальному массиву
ищет точные байты в файле и заменяет их на точные нужные. Передачу этого массива, как аргумента, можно найти в обработчике. Перед записью изменения создаётся бекап файла с расширением
, чтобы в случае чего его можно было бы восстановить. Таким образом,
Код:
MyGlobalPatternSearch
и все функции, что вызываются внутри неё, ищут точные байты в файле
для замены и на основе них создают точные байты,
на которые произойдёт замена. Саму замену байтов выполняет
Код:
MyGlobalPatternPatch
. Связаны 2 эти функции через
.
Таким образом, во всём обработчике "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
Вместо
может быть
. У меня как раз этот случай. Патчинг этих файлов позволяет
скачивать платные программы Adobe из Creative Cloud.
До патча:
После патча:
Экспериментально у себя я выяснил, что для достижения такого результата обязательно нужно пропатчить
и
. Вероятно, патчинг
Код:
Adobe Desktop Service.exe
нужен для подстраховки.
4.2.5.3. Сравниваем пропатченные байты с оригинальными и ищем их
Мы сравним файл до патча и после патча. Я буду делать это через IDA. Но сначала нужно найти точные байты.
Это можно сделать этой утилитой на C++:
C++:
Код:
#include
#include
#include
#include
#include
#include
#include
#include
struct
Pattern
{
std
::
string name
;
std
::
vector
>
patterns
;
}
;
// Функция для парсинга паттернов
std
::
vector
parsePatterns
(
)
{
std
::
vector
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
&
bytes
,
std
::
vector
&
mask
)
{
for
(
size_t i
=
0
;
i
(
std
::
stoi
(
byteString
,
nullptr
,
16
)
)
;
bytes
.
push_back
(
byte
)
;
mask
.
push_back
(
0xFF
)
;
}
catch
(
const
std
::
invalid_argument
&
e
)
{
std
::
cerr
&
data
,
const
std
::
vector
&
pattern
,
const
std
::
vector
&
mask
,
size_t pos
)
{
for
(
size_t i
=
0
;
i
&
data
,
const
std
::
vector
&
pattern
,
const
std
::
vector
&
mask
,
const
std
::
string
&
patternType
,
bool
&
headerPrinted
,
const
std
::
string
&
patternName
)
{
bool
found
=
false
;
for
(
size_t i
=
0
;
i
"
(
data
[
i
+
j
]
)
"
data
(
(
std
::
istreambuf_iterator
(
file
)
)
,
std
::
istreambuf_iterator
(
)
)
;
auto
patterns
=
parsePatterns
(
)
;
for
(
const
auto
&
pattern
:
patterns
)
{
bool
anyFound
=
false
;
bool
headerPrinted
=
false
;
for
(
const
auto
&
[
searchPattern
,
replacePattern
]
:
pattern
.
patterns
)
{
std
::
vector
searchBytes
,
searchMask
;
parseHexPattern
(
searchPattern
,
searchBytes
,
searchMask
)
;
std
::
vector
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
search.exe AppsPanelBL.dll
Искомый : 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
Искомый : 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
Искомый : 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
Только иногда она находит лишние байты. Но нам нужно искать только те, что патчатся. Для
это будет
и
. На
просто не смотрим.
INI:
Код:
AppsPanelBL.dll
="CreativeCloud1","CreativeCloud2"
Далее файл нужно открыть в IDA, нажать ALT+B и ввести первые 5-10 найденных байт:
Нажимаем 2 раза на запись.
Попали сюда. Теперь отобразим опкоды инструкций.
Таким образом можно сравнивать файлы до патча и после. Так мы посмотрим, как поменяются инструкции.
Так как GenP делает бекапы (.bak), то можно сравнивать бекапы с пропатченными файлами.
Код:
C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe
Слева я буду располагать файл до патча, а справа после. Если хотите перейти в режим листинга IDA (как на скрине), нажмите пробел. И снова в настройках включите отображение опкодов.
Код:
C:\Program Files\Common Files\Adobe\Adobe Desktop Common\AppsPanel\AppsPanelBL.dll
Код:
C:\Program Files\Common Files\Adobe\Adobe Desktop Common\ADS\ContainerBL.dll
Проанализировав различия между этими файлами, видим, что ничего вредоносного в них он не добавляет. Просто меняет ход выполнения программы. Где-то через замену инструкций условных переходов, где-то через замену инструкций перед ними. Можно даже сделать всё это вручную и получить тот же результат.
Далее нам нужно будет рассмотреть, как работает обработчик патчинга файлов самих программ Adobe. Но перед этим рассмотрим обработчик кнопки восстановления оригинального файла - "Restore".
4.2.6. Обработчик кнопки "Restore"
Она появляется, если после поиска файлов для патча (кнопка "Search") мы выберем какой-либо или какие-либо из них, и есть файл
для выбранного.
Начало его обработчика:
Visual Basic:
Код:
Case
$
idMsg
=
$
idBtnRestore
GUICtrlSetData
(
$
idLog
,
"Activity Log"
&
@
CRLF
)
ToggleLog
(
0
)
GUICtrlSetState
(
$
idListview
,
128
)
GUICtrlSetState
(
$
idBtnDeselectAll
,
128
)
GUICtrlSetState
(
$
idButtonSearch
,
128
)
GUICtrlSetState
(
$
idBtnCure
,
128
)
Основная функция там - это
:
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
Тут всё просто
Удаляем пропатченный файл и переименовываем
в файл без постфикса
. Конечно же, если бекап этого файла существует.
4.2.7. Обработчик кнопки "Patch"
Ну а теперь переходим к обработчику кнопки "Patch", что доступна после "Search".
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-файла для этого искомого файла, затем проверяется сам файл и уже далее ищутся байты в нём.
После поиска нужных байт и занесения их в
, вызывается
Код:
MyGlobalPatternPatch
. Тут всё, как в патчинге самого Creative Cloud. Но отличие в том, что при патчинге Creative Cloud искомые файлы были в секции
:
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"
Файлов из
явно больше. Как быть с теми, что не в секции со специальными файлами?
В таком случае используется набор патчей из секции
:
INI:
Код:
[DefaultPatterns]
Values
="ProfileExpired1","ProfileExpired3","ProfileExpired4","ProfileExpired5","ProfileExpired6","ValidateLicense1","ValidateLicense2","ValidateLicense3","CmpEax61","CmpEax62","CmpEax63","CmpEax64","Profile1","Profile2","Banner1","Banner2","InstantShutdown1"
Патчи по умолчанию применяются для всех файлов, что не находятся в секции
. В функции
Код:
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
.
.
.
Явно видно, что в случае если
равен 1, то читается значение ключа
из секции
. А если же файл будет из секции
, то и использоваться будут те байты, что указаны в этой секции. Например, как с
:
INI:
Код:
[CustomPatterns]
Acrobat.dll
="Acrobat3","Acrobat5"
5.0 Изучаем патчи из INI-файла
Теперь рассмотрим патчи из секции
. Как и с файлами Creative Cloud, слева на скринах будет файл до патча, а справа - после. Сначала рассмотрим оставшиеся значения для ключей из
, а затем из
. Важно учесть то, что не все значения для ключей будут найдены и применены. Некоторые останутся ненайденными и байты из них не будут использоваться для патча. Скорее всего, они использовались у некоторых старых версий приложений. Я покажу в IDA только то, что нашлось у меня. Также в файлах могут встречаться байты из других. Дубликаты я буду пропускать.
5.1. Секция CustomPatterns
Код:
C:\Program Files\Adobe\Acrobat DC\Acrobat\Acrobat.dll
Код:
C:\Program Files\Adobe\Acrobat DC\Acrobat\acrodistdll.dll
Код:
C:\Program Files\Adobe\Acrobat DC\Acrobat\acrotray.exe
Новых нет.
Код:
C:\Program Files\Adobe\Adobe Bridge 2024\Adobe Bridge.exe
Код:
C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\dvaappsupport.dll
Код:
C:\Program Files\Adobe\Adobe After Effects 2024\Support Files\SweetPeaSupport.dll
Код:
C:\Program Files\Adobe\Adobe Photoshop 2024\Required\UXP\com.adobe.ccx.start\js\4.js
Код:
C:\Program Files\Adobe\Adobe Photoshop 2024\Required\UXP\com.adobe.ccx.start\manifest.json
5.2. Секция DefaultPatterns
Код:
C:\Program Files\Adobe\Adobe Photoshop 2024\Photoshop.exe
Код:
C:\Program Files\Adobe\Adobe Illustrator 2024\Support Files\Contents\Windows\Illustrator.exe
Код:
C:\Program Files\Adobe\Adobe Substance 3D Designer\Adobe Substance 3D Designer.exe
Код:
C:\Program Files\Adobe\Adobe Dreamweaver 2021\Dreamweaver.exe
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"
В этом случае можно поступить по-другому: мы используем онлайн-дизассемблер (Online x86 and x64 Intel Instruction Assembler). Передадим в него искомые и заменяемые байты, чтобы сравнить. Знаки
я заменю
вот так:
https://cyberchef.org/#recipe=Find_/_Replace(%7B'option':'Simple%20string','string':'? ?'%7D,'00',true,false,true,false)From_Hex('Auto')T o_Hex('Space',0)&input=NDg4QkNGRTg/Pz8/Pz8/P0ZGQzAwRjg0Pz8/Pz8/Pz80ODhEPz8/Pz8/Pz8/PzQ4OEJDRkU4Pz8/Pz8/Pz8zMUMwNzU/PzhE
Этот способ даст не на 100% точный вывод дизассемблера, а приблизительный. Но точный нам тут и не нужен. Для проверки того, вредоносен ли патч, хватит и приблизительного вывода. К тому же изменения в патчах маленькие, обычно меняют инструкции перед условным переходом, сам условный переход или что-то заменяют чем-то. Это всё не будет вредоносным.
6.0 Вывод
Мы вместе с вами изучили этот патчер и не нашли ничего особо вредоносного внутри него. Даже рассмотрели все патчи и все функции.
Но важный момент - мы работали с исходником GenP. Помимо исходника в архиве идёт ещё и EXE-файл. Насчёт его ничего сказать не могу, так как ещё не ревёрсил его. Поэтому если вы хотите тоже его изучить,
для безопасности ЗАГРУЖАЙТЕ AutoIt С ОФИЦИАЛЬНОГО САЙТА И СОБИРАЙТЕ СВОЙ EXE ИЗ ИСХОДНИКА! Также обращайте внимание на версию GenP. Ниже я прикреплю контрольные суммы файлов, что были рассмотрены в статье.
Архив с GenP:
MD5:
Код:
6b104ba9deb749a6b6ce88b9c6997dae
SHA1:
Код:
19d9b52477606b78bdce568235c0acb9321c1bc4
SHA256:
Код:
14ce93ae01d50b9d2ff3c36c3edd574a9f8bcec56451f3a865fcc210c617a77b
MD5:
Код:
42c434f0a040132e37eddb5b1d886f8e
SHA1:
Код:
3e94d309d3c1dbd4dd9077082e9caf5926bc0fde
SHA256:
Код:
3dd6cf96e38768110c8f0e64ae8c698e43931ff9fb57b4a1476b63f4e5d45554
MD5:
Код:
add427035968bc6f8bcdf0c5d7580495
SHA1:
Код:
7c1d13771b0546c31b87b36d1f158665ba9f793b
SHA256:
Код:
66232a4d8677cd50612eaebc664b2f2f3556b497d5bf8657967c259ef4723b68