From c947453e6208fc4668c16d5fcf1722582ac2b362 Mon Sep 17 00:00:00 2001 From: rcx Date: Mon, 11 Mar 2019 23:32:18 -0400 Subject: [PATCH] Use sirmabus' IDA7.1 source tree --- Plugin/Class_Informer.txt | 41 +- Plugin/Core.cpp | 1496 ---------------- Plugin/Core.h | 19 - Plugin/IDA_ClassInformer_PlugIn.vcxproj | 59 - .../IDA_ClassInformer_PlugIn.vcxproj.filters | 2 - Plugin/Main.cpp | 1566 ++++++++++++++++- Plugin/Main.h | 64 + Plugin/MainDialog.cpp | 12 +- Plugin/MainDialog.h | 4 +- Plugin/RTTI.cpp | 835 ++++----- Plugin/RTTI.h | 10 +- Plugin/StdAfx.h | 57 +- Plugin/Vftable.cpp | 32 +- Plugin/dialog.ui | 46 +- Plugin/undname.h | 7 +- 15 files changed, 2120 insertions(+), 2130 deletions(-) delete mode 100644 Plugin/Core.cpp delete mode 100644 Plugin/Core.h create mode 100644 Plugin/Main.h diff --git a/Plugin/Class_Informer.txt b/Plugin/Class_Informer.txt index 234062b..5473803 100644 --- a/Plugin/Class_Informer.txt +++ b/Plugin/Class_Informer.txt @@ -2,7 +2,7 @@ Class Informer: =========================================================== IDA Pro class vftable finder, namer, fixer, lister plug-in. -Version 2.4, May 2017 +Version 2.6, July 2018 By Sirmabus https://sourceforge.net/projects/classinformer/ @@ -114,6 +114,15 @@ Disassembler" -- [History] -------------------------------------------- +2.6 - 1) Updated to IDA SDK 7.1 + +2.5 - 1) Updated to IDA 7 and MSVC 2017. + Some of the changes for this: + Doesn't place types/structs, nor names or comments if they already exist. + Now the scanning role is more of looking for and fixing things IDA might have missed. + 2) Now catches a rare secondary case where an assumed member function is unresolved bytes to + increase the accuracy of vftable member count scanning. + 2.4 - 1) Now scans all "DATA" type segments. Before would only scan the first ".data" and ".rdata" segments. 2) Added segment select option to plug-in dialog. @@ -122,23 +131,23 @@ Disassembler" 2.1 - 1) Removed the "IDA updating, please wait.." wait for IDA to update thing at the end of processing to fix an ocassional hang. - 2) Updated to IDA SDK 6.8 version. + 2) Updated to IDA SDK 6.8 version. 2.0 - A complete overhaul of the entire plug-in. - 1) Updated namings to better match that of Microsoft's internals. - 2) Moved custom type creation out of the init() call; now gets created - only on the first run and only if the "Place structures" option is - checked. - 3) Descriptive comments are now placed on C/C++ initializer and - terminator table start and end points. - 4) The UI is now a custom Qt one with "material design" elements. - 5) Added a 64bit (32bit IDA's "__E64__") version. - 6) The RTCI (rare MS MFC type) support was removed. - 7) Now locates vftables by RTTI "Complete Object Locator" structures - directly. - 8) Updated to IDA SDK 6.7 version. - 9) No longer has a default hotkey. Set your own using "plugins.cfg". - 10) Updated and improved the static ctor/dtor processing. + 1) Updated namings to better match that of Microsoft's internals. + 2) Moved custom type creation out of the init() call; now gets created + only on the first run and only if the "Place structures" option is + checked. + 3) Descriptive comments are now placed on C/C++ initializer and + terminator table start and end points. + 4) The UI is now a custom Qt one with "material design" elements. + 5) Added a 64bit (32bit IDA's "__E64__") version. + 6) The RTCI (rare MS MFC type) support was removed. + 7) Now locates vftables by RTTI "Complete Object Locator" structures + directly. + 8) Updated to IDA SDK 6.7 version. + 9) No longer has a default hotkey. Set your own using "plugins.cfg". + 10) Updated and improved the static ctor/dtor processing. 1.06 - 1) Added persistent vftable list storage. 2) Removed the many unnecessary "autoWait()" calls that caused a bit diff --git a/Plugin/Core.cpp b/Plugin/Core.cpp deleted file mode 100644 index df262fe..0000000 --- a/Plugin/Core.cpp +++ /dev/null @@ -1,1496 +0,0 @@ - -// **************************************************************************** -// File: Core.cpp -// Desc: Class Informer -// -// **************************************************************************** -#include "stdafx.h" -#include "Core.h" -#include "Vftable.h" -#include "RTTI.h" -#include "MainDialog.h" -#include -// -#include -#include - -typedef std::map STRMAP; - -// Netnode constants -const static char NETNODE_NAME[] = {"$ClassInformer_node"}; -const char NN_DATA_TAG = 'A'; -const char NN_TABLE_TAG = 'S'; - -// Our netnode value indexes -enum NETINDX -{ - NIDX_VERSION, // ClassInformer version - NIDX_COUNT // Table entry count -}; - -// VFTable entry container (fits in a netnode MAXSPECSIZE size) -#pragma pack(push, 1) -struct TBLENTRY -{ - ea_t vft; - WORD methods; - WORD flags; - WORD strSize; - char str[MAXSPECSIZE - (sizeof(ea_t) + (sizeof(WORD) * 3))]; // IDA MAXSTR = 1024 -}; -#pragma pack(pop) - -// Line background color for non parent/top level hierarchy lines -// TOOD: Assumes text background is white. A way to make these user theme/style color aware? -#define GRAY(v) RGB(v,v,v) -static const bgcolor_t NOT_PARENT_COLOR = GRAY(235); - -// === Function Prototypes === -static BOOL processStaticTables(); -static void showEndStats(); -static BOOL getRttiData(SegSelect::segments *segList); - -// === Data === -static TIMESTAMP s_startTime = 0; -static HMODULE myModuleHandle = NULL; -static UINT staticCCtorCnt = 0, staticCppCtorCnt = 0, staticCDtorCnt = 0; -static UINT startingFuncCount = 0, staticCtorDtorCnt = 0; -static BOOL initResourcesOnce = FALSE; -static int chooserIcon = 0; -static netnode *netNode = NULL; -static eaList colList; - -// Options -BOOL optionPlaceStructs = TRUE; -BOOL optionProcessStatic = TRUE; -BOOL optionOverwriteComments = FALSE; -BOOL optionAudioOnDone = TRUE; - -// List box defs -static const char LBTITLE[] = {"[Class Informer]"}; -static const UINT LBCOLUMNCOUNT = 5; -static const int listBColumnWidth[LBCOLUMNCOUNT] = { (8 | CHCOL_HEX), (4 | CHCOL_DEC), 3, 19, 500 }; -static const LPCSTR columnHeader[LBCOLUMNCOUNT] = -{ - "Vftable", - "Methods", - "Flags", - "Type", - "Hierarchy" -}; - -static ssize_t idaapi uiCallback(PVOID obj, int eventID, va_list va); -static void freeWorkingData() -{ - try - { - RTTI::freeWorkingData(); - colList.clear(); - - if (netNode) - { - delete netNode; - netNode = NULL; - } - } - CATCH() -} - -// Initialize -void CORE_Init() -{ - GetModuleHandleEx((GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS), (LPCTSTR)&CORE_Init, &myModuleHandle); -} - -// Uninitialize -// Normally doesn't happen as we need to stay resident for the modal windows -void CORE_Exit() -{ - try - { - OggPlay::endPlay(); - freeWorkingData(); - - if (initResourcesOnce) - { - unhook_from_notification_point(HT_UI, uiCallback, myModuleHandle); - - if (chooserIcon) - { - free_custom_icon(chooserIcon); - chooserIcon = 0; - } - - Q_CLEANUP_RESOURCE(ClassInformerRes); - initResourcesOnce = FALSE; - } - } - CATCH() -} - - -// Init new netnode storage -static void newNetnodeStore() -{ - // Kill any existing store data first - netNode->altdel_all(NN_DATA_TAG); - netNode->supdel_all(NN_TABLE_TAG); - - // Init defaults - netNode->altset_idx8(NIDX_VERSION, MY_VERSION, NN_DATA_TAG); - netNode->altset_idx8(NIDX_COUNT, 0, NN_DATA_TAG); -} - -static WORD getStoreVersion(){ return((WORD)netNode->altval_idx8(NIDX_VERSION, NN_DATA_TAG)); } -static UINT getTableCount(){ return(netNode->altval_idx8(NIDX_COUNT, NN_DATA_TAG)); } -static BOOL setTableCount(UINT count){ return(netNode->altset_idx8(NIDX_COUNT, count, NN_DATA_TAG)); } -static BOOL getTableEntry(TBLENTRY &entry, UINT index){ return(netNode->supval(index, &entry, sizeof(TBLENTRY), NN_TABLE_TAG) > 0); } -static BOOL setTableEntry(TBLENTRY &entry, UINT index){ return(netNode->supset(index, &entry, (offsetof(TBLENTRY, str) + entry.strSize), NN_TABLE_TAG)); } - -static UINT CALLBACK lw_onGetLineCount(){ return(getTableCount()); } -static void CALLBACK lw_onMakeLine(UINT n, qstrvec_t* cols) -{ - #ifdef __EA64__ - static char addressFormat[16]; - #endif - - // Populate requested row - char buf[MAXSTR]; - - TBLENTRY e; - getTableEntry(e, n); - // vft address - #ifdef __EA64__ - sprintf(buf, addressFormat, e.vft); - #else - sprintf(buf, EAFORMAT, e.vft); - (*cols)[0] = buf; - #endif - // Method count - if (e.methods > 0) - sprintf(buf, "%u", e.methods); // "%04u" - else - strcpy(buf, "???"); - (*cols)[1] = buf; - // Flags - char flags[4]; - int pos = 0; - if (e.flags & RTTI::CHD_MULTINH) flags[pos++] = 'M'; - if (e.flags & RTTI::CHD_VIRTINH) flags[pos++] = 'V'; - if (e.flags & RTTI::CHD_AMBIGUOUS) flags[pos++] = 'A'; - flags[pos++] = 0; - memcpy(buf, flags, pos); - (*cols)[2] = buf; - // Type - LPCSTR tag = strchr(e.str, '@'); - if (tag) - { - pos = (tag - e.str); - memcpy(buf, e.str, pos); - buf[pos] = 0; - ++tag; - } - else - { - // Can happen when string is MAXSTR and greater - //_ASSERT(FALSE); - strcpy(buf, "??** MAXSTR overflow!"); - tag = e.str; - pos = (strlen(e.str) + 1); - } - (*cols)[3] = buf; - // Composition/hierarchy - strncpy(buf, tag, (MAXSTR - 1)); - (*cols)[4] = buf; -} - -static int CALLBACK lw_onGetIcon(UINT n) -{ - //return(n); - if(n == 0) - return(0); - else - { - /* - TBLENTRY e; - getTableEntry(e, (n)); - return((e.flags & RTTI::IS_TOP_LEVEL) ? 77 : 191); - */ - return(191); - } -} - -static void CALLBACK lw_onSelect(UINT n) -{ - TBLENTRY e; - getTableEntry(e, n); - jumpto(e.vft); -} -static void CALLBACK lw_onClose() { freeWorkingData(); } - -// Add an entry to the vftable list -void addTableEntry(UINT flags, ea_t vft, int methodCount, LPCTSTR format, ...) -{ - TBLENTRY e; - e.vft = vft; - e.methods = methodCount; - e.flags = flags; - e.str[SIZESTR(e.str)] = 0; - - va_list vl; - va_start(vl, format); - _vsntprintf(e.str, SIZESTR(e.str), format, vl); - va_end(vl); - e.strSize = (WORD) (strlen(e.str) + 1); - - UINT count = getTableCount(); - setTableEntry(e, count); - setTableCount(++count); -} - - -static QWidget *findChildByClass(QWidgetList &wl, LPCSTR className) -{ - foreach(QWidget *w, wl) - if (strcmp(w->metaObject()->className(), className) == 0) - return(w); - return(NULL); -} - - -// Find widget by title text -// If IDs are constant can use "static QWidget *QWidget::find(WId);"? -void customizeChooseWindow() -{ - try - { - QApplication::processEvents(); - - // Get parent chooser dock widget - QWidgetList pl = QApplication::activeWindow()->findChildren("[Class Informer]"); - if (QWidget *dw = findChildByClass(pl, "IDADockWidget")) - { - QFile file(STYLE_PATH "view-style.qss"); - if (file.open(QFile::ReadOnly | QFile::Text)) - dw->setStyleSheet(QTextStream(&file).readAll()); - } - else - msg("** customizeChooseWindow(): \"IDADockWidget\" not found!\n"); - - // Get chooser widget - if (QTableView *tv = (QTableView *) findChildByClass(pl, "TChooserView")) - { - // Set sort by type name - tv->sortByColumn(3, Qt::DescendingOrder); - - // Resize to contents - tv->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); - tv->resizeColumnsToContents(); - tv->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - - UINT count = getTableCount(); - for (UINT row = 0; row < count; row++) - tv->setRowHeight(row, 24); - } - else - msg("** customizeChooseWindow(): \"TChooserView\" not found!\n"); - } - CATCH() -} - -// UI callback to handle chooser window coloring -static ssize_t idaapi uiCallback(PVOID obj, int eventID, va_list va) -{ - if (eventID == ui_get_chooser_item_attrs) - { - // ** Stack vars, keep in order - void *chooserObj = va_arg(va, PVOID); // 0 - UINT n = va_arg(va, UINT); // 1 - chooser_item_attrs_t *itemAttrubutes = va_arg(va, chooser_item_attrs_t *); // 2 - - // My chooser? - if (obj == myModuleHandle) - { - if (itemAttrubutes) - { - TBLENTRY e; - if (getTableEntry(e, n)) - { - // Indicate entry is not a top/parent level - if (!(e.flags & RTTI::IS_TOP_LEVEL)) - itemAttrubutes->color = NOT_PARENT_COLOR; - } - } - } - } - return(0); -} - - -//static HWND WINAPI getIdaHwnd(){ return((HWND)callui(ui_get_hwnd).vptr); } - -struct result_window_t : public chooser_multi_t // chooser_multi_t has different api, so i won't be using it. -{ - result_window_t() : chooser_multi_t(CH_ATTRS, LBCOLUMNCOUNT, listBColumnWidth, columnHeader, LBTITLE) - { - icon = ((chooserIcon != 0) ? chooserIcon : 160); - }; - - virtual void idaapi closed() - { - lw_onClose(); - } - - virtual cbres_t idaapi enter(sizevec_t* sel) - { - lw_onSelect(sel->front()); - return NOTHING_CHANGED; - } - - virtual void idaapi get_row( - qstrvec_t *cols, - int *icon_, - chooser_item_attrs_t *attrs, - size_t n) const - { - lw_onMakeLine(n, cols); - *icon_ = lw_onGetIcon(n); - } - - virtual size_t idaapi get_count() const - { - return lw_onGetLineCount(); - } - -} results_window; - -void CORE_Process(int arg) -{ - try - { - char version[16]; - sprintf(version, "%u.%u", HIBYTE(MY_VERSION), LOBYTE(MY_VERSION)); - msg("\n>> Class Informer: v: %s, built: %s, By Sirmabus\n", version, __DATE__); - refreshUI(); - - if (!initResourcesOnce) - { - initResourcesOnce = TRUE; - Q_INIT_RESOURCE(ClassInformerRes); - - QFile file(STYLE_PATH "icon.png"); - if (file.open(QFile::ReadOnly)) - { - QByteArray ba = file.readAll(); - chooserIcon = load_custom_icon(ba.constData(), ba.size(), "png"); - } - - // Hook to control line color - hook_to_notification_point(HT_UI, uiCallback, myModuleHandle); - } - - if(!auto_is_ok()) - { - msg("** Class Informer: Must wait for IDA to finish processing before starting plug-in! **\n*** Aborted ***\n\n"); - return; - } - - OggPlay::endPlay(); - freeWorkingData(); - optionAudioOnDone = TRUE; - optionProcessStatic = TRUE; - optionOverwriteComments = FALSE; - optionPlaceStructs = TRUE; - startingFuncCount = get_func_qty(); - colList.clear(); - staticCppCtorCnt = staticCCtorCnt = staticCtorDtorCnt = staticCDtorCnt = 0; - - // Create storage netnode - if(!(netNode = new netnode(NETNODE_NAME, SIZESTR(NETNODE_NAME), TRUE))) - { - QASSERT(66, FALSE); - return; - } - - // Read existing storage if any - UINT tableCount = getTableCount(); - WORD storageVersion = getStoreVersion(); - BOOL storageExists = (tableCount > 0); - - // Ask if we should use storage or process again - if (storageExists) - { - // Version 2.3 didn't change the format - UINT major = HIBYTE(storageVersion), minor = LOBYTE(storageVersion); - if ((major != 2) || (minor < 2)) - { - msg("* Storage version mismatch, must rescan *\n"); - refreshUI(); - } - else - storageExists = (ask_yn(1, "TITLE Class Informer \nHIDECANCEL\nUse previously stored result? ") == 1); - } - - BOOL aborted = FALSE; - if(!storageExists) - { - newNetnodeStore(); - - // Only MS Visual C++ targets are supported - comp_t cmp = get_comp(default_compiler()); - if (cmp != COMP_MS) - { - msg("** IDA reports target compiler: \"%s\"\n", get_compiler_name(cmp)); - refreshUI(); - int iResult = ask_buttons(NULL, NULL, NULL, 0, "TITLE Class Informer\nHIDECANCEL\nIDA reports this IDB's compiler as: \"%s\" \n\nThis plug-in only understands MS Visual C++ targets.\nRunning it on other targets (like Borland© compiled, etc.) will have unpredicted results. \n\nDo you want to continue anyhow?", get_compiler_name(cmp)); - if (iResult != 1) - { - msg("- Aborted -\n\n"); - return; - } - } - - // Do UI - SegSelect::segments *segList = NULL; - if (doMainDialog(optionPlaceStructs, optionProcessStatic, optionOverwriteComments, optionAudioOnDone, &segList)) - { - msg("- Canceled -\n\n"); - return; - } - - msg("Working..\n"); - refreshUI(); - WaitBox::show("Class Informer", "Please wait..", "url(" STYLE_PATH "progress-style.qss)", STYLE_PATH "icon.png"); - WaitBox::updateAndCancelCheck(-1); - s_startTime = getTimeStamp(); - - // Add structure definitions to IDA once per session - static BOOL createStructsOnce = FALSE; - if (optionPlaceStructs && !createStructsOnce) - { - createStructsOnce = TRUE; - RTTI::addDefinitionsToIda(); - } - - if(optionProcessStatic) - { - // Process global and static ctor sections - msg("\nProcessing C/C++ ctor & dtor tables.\n"); - refreshUI(); - if (!(aborted = processStaticTables())) - msg("Processing time: %s.\n", timeString(getTimeStamp() - s_startTime)); - refreshUI(); - } - - if (!aborted) - { - // Get RTTI data - if (!(aborted = getRttiData(segList))) - { - // Optionally play completion sound - if (optionAudioOnDone) - { - TIMESTAMP endTime = (getTimeStamp() - s_startTime); - if (endTime > (TIMESTAMP) 2.4) - { - OggPlay::endPlay(); - QFile file(STYLE_PATH "completed.ogg"); - if (file.open(QFile::ReadOnly)) - { - QByteArray ba = file.readAll(); - OggPlay::playFromMemory((const PVOID)ba.constData(), ba.size(), TRUE); - } - } - } - - showEndStats(); - WaitBox::hide(); - msg("Done.\n\n"); - } - } - - refresh_idaview_anyway(); - if (aborted) - { - msg("- Aborted -\n\n"); - return; - } - } - - // Show list result window - if (!aborted && (getTableCount() > 0)) - { - static result_window_t* results_window = new result_window_t; - results_window->choose(); - - customizeChooseWindow(); - } - } - CATCH() -} - -// Print out end stats -static void showEndStats() -{ - try - { - msg(" \n\n"); - msg("=========== Stats ===========\n"); - msg(" RTTI vftables: %u\n", getTableCount()); - msg("Functions fixed: %u\n", (get_func_qty() - startingFuncCount)); - msg("Processing time: %s\n", timeString(getTimeStamp() - s_startTime)); - } - CATCH() -} - - -// ================================================================================================ - -// Fix/create label and comment C/C++ initializer tables -static void setIntializerTable(ea_t start, ea_t end, BOOL isCpp) -{ - try - { - if (UINT count = ((end - start) / sizeof(ea_t))) - { - // Set table elements as pointers - ea_t ea = start; - while (ea <= end) - { - fixEa(ea); - - // Might fix missing/messed stubs - if (ea_t func = get_32bit(ea)) - fixFunction(func); - - ea += sizeof(ea_t); - }; - - // Start label - if (!hasUniqueName(start)) - { - char name[MAXSTR]; name[SIZESTR(name)] = 0; - if (isCpp) - _snprintf(name, SIZESTR(name), "__xc_a_%d", staticCppCtorCnt); - else - _snprintf(name, SIZESTR(name), "__xi_a_%d", staticCCtorCnt); - set_name(start, name, (SN_NON_AUTO | SN_NOWARN)); - } - - // End label - if (!hasUniqueName(end)) - { - char name[MAXSTR]; name[SIZESTR(name)] = 0; - if (isCpp) - _snprintf(name, SIZESTR(name), "__xc_z_%d", staticCppCtorCnt); - else - _snprintf(name, SIZESTR(name), "__xi_z_%d", staticCCtorCnt); - set_name(end, name, (SN_NON_AUTO | SN_NOWARN)); - } - - // Comment - // Never overwrite, it might be the segment comment - if (!hasAnteriorComment(start)) - { - if (isCpp) - add_extra_cmt(start, TRUE, "%d C++ static ctors (#classinformer)", count); - else - add_extra_cmt(start, TRUE, "%d C initializers (#classinformer)", count); - } - else - // Place comment @ address instead - if (!has_cmt(get_flags(start))) - { - char comment[MAXSTR]; comment[SIZESTR(comment)] = 0; - if (isCpp) - { - _snprintf(comment, SIZESTR(comment), "%d C++ static ctors (#classinformer)", count); - set_cmt(start, comment, TRUE); - } - else - { - _snprintf(comment, SIZESTR(comment), "%d C initializers (#classinformer)", count); - set_cmt(start, comment, TRUE); - } - } - - if (isCpp) - staticCppCtorCnt++; - else - staticCCtorCnt++; - } - } - CATCH() -} - -// Fix/create label and comment C/C++ terminator tables -static void setTerminatorTable(ea_t start, ea_t end) -{ - try - { - if (UINT count = ((end - start) / sizeof(ea_t))) - { - // Set table elements as pointers - ea_t ea = start; - while (ea <= end) - { - fixEa(ea); - - // Fix function - if (ea_t func = getEa(ea)) - fixFunction(func); - - ea += sizeof(ea_t); - }; - - // Start label - if (!hasUniqueName(start)) - { - char name[MAXSTR]; name[SIZESTR(name)] = 0; - _snprintf(name, SIZESTR(name), "__xt_a_%d", staticCDtorCnt); - set_name(start, name, (SN_NON_AUTO | SN_NOWARN)); - } - - // End label - if (!hasUniqueName(end)) - { - char name[MAXSTR]; name[SIZESTR(name)] = 0; - _snprintf(name, SIZESTR(name), "__xt_z_%d", staticCDtorCnt); - set_name(end, name, (SN_NON_AUTO | SN_NOWARN)); - } - - // Comment - // Never overwrite, it might be the segment comment - if (!hasAnteriorComment(start)) - add_extra_cmt(start, TRUE, "%d C terminators (#classinformer)", count); - else - // Place comment @ address instead - if (!has_cmt(get_flags(start))) - { - char comment[MAXSTR]; comment[SIZESTR(comment)] = 0; - _snprintf(comment, SIZESTR(comment), "%d C terminators (#classinformer)", count); - set_cmt(start, comment, TRUE); - } - - staticCDtorCnt++; - } - } - CATCH() -} - -// "" for when we are uncertain of ctor or dtor type table -static void setCtorDtorTable(ea_t start, ea_t end) -{ - try - { - if (UINT count = ((end - start) / sizeof(ea_t))) - { - // Set table elements as pointers - ea_t ea = start; - while (ea <= end) - { - fixEa(ea); - - // Fix function - if (ea_t func = getEa(ea)) - fixFunction(func); - - ea += sizeof(ea_t); - }; - - // Start label - if (!hasUniqueName(start)) - { - char name[MAXSTR]; name[SIZESTR(name)] = 0; - _snprintf(name, SIZESTR(name), "__x?_a_%d", staticCtorDtorCnt); - set_name(start, name, (SN_NON_AUTO | SN_NOWARN)); - } - - // End label - if (!hasUniqueName(end)) - { - char name[MAXSTR]; name[SIZESTR(name)] = 0; - _snprintf(name, SIZESTR(name), "__x?_z_%d", staticCtorDtorCnt); - set_name(end, name, (SN_NON_AUTO | SN_NOWARN)); - } - - // Comment - // Never overwrite, it might be the segment comment - if (!hasAnteriorComment(start)) - add_extra_cmt(start, TRUE, "%d C initializers/terminators (#classinformer)", count); - else - // Place comment @ address instead - if (!has_cmt(get_flags(start))) - { - char comment[MAXSTR]; comment[SIZESTR(comment)] = 0; - _snprintf(comment, SIZESTR(comment), "%d C initializers/terminators (#classinformer)", count); - set_cmt(start, comment, TRUE); - } - - staticCtorDtorCnt++; - } - } - CATCH() -} - - -// Process redister based _initterm() -static void processRegisterInitterm(ea_t start, ea_t end, ea_t call) -{ - if ((end != BADADDR) && (start != BADADDR)) - { - // Should be in the same segment - if (getseg(start) == getseg(end)) - { - if (start > end) - swap_t(start, end); - - msg(" " EAFORMAT " to " EAFORMAT " CTOR table.\n", start, end); - setIntializerTable(start, end, TRUE); - set_cmt(call, "_initterm", TRUE); - } - else - msg(" ** Bad address range of " EAFORMAT ", " EAFORMAT " for \"_initterm\" type ** .\n", start, end); - } -} - -static UINT doInittermTable(func_t *func, ea_t start, ea_t end, LPCTSTR name) -{ - UINT found = FALSE; - - if ((start != BADADDR) && (end != BADADDR)) - { - // Should be in the same segment - if (getseg(start) == getseg(end)) - { - if (start > end) - swap_t(start, end); - - // Try to determine if we are in dtor or ctor section - if (func) - { - qstring qstr; - if (get_long_name(&qstr, func->start_ea) > 0) - { - char funcName[MAXSTR]; funcName[SIZESTR(funcName)] = 0; - strncpy(funcName, qstr.c_str(), (MAXSTR - 1)); - _strlwr(funcName); - - // Start/ctor? - if (strstr(funcName, "cinit") || strstr(funcName, "tmaincrtstartup") || strstr(funcName, "start")) - { - msg(" " EAFORMAT " to " EAFORMAT " CTOR table.\n", start, end); - setIntializerTable(start, end, TRUE); - found = TRUE; - } - else - // Exit/dtor function? - if (strstr(funcName, "exit")) - { - msg(" " EAFORMAT " to " EAFORMAT " DTOR table.\n", start, end); - setTerminatorTable(start, end); - found = TRUE; - } - } - } - - if (!found) - { - // Fall back to generic assumption - msg(" " EAFORMAT " to " EAFORMAT " CTOR/DTOR table.\n", start, end); - setCtorDtorTable(start, end); - found = TRUE; - } - } - else - msg(" ** Miss matched segment table addresses " EAFORMAT ", " EAFORMAT " for \"%s\" type **\n", start, end, name); - } - else - msg(" ** Bad input address range of " EAFORMAT ", " EAFORMAT " for \"%s\" type **\n", start, end, name); - - return(found); -} - -// Process _initterm function -// Returns TRUE if at least one found -static BOOL processInitterm(ea_t address, LPCTSTR name) -{ - msg(EAFORMAT" processInitterm: \"%s\" \n", address, name); - UINT count = 0; - - // Walk xrefs - ea_t xref = get_first_fcref_to(address); - while (xref && (xref != BADADDR)) - { - msg(" " EAFORMAT " \"%s\" xref.\n", xref, name); - - // Should be code - if (is_code(get_flags(xref))) - { - do - { - // The most common are two instruction arguments - // Back up two instructions - ea_t instruction1 = prev_head(xref, 0); - if (instruction1 == BADADDR) - break; - ea_t instruction2 = prev_head(instruction1, 0); - if (instruction2 == BADADDR) - break; - - // Bail instructions are past the function start now - func_t *func = get_func(xref); - if (func && (instruction2 < func->start_ea)) - { - //msg(" " EAFORMAT " arg2 outside of contained function **\n", func->start_ea); - break; - } - - struct ARG2PAT - { - LPCSTR pattern; - UINT start, end, padding; - } static const ALIGN(16) arg2pat[] = - { - #ifndef __EA64__ - { "68 ?? ?? ?? ?? 68 ?? ?? ?? ??", 6, 1 }, // push offset s, push offset e - { "B8 ?? ?? ?? ?? C7 04 24 ?? ?? ?? ??", 8, 1 }, // mov [esp+4+var_4], offset s, mov eax, offset e Maestia - { "68 ?? ?? ?? ?? B8 ?? ?? ?? ??", 6, 1 }, // mov eax, offset s, push offset e - #else - { "48 8D 15 ?? ?? ?? ?? 48 8D 0D ?? ?? ?? ??", 3, 3 }, // lea rdx,s, lea rcx,e - #endif - }; - BOOL matched = FALSE; - for (UINT i = 0; (i < qnumber(arg2pat)) && !matched; i++) - { - ea_t match = find_binary(instruction2, xref, arg2pat[i].pattern, 16, (SEARCH_DOWN | SEARCH_NOBRK | SEARCH_NOSHOW)); - if (match != BADADDR) - { - #ifndef __EA64__ - ea_t start = getEa(match + arg2pat[i].start); - ea_t end = getEa(match + arg2pat[i].end); - #else - UINT startOffset = get_32bit(instruction1 + arg2pat[i].start); - UINT endOffset = get_32bit(instruction2 + arg2pat[i].end); - ea_t start = (instruction1 + 7 + *((PINT) &startOffset)); // TODO: 7 is hard coded instruction length, put this in arg2pat table? - ea_t end = (instruction2 + 7 + *((PINT) &endOffset)); - #endif - msg(" " EAFORMAT " Two instruction pattern match #%d\n", match, i); - count += doInittermTable(func, start, end, name); - matched = TRUE; - break; - } - } - - // 3 instruction - /* - searchStart = prev_head(searchStart, BADADDR); - if (searchStart == BADADDR) - break; - if (func && (searchStart < func->start_ea)) - break; - - if (func && (searchStart < func->start_ea)) - { - msg(" " EAFORMAT " arg3 outside of contained function **\n", func->start_ea); - break; - } - - .text:10008F78 push offset unk_1000B1B8 - .text:10008F7D push offset unk_1000B1B0 - .text:10008F82 mov dword_1000F83C, 1 - "68 ?? ?? ?? ?? 68 ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ??" - */ - - if (!matched) - msg(" ** arguments not located!\n"); - - } while (FALSE); - } - else - msg(" " EAFORMAT " ** \"%s\" xref is not code! **\n", xref, name); - - xref = get_next_fcref_to(address, xref); - }; - - msg(" \n"); - return(count > 0); -} - - -// Process global/static ctor & dtor tables. -// Returns TRUE if user aborted -static BOOL processStaticTables() -{ - staticCppCtorCnt = staticCCtorCnt = staticCtorDtorCnt = staticCDtorCnt = 0; - - // x64 __tmainCRTStartup, _CRT_INIT - - try - { - // Locate _initterm() and _initterm_e() functions - STRMAP inittermMap; - func_t *cinitFunc = NULL; - UINT funcCount = get_func_qty(); - for (UINT i = 0; i < funcCount; i++) - { - if (func_t *func = getn_func(i)) - { - qstring qstr; - if (get_long_name(&qstr, func->start_ea) > 0) - { - char name[MAXSTR]; name[SIZESTR(name)] = 0; - strncpy(name, qstr.c_str(), (MAXSTR - 1)); - - int len = strlen(name); - if (len >= SIZESTR("_cinit")) - { - if (strcmp((name + (len - SIZESTR("_cinit"))), "_cinit") == 0) - { - // Skip stub functions - if (func->size() > 16) - { - msg(EAFORMAT" C: \"%s\", %d bytes.\n", func->start_ea, name, func->size()); - _ASSERT(cinitFunc == NULL); - cinitFunc = func; - } - } - else - if ((len >= SIZESTR("_initterm")) && (strcmp((name + (len - SIZESTR("_initterm"))), "_initterm") == 0)) - { - msg(EAFORMAT" I: \"%s\", %d bytes.\n", func->start_ea, name, func->size()); - inittermMap[func->start_ea] = name; - } - else - if ((len >= SIZESTR("_initterm_e")) && (strcmp((name + (len - SIZESTR("_initterm_e"))), "_initterm_e") == 0)) - { - msg(EAFORMAT" E: \"%s\", %d bytes.\n", func->start_ea, name, func->size()); - inittermMap[func->start_ea] = name; - } - } - } - } - } - refreshUI(); - if (WaitBox::updateAndCancelCheck()) - return(TRUE); - - // Look for import versions - { - static LPCSTR imports[] = - { - "__imp__initterm", "__imp__initterm_e" - }; - for (UINT i = 0; i < qnumber(imports); i++) - { - ea_t adress = get_name_ea(BADADDR, imports[i]); - if (adress != BADADDR) - { - if (inittermMap.find(adress) == inittermMap.end()) - { - msg(EAFORMAT" import: \"%s\".\n", adress, imports[i]); - inittermMap[adress] = imports[i]; - } - } - } - } - - // Process register based _initterm() calls inside _cint() - if (cinitFunc) - { - struct CREPAT - { - LPCSTR pattern; - UINT start, end, call; - } static const ALIGN(16) pat[] = - { - { "B8 ?? ?? ?? ?? BE ?? ?? ?? ?? 59 8B F8 3B C6 73 0F 8B 07 85 C0 74 02 FF D0 83 C7 04 3B FE 72 F1", 1, 6, 0x17}, - { "BE ?? ?? ?? ?? 8B C6 BF ?? ?? ?? ?? 3B C7 59 73 0F 8B 06 85 C0 74 02 FF D0 83 C6 04 3B F7 72 F1", 1, 8, 0x17}, - }; - - for (UINT i = 0; i < qnumber(pat); i++) - { - ea_t match = find_binary(cinitFunc->start_ea, cinitFunc->end_ea, pat[i].pattern, 16, (SEARCH_DOWN | SEARCH_NOBRK | SEARCH_NOSHOW)); - while (match != BADADDR) - { - msg(" " EAFORMAT " Register _initterm(), pattern #%d.\n", match, i); - ea_t start = getEa(match + pat[i].start); - ea_t end = getEa(match + pat[i].end); - processRegisterInitterm(start, end, (match + pat[i].call)); - match = find_binary(match + 30, cinitFunc->end_ea, pat[i].pattern, 16, (SEARCH_NEXT | SEARCH_DOWN | SEARCH_NOBRK | SEARCH_NOSHOW)); - }; - } - } - msg(" \n"); - refreshUI(); - if (WaitBox::updateAndCancelCheck()) - return(TRUE); - - // Process _initterm references - for (STRMAP::iterator it = inittermMap.begin(); it != inittermMap.end(); ++it) - { - if (processInitterm(it->first, it->second.c_str())) - if (WaitBox::updateAndCancelCheck()) - return(TRUE); - } - refreshUI(); - } - CATCH() - - return(FALSE); -} - -// ================================================================================================ - - -// Return TRUE if address as a anterior comment -inline BOOL hasAnteriorComment(ea_t ea) -{ - return(get_first_free_extra_cmtidx(ea, E_PREV) != E_PREV); -} - -// Delete any anterior comment(s) at address if there is some -inline void killAnteriorComments(ea_t ea) -{ - delete_extra_cmts(ea, E_PREV); -} - -// Force a memory location to be DWORD size -void fixDword(ea_t ea) -{ - if (!is_dword(get_flags(ea))) - { - setUnknown(ea, sizeof(DWORD)); - create_dword(ea, sizeof(DWORD)); - } -} - -// Force memory location to be ea_t size -void fixEa(ea_t ea) -{ - #ifndef __EA64__ - if (!is_dword(get_flags(ea))) - #else - if (!is_qword(get_flags(ea))) - #endif - { - setUnknown(ea, sizeof(ea_t)); - #ifndef __EA64__ - create_dword(ea, sizeof(ea_t)); - #else - create_qword(ea, sizeof(ea_t)); - #endif - } -} - -// Make address a function -void fixFunction(ea_t ea) -{ - flags_t flags = get_flags(ea); - if (!is_code(flags)) - { - create_insn(ea); - add_func(ea, BADADDR); - } - else - if (!is_func(flags)) - add_func(ea, BADADDR); -} - -// Get IDA EA bit value with verification -BOOL getVerifyEa(ea_t ea, ea_t &rValue) -{ - // Location valid? - if (is_loaded(ea)) - { - // Get ea_t value - rValue = getEa(ea); - return(TRUE); - } - - return(FALSE); -} - - -// Undecorate to minimal class name -// typeid(T).name() -// http://en.wikipedia.org/wiki/Name_mangling -// http://en.wikipedia.org/wiki/Visual_C%2B%2B_name_mangling -// http://www.agner.org/optimize/calling_conventions.pdf - -BOOL getPlainTypeName(__in LPCSTR mangled, __out_bcount(MAXSTR) LPSTR outStr) -{ - outStr[0] = outStr[MAXSTR - 1] = 0; - - // Use CRT function for type names - if (mangled[0] == '.') - { - __unDName(outStr, mangled + 1, MAXSTR, (_Alloc)malloc, free, (UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY | UNDNAME_NO_ECSU)); - if ((outStr[0] == 0) || (strcmp((mangled + 1), outStr) == 0)) - { - msg("** getPlainClassName:__unDName() failed to unmangle! input: \"%s\"\n", mangled); - return(FALSE); - } - } - else - // IDA demangler for everything else - { - qstring qstr; - int result = demangle_name(&qstr, mangled, (MT_MSCOMP | MNG_NODEFINIT)); - if (result < 0) - { - //msg("** getPlainClassName:demangle_name2() failed to unmangle! result: %d, input: \"%s\"\n", result, mangled); - return(FALSE); - } - - // No inhibit flags will drop this - strncpy(outStr, qstr.c_str(), (MAXSTR - 1)); - if (LPSTR ending = strstr(outStr, "::`vftable'")) - *ending = 0; - } - - return(TRUE); -} - -// Wrapper for 'add_struc_member()' with error messages -// See to make more sense of types: http://idapython.googlecode.com/svn-history/r116/trunk/python/idc.py -int addStrucMember(struc_t *sptr, char *name, ea_t offset, flags_t flag, opinfo_t *type, asize_t nbytes) -{ - int r = add_struc_member(sptr, name, offset, flag, type, nbytes); - switch(r) - { - case STRUC_ERROR_MEMBER_NAME: - msg("AddStrucMember(): error: already has member with this name (bad name)\n"); - break; - - case STRUC_ERROR_MEMBER_OFFSET: - msg("AddStrucMember(): error: already has member at this offset\n"); - break; - - case STRUC_ERROR_MEMBER_SIZE: - msg("AddStrucMember(): error: bad number of bytes or bad sizeof(type)\n"); - break; - - case STRUC_ERROR_MEMBER_TINFO: - msg("AddStrucMember(): error: bad typeid parameter\n"); - break; - - case STRUC_ERROR_MEMBER_STRUCT: - msg("AddStrucMember(): error: bad struct id (the 1st argument)\n"); - break; - - case STRUC_ERROR_MEMBER_UNIVAR: - msg("AddStrucMember(): error: unions can't have variable sized members\n"); - break; - - case STRUC_ERROR_MEMBER_VARLAST: - msg("AddStrucMember(): error: variable sized member should be the last member in the structure\n"); - break; - - case STRUC_ERROR_MEMBER_NESTED: - msg("AddStrucMember(): error: recursive structure nesting is forbidden\n"); - break; - }; - - return(r); -} - - -void setUnknown(ea_t ea, int size) -{ - // TODO: Does the overrun problem still exist? - //do_unknown_range(ea, (size_t)size, DOUNK_SIMPLE); - while (size > 0) - { - int isize = get_item_size(ea); - if (isize > size) - break; - else - { - del_items(ea, DELIT_SIMPLE); - ea += (ea_t)isize, size -= isize; - } - }; -} - - -// Scan segment for COLs -static BOOL scanSeg4Cols(segment_t *seg) -{ - qstring name; - if (get_segm_name(&name, seg) <= 0) - name = "???"; - msg(" N: \"%s\", A: " EAFORMAT " - " EAFORMAT ", S: %s.\n", name.c_str(), seg->start_ea, seg->end_ea, byteSizeString(seg->size())); - refreshUI(); - - UINT found = 0; - if (seg->size() >= sizeof(RTTI::_RTTICompleteObjectLocator)) - { - ea_t startEA = ((seg->start_ea + sizeof(UINT)) & ~((ea_t)(sizeof(UINT) - 1))); - ea_t endEA = (seg->end_ea - sizeof(RTTI::_RTTICompleteObjectLocator)); - - for (ea_t ptr = startEA; ptr < endEA;) - { - #ifdef __EA64__ - // Check for possible COL here - // Signature will be one - // TODO: Is this always 1 or can it be zero like 32bit? - if (get_32bit(ptr + offsetof(RTTI::_RTTICompleteObjectLocator, signature)) == 1) - { - if (RTTI::_RTTICompleteObjectLocator::isValid(ptr)) - { - // yes - colList.push_front(ptr); - RTTI::_RTTICompleteObjectLocator::doStruct(ptr); - ptr += sizeof(RTTI::_RTTICompleteObjectLocator); - continue; - } - } - else - { - // TODO: Should we check stray BCDs? - // Each value would have to be tested for a valid type_def and - // the pattern is pretty ambiguous. - } - #else - // TypeDescriptor address here? - ea_t ea = getEa(ptr); - if (ea >= 0x10000) - { - if (RTTI::type_info::isValid(ea)) - { - // yes, a COL here? - ea_t col = (ptr - offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); - if (RTTI::_RTTICompleteObjectLocator::isValid2(col)) - { - // yes - colList.push_front(col); - RTTI::_RTTICompleteObjectLocator::doStruct(col); - ptr += sizeof(RTTI::_RTTICompleteObjectLocator); - continue; - } - /* - else - // No, is it a BCD then? - if (RTTI::_RTTIBaseClassDescriptor::isValid2(ptr)) - { - // yes - char dontCare[MAXSTR]; - RTTI::_RTTIBaseClassDescriptor::doStruct(ptr, dontCare); - } - */ - } - } - #endif - - if (WaitBox::isUpdateTime()) - if (WaitBox::updateAndCancelCheck()) - return(TRUE); - - ptr += sizeof(UINT); - } - } - - if (found) - { - char numBuffer[32]; - msg(" Count: %s\n", prettyNumberString(found, numBuffer)); - refreshUI(); - } - return(FALSE); -} -// -// Locate COL by descriptor list -static BOOL findCols(SegSelect::segments *segList) -{ - try - { - #ifdef _DEVMODE - TIMESTAMP startTime = getTimeStamp(); - #endif - - // Use user selected segments - if (segList && !segList->empty()) - { - for (SegSelect::segments::iterator it = segList->begin(); it != segList->end(); ++it) - { - if (scanSeg4Cols(*it)) - return(FALSE); - } - } - else - // Scan data segments named - { - int segCount = get_segm_qty(); - for (int i = 0; i < segCount; i++) - { - if (segment_t *seg = getnseg(i)) - { - if (seg->type == SEG_DATA) - { - if (scanSeg4Cols(seg)) - return(FALSE); - } - } - } - } - - char numBuffer[32]; - msg(" Total COL: %s\n", prettyNumberString(colList.size(), numBuffer)); - #ifdef _DEVMODE - msg("COL scan time: %.3f\n", (getTimeStamp() - startTime)); - #endif - refreshUI(); - } - CATCH() - return(FALSE); -} - - -// Locate vftables -static BOOL scanSeg4Vftables(segment_t *seg, eaRefMap &colMap) -{ - qstring name; - if (get_segm_name(&name, seg) <= 0) - name = "???"; - msg(" N: \"%s\", A: " EAFORMAT "-" EAFORMAT ", S: %s.\n", name.c_str(), seg->start_ea, seg->end_ea, byteSizeString(seg->size())); - refreshUI(); - - UINT found = 0; - if (seg->size() >= sizeof(ea_t)) - { - ea_t startEA = ((seg->start_ea + sizeof(ea_t)) & ~((ea_t)(sizeof(ea_t) - 1))); - ea_t endEA = (seg->end_ea - sizeof(ea_t)); - eaRefMap::iterator colEnd = colMap.end(); - - for (ea_t ptr = startEA; ptr < endEA; ptr += sizeof(UINT)) //sizeof(ea_t) - { - // COL here? - ea_t ea = getEa(ptr); - eaRefMap::iterator it = colMap.find(ea); - if (it != colEnd) - { - // yes, look for vftable one ea_t below - ea_t vfptr = (ptr + sizeof(ea_t)); - ea_t method = getEa(vfptr); - // Points to code? - if (segment_t *s = getseg(method)) - { - // yes, - if (s->type == SEG_CODE) - { - RTTI::processVftable(vfptr, it->first); - it->second++, found++; - } - } - } - - if (WaitBox::isUpdateTime()) - if (WaitBox::updateAndCancelCheck()) - return(TRUE); - } - } - - if (found) - { - char numBuffer[32]; - msg(" Count: %s\n", prettyNumberString(found, numBuffer)); - refreshUI(); - } - return(FALSE); -} -// -static BOOL findVftables(SegSelect::segments *segList) -{ - try - { - #ifdef _DEVMODE - TIMESTAMP startTime = getTimeStamp(); - #endif - - // COLs in hash map for speed, plus match counts - eaRefMap colMap; - for (eaList::const_iterator it = colList.begin(), end = colList.end(); it != end; ++it) - colMap[*it] = 0; - - // Use user selected segments - if (segList && !segList->empty()) - { - for (SegSelect::segments::iterator it = segList->begin(); it != segList->end(); ++it) - { - if (scanSeg4Vftables(*it, colMap)) - return(FALSE); - } - } - else - // Scan data segments named - { - int segCount = get_segm_qty(); - for (int i = 0; i < segCount; i++) - { - if (segment_t *seg = getnseg(i)) - { - if (seg->type == SEG_DATA) - { - if (scanSeg4Vftables(seg, colMap)) - return(FALSE); - } - } - } - } - - // Rebuild 'colList' with any that were not located - if (!colList.empty()) - { - colList.clear(); - for (eaRefMap::const_iterator it = colMap.begin(), end = colMap.end(); it != end; ++it) - { - if (it->second == 0) - colList.push_front(it->first); - } - } - - #ifdef _DEVMODE - msg("vftable scan time: %.3f\n", (getTimeStamp() - startTime)); - #endif - } - CATCH() - return(FALSE); -} - - -// ================================================================================================ - -// Gather RTTI data -static BOOL getRttiData(SegSelect::segments *segList) -{ - // Free RTTI working data on return - struct OnReturn { ~OnReturn() { RTTI::freeWorkingData(); }; } onReturn; - - try - { - // ==== Locate __type_info_root_node - BOOL aborted = FALSE; - - // ==== Find and process COLs - msg("\nScanning for for RTTI Complete Object Locators.\n"); - refreshUI(); - if(findCols(segList)) - return(TRUE); - // typeDescList = TDs left that don't have a COL reference - // colList = Located COLs - - // ==== Find and process vftables - msg("\nScanning for vftables.\n"); - refreshUI(); - if(findVftables(segList)) - return(TRUE); - // colList = COLs left that don't have a vft reference - - // Could use the unlocated ref lists typeDescList & colList around for possible separate listing, etc. - // They get cleaned up on return of this function anyhow. - } - CATCH() - - return(FALSE); -} diff --git a/Plugin/Core.h b/Plugin/Core.h deleted file mode 100644 index 7fe68b6..0000000 --- a/Plugin/Core.h +++ /dev/null @@ -1,19 +0,0 @@ - -// **************************************************************************** -// File: Core.h -// Desc: -// -// **************************************************************************** - -extern void fixEa(ea_t ea); -extern void fixDword(ea_t eaAddress); -extern void fixFunction(ea_t eaFunc); -extern void setUnknown(ea_t ea, int size); -extern BOOL getVerifyEa(ea_t ea, ea_t &rValue); -extern BOOL hasAnteriorComment(ea_t ea); -extern void killAnteriorComments(ea_t ea); -extern int addStrucMember(struc_t *sptr, char *name, ea_t offset, flags_t flag, opinfo_t *type, asize_t nbytes); -extern void addTableEntry(UINT flags, ea_t vft, int methodCount, LPCSTR format, ...); -extern BOOL getPlainTypeName(__in LPCSTR mangled, __out_bcount(MAXSTR) LPSTR outStr); - -extern BOOL optionOverwriteComments, optionPlaceStructs; diff --git a/Plugin/IDA_ClassInformer_PlugIn.vcxproj b/Plugin/IDA_ClassInformer_PlugIn.vcxproj index 8beaa5e..a1aad5a 100644 --- a/Plugin/IDA_ClassInformer_PlugIn.vcxproj +++ b/Plugin/IDA_ClassInformer_PlugIn.vcxproj @@ -665,64 +665,6 @@ - - - - - - - - - - Performing Custom Build Tools - Performing Custom Build Tools - Performing Custom Build Tools - Performing Custom Build Tools - - - - - - - - - - - - - - - - - - - - - - - - - Performing Custom Build Tools - Performing Custom Build Tools - Performing Custom Build Tools - Performing Custom Build Tools - - - - - - - - - - - - - - - - - @@ -957,7 +899,6 @@ - true true diff --git a/Plugin/IDA_ClassInformer_PlugIn.vcxproj.filters b/Plugin/IDA_ClassInformer_PlugIn.vcxproj.filters index 2762a87..de748aa 100644 --- a/Plugin/IDA_ClassInformer_PlugIn.vcxproj.filters +++ b/Plugin/IDA_ClassInformer_PlugIn.vcxproj.filters @@ -44,7 +44,6 @@ - @@ -66,7 +65,6 @@ - diff --git a/Plugin/Main.cpp b/Plugin/Main.cpp index 7251e84..ecbe730 100644 --- a/Plugin/Main.cpp +++ b/Plugin/Main.cpp @@ -1,55 +1,1557 @@ // **************************************************************************** -// File: Main.cpp -// Desc: Plug-in main +// File: Core.cpp +// Desc: Class Informer // // **************************************************************************** #include "stdafx.h" +#include "Main.h" +#include "Vftable.h" +#include "RTTI.h" +#include "MainDialog.h" +#include +// +#include +#include + +typedef std::map STRMAP; + +// Netnode constants +const static char NETNODE_NAME[] = {"$ClassInformer_node"}; +const char NN_DATA_TAG = 'A'; +const char NN_TABLE_TAG = 'S'; + +// Our netnode value indexes +enum NETINDX +{ + NIDX_VERSION, // ClassInformer version + NIDX_COUNT // Table entry count +}; + +// VFTable entry container (fits in a netnode MAXSPECSIZE size) +#pragma pack(push, 1) +struct TBLENTRY +{ + ea_t vft; + WORD methods; + WORD flags; + WORD strSize; + char str[MAXSPECSIZE - (sizeof(ea_t) + (sizeof(WORD) * 3))]; // Note: IDA MAXSTR = 1024 +}; +#pragma pack(pop) + +// Line background color for non parent/top level hierarchy lines +// TOOD: Assumes text background is white. A way to make these user theme/style color aware? +#define GRAY(v) RGB(v,v,v) +static const bgcolor_t NOT_PARENT_COLOR = GRAY(235); // === Function Prototypes === -int idaapi IDAP_init(); -void idaapi IDAP_term(); -bool idaapi IDAP_run(size_t arg); -extern void CORE_Init(); -extern void CORE_Process(int iArg); -extern void CORE_Exit(); +static BOOL processStaticTables(); +static void showEndStats(); +static BOOL getRttiData(SegSelect::segments *segList); // === Data === -static char IDAP_comment[] = "Class Informer: Locates and fixes C++ Run Time Type class and structure information."; -static char IDAP_help[] = ""; -static char IDAP_name[] = "Class Informer"; +static TIMESTAMP s_startTime = 0; +static HMODULE myModuleHandle = NULL; +static UINT staticCCtorCnt = 0, staticCppCtorCnt = 0, staticCDtorCnt = 0; +static UINT startingFuncCount = 0, staticCtorDtorCnt = 0; +static UINT colCount = 0, missingColsFixed = 0, vftablesFixed = 0; +static BOOL initResourcesOnce = FALSE; +static int chooserIcon = 0; +static netnode *netNode = NULL; +static eaList colList; -// Plug-in description block -extern "C" ALIGN(16) plugin_t PLUGIN = +// Options +BOOL optionPlaceStructs = TRUE; +BOOL optionProcessStatic = TRUE; +BOOL optionAudioOnDone = TRUE; + + +static void freeWorkingData() { - IDP_INTERFACE_VERSION, // IDA version plug-in is written for - PLUGIN_PROC, // Plug-in flags - IDAP_init, // Initialization function - IDAP_term, // Clean-up function - IDAP_run, // Main plug-in body - IDAP_comment, // Comment - IDAP_help, // Help - IDAP_name, // Plug-in name shown in Edit->Plugins menu - NULL // Hot key to run the plug-in + try + { + RTTI::freeWorkingData(); + colList.clear(); + + if (netNode) + { + delete netNode; + netNode = NULL; + } + } + CATCH() +} + +// Initialize +int idaapi init() +{ + // Want the table entry structure to fit the max netnode size + CASSERT(sizeof(TBLENTRY) == 1024); + + if (strcmp(inf.procname, "metapc") == 0) // (ph.id == PLFM_386) + { + GetModuleHandleEx((GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS), (LPCTSTR)&init, &myModuleHandle); + return PLUGIN_KEEP; + } + + return PLUGIN_SKIP; +} + +// Uninitialize +// Normally doesn't happen as we need to stay resident for the modal windows +void idaapi term() +{ + try + { + OggPlay::endPlay(); + freeWorkingData(); + + if (initResourcesOnce) + { + if (chooserIcon) + { + free_custom_icon(chooserIcon); + chooserIcon = 0; + } + + Q_CLEANUP_RESOURCE(ClassInformerRes); + initResourcesOnce = FALSE; + } + } + CATCH() +} + + +// Init new netnode storage +static void newNetnodeStore() +{ + // Kill any existing store data first + netNode->altdel_all(NN_DATA_TAG); + netNode->supdel_all(NN_TABLE_TAG); + + // Init defaults + netNode->altset_idx8(NIDX_VERSION, MY_VERSION, NN_DATA_TAG); + netNode->altset_idx8(NIDX_COUNT, 0, NN_DATA_TAG); +} + +static WORD getStoreVersion(){ return((WORD)netNode->altval_idx8(NIDX_VERSION, NN_DATA_TAG)); } +static UINT getTableCount(){ return(netNode->altval_idx8(NIDX_COUNT, NN_DATA_TAG)); } +static BOOL setTableCount(UINT count){ return(netNode->altset_idx8(NIDX_COUNT, count, NN_DATA_TAG)); } +static BOOL getTableEntry(TBLENTRY &entry, UINT index){ return(netNode->supval(index, &entry, sizeof(TBLENTRY), NN_TABLE_TAG) > 0); } +static BOOL setTableEntry(TBLENTRY &entry, UINT index){ return(netNode->supset(index, &entry, (offsetof(TBLENTRY, str) + entry.strSize), NN_TABLE_TAG)); } + +// Add an entry to the vftable list +void addTableEntry(UINT flags, ea_t vft, int methodCount, LPCTSTR format, ...) +{ + TBLENTRY e; + e.vft = vft; + e.methods = methodCount; + e.flags = flags; + + va_list vl; + va_start(vl, format); + vsnprintf_s(e.str, sizeof(e.str), SIZESTR(e.str), format, vl); + va_end(vl); + e.strSize = (WORD) (strlen(e.str) + 1); + + UINT count = getTableCount(); + setTableEntry(e, count); + setTableCount(++count); +} + + +// RTTI list chooser +static const char LBTITLE[] = { "[Class Informer]" }; +static const UINT LBCOLUMNCOUNT = 5; +static const int LBWIDTHS[LBCOLUMNCOUNT] = { (8 | CHCOL_HEX), (4 | CHCOL_DEC), 3, 19, 500 }; +static const char *const LBHEADER[LBCOLUMNCOUNT] = +{ + "Vftable", + "Methods", + "Flags", + "Type", + "Hierarchy" }; -int idaapi IDAP_init() + +class rtti_chooser : public chooser_multi_t { - if(strcmp(inf.procname, "metapc") == 0) // (ph.id == PLFM_386) +public: + rtti_chooser() : chooser_multi_t(CH_QFTYP_DEFAULT, LBCOLUMNCOUNT, LBWIDTHS, LBHEADER, LBTITLE) { - CORE_Init(); - return(PLUGIN_KEEP); + #ifdef __EA64__ + // Setup hex address display to the minimal size needed plus a leading zero + UINT count = getTableCount(); + ea_t largestAddres = 0; + for (UINT i = 0; i < count; i++) + { + TBLENTRY e; e.vft = 0; + getTableEntry(e, i); + if (e.vft > largestAddres) + largestAddres = e.vft; + } + + char buffer[32]; + int digits = (int) strlen(_ui64toa(largestAddres, buffer, 16)); + if (++digits > 16) digits = 16; + sprintf_s(addressFormat, sizeof(buffer), "%%0%uI64X", digits); + #endif + + // Chooser icon + icon = chooserIcon; + } + + virtual const void *get_obj_id(size_t *len) const + { + *len = sizeof(LBTITLE); + return LBTITLE; } - return(PLUGIN_SKIP); + + virtual size_t get_count() const { return (size_t)getTableCount(); } + + virtual void get_row(qstrvec_t *cols_, int *icon_, chooser_item_attrs_t *attributes, size_t n) const + { + try + { + if (netNode) + { + // Generate the line + TBLENTRY e; + getTableEntry(e, (UINT)n); + + // vft address + qstrvec_t &cols = *cols_; + #ifdef __EA64__ + cols[0].sprnt(addressFormat, e.vft); + #else + cols[0].sprnt(EAFORMAT, e.vft); + #endif + + // Method count + if (e.methods > 0) + cols[1].sprnt("%u", e.methods); // "%04u" + else + cols[1].sprnt("???"); + + // Flags + char flags[4]; + int pos = 0; + if (e.flags & RTTI::CHD_MULTINH) flags[pos++] = 'M'; + if (e.flags & RTTI::CHD_VIRTINH) flags[pos++] = 'V'; + if (e.flags & RTTI::CHD_AMBIGUOUS) flags[pos++] = 'A'; + flags[pos++] = 0; + cols[2] = flags; + + // Type + LPCSTR tag = strchr(e.str, '@'); + if (tag) + { + char buffer[MAXSTR]; + int pos = (tag - e.str); + if (pos > SIZESTR(buffer)) pos = SIZESTR(buffer); + memcpy(buffer, e.str, pos); + buffer[pos] = 0; + cols[3] = buffer; + ++tag; + } + else + { + // Can happen when string is MAXSTR and greater + cols[3] = "??** MAXSTR overflow!"; + tag = e.str; + } + + // Composition/hierarchy + cols[4] = tag; + + //*icon_ = ((e.flags & RTTI::IS_TOP_LEVEL) ? 77 : 191); + *icon_ = 191; + + // Indicate entry is not a top/parent level by color + if (!(e.flags & RTTI::IS_TOP_LEVEL)) + attributes->color = NOT_PARENT_COLOR; + } + } + CATCH() + } + + virtual cbres_t enter(sizevec_t *sel) + { + size_t n = sel->front(); + if (n < get_count()) + { + TBLENTRY e; + getTableEntry(e, (UINT)n); + jumpto(e.vft); + } + return NOTHING_CHANGED; + } + + virtual void closed() + { + freeWorkingData(); + } + +private: + #ifdef __EA64__ + char addressFormat[16]; + #endif + +}; + +// find_widget +static QWidget *findChildByClass(QWidgetList &wl, LPCSTR className) +{ + foreach(QWidget *w, wl) + if (strcmp(w->metaObject()->className(), className) == 0) + return(w); + return nullptr; } -void idaapi IDAP_term() +// Find widget by title text +// If IDs are constant can use "static QWidget *QWidget::find(WId);"? +void customizeChooseWindow() +{ + try + { + QApplication::processEvents(); + + // Get parent chooser dock widget + QWidgetList pl = QApplication::activeWindow()->findChildren("[Class Informer]"); + if (QWidget *dw = findChildByClass(pl, "IDADockWidget")) + { + QFile file(STYLE_PATH "view-style.qss"); + if (file.open(QFile::ReadOnly | QFile::Text)) + dw->setStyleSheet(QTextStream(&file).readAll()); + } + else + msg("** customizeChooseWindow(): \"IDADockWidget\" not found!\n"); + + // Get chooser widget + if (QTableView *tv = (QTableView *) findChildByClass(pl, "TChooserView")) + { + // Set sort by type name + tv->sortByColumn(3, Qt::DescendingOrder); + + // Resize to contents + tv->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents); + tv->resizeColumnsToContents(); + tv->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); + + UINT count = getTableCount(); + for (UINT row = 0; row < count; row++) + tv->setRowHeight(row, 24); + } + else + msg("** customizeChooseWindow(): \"TChooserView\" not found!\n"); + } + CATCH() +} + + +bool idaapi run(size_t arg) { - CORE_Exit(); + try + { + char version[16]; + sprintf_s(version, sizeof(version), "%u.%u", HIBYTE(MY_VERSION), LOBYTE(MY_VERSION)); + msg("\n>> Class Informer: v: %s, built: %s, By Sirmabus\n", version, __DATE__); + + if (netNode) + { + msg("* Already active. Please close the chooser window first to run it again.\n"); + return true; + } + + if (!initResourcesOnce) + { + initResourcesOnce = TRUE; + Q_INIT_RESOURCE(ClassInformerRes); + + QFile file(STYLE_PATH "icon.png"); + if (file.open(QFile::ReadOnly)) + { + QByteArray ba = file.readAll(); + chooserIcon = load_custom_icon(ba.constData(), ba.size(), "png"); + } + } + + if(!auto_is_ok()) + { + msg("** Class Informer: Must wait for IDA to finish processing before starting plug-in! **\n*** Aborted ***\n\n"); + return true; + } + + OggPlay::endPlay(); + freeWorkingData(); + optionAudioOnDone = TRUE; + optionProcessStatic = TRUE; + optionPlaceStructs = TRUE; + startingFuncCount = (UINT) get_func_qty(); + colList.clear(); + staticCppCtorCnt = staticCCtorCnt = staticCtorDtorCnt = staticCDtorCnt = 0; + missingColsFixed = vftablesFixed = 0; + + // Create storage netnode + if(!(netNode = new netnode(NETNODE_NAME, SIZESTR(NETNODE_NAME), TRUE))) + { + QASSERT(66, FALSE); + return true; + } + + // Read existing storage if any + UINT tableCount = getTableCount(); + WORD storageVersion = getStoreVersion(); + BOOL storageExists = (tableCount > 0); + + // Ask if we should use storage or process again + if (storageExists) + { + // Version 2.3 didn't change the format + UINT major = HIBYTE(storageVersion), minor = LOBYTE(storageVersion); + if ((major != 2) || (minor < 2)) + { + msg("* Storage version mismatch, must rescan *\n"); + } + else + storageExists = (ask_yn(1, "TITLE Class Informer \nHIDECANCEL\nUse previously stored result? ") == 1); + } + + BOOL aborted = FALSE; + if(!storageExists) + { + newNetnodeStore(); + + // Only MS Visual C++ targets are supported + comp_t cmp = get_comp(default_compiler()); + if (cmp != COMP_MS) + { + msg("** IDA reports target compiler: \"%s\"\n", get_compiler_name(cmp)); + int iResult = ask_buttons(NULL, NULL, NULL, 0, "TITLE Class Informer\nHIDECANCEL\nIDA reports this IDB's compiler as: \"%s\" \n\nThis plug-in only understands MS Visual C++ targets.\nRunning it on other targets (like Borland© compiled, etc.) will have unpredicted results. \n\nDo you want to continue anyhow?", get_compiler_name(cmp)); + if (iResult != 1) + { + msg("- Aborted -\n\n"); + return true; + } + } + + // Do UI + SegSelect::segments *segList = NULL; + if (doMainDialog(optionPlaceStructs, optionProcessStatic, optionAudioOnDone, &segList)) + { + msg("- Canceled -\n\n"); + freeWorkingData(); + return true; + } + + msg("Working..\n"); + WaitBox::show("Class Informer", "Please wait..", "url(" STYLE_PATH "progress-style.qss)", STYLE_PATH "icon.png"); + WaitBox::updateAndCancelCheck(-1); + s_startTime = getTimeStamp(); + + // Add structure definitions to IDA once per session + static BOOL createStructsOnce = FALSE; + if (optionPlaceStructs && !createStructsOnce) + { + createStructsOnce = TRUE; + RTTI::addDefinitionsToIda(); + } + + if(optionProcessStatic) + { + // Process global and static ctor sections + msg("\nProcessing C/C++ ctor & dtor tables..\n"); + msg("-------------------------------------------------\n"); + if (!(aborted = processStaticTables())) + msg("Processing time: %s.\n", timeString(getTimeStamp() - s_startTime)); + } + + if (!aborted) + { + // Get RTTI data + if (!(aborted = getRttiData(segList))) + { + // Optionally play completion sound + if (optionAudioOnDone) + { + TIMESTAMP endTime = (getTimeStamp() - s_startTime); + if (endTime > (TIMESTAMP) 2.4) + { + OggPlay::endPlay(); + QFile file(STYLE_PATH "completed.ogg"); + if (file.open(QFile::ReadOnly)) + { + QByteArray ba = file.readAll(); + OggPlay::playFromMemory((const PVOID)ba.constData(), ba.size(), TRUE); + } + } + } + + showEndStats(); + msg("Done.\n\n"); + } + } + + WaitBox::hide(); + refresh_idaview_anyway(); + if (aborted) + { + msg("- Aborted -\n\n"); + return true; + } + } + + // Show list result window + if (!aborted && (getTableCount() > 0)) + { + // The chooser allocation will free it's self automatically + rtti_chooser *chooserPtr = new rtti_chooser(); + chooserPtr->choose(); + + customizeChooseWindow(); + } + } + CATCH() + + return true; } -bool idaapi IDAP_run(size_t arg) +// Print out end stats +static void showEndStats() { - CORE_Process(arg); - return true; // ???? there's no documentation on what the return value should be + try + { + msg(" \n\n"); + msg("=========== Stats ===========\n"); + + UINT vftableCount = getTableCount(); + if (vftablesFixed) + msg(" RTTI vftables: %u, fixed: %u (%.1f%%)\n", vftableCount, vftablesFixed, ((double) vftablesFixed / (double) vftableCount) * 100.0); + else + msg(" RTTI vftables: %u\n", vftableCount); + + // Amount of COLs fixed is usually about the same as vftables fixed, but the same COL can be used in multiple vftables + //if(missingColsFixed) + //msg(" COLs fixed: %u of %u (%.1f%%)\n", missingColsFixed, colCount, ((double) missingColsFixed / (double) colCount) * 100.0); + + UINT functionsFixed = ((UINT) get_func_qty() - startingFuncCount); + if(functionsFixed) + msg("Functions fixed: %u\n", functionsFixed); + + msg("Processing time: %s\n", timeString(getTimeStamp() - s_startTime)); + } + CATCH() } + + +// ================================================================================================ + +// Fix/create label and comment C/C++ initializer tables +static void setIntializerTable(ea_t start, ea_t end, BOOL isCpp) +{ + try + { + if (UINT count = ((end - start) / sizeof(ea_t))) + { + // Set table elements as pointers + ea_t ea = start; + while (ea <= end) + { + fixEa(ea); + + // Might fix missing/messed stubs + if (ea_t func = get_32bit(ea)) + fixFunction(func); + + ea += sizeof(ea_t); + }; + + // Start label + if (!hasName(start)) + { + char name[MAXSTR]; + if (isCpp) + sprintf_s(name, sizeof(name), "__xc_a_%d", staticCppCtorCnt); + else + sprintf_s(name, sizeof(name), "__xi_a_%d", staticCCtorCnt); + setName(start, name); + } + + // End label + if (!hasName(end)) + { + char name[MAXSTR]; + if (isCpp) + sprintf_s(name, sizeof(name), "__xc_z_%d", staticCppCtorCnt); + else + sprintf_s(name, sizeof(name), "__xi_z_%d", staticCCtorCnt); + setName(end, name); + } + + // Comment + // Never overwrite, it might be the segment comment + if (!hasAnteriorComment(start)) + { + if (isCpp) + setAnteriorComment(start, "%d C++ static ctors (#classinformer)", count); + else + setAnteriorComment(start, "%d C initializers (#classinformer)", count); + } + else + // Place comment @ address instead + if (!hasComment(start)) + { + if (isCpp) + { + char comment[MAXSTR]; + sprintf_s(comment, sizeof(comment), "%d C++ static ctors (#classinformer)", count); + setComment(start, comment, TRUE); + } + else + { + char comment[MAXSTR]; + sprintf_s(comment, sizeof(comment), "%d C initializers (#classinformer)", count); + setComment(start, comment, TRUE); + } + } + + if (isCpp) + staticCppCtorCnt++; + else + staticCCtorCnt++; + } + } + CATCH() +} + +// Fix/create label and comment C/C++ terminator tables +static void setTerminatorTable(ea_t start, ea_t end) +{ + try + { + if (UINT count = ((end - start) / sizeof(ea_t))) + { + // Set table elements as pointers + ea_t ea = start; + while (ea <= end) + { + fixEa(ea); + + // Fix function + if (ea_t func = getEa(ea)) + fixFunction(func); + + ea += sizeof(ea_t); + }; + + // Start label + if (!hasName(start)) + { + char name[MAXSTR]; + _snprintf_s(name, sizeof(name), SIZESTR(name), "__xt_a_%d", staticCDtorCnt); + setName(start, name); + } + + // End label + if (!hasName(end)) + { + char name[MAXSTR]; + _snprintf_s(name, sizeof(name), SIZESTR(name), "__xt_z_%d", staticCDtorCnt); + setName(end, name); + } + + // Comment + // Never overwrite, it might be the segment comment + if (!hasAnteriorComment(start)) + setAnteriorComment(start, "%d C terminators (#classinformer)", count); + else + // Place comment @ address instead + if (!hasComment(start)) + { + char comment[MAXSTR]; + _snprintf_s(comment, sizeof(comment), SIZESTR(comment), "%d C terminators (#classinformer)", count); + setComment(start, comment, TRUE); + } + + staticCDtorCnt++; + } + } + CATCH() +} + +// "" for when we are uncertain of ctor or dtor type table +static void setCtorDtorTable(ea_t start, ea_t end) +{ + try + { + if (UINT count = ((end - start) / sizeof(ea_t))) + { + // Set table elements as pointers + ea_t ea = start; + while (ea <= end) + { + fixEa(ea); + + // Fix function + if (ea_t func = getEa(ea)) + fixFunction(func); + + ea += sizeof(ea_t); + }; + + // Start label + if (!hasName(start)) + { + char name[MAXSTR]; + _snprintf_s(name, sizeof(name), SIZESTR(name), "__x?_a_%d", staticCtorDtorCnt); + setName(start, name); + } + + // End label + if (!hasName(end)) + { + char name[MAXSTR]; + _snprintf_s(name, sizeof(name), SIZESTR(name), "__x?_z_%d", staticCtorDtorCnt); + setName(end, name); + } + + // Comment + // Never overwrite, it might be the segment comment + if (!hasAnteriorComment(start)) + setAnteriorComment(start, "%d C initializers/terminators (#classinformer)", count); + else + // Place comment @ address instead + if (!hasComment(start)) + { + char comment[MAXSTR]; + _snprintf_s(comment, sizeof(comment), SIZESTR(comment), "%d C initializers/terminators (#classinformer)", count); + setComment(start, comment, TRUE); + } + + staticCtorDtorCnt++; + } + } + CATCH() +} + + +// Process redister based _initterm() +static void processRegisterInitterm(ea_t start, ea_t end, ea_t call) +{ + if ((end != BADADDR) && (start != BADADDR)) + { + // Should be in the same segment + if (getseg(start) == getseg(end)) + { + if (start > end) + swap_t(start, end); + + msg(" " EAFORMAT " to " EAFORMAT " CTOR table.\n", start, end); + setIntializerTable(start, end, TRUE); + if(!hasComment(call)) + setComment(call, "_initterm", TRUE); + } + else + msg(" ** Bad address range of " EAFORMAT ", " EAFORMAT " for \"_initterm\" type ** .\n", start, end); + } +} + +static UINT doInittermTable(func_t *func, ea_t start, ea_t end, LPCTSTR name) +{ + UINT found = FALSE; + + if ((start != BADADDR) && (end != BADADDR)) + { + // Should be in the same segment + if (getseg(start) == getseg(end)) + { + if (start > end) + swap_t(start, end); + + // Try to determine if we are in dtor or ctor section + if (func) + { + qstring qstr; + if (get_long_name(&qstr, func->start_ea) > 0) + { + char funcName[MAXSTR]; + strncpy_s(funcName, MAXSTR, qstr.c_str(), (MAXSTR - 1)); + _strlwr(funcName); + + // Start/ctor? + if (strstr(funcName, "cinit") || strstr(funcName, "tmaincrtstartup") || strstr(funcName, "start")) + { + msg(" " EAFORMAT " to " EAFORMAT " CTOR table.\n", start, end); + setIntializerTable(start, end, TRUE); + found = TRUE; + } + else + // Exit/dtor function? + if (strstr(funcName, "exit")) + { + msg(" " EAFORMAT " to " EAFORMAT " DTOR table.\n", start, end); + setTerminatorTable(start, end); + found = TRUE; + } + } + } + + if (!found) + { + // Fall back to generic assumption + msg(" " EAFORMAT " to " EAFORMAT " CTOR/DTOR table.\n", start, end); + setCtorDtorTable(start, end); + found = TRUE; + } + } + else + msg(" ** Miss matched segment table addresses " EAFORMAT ", " EAFORMAT " for \"%s\" type **\n", start, end, name); + } + else + msg(" ** Bad input address range of " EAFORMAT ", " EAFORMAT " for \"%s\" type **\n", start, end, name); + + return(found); +} + +// Process _initterm function +// Returns TRUE if at least one found +static BOOL processInitterm(ea_t address, LPCTSTR name) +{ + msg(EAFORMAT" processInitterm: \"%s\" \n", address, name); + UINT count = 0; + + // Walk xrefs + ea_t xref = get_first_fcref_to(address); + while (xref && (xref != BADADDR)) + { + msg(" " EAFORMAT " \"%s\" xref.\n", xref, name); + + // Should be code + if (is_code(get_flags(xref))) + { + do + { + // The most common are two instruction arguments + // Back up two instructions + ea_t instruction1 = prev_head(xref, 0); + if (instruction1 == BADADDR) + break; + ea_t instruction2 = prev_head(instruction1, 0); + if (instruction2 == BADADDR) + break; + + // Bail instructions are past the function start now + func_t *func = get_func(xref); + if (func && (instruction2 < func->start_ea)) + { + //msg(" " EAFORMAT " arg2 outside of contained function **\n", func->start_ea); + break; + } + + struct ARG2PAT + { + LPCSTR pattern; + UINT start, end, padding; + } static const ALIGN(16) arg2pat[] = + { + #ifndef __EA64__ + { "68 ?? ?? ?? ?? 68", 6, 1 }, // push offset s, push offset e + { "B8 ?? ?? ?? ?? C7 04 24", 8, 1 }, // mov [esp+4+var_4], offset s, mov eax, offset e Maestia + { "68 ?? ?? ?? ?? B8", 6, 1 }, // mov eax, offset s, push offset e + #else + { "48 8D 15 ?? ?? ?? ?? 48 8D 0D", 3, 3 }, // lea rdx,s, lea rcx,e + #endif + }; + BOOL matched = FALSE; + for (UINT i = 0; (i < qnumber(arg2pat)) && !matched; i++) + { + ea_t match = FIND_BINARY(instruction2, xref, arg2pat[i].pattern); + if (match != BADADDR) + { + #ifndef __EA64__ + ea_t start = getEa(match + arg2pat[i].start); + ea_t end = getEa(match + arg2pat[i].end); + #else + UINT startOffset = get_32bit(instruction1 + arg2pat[i].start); + UINT endOffset = get_32bit(instruction2 + arg2pat[i].end); + ea_t start = (instruction1 + 7 + *((PINT) &startOffset)); // TODO: 7 is hard coded instruction length, put this in arg2pat table? + ea_t end = (instruction2 + 7 + *((PINT) &endOffset)); + #endif + msg(" " EAFORMAT " Two instruction pattern match #%d\n", match, i); + count += doInittermTable(func, start, end, name); + matched = TRUE; + break; + } + } + + // 3 instruction + /* + searchStart = prev_head(searchStart, BADADDR); + if (searchStart == BADADDR) + break; + if (func && (searchStart < func->start_ea)) + break; + + if (func && (searchStart < func->start_ea)) + { + msg(" " EAFORMAT " arg3 outside of contained function **\n", func->start_ea); + break; + } + + .text:10008F78 push offset unk_1000B1B8 + .text:10008F7D push offset unk_1000B1B0 + .text:10008F82 mov dword_1000F83C, 1 + "68 ?? ?? ?? ?? 68 ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ??" + */ + + if (!matched) + msg(" ** arguments not located!\n"); + + } while (FALSE); + } + else + msg(" " EAFORMAT " ** \"%s\" xref is not code! **\n", xref, name); + + xref = get_next_fcref_to(address, xref); + }; + + msg(" \n"); + return(count > 0); +} + + +// Process global/static ctor & dtor tables. +// Returns TRUE if user aborted +static BOOL processStaticTables() +{ + staticCppCtorCnt = staticCCtorCnt = staticCtorDtorCnt = staticCDtorCnt = 0; + + // x64 __tmainCRTStartup, _CRT_INIT + + try + { + // Locate _initterm() and _initterm_e() functions + STRMAP inittermMap; + func_t *cinitFunc = NULL; + UINT funcCount = (UINT) get_func_qty(); + + for (UINT i = 0; i < funcCount; i++) + { + if (func_t *func = getn_func(i)) + { + qstring qstr; + if (get_long_name(&qstr, func->start_ea) > 0) + { + char name[MAXSTR]; + strncpy_s(name, MAXSTR, qstr.c_str(), (MAXSTR - 1)); + + int len = (int) strlen(name); + if (len >= SIZESTR("_cinit")) + { + if (strcmp((name + (len - SIZESTR("_cinit"))), "_cinit") == 0) + { + // Skip stub functions + if (func->size() > 16) + { + msg(EAFORMAT" C: \"%s\", %d bytes.\n", func->start_ea, name, func->size()); + _ASSERT(cinitFunc == NULL); + cinitFunc = func; + } + } + else + if ((len >= SIZESTR("_initterm")) && (strcmp((name + (len - SIZESTR("_initterm"))), "_initterm") == 0)) + { + msg(EAFORMAT" I: \"%s\", %d bytes.\n", func->start_ea, name, func->size()); + inittermMap[func->start_ea] = name; + } + else + if ((len >= SIZESTR("_initterm_e")) && (strcmp((name + (len - SIZESTR("_initterm_e"))), "_initterm_e") == 0)) + { + msg(EAFORMAT" E: \"%s\", %d bytes.\n", func->start_ea, name, func->size()); + inittermMap[func->start_ea] = name; + } + } + } + } + } + + if(WaitBox::isUpdateTime()) + if (WaitBox::updateAndCancelCheck()) + return(TRUE); + + // Look for import versions + { + static LPCSTR imports[] = + { + "__imp__initterm", "__imp__initterm_e" + }; + for (UINT i = 0; i < qnumber(imports); i++) + { + ea_t adress = get_name_ea(BADADDR, imports[i]); + if (adress != BADADDR) + { + if (inittermMap.find(adress) == inittermMap.end()) + { + msg(EAFORMAT" import: \"%s\".\n", adress, imports[i]); + inittermMap[adress] = imports[i]; + } + } + } + } + + // Process register based _initterm() calls inside _cint() + if (cinitFunc) + { + struct CREPAT + { + LPCSTR pattern; + UINT start, end, call; + } static const ALIGN(16) pat[] = + { + { "B8 ?? ?? ?? ?? BE ?? ?? ?? ?? 59 8B F8 3B C6 73 0F 8B 07 85 C0 74 02 FF D0 83 C7 04 3B FE 72 F1", 1, 6, 0x17}, + { "BE ?? ?? ?? ?? 8B C6 BF ?? ?? ?? ?? 3B C7 59 73 0F 8B 06 85 C0 74 02 FF D0 83 C6 04 3B F7 72 F1", 1, 8, 0x17}, + }; + + for (UINT i = 0; i < qnumber(pat); i++) + { + ea_t match = FIND_BINARY(cinitFunc->start_ea, cinitFunc->end_ea, pat[i].pattern); + while (match != BADADDR) + { + msg(" " EAFORMAT " Register _initterm(), pattern #%d.\n", match, i); + ea_t start = getEa(match + pat[i].start); + ea_t end = getEa(match + pat[i].end); + processRegisterInitterm(start, end, (match + pat[i].call)); + match = FIND_BINARY(match + 30, cinitFunc->end_ea, pat[i].pattern); + }; + } + } + + msg(" \n"); + if (WaitBox::isUpdateTime()) + if (WaitBox::updateAndCancelCheck()) + return(TRUE); + + // Process _initterm references + for (STRMAP::iterator it = inittermMap.begin(); it != inittermMap.end(); ++it) + { + if (processInitterm(it->first, it->second.c_str())) + if (WaitBox::isUpdateTime()) + if (WaitBox::updateAndCancelCheck()) + return(TRUE); + } + + if (WaitBox::isUpdateTime()) + if (WaitBox::updateAndCancelCheck()) + return(TRUE); + } + CATCH() + + return(FALSE); +} + +// ================================================================================================ + + +// Return TRUE if address as a anterior comment +inline BOOL hasAnteriorComment(ea_t ea) +{ + return(get_first_free_extra_cmtidx(ea, E_PREV) != E_PREV); +} + +// Delete any anterior comment(s) at address if there is some +inline void killAnteriorComments(ea_t ea) +{ + delete_extra_cmts(ea, E_PREV); +} + +// Force a memory location to be DWORD size +void fixDword(ea_t ea) +{ + if (!is_dword(get_flags(ea))) + { + setUnknown(ea, sizeof(DWORD)); + create_dword(ea, sizeof(DWORD)); + } +} + +// Force memory location to be ea_t size +void fixEa(ea_t ea) +{ + #ifndef __EA64__ + if (!is_dword(get_flags(ea))) + #else + if (!is_qword(get_flags(ea))) + #endif + { + setUnknown(ea, sizeof(ea_t)); + #ifndef __EA64__ + create_dword(ea, sizeof(ea_t)); + #else + create_qword(ea, sizeof(ea_t)); + #endif + } +} + +// Address should be a code function +void fixFunction(ea_t ea) +{ + flags_t flags = get_flags(ea); + + // No code here? + if (!is_code(flags)) + { + // Attempt to make it so + create_insn(ea); + add_func(ea, BADADDR); + } + else + // Yea there is code here, should have a function boddy too + if (!is_func(flags)) + add_func(ea, BADADDR); +} + +// Get IDA EA bit value with verification +BOOL getVerifyEa(ea_t ea, ea_t &rValue) +{ + // Location valid? + if (is_loaded(ea)) + { + // Get ea_t value + rValue = getEa(ea); + return(TRUE); + } + + return(FALSE); +} + + +// Undecorate to minimal class name +// typeid(T).name() +// http://en.wikipedia.org/wiki/Name_mangling +// http://en.wikipedia.org/wiki/Visual_C%2B%2B_name_mangling +// http://www.agner.org/optimize/calling_conventions.pdf + +BOOL getPlainTypeName(__in LPCSTR mangled, __out_bcount(MAXSTR) LPSTR outStr) +{ + outStr[0] = outStr[MAXSTR - 1] = 0; + + // Use CRT function for type names + if (mangled[0] == '.') + { + __unDName(outStr, mangled + 1, MAXSTR, mallocWrap, free, (UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY | UNDNAME_NO_ECSU)); + if ((outStr[0] == 0) || (strcmp((mangled + 1), outStr) == 0)) + { + msg("** getPlainClassName:__unDName() failed to unmangle! input: \"%s\"\n", mangled); + return(FALSE); + } + } + else + // IDA demangler for everything else + { + qstring qstr; + int result = demangle_name(&qstr, mangled, M_COMPILER /*MT_MSCOMP*/, DQT_FULL); + if (result < 0) + { + //msg("** getPlainClassName:demangle_name2() failed to unmangle! result: %d, input: \"%s\"\n", result, mangled); + return(FALSE); + } + + // No inhibit flags will drop this + strncpy_s(outStr, MAXSTR, qstr.c_str(), (MAXSTR - 1)); + if (LPSTR ending = strstr(outStr, "::`vftable'")) + *ending = 0; + } + + return(TRUE); +} + +// Wrapper for 'add_struc_member()' with error messages +// See to make more sense of types: http://idapython.googlecode.com/svn-history/r116/trunk/python/idc.py +int addStrucMember(struc_t *sptr, char *name, ea_t offset, flags_t flag, opinfo_t *type, asize_t nbytes) +{ + int r = add_struc_member(sptr, name, offset, flag, type, nbytes); + switch(r) + { + case STRUC_ERROR_MEMBER_NAME: + msg("AddStrucMember(): error: already has member with this name (bad name)\n"); + break; + + case STRUC_ERROR_MEMBER_OFFSET: + msg("AddStrucMember(): error: already has member at this offset\n"); + break; + + case STRUC_ERROR_MEMBER_SIZE: + msg("AddStrucMember(): error: bad number of bytes or bad sizeof(type)\n"); + break; + + case STRUC_ERROR_MEMBER_TINFO: + msg("AddStrucMember(): error: bad typeid parameter\n"); + break; + + case STRUC_ERROR_MEMBER_STRUCT: + msg("AddStrucMember(): error: bad struct id (the 1st argument)\n"); + break; + + case STRUC_ERROR_MEMBER_UNIVAR: + msg("AddStrucMember(): error: unions can't have variable sized members\n"); + break; + + case STRUC_ERROR_MEMBER_VARLAST: + msg("AddStrucMember(): error: variable sized member should be the last member in the structure\n"); + break; + + case STRUC_ERROR_MEMBER_NESTED: + msg("AddStrucMember(): error: recursive structure nesting is forbidden\n"); + break; + }; + + return(r); +} + + +void setUnknown(ea_t ea, int size) +{ + del_items(ea, DELIT_EXPAND, size); + +#if 0 + // TODO: Does the item overrun problem still exist in IDA 7? + //do_unknown_range(ea, (size_t)size, DOUNK_SIMPLE); + while (size > 0) + { + int isize = get_item_size(ea); + if (isize > size) + break; + else + { + do_unknown(ea, DOUNK_SIMPLE); + ea += (ea_t)isize, size -= isize; + } + }; +#endif +} + +// Set name for address +void setName(ea_t ea, __in LPCSTR name) +{ + //msg("%08X \"%s\"\n", ea, name); + set_name(ea, name, (SN_NON_AUTO | SN_NOWARN | SN_NOCHECK | SN_FORCE)); +} + +// Set comment at address +void setComment(ea_t ea, LPCSTR comment, BOOL rptble) +{ + //msg("%08X cmt: \"%s\"\n", ea, comment); + set_cmt(ea, comment, rptble); +} + +// Set comment at the line above the address +void setAnteriorComment(ea_t ea, const char *format, ...) +{ + va_list va; + va_start(va, format); + vadd_extra_line(ea, 0, format, va); + va_end(va); +} + + +// Scan segment for COLs +static BOOL scanSeg4Cols(segment_t *seg) +{ + qstring name; + if (get_segm_name(&name, seg) <= 0) + name = "???"; + msg(" N: \"%s\", A: " EAFORMAT " - " EAFORMAT ", S: %s.\n", name.c_str(), seg->start_ea, seg->end_ea, byteSizeString(seg->size())); + + UINT found = 0; + if (seg->size() >= sizeof(RTTI::_RTTICompleteObjectLocator)) + { + ea_t startEA = ((seg->start_ea + sizeof(UINT)) & ~((ea_t) (sizeof(UINT) - 1))); + ea_t endEA = (seg->end_ea - sizeof(RTTI::_RTTICompleteObjectLocator)); + + for (ea_t ptr = startEA; ptr < endEA;) + { + #ifdef __EA64__ + // Check for possible COL here + // Signature will be one + // TODO: Is this always 1 or can it be zero like 32bit? + if (get_32bit(ptr + offsetof(RTTI::_RTTICompleteObjectLocator, signature)) == 1) + { + if (RTTI::_RTTICompleteObjectLocator::isValid(ptr)) + { + // yes + colList.push_front(ptr); + missingColsFixed += (UINT) RTTI::_RTTICompleteObjectLocator::tryStruct(ptr); + ptr += sizeof(RTTI::_RTTICompleteObjectLocator); + continue; + } + } + else + { + // TODO: Should we check stray BCDs? + // Each value would have to be tested for a valid type_def and + // the pattern is pretty ambiguous. + } + #else + // TypeDescriptor address here? + ea_t ea = getEa(ptr); + if (ea >= 0x10000) + { + if (RTTI::type_info::isValid(ea)) + { + // yes, a COL here? + ea_t col = (ptr - offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); + if (RTTI::_RTTICompleteObjectLocator::isValid2(col)) + { + // yes + colList.push_front(col); + missingColsFixed += (UINT) RTTI::_RTTICompleteObjectLocator::tryStruct(col); + ptr += sizeof(RTTI::_RTTICompleteObjectLocator); + continue; + } + /* + else + // No, is it a BCD then? + if (RTTI::_RTTIBaseClassDescriptor::isValid2(ptr)) + { + // yes + char dontCare[MAXSTR]; + RTTI::_RTTIBaseClassDescriptor::doStruct(ptr, dontCare); + } + */ + } + } + #endif + + if (WaitBox::isUpdateTime()) + if (WaitBox::updateAndCancelCheck()) + return(TRUE); + + ptr += sizeof(UINT); + } + } + + if (found) + { + char numBuffer[32]; + msg(" Count: %s\n", prettyNumberString(found, numBuffer)); + } + return(FALSE); +} +// +// Locate COL by descriptor list +static BOOL findCols(SegSelect::segments *segList) +{ + try + { + TIMESTAMP startTime = getTimeStamp(); + + // Use user selected segments + if (segList && !segList->empty()) + { + for (SegSelect::segments::iterator it = segList->begin(); it != segList->end(); ++it) + { + if (scanSeg4Cols(*it)) + return(FALSE); + } + } + else + // Scan data segments named + { + int segCount = get_segm_qty(); + for (int i = 0; i < segCount; i++) + { + if (segment_t *seg = getnseg(i)) + { + if (seg->type == SEG_DATA) + { + if (scanSeg4Cols(seg)) + return(FALSE); + } + } + } + } + + char numBuffer[32]; + msg(" Total COL: %s\n", prettyNumberString(colList.size(), numBuffer)); + msg("COL scan time: %.3f\n", (getTimeStamp() - startTime)); + } + CATCH() + return(FALSE); +} + + +// Locate vftables +static BOOL scanSeg4Vftables(segment_t *seg, eaRefMap &colMap) +{ + qstring name; + if (get_segm_name(&name, seg) <= 0) + name = "???"; + msg(" N: \"%s\", A: " EAFORMAT " - " EAFORMAT ", S: %s.\n", name.c_str(), seg->start_ea, seg->end_ea, byteSizeString(seg->size())); + + UINT foundCount = 0; + if (seg->size() >= sizeof(ea_t)) + { + ea_t startEA = ((seg->start_ea + sizeof(ea_t)) & ~((ea_t) (sizeof(ea_t) - 1))); + ea_t endEA = (seg->end_ea - sizeof(ea_t)); + _ASSERT(((startEA | endEA) & 3) == 0); + eaRefMap::iterator colEnd = colMap.end(); + + // Walk uint32 at the time, at align 4 (same for either 32bit or 64bit targets) + for (ea_t ptr = startEA; ptr < endEA; ptr += sizeof(UINT)) + { + // A COL here? + ea_t ea = getEa(ptr); + eaRefMap::iterator it = colMap.find(ea); + if (it != colEnd) + { + // yes, look for vftable one ea_t below + ea_t vfptr = (ptr + sizeof(ea_t)); + ea_t method = getEa(vfptr); + + // Points to code? + if (segment_t *s = getseg(method)) + { + // yes, + if (s->type == SEG_CODE) + { + BOOL result = RTTI::processVftable(vfptr, it->first); + //if(result) + // msg(EAFORMAT " vft fix **\n", vfptr); + vftablesFixed += (UINT) result; + it->second++, foundCount++; + } + } + } + + if (WaitBox::isUpdateTime()) + if (WaitBox::updateAndCancelCheck()) + return(TRUE); + } + } + + if (foundCount) + { + char numBuffer[32]; + msg(" Count: %s\n", prettyNumberString(foundCount, numBuffer)); + } + return(FALSE); +} +// +static BOOL findVftables(SegSelect::segments *segList) +{ + try + { + TIMESTAMP startTime = getTimeStamp(); + + // COLs in a hash map for speed, plus add match counts + eaRefMap colMap; + for (eaList::const_iterator it = colList.begin(), end = colList.end(); it != end; ++it) + colMap[*it] = 0; + + // Use user selected segments + if (segList && !segList->empty()) + { + for (SegSelect::segments::iterator it = segList->begin(); it != segList->end(); ++it) + { + if (scanSeg4Vftables(*it, colMap)) + return(FALSE); + } + } + else + // Scan data segments named + { + int segCount = get_segm_qty(); + for (int i = 0; i < segCount; i++) + { + if (segment_t *seg = getnseg(i)) + { + if (seg->type == SEG_DATA) + { + if (scanSeg4Vftables(seg, colMap)) + return(FALSE); + } + } + } + } + + // Rebuild 'colList' with any that were not located + if (!colList.empty()) + { + colCount = (UINT) colList.size(); + colList.clear(); + + for (eaRefMap::const_iterator it = colMap.begin(), end = colMap.end(); it != end; ++it) + { + if (it->second == 0) + colList.push_front(it->first); + } + + //msg("\n** COLs not located: %u\n", (UINT)colList.size()); refreshUI(); + } + + msg("Vftable scan time: %.3f\n", (getTimeStamp() - startTime)); + } + CATCH() + return(FALSE); +} + + +// ================================================================================================ + +// Gather RTTI data +static BOOL getRttiData(SegSelect::segments *segList) +{ + // Free RTTI working data on return + struct OnReturn { ~OnReturn() { RTTI::freeWorkingData(); }; } onReturn; + + try + { + // ==== Locate __type_info_root_node + BOOL aborted = FALSE; + + // ==== Find and process Complete Object Locators (COL) + msg("\nScanning for for RTTI Complete Object Locators..\n"); + msg("-------------------------------------------------\n"); + + if(findCols(segList)) + return(TRUE); + // typeDescList = TDs left that don't have a COL reference + // colList = Located COLs + + // ==== Find and process vftables + msg("\nScanning for Virtual Function Tables..\n"); + msg("-------------------------------------------------\n"); + + if(findVftables(segList)) + return(TRUE); + // colList = COLs left that don't have a vft reference + + // Could use the unlocated ref lists typeDescList & colList around for possible separate listing, etc. + // They get cleaned up on return of this function anyhow. + } + CATCH() + + return(FALSE); +} + + +// ================================================================================================ + +static char _comment[] = "Class Informer: Locates and fixes C++ Run Time Type class and structure information."; +static char _help[] = ""; +static char _name[] = "Class Informer"; + +// Plug-in description block +__declspec(dllexport) plugin_t PLUGIN = +{ + IDP_INTERFACE_VERSION, // IDA version plug-in is written for + PLUGIN_PROC, // Plug-in flags + init, // Initialization function + term, // Clean-up function + run, // Main plug-in body + _comment, // Comment + _help, // Help + _name, // Plug-in name shown in Edit->Plugins menu + NULL // Hot key to run the plug-in +}; diff --git a/Plugin/Main.h b/Plugin/Main.h new file mode 100644 index 0000000..8efd9fa --- /dev/null +++ b/Plugin/Main.h @@ -0,0 +1,64 @@ + +// **************************************************************************** +// File: Main.h +// Desc: +// +// **************************************************************************** + +extern void fixEa(ea_t ea); +extern void fixDword(ea_t eaAddress); +extern void fixFunction(ea_t eaFunc); +extern void setUnknown(ea_t ea, int size); +extern BOOL getVerifyEa(ea_t ea, ea_t &rValue); +extern BOOL hasAnteriorComment(ea_t ea); +extern void killAnteriorComments(ea_t ea); +extern int addStrucMember(struc_t *sptr, char *name, ea_t offset, flags_t flag, opinfo_t *type, asize_t nbytes); +extern void addTableEntry(UINT flags, ea_t vft, int methodCount, LPCSTR format, ...); +extern BOOL getPlainTypeName(__in LPCSTR mangled, __out_bcount(MAXSTR) LPSTR outStr); +extern void setName(ea_t ea, __in LPCSTR name); +extern void setComment(ea_t ea, LPCSTR comment, BOOL rptble); +extern void setAnteriorComment(ea_t ea, const char *format, ...); + +// Return TRUE if there is a name at address that is not a dumbly name +inline BOOL hasName(ea_t ea) { return has_name(get_flags(ea)); } + +// Return TRUE if there is a comment at address +inline BOOL hasComment(ea_t ea) { return has_cmt(get_flags(ea)); } + + +// Get IDA 32 bit value with verification +template BOOL getVerify32(ea_t eaPtr, T &rValue) +{ + // Location valid? + if (is_loaded(eaPtr)) + { + // Get 32bit value + rValue = (T) get_32bit(eaPtr); + return(TRUE); + } + + return(FALSE); +} + +// Get address/pointer value +inline ea_t getEa(ea_t ea) +{ + #ifndef __EA64__ + return((ea_t) get_32bit(ea)); + #else + return((ea_t) get_64bit(ea)); + #endif +} + + +// Returns TRUE if ea_t sized value flags +inline BOOL isEa(flags_t f) +{ + #ifndef __EA64__ + return(is_dword(f)); + #else + return(is_qword(f)); + #endif +} + +extern BOOL optionPlaceStructs; diff --git a/Plugin/MainDialog.cpp b/Plugin/MainDialog.cpp index 335bcd7..cf0706c 100644 --- a/Plugin/MainDialog.cpp +++ b/Plugin/MainDialog.cpp @@ -10,7 +10,7 @@ #include -MainDialog::MainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionOverwriteComments, BOOL &optionAudioOnDone, SegSelect::segments **segs) : QDialog(QApplication::activeWindow(), 0) +MainDialog::MainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionAudioOnDone, SegSelect::segments **segs) : QDialog(QApplication::activeWindow(), 0) { Ui::MainCIDialog::setupUi(this); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); @@ -20,8 +20,7 @@ MainDialog::MainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL #define INITSTATE(obj,state) obj->setCheckState((state == TRUE) ? Qt::Checked : Qt::Unchecked); INITSTATE(checkBox1, optionPlaceStructs); INITSTATE(checkBox2, optionProcessStatic); - INITSTATE(checkBox3, optionOverwriteComments); - INITSTATE(checkBox4, optionAudioOnDone); + INITSTATE(checkBox3, optionAudioOnDone); #undef INITSTATE // Apply style sheet @@ -39,17 +38,16 @@ void MainDialog::segmentSelect() } // Do main dialog, return TRUE if canceled -BOOL doMainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionOverwriteComments, BOOL &optionAudioOnDone, SegSelect::segments **segs) +BOOL doMainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionAudioOnDone, SegSelect::segments **segs) { BOOL result = TRUE; - MainDialog *dlg = new MainDialog(optionPlaceStructs, optionProcessStatic, optionOverwriteComments, optionAudioOnDone, segs); + MainDialog *dlg = new MainDialog(optionPlaceStructs, optionProcessStatic, optionAudioOnDone, segs); if (dlg->exec()) { #define CHECKSTATE(obj,var) var = dlg->obj->isChecked() CHECKSTATE(checkBox1, optionPlaceStructs); CHECKSTATE(checkBox2, optionProcessStatic); - CHECKSTATE(checkBox3, optionOverwriteComments); - CHECKSTATE(checkBox4, optionAudioOnDone); + CHECKSTATE(checkBox3, optionAudioOnDone); #undef CHECKSTATE result = FALSE; } diff --git a/Plugin/MainDialog.h b/Plugin/MainDialog.h index 24875fd..2ac54b3 100644 --- a/Plugin/MainDialog.h +++ b/Plugin/MainDialog.h @@ -11,7 +11,7 @@ class MainDialog : public QDialog, public Ui::MainCIDialog { Q_OBJECT public: - MainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionOverwriteComments, BOOL &optionAudioOnDone, SegSelect::segments **segs); + MainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionAudioOnDone, SegSelect::segments **segs); private: SegSelect::segments **segs; @@ -21,4 +21,4 @@ private slots: }; // Do main dialog, return TRUE if canceled -BOOL doMainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionOverwriteComments, BOOL &optionAudioOnDone, SegSelect::segments **segs); +BOOL doMainDialog(BOOL &optionPlaceStructs, BOOL &optionProcessStatic, BOOL &optionAudioOnDone, SegSelect::segments **segs); diff --git a/Plugin/RTTI.cpp b/Plugin/RTTI.cpp index 15b2617..2ca2282 100644 --- a/Plugin/RTTI.cpp +++ b/Plugin/RTTI.cpp @@ -5,7 +5,7 @@ // // **************************************************************************** #include "stdafx.h" -#include "Core.h" +#include "Main.h" #include "RTTI.h" #include "Vftable.h" @@ -25,7 +25,7 @@ static LPCSTR FORMAT_RTTI_COL = "??_R4%s6B@"; static LPCSTR FORMAT_RTTI_COL_PREFIX = "??_R4"; // Skip type_info tag for class/struct mangled name strings -#define SKIP_TD_TAG(_str) (_str + SIZESTR(".?Ax")) +#define SKIP_TD_TAG(_str) ((_str) + SIZESTR(".?Ax")) // Class name list container struct bcdInfo @@ -57,7 +57,7 @@ void RTTI::freeWorkingData() } // Mangle number for labeling -static LPSTR mangleNumber(UINT number, __out_bcount(16) LPSTR buffer) +static LPSTR mangleNumber(UINT number, __out_bcount(64) LPSTR buffer) { // // 0 = A@ @@ -66,41 +66,40 @@ static LPSTR mangleNumber(UINT number, __out_bcount(16) LPSTR buffer) // 0x0..0xF = 'A'..'P' // Can only get unsigned inputs - int iNumber = *((PINT) &number); - - if(iNumber == 0) + int num = *((PINT) &number); + if(num == 0) return("A@"); else { int sign = 0; - if(iNumber < 0) + if(num < 0) { sign = 1; - iNumber = -iNumber; + num = -num; } - if(iNumber <= 10) + if(num <= 10) { - _snprintf(buffer, 16, "%s%d", (sign ? "?" : ""), (iNumber - 1)); + _snprintf_s(buffer, 64, (64 - 1), "%s%d", (sign ? "?" : ""), (num - 1)); return(buffer); } else { - // How many digits max? - char buffer2[512] = {0}; - int iCount = sizeof(buffer2); + // Count digits + char buffer2[64] = {0}; + int count = sizeof(buffer2); - while((iNumber > 0) && (iCount > 0)) + while((num > 0) && (count > 0)) { - buffer2[sizeof(buffer2) - iCount] = ('A' + (iNumber % 16)); - iNumber = (iNumber / 16); - iCount--; + buffer2[sizeof(buffer2) - count] = ('A' + (num % 16)); + num = (num / 16); + count--; }; - if(iCount == 0) - msg(" *** mangleNumber() Overflow! ***"); + if(count == 0) + msg(" *** mangleNumber() overflow! ***"); - _snprintf(buffer, 16, "%s%s@", (sign ? "?" : ""), buffer2); + _snprintf_s(buffer, 64, (64-1), "%s%s@", (sign ? "?" : ""), buffer2); return(buffer); } } @@ -124,48 +123,6 @@ static LPCSTR attributeLabel(UINT attributes) } -/* -from http://moritzraabe.de/2017/03/28/name-identifiers-and-comment-encoding-in-ida-pro/ -- The string cannot be a register name used in the program (but you can use the name rax in a 32-bit program, for example) -- The first character cannot be a digit -- The name can only consist of the characters from the following character sets: -- NameChars: _$?@0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy -- MangleChars: ()[]:% -*/ -static void sanitizeIdaName(__out LPSTR out, __in LPCSTR name, int n) -{ - static const char* allowedChars = "_$?@0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy()[]:%"; - - if (strlen(name) <= 3 /* might be a reserved register, just dodgy overall if it's this short */ || isdigit(*out)) { - *out++ = '_'; // append a _ - n--; - } - strncpy(out, name, n); - for (; *out; out++) - if (!strchr(allowedChars, *out)) - *out = '_'; -} - -// Attempt to serialize a managed name until it succeeds -static BOOL serializeName(ea_t ea, __in LPCSTR name) -{ - char sanitized[MAXSTR]; sanitized[SIZESTR(sanitized)] = 0; - sanitizeIdaName(sanitized, name, SIZESTR(sanitized)); - if (strlen(sanitized) > MAXSTR - 10) - sanitized[MAXSTR - 10] = '\0'; - // remember to check length - for (int i = 0; i < 1000000; i++) - { - char buffer[MAXSTR]; buffer[SIZESTR(buffer)] = 0; - _snprintf(buffer, SIZESTR(buffer), "%s_%d", sanitized, i); - if (set_name(ea, buffer, (SN_NON_AUTO | SN_NOWARN))) { - return(TRUE); - } - } - return(FALSE); -} - - // Add RTTI definitions to IDA // Structure type IDs static tid_t s_type_info_ID = 1; @@ -175,7 +132,7 @@ static tid_t s_BaseClassDescriptor_ID = 4; static tid_t s_CompleteObjectLocator_ID = 5; // Create structure definition w/comment -static struc_t *AddStruct(__out tid_t &id, __in LPCSTR name, LPCSTR comment) +static struc_t *addStruct(__out tid_t &id, __in LPCSTR name, LPCSTR comment) { struc_t *structPtr = NULL; @@ -190,12 +147,12 @@ static struc_t *AddStruct(__out tid_t &id, __in LPCSTR name, LPCSTR comment) { // Clear the old one out if it exists and set the comment int dd = del_struc_members(structPtr, 0, MAXADDR); - dd = dd; + dd = dd; // for debugging bool rr = set_struc_cmt(id, comment, true); rr = rr; } else - msg("** AddStruct(\"%s\") failed!\n", name); + msg("** addStruct(\"%s\") failed!\n", name); return(structPtr); } @@ -203,48 +160,55 @@ static struc_t *AddStruct(__out tid_t &id, __in LPCSTR name, LPCSTR comment) void RTTI::addDefinitionsToIda() { // Member type info for 32bit offset types - opinfo_t mtoff; - ZeroMemory(&mtoff, sizeof(refinfo_t)); - #ifndef __EA64__ - mtoff.ri.flags = REF_OFF32; - #define EAOFFSET (off_flag() | dword_flag()) - #else - mtoff.ri.flags = REF_OFF64; - #define EAOFFSET (off_flag() | qword_flag()) - #endif + opinfo_t mtoff; + ZeroMemory(&mtoff, sizeof(refinfo_t)); + #ifndef __EA64__ + mtoff.ri.flags = REF_OFF32; + #define EAOFFSET (off_flag() | dword_flag()) + #else + mtoff.ri.flags = REF_OFF64; + #define EAOFFSET (off_flag() | qword_flag()) + #endif mtoff.ri.target = BADADDR; - - // Add structure member - #define ADD_MEMBER(_flags, _mtoff, TYPE, _member)\ - {\ - TYPE _type;\ - (void)_type;\ - if(add_struc_member(structPtr, #_member, (ea_t)offsetof(TYPE, _member), (_flags), _mtoff, (asize_t)sizeof(_type._member)) != 0)\ - msg(" ** ADD_MEMBER(): %s failed! %d, %d **\n", #_member, offsetof(TYPE, _member), sizeof(_type._member));\ + struc_t *structPtr; + + // Add structure member + #define ADD_MEMBER(_flags, _mtoff, TYPE, _member) \ + { \ + TYPE _type; \ + (void)_type; \ + if(add_struc_member(structPtr, #_member, (ea_t )offsetof(TYPE, _member), (_flags), _mtoff, (asize_t) sizeof(_type._member)) != 0) \ + msg(" ** ADD_MEMBER(): %s failed! %d, %d **\n", #_member, offsetof(TYPE, _member), sizeof(_type._member)); \ } - struc_t *structPtr; - if (structPtr = AddStruct(s_type_info_ID, "type_info", "RTTI std::type_info class (#classinformer)")) - { - ADD_MEMBER(EAOFFSET, &mtoff, RTTI::type_info, vfptr); - ADD_MEMBER(dword_flag(), NULL, RTTI::type_info, _M_data); + // IDA 7 has a definition for this now + s_type_info_ID = get_struc_id("TypeDescriptor"); + if (s_type_info_ID == BADADDR) + { + msg("** Failed to load the IDA TypeDescriptor type, generating one **\n"); - // Name string zero size - opinfo_t mt; - ZeroMemory(&mt, sizeof(refinfo_t)); - if(addStrucMember(structPtr, "_M_d_name", offsetof(RTTI::type_info, _M_d_name), strlit_flag(), &mt, 0) != 0) - msg("** addDefinitionsToIda(): _M_d_name failed! \n"); - } + if (structPtr = addStruct(s_type_info_ID, "type_info", "RTTI std::type_info class (#classinformer)")) + { + ADD_MEMBER(EAOFFSET, &mtoff, RTTI::type_info, vfptr); + ADD_MEMBER(dword_flag(), NULL, RTTI::type_info, _M_data); + + // Name string zero size + opinfo_t mt; + ZeroMemory(&mt, sizeof(refinfo_t)); + if (addStrucMember(structPtr, "_M_d_name", offsetof(RTTI::type_info, _M_d_name), strlit_flag(), &mt, 0) != 0) + msg("** addDefinitionsToIda(): _M_d_name failed! \n"); + } + } // Must come before the following "_RTTIBaseClassDescriptor" - if (structPtr = AddStruct(s_PMD_ID, "_PMD", "RTTI Base class descriptor displacement container (#classinformer)")) + if (structPtr = addStruct(s_PMD_ID, "_PMD", "RTTI Base class descriptor displacement container (#classinformer)")) { ADD_MEMBER(dword_flag(), NULL, RTTI::PMD, mdisp); ADD_MEMBER(dword_flag(), NULL, RTTI::PMD, pdisp); ADD_MEMBER(dword_flag(), NULL, RTTI::PMD, vdisp); } - if (structPtr = AddStruct(s_ClassHierarchyDescriptor_ID, "_RTTIClassHierarchyDescriptor", "RTTI Class Hierarchy Descriptor (#classinformer)")) + if (structPtr = addStruct(s_ClassHierarchyDescriptor_ID, "_RTTIClassHierarchyDescriptor", "RTTI Class Hierarchy Descriptor (#classinformer)")) { ADD_MEMBER(dword_flag(), NULL, RTTI::_RTTIClassHierarchyDescriptor, signature); ADD_MEMBER(dword_flag(), NULL, RTTI::_RTTIClassHierarchyDescriptor, attributes); @@ -256,7 +220,7 @@ void RTTI::addDefinitionsToIda() #endif } - if (structPtr = AddStruct(s_BaseClassDescriptor_ID, "_RTTIBaseClassDescriptor", "RTTI Base Class Descriptor (#classinformer)")) + if (structPtr = addStruct(s_BaseClassDescriptor_ID, "_RTTIBaseClassDescriptor", "RTTI Base Class Descriptor (#classinformer)")) { #ifndef __EA64__ ADD_MEMBER(EAOFFSET, &mtoff, RTTI::_RTTIBaseClassDescriptor, typeDescriptor); @@ -271,7 +235,7 @@ void RTTI::addDefinitionsToIda() ADD_MEMBER(dword_flag(), NULL, RTTI::_RTTIBaseClassDescriptor, attributes); } - if(structPtr = AddStruct(s_CompleteObjectLocator_ID, "_RTTICompleteObjectLocator", "RTTI Complete Object Locator (#classinformer)")) + if(structPtr = addStruct(s_CompleteObjectLocator_ID, "_RTTICompleteObjectLocator", "RTTI Complete Object Locator (#classinformer)")) { ADD_MEMBER(dword_flag(), NULL, RTTI::_RTTICompleteObjectLocator, signature); ADD_MEMBER(dword_flag(), NULL, RTTI::_RTTICompleteObjectLocator, offset); @@ -289,9 +253,11 @@ void RTTI::addDefinitionsToIda() #undef ADD_MEMBER } -// Version 1.05, manually set fields and then try "create_struct()" +// Version 1.05, manually set fields and then try to place the struct // If it fails at least the fields should be set -static void create_structRTTI(ea_t ea, tid_t tid, __in_opt LPSTR typeName = NULL, BOOL bHasChd = FALSE) +// 2.5: IDA 7 now has RTTI support; only place structs if don't exist at address +// Returns TRUE if structure was placed, else it was already set +static BOOL tryStructRTTI(ea_t ea, tid_t tid, __in_opt LPSTR typeName = NULL, BOOL bHasChd = FALSE) { #define putDword(ea) create_dword(ea, sizeof(DWORD)) #ifndef __EA64__ @@ -302,155 +268,194 @@ static void create_structRTTI(ea_t ea, tid_t tid, __in_opt LPSTR typeName = NULL if(tid == s_type_info_ID) { - _ASSERT(typeName != NULL); - UINT nameLen = (strlen(typeName) + 1); - UINT structSize = (offsetof(RTTI::type_info, _M_d_name) + nameLen); - - // Place struct - setUnknown(ea, structSize); - BOOL result = FALSE; - if (optionPlaceStructs) - result = create_struct(ea, structSize, s_type_info_ID); - if (!result) - { - putEa(ea + offsetof(RTTI::type_info, vfptr)); - putEa(ea + offsetof(RTTI::type_info, _M_data)); - create_strlit((ea + offsetof(RTTI::type_info, _M_d_name)), nameLen, STRTYPE_C); - } + if (!hasName(ea)) + { + _ASSERT(typeName != NULL); + UINT nameLen = (UINT)(strlen(typeName) + 1); + UINT structSize = (offsetof(RTTI::type_info, _M_d_name) + nameLen); + + // Place struct + setUnknown(ea, structSize); + BOOL result = FALSE; + if (optionPlaceStructs) + result = create_struct(ea, structSize, s_type_info_ID); + if (!result) + { + putEa(ea + offsetof(RTTI::type_info, vfptr)); + putEa(ea + offsetof(RTTI::type_info, _M_data)); - // sh!ft: End should be aligned - ea_t end = (ea + offsetof(RTTI::type_info, _M_d_name) + nameLen); - if (end % 4) - create_align(end, (4 - (end % 4)), 0); + create_strlit((ea + offsetof(RTTI::type_info, _M_d_name)), nameLen, STRTYPE_C); + } + + // sh!ft: End should be aligned + ea_t end = (ea + offsetof(RTTI::type_info, _M_d_name) + nameLen); + if (end % 4) + create_align(end, (4 - (end % 4)), 0); + + return TRUE; + } } else - if (tid == s_ClassHierarchyDescriptor_ID) - { - setUnknown(ea, sizeof(RTTI::_RTTIClassHierarchyDescriptor)); - BOOL result = FALSE; - if (optionPlaceStructs) - result = create_struct(ea, sizeof(RTTI::_RTTIClassHierarchyDescriptor), s_ClassHierarchyDescriptor_ID); - if (!result) - { - putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, signature)); - putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, attributes)); - putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, numBaseClasses)); - #ifndef __EA64__ - putEa(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, baseClassArray)); - #else - putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, baseClassArray)); - #endif - } - } - else - if (tid == s_BaseClassDescriptor_ID) - { - setUnknown(ea, sizeof(RTTI::_RTTIBaseClassDescriptor)); - create_structRTTI(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, pmd), s_PMD_ID); - BOOL result = FALSE; - if (optionPlaceStructs) - result = create_struct(ea, sizeof(RTTI::_RTTIBaseClassDescriptor), s_BaseClassDescriptor_ID); - if (!result) - { - #ifndef __EA64__ - putEa(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, typeDescriptor)); - #else - putDword(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, typeDescriptor)); - #endif + if (tid == s_ClassHierarchyDescriptor_ID) + { + if (!hasName(ea)) + { + setUnknown(ea, sizeof(RTTI::_RTTIClassHierarchyDescriptor)); + BOOL result = FALSE; + if (optionPlaceStructs) + result = create_struct(ea, sizeof(RTTI::_RTTIClassHierarchyDescriptor), s_ClassHierarchyDescriptor_ID); + if (!result) + { + putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, signature)); + putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, attributes)); + putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, numBaseClasses)); + #ifndef __EA64__ + putEa(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, baseClassArray)); + #else + putDword(ea + offsetof(RTTI::_RTTIClassHierarchyDescriptor, baseClassArray)); + #endif + } - putDword(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, numContainedBases)); - putDword(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, attributes)); - if (bHasChd) - { - //_RTTIClassHierarchyDescriptor *classDescriptor; *X64 int32 offset - #ifndef __EA64__ - putEa(ea + (offsetof(RTTI::_RTTIBaseClassDescriptor, attributes) + sizeof(UINT))); - #else - putDword(ea + (offsetof(RTTI::_RTTIBaseClassDescriptor, attributes) + sizeof(UINT))); - #endif - } - } - } - else + return TRUE; + } + } + else if(tid == s_PMD_ID) { - setUnknown(ea, sizeof(RTTI::PMD)); - BOOL result = FALSE; - if (optionPlaceStructs) - result = create_struct(ea, sizeof(RTTI::PMD), s_PMD_ID); - if (!result) - { - putDword(ea + offsetof(RTTI::PMD, mdisp)); - putDword(ea + offsetof(RTTI::PMD, pdisp)); - putDword(ea + offsetof(RTTI::PMD, vdisp)); - } + if (!hasName(ea)) + { + setUnknown(ea, sizeof(RTTI::PMD)); + BOOL result = FALSE; + if (optionPlaceStructs) + result = create_struct(ea, sizeof(RTTI::PMD), s_PMD_ID); + if (!result) + { + putDword(ea + offsetof(RTTI::PMD, mdisp)); + putDword(ea + offsetof(RTTI::PMD, pdisp)); + putDword(ea + offsetof(RTTI::PMD, vdisp)); + } + + return TRUE; + } } - else + else if(tid == s_CompleteObjectLocator_ID) { - setUnknown(ea, sizeof(RTTI::_RTTICompleteObjectLocator)); - BOOL result = FALSE; - if (optionPlaceStructs) - result = create_struct(ea, sizeof(RTTI::_RTTICompleteObjectLocator), s_CompleteObjectLocator_ID); - if (!result) - { - putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, signature)); - putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, offset)); - putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, cdOffset)); + if (!hasName(ea)) + { + setUnknown(ea, sizeof(RTTI::_RTTICompleteObjectLocator)); + BOOL result = FALSE; + if (optionPlaceStructs) + result = create_struct(ea, sizeof(RTTI::_RTTICompleteObjectLocator), s_CompleteObjectLocator_ID); + if (!result) + { + putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, signature)); + putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, offset)); + putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, cdOffset)); + + #ifndef __EA64__ + putEa(ea + offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); + putEa(ea + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)); + #else + putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); + putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)); + putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, objectBase)); + #endif + } - #ifndef __EA64__ - putEa(ea + offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); - putEa(ea + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)); - #else - putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); - putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)); - putDword(ea + offsetof(RTTI::_RTTICompleteObjectLocator, objectBase)); - #endif - } + return TRUE; + } + } + else + if (tid == s_BaseClassDescriptor_ID) + { + // Recursive + tryStructRTTI(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, pmd), s_PMD_ID); + + if (!hasName(ea)) + { + setUnknown(ea, sizeof(RTTI::_RTTIBaseClassDescriptor)); + + BOOL result = FALSE; + if (optionPlaceStructs) + result = create_struct(ea, sizeof(RTTI::_RTTIBaseClassDescriptor), s_BaseClassDescriptor_ID); + if (!result) + { + #ifndef __EA64__ + putEa(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, typeDescriptor)); + #else + putDword(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, typeDescriptor)); + #endif + + putDword(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, numContainedBases)); + putDword(ea + offsetof(RTTI::_RTTIBaseClassDescriptor, attributes)); + if (bHasChd) + { + //_RTTIClassHierarchyDescriptor *classDescriptor; *X64 int32 offset + #ifndef __EA64__ + putEa(ea + (offsetof(RTTI::_RTTIBaseClassDescriptor, attributes) + sizeof(UINT))); + #else + putDword(ea + (offsetof(RTTI::_RTTIBaseClassDescriptor, attributes) + sizeof(UINT))); + #endif + } + } + + return TRUE; + } } else { _ASSERT(FALSE); } + + return FALSE; } // Read ASCII string from IDB at address -static int readIdaString(ea_t ea, __out LPSTR buffer, int bufferSize) +static int getIdaString(ea_t ea, __out LPSTR buffer, int bufferSize) { + buffer[0] = 0; + // Return cached name if it exists stringMap::iterator it = stringCache.find(ea); if (it != stringCache.end()) { LPCSTR str = it->second.c_str(); - int len = strlen(str); - if (len > bufferSize) len = bufferSize; - strncpy(buffer, str, len); buffer[len] = 0; - return(len); + int len = (int) strlen(str); + if (len > bufferSize) + len = bufferSize; + strncpy_s(buffer, MAXSTR, str, len); + return len; } else { // Read string at ea if it exists - int len = get_max_strlit_length(ea, STRTYPE_C, ALOPT_IGNHEADS); + int len = (int) get_max_strlit_length(ea, STRTYPE_C, ALOPT_IGNHEADS); if (len > 0) { - if (len > bufferSize) len = bufferSize; - size_t bufferSizeCpy = bufferSize; - qstring qsBuf = qstring(); // hmmm, convert the entire plugin to qstring or just make an awful hack??? :thinking: - if (get_strlit_contents(&qsBuf, ea, len, STRTYPE_C, &bufferSizeCpy)) + // Length includes terminator + if (len > bufferSize) + len = bufferSize; + + qstring str; + int len2 = get_strlit_contents(&str, ea, len, STRTYPE_C); + if (len2 > 0) { - for (int i = 0; i < len; i++) // THIS IS PROBABLY A BAD IDEA - { - buffer[i] = qsBuf.at(i); - } + // Length with out terminator + if (len2 > bufferSize) + len2 = bufferSize; + // Cache it - buffer[len - 1] = 0; + memcpy(buffer, str.c_str(), len2); + buffer[len2] = 0; stringCache[ea] = buffer; } else len = 0; } - return(len); + + return len ; } } @@ -461,7 +466,7 @@ static int readIdaString(ea_t ea, __out LPSTR buffer, int bufferSize) // type_info assumed to be valid int RTTI::type_info::getName(ea_t typeInfo, __out LPSTR buffer, int bufferSize) { - return(readIdaString(typeInfo + offsetof(type_info, _M_d_name), buffer, bufferSize)); + return(getIdaString(typeInfo + offsetof(type_info, _M_d_name), buffer, bufferSize)); } // A valid type_info/TypeDescriptor at pointer? @@ -497,11 +502,11 @@ BOOL RTTI::type_info::isTypeName(ea_t name) if (get_byte(name) == '.') { // Read the rest of the possible name string - char buffer[MAXSTR]; buffer[0] = buffer[SIZESTR(buffer)] = 0; - if (readIdaString(name, buffer, SIZESTR(buffer))) + char buffer[MAXSTR]; + if (getIdaString(name, buffer, SIZESTR(buffer))) { // Should be valid if it properly demangles - if (LPSTR s = __unDName(NULL, buffer+1 /*skip the '.'*/, 0, (_Alloc)malloc, free, (UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY))) + if (LPSTR s = __unDName(NULL, buffer+1 /*skip the '.'*/, 0, mallocWrap, free, (UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY))) { free(s); return(TRUE); @@ -512,33 +517,34 @@ BOOL RTTI::type_info::isTypeName(ea_t name) } // Put struct and place name at address -void RTTI::type_info::doStruct(ea_t typeInfo) +void RTTI::type_info::tryStruct(ea_t typeInfo) { - // Only place once per address - if (tdSet.find(typeInfo) != tdSet.end()) - return; - else - tdSet.insert(typeInfo); + // Only place once per address + if (tdSet.find(typeInfo) != tdSet.end()) + return; + else + tdSet.insert(typeInfo); // Get type name - char name[MAXSTR]; name[0] = name[SIZESTR(name)] = 0; - int nameLen = getName(typeInfo, name, SIZESTR(name)); + char name[MAXSTR]; + int nameLen = getName(typeInfo, name, SIZESTR(name)); - create_structRTTI(typeInfo, s_type_info_ID, name); - if (nameLen > 0) - { - if (!hasUniqueName(typeInfo)) - { - // Set decorated name/label - char name2[MAXSTR]; name2[SIZESTR(name2)] = 0; - _snprintf(name2, SIZESTR(name2), FORMAT_RTTI_TYPE, name + 2); - set_name(typeInfo, name2, (SN_NON_AUTO | SN_NOWARN | SN_NOCHECK)); - } - } - #ifdef _DEVMODE - else - _ASSERT(FALSE); - #endif + tryStructRTTI(typeInfo, s_type_info_ID, name); + + if (nameLen > 0) + { + if (!hasName(typeInfo)) + { + // Set decorated name/label + char name2[MAXSTR]; + _snprintf_s(name2, sizeof(name2), SIZESTR(name2), FORMAT_RTTI_TYPE, (name + 2)); + setName(typeInfo, name2); + } + } + else + { + _ASSERT(FALSE); + } } @@ -551,7 +557,7 @@ BOOL RTTI::_RTTICompleteObjectLocator::isValid(ea_t col) { // Check signature UINT signature = -1; - if (getVerify32_t((col + offsetof(_RTTICompleteObjectLocator, signature)), signature)) + if (getVerify32((col + offsetof(_RTTICompleteObjectLocator, signature)), signature)) { #ifndef __EA64__ if (signature == 0) @@ -610,7 +616,7 @@ BOOL RTTI::_RTTICompleteObjectLocator::isValid2(ea_t col) { // 'signature' should be zero UINT signature = -1; - if (getVerify32_t((col + offsetof(_RTTICompleteObjectLocator, signature)), signature)) + if (getVerify32((col + offsetof(_RTTICompleteObjectLocator, signature)), signature)) { if (signature == 0) { @@ -625,38 +631,62 @@ BOOL RTTI::_RTTICompleteObjectLocator::isValid2(ea_t col) } #endif -// Place full COL hierarchy structures -void RTTI::_RTTICompleteObjectLocator::doStruct(ea_t col) +// Place full COL hierarchy structures if they don't already exist +BOOL RTTI::_RTTICompleteObjectLocator::tryStruct(ea_t col) { - create_structRTTI(col, s_CompleteObjectLocator_ID); - - #ifndef __EA64__ - // Put type_def - ea_t typeInfo = getEa(col + offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); - RTTI::type_info::doStruct(typeInfo); - - // Place CHD hierarchy - ea_t classDescriptor = getEa(col + offsetof(_RTTICompleteObjectLocator, classDescriptor)); - RTTI::_RTTIClassHierarchyDescriptor::doStruct(classDescriptor); - #else - UINT tdOffset = get_32bit(col + offsetof(_RTTICompleteObjectLocator, typeDescriptor)); - UINT cdOffset = get_32bit(col + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)); - UINT objectLocator = get_32bit(col + offsetof(RTTI::_RTTICompleteObjectLocator, objectBase)); - ea_t colBase = (col - (UINT64)objectLocator); + // If it doesn't have a name, IDA's analyzer missed it + if (!hasName(col)) + { + #if 0 + qstring buf; + idaFlags2String(get_flags(col), buf); + msg(EAFORMAT " fix COL (%s)\n", col, buf.c_str()); + #endif + + tryStructRTTI(col, s_CompleteObjectLocator_ID); + + #ifndef __EA64__ + // Put type_def + ea_t typeInfo = getEa(col + offsetof(_RTTICompleteObjectLocator, typeDescriptor)); + type_info::tryStruct(typeInfo); + + // Place CHD hierarchy + ea_t classDescriptor = getEa(col + offsetof(_RTTICompleteObjectLocator, classDescriptor)); + _RTTIClassHierarchyDescriptor::tryStruct(classDescriptor); + #else + UINT tdOffset = get_32bit(col + offsetof(_RTTICompleteObjectLocator, typeDescriptor)); + UINT cdOffset = get_32bit(col + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)); + UINT objectLocator = get_32bit(col + offsetof(RTTI::_RTTICompleteObjectLocator, objectBase)); + ea_t colBase = (col - (UINT64)objectLocator); + + ea_t typeInfo = (colBase + (UINT64)tdOffset); + type_info::tryStruct(typeInfo); + + ea_t classDescriptor = (colBase + (UINT64)cdOffset); + _RTTIClassHierarchyDescriptor::tryStruct(classDescriptor, colBase); + + // Set absolute address comments + ea_t ea = (col + offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)); + if (!hasComment(ea)) + { + char buffer[64]; + sprintf_s(buffer, sizeof(buffer), "0x" EAFORMAT, typeInfo); + setComment(ea, buffer, TRUE); + } - ea_t typeInfo = (colBase + (UINT64)tdOffset); - type_info::doStruct(typeInfo); + ea = (col + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)); + if (!hasComment(ea)) + { + char buffer[64]; + sprintf_s(buffer, sizeof(buffer), "0x" EAFORMAT, classDescriptor); + setComment(ea, buffer, TRUE); + } + #endif - ea_t classDescriptor = (colBase + (UINT64)cdOffset); - _RTTIClassHierarchyDescriptor::doStruct(classDescriptor, colBase); + return TRUE; + } - // Set absolute address comments - char buffer[64]; - sprintf(buffer, "0x" EAFORMAT, typeInfo); - set_cmt((col + offsetof(RTTI::_RTTICompleteObjectLocator, typeDescriptor)), buffer, TRUE); - sprintf(buffer, "0x" EAFORMAT, classDescriptor); - set_cmt((col + offsetof(RTTI::_RTTICompleteObjectLocator, classDescriptor)), buffer, TRUE); - #endif + return FALSE; } @@ -673,7 +703,7 @@ BOOL RTTI::_RTTIBaseClassDescriptor::isValid(ea_t bcd, ea_t colBase64) { // Check attributes flags first UINT attributes = -1; - if (getVerify32_t((bcd + offsetof(_RTTIBaseClassDescriptor, attributes)), attributes)) + if (getVerify32((bcd + offsetof(_RTTIBaseClassDescriptor, attributes)), attributes)) { // Valid flags are the lower byte only if ((attributes & 0xFFFFFF00) == 0) @@ -694,22 +724,22 @@ BOOL RTTI::_RTTIBaseClassDescriptor::isValid(ea_t bcd, ea_t colBase64) } // Put BCD structure at address -void RTTI::_RTTIBaseClassDescriptor::doStruct(ea_t bcd, __out_bcount(MAXSTR) LPSTR baseClassName, ea_t colBase64) +void RTTI::_RTTIBaseClassDescriptor::tryStruct(ea_t bcd, __out_bcount(MAXSTR) LPSTR baseClassName, ea_t colBase64) { // Only place it once if (bcdSet.find(bcd) != bcdSet.end()) { // Seen already, just return type name #ifndef __EA64__ - ea_t typeInfo = getEa(bcd + offsetof(RTTI::_RTTIBaseClassDescriptor, typeDescriptor)); + ea_t typeInfo = getEa(bcd + offsetof(_RTTIBaseClassDescriptor, typeDescriptor)); #else UINT tdOffset = get_32bit(bcd + offsetof(_RTTIBaseClassDescriptor, typeDescriptor)); ea_t typeInfo = (colBase64 + (UINT64) tdOffset); #endif - char buffer[MAXSTR]; buffer[0] = buffer[SIZESTR(buffer)] = 0; - RTTI::type_info::getName(typeInfo, buffer, SIZESTR(buffer)); - strcpy(baseClassName, SKIP_TD_TAG(buffer)); + char buffer[MAXSTR]; + type_info::getName(typeInfo, buffer, SIZESTR(buffer)); + strcpy_s(baseClassName, sizeof(buffer), SKIP_TD_TAG(buffer)); return; } else @@ -717,14 +747,14 @@ void RTTI::_RTTIBaseClassDescriptor::doStruct(ea_t bcd, __out_bcount(MAXSTR) LPS if (is_loaded(bcd)) { - UINT attributes = get_32bit(bcd + offsetof(RTTI::_RTTIBaseClassDescriptor, attributes)); - create_structRTTI(bcd, s_BaseClassDescriptor_ID, NULL, ((attributes & RTTI::BCD_HASPCHD) > 0)); + UINT attributes = get_32bit(bcd + offsetof(_RTTIBaseClassDescriptor, attributes)); + tryStructRTTI(bcd, s_BaseClassDescriptor_ID, NULL, ((attributes & BCD_HASPCHD) > 0)); // Has appended CHD? - if (attributes & RTTI::BCD_HASPCHD) + if (attributes & BCD_HASPCHD) { // yes, process it - ea_t chdOffset = (bcd + (offsetof(RTTI::_RTTIBaseClassDescriptor, attributes) + sizeof(UINT))); + ea_t chdOffset = (bcd + (offsetof(_RTTIBaseClassDescriptor, attributes) + sizeof(UINT))); #ifndef __EA64__ fixEa(chdOffset); @@ -734,13 +764,16 @@ void RTTI::_RTTIBaseClassDescriptor::doStruct(ea_t bcd, __out_bcount(MAXSTR) LPS UINT chdOffset32 = get_32bit(chdOffset); ea_t chd = (colBase64 + (UINT64) chdOffset32); - char buffer[64]; - sprintf(buffer, "0x" EAFORMAT, chd); - set_cmt(chdOffset, buffer, TRUE); + if (!hasComment(chdOffset)) + { + char buffer[64]; + sprintf_s(buffer, sizeof(buffer), "0x" EAFORMAT, chd); + setComment(chdOffset, buffer, TRUE); + } #endif if (is_loaded(chd)) - RTTI::_RTTIClassHierarchyDescriptor::doStruct(chd, colBase64); + _RTTIClassHierarchyDescriptor::tryStruct(chd, colBase64); else _ASSERT(FALSE); } @@ -752,17 +785,18 @@ void RTTI::_RTTIBaseClassDescriptor::doStruct(ea_t bcd, __out_bcount(MAXSTR) LPS UINT tdOffset = get_32bit(bcd + offsetof(_RTTIBaseClassDescriptor, typeDescriptor)); ea_t typeInfo = (colBase64 + (UINT64)tdOffset); #endif - RTTI::type_info::doStruct(typeInfo); + type_info::tryStruct(typeInfo); // Get raw type/class name - char buffer[MAXSTR]; buffer[0] = buffer[SIZESTR(buffer)] = 0; - RTTI::type_info::getName(typeInfo, buffer, SIZESTR(buffer)); - strcpy(baseClassName, SKIP_TD_TAG(buffer)); + char buffer[MAXSTR]; + type_info::getName(typeInfo, buffer, SIZESTR(buffer)); + strcpy_s(baseClassName, sizeof(buffer), SKIP_TD_TAG(buffer)); if (!optionPlaceStructs && attributes) { // Place attributes comment - if (!has_cmt(get_full_flags(bcd + offsetof(_RTTIBaseClassDescriptor, attributes)))) + ea_t ea = (bcd + offsetof(_RTTIBaseClassDescriptor, attributes)); + if (!hasComment(ea)) { qstring s(""); BOOL b = 0; @@ -775,25 +809,24 @@ void RTTI::_RTTIBaseClassDescriptor::doStruct(ea_t bcd, __out_bcount(MAXSTR) LPS ATRIBFLAG(BCD_NONPOLYMORPHIC); ATRIBFLAG(BCD_HASPCHD); #undef ATRIBFLAG - set_cmt((bcd + offsetof(_RTTIBaseClassDescriptor, attributes)), s.c_str(), TRUE); + setComment(ea, s.c_str(), TRUE); } } // Give it a label - if (!hasUniqueName(bcd)) + if (!hasName(bcd)) { // Name::`RTTI Base Class Descriptor at (0, -1, 0, 0)' ZeroMemory(buffer, sizeof(buffer)); - char buffer1[32] = { 0 }, buffer2[32] = { 0 }, buffer3[32] = { 0 }, buffer4[32] = { 0 }; - _snprintf(buffer, SIZESTR(buffer), FORMAT_RTTI_BCD, + char buffer1[64] = { 0 }, buffer2[64] = { 0 }, buffer3[64] = { 0 }, buffer4[64] = { 0 }; + _snprintf_s(buffer, sizeof(buffer), SIZESTR(buffer), FORMAT_RTTI_BCD, mangleNumber(get_32bit(bcd + (offsetof(_RTTIBaseClassDescriptor, pmd) + offsetof(PMD, mdisp))), buffer1), mangleNumber(get_32bit(bcd + (offsetof(_RTTIBaseClassDescriptor, pmd) + offsetof(PMD, pdisp))), buffer2), mangleNumber(get_32bit(bcd + (offsetof(_RTTIBaseClassDescriptor, pmd) + offsetof(PMD, vdisp))), buffer3), mangleNumber(attributes, buffer4), baseClassName); - if (!set_name(bcd, buffer, (SN_NON_AUTO | SN_NOWARN))) - serializeName(bcd, buffer); + setName(bcd, buffer); } } else @@ -814,20 +847,20 @@ BOOL RTTI::_RTTIClassHierarchyDescriptor::isValid(ea_t chd, ea_t colBase64) { // signature should be zero statically UINT signature = -1; - if (getVerify32_t((chd + offsetof(_RTTIClassHierarchyDescriptor, signature)), signature)) + if (getVerify32((chd + offsetof(_RTTIClassHierarchyDescriptor, signature)), signature)) { if (signature == 0) { // Check attributes flags UINT attributes = -1; - if (getVerify32_t((chd + offsetof(_RTTIClassHierarchyDescriptor, attributes)), attributes)) + if (getVerify32((chd + offsetof(_RTTIClassHierarchyDescriptor, attributes)), attributes)) { // Valid flags are the lower nibble only if ((attributes & 0xFFFFFFF0) == 0) { // Should have at least one base class UINT numBaseClasses = 0; - if (getVerify32_t((chd + offsetof(_RTTIClassHierarchyDescriptor, numBaseClasses)), numBaseClasses)) + if (getVerify32((chd + offsetof(_RTTIClassHierarchyDescriptor, numBaseClasses)), numBaseClasses)) { if (numBaseClasses >= 1) { @@ -862,7 +895,7 @@ BOOL RTTI::_RTTIClassHierarchyDescriptor::isValid(ea_t chd, ea_t colBase64) // Put CHD structure at address -void RTTI::_RTTIClassHierarchyDescriptor::doStruct(ea_t chd, ea_t colBase64) +void RTTI::_RTTIClassHierarchyDescriptor::tryStruct(ea_t chd, ea_t colBase64) { // Only place it once per address if (chdSet.find(chd) != chdSet.end()) @@ -873,13 +906,14 @@ void RTTI::_RTTIClassHierarchyDescriptor::doStruct(ea_t chd, ea_t colBase64) if (is_loaded(chd)) { // Place CHD - create_structRTTI(chd, s_ClassHierarchyDescriptor_ID); + tryStructRTTI(chd, s_ClassHierarchyDescriptor_ID); // Place attributes comment UINT attributes = get_32bit(chd + offsetof(_RTTIClassHierarchyDescriptor, attributes)); if (!optionPlaceStructs && attributes) { - if (!has_cmt(get_full_flags(chd + offsetof(_RTTIClassHierarchyDescriptor, attributes)))) + ea_t ea = (chd + offsetof(_RTTIClassHierarchyDescriptor, attributes)); + if (!hasComment(ea)) { qstring s(""); BOOL b = 0; @@ -888,13 +922,13 @@ void RTTI::_RTTIClassHierarchyDescriptor::doStruct(ea_t chd, ea_t colBase64) ATRIBFLAG(CHD_VIRTINH); ATRIBFLAG(CHD_AMBIGUOUS); #undef ATRIBFLAG - set_cmt((chd + offsetof(_RTTIClassHierarchyDescriptor, attributes)), s.c_str(), TRUE); + setComment(ea, s.c_str(), TRUE); } } // ---- Place BCD's ---- UINT numBaseClasses = 0; - if (getVerify32_t((chd + offsetof(_RTTIClassHierarchyDescriptor, numBaseClasses)), numBaseClasses)) + if (getVerify32((chd + offsetof(_RTTIClassHierarchyDescriptor, numBaseClasses)), numBaseClasses)) { // Get pointer #ifndef __EA64__ @@ -903,9 +937,13 @@ void RTTI::_RTTIClassHierarchyDescriptor::doStruct(ea_t chd, ea_t colBase64) UINT baseClassArrayOffset = get_32bit(chd + offsetof(_RTTIClassHierarchyDescriptor, baseClassArray)); ea_t baseClassArray = (colBase64 + (UINT64) baseClassArrayOffset); - char buffer[MAXSTR]; - sprintf(buffer, "0x" EAFORMAT, baseClassArray); - set_cmt((chd + offsetof(RTTI::_RTTIClassHierarchyDescriptor, baseClassArray)), buffer, TRUE); + ea_t ea = (chd + offsetof(RTTI::_RTTIClassHierarchyDescriptor, baseClassArray)); + if (!hasComment(ea)) + { + char buffer[MAXSTR]; + _snprintf_s(buffer, sizeof(buffer), SIZESTR(buffer), "0x" EAFORMAT, baseClassArray); + setComment(ea, buffer, TRUE); + } #endif if (baseClassArray && (baseClassArray != BADADDR)) @@ -915,21 +953,21 @@ void RTTI::_RTTIClassHierarchyDescriptor::doStruct(ea_t chd, ea_t colBase64) char format[32]; if(numBaseClasses > 1) { - int iDigits = strlen(_itoa(numBaseClasses, format, 10)); - if (iDigits > 1) - _snprintf(format, SIZESTR(format), " BaseClass[%%0%dd]", iDigits); + int digits = (int) strlen(_itoa(numBaseClasses, format, 10)); + if (digits > 1) + _snprintf_s(format, sizeof(format), SIZESTR(format), " BaseClass[%%0%dd]", digits); else - strncpy(format, " BaseClass[%d]", SIZESTR(format)); + strcpy_s(format, sizeof(format), " BaseClass[%d]"); } #else char format[128]; if (numBaseClasses > 1) { - int iDigits = strlen(_itoa(numBaseClasses, format, 10)); - if (iDigits > 1) - _snprintf(format, SIZESTR(format), " BaseClass[%%0%dd] 0x%016I64X", iDigits); + int digits = (int) strlen(_itoa(numBaseClasses, format, 10)); + if (digits > 1) + _snprintf_s(format, sizeof(format), SIZESTR(format), " BaseClass[%%0%dd] 0x%%016I64X", digits); else - strncpy(format, " BaseClass[%d] 0x%016I64X", SIZESTR(format)); + strcpy_s(format, sizeof(format), " BaseClass[%d] 0x%016I64X"); } #endif @@ -939,77 +977,71 @@ void RTTI::_RTTIClassHierarchyDescriptor::doStruct(ea_t chd, ea_t colBase64) fixEa(baseClassArray); // Add index comment to to it - if (!has_cmt(get_flags(baseClassArray))) + if (!hasComment(baseClassArray)) { if (numBaseClasses == 1) - set_cmt(baseClassArray, " BaseClass", FALSE); + setComment(baseClassArray, " BaseClass", FALSE); else { - char ptrComent[MAXSTR]; ptrComent[SIZESTR(ptrComent)] = 0; - _snprintf(ptrComent, SIZESTR(ptrComent), format, i); - set_cmt(baseClassArray, ptrComent, false); + char ptrComent[MAXSTR]; + _snprintf_s(ptrComent, sizeof(ptrComent), SIZESTR(ptrComent), format, i); + setComment(baseClassArray, ptrComent, false); } } // Place BCD struct, and grab the base class name char baseClassName[MAXSTR]; - RTTI::_RTTIBaseClassDescriptor::doStruct(getEa(baseClassArray), baseClassName); + _RTTIBaseClassDescriptor::tryStruct(getEa(baseClassArray), baseClassName); #else fixDword(baseClassArray); UINT bcOffset = get_32bit(baseClassArray); ea_t bcd = (colBase64 + (UINT64)bcOffset); // Add index comment to to it - if (!has_cmt(get_flags(baseClassArray))) + if (!hasComment(baseClassArray)) { if (numBaseClasses == 1) { - sprintf(buffer, " BaseClass 0x" EAFORMAT, bcd); - set_cmt(baseClassArray, buffer, FALSE); + char buffer[MAXSTR]; + sprintf_s(buffer, sizeof(buffer), " BaseClass 0x" EAFORMAT, bcd); + setComment(baseClassArray, buffer, FALSE); } else { - _snprintf(buffer, SIZESTR(buffer), format, i, bcd); - set_cmt(baseClassArray, buffer, false); + char buffer[MAXSTR]; + _snprintf_s(buffer, sizeof(buffer), SIZESTR(buffer), format, i, bcd); + setComment(baseClassArray, buffer, false); } } // Place BCD struct, and grab the base class name char baseClassName[MAXSTR]; - _RTTIBaseClassDescriptor::doStruct(bcd, baseClassName, colBase64); + _RTTIBaseClassDescriptor::tryStruct(bcd, baseClassName, colBase64); #endif // Now we have the base class name, name and label some things if (i == 0) { // Set array name - if (!hasUniqueName(baseClassArray)) + if (!hasName(baseClassArray)) { // ??_R2A@@8 = A::`RTTI Base Class Array' - char mangledName[MAXSTR]; mangledName[SIZESTR(mangledName)] = 0; - _snprintf(mangledName, SIZESTR(mangledName), FORMAT_RTTI_BCA, baseClassName); - if (!set_name(baseClassArray, mangledName, (SN_NON_AUTO | SN_NOWARN))) - serializeName(baseClassArray, mangledName); + char mangledName[MAXSTR]; + _snprintf_s(mangledName, sizeof(mangledName), SIZESTR(mangledName), FORMAT_RTTI_BCA, baseClassName); + setName(baseClassArray, mangledName); } // Add a spacing comment line above us - if (optionOverwriteComments) - { - killAnteriorComments(baseClassArray); - add_extra_cmt(baseClassArray, true, ""); - } - else if (!hasAnteriorComment(baseClassArray)) - add_extra_cmt(baseClassArray, true, ""); + setAnteriorComment(baseClassArray, ""); // Set CHD name - if (!hasUniqueName(chd)) + if (!hasName(chd)) { // A::`RTTI Class Hierarchy Descriptor' - char mangledName[MAXSTR]; mangledName[SIZESTR(mangledName)] = 0; - _snprintf(mangledName, (MAXSTR - 1), FORMAT_RTTI_CHD, baseClassName); - if (!set_name(chd, mangledName, (SN_NON_AUTO | SN_NOWARN))) - serializeName(chd, mangledName); + char mangledName[MAXSTR]; + _snprintf_s(mangledName, sizeof(mangledName), SIZESTR(mangledName), FORMAT_RTTI_CHD, baseClassName); + setName(chd, mangledName); } } } @@ -1018,10 +1050,8 @@ void RTTI::_RTTIClassHierarchyDescriptor::doStruct(ea_t chd, ea_t colBase64) if (numBaseClasses > 0) { if (is_loaded(baseClassArray)) - { if (get_32bit(baseClassArray) == 0) fixDword(baseClassArray); - } } } else @@ -1106,8 +1136,11 @@ static void RTTI::getBCDInfo(ea_t col, __out bcdList &list, __out UINT &numBaseC // Process RTTI vftable info -void RTTI::processVftable(ea_t vft, ea_t col) +// Returns TRUE if if vftable and wasn't named on entry +BOOL RTTI::processVftable(ea_t vft, ea_t col) { + BOOL result = FALSE; + #ifdef __EA64__ UINT tdOffset = get_32bit(col + offsetof(_RTTICompleteObjectLocator, typeDescriptor)); UINT objectLocator = get_32bit(col + offsetof(RTTI::_RTTICompleteObjectLocator, objectBase)); @@ -1115,11 +1148,11 @@ void RTTI::processVftable(ea_t vft, ea_t col) ea_t typeInfo = (colBase + (UINT64) tdOffset); #endif - // Get vftable info + // Verify and fix if vftable exists here vftable::vtinfo vi; if (vftable::getTableInfo(vft, vi)) { - //msg(EAFORMAT" - " EAFORMAT " c: %d\n", vi.start, vi.end, vi.methodCount); + //msg(EAFORMAT " - " EAFORMAT " c: %d\n", vi.start, vi.end, vi.methodCount); // Get COL type name #ifndef __EA64__ @@ -1130,7 +1163,7 @@ void RTTI::processVftable(ea_t vft, ea_t col) ea_t chd = (colBase + (UINT64) cdOffset); #endif - char colName[MAXSTR]; colName[0] = colName[SIZESTR(colName)] = 0; + char colName[MAXSTR]; type_info::getName(typeInfo, colName, SIZESTR(colName)); char demangledColName[MAXSTR]; getPlainTypeName(colName, demangledColName); @@ -1150,22 +1183,22 @@ void RTTI::processVftable(ea_t vft, ea_t col) if ((offset == 0) && ((chdAttributes & (CHD_MULTINH | CHD_VIRTINH)) == 0)) { // Set the vftable name - if (!hasUniqueName(vft)) + if (!hasName(vft)) { + result = TRUE; + // Decorate raw name as a vftable. I.E. const Name::`vftable' - char decorated[MAXSTR]; decorated[SIZESTR(decorated)] = 0; - _snprintf(decorated, SIZESTR(decorated), FORMAT_RTTI_VFTABLE, SKIP_TD_TAG(colName)); - if (!set_name(vft, decorated, (SN_NON_AUTO | SN_NOWARN))) - serializeName(vft, decorated); + char decorated[MAXSTR]; + _snprintf_s(decorated, sizeof(decorated), SIZESTR(decorated), FORMAT_RTTI_VFTABLE, SKIP_TD_TAG(colName)); + setName(vft, decorated); } // Set COL name. I.E. const Name::`RTTI Complete Object Locator' - if (!hasUniqueName(col)) + if (!hasName(col)) { - char decorated[MAXSTR]; decorated[SIZESTR(decorated)] = 0; - _snprintf(decorated, SIZESTR(decorated), FORMAT_RTTI_COL, SKIP_TD_TAG(colName)); - if (!set_name(col, decorated, (SN_NON_AUTO | SN_NOWARN))) - serializeName(col, decorated); + char decorated[MAXSTR]; + _snprintf_s(decorated, sizeof(decorated), SIZESTR(decorated), FORMAT_RTTI_COL, SKIP_TD_TAG(colName)); + setName(col, decorated); } // Build object hierarchy string @@ -1198,8 +1231,10 @@ void RTTI::processVftable(ea_t vft, ea_t col) cmt.sprnt("%s%s: ", ((colName[3] == 'V') ? "" : "struct "), demangledColName); isTopLevel = TRUE; } + if (placed > 1) cmt += ';'; + sucess = TRUE; } // ======= Multiple inheritance, and, or, virtual inheritance hierarchies @@ -1251,21 +1286,21 @@ void RTTI::processVftable(ea_t vft, ea_t col) if (isTopLevel) { // Set the vft name - if (!hasUniqueName(vft)) + if (!hasName(vft)) { - char decorated[MAXSTR]; decorated[SIZESTR(decorated)] = 0; - _snprintf(decorated, SIZESTR(decorated), FORMAT_RTTI_VFTABLE, SKIP_TD_TAG(colName)); - if (!set_name(vft, decorated, (SN_NON_AUTO | SN_NOWARN))) - serializeName(vft, decorated); + result = TRUE; + + char decorated[MAXSTR]; + _snprintf_s(decorated, sizeof(decorated), SIZESTR(decorated), FORMAT_RTTI_VFTABLE, SKIP_TD_TAG(colName)); + setName(vft, decorated); } // COL name - if (!hasUniqueName(col)) + if (!hasName(col)) { - char decorated[MAXSTR]; decorated[SIZESTR(decorated)] = 0; - _snprintf(decorated, SIZESTR(decorated), FORMAT_RTTI_COL, SKIP_TD_TAG(colName)); - if (!set_name(col, decorated, (SN_NON_AUTO | SN_NOWARN))) - serializeName(col, decorated); + char decorated[MAXSTR]; + _snprintf_s(decorated, sizeof(decorated), SIZESTR(decorated), FORMAT_RTTI_COL, SKIP_TD_TAG(colName)); + setName(col, decorated); } // Build hierarchy string starting with parent @@ -1287,25 +1322,27 @@ void RTTI::processVftable(ea_t vft, ea_t col) else { // Combine COL and CHD name - char combinedName[MAXSTR]; combinedName[SIZESTR(combinedName)] = 0; - _snprintf(combinedName, SIZESTR(combinedName), "%s6B%s@", SKIP_TD_TAG(colName), SKIP_TD_TAG(bi->m_name)); + char combinedName[MAXSTR]; + _snprintf_s(combinedName, sizeof(combinedName), SIZESTR(combinedName), "%s6B%s@", SKIP_TD_TAG(colName), SKIP_TD_TAG(bi->m_name)); // Set vftable name - if (!hasUniqueName(vft)) + if (!hasName(vft)) { + result = TRUE; + char decorated[MAXSTR]; - strncat(strcpy(decorated, FORMAT_RTTI_VFTABLE_PREFIX), combinedName, (MAXSTR - (1 + SIZESTR(FORMAT_RTTI_VFTABLE_PREFIX)))); - if (!set_name(vft, decorated, (SN_NON_AUTO | SN_NOWARN))) - serializeName(vft, decorated); + strcpy(decorated, FORMAT_RTTI_VFTABLE_PREFIX); + strncat_s(decorated, MAXSTR, combinedName, (MAXSTR - (1 + SIZESTR(FORMAT_RTTI_VFTABLE_PREFIX)))); + setName(vft, decorated); } // COL name - if (!hasUniqueName((ea_t) col)) + if (!hasName((ea_t) col)) { - char decorated[MAXSTR]; - strncat(strcpy(decorated, FORMAT_RTTI_COL_PREFIX), combinedName, (MAXSTR - (1 + SIZESTR(FORMAT_RTTI_COL_PREFIX)))); - if (!set_name((ea_t) col, decorated, (SN_NON_AUTO | SN_NOWARN))) - serializeName((ea_t)col, decorated); + char decorated[MAXSTR]; + strcpy(decorated, FORMAT_RTTI_COL_PREFIX); + strncat_s(decorated, MAXSTR, combinedName, (MAXSTR - (1 + SIZESTR(FORMAT_RTTI_COL_PREFIX)))); + setName((ea_t) col, decorated); } // Build hierarchy string starting with parent @@ -1343,8 +1380,10 @@ void RTTI::processVftable(ea_t vft, ea_t col) } */ } + if (placed > 1) cmt += ';'; + sucess = TRUE; } else @@ -1356,40 +1395,40 @@ void RTTI::processVftable(ea_t vft, ea_t col) // Store entry addTableEntry(((chdAttributes & 0xF) | ((isTopLevel == TRUE) ? RTTI::IS_TOP_LEVEL : 0)), vft, vi.methodCount, "%s@%s", demangledColName, cmt.c_str()); - //cmt.cat_sprnt(" %s O: %d, A: %d (#classinformer)", attributeLabel(chdAttributes, numBaseClasses), offset, chdAttributes); - cmt.cat_sprnt(" %s (#classinformer)", attributeLabel(chdAttributes)); - // Add a separating comment above RTTI COL - ea_t cmtPtr = (vft - sizeof(ea_t)); - if (optionOverwriteComments) - { - killAnteriorComments(cmtPtr); - add_extra_line(cmtPtr, true, "\n; %s %s", ((colName[3] == 'V') ? "class" : "struct"), cmt.c_str()); - } - else - if (!hasAnteriorComment(cmtPtr)) - add_extra_line(cmtPtr, true, "\n; %s %s", ((colName[3] == 'V') ? "class" : "struct"), cmt.c_str()); // add_long_cmt + ea_t colPtr = (vft - sizeof(ea_t)); + fixEa(colPtr); + //cmt.cat_sprnt(" %s O: %d, A: %d (#classinformer)", attributeLabel(chdAttributes, numBaseClasses), offset, chdAttributes); + cmt.cat_sprnt(" %s (#classinformer)", attributeLabel(chdAttributes)); + if (!hasAnteriorComment(colPtr)) + setAnteriorComment(colPtr, "\n; %s %s", ((colName[3] == 'V') ? "class" : "struct"), cmt.c_str()); //vftable::processMembers(plainName, vft, end); } } else + // Usually a typedef reference to a COL, not a vftable { - msg(EAFORMAT" ** Vftable attached to this COL, error?\n", vft); - - // Set COL name - if (!hasUniqueName(col)) + #if 0 + qstring tmp; + idaFlags2String(get_flags(vft), tmp); + msg(EAFORMAT" ** Vftable attached to this COL, error? (%s)\n", vft, tmp.c_str()); + #endif + + // Just set COL name + if (!hasName(col)) { #ifndef __EA64__ ea_t typeInfo = getEa(col + offsetof(_RTTICompleteObjectLocator, typeDescriptor)); #endif - char colName[MAXSTR]; colName[0] = colName[SIZESTR(colName)] = 0; + char colName[MAXSTR]; type_info::getName(typeInfo, colName, SIZESTR(colName)); - char decorated[MAXSTR]; decorated[SIZESTR(decorated)] = 0; - _snprintf(decorated, SIZESTR(decorated), FORMAT_RTTI_COL, SKIP_TD_TAG(colName)); - if (!set_name(col, decorated, (SN_NON_AUTO | SN_NOWARN))) - serializeName(col, decorated); + char decorated[MAXSTR]; + _snprintf_s(decorated, sizeof(decorated), SIZESTR(decorated), FORMAT_RTTI_COL, SKIP_TD_TAG(colName)); + setName(col, decorated); } } + + return result; } diff --git a/Plugin/RTTI.h b/Plugin/RTTI.h index ab937e7..89745ae 100644 --- a/Plugin/RTTI.h +++ b/Plugin/RTTI.h @@ -20,7 +20,7 @@ namespace RTTI static BOOL isValid(ea_t typeInfo); static BOOL isTypeName(ea_t name); static int getName(ea_t typeInfo, __out LPSTR bufffer, int bufferSize); - static void doStruct(ea_t typeInfo); + static void tryStruct(ea_t typeInfo); }; const UINT MIN_TYPE_INFO_SIZE = (offsetof(type_info, _M_d_name) + sizeof(".?AVx")); typedef type_info _TypeDescriptor; @@ -58,7 +58,7 @@ namespace RTTI //_RTTIClassHierarchyDescriptor *classDescriptor; *X64 int32 offset static BOOL isValid(ea_t bcd, ea_t colBase64 = NULL); - static void doStruct(ea_t bcd, __out_bcount(MAXSTR) LPSTR baseClassName, ea_t colBase64 = NULL); + static void tryStruct(ea_t bcd, __out_bcount(MAXSTR) LPSTR baseClassName, ea_t colBase64 = NULL); }; // "Class Hierarchy Descriptor" describes the inheritance hierarchy of a class; shared by all COLs for the class @@ -86,7 +86,7 @@ namespace RTTI #endif static BOOL isValid(ea_t chd, ea_t colBase64 = NULL); - static void doStruct(ea_t chd, ea_t colBase64 = NULL); + static void tryStruct(ea_t chd, ea_t colBase64 = NULL); }; // "Complete Object Locator" location of the complete object from a specific vftable pointer @@ -108,7 +108,7 @@ namespace RTTI #ifndef __EA64__ static BOOL isValid2(ea_t col); #endif - static void doStruct(ea_t col); + static BOOL tryStruct(ea_t col); }; #pragma pack(pop) @@ -116,6 +116,6 @@ namespace RTTI void freeWorkingData(); void addDefinitionsToIda(); - void processVftable(ea_t eaTable, ea_t col); + BOOL processVftable(ea_t eaTable, ea_t col); } diff --git a/Plugin/StdAfx.h b/Plugin/StdAfx.h index 70529aa..957137f 100644 --- a/Plugin/StdAfx.h +++ b/Plugin/StdAfx.h @@ -19,17 +19,19 @@ #define USE_DANGEROUS_FUNCTIONS #define USE_STANDARD_FILE_FUNCTIONS #define NO_OBSOLETE_FUNCS +// Nix the many warning about int type conversions +#pragma warning(push) +#pragma warning(disable:4244) +#pragma warning(disable:4267) #include #include #include #include -#pragma warning(push) -#pragma warning(disable:4267) // "conversion from 'size_t' to 'xxx', possible loss of data" #include -#pragma warning(pop) #include #include #include +#pragma warning(pop) // Qt libs #include @@ -41,7 +43,7 @@ #include #include #include -// (IDA SDK)\lib\x86_win_qt +// IDA SDK Qt libs #pragma comment(lib, "Qt5Core.lib") #pragma comment(lib, "Qt5Gui.lib") #pragma comment(lib, "Qt5Widgets.lib") @@ -60,52 +62,9 @@ typedef qlist eaList; typedef std::unordered_set eaSet; -typedef std::unordered_map eaRefMap; -struct earef -{ - ea_t ea; - UINT refs; -}; -typedef qlist eaRefList; - - -// Get IDA 32 bit value with verification -template BOOL getVerify32_t(ea_t eaPtr, T &rValue) -{ - // Location valid? - if (is_loaded(eaPtr)) - { - // Get 32bit value - rValue = (T) get_32bit(eaPtr); - return(TRUE); - } - - return(FALSE); -} - -// Get address/pointer value -inline ea_t getEa(ea_t ea) -{ - #ifndef __EA64__ - return((ea_t) get_32bit(ea)); - #else - return((ea_t) get_64bit(ea)); - #endif -} - - -// Returns TRUE if ea_t sized value flags -inline BOOL isEa(flags_t f) -{ - #ifndef __EA64__ - return(is_dword(f)); - #else - return(is_qword(f)); - #endif -} - +typedef std::unordered_map eaRefMap; // address & ref count //#define STYLE_PATH "C:/Projects/IDA Pro Work/IDA_ClassInformer_PlugIn/Plugin/" #define STYLE_PATH ":/classinf/" -#define MY_VERSION MAKEWORD(4, 2) // Low, high, convention: 0 to 99 +#define MY_VERSION MAKEWORD(5, 2) // Low, high, convention: 0 to 99 diff --git a/Plugin/Vftable.cpp b/Plugin/Vftable.cpp index ddb363b..a6fc109 100644 --- a/Plugin/Vftable.cpp +++ b/Plugin/Vftable.cpp @@ -5,7 +5,7 @@ // // **************************************************************************** #include "stdafx.h" -#include "Core.h" +#include "Main.h" #include "Vftable.h" #include "RTTI.h" @@ -20,14 +20,14 @@ namespace vftable // Return TRUE along with info if valid vftable parsed at address BOOL vftable::getTableInfo(ea_t ea, vtinfo &info) { - ZeroMemory(&info, sizeof(vtinfo)); - // Start of a vft should have an xref and a name (auto, or user, etc). // Ideal flags 32bit: FF_DWRD, FF_0OFF, FF_REF, FF_NAME, FF_DATA, FF_IVL //dumpFlags(ea); flags_t flags = get_flags(ea); if(has_xref(flags) && has_any_name(flags) && (isEa(flags) || is_unknown(flags))) { + ZeroMemory(&info, sizeof(vtinfo)); + // Get raw (auto-generated mangled, or user named) vft name //if (!get_name(BADADDR, ea, info.name, SIZESTR(info.name))) // msg(EAFORMAT" ** vftable::getTableInfo(): failed to get raw name!\n", ea); @@ -36,7 +36,7 @@ BOOL vftable::getTableInfo(ea_t ea, vtinfo &info) ea_t start = info.start = ea; while (TRUE) { - // Should be an ea_t offset to a function here (could be unknown if dirty IDB) + // Should be an ea_t sized offset to a function here (could be unknown if dirty IDB) // Ideal flags for 32bit: FF_DWRD, FF_0OFF, FF_REF, FF_NAME, FF_DATA, FF_IVL //dumpFlags(ea); flags_t indexFlags = get_flags(ea); @@ -50,7 +50,7 @@ BOOL vftable::getTableInfo(ea_t ea, vtinfo &info) ea_t memberPtr = getEa(ea); if (!(memberPtr && (memberPtr != BADADDR))) { - // vft's often have a zero ea_t (NULL pointer?) following, fix it + // vft's often have a trailing zero ea_t (alignment, or?), fix it if (memberPtr == 0) fixEa(ea); @@ -62,10 +62,24 @@ BOOL vftable::getTableInfo(ea_t ea, vtinfo &info) flags_t flags = get_flags(memberPtr); if (!(is_code(flags) || is_unknown(flags))) { - //msg(" ******* 3\n"); - break; + // New for version 2.5: there are rare cases where IDA hasn't fix unresolved bytes + // So except if the member pointer is in a code segment as a 2nd chance + if (segment_t *s = getseg(memberPtr)) + { + if (s->type != SEG_CODE) + { + //msg(" ******* 3\n"); + break; + } + } + else + { + //msg(" ******* 3.5\n"); + break; + } } + if (ea != start) { // If we see a ref after first index it's probably the beginning of the next vft or something else @@ -150,11 +164,11 @@ int vftable::tryKnownMember(LPCTSTR name, ea_t eaMember) if(eaMember && (eaMember != BADADDR)) { // Skip if it already has a name - flags_t flags = getFlags((ea_t) eaMember); + flags_t flags = get_flags((ea_t) eaMember); if(!has_name(flags) || has_dummy_name(flags)) { // Should be code - if(isCode(flags)) + if(is_code(flags)) { ea_t eaAddress = eaMember; diff --git a/Plugin/dialog.ui b/Plugin/dialog.ui index 6ce1689..35ece31 100644 --- a/Plugin/dialog.ui +++ b/Plugin/dialog.ui @@ -7,7 +7,7 @@ 0 0 292 - 344 + 311 @@ -19,13 +19,13 @@ 292 - 344 + 311 292 - 344 + 311 @@ -45,7 +45,7 @@ 120 - 312 + 272 156 24 @@ -64,7 +64,7 @@ 15 - 95 + 94 121 17 @@ -86,7 +86,7 @@ 15 - 125 + 122 256 17 @@ -108,29 +108,7 @@ 15 - 155 - 201 - 17 - - - - - Noto Sans - 10 - - - - - - - Overwrite anterior comments - - - - - - 15 - 185 + 148 151 17 @@ -152,7 +130,7 @@ 15 - 256 + 225 141 16 @@ -205,9 +183,9 @@ 225 - 45 + 44 61 - 21 + 24 @@ -220,7 +198,7 @@ - Version: 2.4 + Version: 2.6 By Sirmabus @@ -234,7 +212,7 @@ By Sirmabus 15 - 215 + 186 129 27 diff --git a/Plugin/undname.h b/Plugin/undname.h index c1d0ec9..2a6a75a 100644 --- a/Plugin/undname.h +++ b/Plugin/undname.h @@ -4,8 +4,8 @@ // Online: http://demangler.com/ -typedef void * (__cdecl * _Alloc)(UINT); -typedef void(__cdecl * _Free)(PVOID); +typedef PVOID (__cdecl * _Alloc)(UINT); +typedef void (__cdecl * _Free)(PVOID); const UINT UNDNAME_COMPLETE = 0x00000; // Enable full undecoration const UINT UNDNAME_NO_LEADING_UNDERSCORES = 0x00001; // Remove leading underscores from MS extended keywords @@ -27,10 +27,13 @@ const UINT UNDNAME_HAVE_PARAMETERS = 0x04000; // The real templates pa const UINT UNDNAME_NO_ECSU = 0x08000; // Suppress enum/class/struct/union const UINT UNDNAME_NO_IDENT_CHAR_CHECK = 0x10000; // Suppress check for IsValidIdentChar +inline PVOID mallocWrap(UINT size) { return malloc((UINT) size); } + /* To supply a buffer to use use 'buffer' and 'sizeBuffer', else for a allocated buffer 'buffer' = NULL, 'sizeBuffer' = 0, and use the return string. Call Free on the return result when done with the string. Note: CRT documentation error, the Allocator and Free must be supplied regardless if supplied or allocation buffer method desired. */ +// https://www.winehq.org/pipermail/wine-patches/2004-January/009183.html extern "C" LPSTR __cdecl __unDName(__out LPSTR buffer, __in LPCSTR name, int sizeBuffer, _Alloc allocator, _Free _free, UINT flags); \ No newline at end of file