diff --git a/Scylla.sln b/Scylla.sln new file mode 100644 index 0000000..cca0ab2 --- /dev/null +++ b/Scylla.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Scylla", "Scylla\Scylla.vcxproj", "{710434C9-FC4B-4F1D-B318-E10ADC78499F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Debug|Win32.ActiveCfg = Debug|Win32 + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Debug|Win32.Build.0 = Debug|Win32 + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Debug|x64.ActiveCfg = Debug|x64 + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Debug|x64.Build.0 = Debug|x64 + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Release|Win32.ActiveCfg = Release|Win32 + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Release|Win32.Build.0 = Release|Win32 + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Release|x64.ActiveCfg = Release|x64 + {710434C9-FC4B-4F1D-B318-E10ADC78499F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Scylla/AboutGui.cpp b/Scylla/AboutGui.cpp new file mode 100644 index 0000000..0a87d51 --- /dev/null +++ b/Scylla/AboutGui.cpp @@ -0,0 +1,25 @@ +#include "AboutGui.h" +#include "definitions.h" + +INT_PTR AboutGui::initDialog(HINSTANCE hInstance, HWND hWndParent) +{ + return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLG_ABOUT),hWndParent, (DLGPROC)aboutDlgProc); +} + +LRESULT CALLBACK AboutGui::aboutDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + SetDlgItemText(hWnd,IDC_STATIC_ABOUT, TEXT(APPNAME)TEXT(" ")TEXT(ARCHITECTURE)TEXT(" ")TEXT(APPVERSION)TEXT("\n\n")TEXT(DEVELOPED)TEXT("\n\n\n")TEXT(CREDIT_DISTORM)TEXT("\n")TEXT(CREDIT_YODA)TEXT("\n\n")TEXT(GREETINGS)TEXT("\n\n\n")TEXT(VISIT)); + return TRUE; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_BTN_ABOUT_OK: + EndDialog(hWnd, 0); + return TRUE; + } + } + return FALSE; +} \ No newline at end of file diff --git a/Scylla/AboutGui.h b/Scylla/AboutGui.h new file mode 100644 index 0000000..2fcd9d8 --- /dev/null +++ b/Scylla/AboutGui.h @@ -0,0 +1,13 @@ +#pragma once + +#include "MainGui.h" + + +class AboutGui { +public: + + static INT_PTR initDialog(HINSTANCE hInstance, HWND hWndParent); + +private: + static LRESULT CALLBACK aboutDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +}; \ No newline at end of file diff --git a/Scylla/ApiReader.cpp b/Scylla/ApiReader.cpp new file mode 100644 index 0000000..7586b48 --- /dev/null +++ b/Scylla/ApiReader.cpp @@ -0,0 +1,1103 @@ + +#include "ApiReader.h" + +#include "Logger.h" +#include "definitions.h" +#include "SystemInformation.h" + +stdext::hash_multimap ApiReader::apiList; //api look up table +std::map * ApiReader::moduleThunkList; //store found apis + +DWORD_PTR ApiReader::minApiAddress = 0xFFFFFFFF; +DWORD_PTR ApiReader::maxApiAddress = 0; + +//#define DEBUG_COMMENTS + +void ApiReader::readApisFromModuleList() +{ + for (unsigned int i = 0; i < moduleList.size();i++) + { + setModulePriority(&moduleList[i]); + + if (moduleList[i].modBaseAddr + moduleList[i].modBaseSize > maxValidAddress) + { + maxValidAddress = moduleList[i].modBaseAddr + moduleList[i].modBaseSize; + } + + Logger::printfDialog(TEXT("Module parsing: %s"),moduleList[i].fullPath); + + if (!moduleList[i].isAlreadyParsed) + { + parseModule(&moduleList[i]); + } + } + +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("Address Min ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" Max ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\nimagebase ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" maxValidAddress ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),minApiAddress,maxApiAddress,targetImageBase,maxValidAddress); +#endif +} + +void ApiReader::parseModule(ModuleInfo *module) +{ + module->parsing = true; + + if (isWinSxSModule(module)) + { + parseModuleWithMapping(module); + } + else if (isModuleLoadedInOwnProcess(module)) + { + parseModuleWithOwnProcess(module); + } + else + { + parseModuleWithProcess(module); + } + + + module->isAlreadyParsed = true; +} + +void ApiReader::parseModuleWithMapping(ModuleInfo *moduleInfo) +{ + LPVOID fileMapping = 0; + PIMAGE_NT_HEADERS pNtHeader = 0; + PIMAGE_DOS_HEADER pDosHeader = 0; + + fileMapping = createFileMappingViewRead(moduleInfo->fullPath); + + if (fileMapping == 0) + return; + + pDosHeader = (PIMAGE_DOS_HEADER)fileMapping; + pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileMapping + (DWORD_PTR)(pDosHeader->e_lfanew)); + + if (isPeAndExportTableValid(pNtHeader)) + { + parseExportTable(moduleInfo, pNtHeader, (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)fileMapping + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress), (DWORD_PTR)fileMapping); + } + + + UnmapViewOfFile(fileMapping); + +} + +inline bool ApiReader::isApiForwarded(DWORD_PTR rva, PIMAGE_NT_HEADERS pNtHeader) +{ + if ((rva > pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) && (rva < (pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size))) + { + return true; + } + else + { + return false; + } +} + +void ApiReader::handleForwardedApi(DWORD_PTR vaStringPointer,char * functionNameParent, DWORD_PTR rvaParent, DWORD_PTR ordinalParent, ModuleInfo *moduleParent) +{ + size_t dllNameLength = 0; + DWORD_PTR ordinal = 0; + ModuleInfo *module = 0; + DWORD_PTR vaApi = 0; + DWORD_PTR rvaApi = 0; + char dllName[100] = {0}; + WCHAR dllNameW[100] = {0}; + char *fordwardedString = (char *)vaStringPointer; + char *searchFunctionName = strchr(fordwardedString, '.'); + + + if (!searchFunctionName) + return; + + dllNameLength = searchFunctionName - fordwardedString; + + if (dllNameLength >= 99) + { + return; + } + else + { + strncpy_s(dllName,sizeof(dllName),fordwardedString,dllNameLength); + } + + searchFunctionName++; + + //Windows 7 + if (!strncmp(dllName,"api-ms-win-", 11)) + { + /* + Info: http://www.nirsoft.net/articles/windows_7_kernel_architecture_changes.html + */ + + FARPROC addy = GetProcAddress(GetModuleHandleA(dllName), searchFunctionName); + + if (addy != 0) + { + addApi(functionNameParent,0, ordinalParent, (DWORD_PTR)addy, (DWORD_PTR)addy - (DWORD_PTR)GetModuleHandleA(dllName), true, moduleParent); + } + + return; + } + + strcat_s(dllName,sizeof(dllName),".dll"); + + size_t convertedChars = 0; + mbstowcs_s(&convertedChars, dllNameW, strlen(dllName) + 1, dllName, _TRUNCATE); + + if (!_wcsicmp(dllNameW, moduleParent->getFilename())) + { + module = moduleParent; + } + else + { + module = findModuleByName(dllNameW); + } + + if (module != 0) // module == 0 -> can be ignored + { + /*if ((module->isAlreadyParsed == false) && (module != moduleParent)) + { + //do API extract + + if (module->parsing == true) + { + //some stupid circle dependency + printf("stupid circle dependency %s\n",module->getFilename()); + } + else + { + parseModule(module); + } + }*/ + + if (strchr(searchFunctionName,'#')) + { + //forwarding by ordinal + searchFunctionName++; + ordinal = atoi(searchFunctionName); + findApiByModuleAndOrdinal(module, ordinal, &vaApi, &rvaApi); + } + else + { + findApiByModuleAndName(module, searchFunctionName, &vaApi, &rvaApi); + } + + if (rvaApi == 0) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"handleForwardedApi :: Api not found, this is really BAD! %S\r\n",fordwardedString); +#endif + } + else + { + addApi(functionNameParent,0, ordinalParent, vaApi, rvaApi, true, moduleParent); + } + } + +} + +ModuleInfo * ApiReader::findModuleByName(WCHAR *name) +{ + for (unsigned int i = 0; i < moduleList.size(); i++) { + if (!_wcsicmp(moduleList[i].getFilename(), name)) + { + return &moduleList[i]; + } + } + + return 0; +} + +void ApiReader::addApiWithoutName(DWORD_PTR ordinal, DWORD_PTR va, DWORD_PTR rva,bool isForwarded, ModuleInfo *moduleInfo) +{ + addApi(0, 0, ordinal, va, rva, isForwarded, moduleInfo); +} + +void ApiReader::addApi(char *functionName, WORD hint, DWORD_PTR ordinal, DWORD_PTR va, DWORD_PTR rva, bool isForwarded, ModuleInfo *moduleInfo) +{ + ApiInfo *apiInfo = new ApiInfo(); + + if ((functionName != 0) && (strlen(functionName) < MAX_PATH)) + { + strcpy_s(apiInfo->name, MAX_PATH, functionName); + } + else + { + apiInfo->name[0] = 0x00; + } + + apiInfo->ordinal = ordinal; + apiInfo->isForwarded = isForwarded; + apiInfo->module = moduleInfo; + apiInfo->rva = rva; + apiInfo->va = va; + apiInfo->hint = hint; + + setMinMaxApiAddress(va); + + moduleInfo->apiList.push_back(apiInfo); + + apiList.insert(API_Pair(va, apiInfo)); +} + +BYTE * ApiReader::getHeaderFromProcess(ModuleInfo * module) +{ + BYTE *bufferHeader = 0; + DWORD readSize = 0; + + if (module->modBaseSize < PE_HEADER_BYTES_COUNT) + { + readSize = module->modBaseSize; + } + else + { + readSize = PE_HEADER_BYTES_COUNT; + } + + bufferHeader = new BYTE[readSize]; + + if(!readMemoryFromProcess(module->modBaseAddr, readSize, bufferHeader)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"getHeaderFromProcess :: Error reading header\r\n"); +#endif + delete[] bufferHeader; + return 0; + } + else + { + return bufferHeader; + } +} + +BYTE * ApiReader::getExportTableFromProcess(ModuleInfo * module, PIMAGE_NT_HEADERS pNtHeader) +{ + DWORD readSize = 0; + BYTE *bufferExportTable = 0; + + readSize = pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size; + + if (readSize < (sizeof(IMAGE_EXPORT_DIRECTORY) + 8)) + { + //Something is wrong with the PE Header +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"Something is wrong with the PE Header here Export table size %d\r\n",readSize); +#endif + readSize = sizeof(IMAGE_EXPORT_DIRECTORY) + 100; + } + + if (readSize) + { + bufferExportTable = new BYTE[readSize]; + + if(!readMemoryFromProcess(module->modBaseAddr + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, readSize, bufferExportTable)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"getExportTableFromProcess :: Error reading export table from process\r\n"); +#endif + delete[] bufferExportTable; + return 0; + } + else + { + return bufferExportTable; + } + } + else + { + return 0; + } +} + +void ApiReader::parseModuleWithProcess(ModuleInfo * module) +{ + PIMAGE_NT_HEADERS pNtHeader = 0; + PIMAGE_DOS_HEADER pDosHeader = 0; + BYTE *bufferHeader = 0; + BYTE *bufferExportTable = 0; + + + bufferHeader = getHeaderFromProcess(module); + + if (bufferHeader == 0) + return; + + pDosHeader = (PIMAGE_DOS_HEADER)bufferHeader; + pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)bufferHeader + (DWORD_PTR)(pDosHeader->e_lfanew)); + + if (isPeAndExportTableValid(pNtHeader)) + { + bufferExportTable = getExportTableFromProcess(module, pNtHeader); + + if(bufferExportTable) + { + parseExportTable(module,pNtHeader,(PIMAGE_EXPORT_DIRECTORY)bufferExportTable, (DWORD_PTR)bufferExportTable - pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); + delete[] bufferExportTable; + } + } + + delete[] bufferHeader; +} + +void ApiReader::parseExportTable(ModuleInfo *module, PIMAGE_NT_HEADERS pNtHeader, PIMAGE_EXPORT_DIRECTORY pExportDir, DWORD_PTR deltaAddress) +{ + DWORD *addressOfFunctionsArray = 0,*addressOfNamesArray = 0; + WORD *addressOfNameOrdinalsArray = 0; + char *functionName = 0; + DWORD_PTR RVA = 0, VA = 0, ordinal = 0; + DWORD i = 0, j = 0; + bool withoutName; + + + addressOfFunctionsArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfFunctions + deltaAddress); + addressOfNamesArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfNames + deltaAddress); + addressOfNameOrdinalsArray = (WORD *)((DWORD_PTR)pExportDir->AddressOfNameOrdinals + deltaAddress); + +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"parseExportTable :: module %s NumberOfNames %X\r\n",module->fullPath,pExportDir->NumberOfNames); +#endif + + for (i = 0; i < pExportDir->NumberOfNames; i++) + { + functionName = (char*)(addressOfNamesArray[i] + deltaAddress); + ordinal = (addressOfNameOrdinalsArray[i] + pExportDir->Base); + RVA = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]]; + VA = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]] + module->modBaseAddr; + +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("parseExportTable :: api %S ")TEXT(" ordinal %d imagebase ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" RVA ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" VA ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),functionName,ordinal,module->modBaseAddr,RVA,VA); +#endif + if (!isApiBlacklisted(functionName)) + { + if (!isApiForwarded(RVA,pNtHeader)) + { + addApi(functionName, (WORD)i, ordinal,VA,RVA,false,module); + } + else + { + //printf("Forwarded: %s\n",functionName); + handleForwardedApi(RVA + deltaAddress,functionName,RVA,ordinal,module); + } + } + + } + + /*Exports without name*/ + if (pExportDir->NumberOfNames != pExportDir->NumberOfFunctions) + { + for (i = 0; i < pExportDir->NumberOfFunctions; i++) + { + withoutName = true; + for (j = 0; j < pExportDir->NumberOfNames; j++) + { + if(addressOfNameOrdinalsArray[j] == i) + { + withoutName = false; + break; + } + } + if (withoutName && addressOfFunctionsArray[i] != 0) + { + ordinal = (i+pExportDir->Base); + RVA = addressOfFunctionsArray[i]; + VA = (addressOfFunctionsArray[i] + module->modBaseAddr); + + + if (!isApiForwarded(RVA,pNtHeader)) + { + addApiWithoutName(ordinal,VA,RVA,false,module); + } + else + { + handleForwardedApi(RVA + deltaAddress,0,RVA,ordinal,module); + } + + } + } + } +} + +void ApiReader::findApiByModuleAndOrdinal(ModuleInfo * module, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi) +{ + findApiByModule(module,0,ordinal,vaApi,rvaApi); +} + +void ApiReader::findApiByModuleAndName(ModuleInfo * module, char * searchFunctionName, DWORD_PTR * vaApi, DWORD_PTR * rvaApi) +{ + findApiByModule(module,searchFunctionName,0,vaApi,rvaApi); +} + +void ApiReader::findApiByModule(ModuleInfo * module, char * searchFunctionName, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi) +{ + if (isModuleLoadedInOwnProcess(module)) + { + HMODULE hModule = GetModuleHandle(module->getFilename()); + + if (hModule) + { + if (ordinal) + { + *vaApi = (DWORD_PTR)GetProcAddress(hModule, (LPCSTR)ordinal); + } + else + { + *vaApi = (DWORD_PTR)GetProcAddress(hModule, searchFunctionName); + } + + + if (vaApi) + { + *rvaApi = (*vaApi) - (DWORD_PTR)hModule; + *vaApi = (*rvaApi) + module->modBaseAddr; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("findApiByModule :: vaApi == NULL, should never happen %S\r\n"),searchFunctionName); +#endif + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("findApiByModule :: hModule == NULL, should never happen %s\r\n"),module->getFilename()); +#endif + } + } + else + { + //search api in extern process + findApiInProcess(module,searchFunctionName,ordinal,vaApi,rvaApi); + } +} + +bool ApiReader::isModuleLoadedInOwnProcess(ModuleInfo * module) +{ + for (unsigned int i = 0; i < ownModuleList.size(); i++) + { + if (!_wcsicmp(module->fullPath, ownModuleList[i].fullPath)) + { + //printf("isModuleLoadedInOwnProcess :: %s %s\n",module->fullPath,ownModuleList[i].fullPath); + return true; + } + } + return false; +} + +void ApiReader::parseModuleWithOwnProcess( ModuleInfo * module ) +{ + PIMAGE_NT_HEADERS pNtHeader = 0; + PIMAGE_DOS_HEADER pDosHeader = 0; + HMODULE hModule = GetModuleHandle(module->getFilename()); + + if (hModule) + { + pDosHeader = (PIMAGE_DOS_HEADER)hModule; + pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)hModule + (DWORD_PTR)(pDosHeader->e_lfanew)); + + if (isPeAndExportTableValid(pNtHeader)) + { + parseExportTable(module, pNtHeader, (PIMAGE_EXPORT_DIRECTORY)((DWORD_PTR)hModule + pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress), (DWORD_PTR)hModule); + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("parseModuleWithOwnProcess :: hModule is NULL\r\n")); +#endif + } +} + +bool ApiReader::isPeAndExportTableValid(PIMAGE_NT_HEADERS pNtHeader) +{ + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) + { + Logger::printfDialog(TEXT("-> IMAGE_NT_SIGNATURE doesn't match.")); + return false; + } + else if ((pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress == 0) || (pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0)) + { + Logger::printfDialog(TEXT("-> No export table.")); + return false; + } + else + { + return true; + } +} + +void ApiReader::findApiInProcess(ModuleInfo * module, char * searchFunctionName, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi) +{ + PIMAGE_NT_HEADERS pNtHeader = 0; + PIMAGE_DOS_HEADER pDosHeader = 0; + BYTE *bufferHeader = 0; + BYTE *bufferExportTable = 0; + + + bufferHeader = getHeaderFromProcess(module); + + if (bufferHeader == 0) + return; + + pDosHeader = (PIMAGE_DOS_HEADER)bufferHeader; + pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)bufferHeader + (DWORD_PTR)(pDosHeader->e_lfanew)); + + if (isPeAndExportTableValid(pNtHeader)) + { + bufferExportTable = getExportTableFromProcess(module, pNtHeader); + + if(bufferExportTable) + { + findApiInExportTable(module,(PIMAGE_EXPORT_DIRECTORY)bufferExportTable, (DWORD_PTR)bufferExportTable - pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress,searchFunctionName,ordinal,vaApi,rvaApi); + delete[] bufferExportTable; + } + } + + delete[] bufferHeader; +} + +bool ApiReader::findApiInExportTable(ModuleInfo *module, PIMAGE_EXPORT_DIRECTORY pExportDir, DWORD_PTR deltaAddress, char * searchFunctionName, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi) +{ + DWORD *addressOfFunctionsArray = 0,*addressOfNamesArray = 0; + WORD *addressOfNameOrdinalsArray = 0; + char *functionName = 0; + DWORD i = 0, j = 0; + + addressOfFunctionsArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfFunctions + deltaAddress); + addressOfNamesArray = (DWORD *)((DWORD_PTR)pExportDir->AddressOfNames + deltaAddress); + addressOfNameOrdinalsArray = (WORD *)((DWORD_PTR)pExportDir->AddressOfNameOrdinals + deltaAddress); + + if (searchFunctionName) + { + for (i = 0; i < pExportDir->NumberOfNames; i++) + { + functionName = (char*)(addressOfNamesArray[i] + deltaAddress); + + if (!strcmp(functionName,searchFunctionName)) + { + *rvaApi = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]]; + *vaApi = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]] + module->modBaseAddr; + return true; + } + } + } + else + { + for (i = 0; i < pExportDir->NumberOfFunctions; i++) + { + if (ordinal == (i+pExportDir->Base)) + { + *rvaApi = addressOfFunctionsArray[i]; + *vaApi = (addressOfFunctionsArray[i] + module->modBaseAddr); + return true; + } + } + } + + return false; +} + + +void ApiReader::setModulePriority(ModuleInfo * module) +{ + WCHAR *moduleFileName = module->getFilename(); + + if (!_wcsicmp(moduleFileName, TEXT("kernelbase.dll"))) + { + module->priority = 0; + } + else if (!_wcsicmp(moduleFileName, TEXT("ntdll.dll"))) + { + module->priority = 0; + } + else if (!_wcsicmp(moduleFileName, TEXT("shlwapi.dll"))) + { + module->priority = 0; + } + else if (!_wcsicmp(moduleFileName, TEXT("kernel32.dll"))) + { + module->priority = 2; + } + else + { + module->priority = 1; + } +} + +bool ApiReader::isApiAddressValid(DWORD_PTR virtualAddress) +{ + return apiList.count(virtualAddress) > 0; +} + +ApiInfo * ApiReader::getApiByVirtualAddress(DWORD_PTR virtualAddress, bool * isSuspect) +{ + stdext::hash_multimap::iterator it1, it2; + size_t c = 0; + size_t countDuplicates = apiList.count(virtualAddress); + int countHighPriority = 0; + ApiInfo *apiFound = 0; + + + if (countDuplicates == 0) + { + Logger::printfDialog(TEXT("getApiByVirtualAddress :: No Api found ")TEXT(PRINTF_DWORD_PTR_FULL),virtualAddress); + return 0; + } + else if (countDuplicates == 1) + { + //API is 100% correct + *isSuspect = false; + it1 = apiList.find(virtualAddress); // Find first match. + return (ApiInfo *)((*it1).second); + } + else + { + it1 = apiList.find(virtualAddress); // Find first match. + it2 = it1; + for (c = 0; c < countDuplicates; c++, it1++) + { + apiFound = (ApiInfo *)((*it1).second); + + if (apiFound->module->priority >= 1) //1 == high priority + { + countHighPriority++; + } + } + + it1 = it2; + + if (countHighPriority == 0) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("getApiByVirtualAddress :: countHighPriority == 0 ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),virtualAddress); +#endif + *isSuspect = true; + return (ApiInfo *)((*it1).second); + } + else if (countHighPriority == 1) + { + //API is 100% correct if countHighPriority == 1 + + *isSuspect = false; + for (c = 0; c < countDuplicates; c++, it1++) + { + apiFound = (ApiInfo *)((*it1).second); + + if (apiFound->module->priority >= 1) //1 == high priority + { + return apiFound; + } + } + } + else + { + //API not 100% correct +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("getApiByVirtualAddress :: countHighPriority == %d ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),countHighPriority,virtualAddress); +#endif + *isSuspect = true; + /*for (c = 0; c < countDuplicates; c++, it1++) + { + apiFound = (ApiInfo *)((*it1).second); + Logger::printfDialog("%s - %s %X %X\n",apiFound->name,apiFound->module->getFilename(),apiFound->rva, apiFound->ordinal); + } + it1 = it2;*/ + + for (c = 0; c < countDuplicates; c++, it1++) + { + apiFound = (ApiInfo *)((*it1).second); + + //prefer APIs with a name + if (apiFound->module->priority >= 1 && apiFound->name[0] != 0x00) //1 == high priority + { + //prefer ANSI/UNICODE APIs + if (strrchr(apiFound->name,TEXT('W')) || strrchr(apiFound->name,TEXT('A'))) + { + return apiFound; + } + } + } + + it1 = it2; + + for (c = 0; c < countDuplicates; c++, it1++) + { + apiFound = (ApiInfo *)((*it1).second); + + //prefer APIs with a name + if (apiFound->module->priority == 2 && !strrchr(apiFound->name,TEXT('_'))) //1 == high priority + { + return apiFound; + } + } + + it1 = it2; + + for (c = 0; c < countDuplicates; c++, it1++) + { + apiFound = (ApiInfo *)((*it1).second); + if (apiFound->module->priority == 1 && apiFound->name[0] != 0x00) //1 == high priority + { + return apiFound; + } + } + + it1 = it2; + + for (c = 0; c < countDuplicates; c++, it1++) + { + apiFound = (ApiInfo *)((*it1).second); + + if (apiFound->module->priority == 1) //1 == high priority + { + return apiFound; + } + } + } + } + + //is never reached + Logger::printfDialog(TEXT("getApiByVirtualAddress :: There is a big bug\n")); + return (ApiInfo *) 1; +} + +void ApiReader::setMinMaxApiAddress(DWORD_PTR virtualAddress) +{ + if (virtualAddress < minApiAddress) + { + minApiAddress = virtualAddress - 1; + } + if (virtualAddress > maxApiAddress) + { + maxApiAddress = virtualAddress + 1; + } +} + +void ApiReader::readAndParseIAT(DWORD_PTR addressIAT, DWORD sizeIAT, std::map &moduleListNew) +{ + moduleThunkList = &moduleListNew; + BYTE *dataIat = new BYTE[sizeIAT]; + if (readMemoryFromProcess(addressIAT,sizeIAT,dataIat)) + { + parseIAT(addressIAT,dataIat,sizeIAT); + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("ApiReader::readAndParseIAT :: error reading iat ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),addressIAT); +#endif + } + + delete[] dataIat; +} + +void ApiReader::parseIAT(DWORD_PTR addressIAT, BYTE * iatBuffer, SIZE_T size) +{ + ApiInfo *apiFound = 0; + ModuleInfo *module = 0; + bool isSuspect = false; + int countApiFound = 0, countApiNotFound = 0; + DWORD_PTR * pIATAddress = (DWORD_PTR *)iatBuffer; + SIZE_T sizeIAT = size / sizeof(DWORD_PTR); + + bool foundModuleBreak = false; + + for (SIZE_T i = 0; i < sizeIAT; i++) + { + //Logger::printfDialog("%08X %08X %d von %d",addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer,pIATAddress[i],i,sizeIAT); + + + if (pIATAddress[i] == 0 || pIATAddress[i] == -1) + { + /*if (pIATAddress[i+1] != 0) + { + printf("parseIAT :: Module break\n"); + }*/ + /*else + { + printf("parseIAT :: IAT finished\n"); + break; + }*/ + foundModuleBreak = true; + } + else if ( (pIATAddress[i] > minApiAddress) && (pIATAddress[i] < maxApiAddress) ) + { + + apiFound = getApiByVirtualAddress(pIATAddress[i], &isSuspect); + if (apiFound == (ApiInfo *)1) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("apiFound == (ApiInfo *)1 -> ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),pIATAddress[i]); +#endif + } + else if (apiFound) + { + countApiFound++; +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" %s %d %s\r\n"),apiFound->va, apiFound->module->getFilename(), apiFound->ordinal,apiFound->name); +#endif + if (module != apiFound->module) + { + module = apiFound->module; + addFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, apiFound, true, isSuspect); + } + else + { + addFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, apiFound, false, isSuspect); + } + + } + else + { + countApiNotFound++; + addNotFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, pIATAddress[i]); + //printf("parseIAT :: API not found %08X\n", pIATAddress[i]); + } + } + else + { + //printf("parseIAT :: API not found %08X\n", pIATAddress[i]); + countApiNotFound++; + addNotFoundApiToModuleList(addressIAT + (DWORD_PTR)&pIATAddress[i] - (DWORD_PTR)iatBuffer, pIATAddress[i]); + } + } + + Logger::printfDialog(TEXT("IAT parsing finished, found %d valid APIs, missed %d APIs"),countApiFound,countApiNotFound); +} + +void ApiReader::addFoundApiToModuleList(DWORD_PTR iatAddressVA, ApiInfo * apiFound, bool isNewModule, bool isSuspect) +{ + if (isNewModule) + { + addModuleToModuleList(apiFound->module->getFilename(), iatAddressVA - targetImageBase); + } + addFunctionToModuleList(apiFound, iatAddressVA, iatAddressVA - targetImageBase, apiFound->ordinal, true, isSuspect); +} + +bool ApiReader::addModuleToModuleList(const WCHAR * moduleName, DWORD_PTR firstThunk) +{ + ImportModuleThunk module; + + module.firstThunk = firstThunk; + wcscpy_s(module.moduleName, MAX_PATH, moduleName); + + (*moduleThunkList).insert(std::pair(firstThunk,module)); + + return true; +} + +void ApiReader::addUnknownModuleToModuleList(DWORD_PTR firstThunk) +{ + ImportModuleThunk module; + + module.firstThunk = firstThunk; + wcscpy_s(module.moduleName, MAX_PATH, TEXT("?")); + + (*moduleThunkList).insert(std::pair(firstThunk,module)); +} + +bool ApiReader::addFunctionToModuleList(ApiInfo * apiFound, DWORD_PTR va, DWORD_PTR rva, DWORD_PTR ordinal, bool valid, bool suspect) +{ + ImportThunk import; + ImportModuleThunk * module = 0; + std::map::iterator iterator1; + + if ((*moduleThunkList).size() > 1) + { + iterator1 = (*moduleThunkList).begin(); + while (iterator1 != (*moduleThunkList).end()) + { + if (rva >= iterator1->second.firstThunk) + { + iterator1++; + if (iterator1 == (*moduleThunkList).end()) + { + iterator1--; + module = &(iterator1->second); + break; + } + else if (rva < iterator1->second.firstThunk) + { + iterator1--; + module = &(iterator1->second); + break; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("Error iterator1 != (*moduleThunkList).end()\r\n")); +#endif + break; + } + } + } + else + { + iterator1 = (*moduleThunkList).begin(); + module = &(iterator1->second); + } + + if (!module) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("ImportsHandling::addFunction module not found rva ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),rva); +#endif + return false; + } + + + import.suspect = suspect; + import.valid = valid; + import.va = va; + import.rva = rva; + import.apiAddressVA = apiFound->va; + import.ordinal = ordinal; + import.hint = (WORD)apiFound->hint; + + wcscpy_s(import.moduleName, MAX_PATH, apiFound->module->getFilename()); + strcpy_s(import.name, MAX_PATH, apiFound->name); + + module->thunkList.insert(std::pair(import.rva, import)); + + return true; +} + +void ApiReader::clearAll() +{ + minApiAddress = -1; + maxApiAddress = 0; + + for ( stdext::hash_multimap::iterator it = apiList.begin(); it != apiList.end(); ++it ) + { + delete it->second; + } + apiList.clear(); + + if (moduleThunkList != 0) + { + (*moduleThunkList).clear(); + } +} + +bool ApiReader::addNotFoundApiToModuleList(DWORD_PTR iatAddressVA, DWORD_PTR apiAddress) +{ + ImportThunk import; + ImportModuleThunk * module = 0; + std::map::iterator iterator1; + DWORD_PTR rva = iatAddressVA - targetImageBase; + + if ((*moduleThunkList).size() > 0) + { + iterator1 = (*moduleThunkList).begin(); + while (iterator1 != (*moduleThunkList).end()) + { + if (rva >= iterator1->second.firstThunk) + { + iterator1++; + if (iterator1 == (*moduleThunkList).end()) + { + iterator1--; + //new unknown module + if (iterator1->second.moduleName[0] == L'?') + { + module = &(iterator1->second); + } + else + { + addUnknownModuleToModuleList(rva); + module = &((*moduleThunkList).find(rva)->second); + } + + break; + } + else if (rva < iterator1->second.firstThunk) + { + iterator1--; + module = &(iterator1->second); + break; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("Error iterator1 != (*moduleThunkList).end()\r\n")); +#endif + break; + } + } + } + else + { + //new unknown module + addUnknownModuleToModuleList(rva); + module = &((*moduleThunkList).find(rva)->second); + } + + if (!module) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("ImportsHandling::addFunction module not found rva ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),rva); +#endif + return false; + } + + + import.suspect = true; + import.valid = false; + import.va = iatAddressVA; + import.rva = rva; + import.apiAddressVA = apiAddress; + import.ordinal = 0; + + wcscpy_s(import.moduleName, MAX_PATH, TEXT("?")); + strcpy_s(import.name, MAX_PATH, "?"); + + module->thunkList.insert(std::pair(import.rva, import)); + + return true; +} + +bool ApiReader::isApiBlacklisted( const char * functionName ) +{ + if (SystemInformation::currenOS < WIN_VISTA_32) + { + if (!strcmp(functionName, "RestoreLastError")) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } + + + /*#ifdef _WIN64 + else if (SystemInformation::currenOS == WIN_XP_64 && !strcmp(functionName, "DecodePointer")) + { + return true; + } +#endif*/ +} + +bool ApiReader::isWinSxSModule( ModuleInfo * module ) +{ + if (wcsstr(module->fullPath, TEXT("\\WinSxS\\"))) + { + return true; + } + else if (wcsstr(module->fullPath, TEXT("\\winsxs\\"))) + { + return true; + } + else + { + return false; + } +} diff --git a/Scylla/ApiReader.h b/Scylla/ApiReader.h new file mode 100644 index 0000000..de09da2 --- /dev/null +++ b/Scylla/ApiReader.h @@ -0,0 +1,70 @@ +#pragma once + +#include "ProcessAccessHelp.h" +#include "Thunks.h" + +typedef std::pair API_Pair; + +class ApiReader : public ProcessAccessHelp { +public: + static stdext::hash_multimap apiList; //api look up table + + static std::map * moduleThunkList; //store found apis + + static DWORD_PTR minApiAddress; + static DWORD_PTR maxApiAddress; + + /* + * Read all APIs from target process + */ + void readApisFromModuleList(); + + + bool isApiAddressValid(DWORD_PTR virtualAddress); + ApiInfo * getApiByVirtualAddress(DWORD_PTR virtualAddress, bool * isSuspect); + void readAndParseIAT(DWORD_PTR addressIAT, DWORD sizeIAT,std::map &moduleListNew ); + + void clearAll(); + +private: + void parseIAT(DWORD_PTR addressIAT, BYTE * iatBuffer, SIZE_T size); + + void addApi(char *functionName, WORD hint, DWORD_PTR ordinal, DWORD_PTR va, DWORD_PTR rva, bool isForwarded, ModuleInfo *moduleInfo); + void addApiWithoutName(DWORD_PTR ordinal, DWORD_PTR va, DWORD_PTR rva,bool isForwarded, ModuleInfo *moduleInfo); + inline bool isApiForwarded(DWORD_PTR rva, PIMAGE_NT_HEADERS pNtHeader); + void handleForwardedApi(DWORD_PTR vaStringPointer,char *functionNameParent,DWORD_PTR rvaParent, DWORD_PTR ordinalParent, ModuleInfo *moduleParent); + void parseModule(ModuleInfo *module); + void parseModuleWithProcess(ModuleInfo * module); + + + void parseExportTable(ModuleInfo *module, PIMAGE_NT_HEADERS pNtHeader, PIMAGE_EXPORT_DIRECTORY pExportDir, DWORD_PTR deltaAddress); + + ModuleInfo * findModuleByName(WCHAR *name); + + void findApiByModuleAndOrdinal(ModuleInfo * module, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi); + void findApiByModuleAndName(ModuleInfo * module, char * searchFunctionName, DWORD_PTR * vaApi, DWORD_PTR * rvaApi); + void findApiByModule(ModuleInfo * module, char * searchFunctionName, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi); + + bool isModuleLoadedInOwnProcess( ModuleInfo * module ); + void parseModuleWithOwnProcess( ModuleInfo * module ); + bool isPeAndExportTableValid(PIMAGE_NT_HEADERS pNtHeader); + void findApiInProcess( ModuleInfo * module, char * searchFunctionName, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi ); + bool findApiInExportTable(ModuleInfo *module, PIMAGE_EXPORT_DIRECTORY pExportDir, DWORD_PTR deltaAddress, char * searchFunctionName, DWORD_PTR ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi); + + BYTE * getHeaderFromProcess(ModuleInfo * module); + BYTE * getExportTableFromProcess(ModuleInfo * module, PIMAGE_NT_HEADERS pNtHeader); + + void setModulePriority(ModuleInfo * module); + void setMinMaxApiAddress(DWORD_PTR virtualAddress); + + + void parseModuleWithMapping(ModuleInfo *moduleInfo); //not used + void addFoundApiToModuleList(DWORD_PTR iatAddress, ApiInfo * apiFound, bool isNewModule, bool isSuspect); + bool addModuleToModuleList(const WCHAR * moduleName, DWORD_PTR firstThunk); + bool addFunctionToModuleList(ApiInfo * apiFound, DWORD_PTR va, DWORD_PTR rva, DWORD_PTR ordinal, bool valid, bool suspect); + bool addNotFoundApiToModuleList(DWORD_PTR iatAddressVA, DWORD_PTR apiAddress); + + void addUnknownModuleToModuleList(DWORD_PTR firstThunk); + bool isApiBlacklisted( const char * functionName ); + bool isWinSxSModule( ModuleInfo * module ); +}; \ No newline at end of file diff --git a/Scylla/ConfigurationHolder.cpp b/Scylla/ConfigurationHolder.cpp new file mode 100644 index 0000000..d7af60b --- /dev/null +++ b/Scylla/ConfigurationHolder.cpp @@ -0,0 +1,266 @@ + +#include "ConfigurationHolder.h" +#include "resource.h" + +WCHAR ConfigurationHolder::configPath[MAX_PATH]; +ConfigurationInitializer ConfigurationHolder::config; + +//#define DEBUG_COMMENTS + +ConfigurationInitializer::ConfigurationInitializer() +{ + ConfigObject configObject; + + mapConfig[USE_PE_HEADER_FROM_DISK] = configObject.newValues(L"USE_PE_HEADER_FROM_DISK", Boolean, IDC_CHECK_PE_HEADER_FROM_DISK); + mapConfig[DEBUG_PRIVILEGE] = configObject.newValues(L"DEBUG_PRIVILEGE", Boolean, IDC_CHECK_DEBUG_PRIVILEGES); + mapConfig[CREATE_BACKUP] = configObject.newValues(L"CREATE_BACKUP", Boolean, IDC_CHECK_CREATE_BACKUP); + mapConfig[DLL_INJECTION_AUTO_UNLOAD] = configObject.newValues(L"DLL_INJECTION_AUTO_UNLOAD", Boolean, IDC_CHECK_UNLOAD_DLL); + mapConfig[UPDATE_HEADER_CHECKSUM] = configObject.newValues(L"UPDATE_HEADER_CHECKSUM", Boolean, IDC_CHECK_HEADER_CHECKSUM); + + mapConfig[IAT_SECTION_NAME] = configObject.newValues(L"IAT_SECTION_NAME", String, IDC_OPTIONS_SECTIONNAME); +} + + +bool ConfigurationHolder::loadConfiguration() +{ + std::map::iterator mapIter; + + if (!buildConfigFilePath()) + { + return false; + } + + for (mapIter = config.mapConfig.begin() ; mapIter != config.mapConfig.end(); mapIter++) + { + if (!loadConfig((*mapIter).second)) + { + return false; + } + } + + return true; +} + +bool ConfigurationHolder::saveConfiguration() +{ + std::map::iterator mapIter; + + if (!buildConfigFilePath()) + { + return false; + } + + for (mapIter = config.mapConfig.begin() ; mapIter != config.mapConfig.end(); mapIter++) + { + if (!saveConfig((*mapIter).second)) + { + return false; + } + } + + return true; +} + +bool ConfigurationHolder::saveNumericToConfigFile(ConfigObject & configObject, int nBase) +{ + + if (nBase == 16) + { +#ifdef _WIN64 + swprintf_s(configObject.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%016I64X"),configObject.valueNumeric); +#else + swprintf_s(configObject.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%08X"),configObject.valueNumeric); +#endif + } + else + { +#ifdef _WIN64 + swprintf_s(configObject.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%I64u"),configObject.valueNumeric); +#else + swprintf_s(configObject.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%u"),configObject.valueNumeric); +#endif + } + + + if (WritePrivateProfileString(TEXT(CONFIG_FILE_SECTION_NAME), configObject.name, configObject.valueString, configPath)) + { + return true; + } + else + { + return false; + } +} + +bool ConfigurationHolder::readNumericFromConfigFile(ConfigObject & configObject, int nBase) +{ + GetPrivateProfileString(TEXT(CONFIG_FILE_SECTION_NAME),configObject.name,TEXT(""),configObject.valueString, 100, configPath); + + if (wcslen(configObject.valueString) > 0) + { + +#ifdef _WIN64 + configObject.valueNumeric = _wcstoui64(configObject.valueString, NULL, nBase); +#else + configObject.valueNumeric = wcstoul(configObject.valueString, NULL, nBase); +#endif + + if (configObject.valueNumeric) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} + + +bool ConfigurationHolder::saveStringToConfigFile(ConfigObject & configObject) +{ + if (WritePrivateProfileString(TEXT(CONFIG_FILE_SECTION_NAME), configObject.name, configObject.valueString, configPath)) + { + return true; + } + else + { + return false; + } +} + +bool ConfigurationHolder::readStringFromConfigFile(ConfigObject & configObject) +{ + GetPrivateProfileString(TEXT(CONFIG_FILE_SECTION_NAME),configObject.name,TEXT(""),configObject.valueString, 100, configPath); + + if (wcslen(configObject.valueString) > 0) + { + return true; + } + else + { + return false; + } +} + +bool ConfigurationHolder::readBooleanFromConfigFile(ConfigObject & configObject) +{ + if (GetPrivateProfileInt(TEXT(CONFIG_FILE_SECTION_NAME), configObject.name, 0, configPath) != 0) + { + configObject.valueNumeric = 1; + } + else + { + configObject.valueNumeric = 0; + } + + return true; +} + +bool ConfigurationHolder::saveBooleanToConfigFile(ConfigObject & configObject) +{ + WCHAR *boolValue = 0; + + if (configObject.valueNumeric == 0) + { + boolValue = L"0"; + } + else + { + boolValue = L"1"; + } + + if (WritePrivateProfileString(TEXT(CONFIG_FILE_SECTION_NAME), configObject.name, boolValue, configPath)) + { + return true; + } + else + { + return false; + } +} + +bool ConfigurationHolder::loadConfig(ConfigObject & configObject) +{ + switch (configObject.configType) + { + case String: + return readStringFromConfigFile(configObject); + break; + case Boolean: + return readBooleanFromConfigFile(configObject); + break; + case Decimal: + return readNumericFromConfigFile(configObject, 10); + break; + case Hexadecimal: + return readNumericFromConfigFile(configObject, 16); + break; + default: + return false; + } +} + +bool ConfigurationHolder::saveConfig(ConfigObject & configObject) +{ + switch (configObject.configType) + { + case String: + return saveStringToConfigFile(configObject); + break; + case Boolean: + return saveBooleanToConfigFile(configObject); + break; + case Decimal: + return saveNumericToConfigFile(configObject, 10); + break; + case Hexadecimal: + return saveNumericToConfigFile(configObject, 16); + break; + default: + return false; + } +} + +ConfigObject * ConfigurationHolder::getConfigObject(Configuration configuration) +{ + return &(config.mapConfig[configuration]); +} + +bool ConfigurationHolder::buildConfigFilePath() +{ + ZeroMemory(configPath, sizeof(configPath)); + + if (!GetModuleFileName(0, configPath, _countof(configPath))) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("buildConfigFilePath :: GetModuleFileName failed %d\r\n",GetLastError()); +#endif + return false; + } + + //remove exe file name + for (size_t i = wcslen(configPath) - 1; i >= 0; i--) + { + if (configPath[i] == L'\\') + { + configPath[i + 1] = 0; + break; + } + } + + wcscat_s(configPath, _countof(configPath), TEXT(CONFIG_FILE_NAME) ); + + //wprintf(L"configPath %s\n\n", configPath); + + return true; +} + +std::map & ConfigurationHolder::getConfigList() +{ + return config.mapConfig; +} \ No newline at end of file diff --git a/Scylla/ConfigurationHolder.h b/Scylla/ConfigurationHolder.h new file mode 100644 index 0000000..c617832 --- /dev/null +++ b/Scylla/ConfigurationHolder.h @@ -0,0 +1,100 @@ + +#pragma once + +#include +#include +#include + +#define CONFIG_FILE_NAME "Scylla.ini" +#define CONFIG_FILE_SECTION_NAME "SCYLLA_CONFIG" + +enum ConfigType { + String, + Decimal, + Hexadecimal, + Boolean +}; + +enum Configuration { + USE_PE_HEADER_FROM_DISK, + DEBUG_PRIVILEGE, + CREATE_BACKUP, + DLL_INJECTION_AUTO_UNLOAD, + IAT_SECTION_NAME, + UPDATE_HEADER_CHECKSUM, +}; + +#define CONFIG_OPTIONS_STRING_LENGTH 100 + +class ConfigObject { +public: + WCHAR name[MAX_PATH]; + ConfigType configType; + + DWORD_PTR valueNumeric; + WCHAR valueString[CONFIG_OPTIONS_STRING_LENGTH]; + + int dialogItemValue; + + ConfigObject& newValues(WCHAR * configname, ConfigType config, int dlgValue) + { + wcscpy_s(name, MAX_PATH, configname); + configType = config; + valueNumeric = 0; + ZeroMemory(valueString, sizeof(valueString)); + dialogItemValue = dlgValue; + + return *this; + } + + bool isTrue() + { + return (valueNumeric == 1); + } + + void setTrue() + { + valueNumeric = 1; + } + + void setFalse() + { + valueNumeric = 0; + } +}; + +class ConfigurationInitializer { +public: + std::map mapConfig; + + ConfigurationInitializer(); +}; + +class ConfigurationHolder { +public: + + static bool loadConfiguration(); + static bool saveConfiguration(); + + static ConfigObject * getConfigObject(Configuration configuration); + static std::map & getConfigList(); + +private: + static ConfigurationInitializer config; + static WCHAR configPath[MAX_PATH]; + + static bool buildConfigFilePath(); + + + + static bool readStringFromConfigFile(ConfigObject & configObject); + static bool readBooleanFromConfigFile(ConfigObject & configObject); + static bool readNumericFromConfigFile(ConfigObject & configObject, int nBase); + + static bool saveStringToConfigFile(ConfigObject & configObject); + static bool saveBooleanToConfigFile(ConfigObject & configObject); + static bool saveNumericToConfigFile(ConfigObject & configObject, int nBase); + + static bool loadConfig(ConfigObject & configObject); + static bool saveConfig(ConfigObject & configObject); +}; \ No newline at end of file diff --git a/Scylla/DisassemblerGui.cpp b/Scylla/DisassemblerGui.cpp new file mode 100644 index 0000000..c1331ef --- /dev/null +++ b/Scylla/DisassemblerGui.cpp @@ -0,0 +1,262 @@ +#include "DisassemblerGui.h" +#include "ProcessAccessHelp.h" + +HWND DisassemblerGui::hWndDlg = 0; +HINSTANCE DisassemblerGui::hInstance = 0; +DWORD_PTR DisassemblerGui::startAddress = 0; +WCHAR DisassemblerGui::tempBuffer[100]; + +INT_PTR DisassemblerGui::initDialog(HINSTANCE hInst, HWND hWndParent, DWORD_PTR address) +{ + hInstance = hInst; + startAddress = address; + return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLG_DISASSEMBLER),hWndParent, (DLGPROC)disassemblerDlgProc); +} + +LRESULT CALLBACK DisassemblerGui::disassemblerDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + hWndDlg = hWnd; + switch (uMsg) + { + case WM_INITDIALOG: + addColumnsToDisassembler(GetDlgItem(hWnd, IDC_LIST_DISASSEMBLER)); + displayDisassembly(); + break; + + case WM_CONTEXTMENU: + OnContextMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_BTN_PICKDLL_OK: + /*index = ListView_GetSelectionMark(GetDlgItem(hWnd, IDC_LIST_DLLSELECT)); + if (index != -1) + { + selectedModule = &(*moduleList).at(index); + EndDialog(hWnd, 1); + }*/ + + return TRUE; + case IDC_BTN_PICKDLL_CANCEL: + EndDialog(hWnd, 0); + return TRUE; + case IDCANCEL: + EndDialog(hWnd, 0); + return TRUE; + } + } + return FALSE; +} + +void DisassemblerGui::addColumnsToDisassembler( HWND list ) +{ + if (list) + { + LVCOLUMN * lvc = (LVCOLUMN*)malloc(sizeof(LVCOLUMN)); + + ListView_SetExtendedListViewStyleEx(list,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT); + + lvc->mask = LVCF_TEXT | LVCF_WIDTH; + lvc->fmt = LVCFMT_LEFT; + lvc->cx = 105; + lvc->pszText = L"Address"; + ListView_InsertColumn(list, COL_ADDRESS, lvc); + + lvc->mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; + lvc->fmt = LVCFMT_CENTER; + lvc->cx = 40; + lvc->pszText = L"Size"; + ListView_InsertColumn(list, COL_INSTRUCTION_SIZE, lvc); + + lvc->mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; + lvc->fmt = LVCFMT_LEFT; + lvc->cx = 130; + lvc->pszText = L"OpCodes"; + ListView_InsertColumn(list, COL_OPCODES, lvc); + + lvc->mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; + lvc->fmt = LVCFMT_LEFT; + lvc->cx = 200; + lvc->pszText = L"Instructions"; + ListView_InsertColumn(list, COL_INSTRUCTION, lvc); + + + free(lvc); + } +} + +void DisassemblerGui::displayDisassembly() +{ + LVITEM item; + HWND hList = GetDlgItem(hWndDlg, IDC_LIST_DISASSEMBLER); + + ListView_DeleteAllItems(hList); + + item.mask = LVIF_TEXT; + + BYTE * data = new BYTE[DISASSEMBLER_GUI_MEMORY_SIZE]; + + ProcessAccessHelp::readMemoryFromProcess(startAddress,DISASSEMBLER_GUI_MEMORY_SIZE, data); + + ProcessAccessHelp::disassembleMemory(data,DISASSEMBLER_GUI_MEMORY_SIZE, startAddress); + + for (unsigned int i = 0; i < ProcessAccessHelp::decodedInstructionsCount; i++) + { + +#ifdef _WIN64 + swprintf_s(tempBuffer, _countof(tempBuffer),L"%016I64X",ProcessAccessHelp::decodedInstructions[i].offset); +#else + swprintf_s(tempBuffer, _countof(tempBuffer),L"%08X",ProcessAccessHelp::decodedInstructions[i].offset); +#endif + + item.iItem = i; + item.iSubItem = COL_ADDRESS; + item.pszText = tempBuffer; + item.iItem = ListView_InsertItem(hList, &item); + + swprintf_s(tempBuffer, _countof(tempBuffer),L"%02d",ProcessAccessHelp::decodedInstructions[i].size); + + item.iSubItem = COL_INSTRUCTION_SIZE; + item.pszText = tempBuffer; + ListView_SetItem(hList, &item); + + swprintf_s(tempBuffer, _countof(tempBuffer),L"%-24S",(char *)ProcessAccessHelp::decodedInstructions[i].instructionHex.p); + + item.iSubItem = COL_OPCODES; + item.pszText = tempBuffer; + ListView_SetItem(hList, &item); + + swprintf_s(tempBuffer, _countof(tempBuffer),L"%S%S%S",(char*)ProcessAccessHelp::decodedInstructions[i].mnemonic.p, ProcessAccessHelp::decodedInstructions[i].operands.length != 0 ? " " : "", (char*)ProcessAccessHelp::decodedInstructions[i].operands.p); + + item.iSubItem = COL_INSTRUCTION; + item.pszText = tempBuffer; + ListView_SetItem(hList, &item); + } + + delete [] data; +} + +void DisassemblerGui::OnContextMenu(int x, int y) +{ + HWND hwnd = 0; + int selection; + POINT pt = { x, y }; // location of mouse click + HTREEITEM selectedTreeNode = 0; + + if ((hwnd = mouseInDialogItem(IDC_LIST_DISASSEMBLER, pt)) != NULL) + { + HMENU hmenuTrackPopup = getCorrectSubMenu(IDR_MENU_DISASSEMBLER, 0); + + BOOL menuItem = TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, 0); + + if (menuItem) + { + selection = ListView_GetSelectionMark(hwnd); + if (selection != -1) //valid selection? + { + switch (menuItem) + { + case ID__DIS_ADDRESS: + getModuleListItem(COL_ADDRESS,selection,tempBuffer); + copyToClipboard(); + break; + case ID__DIS_SIZE: + getModuleListItem(COL_INSTRUCTION_SIZE,selection,tempBuffer); + copyToClipboard(); + break; + case ID__DIS_OPCODES: + getModuleListItem(COL_OPCODES,selection,tempBuffer); + copyToClipboard(); + break; + case ID__DIS_INSTRUCTIONS: + getModuleListItem(COL_INSTRUCTION,selection,tempBuffer); + copyToClipboard(); + break; + } + } + } + } + +} + +void DisassemblerGui::copyToClipboard() +{ + HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(tempBuffer)); + LPVOID lock = GlobalLock(hMem); + + CopyMemory(lock,tempBuffer, sizeof(tempBuffer)); + + if (!OpenClipboard(hWndDlg)) + return; + + EmptyClipboard(); + + GlobalUnlock(hMem); + + SetClipboardData(CF_UNICODETEXT, hMem); + CloseClipboard(); + + GlobalFree(hMem); +} + +void DisassemblerGui::getModuleListItem(int column, int iItem, WCHAR * buffer) +{ + LVITEM pitem = {0}; + pitem.iItem = iItem; + pitem.iSubItem = column; + pitem.mask = LVIF_TEXT; + pitem.cchTextMax = 256; + pitem.pszText = buffer; + ListView_GetItem(GetDlgItem(hWndDlg, IDC_LIST_DISASSEMBLER),&pitem); +} + + +HMENU DisassemblerGui::getCorrectSubMenu(int menuItem, int subMenuItem) +{ + HMENU hmenu; // top-level menu + HMENU hmenuTrackPopup; // shortcut menu + // Load the menu resource. + if ((hmenu = LoadMenu(hInstance, MAKEINTRESOURCE(menuItem))) == NULL) + return 0; + + hmenuTrackPopup = GetSubMenu(hmenu, subMenuItem); + + if (hmenuTrackPopup) + { + return hmenuTrackPopup; + } + else + { + return 0; + } +} + +HWND DisassemblerGui::mouseInDialogItem(int dlgItem, POINT pt) +{ + RECT rc; + HWND hwnd = GetDlgItem(hWndDlg, dlgItem); + if (hwnd) + { + // Get the bounding rectangle of the client area. + GetClientRect(hwnd, &rc); + + // Convert the mouse position to client coordinates. + ScreenToClient(hwnd, &pt); + + // If the position is in the client area, display a + // shortcut menu. + if (PtInRect(&rc, pt)) + { + return hwnd; + } + else + { + return 0; + } + } + else + { + return 0; + } +} diff --git a/Scylla/DisassemblerGui.h b/Scylla/DisassemblerGui.h new file mode 100644 index 0000000..13dd93c --- /dev/null +++ b/Scylla/DisassemblerGui.h @@ -0,0 +1,31 @@ +#pragma once + +#include "MainGui.h" + +static const enum DisassemblerColumns { + COL_ADDRESS, + COL_INSTRUCTION_SIZE, + COL_OPCODES, + COL_INSTRUCTION +}; + +#define DISASSEMBLER_GUI_MEMORY_SIZE 0x100 + +class DisassemblerGui { +public: + static WCHAR tempBuffer[100]; + static HWND hWndDlg; + static HINSTANCE hInstance; + static DWORD_PTR startAddress; + + static INT_PTR initDialog(HINSTANCE hInstance, HWND hWndParent, DWORD_PTR address); + static void addColumnsToDisassembler( HWND list ); + static void displayDisassembly(); +private: + static LRESULT CALLBACK disassemblerDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static HWND mouseInDialogItem(int dlgItem, POINT pt); + static void OnContextMenu(int x, int y); + static HMENU getCorrectSubMenu(int menuItem, int subMenuItem); + static void getModuleListItem(int column, int iItem, WCHAR * buffer); + static void copyToClipboard(); +}; \ No newline at end of file diff --git a/Scylla/DllInjection.cpp b/Scylla/DllInjection.cpp new file mode 100644 index 0000000..bdd8d38 --- /dev/null +++ b/Scylla/DllInjection.cpp @@ -0,0 +1,246 @@ +#include "DllInjection.h" +#include "Logger.h" +#include + +#include "NativeWinApi.h" + +//#define DEBUG_COMMENTS + + HMODULE DllInjection::dllInjection(HANDLE hProcess, const WCHAR * filename) + { + LPVOID remoteMemory = 0; + SIZE_T memorySize = 0; + HANDLE hThread = 0; + HMODULE hModule = 0; + + memorySize = (wcslen(filename) + 1) * sizeof(WCHAR); + + if (memorySize < 7) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"dllInjection :: memorySize invalid\r\n"); +#endif + return 0; + } + + remoteMemory = VirtualAllocEx(hProcess, NULL, memorySize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + + if (remoteMemory == 0) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"dllInjection :: VirtualAllocEx failed 0x%X\r\n",GetLastError()); +#endif + return 0; + } + + if (WriteProcessMemory(hProcess, remoteMemory, filename, memorySize, &memorySize)) + { + hThread = startRemoteThread(hProcess,LoadLibraryW,remoteMemory); + + if (hThread) + { + WaitForSingleObject(hThread, INFINITE); + +#ifdef _WIN64 + + hModule = getModuleHandleByFilename(hProcess, filename); + +#else + //returns only 32 bit values -> design bug by microsoft + if (!GetExitCodeThread(hThread, (LPDWORD) &hModule)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"dllInjection :: GetExitCodeThread failed 0x%X\r\n",GetLastError()); +#endif + hModule = 0; + } +#endif + + CloseHandle(hThread); + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"dllInjection :: CreateRemoteThread failed 0x%X\r\n",GetLastError()); +#endif + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"dllInjection :: WriteProcessMemory failed 0x%X\r\n",GetLastError()); +#endif + } + + + VirtualFreeEx(hProcess, remoteMemory, 0, MEM_RELEASE); + + return hModule; + } + + bool DllInjection::unloadDllInProcess(HANDLE hProcess, HMODULE hModule) + { + HANDLE hThread = 0; + DWORD lpThreadId = 0; + BOOL freeLibraryRet = 0; + + + hThread = startRemoteThread(hProcess,FreeLibrary,hModule); + + if (hThread) + { + WaitForSingleObject(hThread, INFINITE); + + if (!GetExitCodeThread(hThread, (LPDWORD) &freeLibraryRet)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"unloadDllInProcess :: GetExitCodeThread failed 0x%X\r\n",GetLastError()); +#endif + freeLibraryRet = 0; + } + + CloseHandle(hThread); + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"unloadDllInProcess :: CreateRemoteThread failed 0x%X\r\n",GetLastError()); +#endif + } + + return freeLibraryRet != 0; + } + + HMODULE DllInjection::getModuleHandleByFilename( HANDLE hProcess, const WCHAR * filename ) + { + HMODULE * hMods = 0; + HMODULE hModResult = 0; + DWORD count = 0; + WCHAR target[MAX_PATH]; + DWORD cbNeeded = 0; + bool notEnough = true; + + count = 100; + hMods = new HMODULE[count]; + + do + { + if (!EnumProcessModules(hProcess, hMods, count * sizeof(HMODULE), &cbNeeded)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"DllInjection::getModuleHandle :: EnumProcessModules failed count %d\r\n",count); +#endif + delete [] hMods; + return 0; + } + + if ( (count * sizeof(HMODULE)) < cbNeeded ) + { + delete [] hMods; + count += 100; + hMods = new HMODULE[count]; + } + else + { + notEnough = false; + } + } while (notEnough); + + + for (DWORD i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ ) + { + if (GetModuleFileNameExW(hProcess, hMods[i], target, _countof(target))) + { + if (!_wcsicmp(target,filename)) + { + hModResult = hMods[i]; + break; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"DllInjection::getModuleHandle :: GetModuleFileNameExW failed 0x%X\r\n", GetLastError()); +#endif + } + } + + if (!hModResult) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"DllInjection::getModuleHandle :: Handle not found\r\n"); +#endif + } + + delete [] hMods; + + return hModResult; + } + + void DllInjection::specialThreadSettings( HANDLE hThread ) + { + if (hThread) + { + if (!SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"specialThreadSettings :: SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL) failed 0x%X\r\n",GetLastError()); +#endif + } + + if (NativeWinApi::NtSetInformationThread) + { + if (NativeWinApi::NtSetInformationThread(hThread, ThreadHideFromDebugger, 0, 0) != STATUS_SUCCESS) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"specialThreadSettings :: NtSetInformationThread ThreadHideFromDebugger failed\r\n"); +#endif + } + } + } + } + + HANDLE DllInjection::startRemoteThread(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter) + { + HANDLE hThread = 0; + + hThread = customCreateRemoteThread(hProcess, lpStartAddress, lpParameter); + + if (hThread) + { + specialThreadSettings(hThread); + ResumeThread(hThread); + } + + return hThread; + } + + HANDLE DllInjection::customCreateRemoteThread(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter) + { + DWORD lpThreadId = 0; + HANDLE hThread = 0; + NTSTATUS ntStatus = 0; + + if (NativeWinApi::NtCreateThreadEx) + { + #define THREAD_ALL_ACCESS_VISTA_7 (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF) + + //for windows vista/7 + ntStatus = NativeWinApi::NtCreateThreadEx(&hThread, THREAD_ALL_ACCESS_VISTA_7, 0, hProcess, (LPTHREAD_START_ROUTINE)lpStartAddress, (LPVOID)lpParameter, TRUE, 0, 0, 0, 0); + if (NT_SUCCESS(ntStatus)) + { + return hThread; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"customCreateRemoteThread :: NtCreateThreadEx failed 0x%X\r\n",NativeWinApi::RtlNtStatusToDosError(ntStatus)); +#endif + return 0; + } + } + else + { + return CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_START_ROUTINE)lpStartAddress,lpParameter,CREATE_SUSPENDED,&lpThreadId); + } + } diff --git a/Scylla/DllInjection.h b/Scylla/DllInjection.h new file mode 100644 index 0000000..fd84662 --- /dev/null +++ b/Scylla/DllInjection.h @@ -0,0 +1,18 @@ +#include +#include +#include +#include +#include + + +class DllInjection { +public: + HMODULE dllInjection(HANDLE hProcess, const WCHAR * filename); + bool unloadDllInProcess(HANDLE hProcess, HMODULE hModule); + HANDLE startRemoteThread(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter); + +private: + HANDLE customCreateRemoteThread(HANDLE hProcess, LPVOID lpStartAddress, LPVOID lpParameter); + void specialThreadSettings( HANDLE hThread ); + HMODULE getModuleHandleByFilename( HANDLE hProcess, const WCHAR * filename ); +}; \ No newline at end of file diff --git a/Scylla/DllInjectionPlugin.cpp b/Scylla/DllInjectionPlugin.cpp new file mode 100644 index 0000000..097a81a --- /dev/null +++ b/Scylla/DllInjectionPlugin.cpp @@ -0,0 +1,264 @@ +#include "DllInjectionPlugin.h" +#include "Logger.h" + +const WCHAR * DllInjectionPlugin::FILE_MAPPING_NAME = L"ScyllaPluginExchange"; + +HANDLE DllInjectionPlugin::hProcess = 0; + +//#define DEBUG_COMMENTS + +void DllInjectionPlugin::injectPlugin(Plugin & plugin, std::map & moduleList, DWORD_PTR imageBase, DWORD_PTR imageSize) +{ + PSCYLLA_EXCHANGE scyllaExchange = 0; + PUNRESOLVED_IMPORT unresImp = 0; + + BYTE * dataBuffer = 0; + DWORD_PTR numberOfUnresolvedImports = getNumberOfUnresolvedImports(moduleList); + + if (numberOfUnresolvedImports == 0) + { + Logger::printfDialog(L"No unresolved Imports"); + return; + } + + if (!createFileMapping((DWORD)(sizeof(SCYLLA_EXCHANGE) + sizeof(UNRESOLVED_IMPORT) + (sizeof(UNRESOLVED_IMPORT) * numberOfUnresolvedImports)))) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"injectPlugin :: createFileMapping %X failed\r\n",sizeof(SCYLLA_EXCHANGE) + sizeof(UNRESOLVED_IMPORT) + (sizeof(UNRESOLVED_IMPORT) * numberOfUnresolvedImports)); +#endif + return; + } + + scyllaExchange = (PSCYLLA_EXCHANGE)lpViewOfFile; + scyllaExchange->status = 0xFF; + scyllaExchange->imageBase = imageBase; + scyllaExchange->imageSize = imageSize; + scyllaExchange->numberOfUnresolvedImports = numberOfUnresolvedImports; + scyllaExchange->offsetUnresolvedImportsArray = sizeof(SCYLLA_EXCHANGE); + + unresImp = (PUNRESOLVED_IMPORT)((DWORD_PTR)lpViewOfFile + sizeof(SCYLLA_EXCHANGE)); + + addUnresolvedImports(unresImp, moduleList); + + UnmapViewOfFile(lpViewOfFile); + lpViewOfFile = 0; + + HMODULE hDll = dllInjection(hProcess, plugin.fullpath); + if (hDll) + { + Logger::printfDialog(L"Plugin injection was successful"); + if (!unloadDllInProcess(hProcess,hDll)) + { + Logger::printfDialog(L"Plugin unloading failed"); + } + lpViewOfFile = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0); + + if (lpViewOfFile) + { + scyllaExchange = (PSCYLLA_EXCHANGE)lpViewOfFile; + handlePluginResults(scyllaExchange, moduleList); + } + + } + else + { + Logger::printfDialog(L"Plugin injection failed"); + } + + closeAllHandles(); +} + + + +bool DllInjectionPlugin::createFileMapping(DWORD mappingSize) +{ + hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE|SEC_COMMIT, 0, mappingSize, FILE_MAPPING_NAME); + + if (hMapFile == NULL) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("createFileMapping :: CreateFileMapping failed 0x%X\r\n",GetLastError()); +#endif + return false; + } + + lpViewOfFile = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0); + + if (lpViewOfFile == NULL) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("createFileMapping :: MapViewOfFile failed 0x%X\r\n",GetLastError()); +#endif + CloseHandle(hMapFile); + hMapFile = 0; + return false; + } + else + { + return true; + } +} + +void DllInjectionPlugin::closeAllHandles() +{ + if (lpViewOfFile) + { + UnmapViewOfFile(lpViewOfFile); + lpViewOfFile = 0; + } + if (hMapFile) + { + CloseHandle(hMapFile); + hMapFile = 0; + } +} + +DWORD_PTR DllInjectionPlugin::getNumberOfUnresolvedImports( std::map & moduleList ) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk = 0; + ImportThunk * importThunk = 0; + DWORD_PTR dwNumber = 0; + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->valid == false) + { + dwNumber++; + } + + iterator2++; + } + + iterator1++; + } + + return dwNumber; +} + +void DllInjectionPlugin::addUnresolvedImports( PUNRESOLVED_IMPORT firstUnresImp, std::map & moduleList ) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk = 0; + ImportThunk * importThunk = 0; + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->valid == false) + { + firstUnresImp->InvalidApiAddress = importThunk->apiAddressVA; + firstUnresImp->ImportTableAddressPointer = importThunk->va; + firstUnresImp++; + } + + iterator2++; + } + + iterator1++; + } + + firstUnresImp->InvalidApiAddress = 0; + firstUnresImp->ImportTableAddressPointer = 0; +} + +void DllInjectionPlugin::handlePluginResults( PSCYLLA_EXCHANGE scyllaExchange, std::map & moduleList ) +{ + PUNRESOLVED_IMPORT unresImp = (PUNRESOLVED_IMPORT)((DWORD_PTR)scyllaExchange + scyllaExchange->offsetUnresolvedImportsArray);; + + switch (scyllaExchange->status) + { + case SCYLLA_STATUS_SUCCESS: + Logger::printfDialog(L"Plugin was successful"); + updateImportsWithPluginResult(unresImp, moduleList); + break; + case SCYLLA_STATUS_UNKNOWN_ERROR: + Logger::printfDialog(L"Plugin reported Unknown Error"); + break; + case SCYLLA_STATUS_UNSUPPORTED_PROTECTION: + Logger::printfDialog(L"Plugin detected unknown protection"); + updateImportsWithPluginResult(unresImp, moduleList); + break; + case SCYLLA_STATUS_IMPORT_RESOLVING_FAILED: + Logger::printfDialog(L"Plugin import resolving failed"); + updateImportsWithPluginResult(unresImp, moduleList); + break; + case SCYLLA_STATUS_MAPPING_FAILED: + Logger::printfDialog(L"Plugin file mapping failed"); + break; + default: + Logger::printfDialog(L"Plugin failed without reason"); + } +} + +void DllInjectionPlugin::updateImportsWithPluginResult( PUNRESOLVED_IMPORT firstUnresImp, std::map & moduleList ) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk = 0; + ImportThunk * importThunk = 0; + ApiInfo * apiInfo = 0; + bool isSuspect = 0; + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->valid == false) + { + if (apiReader->isApiAddressValid(firstUnresImp->InvalidApiAddress)) + { + apiInfo = apiReader->getApiByVirtualAddress(firstUnresImp->InvalidApiAddress,&isSuspect); + + importThunk->suspect = isSuspect; + importThunk->valid = true; + importThunk->apiAddressVA = firstUnresImp->InvalidApiAddress; + importThunk->hint = (WORD)apiInfo->hint; + importThunk->ordinal = apiInfo->ordinal; + strcpy_s(importThunk->name, MAX_PATH,apiInfo->name); + wcscpy_s(importThunk->moduleName, MAX_PATH, apiInfo->module->getFilename()); + + if (moduleThunk->moduleName[0] == TEXT('?')) + { + wcscpy_s(moduleThunk->moduleName, MAX_PATH, apiInfo->module->getFilename()); + } + } + + firstUnresImp++; + } + + iterator2++; + } + + iterator1++; + } +} diff --git a/Scylla/DllInjectionPlugin.h b/Scylla/DllInjectionPlugin.h new file mode 100644 index 0000000..9f8fb14 --- /dev/null +++ b/Scylla/DllInjectionPlugin.h @@ -0,0 +1,61 @@ +#include "DllInjection.h" +#include "PluginLoader.h" +#include "Thunks.h" +#include "ApiReader.h" + +#define SCYLLA_STATUS_SUCCESS 0 +#define SCYLLA_STATUS_UNKNOWN_ERROR 1 +#define SCYLLA_STATUS_UNSUPPORTED_PROTECTION 2 +#define SCYLLA_STATUS_IMPORT_RESOLVING_FAILED 3 +#define SCYLLA_STATUS_MAPPING_FAILED 0xFF + +/* Important note: + * + * If you write a plugin for the x86 (32-Bit) edition: DWORD_PTR address has 32 bit (4 byte) + * If you write a plugin for the x64 (64-Bit) edition: DWORD_PTR address has 64 bit (8 byte) + */ +typedef struct _UNRESOLVED_IMPORT { // Scylla Plugin exchange format + DWORD_PTR ImportTableAddressPointer; //in VA, address in IAT which points to an invalid api address + DWORD_PTR InvalidApiAddress; //in VA, invalid api address that needs to be resolved +} UNRESOLVED_IMPORT, *PUNRESOLVED_IMPORT; + +typedef struct _SCYLLA_EXCHANGE { + BYTE status; //return a status, default 0xFF + DWORD_PTR imageBase; //image base + DWORD_PTR imageSize; //size of the image + DWORD_PTR numberOfUnresolvedImports; //number of unresolved imports in this structure + BYTE offsetUnresolvedImportsArray; +} SCYLLA_EXCHANGE, *PSCYLLA_EXCHANGE; + +class DllInjectionPlugin : public DllInjection { + +public: + static const WCHAR * FILE_MAPPING_NAME; + static HANDLE hProcess; + + ApiReader * apiReader; + HANDLE hMapFile; + LPVOID lpViewOfFile; + + DllInjectionPlugin() + { + hMapFile = 0; + lpViewOfFile = 0; + apiReader = 0; + } + + ~DllInjectionPlugin() + { + closeAllHandles(); + } + + void injectPlugin(Plugin & plugin, std::map & moduleList, DWORD_PTR imageBase, DWORD_PTR imageSize); + +private: + bool createFileMapping(DWORD mappingSize); + void closeAllHandles(); + DWORD_PTR getNumberOfUnresolvedImports( std::map & moduleList ); + void addUnresolvedImports( PUNRESOLVED_IMPORT firstUnresImp, std::map & moduleList ); + void handlePluginResults( PSCYLLA_EXCHANGE scyllaExchange, std::map & moduleList ); + void updateImportsWithPluginResult( PUNRESOLVED_IMPORT firstUnresImp, std::map & moduleList ); +}; \ No newline at end of file diff --git a/Scylla/IATSearch.cpp b/Scylla/IATSearch.cpp new file mode 100644 index 0000000..a1f978b --- /dev/null +++ b/Scylla/IATSearch.cpp @@ -0,0 +1,342 @@ +#include "IATSearch.h" +#include "Logger.h" +#include "definitions.h" + +//#define DEBUG_COMMENTS + + +bool IATSearch::searchImportAddressTableInProcess(DWORD_PTR startAddress, DWORD_PTR* addressIAT, DWORD* sizeIAT) +{ + DWORD_PTR addressInIAT = 0; + + addressInIAT = findAPIAddressInIAT(startAddress); + + if(!addressInIAT) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("searchImportAddressTableInProcess :: addressInIAT not found, startAddress ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),startAddress); +#endif + return false; + } + else + { + return findIATStartAndSize(addressInIAT, addressIAT,sizeIAT); + } +} + +DWORD_PTR IATSearch::findAPIAddressInIAT(DWORD_PTR startAddress) +{ + static const int MEMORY_READ_SIZE = 200; + BYTE *dataBuffer = new BYTE[MEMORY_READ_SIZE]; + DWORD_PTR iatPointer = 0; + int counter = 0; + + // to detect stolen api + memoryAddress = 0; + memorySize = 0; + + do + { + counter++; + + if (!readMemoryFromProcess(startAddress,MEMORY_READ_SIZE,dataBuffer)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("findAPIAddressInIAT :: error reading memory ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"), startAddress); +#endif + return 0; + } + + if (decomposeMemory(dataBuffer,MEMORY_READ_SIZE,startAddress)) + { + iatPointer = findIATPointer(); + if (iatPointer) + { + if (isIATPointerValid(iatPointer)) + { + delete[] dataBuffer; + return iatPointer; + } + } + } + + startAddress = findNextFunctionAddress(); + //printf("startAddress %08X\n",startAddress); + } while (startAddress != 0 && counter != 8); + + + delete[] dataBuffer; + return 0; +} + +DWORD_PTR IATSearch::findNextFunctionAddress() +{ +#ifdef DEBUG_COMMENTS + _DecodedInst inst; +#endif + + for (unsigned int i = 0; i < decomposerInstructionsCount; i++) + { + + if (decomposerResult[i].flags != FLAG_NOT_DECODABLE) + { + if (META_GET_FC(decomposerResult[i].meta) == FC_CALL || META_GET_FC(decomposerResult[i].meta) == FC_UNC_BRANCH) + { + if (decomposerResult[i].size >= 5) + { + if (decomposerResult[i].ops[0].type == O_PC) + { +#ifdef DEBUG_COMMENTS + distorm_format(&decomposerCi, &decomposerResult[i], &inst); + Logger::debugLog(TEXT("%S %S %d %d - target address: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"), inst.mnemonic.p, inst.operands.p,decomposerResult[i].ops[0].type,decomposerResult[i].size, INSTRUCTION_GET_TARGET(&decomposerResult[i])); +#endif + return (DWORD_PTR)INSTRUCTION_GET_TARGET(&decomposerResult[i]); + } + } + } + } + } + + return 0; +} + +DWORD_PTR IATSearch::findIATPointer() +{ +#ifdef DEBUG_COMMENTS + _DecodedInst inst; +#endif + + for (unsigned int i = 0; i < decomposerInstructionsCount; i++) + { + + if (decomposerResult[i].flags != FLAG_NOT_DECODABLE) + { + if (META_GET_FC(decomposerResult[i].meta) == FC_CALL || META_GET_FC(decomposerResult[i].meta) == FC_UNC_BRANCH) + { + if (decomposerResult[i].size >= 5) + { +#ifdef _WIN64 + if (decomposerResult[i].flags & FLAG_RIP_RELATIVE) + { +#ifdef DEBUG_COMMENTS + distorm_format(&decomposerCi, &decomposerResult[i], &inst); + Logger::debugLog(TEXT("%S %S %d %d - target address: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"), inst.mnemonic.p, inst.operands.p,decomposerResult[i].ops[0].type,decomposerResult[i].size,INSTRUCTION_GET_RIP_TARGET(&decomposerResult[i])); +#endif + return INSTRUCTION_GET_RIP_TARGET(&decomposerResult[i]); + } +#else + if (decomposerResult[i].ops[0].type == O_DISP) + { + //jmp dword ptr || call dword ptr +#ifdef DEBUG_COMMENTS + distorm_format(&decomposerCi, &decomposerResult[i], &inst); + Logger::debugLog(TEXT("%S %S %d %d - target address: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"), inst.mnemonic.p, inst.operands.p,decomposerResult[i].ops[0].type,decomposerResult[i].size,decomposerResult[i].disp); +#endif + return (DWORD_PTR)decomposerResult[i].disp; + } +#endif + } + } + } + } + + return 0; +} + +DWORD_PTR IATSearch::findAddressFromWORDString(char * stringBuffer) +{ + char * pAddress = 0; + char * pTemp = 0; + DWORD_PTR address = 0; + + //string split it e.g. DWORD [0x40f0fc], QWORD [RIP+0x40f0] + pAddress = strchr(stringBuffer, 'x'); + + if (pAddress) + { + pAddress++; + + pTemp = strchr(pAddress, ']'); + *pTemp = 0x00; + + address = strtoul(pAddress, 0, 16); + + //printf("findAddressFromWORDString :: %08X\n",address); + + if (address == ULONG_MAX) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("findAddressFromDWORDString :: strtoul ULONG_MAX\r\n"); +#endif + return 0; + } + else + { + return address; + } + } + else + { + return 0; + } +} + +DWORD_PTR IATSearch::findAddressFromNormalCALLString(char * stringBuffer) +{ + char * pAddress = 0; + DWORD_PTR address = 0; + + //e.g. CALL 0x7238 + pAddress = strchr(stringBuffer, 'x'); + + if (pAddress) + { + pAddress++; + + address = strtoul(pAddress, 0, 16); + + //printf("findAddressFromNormalCALLString :: %08X\n",address); + + if (address == ULONG_MAX) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("findAddressFromNormalCALLString :: strtoul ULONG_MAX\r\n"); +#endif + return 0; + } + else + { + return address; + } + } + else + { + return 0; + } +} + +bool IATSearch::isIATPointerValid(DWORD_PTR iatPointer) +{ + DWORD_PTR apiAddress = 0; + + if (!readMemoryFromProcess(iatPointer,sizeof(DWORD_PTR),&apiAddress)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("isIATPointerValid :: error reading memory\r\n"); +#endif + return false; + } + + //printf("Win api ? %08X\n",apiAddress); + + if (isApiAddressValid(apiAddress) != 0) + { + return true; + } + else + { + //maybe redirected import? + //if the address is 2 times inside a memory region it is possible a redirected api + if (apiAddress > memoryAddress && apiAddress < (memoryAddress+memorySize)) + { + return true; + } + else + { + getMemoryRegionFromAddress(apiAddress, &memoryAddress, &memorySize); + return false; + } + + } +} + +bool IATSearch::findIATStartAndSize(DWORD_PTR address, DWORD_PTR * addressIAT, DWORD * sizeIAT) +{ + MEMORY_BASIC_INFORMATION memBasic; + BYTE *dataBuffer = 0; + + if (VirtualQueryEx(hProcess,(LPCVOID)address,&memBasic,sizeof(MEMORY_BASIC_INFORMATION)) != sizeof(MEMORY_BASIC_INFORMATION)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("findIATStartAddress :: VirtualQueryEx error %u\r\n",GetLastError()); +#endif + return false; + } + + dataBuffer = new BYTE[memBasic.RegionSize]; + + if (!readMemoryFromProcess((DWORD_PTR)memBasic.BaseAddress,memBasic.RegionSize,dataBuffer)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("findIATStartAddress :: error reading memory\r\n"); +#endif + return false; + } + + //printf("address %X memBasic.BaseAddress %X memBasic.RegionSize %X\n",address,memBasic.BaseAddress,memBasic.RegionSize); + + *addressIAT = findIATStartAddress((DWORD_PTR)memBasic.BaseAddress, address, dataBuffer); + + *sizeIAT = findIATSize((DWORD_PTR)memBasic.BaseAddress, *addressIAT,dataBuffer,(DWORD)memBasic.RegionSize); + + return true; +} + +DWORD_PTR IATSearch::findIATStartAddress(DWORD_PTR baseAddress, DWORD_PTR startAddress, BYTE * dataBuffer) +{ + DWORD_PTR *pIATAddress = 0; + + pIATAddress = (DWORD_PTR *)((startAddress - baseAddress) + (DWORD_PTR)dataBuffer); + + while((DWORD_PTR)pIATAddress != (DWORD_PTR)dataBuffer) + { + if (*pIATAddress == 0 && *(pIATAddress - 1) == 0) + { + if (((DWORD_PTR)(pIATAddress - 2) >= (DWORD_PTR)dataBuffer) && isApiAddressValid(*(pIATAddress - 2))) + { + + } + else + { + return (((DWORD_PTR)pIATAddress - (DWORD_PTR)dataBuffer) + baseAddress); + } + } + else if (*pIATAddress < 0xFFFF && *(pIATAddress - 1) < 0xFFFF) + { + //IAT end + return (((DWORD_PTR)pIATAddress - (DWORD_PTR)dataBuffer) + baseAddress); + } + + pIATAddress--; + } + + return baseAddress; +} + +DWORD IATSearch::findIATSize(DWORD_PTR baseAddress, DWORD_PTR iatAddress, BYTE * dataBuffer, DWORD bufferSize) +{ + DWORD_PTR *pIATAddress = 0; + + pIATAddress = (DWORD_PTR *)((iatAddress - baseAddress) + (DWORD_PTR)dataBuffer); + +#ifdef DEBUG_COMMENTS + Logger::debugLog("findIATSize :: baseAddress %X iatAddress %X dataBuffer %X\r\n",baseAddress,iatAddress, dataBuffer); +#endif + + while((DWORD_PTR)pIATAddress < ((DWORD_PTR)dataBuffer + bufferSize - 1)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("findIATSize :: %X %X %X\r\n",pIATAddress,*pIATAddress, *(pIATAddress + 1)); +#endif + if (*pIATAddress < 0xFFFF && *(pIATAddress + 1) < 0xFFFF) //normal is 0 + { + + //IAT end + return (DWORD)((DWORD_PTR)pIATAddress - (DWORD_PTR)dataBuffer); + } + + pIATAddress++; + } + + return bufferSize; +} \ No newline at end of file diff --git a/Scylla/IATSearch.h b/Scylla/IATSearch.h new file mode 100644 index 0000000..1def25f --- /dev/null +++ b/Scylla/IATSearch.h @@ -0,0 +1,27 @@ +#pragma once + +#include "ApiReader.h" + + +class IATSearch : protected ApiReader { +public: + + DWORD_PTR memoryAddress; + SIZE_T memorySize; + + bool searchImportAddressTableInProcess(DWORD_PTR startAddress, DWORD_PTR* addressIAT, DWORD* sizeIAT); + +private: + + DWORD_PTR findAPIAddressInIAT(DWORD_PTR startAddress); + DWORD_PTR findNextFunctionAddress(); + DWORD_PTR findIATPointer(); + DWORD_PTR findAddressFromWORDString(char * stringBuffer); + DWORD_PTR findAddressFromNormalCALLString(char * stringBuffer); + bool isIATPointerValid(DWORD_PTR iatPointer); + + bool findIATStartAndSize(DWORD_PTR address, DWORD_PTR * addressIAT, DWORD * sizeIAT); + + DWORD_PTR findIATStartAddress( DWORD_PTR baseAddress, DWORD_PTR startAddress, BYTE * dataBuffer ); + DWORD findIATSize( DWORD_PTR baseAddress, DWORD_PTR iatAddress, BYTE * dataBuffer, DWORD bufferSize ); +}; \ No newline at end of file diff --git a/Scylla/ImportRebuild.cpp b/Scylla/ImportRebuild.cpp new file mode 100644 index 0000000..2af8670 --- /dev/null +++ b/Scylla/ImportRebuild.cpp @@ -0,0 +1,578 @@ +#include "ImportRebuild.h" + +#include "Logger.h" +#include "ConfigurationHolder.h" +//#define DEBUG_COMMENTS + + +bool ImportRebuild::splitTargetFile() +{ + PIMAGE_SECTION_HEADER pSecHeader = 0; + WORD i = 0; + BYTE * data = 0; + DWORD alignment = 0; + DWORD dwSize = 0; + + pDosHeader = new IMAGE_DOS_HEADER; + CopyMemory(pDosHeader, imageData, sizeof(IMAGE_DOS_HEADER)); + + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + { + return false; + } + + pNTHeader = new IMAGE_NT_HEADERS; + CopyMemory(pNTHeader, (PVOID)((DWORD_PTR)imageData + pDosHeader->e_lfanew), sizeof(IMAGE_NT_HEADERS)); + + if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) + { + return false; + } + + if (pDosHeader->e_lfanew > sizeof(IMAGE_DOS_HEADER)) + { + dwSize = pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER); + pDosStub = new BYTE[dwSize]; + CopyMemory(pDosStub, (PVOID)((DWORD_PTR)imageData + sizeof(IMAGE_DOS_HEADER)), dwSize); + } + else + { + pDosStub = 0; + } + + pSecHeader = IMAGE_FIRST_SECTION((PIMAGE_NT_HEADERS)((DWORD_PTR)imageData + pDosHeader->e_lfanew)); + + for (i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++) + { + dwSize = pSecHeader->SizeOfRawData; + + if (dwSize > 300000000) + { + dwSize = 300000000; + } + + alignment = alignValue(dwSize, pNTHeader->OptionalHeader.FileAlignment); + data = new BYTE[alignment]; + + ZeroMemory(data, alignment); + CopyMemory(data, (PVOID)((DWORD_PTR)imageData + pSecHeader->PointerToRawData), dwSize); + + vecSectionData.push_back(data); + vecSectionHeaders.push_back(*pSecHeader); + + pSecHeader++; + } + + delete [] imageData; + imageData = 0; + + return true; +} + +bool ImportRebuild::alignSectionHeaders() +{ + for (WORD i = 0; i < vecSectionHeaders.size(); i++) + { + vecSectionHeaders[i].VirtualAddress = alignValue(vecSectionHeaders[i].VirtualAddress, pNTHeader->OptionalHeader.SectionAlignment); + vecSectionHeaders[i].Misc.VirtualSize = alignValue(vecSectionHeaders[i].Misc.VirtualSize, pNTHeader->OptionalHeader.SectionAlignment); + + vecSectionHeaders[i].PointerToRawData = alignValue(vecSectionHeaders[i].PointerToRawData, pNTHeader->OptionalHeader.FileAlignment); + vecSectionHeaders[i].SizeOfRawData = alignValue(vecSectionHeaders[i].SizeOfRawData, pNTHeader->OptionalHeader.FileAlignment); + } + + return true; +} + +bool ImportRebuild::saveNewFile(const WCHAR * filepath) +{ + DWORD fileOffset = 0; + DWORD dwWriteSize = 0; + size_t i = 0; + + if (vecSectionHeaders.size() != vecSectionData.size()) + { + return false; + } + + HANDLE hFile = CreateFile(filepath, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 0,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + + if(hFile == INVALID_HANDLE_VALUE) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("saveNewFile :: INVALID_HANDLE_VALUE %u\r\n",GetLastError()); +#endif + + return false; + } + + //alignSectionHeaders(); + updatePeHeader(); + + fileOffset = 0; + dwWriteSize = sizeof(IMAGE_DOS_HEADER); + ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, pDosHeader); + + fileOffset += dwWriteSize; + dwWriteSize = pDosHeader->e_lfanew - sizeof(IMAGE_DOS_HEADER); + ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, pDosStub); + + fileOffset += dwWriteSize; + dwWriteSize = sizeof(IMAGE_NT_HEADERS); + ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, pNTHeader); + + fileOffset += dwWriteSize; + dwWriteSize = sizeof(IMAGE_SECTION_HEADER); + + for (i = 0; i < vecSectionHeaders.size(); i++) + { + if (!ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, &vecSectionHeaders[i])) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("saveNewFile :: writeMemoryToFile failed offset %X size %X\r\n"),fileOffset,dwWriteSize); +#endif + CloseHandle(hFile); + return false; + } + fileOffset += dwWriteSize; + } + + for (i = 0; i < vecSectionHeaders.size(); i++) + { + dwWriteSize = vecSectionHeaders[i].PointerToRawData - fileOffset; + + if (dwWriteSize) + { + if (!writeZeroMemoryToFile(hFile, fileOffset, dwWriteSize)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("saveNewFile :: writeZeroMemoryToFile failed offset %X size %X\r\n"),fileOffset,dwWriteSize); +#endif + CloseHandle(hFile); + return false; + } + fileOffset += dwWriteSize; + } + + dwWriteSize = vecSectionHeaders[i].SizeOfRawData; + + ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, vecSectionData[i]); + fileOffset += dwWriteSize; + } + + + CloseHandle(hFile); + + return true; +} + +bool ImportRebuild::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); + } + else + { + retValue = false; + } + + return retValue; +} + +void ImportRebuild::clearAllData() +{ + if (pDosStub) + { + delete [] pDosStub; + pDosStub = 0; + } + + if (imageData) + { + delete [] imageData; + imageData = 0; + } + + delete pDosHeader; + pDosHeader = 0; + + delete pNTHeader; + pNTHeader = 0; + + vecSectionHeaders.clear(); + + for (size_t i = 0; i < vecSectionData.size(); i++) + { + delete [] vecSectionData[i]; + } + + vecSectionData.clear(); +} + +bool ImportRebuild::addNewSection(char * sectionName, DWORD sectionSize, BYTE * sectionData) +{ + BYTE * newBuffer = 0; + IMAGE_SECTION_HEADER pNewSection = {0}; + size_t lastSectionIndex = vecSectionHeaders.size() - 1; + size_t nameLength = strlen(sectionName); + + if (nameLength > IMAGE_SIZEOF_SHORT_NAME) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("addNewSection :: sectionname is too long %d\r\n"),nameLength); +#endif + return false; + } + + memcpy_s(pNewSection.Name, IMAGE_SIZEOF_SHORT_NAME, sectionName, nameLength); + + pNewSection.SizeOfRawData = alignValue(sectionSize, pNTHeader->OptionalHeader.FileAlignment); + pNewSection.Misc.VirtualSize = alignValue(sectionSize, pNTHeader->OptionalHeader.SectionAlignment); + + pNewSection.PointerToRawData = alignValue(vecSectionHeaders[lastSectionIndex].PointerToRawData + vecSectionHeaders[lastSectionIndex].SizeOfRawData, pNTHeader->OptionalHeader.FileAlignment); + pNewSection.VirtualAddress = alignValue(vecSectionHeaders[lastSectionIndex].VirtualAddress + vecSectionHeaders[lastSectionIndex].Misc.VirtualSize, pNTHeader->OptionalHeader.SectionAlignment); + + pNewSection.Characteristics = IMAGE_SCN_MEM_EXECUTE|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_CODE|IMAGE_SCN_CNT_INITIALIZED_DATA; + + vecSectionHeaders.push_back(pNewSection); + + if ( (sectionSize != pNewSection.SizeOfRawData) || (sectionData == 0) ) + { + newBuffer = new BYTE[pNewSection.SizeOfRawData]; + ZeroMemory(newBuffer, pNewSection.SizeOfRawData); + + if (sectionData) + { + CopyMemory(newBuffer, sectionData, sectionSize); + } + + } + else + { + newBuffer = sectionData; + } + + vecSectionData.push_back(newBuffer); + + return true; +} + +bool ImportRebuild::loadTargetFile(const WCHAR * filepath) +{ + HANDLE hTargetFile = INVALID_HANDLE_VALUE; + DWORD fileSize = 0; + bool retValue = false; + + hTargetFile = CreateFile(filepath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if(hTargetFile == INVALID_HANDLE_VALUE) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("loadTargetFile :: INVALID_HANDLE_VALUE %u\r\n",GetLastError()); +#endif + + return false; + } + + fileSize = (DWORD)ProcessAccessHelp::getFileSize(hTargetFile); + + if (!fileSize) + { + CloseHandle(hTargetFile); + hTargetFile = 0; + return false; + } + + imageData = new BYTE[fileSize]; + + if (!imageData) + { + retValue = false; + } + else + { + retValue = ProcessAccessHelp::readMemoryFromFile(hTargetFile, 0, fileSize, imageData); + } + + CloseHandle(hTargetFile); + hTargetFile = 0; + + return retValue; +} + +DWORD ImportRebuild::alignValue(DWORD badValue, DWORD alignTo) +{ + return (((badValue + alignTo - 1) / alignTo) * alignTo); +} + +DWORD ImportRebuild::convertRVAToOffsetVector(DWORD dwRVA) +{ + for (size_t i = 0; i < vecSectionHeaders.size(); i++) + { + if ((vecSectionHeaders[i].VirtualAddress <= dwRVA) && ((vecSectionHeaders[i].VirtualAddress + vecSectionHeaders[i].Misc.VirtualSize) > dwRVA)) + { + return ((dwRVA - vecSectionHeaders[i].VirtualAddress) + vecSectionHeaders[i].PointerToRawData); + } + } + + return 0; +} + +DWORD ImportRebuild::convertRVAToOffset(DWORD dwRVA) +{ + PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNTHeader); + + for (WORD i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++) + { + if ((pSectionHeader->VirtualAddress <= dwRVA) && ((pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize) > dwRVA)) + { + return ((dwRVA - pSectionHeader->VirtualAddress) + pSectionHeader->PointerToRawData); + } + pSectionHeader++; + } + + return 0; +} + +DWORD_PTR ImportRebuild::convertOffsetToRVAVector(DWORD dwOffset) +{ + for (size_t i = 0; i < vecSectionHeaders.size(); i++) + { + if ((vecSectionHeaders[i].PointerToRawData <= dwOffset) && ((vecSectionHeaders[i].PointerToRawData + vecSectionHeaders[i].SizeOfRawData) > dwOffset)) + { + return ((dwOffset - vecSectionHeaders[i].PointerToRawData) + vecSectionHeaders[i].VirtualAddress); + } + } + + return 0; +} + +DWORD ImportRebuild::convertOffsetToRVA(DWORD dwOffset) +{ + PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNTHeader); + + for (WORD i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++) + { + if ((pSectionHeader->PointerToRawData <= dwOffset) && ((pSectionHeader->PointerToRawData + pSectionHeader->SizeOfRawData) > dwOffset)) + { + return ((dwOffset - pSectionHeader->PointerToRawData) + pSectionHeader->VirtualAddress); + } + pSectionHeader++; + } + + return 0; +} + +void ImportRebuild::updatePeHeader() +{ + size_t lastSectionIndex = vecSectionHeaders.size() - 1; + + pNTHeader->FileHeader.NumberOfSections = (WORD)(lastSectionIndex + 1); + pNTHeader->OptionalHeader.SizeOfImage = vecSectionHeaders[lastSectionIndex].VirtualAddress + vecSectionHeaders[lastSectionIndex].Misc.VirtualSize; + + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; + + if (pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) + { + for (size_t i = 0; i < vecSectionHeaders.size(); i++) + { + if ((vecSectionHeaders[i].VirtualAddress <= pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) && ((vecSectionHeaders[i].VirtualAddress + vecSectionHeaders[i].Misc.VirtualSize) > pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress)) + { + //section must be read and writeable + vecSectionHeaders[i].Characteristics |= IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; + } + } + + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0; + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0; + } + + + pNTHeader->OptionalHeader.NumberOfRvaAndSizes = 0x10; + + pNTHeader->OptionalHeader.SizeOfHeaders = alignValue(pDosHeader->e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + pNTHeader->FileHeader.SizeOfOptionalHeader + (pNTHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)), pNTHeader->OptionalHeader.FileAlignment); + +} + + + +bool ImportRebuild::buildNewImportTable(std::map & moduleList) +{ + size_t lastSectionIndex = 0; + + createNewImportSection(moduleList); + + lastSectionIndex = vecSectionHeaders.size() - 1; + + DWORD dwSize = fillImportSection(moduleList, lastSectionIndex); + + setFlagToIATSection((*moduleList.begin()).second.firstThunk); + + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = vecSectionHeaders[lastSectionIndex].VirtualAddress; + pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (DWORD)(moduleList.size() * sizeof(IMAGE_IMPORT_DESCRIPTOR)); + return true; +} + +bool ImportRebuild::createNewImportSection(std::map & moduleList) +{ + char sectionName[9] = {0}; + size_t i = 0; + + DWORD sectionSize = calculateMinSize(moduleList); + + if (wcslen(ConfigurationHolder::getConfigObject(IAT_SECTION_NAME)->valueString) > IMAGE_SIZEOF_SHORT_NAME) + { + strcpy_s(sectionName, sizeof(sectionName), ".SCY"); + } + else + { + wcstombs_s(&i, sectionName, sizeof(sectionName), ConfigurationHolder::getConfigObject(IAT_SECTION_NAME)->valueString, _TRUNCATE); + } + + + return addNewSection(sectionName,sectionSize,0); +} + +DWORD ImportRebuild::calculateMinSize(std::map & moduleList) +{ + DWORD dwSize = 0; + std::map::iterator mapIt; + std::map::iterator mapIt2; + + dwSize = (DWORD)((moduleList.size() + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR)); //last is zero'ed + + for ( mapIt = moduleList.begin() ; mapIt != moduleList.end(); mapIt++ ) + { + dwSize += (DWORD)((*mapIt).second.thunkList.size() + sizeof(IMAGE_IMPORT_BY_NAME)); + dwSize += (DWORD)(wcslen((*mapIt).second.moduleName) + 1); + + for ( mapIt2 = (*mapIt).second.thunkList.begin() ; mapIt2 != (*mapIt).second.thunkList.end(); mapIt2++ ) + { + dwSize += (DWORD)(strlen((*mapIt2).second.name) + 1); + } + } + + return dwSize; +} + +BYTE * ImportRebuild::getMemoryPointerFromRVA(DWORD_PTR dwRVA) +{ + DWORD_PTR offset = convertRVAToOffsetVector((DWORD)dwRVA); + + for (size_t i = 0; i < vecSectionHeaders.size(); i++) + { + if ((vecSectionHeaders[i].PointerToRawData <= offset) && ((vecSectionHeaders[i].PointerToRawData + vecSectionHeaders[i].SizeOfRawData) > offset)) + { + return (BYTE *)((DWORD_PTR)vecSectionData[i] + (offset - vecSectionHeaders[i].PointerToRawData)); + } + } + + return 0; +} + +DWORD ImportRebuild::fillImportSection( std::map & moduleList, size_t importSectionIndex ) +{ + std::map::iterator mapIt; + std::map::iterator mapIt2; + PIMAGE_IMPORT_DESCRIPTOR pImportDesc = 0; + PIMAGE_IMPORT_BY_NAME pImportByName = 0; + PIMAGE_THUNK_DATA pThunk = 0; + + char dllName[MAX_PATH]; + size_t stringLength = 0; + + BYTE * data = vecSectionData[importSectionIndex]; + DWORD offset = 0; + + pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)(data); + + //skip the IMAGE_IMPORT_DESCRIPTOR + offset += (DWORD)((moduleList.size() + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR)); + + for ( mapIt = moduleList.begin() ; mapIt != moduleList.end(); mapIt++ ) + { + wcstombs_s(&stringLength, dllName, (size_t)MAX_PATH, (*mapIt).second.moduleName, (size_t)MAX_PATH); + + memcpy((data + offset), dllName, stringLength); //copy module name to section + + pImportDesc->FirstThunk = (DWORD)(*mapIt).second.firstThunk; + pImportDesc->Name = (DWORD)convertOffsetToRVAVector(vecSectionHeaders[importSectionIndex].PointerToRawData + offset); + +#ifdef DEBUG_COMMENTS + Logger::debugLog("fillImportSection :: importDesc.Name %X\r\n", pImportDesc->Name); +#endif + + offset += (DWORD)stringLength; //stringLength has null termination char + + pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)data + offset); + + pThunk = (PIMAGE_THUNK_DATA)(getMemoryPointerFromRVA((*mapIt).second.firstThunk)); + + for ( mapIt2 = (*mapIt).second.thunkList.begin() ; mapIt2 != (*mapIt).second.thunkList.end(); mapIt2++ ) + { + pImportByName->Hint = (*mapIt2).second.hint; + + stringLength = strlen((*mapIt2).second.name) + 1; + memcpy(pImportByName->Name, (*mapIt2).second.name, stringLength); + + //pThunk = (PIMAGE_THUNK_DATA)(getMemoryPointerFromRVA((*mapIt2).second.rva)); + + pThunk->u1.AddressOfData = convertOffsetToRVAVector(vecSectionHeaders[importSectionIndex].PointerToRawData + offset); + +#ifdef DEBUG_COMMENTS + Logger::debugLog("pThunk->u1.AddressOfData %X %X %X\n",pThunk->u1.AddressOfData, pThunk, vecSectionHeaders[importSectionIndex].PointerToRawData + offset); +#endif + offset += (DWORD)(sizeof(WORD) + stringLength); + pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)pImportByName + (sizeof(WORD) + stringLength)); + pThunk++; + } + + pThunk->u1.AddressOfData = 0; + + pImportDesc++; + } + + return offset; +} + +bool ImportRebuild::rebuildImportTable(const WCHAR * targetFilePath, const WCHAR * newFilePath, std::map & moduleList) +{ + bool retValue = false; + + if (loadTargetFile(targetFilePath)) + { + splitTargetFile(); + + buildNewImportTable(moduleList); + + retValue = saveNewFile(newFilePath); + + clearAllData(); + + return retValue; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("rebuildImportTable ::Failed to load target %s\n"), targetFilePath); +#endif + return true; + } +} + +void ImportRebuild::setFlagToIATSection(DWORD_PTR iatAddress) +{ + for (size_t i = 0; i < vecSectionHeaders.size(); i++) + { + if ((vecSectionHeaders[i].VirtualAddress <= iatAddress) && ((vecSectionHeaders[i].VirtualAddress + vecSectionHeaders[i].Misc.VirtualSize) > iatAddress)) + { + //section must be read and writeable + vecSectionHeaders[i].Characteristics |= IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; + } + } +} diff --git a/Scylla/ImportRebuild.h b/Scylla/ImportRebuild.h new file mode 100644 index 0000000..14239c4 --- /dev/null +++ b/Scylla/ImportRebuild.h @@ -0,0 +1,54 @@ +#pragma once + +#include "ProcessAccessHelp.h" +#include "Thunks.h" + +class ImportRebuild { +public: + + bool rebuildImportTable(const WCHAR * targetFilePath, const WCHAR * newFilePath, std::map & moduleList); + + DWORD convertRVAToOffset(DWORD dwRVA); + DWORD convertOffsetToRVA(DWORD dwOffset); + + bool addNewSection(char * sectionName, DWORD sectionSize, BYTE * sectionData); + + bool writeZeroMemoryToFile(HANDLE hFile, DWORD fileOffset, DWORD size); + +private: + std::vector vecSectionHeaders; + std::vector vecSectionData; + + BYTE * imageData; + BYTE * pDosStub; + PIMAGE_DOS_HEADER pDosHeader; + PIMAGE_NT_HEADERS pNTHeader; + + PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; + PIMAGE_THUNK_DATA pThunkData; + PIMAGE_IMPORT_BY_NAME pImportByName; + + DWORD getOffsetLastSection(); + + void clearAllData(); + void updatePeHeader(); + DWORD fillImportSection( std::map & moduleList, size_t lastSectionIndex ); + bool splitTargetFile(); + DWORD calculateMinSize(std::map & moduleList); + + DWORD convertRVAToOffsetVector(DWORD dwRVA); + DWORD_PTR convertOffsetToRVAVector(DWORD dwOffset); + + BYTE * getMemoryPointerFromRVA(DWORD_PTR dwRVA); + + DWORD alignValue(DWORD badValue, DWORD alignTo); + + bool alignSectionHeaders(); + + bool saveNewFile(const WCHAR * filepath); + bool loadTargetFile(const WCHAR * filepath); + + bool createNewImportSection(std::map & moduleList); + bool buildNewImportTable(std::map & moduleList); + void setFlagToIATSection( DWORD_PTR iatAddress ); +}; \ No newline at end of file diff --git a/Scylla/ImportsHandling.cpp b/Scylla/ImportsHandling.cpp new file mode 100644 index 0000000..c94a798 --- /dev/null +++ b/Scylla/ImportsHandling.cpp @@ -0,0 +1,825 @@ + +#include "ImportsHandling.h" +#include "definitions.h" + +//#define DEBUG_COMMENTS + +bool ImportModuleThunk::isValid() +{ + std::map::iterator iterator = thunkList.begin(); + while (iterator != thunkList.end()) + { + if (iterator->second.valid == false) + { + return false; + } + iterator++; + } + + return true; +} + +DWORD_PTR ImportModuleThunk::getFirstThunk() +{ + if (thunkList.size() > 0) + { + std::map::iterator iterator = thunkList.begin(); + return iterator->first; + } + else + { + return 0; + } +} + +/*bool ImportsHandling::addModule(WCHAR * moduleName, DWORD_PTR firstThunk) +{ + ImportModuleThunk module; + + module.firstThunk = firstThunk; + wcscpy_s(module.moduleName, MAX_PATH, moduleName); + + moduleList.insert(std::pair(firstThunk,module)); + + return true; +}*/ + +/*bool ImportsHandling::addFunction(WCHAR * moduleName, char * name, DWORD_PTR va, DWORD_PTR rva, DWORD_PTR ordinal, bool valid, bool suspect) +{ + ImportThunk import; + ImportModuleThunk * module = 0; + std::map::iterator iterator1; + + if (moduleList.size() > 1) + { + iterator1 = moduleList.begin(); + while (iterator1 != moduleList.end()) + { + if (rva >= iterator1->second.firstThunk) + { + iterator1++; + if (iterator1 == moduleList.end()) + { + iterator1--; + module = &(iterator1->second); + break; + } + else if (rva < iterator1->second.firstThunk) + { + iterator1--; + module = &(iterator1->second); + break; + } + } + } + } + else + { + iterator1 = moduleList.begin(); + module = &(iterator1->second); + } + + if (!module) + { + Logger::debugLog(TEXT("ImportsHandling::addFunction module not found rva ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(""),rva); + return false; + } + + //TODO + import.suspect = true; + import.valid = false; + import.va = va; + import.rva = rva; + import.ordinal = ordinal; + + wcscpy_s(import.moduleName, MAX_PATH, moduleName); + strcpy_s(import.name, MAX_PATH, name); + + module->thunkList.insert(std::pair(import.rva, import)); + + return true; +}*/ + +void ImportsHandling::displayAllImports() +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk; + ImportThunk * importThunk; + HTREEITEM module; + HTREEITEM apiFunction; + HWND idTreeView = GetDlgItem(hWndMainDlg, IDC_TREE_IMPORTS); + + TreeView_DeleteAllItems(idTreeView); + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + module = addDllToTreeView(idTreeView,moduleThunk->moduleName,moduleThunk->firstThunk,moduleThunk->thunkList.size(),moduleThunk->isValid()); + + moduleThunk->hTreeItem = module; + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + apiFunction = addApiToTreeView(idTreeView,module,importThunk); + importThunk->hTreeItem = apiFunction; + iterator2++; + } + + iterator1++; + } + +} + +HTREEITEM ImportsHandling::addDllToTreeView(HWND idTreeView, const WCHAR * dllName, DWORD_PTR firstThunk, size_t numberOfFunctions, bool valid) +{ + WCHAR validString[4]; + + if (valid) + { + wcscpy_s(validString,_countof(validString),TEXT("YES")); + } + else + { + wcscpy_s(validString,_countof(validString),TEXT("NO")); + } + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("%s FThunk: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(" NbThunk: %02X (dec: %02d) valid: %s"),dllName,firstThunk,numberOfFunctions,numberOfFunctions,validString); + + tvInsert.hParent = NULL; + tvInsert.hInsertAfter = TVI_ROOT; + tvInsert.item.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE; + tvInsert.item.pszText = stringBuffer; + return TreeView_InsertItem(idTreeView, &tvInsert); +} + +HTREEITEM ImportsHandling::addApiToTreeView(HWND idTreeView, HTREEITEM parentDll, ImportThunk * importThunk) +{ + if (importThunk->ordinal != 0) + { + if (importThunk->name[0] != 0x00) + { + swprintf_s(tempString, _countof(tempString),TEXT("ord: %04X name: %S"),importThunk->ordinal,importThunk->name); + } + else + { + swprintf_s(tempString, _countof(tempString),TEXT("ord: %04X"),importThunk->ordinal); + } + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("va: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" rva: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(" mod: %s %s"),importThunk->va,importThunk->rva,importThunk->moduleName,tempString); + } + else + { + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("va: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" rva: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(" prt: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(""),importThunk->va,importThunk->rva,importThunk->apiAddressVA); + } + + + tvInsert.hParent = parentDll; + tvInsert.hInsertAfter = TVI_LAST; + tvInsert.item.mask = TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE; + tvInsert.item.pszText = stringBuffer; + return TreeView_InsertItem(idTreeView, &tvInsert); +} + +void ImportsHandling::showImports(bool invalid, bool suspect) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk; + ImportThunk * importThunk; + + HWND idTreeView = GetDlgItem(hWndMainDlg, IDC_TREE_IMPORTS); + + SetFocus(idTreeView); + TreeView_SelectItem(idTreeView,0); //remove selection + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (invalid && !importThunk->valid) + { + selectItem(idTreeView, importThunk->hTreeItem); + setFocus(idTreeView,importThunk->hTreeItem); + } + else if (suspect && importThunk->suspect) + { + selectItem(idTreeView, importThunk->hTreeItem); + setFocus(idTreeView,importThunk->hTreeItem); + } + else + { + unselectItem(idTreeView, importThunk->hTreeItem); + } + + iterator2++; + } + + iterator1++; + } +} + +bool ImportsHandling::isItemSelected(HWND hwndTV, HTREEITEM hItem) +{ + TV_ITEM tvi; + tvi.mask = TVIF_STATE | TVIF_HANDLE; + tvi.stateMask = TVIS_SELECTED; + tvi.hItem = hItem; + + TreeView_GetItem(hwndTV, &tvi); + + return (tvi.state & TVIS_SELECTED) != 0; +} + +void ImportsHandling::unselectItem(HWND hwndTV, HTREEITEM htItem) +{ + selectItem(hwndTV, htItem, false); +} + +bool ImportsHandling::selectItem(HWND hwndTV, HTREEITEM hItem, bool select) +{ + TV_ITEM tvi; + tvi.mask = TVIF_STATE | TVIF_HANDLE; + tvi.stateMask = TVIS_SELECTED; + tvi.state = select ? TVIS_SELECTED : 0; + tvi.hItem = hItem; + + + if ( TreeView_SetItem(hwndTV, &tvi) == -1 ) + { + return false; + } + + return true; +} + +void ImportsHandling::setFocus(HWND hwndTV, HTREEITEM htItem) +{ + // the current focus + HTREEITEM htFocus = (HTREEITEM)TreeView_GetSelection(hwndTV); + + if ( htItem ) + { + // set the focus + if ( htItem != htFocus ) + { + // remember the selection state of the item + bool wasSelected = isItemSelected(hwndTV, htItem); + + if ( htFocus && isItemSelected(hwndTV, htFocus) ) + { + // prevent the tree from unselecting the old focus which it + // would do by default (TreeView_SelectItem unselects the + // focused item) + TreeView_SelectItem(hwndTV, 0); + selectItem(hwndTV, htFocus); + } + + TreeView_SelectItem(hwndTV, htItem); + + if ( !wasSelected ) + { + // need to clear the selection which TreeView_SelectItem() gave + // us + unselectItem(hwndTV, htItem); + } + //else: was selected, still selected - ok + } + //else: nothing to do, focus already there + } + else + { + if ( htFocus ) + { + bool wasFocusSelected = isItemSelected(hwndTV, htFocus); + + // just clear the focus + TreeView_SelectItem(hwndTV, 0); + + if ( wasFocusSelected ) + { + // restore the selection state + selectItem(hwndTV, htFocus); + } + } + //else: nothing to do, no focus already + } +} + +bool ImportsHandling::invalidateFunction( HTREEITEM selectedTreeNode ) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk; + ImportThunk * importThunk; + + TV_ITEM tvi = {0}; + + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->hTreeItem == selectedTreeNode) + { + importThunk->ordinal = 0; + importThunk->hint = 0; + importThunk->valid = false; + importThunk->suspect = false; + importThunk->moduleName[0] = 0; + importThunk->name[0] = 0; + + updateImportInTreeView(importThunk); + updateModuleInTreeView(moduleThunk); + return true; + } + + iterator2++; + } + + iterator1++; + } + + return false; +} + +void ImportsHandling::updateImportInTreeView(ImportThunk * importThunk) +{ + TV_ITEM tvi = {0}; + HWND treeControl = GetDlgItem(hWndMainDlg,IDC_TREE_IMPORTS); + + if (importThunk->ordinal != 0) + { + if (importThunk->name[0] != 0x00) + { + swprintf_s(tempString, _countof(tempString),TEXT("ord: %04X name: %S"),importThunk->ordinal,importThunk->name); + } + else + { + swprintf_s(tempString, _countof(tempString),TEXT("ord: %04X"),importThunk->ordinal); + } + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("va: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" rva: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(" mod: %s %s"),importThunk->va,importThunk->rva,importThunk->moduleName,tempString); + } + else + { + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("va: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" rva: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(" prt: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(""),importThunk->va,importThunk->rva,importThunk->apiAddressVA); + } + + tvi.pszText = stringBuffer; + tvi.cchTextMax = 260; + tvi.hItem = importThunk->hTreeItem; + tvi.mask = TVIF_TEXT; + TreeView_SetItem(treeControl,&tvi); +} + +void ImportsHandling::updateModuleInTreeView(ImportModuleThunk * importThunk) +{ + TV_ITEM tvi = {0}; + HWND treeControl = GetDlgItem(hWndMainDlg,IDC_TREE_IMPORTS); + + WCHAR validString[4]; + + if (importThunk->isValid()) + { + wcscpy_s(validString,_countof(validString),TEXT("YES")); + } + else + { + wcscpy_s(validString,_countof(validString),TEXT("NO")); + } + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("%s FThunk: ")TEXT(PRINTF_DWORD_PTR_HALF)TEXT(" NbThunk: %02X (dec: %02d) valid: %s"),importThunk->moduleName,importThunk->firstThunk,importThunk->thunkList.size(),importThunk->thunkList.size(),validString); + + + tvi.pszText = stringBuffer; + tvi.cchTextMax = 260; + tvi.hItem = importThunk->hTreeItem; + tvi.mask = TVIF_TEXT; + TreeView_SetItem(treeControl,&tvi); +} + +bool ImportsHandling::cutThunk( HTREEITEM selectedTreeNode ) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk; + ImportThunk * importThunk; + + TV_ITEM tvi = {0}; + HWND treeControl = GetDlgItem(hWndMainDlg,IDC_TREE_IMPORTS); + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->hTreeItem == selectedTreeNode) + { + + TreeView_DeleteItem(treeControl,importThunk->hTreeItem); + moduleThunk->thunkList.erase(iterator2); + + if (moduleThunk->thunkList.empty()) + { + TreeView_DeleteItem(treeControl,moduleThunk->hTreeItem); + moduleList.erase(iterator1); + } + else + { + updateModuleInTreeView(moduleThunk); + } + return true; + } + + iterator2++; + } + + iterator1++; + } + + return false; +} + +bool ImportsHandling::deleteTreeNode( HTREEITEM selectedTreeNode ) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk; + ImportThunk * importThunk; + + TV_ITEM tvi = {0}; + HWND treeControl = GetDlgItem(hWndMainDlg,IDC_TREE_IMPORTS); + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + + + if (moduleThunk->hTreeItem == selectedTreeNode) + { + TreeView_DeleteItem(treeControl,moduleThunk->hTreeItem); + moduleThunk->thunkList.clear(); + moduleList.erase(iterator1); + return true; + } + else + { + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->hTreeItem == selectedTreeNode) + { + TreeView_DeleteItem(treeControl,moduleThunk->hTreeItem); + moduleThunk->thunkList.clear(); + moduleList.erase(iterator1); + return true; + } + + iterator2++; + } + } + + + + iterator1++; + } + + return false; +} + +DWORD_PTR ImportsHandling::getApiAddressByNode( HTREEITEM selectedTreeNode ) +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk; + ImportThunk * importThunk; + + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->hTreeItem == selectedTreeNode) + { + return importThunk->apiAddressVA; + } + + iterator2++; + } + + iterator1++; + } + return 0; +} + + +void ImportsHandling::scanAndFixModuleList() +{ + std::map::iterator iterator1; + std::map::iterator iterator2; + ImportModuleThunk * moduleThunk; + ImportThunk * importThunk; + + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + iterator2 = moduleThunk->thunkList.begin(); + + while (iterator2 != moduleThunk->thunkList.end()) + { + importThunk = &(iterator2->second); + + if (importThunk->moduleName[0] == 0 || importThunk->moduleName[0] == L'?') + { + addNotFoundApiToModuleList(importThunk); + } + else + { + if (isNewModule(importThunk->moduleName)) + { + addModuleToModuleList(importThunk->moduleName, importThunk->rva); + } + + addFunctionToModuleList(importThunk); + } + + iterator2++; + } + + moduleThunk->thunkList.clear(); + + iterator1++; + } + + moduleList.clear(); + moduleList.insert(moduleListNew.begin(), moduleListNew.end()); + moduleListNew.clear(); +} + +bool ImportsHandling::findNewModules( std::map & thunkList ) +{ + throw std::exception("The method or operation is not implemented."); +} + +bool ImportsHandling::addModuleToModuleList(const WCHAR * moduleName, DWORD_PTR firstThunk) +{ + ImportModuleThunk module; + + module.firstThunk = firstThunk; + wcscpy_s(module.moduleName, MAX_PATH, moduleName); + + moduleListNew.insert(std::pair(firstThunk,module)); + + return true; +} + +bool ImportsHandling::isNewModule(const WCHAR * moduleName) +{ + std::map::iterator iterator1; + + iterator1 = moduleListNew.begin(); + + while (iterator1 != moduleListNew.end()) + { + if (!_wcsicmp(iterator1->second.moduleName, moduleName)) + { + return false; + } + + iterator1++; + } + + return true; +} + +void ImportsHandling::addUnknownModuleToModuleList(DWORD_PTR firstThunk) +{ + ImportModuleThunk module; + + module.firstThunk = firstThunk; + wcscpy_s(module.moduleName, MAX_PATH, TEXT("?")); + + moduleListNew.insert(std::pair(firstThunk,module)); +} + +bool ImportsHandling::addNotFoundApiToModuleList(ImportThunk * apiNotFound) +{ + ImportThunk import; + ImportModuleThunk * module = 0; + std::map::iterator iterator1; + DWORD_PTR rva = apiNotFound->rva; + + if (moduleListNew.size() > 0) + { + iterator1 = moduleListNew.begin(); + while (iterator1 != moduleListNew.end()) + { + if (rva >= iterator1->second.firstThunk) + { + iterator1++; + if (iterator1 == moduleListNew.end()) + { + iterator1--; + //new unknown module + if (iterator1->second.moduleName[0] == L'?') + { + module = &(iterator1->second); + } + else + { + addUnknownModuleToModuleList(apiNotFound->rva); + module = &(moduleListNew.find(rva)->second); + } + + break; + } + else if (rva < iterator1->second.firstThunk) + { + iterator1--; + module = &(iterator1->second); + break; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("Error iterator1 != (*moduleThunkList).end()\r\n"); +#endif + break; + } + } + } + else + { + //new unknown module + addUnknownModuleToModuleList(apiNotFound->rva); + module = &(moduleListNew.find(rva)->second); + } + + if (!module) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("ImportsHandling::addFunction module not found rva ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),rva); +#endif + return false; + } + + + import.suspect = true; + import.valid = false; + import.va = apiNotFound->va; + import.rva = apiNotFound->rva; + import.apiAddressVA = apiNotFound->apiAddressVA; + import.ordinal = 0; + + wcscpy_s(import.moduleName, MAX_PATH, TEXT("?")); + strcpy_s(import.name, MAX_PATH, "?"); + + module->thunkList.insert(std::pair(import.rva, import)); + + return true; +} + +bool ImportsHandling::addFunctionToModuleList(ImportThunk * apiFound) +{ + ImportThunk import; + ImportModuleThunk * module = 0; + std::map::iterator iterator1; + + if (moduleListNew.size() > 1) + { + iterator1 = moduleListNew.begin(); + while (iterator1 != moduleListNew.end()) + { + if (apiFound->rva >= iterator1->second.firstThunk) + { + iterator1++; + if (iterator1 == moduleListNew.end()) + { + iterator1--; + module = &(iterator1->second); + break; + } + else if (apiFound->rva < iterator1->second.firstThunk) + { + iterator1--; + module = &(iterator1->second); + break; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("Error iterator1 != moduleListNew.end()\r\n")); +#endif + break; + } + } + } + else + { + iterator1 = moduleListNew.begin(); + module = &(iterator1->second); + } + + if (!module) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("ImportsHandling::addFunction module not found rva ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),apiFound->rva); +#endif + return false; + } + + + import.suspect = apiFound->suspect; + import.valid = apiFound->valid; + import.va = apiFound->va; + import.rva = apiFound->rva; + import.apiAddressVA = apiFound->apiAddressVA; + import.ordinal = apiFound->ordinal; + import.hint = apiFound->hint; + + wcscpy_s(import.moduleName, MAX_PATH, apiFound->moduleName); + strcpy_s(import.name, MAX_PATH, apiFound->name); + + module->thunkList.insert(std::pair(import.rva, import)); + + return true; +} + +void ImportsHandling::expandAllTreeNodes() +{ + changeExpandStateOfTreeNodes(TVE_EXPAND); +} + +void ImportsHandling::collapseAllTreeNodes() +{ + changeExpandStateOfTreeNodes(TVE_COLLAPSE); +} + +void ImportsHandling::changeExpandStateOfTreeNodes(UINT flag) +{ + std::map::iterator iterator1; + ImportModuleThunk * moduleThunk; + + HWND treeControl = GetDlgItem(hWndMainDlg,IDC_TREE_IMPORTS); + + iterator1 = moduleList.begin(); + + while (iterator1 != moduleList.end()) + { + moduleThunk = &(iterator1->second); + + TreeView_Expand(treeControl, moduleThunk->hTreeItem, flag); + + iterator1++; + } +} diff --git a/Scylla/ImportsHandling.h b/Scylla/ImportsHandling.h new file mode 100644 index 0000000..31aff46 --- /dev/null +++ b/Scylla/ImportsHandling.h @@ -0,0 +1,54 @@ +#pragma once + +#include "Thunks.h" +#include "MainGui.h" + +class ImportsHandling : public MainGui { +public: + std::map moduleList; + std::map moduleListNew; + + //bool addFunction(WCHAR * moduleName, char * name, DWORD_PTR va, DWORD_PTR rva, DWORD_PTR ordinal, bool valid, bool suspect); + //bool addModule(WCHAR * moduleName, DWORD_PTR firstThunk); + + void displayAllImports(); + void showImports(bool invalid, bool suspect); + bool invalidateFunction(HTREEITEM selectedTreeNode); + bool cutThunk( HTREEITEM selectedTreeNode ); + bool deleteTreeNode( HTREEITEM selectedTreeNode ); + + void updateImportInTreeView(ImportThunk * importThunk); + void updateModuleInTreeView(ImportModuleThunk * importThunk); + DWORD_PTR getApiAddressByNode( HTREEITEM selectedTreeNode ); + void scanAndFixModuleList(); + void expandAllTreeNodes(); + void collapseAllTreeNodes(); + +private: + DWORD numberOfFunctions; + + WCHAR tempString[100]; + + TV_INSERTSTRUCT tvInsert; + HTREEITEM m_hItemFirstSel; + + HTREEITEM addDllToTreeView(HWND idTreeView, const WCHAR * dllName, DWORD_PTR firstThunk, size_t numberOfFunctions, bool valid); + HTREEITEM addApiToTreeView(HWND idTreeView, HTREEITEM parentDll, ImportThunk * importThunk); + + + bool isItemSelected(HWND hwndTV, HTREEITEM hItem); + void unselectItem(HWND hwndTV, HTREEITEM htItem); + bool selectItem(HWND hwndTV, HTREEITEM hItem, bool select = true); + void setFocus(HWND hwndTV, HTREEITEM htItem); + bool findNewModules( std::map & thunkList ); + + bool addModuleToModuleList(const WCHAR * moduleName, DWORD_PTR firstThunk); + void addUnknownModuleToModuleList(DWORD_PTR firstThunk); + bool addNotFoundApiToModuleList(ImportThunk * apiNotFound); + bool addFunctionToModuleList(ImportThunk * apiFound); + bool isNewModule(const WCHAR * moduleName); + + void changeExpandStateOfTreeNodes(UINT flag); + + +}; \ No newline at end of file diff --git a/Scylla/Logger.cpp b/Scylla/Logger.cpp new file mode 100644 index 0000000..0b08567 --- /dev/null +++ b/Scylla/Logger.cpp @@ -0,0 +1,90 @@ +#include "Logger.h" + +#include "MainGui.h" + +WCHAR Logger::debugLogFile[MAX_PATH]; +WCHAR Logger::logbuf[300]; +char Logger::logbufChar[300]; + +void Logger::getDebugLogFilePath() +{ + GetModuleFileName(0, debugLogFile, MAX_PATH); + + for(size_t i = wcslen(debugLogFile); i > 0; i--) + { + if(debugLogFile[i] == L'\\') + { + debugLogFile[i+1] = 0x00; + break; + } + } + + wcscat_s(debugLogFile, _countof(debugLogFile), TEXT(DEBUG_LOG_FILENAME)); +} + + +void Logger::debugLog(const WCHAR * format, ...) +{ + FILE * pFile; + va_list va_alist; + + if (!format) + { + return; + } + + ZeroMemory(logbuf, sizeof(logbuf)); + + va_start (va_alist, format); + _vsnwprintf_s(logbuf, _countof(logbuf), _countof(logbuf) - 1, format, va_alist); + va_end (va_alist); + + if (_wfopen_s(&pFile,debugLogFile,L"a") == NULL) + { + fputws(logbuf,pFile); + fclose (pFile); + } +} + +void Logger::debugLog(const char * format, ...) +{ + FILE * pFile; + va_list va_alist; + + if (!format) + { + return; + } + + ZeroMemory(logbufChar, sizeof(logbufChar)); + + va_start (va_alist, format); + _vsnprintf_s(logbufChar, _countof(logbufChar), _countof(logbufChar) - 1, format, va_alist); + va_end (va_alist); + + if (_wfopen_s(&pFile,debugLogFile,L"a") == NULL) + { + fputs(logbufChar,pFile); + fclose (pFile); + } +} + +void Logger::printfDialog(const WCHAR * format, ...) +{ + va_list va_alist; + + if (!format) + { + return; + } + + ZeroMemory(logbuf, sizeof(logbuf)); + + va_start (va_alist, format); + _vsnwprintf_s(logbuf, _countof(logbuf), _countof(logbuf) - 1, format, va_alist); + va_end (va_alist); + + + MainGui::addTextToOutputLog(logbuf); + UpdateWindow(MainGui::hWndMainDlg); +} \ No newline at end of file diff --git a/Scylla/Logger.h b/Scylla/Logger.h new file mode 100644 index 0000000..2ff6a97 --- /dev/null +++ b/Scylla/Logger.h @@ -0,0 +1,20 @@ + +#pragma once + +#include + +#define DEBUG_LOG_FILENAME "Scylla_debug.log" + +class Logger { +public: + static void debugLog(const WCHAR * format, ...); + static void debugLog(const CHAR * format, ...); + static void printfDialog(const WCHAR * format, ...); + + static void getDebugLogFilePath(); + +private: + static WCHAR debugLogFile[MAX_PATH]; + static WCHAR logbuf[300]; + static char logbufChar[300]; +}; \ No newline at end of file diff --git a/Scylla/MainGui.aps b/Scylla/MainGui.aps new file mode 100644 index 0000000..d559401 Binary files /dev/null and b/Scylla/MainGui.aps differ diff --git a/Scylla/MainGui.cpp b/Scylla/MainGui.cpp new file mode 100644 index 0000000..567fd08 --- /dev/null +++ b/Scylla/MainGui.cpp @@ -0,0 +1,880 @@ +#include "MainGui.h" +#include "ImportsHandling.h" +#include "definitions.h" +#include "PluginLoader.h" +#include "ConfigurationHolder.h" +#include "PeDump.h" +#include "PeRebuild.h" +#include "DllInjectionPlugin.h" +#include "DisassemblerGui.h" +#include "NativeWinApi.h" +#include "ImportRebuild.h" +#include "SystemInformation.h" +#include "AboutGui.h" +#include "OptionsGui.h" + +HINSTANCE MainGui::hInstance = 0; +HWND MainGui::hWndParent = 0; +HWND MainGui::hWndMainDlg = 0; +Process * MainGui::selectedProcess = 0; + +WCHAR MainGui::stringBuffer[300] = {0}; + +ProcessLister MainGui::processLister; +ImportsHandling MainGui::importsHandling; +ProcessAccessHelp MainGui::processAccessHelp; +ApiReader MainGui::apiReader; + +void MainGui::initDialog(HINSTANCE hInstance) +{ + hInstance = hInstance; + Logger::getDebugLogFilePath(); + ConfigurationHolder::loadConfiguration(); + PluginLoader::findAllPlugins(); + NativeWinApi::initialize(); + SystemInformation::getSystemInformation(); + + + if (SystemInformation::currenOS == UNKNOWN_OS) + { + MessageBox(0, TEXT("Operating System is not supported"), TEXT("Error Operating System"),MB_OK); + return; + } + + + processAccessHelp.getProcessModules(GetCurrentProcessId(), processAccessHelp.ownModuleList); + + //Register controls, required for Windows XP + InitCommonControls(); + return; + DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLG_MAIN),hWndParent, (DLGPROC)mainDlgProc); + + //ConfigurationHolder::saveConfiguration(); +} + +LRESULT CALLBACK MainGui::mainDlgProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + hWndMainDlg = hWndDlg; + HTREEITEM selectedTreeNode = 0; + switch(uMsg) + { + case WM_INITDIALOG: + //init dialog elements + dialogInitActionHandler(); + return TRUE; + + case WM_NOTIFY: + switch(LOWORD(wParam)) + { + case IDC_TREE_IMPORTS: + { + if(((LPNMHDR)lParam)->code == NM_CLICK) + { + //Logger::printfDialog(L"NM_CLICK"); + } + if(((LPNMHDR)lParam)->code == NM_DBLCLK) + { + //Logger::printfDialog(L"NM_DBLCLK"); + } + if(((LPNMHDR)lParam)->code == NM_RCLICK) + { + //Logger::printfDialog(L"NM_RCLICK"); + + selectedTreeNode=(HTREEITEM)SendDlgItemMessage (hWndMainDlg,IDC_TREE_IMPORTS,TVM_GETNEXTITEM,TVGN_DROPHILITE,0); + if(selectedTreeNode != NULL) + { + SendDlgItemMessage(hWndMainDlg,IDC_TREE_IMPORTS,TVM_SELECTITEM,TVGN_CARET,(LPARAM)selectedTreeNode); + } + } + if(((LPNMHDR)lParam)->code == NM_RDBLCLK) + { + //Logger::printfDialog(L"NM_RDBLCLK"); + } + } + break; + /*case IDC_MODULELIST: + LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; + if (pnmv->uChanged & LVIF_STATE) + { + if ((pnmv->uNewState & LVIS_SELECTED) && (!(pnmv->uOldState & LVIS_SELECTED)) && (pnmv->hdr.code == LVN_ITEMCHANGED)) + { + + //sprintf(stringBuffer,"%X",i); + //MessageBox(hWndDlg, text,"Display Notification", MB_OK); + break; + } + }*/ + } + return TRUE; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_CBO_PROCESSLIST: + switch(HIWORD(wParam)) + { + case CBN_DROPDOWN: //list is about to display + fillProcessListComboBox(GetDlgItem(hWndDlg, IDC_CBO_PROCESSLIST)); + break; + case CBN_SELENDOK: //item selected + processSelectedActionHandler(SendMessage(GetDlgItem(hWndDlg, IDC_CBO_PROCESSLIST),CB_GETCURSEL,0,0)); + break; + } + + return TRUE; + + case IDC_BTN_PICKDLL: + pickDllActionHandler(); + return TRUE; + case IDC_BTN_OPTIONS: + optionsActionHandler(); + return TRUE; + case IDC_BTN_DUMP: + dumpActionHandler(); + return TRUE; + case IDC_BTN_FIXDUMP: + dumpFixActionHandler(); + return TRUE; + case IDC_BTN_PEREBUILD: + peRebuildActionHandler(); + return TRUE; + case IDC_BTN_DLLINJECT: + dllInjectActionHandler(); + return TRUE; + case ID_MISC_DLLINJECTION: + dllInjectActionHandler(); + return TRUE; + case ID_MISC_PREFERENCES: + optionsActionHandler(); + return TRUE; + case IDC_BTN_IATAUTOSEARCH: + iatAutosearchActionHandler(); + return TRUE; + case IDC_BTN_GETIMPORTS: + getImportsActionHandler(); + return TRUE; + case IDC_BTN_INVALIDIMPORTS: + showInvalidImportsActionHandler(); + return TRUE; + case IDC_BTN_SUSPECTIMPORTS: + showSuspectImportsActionHandler(); + return TRUE; + case IDC_BTN_CLEARIMPORTS: + TreeView_DeleteAllItems(GetDlgItem(hWndDlg, IDC_TREE_IMPORTS)); + importsHandling.moduleList.clear(); + return TRUE; + case IDC_BTN_CLEARLOG: + clearOutputLog(); + return TRUE; + case IDC_BTN_ABOUT: + showAboutDialog(); + return TRUE; + case ID_HELP_ABOUT: + showAboutDialog(); + return TRUE; + case IDC_BTN_EXIT: + PostQuitMessage(0); + EndDialog(hWndDlg, 0); + return TRUE; + case ID_FILE_EXIT: + PostQuitMessage(0); + EndDialog(hWndDlg, 0); + return TRUE; + case IDCANCEL: + PostQuitMessage(0); + EndDialog(hWndDlg, 0); + return TRUE; + } + return TRUE; + + case WM_LBUTTONDOWN: + //leftButtonDownActionHandler(); + //return TRUE; + + case WM_CONTEXTMENU: + return OnContextMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + default: + return FALSE; + } +} + +void MainGui::setIconAndDialogCaption() +{ + if (hWndMainDlg) + { + HICON hicon = LoadIcon(GetModuleHandle(0),MAKEINTRESOURCE(IDI_ICON_SCYLLA1)); + SendMessage(hWndMainDlg, WM_SETICON, ICON_BIG, (LPARAM)hicon); + SendMessage(hWndMainDlg, WM_SETICON, ICON_SMALL, (LPARAM)hicon); + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT(APPNAME)TEXT(" ")TEXT(ARCHITECTURE)TEXT(" ")TEXT(APPVERSION)TEXT(" ")); + SetWindowText(hWndMainDlg,stringBuffer); + } +} + + +void MainGui::leftButtonDownActionHandler(WPARAM wParam, LPARAM lParam) +{ + if(wParam & MK_CONTROL) + { + + } + else if(wParam & MK_SHIFT) + { + + } + else + { + + } +} + +void MainGui::dialogInitActionHandler() +{ + setIconAndDialogCaption(); + + if (ConfigurationHolder::getConfigObject(DEBUG_PRIVILEGE)->isTrue()) + { + processLister.setDebugPrivileges(); + } + + + enableDialogButtons(FALSE); + + Edit_LimitText(GetDlgItem(hWndMainDlg,IDC_EDIT_OEPADDRESS), MAX_HEX_VALUE_EDIT_LENGTH); + Edit_LimitText(GetDlgItem(hWndMainDlg,IDC_EDIT_IATADDRESS), MAX_HEX_VALUE_EDIT_LENGTH); + Edit_LimitText(GetDlgItem(hWndMainDlg,IDC_EDIT_IATSIZE), MAX_HEX_VALUE_EDIT_LENGTH); +} + +void MainGui::pickDllActionHandler() +{ + if (PickDllGui::initDialog(hInstance,hWndMainDlg, processAccessHelp.moduleList)) + { + //get selected module + processAccessHelp.selectedModule = PickDllGui::selectedModule; + Logger::printfDialog(TEXT("->>> Module %s selected."), processAccessHelp.selectedModule->getFilename()); + Logger::printfDialog(TEXT("Imagebase: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" Size: %08X"),processAccessHelp.selectedModule->modBaseAddr,processAccessHelp.selectedModule->modBaseSize); + } + else + { + processAccessHelp.selectedModule = 0; + } +} + +void MainGui::startDisassemblerGui(HTREEITEM selectedTreeNode) +{ + DWORD_PTR address = importsHandling.getApiAddressByNode(selectedTreeNode); + if (address) + { + DisassemblerGui::initDialog(hInstance,hWndMainDlg,address); + } + +} + +void MainGui::processSelectedActionHandler(LRESULT index) +{ + std::vector& processList = processLister.getProcessList(); + Process &process = processList.at(index); + selectedProcess = &process; + + enableDialogButtons(TRUE); + + Logger::printfDialog(TEXT("Analyzing %s"),process.fullPath); + + if (processAccessHelp.hProcess != 0) + { + processAccessHelp.closeProcessHandle(); + apiReader.clearAll(); + } + + if (!processAccessHelp.openProcessHandle(process.PID)) + { + Logger::printfDialog(TEXT("Error: Cannot open process handle.")); + return; + } + + processAccessHelp.getProcessModules(process.PID, processAccessHelp.moduleList); + + apiReader.readApisFromModuleList(); + + Logger::printfDialog(TEXT("Loading modules done.")); + + //TODO improve + processAccessHelp.selectedModule = 0; + processAccessHelp.targetSizeOfImage = process.imageSize; + processAccessHelp.targetImageBase = process.imageBase; + + ProcessAccessHelp::getSizeOfImageCurrentProcess(); + + process.imageSize = (DWORD)processAccessHelp.targetSizeOfImage; + + + Logger::printfDialog(TEXT("Imagebase: ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" Size: %08X"),process.imageBase, process.imageSize); + + selectedProcess->entryPoint = ProcessAccessHelp::getEntryPointFromFile(selectedProcess->fullPath); + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT(PRINTF_DWORD_PTR_FULL),selectedProcess->entryPoint + selectedProcess->imageBase); + SetDlgItemText(hWndMainDlg, IDC_EDIT_OEPADDRESS, stringBuffer); +} + + + +void MainGui::fillProcessListComboBox(HWND hCombo) +{ + if (hCombo) + { + SendMessage(hCombo,CB_RESETCONTENT,0,0); + + std::vector& processList = processLister.getProcessListSnapshot(); + + for (size_t i = 0; i < processList.size(); i++) + { + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("0x%04X - %s - %s"),processList[i].PID,processList[i].filename,processList[i].fullPath); + SendMessage(hCombo,CB_ADDSTRING,0,(LPARAM)stringBuffer); + } + } +} + +void MainGui::addTextToOutputLog(const WCHAR * text) +{ + if (hWndMainDlg) + { + HWND hList = GetDlgItem(hWndMainDlg,IDC_LIST_LOG); + + ListBox_SetCurSel(hList, ListBox_AddString(hList,text)); + } +} + +void MainGui::clearOutputLog() +{ + if (hWndMainDlg) + { + SendDlgItemMessage(hWndMainDlg, IDC_LIST_LOG, LB_RESETCONTENT, 0, 0); + } +} + +void MainGui::showInvalidImportsActionHandler() +{ + importsHandling.showImports(true, false); +} + +void MainGui::showSuspectImportsActionHandler() +{ + importsHandling.showImports(false, true); +} + +void MainGui::iatAutosearchActionHandler() +{ + DWORD_PTR searchAddress = 0; + DWORD_PTR addressIAT = 0; + DWORD sizeIAT = 0; + IATSearch iatSearch; + + GetDlgItemText(hWndMainDlg, IDC_EDIT_OEPADDRESS, stringBuffer, _countof(stringBuffer)); + + if (wcslen(stringBuffer) > 1) + { + searchAddress = stringToDwordPtr(stringBuffer); + if (searchAddress) + { + if (iatSearch.searchImportAddressTableInProcess(searchAddress, &addressIAT, &sizeIAT)) + { + Logger::printfDialog(TEXT("IAT found at VA ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" RVA ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" Size 0x%04X (%d)"),addressIAT, addressIAT - processAccessHelp.targetImageBase,sizeIAT,sizeIAT); + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT(PRINTF_DWORD_PTR_FULL),addressIAT); + SetDlgItemText(hWndMainDlg,IDC_EDIT_IATADDRESS,stringBuffer); + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("%08X"),sizeIAT); + SetDlgItemText(hWndMainDlg,IDC_EDIT_IATSIZE,stringBuffer); + + swprintf_s(stringBuffer, _countof(stringBuffer),TEXT("IAT found! Start Address ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" Size 0x%04X (%d) "),addressIAT,sizeIAT,sizeIAT); + MessageBox(hWndMainDlg,stringBuffer, TEXT("IAT found"), MB_OK); + + } + else + { + Logger::printfDialog(TEXT("IAT not found at OEP ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("!"),searchAddress); + } + } + + } +} + +void MainGui::getImportsActionHandler() +{ + DWORD_PTR addressIAT = 0; + DWORD sizeIAT = 0; + + GetDlgItemText(hWndMainDlg, IDC_EDIT_IATADDRESS, stringBuffer, sizeof(stringBuffer)); + addressIAT = stringToDwordPtr(stringBuffer); + + GetDlgItemText(hWndMainDlg, IDC_EDIT_IATSIZE, stringBuffer, sizeof(stringBuffer)); + sizeIAT = wcstoul(stringBuffer, NULL, 16); + + if (addressIAT && sizeIAT) + { + apiReader.readAndParseIAT(addressIAT, sizeIAT,importsHandling.moduleList); + importsHandling.displayAllImports(); + } +} + + +DWORD_PTR MainGui::stringToDwordPtr(WCHAR * hexString) +{ + DWORD_PTR address = 0; + +#ifdef _WIN64 + address = _wcstoui64(hexString, NULL, 16); +#else + address = wcstoul(hexString, NULL, 16); +#endif + + if (address == 0) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"stringToDwordPtr :: address == 0, %s",hexString); +#endif + return 0; + } + else + { + return address; + } +} + +bool MainGui::OnContextMenu(int x, int y) +{ + HWND hwnd = 0; + POINT pt = { x, y }; // location of mouse click + //TV_ITEM tvi; + //WCHAR ttt[260] = {0}; + //HTREEITEM selectedTreeNode = 0; + + if ((hwnd = mouseInDialogItem(IDC_TREE_IMPORTS, pt)) != NULL) + { + if (TreeView_GetCount(hwnd)) //module list should not be empty + { + /*selectedTreeNode = (HTREEITEM)SendDlgItemMessage(hWndMainDlg,IDC_TREE_IMPORTS,TVM_GETNEXTITEM,TVGN_CARET,(LPARAM)selectedTreeNode); + tvi.mask=TVIF_TEXT; // item text attrivute + + tvi.pszText=ttt; // Text is the pointer to the text + + tvi.cchTextMax=260; // size of text to retrieve. + + tvi.hItem=selectedTreeNode; // the selected item + + SendDlgItemMessage(hWndMainDlg,IDC_TREE_IMPORTS,TVM_GETITEM,TVGN_CARET,(LPARAM)&tvi); + Logger::printfDialog(L"selected %s",tvi.pszText);*/ + DisplayContextMenuImports(hwnd, pt); + } + return true; + } + //if (PtInRect(&rc, pt)) + //{ + // ClientToScreen(hwnd, &pt); + // DisplayContextMenu(hwnd, pt); + // return TRUE; + //} + + // Return FALSE if no menu is displayed. + + return false; +} + +void MainGui::DisplayContextMenuImports(HWND hwnd, POINT pt) +{ + BOOL menuItem = 0; + HTREEITEM selectedTreeNode = 0; + std::vector &pluginList = PluginLoader::getPluginList(); + HMENU hmenuTrackPopup = getCorrectSubMenu(IDR_MENU_IMPORTS, 0); + + appendPluginListToMenu(hmenuTrackPopup); + + if (hmenuTrackPopup) + { + menuItem = TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, 0); + if (menuItem) + { + + if ((menuItem >= PLUGIN_MENU_BASE_ID) && (menuItem <= (int)(pluginList.size() + PLUGIN_MENU_BASE_ID))) + { + //wsprintf(stringBuffer, L"%d %s\n",menuItem,pluginList[menuItem - PLUGIN_MENU_BASE_ID].pluginName); + //MessageBox(0,stringBuffer,L"plugin selection",0); + + DllInjectionPlugin dllInjectionPlugin; + dllInjectionPlugin.hProcess = ProcessAccessHelp::hProcess; + dllInjectionPlugin.apiReader = &apiReader; + dllInjectionPlugin.injectPlugin(pluginList[menuItem - PLUGIN_MENU_BASE_ID], importsHandling.moduleList,selectedProcess->imageBase, selectedProcess->imageSize); + + importsHandling.scanAndFixModuleList(); + importsHandling.displayAllImports(); + return; + } + + selectedTreeNode = (HTREEITEM)SendDlgItemMessage(hWndMainDlg,IDC_TREE_IMPORTS,TVM_GETNEXTITEM,TVGN_CARET,(LPARAM)selectedTreeNode); + + switch (menuItem) + { + case ID__INVALIDATEFUNCTION: + { + importsHandling.invalidateFunction(selectedTreeNode); + } + + break; + case ID__DISASSEMBLE: + { + startDisassemblerGui(selectedTreeNode); + } + break; + case ID__CUTTHUNK: + { + importsHandling.cutThunk(selectedTreeNode); + } + break; + case ID__DELETETREENODE: + { + importsHandling.deleteTreeNode(selectedTreeNode); + } + break; + case ID__EXPANDALLNODES: + { + importsHandling.expandAllTreeNodes(); + } + break; + case ID__COLLAPSEALLNODES: + { + importsHandling.collapseAllTreeNodes(); + } + break; + } + + + } + } +} + +HWND MainGui::mouseInDialogItem(int dlgItem, POINT pt) +{ + RECT rc; + HWND hwnd = GetDlgItem(hWndMainDlg, dlgItem); + if (hwnd) + { + // Get the bounding rectangle of the client area. + GetClientRect(hwnd, &rc); + + // Convert the mouse position to client coordinates. + ScreenToClient(hwnd, &pt); + + // If the position is in the client area, display a + // shortcut menu. + if (PtInRect(&rc, pt)) + { + return hwnd; + } + else + { + return 0; + } + } + else + { + return 0; + } +} + +HMENU MainGui::getCorrectSubMenu(int menuItem, int subMenuItem) +{ + HMENU hmenu; // top-level menu + HMENU hmenuTrackPopup; // shortcut menu + // Load the menu resource. + if ((hmenu = LoadMenu(hInstance, MAKEINTRESOURCE(menuItem))) == NULL) + return 0; + + hmenuTrackPopup = GetSubMenu(hmenu, subMenuItem); + + if (hmenuTrackPopup) + { + return hmenuTrackPopup; + } + else + { + return 0; + } +} + +void MainGui::DisplayContextMenu(HWND hwnd, POINT pt) +{ + HMENU hmenu; // top-level menu + HMENU hmenuTrackPopup; // shortcut menu + int menuItem; // selected menu item + + // Load the menu resource. + if ((hmenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU_IMPORTS))) == NULL) + return; + + // TrackPopupMenu cannot display the menu bar so get + // a handle to the first shortcut menu. + + hmenuTrackPopup = GetSubMenu(hmenu, 0); + + // Display the shortcut menu. Track the right mouse + // button. + if (!hmenuTrackPopup) + { + MessageBoxA(0,"hmenuTrackPopup == null","hmenuTrackPopup",0); + } + + menuItem = TrackPopupMenu(hmenuTrackPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwnd, NULL); + + if (menuItem) + { + /*if (menuItem == ID_LISTCONTROL_SHOWEXPORTS) + { + MessageBox(0,"exports","dshhhhh",0); + }*/ + } + + // Destroy the menu. + + DestroyMenu(hmenu); +} + +void MainGui::appendPluginListToMenu(HMENU hMenuTrackPopup) +{ + HMENU newMenu = CreatePopupMenu(); + + std::vector &pluginList = PluginLoader::getPluginList(); + + if (pluginList.size() > 0) + { + for (size_t i = 0; i < pluginList.size(); i++) + { + AppendMenu(newMenu, MF_STRING, i + PLUGIN_MENU_BASE_ID, pluginList[i].pluginName); + } + + AppendMenu(hMenuTrackPopup,MF_MENUBARBREAK,0,0); + AppendMenu(hMenuTrackPopup,MF_POPUP,(UINT_PTR)newMenu,TEXT("Plugins")); + } + +} + +void MainGui::dumpActionHandler() +{ + WCHAR * targetFile = 0; + PeDump peDump; + + if (processAccessHelp.selectedModule) + { + targetFile = ProcessAccessHelp::selectFileToSave(0, 0); + } + else + { + targetFile = ProcessAccessHelp::selectFileToSave(0, 1); + } + + + if (targetFile) + { + if (processAccessHelp.selectedModule) + { + //dump DLL + + peDump.imageBase = processAccessHelp.selectedModule->modBaseAddr; + peDump.sizeOfImage = processAccessHelp.selectedModule->modBaseSize; + //get it from gui + peDump.entryPoint = getOEPFromGui(); + wcscpy_s(peDump.fullpath, MAX_PATH, processAccessHelp.selectedModule->fullPath); + } + else + { + peDump.imageBase = ProcessAccessHelp::targetImageBase; + peDump.sizeOfImage = (DWORD)ProcessAccessHelp::targetSizeOfImage; + //get it from gui + peDump.entryPoint = getOEPFromGui(); + wcscpy_s(peDump.fullpath, MAX_PATH, selectedProcess->fullPath); + } + + peDump.useHeaderFromDisk = ConfigurationHolder::getConfigObject(USE_PE_HEADER_FROM_DISK)->isTrue(); + if (peDump.dumpCompleteProcessToDisk(targetFile)) + { + Logger::printfDialog(TEXT("Dump success %s"),targetFile); + //MessageBox(hWndMainDlg,TEXT("Image dumped successfully."),TEXT("Success"),MB_OK); + } + else + { + Logger::printfDialog(TEXT("Error: Cannot dump image.")); + MessageBox(hWndMainDlg,TEXT("Cannot dump image."),TEXT("Failure"),MB_OK); + } + + delete [] targetFile; + } +} + +DWORD_PTR MainGui::getOEPFromGui() +{ + if (GetDlgItemText(hWndMainDlg, IDC_EDIT_OEPADDRESS, stringBuffer, _countof(stringBuffer))) + { + return stringToDwordPtr(stringBuffer); + } + else + { + return 0; + } +} + +void MainGui::peRebuildActionHandler() +{ + DWORD newSize = 0; + WCHAR * targetFile = 0; + PeRebuild peRebuild; + + targetFile = ProcessAccessHelp::selectFileToSave(OFN_FILEMUSTEXIST, 2); + + if (targetFile) + { + if (ConfigurationHolder::getConfigObject(CREATE_BACKUP)->isTrue()) + { + if (!ProcessAccessHelp::createBackupFile(targetFile)) + { + Logger::printfDialog(TEXT("Creating backup file failed %s"), targetFile); + } + } + + LONGLONG fileSize = ProcessAccessHelp::getFileSize(targetFile); + LPVOID mapped = peRebuild.createFileMappingViewFull(targetFile); + + newSize = peRebuild.realignPE(mapped, (DWORD)fileSize); + peRebuild.closeAllMappingHandles(); + + if (newSize < 10) + { + Logger::printfDialog(TEXT("Rebuild failed %s"), targetFile); + MessageBox(hWndMainDlg,TEXT("Rebuild failed."),TEXT("Failure"),MB_OK); + } + else + { + peRebuild.truncateFile(targetFile, newSize); + + Logger::printfDialog(TEXT("Rebuild success %s"), targetFile); + Logger::printfDialog(TEXT("-> Old file size 0x%08X new file size 0x%08X (%d %%)"), (DWORD)fileSize, newSize, (DWORD)((newSize * 100) / (DWORD)fileSize) ); + //MessageBox(hWndMainDlg,TEXT("Image rebuilded successfully."),TEXT("Success"),MB_OK); + } + + + delete [] targetFile; + } +} + +void MainGui::dumpFixActionHandler() +{ + WCHAR * targetFile = 0; + WCHAR newFilePath[MAX_PATH]; + ImportRebuild importRebuild; + + if (TreeView_GetCount(GetDlgItem(hWndMainDlg, IDC_TREE_IMPORTS)) < 2) + { + Logger::printfDialog(TEXT("Nothing to rebuild")); + return; + } + + if (processAccessHelp.selectedModule) + { + targetFile = ProcessAccessHelp::selectFileToSave(OFN_FILEMUSTEXIST, 0); + } + else + { + targetFile = ProcessAccessHelp::selectFileToSave(OFN_FILEMUSTEXIST, 1); + } + + if (targetFile) + { + wcscpy_s(newFilePath,MAX_PATH,targetFile); + + for (size_t i = wcslen(newFilePath) - 1; i >= 0; i--) + { + if (newFilePath[i] == L'.') + { + newFilePath[i] = 0; + break; + } + } + + wcscat_s(newFilePath,MAX_PATH, L"_SCY.exe"); + + if (importRebuild.rebuildImportTable(targetFile,newFilePath,importsHandling.moduleList)) + { + //MessageBox(hWndMainDlg,L"Imports rebuilding successful",L"Success",MB_OK); + + Logger::printfDialog(TEXT("Import Rebuild success %s"), newFilePath); + } + else + { + Logger::printfDialog(TEXT("Import Rebuild failed, target %s"), targetFile); + MessageBox(hWndMainDlg,L"Imports rebuilding failed",L"Failure",MB_OK); + } + + delete [] targetFile; + } + +} + +void MainGui::enableDialogButtons(BOOL value) +{ + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_PICKDLL), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_DUMP), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_DLLINJECT), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_FIXDUMP), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_IATAUTOSEARCH), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_GETIMPORTS), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_SUSPECTIMPORTS), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_INVALIDIMPORTS), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_CLEARIMPORTS), value); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_OPTIONS), TRUE); + + //not yet implemented + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_AUTOTRACE), FALSE); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_SAVETREE), FALSE); + EnableWindow(GetDlgItem(hWndMainDlg, IDC_BTN_LOADTREE), FALSE); + +} + +void MainGui::showAboutDialog() +{ + AboutGui::initDialog(hInstance,hWndMainDlg); +} + +void MainGui::dllInjectActionHandler() +{ + WCHAR * targetFile = 0; + HMODULE hMod = 0; + DllInjection dllInjection; + + targetFile = ProcessAccessHelp::selectFileToSave(OFN_FILEMUSTEXIST, 0); + + if (targetFile) + { + hMod = dllInjection.dllInjection(ProcessAccessHelp::hProcess, targetFile); + if (hMod && ConfigurationHolder::getConfigObject(DLL_INJECTION_AUTO_UNLOAD)->isTrue()) + { + if (!dllInjection.unloadDllInProcess(ProcessAccessHelp::hProcess, hMod)) + { + Logger::printfDialog(TEXT("DLL unloading failed, target %s"), targetFile); + } + } + + if (hMod) + { + Logger::printfDialog(TEXT("DLL Injection was successful, target %s"), targetFile); + } + else + { + Logger::printfDialog(TEXT("DLL Injection failed, target %s"), targetFile); + } + + delete [] targetFile; + } + +} + +void MainGui::optionsActionHandler() +{ + OptionsGui::initOptionsDialog(hInstance, hWndMainDlg); +} diff --git a/Scylla/MainGui.h b/Scylla/MainGui.h new file mode 100644 index 0000000..e086863 --- /dev/null +++ b/Scylla/MainGui.h @@ -0,0 +1,95 @@ +#pragma once + +//#define _CRTDBG_MAP_ALLOC +//#include +//#include + +#include +#include +#include +#include +#include + +#include "resource.h" +#include "Logger.h" +#include "ProcessLister.h" +#include "IATSearch.h" +#include "PickDllGui.h" + + +#pragma comment(lib, "comctl32.lib") + + +#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) + +class ImportsHandling; + +class MainGui +{ +public: + static HWND hWndMainDlg; + static Process * selectedProcess; + + static void initDialog(HINSTANCE hInstance); + + //Output Window + static void addTextToOutputLog(const WCHAR * text); + + static DWORD_PTR stringToDwordPtr(WCHAR * hexString); + +protected: + static HWND hWndParent; + + static HINSTANCE hInstance; + static ProcessLister processLister; + static WCHAR stringBuffer[300]; + + static ImportsHandling importsHandling; + static ProcessAccessHelp processAccessHelp; + static ApiReader apiReader; + +private: + + static LRESULT CALLBACK mainDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + static void setIconAndDialogCaption(); + + + static void fillProcessListComboBox(HWND hCombo); + static void getModuleListItem(int column, int iItem, char * buffer); + + + static void leftButtonDownActionHandler(WPARAM wParam, LPARAM lParam); + static void dialogInitActionHandler(); + static void pickDllActionHandler(); + static void processSelectedActionHandler(LRESULT index); + + //static bool displayModuleList(HWND hWndDlg, HWND hList, LRESULT index); + + + // POPUP MENU Prototypes + static bool OnContextMenu(int, int); + static void DisplayContextMenu(HWND, POINT); + static HWND mouseInDialogItem(int, POINT); + static void DisplayContextMenuImports(HWND, POINT); + static HMENU getCorrectSubMenu(int, int); + + + static void clearOutputLog();//Output Window + static void showInvalidImportsActionHandler(); + static void showSuspectImportsActionHandler(); + static void iatAutosearchActionHandler(); + static void getImportsActionHandler(); + static void appendPluginListToMenu( HMENU hMenuTrackPopup ); + static void dumpActionHandler(); + static DWORD_PTR getOEPFromGui(); + static void peRebuildActionHandler(); + + static void startDisassemblerGui(HTREEITEM selectedTreeNode); + static void dumpFixActionHandler(); + static void enableDialogButtons( BOOL value ); + static void showAboutDialog(); + static void dllInjectActionHandler(); + static void optionsActionHandler(); +}; \ No newline at end of file diff --git a/Scylla/MainGui.rc b/Scylla/MainGui.rc new file mode 100644 index 0000000..3e25378 Binary files /dev/null and b/Scylla/MainGui.rc differ diff --git a/Scylla/NativeWinApi.cpp b/Scylla/NativeWinApi.cpp new file mode 100644 index 0000000..42cda73 --- /dev/null +++ b/Scylla/NativeWinApi.cpp @@ -0,0 +1,66 @@ +#include "NativeWinApi.h" + +def_NtCreateThreadEx NativeWinApi::NtCreateThreadEx = 0; +def_NtDuplicateObject NativeWinApi::NtDuplicateObject = 0; +def_NtOpenProcess NativeWinApi::NtOpenProcess = 0; +def_NtOpenThread NativeWinApi::NtOpenThread = 0; +def_NtQueryObject NativeWinApi::NtQueryObject = 0; +def_NtQueryInformationFile NativeWinApi::NtQueryInformationFile = 0; +def_NtQueryInformationProcess NativeWinApi::NtQueryInformationProcess = 0; +def_NtQueryInformationThread NativeWinApi::NtQueryInformationThread = 0; +def_NtQuerySystemInformation NativeWinApi::NtQuerySystemInformation = 0; +def_NtResumeThread NativeWinApi::NtResumeThread = 0; +def_NtSetInformationThread NativeWinApi::NtSetInformationThread = 0; +def_NtTerminateProcess NativeWinApi::NtTerminateProcess = 0; + +def_RtlNtStatusToDosError NativeWinApi::RtlNtStatusToDosError = 0; + +void NativeWinApi::initialize() +{ + HMODULE hModuleNtdll = GetModuleHandle(TEXT("ntdll.dll")); + + if (!hModuleNtdll) + { + return; + } + + NtCreateThreadEx = (def_NtCreateThreadEx)GetProcAddress(hModuleNtdll, "NtCreateThreadEx"); + NtDuplicateObject = (def_NtDuplicateObject)GetProcAddress(hModuleNtdll, "NtDuplicateObject"); + NtOpenProcess = (def_NtOpenProcess)GetProcAddress(hModuleNtdll, "NtOpenProcess"); + NtOpenThread = (def_NtOpenThread)GetProcAddress(hModuleNtdll, "NtOpenThread"); + NtQueryObject = (def_NtQueryObject)GetProcAddress(hModuleNtdll, "NtQueryObject"); + NtQueryInformationFile = (def_NtQueryInformationFile)GetProcAddress(hModuleNtdll, "NtQueryInformationFile"); + NtQueryInformationProcess = (def_NtQueryInformationProcess)GetProcAddress(hModuleNtdll, "NtQueryInformationProcess"); + NtQueryInformationThread = (def_NtQueryInformationThread)GetProcAddress(hModuleNtdll, "NtQueryInformationThread"); + NtQuerySystemInformation = (def_NtQuerySystemInformation)GetProcAddress(hModuleNtdll, "NtQuerySystemInformation"); + NtResumeThread = (def_NtResumeThread)GetProcAddress(hModuleNtdll, "NtResumeThread"); + NtSetInformationThread = (def_NtSetInformationThread)GetProcAddress(hModuleNtdll, "NtSetInformationThread"); + NtTerminateProcess = (def_NtTerminateProcess)GetProcAddress(hModuleNtdll, "NtTerminateProcess"); + + RtlNtStatusToDosError = (def_RtlNtStatusToDosError)GetProcAddress(hModuleNtdll, "RtlNtStatusToDosError"); + +} + + +PPEB NativeWinApi::getCurrentProcessEnvironmentBlock() +{ + return getProcessEnvironmentBlockAddress(GetCurrentProcess()); +} + +PPEB NativeWinApi::getProcessEnvironmentBlockAddress(HANDLE processHandle) +{ + ULONG lReturnLength = 0; + PROCESS_BASIC_INFORMATION processBasicInformation; + + if ((NtQueryInformationProcess(processHandle,ProcessBasicInformation,&processBasicInformation,sizeof(PROCESS_BASIC_INFORMATION),&lReturnLength) >= 0) && (lReturnLength == sizeof(PROCESS_BASIC_INFORMATION))) + { + //printf("NtQueryInformationProcess success %d\n",sizeof(PROCESS_BASIC_INFORMATION)); + + return processBasicInformation.PebBaseAddress; + } + else + { + //printf("NtQueryInformationProcess failed %d vs %d\n",lReturnLength,sizeof(PROCESS_BASIC_INFORMATION)); + return 0; + } +} \ No newline at end of file diff --git a/Scylla/NativeWinApi.h b/Scylla/NativeWinApi.h new file mode 100644 index 0000000..262f776 --- /dev/null +++ b/Scylla/NativeWinApi.h @@ -0,0 +1,261 @@ +#pragma once + +#include + +#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L) +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) +#define DUPLICATE_SAME_ATTRIBUTES 0x00000004 +#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0) + +typedef enum _SYSTEM_INFORMATION_CLASS { + + SystemBasicInformation, + SystemProcessorInformation, + SystemPerformanceInformation, + SystemTimeOfDayInformation, + SystemPathInformation, + SystemProcessInformation, + SystemCallCountInformation, + SystemDeviceInformation, + SystemProcessorPerformanceInformation, + SystemFlagsInformation, + SystemCallTimeInformation, + SystemModuleInformation, + SystemLocksInformation, + SystemStackTraceInformation, + SystemPagedPoolInformation, + SystemNonPagedPoolInformation, + SystemHandleInformation, + SystemObjectInformation, + SystemPageFileInformation, + SystemVdmInstemulInformation, + SystemVdmBopInformation, + SystemFileCacheInformation, + SystemPoolTagInformation, + SystemInterruptInformation, + SystemDpcBehaviorInformation, + SystemFullMemoryInformation, + SystemLoadGdiDriverInformation, + SystemUnloadGdiDriverInformation, + SystemTimeAdjustmentInformation, + SystemSummaryMemoryInformation, + SystemNextEventIdInformation, + SystemEventIdsInformation, + SystemCrashDumpInformation, + SystemExceptionInformation, + SystemCrashDumpStateInformation, + SystemKernelDebuggerInformation, + SystemContextSwitchInformation, + SystemRegistryQuotaInformation, + SystemExtendServiceTableInformation, + SystemPrioritySeperation, + SystemPlugPlayBusInformation, + SystemDockInformation, + SystemPowerInformation2, + SystemProcessorSpeedInformation, + SystemCurrentTimeZoneInformation, + SystemLookasideInformation + +} SYSTEM_INFORMATION_CLASS; + +typedef struct _IO_STATUS_BLOCK { + union { + NTSTATUS Status; + PVOID Pointer; + }; + ULONG_PTR Information; +} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; + +typedef struct _FILE_NAME_INFORMATION { // Information Classes 9 and 21 + ULONG FileNameLength; + WCHAR FileName[1]; +} FILE_NAME_INFORMATION; + +typedef enum _FILE_INFORMATION_CLASS { + FileNameInformation=9, +} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _CLIENT_ID{ + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + +#define InitializeObjectAttributes(p,n,a,r,s) \ +{ \ + (p)->Length = sizeof(OBJECT_ATTRIBUTES); \ + (p)->ObjectName = n; \ + (p)->Attributes = a; \ + (p)->RootDirectory = r; \ + (p)->SecurityDescriptor = s; \ + (p)->SecurityQualityOfService = NULL; \ +} + +typedef struct _OBJECT_ATTRIBUTES +{ + ULONG Length; + PVOID RootDirectory; + PUNICODE_STRING ObjectName; + ULONG Attributes; + PVOID SecurityDescriptor; + PVOID SecurityQualityOfService; +} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; + +typedef enum _OBJECT_INFORMATION_CLASS { + ObjectBasicInformation, + ObjectNameInformation, + ObjectTypeInformation, + ObjectAllInformation, + ObjectDataInformation +} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS; + +typedef enum _THREADINFOCLASS { + ThreadBasicInformation, + ThreadTimes, + ThreadPriority, + ThreadBasePriority, + ThreadAffinityMask, + ThreadImpersonationToken, + ThreadDescriptorTableEntry, + ThreadEnableAlignmentFaultFixup, + ThreadEventPair_Reusable, + ThreadQuerySetWin32StartAddress, + ThreadZeroTlsCell, + ThreadPerformanceCount, + ThreadAmILastThread, + ThreadIdealProcessor, + ThreadPriorityBoost, + ThreadSetTlsArrayAddress, + ThreadIsIoPending, + ThreadHideFromDebugger, + ThreadBreakOnTermination, + MaxThreadInfoClass +} THREADINFOCLASS; + +typedef enum _PROCESSINFOCLASS { + ProcessBasicInformation, + ProcessQuotaLimits, + ProcessIoCounters, + ProcessVmCounters, + ProcessTimes, + ProcessBasePriority, + ProcessRaisePriority, + ProcessDebugPort, + ProcessExceptionPort, + ProcessAccessToken, + ProcessLdtInformation, + ProcessLdtSize, + ProcessDefaultHardErrorMode, + ProcessIoPortHandlers, + ProcessPooledUsageAndLimits, + ProcessWorkingSetWatch, + ProcessUserModeIOPL, + ProcessEnableAlignmentFaultFixup, + ProcessPriorityClass, + ProcessWx86Information, + ProcessHandleCount, + ProcessAffinityMask, + ProcessPriorityBoost, + ProcessDeviceMap, + ProcessSessionInformation, + ProcessForegroundInformation, + ProcessWow64Information, + ProcessImageFileName, + ProcessLUIDDeviceMapsEnabled, + ProcessBreakOnTermination, + ProcessDebugObjectHandle, + ProcessDebugFlags, + ProcessHandleTracing, + ProcessIoPriority, + ProcessExecuteFlags, + ProcessResourceManagement, + ProcessCookie, + ProcessImageInformation, + MaxProcessInfoClass +} PROCESSINFOCLASS; + +typedef struct _PEB_LDR_DATA { + BYTE Reserved1[8]; + PVOID Reserved2[3]; + LIST_ENTRY InMemoryOrderModuleList; +} PEB_LDR_DATA, *PPEB_LDR_DATA; + +typedef struct _RTL_USER_PROCESS_PARAMETERS { + BYTE Reserved1[16]; + PVOID Reserved2[10]; + UNICODE_STRING ImagePathName; + UNICODE_STRING CommandLine; +} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS; + +typedef struct _PEB { + BYTE Reserved1[2]; + BYTE BeingDebugged; + BYTE Reserved2[1]; + PVOID Reserved3[2]; + PPEB_LDR_DATA Ldr; + PRTL_USER_PROCESS_PARAMETERS ProcessParameters; + BYTE Reserved4[104]; + PVOID Reserved5[52]; + PVOID PostProcessInitRoutine; + BYTE Reserved6[128]; + PVOID Reserved7[1]; + ULONG SessionId; +} PEB, *PPEB; + +typedef struct _PROCESS_BASIC_INFORMATION { + PVOID Reserved1; + PPEB PebBaseAddress; + PVOID Reserved2[2]; + ULONG_PTR UniqueProcessId; + PVOID Reserved3; +} PROCESS_BASIC_INFORMATION; + + +typedef NTSTATUS (WINAPI *def_NtTerminateProcess)(HANDLE ProcessHandle, NTSTATUS ExitStatus); +typedef NTSTATUS (WINAPI *def_NtQueryObject)(HANDLE Handle,OBJECT_INFORMATION_CLASS ObjectInformationClass,PVOID ObjectInformation,ULONG ObjectInformationLength,PULONG ReturnLength); +typedef NTSTATUS (WINAPI *def_NtDuplicateObject)(HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, BOOLEAN InheritHandle, ULONG Options ); +typedef NTSTATUS (WINAPI *def_NtQueryInformationFile)(HANDLE FileHandle, PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, FILE_INFORMATION_CLASS FileInformationClass); +typedef ULONG (WINAPI *def_NtQueryInformationThread)(HANDLE ThreadHandle,THREADINFOCLASS ThreadInformationClass,PVOID ThreadInformation,ULONG ThreadInformationLength,PULONG ReturnLength); +typedef NTSTATUS (WINAPI *def_NtQueryInformationProcess)(HANDLE ProcessHandle,PROCESSINFOCLASS ProcessInformationClass,PVOID ProcessInformation,ULONG ProcessInformationLength,PULONG ReturnLength); +typedef NTSTATUS (WINAPI *def_NtQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength, PULONG ReturnLength); +typedef NTSTATUS (WINAPI *def_NtOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK AccessMask, PVOID ObjectAttributes, PCLIENT_ID ClientId ); +typedef NTSTATUS (WINAPI *def_NtOpenThread)(PHANDLE ThreadHandle,ACCESS_MASK DesiredAccess,POBJECT_ATTRIBUTES ObjectAttributes,PCLIENT_ID ClientId); +typedef NTSTATUS (WINAPI *def_NtResumeThread)(HANDLE ThreadHandle, PULONG SuspendCount); +typedef NTSTATUS (WINAPI *def_NtSetInformationThread)(HANDLE ThreadHandle,THREADINFOCLASS ThreadInformationClass,PVOID ThreadInformation,ULONG ThreadInformationLength); +typedef NTSTATUS (WINAPI *def_NtCreateThreadEx)(PHANDLE hThread,ACCESS_MASK DesiredAccess,LPVOID ObjectAttributes,HANDLE ProcessHandle,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,BOOL CreateSuspended,ULONG StackZeroBits,LPVOID SizeOfStackCommit,LPVOID SizeOfStackReserve,LPVOID lpBytesBuffer); + + + + +typedef ULONG (WINAPI *def_RtlNtStatusToDosError)(NTSTATUS Status); + + + +class NativeWinApi { +public: + + static def_NtCreateThreadEx NtCreateThreadEx; + static def_NtDuplicateObject NtDuplicateObject; + static def_NtOpenProcess NtOpenProcess; + static def_NtOpenThread NtOpenThread; + static def_NtQueryObject NtQueryObject; + static def_NtQueryInformationFile NtQueryInformationFile; + static def_NtQueryInformationProcess NtQueryInformationProcess; + static def_NtQueryInformationThread NtQueryInformationThread; + static def_NtQuerySystemInformation NtQuerySystemInformation; + static def_NtResumeThread NtResumeThread; + static def_NtSetInformationThread NtSetInformationThread; + static def_NtTerminateProcess NtTerminateProcess; + + static def_RtlNtStatusToDosError RtlNtStatusToDosError; + + static void initialize(); + + static PPEB getCurrentProcessEnvironmentBlock(); + static PPEB getProcessEnvironmentBlockAddress(HANDLE processHandle); +}; diff --git a/Scylla/OptionsGui.cpp b/Scylla/OptionsGui.cpp new file mode 100644 index 0000000..13fbb19 --- /dev/null +++ b/Scylla/OptionsGui.cpp @@ -0,0 +1,186 @@ +#include "OptionsGui.h" +#include "ConfigurationHolder.h" + +HWND OptionsGui::hWndDlg = 0; + +INT_PTR OptionsGui::initOptionsDialog(HINSTANCE hInstance, HWND hWndParent) +{ + return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLG_OPTIONS),hWndParent, (DLGPROC)optionsDlgProc); +} + +LRESULT CALLBACK OptionsGui::optionsDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + hWndDlg = hWnd; + + switch (uMsg) + { + case WM_INITDIALOG: + Edit_LimitText(GetDlgItem(hWndDlg,IDC_OPTIONS_SECTIONNAME), IMAGE_SIZEOF_SHORT_NAME); + loadOptions(); + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_BTN_OPTIONS_OK: + { + saveOptions(); + ConfigurationHolder::saveConfiguration(); + EndDialog(hWnd, 0); + } + return TRUE; + case IDC_BTN_OPTIONS_CANCEL: + EndDialog(hWnd, 0); + return TRUE; + case IDCANCEL: + EndDialog(hWnd, 0); + return TRUE; + } + } + return FALSE; +} + +void OptionsGui::saveOptions() +{ + std::map::iterator mapIter; + + for (mapIter = ConfigurationHolder::getConfigList().begin() ; mapIter != ConfigurationHolder::getConfigList().end(); mapIter++) + { + getConfigOptionsFromDlg((*mapIter).second); + } +} + +void OptionsGui::loadOptions() +{ + std::map::iterator mapIter; + + for (mapIter = ConfigurationHolder::getConfigList().begin() ; mapIter != ConfigurationHolder::getConfigList().end(); mapIter++) + { + displayConfigInDlg((*mapIter).second); + } +} + +void OptionsGui::setCheckBox( int nIDDlgItem, bool bValue ) +{ + if (bValue) + { + Button_SetCheck(GetDlgItem(hWndDlg, nIDDlgItem),BST_CHECKED); + } + else + { + Button_SetCheck(GetDlgItem(hWndDlg, nIDDlgItem),BST_UNCHECKED); + } +} + +void OptionsGui::displayConfigInDlg( ConfigObject & config ) +{ + switch (config.configType) + { + case String: + { + setEditControl(config.dialogItemValue, config.valueString); + } + break; + case Boolean: + { + setCheckBox(config.dialogItemValue, config.isTrue()); + } + break; + case Decimal: + { +#ifdef _WIN64 + swprintf_s(config.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%I64u"),config.valueNumeric); +#else + swprintf_s(config.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%u"),config.valueNumeric); +#endif + setEditControl(config.dialogItemValue, config.valueString); + } + break; + case Hexadecimal: + { +#ifdef _WIN64 + swprintf_s(config.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%016I64X"),config.valueNumeric); +#else + swprintf_s(config.valueString, CONFIG_OPTIONS_STRING_LENGTH, TEXT("%08X"),config.valueNumeric); +#endif + setEditControl(config.dialogItemValue, config.valueString); + } + break; + } +} + +void OptionsGui::setEditControl( int nIDDlgItem, const WCHAR * valueString ) +{ + SetDlgItemText(hWndDlg,nIDDlgItem,valueString); +} + +void OptionsGui::getConfigOptionsFromDlg( ConfigObject & config ) +{ + switch (config.configType) + { + case String: + { + getEditControl(config.dialogItemValue, config.valueString); + } + break; + case Boolean: + { + getCheckBox(config.dialogItemValue, &config.valueNumeric); + } + break; + case Decimal: + { + getEditControlNumeric(config.dialogItemValue, &config.valueNumeric, 10); + } + break; + case Hexadecimal: + { + getEditControlNumeric(config.dialogItemValue, &config.valueNumeric, 16); + } + break; + } +} + +bool OptionsGui::getEditControl( int nIDDlgItem, WCHAR * valueString ) +{ + if (GetDlgItemText(hWndDlg, nIDDlgItem, valueString, CONFIG_OPTIONS_STRING_LENGTH)) + { + return true; + } + else + { + return false; + } +} + +void OptionsGui::getCheckBox( int nIDDlgItem, DWORD_PTR * valueNumeric ) +{ + switch (Button_GetCheck(GetDlgItem(hWndDlg, nIDDlgItem))) + { + case BST_CHECKED: + *valueNumeric = 1; + return; + case BST_UNCHECKED: + *valueNumeric = 0; + return; + default: + *valueNumeric = 0; + } +} + +void OptionsGui::getEditControlNumeric( int nIDDlgItem, DWORD_PTR * valueNumeric, int nBase ) +{ + WCHAR temp[CONFIG_OPTIONS_STRING_LENGTH] = {0}; + + if (getEditControl(nIDDlgItem, temp)) + { +#ifdef _WIN64 + *valueNumeric = _wcstoui64(temp, NULL, nBase); +#else + *valueNumeric = wcstoul(temp, NULL, nBase); +#endif + } + else + { + *valueNumeric = 0; + } +} diff --git a/Scylla/OptionsGui.h b/Scylla/OptionsGui.h new file mode 100644 index 0000000..21a32cb --- /dev/null +++ b/Scylla/OptionsGui.h @@ -0,0 +1,23 @@ +#pragma once + +#include "MainGui.h" + +class ConfigObject; + +class OptionsGui { +public: + static HWND hWndDlg; + static INT_PTR initOptionsDialog(HINSTANCE hInstance, HWND hWndParent); +private: + static LRESULT CALLBACK optionsDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static void saveOptions(); + static void loadOptions(); + static void setCheckBox( int nIDDlgItem, bool bValue ); + static void displayConfigInDlg( ConfigObject & config ); + static void setEditControl( int nIDDlgItem, const WCHAR * valueString ); + static void getConfigOptionsFromDlg( ConfigObject & config ); + + static bool getEditControl( int nIDDlgItem, WCHAR * valueString ); + static void getCheckBox( int dialogItemValue, DWORD_PTR * valueNumeric ); + static void getEditControlNumeric( int nIDDlgItem, DWORD_PTR * valueNumeric, int nBase ); +}; \ No newline at end of file diff --git a/Scylla/PeDump.cpp b/Scylla/PeDump.cpp new file mode 100644 index 0000000..77f7f80 --- /dev/null +++ b/Scylla/PeDump.cpp @@ -0,0 +1,436 @@ +#include "PeDump.h" +#include "ProcessAccessHelp.h" + +#include "Logger.h" +#include "definitions.h" + +bool PeDump::useHeaderFromDisk = true; +bool PeDump::appendOverlayData = true; + +//#define DEBUG_COMMENTS + +bool PeDump::fillPeHeaderStructs(bool fromDisk) +{ + DWORD dwSize = ProcessAccessHelp::PE_HEADER_BYTES_COUNT; + + if (dwSize > sizeOfImage) + { + dwSize = (DWORD)sizeOfImage; + } + + headerData = new BYTE[dwSize]; + + if (!headerData) + return false; + + if (fromDisk) + { + //from disk + if (!ProcessAccessHelp::readHeaderFromFile(headerData, dwSize, fullpath)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"fillPeHeaderStructs -> ProcessAccessHelp::readHeaderFromFile failed - %X %s\r\n", dwSize, fullpath); +#endif + return false; + } + } + else + { + //from memory + if (!ProcessAccessHelp::readMemoryFromProcess(imageBase, dwSize, headerData)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("fillPeHeaderStructs -> ProcessAccessHelp::readMemoryFromProcess failed - ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT(" %X ")TEXT(PRINTF_DWORD_PTR_FULL)TEXT("\r\n"),imageBase, dwSize, headerData); +#endif + return false; + } + } + + pDOSHeader = (PIMAGE_DOS_HEADER)headerData; + pNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)headerData + (DWORD_PTR)pDOSHeader->e_lfanew); + pSectionHeader = IMAGE_FIRST_SECTION(pNTHeader); + + return true; +} + +bool PeDump::validateHeaders() +{ + if ((pDOSHeader != 0) && (pDOSHeader->e_magic == IMAGE_DOS_SIGNATURE) && (pNTHeader->Signature == IMAGE_NT_SIGNATURE)) + { +#ifdef _WIN64 + if (pNTHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) +#else + if (pNTHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) +#endif + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} + +bool PeDump::dumpCompleteProcessToDisk(const WCHAR * dumpFilePath) +{ + if (!fillPeHeaderStructs(useHeaderFromDisk)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("dumpCompleteProcessToDisk -> fillPeHeaderStructs failed\r\n"); +#endif + return false; + } + + if (!validateHeaders()) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("dumpCompleteProcessToDisk -> validateHeaders failed\r\n"); +#endif + return false; + } + + dumpData = new BYTE[sizeOfImage]; + + if (dumpData) + { + if (!ProcessAccessHelp::readMemoryFromProcess(imageBase,sizeOfImage,dumpData)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("dumpCompleteProcessToDisk -> readMemoryFromProcess failed\r\n"); +#endif + return false; + } + else + { + + fixDump(dumpData); + + if (saveDumpToDisk(dumpFilePath, dumpData, (DWORD)sizeOfImage)) + { + + if (appendOverlayData) + { + appendOverlayDataToDump(dumpFilePath); + } + + //printf("dump success\n"); + return true; + } + else + { + return false; + } + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("dumpCompleteProcessToDisk -> new BYTE[sizeOfImage] failed %X\r\n",sizeOfImage); +#endif + return false; + } +} + +bool PeDump::appendOverlayDataToDump(const WCHAR *dumpFilePath) +{ + DWORD_PTR offset = 0; + DWORD size = 0; + + if (getOverlayData(fullpath,&offset,&size)) + { + if (offset == 0) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("appendOverlayDataToDump :: No overlay exists\r\n"); +#endif + return true; + } + else + { + if (copyFileDataFromOffset(fullpath, dumpFilePath, offset, size)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("appendOverlayDataToDump :: appending overlay success\r\n"); +#endif + return true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("appendOverlayDataToDump :: appending overlay failed\r\n"); +#endif + return false; + } + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("appendOverlayDataToDump :: getOverlayData failed\r\n"); +#endif + return false; + } +} + +bool PeDump::copyFileDataFromOffset(const WCHAR * sourceFile, const WCHAR * destFile, DWORD_PTR fileOffset, DWORD dwSize) +{ + HANDLE hSourceFile, hDestFile; + BYTE * dataBuffer = 0; + bool retValue = false; + + hSourceFile = CreateFile(sourceFile, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if(hSourceFile == INVALID_HANDLE_VALUE) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("copyFileDataFromOffset :: failed to open source file\r\n"); +#endif + return false; + } + + hDestFile = CreateFile(destFile, GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + + if(hSourceFile == INVALID_HANDLE_VALUE) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("copyFileDataFromOffset :: failed to open destination file\r\n"); +#endif + CloseHandle(hSourceFile); + return false; + } + + dataBuffer = new BYTE[dwSize]; + + if (ProcessAccessHelp::readMemoryFromFile(hSourceFile, (LONG)fileOffset, dwSize, dataBuffer)) + { + if (ProcessAccessHelp::writeMemoryToFileEnd(hDestFile,dwSize,dataBuffer)) + { + retValue = true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("copyFileDataFromOffset :: writeMemoryToFileEnd failed\r\n"); +#endif + retValue = false; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("copyFileDataFromOffset :: readMemoryFromFile failed to read from source file\r\n"); +#endif + retValue = false; + } + + delete [] dataBuffer; + + CloseHandle(hSourceFile); + CloseHandle(hDestFile); + + return retValue; +} + +void PeDump::fixDump(BYTE * dumpBuffer) +{ + int counter = 0; + PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)dumpBuffer; + PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD_PTR)dumpBuffer + pDos->e_lfanew); + PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(pNt); + + fixNtHeaderForDump(pNt, pNTHeader); + + do + { + fixSectionHeaderForDump(pSec, pSectionHeader); + + pSectionHeader++; + pSec++; + + counter++; + } while (counter < pNt->FileHeader.NumberOfSections); + + +} + +void PeDump::fixBadNtHeaderValues(PIMAGE_NT_HEADERS pNtHead) +{ + //maybe imagebase in process is not real imagebase + pNtHead->OptionalHeader.AddressOfEntryPoint = (DWORD)(entryPoint - imageBase); + pNtHead->OptionalHeader.SizeOfImage = sizeOfImage; +} + +void PeDump::fixSectionHeaderForDump(PIMAGE_SECTION_HEADER oldSecHead, PIMAGE_SECTION_HEADER newSecHead) +{ + memcpy_s(oldSecHead->Name, IMAGE_SIZEOF_SHORT_NAME, newSecHead->Name, IMAGE_SIZEOF_SHORT_NAME); + + oldSecHead->Characteristics = newSecHead->Characteristics; + + oldSecHead->Misc.VirtualSize = newSecHead->Misc.VirtualSize; + oldSecHead->VirtualAddress = newSecHead->VirtualAddress; + + oldSecHead->SizeOfRawData = newSecHead->Misc.VirtualSize; + oldSecHead->PointerToRawData = newSecHead->VirtualAddress; +} + +void PeDump::fixNtHeaderForDump(PIMAGE_NT_HEADERS oldNtHead, PIMAGE_NT_HEADERS newNtHead) +{ + //some special + fixBadNtHeaderValues(newNtHead); + + //fix FileHeader + oldNtHead->FileHeader.NumberOfSections = newNtHead->FileHeader.NumberOfSections; + + //fix OptionalHeader + oldNtHead->OptionalHeader.ImageBase = newNtHead->OptionalHeader.ImageBase; + oldNtHead->OptionalHeader.SizeOfImage = newNtHead->OptionalHeader.SizeOfImage; + oldNtHead->OptionalHeader.BaseOfCode = newNtHead->OptionalHeader.BaseOfCode; + oldNtHead->OptionalHeader.AddressOfEntryPoint = newNtHead->OptionalHeader.AddressOfEntryPoint; + oldNtHead->OptionalHeader.SectionAlignment = newNtHead->OptionalHeader.SectionAlignment; + oldNtHead->OptionalHeader.FileAlignment = newNtHead->OptionalHeader.SectionAlignment; + + //deleted in x64 PE +#ifndef _WIN64 + oldNtHead->OptionalHeader.BaseOfData = newNtHead->OptionalHeader.BaseOfData; +#endif +} + +bool PeDump::saveDumpToDisk(const WCHAR * dumpFilePath, BYTE *dumpBuffer, DWORD dumpSize) +{ + DWORD lpNumberOfBytesWritten = 0; + bool retValue = false; + + HANDLE hFile = CreateFile(dumpFilePath, GENERIC_WRITE, FILE_SHARE_WRITE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + + if(hFile == INVALID_HANDLE_VALUE) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("saveDumpToDisk :: INVALID_HANDLE_VALUE %u\r\n",GetLastError()); +#endif + retValue = false; + } + else + { + if (WriteFile(hFile, dumpBuffer, dumpSize, &lpNumberOfBytesWritten, 0)) + { + if (lpNumberOfBytesWritten != dumpSize) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("saveDumpToDisk :: lpNumberOfBytesWritten != dumpSize %d %d\r\n",lpNumberOfBytesWritten,dumpSize); +#endif + retValue = false; + } + else + { + retValue = true; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("saveDumpToDisk :: WriteFile failed %u\r\n",GetLastError()); +#endif + retValue = false; + } + + CloseHandle(hFile); + } + + return retValue; +} + +bool PeDump::getOverlayData(const WCHAR * filepath, DWORD_PTR * overlayFileOffset, DWORD * overlaySize) +{ + LONGLONG fileSize = 0; + DWORD dwSize = 0; + DWORD bufferSize = 1000; + BYTE *buffer = 0; + bool returnValue = 0; + PIMAGE_DOS_HEADER pDOSh = 0; + PIMAGE_NT_HEADERS pNTh = 0; + PIMAGE_SECTION_HEADER pSech = 0; + int counter = 0; + DWORD calcSize = 0; + + HANDLE hFile = CreateFile(filepath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if( hFile == INVALID_HANDLE_VALUE ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("getOverlayData :: INVALID_HANDLE_VALUE %u\r\n",GetLastError()); +#endif + returnValue = false; + } + else + { + fileSize = ProcessAccessHelp::getFileSize(hFile); + + if (fileSize > 0) + { + if (fileSize > bufferSize) + { + dwSize = bufferSize; + } + else + { + dwSize = (DWORD)(fileSize - 1); + } + + buffer = new BYTE[dwSize]; + + if (ProcessAccessHelp::readMemoryFromFile(hFile, 0, dwSize, buffer)) + { + pDOSh = (PIMAGE_DOS_HEADER)buffer; + pNTh = (PIMAGE_NT_HEADERS)((DWORD_PTR)buffer + pDOSh->e_lfanew); + + //first section + pSech = IMAGE_FIRST_SECTION(pNTh); + counter = 1; + + //get last section + while(counter < pNTh->FileHeader.NumberOfSections) + { + counter++; + pSech++; + } + + //printf("PointerToRawData %X\nSizeOfRawData %X\nfile size %X\n",pSech->PointerToRawData,pSech->SizeOfRawData,pSech->PointerToRawData+pSech->SizeOfRawData); + + calcSize = pSech->PointerToRawData + pSech->SizeOfRawData; + + if (calcSize < fileSize) + { + //overlay found + *overlayFileOffset = calcSize; + *overlaySize = (DWORD)(fileSize - calcSize); + } + else + { + *overlayFileOffset = 0; + *overlaySize = 0; + } + + returnValue = true; + } + else + { + returnValue = false; + } + + delete [] buffer; + } + else + { + returnValue = false; + } + + CloseHandle(hFile); + } + + return returnValue; +} \ No newline at end of file diff --git a/Scylla/PeDump.h b/Scylla/PeDump.h new file mode 100644 index 0000000..6f0bcfd --- /dev/null +++ b/Scylla/PeDump.h @@ -0,0 +1,63 @@ + +#include + +class PeDump { +public: + DWORD_PTR entryPoint; //VA + DWORD_PTR imageBase; //VA + DWORD sizeOfImage; + WCHAR fullpath[MAX_PATH]; + + //Settings + static bool useHeaderFromDisk; + static bool appendOverlayData; + + PeDump() + { + imageBase = 0; + sizeOfImage = 0; + dumpData = 0; + headerData = 0; + pNTHeader = 0; + pDOSHeader = 0; + pSectionHeader = 0; + } + + ~PeDump() + { + if (dumpData != 0) + { + delete [] dumpData; + } + + if (headerData != 0) + { + delete [] headerData; + } + } + + bool dumpCompleteProcessToDisk(const WCHAR * dumpFilePath); + + bool saveDumpToDisk(const WCHAR * dumpFilePath, BYTE *dumpBuffer, DWORD dumpSize); + + + bool copyFileDataFromOffset(const WCHAR * sourceFile, const WCHAR * destFile, DWORD_PTR fileOffset, DWORD dwSize); + bool appendOverlayDataToDump(const WCHAR * dumpFilePath); + bool getOverlayData(const WCHAR * filepath, DWORD_PTR * overlayFileOffset, DWORD * overlaySize); + +private: + BYTE * dumpData; + BYTE * headerData; + + PIMAGE_DOS_HEADER pDOSHeader; + PIMAGE_NT_HEADERS pNTHeader; + PIMAGE_SECTION_HEADER pSectionHeader; + + bool validateHeaders(); + bool fillPeHeaderStructs(bool fromDisk); + + void fixDump(BYTE * dumpBuffer); + void fixBadNtHeaderValues(PIMAGE_NT_HEADERS pNtHead); + void fixSectionHeaderForDump(PIMAGE_SECTION_HEADER oldSecHead, PIMAGE_SECTION_HEADER newSecHead); + void fixNtHeaderForDump(PIMAGE_NT_HEADERS oldNtHead, PIMAGE_NT_HEADERS newNtHead); +}; \ No newline at end of file diff --git a/Scylla/PeRebuild.cpp b/Scylla/PeRebuild.cpp new file mode 100644 index 0000000..09e4922 --- /dev/null +++ b/Scylla/PeRebuild.cpp @@ -0,0 +1,732 @@ +#include "PeRebuild.h" + +#include +#pragma comment(lib,"imagehlp.lib") + +#include "ProcessAccessHelp.h" + +#include "Logger.h" +#include "ConfigurationHolder.h" + +//#define DEBUG_COMMENTS + +bool PeRebuild::truncateFile(WCHAR * szFilePath, DWORD dwNewFsize) +{ + bool retValue = true; + HANDLE hFile = CreateFile(szFilePath,GENERIC_WRITE, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (hFile == INVALID_HANDLE_VALUE) + { + return false; + } + + if (SetFilePointer(hFile, dwNewFsize, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + { + if (GetLastError() == NO_ERROR) + { + retValue = true; + } + else + { + retValue = false; + } + } + else + { + retValue = true; + } + + if (!SetEndOfFile(hFile)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("SetEndOfFile failed error %d\r\n", GetLastError()); +#endif + retValue = false; + } + + CloseHandle(hFile); + + return retValue; +} + +DWORD PeRebuild::validAlignment(DWORD BadSize) +{ + div_t DivRes; + + DivRes = div(BadSize, FileAlignmentConstant); + if (DivRes.rem == 0) + return BadSize; + return ((DivRes.quot+1) * FileAlignmentConstant); +} + +DWORD PeRebuild::validAlignmentNew(DWORD badAddress) +{ + DWORD moduloResult = badAddress % FileAlignmentConstant; + + if (moduloResult) + { + return (FileAlignmentConstant - moduloResult); + } + else + { + return 0; + } +} + +bool PeRebuild::isRoundedTo(DWORD_PTR dwTarNum, DWORD_PTR dwRoundNum) +{ +#ifdef _WIN64 + lldiv_t d; + + d = div((__int64)dwTarNum, (__int64)dwRoundNum); +#else + ldiv_t d; + + d = div((long)dwTarNum, (long)dwRoundNum); +#endif + + return (d.rem == 0); +} + +void PeRebuild::cleanSectionPointer() +{ + if (pSections) + { + for (int j = 0; j < MAX_SEC_NUM; j++) + { + if (pSections[j]) + { + free(pSections[j]); + pSections[j] = 0; + } + } + } +} + +DWORD PeRebuild::realignPE(LPVOID AddressOfMapFile, DWORD dwFsize) +{ + PIMAGE_DOS_HEADER pDosh = 0; + PIMAGE_NT_HEADERS pPeh = 0; + PIMAGE_SECTION_HEADER pSectionh = 0; + int i = 0; + DWORD extraAlign = 0; + + ZeroMemory(&pSections, sizeof(pSections)); + + // get the other parameters + pMap = AddressOfMapFile; + dwMapBase = (DWORD_PTR)pMap; + + if (dwFsize == 0 || pMap == NULL) + return 1; + + // access the PE Header and check whether it's a valid one + pDosh = (PIMAGE_DOS_HEADER)(pMap); + pPeh = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosh+pDosh->e_lfanew); + + if (!validatePeHeaders(pDosh)) + { + return 0; + } + + if (pPeh->FileHeader.NumberOfSections > MAX_SEC_NUM) + { + return 3; + } + + __try + { + /* START */ + pPeh->OptionalHeader.FileAlignment = FileAlignmentConstant; + + /* Realign the PE Header */ + // get the size of all headers + dwTmpNum = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + pPeh->FileHeader.SizeOfOptionalHeader + (pPeh->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)); + + + // kill room between the "win32 pls" message and the PE signature + // find the end of the message + pW = (WORD*)(dwMapBase + ScanStartDS); + while (*pW != 0 || (!isRoundedTo((DWORD_PTR)pW, 0x10))) + { + pW = (WORD*)((DWORD_PTR)pW + 1); + } + + wTmpNum = (WORD)((DWORD_PTR)pW - dwMapBase); + if (wTmpNum < pDosh->e_lfanew) + { + CopyMemory((LPVOID)pW,(VOID*)pPeh,dwTmpNum); // copy the Header to the right place + pDosh->e_lfanew = wTmpNum; + } + + dwSectionBase = validAlignment(dwTmpNum + pDosh->e_lfanew); + pPeh = (PIMAGE_NT_HEADERS)(dwMapBase + pDosh->e_lfanew); // because the NT header moved + // correct the SizeOfHeaders + pPeh->OptionalHeader.SizeOfHeaders = dwSectionBase; + + /* Realign all sections */ + // make a copy of all sections + // this is needed if the sections aren't sorted by their RawOffset (e.g. Petite) + pSectionh = IMAGE_FIRST_SECTION(pPeh); + + for (i=0; iFileHeader.NumberOfSections; i++) + { + if (pSectionh->SizeOfRawData == 0 || pSectionh->PointerToRawData == 0) + { + ++pSectionh; + continue; + } + // get a valid size + dwTmpNum = pSectionh->SizeOfRawData; + if ((pSectionh->SizeOfRawData + pSectionh->PointerToRawData) > dwFsize) + { + dwTmpNum = dwFsize - pSectionh->PointerToRawData; + } + + //dwTmpNum -= 1; + + // copy the section into some memory + // limit max section size to 300 MB = 300000 KB = 300000000 B + if (dwTmpNum > 300000000) + { + dwTmpNum = 300000000; + } + + //because of validAlignment we need some extra space, max 0x200 extra + extraAlign = validAlignmentNew(dwTmpNum); + + pSections[i] = malloc(dwTmpNum + extraAlign); + ZeroMemory(pSections[i], dwTmpNum + extraAlign); + + if (pSections[i] == NULL) // fatal error !!! + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("realignPE :: malloc failed with dwTmpNum %08X %08X\r\n", dwTmpNum, extraAlign); +#endif + + cleanSectionPointer(); + + return 4; + } + CopyMemory(pSections[i],(LPVOID)(pSectionh->PointerToRawData+dwMapBase),dwTmpNum); + ++pSectionh; + } + + // start realigning the sections + pSectionh = IMAGE_FIRST_SECTION(pPeh); + + for (i=0;iFileHeader.NumberOfSections;i++) + { + // some anti crash code :P + if (pSectionh->SizeOfRawData == 0 || pSectionh->PointerToRawData == 0) + { + ++pSectionh; + if (pSectionh->PointerToRawData == 0) + { + continue; + } + pSectionh->PointerToRawData = dwSectionBase; + continue; + } + // let pCH point to the end of the current section + if ((pSectionh->PointerToRawData+pSectionh->SizeOfRawData) <= dwFsize) + { + pCH = (char*)(dwMapBase+pSectionh->PointerToRawData+pSectionh->SizeOfRawData-1); + } + else + { + pCH = (char*)(dwMapBase+dwFsize-1); + } + // look for the end of this section + while (*pCH == 0) + { + --pCH; + } + // calculate the new RawSize + dwTmpNum = (DWORD)(((DWORD_PTR)pCH - dwMapBase) + MinSectionTerm - pSectionh->PointerToRawData); + if (dwTmpNum < pSectionh->SizeOfRawData) + { + pSectionh->SizeOfRawData = dwTmpNum; + } + else // the new size is too BIG + { + dwTmpNum = pSectionh->SizeOfRawData; + } + // copy the section to the new place + if (i != pPeh->FileHeader.NumberOfSections-1) + { + dwTmpNum = validAlignment(dwTmpNum); + } + + CopyMemory((LPVOID)(dwMapBase+dwSectionBase), pSections[i], dwTmpNum); + // set the RawOffset + pSectionh->PointerToRawData = dwSectionBase; + // get the RawOffset for the next section + dwSectionBase = dwTmpNum+dwSectionBase; // the last section doesn't need to be aligned + // go to the next section + ++pSectionh; + } + + // delete bound import directories because it is destroyed if present + pPeh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; + pPeh->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; + + // clean up + cleanSectionPointer(); + } + __except(1) + { + // clean up + cleanSectionPointer(); + +#ifdef DEBUG_COMMENTS + Logger::debugLog("realignPE :: Exception occured\r\n"); +#endif + return 0; + } + + if (ConfigurationHolder::getConfigObject(UPDATE_HEADER_CHECKSUM)->isTrue()) + { + updatePeHeaderChecksum(AddressOfMapFile, dwSectionBase); + } + + return dwSectionBase; // return the new filesize +} + + +// returns: +// -1 - access violation +// -2 - no relocation found +// -3 - no own section +// -4 - dll characteristics found +// -5 - invalid PE file +// else the new raw size +DWORD PeRebuild::wipeReloc(void* pMap, DWORD dwFsize) +{ + PIMAGE_DOS_HEADER pDosH; + PIMAGE_NT_HEADERS pNTH; + PIMAGE_SECTION_HEADER pSecH; + PIMAGE_SECTION_HEADER pSH, pSH2; + DWORD dwRelocRVA, i; + BOOL bOwnSec = FALSE; + DWORD dwNewFsize; + + __try // =) + { + // get pe header pointers + pDosH = (PIMAGE_DOS_HEADER)pMap; + + if (pDosH->e_magic != IMAGE_DOS_SIGNATURE) + return -5; + + pNTH = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosH + pDosH->e_lfanew); + + if (pNTH->Signature != IMAGE_NT_SIGNATURE) + return -5; + + pSecH = IMAGE_FIRST_SECTION(pNTH); + + // has PE dll characteristics ? + if (pNTH->FileHeader.Characteristics & IMAGE_FILE_DLL) + return -4; + + // is there a reloc section ? + dwRelocRVA = pNTH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress; + + if (!dwRelocRVA) + return -2; + + // check whether the relocation has an own section + pSH = pSecH; + for (i=0; i < pNTH->FileHeader.NumberOfSections; i++) + { + if (pSH->VirtualAddress == dwRelocRVA) + { + bOwnSec = TRUE; + break; // pSH -> reloc section header and i == section index + } + ++pSH; + } + if (!bOwnSec) + return -3; + + if (i+1 == pNTH->FileHeader.NumberOfSections) + { + //--- relocation is the last section --- + // truncate at the start of the reloc section + dwNewFsize = pSH->PointerToRawData; + } + else + { + //--- relocation isn't the last section --- + dwNewFsize = dwFsize - pSH->SizeOfRawData; + + //-> copy the section(s) after the relocation to the start of the relocation + pSH2 = pSH; + ++pSH2; // pSH2 -> pointer to first section after relocation + memcpy( + (void*)(pSH->PointerToRawData + (DWORD)pMap), + (const void*)(pSH2->PointerToRawData + (DWORD)pMap), + dwFsize - pSH2->PointerToRawData); + + //-> fix the section headers + // (pSH -> reloc section header) + // (pSH2 -> first section after reloc section) + for (++i; i < pNTH->FileHeader.NumberOfSections; i++) + { + // apply important values + pSH->SizeOfRawData = pSH2->SizeOfRawData; + pSH->VirtualAddress = pSH2->VirtualAddress; + pSH->Misc.VirtualSize = pSH2->Misc.VirtualSize; + + // apply section name + memcpy( + (void*)(pSH->Name), + (const void*)(pSH2->Name), + sizeof(pSH2->Name)); + ++pSH; + ++pSH2; + } + } + + // dec section number + --pNTH->FileHeader.NumberOfSections; + + // kill reloc directory entry + pNTH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress = 0; + pNTH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size = 0; + + // fix virtual parts of the PE Header (a must for win2k) + pSH2 = pSH = pSecH; + ++pSH2; + for (i=0; i < (DWORD)pNTH->FileHeader.NumberOfSections-1; i++) + { + pSH->Misc.VirtualSize = pSH2->VirtualAddress - pSH->VirtualAddress; + ++pSH; + ++pSH2; + } + // (pSH -> pointer to last section) + if (pSH->Misc.PhysicalAddress) + pNTH->OptionalHeader.SizeOfImage = pSH->VirtualAddress + pSH->Misc.VirtualSize; + else // WATCOM is always a bit special >:-) + pNTH->OptionalHeader.SizeOfImage = pSH->VirtualAddress + pSH->SizeOfRawData; + } + __except(1) + { + // an access violation occurred :( + return -1; + } + + return dwNewFsize; +} + +bool PeRebuild::validatePE(void* pPEImage, DWORD dwFileSize) +{ + PIMAGE_NT_HEADERS pNTh; + PIMAGE_SECTION_HEADER pSech,pSH, pSH2, pLastSH; + UINT i; + DWORD dwHeaderSize; + + // get PE base information + pNTh = ImageNtHeader(pPEImage); + + if (!pNTh) + return FALSE; + + + pSech = IMAGE_FIRST_SECTION(pNTh); + + // FIX: + // ... the SizeOfHeaders + pSH = pSech; + dwHeaderSize = 0xFFFFFFFF; + for(i=0; i < pNTh->FileHeader.NumberOfSections; i++) + { + if (pSH->PointerToRawData && pSH->PointerToRawData < dwHeaderSize) + { + dwHeaderSize = pSH->PointerToRawData; + } + ++pSH; + } + pNTh->OptionalHeader.SizeOfHeaders = dwHeaderSize; + + // ...Virtual Sizes + pSH2 = pSH = pSech; + ++pSH2; + for (i=0; i < (DWORD)pNTh->FileHeader.NumberOfSections-1; i++) + { + pSH->Misc.VirtualSize = pSH2->VirtualAddress - pSH->VirtualAddress; + ++pSH; + ++pSH2; + } + + // (pSH -> pointer to last section) + pLastSH = pSH; + + // ...RawSize of the last section + pLastSH->SizeOfRawData = dwFileSize - pLastSH->PointerToRawData; + + // ...SizeOfImage + if (pLastSH->Misc.PhysicalAddress) + { + pNTh->OptionalHeader.SizeOfImage = pLastSH->VirtualAddress + pLastSH->Misc.VirtualSize; + } + else // WATCOM is always a bit special >:-) + { + pNTh->OptionalHeader.SizeOfImage = pLastSH->VirtualAddress + pLastSH->SizeOfRawData; + } + + return true; +} + +ReBaseErr PeRebuild::reBasePEImage(void* pPE, DWORD_PTR dwNewBase) +{ + PIMAGE_NT_HEADERS pNT; + PIMAGE_RELOCATION pR; + ReBaseErr ret; + DWORD_PTR dwDelta; + DWORD *pdwAddr, dwRva, dwType; + UINT iItems, i; + WORD *pW; + + // dwNewBase valid ? + if (dwNewBase & 0xFFFF) + { + ret = RB_INVALIDNEWBASE; + goto Exit; // ERR + } + + // + // get relocation dir ptr + // + pNT = ImageNtHeader(pPE); + if (!pNT) + { + ret = RB_INVALIDPE; + goto Exit; // ERR + } + // new base = old base ? + if (pNT->OptionalHeader.ImageBase == dwNewBase) + { + ret = RB_OK; + goto Exit; // OK + } + if (!pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) + { + ret = RB_NORELOCATIONINFO; + goto Exit; // ERR + } + + pR = (PIMAGE_RELOCATION)ImageRvaToVa( + pNT, + pPE, + pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress, + NULL); + + if (!pR) + { + ret = RB_INVALIDRVA; + goto Exit; // ERR + } + + // + // add delta to relocation items + // + dwDelta = dwNewBase - pNT->OptionalHeader.ImageBase; + __try + { + do + { + // get number of items + if (pR->SymbolTableIndex) + iItems = (pR->SymbolTableIndex - 8) / 2; + else + break; // no items in this block + + // trace/list block items... + pW = (WORD*)((DWORD_PTR)pR + 8); + + for (i = 0; i < iItems; i++) + { + dwRva = (*pW & 0xFFF) + pR->VirtualAddress; + dwType = *pW >> 12; + if (dwType != 0) // fully compatible ??? + { + // add delta + pdwAddr = (PDWORD)ImageRvaToVa( + pNT, + pPE, + dwRva, + NULL); + if (!pdwAddr) + { + ret = RB_INVALIDRVA; + goto Exit; // ERR + } + *pdwAddr += dwDelta; + } + // next item + ++pW; + } + + pR = (PIMAGE_RELOCATION)pW; // pR -> next block header + } while ( *(DWORD*)pW ); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + ret = RB_ACCESSVIOLATION; + goto Exit; // ERR + } + + // apply new base to header + pNT->OptionalHeader.ImageBase = dwNewBase; + + ret = RB_OK; // OK + +Exit: + return ret; +} + + +bool PeRebuild::updatePeHeaderChecksum(LPVOID AddressOfMapFile, DWORD dwFsize) +{ + PIMAGE_NT_HEADERS32 pNTHeader32 = 0; + PIMAGE_NT_HEADERS64 pNTHeader64 = 0; + DWORD headerSum = 0; + DWORD checkSum = 0; + + pNTHeader32 = (PIMAGE_NT_HEADERS32)CheckSumMappedFile(AddressOfMapFile, dwFsize, &headerSum, &checkSum); + + if (!pNTHeader32) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("updatePeHeaderChecksum :: CheckSumMappedFile failed error %X\r\n", GetLastError()); +#endif + return false; + } + +#ifdef DEBUG_COMMENTS + Logger::debugLog("Old checksum %08X new checksum %08X\r\n",headerSum,checkSum); +#endif + + if (pNTHeader32->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + pNTHeader64 = (PIMAGE_NT_HEADERS64)pNTHeader32; + pNTHeader64->OptionalHeader.CheckSum = checkSum; + } + else + { + pNTHeader32->OptionalHeader.CheckSum = checkSum; + } + + return true; +} + +LPVOID PeRebuild::createFileMappingViewFull(const WCHAR * filePath) +{ + hFileToMap = CreateFile(filePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if( hFileToMap == INVALID_HANDLE_VALUE ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("createFileMappingView :: INVALID_HANDLE_VALUE %u\r\n",GetLastError()); +#endif + hMappedFile = 0; + hFileToMap = 0; + addrMappedDll = 0; + return NULL; + } + + hMappedFile = CreateFileMapping(hFileToMap, 0, PAGE_READWRITE, 0, 0, NULL); + + if( hMappedFile == NULL ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("createFileMappingViewFull :: hMappedFile == NULL\r\n"); +#endif + CloseHandle(hFileToMap); + hMappedFile = 0; + hFileToMap = 0; + addrMappedDll = 0; + return NULL; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("createFileMappingView :: GetLastError() == ERROR_ALREADY_EXISTS\r\n"); +#endif + CloseHandle(hFileToMap); + hMappedFile = 0; + hFileToMap = 0; + addrMappedDll = 0; + return NULL; + } + + addrMappedDll = MapViewOfFile(hMappedFile, FILE_MAP_ALL_ACCESS, 0, 0, 0); + + if(!addrMappedDll) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("createFileMappingView :: addrMappedDll == NULL\r\n"); +#endif + CloseHandle(hFileToMap); + CloseHandle(hMappedFile); + hMappedFile = 0; + hFileToMap = 0; + return NULL; + } + + return addrMappedDll; +} + +void PeRebuild::closeAllMappingHandles() +{ + if (addrMappedDll) + { + if (!FlushViewOfFile(addrMappedDll, 0)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("closeAllMappingHandles :: Could not flush memory to disk (%d)\r\n", GetLastError()); +#endif + } + + UnmapViewOfFile(addrMappedDll); + addrMappedDll = 0; + } + if (hMappedFile) + { + CloseHandle(hMappedFile); + hMappedFile = 0; + } + if (hFileToMap) + { + CloseHandle(hFileToMap); + hFileToMap = 0; + } +} + +bool PeRebuild::validatePeHeaders( PIMAGE_DOS_HEADER pDosh ) +{ + PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDosh + pDosh->e_lfanew); + + if ((pDosh != 0) && (pDosh->e_magic == IMAGE_DOS_SIGNATURE) && (pNTHeader->Signature == IMAGE_NT_SIGNATURE)) + { +#ifdef _WIN64 + if (pNTHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) +#else + if (pNTHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) +#endif + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} diff --git a/Scylla/PeRebuild.h b/Scylla/PeRebuild.h new file mode 100644 index 0000000..a180cf4 --- /dev/null +++ b/Scylla/PeRebuild.h @@ -0,0 +1,62 @@ +#include +#include + +typedef enum _ReBaseErr +{ + RB_OK = 0, + RB_INVALIDPE, + RB_NORELOCATIONINFO, + RB_INVALIDRVA, + RB_INVALIDNEWBASE, + RB_ACCESSVIOLATION +} ReBaseErr; + +class PeRebuild { + +/***************************************************************************** + Improved Realign DLL version 1.5 by yoda +*****************************************************************************/ +public: + bool truncateFile(WCHAR * szFilePath, DWORD dwNewFsize); + DWORD realignPE(LPVOID AddressOfMapFile,DWORD dwFsize); + DWORD wipeReloc(void* pMap, DWORD dwFsize); + bool validatePE(void* pPEImage, DWORD dwFileSize); + ReBaseErr reBasePEImage(void* pPE, DWORD_PTR dwNewBase); + + bool updatePeHeaderChecksum(LPVOID AddressOfMapFile, DWORD dwFsize); + + LPVOID createFileMappingViewFull(const WCHAR * filePath); + void closeAllMappingHandles(); + + +private: + // constants +#define MAX_SEC_NUM 30 + + const static DWORD ScanStartDS = 0x40; + const static int MinSectionTerm = 5; + const static int FileAlignmentConstant = 0x200; + + // variables + DWORD_PTR dwMapBase; + LPVOID pMap; + DWORD dwTmpNum,dwSectionBase; + WORD wTmpNum; + CHAR * pCH; + WORD * pW; + DWORD * pDW; + LPVOID pSections[MAX_SEC_NUM]; + + //my vars + HANDLE hFileToMap; + HANDLE hMappedFile; + LPVOID addrMappedDll; + + + DWORD validAlignment(DWORD BadSize); + DWORD validAlignmentNew(DWORD badAddress); + bool isRoundedTo(DWORD_PTR dwTarNum, DWORD_PTR dwRoundNum); + + void cleanSectionPointer(); + bool validatePeHeaders( PIMAGE_DOS_HEADER pDosh ); +}; \ No newline at end of file diff --git a/Scylla/PickDllGui.cpp b/Scylla/PickDllGui.cpp new file mode 100644 index 0000000..1d749e4 --- /dev/null +++ b/Scylla/PickDllGui.cpp @@ -0,0 +1,131 @@ +#include "PickDllGui.h" + + +HWND PickDllGui::hWndDlg; +std::vector * PickDllGui::moduleList = 0; +ModuleInfo * PickDllGui::selectedModule = 0; + +INT_PTR PickDllGui::initDialog(HINSTANCE hInstance, HWND hWndParent, std::vector &moduleListNew) +{ + moduleList = &moduleListNew; + return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DLG_PICKDLL),hWndParent, (DLGPROC)pickDllDlgProc); +} + +LRESULT CALLBACK PickDllGui::pickDllDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int index; + hWndDlg = hWnd; + switch (uMsg) + { + case WM_INITDIALOG: + addColumnsToModuleList(GetDlgItem(hWnd, IDC_LIST_DLLSELECT)); + displayModuleList(); + break; + + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_BTN_PICKDLL_OK: + index = ListView_GetSelectionMark(GetDlgItem(hWnd, IDC_LIST_DLLSELECT)); + if (index != -1) + { + selectedModule = &(*moduleList).at(index); + EndDialog(hWnd, 1); + } + + return TRUE; + case IDC_BTN_PICKDLL_CANCEL: + EndDialog(hWnd, 0); + return TRUE; + case IDCANCEL: + EndDialog(hWnd, 0); + return TRUE; + } + } + return FALSE; +} + +void PickDllGui::addColumnsToModuleList(HWND hList) +{ + if (hList) + { + LVCOLUMN * lvc = (LVCOLUMN*)malloc(sizeof(LVCOLUMN)); + + ListView_SetExtendedListViewStyleEx(hList,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT); + + lvc->mask = LVCF_TEXT | LVCF_WIDTH; + lvc->cx = 210; + lvc->pszText = L"Path"; + ListView_InsertColumn(hList, COL_PATH, lvc); + + lvc->mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; + lvc->fmt = LVCFMT_CENTER; + lvc->cx = 130; + lvc->pszText = L"Name"; + ListView_InsertColumn(hList, COL_NAME, lvc); + + lvc->mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; + lvc->fmt = LVCFMT_CENTER; + lvc->cx = 70; + lvc->pszText = L"ImageBase"; + ListView_InsertColumn(hList, COL_IMAGEBASE, lvc); + + lvc->mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH; + lvc->fmt = LVCFMT_CENTER; + lvc->cx = 70; + lvc->pszText = L"ImageSize"; + ListView_InsertColumn(hList, COL_IMAGESIZE, lvc); + + free(lvc); + } +} + +void PickDllGui::getModuleListItem(int column, int iItem, WCHAR * buffer) +{ + LVITEM pitem = {0}; + pitem.iItem = iItem; + pitem.iSubItem = column; + pitem.mask = LVIF_TEXT; + pitem.cchTextMax = 256; + pitem.pszText = buffer; + ListView_GetItem(GetDlgItem(hWndDlg, IDC_LIST_DLLSELECT),&pitem); +} + +bool PickDllGui::displayModuleList() +{ + LVITEM item; + WCHAR temp[20]; + HWND hList = GetDlgItem(hWndDlg, IDC_LIST_DLLSELECT); + + ListView_DeleteAllItems(hList); + + item.mask = LVIF_TEXT; + + + + std::vector::iterator iter; + int count = 0; + + for( iter = (*moduleList).begin(); iter != (*moduleList).end(); iter++ , count++) { + item.iItem = count; + item.iSubItem = COL_PATH; + item.pszText = iter->fullPath; + item.iItem = ListView_InsertItem(hList, &item); + + item.iSubItem = COL_NAME; + item.pszText = iter->getFilename(); + ListView_SetItem(hList, &item); + + item.iSubItem = COL_IMAGEBASE; + swprintf_s(temp,_countof(temp),L"%08X",iter->modBaseAddr); + item.pszText = temp; + ListView_SetItem(hList, &item); + + item.iSubItem = COL_IMAGESIZE; + swprintf_s(temp,_countof(temp),L"%08X",iter->modBaseSize); + item.pszText = temp; + ListView_SetItem(hList, &item); + } + + return true; +} \ No newline at end of file diff --git a/Scylla/PickDllGui.h b/Scylla/PickDllGui.h new file mode 100644 index 0000000..d04f253 --- /dev/null +++ b/Scylla/PickDllGui.h @@ -0,0 +1,28 @@ +#pragma once + +#include "MainGui.h" + +static const enum ListColumns { + COL_PATH, + COL_NAME, + COL_IMAGEBASE, + COL_IMAGESIZE +}; + +class PickDllGui { +public: + static HWND hWndDlg; + + static std::vector * moduleList; + + static ModuleInfo * selectedModule; + + static LRESULT CALLBACK pickDllDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + static INT_PTR initDialog(HINSTANCE hInstance, HWND hWndParent, std::vector &moduleList); + + static void addColumnsToModuleList(HWND hList); + + static void getModuleListItem(int column, int iItem, WCHAR * buffer); + + static bool displayModuleList(); +}; \ No newline at end of file diff --git a/Scylla/PluginLoader.cpp b/Scylla/PluginLoader.cpp new file mode 100644 index 0000000..57ca43b --- /dev/null +++ b/Scylla/PluginLoader.cpp @@ -0,0 +1,258 @@ +#include "PluginLoader.h" +#include "Logger.h" + +#include "ProcessAccessHelp.h" + +std::vector PluginLoader::pluginList; +WCHAR PluginLoader::dirSearchString[MAX_PATH]; +WCHAR PluginLoader::baseDirPath[MAX_PATH]; + +//#define DEBUG_COMMENTS + +std::vector & PluginLoader::getPluginList() +{ + return pluginList; +} + +bool PluginLoader::findAllPlugins() +{ + WIN32_FIND_DATA ffd; + HANDLE hFind = 0; + DWORD dwError = 0; + Plugin pluginData; + + if (!pluginList.empty()) + { + pluginList.clear(); + } + + if (!buildSearchString()) + { + return false; + } + + hFind = FindFirstFile(dirSearchString, &ffd); + + dwError = GetLastError(); + + if (dwError == ERROR_FILE_NOT_FOUND) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("findAllPlugins :: No files found\r\n"); +#endif + return true; + } + + if (hFind == INVALID_HANDLE_VALUE) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("findAllPlugins :: FindFirstFile failed %d\r\n", dwError); +#endif + return false; + } + + do + { + if ( !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) + { + + if ((ffd.nFileSizeHigh != 0) || (ffd.nFileSizeLow < 200)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("findAllPlugins :: Plugin invalid file size: %s\r\n"), ffd.cFileName); +#endif + } + else + { + pluginData.fileSize = ffd.nFileSizeLow; + wcscpy_s(pluginData.fullpath, _countof(baseDirPath), baseDirPath); + wcscat_s(pluginData.fullpath, _countof(baseDirPath), ffd.cFileName); + +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"findAllPlugins :: Plugin %s\r\n",pluginData.fullpath); +#endif + if (isValidDllFile(pluginData.fullpath)) + { + if (getPluginName(&pluginData)) + { + //add valid plugin + pluginList.push_back(pluginData); + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("Cannot get plugin name %s\r\n"),pluginData.fullpath); +#endif + } + } + + } + + } + } + while (FindNextFile(hFind, &ffd) != 0); + + dwError = GetLastError(); + + if (dwError == ERROR_NO_MORE_FILES) + { + return true; + } + else + { + return false; + } +} + +bool PluginLoader::getPluginName(Plugin * pluginData) +{ + bool retValue = false; + char * pluginName = 0; + size_t convertedChars = 0; + def_ScyllaPluginNameW ScyllaPluginNameW = 0; + def_ScyllaPluginNameA ScyllaPluginNameA = 0; + + HMODULE hModule = LoadLibraryEx(pluginData->fullpath, 0, DONT_RESOLVE_DLL_REFERENCES); //do not call DllMain + + if (hModule) + { + ScyllaPluginNameW = (def_ScyllaPluginNameW)GetProcAddress(hModule, "ScyllaPluginNameW"); + + if (ScyllaPluginNameW) + { + wcscpy_s(pluginData->pluginName, MAX_PATH, ScyllaPluginNameW()); + +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"getPluginName :: Plugin name %s\r\n", pluginData->pluginName); +#endif + + retValue = true; + } + else + { + ScyllaPluginNameA = (def_ScyllaPluginNameA)GetProcAddress(hModule, "ScyllaPluginNameA"); + + if (ScyllaPluginNameA) + { + pluginName = ScyllaPluginNameA(); + + mbstowcs_s(&convertedChars, pluginData->pluginName, strlen(pluginName) + 1, pluginName, _TRUNCATE); + +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"getPluginName :: Plugin name mbstowcs_s %s\r\n", pluginData->pluginName); +#endif + + if (convertedChars > 1) + { + retValue = true; + } + else + { + retValue = false; + } + } + else + { + retValue = false; + } + } + + FreeLibrary(hModule); + + return retValue; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"getPluginName :: LoadLibraryEx failed %s\r\n", pluginData->fullpath); +#endif + return false; + } +} + +bool PluginLoader::buildSearchString() +{ + ZeroMemory(dirSearchString, sizeof(dirSearchString)); + ZeroMemory(baseDirPath, sizeof(baseDirPath)); + + if (!GetModuleFileName(0, dirSearchString, _countof(dirSearchString))) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog("buildSearchString :: GetModuleFileName failed %d\r\n",GetLastError()); +#endif + return false; + } + + //wprintf(L"dirSearchString 1 %s\n\n", dirSearchString); + + + //remove exe file name + for (size_t i = wcslen(dirSearchString) - 1; i >= 0; i--) + { + if (dirSearchString[i] == L'\\') + { + dirSearchString[i + 1] = 0; + break; + } + } + + //wprintf(L"dirSearchString 2 %s\n\n", dirSearchString); + + wcscat_s(dirSearchString, _countof(dirSearchString), TEXT(PLUGIN_DIR)TEXT("\\") ); + + wcscpy_s(baseDirPath, _countof(baseDirPath), dirSearchString); + + wcscat_s(dirSearchString, _countof(dirSearchString), TEXT(PLUGIN_SEARCH_STRING) ); + + //wprintf(L"dirSearchString 3 %s\n\n", dirSearchString); + +#ifdef DEBUG_COMMENTS + Logger::debugLog(L"dirSearchString final %s\r\n", dirSearchString); +#endif + + + return true; +} + +bool PluginLoader::isValidDllFile( const WCHAR * fullpath ) +{ + BYTE * data = 0; + DWORD lpNumberOfBytesRead = 0; + PIMAGE_DOS_HEADER pDos = 0; + PIMAGE_NT_HEADERS pNT = 0; + bool retValue = false; + + HANDLE hFile = CreateFile(fullpath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (hFile != INVALID_HANDLE_VALUE) + { + data = new BYTE[sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) + 0x100]; + + if (ReadFile(hFile, data, sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) + 0x100, &lpNumberOfBytesRead, 0)) + { + pDos = (PIMAGE_DOS_HEADER)data; + + if (pDos->e_magic == IMAGE_DOS_SIGNATURE) + { + pNT = (PIMAGE_NT_HEADERS)((DWORD_PTR)pDos + pDos->e_lfanew); + + if (pNT->Signature == IMAGE_NT_SIGNATURE) + { +#ifdef _WIN64 + if (pNT->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) +#else + if (pNT->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) +#endif + { + retValue = true; + } + } + } + } + + delete [] data; + CloseHandle(hFile); + } + + return retValue; +} diff --git a/Scylla/PluginLoader.h b/Scylla/PluginLoader.h new file mode 100644 index 0000000..27c3f0b --- /dev/null +++ b/Scylla/PluginLoader.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include + +class Plugin { +public: + DWORD fileSize; + WCHAR fullpath[MAX_PATH]; + WCHAR pluginName[MAX_PATH]; +}; + +#define PLUGIN_DIR "Plugins" +#define PLUGIN_SEARCH_STRING "*.dll" + +typedef wchar_t * (__cdecl * def_ScyllaPluginNameW)(); +typedef char * (__cdecl * def_ScyllaPluginNameA)(); + +class PluginLoader { +public: + static bool findAllPlugins(); + + static std::vector & getPluginList(); + +private: + static std::vector pluginList; + + static WCHAR dirSearchString[MAX_PATH]; + static WCHAR baseDirPath[MAX_PATH]; + + static bool buildSearchString(); + static bool getPluginName(Plugin * pluginData); + static bool isValidDllFile( const WCHAR * fullpath ); +}; \ No newline at end of file diff --git a/Scylla/ProcessAccessHelp.cpp b/Scylla/ProcessAccessHelp.cpp new file mode 100644 index 0000000..06d08e3 --- /dev/null +++ b/Scylla/ProcessAccessHelp.cpp @@ -0,0 +1,775 @@ + +#include "ProcessAccessHelp.h" + +#include "Logger.h" +#include "NativeWinApi.h" + +HANDLE ProcessAccessHelp::hProcess = 0; + +ModuleInfo * ProcessAccessHelp::selectedModule; +DWORD_PTR ProcessAccessHelp::targetImageBase = 0; +DWORD_PTR ProcessAccessHelp::targetSizeOfImage = 0; +DWORD_PTR ProcessAccessHelp::maxValidAddress = 0; + +std::vector ProcessAccessHelp::moduleList; //target process module list +std::vector ProcessAccessHelp::ownModuleList; //own module list + + +_DInst ProcessAccessHelp::decomposerResult[MAX_INSTRUCTIONS]; + +_CodeInfo ProcessAccessHelp::decomposerCi = {0}; + +_DecodedInst ProcessAccessHelp::decodedInstructions[MAX_INSTRUCTIONS]; +unsigned int ProcessAccessHelp::decodedInstructionsCount = 0; + +BYTE ProcessAccessHelp::fileHeaderFromDisk[PE_HEADER_BYTES_COUNT]; + +//#define DEBUG_COMMENTS + +bool ProcessAccessHelp::openProcessHandle(DWORD dwPID) +{ + if (dwPID > 0) + { + if (hProcess) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("openProcessHandle :: There is already a process handle, HANDLE %X\r\n"),hProcess); +#endif + return false; + } + else + { + //hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_VM_WRITE, 0, dwPID); + //if (!NT_SUCCESS(NativeWinApi::NtOpenProcess(&hProcess,PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_VM_WRITE,&ObjectAttributes, &cid))) + + hProcess = NativeOpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_VM_WRITE, dwPID); + + if (hProcess) + { + return true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("openProcessHandle :: Failed to open handle, PID %X\r\n"),dwPID); +#endif + return false; + } + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("openProcessHandle :: Wrong PID, PID %X \r\n"),dwPID); +#endif + return false; + } + +} + +HANDLE ProcessAccessHelp::NativeOpenProcess(DWORD dwDesiredAccess, DWORD dwProcessId) +{ + HANDLE hProcess = 0; + CLIENT_ID cid = {0}; + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS ntStatus = 0; + + InitializeObjectAttributes(&ObjectAttributes, 0, 0, 0, 0); + cid.UniqueProcess = (HANDLE)dwProcessId; + + ntStatus = NativeWinApi::NtOpenProcess(&hProcess,dwDesiredAccess,&ObjectAttributes, &cid); + + if (NT_SUCCESS(ntStatus)) + { + return hProcess; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("NativeOpenProcess :: Failed to open handle, PID %X Error 0x%X\r\n"),dwProcessId, NativeWinApi::RtlNtStatusToDosError(ntStatus)); +#endif + return 0; + } +} + +void ProcessAccessHelp::closeProcessHandle() +{ + CloseHandle(hProcess); + hProcess = 0; + moduleList.clear(); + targetImageBase = 0; + selectedModule = 0; +} + +bool ProcessAccessHelp::readMemoryFromProcess(DWORD_PTR address, SIZE_T size, LPVOID dataBuffer) +{ + SIZE_T lpNumberOfBytesRead = 0; + DWORD dwProtect = 0; + bool returnValue = false; + + if (!hProcess) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readMemoryFromProcess :: hProcess == NULL\r\n")); +#endif + return returnValue; + } + + if (!ReadProcessMemory(hProcess, (LPVOID)address, dataBuffer, size, &lpNumberOfBytesRead)) + { + if (!VirtualProtectEx(hProcess, (LPVOID)address, size, PAGE_READWRITE, &dwProtect)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readMemoryFromProcess :: Error VirtualProtectEx %X %X err: %u\r\n"),address,size,GetLastError()); +#endif + returnValue = false; + } + else + { + if (!ReadProcessMemory(hProcess, (LPVOID)address, dataBuffer, size, &lpNumberOfBytesRead)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readMemoryFromProcess :: Error ReadProcessMemory %X %X err: %u\r\n"),address,size,GetLastError()); +#endif + returnValue = false; + } + else + { + returnValue = true; + } + VirtualProtectEx(hProcess, (LPVOID)address, size, dwProtect, &dwProtect); + } + } + else + { + returnValue = true; + } + + if (returnValue) + { + if (size != lpNumberOfBytesRead) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readMemoryFromProcess :: Error ReadProcessMemory read %d bytes requested %d bytes\r\n"), lpNumberOfBytesRead, size); +#endif + returnValue = false; + } + else + { + returnValue = true; + } + } + + return returnValue; +} + +bool ProcessAccessHelp::decomposeMemory(BYTE * dataBuffer, SIZE_T bufferSize, DWORD_PTR startAddress) +{ + + ZeroMemory(&decomposerCi, sizeof(_CodeInfo)); + decomposerCi.code = dataBuffer; + decomposerCi.codeLen = (int)bufferSize; + decomposerCi.dt = dt; + decomposerCi.codeOffset = startAddress; + + decomposerInstructionsCount = 0; + + if (distorm_decompose(&decomposerCi, decomposerResult, sizeof(decomposerResult)/sizeof(decomposerResult[0]), &decomposerInstructionsCount) == DECRES_INPUTERR) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("decomposeMemory :: distorm_decompose == DECRES_INPUTERR\r\n")); +#endif + return false; + } + else + { + return true; + } +} + +bool ProcessAccessHelp::disassembleMemory(BYTE * dataBuffer, SIZE_T bufferSize, DWORD_PTR startOffset) +{ + // Holds the result of the decoding. + _DecodeResult res; + + // next is used for instruction's offset synchronization. + // decodedInstructionsCount holds the count of filled instructions' array by the decoder. + + decodedInstructionsCount = 0; + + _OffsetType offset = startOffset; + + res = distorm_decode(offset, dataBuffer, (int)bufferSize, dt, decodedInstructions, MAX_INSTRUCTIONS, &decodedInstructionsCount); + +/* for (unsigned int i = 0; i < decodedInstructionsCount; i++) { +#ifdef SUPPORT_64BIT_OFFSET + printf("%0*I64x (%02d) %-24s %s%s%s\n", dt != Decode64Bits ? 8 : 16, decodedInstructions[i].offset, decodedInstructions[i].size, (char*)decodedInstructions[i].instructionHex.p, (char*)decodedInstructions[i].mnemonic.p, decodedInstructions[i].operands.length != 0 ? " " : "", (char*)decodedInstructions[i].operands.p); +#else + printf("%08x (%02d) %-24s %s%s%s\n", decodedInstructions[i].offset, decodedInstructions[i].size, (char*)decodedInstructions[i].instructionHex.p, (char*)decodedInstructions[i].mnemonic.p, decodedInstructions[i].operands.length != 0 ? " " : "", (char*)decodedInstructions[i].operands.p); +#endif + + }*/ + + if (res == DECRES_INPUTERR) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("disassembleMemory :: res == DECRES_INPUTERR\r\n")); +#endif + return false; + } + else if (res == DECRES_SUCCESS) + { + //printf("disassembleMemory :: res == DECRES_SUCCESS\n"); + return true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("disassembleMemory :: res == %d\r\n"),res); +#endif + return false; + } +} + +DWORD_PTR ProcessAccessHelp::findPattern(DWORD_PTR startOffset, DWORD size, BYTE * pattern, const char * mask) +{ + DWORD pos = 0; + size_t searchLen = strlen(mask) - 1; + + for(DWORD_PTR retAddress = startOffset; retAddress < startOffset + size; retAddress++) + { + if( *(BYTE*)retAddress == pattern[pos] || mask[pos] == '?' ) + { + if(mask[pos+1] == 0x00) + { + return (retAddress - searchLen); + } + pos++; + } else { + pos = 0; + } + } + return 0; +} + +bool ProcessAccessHelp::readHeaderFromCurrentFile(const WCHAR * filePath) +{ + return readHeaderFromFile(fileHeaderFromDisk, sizeof(fileHeaderFromDisk), filePath); +} + +LONGLONG ProcessAccessHelp::getFileSize(const WCHAR * filePath) +{ + LONGLONG fileSize = 0; + + HANDLE hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if (hFile != INVALID_HANDLE_VALUE) + { + fileSize = getFileSize(hFile); + CloseHandle(hFile); + hFile = 0; + } + + return fileSize; +} + +LONGLONG ProcessAccessHelp::getFileSize(HANDLE hFile) +{ + LARGE_INTEGER lpFileSize = {0}; + + if ((hFile != INVALID_HANDLE_VALUE) && (hFile != 0)) + { + if (!GetFileSizeEx(hFile, &lpFileSize)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("ProcessAccessHelp::getFileSize :: GetFileSizeEx failed %u\r\n"),GetLastError()); +#endif + return 0; + } + else + { + return lpFileSize.QuadPart; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("ProcessAccessHelp::getFileSize hFile invalid\r\n")); +#endif + return 0; + } +} + + +bool ProcessAccessHelp::readMemoryFromFile(HANDLE hFile, LONG offset, DWORD size, LPVOID dataBuffer) +{ + DWORD lpNumberOfBytesRead = 0; + DWORD retValue = 0; + DWORD dwError = 0; + + if ((hFile != INVALID_HANDLE_VALUE) && (hFile != 0)) + { + retValue = SetFilePointer(hFile, offset, NULL, FILE_BEGIN); + dwError = GetLastError(); + + if ((retValue == INVALID_SET_FILE_POINTER) && (dwError != NO_ERROR)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readMemoryFromFile :: SetFilePointer failed error %u\r\n"),dwError); +#endif + return false; + } + else + { + if (ReadFile(hFile, dataBuffer, size, &lpNumberOfBytesRead, 0)) + { + return true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readMemoryFromFile :: ReadFile failed - size %d - error %u\r\n"),size,GetLastError()); +#endif + return false; + } + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readMemoryFromFile :: hFile invalid\r\n")); +#endif + return false; + } +} + +bool ProcessAccessHelp::writeMemoryToFile(HANDLE hFile, LONG offset, DWORD size, LPVOID dataBuffer) +{ + DWORD lpNumberOfBytesWritten = 0; + DWORD retValue = 0; + DWORD dwError = 0; + + if ((hFile != INVALID_HANDLE_VALUE) && (hFile != 0)) + { + retValue = SetFilePointer(hFile, offset, NULL, FILE_BEGIN); + dwError = GetLastError(); + + if ((retValue == INVALID_SET_FILE_POINTER) && (dwError != NO_ERROR)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("writeMemoryToFile :: SetFilePointer failed error %u\r\n"),dwError); +#endif + return false; + } + else + { + if (WriteFile(hFile, dataBuffer, size, &lpNumberOfBytesWritten, 0)) + { + return true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("writeMemoryToFile :: WriteFile failed - size %d - error %u\r\n"),size,GetLastError()); +#endif + return false; + } + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("writeMemoryToFile :: hFile invalid\r\n")); +#endif + return false; + } +} + +bool ProcessAccessHelp::writeMemoryToFileEnd(HANDLE hFile, DWORD size, LPVOID dataBuffer) +{ + DWORD lpNumberOfBytesWritten = 0; + DWORD retValue = 0; + + if ((hFile != INVALID_HANDLE_VALUE) && (hFile != 0)) + { + SetFilePointer(hFile, 0, 0, FILE_END); + + if (WriteFile(hFile, dataBuffer, size, &lpNumberOfBytesWritten, 0)) + { + return true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("writeMemoryToFileEnd :: WriteFile failed - size %d - error %u\r\n"),size,GetLastError()); +#endif + return false; + } + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("writeMemoryToFileEnd :: hFile invalid\r\n")); +#endif + return false; + } +} + +bool ProcessAccessHelp::readHeaderFromFile(BYTE * buffer, DWORD bufferSize, const WCHAR * filePath) +{ + DWORD lpNumberOfBytesRead = 0; + LONGLONG fileSize = 0; + DWORD dwSize = 0; + bool returnValue = 0; + + HANDLE hFile = CreateFile(filePath, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if( hFile == INVALID_HANDLE_VALUE ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("readHeaderFromFile :: INVALID_HANDLE_VALUE %u\r\n"),GetLastError()); +#endif + returnValue = false; + } + else + { + fileSize = getFileSize(hFile); + + if (fileSize > 0) + { + if (fileSize > bufferSize) + { + dwSize = bufferSize; + } + else + { + dwSize = (DWORD)(fileSize - 1); + } + + returnValue = readMemoryFromFile(hFile, 0, dwSize, buffer); + } + + CloseHandle(hFile); + } + + return returnValue; +} + +LPVOID ProcessAccessHelp::createFileMappingViewRead(const WCHAR * filePath) +{ + return createFileMappingView(filePath, GENERIC_READ, PAGE_READONLY | SEC_IMAGE, FILE_MAP_READ); +} + +LPVOID ProcessAccessHelp::createFileMappingViewFull(const WCHAR * filePath) +{ + return createFileMappingView(filePath, GENERIC_ALL, PAGE_EXECUTE_READWRITE, FILE_MAP_ALL_ACCESS); +} + +LPVOID ProcessAccessHelp::createFileMappingView(const WCHAR * filePath, DWORD accessFile, DWORD flProtect, DWORD accessMap) +{ + HANDLE hFile = CreateFile(filePath, accessFile, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + + if( hFile == INVALID_HANDLE_VALUE ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("createFileMappingView :: INVALID_HANDLE_VALUE %u\r\n"),GetLastError()); +#endif + return NULL; + } + + HANDLE hMappedFile = CreateFileMapping(hFile, NULL, flProtect, 0, 0, NULL); + CloseHandle(hFile); + + if( hMappedFile == NULL ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("createFileMappingView :: hMappedFile == NULL\r\n")); +#endif + return NULL; + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("createFileMappingView :: GetLastError() == ERROR_ALREADY_EXISTS\r\n")); +#endif + return NULL; + } + + LPVOID addrMappedDll = MapViewOfFile(hMappedFile, accessMap, 0, 0, 0); + + if( addrMappedDll == NULL ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("createFileMappingView :: addrMappedDll == NULL\r\n")); +#endif + CloseHandle(hMappedFile); + return NULL; + } + + CloseHandle(hMappedFile); + + return addrMappedDll; +} + +DWORD ProcessAccessHelp::getProcessByName(const WCHAR * processName) +{ + DWORD dwPID = 0; + HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32W pe32; + pe32.dwSize = sizeof(PROCESSENTRY32W); + + if( !Process32FirstW( hProcessSnap, &pe32 ) ) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("getProcessByName :: Error getting first Process\r\n")); +#endif + CloseHandle( hProcessSnap ); + return 0; + } + + do + { + if(!_wcsicmp(pe32.szExeFile, processName)) + { + dwPID = pe32.th32ProcessID; + break; + } + } while(Process32NextW(hProcessSnap, &pe32)); + + CloseHandle(hProcessSnap); + + return dwPID; +} + +bool ProcessAccessHelp::getProcessModules(DWORD dwPID, std::vector &moduleList) +{ + HANDLE hModuleSnap = INVALID_HANDLE_VALUE; + MODULEENTRY32 me32; + ModuleInfo module; + + // Take a snapshot of all modules in the specified process. + hModuleSnap = CreateToolhelp32Snapshot( TH32CS_SNAPMODULE, dwPID ); + if( hModuleSnap == INVALID_HANDLE_VALUE ) + { + return false; + } + + // Set the size of the structure before using it. + me32.dwSize = sizeof( MODULEENTRY32 ); + + // Retrieve information about the first module, + // and exit if unsuccessful + if( !Module32First( hModuleSnap, &me32 ) ) + { + CloseHandle( hModuleSnap ); + return false; + } + + // Now walk the module list of the process, + // and display information about each module + + //the first is always the .exe + if (!Module32Next(hModuleSnap, &me32)) + { + CloseHandle( hModuleSnap ); + return false; + } + + moduleList.reserve(20); + + do + { + //printf(TEXT("\n MODULE NAME: %s"), me32.szModule); + module.modBaseAddr = (DWORD_PTR)me32.modBaseAddr; + module.modBaseSize = me32.modBaseSize; + module.isAlreadyParsed = false; + module.parsing = false; + wcscpy_s(module.fullPath, MAX_PATH, me32.szExePath); + + moduleList.push_back(module); + + } while(Module32Next(hModuleSnap, &me32)); + + CloseHandle( hModuleSnap ); + return true; +} + +bool ProcessAccessHelp::getMemoryRegionFromAddress(DWORD_PTR address, DWORD_PTR * memoryRegionBase, SIZE_T * memoryRegionSize) +{ + MEMORY_BASIC_INFORMATION memBasic; + + if (VirtualQueryEx(hProcess,(LPCVOID)address,&memBasic,sizeof(MEMORY_BASIC_INFORMATION)) != sizeof(MEMORY_BASIC_INFORMATION)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("getMemoryRegionFromAddress :: VirtualQueryEx error %u\r\n"), GetLastError()); +#endif + return false; + } + else + { + *memoryRegionBase = (DWORD_PTR)memBasic.BaseAddress; + *memoryRegionSize = memBasic.RegionSize; + return true; + } +} + +bool ProcessAccessHelp::getSizeOfImageCurrentProcess() +{ + DWORD_PTR newSizeOfImage = getSizeOfImageProcess(ProcessAccessHelp::hProcess, ProcessAccessHelp::targetImageBase); + + if (newSizeOfImage != 0) + { + ProcessAccessHelp::targetSizeOfImage = newSizeOfImage; + return true; + } + else + { + return false; + } +} + +SIZE_T ProcessAccessHelp::getSizeOfImageProcess(HANDLE processHandle, DWORD_PTR moduleBase) +{ + SIZE_T sizeOfImage = 0; + MEMORY_BASIC_INFORMATION lpBuffer = {0}; + SIZE_T dwLength = sizeof(MEMORY_BASIC_INFORMATION); + + do + { + moduleBase = (DWORD_PTR)((SIZE_T)moduleBase + lpBuffer.RegionSize); + sizeOfImage += lpBuffer.RegionSize; + + //printf("Query 0x"PRINTF_DWORD_PTR_FULL" size 0x%08X\n",moduleBase,sizeOfImage); + + if (!VirtualQueryEx(processHandle, (LPCVOID)moduleBase, &lpBuffer, dwLength)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("getSizeOfImageProcess :: VirtualQuery failed %X\r\n"),GetLastError()); +#endif + lpBuffer.Type = 0; + sizeOfImage = 0; + } + /*else + { + printf("\nAllocationBase %X\n",lpBuffer.AllocationBase); + printf("AllocationProtect %X\n",lpBuffer.AllocationProtect); + printf("BaseAddress %X\n",lpBuffer.BaseAddress); + printf("Protect %X\n",lpBuffer.Protect); + printf("RegionSize %X\n",lpBuffer.RegionSize); + printf("State %X\n",lpBuffer.State); + printf("Type %X\n",lpBuffer.Type); + }*/ + } while (lpBuffer.Type == MEM_IMAGE); + + //printf("Real sizeOfImage %X\n",sizeOfImage); + + return sizeOfImage; +} + +//OFN_FILEMUSTEXIST +WCHAR * ProcessAccessHelp::selectFileToSave(DWORD flags, int type) +{ + OPENFILENAME ofn = {0}; + WCHAR * targetFile = new WCHAR[MAX_PATH]; + targetFile[0] = 0; + + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = 0; + + switch (type) + { + case 0: + { + ofn.lpstrFilter = TEXT("Dynamic Link Library (*.dll)\0*.dll\00"); + ofn.lpstrDefExt = TEXT(".dll"); + } + break; + case 1: + { + ofn.lpstrFilter = TEXT("Executable (*.exe)\0*.exe\00"); + ofn.lpstrDefExt = TEXT(".exe"); + } + break; + default: + { + ofn.lpstrFilter = TEXT("Executable (*.exe)\0*.exe\0Dynamic Link Library (*.dll)\0*.dll\00"); + ofn.lpstrDefExt = 0; + } + } + + ofn.lpstrCustomFilter = 0; + ofn.nFilterIndex = 1; + ofn.lpstrFile = targetFile; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrFileTitle = 0; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = 0; + ofn.lpstrTitle = TEXT("Select a file"); + ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_ENABLESIZING | flags; + + if(GetOpenFileName(&ofn)) { + Logger::printfDialog(TEXT("Selected %s"),targetFile); + return targetFile; + } else { + delete [] targetFile; + +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("selectFileToSave :: CommDlgExtendedError 0x%X\r\n"), CommDlgExtendedError()); +#endif + return 0; + } +} + +DWORD ProcessAccessHelp::getEntryPointFromFile(const WCHAR * filePath) +{ + PIMAGE_NT_HEADERS pNtHeader = 0; + PIMAGE_DOS_HEADER pDosHeader = 0; + + readHeaderFromCurrentFile(filePath); + + pDosHeader = (PIMAGE_DOS_HEADER)fileHeaderFromDisk; + + if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) + { + return 0; + } + + pNtHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)fileHeaderFromDisk + (DWORD_PTR)(pDosHeader->e_lfanew)); + + if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) + { + return 0; + } + + return pNtHeader->OptionalHeader.AddressOfEntryPoint; +} + +bool ProcessAccessHelp::createBackupFile(const WCHAR * filePath) +{ + size_t fileNameLength = wcslen(filePath) + (5 * sizeof(WCHAR)); //.bak + null + BOOL retValue = 0; + WCHAR * backupFile = new WCHAR[fileNameLength]; + + if (backupFile) + { + wcscpy_s(backupFile, fileNameLength, filePath); + wcscat_s(backupFile, fileNameLength, TEXT(".bak")); + retValue = CopyFile(filePath, backupFile, FALSE); + + if (!retValue) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("createBackupFile :: CopyFile failed with error 0x%X\r\n"), GetLastError()); +#endif + } + + delete [] backupFile; + + return retValue != 0; + } + else + { + return false; + } +} \ No newline at end of file diff --git a/Scylla/ProcessAccessHelp.h b/Scylla/ProcessAccessHelp.h new file mode 100644 index 0000000..81ee0c3 --- /dev/null +++ b/Scylla/ProcessAccessHelp.h @@ -0,0 +1,215 @@ +#pragma once + +#include +#include +#include +#include +#include + + +/************************************************************************/ +/* distorm */ +/************************************************************************/ +#include "distorm.h" + +#ifdef _WIN64 +#pragma comment(lib, "distorm_x64.lib") +#else +#pragma comment(lib, "distorm_x86.lib") +#endif + +// The number of the array of instructions the decoder function will use to return the disassembled instructions. +// Play with this value for performance... +#define MAX_INSTRUCTIONS (200) + +/************************************************************************/ + +class ApiInfo; + +class ModuleInfo { +public: + WCHAR fullPath[MAX_PATH]; + DWORD_PTR modBaseAddr; + DWORD modBaseSize; + + bool isAlreadyParsed; + bool parsing; + + /* + for iat rebuilding with duplicate entries: + + ntdll = low priority + kernelbase = low priority + SHLWAPI = low priority + + kernel32 = high priority + + priority = 1 -> normal/high priority + priority = 0 -> low priority + */ + int priority; + + std::vector apiList; + + ModuleInfo() + { + modBaseAddr = 0; + modBaseSize = 0; + priority = 1; + isAlreadyParsed = false; + parsing = false; + } + + WCHAR * getFilename() + { + for (size_t i = wcslen(fullPath) - 1; i >= 0; i--) + { + if (fullPath[i] == L'\\') + { + return (&fullPath[i] + 1); + } + } + return fullPath; + } +}; + +class ApiInfo { + public: + char name[MAX_PATH]; + DWORD hint; + DWORD_PTR va; + DWORD_PTR rva; + DWORD_PTR ordinal; + bool isForwarded; + ModuleInfo * module; +}; + +class ProcessAccessHelp { +public: + static HANDLE hProcess; //OpenProcess handle to target process + + static DWORD_PTR targetImageBase; + static DWORD_PTR targetSizeOfImage; + static DWORD_PTR maxValidAddress; + + static ModuleInfo * selectedModule; + + static std::vector moduleList; //target process module list + static std::vector ownModuleList; //own module list + + static const int PE_HEADER_BYTES_COUNT = 2000; + + static BYTE fileHeaderFromDisk[PE_HEADER_BYTES_COUNT]; + + + //for decomposer + static _DInst decomposerResult[MAX_INSTRUCTIONS]; + static unsigned int decomposerInstructionsCount; + static _CodeInfo decomposerCi; + + //distorm :: Decoded instruction information. + static _DecodedInst decodedInstructions[MAX_INSTRUCTIONS]; + static unsigned int decodedInstructionsCount; +#ifdef _WIN64 + static const _DecodeType dt = Decode64Bits; +#else + static const _DecodeType dt = Decode32Bits; +#endif + + /* + * Open a new process handle + */ + static bool openProcessHandle(DWORD dwPID); + + static HANDLE NativeOpenProcess(DWORD dwDesiredAccess, DWORD dwProcessId); + + static void closeProcessHandle(); + + /* + * Get all modules from a process + */ + static bool getProcessModules(DWORD dwPID, std::vector &moduleList); + + + /* + * file mapping view with different access level + */ + static LPVOID createFileMappingViewRead(const WCHAR * filePath); + static LPVOID createFileMappingViewFull(const WCHAR * filePath); + + /* + * Create a file mapping view of a file + */ + static LPVOID createFileMappingView(const WCHAR * filePath, DWORD accessFile, DWORD flProtect, DWORD accessMap); + + /* + * Read memory from target process + */ + static bool readMemoryFromProcess(DWORD_PTR address, SIZE_T size, LPVOID dataBuffer); + + /* + * Read memory from file + */ + static bool readMemoryFromFile(HANDLE hFile, LONG offset, DWORD size, LPVOID dataBuffer); + + /* + * Write memory to file + */ + static bool writeMemoryToFile(HANDLE hFile, LONG offset, DWORD size, LPVOID dataBuffer); + + /* + * Write memory to file end + */ + static bool writeMemoryToFileEnd(HANDLE hFile, DWORD size, LPVOID dataBuffer); + + /* + * Disassemble Memory + */ + static bool disassembleMemory(BYTE * dataBuffer, SIZE_T bufferSize, DWORD_PTR startOffset); + + static bool decomposeMemory(BYTE * dataBuffer, SIZE_T bufferSize, DWORD_PTR startAddress); + + /* + * Search for pattern + */ + static DWORD_PTR findPattern(DWORD_PTR startOffset, DWORD size, BYTE * pattern, const char * mask); + + /* + * Get process ID by process name + */ + static DWORD getProcessByName(const WCHAR * processName); + + /* + * Get memory region from address + */ + bool getMemoryRegionFromAddress(DWORD_PTR address, DWORD_PTR * memoryRegionBase, SIZE_T * memoryRegionSize); + + + /* + * Read PE Header from file + */ + static bool readHeaderFromFile(BYTE * buffer, DWORD bufferSize, const WCHAR * filePath); + + static bool readHeaderFromCurrentFile(const WCHAR * filePath); + + /* + * Get real sizeOfImage value + */ + static SIZE_T getSizeOfImageProcess(HANDLE processHandle, DWORD_PTR moduleBase); + + /* + * Get real sizeOfImage value current process + */ + static bool getSizeOfImageCurrentProcess(); + + + static LONGLONG getFileSize(HANDLE hFile); + static LONGLONG getFileSize(const WCHAR * filePath); + + static WCHAR * selectFileToSave(DWORD flags, int type); + + static DWORD getEntryPointFromFile(const WCHAR * filePath); + + + static bool createBackupFile(const WCHAR * filePath); +}; diff --git a/Scylla/ProcessLister.cpp b/Scylla/ProcessLister.cpp new file mode 100644 index 0000000..96eb708 --- /dev/null +++ b/Scylla/ProcessLister.cpp @@ -0,0 +1,364 @@ +#include "ProcessLister.h" + +#include "SystemInformation.h" +#include "Logger.h" +#include "ProcessAccessHelp.h" + +//#define DEBUG_COMMENTS + +def_IsWow64Process ProcessLister::_IsWow64Process = 0; + +std::vector& ProcessLister::getProcessList() +{ + return processList; +} + +bool ProcessLister::isWindows64() +{ +#ifdef _WIN64 + //compiled 64bit application + return true; +#else + //32bit exe, check wow64 + BOOL bIsWow64 = FALSE; + + //not available in all windows operating systems + //Minimum supported client: Windows Vista, Windows XP with SP2 + //Minimum supported server: Windows Server 2008, Windows Server 2003 with SP1 + + if (_IsWow64Process) + { + _IsWow64Process(GetCurrentProcess(), &bIsWow64); + if (bIsWow64 == TRUE) + { + return true; + } + else + { + return false; + } + } + else + { + return false; + } +#endif +} + + +void ProcessLister::initDeviceNameList() +{ + TCHAR shortName[3] = {0}; + TCHAR longName[MAX_PATH] = {0}; + HardDisk hardDisk; + + shortName[1] = TEXT(':'); + + for ( WCHAR shortD = TEXT('a'); shortD < TEXT('z'); shortD++ ) + { + shortName[0] = shortD; + if (QueryDosDeviceW( shortName, longName, MAX_PATH ) > 0) + { + hardDisk.shortName[0] = towupper(shortD); + hardDisk.shortName[1] = TEXT(':'); + hardDisk.shortName[2] = 0; + + hardDisk.longNameLength = wcslen(longName); + + wcscpy_s(hardDisk.longName, MAX_PATH, longName); + deviceNameList.push_back(hardDisk); + } + } +} + +//only needed in windows xp +DWORD ProcessLister::setDebugPrivileges() +{ + DWORD err = 0; + HANDLE hToken = 0; + TOKEN_PRIVILEGES Debug_Privileges = {0}; + + if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Debug_Privileges.Privileges[0].Luid)) + { + return GetLastError(); + } + + if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) + { + err = GetLastError(); + if(hToken) CloseHandle(hToken); + return err; + } + + Debug_Privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + Debug_Privileges.PrivilegeCount = 1; + + AdjustTokenPrivileges(hToken, false, &Debug_Privileges, 0, NULL, NULL); + + CloseHandle(hToken); + return GetLastError(); +} + + +/************************************************************************/ +/* Check if a process is 32 or 64bit */ +/************************************************************************/ +ProcessType ProcessLister::checkIsProcess64(DWORD dwPID) +{ + HANDLE hProcess; + BOOL bIsWow64 = FALSE; + + if (dwPID == 0) + { + //unknown + return PROCESS_UNKNOWN; + } + + //hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, NULL, dwPID); + + hProcess = ProcessAccessHelp::NativeOpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, dwPID); + + if(!hProcess) + { + //missing rights + return PROCESS_MISSING_RIGHTS; + } + + if (!isWindows64()) + { + //32bit win can only run 32bit process + CloseHandle(hProcess); + return PROCESS_32; + } + + _IsWow64Process(hProcess, &bIsWow64); + CloseHandle(hProcess); + + if (bIsWow64 == FALSE) + { + //process not running under wow + return PROCESS_64; + } + else + { + //process running under wow -> 32bit + return PROCESS_32; + } +} + +bool ProcessLister::getAbsoluteFilePath(Process * process) +{ + WCHAR processPath[MAX_PATH]; + HANDLE hProcess; + + //hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, NULL, process->PID); + hProcess = ProcessAccessHelp::NativeOpenProcess(PROCESS_QUERY_INFORMATION, process->PID); + + if(!hProcess) + { + //missing rights + return false; + } + + if (GetProcessImageFileName(hProcess, processPath, _countof(processPath)) > 0) + { + CloseHandle(hProcess); + + if (!resolveDeviceLongNameToShort(processPath, process->fullPath)) + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("getAbsoluteFilePath :: resolveDeviceLongNameToShort failed with path %s\r\n"), processPath); +#endif + } + return true; + } + else + { +#ifdef DEBUG_COMMENTS + Logger::debugLog(TEXT("getAbsoluteFilePath :: GetProcessImageFileName failed %u\r\n"),GetLastError()); +#endif + CloseHandle(hProcess); + return false; + } + +} + +std::vector& ProcessLister::getProcessListSnapshot() +{ + HANDLE hProcessSnap; + ProcessType processType; + PROCESSENTRY32 pe32; + HANDLE hModuleSnap = INVALID_HANDLE_VALUE; + MODULEENTRY32 me32 = {0}; + Process process; + + processList.reserve(34); + + if (!processList.empty()) + { + processList.clear(); + } + + hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if(hProcessSnap == INVALID_HANDLE_VALUE) + { + return processList; + } + + pe32.dwSize = sizeof(PROCESSENTRY32); + + if(!Process32First(hProcessSnap, &pe32)) + { + CloseHandle(hProcessSnap); + return processList; + } + + do + { + //filter process list + if (pe32.th32ProcessID > 4) + { + + processType = checkIsProcess64(pe32.th32ProcessID); + + if (processType != PROCESS_MISSING_RIGHTS) + { + + +#ifdef _WIN64 + if (processType == PROCESS_64) +#else + if (processType == PROCESS_32) +#endif + { + process.PID = pe32.th32ProcessID; + + + hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, process.PID); + if(hModuleSnap != INVALID_HANDLE_VALUE) + { + me32.dwSize = sizeof(MODULEENTRY32); + + Module32First(hModuleSnap, &me32); + process.imageBase = (DWORD_PTR)me32.hModule; + process.imageSize = me32.modBaseSize; + CloseHandle(hModuleSnap); + } + + wcscpy_s(process.filename, MAX_PATH, pe32.szExeFile); + + getAbsoluteFilePath(&process); + + processList.push_back(process); + } + } + } + } while(Process32Next(hProcessSnap, &pe32)); + + CloseHandle(hProcessSnap); + + //get module informations + //getAllModuleInformation(); + + return processList; +} + +void ProcessLister::getAllModuleInformation() +{ + /*for (std::size_t i = 0; i < processList.size(); i++) + { + getModuleInformationByProcess(&processList[i]); + }*/ +} + +void ProcessLister::getModuleInformationByProcess(Process *process) +{ +/* MODULEENTRY32 me32 = {0}; + HANDLE hModuleSnap = INVALID_HANDLE_VALUE; + char temp[111]; + + + if (process->PID == 0) + { + MessageBox(0, "PID == NULL","ProcessLister::getModuleInformationByProcess", MB_OK|MB_ICONWARNING); + return; + } + +#ifdef _WIN64 + if (!process->is64BitProcess) + { + //MessageBox(hWndDlg, "I'm a x64 process and you're trying to access a 32-bit process!","displayModuleList", MB_OK); + return; + } +#else + if (process->is64BitProcess) + { + //MessageBox(hWndDlg, "I'm a 32-bit process and you're trying to access a x64 process!","displayModuleList", MB_OK); + return; + } +#endif + + hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, process->PID); + + if(hModuleSnap == INVALID_HANDLE_VALUE) + { + sprintf_s(temp,sizeof(temp),"GetLastError %d",GetLastError()); + MessageBox(0, temp,"ProcessLister::getModuleInformationByProcess", MB_OK|MB_ICONWARNING); + return; + } + + me32.dwSize = sizeof(MODULEENTRY32); + + if(!Module32First(hModuleSnap, &me32)) + { + MessageBox(0, "Module32First error","ProcessLister::getModuleInformationByProcess", MB_OK|MB_ICONWARNING); + CloseHandle(hModuleSnap); + return; + } + + do { + + ModuleInfo moduleInfo; + + if (!_strnicmp(me32.szExePath,"\\Systemroot",11)) + { + char * path = (char *)malloc(MAX_PATH); + sprintf_s(path,MAX_PATH,"%s\\%s",getenv("SystemRoot"),(me32.szExePath + 12)); + strcpy_s(moduleInfo.fullPath,MAX_PATH, path); + free(path); + } + else if(!_strnicmp(me32.szExePath,"\\??\\",4)) + { + strcpy_s(moduleInfo.fullPath,MAX_PATH, (me32.szExePath + 4)); + } + else + { + strcpy_s(moduleInfo.fullPath,MAX_PATH,me32.szExePath); + } + + moduleInfo.hModule = (DWORD_PTR)me32.hModule; + moduleInfo.modBaseSize = me32.modBaseSize; + moduleInfo.modBaseAddr = (DWORD_PTR)me32.modBaseAddr; + + process->moduleList[moduleInfo.hModule] = moduleInfo; + + } while(Module32Next(hModuleSnap, &me32)); + + CloseHandle(hModuleSnap);*/ + +} + +bool ProcessLister::resolveDeviceLongNameToShort( WCHAR * sourcePath, WCHAR * targetPath ) +{ + for (unsigned int i = 0; i < deviceNameList.size(); i++) + { + if (!_wcsnicmp(deviceNameList[i].longName, sourcePath, deviceNameList[i].longNameLength)) + { + wcscpy_s(targetPath, MAX_PATH,deviceNameList[i].shortName); + wcscat_s(targetPath, MAX_PATH, sourcePath + deviceNameList[i].longNameLength); + return true; + } + } + + return false; +} diff --git a/Scylla/ProcessLister.h b/Scylla/ProcessLister.h new file mode 100644 index 0000000..120da44 --- /dev/null +++ b/Scylla/ProcessLister.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +#pragma comment(lib, "Psapi.lib") + + +typedef BOOL (WINAPI *def_IsWow64Process)(HANDLE hProcess,PBOOL Wow64Process); + +class Process { +public: + DWORD PID; + DWORD_PTR imageBase; + DWORD entryPoint; //without imagebase + DWORD imageSize; + WCHAR filename[MAX_PATH]; + WCHAR fullPath[MAX_PATH]; + + Process() + { + PID = 0; + } +}; + +class HardDisk { +public: + WCHAR shortName[3]; + WCHAR longName[MAX_PATH]; + size_t longNameLength; +}; + +enum ProcessType { + PROCESS_UNKNOWN, + PROCESS_MISSING_RIGHTS, + PROCESS_32, + PROCESS_64 +}; + +class ProcessLister { +public: + + static def_IsWow64Process _IsWow64Process; + + ProcessLister() + { + initDeviceNameList(); + _IsWow64Process = (def_IsWow64Process)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")),"IsWow64Process"); + } + + std::vector& getProcessList(); + static bool isWindows64(); + static DWORD setDebugPrivileges(); + std::vector& ProcessLister::getProcessListSnapshot(); + +private: + std::vector processList; + + std::vector deviceNameList; + + ProcessType checkIsProcess64(DWORD dwPID); + + void initDeviceNameList(); + + + bool getAbsoluteFilePath(Process * process); + + void getAllModuleInformation(); + void getModuleInformationByProcess(Process *process); + bool resolveDeviceLongNameToShort( WCHAR * sourcePath, WCHAR * targetPath ); +}; \ No newline at end of file diff --git a/Scylla/Scylla.vcxproj b/Scylla/Scylla.vcxproj new file mode 100644 index 0000000..9106a59 --- /dev/null +++ b/Scylla/Scylla.vcxproj @@ -0,0 +1,198 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {710434C9-FC4B-4F1D-B318-E10ADC78499F} + Win32Proj + Scylla + + + + Application + true + Unicode + + + Application + true + Unicode + + + Application + false + true + Unicode + v90 + + + Application + false + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + + + Windows + false + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + + + Windows + false + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Scylla/Scylla.vcxproj.filters b/Scylla/Scylla.vcxproj.filters new file mode 100644 index 0000000..98cc2a3 --- /dev/null +++ b/Scylla/Scylla.vcxproj.filters @@ -0,0 +1,172 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {e037d0d5-35ad-4034-83db-746a56a4fee7} + + + {6f76186f-b79c-41e2-8939-05d9de028aad} + + + + + Quelldateien + + + Quelldateien\GUI + + + Quelldateien\GUI + + + Quelldateien + + + Quelldateien\GUI + + + Quelldateien\GUI + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien + + + Quelldateien\GUI + + + Quelldateien + + + Quelldateien + + + Quelldateien\GUI + + + Quelldateien\GUI + + + + + Headerdateien + + + Headerdateien\GUI + + + Headerdateien\GUI + + + Headerdateien + + + Headerdateien\GUI + + + Headerdateien\GUI + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien\GUI + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien + + + Headerdateien\GUI + + + Headerdateien\GUI + + + Headerdateien + + + Headerdateien + + + Headerdateien\GUI + + + Headerdateien\GUI + + + + + Ressourcendateien + + + + + Ressourcendateien + + + \ No newline at end of file diff --git a/Scylla/Scylla.vcxproj.user b/Scylla/Scylla.vcxproj.user new file mode 100644 index 0000000..ace9a86 --- /dev/null +++ b/Scylla/Scylla.vcxproj.user @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/Scylla/SystemInformation.cpp b/Scylla/SystemInformation.cpp new file mode 100644 index 0000000..bce7a6b --- /dev/null +++ b/Scylla/SystemInformation.cpp @@ -0,0 +1,62 @@ +#include "SystemInformation.h" + +OPERATING_SYSTEM SystemInformation::currenOS = UNKNOWN_OS; + +bool SystemInformation::getSystemInformation() +{ + OSVERSIONINFOEX osvi = {0}; + SYSTEM_INFO si = {0}; + def_GetNativeSystemInfo _GetNativeSystemInfo = 0; + + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (!GetVersionEx((OSVERSIONINFO*) &osvi)) + { + return false; + } + + if ((osvi.dwMajorVersion < 5) || ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0))) + { + return false; + } + + _GetNativeSystemInfo = (def_GetNativeSystemInfo)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); + if (_GetNativeSystemInfo) + { + _GetNativeSystemInfo(&si); + } + else + { + GetSystemInfo(&si); + } + + if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 && osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) + { + currenOS = WIN_VISTA_64; + } + else if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL && osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) + { + currenOS = WIN_VISTA_32; + } + else if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 && osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) + { + currenOS = WIN_7_64; + } + else if(si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL && osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) + { + currenOS = WIN_7_32; + } + else if(si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 && osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) + { + currenOS = WIN_XP_64; + } + else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) + { + currenOS = WIN_XP_32; + } + else + { + currenOS = UNKNOWN_OS; + } + + return (currenOS != UNKNOWN_OS); +} \ No newline at end of file diff --git a/Scylla/SystemInformation.h b/Scylla/SystemInformation.h new file mode 100644 index 0000000..ccac3a4 --- /dev/null +++ b/Scylla/SystemInformation.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +enum OPERATING_SYSTEM { + UNKNOWN_OS, + WIN_XP_32, + WIN_XP_64, + WIN_VISTA_32, + WIN_VISTA_64, + WIN_7_32, + WIN_7_64, +}; + +typedef void (WINAPI *def_GetNativeSystemInfo)(LPSYSTEM_INFO lpSystemInfo); + +class SystemInformation { +public: + static OPERATING_SYSTEM currenOS; + static bool getSystemInformation(); +private: +}; \ No newline at end of file diff --git a/Scylla/Thunks.h b/Scylla/Thunks.h new file mode 100644 index 0000000..586c34b --- /dev/null +++ b/Scylla/Thunks.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include + +class ImportModuleThunk; +class ImportThunk; + +class ImportThunk { +public: + WCHAR moduleName[MAX_PATH]; + char name[MAX_PATH]; + DWORD_PTR va; + DWORD_PTR rva; + DWORD_PTR ordinal; + DWORD_PTR apiAddressVA; + WORD hint; + bool valid; + bool suspect; + + HTREEITEM hTreeItem; +}; + + +class ImportModuleThunk { +public: + WCHAR moduleName[MAX_PATH]; + std::map thunkList; + + DWORD_PTR firstThunk; + + HTREEITEM hTreeItem; + + DWORD_PTR getFirstThunk(); + bool isValid(); + + ~ImportModuleThunk() + { + if (!thunkList.empty()) + { + thunkList.clear(); + } + } + +}; \ No newline at end of file diff --git a/Scylla/definitions.h b/Scylla/definitions.h new file mode 100644 index 0000000..588c0bc --- /dev/null +++ b/Scylla/definitions.h @@ -0,0 +1,34 @@ + +#pragma once + +#define APPNAME "Scylla" + +#ifdef _WIN64 + +#define ARCHITECTURE "x64" +#define PRINTF_DWORD_PTR "%I64X" +#define PRINTF_DWORD_PTR_FULL "%016I64X" +#define PRINTF_DWORD_PTR_HALF "%08I64X" +#define MAX_HEX_VALUE_EDIT_LENGTH 16 + +#else + +#define ARCHITECTURE "x86" +#define PRINTF_DWORD_PTR "%X" +#define PRINTF_DWORD_PTR_FULL "%08X" +#define PRINTF_DWORD_PTR_HALF "%08X" +#define MAX_HEX_VALUE_EDIT_LENGTH 8 + +#endif + +#define APPVERSION "v0.2a" + +#define RECOMMENDED_OS "This tool was designed to work with Windows 7 x64" +#define DEVELOPED "Developed with Microsoft Visual Studio 2010, written in pure C/C++" +#define CREDIT_DISTORM "This tool uses the diStorm disassembler library http://code.google.com/p/distorm/" +#define CREDIT_YODA "The PE Rebuilder engine is based on the Realign DLL version 1.5 by yoda" +#define GREETINGS "Greetz: metr0, G36KV and all from the gRn Team" +#define VISIT "Visit http://kickme.to/grn and http://forum.tuts4you.com " + + +#define PLUGIN_MENU_BASE_ID 0x10 \ No newline at end of file diff --git a/Scylla/main.cpp b/Scylla/main.cpp new file mode 100644 index 0000000..be84b32 --- /dev/null +++ b/Scylla/main.cpp @@ -0,0 +1,8 @@ +#include "MainGui.h" + +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + //start main dialog + MainGui::initDialog(hInstance); + return 0; +} \ No newline at end of file diff --git a/Scylla/resource.h b/Scylla/resource.h new file mode 100644 index 0000000..2104e9d Binary files /dev/null and b/Scylla/resource.h differ diff --git a/Scylla/scylla.ico b/Scylla/scylla.ico new file mode 100644 index 0000000..08811ac Binary files /dev/null and b/Scylla/scylla.ico differ diff --git a/readme b/readme deleted file mode 100644 index e69de29..0000000