VMProtect 是一种很可靠的工具,可以保护应用程序代码免受分析和破解,但只有在应用程序内保护机制正确构建且没有可能破坏整个保护的严重错误的情况下,才能实现最好的效果。
VMProtect最新试用版下载https://www.evget.com/product/1859/download接下来我们一起来看一个非常简单的应用程序,它只包含一个表单(Form1),一个文本元素(Edit1)和一个按钮(Button1)。该应用程序的工作方式如下:单击Button1时,应用程序会检查输入的密码是否正确并显示相应的消息。
这里使用的是一种非常简单的算法来检查密码:第一步,我们将其转换为数字形式,然后我们通过除以17来计算余数。如果输入密码的数字表示除以17的余数等于13,则密码正确。Delphi上的密码检查程序实现如下:
function TForm1.CheckPassword: Boolean; begin Result:=(StrToIntDef(Edit1.Text, 0) mod 17=13); end; procedure TForm1.Button1Click(Sender: TObject); begin if CheckPassword then MessageDlg('Correct password', mtInformation, [mbOK], 0) else begin MessageDlg('Incorrect password', mtError, [mbOK], 0); Edit1.SetFocus; end; end;
保护程序和功能的选择可以通过三种方式完成:
- 使用编译器创建的MAP文件以及程序的可执行文件。MAP文件包含有关应用程序的所有过程和功能的名称以及地址的所有必要信息。如果使用MAP文件,则可以选择按名称保护的过程和函数。使用MAP文件,每次重新编译项目时,VMProtect都会自动确定过程和函数的新地址。
- 使用插入应用程序源代码的标记。 标记是VMProtect用于确定受保护片段边界的特殊标记。 此外,VMProtect支持具有预定义编译类型的标记。当你只想保护函数或过程的一部分时,使用标记非常有用的。使用标记可以指定要保护的字符串常量的代码部分。
- 通过可执行文件中受保护过程的地址。 与上述两种方式相比,这种方式使用起来不太方便。 每次修改和重新编译应用程序时,都必须再次指定所有地址。 对于没有源代码的应用,建议使用此类保护。
使用MAP文件来定义受保护代码的边界有一个很重要的优点。几乎所有具有局部变量或使用堆栈来保存寄存器和/或中间计算结果的过程或函数都有所谓的序言(prologue)和结尾(epilogue),它们相应地位于编译过程或函数的开头和结尾:
push ebp \ mov ebp, esp \ prologue push 00 / push ebx / ... pop ebx \ pop ecx \ epilogue pop ebp / ret /
由于现代编译器的工作方式,代码标记从不包含功能的序言和结尾。即使开始和结束之间的 CheckPassword 函数的整个代码都包含在标记中。对于黑客来说,只需要修改函数的序言让程序不执行虚拟化代码可能就足够了。 对于CheckPassword 函数,可以按如下方式完成:
mov eax, 1 ret
注意:
如果使用MAP文件来选择用于虚拟化的代码片段,则序言和结尾也被虚拟化,从而显着提高受保护程序的防黑客能力。此外,一个虚拟化函数是从另一个虚拟化函数调用的,控件在它们之间传输而不实际跳转到被调用函数的地址(在这种情况下,调用是一个简单的跳转到虚拟机解释器的字节码内的另一个地址)。 这也加强了对程序的保护,因为黑客对入口点的所有修改都没有用。使用虚拟化函数时,只有在从未受保护或变异的代码片段调用受保护函数时,才会将控制权转移到虚拟化函数的入口点。