C++ exception handling on custom pe loader

I am trying to manually handle a C++ exception ,caused by dynamically loaded code, through a VEH handler.At first I capture the exception with VEH then perform a stackwalk then call the __c_specific_handler to see if there is an actual handler and at last I need to make a call to RtlUnwindEx to actually call the catch callback. Reading at Skywing's study on X64 exception handling (Programming against the x64 exception handling support, part 6: Frame consolidation unwinds « Nynaeve) I find this:

Consolidation unwinds are a special form of unwind that is indicated to RtlUnwindEx with a special exception code, STATUS_UNWIND_CONSOLIDATE. This exception code changes RtlUnwindEx’s behavior slightly; it suppresses the behavior of substituting the TargetIp argument to RtlUnwindEx with the unwound context’s Rip value. Make a local copy of the passed-in context record.
Treating ExceptionRecord->ExceptionInformation[0] as a callback function, this callback function is called (given a single argument pointing to the ExceptionRecord provided to RtlRestoreContext). The return address of the callback function is treated as a new Rip value to place in the context that is about to be restored. The context is restored as normal after the Rip value is updated based on the callback’s decision. The callback routine pointed to by ExceptionRecord-ExceptionInformation[0] has the following signature:

Returns a new RIP value to be used in the restored context. typedef ULONG64 (* PFRAME_CONSOLIDATON_ROUTINE)( __in PEXCEPTION_RECORD ExceptionRecord );

Frame consolidation unwinds are typically used in conjunction with C++ try/catch/throw support.

RtlAddFunctionTable works fine for _try/_except (pure seh)

This is my attempt to do it

#include <stdio.h>
#include <iostream>
#include <windows.h>
#pragma warning(disable : 4996)
#define getNtHdr(buf) ((IMAGE_NT_HEADERS *)((size_t)buf + ((IMAGE_DOS_HEADER *)buf)->e_lfanew))
#define getSectionArr(buf) ((IMAGE_SECTION_HEADER *)((size_t)buf + ((IMAGE_DOS_HEADER *)buf)->e_lfanew + sizeof(IMAGE_NT_HEADERS)))
 
 
typedef enum _UNWIND_OP_CODES {
    UWOP_PUSH_NONVOL = 0, /* info == register number */
    UWOP_ALLOC_LARGE,     /* no info, alloc size in next 2 slots */
    UWOP_ALLOC_SMALL,     /* info == size of allocation / 8 - 1 */
    UWOP_SET_FPREG,       /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
    UWOP_SAVE_NONVOL,     /* info == register number, offset in next slot */
    UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
    UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */
    UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
    UWOP_PUSH_MACHFRAME   /* info == 0: no error-code, 1: error-code */
} UNWIND_CODE_OPS;
 
typedef unsigned char UBYTE;
 
typedef union _UNWIND_CODE {
    struct {
        UBYTE CodeOffset;
        UBYTE UnwindOp : 4;
        UBYTE OpInfo : 4;
    };
    USHORT FrameOffset;
} UNWIND_CODE, * PUNWIND_CODE;
 
#define UNW_FLAG_EHANDLER  0x01
#define UNW_FLAG_UHANDLER  0x02
#define UNW_FLAG_CHAININFO 0x04
 
typedef struct _UNWIND_INFO {
    UBYTE Version : 3;
    UBYTE Flags : 5;
    UBYTE SizeOfProlog;
    UBYTE CountOfCodes;
    UBYTE FrameRegister : 4;
    UBYTE FrameOffset : 4;
    UNWIND_CODE UnwindCode[1];
    /*  UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
    *   union {
    *       OPTIONAL ULONG ExceptionHandler;
    *       OPTIONAL ULONG FunctionEntry;
    *   };
    *   OPTIONAL ULONG ExceptionData[]; */
} UNWIND_INFO, * PUNWIND_INFO;
 
 
bool readBinFile(const char fileName[], char** bufPtr, size_t& length)
{
    if (FILE* fp = fopen(fileName, "rb"))
    {
        fseek(fp, 0, SEEK_END);
        length = ftell(fp);
        *bufPtr = (char*)malloc(length + 1);
        fseek(fp, 0, SEEK_SET);
        fread(*bufPtr, sizeof(char), length, fp);
        return true;
    }
    else
        return false;
}
 
void fixIat(char* peImage)
{
    auto dir_ImportTable = getNtHdr(peImage)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    auto impModuleList = (IMAGE_IMPORT_DESCRIPTOR*)&peImage[dir_ImportTable.VirtualAddress];
    for (HMODULE currMod; impModuleList->Name; impModuleList++)
    {
        printf("\timport module : %s\n", &peImage[impModuleList->Name]);
        currMod = LoadLibraryA(&peImage[impModuleList->Name]);
 
        auto arr_callVia = (IMAGE_THUNK_DATA*)&peImage[impModuleList->FirstThunk];
        for (int count = 0; arr_callVia->u1.Function; count++, arr_callVia++)
        {
            auto curr_impApi = (PIMAGE_IMPORT_BY_NAME)&peImage[arr_callVia->u1.Function];
            arr_callVia->u1.Function = (size_t)GetProcAddress(currMod, (char*)curr_impApi->Name);
            if (count < 5)
                printf("\t\t- fix imp_%s\n", curr_impApi->Name);
        }
    }
}
 
#define RELOC_32BIT_FIELD 0x03
#define RELOC_64BIT_FIELD 0x0A
typedef struct BASE_RELOCATION_ENTRY
{
    WORD Offset : 12;
    WORD Type : 4;
} entry;
 
void fixReloc(char* peImage)
{
    auto dir_RelocTable = getNtHdr(peImage)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
    auto relocHdrBase = &peImage[dir_RelocTable.VirtualAddress];
    for (UINT hdrOffset = 0; hdrOffset < dir_RelocTable.Size;)
    {
        auto relocHdr = (IMAGE_BASE_RELOCATION*)&relocHdrBase[hdrOffset];
        entry* entryList = (entry*)((size_t)relocHdr + sizeof(*relocHdr));
        for (size_t i = 0; i < (relocHdr->SizeOfBlock - sizeof(*relocHdr)) / sizeof(entry); i++)
        {
            size_t rva_Where2Patch = relocHdr->VirtualAddress + entryList[i].Offset;
            if (entryList[i].Type == RELOC_32BIT_FIELD)
            {
                *(UINT32*)&peImage[rva_Where2Patch] -= (size_t)getNtHdr(peImage)->OptionalHeader.ImageBase;
                *(UINT32*)&peImage[rva_Where2Patch] += (size_t)peImage;
            }
            else if (entryList[i].Type == RELOC_64BIT_FIELD)
            {
                *(UINT64*)&peImage[rva_Where2Patch] -= (size_t)getNtHdr(peImage)->OptionalHeader.ImageBase;
                *(UINT64*)&peImage[rva_Where2Patch] += (size_t)peImage;
            }
        }
        hdrOffset += relocHdr->SizeOfBlock;
    }
}
char* basee = (char*)malloc(sizeof(char*));
int* sizee = (int*)malloc(sizeof(int));
void peLoader(char* exeData)
{
    auto imgBaseAt = (void*)getNtHdr(exeData)->OptionalHeader.ImageBase;
    auto imgSize = getNtHdr(exeData)->OptionalHeader.SizeOfImage;
    bool relocOk = !!getNtHdr(exeData)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
    *sizee = imgSize;
    char* peImage = (char*)VirtualAlloc(relocOk ? 0 : imgBaseAt, imgSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    basee = peImage;
    if (peImage)
    {
        printf("[v] exe file mapped @ %p\n", peImage);
        memcpy(peImage, exeData, getNtHdr(exeData)->OptionalHeader.SizeOfHeaders);
        for (int i = 0; i < getNtHdr(exeData)->FileHeader.NumberOfSections; i++)
        {
            auto curr_section = getSectionArr(exeData)[i];
            memcpy(
                &peImage[curr_section.VirtualAddress],
                &exeData[curr_section.PointerToRawData],
                curr_section.SizeOfRawData);
        }
        printf("[v] file mapping ok\n");
 
        fixIat(peImage);
        printf("[v] fix iat.\n");
 
        if (relocOk)
        {
            fixReloc(peImage);
            printf("[v] apply reloc.\n");
        }
 
        auto addrOfEntry = getNtHdr(exeData)->OptionalHeader.AddressOfEntryPoint;
        auto table = getNtHdr(peImage)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress;
        auto tablesz = getNtHdr(peImage)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size;
 
        RtlAddFunctionTable((RUNTIME_FUNCTION*)(table + basee), tablesz / sizeof(RUNTIME_FUNCTION), (DWORD64)peImage);
 
        printf("[v] invoke entry @ %p ...\n", &peImage[addrOfEntry]);
        ((void (*)()) & peImage[addrOfEntry])();
    }
    else
        printf("[x] alloc memory for exe @ %p failure.\n", imgBaseAt);
}
DWORD64 imagebase = NULL;
 
LONG handler(EXCEPTION_POINTERS* ex) {
 
   if (ex->ExceptionRecord->ExceptionCode == 0xE06D7363) {
 
       const int maxFrames = 100;
       void* frames[maxFrames];
       USHORT framesCaptured = RtlCaptureStackBackTrace(0, maxFrames, frames, nullptr);
       void* handler_data = NULL;
       uint64_t establisher_frame = NULL;
       CONTEXT  context;
       RtlCaptureContext(&context);
 
 
       for (USHORT i = 0; i < framesCaptured; ++i)
       {
 
           auto lookup = RtlLookupFunctionEntry((DWORD64)frames[i], &imagebase, NULL);
 
 
 
           UNWIND_INFO* ui = (UNWIND_INFO*)(imagebase + lookup->UnwindInfoAddress);
 
 
           if (ui->Flags & UNW_FLAG_EHANDLER || ui->Flags & UNW_FLAG_UHANDLER) {
 
               if ((DWORD64)imagebase < (DWORD64)GetModuleHandle(NULL)) {
 
                   auto language_handler = RtlVirtualUnwind(UNW_FLAG_EHANDLER, imagebase, (DWORD64)frames[i], lookup, &context, &handler_data, &establisher_frame, NULL);
 
 
                   DISPATCHER_CONTEXT new_dc = {};
                   new_dc.ControlPc = (DWORD64)frames[i];
                   new_dc.ImageBase = imagebase;
                   new_dc.FunctionEntry = lookup;
                   new_dc.EstablisherFrame = establisher_frame;
                   new_dc.TargetIp = reinterpret_cast<uint64_t>(&lookup);
                   new_dc.ContextRecord = ex->ContextRecord;
                   new_dc.LanguageHandler = language_handler;
                   new_dc.HandlerData = handler_data;
 
                   auto result = language_handler(ex->ExceptionRecord, (void*)establisher_frame, ex->ContextRecord, &new_dc);
  /////CALL CATCH FILTER?
                   if (result == EXCEPTION_EXECUTE_HANDLER) {
 
                       EXCEPTION_RECORD EH;
                       EH.ExceptionCode = STATUS_UNWIND_CONSOLIDATE;
                       EH.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
                       EH.NumberParameters = 1;
                       EH.ExceptionInformation[0] = ;
 
                       RtlUnwindEx((void*)establisher_frame, frames[i], &EH, NULL, ex->ContextRecord, NULL);
                       return EXCEPTION_CONTINUE_EXECUTION;
                   }
 
               }
 
 
           }
 
 
 
 
 
 
 
       }
 
       return EXCEPTION_CONTINUE_EXECUTION;
   }
    return  EXCEPTION_CONTINUE_SEARCH;
 
 
 
 
 
}
 
 
int main(int argc, char** argv)
{
 
    AddVectoredExceptionHandler(1, handler);
    char* exeBuf;
    size_t exeSize;
    readBinFile(argv[1], &exeBuf, exeSize);
    peLoader(exeBuf);
 
    return 0;
}

this is an exe-example that I used for testing:


#include <iostream>



void a() {
    try {
        int j = 9;
        throw(j);
    }
    catch(int k){
        std::cout << k;
    }
}




int main()
{
    a();
    return 0;
}

Any ideas?

Yes -- go find a forum that deals with user-mode programming. This is off-topic here.

yeah in fact I did that, and I thought maybe you could help