Page Menu
Home
desp's stash
Search
Configure Global Search
Log In
Files
F368701
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
59 KB
Subscribers
None
View Options
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(§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<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
Details
Attached
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
Attached To
rSCY Scylla
Event Timeline
Log In to Comment