I have this pe that I dynamically want to load using a pe loader:
void excep() {
try {
int k = 11;
throw (k);
}
catch (int j) {
std::cout << j;
}
}
int main() {
excep();
return 0;
}
And this is the pe loader:
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 : %sn", &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("tt- fix imp_%sn", 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 @ %pn", 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 okn");
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)) {
std::cout << lookup << std::endl;
auto language_handler = RtlVirtualUnwind(UNW_FLAG_EHANDLER, imagebase, (DWORD64)frames[i], lookup, &context, &handler_data, &establisher_frame, NULL);
std::cout << language_handler;
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);
return result;
}
}
}
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;
}
It performs the necessary actions to load the pe, (fix iat, relocs, load the sections) and then I add a dynamic function table using RtlAddFunction
table and a VEH
which is catching exceptions of code: 0xE06D7363
(that is the code of C++ exceptions and not seh).Then I perform a stackwalk inside that veh and finally call the langua specific handler of that function that raised the exception.The problem is that after that call the loader breaks on runtime and does not handle that exception (although addresses and everything is right, based on some tests I did).Why is this happening?
xmr21 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.