Page MenuHomedesp's stash

No OneTemporary

diff --git a/Scylla/DisassemblerGui.cpp b/Scylla/DisassemblerGui.cpp
index d0d1e2c..4b9257b 100644
--- a/Scylla/DisassemblerGui.cpp
+++ b/Scylla/DisassemblerGui.cpp
@@ -1,362 +1,385 @@
#include "DisassemblerGui.h"
#include "ProcessAccessHelp.h"
#include "Architecture.h"
-DisassemblerGui::DisassemblerGui(DWORD_PTR startAddress) : startAddress(startAddress)
+DisassemblerGui::DisassemblerGui(DWORD_PTR startAddress)
{
- prevAddress = startAddress;
+ addressHistoryIndex = 0;
+ addressHistory.push_back(startAddress);
hMenuDisassembler.LoadMenu(IDR_MENU_DISASSEMBLER);
}
BOOL DisassemblerGui::OnInitDialog(CWindow wndFocus, LPARAM lInitParam)
{
DoDataExchange(); // attach controls
DlgResize_Init(true, true);
addColumnsToDisassembler(ListDisassembler);
displayDisassembly();
- EditAddress.SetValue(startAddress);
+ EditAddress.SetValue(addressHistory[addressHistoryIndex]);
CenterWindow();
return TRUE;
}
void DisassemblerGui::OnContextMenu(CWindow wnd, CPoint point)
{
if (wnd.GetDlgCtrlID() == IDC_LIST_DISASSEMBLER)
{
int selection = ListDisassembler.GetSelectionMark();
if(selection == -1) // no item selected
return;
if(point.x == -1 && point.y == -1) // invoked by keyboard
{
ListDisassembler.EnsureVisible(selection, TRUE);
ListDisassembler.GetItemPosition(selection, &point);
ListDisassembler.ClientToScreen(&point);
}
CMenuHandle hSub = hMenuDisassembler.GetSubMenu(0);
BOOL menuItem = hSub.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, wnd);
if (menuItem)
{
int column = -1;
switch (menuItem)
{
case ID__DIS_ADDRESS:
column = COL_ADDRESS;
break;
case ID__DIS_SIZE:
column = COL_INSTRUCTION_SIZE;
break;
case ID__DIS_OPCODES:
column = COL_OPCODES;
break;
case ID__DIS_INSTRUCTIONS:
column = COL_INSTRUCTION;
break;
case ID__DIS_FOLLOW:
followInstruction(selection);
break;
+ case ID__DIS_DISASSEMBLEHERE:
+ {
+ disassembleNewAddress((DWORD_PTR)ProcessAccessHelp::decomposerResult[selection].addr);
+ }
+
}
if(column != -1)
{
tempBuffer[0] = '\0';
ListDisassembler.GetItemText(selection, column, tempBuffer, _countof(tempBuffer));
copyToClipboard(tempBuffer);
}
}
}
}
LRESULT DisassemblerGui::OnNMCustomdraw(NMHDR* pnmh)
{
LRESULT pResult = 0;
LPNMLVCUSTOMDRAW lpLVCustomDraw = (LPNMLVCUSTOMDRAW)(pnmh);
DWORD_PTR itemIndex = 0;
switch(lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_ITEMPREPAINT:
case CDDS_ITEMPREPAINT | CDDS_SUBITEM:
{
itemIndex = lpLVCustomDraw->nmcd.dwItemSpec;
if (lpLVCustomDraw->iSubItem == COL_INSTRUCTION)
{
doColorInstruction(lpLVCustomDraw, itemIndex);
}
else
{
lpLVCustomDraw->clrText = CLR_DEFAULT;
lpLVCustomDraw->clrTextBk = CLR_DEFAULT;
}
}
break;
}
pResult |= CDRF_NOTIFYPOSTPAINT;
pResult |= CDRF_NOTIFYITEMDRAW;
pResult |= CDRF_NOTIFYSUBITEMDRAW;
return pResult;
}
void DisassemblerGui::OnExit(UINT uNotifyCode, int nID, CWindow wndCtl)
{
EndDialog(0);
}
void DisassemblerGui::OnDisassemble(UINT uNotifyCode, int nID, CWindow wndCtl)
{
DWORD_PTR address = EditAddress.GetValue();
if (address)
{
- prevAddress = startAddress;
- startAddress = address;
+ disassembleNewAddress(address);
+ }
+}
+
+void DisassemblerGui::disassembleNewAddress(DWORD_PTR address)
+{
+ if (addressHistory[addressHistory.size() - 1] != address)
+ {
+ addressHistory.push_back(address);
+ addressHistoryIndex = (int)addressHistory.size() - 1;
+ EditAddress.SetValue(addressHistory[addressHistoryIndex]);
+
+ if (!displayDisassembly())
+ {
+ MessageBox(L"Cannot disassemble memory at this address",L"Error",MB_ICONERROR);
+ }
+ }
+
+}
+
+void DisassemblerGui::OnDisassembleForward(UINT uNotifyCode, int nID, CWindow wndCtl)
+{
+ if (addressHistoryIndex != (addressHistory.size() - 1))
+ {
+ addressHistoryIndex++;
+ EditAddress.SetValue(addressHistory[addressHistoryIndex]);
if (!displayDisassembly())
{
MessageBox(L"Cannot disassemble memory at this address",L"Error",MB_ICONERROR);
}
}
}
void DisassemblerGui::OnDisassembleBack(UINT uNotifyCode, int nID, CWindow wndCtl)
{
- startAddress = prevAddress;
- EditAddress.SetValue(startAddress);
- if (!displayDisassembly())
+ if (addressHistoryIndex != 0)
{
- MessageBox(L"Cannot disassemble memory at this address",L"Error",MB_ICONERROR);
+ addressHistoryIndex--;
+ EditAddress.SetValue(addressHistory[addressHistoryIndex]);
+ if (!displayDisassembly())
+ {
+ MessageBox(L"Cannot disassemble memory at this address",L"Error",MB_ICONERROR);
+ }
}
}
void DisassemblerGui::addColumnsToDisassembler(CListViewCtrl& list)
{
list.SetExtendedListViewStyle(LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
list.InsertColumn(COL_ADDRESS, L"Address", LVCFMT_LEFT);
list.InsertColumn(COL_INSTRUCTION_SIZE, L"Size", LVCFMT_CENTER);
list.InsertColumn(COL_OPCODES, L"Opcodes", LVCFMT_LEFT);
list.InsertColumn(COL_INSTRUCTION, L"Instructions", LVCFMT_LEFT);
list.InsertColumn(COL_COMMENT, L"Comment", LVCFMT_LEFT);
}
bool DisassemblerGui::displayDisassembly()
{
ListDisassembler.DeleteAllItems();
- if(!ProcessAccessHelp::readMemoryFromProcess(startAddress, sizeof(data), data))
+ if(!ProcessAccessHelp::readMemoryFromProcess(addressHistory[addressHistoryIndex], sizeof(data), data))
return false;
- if (!ProcessAccessHelp::decomposeMemory(data, sizeof(data), startAddress))
+ if (!ProcessAccessHelp::decomposeMemory(data, sizeof(data), addressHistory[addressHistoryIndex]))
return false;
- if (!ProcessAccessHelp::disassembleMemory(data, sizeof(data), startAddress))
+ if (!ProcessAccessHelp::disassembleMemory(data, sizeof(data), addressHistory[addressHistoryIndex]))
return false;
for (unsigned int i = 0; i < ProcessAccessHelp::decodedInstructionsCount; i++)
{
swprintf_s(tempBuffer, PRINTF_DWORD_PTR_FULL,ProcessAccessHelp::decodedInstructions[i].offset);
ListDisassembler.InsertItem(i, tempBuffer);
swprintf_s(tempBuffer, L"%02d",ProcessAccessHelp::decodedInstructions[i].size);
ListDisassembler.SetItemText(i, COL_INSTRUCTION_SIZE, tempBuffer);
swprintf_s(tempBuffer, L"%S", (char *)ProcessAccessHelp::decodedInstructions[i].instructionHex.p);
toUpperCase(tempBuffer);
ListDisassembler.SetItemText(i, COL_OPCODES, tempBuffer);
swprintf_s(tempBuffer, L"%S%S%S",(char*)ProcessAccessHelp::decodedInstructions[i].mnemonic.p, ProcessAccessHelp::decodedInstructions[i].operands.length != 0 ? " " : "", (char*)ProcessAccessHelp::decodedInstructions[i].operands.p);
toUpperCase(tempBuffer);
ListDisassembler.SetItemText(i, COL_INSTRUCTION, tempBuffer);
tempBuffer[0] = 0;
if (getDisassemblyComment(i))
{
ListDisassembler.SetItemText(i, COL_COMMENT, tempBuffer);
}
}
ListDisassembler.SetColumnWidth(COL_ADDRESS, LVSCW_AUTOSIZE_USEHEADER);
ListDisassembler.SetColumnWidth(COL_INSTRUCTION_SIZE, LVSCW_AUTOSIZE_USEHEADER);
- ListDisassembler.SetColumnWidth(COL_OPCODES, LVSCW_AUTOSIZE_USEHEADER);
+ ListDisassembler.SetColumnWidth(COL_OPCODES, 140);
ListDisassembler.SetColumnWidth(COL_INSTRUCTION, LVSCW_AUTOSIZE_USEHEADER);
ListDisassembler.SetColumnWidth(COL_COMMENT, LVSCW_AUTOSIZE_USEHEADER);
return true;
}
void DisassemblerGui::copyToClipboard(const WCHAR * text)
{
if(OpenClipboard())
{
EmptyClipboard();
size_t len = wcslen(text);
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, (len + 1) * sizeof(WCHAR));
if(hMem)
{
wcscpy_s(static_cast<WCHAR *>(GlobalLock(hMem)), len + 1, text);
GlobalUnlock(hMem);
if(!SetClipboardData(CF_UNICODETEXT, hMem))
{
GlobalFree(hMem);
}
}
CloseClipboard();
}
}
void DisassemblerGui::toUpperCase(WCHAR * lowercase)
{
for (size_t i = 0; i < wcslen(lowercase); i++)
{
if (lowercase[i] != L'x')
{
lowercase[i] = towupper(lowercase[i]);
}
}
}
void DisassemblerGui::doColorInstruction( LPNMLVCUSTOMDRAW lpLVCustomDraw, DWORD_PTR itemIndex )
{
if (ProcessAccessHelp::decomposerResult[itemIndex].flags == FLAG_NOT_DECODABLE)
{
lpLVCustomDraw->clrText = RGB(255,255,255); // white text
lpLVCustomDraw->clrTextBk = RGB(255,0,0); // red background
}
else if (META_GET_FC(ProcessAccessHelp::decomposerResult[itemIndex].meta) == FC_RET)
{
lpLVCustomDraw->clrTextBk = RGB(0,255,255); // aqua
}
else if (META_GET_FC(ProcessAccessHelp::decomposerResult[itemIndex].meta) == FC_CALL)
{
lpLVCustomDraw->clrTextBk = RGB(255,255,0); // yellow
}
else if (META_GET_FC(ProcessAccessHelp::decomposerResult[itemIndex].meta) == FC_UNC_BRANCH)
{
lpLVCustomDraw->clrTextBk = RGB(0x32,0xCD,0x32); // limegreen
}
else if (META_GET_FC(ProcessAccessHelp::decomposerResult[itemIndex].meta) == FC_CND_BRANCH)
{
lpLVCustomDraw->clrTextBk = RGB(0xAD,0xFF,0x2F); // greenyellow
}
}
void DisassemblerGui::followInstruction(int index)
{
DWORD_PTR address = 0;
DWORD_PTR addressTemp = 0;
DWORD type = META_GET_FC(ProcessAccessHelp::decomposerResult[index].meta);
if (ProcessAccessHelp::decomposerResult[index].flags != FLAG_NOT_DECODABLE)
{
if (type == FC_CALL || type == FC_UNC_BRANCH || type == FC_CND_BRANCH)
{
#ifdef _WIN64
if (ProcessAccessHelp::decomposerResult[index].flags & FLAG_RIP_RELATIVE)
{
addressTemp = INSTRUCTION_GET_RIP_TARGET(&ProcessAccessHelp::decomposerResult[index]);
if(!ProcessAccessHelp::readMemoryFromProcess(addressTemp, sizeof(DWORD_PTR), &address))
{
address = 0;
}
}
#endif
if (ProcessAccessHelp::decomposerResult[index].ops[0].type == O_PC)
{
address = (DWORD_PTR)INSTRUCTION_GET_TARGET(&ProcessAccessHelp::decomposerResult[index]);
}
if (ProcessAccessHelp::decomposerResult[index].ops[0].type == O_DISP)
{
addressTemp = (DWORD_PTR)ProcessAccessHelp::decomposerResult[index].disp;
if(!ProcessAccessHelp::readMemoryFromProcess(addressTemp, sizeof(DWORD_PTR), &address))
{
address = 0;
}
}
if (address != 0)
{
- prevAddress = startAddress;
- startAddress = address;
-
- if (displayDisassembly())
- {
- EditAddress.SetValue(startAddress);
- }
- else
- {
- MessageBox(L"Cannot disassemble memory at this address",L"Error",MB_ICONERROR);
- }
+ disassembleNewAddress(address);
}
}
}
}
bool DisassemblerGui::getDisassemblyComment(unsigned int index)
{
DWORD_PTR address = 0;
DWORD_PTR addressTemp = 0;
DWORD type = META_GET_FC(ProcessAccessHelp::decomposerResult[index].meta);
tempBuffer[0] = 0;
if (ProcessAccessHelp::decomposerResult[index].flags != FLAG_NOT_DECODABLE)
{
if (type == FC_CALL || type == FC_UNC_BRANCH || type == FC_CND_BRANCH)
{
#ifdef _WIN64
if (ProcessAccessHelp::decomposerResult[index].flags & FLAG_RIP_RELATIVE)
{
addressTemp = (DWORD_PTR)INSTRUCTION_GET_RIP_TARGET(&ProcessAccessHelp::decomposerResult[index]);
swprintf_s(tempBuffer,L"-> "PRINTF_DWORD_PTR_FULL,addressTemp);
if(ProcessAccessHelp::readMemoryFromProcess(addressTemp, sizeof(DWORD_PTR), &address))
{
swprintf_s(tempBuffer,L"%s -> "PRINTF_DWORD_PTR_FULL,tempBuffer,address);
return true;
}
}
#endif
if (ProcessAccessHelp::decomposerResult[index].ops[0].type == O_PC)
{
address = (DWORD_PTR)INSTRUCTION_GET_TARGET(&ProcessAccessHelp::decomposerResult[index]);
swprintf_s(tempBuffer,L"-> "PRINTF_DWORD_PTR_FULL,address);
return true;
}
if (ProcessAccessHelp::decomposerResult[index].ops[0].type == O_DISP)
{
addressTemp = (DWORD_PTR)ProcessAccessHelp::decomposerResult[index].disp;
swprintf_s(tempBuffer,L"-> "PRINTF_DWORD_PTR_FULL,addressTemp);
if(ProcessAccessHelp::readMemoryFromProcess(addressTemp, sizeof(DWORD_PTR), &address))
{
swprintf_s(tempBuffer,L"%s -> "PRINTF_DWORD_PTR_FULL,tempBuffer,address);
return true;
}
}
}
}
return false;
}
diff --git a/Scylla/DisassemblerGui.h b/Scylla/DisassemblerGui.h
index d6802aa..a789605 100644
--- a/Scylla/DisassemblerGui.h
+++ b/Scylla/DisassemblerGui.h
@@ -1,106 +1,111 @@
#pragma once
#include <windows.h>
#include "resource.h"
// WTL
#include <atlbase.h> // base ATL classes
#include <atlapp.h> // base WTL classes
#include <atlwin.h> // ATL GUI classes
#include <atlframe.h> // WTL window frame helpers
#include <atlmisc.h> // WTL utility classes like CString
#include <atlcrack.h> // WTL enhanced msg map macros
#include <atlctrls.h> // WTL controls
#include <atlddx.h> // WTL dialog data exchange
-
+#include <vector>
#include "hexedit.h"
class DisassemblerGui : public CDialogImpl<DisassemblerGui>, public CWinDataExchange<DisassemblerGui>, public CDialogResize<DisassemblerGui>
{
public:
enum { IDD = IDD_DLG_DISASSEMBLER };
BEGIN_DDX_MAP(DisassemblerGui)
DDX_CONTROL_HANDLE(IDC_LIST_DISASSEMBLER, ListDisassembler)
DDX_CONTROL(IDC_EDIT_ADDRESS_DISASSEMBLE, EditAddress)
END_DDX_MAP()
BEGIN_MSG_MAP(DisassemblerGui)
MSG_WM_INITDIALOG(OnInitDialog)
MSG_WM_CONTEXTMENU(OnContextMenu)
NOTIFY_HANDLER_EX(IDC_LIST_DISASSEMBLER, NM_CUSTOMDRAW, OnNMCustomdraw)
COMMAND_ID_HANDLER_EX(IDC_BUTTON_DISASSEMBLE, OnDisassemble)
COMMAND_ID_HANDLER_EX(IDC_BUTTON_DISASSEMBLER_BACK, OnDisassembleBack)
+ COMMAND_ID_HANDLER_EX(IDC_BUTTON_DISASSEMBLER_FORWARD, OnDisassembleForward)
COMMAND_ID_HANDLER_EX(IDCANCEL, OnExit)
COMMAND_ID_HANDLER_EX(IDOK, OnExit)
CHAIN_MSG_MAP(CDialogResize<DisassemblerGui>)
END_MSG_MAP()
BEGIN_DLGRESIZE_MAP(DisassemblerGui)
DLGRESIZE_CONTROL(IDC_LIST_DISASSEMBLER, DLSZ_SIZE_X | DLSZ_SIZE_Y)
DLGRESIZE_CONTROL(IDC_BUTTON_DISASSEMBLE, DLSZ_MOVE_X | DLSZ_MOVE_Y)
DLGRESIZE_CONTROL(IDC_BUTTON_DISASSEMBLER_BACK, DLSZ_MOVE_X | DLSZ_MOVE_Y)
+ DLGRESIZE_CONTROL(IDC_BUTTON_DISASSEMBLER_FORWARD, DLSZ_MOVE_X | DLSZ_MOVE_Y)
DLGRESIZE_CONTROL(IDC_EDIT_ADDRESS_DISASSEMBLE, DLSZ_MOVE_Y)
DLGRESIZE_CONTROL(IDC_STATIC_ADDRESS_DISASSEMBLE, DLSZ_MOVE_Y)
END_DLGRESIZE_MAP()
DisassemblerGui(DWORD_PTR startAddress);
protected:
// Variables
static const size_t DISASSEMBLER_GUI_MEMORY_SIZE = 0x120;
WCHAR tempBuffer[100];
- DWORD_PTR startAddress;
- DWORD_PTR prevAddress;
+ int addressHistoryIndex;
+
+ std::vector<DWORD_PTR> addressHistory;
// Controls
CListViewCtrl ListDisassembler;
CHexEdit<DWORD_PTR> EditAddress;
enum DisassemblerColumns {
COL_ADDRESS = 0,
COL_INSTRUCTION_SIZE,
COL_OPCODES,
COL_INSTRUCTION,
COL_COMMENT
};
// Handles
CMenu hMenuDisassembler;
protected:
// Message handlers
BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam);
void OnContextMenu(CWindow wnd, CPoint point);
void OnExit(UINT uNotifyCode, int nID, CWindow wndCtl);
LRESULT OnNMCustomdraw(NMHDR* pnmh);
void OnDisassemble(UINT uNotifyCode, int nID, CWindow wndCtl);
void OnDisassembleBack(UINT uNotifyCode, int nID, CWindow wndCtl);
-
+ void OnDisassembleForward(UINT uNotifyCode, int nID, CWindow wndCtl);
// GUI functions
void addColumnsToDisassembler(CListViewCtrl& list);
bool displayDisassembly();
// Misc
void copyToClipboard(const WCHAR * text);
private:
BYTE data[DISASSEMBLER_GUI_MEMORY_SIZE];
void toUpperCase(WCHAR * lowercase);
void doColorInstruction( LPNMLVCUSTOMDRAW lpLVCustomDraw, DWORD_PTR itemIndex );
void followInstruction(int index);
bool getDisassemblyComment(unsigned int index);
+
+ void disassembleNewAddress(DWORD_PTR address);
};
diff --git a/Scylla/ImportRebuilder.cpp b/Scylla/ImportRebuilder.cpp
index ab0afdf..c477585 100644
--- a/Scylla/ImportRebuilder.cpp
+++ b/Scylla/ImportRebuilder.cpp
@@ -1,288 +1,309 @@
#include "ImportRebuilder.h"
#include "Scylla.h"
#include "StringConversion.h"
//#define DEBUG_COMMENTS
bool ImportRebuilder::rebuildImportTable(const WCHAR * newFilePath, std::map<DWORD_PTR, ImportModuleThunk> & moduleList)
{
bool retValue = false;
if (isValidPeFile())
{
if (readPeSectionsFromFile())
{
setDefaultFileAlignment();
retValue = buildNewImportTable(moduleList);
if (retValue)
{
alignAllSectionHeaders();
fixPeHeader();
retValue = savePeFileToDisk(newFilePath);
}
}
}
return retValue;
}
bool ImportRebuilder::buildNewImportTable(std::map<DWORD_PTR, ImportModuleThunk> & moduleList)
{
createNewImportSection(moduleList);
importSectionIndex = listPeSection.size() - 1;
DWORD dwSize = fillImportSection(moduleList);
if (!dwSize)
{
return false;
}
setFlagToIATSection((*moduleList.begin()).second.firstThunk);
if (isPE32())
{
pNTHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = listPeSection[importSectionIndex].sectionHeader.VirtualAddress;
pNTHeader32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (DWORD)(numberOfImportDescriptors * sizeof(IMAGE_IMPORT_DESCRIPTOR));
}
else
{
pNTHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = listPeSection[importSectionIndex].sectionHeader.VirtualAddress;
pNTHeader64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (DWORD)(numberOfImportDescriptors * sizeof(IMAGE_IMPORT_DESCRIPTOR));
}
return true;
}
bool ImportRebuilder::createNewImportSection(std::map<DWORD_PTR, ImportModuleThunk> & moduleList)
{
char sectionName[IMAGE_SIZEOF_SHORT_NAME + 1] = {0};
const WCHAR * sectionNameW = Scylla::config[IAT_SECTION_NAME].getString();
calculateImportSizes(moduleList);
if (wcslen(sectionNameW) > IMAGE_SIZEOF_SHORT_NAME)
{
strcpy_s(sectionName, ".SCY");
}
else
{
StringConversion::ToASCII(sectionNameW, sectionName, _countof(sectionName));
}
return addNewLastSection(sectionName, (DWORD)sizeOfImportSection, 0);
}
void ImportRebuilder::setFlagToIATSection(DWORD_PTR iatAddress)
{
for (size_t i = 0; i < listPeSection.size(); i++)
{
if ((listPeSection[i].sectionHeader.VirtualAddress <= iatAddress) && ((listPeSection[i].sectionHeader.VirtualAddress + listPeSection[i].sectionHeader.Misc.VirtualSize) > iatAddress))
{
//section must be read and writeable
listPeSection[i].sectionHeader.Characteristics |= IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE;
}
}
}
DWORD ImportRebuilder::fillImportSection(std::map<DWORD_PTR, ImportModuleThunk> & moduleList)
{
std::map<DWORD_PTR, ImportModuleThunk>::iterator mapIt;
std::map<DWORD_PTR, ImportThunk>::iterator mapIt2;
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = 0;
PIMAGE_IMPORT_BY_NAME pImportByName = 0;
PIMAGE_THUNK_DATA pThunk = 0;
ImportModuleThunk * importModuleThunk = 0;
ImportThunk * importThunk = 0;
size_t stringLength = 0;
DWORD_PTR lastRVA = 0;
BYTE * sectionData = listPeSection[importSectionIndex].data;
DWORD offset = 0;
pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(sectionData);
//skip the IMAGE_IMPORT_DESCRIPTOR
offset += (DWORD)(numberOfImportDescriptors * sizeof(IMAGE_IMPORT_DESCRIPTOR));
for ( mapIt = moduleList.begin() ; mapIt != moduleList.end(); mapIt++ )
{
importModuleThunk = &((*mapIt).second);
stringLength = addImportDescriptor(importModuleThunk, offset);
#ifdef DEBUG_COMMENTS
Scylla::debugLog.log(L"fillImportSection :: importDesc.Name %X", pImportDescriptor->Name);
#endif
offset += (DWORD)stringLength; //stringLength has null termination char
pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)sectionData + offset);
//pThunk = (PIMAGE_THUNK_DATA)(getMemoryPointerFromRVA(importModuleThunk->firstThunk));
lastRVA = importModuleThunk->firstThunk - sizeof(DWORD_PTR);
for ( mapIt2 = (*mapIt).second.thunkList.begin() ; mapIt2 != (*mapIt).second.thunkList.end(); mapIt2++ )
{
importThunk = &((*mapIt2).second);
pThunk = (PIMAGE_THUNK_DATA)(getMemoryPointerFromRVA(importThunk->rva));
//check wrong iat pointer
if (!pThunk)
{
#ifdef DEBUG_COMMENTS
Scylla::debugLog.log(L"fillImportSection :: Failed to get pThunk RVA: %X", importThunk->rva);
#endif
return 0;
}
if ((lastRVA + sizeof(DWORD_PTR)) != importThunk->rva)
{
//add additional import desc
addSpecialImportDescriptor(importThunk->rva);
}
lastRVA = importThunk->rva;
#ifdef DEBUG_COMMENTS
Scylla::debugLog.log(L"fillImportSection :: importThunk %X pThunk %X pImportByName %X offset %X", importThunk,pThunk,pImportByName,offset);
#endif
stringLength = addImportToImportTable(importThunk, pThunk, pImportByName, offset);
offset += (DWORD)stringLength; //is 0 bei import by ordinal
pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)pImportByName + stringLength);
}
pImportDescriptor++;
}
return offset;
}
size_t ImportRebuilder::addImportDescriptor(ImportModuleThunk * pImportModule, DWORD sectionOffset)
{
char dllName[MAX_PATH];
StringConversion::ToASCII(pImportModule->moduleName, dllName, _countof(dllName));
size_t stringLength = strlen(dllName) + 1;
/*
Warning: stringLength MUST include null termination char
*/
memcpy((listPeSection[importSectionIndex].data + sectionOffset), dllName, stringLength); //copy module name to section
pImportDescriptor->FirstThunk = (DWORD)pImportModule->firstThunk;
pImportDescriptor->Name = (DWORD)convertOffsetToRVAVector(listPeSection[importSectionIndex].sectionHeader.PointerToRawData + sectionOffset);
return stringLength;
}
void ImportRebuilder::addSpecialImportDescriptor(DWORD_PTR rvaFirstThunk)
{
PIMAGE_IMPORT_DESCRIPTOR oldID = pImportDescriptor;
pImportDescriptor++;
pImportDescriptor->FirstThunk = (DWORD)rvaFirstThunk;
pImportDescriptor->Name = oldID->Name;
}
void ImportRebuilder::calculateImportSizes(std::map<DWORD_PTR, ImportModuleThunk> & moduleList)
{
std::map<DWORD_PTR, ImportModuleThunk>::iterator mapIt;
std::map<DWORD_PTR, ImportThunk>::iterator mapIt2;
DWORD_PTR lastRVA = 0;
numberOfImportDescriptors = 0;
sizeOfImportSection = 0;
sizeOfApiAndModuleNames = 0;
numberOfImportDescriptors = moduleList.size() + 1; //last is zero'd
for ( mapIt = moduleList.begin() ; mapIt != moduleList.end(); mapIt++ )
{
lastRVA = (*mapIt).second.firstThunk - sizeof(DWORD_PTR);
sizeOfApiAndModuleNames += (DWORD)(wcslen((*mapIt).second.moduleName) + 1);
for ( mapIt2 = (*mapIt).second.thunkList.begin() ; mapIt2 != (*mapIt).second.thunkList.end(); mapIt2++ )
{
if ((lastRVA + sizeof(DWORD_PTR)) != (*mapIt2).second.rva)
{
numberOfImportDescriptors++; //add additional import desc
}
if((*mapIt2).second.name[0] != '\0')
{
sizeOfApiAndModuleNames += sizeof(WORD); //Hint from IMAGE_IMPORT_BY_NAME
sizeOfApiAndModuleNames += (DWORD)(strlen((*mapIt2).second.name) + 1);
}
lastRVA = (*mapIt2).second.rva;
}
}
sizeOfImportSection = sizeOfApiAndModuleNames + (numberOfImportDescriptors * sizeof(IMAGE_IMPORT_DESCRIPTOR));
}
size_t ImportRebuilder::addImportToImportTable( ImportThunk * pImport, PIMAGE_THUNK_DATA pThunk, PIMAGE_IMPORT_BY_NAME pImportByName, DWORD sectionOffset)
{
size_t stringLength = 0;
if(pImport->name[0] == '\0')
{
pThunk->u1.AddressOfData = (IMAGE_ORDINAL(pImport->ordinal) | IMAGE_ORDINAL_FLAG);
}
else
{
pImportByName->Hint = pImport->hint;
stringLength = strlen(pImport->name) + 1;
memcpy(pImportByName->Name, pImport->name, stringLength);
pThunk->u1.AddressOfData = convertOffsetToRVAVector(listPeSection[importSectionIndex].sectionHeader.PointerToRawData + sectionOffset);
if (!pThunk->u1.AddressOfData)
{
#ifdef DEBUG_COMMENTS
Scylla::debugLog.log(L"addImportToImportTable :: failed to get AddressOfData %X %X", listPeSection[importSectionIndex].sectionHeader.PointerToRawData, sectionOffset);
#endif
}
//next import should be nulled
pThunk++;
pThunk->u1.AddressOfData = 0;
#ifdef DEBUG_COMMENTS
Scylla::debugLog.log(L"addImportToImportTable :: pThunk->u1.AddressOfData %X %X %X", pThunk->u1.AddressOfData, pThunk, listPeSection[importSectionIndex].sectionHeader.PointerToRawData + sectionOffset);
#endif
stringLength += sizeof(WORD);
}
return stringLength;
}
BYTE * ImportRebuilder::getMemoryPointerFromRVA(DWORD_PTR dwRVA)
{
- DWORD_PTR offset = convertRVAToOffsetVector(dwRVA);
+ int peSectionIndex = convertRVAToOffsetVectorIndex(dwRVA);
- for (size_t i = 0; i < listPeSection.size(); i++)
+ if (peSectionIndex == -1)
{
- if ((listPeSection[i].sectionHeader.PointerToRawData <= offset) && ((listPeSection[i].sectionHeader.PointerToRawData + listPeSection[i].sectionHeader.SizeOfRawData) > offset))
- {
- return (BYTE *)((DWORD_PTR)listPeSection[i].data + (offset - listPeSection[i].sectionHeader.PointerToRawData));
- }
+ return 0;
+ }
+
+ DWORD rvaPointer = ((DWORD)dwRVA - listPeSection[peSectionIndex].sectionHeader.VirtualAddress);
+ DWORD minSectionSize = rvaPointer + (sizeof(DWORD_PTR) * 2); //add space for 1 IAT address
+
+ if (listPeSection[peSectionIndex].data == 0 || listPeSection[peSectionIndex].dataSize == 0)
+ {
+ listPeSection[peSectionIndex].dataSize = minSectionSize;
+ listPeSection[peSectionIndex].normalSize = minSectionSize;
+ listPeSection[peSectionIndex].data = new BYTE[listPeSection[peSectionIndex].dataSize];
+
+ listPeSection[peSectionIndex].sectionHeader.SizeOfRawData = listPeSection[peSectionIndex].dataSize;
+ }
+ else if(listPeSection[peSectionIndex].dataSize < minSectionSize)
+ {
+ BYTE * temp = new BYTE[minSectionSize];
+ memcpy(temp, listPeSection[peSectionIndex].data, listPeSection[peSectionIndex].dataSize);
+ delete [] listPeSection[peSectionIndex].data;
+
+ listPeSection[peSectionIndex].data = temp;
+ listPeSection[peSectionIndex].dataSize = minSectionSize;
+ listPeSection[peSectionIndex].normalSize = minSectionSize;
+
+ listPeSection[peSectionIndex].sectionHeader.SizeOfRawData = listPeSection[peSectionIndex].dataSize;
}
- return 0;
+ return (BYTE *)((DWORD_PTR)listPeSection[peSectionIndex].data + rvaPointer);
}
diff --git a/Scylla/MainGui.rc b/Scylla/MainGui.rc
index 6673fdd..923ace0 100644
Binary files a/Scylla/MainGui.rc and b/Scylla/MainGui.rc differ
diff --git a/Scylla/PeParser.cpp b/Scylla/PeParser.cpp
index a1cbce0..39d57f7 100644
--- a/Scylla/PeParser.cpp
+++ b/Scylla/PeParser.cpp
@@ -1,1220 +1,1233 @@
#include "PeParser.h"
#include "ProcessAccessHelp.h"
#include <algorithm>
#include <imagehlp.h>
#pragma comment(lib, "Imagehlp.lib")
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<PeFileSection> & 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;
}
+int PeParser::convertRVAToOffsetVectorIndex(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 i;
+ }
+ }
+
+ return -1;
+}
+
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<PeSection> & 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);
setEntryPointRva(entryPointRva);
}
void PeParser::setEntryPointRva(DWORD entryPoint)
{
if (isPE32())
{
pNTHeader32->OptionalHeader.AddressOfEntryPoint = entryPoint;
}
else if (isPE64())
{
pNTHeader64->OptionalHeader.AddressOfEntryPoint = entryPoint;
}
}
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;
}
}
bool PeParser::updatePeHeaderChecksum(const WCHAR * targetFile, DWORD fileSize)
{
PIMAGE_NT_HEADERS32 pNTHeader32 = 0;
PIMAGE_NT_HEADERS64 pNTHeader64 = 0;
DWORD headerSum = 0;
DWORD checkSum = 0;
bool retValue = false;
if (!fileSize)
return retValue;
HANDLE hFileToMap = CreateFile(targetFile, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if(hFileToMap != INVALID_HANDLE_VALUE)
{
HANDLE hMappedFile = CreateFileMapping(hFileToMap, 0, PAGE_READWRITE, 0, 0, 0);
if(hMappedFile)
{
if (GetLastError() != ERROR_ALREADY_EXISTS)
{
LPVOID addrMappedDll = MapViewOfFile(hMappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (addrMappedDll)
{
pNTHeader32 = (PIMAGE_NT_HEADERS32)CheckSumMappedFile(addrMappedDll, fileSize, &headerSum, &checkSum);
if (pNTHeader32)
{
if (pNTHeader32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
{
pNTHeader64 = (PIMAGE_NT_HEADERS64)pNTHeader32;
pNTHeader64->OptionalHeader.CheckSum = checkSum;
}
else
{
pNTHeader32->OptionalHeader.CheckSum = checkSum;
}
retValue = true;
}
UnmapViewOfFile(addrMappedDll);
}
}
CloseHandle(hMappedFile);
}
CloseHandle(hFileToMap);
}
return retValue;
}
\ No newline at end of file
diff --git a/Scylla/PeParser.h b/Scylla/PeParser.h
index 687c318..329354b 100644
--- a/Scylla/PeParser.h
+++ b/Scylla/PeParser.h
@@ -1,131 +1,132 @@
#pragma once
#include <windows.h>
#include <vector>
#include "DumpSectionGui.h"
class PeFileSection {
public:
IMAGE_SECTION_HEADER sectionHeader;
BYTE * data;
DWORD dataSize;
DWORD normalSize;
PeFileSection()
{
ZeroMemory(&sectionHeader, 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<PeFileSection> & 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<PeSection> & sectionList);
void setEntryPointVa(DWORD_PTR entryPoint);
void setEntryPointRva(DWORD entryPoint);
static bool updatePeHeaderChecksum(const WCHAR * targetFile, DWORD fileSize);
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<PeFileSection> 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);
+ int convertRVAToOffsetVectorIndex(DWORD_PTR dwRVA);
DWORD_PTR convertOffsetToRVAVector(DWORD_PTR dwOffset);
DWORD_PTR convertRVAToOffsetVector(DWORD_PTR dwRVA);
void setNumberOfSections(WORD numberOfSections);
void removeIatDirectory();
bool getFileOverlay();
};
diff --git a/Scylla/Scylla.h b/Scylla/Scylla.h
index fe39f06..2e6a862 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.7 Beta 1"
+#define APPVERSION_S "v0.7 Beta 2"
#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[];
};
diff --git a/Scylla/resource.h b/Scylla/resource.h
index 09a2ab4..dd0cdbb 100644
Binary files a/Scylla/resource.h and b/Scylla/resource.h differ

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jul 5, 4:43 AM (4 h, 53 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
61/f3/b68ea73efb29e43d171181bf4405

Event Timeline