# Улучшенная декомпиляция Native Code в VB Decompiler v26.2

> Мы рады сообщить о выходе VB Decompiler версии 26.2. Это крайне насыщенное обновление, сосредоточенное на существенном повышении точности декомпиляции…

Canonical HTML: <https://www.dotfix.net/doc/vb_decompiler_262.htm>

**Автор:** **Сергей Чубченко**. **Дата публикации:** 05.04.2026

## Улучшенная декомпиляция Native Code в VB Decompiler v26.2


Мы рады сообщить о выходе [VB Decompiler](https://www.dotfix.net/vb_decompiler.htm) версии 26.2. Это крайне насыщенное обновление, сосредоточенное на существенном повышении точности декомпиляции программ, скомпилированных в Native Code. Трёхуровневая система определения переменных типа Variant, полноценная поддержка чтения и записи многомерных массивов SafeArray, изоляция состояний ветвей If ... Else ... End If на основе снимков, значительно расширенная эвристическая база данных COM/Interface ID, а также многочисленные исправления и рефакторинг на уровне ядра, делающие декомпилятор более надёжным при работе со сложными реальными бинарными файлами.

### Трёхуровневое определение переменных типа Variant в Native Code


Одна из ключевых сложностей при декомпиляции VB6 Native Code - корректное определение переменных типа **Variant** на стеке во время вызовов функций. Variant является универсальным контейнерным типом VB6 и может хранить целые числа, строки, объекты и значения любых других подтипов во время выполнения. В скомпилированном нативном коде они передаются как сырые структуры в памяти без явных аннотаций типов, что делает точное восстановление крайне затруднительным.


В версии 26.2 реализован многоуровневый подход обнаружения, решающий вопрос сразу с нескольких сторон одновременно:

- **Эвристическое обнаружение на основе анализа стека.** Декомпилятор анализирует компоновку стека и соглашения о вызовах, чтобы определить места построения и передачи структур Variant даже при полном отсутствии иной контекстной информации.

- **Сигнатурное обнаружение с использованием заранее подготовленной базы данных свойств и методов.** Тщательно подобранная база данных известных свойств и методов объектов, охватывающая наиболее часто вызываемые COM-интерфейсы в программах VB6, позволяет декомпилятору распознавать параметры типа Variant путём сопоставления вызываемой цели с известными сигнатурами.

- **Определение типа на лету путём разбора интерфейса вызываемого объекта по его Interface ID.** Когда декомпилятор сталкивается с вызовом с поздним связыванием и может определить идентификатор интерфейса (Interface ID) целевого объекта, он разбирает описание интерфейса объекта в процессе декомпиляции и использует полученную информацию для точного определения типов параметров.


В совокупности эти три механизма существенно сокращают число случаев, когда параметры типа Variant остаются неопределёнными или определяются неверно - особенно в коде, активно использующем COM-автоматизацию.


Разбор параметров вызываемых функций при **раннем связывании** (разрешаемом на этапе компиляции через TypeLib) и **позднем связывании** (разрешаемом во время выполнения через `IDispatch`), а также при прямых вызовах **Win32 API**, также стал значительно более точным и последовательным.

![Декомпилированный VB6 Native Code: корректно определённые параметры типа Variant в вызовах функций до и после улучшения трёхуровневого обнаружения](https://www.dotfix.net/images/doc/vb_decompiler_262/vb_decompiler_262_variant_detection.png)

### Расширенный разбор подтипов Variant


Наряду с новыми уровнями обнаружения расширен и сам **парсер типа Variant**. Ранее парсер выполнял специализированную обработку лишь пяти подтипов: `vbBoolean`, `vbSingle`, `vbDouble`, `vbCurrency` и `vbDate`. Все прочие подтипы обрабатывались обобщённо, а их типы определялись исключительно эвристически.


В версии 26.2 добавлена специализированная поддержка разбора ещё пяти подтипов:

- **vbInteger** - 16-битные знаковые целые числа.

- **vbLong** - 32-битные знаковые целые числа.

- **vbByte** - беззнаковые 8-битные значения.

- **vbString** - строковые значения BSTR, хранящиеся внутри Variant.

- **vbObject** - ссылки на объекты внутри Variant.


Разбор TypeLibrary и конвейер построения базы данных объектов обновлены с учётом новых подтипов. Это теперь позволяет точно и последовательно определять типы в тех фрагментах кода, которые ранее обрабатывались исключительно эвристически, что даёт более чистый и точный результат декомпиляции.

### Поддержка большинства сценариев SafeArray: чтение и запись многомерных массивов


Одно из наиболее значимых улучшений декомпиляции в версии 26.2. VB6 хранит большинство массивов внутри в виде структур **SafeArray** - COM-дескриптора, содержащего тип элемента, размерность, границы и указатель на данные. Для корректного восстановления обращений к массивам из нативного кода декомпилятор должен полностью понимать эту структуру: разобрать дескриптор, отследить количество измерений и их границы, вычислить адреса элементов из многоиндексных выражений и отобразить всё это обратно в читаемый VB6 код.


В предыдущих версиях VB Decompiler поддержка SafeArray была лишь частичной. Декомпилятор распознавал операции **создания и изменения размера массивов** - вызовы `__vbaRedim`, `__vbaRedimPreserve` и аналогичных функций среды выполнения VB - и корректно генерировал соответствующие инструкции `ReDim`. Однако само по себе **чтение и запись элементов массива** во время выполнения не поддерживалось. Скомпилированный код, обращающийся к памяти массива - загрузка указателя SafeArray, чтение дескрипторов измерений, вычисление смещения в памяти по многомерным индексам и разыменование элемента - оставался нераскрытым. Декомпилятор выдавал его в виде запутанной последовательности выражений на уровне регистров, арифметики указателей и частично вычисленных стековых операций, не имеющей ничего общего с исходным кодом.


Результат был особенно печальный для методов, содержащих интенсивную работу с массивами. Обращения к элементам массивов превращались в множественные  выражения, смешивающие сырые смещения в памяти с частично распознанными полями структур - всё это соединялось знаками равенства, скобками и значениями индексов так, что не имело синтаксической силы ни в одном языке. Любое осмысленное восстановление логики метода из такого вывода было практически невозможным.

#### Как это выглядело раньше


Чтобы наглядно представить масштаб проблемы, рассмотрим фрагмент из результата декомпиляции `до` - реального метода, работающего с четырёхмерным массивом. В выводе появлялись следующие строки:

```
`loc_00412DEE:   If var_7C(152)(156) = var_7C(152)(160) Then
loc_00412DF8:     If var_7C(152)(156) = var_7C(152)(160) = 4 Then
loc_00412E15:       If (eax - var_7C(152)(156) = var_7C(152)(160)(44)) >= var_7C(152)(156) = var_7C(152)(160)(40) Then
loc_00412E62:       var_20(24)*(edi - var_7C(152)(156) = var_7C(152)(160)(20)) = ...
loc_00412E68:       var_20(24)*(edi - ...)+(ebx - ...)*var_7C(152)(156) = ... + var_BC
loc_00412E72:       var_20(24)*(edi - ...)+(ebx - ...)*...+var_BC*... = ... + (eax - ...)`
```


Эти строки отражают то, что декомпилятор выдавал при встрече с чтением и записью элементов SafeArray без анализа и разбора внутренней структуры SafeArray. Каждое выражение в действительности является свёрнутым представлением сырой арифметики указателей над полями дескрипторов `SAFEARRAY` и `SAFEARRAYBOUND` - членами `cElements` и `lLbound` записи границ каждого измерения в сочетании с вычисленным смещением в буфере данных. Результат был синтаксически недопустимым, семантически бессмысленным и совершенно нечитаемым.

![Сравнение декомпиляции одного и того же метода VB6 до и после поддержки SafeArray: в столбце «До» - десятки строк сырой арифметики указателей; в столбце «После» - чистые объявления Dim и лаконичные выражения с индексами массива](https://www.dotfix.net/images/doc/vb_decompiler_262/vb_decompiler_262_safearray_1.png)

#### Новый движок SafeArray


В версии 26.2 реализован полноценный анализ SafeArray, охватывающий полный жизненный цикл операций с массивами в скомпилированном VB6 Native Code:

- **Разбор дескриптора SafeArray.** Декомпилятор Native Code теперь полностью разбирает находящуюся в памяти структуру `SAFEARRAY`: размер элемента (`cbElements`), количество измерений (`cDims`), указатель на данные (`pvData`) и записи `SAFEARRAYBOUND` для каждого измерения, содержащие количество элементов и нижнюю границу. В большинстве случаев это даёт декомпилятору полную модель формы массива на этапе декомпиляции.

- **Восстановление многомерных индексов.** VB6 компилирует обращение по многомерным индексам (`arr(i, j, k)`) в плоское смещение в памяти, вычисляемое как последовательность операций умножения и сложения над размерами измерений и нижними границами. Декомпилятор теперь в большинстве случаев распознаёт существенное число таких шаблонов, обращает вычисление смещения и восстанавливает исходные индексные выражения для каждого измерения.

- **Восстановление операций чтения.** Чтение элементов массива - когда скомпилированный код загружает значение по вычисленному адресу элемента - теперь в большинстве случаев распознаётся и выводится в виде правильных выражений с индексами: `var_20(i, j, k, l)`.

- **Восстановление операций записи.** Запись в элементы массива, включая индексированные цели присваивания в левой части операторов присвоения, обрабатывается корректно в большинстве случаев.

- **VarPtr для элементов массива.** Вызовы `VarPtr` с элементом массива в качестве аргумента - используемые для получения сырого указателя на конкретный элемент, что является распространённым шаблоном в коде VB6, передающем данные массивов в функции API, - теперь корректно восстанавливаются как `VarPtr(arr(i, j, ...))` с правильными индексными выражениями.

- **Распознавание шаблонов проверки границ.** Скомпилированный код VB6 вставляет явные проверки границ перед каждым обращением к массиву, сравнивая запрошенный индекс с нижней границей и количеством элементов, хранящимися в записи `SAFEARRAYBOUND`. Декомпилятор распознаёт эти генерируемые компилятором последовательности проверок и подавляет их в выводе - они являются шаблонным кодом компилятора, а не логикой исходного кода, и их присутствие в прежнем выводе было лишь источником лишнего кода.

- **Аннотация типов переменных.** Переменные, идентифицированные как ссылки на SafeArray, теперь объявляются с типом `SafeArray` в декомпилированном выводе, делая явную связь между объявлениями и использованием переменной.

#### Результат


Тот же метод, который ранее порождал десятки строк нечитаемого кода на уровне регистров, теперь декомпилируется в чистый, сразу узнаваемый исходный код VB6. Критический фрагмент, ранее превращавшийся в многострочную "катастрофу", показанную выше, теперь выглядит так:

```
`Dim var_20 As SafeArray
loc_00412DE3:   var_20 = global_160
loc_00412EA7:   global_176 = VarPtr(var_20(1, 1, 1, 1))
loc_00412EB6:   global_180 = VarPtr(global_108(3))`
```


Три строки вместо более двадцати. Обращение к четырёхмерному массиву в виде `var_20(1, 1, 1, 1)` вместо арифметического взрыва из смещений полей дескриптора. Переменная `var_20` корректно объявлена как `Dim var_20 As SafeArray` в начале процедуры. Шаблонный код проверки границ исчез. Метод читаем.


Это улучшение затрагивает большинство приложений VB6 в Native Code, использующих массивы нетривиальным образом - код обработки изображений, численные алгоритмы, процедуры преобразования данных, игровую логику и любые другие области, где многомерные массивы являются естественным инструментом. Прирост качества для подобных методов - одно из наиболее визуально впечатляющих изменений за последние версии.

![Сравнение декомпиляции одного и того же метода VB6 до и после поддержки SafeArray: столбец «До» - десятки строк сырой арифметики указателей и недопустимых выражений; столбец «После» - чистые объявления Dim и лаконичные обращения к элементам массива по индексам](https://www.dotfix.net/images/doc/vb_decompiler_262/vb_decompiler_262_safearray.png)

### Изоляция состояний условных ветвей на основе снимков


Давней причиной ошибок декомпиляции в сложных методах VB6 Native Code было **загрязнение состояния между ветвями If/Else**. При анализе ветви `If` условной конструкции все изменения, которые декомпилятор вносил в свою внутреннюю модель - значения регистров, состояние стека CPU и FPU, присвоения переменных - переносились в анализ ветви `Else`, как если бы оба пути выполнения разделяли один контекст. Это приводило к некорректным присвоениям переменных, фантомным ссылкам и некорректному выводу, особенно в пользовательских методах классов со сложной логикой.


Версия 26.2 решает эту проблему кардинально. Когда декомпилятор встречает конструкцию `If/Else`, он теперь:

- Создаёт **полный снимок состояния** в точке ветвления, включая все значения регистров, состояние стека CPU и FPU, а также полную карту присвоений переменных.

- Анализирует ветвь `If`, используя рабочую копию этого снимка.

- **Восстанавливает сохранённый снимок** перед входом в блок `Else`, обеспечивая полную изоляцию анализа двух ветвей друг от друга.


Это изменение оказывает существенное влияние на качество декомпиляции для любого бинарного файла, содержащего нетривиальную условную логику - что на практике означает подавляющее большинство реальных программ VB6 Native Code.

![Декомпилированный метод класса VB6: корректная изоляция ветвей If/Else с правильной областью видимости переменных в каждой ветви](https://www.dotfix.net/images/doc/vb_decompiler_262/vb_decompiler_262_branch_snapshot.png)

### Переписанный анализ временных и неиспользуемых переменных


Анализ и фильтрация **временных и неиспользуемых переменных** в декомпилированном VB6 Native Code полностью переписаны с нуля. Предыдущая реализация развивалась эволюционно и в ряде случаев не фильтровала мусорный код при обработке оптимизированных шаблонов промежуточного кода, генерируемого компилятором.


Новая реализация - это чистый, структурированный проход анализа, корректно определяющий переменные, существующие исключительно как компиляторные временные, и исключающий их из вывода, не удаляя при этом переменные, которые, пусть и короткоживущие, несут семантическую нагрузку. В результате декомпилированный результат содержит значительно меньше "шума", а восстановленный исходный код становится более читаемым.

### Значительно расширена эвристика Interface ID для позднего связывания


Программы VB6, использующие **позднее связывание** - то есть объектные переменные, объявленные как `Object`, а не с конкретным типом - практически не оставляют информации о типах в скомпилированном бинарном файле. Эвристический анализатор позднего связывания декомпилятора идентифицирует вызываемый во время выполнения интерфейс, сопоставляя **идентификатор интерфейса (IID)** цели с известной базой данных.


Эта база данных существенно расширена в версии 26.2 и теперь охватывает следующие семейства интерфейсов:

- **Базовые интерфейсы COM/OLE** - `ITypeLib`, `ICreateTypeInfo`, `ITypeInfo` и связанные объекты системы типов.

- **Автоматизация Microsoft Office** - интерфейсы Word, Excel, Access, Outlook и PowerPoint.

- **Доступ к данным** - ADO (ActiveX Data Objects) и DAO (Data Access Objects).

- **XML и HTTP** - интерфейсы MSXML, WinHTTP и WinInet.

- **Скриптинг и оболочка** - `FileSystemObject` (Scripting runtime), объекты Shell и WScript.

- **Веб-браузер** - интерфейсы Internet Explorer и элемента управления WebBrowser.

- **Обмен сообщениями** - интерфейсы CDO и MAPI для автоматизации электронной почты.

- **Служба каталогов** - ADSI (Active Directory Service Interfaces).

- **Скриптинг** - `VBScript.RegExp` и связанные объекты.


Каждая запись интерфейса в базе данных также содержит **GUID родительской библиотеки**. Если соответствующий TypeLib зарегистрирован в реестре Windows на машине, где запущен VB Decompiler, инструмент автоматически извлечёт из него полную информацию TypeLib и использует её для улучшения декомпиляции методов и свойств этого интерфейса без какой-либо ручной настройки.

![Декомпилированный код VB6 с объектами Excel и ADO с поздним связыванием: корректно определённые имена интерфейсов и вызовы методов](https://www.dotfix.net/images/doc/vb_decompiler_262/vb_decompiler_262_iid_heuristics.png)

### Переработанная эмуляция создания объектов __vbaNew2


Создание объектов в VB6 Native Code обрабатывается функцией `__vbaNew2` из `msvbvm60.dll` (и её эквивалентами в других версиях среды выполнения). Декомпиляция этого шаблона **полностью переработана**. Новая реализация более точно восстанавливает присвоение объекта через оператор `Set` с созданием нового экземпляра класса и обрабатывает граничные случаи, которые предыдущая версия могла некорректно отображать в выводе.

### Переработанная декомпиляция __vbaPrintObj


API среды выполнения `__vbaPrintObj`, используемый VB6 внутренне для вызовов метода `Print` на различных объектах вывода, полностью переработан. Новая реализация корректно определяет и различает разные **типы объектов печати**, которые могут выступать содержимым операции вывода - такие как строки, символы, табуляции и отладочный вывод - и точно представляет каждый из них в декомпилированном коде.

### Эмуляция инструкции SAR для знакового деления


Компилятор VB6 часто использует ассемблерную инструкцию **sar** (shift arithmetic right, арифметический сдвиг вправо) как оптимизацию деления знаковых целых чисел на степени двойки. Например, `sar eax, 1` эквивалентно делению `eax` на 2, а `sar eax, 2` - делению на 4. В версии 26.2 добавлена полная эмуляция этой инструкции в движке анализа Native Code. Код, который ранее отображался как сырая операция побитового сдвига, теперь корректно восстанавливается как выражение целочисленного деления, соответствующее исходному намерению.

### Улучшенный анализатор кода: предварительное обнаружение условных переходов


Анализатор кода теперь выполняет **предварительный проход по всем строкам, содержащим условные переходы**, до начала основного анализа. При обнаружении такой строки анализатор больше не будет сворачивать её адрес, даже если в этом месте находятся временные переменные, являющиеся кандидатами на исключение. Это предотвращает ситуации, при которых важные адреса переходов удалялись в ходе оптимизации, а ссылки на них оставались.

### Рефакторинг и ускорение анализатора и оптимизатора кода


Движок анализатора и оптимизатора кода прошёл существенный **внутренний рефакторинг**. Помимо улучшения корректности, рефакторинг сделал конвейер постобработки - этап преобразования сырого декомпилированного вывода в чистый структурированный код VB6 - **значительно быстрее**. Пользователи, работающие с большими бинарными файлами заметят ощутимое сокращение времени анализа.

### Подсветка глобальных переменных


В версии 26.1 была введена подсветка по клику для локальных строковых переменных, позволяющая видеть все вхождения выбранной переменной `str_XX`. Версия 26.2 распространяет эту возможность на **глобальные переменные**: клик по любой переменной `global_XX` в представлении кода теперь подсвечивает все её вхождения в текущей процедуре, упрощая отслеживание изменений переменной внутри больших и сложных функций.

![Представление кода VB Decompiler: все вхождения переменной global_XX подсвечены после клика на одном из них](https://www.dotfix.net/images/doc/vb_decompiler_262/vb_decompiler_262_global_highlight.png)

### Диалог ссылок на строки поддерживает более 32 000 строк

**Диалог ссылок на строки** обновлён для поддержки бинарных файлов, содержащих более 32 000 строковых ссылок - ограничение, которое ранее было жёстко задано в коде и вызывало усечение или проблемы с отображением в очень больших программах. Кроме того, в диалоге теперь полностью поддерживаются **поиск строк и навигация по ним**, позволяющие быстро находить нужные строки в программах с большим количеством строковых переменных и констант.


Также оптимизирована работа с памятью при операциях со списками строк, что снижает потребление памяти при работе с большими строковыми таблицами.

![Диалог ссылок на строки: поле поиска и большой список строк с более чем 32 000 записями](https://www.dotfix.net/images/doc/vb_decompiler_262/vb_decompiler_262_string_reference.png)

### Обновлённые сигнатуры API: rtcGetSetting и rtcDeleteSetting


Сигнатуры функций API среды выполнения VB `rtcGetSetting` и `rtcDeleteSetting` обновлены для корректного отражения использования ими **параметров типа Variant**. Это исправление обеспечивает корректную декомпиляцию вызовов этих функций, используемых для чтения и удаления параметров приложения в реестре. Теперь параметры функций корректно типизированы и декомпилируются без использования эвристики.

### Полностью переработанная справочная система на испанском языке


Справочная система на испанском языке **полностью переписана и пересмотрена** носителем языка. Предыдущий перевод содержал различные устаревшие данные и разделы, не отражавшие текущий набор функций. Новый перевод охватывает весь справочный материал с точным профессиональным техническим изложением на испанском языке. Особая благодарность **Leonardo Donaire** за масштабную работу над переводом.

### Исправления ошибок

- **Изменение размера вкладки AI Helper при разворачивании окна:** при разворачивании окна VB Decompiler на весь экран вкладка AI Helper не изменяла размер корректно. Теперь вкладка и её содержимое корректно масштабируются вместе с остальным окном.

- **Навигация в диалоге ссылок на строки (.NET):** попытка перейти к скрытой функции, сгенерированной компилятором, из диалога ссылок на строки в бинарном файле .NET больше не вызывает ошибку.

- **Остаточные строки в окне трассировки:** при повторном открытии окна трассировки после предыдущего сеанса строки кода из прошлой трассировки в ряде случаев оставались видимыми. Теперь окно трассировки корректно очищает старое содержимое при открытии.

- **Отсутствующий End If при обнаружении ветви Else:** функция обнаружения ветви Else в анализаторе и оптимизаторе Native Code не вставляла закрывающий блок `End If` в ряде редких шаблонов кода.

- Если программа, скомпилированная в P-Code, декомпилировалась сразу после декомпиляции программы, скомпилированной в Native Code, указатель `this` некорректно добавлялся к базовым свойствам объекта Global в результате декомпиляции P-Code. Теперь декомпилятор корректно сбрасывает всё состояние между сеансами.

- **Не распознавалась конструкция Else перед условным переходом:** конструкция `Else` не генерировалась, когда непосредственно перед блоком Else в VB6 Native Code находился условный переход.

(C) Сергей Чубченко, разработчик [VB Decompiler](https://www.dotfix.net/vb_decompiler.htm)

* Visual Basic, Visual Studio и Windows являются зарегистрированными товарными знаками корпорации Microsoft.

### Материалы по теме

- [Исследование Visual Basic 6.0 - подробное руководство по дизассемблированию и отладке](https://www.dotfix.net/doc/vb60_reversing.htm)

- [Декомпиляция и эмуляция Native Code лучше чем когда либо в VB Decompiler 11](https://www.dotfix.net/doc/vb_decompiler_11.htm)

- [Многократное ускорение работы, рефакторинг эмулятора и улучшенное отслеживание переходов в VB Decompiler v26.3](https://www.dotfix.net/doc/vb_decompiler_263.htm)
