diff --git a/README.md b/README.md index e85e941..1e7192a 100644 --- a/README.md +++ b/README.md @@ -1,98 +1,106 @@ Scylla - x64/x86 Imports Reconstruction ======================================= ImpREC, CHimpREC, Imports Fixer... this are all great tools to rebuild an import table, but they all have some major disadvantages, so I decided to create my own tool for this job. Scylla's key benefits are: - x64 and x86 support - full unicode support (probably some russian or chinese will like this :-) ) - written in C/C++ - plugin support - works great with Windows 7 This tool was designed to be used with Windows 7 x64, so it is recommend to use this operating system. But it may work with XP and Vista, too. Source code is licensed under GNU GENERAL PUBLIC LICENSE v3.0 Known Bugs ---------- ### Only Windows XP x64: Windows XP x64 has some API bugs. 100% correct imports reconstruction is impossible. If you still want to use XP x64, here are some hints: * EncodePointer/DecodePointer exported by kernel32.dll have both the same VA. Scylla, CHimpREC and other tools cannot know which API is correct. You need to fix this manually. Your fixed dump will probably run fine on XP but crash on Vista/7. ### ImpREC plugin support: Some ImpREC Plugins don't work with Windows Vista/7 because they don't "return 1" in the DllMain function. Keyboard Shortcuts ------------------ - CTRL + D: [D]ump - CTRL + F: [F]ix Dump - CTRL + R: PE [R]ebuild - CTRL + O: L[o]ad Tree - CTRL + S: [S]ave Tree - CTRL + T: Auto[t]race - CTRL + G: [G]et Imports - CTRL + I: [I]AT Autosearch Changelog --------- +Version 0.6 + +- added dump memory regions +- added dump pe sections -> you can edit some values in the dialog +- improved dump engine with intelligent dumping +- improved pe rebuild engine -> removed yoda's code +- fixed various bugs + Version 0.5a: - fixed memory leak - improved IAT search Version 0.5: - added save/load import tree feature - multi-select in tree view - fixed black icons problem in tree view - added keyboard shortcuts - dll dump + dll dump fix now working - added support for scattered IATs - pre select target path in open file dialogs - improved import resolving engine with api scoring - api selection dialog - minor bug fixes and improvements Version 0.4: - GUI code improvements - bug fixes - imports by ordinal Version 0.3a: - Improved import resolving - fixed buffer overflow errors Version 0.3: - ImpREC plugin support - minor bug fix Version 0.2a: - improved disassembler dialog - improved iat search Version 0.2: - improved process detection - added some options - new options dialog - improved source code diff --git a/Scylla/DumpSectionGui.cpp b/Scylla/DumpSectionGui.cpp index 5b2480b..85785a9 100644 --- a/Scylla/DumpSectionGui.cpp +++ b/Scylla/DumpSectionGui.cpp @@ -1,355 +1,382 @@ #include "DumpSectionGui.h" #include "Architecture.h" #include "ProcessAccessHelp.h" #include "PeParser.h" bool PeSection::highlightVirtualSize() { //highlight big virtual sizes -> anti-dump protection return (virtualSize > 0x2000000); } std::vector & DumpSectionGui::getSectionList() { return sectionList; } BOOL DumpSectionGui::OnInitDialog(CWindow wndFocus, LPARAM lInitParam) { DoDataExchange(); // attach controls DlgResize_Init(true, true); addColumnsToSectionList(ListSectionSelect); displaySectionList(ListSectionSelect); selectOrDeselectAll(); isEditing = false; selectedSection = 0; CenterWindow(); return TRUE; } LRESULT DumpSectionGui::OnListSectionColumnClicked(NMHDR* pnmh) { NMLISTVIEW* list = (NMLISTVIEW*)pnmh; int column = list->iSubItem; if(column == prevColumn) { ascending = !ascending; } else { prevColumn = column; ascending = true; } // lo-byte: column, hi-byte: sort-order ListSectionSelect.SortItems(&listviewCompareFunc, MAKEWORD(column, ascending)); return 0; } LRESULT DumpSectionGui::OnListSectionClick(NMHDR* pnmh) { //int index = ListSectionSelect.GetSelectionMark(); //if (index != -1) //{ //} return 0; } LRESULT DumpSectionGui::OnListDoubleClick(NMHDR* pnmh) { - RECT rect; - RECT rect1,rect2; + LVHITTESTINFO hti; + RECT rect, rect1, rect2; NMITEMACTIVATE* ia = (NMITEMACTIVATE*)pnmh; - if (ia->iSubItem != COL_VSize) + editingSubItem = ia->iSubItem; + + if (editingSubItem == COL_NAME || editingSubItem == COL_VA || editingSubItem == COL_RVA) { return 0; } - LVHITTESTINFO hti; + + hti.pt = ia->ptAction; int clicked = ListSectionSelect.HitTest(&hti); if(clicked != -1) { selectedSection = (PeSection *)ListSectionSelect.GetItemData(clicked); } ListSectionSelect.GetSubItemRect(ia->iItem,ia->iSubItem,LVIR_BOUNDS,&rect); //Get the Rectange of the listControl ListSectionSelect.GetWindowRect(&rect1); //Get the Rectange of the Dialog GetWindowRect(&rect2); int x = rect1.left - rect2.left; int y = rect1.top - rect2.top; isEditing = true; + switch (editingSubItem) + { + case COL_VSize: + valueBeforeEditing = selectedSection->virtualSize; + break; + case COL_RSize: + valueBeforeEditing = selectedSection->rawSize; + break; + case COL_Characteristics: + valueBeforeEditing = selectedSection->characteristics; + break; + default: + valueBeforeEditing = 0; + } + + EditListControl.SetValue(valueBeforeEditing); + EditListControl.SetWindowPos(HWND_TOP,rect.left + 7, rect.top + 7, rect.right - rect.left, rect.bottom - rect.top, NULL); EditListControl.ShowWindow(SW_SHOW); EditListControl.SetFocus(); - //Draw a Rectangle around the SubItem - //Rectangle(ListSectionSelect.GetDC(),rect.left,rect.top-1,rect.right,rect.bottom); - //Set the listItem text in the EditBox - EditListControl.SetValue(selectedSection->virtualSize); - return 0; } void DumpSectionGui::OnSectionSelectAll(UINT uNotifyCode, int nID, CWindow wndCtl) { selectOrDeselectAll(); } void DumpSectionGui::OnEditList(UINT uNotifyCode, int nID, CWindow wndCtl) { switch (uNotifyCode) { case EN_KILLFOCUS: { isEditing = false; updateEditedItem(); EditListControl.ShowWindow(SW_HIDE); } break; } } void DumpSectionGui::OnOK(UINT uNotifyCode, int nID, CWindow wndCtl) { if (isEditing) //EN_KILLFOCUS not sent? { updateEditedItem(); } updateCheckState(); EndDialog(1); } void DumpSectionGui::OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl) { EndDialog(0); } int DumpSectionGui::listviewCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { const PeSection * module1 = (PeSection *)lParam1; const PeSection * module2 = (PeSection *)lParam2; int column = LOBYTE(lParamSort); bool ascending = (HIBYTE(lParamSort) == TRUE); int diff = 0; switch(column) { case COL_NAME: diff = _wcsicmp(module1->name, module2->name); break; case COL_VA: diff = module1->virtualAddress < module2->virtualAddress ? -1 : 1; break; case COL_VSize: diff = module1->virtualSize < module2->virtualSize ? -1 : 1; break; case COL_RVA: diff = module1->rawAddress < module2->rawAddress ? -1 : 1; break; case COL_RSize: diff = module1->rawSize < module2->rawSize ? -1 : 1; break; case COL_Characteristics: diff = module1->characteristics < module2->characteristics ? -1 : 1; break; } return ascending ? diff : -diff; } void DumpSectionGui::addColumnsToSectionList(CListViewCtrl& list) { list.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT|LVS_EX_CHECKBOXES|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_CHECKBOXES|LVS_EX_GRIDLINES); list.InsertColumn(COL_NAME, L"Name", LVCFMT_CENTER); list.InsertColumn(COL_VA, L"Virtual Address", LVCFMT_CENTER); list.InsertColumn(COL_VSize, L"Virtual Size", LVCFMT_CENTER); list.InsertColumn(COL_RVA, L"Raw Address", LVCFMT_CENTER); list.InsertColumn(COL_RSize, L"Raw Size", LVCFMT_CENTER); list.InsertColumn(COL_Characteristics, L"Characteristics", LVCFMT_CENTER); } void DumpSectionGui::displaySectionList(CListViewCtrl& list) { int count = 0; WCHAR temp[20]; list.DeleteAllItems(); if (sectionList.empty()) { getAllSectionsFromFile(); } std::vector::const_iterator iter; for( iter = sectionList.begin(); iter != sectionList.end(); iter++ , count++) { list.InsertItem(count, iter->name); swprintf_s(temp, PRINTF_DWORD_PTR_FULL, iter->virtualAddress); list.SetItemText(count, COL_VA, temp); swprintf_s(temp, L"%08X", iter->virtualSize); list.SetItemText(count, COL_VSize, temp); swprintf_s(temp, L"%08X", iter->rawAddress); list.SetItemText(count, COL_RVA, temp); swprintf_s(temp, L"%08X", iter->rawSize); list.SetItemText(count, COL_RSize, temp); swprintf_s(temp, L"%08X", iter->characteristics); list.SetItemText(count, COL_Characteristics, temp); list.SetItemData(count, (DWORD_PTR)&(*iter)); } list.SetColumnWidth(COL_NAME, LVSCW_AUTOSIZE_USEHEADER); list.SetColumnWidth(COL_VA, LVSCW_AUTOSIZE_USEHEADER); list.SetColumnWidth(COL_VSize, LVSCW_AUTOSIZE_USEHEADER); list.SetColumnWidth(COL_RVA, LVSCW_AUTOSIZE_USEHEADER); list.SetColumnWidth(COL_RSize, LVSCW_AUTOSIZE_USEHEADER); list.SetColumnWidth(COL_Characteristics, LVSCW_AUTOSIZE_USEHEADER); } LRESULT DumpSectionGui::OnNMCustomdraw(NMHDR* pnmh) { LRESULT pResult = 0; unsigned int vectorIndex = 0; LPNMLVCUSTOMDRAW lpLVCustomDraw = (LPNMLVCUSTOMDRAW)(pnmh); switch(lpLVCustomDraw->nmcd.dwDrawStage) { case CDDS_ITEMPREPAINT: case CDDS_ITEMPREPAINT | CDDS_SUBITEM: { vectorIndex = (unsigned int)lpLVCustomDraw->nmcd.dwItemSpec; if (lpLVCustomDraw->iSubItem == COL_VSize) { if (sectionList[vectorIndex].highlightVirtualSize()) { lpLVCustomDraw->clrText = RGB(255,255,255); // white text lpLVCustomDraw->clrTextBk = RGB(255,0,0); // red background } } else { lpLVCustomDraw->clrText = CLR_DEFAULT; lpLVCustomDraw->clrTextBk = CLR_DEFAULT; } } break; default: break; } pResult |= CDRF_NOTIFYPOSTPAINT; pResult |= CDRF_NOTIFYITEMDRAW; pResult |= CDRF_NOTIFYSUBITEMDRAW; return pResult; } void DumpSectionGui::getAllSectionsFromFile() { PeSection peSection; if (sectionList.empty()) { sectionList.reserve(3); } else { sectionList.clear(); } PeParser peFile(fullpath); if (peFile.isValidPeFile()) { std::vector & listSectionHeader = peFile.getSectionHeaderList(); for (WORD i = 0; i < peFile.getNumberOfSections(); i++) { peFile.getSectionNameUnicode(i, peSection.name, _countof(peSection.name)); peSection.virtualAddress = imageBase + listSectionHeader[i].sectionHeader.VirtualAddress; peSection.virtualSize = listSectionHeader[i].sectionHeader.Misc.VirtualSize; peSection.rawAddress = listSectionHeader[i].sectionHeader.PointerToRawData; peSection.rawSize = listSectionHeader[i].sectionHeader.SizeOfRawData; peSection.characteristics = listSectionHeader[i].sectionHeader.Characteristics; peSection.isDumped = true; sectionList.push_back(peSection); } } else { MessageBox(fullpath, L"Not a valid PE -> This should never happen", MB_ICONERROR); } } void DumpSectionGui::updateEditedItem() { if (selectedSection) { DWORD newValue = EditListControl.GetValue(); - if (selectedSection->virtualSize != newValue) + if (valueBeforeEditing != newValue) { - selectedSection->virtualSize = newValue; + switch (editingSubItem) + { + case COL_VSize: + selectedSection->virtualSize = newValue; + break; + case COL_RSize: + selectedSection->rawSize = newValue; + break; + case COL_Characteristics: + selectedSection->characteristics = newValue; + break; + } + displaySectionList(ListSectionSelect); selectOrDeselectAll(); } } } void DumpSectionGui::updateCheckState() { PeSection * pesection; for (size_t i = 0; i < sectionList.size(); i++) { pesection = (PeSection *)ListSectionSelect.GetItemData((int)i); pesection->isDumped = ListSectionSelect.GetCheckState((int)i) == TRUE; } } void DumpSectionGui::selectOrDeselectAll() { BOOL checkState = ListSectionSelect.GetCheckState((int)0) ? FALSE : TRUE; for (size_t i = 0; i < sectionList.size(); i++) { ListSectionSelect.SetCheckState((int)i, checkState); } -} +} \ No newline at end of file diff --git a/Scylla/DumpSectionGui.h b/Scylla/DumpSectionGui.h index 79a14d8..cfc8412 100644 --- a/Scylla/DumpSectionGui.h +++ b/Scylla/DumpSectionGui.h @@ -1,130 +1,132 @@ #pragma once #include #include "resource.h" // WTL #include // base ATL classes #include // base WTL classes #include // ATL GUI classes #include // WTL window frame helpers #include // WTL utility classes #include // WTL enhanced msg map macros #include // WTL controls #include // WTL dialog data exchange #include #include "hexedit.h" class PeSection { public: WCHAR name[IMAGE_SIZEOF_SHORT_NAME + 1]; DWORD_PTR virtualAddress; DWORD virtualSize; DWORD_PTR rawAddress; DWORD rawSize; DWORD characteristics; bool isDumped; bool highlightVirtualSize(); }; class DumpSectionGui : public CDialogImpl, public CWinDataExchange, public CDialogResize { public: enum { IDD = IDD_DLG_DUMPSECTION }; BEGIN_DDX_MAP(DumpSectionGui) DDX_CONTROL_HANDLE(IDC_LIST_DUMPSECTION, ListSectionSelect) DDX_CONTROL(IDC_EDIT_LISTCONTROL, EditListControl) END_DDX_MAP() BEGIN_MSG_MAP(DumpSectionGui) MSG_WM_INITDIALOG(OnInitDialog) NOTIFY_HANDLER_EX(IDC_LIST_DUMPSECTION, LVN_COLUMNCLICK, OnListSectionColumnClicked) NOTIFY_HANDLER_EX(IDC_LIST_DUMPSECTION, NM_CLICK, OnListSectionClick) NOTIFY_HANDLER_EX(IDC_LIST_DUMPSECTION, NM_CUSTOMDRAW, OnNMCustomdraw) NOTIFY_HANDLER_EX(IDC_LIST_DUMPSECTION, NM_DBLCLK, OnListDoubleClick) COMMAND_ID_HANDLER_EX(IDC_BUTTON_SELECT_DESELECT, OnSectionSelectAll) COMMAND_ID_HANDLER_EX(IDC_BTN_DUMPSECTION_OK, OnOK) COMMAND_ID_HANDLER_EX(IDC_EDIT_LISTCONTROL, OnEditList) COMMAND_ID_HANDLER_EX(IDC_BTN_DUMPSECTION_CANCEL, OnCancel) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) CHAIN_MSG_MAP(CDialogResize) END_MSG_MAP() BEGIN_DLGRESIZE_MAP(DumpSectionGui) DLGRESIZE_CONTROL(IDC_LIST_DUMPSECTION, DLSZ_SIZE_X | DLSZ_SIZE_Y) DLGRESIZE_CONTROL(IDC_BTN_DUMPSECTION_OK, DLSZ_MOVE_X | DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_DUMPSECTION_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BUTTON_SELECT_DESELECT, DLSZ_MOVE_Y) END_DLGRESIZE_MAP() DumpSectionGui() { imageBase = 0; - sizeOfImage = 0; + entryPoint = 0; fullpath[0] = 0; } //~DumpSectionGui(); DWORD_PTR imageBase; //VA - DWORD sizeOfImage; + DWORD_PTR entryPoint; WCHAR fullpath[MAX_PATH]; std::vector & getSectionList(); private: CListViewCtrl ListSectionSelect; CHexEdit EditListControl; std::vector sectionList; PeSection *selectedSection; bool isEditing; + int editingSubItem; + DWORD valueBeforeEditing; enum ListColumns { COL_NAME = 0, COL_VA, COL_VSize, COL_RVA, COL_RSize, COL_Characteristics }; int prevColumn; bool ascending; // Message handlers BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); LRESULT OnListSectionColumnClicked(NMHDR* pnmh); LRESULT OnListSectionClick(NMHDR* pnmh); LRESULT OnNMCustomdraw(NMHDR* pnmh); LRESULT OnListDoubleClick(NMHDR* pnmh); void OnSectionSelectAll(UINT uNotifyCode, int nID, CWindow wndCtl); void OnEditList(UINT uNotifyCode, int nID, CWindow wndCtl); void OnOK(UINT uNotifyCode, int nID, CWindow wndCtl); void OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl); // GUI functions void addColumnsToSectionList(CListViewCtrl& list); void displaySectionList(CListViewCtrl& list); static int CALLBACK listviewCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); WCHAR * getCharacteristicsString( DWORD characteristics ); void getAllSectionsFromFile(); void updateEditedItem(); void updateCheckState(); void selectOrDeselectAll(); }; \ No newline at end of file diff --git a/Scylla/ImportsHandling.cpp b/Scylla/ImportsHandling.cpp index dd005e9..c571def 100644 --- a/Scylla/ImportsHandling.cpp +++ b/Scylla/ImportsHandling.cpp @@ -1,807 +1,816 @@ #include "ImportsHandling.h" #include "Thunks.h" #include "Architecture.h" #include #include #include "multitree.h" // CMultiSelectTreeViewCtrl #include "resource.h" //#define DEBUG_COMMENTS void ImportThunk::invalidate() { ordinal = 0; hint = 0; valid = false; suspect = false; moduleName[0] = 0; name[0] = 0; } bool ImportModuleThunk::isValid() const { std::map::const_iterator iterator = thunkList.begin(); while (iterator != thunkList.end()) { if (iterator->second.valid == false) { return false; } iterator++; } return true; } DWORD_PTR ImportModuleThunk::getFirstThunk() const { if (thunkList.size() > 0) { const std::map::const_iterator iterator = thunkList.begin(); return iterator->first; } else { return 0; } } ImportsHandling::ImportsHandling(CMultiSelectTreeViewCtrl& TreeImports) : TreeImports(TreeImports) { hIconCheck.LoadIcon(IDI_ICON_CHECK, 16, 16); hIconWarning.LoadIcon(IDI_ICON_WARNING, 16, 16); hIconError.LoadIcon(IDI_ICON_ERROR, 16, 16); CDCHandle dc = CWindow(::GetDesktopWindow()).GetDC(); int bits = dc.GetDeviceCaps(BITSPIXEL); const UINT FLAGS = bits > 16 ? ILC_COLOR32 : (ILC_COLOR24 | ILC_MASK); TreeIcons.Create(16, 16, FLAGS, 3, 1); TreeIcons.AddIcon(hIconCheck); TreeIcons.AddIcon(hIconWarning); TreeIcons.AddIcon(hIconError); m_thunkCount = m_invalidThunkCount = m_suspectThunkCount = 0; } ImportsHandling::~ImportsHandling() { TreeIcons.Destroy(); } bool ImportsHandling::isModule(CTreeItem item) { return (0 != getModuleThunk(item)); } bool ImportsHandling::isImport(CTreeItem item) { return (0 != getImportThunk(item)); } ImportModuleThunk * ImportsHandling::getModuleThunk(CTreeItem item) { stdext::hash_map::const_iterator it; it = itemData.find(item); if(it != itemData.end()) { const TreeItemData * data = &it->second; if(data->isModule) { return data->module; } } return NULL; } ImportThunk * ImportsHandling::getImportThunk(CTreeItem item) { stdext::hash_map::const_iterator it; TreeItemData * data = getItemData(item); if(data && !data->isModule) { return data->import; } return NULL; } void ImportsHandling::setItemData(CTreeItem item, const TreeItemData * data) { itemData[item] = *data; } ImportsHandling::TreeItemData * ImportsHandling::getItemData(CTreeItem item) { stdext::hash_map::iterator it; it = itemData.find(item); if(it != itemData.end()) { return &it->second; } return NULL; } void ImportsHandling::updateCounts() { std::map::iterator it_module; std::map::iterator it_import; m_thunkCount = m_invalidThunkCount = m_suspectThunkCount = 0; it_module = moduleList.begin(); while (it_module != moduleList.end()) { ImportModuleThunk &moduleThunk = it_module->second; it_import = moduleThunk.thunkList.begin(); while (it_import != moduleThunk.thunkList.end()) { ImportThunk &importThunk = it_import->second; m_thunkCount++; if(!importThunk.valid) m_invalidThunkCount++; else if(importThunk.suspect) m_suspectThunkCount++; it_import++; } it_module++; } } /*bool ImportsHandling::addImport(const WCHAR * moduleName, const CHAR * name, DWORD_PTR va, DWORD_PTR rva, WORD ordinal, bool valid, bool suspect) { ImportThunk import; ImportModuleThunk * module = 0; std::map::iterator iterator1; if (moduleList.size() > 1) { iterator1 = moduleList.begin(); while (iterator1 != moduleList.end()) { if (rva >= iterator1->second.firstThunk) { iterator1++; if (iterator1 == moduleList.end()) { iterator1--; module = &(iterator1->second); break; } else if (rva < iterator1->second.firstThunk) { iterator1--; module = &(iterator1->second); break; } } } } else { iterator1 = moduleList.begin(); module = &(iterator1->second); } if (!module) { Scylla::debugLog.log(L"ImportsHandling::addFunction module not found rva " PRINTF_DWORD_PTR_FULL, rva); return false; } //TODO import.suspect = true; import.valid = false; import.va = va; import.rva = rva; import.ordinal = ordinal; wcscpy_s(import.moduleName, MAX_PATH, moduleName); strcpy_s(import.name, MAX_PATH, name); module->thunkList.insert(std::pair(import.rva, import)); return true; } */ /* bool ImportsHandling::addModule(const WCHAR * moduleName, DWORD_PTR firstThunk) { ImportModuleThunk module; module.firstThunk = firstThunk; wcscpy_s(module.moduleName, MAX_PATH, moduleName); moduleList.insert(std::pair(firstThunk,module)); return true; } */ void ImportsHandling::displayAllImports() { std::map::iterator it_module; std::map::iterator it_import; TreeImports.DeleteAllItems(); itemData.clear(); TreeImports.SetImageList(TreeIcons); it_module = moduleList.begin(); while (it_module != moduleList.end()) { ImportModuleThunk &moduleThunk = it_module->second; moduleThunk.key = moduleThunk.firstThunk; // This belongs elsewhere... moduleThunk.hTreeItem = addDllToTreeView(TreeImports, &moduleThunk); it_import = moduleThunk.thunkList.begin(); while (it_import != moduleThunk.thunkList.end()) { ImportThunk &importThunk = it_import->second; importThunk.key = importThunk.rva; // This belongs elsewhere... importThunk.hTreeItem = addApiToTreeView(TreeImports, moduleThunk.hTreeItem, &importThunk); it_import++; } it_module++; } updateCounts(); } void ImportsHandling::clearAllImports() { TreeImports.DeleteAllItems(); itemData.clear(); moduleList.clear(); updateCounts(); } CTreeItem ImportsHandling::addDllToTreeView(CMultiSelectTreeViewCtrl& idTreeView, ImportModuleThunk * moduleThunk) { CTreeItem item = idTreeView.InsertItem(L"", NULL, TVI_ROOT); item.SetData(itemData.size()); TreeItemData data; data.isModule = true; data.module = moduleThunk; setItemData(item, &data); updateModuleInTreeView(moduleThunk, item); return item; } CTreeItem ImportsHandling::addApiToTreeView(CMultiSelectTreeViewCtrl& idTreeView, CTreeItem parentDll, ImportThunk * importThunk) { CTreeItem item = idTreeView.InsertItem(L"", parentDll, TVI_LAST); item.SetData(itemData.size()); TreeItemData data; data.isModule = false; data.import = importThunk; setItemData(item, &data); updateImportInTreeView(importThunk, item); return item; } void ImportsHandling::selectImports(bool invalid, bool suspect) { std::map::iterator it_module; std::map::iterator it_import; TreeImports.SelectAllItems(FALSE); //remove selection it_module = moduleList.begin(); while (it_module != moduleList.end()) { ImportModuleThunk &moduleThunk = it_module->second; it_import = moduleThunk.thunkList.begin(); while (it_import != moduleThunk.thunkList.end()) { ImportThunk &importThunk = it_import->second; if ((invalid && !importThunk.valid) || (suspect && importThunk.suspect)) { TreeImports.SelectItem(importThunk.hTreeItem, TRUE); importThunk.hTreeItem.EnsureVisible(); } it_import++; } it_module++; } } bool ImportsHandling::invalidateImport(CTreeItem item) { ImportThunk * import = getImportThunk(item); if(import) { CTreeItem parent = item.GetParent(); if(!parent.IsNull()) { const ImportModuleThunk * module = getModuleThunk(parent); if(module) { import->invalidate(); updateImportInTreeView(import, import->hTreeItem); updateModuleInTreeView(module, module->hTreeItem); updateCounts(); return true; } } } return false; } bool ImportsHandling::invalidateModule(CTreeItem item) { ImportModuleThunk * module = getModuleThunk(item); if(module) { std::map::iterator it_import; it_import = module->thunkList.begin(); while(it_import != module->thunkList.end()) { ImportThunk * import = &it_import->second; import->invalidate(); updateImportInTreeView(import, import->hTreeItem); it_import++; } updateModuleInTreeView(module, module->hTreeItem); updateCounts(); return true; } return false; } bool ImportsHandling::setImport(CTreeItem item, const WCHAR * moduleName, const CHAR * apiName, WORD ordinal, WORD hint, bool valid, bool suspect) { ImportThunk * import = getImportThunk(item); if(import) { CTreeItem parent = item.GetParent(); if(!parent.IsNull()) { - const ImportModuleThunk * module = getModuleThunk(parent); + ImportModuleThunk * module = getModuleThunk(parent); if(module) { + wcscpy_s(import->moduleName, moduleName); strcpy_s(import->name, apiName); import->ordinal = ordinal; //import->apiAddressVA = api->va; //?? import->hint = hint; import->valid = valid; import->suspect = suspect; updateImportInTreeView(import, item); + + if (module->isValid() && module->moduleName[0] == L'?') + { + //update module name + wcscpy_s(module->moduleName, module->thunkList.begin()->second.moduleName); + } + updateModuleInTreeView(module, module->hTreeItem); updateCounts(); return true; } } } return false; } void ImportsHandling::updateImportInTreeView(const ImportThunk * importThunk, CTreeItem item) { if (importThunk->valid) { WCHAR tempString[300]; if (importThunk->name[0] != 0x00) { swprintf_s(tempString, L"ord: %04X name: %S", importThunk->ordinal, importThunk->name); } else { swprintf_s(tempString, L"ord: %04X", importThunk->ordinal); } swprintf_s(stringBuffer, L" rva: " PRINTF_DWORD_PTR_HALF L" mod: %s %s", importThunk->rva, importThunk->moduleName, tempString); } else { swprintf_s(stringBuffer, L" rva: " PRINTF_DWORD_PTR_HALF L" ptr: " PRINTF_DWORD_PTR_FULL, importThunk->rva, importThunk->apiAddressVA); } item.SetText(stringBuffer); Icon icon = getAppropiateIcon(importThunk); item.SetImage(icon, icon); } void ImportsHandling::updateModuleInTreeView(const ImportModuleThunk * importThunk, CTreeItem item) { swprintf_s(stringBuffer, L"%s (%d) FThunk: " PRINTF_DWORD_PTR_HALF, importThunk->moduleName,importThunk->thunkList.size(), importThunk->firstThunk); item.SetText(stringBuffer); Icon icon = getAppropiateIcon(importThunk->isValid()); item.SetImage(icon, icon); } ImportsHandling::Icon ImportsHandling::getAppropiateIcon(const ImportThunk * importThunk) { if(importThunk->valid) { if(importThunk->suspect) { return iconWarning; } else { return iconCheck; } } else { return iconError; } } ImportsHandling::Icon ImportsHandling::getAppropiateIcon(bool valid) { if(valid) { return iconCheck; } else { return iconError; } } bool ImportsHandling::cutImport(CTreeItem item) { ImportThunk * import = getImportThunk(item); if(import) { CTreeItem parent = item.GetParent(); if(!parent.IsNull()) { ImportModuleThunk * module = getModuleThunk(parent); if(module) { itemData.erase(item); import->hTreeItem.Delete(); module->thunkList.erase(import->key); import = 0; if (module->thunkList.empty()) { itemData.erase(parent); module->hTreeItem.Delete(); moduleList.erase(module->key); module = 0; } else { if (module->isValid() && module->moduleName[0] == L'?') { //update module name wcscpy_s(module->moduleName, module->thunkList.begin()->second.moduleName); } module->firstThunk = module->thunkList.begin()->second.rva; updateModuleInTreeView(module, module->hTreeItem); } updateCounts(); return true; } } } return false; } bool ImportsHandling::cutModule(CTreeItem item) { ImportModuleThunk * module = getModuleThunk(item); if(module) { CTreeItem child = item.GetChild(); while(!child.IsNull()) { itemData.erase(child); child = child.GetNextSibling(); } itemData.erase(item); module->hTreeItem.Delete(); moduleList.erase(module->key); module = 0; updateCounts(); return true; } return false; } DWORD_PTR ImportsHandling::getApiAddressByNode(CTreeItem item) { const ImportThunk * import = getImportThunk(item); if(import) { return import->apiAddressVA; } return 0; } void ImportsHandling::scanAndFixModuleList() { std::map::iterator it_module; std::map::iterator it_import; it_module = moduleList.begin(); while (it_module != moduleList.end()) { ImportModuleThunk &moduleThunk = it_module->second; it_import = moduleThunk.thunkList.begin(); while (it_import != moduleThunk.thunkList.end()) { ImportThunk &importThunk = it_import->second; if (importThunk.moduleName[0] == 0 || importThunk.moduleName[0] == L'?') { addNotFoundApiToModuleList(&importThunk); } else { if (isNewModule(importThunk.moduleName)) { addModuleToModuleList(importThunk.moduleName, importThunk.rva); } addFunctionToModuleList(&importThunk); } it_import++; } moduleThunk.thunkList.clear(); it_module++; } moduleList = moduleListNew; moduleListNew.clear(); } bool ImportsHandling::findNewModules(std::map & thunkList) { throw std::exception("The method or operation is not implemented."); } bool ImportsHandling::addModuleToModuleList(const WCHAR * moduleName, DWORD_PTR firstThunk) { ImportModuleThunk module; module.firstThunk = firstThunk; wcscpy_s(module.moduleName, moduleName); module.key = module.firstThunk; moduleListNew[module.key] = module; return true; } bool ImportsHandling::isNewModule(const WCHAR * moduleName) { std::map::iterator it_module; it_module = moduleListNew.begin(); while (it_module != moduleListNew.end()) { if (!_wcsicmp(it_module->second.moduleName, moduleName)) { return false; } it_module++; } return true; } void ImportsHandling::addUnknownModuleToModuleList(DWORD_PTR firstThunk) { ImportModuleThunk module; module.firstThunk = firstThunk; wcscpy_s(module.moduleName, L"?"); module.key = module.firstThunk; moduleListNew[module.key] = module; } bool ImportsHandling::addNotFoundApiToModuleList(const ImportThunk * apiNotFound) { ImportThunk import; ImportModuleThunk * module = 0; std::map::iterator it_module; DWORD_PTR rva = apiNotFound->rva; if (moduleListNew.size() > 0) { it_module = moduleListNew.begin(); while (it_module != moduleListNew.end()) { if (rva >= it_module->second.firstThunk) { it_module++; if (it_module == moduleListNew.end()) { it_module--; //new unknown module if (it_module->second.moduleName[0] == L'?') { module = &(it_module->second); } else { addUnknownModuleToModuleList(apiNotFound->rva); module = &(moduleListNew.find(rva)->second); } break; } else if (rva < it_module->second.firstThunk) { it_module--; module = &(it_module->second); break; } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"Error iterator1 != (*moduleThunkList).end()"); #endif break; } } } else { //new unknown module addUnknownModuleToModuleList(apiNotFound->rva); module = &(moduleListNew.find(rva)->second); } if (!module) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"ImportsHandling::addFunction module not found rva " PRINTF_DWORD_PTR_FULL, rva); #endif return false; } import.suspect = true; import.valid = false; import.va = apiNotFound->va; import.rva = apiNotFound->rva; import.apiAddressVA = apiNotFound->apiAddressVA; import.ordinal = 0; wcscpy_s(import.moduleName, L"?"); strcpy_s(import.name, "?"); import.key = import.rva; module->thunkList[import.key] = import; return true; } bool ImportsHandling::addFunctionToModuleList(const ImportThunk * apiFound) { ImportThunk import; ImportModuleThunk * module = 0; std::map::iterator it_module; if (moduleListNew.size() > 1) { it_module = moduleListNew.begin(); while (it_module != moduleListNew.end()) { if (apiFound->rva >= it_module->second.firstThunk) { it_module++; if (it_module == moduleListNew.end()) { it_module--; module = &(it_module->second); break; } else if (apiFound->rva < it_module->second.firstThunk) { it_module--; module = &(it_module->second); break; } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"Error iterator1 != moduleListNew.end()"); #endif break; } } } else { it_module = moduleListNew.begin(); module = &(it_module->second); } if (!module) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"ImportsHandling::addFunction module not found rva " PRINTF_DWORD_PTR_FULL, apiFound->rva); #endif return false; } import.suspect = apiFound->suspect; import.valid = apiFound->valid; import.va = apiFound->va; import.rva = apiFound->rva; import.apiAddressVA = apiFound->apiAddressVA; import.ordinal = apiFound->ordinal; import.hint = apiFound->hint; wcscpy_s(import.moduleName, apiFound->moduleName); strcpy_s(import.name, apiFound->name); import.key = import.rva; module->thunkList[import.key] = import; return true; } void ImportsHandling::expandAllTreeNodes() { changeExpandStateOfTreeNodes(TVE_EXPAND); } void ImportsHandling::collapseAllTreeNodes() { changeExpandStateOfTreeNodes(TVE_COLLAPSE); } void ImportsHandling::changeExpandStateOfTreeNodes(UINT flag) { std::map::iterator it_module; it_module = moduleList.begin(); while (it_module != moduleList.end()) { ImportModuleThunk &moduleThunk = it_module->second; moduleThunk.hTreeItem.Expand(flag); it_module++; } } + diff --git a/Scylla/ImportsHandling.h b/Scylla/ImportsHandling.h index ef4444d..d1fd9e0 100644 --- a/Scylla/ImportsHandling.h +++ b/Scylla/ImportsHandling.h @@ -1,112 +1,113 @@ #pragma once #include #include // WTL #include #include #include // CTreeItem class CMultiSelectTreeViewCtrl; class ImportThunk; class ImportModuleThunk; class ImportsHandling { public: std::map moduleList; std::map moduleListNew; ImportsHandling(CMultiSelectTreeViewCtrl& TreeImports); ~ImportsHandling(); unsigned int thunkCount() const { return m_thunkCount; } unsigned int invalidThunkCount() const { return m_invalidThunkCount; } unsigned int suspectThunkCount() const { return m_suspectThunkCount; } bool isModule(CTreeItem item); bool isImport(CTreeItem item); ImportModuleThunk * getModuleThunk(CTreeItem item); ImportThunk * getImportThunk(CTreeItem item); void displayAllImports(); void clearAllImports(); void selectImports(bool invalid, bool suspect); bool invalidateImport(CTreeItem item); bool invalidateModule(CTreeItem item); bool setImport(CTreeItem item, const WCHAR * moduleName, const CHAR * apiName, WORD ordinal = 0, WORD hint = 0, bool valid = true, bool suspect = false); bool cutImport(CTreeItem item); bool cutModule(CTreeItem item); //bool addImport(const WCHAR * moduleName, const CHAR * name, DWORD_PTR va, DWORD_PTR rva, WORD ordinal = 0, bool valid = true, bool suspect = false); //bool addModule(const WCHAR * moduleName, DWORD_PTR firstThunk); DWORD_PTR getApiAddressByNode(CTreeItem selectedTreeNode); void scanAndFixModuleList(); void expandAllTreeNodes(); void collapseAllTreeNodes(); private: DWORD numberOfFunctions; unsigned int m_thunkCount; unsigned int m_invalidThunkCount; unsigned int m_suspectThunkCount; struct TreeItemData { bool isModule; union { ImportModuleThunk * module; ImportThunk * import; }; }; stdext::hash_map itemData; void setItemData(CTreeItem item, const TreeItemData * data); TreeItemData * getItemData(CTreeItem item); WCHAR stringBuffer[600]; CMultiSelectTreeViewCtrl& TreeImports; CImageList TreeIcons; CIcon hIconCheck; CIcon hIconWarning; CIcon hIconError; // They have to be added to the image list in that order! enum Icon { iconCheck = 0, iconWarning, iconError }; void updateCounts(); CTreeItem addDllToTreeView(CMultiSelectTreeViewCtrl& idTreeView, ImportModuleThunk * moduleThunk); CTreeItem addApiToTreeView(CMultiSelectTreeViewCtrl& idTreeView, CTreeItem parentDll, ImportThunk * importThunk); void updateImportInTreeView(const ImportThunk * importThunk, CTreeItem item); void updateModuleInTreeView(const ImportModuleThunk * importThunk, CTreeItem item); //bool isItemSelected(CTreeItem hItem); //void unselectItem(CTreeItem htItem); //bool selectItem(CTreeItem hItem, bool select = true); bool findNewModules(std::map & thunkList); Icon getAppropiateIcon(const ImportThunk * importThunk); Icon getAppropiateIcon(bool valid); bool addModuleToModuleList(const WCHAR * moduleName, DWORD_PTR firstThunk); void addUnknownModuleToModuleList(DWORD_PTR firstThunk); bool addNotFoundApiToModuleList(const ImportThunk * apiNotFound); bool addFunctionToModuleList(const ImportThunk * apiFound); bool isNewModule(const WCHAR * moduleName); void changeExpandStateOfTreeNodes(UINT flag); + }; diff --git a/Scylla/MainGui.cpp b/Scylla/MainGui.cpp index 188a08a..22ccdda 100644 --- a/Scylla/MainGui.cpp +++ b/Scylla/MainGui.cpp @@ -1,1334 +1,1365 @@ #include "MainGui.h" #include "Architecture.h" //#include "PluginLoader.h" //#include "ConfigurationHolder.h" -#include "PeDump.h" +//#include "PeDump.h" //#include "PeRebuild.h" #include "PeParser.h" #include "DllInjectionPlugin.h" #include "DisassemblerGui.h" #include "PickApiGui.h" //#include "NativeWinApi.h" #include "ImportRebuild.h" #include "SystemInformation.h" #include "Scylla.h" #include "AboutGui.h" #include "OptionsGui.h" #include "TreeImportExport.h" extern CAppModule _Module; // o_O const WCHAR MainGui::filterExe[] = L"Executable (*.exe)\0*.exe\0All files\0*.*\0"; const WCHAR MainGui::filterDll[] = L"Dynamic Link Library (*.dll)\0*.dll\0All files\0*.*\0"; const WCHAR MainGui::filterExeDll[] = L"Executable (*.exe)\0*.exe\0Dynamic Link Library (*.dll)\0*.dll\0All files\0*.*\0"; const WCHAR MainGui::filterTxt[] = L"Text file (*.txt)\0*.txt\0All files\0*.*\0"; const WCHAR MainGui::filterXml[] = L"XML file (*.xml)\0*.xml\0All files\0*.*\0"; const WCHAR MainGui::filterMem[] = L"MEM file (*.mem)\0*.mem\0All files\0*.*\0"; MainGui::MainGui() : selectedProcess(0), importsHandling(TreeImports), TreeImportsSubclass(this, IDC_TREE_IMPORTS) { /* Logger::getDebugLogFilePath(); ConfigurationHolder::loadConfiguration(); PluginLoader::findAllPlugins(); NativeWinApi::initialize(); SystemInformation::getSystemInformation(); if(ConfigurationHolder::getConfigObject(DEBUG_PRIVILEGE)->isTrue()) { processLister.setDebugPrivileges(); } ProcessAccessHelp::getProcessModules(GetCurrentProcessId(), ProcessAccessHelp::ownModuleList); */ Scylla::init(); hIcon.LoadIcon(IDI_ICON_SCYLLA); hMenuImports.LoadMenu(IDR_MENU_IMPORTS); hMenuLog.LoadMenu(IDR_MENU_LOG); accelerators.LoadAccelerators(IDR_ACCELERATOR_MAIN); hIconCheck.LoadIcon(IDI_ICON_CHECK, 16, 16); hIconWarning.LoadIcon(IDI_ICON_WARNING, 16, 16); hIconError.LoadIcon(IDI_ICON_ERROR, 16, 16); } BOOL MainGui::PreTranslateMessage(MSG* pMsg) { if(accelerators.TranslateAccelerator(m_hWnd, pMsg)) { return TRUE; // handled keyboard shortcuts } else if(IsDialogMessage(pMsg)) { return TRUE; // handled dialog messages } return FALSE; } BOOL MainGui::OnInitDialog(CWindow wndFocus, LPARAM lInitParam) { if (SystemInformation::currenOS == UNKNOWN_OS) { if(IDCANCEL == MessageBox(L"Operating System is not supported\r\nContinue anyway?", L"Scylla", MB_ICONWARNING | MB_OKCANCEL)) { SendMessage(WM_CLOSE); return FALSE; } } // register ourselves to receive PreTranslateMessage CMessageLoop* pLoop = _Module.GetMessageLoop(); pLoop->AddMessageFilter(this); setupStatusBar(); DoDataExchange(); // attach controls DlgResize_Init(true, true); // init CDialogResize Scylla::windowLog.setWindow(ListLog); appendPluginListToMenu(hMenuImports.GetSubMenu(0)); appendPluginListToMenu(CMenuHandle(GetMenu()).GetSubMenu(MenuImportsOffsetTrace)); enableDialogControls(FALSE); setIconAndDialogCaption(); return TRUE; } void MainGui::OnDestroy() { PostQuitMessage(0); } void MainGui::OnSize(UINT nType, CSize size) { StatusBar.SendMessage(WM_SIZE); SetMsgHandled(FALSE); } void MainGui::OnContextMenu(CWindow wnd, CPoint point) { switch(wnd.GetDlgCtrlID()) { case IDC_TREE_IMPORTS: DisplayContextMenuImports(wnd, point); return; case IDC_LIST_LOG: DisplayContextMenuLog(wnd, point); return; } SetMsgHandled(FALSE); } void MainGui::OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl) { // Handle plugin trace menu selection if(uNotifyCode == 0 && !wndCtl.IsWindow()) // make sure it's a menu { if ((nID >= PLUGIN_MENU_BASE_ID) && (nID <= (int)(Scylla::plugins.getScyllaPluginList().size() + Scylla::plugins.getImprecPluginList().size() + PLUGIN_MENU_BASE_ID))) { pluginActionHandler(nID); return; } } SetMsgHandled(FALSE); } LRESULT MainGui::OnTreeImportsDoubleClick(const NMHDR* pnmh) { if(TreeImports.GetCount() < 1) return 0; // Get item under cursor CTreeItem over = findTreeItem(CPoint(GetMessagePos()), true); if(over && importsHandling.isImport(over)) { pickApiActionHandler(over); } return 0; } LRESULT MainGui::OnTreeImportsKeyDown(const NMHDR* pnmh) { const NMTVKEYDOWN * tkd = (NMTVKEYDOWN *)pnmh; switch(tkd->wVKey) { case VK_RETURN: { CTreeItem selected = TreeImports.GetFocusItem(); if(!selected.IsNull() && importsHandling.isImport(selected)) { pickApiActionHandler(selected); } } return 1; case VK_DELETE: deleteSelectedImportsActionHandler(); return 1; } SetMsgHandled(FALSE); return 0; } UINT MainGui::OnTreeImportsSubclassGetDlgCode(const MSG * lpMsg) { if(lpMsg) { switch(lpMsg->wParam) { case VK_RETURN: return DLGC_WANTMESSAGE; } } SetMsgHandled(FALSE); return 0; } void MainGui::OnTreeImportsSubclassChar(UINT nChar, UINT nRepCnt, UINT nFlags) { switch(nChar) { case VK_RETURN: break; default: SetMsgHandled(FALSE); break; } } void MainGui::OnProcessListDrop(UINT uNotifyCode, int nID, CWindow wndCtl) { fillProcessListComboBox(ComboProcessList); } void MainGui::OnProcessListSelected(UINT uNotifyCode, int nID, CWindow wndCtl) { processSelectedActionHandler(ComboProcessList.GetCurSel()); } void MainGui::OnPickDLL(UINT uNotifyCode, int nID, CWindow wndCtl) { pickDllActionHandler(); } void MainGui::OnOptions(UINT uNotifyCode, int nID, CWindow wndCtl) { optionsActionHandler(); } void MainGui::OnDump(UINT uNotifyCode, int nID, CWindow wndCtl) { dumpActionHandler(); } void MainGui::OnDumpMemory(UINT uNotifyCode, int nID, CWindow wndCtl) { dumpMemoryActionHandler(); } void MainGui::OnDumpSection(UINT uNotifyCode, int nID, CWindow wndCtl) { dumpSectionActionHandler(); } void MainGui::OnFixDump(UINT uNotifyCode, int nID, CWindow wndCtl) { dumpFixActionHandler(); } void MainGui::OnPERebuild(UINT uNotifyCode, int nID, CWindow wndCtl) { peRebuildActionHandler(); } void MainGui::OnDLLInject(UINT uNotifyCode, int nID, CWindow wndCtl) { dllInjectActionHandler(); } void MainGui::OnIATAutoSearch(UINT uNotifyCode, int nID, CWindow wndCtl) { iatAutosearchActionHandler(); } void MainGui::OnGetImports(UINT uNotifyCode, int nID, CWindow wndCtl) { getImportsActionHandler(); } void MainGui::OnInvalidImports(UINT uNotifyCode, int nID, CWindow wndCtl) { showInvalidImportsActionHandler(); } void MainGui::OnSuspectImports(UINT uNotifyCode, int nID, CWindow wndCtl) { showSuspectImportsActionHandler(); } void MainGui::OnClearImports(UINT uNotifyCode, int nID, CWindow wndCtl) { clearImportsActionHandler(); } void MainGui::OnInvalidateSelected(UINT uNotifyCode, int nID, CWindow wndCtl) { invalidateSelectedImportsActionHandler(); } void MainGui::OnCutSelected(UINT uNotifyCode, int nID, CWindow wndCtl) { deleteSelectedImportsActionHandler(); } void MainGui::OnSaveTree(UINT uNotifyCode, int nID, CWindow wndCtl) { saveTreeActionHandler(); } void MainGui::OnLoadTree(UINT uNotifyCode, int nID, CWindow wndCtl) { loadTreeActionHandler(); } void MainGui::OnAutotrace(UINT uNotifyCode, int nID, CWindow wndCtl) { // TODO } void MainGui::OnExit(UINT uNotifyCode, int nID, CWindow wndCtl) { DestroyWindow(); } void MainGui::OnAbout(UINT uNotifyCode, int nID, CWindow wndCtl) { showAboutDialog(); } void MainGui::setupStatusBar() { StatusBar.Create(m_hWnd, NULL, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | SBARS_TOOLTIPS, NULL, IDC_STATUS_BAR); CRect rcMain, rcStatus; GetClientRect(&rcMain); StatusBar.GetWindowRect(&rcStatus); const int PARTS = 4; int widths[PARTS]; widths[PART_COUNT] = rcMain.Width() / 5; widths[PART_INVALID] = widths[PART_COUNT] + rcMain.Width() / 5; widths[PART_IMAGEBASE] = widths[PART_INVALID] + rcMain.Width() / 3; widths[PART_MODULE] = -1; StatusBar.SetParts(PARTS, widths); ResizeClient(rcMain.Width(), rcMain.Height() + rcStatus.Height(), FALSE); } void MainGui::updateStatusBar() { // Rewrite ImportsHandling so we get these easily unsigned int totalImports = importsHandling.thunkCount(); unsigned int invalidImports = importsHandling.invalidThunkCount(); // \t = center, \t\t = right-align swprintf_s(stringBuffer, L"\tImports: %u", totalImports); StatusBar.SetText(PART_COUNT, stringBuffer); if(invalidImports > 0) { StatusBar.SetIcon(PART_INVALID, hIconError); } else { StatusBar.SetIcon(PART_INVALID, hIconCheck); } swprintf_s(stringBuffer, L"\tInvalid: %u", invalidImports); StatusBar.SetText(PART_INVALID, stringBuffer); if(selectedProcess) { DWORD_PTR imageBase = 0; const WCHAR * fileName = 0; if(ProcessAccessHelp::selectedModule) { imageBase = ProcessAccessHelp::selectedModule->modBaseAddr; fileName = ProcessAccessHelp::selectedModule->getFilename(); } else { imageBase = selectedProcess->imageBase; fileName = selectedProcess->filename; } swprintf_s(stringBuffer, L"\tImagebase: " PRINTF_DWORD_PTR_FULL, imageBase); StatusBar.SetText(PART_IMAGEBASE, stringBuffer); StatusBar.SetText(PART_MODULE, fileName); StatusBar.SetTipText(PART_MODULE, fileName); } else { StatusBar.SetText(PART_IMAGEBASE, L""); StatusBar.SetText(PART_MODULE, L""); } } bool MainGui::showFileDialog(WCHAR * selectedFile, bool save, const WCHAR * defFileName, const WCHAR * filter, const WCHAR * defExtension, const WCHAR * directory) { OPENFILENAME ofn = {0}; // WTL doesn't support new explorer styles on Vista and up // This is because it uses a custom hook, we could remove it or derive // from CFileDialog but this solution is easier and allows more control anyway (e.g. initial dir) if(defFileName) { wcscpy_s(selectedFile, MAX_PATH, defFileName); } else { selectedFile[0] = L'\0'; } ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = m_hWnd; ofn.lpstrFilter = filter; ofn.lpstrDefExt = defExtension; // only first 3 chars are used, no dots! ofn.lpstrFile = selectedFile; ofn.lpstrInitialDir = directory; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; /* *OFN_EXPLORER is automatically used, it only has to be specified *if using a custom hook *OFN_LONGNAMES is automatically used by explorer-style dialogs */ if(save) ofn.Flags |= OFN_OVERWRITEPROMPT; else ofn.Flags |= OFN_FILEMUSTEXIST; if(save) return 0 != GetSaveFileName(&ofn); else return 0 != GetOpenFileName(&ofn); } void MainGui::setIconAndDialogCaption() { SetIcon(hIcon, TRUE); SetIcon(hIcon, FALSE); SetWindowText(APPNAME L" " ARCHITECTURE L" " APPVERSION); } void MainGui::pickDllActionHandler() { if(!selectedProcess) return; PickDllGui dlgPickDll(ProcessAccessHelp::moduleList); if(dlgPickDll.DoModal()) { //get selected module ProcessAccessHelp::selectedModule = dlgPickDll.getSelectedModule(); ProcessAccessHelp::targetImageBase = ProcessAccessHelp::selectedModule->modBaseAddr; Scylla::windowLog.log(L"->>> Module %s selected.", ProcessAccessHelp::selectedModule->getFilename()); Scylla::windowLog.log(L"Imagebase: " PRINTF_DWORD_PTR_FULL L" Size: %08X", ProcessAccessHelp::selectedModule->modBaseAddr, ProcessAccessHelp::selectedModule->modBaseSize); } else { ProcessAccessHelp::selectedModule = 0; } updateStatusBar(); } void MainGui::pickApiActionHandler(CTreeItem item) { if(!importsHandling.isImport(item)) return; // TODO: new node when user picked an API from another DLL? PickApiGui dlgPickApi(ProcessAccessHelp::moduleList); if(dlgPickApi.DoModal()) { const ApiInfo* api = dlgPickApi.getSelectedApi(); if(api && api->module) { importsHandling.setImport(item, api->module->getFilename(), api->name, api->ordinal, api->hint, true, api->isForwarded); } } updateStatusBar(); } void MainGui::startDisassemblerGui(CTreeItem selectedTreeNode) { if(!selectedProcess) return; DWORD_PTR address = importsHandling.getApiAddressByNode(selectedTreeNode); if (address) { BYTE test; if(!ProcessAccessHelp::readMemoryFromProcess(address, sizeof(test), &test)) { swprintf_s(stringBuffer, L"Can't read memory at " PRINTF_DWORD_PTR_FULL, address); MessageBox(stringBuffer, L"Failure", MB_ICONERROR); } else { DisassemblerGui dlgDisassembler(address); dlgDisassembler.DoModal(); } } } void MainGui::processSelectedActionHandler(int index) { std::vector& processList = Scylla::processLister.getProcessList(); Process &process = processList.at(index); selectedProcess = 0; clearImportsActionHandler(); Scylla::windowLog.log(L"Analyzing %s", process.fullPath); if (ProcessAccessHelp::hProcess != 0) { ProcessAccessHelp::closeProcessHandle(); apiReader.clearAll(); } if (!ProcessAccessHelp::openProcessHandle(process.PID)) { enableDialogControls(FALSE); Scylla::windowLog.log(L"Error: Cannot open process handle."); updateStatusBar(); return; } ProcessAccessHelp::getProcessModules(process.PID, ProcessAccessHelp::moduleList); apiReader.readApisFromModuleList(); Scylla::windowLog.log(L"Loading modules done."); //TODO improve ProcessAccessHelp::selectedModule = 0; ProcessAccessHelp::targetSizeOfImage = process.imageSize; ProcessAccessHelp::targetImageBase = process.imageBase; ProcessAccessHelp::getSizeOfImageCurrentProcess(); process.imageSize = (DWORD)ProcessAccessHelp::targetSizeOfImage; Scylla::windowLog.log(L"Imagebase: " PRINTF_DWORD_PTR_FULL L" Size: %08X", process.imageBase, process.imageSize); process.entryPoint = ProcessAccessHelp::getEntryPointFromFile(process.fullPath); EditOEPAddress.SetValue(process.entryPoint + process.imageBase); selectedProcess = &process; enableDialogControls(TRUE); updateStatusBar(); } void MainGui::fillProcessListComboBox(CComboBox& hCombo) { hCombo.ResetContent(); std::vector& processList = Scylla::processLister.getProcessListSnapshot(); for (size_t i = 0; i < processList.size(); i++) { swprintf_s(stringBuffer, L"0x%04X - %s - %s", processList[i].PID, processList[i].filename, processList[i].fullPath); hCombo.AddString(stringBuffer); } } /* void MainGui::addTextToOutputLog(const WCHAR * text) { if (m_hWnd) { ListLog.SetCurSel(ListLog.AddString(text)); } } */ void MainGui::clearOutputLog() { if (m_hWnd) { ListLog.ResetContent(); } } bool MainGui::saveLogToFile(const WCHAR * file) { const BYTE BOM[] = {0xFF, 0xFE}; // UTF-16 little-endian const WCHAR newLine[] = L"\r\n"; bool success = true; HANDLE hFile = CreateFile(file, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if(hFile != INVALID_HANDLE_VALUE) { ProcessAccessHelp::writeMemoryToFileEnd(hFile, sizeof(BOM), BOM); WCHAR * buffer = 0; size_t bufsize = 0; for(int i = 0; i < ListLog.GetCount(); i++) { size_t size = ListLog.GetTextLen(i); size += _countof(newLine)-1; if(size+1 > bufsize) { bufsize = size+1; delete[] buffer; try { buffer = new WCHAR[bufsize]; } catch(std::bad_alloc&) { buffer = 0; success = false; break; } } ListLog.GetText(i, buffer); wcscat_s(buffer, bufsize, newLine); ProcessAccessHelp::writeMemoryToFileEnd(hFile, (DWORD)(size * sizeof(WCHAR)), buffer); } delete[] buffer; CloseHandle(hFile); } return success; } void MainGui::showInvalidImportsActionHandler() { importsHandling.selectImports(true, false); GotoDlgCtrl(TreeImports); } void MainGui::showSuspectImportsActionHandler() { importsHandling.selectImports(false, true); GotoDlgCtrl(TreeImports); } void MainGui::deleteSelectedImportsActionHandler() { CTreeItem selected = TreeImports.GetFirstSelectedItem(); while(!selected.IsNull()) { if(importsHandling.isModule(selected)) { importsHandling.cutModule(selected); } else { importsHandling.cutImport(selected); } selected = TreeImports.GetNextSelectedItem(selected); } updateStatusBar(); } void MainGui::invalidateSelectedImportsActionHandler() { CTreeItem selected = TreeImports.GetFirstSelectedItem(); while(!selected.IsNull()) { if(importsHandling.isImport(selected)) { importsHandling.invalidateImport(selected); } selected = TreeImports.GetNextSelectedItem(selected); } updateStatusBar(); } void MainGui::loadTreeActionHandler() { if(!selectedProcess) return; WCHAR selectedFilePath[MAX_PATH]; getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if(showFileDialog(selectedFilePath, false, NULL, filterXml, NULL, stringBuffer)) { TreeImportExport treeIO(selectedFilePath); DWORD_PTR addrOEP = 0; DWORD_PTR addrIAT = 0; DWORD sizeIAT = 0; if(!treeIO.importTreeList(importsHandling.moduleList, &addrOEP, &addrIAT, &sizeIAT)) { Scylla::windowLog.log(L"Loading tree file failed %s", selectedFilePath); MessageBox(L"Loading tree file failed.", L"Failure", MB_ICONERROR); } else { EditOEPAddress.SetValue(addrOEP); EditIATAddress.SetValue(addrIAT); EditIATSize.SetValue(sizeIAT); importsHandling.displayAllImports(); updateStatusBar(); Scylla::windowLog.log(L"Loaded tree file %s", selectedFilePath); Scylla::windowLog.log(L"-> OEP: " PRINTF_DWORD_PTR_FULL, addrOEP); Scylla::windowLog.log(L"-> IAT: " PRINTF_DWORD_PTR_FULL L" Size: " PRINTF_DWORD_PTR, addrIAT, sizeIAT); } } } void MainGui::saveTreeActionHandler() { if(!selectedProcess) return; WCHAR selectedFilePath[MAX_PATH]; getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if(showFileDialog(selectedFilePath, true, NULL, filterXml, L"xml", stringBuffer)) { TreeImportExport treeIO(selectedFilePath); DWORD_PTR addrOEP = EditOEPAddress.GetValue(); DWORD_PTR addrIAT = EditIATAddress.GetValue(); DWORD sizeIAT = EditIATSize.GetValue(); if(!treeIO.exportTreeList(importsHandling.moduleList, selectedProcess, addrOEP, addrIAT, sizeIAT)) { Scylla::windowLog.log(L"Saving tree file failed %s", selectedFilePath); MessageBox(L"Saving tree file failed.", L"Failure", MB_ICONERROR); } else { Scylla::windowLog.log(L"Saved tree file %s", selectedFilePath); } } } void MainGui::iatAutosearchActionHandler() { DWORD_PTR searchAddress = 0; DWORD_PTR addressIAT = 0; DWORD sizeIAT = 0; IATSearch iatSearch; if(!selectedProcess) return; if(EditOEPAddress.GetWindowTextLength() > 0) { searchAddress = EditOEPAddress.GetValue(); if (searchAddress) { if (iatSearch.searchImportAddressTableInProcess(searchAddress, &addressIAT, &sizeIAT)) { Scylla::windowLog.log(L"IAT found at VA " PRINTF_DWORD_PTR_FULL L" RVA " PRINTF_DWORD_PTR_FULL L" Size 0x%04X (%d)", addressIAT, addressIAT - ProcessAccessHelp::targetImageBase, sizeIAT, sizeIAT); EditIATAddress.SetValue(addressIAT); EditIATSize.SetValue(sizeIAT); swprintf_s(stringBuffer, L"IAT found:\r\n\r\nStart: " PRINTF_DWORD_PTR_FULL L"\r\nSize: 0x%04X (%d) ", addressIAT, sizeIAT, sizeIAT); MessageBox(stringBuffer, L"IAT found", MB_ICONINFORMATION); } else { Scylla::windowLog.log(L"IAT not found at OEP " PRINTF_DWORD_PTR_FULL L"!", searchAddress); } } } } void MainGui::getImportsActionHandler() { if(!selectedProcess) return; DWORD_PTR addressIAT = EditIATAddress.GetValue(); DWORD sizeIAT = EditIATSize.GetValue(); if (addressIAT && sizeIAT) { apiReader.readAndParseIAT(addressIAT, sizeIAT, importsHandling.moduleList); importsHandling.displayAllImports(); updateStatusBar(); } } void MainGui::SetupImportsMenuItems(CTreeItem item) { bool isItem, isImport = false; isItem = !item.IsNull(); if(isItem) { isImport = importsHandling.isImport(item); } CMenuHandle hSub = hMenuImports.GetSubMenu(0); UINT itemOnly = isItem ? MF_ENABLED : MF_GRAYED; UINT importOnly = isImport ? MF_ENABLED : MF_GRAYED; hSub.EnableMenuItem(ID__INVALIDATE, itemOnly); hSub.EnableMenuItem(ID__DISASSEMBLE, importOnly); hSub.EnableMenuItem(ID__CUTTHUNK, importOnly); hSub.EnableMenuItem(ID__DELETETREENODE, itemOnly); } void MainGui::DisplayContextMenuImports(CWindow hwnd, CPoint pt) { if(TreeImports.GetCount() < 1) return; CTreeItem over, parent; if(pt.x == -1 && pt.y == -1) // invoked by keyboard { CRect pos; over = TreeImports.GetFocusItem(); if(over) { over.EnsureVisible(); over.GetRect(&pos, TRUE); TreeImports.ClientToScreen(&pos); } else { TreeImports.GetWindowRect(&pos); } pt = pos.TopLeft(); } else { // Get item under cursor over = findTreeItem(pt, true); } SetupImportsMenuItems(over); CMenuHandle hSub = hMenuImports.GetSubMenu(0); BOOL menuItem = hSub.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, hwnd); if (menuItem) { if ((menuItem >= PLUGIN_MENU_BASE_ID) && (menuItem <= (int)(Scylla::plugins.getScyllaPluginList().size() + Scylla::plugins.getImprecPluginList().size() + PLUGIN_MENU_BASE_ID))) { //wsprintf(stringBuffer, L"%d %s\n",menuItem,pluginList[menuItem - PLUGIN_MENU_BASE_ID].pluginName); //MessageBox(stringBuffer, L"plugin selection"); pluginActionHandler(menuItem); return; } switch (menuItem) { case ID__INVALIDATE: if(importsHandling.isModule(over)) importsHandling.invalidateModule(over); else importsHandling.invalidateImport(over); break; case ID__DISASSEMBLE: startDisassemblerGui(over); break; case ID__EXPANDALLNODES: importsHandling.expandAllTreeNodes(); break; case ID__COLLAPSEALLNODES: importsHandling.collapseAllTreeNodes(); break; case ID__CUTTHUNK: importsHandling.cutImport(over); break; case ID__DELETETREENODE: importsHandling.cutModule(importsHandling.isImport(over) ? over.GetParent() : over); break; } } updateStatusBar(); } void MainGui::DisplayContextMenuLog(CWindow hwnd, CPoint pt) { if(pt.x == -1 && pt.y == -1) // invoked by keyboard { CRect pos; ListLog.GetWindowRect(&pos); pt = pos.TopLeft(); } CMenuHandle hSub = hMenuLog.GetSubMenu(0); BOOL menuItem = hSub.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, hwnd); if (menuItem) { switch (menuItem) { case ID__SAVE: WCHAR selectedFilePath[MAX_PATH]; getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if(showFileDialog(selectedFilePath, true, NULL, filterTxt, L"txt", stringBuffer)) { saveLogToFile(selectedFilePath); } break; case ID__CLEAR: clearOutputLog(); break; } } } void MainGui::appendPluginListToMenu(CMenuHandle hMenu) { std::vector &scyllaPluginList = Scylla::plugins.getScyllaPluginList(); std::vector &imprecPluginList = Scylla::plugins.getImprecPluginList(); if (scyllaPluginList.size() > 0) { CMenuHandle newMenu; newMenu.CreatePopupMenu(); for (size_t i = 0; i < scyllaPluginList.size(); i++) { newMenu.AppendMenu(MF_STRING, i + PLUGIN_MENU_BASE_ID, scyllaPluginList[i].pluginName); } hMenu.AppendMenu(MF_MENUBARBREAK); hMenu.AppendMenu(MF_POPUP, newMenu, L"Scylla Plugins"); } if (imprecPluginList.size() > 0) { CMenuHandle newMenu; newMenu.CreatePopupMenu(); for (size_t i = 0; i < imprecPluginList.size(); i++) { newMenu.AppendMenu(MF_STRING, scyllaPluginList.size() + i + PLUGIN_MENU_BASE_ID, imprecPluginList[i].pluginName); } hMenu.AppendMenu(MF_MENUBARBREAK); hMenu.AppendMenu(MF_POPUP, newMenu, L"ImpREC Plugins"); } } void MainGui::dumpMemoryActionHandler() { WCHAR selectedFilePath[MAX_PATH]; DumpMemoryGui dlgDumpMemory; if(dlgDumpMemory.DoModal()) { getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if(showFileDialog(selectedFilePath, true, dlgDumpMemory.dumpFilename, filterMem, L"mem", stringBuffer)) { if (ProcessAccessHelp::writeMemoryToNewFile(selectedFilePath,dlgDumpMemory.dumpedMemorySize,dlgDumpMemory.dumpedMemory)) { Scylla::windowLog.log(L"Memory dump saved %s", selectedFilePath); } else { Scylla::windowLog.log(L"Error! Cannot write memory dump to disk"); } } } } void MainGui::dumpSectionActionHandler() { WCHAR selectedFilePath[MAX_PATH]; DumpSectionGui dlgDumpSection; + const WCHAR * fileFilter; + const WCHAR * defExtension; + PeParser * peFile = 0; + + dlgDumpSection.entryPoint = EditOEPAddress.GetValue(); if (ProcessAccessHelp::selectedModule) { //dump DLL + fileFilter = filterDll; + defExtension = L"dll"; dlgDumpSection.imageBase = ProcessAccessHelp::selectedModule->modBaseAddr; - dlgDumpSection.sizeOfImage = ProcessAccessHelp::selectedModule->modBaseSize; //get it from gui wcscpy_s(dlgDumpSection.fullpath, ProcessAccessHelp::selectedModule->fullPath); } else { + fileFilter = filterExe; + defExtension = L"exe"; + dlgDumpSection.imageBase = ProcessAccessHelp::targetImageBase; - dlgDumpSection.sizeOfImage = (DWORD)ProcessAccessHelp::targetSizeOfImage; //get it from gui wcscpy_s(dlgDumpSection.fullpath, selectedProcess->fullPath); } if(dlgDumpSection.DoModal()) { - //getCurrentModulePath(stringBuffer, _countof(stringBuffer)); - //if(showFileDialog(selectedFilePath, true, dlgDumpMemory.dumpFilename, filterMem, L"mem", stringBuffer)) - //{ - // if (ProcessAccessHelp::writeMemoryToNewFile(selectedFilePath,dlgDumpMemory.dumpedMemorySize,dlgDumpMemory.dumpedMemory)) - // { - // Scylla::windowLog.log(L"Memory dump saved %s", selectedFilePath); - // } - // else - // { - // Scylla::windowLog.log(L"Error! Cannot write memory dump to disk"); - // } - //} + getCurrentModulePath(stringBuffer, _countof(stringBuffer)); + if(showFileDialog(selectedFilePath, true, NULL, fileFilter, defExtension, stringBuffer)) + { + if (Scylla::config[USE_PE_HEADER_FROM_DISK].isTrue()) + { + peFile = new PeParser(dlgDumpSection.fullpath, true); + } + else + { + peFile = new PeParser(dlgDumpSection.imageBase, true); + } + + std::vector & sectionList = dlgDumpSection.getSectionList(); + + if (peFile->dumpProcess(dlgDumpSection.imageBase, dlgDumpSection.entryPoint, selectedFilePath, sectionList)) + { + Scylla::windowLog.log(L"Dump success %s", selectedFilePath); + } + else + { + Scylla::windowLog.log(L"Error: Cannot dump image."); + MessageBox(L"Cannot dump image.", L"Failure", MB_ICONERROR); + } + + delete peFile; + } } } void MainGui::dumpActionHandler() { if(!selectedProcess) return; WCHAR selectedFilePath[MAX_PATH]; const WCHAR * fileFilter; const WCHAR * defExtension; - PeDump peDump; + DWORD_PTR modBase = 0; + DWORD_PTR entrypoint = 0; + WCHAR * filename = 0; + PeParser * peFile = 0; if (ProcessAccessHelp::selectedModule) { fileFilter = filterDll; defExtension = L"dll"; } else { fileFilter = filterExe; defExtension = L"exe"; } getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if(showFileDialog(selectedFilePath, true, NULL, fileFilter, defExtension, stringBuffer)) { + entrypoint = EditOEPAddress.GetValue(); + if (ProcessAccessHelp::selectedModule) { //dump DLL - - peDump.imageBase = ProcessAccessHelp::selectedModule->modBaseAddr; - peDump.sizeOfImage = ProcessAccessHelp::selectedModule->modBaseSize; - //get it from gui - peDump.entryPoint = EditOEPAddress.GetValue(); - wcscpy_s(peDump.fullpath, ProcessAccessHelp::selectedModule->fullPath); + modBase = ProcessAccessHelp::selectedModule->modBaseAddr; + filename = ProcessAccessHelp::selectedModule->fullPath; + } + else + { + //dump exe + modBase = ProcessAccessHelp::targetImageBase; + filename = selectedProcess->fullPath; + } + + if (Scylla::config[USE_PE_HEADER_FROM_DISK].isTrue()) + { + peFile = new PeParser(filename, true); } else { - peDump.imageBase = ProcessAccessHelp::targetImageBase; - peDump.sizeOfImage = (DWORD)ProcessAccessHelp::targetSizeOfImage; - //get it from gui - peDump.entryPoint = EditOEPAddress.GetValue(); - wcscpy_s(peDump.fullpath, selectedProcess->fullPath); + peFile = new PeParser(modBase, true); } - peDump.useHeaderFromDisk = Scylla::config[USE_PE_HEADER_FROM_DISK].isTrue(); - if (peDump.dumpCompleteProcessToDisk(selectedFilePath)) + if (peFile->dumpProcess(modBase, entrypoint, selectedFilePath)) { Scylla::windowLog.log(L"Dump success %s", selectedFilePath); } else { Scylla::windowLog.log(L"Error: Cannot dump image."); MessageBox(L"Cannot dump image.", L"Failure", MB_ICONERROR); } + + delete peFile; } } void MainGui::peRebuildActionHandler() { DWORD newSize = 0; WCHAR selectedFilePath[MAX_PATH]; getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if(showFileDialog(selectedFilePath, false, NULL, filterExeDll, NULL, stringBuffer)) { if (Scylla::config[CREATE_BACKUP].isTrue()) { if (!ProcessAccessHelp::createBackupFile(selectedFilePath)) { Scylla::windowLog.log(L"Creating backup file failed %s", selectedFilePath); } } DWORD fileSize = (DWORD)ProcessAccessHelp::getFileSize(selectedFilePath); PeParser peFile(selectedFilePath, true); if (peFile.readPeSectionsFromFile()) { peFile.setDefaultFileAlignment(); if (Scylla::config[REMOVE_DOS_HEADER_STUB].isTrue()) { peFile.removeDosStub(); } peFile.alignAllSectionHeaders(); peFile.fixPeHeader(); if (peFile.savePeFileToDisk(selectedFilePath)) { newSize = (DWORD)ProcessAccessHelp::getFileSize(selectedFilePath); Scylla::windowLog.log(L"Rebuild success %s", selectedFilePath); Scylla::windowLog.log(L"-> Old file size 0x%08X new file size 0x%08X (%d %%)", fileSize, newSize, ((newSize * 100) / fileSize) ); } else { Scylla::windowLog.log(L"Rebuild failed, cannot save file %s", selectedFilePath); MessageBox(L"Rebuild failed. Cannot save file.", L"Failure", MB_ICONERROR); } } else { Scylla::windowLog.log(L"Rebuild failed, cannot read file %s", selectedFilePath); MessageBox(L"Rebuild failed. Cannot read file.", L"Failure", MB_ICONERROR); } } } void MainGui::dumpFixActionHandler() { if(!selectedProcess) return; if (TreeImports.GetCount() < 2) { Scylla::windowLog.log(L"Nothing to rebuild"); return; } WCHAR newFilePath[MAX_PATH]; WCHAR selectedFilePath[MAX_PATH]; const WCHAR * fileFilter; if (ProcessAccessHelp::selectedModule) { fileFilter = filterDll; } else { fileFilter = filterExe; } getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if (showFileDialog(selectedFilePath, false, NULL, fileFilter, NULL, stringBuffer)) { wcscpy_s(newFilePath, selectedFilePath); const WCHAR * extension = 0; WCHAR* dot = wcsrchr(newFilePath, L'.'); if (dot) { *dot = L'\0'; extension = selectedFilePath + (dot - newFilePath); //wcsrchr(selectedFilePath, L'.'); } wcscat_s(newFilePath, L"_SCY"); if(extension) { wcscat_s(newFilePath, extension); } ImportRebuild importRebuild; if (importRebuild.rebuildImportTable(selectedFilePath,newFilePath,importsHandling.moduleList)) { Scylla::windowLog.log(L"Import Rebuild success %s", newFilePath); } else { Scylla::windowLog.log(L"Import Rebuild failed %s", selectedFilePath); MessageBox(L"Import Rebuild failed", L"Failure", MB_ICONERROR); } } } void MainGui::enableDialogControls(BOOL value) { BOOL valButton = value ? TRUE : FALSE; GetDlgItem(IDC_BTN_PICKDLL).EnableWindow(valButton); GetDlgItem(IDC_BTN_DUMP).EnableWindow(valButton); GetDlgItem(IDC_BTN_FIXDUMP).EnableWindow(valButton); GetDlgItem(IDC_BTN_IATAUTOSEARCH).EnableWindow(valButton); GetDlgItem(IDC_BTN_GETIMPORTS).EnableWindow(valButton); GetDlgItem(IDC_BTN_SUSPECTIMPORTS).EnableWindow(valButton); GetDlgItem(IDC_BTN_INVALIDIMPORTS).EnableWindow(valButton); GetDlgItem(IDC_BTN_CLEARIMPORTS).EnableWindow(valButton); CMenuHandle menu = GetMenu(); UINT valMenu = value ? MF_ENABLED : MF_GRAYED; menu.EnableMenuItem(ID_FILE_DUMP, valMenu); menu.EnableMenuItem(ID_FILE_DUMPMEMORY, valMenu); menu.EnableMenuItem(ID_FILE_DUMPSECTION, valMenu); menu.EnableMenuItem(ID_FILE_FIXDUMP, valMenu); menu.EnableMenuItem(ID_IMPORTS_INVALIDATESELECTED, valMenu); menu.EnableMenuItem(ID_IMPORTS_CUTSELECTED, valMenu); menu.EnableMenuItem(ID_IMPORTS_SAVETREE, valMenu); menu.EnableMenuItem(ID_IMPORTS_LOADTREE, valMenu); menu.EnableMenuItem(ID_MISC_DLLINJECTION, valMenu); menu.GetSubMenu(MenuImportsOffsetTrace).EnableMenuItem(MenuImportsTraceOffsetScylla, MF_BYPOSITION | valMenu); menu.GetSubMenu(MenuImportsOffsetTrace).EnableMenuItem(MenuImportsTraceOffsetImpRec, MF_BYPOSITION | valMenu); //not yet implemented GetDlgItem(IDC_BTN_AUTOTRACE).EnableWindow(FALSE); menu.EnableMenuItem(ID_TRACE_AUTOTRACE, MF_GRAYED); } CTreeItem MainGui::findTreeItem(CPoint pt, bool screenCoordinates) { if(screenCoordinates) { TreeImports.ScreenToClient(&pt); } UINT flags; CTreeItem over = TreeImports.HitTest(pt, &flags); if(over) { if(!(flags & TVHT_ONITEM)) { over.m_hTreeItem = NULL; } } return over; } void MainGui::showAboutDialog() { AboutGui dlgAbout; dlgAbout.DoModal(); } void MainGui::dllInjectActionHandler() { if(!selectedProcess) return; WCHAR selectedFilePath[MAX_PATH]; HMODULE hMod = 0; DllInjection dllInjection; getCurrentModulePath(stringBuffer, _countof(stringBuffer)); if (showFileDialog(selectedFilePath, false, NULL, filterDll, NULL, stringBuffer)) { hMod = dllInjection.dllInjection(ProcessAccessHelp::hProcess, selectedFilePath); if (hMod && Scylla::config[DLL_INJECTION_AUTO_UNLOAD].isTrue()) { if (!dllInjection.unloadDllInProcess(ProcessAccessHelp::hProcess, hMod)) { Scylla::windowLog.log(L"DLL unloading failed, target %s", selectedFilePath); } } if (hMod) { Scylla::windowLog.log(L"DLL Injection was successful, target %s", selectedFilePath); } else { Scylla::windowLog.log(L"DLL Injection failed, target %s", selectedFilePath); } } } void MainGui::optionsActionHandler() { OptionsGui dlgOptions; dlgOptions.DoModal(); } void MainGui::clearImportsActionHandler() { importsHandling.clearAllImports(); updateStatusBar(); } void MainGui::pluginActionHandler( int menuItem ) { if(!selectedProcess) return; DllInjectionPlugin dllInjectionPlugin; std::vector &scyllaPluginList = Scylla::plugins.getScyllaPluginList(); std::vector &imprecPluginList = Scylla::plugins.getImprecPluginList(); menuItem -= PLUGIN_MENU_BASE_ID; dllInjectionPlugin.hProcess = ProcessAccessHelp::hProcess; dllInjectionPlugin.apiReader = &apiReader; if (menuItem < (int)scyllaPluginList.size()) { //scylla plugin dllInjectionPlugin.injectPlugin(scyllaPluginList[menuItem], importsHandling.moduleList,selectedProcess->imageBase, selectedProcess->imageSize); } else { #ifndef _WIN64 menuItem -= (int)scyllaPluginList.size(); //imprec plugin dllInjectionPlugin.injectImprecPlugin(imprecPluginList[menuItem], importsHandling.moduleList,selectedProcess->imageBase, selectedProcess->imageSize); #endif } importsHandling.scanAndFixModuleList(); importsHandling.displayAllImports(); updateStatusBar(); } bool MainGui::getCurrentModulePath(WCHAR * buffer, size_t bufferSize) { if(!selectedProcess) return false; if(ProcessAccessHelp::selectedModule) { wcscpy_s(buffer, bufferSize, ProcessAccessHelp::selectedModule->fullPath); } else { wcscpy_s(buffer, bufferSize, selectedProcess->fullPath); } WCHAR * slash = wcsrchr(buffer, L'\\'); if(slash) { *(slash+1) = L'\0'; } return true; } diff --git a/Scylla/PeParser.cpp b/Scylla/PeParser.cpp index b3584e5..cfba855 100644 --- a/Scylla/PeParser.cpp +++ b/Scylla/PeParser.cpp @@ -1,1046 +1,1162 @@ #include "PeParser.h" #include "ProcessAccessHelp.h" #include PeParser::PeParser() { initClass(); } PeParser::PeParser(const WCHAR * file, bool readSectionHeaders) { initClass(); filename = file; if (filename && wcslen(filename) > 3) { readPeHeaderFromFile(readSectionHeaders); if (readSectionHeaders) { if (isValidPeFile()) { getSectionHeaders(); } } } } PeParser::PeParser(const DWORD_PTR moduleBase, bool readSectionHeaders) { initClass(); moduleBaseAddress = moduleBase; if (moduleBaseAddress) { readPeHeaderFromProcess(readSectionHeaders); if (readSectionHeaders) { if (isValidPeFile()) { getSectionHeaders(); } } } } PeParser::~PeParser() { if (headerMemory) { delete [] headerMemory; } if (fileMemory) { delete [] fileMemory; } for (size_t i = 0; i < listPeSection.size(); i++) { if (listPeSection[i].data) { delete [] listPeSection[i].data; } } listPeSection.clear(); } void PeParser::initClass() { fileMemory = 0; headerMemory = 0; pDosHeader = 0; pDosStub = 0; dosStubSize = 0; pNTHeader32 = 0; pNTHeader64 = 0; overlayData = 0; overlaySize = 0; filename = 0; fileSize = 0; moduleBaseAddress = 0; hFile = INVALID_HANDLE_VALUE; } bool PeParser::isPE64() { if (isValidPeFile()) { return (pNTHeader32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC); } else { return false; } } bool PeParser::isPE32() { if (isValidPeFile()) { return (pNTHeader32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC); } else { return false; } } bool PeParser::isTargetFileSamePeFormat() { #ifdef _WIN64 return isPE64(); #else return isPE32(); #endif } bool PeParser::isValidPeFile() { bool retValue = false; if (pDosHeader) { if (pDosHeader->e_magic == IMAGE_DOS_SIGNATURE) { if (pNTHeader32) { if (pNTHeader32->Signature == IMAGE_NT_SIGNATURE) { retValue = true; } } } } return retValue; } bool PeParser::hasDirectory(const int directoryIndex) { if (isPE32()) { return (pNTHeader32->OptionalHeader.DataDirectory[directoryIndex].VirtualAddress != 0); } else if (isPE64()) { return (pNTHeader64->OptionalHeader.DataDirectory[directoryIndex].VirtualAddress != 0); } else { return false; } } bool PeParser::hasExportDirectory() { return hasDirectory(IMAGE_DIRECTORY_ENTRY_EXPORT); } bool PeParser::hasTLSDirectory() { return hasDirectory(IMAGE_DIRECTORY_ENTRY_TLS); } bool PeParser::hasRelocationDirectory() { return hasDirectory(IMAGE_DIRECTORY_ENTRY_BASERELOC); } DWORD PeParser::getEntryPoint() { if (isPE32()) { return pNTHeader32->OptionalHeader.AddressOfEntryPoint; } else if (isPE64()) { return pNTHeader64->OptionalHeader.AddressOfEntryPoint; } else { return 0; } } bool PeParser::readPeHeaderFromProcess(bool readSectionHeaders) { bool retValue = false; DWORD correctSize = 0; DWORD readSize = getInitialHeaderReadSize(readSectionHeaders); headerMemory = new BYTE[readSize]; if (ProcessAccessHelp::readMemoryPartlyFromProcess(moduleBaseAddress, readSize, headerMemory)) { retValue = true; getDosAndNtHeader(headerMemory, (LONG)readSize); if (isValidPeFile()) { correctSize = calcCorrectPeHeaderSize(readSectionHeaders); if (readSize < correctSize) { readSize = correctSize; delete [] headerMemory; headerMemory = new BYTE[readSize]; if (ProcessAccessHelp::readMemoryPartlyFromProcess(moduleBaseAddress, readSize, headerMemory)) { getDosAndNtHeader(headerMemory, (LONG)readSize); } } } } return retValue; } bool PeParser::readPeHeaderFromFile(bool readSectionHeaders) { bool retValue = false; DWORD correctSize = 0; DWORD numberOfBytesRead = 0; DWORD readSize = getInitialHeaderReadSize(readSectionHeaders); headerMemory = new BYTE[readSize]; if (openFileHandle()) { fileSize = (DWORD)ProcessAccessHelp::getFileSize(hFile); if (ReadFile(hFile, headerMemory, readSize, &numberOfBytesRead, 0)) { retValue = true; getDosAndNtHeader(headerMemory, (LONG)readSize); if (isValidPeFile()) { correctSize = calcCorrectPeHeaderSize(readSectionHeaders); if (readSize < correctSize) { readSize = correctSize; if (fileSize > 0) { if (fileSize < correctSize) { readSize = fileSize; } } delete [] headerMemory; headerMemory = new BYTE[readSize]; SetFilePointer(hFile, 0, 0, FILE_BEGIN); if (ReadFile(hFile, headerMemory, readSize, &numberOfBytesRead, 0)) { getDosAndNtHeader(headerMemory, (LONG)readSize); } } } } closeFileHandle(); } return retValue; } bool PeParser::readPeSectionsFromProcess() { bool retValue = true; DWORD_PTR readOffset = 0; listPeSection.reserve(getNumberOfSections()); for (WORD i = 0; i < getNumberOfSections(); i++) { readOffset = listPeSection[i].sectionHeader.VirtualAddress + moduleBaseAddress; listPeSection[i].normalSize = listPeSection[i].sectionHeader.Misc.VirtualSize; if (!readSectionFromProcess(readOffset, listPeSection[i])) { retValue = false; } } return retValue; } bool PeParser::readPeSectionsFromFile() { bool retValue = true; DWORD readOffset = 0; listPeSection.reserve(getNumberOfSections()); if (openFileHandle()) { for (WORD i = 0; i < getNumberOfSections(); i++) { readOffset = listPeSection[i].sectionHeader.PointerToRawData; listPeSection[i].normalSize = listPeSection[i].sectionHeader.SizeOfRawData; if (!readSectionFromFile(readOffset, listPeSection[i])) { retValue = false; } } closeFileHandle(); } else { retValue = false; } return retValue; } bool PeParser::getSectionHeaders() { PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNTHeader32); PeFileSection peFileSection; listPeSection.clear(); listPeSection.reserve(getNumberOfSections()); for (WORD i = 0; i < getNumberOfSections(); i++) { memcpy_s(&peFileSection.sectionHeader, sizeof(IMAGE_SECTION_HEADER), pSection, sizeof(IMAGE_SECTION_HEADER)); listPeSection.push_back(peFileSection); pSection++; } return true; } bool PeParser::getSectionNameUnicode(const int sectionIndex, WCHAR * output, const int outputLen) { CHAR sectionNameA[IMAGE_SIZEOF_SHORT_NAME + 1] = {0}; output[0] = 0; memcpy(sectionNameA, listPeSection[sectionIndex].sectionHeader.Name, IMAGE_SIZEOF_SHORT_NAME); //not null terminated return (swprintf_s(output, outputLen, L"%S", sectionNameA) != -1); } WORD PeParser::getNumberOfSections() { return pNTHeader32->FileHeader.NumberOfSections; } void PeParser::setNumberOfSections(WORD numberOfSections) { pNTHeader32->FileHeader.NumberOfSections = numberOfSections; } std::vector & PeParser::getSectionHeaderList() { return listPeSection; } void PeParser::getDosAndNtHeader(BYTE * memory, LONG size) { pDosHeader = (PIMAGE_DOS_HEADER)memory; pNTHeader32 = 0; pNTHeader64 = 0; dosStubSize = 0; pDosStub = 0; if (pDosHeader->e_lfanew > 0 && pDosHeader->e_lfanew < size) //malformed PE { pNTHeader32 = (PIMAGE_NT_HEADERS32)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); pNTHeader64 = (PIMAGE_NT_HEADERS64)((DWORD_PTR)pDosHeader + pDosHeader->e_lfanew); if (pDosHeader->e_lfanew > sizeof(IMAGE_DOS_HEADER)) { dosStubSize = pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER); pDosStub = (BYTE *)((DWORD_PTR)pDosHeader + sizeof(IMAGE_DOS_HEADER)); } } } DWORD PeParser::calcCorrectPeHeaderSize(bool readSectionHeaders) { DWORD correctSize = pDosHeader->e_lfanew + 50; //extra buffer if (readSectionHeaders) { correctSize += getNumberOfSections() * sizeof(IMAGE_SECTION_HEADER); } if (isPE32()) { correctSize += sizeof(IMAGE_NT_HEADERS32); } else if(isPE64()) { correctSize += sizeof(IMAGE_NT_HEADERS64); } else { correctSize = 0; //not a valid PE } return correctSize; } DWORD PeParser::getInitialHeaderReadSize(bool readSectionHeaders) { DWORD readSize = sizeof(IMAGE_DOS_HEADER) + 200 + sizeof(IMAGE_NT_HEADERS64); if (readSectionHeaders) { readSize += (10 * sizeof(IMAGE_SECTION_HEADER)); } return readSize; } DWORD PeParser::getSectionHeaderBasedFileSize() { DWORD lastRawOffset = 0, lastRawSize = 0; //this is needed if the sections aren't sorted by their RawOffset (e.g. Petite) for (WORD i = 0; i < getNumberOfSections(); i++) { if (listPeSection[i].sectionHeader.PointerToRawData > lastRawOffset) { lastRawOffset = listPeSection[i].sectionHeader.PointerToRawData; lastRawSize = listPeSection[i].sectionHeader.SizeOfRawData; } } return (lastRawSize + lastRawOffset); } DWORD PeParser::getSectionHeaderBasedSizeOfImage() { DWORD lastVirtualOffset = 0, lastVirtualSize = 0; //this is needed if the sections aren't sorted by their RawOffset (e.g. Petite) for (WORD i = 0; i < getNumberOfSections(); i++) { if (listPeSection[i].sectionHeader.VirtualAddress > lastVirtualOffset) { lastVirtualOffset = listPeSection[i].sectionHeader.VirtualAddress; lastVirtualSize = listPeSection[i].sectionHeader.Misc.VirtualSize; } } return (lastVirtualSize + lastVirtualOffset); } bool PeParser::openFileHandle() { if (hFile == INVALID_HANDLE_VALUE) { if (filename) { hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); } else { hFile = INVALID_HANDLE_VALUE; } } return (hFile != INVALID_HANDLE_VALUE); } bool PeParser::openWriteFileHandle( const WCHAR * newFile ) { if (newFile) { hFile = CreateFile(newFile, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); } else { hFile = INVALID_HANDLE_VALUE; } return (hFile != INVALID_HANDLE_VALUE); } void PeParser::closeFileHandle() { if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } } bool PeParser::readSectionFromFile(DWORD readOffset, PeFileSection & peFileSection) { const DWORD maxReadSize = 100; BYTE data[maxReadSize]; DWORD bytesRead = 0; bool retValue = true; DWORD valuesFound = 0; DWORD currentOffset = 0; DWORD readSize = 0; peFileSection.data = 0; peFileSection.dataSize = 0; readSize = peFileSection.normalSize; if (!readOffset || !readSize) { return true; //section without data is valid } if (readSize <= maxReadSize) { peFileSection.dataSize = readSize; peFileSection.normalSize = readSize; return readPeSectionFromFile(readOffset, peFileSection); } currentOffset = readOffset + readSize - maxReadSize; while(currentOffset >= readOffset) //start from the end { SetFilePointer(hFile, currentOffset, 0, FILE_BEGIN); ZeroMemory(data, sizeof(data)); if (!ReadFile(hFile, data, sizeof(data), &bytesRead, 0)) { retValue = false; break; } valuesFound = isMemoryNotNull(data, sizeof(data)); if (valuesFound) { //found some real code currentOffset += valuesFound; if (readOffset < currentOffset) { //real size peFileSection.dataSize = currentOffset - readOffset; } break; } currentOffset -= maxReadSize; } if (peFileSection.dataSize) { retValue = readPeSectionFromFile(readOffset, peFileSection); } return retValue; } DWORD PeParser::isMemoryNotNull( BYTE * data, int dataSize ) { for (int i = (dataSize - 1); i >= 0; i--) { if (data[i] != 0) { return i + 1; } } return 0; } bool PeParser::savePeFileToDisk( const WCHAR * newFile ) { bool retValue = true; DWORD dwFileOffset = 0, dwWriteSize = 0; if (getNumberOfSections() != listPeSection.size()) { return false; } if (openWriteFileHandle(newFile)) { //Dos header dwWriteSize = sizeof(IMAGE_DOS_HEADER); if (!ProcessAccessHelp::writeMemoryToFile(hFile, dwFileOffset, dwWriteSize, pDosHeader)) { retValue = false; } dwFileOffset += dwWriteSize; if (dosStubSize && pDosStub) { //Dos Stub dwWriteSize = dosStubSize; if (!ProcessAccessHelp::writeMemoryToFile(hFile, dwFileOffset, dwWriteSize, pDosStub)) { retValue = false; } dwFileOffset += dwWriteSize; } //Pe Header if (isPE32()) { dwWriteSize = sizeof(IMAGE_NT_HEADERS32); } else { dwWriteSize = sizeof(IMAGE_NT_HEADERS64); } if (!ProcessAccessHelp::writeMemoryToFile(hFile, dwFileOffset, dwWriteSize, pNTHeader32)) { retValue = false; } dwFileOffset += dwWriteSize; //section headers dwWriteSize = sizeof(IMAGE_SECTION_HEADER); for (WORD i = 0; i < getNumberOfSections(); i++) { if (!ProcessAccessHelp::writeMemoryToFile(hFile, dwFileOffset, dwWriteSize, &listPeSection[i].sectionHeader)) { retValue = false; break; } dwFileOffset += dwWriteSize; } for (WORD i = 0; i < getNumberOfSections(); i++) { if (!listPeSection[i].sectionHeader.PointerToRawData) continue; if (listPeSection[i].sectionHeader.PointerToRawData > dwFileOffset) { dwWriteSize = listPeSection[i].sectionHeader.PointerToRawData - dwFileOffset; //padding if (!writeZeroMemoryToFile(hFile, dwFileOffset, dwWriteSize)) { retValue = false; break; } dwFileOffset += dwWriteSize; } dwWriteSize = listPeSection[i].dataSize; if (dwWriteSize) { if (!ProcessAccessHelp::writeMemoryToFile(hFile, listPeSection[i].sectionHeader.PointerToRawData, dwWriteSize, listPeSection[i].data)) { retValue = false; break; } dwFileOffset += dwWriteSize; if (listPeSection[i].dataSize < listPeSection[i].sectionHeader.SizeOfRawData) //padding { dwWriteSize = listPeSection[i].sectionHeader.SizeOfRawData - listPeSection[i].dataSize; if (!writeZeroMemoryToFile(hFile, dwFileOffset, dwWriteSize)) { retValue = false; break; } dwFileOffset += dwWriteSize; } } } //add overlay? if (overlaySize && overlayData) { dwWriteSize = overlaySize; if (!ProcessAccessHelp::writeMemoryToFile(hFile, dwFileOffset, dwWriteSize, overlayData)) { retValue = false; } dwFileOffset += dwWriteSize; } SetEndOfFile(hFile); closeFileHandle(); } return retValue; } bool PeParser::writeZeroMemoryToFile(HANDLE hFile, DWORD fileOffset, DWORD size) { bool retValue = false; PVOID zeromemory = calloc(size, 1); if (zeromemory) { retValue = ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, size, zeromemory); free(zeromemory); } return retValue; } void PeParser::removeDosStub() { if (pDosHeader) { dosStubSize = 0; pDosStub = 0; //must not delete [] pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER); } } bool PeParser::readPeSectionFromFile(DWORD readOffset, PeFileSection & peFileSection) { DWORD bytesRead = 0; peFileSection.data = new BYTE[peFileSection.dataSize]; SetFilePointer(hFile, readOffset, 0, FILE_BEGIN); return (ReadFile(hFile, peFileSection.data, peFileSection.dataSize, &bytesRead, 0) != FALSE); } bool PeParser::readSectionFromProcess( DWORD_PTR readOffset, PeFileSection & peFileSection ) { const DWORD maxReadSize = 100; BYTE data[maxReadSize]; bool retValue = true; DWORD valuesFound = 0; DWORD readSize = 0; DWORD_PTR currentOffset = 0; peFileSection.data = 0; peFileSection.dataSize = 0; readSize = peFileSection.normalSize; if (!readOffset || !readSize) { return true; //section without data is valid } if (readSize <= maxReadSize) { peFileSection.dataSize = readSize; peFileSection.normalSize = readSize; return readPeSectionFromProcess(readOffset, peFileSection); } currentOffset = readOffset + readSize - maxReadSize; while(currentOffset >= readOffset) //start from the end { if (!ProcessAccessHelp::readMemoryPartlyFromProcess(currentOffset, sizeof(data), data)) { retValue = false; break; } valuesFound = isMemoryNotNull(data, sizeof(data)); if (valuesFound) { //found some real code currentOffset += valuesFound; if (readOffset < currentOffset) { //real size peFileSection.dataSize = (DWORD)(currentOffset - readOffset); } break; } currentOffset -= maxReadSize; } if (peFileSection.dataSize) { retValue = readPeSectionFromProcess(readOffset, peFileSection); } return retValue; } bool PeParser::readPeSectionFromProcess(DWORD_PTR readOffset, PeFileSection & peFileSection) { peFileSection.data = new BYTE[peFileSection.dataSize]; return ProcessAccessHelp::readMemoryPartlyFromProcess(readOffset, peFileSection.dataSize, peFileSection.data); } DWORD PeParser::alignValue(DWORD badValue, DWORD alignTo) { return (((badValue + alignTo - 1) / alignTo) * alignTo); } bool PeParser::addNewLastSection(const CHAR * sectionName, DWORD sectionSize, BYTE * sectionData) { size_t nameLength = strlen(sectionName); DWORD fileAlignment = 0, sectionAlignment = 0; PeFileSection peFileSection; if (nameLength > IMAGE_SIZEOF_SHORT_NAME) { return false; } if (isPE32()) { fileAlignment = pNTHeader32->OptionalHeader.FileAlignment; sectionAlignment = pNTHeader32->OptionalHeader.SectionAlignment; } else { fileAlignment = pNTHeader64->OptionalHeader.FileAlignment; sectionAlignment = pNTHeader64->OptionalHeader.SectionAlignment; } memcpy_s(peFileSection.sectionHeader.Name, IMAGE_SIZEOF_SHORT_NAME, sectionName, nameLength); //last section doesn't need SizeOfRawData alignment peFileSection.sectionHeader.SizeOfRawData = sectionSize; //alignValue(sectionSize, fileAlignment); peFileSection.sectionHeader.Misc.VirtualSize = alignValue(sectionSize, sectionAlignment); peFileSection.sectionHeader.PointerToRawData = alignValue(getSectionHeaderBasedFileSize(), fileAlignment); peFileSection.sectionHeader.VirtualAddress = alignValue(getSectionHeaderBasedSizeOfImage(), sectionAlignment); peFileSection.sectionHeader.Characteristics = IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA; peFileSection.normalSize = peFileSection.sectionHeader.SizeOfRawData; peFileSection.dataSize = peFileSection.sectionHeader.SizeOfRawData; if (sectionData == 0) { peFileSection.data = new BYTE[peFileSection.sectionHeader.SizeOfRawData]; ZeroMemory(peFileSection.data , peFileSection.sectionHeader.SizeOfRawData); } else { peFileSection.data = sectionData; } listPeSection.push_back(peFileSection); setNumberOfSections(getNumberOfSections() + 1); return true; } DWORD_PTR PeParser::convertRVAToOffsetVector(DWORD_PTR dwRVA) { for (WORD i = 0; i < getNumberOfSections(); i++) { if ((listPeSection[i].sectionHeader.VirtualAddress <= dwRVA) && ((listPeSection[i].sectionHeader.VirtualAddress + listPeSection[i].sectionHeader.Misc.VirtualSize) > dwRVA)) { return ((dwRVA - listPeSection[i].sectionHeader.VirtualAddress) + listPeSection[i].sectionHeader.PointerToRawData); } } return 0; } DWORD_PTR PeParser::convertOffsetToRVAVector(DWORD_PTR dwOffset) { for (WORD i = 0; i < getNumberOfSections(); i++) { if ((listPeSection[i].sectionHeader.PointerToRawData <= dwOffset) && ((listPeSection[i].sectionHeader.PointerToRawData + listPeSection[i].sectionHeader.SizeOfRawData) > dwOffset)) { return ((dwOffset - listPeSection[i].sectionHeader.PointerToRawData) + listPeSection[i].sectionHeader.VirtualAddress); } } return 0; } void PeParser::fixPeHeader() { DWORD dwSize = pDosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER); if (isPE32()) { //delete bound import directories pNTHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; pNTHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; //max 16 if (pNTHeader64->OptionalHeader.NumberOfRvaAndSizes > 0x10) { pNTHeader64->OptionalHeader.NumberOfRvaAndSizes = 0x10; } pNTHeader32->OptionalHeader.SizeOfImage = getSectionHeaderBasedSizeOfImage(); + if (moduleBaseAddress) + { + pNTHeader32->OptionalHeader.ImageBase = (DWORD)moduleBaseAddress; + } + pNTHeader32->OptionalHeader.SizeOfHeaders = alignValue(dwSize + pNTHeader32->FileHeader.SizeOfOptionalHeader + (getNumberOfSections() * sizeof(IMAGE_SECTION_HEADER)), pNTHeader32->OptionalHeader.FileAlignment); } else { //delete bound import directories pNTHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; pNTHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; //max 16 if (pNTHeader64->OptionalHeader.NumberOfRvaAndSizes > 0x10) { pNTHeader64->OptionalHeader.NumberOfRvaAndSizes = 0x10; } pNTHeader64->OptionalHeader.SizeOfImage = getSectionHeaderBasedSizeOfImage(); + if (moduleBaseAddress) + { + pNTHeader64->OptionalHeader.ImageBase = moduleBaseAddress; + } + pNTHeader64->OptionalHeader.SizeOfHeaders = alignValue(dwSize + pNTHeader64->FileHeader.SizeOfOptionalHeader + (getNumberOfSections() * sizeof(IMAGE_SECTION_HEADER)), pNTHeader64->OptionalHeader.FileAlignment); } removeIatDirectory(); } void PeParser::removeIatDirectory() { DWORD searchAddress = 0; if (isPE32()) { searchAddress = pNTHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress; pNTHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0; pNTHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0; } else { searchAddress = pNTHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress; pNTHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0; pNTHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0; } if (searchAddress) { for (WORD i = 0; i < getNumberOfSections(); i++) { if ((listPeSection[i].sectionHeader.VirtualAddress <= searchAddress) && ((listPeSection[i].sectionHeader.VirtualAddress + listPeSection[i].sectionHeader.Misc.VirtualSize) > searchAddress)) { //section must be read and writable listPeSection[i].sectionHeader.Characteristics |= IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; } } } } void PeParser::setDefaultFileAlignment() { if (isPE32()) { pNTHeader32->OptionalHeader.FileAlignment = FileAlignmentConstant; } else { pNTHeader64->OptionalHeader.FileAlignment = FileAlignmentConstant; } } bool PeFileSectionSortByPointerToRawData(const PeFileSection& d1, const PeFileSection& d2) { return d1.sectionHeader.PointerToRawData < d2.sectionHeader.PointerToRawData; } bool PeFileSectionSortByVirtualAddress(const PeFileSection& d1, const PeFileSection& d2) { return d1.sectionHeader.VirtualAddress < d2.sectionHeader.VirtualAddress; } void PeParser::alignAllSectionHeaders() { DWORD sectionAlignment = 0; DWORD fileAlignment = 0; DWORD newFileSize = 0; if (isPE32()) { sectionAlignment = pNTHeader32->OptionalHeader.SectionAlignment; fileAlignment = pNTHeader32->OptionalHeader.FileAlignment; } else { sectionAlignment = pNTHeader64->OptionalHeader.SectionAlignment; fileAlignment = pNTHeader64->OptionalHeader.FileAlignment; } std::sort(listPeSection.begin(), listPeSection.end(), PeFileSectionSortByPointerToRawData); //sort by PointerToRawData ascending newFileSize = pDosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + pNTHeader32->FileHeader.SizeOfOptionalHeader + (getNumberOfSections() * sizeof(IMAGE_SECTION_HEADER)); for (WORD i = 0; i < getNumberOfSections(); i++) { listPeSection[i].sectionHeader.VirtualAddress = alignValue(listPeSection[i].sectionHeader.VirtualAddress, sectionAlignment); listPeSection[i].sectionHeader.Misc.VirtualSize = alignValue(listPeSection[i].sectionHeader.Misc.VirtualSize, sectionAlignment); listPeSection[i].sectionHeader.PointerToRawData = alignValue(newFileSize, fileAlignment); listPeSection[i].sectionHeader.SizeOfRawData = alignValue(listPeSection[i].dataSize, fileAlignment); newFileSize = listPeSection[i].sectionHeader.PointerToRawData + listPeSection[i].sectionHeader.SizeOfRawData; } std::sort(listPeSection.begin(), listPeSection.end(), PeFileSectionSortByVirtualAddress); //sort by VirtualAddress ascending } +bool PeParser::dumpProcess(DWORD_PTR modBase, DWORD_PTR entryPoint, const WCHAR * dumpFilePath) +{ + moduleBaseAddress = modBase; + + if (readPeSectionsFromProcess()) + { + setDefaultFileAlignment(); + + setEntryPointVa(entryPoint); + + alignAllSectionHeaders(); + fixPeHeader(); + + getFileOverlay(); + + return savePeFileToDisk(dumpFilePath); + } + + return false; +} + +bool PeParser::dumpProcess(DWORD_PTR modBase, DWORD_PTR entryPoint, const WCHAR * dumpFilePath, std::vector & sectionList) +{ + if (listPeSection.size() == sectionList.size()) + { + for (int i = (getNumberOfSections() - 1); i >= 0; i--) + { + if (!sectionList[i].isDumped) + { + listPeSection.erase(listPeSection.begin() + i); + setNumberOfSections(getNumberOfSections() - 1); + } + else + { + listPeSection[i].sectionHeader.Misc.VirtualSize = sectionList[i].virtualSize; + listPeSection[i].sectionHeader.SizeOfRawData = sectionList[i].rawSize; + listPeSection[i].sectionHeader.Characteristics = sectionList[i].characteristics; + } + } + } + + return dumpProcess(modBase, entryPoint, dumpFilePath); +} + +void PeParser::setEntryPointVa(DWORD_PTR entryPoint) +{ + DWORD entryPointRva = (DWORD)(entryPoint - moduleBaseAddress); + + if (isPE32()) + { + pNTHeader32->OptionalHeader.AddressOfEntryPoint = entryPointRva; + } + else + { + pNTHeader64->OptionalHeader.AddressOfEntryPoint = entryPointRva; + } + +} + +bool PeParser::getFileOverlay() +{ + DWORD numberOfBytesRead; + bool retValue = false; + + if (!hasOverlayData()) + { + return false; + } + + if (openFileHandle()) + { + DWORD overlayOffset = getSectionHeaderBasedFileSize(); + DWORD fileSize = (DWORD)ProcessAccessHelp::getFileSize(hFile); + overlaySize = fileSize - overlayOffset; + + overlayData = new BYTE[overlaySize]; + + SetFilePointer(hFile, overlayOffset, 0, FILE_BEGIN); + + if (ReadFile(hFile, overlayData, overlaySize, &numberOfBytesRead, 0)) + { + retValue = true; + } + + closeFileHandle(); + } + + return retValue; +} + +bool PeParser::hasOverlayData() +{ + if (!filename) + return false; + + if (isValidPeFile()) + { + DWORD fileSize = (DWORD)ProcessAccessHelp::getFileSize(filename); + + return (fileSize > getSectionHeaderBasedFileSize()); + } + else + { + return false; + } +} diff --git a/Scylla/PeParser.h b/Scylla/PeParser.h index e4ddfba..d0a39f1 100644 --- a/Scylla/PeParser.h +++ b/Scylla/PeParser.h @@ -1,122 +1,127 @@ #pragma once #include #include +#include "DumpSectionGui.h" class PeFileSection { public: IMAGE_SECTION_HEADER sectionHeader; BYTE * data; DWORD dataSize; DWORD normalSize; PeFileSection() { ZeroMemory(§ionHeader, sizeof(IMAGE_SECTION_HEADER)); data = 0; dataSize = 0; normalSize = 0; } }; class PeParser { public: PeParser(const WCHAR * file, bool readSectionHeaders = true); PeParser(const DWORD_PTR moduleBase, bool readSectionHeaders = true); ~PeParser(); bool isValidPeFile(); bool isPE64(); bool isPE32(); bool isTargetFileSamePeFormat(); WORD getNumberOfSections(); std::vector & getSectionHeaderList(); bool hasExportDirectory(); bool hasTLSDirectory(); bool hasRelocationDirectory(); + bool hasOverlayData(); DWORD getEntryPoint(); bool getSectionNameUnicode(const int sectionIndex, WCHAR * output, const int outputLen); DWORD getSectionHeaderBasedFileSize(); DWORD getSectionHeaderBasedSizeOfImage(); bool readPeSectionsFromProcess(); bool readPeSectionsFromFile(); bool savePeFileToDisk(const WCHAR * newFile); void removeDosStub(); void alignAllSectionHeaders(); void fixPeHeader(); void setDefaultFileAlignment(); - + bool dumpProcess(DWORD_PTR modBase, DWORD_PTR entryPoint, const WCHAR * dumpFilePath); + bool dumpProcess(DWORD_PTR modBase, DWORD_PTR entryPoint, const WCHAR * dumpFilePath, std::vector & sectionList); protected: PeParser(); static const DWORD FileAlignmentConstant = 0x200; const WCHAR * filename; DWORD_PTR moduleBaseAddress; /************************************************************************/ /* PE FILE */ /* */ /* IMAGE_DOS_HEADER 64 0x40 */ /* IMAGE_NT_HEADERS32 248 0xF8 */ /* IMAGE_NT_HEADERS64 264 0x108 */ /* IMAGE_SECTION_HEADER 40 0x28 */ /************************************************************************/ PIMAGE_DOS_HEADER pDosHeader; BYTE * pDosStub; //between dos header and section header DWORD dosStubSize; PIMAGE_NT_HEADERS32 pNTHeader32; PIMAGE_NT_HEADERS64 pNTHeader64; std::vector listPeSection; BYTE * overlayData; DWORD overlaySize; /************************************************************************/ BYTE * fileMemory; BYTE * headerMemory; HANDLE hFile; DWORD fileSize; bool readPeHeaderFromFile(bool readSectionHeaders); bool readPeHeaderFromProcess(bool readSectionHeaders); bool hasDirectory(const int directoryIndex); bool getSectionHeaders(); void getDosAndNtHeader(BYTE * memory, LONG size); DWORD calcCorrectPeHeaderSize( bool readSectionHeaders ); DWORD getInitialHeaderReadSize( bool readSectionHeaders ); bool openFileHandle(); void closeFileHandle(); void initClass(); DWORD isMemoryNotNull( BYTE * data, int dataSize ); bool openWriteFileHandle( const WCHAR * newFile ); bool writeZeroMemoryToFile(HANDLE hFile, DWORD fileOffset, DWORD size); bool readPeSectionFromFile( DWORD readOffset, PeFileSection & peFileSection ); bool readPeSectionFromProcess( DWORD_PTR readOffset, PeFileSection & peFileSection ); bool readSectionFromFile( DWORD readOffset, PeFileSection & peFileSection ); bool readSectionFromProcess( DWORD_PTR readOffset, PeFileSection & peFileSection ); bool addNewLastSection(const CHAR * sectionName, DWORD sectionSize, BYTE * sectionData); DWORD alignValue(DWORD badValue, DWORD alignTo); DWORD_PTR convertOffsetToRVAVector(DWORD_PTR dwOffset); DWORD_PTR convertRVAToOffsetVector(DWORD_PTR dwRVA); void setNumberOfSections(WORD numberOfSections); void removeIatDirectory(); + void setEntryPointVa( DWORD_PTR entryPoint ); + bool getFileOverlay(); }; diff --git a/Scylla/Scylla.h b/Scylla/Scylla.h index 6163ba5..a14486f 100644 --- a/Scylla/Scylla.h +++ b/Scylla/Scylla.h @@ -1,31 +1,31 @@ #pragma once #include "ConfigurationHolder.h" #include "PluginLoader.h" #include "ProcessLister.h" #include "Logger.h" #define APPNAME_S "Scylla" -#define APPVERSION_S "v0.6 Beta 4" +#define APPVERSION_S "v0.6" #define APPNAME TEXT(APPNAME_S) #define APPVERSION TEXT(APPVERSION_S) class Scylla { public: static void init(); static ConfigurationHolder config; static PluginLoader plugins; static ProcessLister processLister; static FileLog debugLog; static ListboxLog windowLog; private: static const WCHAR DEBUG_LOG_FILENAME[]; };