Some VMProtectDecryptString calls are not detected by VMP

Issues related to VMProtect
Post Reply
ChMo
Posts: 21
Joined: Mon Aug 26, 2013 11:54 am

Some VMProtectDecryptString calls are not detected by VMP

Post by ChMo »

I've come across an issue due to which some VMProtectDecryptStrings() protected strings show up in the UI for protection, while others don't.
The issue goes away when using #pragma optimize("g", off), however that's no good workaround when having lots of rarely used error messages in frequently called functions.

The string functions are called through registers, but that's been fixed a long time ago, according to this thread:

Code: Select all

00F6E6F4  mov         esi,dword ptr [__imp__VMProtectDecryptStringA@4 (11CF910h)]  
00F6E6FA  mov         edi,dword ptr [__imp__VMProtectFreeString@4 (11CF914h)]
Later on those functions aren't called directly like this:

Code: Select all

00F6E75F  push        offset string "Trial version expired"... (124D508h)  
00F6E764  call        esi  
... but like this:

Code: Select all

00F6E75F  push        offset string "Trial version expired"... (124D508h)  
00F6E764  mov         byte ptr [ebp-4],3  
00F6E768  call        esi  

00F6E7AF  push        offset string "License corrupted"... (124D4CCh)  
00F6E7B4  mov         byte ptr [ebp-4],5  
00F6E7B8  call        esi  
This code appears to occur due to the way MSVC performs exception handling. In each local block, which contains a protected string, I also create some temporary object, which must be destroyed in case of an exception. MSVC seems to keep track of it using an invisible local variable in the function.
The "mov" disappears if I move the creation of the temporary error-object out of the conditional scope that triggers the message.
It would be really nice if the VMP protected string detection could be adapted, so strings like those (and I have many of them, as error handling is performed through a macro), are protected as well.

If it can't be fixed, it should at least be detected. So if VMP comes across an ESI/EDI call to DecryptString, which doesn't seem to reference any actual string, it should display a warning, along with the disassembly of the function, so a workaround can be made in the source code.
Admin
Site Admin
Posts: 2686
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Some VMProtectDecryptString calls are not detected by VM

Post by Admin »

Could you send us a test application with this problem?
ChMo
Posts: 21
Joined: Mon Aug 26, 2013 11:54 am

Re: Some VMProtectDecryptString calls are not detected by VM

Post by ChMo »

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?
Admin
Site Admin
Posts: 2686
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Some VMProtectDecryptString calls are not detected by VM

Post by Admin »

Should I mail it to your info@ address?
Yes.
ChMo
Posts: 21
Joined: Mon Aug 26, 2013 11:54 am

Re: Some VMProtectDecryptString calls are not detected by VM

Post by ChMo »

Ok, sent :)
Admin
Site Admin
Posts: 2686
Joined: Mon Aug 21, 2006 8:19 pm
Location: Russia, E-burg
Contact:

Re: Some VMProtectDecryptString calls are not detected by VM

Post by Admin »

We are going to improve the detection algorithm in the version 3.0
ChMo
Posts: 21
Joined: Mon Aug 26, 2013 11:54 am

Re: Some VMProtectDecryptString calls are not detected by VM

Post by ChMo »

Great, thanks.
Any rough estimate on when it will be ready (for testing)?
Post Reply