I've tried to reproduce the issue in a small sample application, or even in a sub-project compiled as DLL.
It only occurs when compiling my full main project as big executable.
This one, compiled with VS 2010 with default settings in release mode, generates code like mentioned in the first posting.
Code: Select all
#define SECSTR(_ShortAlphaNumeric, _RegularText) \
char* _ShortAlphaNumeric = VMProtectDecryptStringA(_RegularText)
#define FREESEC(_ShortAlphaNumeric) \
VMProtectFreeString(_ShortAlphaNumeric)
__declspec(noinline) char* ThisCanThrow(char* _Prm)
{
if (*_Prm == 'x')
{
throw 187;
}
return _Prm + 2;
}
class CHasDestructor
{
public:
__declspec(noinline) CHasDestructor()
: m_pNum(new int)
{
*m_pNum = __rdtsc();
}
__declspec(noinline) ~CHasDestructor()
{
delete m_pNum;
}
__declspec(noinline) int GetNum()
{
return *m_pNum;
}
private:
int* m_pNum;
};
int _tmain(int argc, char* argv[])
{
SECSTR(pFmtX, "Testdata x! %s %d %s\n");
CHasDestructor TmpX;
SECSTR(pExtra, "Some extra calls to force edi usage");
printf(pFmtX, "Is x %d %s %s\n", TmpX.GetNum(), ThisCanThrow(argv[0]), pExtra);
FREESEC(pExtra);
FREESEC(pFmtX);
if (argc == 2)
{
SECSTR(pFmt, "Testdata 2! %s %d %s\n");
if (GetTickCount() < 0xEFFFFFFF)
{
CHasDestructor Tmp;
SECSTR(pExtra, "Some extra calls to force edi usage");
printf(pFmt, "Is 2 %d %s %s\n", Tmp.GetNum(), ThisCanThrow(argv[0]), pExtra);
FREESEC(pExtra);
FREESEC(pFmt);
}
}
else
{
CHasDestructor Tmp;
SECSTR(pFmt, "Testdata not 2! %s %d %s\n");
printf(pFmt, "Is not 2 ", Tmp.GetNum(), ThisCanThrow(argv[1]));
FREESEC(pFmt);
}
CHasDestructor Tmp;
SECSTR(pFmt, "Done! %s %d");
printf(pFmt, "Done?\n", Tmp.GetNum());
FREESEC(pFmt);
}
However VMProtect handles it just fine, even though the relevant code sequences are identical.
I've put these two strings in my main executable for testing:
Code: Select all
SECSTR(pFmt, "VMProtect Debug - This string is protected %s %s %s");
012948B7 push offset string "VMProtect Debug - This string is"... (1537578h)
012948BC mov byte ptr [ebp-4],3
012948C0 call esi
...
SECSTR(pFmt, "VMProtect Debug - This string is not protected %s");
01294907 push offset string "VMProtect Debug - This string is"... (1537544h)
0129490C mov byte ptr [ebp-4],5
01294910 call esi
ESI isn't touched between the two calls. VMProtect shows the first string, but not the second one.
Both strings appear in the unprotected executable and are referenced properly.
Having strings stay unprotected without any warning is a big problem.
While trying the find the cause of the issue I also came across something else.
It also broke when a string was references two times, and thus moved to a register.
VMProtect didn't display the string at all then.
I think this one is easy to avoid in practice, so I don't really need a fix for it anytime soon.
Code: Select all
0101E721 mov esi,dword ptr [__imp__VMProtectDecryptStringA@4 (127F918h)]
0101E727 mov edi,dword ptr [__imp__VMProtectFreeString@4 (127F914h)]
...
0101E72F mov ebx,offset string "VMProtect Debug - This string is"... (12FD528h)
...
0101E793 push ebx
0101E794 mov byte ptr [ebp-4],3
0101E798 call esi
I'm currently trying to get permission to send the main executable which can be used to reproduce the first mentioned issue.
Should I mail it to your info@ address?