Jump to content
Tuts 4 You

VMProtect 3.1.2 (Build 886) Anti-debug Method Improved


Xjun
Go to solution Solved by mrexodia,

Recommended Posts

Difficulty : 6
Language : C++
Platform :  Windows
OS Version : All 
Packer / Protector : Safengine

Description :

hi,friends .  

I reversed the VMP3.1.2 (build 886) anti debug method and improved this method. You need to use the debugger to bypass his tooltip

Screenshot :

success screenshot:

image.png.099808da67e2b5bb0fabe68d478df1b9.png

file :

XAntiDebug2.rar

Edited by Xjun
  • Like 1
Link to comment
Share on other sites

  • Solution

Thanks for sharing @Xjun!

Quick summary:

  • IsDebuggerPresent
  • CheckRemoteDebuggerPresent
  • CloseHandle(0xDEADC0DE)
  • ZwQueryInformationProcess(ProcessDebugObjectHandle), called correctly 
  • crc32 check on direct syscall
  • ZwQueryInformationProcess(ProcessDebugObjectHandle), called with ReturnLength == ProcessInformation

I think TitanHide fails on the last check only, is this correct?

  • Like 2
Link to comment
Share on other sites

3 hours ago, mrexodia said:

Thanks for sharing @Xjun!

Quick summary:

  • IsDebuggerPresent
  • CheckRemoteDebuggerPresent
  • CloseHandle(0xDEADC0DE)
  • ZwQueryInformationProcess(ProcessDebugObjectHandle), called correctly 
  • crc32 check on direct syscall
  • ZwQueryInformationProcess(ProcessDebugObjectHandle), called with ReturnLength == ProcessInformation

I think TitanHide fails on the last check only, is this correct?

Yes  TitanHide plugin Only the last time will be detected.

Other case also have this problem . Let's take a look at the WindowsResearchKernel source code

    case ProcessDebugObjectHandle :
        //
        if (ProcessInformationLength != sizeof (HANDLE)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }

        st = DbgkOpenProcessDebugPort (Process,
                                       PreviousMode,
                                       &DebugPort);

        if (!NT_SUCCESS (st)) {
            DebugPort = NULL;
        }

        ObDereferenceObject (Process);

        //
        // Either of these may cause an access violation. The
        // exception handler will return access violation as
        // status code. No further cleanup needs to be done.
        //

        try {
            *(PHANDLE) ProcessInformation = DebugPort;

            if (ARGUMENT_PRESENT (ReturnLength)) {
                *ReturnLength = sizeof (HANDLE);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            if (DebugPort != NULL) {
                ObCloseHandle (DebugPort, PreviousMode);
            }
            return GetExceptionCode ();
        }

        return st;

    case ProcessDebugFlags :

        if (ProcessInformationLength != sizeof (ULONG)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        st = ObReferenceObjectByHandle (ProcessHandle,
                                        PROCESS_QUERY_INFORMATION,
                                        PsProcessType,
                                        PreviousMode,
                                        &Process,
                                        NULL);

        if (!NT_SUCCESS (st)) {
            return st;
        }


        try {
            *(PULONG) ProcessInformation = (Process->Flags&PS_PROCESS_FLAGS_NO_DEBUG_INHERIT)?0:PROCESS_DEBUG_INHERIT;

            if (ARGUMENT_PRESENT (ReturnLength) ) {
                *ReturnLength = sizeof(HANDLE);
            }
        } except (EXCEPTION_EXECUTE_HANDLER) {
            st = GetExceptionCode ();
        }

        ObDereferenceObject (Process);

        return st;

Look   *ReturnLength = sizeof (HANDLE);

Many anti debug plug-ins first call the original function, then * (HANDLE *) ProcessInformation = NULL;

And windows system is the last *ReturnLength = sizeof (HANDLE);

 

  • Like 4
Link to comment
Share on other sites

Very insightful, thanks a lot! I created issues on ScyllaHide and TitanHide and fixed the implementations of NtQuerySystemInformation and NtQueryInformationProcess, but I don't really know how to handle this for NtQueryObject... Should I just read the size from ReturnLength before overwriting the other buffers and then restore it afterwards or should I do something else? The research kernel has pretty weird handling for invalid ReturnLength addresses so I'm not entirely sure. How did you handle this in your driver?

Also, is this actually used in VMProtect or is this your POC?

Link to comment
Share on other sites

NtQueryObject should work the same in principle, it's just a longer function. Taking only ObjectTypeInformation (paying $1 to whoever tells me how to format C++ on this forum):

ULONG TempReturnLength;

switch( ObjectInformationClass ) {

    // ...lots of cases...

    case ObjectTypeInformation:

        //
        // Call a local worker routine
        //

        Status = ObQueryTypeInfo( ObjectType,
                                 (POBJECT_TYPE_INFORMATION)ObjectInformation,
                                 ObjectInformationLength,
                                 &TempReturnLength );
        break;

    default:

        //
        // To get to this point we must have had an object and the
        // information class is not defined, so we should dereference the
        // object and return to our user the bad status
        //

        ObDereferenceObject( Object );

        return( STATUS_INVALID_INFO_CLASS );
    }

    //
    // Now if the caller asked for a return length we'll set it from
    // our local copy
    //

    try {

        if (ARGUMENT_PRESENT( ReturnLength ) ) {

            *ReturnLength = TempReturnLength;
        }

    } except( EXCEPTION_EXECUTE_HANDLER ) {

        //
        // Fall through, since we cannot undo what we have done.
        //
    }

ObQueryTypeInfo actually writes to TempReturnLength before doing anything else, but TempReturnLength is a KM-only variable until it is copied back to the caller at the end, so the final order of writes to user mode is the same.

Link to comment
Share on other sites

On 31/10/2017 at 2:44 AM, Xjun said:

Difficulty : 6
Language : C++
Platform :  Windows
OS Version : All 
Packer / Protector : Safengine

Description :

hi,friends .  

I reversed the VMP3.1.2 (build 886) anti debug method and improved this method. You need to use the debugger to bypass his tooltip

Screenshot :

success screenshot:

image.png.099808da67e2b5bb0fabe68d478df1b9.png

file :

XAntiDebug2.rar

Thanks for sharing.

 

I would like to comment, that since this version of the protection you created,  copies the index of ZwQueryInformationProcess to the SSDT directly from the ntdll.dll file itself, and does not rely on hard coded values, then this is a great improvement over vmprotect's own version which can be easily fooled by changing the windows OS build number to an unsupported random number.. I guess that randomly locating the newly created syscall function in a none fixed memory area is implemented to prevent static patching. Great work.

  • Like 1
Link to comment
Share on other sites

On 2017/11/8 at 2:49 AM, mrexodia said:

Very insightful, thanks a lot! I created issues on ScyllaHide and TitanHide and fixed the implementations of NtQuerySystemInformation and NtQueryInformationProcess, but I don't really know how to handle this for NtQueryObject... Should I just read the size from ReturnLength before overwriting the other buffers and then restore it afterwards or should I do something else? The research kernel has pretty weird handling for invalid ReturnLength addresses so I'm not entirely sure. How did you handle this in your driver?

Also, is this actually used in VMProtect or is this your POC?

This is what I learned from VMP!

In my humble opinion .When you call the original function, you should first save the returnLenght and check whether it is empty. After processing ProcessDebugObjectHandle, restore the returnLenght. The rest is the same.:)

 

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...