Click here to Skip to main content
15,892,737 members
Articles / Programming Languages / C++

Detect Driver

,
Rate me:
Please Sign up or sign in to vote.
5.00/5 (46 votes)
10 Mar 2010CPOL12 min read 110.4K   9.1K   155  
This article is the continue of the previously posted article Hide Driver. Some methods to detect hidden files and processes are described in it
/*
    This is a C++ run-time library for Windows kernel-mode drivers.
    Copyright (C) 2004 Bo Branten.
*/

/*
    The following is a modified subset of wine/dlls/msvcrt/cppexcept.c
    and wine/dlls/msvcrt/cppexcept.h from version wine20040505.
*/

/*
 * msvcrt C++ exception handling
 *
 * Copyright 2002 Alexandre Julliard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <ntddk.h>
#include "cxx.h"

void __cdecl _global_unwind2(void * pRegistFrame);

typedef void (__cdecl *_se_translator_function)(unsigned int, struct _EXCEPTION_POINTERS*);

_se_translator_function _se_translator;

_se_translator_function __cdecl _set_se_translator(_se_translator_function _new_se_translator)
{
    _se_translator_function _prev_se_translator = _se_translator;
    _se_translator = _new_se_translator;
    return _prev_se_translator;
}

/*
 * The exception frame, used for registering exception handlers
 * Win32 cares only about this, but compilers generally emit
 * larger exception frames for their own use.
 */

struct __EXCEPTION_FRAME;

typedef EXCEPTION_DISPOSITION (__stdcall *PEXCEPTION_HANDLER)(PEXCEPTION_RECORD,struct __EXCEPTION_FRAME*,
                               PCONTEXT,struct __EXCEPTION_FRAME **);

typedef struct __EXCEPTION_FRAME
{
    struct __EXCEPTION_FRAME *Prev;
    PEXCEPTION_HANDLER       Handler;
} EXCEPTION_FRAME, *PEXCEPTION_FRAME;

/*
 * From OS/2 2.0 exception handling
 * Win32 seems to use the same flags as ExceptionFlags in an EXCEPTION_RECORD
 */

#define EH_NONCONTINUABLE   0x01
#define EH_UNWINDING        0x02
#define EH_EXIT_UNWIND      0x04
#define EH_STACK_INVALID    0x08
#define EH_NESTED_CALL      0x10

typedef void (*vtable_ptr)();

/* the object the typeid operator returns a reference to */
typedef struct _type_info
{
    vtable_ptr *vtable;  /* pointer to the objects vtable */
    void       *data;    /* unused in drivers, dynamically allocated undecorated type name in Win32 */
    char        name[1]; /* decorated (raw) type name */
} type_info;

/* the extended exception frame used by C++ */
typedef struct _cxx_exception_frame
{
    EXCEPTION_FRAME frame;    /* the standard exception frame */
    int             trylevel; /* current trylevel */
    ULONG           ebp;      /* saved ebp */
} cxx_exception_frame;

/* info about a single catch {} block */
typedef struct _catchblock_info
{
    unsigned int flags;      /* flags (see TYPE_FLAG_* flags below) */
    type_info   *type_info;  /* C++ type caught by this block */
    int          offset;     /* stack offset to copy exception object to */
    void       (*handler)(); /* catch block handler code */
} catchblock_info;

#define TYPE_FLAG_CONST      1
#define TYPE_FLAG_VOLATILE   2
#define TYPE_FLAG_REFERENCE  8

/* info about a single try {} block */
typedef struct _tryblock_info
{
    int              start_level;      /* start trylevel of that block */
    int              end_level;        /* end trylevel of that block */
    int              catch_level;      /* initial trylevel of the catch block */
    int              catchblock_count; /* count of catch blocks in array */
    catchblock_info *catchblock;       /* array of catch blocks */
} tryblock_info;

/* info about the unwind handler for a given trylevel */
typedef struct _unwind_info
{
    int    prev;       /* prev trylevel unwind handler, to run after this one */
    void (*handler)(); /* unwind handler */
} unwind_info;

/* descriptor of all try blocks of a given function */
typedef struct _cxx_function_descr
{
    unsigned int   magic;          /* must be CXX_FRAME_MAGIC */
    int            unwind_count;   /* number of unwind handlers */
    unwind_info   *unwind_table;   /* array of unwind handlers */
    unsigned int   tryblock_count; /* number of try blocks */
    tryblock_info *tryblock;       /* array of try blocks */
    unsigned int   unknown[3];
} cxx_function_descr;

typedef void (*cxx_copy_ctor)(void);

/* complete information about a C++ type */
typedef struct _cxx_type_info
{
    unsigned int  flags;            /* flags (see CLASS_* flags below) */
    type_info    *type_info;        /* C++ type info */
    int           this_offset;      /* offset of base class this pointer from start of object */
    int           vbase_descr;      /* offset of virtual base class descriptor */
    int           vbase_offset;     /* offset of this pointer offset in virtual base class descriptor */
    size_t        size;             /* object size */
    cxx_copy_ctor copy_constructor; /* copy constructor */
} cxx_type_info;

#define CLASS_IS_SIMPLE_TYPE          1
#define CLASS_HAS_VIRTUAL_BASE_CLASS  4

/* table of C++ types that apply for a given object */
typedef struct _cxx_type_info_table
{
    unsigned int         count;            /* number of types */
    const cxx_type_info *cxx_type_info[1]; /* array of types */
} cxx_type_info_table;

/* type information for an exception object */
typedef struct _cxx_exception_type
{
    unsigned int               flags;           /* TYPE_FLAG_* flags */
    void                     (*destructor)();   /* exception object destructor */
    void*                      custom_handler;  /* custom handler for this exception */
    const cxx_type_info_table *type_info_table; /* list of types for this exception object */
} cxx_exception_type;

/* installs a new exception handler */
#define cxx_push_handler(handler) __asm {   \
    __asm push offset handler               \
    __asm mov  eax, dword ptr fs:[0]        \
    __asm push eax                          \
    __asm mov  dword ptr fs:[0], esp        \
}

/* restores the previous exception handler */
#define cxx_pop_handler() __asm {           \
    __asm mov eax, dword ptr [esp]          \
    __asm mov dword ptr fs:[0], eax         \
    __asm add esp, 8                        \
}

/* called when an unrecoverable error has been detected */
void terminate()
{
    DbgPrint("terminate was called\n");
    KeBugCheck(STATUS_DRIVER_INTERNAL_ERROR);
}

/* exception handler for unexpected exceptions thrown while handling an exception */
static EXCEPTION_DISPOSITION __stdcall unexpected_handler(PEXCEPTION_RECORD rec, PEXCEPTION_FRAME frame,
                                                          PCONTEXT context, void *dispatch)
{
    DbgPrint("unexpected exception\n");
    KeBugCheck(STATUS_DRIVER_INTERNAL_ERROR);
    return ExceptionContinueSearch;
}

/* frame pointer register 'ebp' modified by inline assembly code */
#pragma warning(disable:4731)

/* call a function with a given ebp */
static void *cxx_call_handler(void *func, void *func_ebp)
{
    void *result;

    __asm
    {
        mov  eax, func
        push ebp
        mov  ebp, func_ebp
        call eax
        pop  ebp
        mov  result, eax
    }

    return result;
}

#pragma warning(default:4731)

/* call a copy constructor */
static void cxx_call_copy_constructor(void *func, void *object, void *src_object,
                                      int has_vbase)
{
    __asm
    {
        mov  ecx, object
        cmp  has_vbase, 0
        je   no_vbase
        push 1
no_vbase:
        push src_object
        call func
    }
}

/* call the destructor of an object */
static void cxx_call_destructor(void *func, void *object)
{
    __asm
    {
        mov  ecx, object
        call func
    }
}

/* compute the this pointer for a base class of a given type */
static void *get_this_pointer(const cxx_type_info *type, void *object)
{
    void *this_ptr;
    int *offset_ptr;

    if (!type || !object) return NULL;
    this_ptr = (char *)object + type->this_offset;
    if (type->vbase_descr >= 0)
    {
        /* move this ptr to vbase descriptor */
        this_ptr = (char *)this_ptr + type->vbase_descr;
        /* and fetch additional offset from vbase descriptor */
        offset_ptr = (int *)(*(char **)this_ptr + type->vbase_offset);
        this_ptr = (char *)this_ptr + *offset_ptr;
    }
    return this_ptr;
}

/* check if the exception type is caught by a given catch block, and
   return the type that matched */
static const cxx_type_info *find_caught_type(cxx_exception_type *exception_type,
                                             catchblock_info *catchblock)
{
    unsigned int i;

    if (!catchblock->type_info)
    {
        return exception_type->type_info_table->cxx_type_info[0]; /* catch(...) matches any type */
    }

    for (i = 0; i < exception_type->type_info_table->count; i++)
    {
        const cxx_type_info *type = exception_type->type_info_table->cxx_type_info[i];

        if (catchblock->type_info != type->type_info)
        {
            if (strcmp(catchblock->type_info->name, type->type_info->name)) continue;
        }
        /* type is the same, now check the flags */
        if ((exception_type->flags & TYPE_FLAG_CONST) &&
            !(catchblock->flags & TYPE_FLAG_CONST)) continue;
        if ((exception_type->flags & TYPE_FLAG_VOLATILE) &&
            !(catchblock->flags & TYPE_FLAG_VOLATILE)) continue;
        return type;  /* it matched */
    }

    return NULL;
}

/* copy the exception object where the catch block wants it */
static void copy_exception(void *object, cxx_exception_frame *frame,
                           catchblock_info *catchblock, const cxx_type_info *type)
{
    void **dest_ptr;

    if (!catchblock->type_info || !catchblock->type_info->name[0]) return;
    if (!catchblock->offset) return;
    dest_ptr = (void **)((char *)&frame->ebp + catchblock->offset);

    if (catchblock->flags & TYPE_FLAG_REFERENCE)
    {
        *dest_ptr = get_this_pointer(type, object);
    }
    else if (type->flags & CLASS_IS_SIMPLE_TYPE)
    {
        memcpy(dest_ptr, object, type->size);
        /* if it is a pointer, adjust it */
        if (type->size == sizeof(void *)) *dest_ptr = get_this_pointer(type, *dest_ptr);
    }
    else  /* copy the object */
    {
        if (type->copy_constructor)
        {
            cxx_push_handler(unexpected_handler);
            cxx_call_copy_constructor(type->copy_constructor, dest_ptr, get_this_pointer(type, object),
                (type->flags & CLASS_HAS_VIRTUAL_BASE_CLASS));
            cxx_pop_handler();
        }
        else
        {
            memcpy(dest_ptr, get_this_pointer(type, object), type->size);
        }
    }
}

/* unwind the local function up to a given trylevel */
static void cxx_local_unwind(cxx_exception_frame *frame, cxx_function_descr *descr,
                             int last_level)
{
    while (frame->trylevel != last_level)
    {
        if (frame->trylevel < 0 || frame->trylevel >= descr->unwind_count)
        {
            terminate();
        }
        if (descr->unwind_table[frame->trylevel].handler)
        {
            cxx_push_handler(unexpected_handler);
            cxx_call_handler(descr->unwind_table[frame->trylevel].handler, &frame->ebp);
            cxx_pop_handler();
        }
        frame->trylevel = descr->unwind_table[frame->trylevel].prev;
    }
}

/* global unwind */
static void cxx_global_unwind(PEXCEPTION_FRAME last_frame)
{
    PEXCEPTION_FRAME frame, current_frame, stack_base, stack_top;
    PEXCEPTION_RECORD rec;
    PCONTEXT context;

    __asm
    {
        mov  eax, dword ptr fs:[0]
        mov  dword ptr [current_frame], eax

        mov  eax, dword ptr fs:[4]
        mov  dword ptr [stack_top], eax

        mov  eax, dword ptr fs:[8]
        mov  dword ptr [stack_base], eax
    }

    rec = ExAllocatePool(NonPagedPool, (sizeof(EXCEPTION_RECORD)));
    memset(rec, 0, sizeof(EXCEPTION_RECORD));
    //rec->ExceptionCode = STATUS_UNWIND;
    rec->ExceptionFlags = EH_UNWINDING;
    rec->ExceptionAddress = 0;
    rec->ExceptionRecord = 0;

    context = ExAllocatePool(NonPagedPool, (sizeof(CONTEXT)));
    memset(context, 0, sizeof(CONTEXT));

    for (frame = current_frame; frame != last_frame; frame = frame->Prev)
    {
        //check for possible stack corruption
        if (frame < stack_base ||
            frame + sizeof(EXCEPTION_FRAME) > stack_top ||
            frame > last_frame ||
            (frame->Prev != (PEXCEPTION_FRAME) -1 && frame > frame->Prev)
            )
        {
            terminate();
        }

        cxx_push_handler(unexpected_handler);
        (*frame->Handler)(rec, frame, context, 0);
        cxx_pop_handler();
    }

    ExFreePool(rec);
    ExFreePool(context);

    __asm
    {
        mov  eax, dword ptr [last_frame]
        mov  dword ptr fs:[0], eax
    }
}

/* exception handler for exceptions thrown from a catch block */
static EXCEPTION_DISPOSITION __stdcall catch_block_protector(PEXCEPTION_RECORD rec, PEXCEPTION_FRAME frame,
                                                             PCONTEXT context, void *dispatch)
{
    if (!(rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND)))
    {
        /* get the previous exception saved on the stack */
        void* exception_object = (void*) ((ULONG*)frame)[2];
        cxx_exception_type* exception_type = (cxx_exception_type*) ((ULONG*)frame)[3];

        if (rec->ExceptionCode == CXX_EXCEPTION &&
            rec->NumberParameters == 3 &&
            rec->ExceptionInformation[0] == CXX_FRAME_MAGIC &&
            rec->ExceptionInformation[1] == 0 &&
            rec->ExceptionInformation[2] == 0)
        {
            /* rethrow the previous exception */
            rec->ExceptionInformation[1] = (ULONG) exception_object;
            rec->ExceptionInformation[2] = (ULONG) exception_type;
        }
        else
        {
            /* throw of new exception, delete the previous exception object */
            if (exception_object && exception_type->destructor)
            {
                cxx_push_handler(unexpected_handler);
                cxx_call_destructor(exception_type->destructor, exception_object);
                cxx_pop_handler();
            }
        }
    }
    return ExceptionContinueSearch;
}

/* find and call the appropriate catch block for an exception */
/* returns the address to continue execution to after the catch block was called */
static void *call_catch_block(PEXCEPTION_RECORD rec, cxx_exception_frame *frame,
                              cxx_function_descr *descr)
{
    unsigned int i;
    int j;
    void *ret_addr;
    void *exception_object = (void *) rec->ExceptionInformation[1];
    cxx_exception_type *exception_type = (cxx_exception_type *) rec->ExceptionInformation[2];
    int trylevel = frame->trylevel;

    for (i = 0; i < descr->tryblock_count; i++)
    {
        tryblock_info *tryblock = &descr->tryblock[i];

        if (trylevel < tryblock->start_level) continue;
        if (trylevel > tryblock->end_level) continue;

        /* got a try block */
        for (j = 0; j < tryblock->catchblock_count; j++)
        {
            catchblock_info *catchblock = &tryblock->catchblock[j];
            const cxx_type_info *type = find_caught_type(exception_type, catchblock);
            if (!type) continue;

            /* copy the exception to its destination on the stack */
            copy_exception(exception_object, frame, catchblock, type);

            /* unwind the stack */
            _global_unwind2(frame);
            //cxx_global_unwind((PEXCEPTION_FRAME)frame);
            cxx_local_unwind(frame, descr, tryblock->start_level);

            frame->trylevel = tryblock->end_level + 1;

            /* call the catch block */
            __asm push exception_type
            __asm push exception_object
            cxx_push_handler(catch_block_protector);
            ret_addr = cxx_call_handler(catchblock->handler, &frame->ebp);
            cxx_pop_handler();
            __asm add esp, 8

            /* delete the exception object */
            if (exception_object && exception_type->destructor)
            {
                cxx_push_handler(unexpected_handler);
                cxx_call_destructor(exception_type->destructor, exception_object);
                cxx_pop_handler();
            }
            return ret_addr;
        }
    }
    return NULL;
}

static EXCEPTION_DISPOSITION cxx_frame_handler(PEXCEPTION_RECORD rec, cxx_exception_frame *frame,
                                               PCONTEXT context, void *dispatch,
                                               cxx_function_descr *descr)
{
    if (descr->magic != CXX_FRAME_MAGIC)
    {
        return ExceptionContinueSearch;
    }

    /* stack unwinding */
    if (rec->ExceptionFlags & (EH_UNWINDING | EH_EXIT_UNWIND))
    {
        if(frame->trylevel >= descr->unwind_count) //stack corruption
        {
            terminate();
        }
        if (descr->unwind_count)
        {
            cxx_local_unwind(frame, descr, -1);
        }
    }
    /* non C++ exception */
    else if (rec->ExceptionCode != CXX_EXCEPTION)
    {
        if (_se_translator)
        {
            EXCEPTION_POINTERS ep = {rec, context};
            (*_se_translator)(rec->ExceptionCode, &ep);
        }
    }
    /* C++ exception */
    else if (rec->NumberParameters == 3 &&
             rec->ExceptionInformation[0] == CXX_FRAME_MAGIC &&
             rec->ExceptionInformation[1] != 0 &&
             rec->ExceptionInformation[2] != 0)
    {
        void *ret_addr = call_catch_block(rec, frame, descr);

        if (ret_addr)
        {
            rec->ExceptionFlags &= ~EH_NONCONTINUABLE;
            context->Eip = (ULONG)ret_addr;
            context->Ebp = (ULONG)&frame->ebp;
            context->Esp = ((ULONG*)frame)[-1];
            return ExceptionContinueExecution;
        }
    }
    return ExceptionContinueSearch;
}

__declspec (naked)
EXCEPTION_DISPOSITION __cdecl __CxxFrameHandler(PEXCEPTION_RECORD rec, PEXCEPTION_FRAME frame,
                                                PCONTEXT context, void *dispatch)
{
    __asm
    {
        push ebp
        mov  ebp, esp
        cld
        push eax
        push dispatch
        push context
        push frame
        push rec
        call cxx_frame_handler
        mov  esp, ebp
        pop  ebp
        ret  16
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Apriorit Inc.
United States United States
ApriorIT is a software research and development company specializing in cybersecurity and data management technology engineering. We work for a broad range of clients from Fortune 500 technology leaders to small innovative startups building unique solutions.

As Apriorit offers integrated research&development services for the software projects in such areas as endpoint security, network security, data security, embedded Systems, and virtualization, we have strong kernel and driver development skills, huge system programming expertise, and are reals fans of research projects.

Our specialty is reverse engineering, we apply it for security testing and security-related projects.

A separate department of Apriorit works on large-scale business SaaS solutions, handling tasks from business analysis, data architecture design, and web development to performance optimization and DevOps.

Official site: https://www.apriorit.com
Clutch profile: https://clutch.co/profile/apriorit
This is a Organisation

33 members

Written By
Software Developer Codedgers Inc
Ukraine Ukraine
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions