Хотелось бы на этом примере показать еще раз как не стоит организовывать защиту программы:
- Нельзя использовать упаковку как единственный метод защиты. Основная фишка VMProtect - это все-таки виртуализация критичного кода программы и если из всего функционала использовать лишь упаковку, то снятие защиты будет не труднее чем снятие UPX.
- Разработчики допускают классические ошибки вида:
(some code)
...
VMProtectBegin('My marker');
int res = VMProtectSetSerialNumber(...);
VMProtectEnd();
if (res) {
MessageBox(...,'Invalid serial number');
}
...
(some code)
Что мы здесь видим? Здесь мы видим, что результат работы защищенной функции (в нашем случае это VMProtectSetSerialNumber, но там может быть любая другая пользовательская функция) анализируется уже в незащищенном коде. Т..е. опять имеет классически патч в виде обхода маркера и самой проверки:
(some code)
...
JMP @1 <- patch here
VMProtectBegin('My marker');
int res = VMProtectSetSerialNumber(...);
VMProtectEnd();
if (res) {
MessageBox(...,'Invalid serial number');
}
@1:
...
(some code)
В данном примере в маркер должна попадать как минимум сама проверка “if (res)”, а как максимум я бы порекомендовал засунуть в маркер еще и код, который инициализирует какие-либо объекты, без которых программа просто не сможет работать:
VMProtectBegin('My marker');
(important code)
...
int res = VMProtectSetSerialNumber(...);
if (res) {
MessageBox(...,'Invalid serial number');
}
...
(important code)
VMProtectEnd();
- Рассмотрим еще один пример:
int foo()
{
VMProtectBegin('foo');
return VMProtectGetSerialNumber(...);
VMProtectEnd();
}
void main()
{
VMProtectBegin('My marker');
(some code)
...
int res = foo();
if (res) {
MessageBox(...,'Invalid serial number');
}
...
(some code)
VMProtectEnd();
}
Что мы здесь видим? Видим, что foo, результат которой нам очень важен, на самом деле оказывается не защищен из-за особенностей маркеров на захватывать прологи и эпилоги. Т.е. после виртуализации foo мы получим нечто подобное:
push ebp
mov ebp, esp
sub ebp, XXXX
...
(virtualized code)
...
pop ebp
ret
Теперь крякеру не составит труда подкорретировать результат функции:
...
pop ebp
xor eax, eax <- patch here
ret