Искусство редактирования интерфейса программ на VB

Дата публикации 2 окт 2008

Искусство редактирования интерфейса программ на VB — Архив WASM.RU

Вступление

Опытным реверсерам или русификаторщикам наверняка попадались программы, написанные на VB. Что удивительно, про редактирование форм и контролов на них вообще на данный момент нет никакой информации. Это надо исправлять, чем мы и займемся :smile3:

Вообще, русификаторщики, реверсеры и просто продвинутые пользователи :smile3: давно привыкли юзать Restorator или Resource Hacker для редактирования интерфейса программ. К сожалению, эти утилиты никак не видят ресурсы VB программ. Формат, в котором они хранятся, в принципе, не сложный, но кому охота писать отдельный редактор ресурсов чисто под VB? Что поделать, придется изучать этот формат самим. Для начала разберем, как найти эти самые ресурсы в VB проге. Начнем с рассмотрения оригинальной точки входа в программу. Чтобы на нее перейти из HEX редактора HIEW нам потребуется лишь загрузить EXE'шник в данный HEX редактор и нажать поочереди Enter, Enter, F8, F5. У кого хиев купленный, знают как оптимизировать эту операцию до командной строки, но это так, к слову. Нашему взгляду будет представлено примерно следующее:

Код (Text):
  1.  
  2. push 0004042E8 ;'VB5!'
  3. call ThunRTMain ;MSVBVM60 --?2

Теперь считываем структуру VBHeader по адресу 0004042E8. Как бы это не было парадоксально - больше нам для исследования никакие сторонние структуры не потребуются - все нужное мы выцепим из VBHeader.

Структура VBHeader

ПолеТипОписание
SignatureString * 4Сигнатура "VB5!"
RuntimeBuildIntegerПоказатель рантаймовости
LanguageDLLString * 14Языковая библиотека
BackupLanguageDLLString * 14Не влияет на работу EXE
RuntimeDLLVersionIntegerВерсия рантайм библиотеки
LanguageIDLongЯзык программы
BackupLanguageIDLongИспользуется совместно с LanguageDLL
aSubMainLongMain процедура, запускаемая при старте EXE. Если отсутствует, то при загрузке грузится самая первая форма
aProjectInfoLongУказатель на структуру ProjectInfo
fMDLIntObjsLong
fMDLIntObjs2Long
ThreadFlagsLongФлаги потока
ThreadCountLongЧисло потоков (смысл малопонятен, так как VB не позволяет создавать многопоточные программы)
FormCountIntegerЧисло форм в данном файле
ExternalComponentCountIntegerЧисло внешних OCX компонентов
ThunkCountLong
aGUITableLongУказатель на GUITable
aExternalComponentTableLongУказатель на ExternalComponentTable
aComRegisterDataLongУказатель на ComRegisterData
oProjectExenameLongАдрес строки с именем EXE файла
oProjectTitleLongАдрес строки с заголовком проекта
oHelpFileLongАдрес строки с именем Help файла
oProjectNameLongАдрес строки с именем проекта

Мощная структура, не правда ли? Все элементарно! Нам потребуется только FormCount для определения числа форм и указатель на структуры описывающие формы - aGUITable. Структура GUITable имеет вид:

Структура tGuiTable

ПолеТипОписание
SectionHeaderLongАдрес заголовка описывающего секции
unknown(59)ByteНеиспользуемый блок байт
FormSizeLongРазмер блока, описывающего форму и контролы, лежащие на ней
un1LongНеиспользуемый DWORD
aFormPointerLongУказатель на блок, описывающий форму и контролы, лежащие на ней
un2LongНеиспользуемый DWORD

Таких структур столько же, сколько форм в проекте, и они идут одна за одной. Чтобы получить адрес начала формы, нужно к aFormPointer прибавить 93. Этот адрес должен указывать на длину информации о форме. Тут есть небольшая хитрость - он может занимать 2 либо 4 байта. Если считанный DWORD отANDить с &H80000000 то мы определим число байт информации. Если DWORD содержит флаг &H80000000 то длина записана в 4 байта, иначе в два. После длины идет собственно описание формы и лежащих на ней контролов. Наконецто мы нашли то, что нам нужно, пора бы разобраться с бинарным форматом форм и контролов.

Бинарный формат формы

Когда-то во времена VB 1.0 for DOS все формы по умолчанию сохранялись в бинарном формате, и это не считалось ненормальным. Теперь же, когда мы привыкли, что frm файлы можно редактировать прямо в блокноте, трудно представить, что эти же формы можно представить в упакованном бинарном формате. Почему упакованном? А потому, что чтобы узнать информацию о последнем контроле на форме, нужно последовательно пропарсить все предыдущие контролы. Из этого следует, что чтобы добавить новое свойство контролу придется перепаковать всю структуру. То есть сначала декомпилировать ее, потом изменить и снова скомпилировать, как это делает VB. Сложно, не спорю, но что поделать. Самое сложное если на форме лежит ActiveX или UserControl, который нужно выделить, чтобы не изменить неизвестных его свойств. Все это оказало сильное впечатление на программеров, потому по сей день нет ни одного нормального редактора интерфеса VB программ и русификаторы к VB прогах практически не делают. Надеюсь ты прочитав эту статью разберешься, как устроены формы VB и как их разбирать и заново собирать. В бинарном упакованном виде каждый объект начинается со свойства Name и заканчивается идентификатором, по которому можно узнать идут ли дальше другие объекты, вложенность объектов и их завершение, а также меню. Свойства чередуются крайне просто. Сначала идет идентификатор свойства, потом само значение, затем следующий идентификатор. Идентификаторы FF00-FF05 зарезервированы. Вот их описание:

Код (Text):
  1.  
  2. Public Const vbFormNewChildControl = &H1FF;
  3. Public Const vbFormExistingChildControl = &H2FF;
  4. Public Const vbFormChildControl = &H3FF;
  5. Public Const vbFormEnd = &H4FF;
  6. Public Const vbFormMenu = &H5FF;

Теперь надо решить одну проблемку - откуда же нам брать идентификаторы всех свойств всех контролов? Все просто - я уже составил таблицу, которая мной была получена поутем выдирания этих свойств из TypeLib'ов VB и их многочисленными исправлениями. Таблицу берите на диске и давайте приступать к реальному примеру.

Код (Text):
  1.  
  2. 00 00 00 00-00 00 00 00-00 00 04 00-00 00 0D 00       ??  ??   ?
  3. 41 43 5F 45-78 44 65 63-5F 30 33 5F-42 00 0D 01  AC_ExDec_03_B ??
  4. 27 00 43 72-61 63 6B 6D-65 20 66 6F-72 20 4A 6F  ' Crackme for Jo
  5. 73 65 70 68-43 6F 27 73-20 45 78 44-65 63 20 50  sephCo's ExDec P
  6. 72 6F 67 72-61 6D 2E 2E-2E 00 03 08-00 00 80 19  rogram... ??  ??
  7. 01 00 42 00-23 3E 04 00-00 6C 74 00-00 36 04 00  ? B #>?  lt  6?
  8. 00 00 00 01-00 02 00 20-20 10 00 00-00 00 00 E8     ? ?   ?     ?
  9. 02 00 00 26-00 00 00 10-10 10 00 00-00 00 00 28  ?  &   ???     (
  10. 01 00 00 0E-03 00 00 28-00 00 00 20-00 00 00 40  ?  ??  (       @
  11. 00 00 00 01-00 04 00 00-00 00 00 80-02 00 00 00     ? ?     ??
  12. 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
  13. 00 00 00 00-00 80 00 00-80 00 00 00-80 80 00 80       ?  ?   ?? ?
  14. 00 00 00 80-00 80 00 80-80 00 00 80-80 80 00 C0     ? ? ??  ??? +
  15. C0 C0 00 00-00 FF 00 00-FF 00 00 00-FF FF 00 FF  ++              
  16. 00 00 00 FF-00 FF 00 FF-FF 00 00 FF-FF FF 00 00                

0D указывает на то, что имя формы содержит 0Dh символов. Далее идет имя "AC_ExDec_03_B", зевершающееся нулевым байтом, после которого опять стоит 0Dh. Следующий байт 01h определяем по таблице для формы - это Caption, следовательно после него должна идти длина строки и сама строка. Со строками у VB не все гладко - строки в некоторых свойствах объектов он хранит в ACIIZ формате, а в некоторых - в Unicode формате. Распознать формат нереально; единственный способ, просто запомнить те свойства которые имеют Unicode формат, а какие нет. К примеру Caption и Name всегда ASCII, а вот Tag, Connect и некоторые другие в Unicode формате. Вернемся к нашим данным. 03 - BackColor согласно нашей таблице, следовательно следующие 4 байта отвечают за 32 битный код цвета. Далее идет 19 - ScaleMode, следующий за ним Word определяет масштаб. 42 - WhatsThisButton, за ним 1 байт определяющий логическое True (FF) или False (0). Теперь рассмотрим самое интересное, что есть в формах - слудующий байт 23. Это Icon. Вообще при программировании на VB формы хранятся в файле frm,а вот графика и прочие большие данные хранятся в frx. frm в свою очередь ссылается на определенный адрес в этом frx, храня в котором все используемые данные простосто один за другим. После компиляции содержимое frx встраивается в форму, поэтому в рассматриваемом случае после байта 23 будет идти иконка в формате stdole.Picture. Если иконка берется дефолтная из MSVBVM60.DLL, то после 23 будет FFFFFFFF, иначе будет размер картинки. Именно столько байт мы должны считать после адреса, чтобы получить всю используемую иконку. 3E 04 00-00 = 43E = 1086 байт. Именно через столько байт кончится иконка и продолжится форма, которую мы декомпиляем:

Код (Text):
  1.  
  2. FF 00 00 35-FF 00 00 24-05 00 46 6F-72 6D 31 00     5   $? Form1
  3. 35 3C 00 00-00 59 01 00-00 CC 15 00-00 03 0C 00  5<   Y?  ¦§  ??
  4. 00 46 03 FF-01 55 00 00-00 01 06 00-46 72 61 6D   F? ?U   ?? Fram
  5. 65 31 00 03-01 11 00 42-75 74 74 6F-6E 20 69 73  e1 ??? Button is
  6. 20 69 6E 20-68 65 72 65-00 03 00 00-00 00 04 FF   in here ?    ? 
  7. FF FF 00 05-78 00 A0 05-AF 14 37 05-12 01 00 1B     ?x ???¶7??? ?
  8. 01 00 00 00-BC 02 A4 2C-02 00 0E 43-65 6E 74 75  ?   +??,? ?Centu
  9. 72 79 20 47-6F 74 68 69-63 FF 01 2B-00 00 00 03  ry Gothic ?+   ?
  10. 08 00 43 6F-6D 6D 61 6E-64 31 00 04-01 09 00 45  ? Command1 ??? E
  11. 6E 61 62 6C-65 20 4D 65-00 04 78 00-58 02 BF 13  nable Me ?x X?+?
  12. EF 01 11 02-00 FF 02 03-AE 00 00 00-02 06 00 4C  ????  ???   ?? L
  13. 61 62 65 6C-31 00 01 01-6A 00 41 63-69 64 5F 43  abel1 ??j Acid_C
  14. 6F 6F 6C 5F-31 37 38 27-73 20 45 78-44 65 63 20  ool_178's ExDec
  15. 43 72 61 63-6B 6D 65 20-30 33 2E 42-2C 20 6A 75  Crackme 03.B, ju
  16. 73 74 20 65-6E 61 62 6C-65 20 74 68-65 20 62 75  st enable the bu
  17. 74 74 6F 6E-2E 2E 2E 20-4D 61 79 62-65 20 74 68  tton... Maybe th
  18. 69 73 6F 6E-65 20 69 73-20 61 62 69-74 20 65 61  isone is abit ea
  19. 73 69 65 72-20 74 68 61-6E 20 45 78-44 65 63 20  sier than ExDec
  20. 30 33 2E 41-00 03 00 00-00 00 04 FF-FF FF 00 05  03.A ?    ?    ?
  21. 78 00 78 00-AF 14 47 04-12 00 00 25-01 00 00 00  x x ?¶G??  %?
  22. BC 02 A4 2C-02 00 0E 43-65 6E 74 75-72 79 20 47  +??,? ?Century G
  23. 6F 74 68 69-63 FF 02 04-50 00 00 00-2E F4 B5 01  othic ??P   .?¦?
  24. C9 42 34 4B-9A 3F 43 B2-41 04 7C 5E-00 00 00 00  +B4K??C¦A?|^

Теперь видим 24. Это LinkTopic, после которого идет строка, строки мы уже умеем достать потому продолжим. 35 - этого опкода в таблице нет, но я тебе расскажу - это всего лишь линейные размеры клиентской части формы. За байтом 35 идут 4 dword'а. Это соответственно ClientLeft, ClientTop, ClientWidth, ClientHeight. Затем видим 46 - StartUpPosition. Это один байт, определяющий позицию формы при запуске (в центре экрана, где получится или в центре Parent формы. Теперь мы дошли до самого интересного - FF01. Помнишь, я говорил про константы, определяющие конец одних контролов или начало других? Так вот FF01 - это vbFormNewChildControl определяет что далее идет контрол, контейнером для которого является форма. Сначало стандартно dword размер информации о следующем контроле, затем имя контрола и пошли свойства. 01 - Caption, 03 - BackColor, 04 - ForeColor, 05 - линейные размеры, декомпиляются подобно линейным размерам клиентской части формы, с одной небольшой разницей - каждый из размеров занимает не 4, а 2 байта. Продолжим 12 - TabIndex, индекс, используемый для перечисления контролов на формы при нажатии Tab. Многие программисты забывают его проставить после разработки программы, отсюда любители работать только с использованием клавы плюются и ругают программу и руки автора. Причем я их в этом поддерживаю, это свойство программист обязан выставить - это правило профессиональной разработки интерфейсов. Так вот этот индекс определяется двумя байтами, и это означает, что более 65535 контролов на форму не поместить. Затем идет 1B. Это одно из самых интересных свойств - Font. В отличии от других оно описывается классом stdole.Font,который есть только в VB. Поэтому писать декомпилятор VB не на VB это большой геморрой именно из-за классов, зашитых в библиотеки VB. В конце всех контролов мы увидим FF0204 Как мы помним 02 это vbFormExistingChildControl, то есть надо закрыть контрол и 04 это vbFormEnd закрывает форму. Вот что получится, если бы мы записывали то, что декомпиляли в уме (листинг взят из моего декомпилятора VB Decompiler):

Код (Text):
  1.  
  2. Begin VB.Form AC_ExDec_03_B 'Offset: 000010FA
  3.     Caption = "Crackme for JosephCo's ExDec Program..."
  4.     BackColor = &H80000008&
  5.     ScaleMode = 1
  6.     WhatsThisButton = 0  'False
  7.     Icon = "AC_ExDec_03_B.frx":0
  8.     LinkTopic = "Form1"
  9.     ClientLeft = 60
  10.     ClientTop = 345
  11.     ClientWidth = 5580
  12.     ClientHeight = 3075
  13.     StartUpPosition = 3 'Windows Default
  14.     Begin VB.Frame Frame1 'Offset: 000015A6
  15.         Caption = "Button is in here"
  16.         BackColor = &H0&
  17.         ForeColor = &HFFFFFF&
  18.         Left = 120
  19.         Top = 1440
  20.         Width = 5295
  21.         Height = 1335
  22.         TabIndex = 1
  23.         BeginProperty Font
  24.             Name = "Century Gothic"
  25.             Size = 14,25
  26.             Charset = 0
  27.             Weight = 700
  28.             Underline = 0 'False
  29.             Italic = 0 'False
  30.             Strikethrough = 0 'False
  31.         EndProperty
  32.         ... и так далее

Теперь предлагаю на конкретном примере разблокировать залоченное меню и показать невидимую кнопку.

Разлочка меню

Специально для демонстрации залоченного меню я написал простенький крякми.


В декомпиляторе отчетливо видно, что патчить.

В нем заблокирован пункт меню "Сохранить", как это обычно бывает в коммерческих программах. Попробуем его разблокировать. Для начала необходимо знать как можно сделать меню неактивным. Тут есть два способа. Первый - при проектировании поставить свойство Enabled у меню в False, второй - установить это свойство кодом при запуске формы. Представим что кодер поленился и установил это свойство при разработке меню. Тогда декомпиляем этот проект и поглядим. Для простоты не будем заново декомпилять в уме, а возьмем мой декомпилятор (Lite версия вполне подойдет) и декомпиляем. В разделе формы всего одна форма. Поищем там меню:

Код (Text):
  1.  
  2. Begin VB.Menu mnuFile 'Offset: 000011B3
  3.     Caption = "Файл"
  4.     Begin VB.Menu mnuSave 'Offset: 000011CF
  5.         Caption = "Сохранить"
  6.         Enabled = 0  'False
  7.     End
  8.     Begin VB.Menu Separator 'Offset: 000011F0
  9.         Caption = "-"
  10.     End
  11.     Begin VB.Menu mnuExit 'Offset: 0000120B
  12.         Caption = "Выход"
  13.     End
  14. End

Опа, "Enabled = 0" это то что мы искали. Теперь подумаем как поправить. Откроем программу в hiew и перейдем по адресу 11F0:

Код (Text):
  1.  
  2. 00 00 02 07-00 6D 6E 75-53 61 76 65-00 13 03 09  O• mnuSave !¦0
  3. 00 D1 EE F5-F0 E0 ED E8-F2 FC 00 05-00 FF 02 1A  Сохранить ¦ яO>

Все стандартно сначало Name, затем Caption (03) и 05 - Enabled. После него идет один байт 00б что означает False. Замени на FF (True) и попробуй запустить. Меню разблокировано и при нажитии на него выводится MessageBox "cool". Вот и все - легкий взлом верно?

Палим инвиз

Несмотря на ICQ'шный стиль фразы мы не будем писать плагин детекта инвиза :smile3: Мы будем делать видимыми скрытые кнопки.


Декомпилятор и на этот раз раскусил злой замысел шароварщика.

Для большего реализма я написал крякми, который после запуска делает видимой кнопку "Сохранить" через 3 секунды. Типа, если мы его купим, то ждать не придется - стандартно для Trial прог. Что ж, поглядим в декомпиляторе, что у нас с кнопкой "Сохранить":

Код (Text):
  1.  
  2. Begin VB.CommandButton cmdSave 'Offset: 00001175
  3.     Caption = "Сохранить"
  4.     Left = 1680
  5.     Top = 1800
  6.     Width = 1335
  7.     Height = 375
  8.     Visible = 0  'False
  9.     TabIndex = 1
  10. End

Сразу бросается в глаза "Visible = 0", которое в таблице значится как 09. Переходим по смещению 1175 и проходим все свойства до 09. Видим 00, это False, меняем на FF (True) и дело сделано. Но! Данный крякми я сделал специально так, чтобы его можно было взломать разными способами. Рассмотрим еще один способ. Как можно сделать кнопку видимой через 3 секунды? Можно сделать цикл при запуске программы, но он на разных процессорах будет работать с разной скоростью. Можно использовать GetTickCount, но потребуется его проверять в While цикле, что тоже неудобно. Это все геморрой, который программисты не любят, потому они юзают таймеры. Таймер - это невидимый контрол на форме, у которого событие срабатывает каждые Interval милисекунд. Поищем ка мы любимый таймерна форме:

Код (Text):
  1.  
  2. Begin VB.Timer Timer1 'Offset: 00001155
  3.     Interval = 3000
  4.     Left = 2880
  5.     Top = 0
  6.     Width = 59400
  7.     Height = 8
  8. End

Так и есть! 3000 милисекунд это 3 секунды. Заменим ка мы их на 1. Но! Автор забыл поставить End после установки свойства в кнопку поэтому таймер после события будет срабатывать и дальше и тормозить программу, потому проще использовать предыдущий способ, а таймер просто отключить, поставив интервал в 0.

Добавление новых свойств

Читая статью, ты наверняка задался вопросом, а как же добавить новое свойство в упакованный контрол? Добавить можно только разбором, вставкой и последующей сборкой обратно всей формы. Ясное дело что уже на старое место данные не поместятся, поэтому придется создавать новую секцию в файле или расширять последнюю и редиректить данные туда. Кроме того адрес на новое расположение формы придется прописать в структуре информации о форме. При этом нужно учитывать что если пользователь будет часто добавлять свойства, то нужно заранее в новой секции сделать запас в виде резервных байт под расширение каждой формы. Всю эту информацию о резерве байт и начале и длине каждой формы, вынесенной в новую секцию придется хранить. Для этого придется создать свою служебную структуру. Это все будет полезно если ты будешь писать свой редактор ресурсов VB. Если же ты просто собираешься исследовать программы, то знаний из этой статьи тебе вполне хватит.


Подпись: Луxitt средство для исследования VB программ

Заключение

Надеюсь, что у тебя не осталось после прочтения статьи вопросов по теме редактирования интерфейса VB программ. Если же они всеже есть - задавай их на моем форуме, посвященном вопросам декомпилированию VB http://vbdecompiler.dotfix.net я его периодически читаю и отвечаю на вопросы :smile3: © GPcH


0 2.079
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532