Автор: Сергей Чубченко. Дата первой публикации: 15.09.2004. Переработка и дополнение: 24.03.2025.
О чем пойдет речь
В среде программистов бытует мнение, что Visual Basic ужасный язык программирования. Это породило множество самых различных мифов и легенд вокруг него. Одни говорят, что не может компилировать программы в машинный код. Другие - что не может работать с адресами переменных в памяти. Третьи пишут, что он не позволяет вставлять ассемблерные процедуры в высокоуровневый код программы. Только вот все эти мифы - откровенная ложь и некомпетентность тех, что посмотрел на данный язык программирования со стороны, не нашел похожих на C++ методов реализации требуемых задач и голословно решил, что Visual Basic неспособен на большее, чем обучение программированию студентов. В реальности же, начиная с версии 5.0, данный язык позволяет компилировать программы в полноценный машинный Native Code. Имеется возможность работы с адресами переменных в оперативной памяти, для чего существуют функции VarPtr и StrPtr. Ассемблерные процедуры тоже можно использовать. Не так просто как в Delphi и C++, но все же. Для этого я написал уже 2 части статьи по вставке ассемблерных процедур в код на Visual Basic. Первая статья размещена здесь: Ассемблер в VB6 часть 1, вторая тут: Ассемблер в VB6 часть 2. Как видим, недостатков у Visual Basic не так уж и много, а преимуществ столько, что их описать не хватит и нескольких статей.
Сейчас же я расскажу лишь о самых очевидных и важных преимуществах:
- Полноценная компиляция и маленький размер получаемых exe файлов. Несмотря на то, что программы требуют библиотеку MSVBVM60.DLL, это не является недостатком, так как эта библиотека интегрирована во все новые операционные системы и комплектовать ее вместе с приложением вовсе не обязательно. Но даже в этом случае ее вес всего 1,3Mb, что ничтожно мало.
- Простота написания кода. Все удобно и наглядно. Даже начинающий может писать неоптимизированные, но рабочие программы. Как говорится: умеешь писать качественно - пиши, от этого программы будут только лучше. Не умеешь - можешь писать неоптимизированные программы, не объявляя переменные (это не обязательно), не преобразуя типы данных (тоже не обязательно) и т.д. И все будет прекрасно работать, причем не сильно медленнее, чем после компиляции программ, написанных по всем правилам грамотной разработки. Даже новичку постепенно придут более глубокие познания, и он начнет разрабатывать крупные проекты, не уступающие аналогам на популярных C/C++ подобных языках.
Обратная медаль непопулярности Visual Basic в том, что исследователи кода ошибочно считают, что написанные на Visual Basic'е программы совершенно невозможно анализировать. Если программа скомпилирована в P-Code (псевдокод виртуальной машины VB) - года так до 2005 их частично было можно понять, когда не было доступных декомпиляторов, как например VB Decompiler. Но даже в те времена для этого вида компиляции уже были написаны и отладчик (хоть и часто выпадающий в ошибки) и дизассемблеры P-Code (примерно схожего качества) и работать с кодом хоть и сложно, но уже было реально. Плюс еще тогда я написал статью Декомпилируем p-code в уме, которая отвечает на многие вопросы по работе с P-Code.
Что же касается программ, скомпилированных в бейсике в машинный Native Code, то они исследуются почти так же, как и любой x86 программный код, написанный например на C++ или Delphi. При этом есть ряд особенностей, о которых стоит отдельно упомянуть. Все операции, которые выполняет программа, выполняются с использованием узкоспециализированных функций из библиотеки MSVBVM60.DLL. Имена многих из этих функций напоминают операторы Visual Basic'а, поэтому глядя на названия части из них, можно догадаться, какие операции они позволяют выполнять. Есть, конечно, и такие варианты, которые не всегда поддаются логике. О них я и расскажу в данной статье.
Функции библиотеки Visual Basic 6.0
Самые непонятные аналитикам функции - это обычно функции для преобразования данных из одного типа в другой. Чуть ниже я для удобства приведу существующие типы данных, используемые в Visual Basic 6.0 и функции для работы с ними.
bool - boolean
str - string
i2 - integer (integer в VB двухбайтный)
ui2 - unsigned integer (2 байтный unsigned integer)
i4 - long (long integer в VB 4 байтный)
r4 - single (4 байтное число с плавающей запятой)
r8 - double (8 байтное число с плавающей запятой)
cy - currency (аналог double но с привязкой к валюте)
var - variant (VB) или variable (OLEAUT)
Названия частей, из которых составляются имена функций:
fp - работа с float данными, переданными через st регистры или сохраняемые туда командами сопроцессора
cmp - сравнение аргументов
comp - также сравнение аргументов
Функции ля преобразования типов данных:
__vbaI2Str - преобразует String в Integer
__vbaI4Str - преобразует String в Long
__vbar4Str - преобразует String в Single
__vbar8Str - преобразует String в Double
VarCyFromStr - преобразует String в Currency
VarBstrFromI2 - преобразует Integer в String
Перенос данных
__vbaStrCopy - копирует строку в память, аналог API функции HMEMCPY
__vbaVarCopy - копирует переменный тип (Variant) в память
__vbaVarMove - перемещает переменный тип (Variant) в память
Математические функции
__vbavaradd - сложение двух переменных типа Variant
__vbavarsub - вычитание двух переменных типа Variant
__vbavarmul - умножение двух переменных типа Variant
__vbavaridiv - деление двух переменных типа Variant
'с выводом результата в переменную типа Integer
__vbavarxor - логическая операция xor
Другие функции
__vbavarfornext - используется для инициализации цикла For... Next... (Loop)
__vbafreestr - удаление строковой переменной
__vbafreeobj - удаление объекта
__vbastrvarval - получения численного значения из строки
multibytetowidechar - преобразование кодировки
rtcMsgBox - показывает сообщение, аналог API messagebox/A/Ex
__vbavarcat - объединяет две переменные типа Variant
__vbafreevar - удаляет переменную типа Variant
__vbaobjset - создает объект
__vbaLenBstr - определяет длину строки
rtcInputBox - показывает форму с полем ввода (используются также API функции getwindowtext/a, GetDlgItemtext/a)
__vbaNew - создание нового объекта
__vbaNew2 - другой вариант создания нового объекта
rtcTrimBstr - удаляет пробелы вначале и в конце строки
Функции сравнения
__vbastrcomp - сравнивает 2 строковые переменные, аналог API функции lstrcmp
__vbastrcmp - сравнивает 2 строковые переменные, аналог API функции lstrcmp
__vbavartsteq - сравнивает 2 Variant переменные
__vbaFpCmpCy - сравнивает значение с плавающей точкой с Currency значением
Разблокирование элементов управления
Любой элемент управления на форме может быть видимым или невидимым, доступным или заблокированным. Для установки свойств объектов существует функция __vbaObjSet. Именно с помощью нее можно заблокировать или разблокировать элемент управления на форме или изменить любое из его свойств. При анализе кода нам потребуется отлавливать вызов как раз данной функции. Откроем любой отладчик, например Olly Debugger, найдем функцию __vbaObjSet в таблице импорта программы, а затем поставим на нее breakpoint, нажав кнопку F2. После чего запустим исследуемый файл. Когда breakpoint сработает, посмотрите окружающий код. Если он напоминает
то Вы на верном пути и можно продолжать исследование. Как Вы считаете, что это за "push 00"? 00h при передаче булевых значений в Visual Basic означает False, а FFh (255) - True. Из этого следует, что при таком коде текущая команда устанавливает какое-то свойство того или иного элемента управления на форме в False. Это в свою очередь может означать блокировку или отключение видимости того или иного элемента управления на форме.
Естественно это также может быть установкой любого другого свойства формы в True. Так как синтаксис один и тот же, то принадлежность данной команды к изменению свойства блокировки можно установить только анализом окружающего кода. Но думаю, с этой мелочью Вы справитесь сами. Или воспользуетесь VB Decompiler, который в отличии от OllyDbg умеет декомпилировать код и показывает не машинный ассемблерный код, а бейсико-подобный с декомпиляцией типов и свойств объектов на форме.
Методика анализа простейших проверок строковых данных
Нижеследующий текст частично основан на довольно старой, но актуальной статье How to Research Visual Basic. Когда-то очень давно она была доступна на сайте infonegocio.com, но там уже давно ничего нет.
Что нам может понадобиться? Любой дизассемблер/отладчик. Подойдет любой современный отладчик - Olly Debuggerб IDA Pro или даже древний Win32Dasm. Если Вы используете последний, то функции, используемые программой, Вы можете посмотреть в меню "Functions" -> "Imports".
Если Вы будете исследовать код, работающий с базами данных, то помните, что названия функции не говорят сами за себя и вам будет трудно узнать действие, которое выполняет та или иная из них. В данном случае Вам остается только смотреть названия функций, которые возможно оставлены авторами кода. Уязвимых мест же в драйверах баз данных не много, поэтому исследовать такой код довольно сложно. Опять же если не пользоваться VB Decompiler, а разбирать этот код вручную в отладчике.
Рассмотрим пример кода для проверки строковых данных и выполнения/невыполнения того или иного функционала взависимости от результатов такого сравнения. Для удобства поиска нужного кода поставим MessageBox. Откройте IDE Visual Basic (оно нам и дальше будет пригождаться для компиляции примеров), добавьте на форму текстовое поле и кнопку, затем в обработчике нажатия кнопки напишите следующий код:
Теперь откроем Olly Debugger, загрузим в него файл (предварительно скомпилировав его в Visual Basic 6.0) и ищем вызов функции rtcMsgBox.
Чтобы убрать проверку значения достаточно изменить переход по адресу 00401FF9 с условного на безусловный. Для этого меняется JE на JMP (то есть 74h на EBh). Как видим, такие простые проверки в Visual Basic малоэффективны, равно как и их аналоги на С++. А потому, если речь идет о проверке серийного номера или привязки ПО к диску, то разработчикам настоятельно рекомендуется защищать подобный код. Одним из хороших вариантов для такой защиты рекомендую использовать DotFix NiceProtect. Он может как виртуализовать критичный код, так и защитить программу от анализа в целом.
Анализ значений в виде разных типов данных
Ниже будут рассмотрены ряд методик эффективного анализа всевозможных значений в коде на Visual Basic.
Получение данных, если они лежат строкой в открытом виде
Когда бейсик копирует строку в память - Вы ее всегда можете отловить в отладке.
Для этого нужно поставить breakpoint на функцию __vbaStrCopy и когда он у Вас сработает, просто посмотрите код после je 66047B00 (он содержится в библиотеке и един для всех программ на VB6). Затем нужно посмотреть содержимое регистра edx минус 4 байта. В этом случае Вы увидите строчку, с которой работает в данный момент функция.
Получение строки, если она сравнивается с введенной
Поставьте breakpoint на функцию __vbaStrComp и чуть выше ее вызова будут два push'а. Они заносят в стек две Variant переменные для сравнения. Посмотрите содержимое регистра eax, чтобы узнать адрес текста. Взлянем, что лежит по этому адресу, обычно там нужная нам искомая строка.
Получение данных, если происходит сравнение 4х байтовых числовых переменных
Поставьте breakpoint на функцию __vbai4str. Эта функция используется программой для перевода введенной текстовой строки в 4х байтовое число. Когда breakpoint сработает, посмотрите код ниже, там наверняка будет процедура сравнения строк. Но нужное нам значение будет в HEX виде (например, десятичное число 987654321 равно шестнадцатеричному числу 3ADE68B1).
Тут мы можем узнать верное значение или отключить проверку вовсе (для этого нужно JNZ заменить на NOP). Как видим, в данном случае нужно поменять байты по адресу 00401B97 с 7520 на 9090.
Получение данных при сравнивании двухбайтовых чисел
Отличие от предыдущего примера в том, что нужно ставить breakpoint на функцию __vbaI2Str. Но иногда предварительно производится преобразование строки сначала в 4х байтовое число, а затем 4х байтовое число преобразуется в двухбайтовое посредством функции __vbaI2I4.
Сравнение двух переменных типа Single
Эту проверку также обойти довольно просто. Ставится breakpoint на функцию __vbaR4Str и когда он сработает, пролистываем машинный код вниз. Вы увидите переход после проверки. Для инверсии условия достаточно просто изменить условие перехода на обратное.
Замените инструкцию по адресу 00401C38 на 9090 (чтобы в данном месте не выполнялось никаких операций) или измените условный переход на противоположный. На Visual Basic нельзя проверить то, что вы заменили эти байты на два NOP'а (9090). Если у Вас все же возникают опасения, что код перестанет работать при отсутствии команд, просто введите команды, которые ничего не поменяют, например:
Эти команды однобайтовые и как раз впишутся на место условного перехода
Обход проверки значений с плавающей точкой (double).
Это также сделать очень просто. Ставим breakpoint на функцию __vbaR8Str и листаем код немного ниже останова на breakpoint'е, Вы увидите процедуру сравнения (FCOMP REAL8 PTR [Address]), после нее идет test и jmp. Условие этого перехода также легко изменить на противоположное.
Замените инструкцию по адресу 00401C78 на 9090 (чтобы в данном месте не выполнялось никаких операций) или измените условный переход на противоположный. Как уже упоминалось ранее, на Visual Basic никак нельзя проверить то, что Вы заменили эти байты на два NOP'а. Только внешняя защита поможет защититься от модификации кода.
Получение строки, если она по-XOR-ена.
Алгоритм работы бейсиковской процедуры xor часто бывает таков. Введенная строка посимвольно переводится в ASCII коды, которые по очереди XOR'ятся. Затем производится обратный перевод, возможно с предварительными математическими манипуляциями с ASCII кодами. ANSI коды могут XOR'иться со случайным или фиксированным числом.
Ставим breakpoint на 00401E8A 51 PUSH ECX и отслеживаем изменение содержимого регистра ECX при прохождении цикла. Когда программа пройдет весь цикл - Вы получите расшифрованную строку.
В этом случае, следуя ниже по коду, скорее всего вы встретите сравнение и переход. При этом CALL [MSVBVM60!__vbaStrComp] может быть использован для сравнения строк. Если не производится обращение к MSVBVM60!rtcBstrFromAnsi, тогда со строкой производятся определенные математические манипуляции, при этом она может быть представлена как 2х или 4х байтное число или число с плавающей точкой. Об этих случаях уже было сказано выше.
Заключение
Надеюсь, что прочитав данную статью Вас уже больше не пугают программы на Visual Basic и примерно понятен их анализ. Чтобы посмотреть, как выглядит код на Visual Basic вживую, советую посмотреть примеры, которые лежат на данном сайте в разделе "Разное".
Благодарю Вас за интерес к данной статье!
Исследование Visual Basic 6.0 - подробное руководство по дизассемблированию и отладке
О чем пойдет речь
В среде программистов бытует мнение, что Visual Basic ужасный язык программирования. Это породило множество самых различных мифов и легенд вокруг него. Одни говорят, что не может компилировать программы в машинный код. Другие - что не может работать с адресами переменных в памяти. Третьи пишут, что он не позволяет вставлять ассемблерные процедуры в высокоуровневый код программы. Только вот все эти мифы - откровенная ложь и некомпетентность тех, что посмотрел на данный язык программирования со стороны, не нашел похожих на C++ методов реализации требуемых задач и голословно решил, что Visual Basic неспособен на большее, чем обучение программированию студентов. В реальности же, начиная с версии 5.0, данный язык позволяет компилировать программы в полноценный машинный Native Code. Имеется возможность работы с адресами переменных в оперативной памяти, для чего существуют функции VarPtr и StrPtr. Ассемблерные процедуры тоже можно использовать. Не так просто как в Delphi и C++, но все же. Для этого я написал уже 2 части статьи по вставке ассемблерных процедур в код на Visual Basic. Первая статья размещена здесь: Ассемблер в VB6 часть 1, вторая тут: Ассемблер в VB6 часть 2. Как видим, недостатков у Visual Basic не так уж и много, а преимуществ столько, что их описать не хватит и нескольких статей.

Сейчас же я расскажу лишь о самых очевидных и важных преимуществах:
- Полноценная компиляция и маленький размер получаемых exe файлов. Несмотря на то, что программы требуют библиотеку MSVBVM60.DLL, это не является недостатком, так как эта библиотека интегрирована во все новые операционные системы и комплектовать ее вместе с приложением вовсе не обязательно. Но даже в этом случае ее вес всего 1,3Mb, что ничтожно мало.
- Простота написания кода. Все удобно и наглядно. Даже начинающий может писать неоптимизированные, но рабочие программы. Как говорится: умеешь писать качественно - пиши, от этого программы будут только лучше. Не умеешь - можешь писать неоптимизированные программы, не объявляя переменные (это не обязательно), не преобразуя типы данных (тоже не обязательно) и т.д. И все будет прекрасно работать, причем не сильно медленнее, чем после компиляции программ, написанных по всем правилам грамотной разработки. Даже новичку постепенно придут более глубокие познания, и он начнет разрабатывать крупные проекты, не уступающие аналогам на популярных C/C++ подобных языках.
Обратная медаль непопулярности Visual Basic в том, что исследователи кода ошибочно считают, что написанные на Visual Basic'е программы совершенно невозможно анализировать. Если программа скомпилирована в P-Code (псевдокод виртуальной машины VB) - года так до 2005 их частично было можно понять, когда не было доступных декомпиляторов, как например VB Decompiler. Но даже в те времена для этого вида компиляции уже были написаны и отладчик (хоть и часто выпадающий в ошибки) и дизассемблеры P-Code (примерно схожего качества) и работать с кодом хоть и сложно, но уже было реально. Плюс еще тогда я написал статью Декомпилируем p-code в уме, которая отвечает на многие вопросы по работе с P-Code.
Что же касается программ, скомпилированных в бейсике в машинный Native Code, то они исследуются почти так же, как и любой x86 программный код, написанный например на C++ или Delphi. При этом есть ряд особенностей, о которых стоит отдельно упомянуть. Все операции, которые выполняет программа, выполняются с использованием узкоспециализированных функций из библиотеки MSVBVM60.DLL. Имена многих из этих функций напоминают операторы Visual Basic'а, поэтому глядя на названия части из них, можно догадаться, какие операции они позволяют выполнять. Есть, конечно, и такие варианты, которые не всегда поддаются логике. О них я и расскажу в данной статье.
Функции библиотеки Visual Basic 6.0
Самые непонятные аналитикам функции - это обычно функции для преобразования данных из одного типа в другой. Чуть ниже я для удобства приведу существующие типы данных, используемые в Visual Basic 6.0 и функции для работы с ними.
bool - boolean
str - string
i2 - integer (integer в VB двухбайтный)
ui2 - unsigned integer (2 байтный unsigned integer)
i4 - long (long integer в VB 4 байтный)
r4 - single (4 байтное число с плавающей запятой)
r8 - double (8 байтное число с плавающей запятой)
cy - currency (аналог double но с привязкой к валюте)
var - variant (VB) или variable (OLEAUT)
Названия частей, из которых составляются имена функций:
fp - работа с float данными, переданными через st регистры или сохраняемые туда командами сопроцессора
cmp - сравнение аргументов
comp - также сравнение аргументов
Функции ля преобразования типов данных:
__vbaI2Str - преобразует String в Integer
__vbaI4Str - преобразует String в Long
__vbar4Str - преобразует String в Single
__vbar8Str - преобразует String в Double
VarCyFromStr - преобразует String в Currency
VarBstrFromI2 - преобразует Integer в String
Перенос данных
__vbaStrCopy - копирует строку в память, аналог API функции HMEMCPY
__vbaVarCopy - копирует переменный тип (Variant) в память
__vbaVarMove - перемещает переменный тип (Variant) в память
Математические функции
__vbavaradd - сложение двух переменных типа Variant
__vbavarsub - вычитание двух переменных типа Variant
__vbavarmul - умножение двух переменных типа Variant
__vbavaridiv - деление двух переменных типа Variant
'с выводом результата в переменную типа Integer
__vbavarxor - логическая операция xor
Другие функции
__vbavarfornext - используется для инициализации цикла For... Next... (Loop)
__vbafreestr - удаление строковой переменной
__vbafreeobj - удаление объекта
__vbastrvarval - получения численного значения из строки
multibytetowidechar - преобразование кодировки
rtcMsgBox - показывает сообщение, аналог API messagebox/A/Ex
__vbavarcat - объединяет две переменные типа Variant
__vbafreevar - удаляет переменную типа Variant
__vbaobjset - создает объект
__vbaLenBstr - определяет длину строки
rtcInputBox - показывает форму с полем ввода (используются также API функции getwindowtext/a, GetDlgItemtext/a)
__vbaNew - создание нового объекта
__vbaNew2 - другой вариант создания нового объекта
rtcTrimBstr - удаляет пробелы вначале и в конце строки
Функции сравнения
__vbastrcomp - сравнивает 2 строковые переменные, аналог API функции lstrcmp
__vbastrcmp - сравнивает 2 строковые переменные, аналог API функции lstrcmp
__vbavartsteq - сравнивает 2 Variant переменные
__vbaFpCmpCy - сравнивает значение с плавающей точкой с Currency значением
Разблокирование элементов управления
Любой элемент управления на форме может быть видимым или невидимым, доступным или заблокированным. Для установки свойств объектов существует функция __vbaObjSet. Именно с помощью нее можно заблокировать или разблокировать элемент управления на форме или изменить любое из его свойств. При анализе кода нам потребуется отлавливать вызов как раз данной функции. Откроем любой отладчик, например Olly Debugger, найдем функцию __vbaObjSet в таблице импорта программы, а затем поставим на нее breakpoint, нажав кнопку F2. После чего запустим исследуемый файл. Когда breakpoint сработает, посмотрите окружающий код. Если он напоминает
50 push eax
52 push edx
FFD7 call edi
8BD8 mov ebx, eax
6A00 push 00
53 push ebx
8B03 mov eax, dword ptr [ebx]
то Вы на верном пути и можно продолжать исследование. Как Вы считаете, что это за "push 00"? 00h при передаче булевых значений в Visual Basic означает False, а FFh (255) - True. Из этого следует, что при таком коде текущая команда устанавливает какое-то свойство того или иного элемента управления на форме в False. Это в свою очередь может означать блокировку или отключение видимости того или иного элемента управления на форме.
Естественно это также может быть установкой любого другого свойства формы в True. Так как синтаксис один и тот же, то принадлежность данной команды к изменению свойства блокировки можно установить только анализом окружающего кода. Но думаю, с этой мелочью Вы справитесь сами. Или воспользуетесь VB Decompiler, который в отличии от OllyDbg умеет декомпилировать код и показывает не машинный ассемблерный код, а бейсико-подобный с декомпиляцией типов и свойств объектов на форме.
Методика анализа простейших проверок строковых данных
Нижеследующий текст частично основан на довольно старой, но актуальной статье How to Research Visual Basic. Когда-то очень давно она была доступна на сайте infonegocio.com, но там уже давно ничего нет.
Что нам может понадобиться? Любой дизассемблер/отладчик. Подойдет любой современный отладчик - Olly Debuggerб IDA Pro или даже древний Win32Dasm. Если Вы используете последний, то функции, используемые программой, Вы можете посмотреть в меню "Functions" -> "Imports".
Если Вы будете исследовать код, работающий с базами данных, то помните, что названия функции не говорят сами за себя и вам будет трудно узнать действие, которое выполняет та или иная из них. В данном случае Вам остается только смотреть названия функций, которые возможно оставлены авторами кода. Уязвимых мест же в драйверах баз данных не много, поэтому исследовать такой код довольно сложно. Опять же если не пользоваться VB Decompiler, а разбирать этот код вручную в отладчике.
Рассмотрим пример кода для проверки строковых данных и выполнения/невыполнения того или иного функционала взависимости от результатов такого сравнения. Для удобства поиска нужного кода поставим MessageBox. Откройте IDE Visual Basic (оно нам и дальше будет пригождаться для компиляции примеров), добавьте на форму текстовое поле и кнопку, затем в обработчике нажатия кнопки напишите следующий код:
Private Sub Command1_Click()
Dim X As Integer
X = 43690
MsgBox "Checking value"
If CLng(Trim$(Text1.Text)) = X Then MsgBox ("Complete")
End Sub
Теперь откроем Olly Debugger, загрузим в него файл (предварительно скомпилировав его в Visual Basic 6.0) и ищем вызов функции rtcMsgBox.
00401F54 FF1520104000 CALL [MSVBVM60!rtcMsgBox] ; MsgBox "Checking value"
00401F5A 8D459C LEA EAX,[EBP-64]
00401F5D 8D4DAC LEA ECX,[EBP-54]
00401F60 50 PUSH EAX
00401F61 8D55BC LEA EDX,[EBP-44]
00401F64 51 PUSH ECX
00401F65 8D45CCLEA EAX,[EBP-34]
00401F68 52 PUSH EDX
00401F69 50 PUSH EAX
00401F6A 6A04 PUSH 04
00401F6C FF1508104000 CALL [MSVBVM60!__vbaFreeVarList]
00401F72 8B0E MOV ECX,[ESI]
00401F74 83C414 ADD ESP,14
00401F77 56 PUSH ESI
00401F78 FF9104030000 CALL [ECX+00000304]
00401F7E 8D55DCLEA EDX,[EBP-24]
00401F81 50 PUSH EAX
00401F82 52 PUSH EDX
00401F83 FF1524104000 CALL [MSVBVM60!__vbaObjSet]
00401F89 8BF0 MOV ESI,EAX
00401F8B 8D4DE4LEA ECX,[EBP-1C]
00401F8E 51 PUSH ECX
00401F8F 56 PUSH ESI
00401F90 8B06 MOV EAX,[ESI]
00401F92 FF90A0000000 CALL [EAX+000000A0]
00401F98 3BC7 CMP EAX,EDI
00401F9A DBE2 FCLEX
00401F9C 7D12 JGE 00401FB0
00401F9E 68A0000000 PUSH 000000A0
00401FA3 6804184000 PUSH 00401804
00401FA8 56 PUSH ESI
00401FA9 50 PUSH EAX
00401FAA FF1518104000 CALL [MSVBVM60!__vbaHresultCheckObj] ; Получение содержимого Text1.Text
00401FB0 8B55E4MOV EDX,[EBP-1C]
00401FB3 52 PUSH EDX
00401FB4 FF1514104000 CALL [MSVBVM60!rtcTrimBstr] ; Trim$
00401FBA 8BD0 MOV EDX,EAX
00401FBC 8D4DE0 LEA ECX,[EBP-20]
00401FBF FF1584104000 CALL [MSVBVM60!__vbaStrMove]
00401FC5 50 PUSH EAX
00401FC6 FF1568104000 CALL [MSVBVM60!__vbaI4Str] ; CLng
00401FCC 33C9 XOR ECX,ECX
00401FCE 3DAAAA0000 CMP EAX,0000AAAA ; Число 43690 в HEX виде
00401FD3 8D55E0LEA EDX,[EBP-20]
00401FD6 8D45E4LEA EAX,[EBP-1C]
00401FD9 0F94C1 SETZ CL
00401FDC 52 PUSH EDX
00401FDD 50 PUSH EAX
00401FDE F7D9 NEG ECX
00401FE0 6A02 PUSH 02
00401FE2 8BF1 MOV ESI,ECX
00401FE4 FF156C104000 CALL [MSVBVM60!__vbaFreeStrList]
00401FEA 83C40C ADD ESP,0C
00401FED 8D4DDC LEA ECX,[EBP-24]
00401FF0 FF1594104000 CALL [MSVBVM60!__vbaFreeObj]
00401FF6 663BF7 CMP SI,DI
00401FF9 7463 JZ 0040205E ; Переход на Complete код (MsgBox "Complete")
00401FFB B804000280 MOV EAX,80020004
00402000 8D558C LEA EDX,[EBP-74]
00402003 8D4DCCLEA ECX,[EBP-34]
00402006 8945A4 MOV [EBP-5C],EAX
00402009 895D9C MOV [EBP-64],EBX
0040200C 8945B4 MOV [EBP-4C],EAX
0040200F 895DAC MOV [EBP-54],EBX
00402012 8945C4 MOV [EBP-3C],EAX
00402015 895DBC MOV [EBP-44],EBX
00402018 C7459418184000 MOV DWORD PTR [EBP-6C],00401818
0040201F C7458C08000000 MOV DWORD PTR [EBP-74],00000008
00402026 FF157C104000 CALL [MSVBVM60!__vbaVarDup]
0040202C 8D4D9C LEA ECX,[EBP-64]
Чтобы убрать проверку значения достаточно изменить переход по адресу 00401FF9 с условного на безусловный. Для этого меняется JE на JMP (то есть 74h на EBh). Как видим, такие простые проверки в Visual Basic малоэффективны, равно как и их аналоги на С++. А потому, если речь идет о проверке серийного номера или привязки ПО к диску, то разработчикам настоятельно рекомендуется защищать подобный код. Одним из хороших вариантов для такой защиты рекомендую использовать DotFix NiceProtect. Он может как виртуализовать критичный код, так и защитить программу от анализа в целом.
Анализ значений в виде разных типов данных
Ниже будут рассмотрены ряд методик эффективного анализа всевозможных значений в коде на Visual Basic.
Получение данных, если они лежат строкой в открытом виде
Когда бейсик копирует строку в память - Вы ее всегда можете отловить в отладке.
Для этого нужно поставить breakpoint на функцию __vbaStrCopy и когда он у Вас сработает, просто посмотрите код после je 66047B00 (он содержится в библиотеке и един для всех программ на VB6). Затем нужно посмотреть содержимое регистра edx минус 4 байта. В этом случае Вы увидите строчку, с которой работает в данный момент функция.
Exported fn(): __vbaStrCopy - Ord:008Ah
:66024532 56 PUSH ESI
:66024533 57 PUSH EDI
:66024534 85D2 TEST EDX, EDX
:66024536 8BF9 MOV EDI, ECX
:66024538 0F84C2350200 JE 66047B00
:6602453E FF72FC PUSH [EDX-04] ; По этому адресу лежит строка
:66024541 52 PUSH EDX
Получение строки, если она сравнивается с введенной
Поставьте breakpoint на функцию __vbaStrComp и чуть выше ее вызова будут два push'а. Они заносят в стек две Variant переменные для сравнения. Посмотрите содержимое регистра eax, чтобы узнать адрес текста. Взлянем, что лежит по этому адресу, обычно там нужная нам искомая строка.
00401BC7 50 PUSH EAX ; То что Вы ввели
00401BC8 6880174000 PUSH 00401780 ; Искомая строка
00401BCD FF1530104000 CALL [MSVBVM60!__vbaStrComp]
Получение данных, если происходит сравнение 4х байтовых числовых переменных
Поставьте breakpoint на функцию __vbai4str. Эта функция используется программой для перевода введенной текстовой строки в 4х байтовое число. Когда breakpoint сработает, посмотрите код ниже, там наверняка будет процедура сравнения строк. Но нужное нам значение будет в HEX виде (например, десятичное число 987654321 равно шестнадцатеричному числу 3ADE68B1).
00401B77 FF155C104000 CALL [MSVBVM60!__vbaI4Str]
00401B7D 8D4DE0 LEA ECX, [EBP-20]
00401B80 8BF8 MOV EDI, EAX
00401B82 FF157C104000 CALL [MSVBVM60!__vbaFreeStr]
00401B88 8D4DDC LEA ECX, [EBP-24]
00401B8B FF1580104000 CALL [MSVBVM60!__vbaFreeObj]
00401B91 81FFB168DE3A CMP EDI, 3ADE68B1 ; 3ADE68B1 - это нужное значение.
00401B97 7520 JNZ 00401BB9 ; Функция вернет ноль, если введенное
; и сравниваемое значения равны.
; Следовательно этот переход сработает только если
; сравниваемые данные отличны друг от друга
Тут мы можем узнать верное значение или отключить проверку вовсе (для этого нужно JNZ заменить на NOP). Как видим, в данном случае нужно поменять байты по адресу 00401B97 с 7520 на 9090.
Получение данных при сравнивании двухбайтовых чисел
Отличие от предыдущего примера в том, что нужно ставить breakpoint на функцию __vbaI2Str. Но иногда предварительно производится преобразование строки сначала в 4х байтовое число, а затем 4х байтовое число преобразуется в двухбайтовое посредством функции __vbaI2I4.
Сравнение двух переменных типа Single
Эту проверку также обойти довольно просто. Ставится breakpoint на функцию __vbaR4Str и когда он сработает, пролистываем машинный код вниз. Вы увидите переход после проверки. Для инверсии условия достаточно просто изменить условие перехода на обратное.
00401C0C FF153C104000 CALL [MSVBVM60!_vbaR4Str]
00401C12 D95DE4 FSTP REAL4 PTR [EBP-1C]
00401C15 8D4DDC LEA ECX, [EBP-24]
00401C18 8D55E0 LEA EDX, [EBP-20]
00401C1B 51 PUSH ECX
00401C1C 52 PUSH EDX
00401C1D 6A02 PUSH 02
00401C1F FF1570104000 CALL [MSVBVM60!__vbaFreeStrList]
00401C25 83C40C ADD ESP, 0C
00401C28 8D4DD8 LEA ECX, [EBP-28]
00401C2B FF1594104000 CALL [MSVBVM60!__vbaFreeObj]
00401C31 817DE43A92FC42 CMP DWORD PTR [EBP-1C], 42FC923A
00401C38 7520 JNZ 00401C5A ; Если ноль, то введенно неверное значение
Замените инструкцию по адресу 00401C38 на 9090 (чтобы в данном месте не выполнялось никаких операций) или измените условный переход на противоположный. На Visual Basic нельзя проверить то, что вы заменили эти байты на два NOP'а (9090). Если у Вас все же возникают опасения, что код перестанет работать при отсутствии команд, просто введите команды, которые ничего не поменяют, например:
inc eax
dec eax
Эти команды однобайтовые и как раз впишутся на место условного перехода
Обход проверки значений с плавающей точкой (double).
Это также сделать очень просто. Ставим breakpoint на функцию __vbaR8Str и листаем код немного ниже останова на breakpoint'е, Вы увидите процедуру сравнения (FCOMP REAL8 PTR [Address]), после нее идет test и jmp. Условие этого перехода также легко изменить на противоположное.
00401C55 FF1510104000 CALL [MSVBVM60!rtcTrimBstr]
00401C5B 8BD0 MOV EDX, EAX
00401C5D 8D4DD8 LEA ECX, DWORD PTR [EBP-28]
00401C60 FF1580104000 CALL [MSVBVM60!__vbaStrMove]
00401C66 50 PUSH EAX
00401C67 FF1560104000 CALL [MSVBVM60!__vbaR8Str]
00401C6D DC1DD8104000 FCOMP REAL8 PTR [004010D8] ;сравнение значений
00401C73 DFE0 FSTSW AX ;обработка сопроцессором
00401C75 F6C440 TEST AH, 40 ;проверка правильности значений
00401C78 7409 JE 00401C83 ;переход
Замените инструкцию по адресу 00401C78 на 9090 (чтобы в данном месте не выполнялось никаких операций) или измените условный переход на противоположный. Как уже упоминалось ранее, на Visual Basic никак нельзя проверить то, что Вы заменили эти байты на два NOP'а. Только внешняя защита поможет защититься от модификации кода.
Получение строки, если она по-XOR-ена.
Алгоритм работы бейсиковской процедуры xor часто бывает таков. Введенная строка посимвольно переводится в ASCII коды, которые по очереди XOR'ятся. Затем производится обратный перевод, возможно с предварительными математическими манипуляциями с ASCII кодами. ANSI коды могут XOR'иться со случайным или фиксированным числом.
00401E6C 50 PUSH EAX
00401E6D FF1544104000 CALL [MSVBVM60!rtcMidCharBstr]
00401E73 8BD0 MOV EDX, EAX
00401E75 8D4DC8 LEA ECX, [EBP-38]
00401E78 FFD6 CALL ESI
00401E7A 50 PUSH EAX
00401E7B FF1518104000 CALL [MSVBVM60!rtcAnsiValueBstr]
00401E81 0FBFC8 MOVSX ECX, AX
00401E84 81F191000000 XOR ECX, 00000091 ;ANSI XOR 91
00401E8A 51 PUSH ECX ;результат XOR'а
00401E8B FF1570104000 CALL [MSVBVM60!rtcBstrFromAnsi]
Ставим breakpoint на 00401E8A 51 PUSH ECX и отслеживаем изменение содержимого регистра ECX при прохождении цикла. Когда программа пройдет весь цикл - Вы получите расшифрованную строку.
CALL [MSVBVM60!rtcMidCharBstr] ;получает один символ из строки
CALL [MSVBVM60!rtcAnsiValueBstr] ;получает из символа Ansi код
CALL [MSVBVM60!rtcBstrFromAnsi] ;преобразует Ansi код в строку
В этом случае, следуя ниже по коду, скорее всего вы встретите сравнение и переход. При этом CALL [MSVBVM60!__vbaStrComp] может быть использован для сравнения строк. Если не производится обращение к MSVBVM60!rtcBstrFromAnsi, тогда со строкой производятся определенные математические манипуляции, при этом она может быть представлена как 2х или 4х байтное число или число с плавающей точкой. Об этих случаях уже было сказано выше.
Заключение
Надеюсь, что прочитав данную статью Вас уже больше не пугают программы на Visual Basic и примерно понятен их анализ. Чтобы посмотреть, как выглядит код на Visual Basic вживую, советую посмотреть примеры, которые лежат на данном сайте в разделе "Разное".
Благодарю Вас за интерес к данной статье!
Комментарии |
отсутствуют |
Добавление комментария |