Jump to content
Tuts 4 You
  • 0
Xjun

DebugMe - VMProtect 3.1.2 (Build 886) Anti-debug Method Improved

Question

Xjun

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

Share this post


Link to post
Share on other sites

12 answers to this question

Recommended Posts

  • 0
mrexodia

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

Share this post


Link to post
Share on other sites
  • 0
Teddy Rogers

There seems to be a problem with the screenshot, please can you reattach. Thank you...

Ted.

Share this post


Link to post
Share on other sites
  • 0
Xjun

Everyone seems not interested in challenging this process, I decided to open source I improved the anti-debugging method :)

 

  • Like 1

Share this post


Link to post
Share on other sites
  • 0
recrc

Good work Xjun and thank you for sharing.

Share this post


Link to post
Share on other sites
  • 0
Xjun
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

Share this post


Link to post
Share on other sites
  • 0
mrexodia

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?

Share this post


Link to post
Share on other sites
  • 0
Mattiwatti

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.

Share this post


Link to post
Share on other sites
  • 0
Aesculapius
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.

Share this post


Link to post
Share on other sites
  • 0
Xjun
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

Share this post


Link to post
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

×