diff --git a/Scylla/ApiReader.cpp b/Scylla/ApiReader.cpp index 3975f31..9133f4d 100644 --- a/Scylla/ApiReader.cpp +++ b/Scylla/ApiReader.cpp @@ -1,1325 +1,1325 @@ #include "ApiReader.h" #include "Scylla.h" #include "Architecture.h" #include "SystemInformation.h" +#include "StringConversion.h" -stdext::hash_multimap ApiReader::apiList; //api look up table +std::unordered_map 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; } Scylla::windowLog.log(L"Module parsing: %s",moduleList[i].fullPath); if (!moduleList[i].isAlreadyParsed) { parseModule(&moduleList[i]); } } #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"Address Min " PRINTF_DWORD_PTR_FULL L" Max " PRINTF_DWORD_PTR_FULL L"\nimagebase " PRINTF_DWORD_PTR_FULL L" maxValidAddress " PRINTF_DWORD_PTR_FULL, 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, WORD ordinalParent, ModuleInfo *moduleParent) { size_t dllNameLength = 0; WORD 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"); + strcat_s(dllName, sizeof(dllName), ".dll"); - size_t convertedChars = 0; - mbstowcs_s(&convertedChars, dllNameW, strlen(dllName) + 1, dllName, _TRUNCATE); + StringConversion::ToUTF16(dllName, dllNameW, _countof(dllNameW)); 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 = (WORD)atoi(searchFunctionName); findApiByModuleAndOrdinal(module, ordinal, &vaApi, &rvaApi); } else { findApiByModuleAndName(module, searchFunctionName, &vaApi, &rvaApi); } if (rvaApi == 0) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"handleForwardedApi :: Api not found, this is really BAD! %S",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(WORD 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, WORD ordinal, DWORD_PTR va, DWORD_PTR rva, bool isForwarded, ModuleInfo *moduleInfo) { ApiInfo *apiInfo = new ApiInfo(); if ((functionName != 0) && (strlen(functionName) < _countof(apiInfo->name))) { strcpy_s(apiInfo->name, _countof(apiInfo->name), 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 Scylla::debugLog.log(L"getHeaderFromProcess :: Error reading header"); #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 Scylla::debugLog.log(L"Something is wrong with the PE Header here Export table size %d", 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 Scylla::debugLog.log(L"getExportTableFromProcess :: Error reading export table from process"); #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; WORD ordinal = 0; WORD 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 Scylla::debugLog.log(L"parseExportTable :: module %s NumberOfNames %X", module->fullPath, pExportDir->NumberOfNames); #endif for (i = 0; i < pExportDir->NumberOfNames; i++) { functionName = (char*)(addressOfNamesArray[i] + deltaAddress); ordinal = (WORD)(addressOfNameOrdinalsArray[i] + pExportDir->Base); RVA = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]]; VA = addressOfFunctionsArray[addressOfNameOrdinalsArray[i]] + module->modBaseAddr; #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"parseExportTable :: api %S ordinal %d imagebase " PRINTF_DWORD_PTR_FULL L" RVA " PRINTF_DWORD_PTR_FULL L" VA " PRINTF_DWORD_PTR_FULL, functionName, ordinal, module->modBaseAddr, RVA, VA); #endif if (!isApiBlacklisted(functionName)) { if (!isApiForwarded(RVA,pNtHeader)) { addApi(functionName, 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 = (WORD)(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, WORD 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, WORD 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 Scylla::debugLog.log(L"findApiByModule :: vaApi == NULL, should never happen %S", searchFunctionName); #endif } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findApiByModule :: hModule == NULL, should never happen %s", 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 Scylla::debugLog.log(L"parseModuleWithOwnProcess :: hModule is NULL"); #endif } } bool ApiReader::isPeAndExportTableValid(PIMAGE_NT_HEADERS pNtHeader) { if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { Scylla::windowLog.log(L"-> 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)) { Scylla::windowLog.log(L"-> No export table."); return false; } else { return true; } } void ApiReader::findApiInProcess(ModuleInfo * module, char * searchFunctionName, WORD 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, WORD 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) { const WCHAR *moduleFileName = module->getFilename(); //imports by kernelbase don't exist if (!_wcsicmp(moduleFileName, L"kernelbase.dll")) { module->priority = -1; } else if (!_wcsicmp(moduleFileName, L"ntdll.dll")) { module->priority = 0; } else if (!_wcsicmp(moduleFileName, L"shlwapi.dll")) { module->priority = 0; } else if (!_wcsicmp(moduleFileName, L"ShimEng.dll")) { module->priority = 0; } else if (!_wcsicmp(moduleFileName, L"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; + std::unordered_map::iterator it1, it2; size_t c = 0; size_t countDuplicates = apiList.count(virtualAddress); int countHighPriority = 0; ApiInfo *apiFound = 0; if (countDuplicates == 0) { Scylla::windowLog.log(L"getApiByVirtualAddress :: No Api found " 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. //any high priority with a name apiFound = getScoredApi(it1,countDuplicates,true,false,false,true,false,false,false,false); if (apiFound) return apiFound; *isSuspect = true; //high priority with a name and ansi/unicode name apiFound = getScoredApi(it1,countDuplicates,true,true,false,true,false,false,false,false); if (apiFound) return apiFound; //priority 2 with no underline in name apiFound = getScoredApi(it1,countDuplicates,true,false,true,false,false,false,true,false); if (apiFound) return apiFound; //priority 1 with a name apiFound = getScoredApi(it1,countDuplicates,true,false,false,false,false,true,false,false); if (apiFound) return apiFound; //With a name apiFound = getScoredApi(it1,countDuplicates,true,false,false,false,false,false,false,false); if (apiFound) return apiFound; //any with priority, name, ansi/unicode apiFound = getScoredApi(it1,countDuplicates,true,true,false,true,false,false,false,true); if (apiFound) return apiFound; //any with priority apiFound = getScoredApi(it1,countDuplicates,false,false,false,true,false,false,false,true); if (apiFound) return apiFound; //has prio 0 and name apiFound = getScoredApi(it1,countDuplicates,false,false,false,false,true,false,false,true); if (apiFound) return apiFound; } //is never reached Scylla::windowLog.log(L"getApiByVirtualAddress :: There is a api resolving bug, VA: " PRINTF_DWORD_PTR_FULL, virtualAddress); for (size_t c = 0; c < countDuplicates; c++, it1++) { apiFound = (ApiInfo *)((*it1).second); Scylla::windowLog.log(L"-> Possible API: %S ord: %d ", apiFound->name, apiFound->ordinal); } return (ApiInfo *) 1; } /*ApiInfo * ApiReader::getApiByVirtualAddress(DWORD_PTR virtualAddress, bool * isSuspect) { - stdext::hash_multimap::iterator it1, it2; + std::unordered_map::iterator it1, it2; size_t c = 0; size_t countDuplicates = apiList.count(virtualAddress); int countHighPriority = 0; ApiInfo *apiFound = 0; if (countDuplicates == 0) { Scylla::windowLog.log(L"getApiByVirtualAddress :: No Api found " 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; This is flawed: It chooses api(prio:1, name:no) over api(prio:0, name:yes) (e.g. SHLWAPI.PathCombineW vs SHELL32.#25) Maybe there should be a check higher up in the code, to see if this API is surrounded by APIs of a DLL and pick the duplicate from that DLL if (countHighPriority == 0) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"getApiByVirtualAddress :: countHighPriority == 0 " PRINTF_DWORD_PTR_FULL, virtualAddress); #endif *isSuspect = true; return (ApiInfo *)((*it1).second); } else if (countHighPriority == 1) // what about kernel32, it has priority 2 { //API is 100% correct if countHighPriority == 1 and name export *isSuspect = false; 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; } } } //else // fall through for case api1(priority:1, name:false) <> api2(priority:0, name:true) { //API not 100% correct #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"getApiByVirtualAddress :: countHighPriority == %d " PRINTF_DWORD_PTR_FULL, countHighPriority, virtualAddress); #endif *isSuspect = true; for (c = 0; c < countDuplicates; c++, it1++) { apiFound = (ApiInfo *)((*it1).second); Scylla::windowLog.log(L"%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, 'W') || strrchr(apiFound->name, '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, '_')) //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 Scylla::windowLog.log(L"getApiByVirtualAddress :: There is a big bug"); return (ApiInfo *) 1; }*/ -ApiInfo * ApiReader::getScoredApi(stdext::hash_multimap::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ) +ApiInfo * ApiReader::getScoredApi(std::unordered_map::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ) { ApiInfo * foundApi = 0; ApiInfo * foundMatchingApi = 0; int countFoundApis = 0; int scoreNeeded = 0; int scoreValue = 0; size_t apiNameLength = 0; if (hasUnicodeAnsiName || hasNoUnderlineInName) { hasName = true; } if (hasName) scoreNeeded++; if (hasUnicodeAnsiName) scoreNeeded++; if (hasNoUnderlineInName) scoreNeeded++; if (hasPrioDll) scoreNeeded++; if (hasPrio0Dll) scoreNeeded++; if (hasPrio1Dll) scoreNeeded++; if (hasPrio2Dll) scoreNeeded++; for (size_t c = 0; c < countDuplicates; c++, it1++) { foundApi = (ApiInfo *)((*it1).second); scoreValue = 0; if (hasName) { if (foundApi->name[0] != 0x00) { scoreValue++; if (hasUnicodeAnsiName) { apiNameLength = strlen(foundApi->name); if ((foundApi->name[apiNameLength - 1] == 'W') || (foundApi->name[apiNameLength - 1] == 'A')) { scoreValue++; } } if (hasNoUnderlineInName) { if (!strrchr(foundApi->name, '_')) { scoreValue++; } } } } if (hasPrioDll) { if (foundApi->module->priority >= 1) { scoreValue++; } } if (hasPrio0Dll) { if (foundApi->module->priority == 0) { scoreValue++; } } if (hasPrio1Dll) { if (foundApi->module->priority == 1) { scoreValue++; } } if (hasPrio2Dll) { if (foundApi->module->priority == 2) { scoreValue++; } } if (scoreValue == scoreNeeded) { foundMatchingApi = foundApi; countFoundApis++; if (firstWin) { return foundMatchingApi; } } } if (countFoundApis == 1) { return foundMatchingApi; } else { return (ApiInfo *)0; } } 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 Scylla::debugLog.log(L"ApiReader::readAndParseIAT :: error reading iat " PRINTF_DWORD_PTR_FULL, 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++) { //Scylla::windowLog.log(L"%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 Scylla::debugLog.log(L"apiFound == (ApiInfo *)1 -> " PRINTF_DWORD_PTR_FULL, pIATAddress[i]); #endif } else if (apiFound) { countApiFound++; #ifdef DEBUG_COMMENTS Scylla::debugLog.log(PRINTF_DWORD_PTR_FULL L" %s %d %s", 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]); } } Scylla::windowLog.log(L"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, _countof(module.moduleName), moduleName); (*moduleThunkList).insert(std::pair(firstThunk,module)); return true; } void ApiReader::addUnknownModuleToModuleList(DWORD_PTR firstThunk) { ImportModuleThunk module; module.firstThunk = firstThunk; wcscpy_s(module.moduleName, _countof(module.moduleName), L"?"); (*moduleThunkList).insert(std::pair(firstThunk,module)); } bool ApiReader::addFunctionToModuleList(ApiInfo * apiFound, DWORD_PTR va, DWORD_PTR rva, WORD 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 Scylla::debugLog.log(L"Error iterator1 != (*moduleThunkList).end()"); #endif break; } } } else { iterator1 = (*moduleThunkList).begin(); module = &(iterator1->second); } if (!module) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"ImportsHandling::addFunction module not found rva " PRINTF_DWORD_PTR_FULL, rva); #endif return false; } import.suspect = 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, _countof(import.moduleName), apiFound->module->getFilename()); strcpy_s(import.name, _countof(import.name), 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 ) + for ( std::unordered_map::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 Scylla::debugLog.log(L"Error iterator1 != (*moduleThunkList).end()\r\n"); #endif break; } } } else { //new unknown module addUnknownModuleToModuleList(rva); module = &((*moduleThunkList).find(rva)->second); } if (!module) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"ImportsHandling::addFunction module not found rva " PRINTF_DWORD_PTR_FULL,rva); #endif return false; } import.suspect = true; import.valid = false; import.va = iatAddressVA; import.rva = rva; import.apiAddressVA = apiAddress; import.ordinal = 0; wcscpy_s(import.moduleName, _countof(import.moduleName), L"?"); strcpy_s(import.name, _countof(import.name), "?"); 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, L"\\WinSxS\\")) { return true; } else if (wcsstr(module->fullPath, L"\\winsxs\\")) { return true; } else { return false; } } diff --git a/Scylla/ApiReader.h b/Scylla/ApiReader.h index ebcc6f2..81e4bea 100644 --- a/Scylla/ApiReader.h +++ b/Scylla/ApiReader.h @@ -1,72 +1,74 @@ #pragma once +#include +#include +#include #include "ProcessAccessHelp.h" #include "Thunks.h" typedef std::pair API_Pair; -class ApiReader : public ProcessAccessHelp { +class ApiReader : public ProcessAccessHelp +{ public: - static stdext::hash_multimap apiList; //api look up table + static std::unordered_map 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 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, WORD ordinal, DWORD_PTR va, DWORD_PTR rva, bool isForwarded, ModuleInfo *moduleInfo); void addApiWithoutName(WORD 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, WORD 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, WORD 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, WORD 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, WORD ordinal, DWORD_PTR * vaApi, DWORD_PTR * rvaApi ); bool findApiInExportTable(ModuleInfo *module, PIMAGE_EXPORT_DIRECTORY pExportDir, DWORD_PTR deltaAddress, char * searchFunctionName, WORD 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, WORD 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 ); - ApiInfo * getScoredApi(stdext::hash_multimap::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ); -}; \ No newline at end of file + ApiInfo * getScoredApi(std::unordered_map::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ); +}; diff --git a/Scylla/ConfigurationHolder.cpp b/Scylla/ConfigurationHolder.cpp index 52ab836..c04838f 100644 --- a/Scylla/ConfigurationHolder.cpp +++ b/Scylla/ConfigurationHolder.cpp @@ -1,205 +1,202 @@ #include "ConfigurationHolder.h" #include -#include #include "Architecture.h" const WCHAR ConfigurationHolder::CONFIG_FILE_SECTION_NAME[] = L"SCYLLA_CONFIG"; //#define DEBUG_COMMENTS ConfigurationHolder::ConfigurationHolder(const WCHAR* fileName) { config[USE_PE_HEADER_FROM_DISK] = Configuration(L"USE_PE_HEADER_FROM_DISK", Configuration::Boolean); config[DEBUG_PRIVILEGE] = Configuration(L"DEBUG_PRIVILEGE", Configuration::Boolean); config[CREATE_BACKUP] = Configuration(L"CREATE_BACKUP", Configuration::Boolean); config[DLL_INJECTION_AUTO_UNLOAD] = Configuration(L"DLL_INJECTION_AUTO_UNLOAD", Configuration::Boolean); config[UPDATE_HEADER_CHECKSUM] = Configuration(L"UPDATE_HEADER_CHECKSUM", Configuration::Boolean); config[IAT_SECTION_NAME] = Configuration(L"IAT_SECTION_NAME", Configuration::String); buildConfigFilePath(fileName); } bool ConfigurationHolder::loadConfiguration() { std::map::iterator mapIter; if (configPath[0] == '\0') { return false; } for (mapIter = config.begin() ; mapIter != config.end(); mapIter++) { Configuration& configObject = mapIter->second; if (!loadConfig(configObject)) { return false; } } return true; } bool ConfigurationHolder::saveConfiguration() const { std::map::const_iterator mapIter; if (configPath[0] == '\0') { return false; } for (mapIter = config.begin() ; mapIter != config.end(); mapIter++) { const Configuration& configObject = mapIter->second; if (!saveConfig(configObject)) { return false; } } return true; } Configuration& ConfigurationHolder::operator[](ConfigOption option) { return config[option]; } const Configuration& ConfigurationHolder::operator[](ConfigOption option) const { static const Configuration dummy; std::map::const_iterator found = config.find(option); if(found != config.end()) { return found->second; } else { return dummy; } } bool ConfigurationHolder::saveNumericToConfigFile(const Configuration & configObject, int nBase) const { WCHAR buf[21]; // UINT64_MAX in dec has 20 digits if (nBase == 16) { swprintf_s(buf, PRINTF_DWORD_PTR_FULL, configObject.getNumeric()); } else { swprintf_s(buf, PRINTF_INTEGER, configObject.getNumeric()); } BOOL ret = WritePrivateProfileString(CONFIG_FILE_SECTION_NAME, configObject.getName(), buf, configPath); return ret == TRUE; } bool ConfigurationHolder::readNumericFromConfigFile(Configuration & configObject, int nBase) { WCHAR buf[21]; // UINT64_MAX in dec has 20 digits DWORD read = GetPrivateProfileString(CONFIG_FILE_SECTION_NAME, configObject.getName(), L"", buf, _countof(buf), configPath); if (read > 0 && wcslen(buf) > 0) { #ifdef _WIN64 configObject.setNumeric(_wcstoui64(buf, NULL, nBase)); #else configObject.setNumeric(wcstoul(buf, NULL, nBase)); #endif return true; } return false; } bool ConfigurationHolder::saveStringToConfigFile(const Configuration & configObject) const { BOOL ret = WritePrivateProfileString(CONFIG_FILE_SECTION_NAME, configObject.getName(), configObject.getString(), configPath); return ret == TRUE; } bool ConfigurationHolder::readStringFromConfigFile(Configuration & configObject) { WCHAR buf[Configuration::CONFIG_STRING_LENGTH]; DWORD read = GetPrivateProfileString(CONFIG_FILE_SECTION_NAME, configObject.getName(), L"", buf, _countof(buf), configPath); if(read > 0 && wcslen(buf) > 0) { configObject.setString(buf); return true; } return false; } bool ConfigurationHolder::readBooleanFromConfigFile(Configuration & configObject) { UINT val = GetPrivateProfileInt(CONFIG_FILE_SECTION_NAME, configObject.getName(), 0, configPath); configObject.setBool(val != 0); return true; } bool ConfigurationHolder::saveBooleanToConfigFile(const Configuration & configObject) const { const WCHAR *boolValue = configObject.isTrue() ? L"1" : L"0"; BOOL ret = WritePrivateProfileString(CONFIG_FILE_SECTION_NAME, configObject.getName(), boolValue, configPath); return ret == TRUE; } bool ConfigurationHolder::loadConfig(Configuration & configObject) { switch (configObject.getType()) { case Configuration::String: return readStringFromConfigFile(configObject); case Configuration::Boolean: return readBooleanFromConfigFile(configObject); case Configuration::Decimal: return readNumericFromConfigFile(configObject, 10); case Configuration::Hexadecimal: return readNumericFromConfigFile(configObject, 16); default: return false; } } bool ConfigurationHolder::saveConfig(const Configuration & configObject) const { switch (configObject.getType()) { case Configuration::String: return saveStringToConfigFile(configObject); case Configuration::Boolean: return saveBooleanToConfigFile(configObject); case Configuration::Decimal: return saveNumericToConfigFile(configObject, 10); case Configuration::Hexadecimal: return saveNumericToConfigFile(configObject, 16); default: return false; } } bool ConfigurationHolder::buildConfigFilePath(const WCHAR* fileName) { ZeroMemory(configPath, sizeof(configPath)); if (!GetModuleFileName(0, configPath, _countof(configPath))) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"buildConfigFilePath :: GetModuleFileName failed %d", GetLastError()); #endif return false; } PathRemoveFileSpec(configPath); PathAppend(configPath, fileName); - //wprintf(L"configPath %s\n\n", configPath); - return true; } diff --git a/Scylla/DllInjection.cpp b/Scylla/DllInjection.cpp index d309309..c8f5fed 100644 --- a/Scylla/DllInjection.cpp +++ b/Scylla/DllInjection.cpp @@ -1,246 +1,246 @@ #include "DllInjection.h" -#include "Logger.h" #include +#include "Scylla.h" #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 Scylla::debugLog.log(L"dllInjection :: memorySize invalid"); #endif return 0; } remoteMemory = VirtualAllocEx(hProcess, NULL, memorySize, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (remoteMemory == 0) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"dllInjection :: VirtualAllocEx failed 0x%X", 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 Scylla::debugLog.log(L"dllInjection :: GetExitCodeThread failed 0x%X", GetLastError()); #endif hModule = 0; } #endif CloseHandle(hThread); } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"dllInjection :: CreateRemoteThread failed 0x%X", GetLastError()); #endif } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"dllInjection :: WriteProcessMemory failed 0x%X", 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 Scylla::debugLog.log(L"unloadDllInProcess :: GetExitCodeThread failed 0x%X", GetLastError()); #endif freeLibraryRet = 0; } CloseHandle(hThread); } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"unloadDllInProcess :: CreateRemoteThread failed 0x%X", 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 Scylla::debugLog.log(L"DllInjection::getModuleHandle :: EnumProcessModules failed count %d", 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 Scylla::debugLog.log(L"DllInjection::getModuleHandle :: GetModuleFileNameExW failed 0x%X", GetLastError()); #endif } } if (!hModResult) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"DllInjection::getModuleHandle :: Handle not found"); #endif } delete [] hMods; return hModResult; } void DllInjection::specialThreadSettings( HANDLE hThread ) { if (hThread) { if (!SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"specialThreadSettings :: SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL) failed 0x%X", GetLastError()); #endif } if (NativeWinApi::NtSetInformationThread) { if (NativeWinApi::NtSetInformationThread(hThread, ThreadHideFromDebugger, 0, 0) != STATUS_SUCCESS) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"specialThreadSettings :: NtSetInformationThread ThreadHideFromDebugger failed"); #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 Scylla::debugLog.log(L"customCreateRemoteThread :: NtCreateThreadEx failed 0x%X", 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 index ddd0a6d..a7af9e6 100644 --- a/Scylla/DllInjection.h +++ b/Scylla/DllInjection.h @@ -1,17 +1,18 @@ -#include -#include -#include -#include +#pragma once +#include -class DllInjection { +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 + void specialThreadSettings(HANDLE hThread); + HMODULE getModuleHandleByFilename(HANDLE hProcess, const WCHAR * filename); +}; diff --git a/Scylla/DllInjectionPlugin.h b/Scylla/DllInjectionPlugin.h index 1f22613..131970c 100644 --- a/Scylla/DllInjectionPlugin.h +++ b/Scylla/DllInjectionPlugin.h @@ -1,65 +1,66 @@ +#pragma once + #include "DllInjection.h" #include "PluginLoader.h" #include "Thunks.h" #include "ApiReader.h" - #define PLUGIN_IMPREC_EXCHANGE_DLL_PATH "ScyllaImprecPluginExchangePath" #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); void injectImprecPlugin(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.h b/Scylla/IATSearch.h index 781eaba..633527e 100644 --- a/Scylla/IATSearch.h +++ b/Scylla/IATSearch.h @@ -1,29 +1,29 @@ #pragma once #include "ApiReader.h" - -class IATSearch : protected ApiReader { +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 ); bool isAddressAccessable(DWORD_PTR address); -}; \ No newline at end of file +}; diff --git a/Scylla/ImportRebuild.cpp b/Scylla/ImportRebuild.cpp index 38c56ff..03111e6 100644 --- a/Scylla/ImportRebuild.cpp +++ b/Scylla/ImportRebuild.cpp @@ -1,715 +1,713 @@ #include "ImportRebuild.h" #include "Scylla.h" -//#include "ConfigurationHolder.h" +#include "StringConversion.h" //#define DEBUG_COMMENTS ImportRebuild::ImportRebuild() { imageData = NULL; sizeOfFile = 0; pDosStub = NULL; pOverlay = NULL; sizeOfOverlay = 0; pImportDescriptor = NULL; pThunkData = NULL; pImportByName = NULL; numberOfImportDescriptors = 0; sizeOfImportSection = 0; sizeOfApiAndModuleNames = 0; importSectionIndex = 0; } ImportRebuild::~ImportRebuild() { delete [] pDosStub; delete [] imageData; for (size_t i = 0; i < vecSectionData.size(); i++) { delete [] vecSectionData[i]; } delete [] pOverlay; } bool ImportRebuild::splitTargetFile() { PIMAGE_SECTION_HEADER pSecHeader = 0; BYTE * data = 0; DWORD alignment = 0; DosHeader = *(IMAGE_DOS_HEADER*)imageData; if (DosHeader.e_magic != IMAGE_DOS_SIGNATURE) { return false; } NTHeader = *(IMAGE_NT_HEADERS*)(imageData + DosHeader.e_lfanew); if (NTHeader.Signature != IMAGE_NT_SIGNATURE) { return false; } if (DosHeader.e_lfanew > sizeof(IMAGE_DOS_HEADER)) { size_t sizeOfStub = DosHeader.e_lfanew - sizeof(IMAGE_DOS_HEADER); pDosStub = new BYTE[sizeOfStub]; CopyMemory(pDosStub, imageData + sizeof(IMAGE_DOS_HEADER), sizeOfStub); } pSecHeader = IMAGE_FIRST_SECTION((IMAGE_NT_HEADERS*)(imageData + DosHeader.e_lfanew)); for (WORD i = 0; i < NTHeader.FileHeader.NumberOfSections; i++) { const DWORD SECTION_SIZE_MAX = 300000000; DWORD sizeOfSection = pSecHeader->SizeOfRawData; if (sizeOfSection > SECTION_SIZE_MAX) { sizeOfSection = SECTION_SIZE_MAX; } //TODO better use section alignment because it is better? alignment = alignValue(sizeOfSection, NTHeader.OptionalHeader.SectionAlignment); data = new BYTE[alignment]; ZeroMemory(data, alignment); CopyMemory(data, imageData + pSecHeader->PointerToRawData, sizeOfSection); vecSectionData.push_back(data); vecSectionHeaders.push_back(*pSecHeader); pSecHeader++; } if(NTHeader.FileHeader.NumberOfSections > 0) // ?? { const IMAGE_SECTION_HEADER* pLastSec = &(*vecSectionHeaders.rbegin()); DWORD calcSize = pLastSec->PointerToRawData + pLastSec->SizeOfRawData; if (calcSize < sizeOfFile) { sizeOfOverlay = sizeOfFile - calcSize; pOverlay = new BYTE[sizeOfOverlay]; memcpy(pOverlay, imageData + calcSize, sizeOfOverlay); } } delete [] imageData; imageData = 0; return true; } bool ImportRebuild::alignSectionHeaders() { for (WORD i = 0; i < vecSectionHeaders.size(); i++) { vecSectionHeaders[i].VirtualAddress = alignValue(vecSectionHeaders[i].VirtualAddress, NTHeader.OptionalHeader.SectionAlignment); vecSectionHeaders[i].Misc.VirtualSize = alignValue(vecSectionHeaders[i].Misc.VirtualSize, NTHeader.OptionalHeader.SectionAlignment); vecSectionHeaders[i].PointerToRawData = alignValue(vecSectionHeaders[i].PointerToRawData, NTHeader.OptionalHeader.FileAlignment); vecSectionHeaders[i].SizeOfRawData = alignValue(vecSectionHeaders[i].SizeOfRawData, NTHeader.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 Scylla::debugLog.log(L"saveNewFile :: INVALID_HANDLE_VALUE %u", GetLastError()); #endif return false; } //alignSectionHeaders(); updatePeHeader(); fileOffset = 0; dwWriteSize = sizeof(IMAGE_DOS_HEADER); ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, &DosHeader); fileOffset += dwWriteSize; dwWriteSize = DosHeader.e_lfanew - sizeof(IMAGE_DOS_HEADER); ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, pDosStub); fileOffset += dwWriteSize; dwWriteSize = sizeof(IMAGE_NT_HEADERS); ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, &NTHeader); 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 Scylla::debugLog.log(L"saveNewFile :: writeMemoryToFile failed offset %X size %X", 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 Scylla::debugLog.log(L"saveNewFile :: writeZeroMemoryToFile failed offset %X size %X", fileOffset, dwWriteSize); #endif CloseHandle(hFile); return false; } fileOffset += dwWriteSize; } dwWriteSize = vecSectionHeaders[i].SizeOfRawData; ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, dwWriteSize, vecSectionData[i]); fileOffset += dwWriteSize; } if(pOverlay) { ProcessAccessHelp::writeMemoryToFile(hFile, fileOffset, sizeOfOverlay, pOverlay); fileOffset += sizeOfOverlay; } 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; } 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 Scylla::debugLog.log(L"addNewSection :: sectionname is too long %d", nameLength); #endif return false; } memcpy_s(pNewSection.Name, IMAGE_SIZEOF_SHORT_NAME, sectionName, nameLength); pNewSection.SizeOfRawData = alignValue(sectionSize, NTHeader.OptionalHeader.FileAlignment); pNewSection.Misc.VirtualSize = alignValue(sectionSize, NTHeader.OptionalHeader.SectionAlignment); pNewSection.PointerToRawData = alignValue(vecSectionHeaders[lastSectionIndex].PointerToRawData + vecSectionHeaders[lastSectionIndex].SizeOfRawData, NTHeader.OptionalHeader.FileAlignment); pNewSection.VirtualAddress = alignValue(vecSectionHeaders[lastSectionIndex].VirtualAddress + vecSectionHeaders[lastSectionIndex].Misc.VirtualSize, NTHeader.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 Scylla::debugLog.log(L"loadTargetFile :: INVALID_HANDLE_VALUE %u", 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 { sizeOfFile = fileSize; 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(&NTHeader); for (WORD i = 0; i < NTHeader.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(&NTHeader); for (WORD i = 0; i < NTHeader.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; NTHeader.FileHeader.NumberOfSections = (WORD)(lastSectionIndex + 1); NTHeader.OptionalHeader.SizeOfImage = vecSectionHeaders[lastSectionIndex].VirtualAddress + vecSectionHeaders[lastSectionIndex].Misc.VirtualSize; NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0; NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0; if (NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) { for (size_t i = 0; i < vecSectionHeaders.size(); i++) { if ((vecSectionHeaders[i].VirtualAddress <= NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress) && ((vecSectionHeaders[i].VirtualAddress + vecSectionHeaders[i].Misc.VirtualSize) > NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress)) { //section must be read and writeable vecSectionHeaders[i].Characteristics |= IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE; } } NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0; NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0; } NTHeader.OptionalHeader.NumberOfRvaAndSizes = 0x10; NTHeader.OptionalHeader.SizeOfHeaders = alignValue(DosHeader.e_lfanew + sizeof(DWORD) + sizeof(IMAGE_FILE_HEADER) + NTHeader.FileHeader.SizeOfOptionalHeader + (NTHeader.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)), NTHeader.OptionalHeader.FileAlignment); } bool ImportRebuild::buildNewImportTable(std::map & moduleList) { createNewImportSection(moduleList); importSectionIndex = vecSectionHeaders.size() - 1; DWORD dwSize = fillImportSection(moduleList); if (!dwSize) { return false; } setFlagToIATSection((*moduleList.begin()).second.firstThunk); NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = vecSectionHeaders[importSectionIndex].VirtualAddress; NTHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (DWORD)(numberOfImportDescriptors * sizeof(IMAGE_IMPORT_DESCRIPTOR)); return true; } bool ImportRebuild::createNewImportSection(std::map & moduleList) { char sectionName[9] = {0}; - size_t i = 0; //DWORD sectionSize = calculateMinSize(moduleList); calculateImportSizes(moduleList); if (wcslen(Scylla::config[IAT_SECTION_NAME].getString()) > IMAGE_SIZEOF_SHORT_NAME) { strcpy_s(sectionName, sizeof(sectionName), ".SCY"); } else { - wcstombs_s(&i, sectionName, sizeof(sectionName), Scylla::config[IAT_SECTION_NAME].getString(), _TRUNCATE); + StringConversion::ToASCII(Scylla::config[IAT_SECTION_NAME].getString(), sectionName, _countof(sectionName)); } - return addNewSection(sectionName, (DWORD)sizeOfImportSection, 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++ ) { if((*mapIt2).second.name[0] != '\0') { dwSize += sizeof(IMAGE_IMPORT_BY_NAME); dwSize += (DWORD)strlen((*mapIt2).second.name); } } } 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 ) { std::map::iterator mapIt; std::map::iterator mapIt2; PIMAGE_IMPORT_DESCRIPTOR pImportDesc = 0; PIMAGE_IMPORT_BY_NAME pImportByName = 0; PIMAGE_THUNK_DATA pThunk = 0; ImportModuleThunk * importModuleThunk = 0; ImportThunk * importThunk = 0; size_t stringLength = 0; DWORD_PTR lastRVA = 0; BYTE * sectionData = vecSectionData[importSectionIndex]; DWORD offset = 0; pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(sectionData); //skip the IMAGE_IMPORT_DESCRIPTOR offset += (DWORD)(numberOfImportDescriptors * sizeof(IMAGE_IMPORT_DESCRIPTOR)); for ( mapIt = moduleList.begin() ; mapIt != moduleList.end(); mapIt++ ) { importModuleThunk = &((*mapIt).second); stringLength = addImportDescriptor(importModuleThunk, offset); #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"fillImportSection :: importDesc.Name %X", pImportDescriptor->Name); #endif offset += (DWORD)stringLength; //stringLength has null termination char pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)sectionData + offset); //pThunk = (PIMAGE_THUNK_DATA)(getMemoryPointerFromRVA(importModuleThunk->firstThunk)); lastRVA = importModuleThunk->firstThunk - sizeof(DWORD_PTR); for ( mapIt2 = (*mapIt).second.thunkList.begin() ; mapIt2 != (*mapIt).second.thunkList.end(); mapIt2++ ) { importThunk = &((*mapIt2).second); pThunk = (PIMAGE_THUNK_DATA)(getMemoryPointerFromRVA(importThunk->rva)); //check wrong iat pointer if (!pThunk) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"fillImportSection :: Failed to get pThunk RVA: %X", importThunk->rva); #endif return 0; } if ((lastRVA + sizeof(DWORD_PTR)) != importThunk->rva) { //add additional import desc addSpecialImportDescriptor(importThunk->rva); } lastRVA = importThunk->rva; #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"fillImportSection :: importThunk %X pThunk %X pImportByName %X offset %X", importThunk,pThunk,pImportByName,offset); #endif stringLength = addImportToImportTable(importThunk, pThunk, pImportByName, offset); offset += (DWORD)stringLength; //is 0 bei import by ordinal pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)pImportByName + stringLength); } pImportDescriptor++; } return offset; } bool ImportRebuild::rebuildImportTable(const WCHAR * targetFilePath, const WCHAR * newFilePath, std::map & moduleList) { bool retValue = false; if (loadTargetFile(targetFilePath)) { splitTargetFile(); retValue = buildNewImportTable(moduleList); if (retValue) { retValue = saveNewFile(newFilePath); } return retValue; } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"rebuildImportTable ::Failed to load target %s", targetFilePath); #endif return false; } } 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; } } } size_t ImportRebuild::addImportToImportTable( ImportThunk * pImport, PIMAGE_THUNK_DATA pThunk, PIMAGE_IMPORT_BY_NAME pImportByName, DWORD sectionOffset) { size_t stringLength = 0; if(pImport->name[0] == '\0') { pThunk->u1.AddressOfData = (IMAGE_ORDINAL(pImport->ordinal) | IMAGE_ORDINAL_FLAG); } else { pImportByName->Hint = pImport->hint; stringLength = strlen(pImport->name) + 1; memcpy(pImportByName->Name, pImport->name, stringLength); pThunk->u1.AddressOfData = convertOffsetToRVAVector(vecSectionHeaders[importSectionIndex].PointerToRawData + sectionOffset); if (!pThunk->u1.AddressOfData) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"addImportToImportTable :: failed to get AddressOfData %X %X", vecSectionHeaders[importSectionIndex].PointerToRawData, sectionOffset); #endif } //next import should be nulled pThunk++; pThunk->u1.AddressOfData = 0; #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"addImportToImportTable :: pThunk->u1.AddressOfData %X %X %X", pThunk->u1.AddressOfData, pThunk, vecSectionHeaders[importSectionIndex].PointerToRawData + sectionOffset); #endif stringLength += sizeof(WORD); } return stringLength; } size_t ImportRebuild::addImportDescriptor(ImportModuleThunk * pImportModule, DWORD sectionOffset) { char dllName[MAX_PATH]; - size_t stringLength = 0; - wcstombs_s(&stringLength, dllName, (size_t)_countof(dllName), pImportModule->moduleName, (size_t)_countof(pImportModule->moduleName)); + StringConversion::ToASCII(pImportModule->moduleName, dllName, _countof(dllName)); + size_t stringLength = strlen(dllName); memcpy((vecSectionData[importSectionIndex] + sectionOffset), dllName, stringLength); //copy module name to section pImportDescriptor->FirstThunk = (DWORD)pImportModule->firstThunk; pImportDescriptor->Name = (DWORD)convertOffsetToRVAVector(vecSectionHeaders[importSectionIndex].PointerToRawData + sectionOffset); return stringLength; } void ImportRebuild::addSpecialImportDescriptor(DWORD_PTR rvaFirstThunk) { PIMAGE_IMPORT_DESCRIPTOR oldID = pImportDescriptor; pImportDescriptor++; pImportDescriptor->FirstThunk = (DWORD)rvaFirstThunk; pImportDescriptor->Name = oldID->Name; } void ImportRebuild::calculateImportSizes(std::map & moduleList) { std::map::iterator mapIt; std::map::iterator mapIt2; DWORD_PTR lastRVA = 0; numberOfImportDescriptors = 0; sizeOfImportSection = 0; sizeOfApiAndModuleNames = 0; numberOfImportDescriptors = moduleList.size() + 1; //last is zero'd for ( mapIt = moduleList.begin() ; mapIt != moduleList.end(); mapIt++ ) { lastRVA = (*mapIt).second.firstThunk - sizeof(DWORD_PTR); sizeOfApiAndModuleNames += (DWORD)(wcslen((*mapIt).second.moduleName) + 1); for ( mapIt2 = (*mapIt).second.thunkList.begin() ; mapIt2 != (*mapIt).second.thunkList.end(); mapIt2++ ) { if ((lastRVA + sizeof(DWORD_PTR)) != (*mapIt2).second.rva) { numberOfImportDescriptors++; //add additional import desc } if((*mapIt2).second.name[0] != '\0') { sizeOfApiAndModuleNames += sizeof(WORD); //Hint from IMAGE_IMPORT_BY_NAME sizeOfApiAndModuleNames += (DWORD)(strlen((*mapIt2).second.name) + 1); } lastRVA = (*mapIt2).second.rva; } } sizeOfImportSection = sizeOfApiAndModuleNames + (numberOfImportDescriptors * sizeof(IMAGE_IMPORT_DESCRIPTOR)); } \ No newline at end of file diff --git a/Scylla/ImportRebuild.h b/Scylla/ImportRebuild.h index f4fff23..27bad7f 100644 --- a/Scylla/ImportRebuild.h +++ b/Scylla/ImportRebuild.h @@ -1,71 +1,73 @@ #pragma once +#include #include "ProcessAccessHelp.h" #include "Thunks.h" -class ImportRebuild { +class ImportRebuild +{ public: ImportRebuild(); ~ImportRebuild(); 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; size_t sizeOfFile; BYTE * pDosStub; IMAGE_DOS_HEADER DosHeader; IMAGE_NT_HEADERS NTHeader; BYTE * pOverlay; size_t sizeOfOverlay; PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; PIMAGE_THUNK_DATA pThunkData; PIMAGE_IMPORT_BY_NAME pImportByName; size_t numberOfImportDescriptors; size_t sizeOfImportSection; size_t sizeOfApiAndModuleNames; size_t importSectionIndex; DWORD getOffsetLastSection(); void updatePeHeader(); DWORD fillImportSection( std::map & moduleList ); bool splitTargetFile(); 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 ); size_t addImportToImportTable( ImportThunk * pImport, PIMAGE_THUNK_DATA pThunk, PIMAGE_IMPORT_BY_NAME pImportByName, DWORD sectionOffset); size_t addImportDescriptor(ImportModuleThunk * pImportModule, DWORD sectionOffset); void calculateImportSizes(std::map & moduleList); void addSpecialImportDescriptor(DWORD_PTR rvaFirstThunk); }; \ No newline at end of file diff --git a/Scylla/MainGui.h b/Scylla/MainGui.h index 592ba90..abc758e 100644 --- a/Scylla/MainGui.h +++ b/Scylla/MainGui.h @@ -1,301 +1,295 @@ #pragma once #include #include "resource.h" // WTL #include // base ATL classes #include // base WTL classes #include // ATL GUI classes #include // WTL window frame helpers #include // WTL utility classes #include // WTL enhanced msg map macros #include // WTL controls #include // WTL dialog data exchange #include "multitree.h" #include "hexedit.h" -//#define _CRTDBG_MAP_ALLOC -//#include -//#include - -//#include - #include "Logger.h" #include "ProcessLister.h" #include "IATSearch.h" #include "PickDllGui.h" #include "ImportsHandling.h" class MainGui : public CDialogImpl, public CWinDataExchange, public CDialogResize, public CMessageFilter { public: enum { IDD = IDD_DLG_MAIN }; // Dialog Data eXchange, attaches/subclasses child controls to wrappers // DDX_CONTROL : subclass // DDX_CONTROL_HANDLE : attach BEGIN_DDX_MAP(MainGui) DDX_CONTROL(IDC_TREE_IMPORTS, TreeImportsSubclass) // needed for message reflection DDX_CONTROL(IDC_TREE_IMPORTS, TreeImports) DDX_CONTROL_HANDLE(IDC_CBO_PROCESSLIST, ComboProcessList) DDX_CONTROL_HANDLE(IDC_LIST_LOG, ListLog) DDX_CONTROL(IDC_EDIT_OEPADDRESS, EditOEPAddress) DDX_CONTROL(IDC_EDIT_IATADDRESS, EditIATAddress) DDX_CONTROL(IDC_EDIT_IATSIZE, EditIATSize) END_DDX_MAP() // Our message map // Messages are passed from top to bottom // The first handler that doesn't call SetMsgHandled(FALSE) aborts the chain // If none signals the message as handled, it will be passed to mixins (CHAIN_MSG_MAP) // or ultimately passed to DefWindowProc BEGIN_MSG_MAP_EX(MainGui) MSG_WM_INITDIALOG(OnInitDialog) MSG_WM_DESTROY(OnDestroy) MSG_WM_SIZE(OnSize) MSG_WM_CONTEXTMENU(OnContextMenu) MSG_WM_COMMAND(OnCommand) NOTIFY_HANDLER_EX(IDC_TREE_IMPORTS, NM_DBLCLK, OnTreeImportsDoubleClick) NOTIFY_HANDLER_EX(IDC_TREE_IMPORTS, TVN_KEYDOWN, OnTreeImportsKeyDown) COMMAND_HANDLER_EX(IDC_CBO_PROCESSLIST, CBN_DROPDOWN, OnProcessListDrop) COMMAND_HANDLER_EX(IDC_CBO_PROCESSLIST, CBN_SELENDOK, OnProcessListSelected) COMMAND_ID_HANDLER_EX(IDC_BTN_PICKDLL, OnPickDLL) COMMAND_ID_HANDLER_EX(IDC_BTN_OPTIONS, OnOptions) COMMAND_ID_HANDLER_EX(IDC_BTN_DUMP, OnDump) COMMAND_ID_HANDLER_EX(IDC_BTN_FIXDUMP, OnFixDump) COMMAND_ID_HANDLER_EX(IDC_BTN_PEREBUILD, OnPERebuild) COMMAND_ID_HANDLER_EX(IDC_BTN_IATAUTOSEARCH, OnIATAutoSearch) COMMAND_ID_HANDLER_EX(IDC_BTN_GETIMPORTS, OnGetImports) COMMAND_ID_HANDLER_EX(IDC_BTN_INVALIDIMPORTS, OnInvalidImports) COMMAND_ID_HANDLER_EX(IDC_BTN_SUSPECTIMPORTS, OnSuspectImports) COMMAND_ID_HANDLER_EX(IDC_BTN_CLEARIMPORTS, OnClearImports) COMMAND_ID_HANDLER_EX(ID_FILE_DUMP, OnDump) COMMAND_ID_HANDLER_EX(ID_FILE_PEREBUILD, OnPERebuild) COMMAND_ID_HANDLER_EX(ID_FILE_FIXDUMP, OnFixDump) COMMAND_ID_HANDLER_EX(ID_FILE_EXIT, OnExit) COMMAND_ID_HANDLER_EX(ID_IMPORTS_SHOWINVALID, OnInvalidImports) COMMAND_ID_HANDLER_EX(ID_IMPORTS_SHOWSUSPECT, OnSuspectImports) COMMAND_ID_HANDLER_EX(ID_IMPORTS_INVALIDATESELECTED, OnInvalidateSelected) COMMAND_ID_HANDLER_EX(ID_IMPORTS_CUTSELECTED, OnCutSelected) COMMAND_ID_HANDLER_EX(ID_IMPORTS_CLEARIMPORTS, OnClearImports) COMMAND_ID_HANDLER_EX(ID_IMPORTS_SAVETREE, OnSaveTree) COMMAND_ID_HANDLER_EX(ID_IMPORTS_LOADTREE, OnLoadTree) COMMAND_ID_HANDLER_EX(ID_TRACE_AUTOTRACE, OnAutotrace) COMMAND_ID_HANDLER_EX(ID_MISC_DLLINJECTION, OnDLLInject) COMMAND_ID_HANDLER_EX(ID_MISC_OPTIONS, OnOptions) COMMAND_ID_HANDLER_EX(ID_HELP_ABOUT, OnAbout) COMMAND_ID_HANDLER_EX(IDCANCEL, OnExit) REFLECT_NOTIFY_ID(IDC_TREE_IMPORTS) // pass WM_NOTIFY to child control CHAIN_MSG_MAP(CDialogResize) // Message map for subclassed treeview // CContainedWindow forwards all messages to this map ALT_MSG_MAP(IDC_TREE_IMPORTS) MSG_WM_GETDLGCODE(OnTreeImportsSubclassGetDlgCode) MSG_WM_CHAR(OnTreeImportsSubclassChar) END_MSG_MAP() // Dialog resize 'table' // States if child controls move or resize or center in a specific direction // when the parent dialog is resized BEGIN_DLGRESIZE_MAP(MainGui) DLGRESIZE_CONTROL(IDC_GROUP_ATTACH, DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_CBO_PROCESSLIST, DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_BTN_PICKDLL, DLSZ_MOVE_X) DLGRESIZE_CONTROL(IDC_GROUP_IMPORTS, DLSZ_SIZE_X | DLSZ_SIZE_Y) DLGRESIZE_CONTROL(IDC_TREE_IMPORTS, DLSZ_SIZE_X | DLSZ_SIZE_Y) DLGRESIZE_CONTROL(IDC_BTN_INVALIDIMPORTS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_SUSPECTIMPORTS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_CLEARIMPORTS, DLSZ_MOVE_X | DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_GROUP_IATINFO, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_STATIC_OEPADDRESS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_STATIC_IATADDRESS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_STATIC_IATSIZE, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_EDIT_OEPADDRESS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_EDIT_IATADDRESS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_EDIT_IATSIZE, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_IATAUTOSEARCH, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_GETIMPORTS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_GROUP_ACTIONS, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_AUTOTRACE, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_GROUP_DUMP, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_DUMP, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_PEREBUILD, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_FIXDUMP, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_GROUP_LOG, DLSZ_MOVE_Y | DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_LIST_LOG, DLSZ_MOVE_Y | DLSZ_SIZE_X) END_DLGRESIZE_MAP() MainGui(); //void addTextToOutputLog(const WCHAR * text); //CWindow getLogListboxHandle() const { return ListLog; } protected: // Variables WCHAR stringBuffer[600]; ImportsHandling importsHandling; //ProcessAccessHelp processAccessHelp; ApiReader apiReader; Process * selectedProcess; // File selection filters static const WCHAR filterExe[]; static const WCHAR filterDll[]; static const WCHAR filterExeDll[]; static const WCHAR filterTxt[]; static const WCHAR filterXml[]; // Controls CMultiSelectTreeViewCtrl TreeImports; CComboBox ComboProcessList; CHexEdit EditOEPAddress; CHexEdit EditIATAddress; CHexEdit EditIATSize; CListBox ListLog; CStatusBarCtrl StatusBar; enum StatusParts { PART_COUNT = 0, PART_INVALID, PART_IMAGEBASE, PART_MODULE }; CContainedWindow TreeImportsSubclass; // Handles CIcon hIcon; CMenu hMenuImports; CMenu hMenuLog; CAccelerator accelerators; CIcon hIconCheck; CIcon hIconWarning; CIcon hIconError; static const int MenuImportsOffsetTrace = 2; static const int MenuImportsTraceOffsetScylla = 2; static const int MenuImportsTraceOffsetImpRec = 4; static const int PLUGIN_MENU_BASE_ID = 0x10; protected: virtual BOOL PreTranslateMessage(MSG* pMsg); // Message handlers BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); void OnDestroy(); void OnSize(UINT nType, CSize size); void OnContextMenu(CWindow wnd, CPoint point); void OnCommand(UINT uNotifyCode, int nID, CWindow wndCtl); // WM_NOTIFY handlers LRESULT OnTreeImportsDoubleClick(const NMHDR* pnmh); LRESULT OnTreeImportsKeyDown(const NMHDR* pnmh); // Forwarded messages from subclassed treeview UINT OnTreeImportsSubclassGetDlgCode(const MSG * lpMsg); void OnTreeImportsSubclassChar(UINT nChar, UINT nRepCnt, UINT nFlags); // WM_COMMAND handlers void OnProcessListDrop(UINT uNotifyCode, int nID, CWindow wndCtl); void OnProcessListSelected(UINT uNotifyCode, int nID, CWindow wndCtl); void OnPickDLL(UINT uNotifyCode, int nID, CWindow wndCtl); void OnOptions(UINT uNotifyCode, int nID, CWindow wndCtl); void OnDump(UINT uNotifyCode, int nID, CWindow wndCtl); void OnFixDump(UINT uNotifyCode, int nID, CWindow wndCtl); void OnPERebuild(UINT uNotifyCode, int nID, CWindow wndCtl); void OnDLLInject(UINT uNotifyCode, int nID, CWindow wndCtl); void OnIATAutoSearch(UINT uNotifyCode, int nID, CWindow wndCtl); void OnGetImports(UINT uNotifyCode, int nID, CWindow wndCtl); void OnInvalidImports(UINT uNotifyCode, int nID, CWindow wndCtl); void OnSuspectImports(UINT uNotifyCode, int nID, CWindow wndCtl); void OnClearImports(UINT uNotifyCode, int nID, CWindow wndCtl); void OnInvalidateSelected(UINT uNotifyCode, int nID, CWindow wndCtl); void OnCutSelected(UINT uNotifyCode, int nID, CWindow wndCtl); void OnSaveTree(UINT uNotifyCode, int nID, CWindow wndCtl); void OnLoadTree(UINT uNotifyCode, int nID, CWindow wndCtl); void OnAutotrace(UINT uNotifyCode, int nID, CWindow wndCtl); void OnExit(UINT uNotifyCode, int nID, CWindow wndCtl); void OnAbout(UINT uNotifyCode, int nID, CWindow wndCtl); // GUI functions bool showFileDialog(WCHAR * selectedFile, bool save, const WCHAR * defFileName, const WCHAR * filter = NULL, const WCHAR * defExtension = NULL, const WCHAR * directory = NULL); void setupStatusBar(); void updateStatusBar(); void fillProcessListComboBox(CComboBox& hCombo); void setIconAndDialogCaption(); void enableDialogControls(BOOL value); CTreeItem findTreeItem(CPoint pt, bool screenCoordinates); // Actions void pickDllActionHandler(); void pickApiActionHandler(CTreeItem item); void processSelectedActionHandler(int index); void showInvalidImportsActionHandler(); void showSuspectImportsActionHandler(); void deleteSelectedImportsActionHandler(); void invalidateSelectedImportsActionHandler(); void loadTreeActionHandler(); void saveTreeActionHandler(); void iatAutosearchActionHandler(); void getImportsActionHandler(); void dumpActionHandler(); void peRebuildActionHandler(); void startDisassemblerGui(CTreeItem selectedTreeNode); void dumpFixActionHandler(); void showAboutDialog(); void dllInjectActionHandler(); void optionsActionHandler(); void clearImportsActionHandler(); void pluginActionHandler(int menuItem); // Popup menu functions void SetupImportsMenuItems(CTreeItem item); void appendPluginListToMenu(CMenuHandle hMenuTrackPopup); void DisplayContextMenuImports(CWindow, CPoint); void DisplayContextMenuLog(CWindow, CPoint); // Log void clearOutputLog(); bool saveLogToFile(const WCHAR * file); // Misc bool getCurrentModulePath(TCHAR * buffer, size_t bufferSize); }; diff --git a/Scylla/PeDump.h b/Scylla/PeDump.h index 6f0bcfd..6f7785e 100644 --- a/Scylla/PeDump.h +++ b/Scylla/PeDump.h @@ -1,63 +1,67 @@ +#pragma once #include -class PeDump { +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 index 0d50ec6..a8ea49a 100644 --- a/Scylla/PeRebuild.cpp +++ b/Scylla/PeRebuild.cpp @@ -1,736 +1,732 @@ #include "PeRebuild.h" #include -#pragma comment(lib,"imagehlp.lib") - #include "ProcessAccessHelp.h" - #include "Scylla.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 Scylla::debugLog.log(L"SetEndOfFile failed error %d", 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) { return (dwTarNum % dwRoundNum) == 0; // WTF: /* #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 Scylla::debugLog.log(L"realignPE :: malloc failed with dwTmpNum %08X %08X", 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 Scylla::debugLog.log(L"realignPE :: Exception occured"); #endif return 0; } if (Scylla::config[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 Scylla::debugLog.log(L"updatePeHeaderChecksum :: CheckSumMappedFile failed error %X", GetLastError()); #endif return false; } #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"Old checksum %08X new checksum %08X", 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 Scylla::debugLog.log(L"createFileMappingView :: INVALID_HANDLE_VALUE %u", 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 Scylla::debugLog.log(L"createFileMappingViewFull :: hMappedFile == NULL"); #endif CloseHandle(hFileToMap); hMappedFile = 0; hFileToMap = 0; addrMappedDll = 0; return NULL; } if (GetLastError() == ERROR_ALREADY_EXISTS) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"createFileMappingView :: GetLastError() == ERROR_ALREADY_EXISTS"); #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 Scylla::debugLog.log(L"createFileMappingView :: addrMappedDll == NULL"); #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 Scylla::debugLog.log(L"closeAllMappingHandles :: Could not flush memory to disk (%d)", 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 index a180cf4..032eb43 100644 --- a/Scylla/PeRebuild.h +++ b/Scylla/PeRebuild.h @@ -1,62 +1,66 @@ +#pragma once + #include -#include +#include -typedef enum _ReBaseErr +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 *****************************************************************************/ + +class PeRebuild +{ 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 + static const size_t MAX_SEC_NUM = 30; - const static DWORD ScanStartDS = 0x40; - const static int MinSectionTerm = 5; - const static int FileAlignmentConstant = 0x200; + static const DWORD ScanStartDS = 0x40; + static const int MinSectionTerm = 5; + static const 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/PickApiGui.h b/Scylla/PickApiGui.h index a8f8e19..912fa4b 100644 --- a/Scylla/PickApiGui.h +++ b/Scylla/PickApiGui.h @@ -1,94 +1,94 @@ #pragma once #include #include "resource.h" // WTL #include // base ATL classes #include // base WTL classes #include // ATL GUI classes #include // WTL window frame helpers #include // WTL utility classes #include // WTL enhanced msg map macros #include // WTL controls #include // WTL dialog data exchange #include -#include "ProcessAccessHelp.h" +#include "ProcessAccessHelp.h" // ModuleInfo, ApiInfo class PickApiGui : public CDialogImpl, public CWinDataExchange, public CDialogResize { public: enum { IDD = IDD_DLG_PICKAPI }; BEGIN_DDX_MAP(PickApiGui) DDX_CONTROL_HANDLE(IDC_CBO_DLLSELECT, ComboDllSelect) DDX_CONTROL_HANDLE(IDC_LIST_APISELECT, ListApiSelect) DDX_CONTROL_HANDLE(IDC_EDIT_APIFILTER, EditApiFilter) END_DDX_MAP() BEGIN_MSG_MAP(PickDllGui) MSG_WM_INITDIALOG(OnInitDialog) COMMAND_HANDLER_EX(IDC_CBO_DLLSELECT, CBN_SELENDOK, OnDllListSelected) COMMAND_HANDLER_EX(IDC_LIST_APISELECT, LBN_DBLCLK, OnApiListDoubleClick) COMMAND_HANDLER_EX(IDC_EDIT_APIFILTER, EN_UPDATE, OnApiFilterUpdated) COMMAND_ID_HANDLER_EX(IDC_BTN_PICKAPI_OK, OnOK) COMMAND_ID_HANDLER_EX(IDC_BTN_PICKAPI_CANCEL, OnCancel) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) CHAIN_MSG_MAP(CDialogResize) END_MSG_MAP() BEGIN_DLGRESIZE_MAP(PickApiGui) DLGRESIZE_CONTROL(IDC_GROUP_DLL, DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_CBO_DLLSELECT, DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_GROUP_APIS, DLSZ_SIZE_X | DLSZ_SIZE_Y) DLGRESIZE_CONTROL(IDC_LIST_APISELECT, DLSZ_SIZE_X | DLSZ_SIZE_Y) DLGRESIZE_CONTROL(IDC_STATIC_APIFILTER, DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_EDIT_APIFILTER, DLSZ_MOVE_Y | DLSZ_SIZE_X) DLGRESIZE_CONTROL(IDC_BTN_PICKAPI_OK, DLSZ_MOVE_X | DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_PICKAPI_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y) END_DLGRESIZE_MAP() PickApiGui(const std::vector &moduleList); ApiInfo* getSelectedApi() const { return selectedApi; } protected: // Variables const std::vector &moduleList; ApiInfo* selectedApi; // Controls CComboBox ComboDllSelect; CListBox ListApiSelect; CEdit EditApiFilter; protected: // Message handlers BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); void OnOK(UINT uNotifyCode, int nID, CWindow wndCtl); void OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl); void OnDllListSelected(UINT uNotifyCode, int nID, CWindow wndCtl); void OnApiListDoubleClick(UINT uNotifyCode, int nID, CWindow wndCtl); void OnApiFilterUpdated(UINT uNotifyCode, int nID, CWindow wndCtl); // Actions void actionApiSelected(); // GUI functions void fillDllComboBox(CComboBox& combo); void fillApiListBox(CListBox& list, const std::vector &apis); }; diff --git a/Scylla/PickDllGui.h b/Scylla/PickDllGui.h index 1814440..99fd1e5 100644 --- a/Scylla/PickDllGui.h +++ b/Scylla/PickDllGui.h @@ -1,89 +1,89 @@ #pragma once #include #include "resource.h" // WTL #include // base ATL classes #include // base WTL classes #include // ATL GUI classes #include // WTL window frame helpers #include // WTL utility classes #include // WTL enhanced msg map macros #include // WTL controls #include // WTL dialog data exchange #include -#include "ProcessAccessHelp.h" +#include "ProcessAccessHelp.h" // ModuleInfo class PickDllGui : public CDialogImpl, public CWinDataExchange, public CDialogResize { public: enum { IDD = IDD_DLG_PICKDLL }; BEGIN_DDX_MAP(PickDllGui) DDX_CONTROL_HANDLE(IDC_LIST_DLLSELECT, ListDLLSelect) END_DDX_MAP() BEGIN_MSG_MAP(PickDllGui) MSG_WM_INITDIALOG(OnInitDialog) NOTIFY_HANDLER_EX(IDC_LIST_DLLSELECT, LVN_COLUMNCLICK, OnListDllColumnClicked) NOTIFY_HANDLER_EX(IDC_LIST_DLLSELECT, NM_DBLCLK, OnListDllDoubleClick) COMMAND_ID_HANDLER_EX(IDC_BTN_PICKDLL_OK, OnOK) COMMAND_ID_HANDLER_EX(IDC_BTN_PICKDLL_CANCEL, OnCancel) COMMAND_ID_HANDLER_EX(IDCANCEL, OnCancel) CHAIN_MSG_MAP(CDialogResize) END_MSG_MAP() BEGIN_DLGRESIZE_MAP(PickDllGui) DLGRESIZE_CONTROL(IDC_LIST_DLLSELECT, DLSZ_SIZE_X | DLSZ_SIZE_Y) DLGRESIZE_CONTROL(IDC_BTN_PICKDLL_OK, DLSZ_MOVE_X | DLSZ_MOVE_Y) DLGRESIZE_CONTROL(IDC_BTN_PICKDLL_CANCEL, DLSZ_MOVE_X | DLSZ_MOVE_Y) END_DLGRESIZE_MAP() PickDllGui(std::vector &moduleList); ModuleInfo* getSelectedModule() const { return selectedModule; } protected: // Variables std::vector &moduleList; ModuleInfo* selectedModule; // Controls CListViewCtrl ListDLLSelect; enum ListColumns { COL_NAME = 0, COL_IMAGEBASE, COL_IMAGESIZE, COL_PATH }; int prevColumn; bool ascending; protected: // Message handlers BOOL OnInitDialog(CWindow wndFocus, LPARAM lInitParam); LRESULT OnListDllColumnClicked(NMHDR* pnmh); LRESULT OnListDllDoubleClick(NMHDR* pnmh); void OnOK(UINT uNotifyCode, int nID, CWindow wndCtl); void OnCancel(UINT uNotifyCode, int nID, CWindow wndCtl); // GUI functions void addColumnsToModuleList(CListViewCtrl& list); void displayModuleList(CListViewCtrl& list); static int CALLBACK listviewCompareFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort); }; diff --git a/Scylla/PluginLoader.cpp b/Scylla/PluginLoader.cpp index f39a829..3e03b8c 100644 --- a/Scylla/PluginLoader.cpp +++ b/Scylla/PluginLoader.cpp @@ -1,361 +1,360 @@ #include "PluginLoader.h" #include "Logger.h" #include "ProcessAccessHelp.h" +#include "StringConversion.h" #include -#include const WCHAR PluginLoader::PLUGIN_DIR[] = L"Plugins\\"; const WCHAR PluginLoader::PLUGIN_SEARCH_STRING[] = L"*.dll"; const WCHAR PluginLoader::PLUGIN_IMPREC_DIR[] = L"ImpRec_Plugins\\"; const WCHAR PluginLoader::PLUGIN_IMPREC_WRAPPER_DLL[] = L"Imprec_Wrapper_DLL.dll"; //#define DEBUG_COMMENTS std::vector & PluginLoader::getScyllaPluginList() { return scyllaPluginList; } std::vector & PluginLoader::getImprecPluginList() { return imprecPluginList; } bool PluginLoader::findAllPlugins() { if (!scyllaPluginList.empty()) { scyllaPluginList.clear(); } if (!imprecPluginList.empty()) { imprecPluginList.clear(); } if (!buildSearchString()) { return false; } if (!searchForPlugin(scyllaPluginList, dirSearchString, true)) { return false; } #ifndef _WIN64 if (!buildSearchStringImprecPlugins()) { return false; } if (!searchForPlugin(imprecPluginList, dirSearchString, false)) { return false; } #endif return true; } bool PluginLoader::searchForPlugin(std::vector & newPluginList, const WCHAR * searchPath, bool isScyllaPlugin) { WIN32_FIND_DATA ffd; HANDLE hFind = 0; DWORD dwError = 0; Plugin pluginData; hFind = FindFirstFile(searchPath, &ffd); dwError = GetLastError(); if (dwError == ERROR_FILE_NOT_FOUND) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findAllPlugins :: No files found"); #endif return true; } if (hFind == INVALID_HANDLE_VALUE) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findAllPlugins :: FindFirstFile failed %d", dwError); #endif return false; } do { if ( !(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) { if ((ffd.nFileSizeHigh != 0) || (ffd.nFileSizeLow < 200)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findAllPlugins :: Plugin invalid file size: %s", 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 Scylla::debugLog.log(L"findAllPlugins :: Plugin %s", pluginData.fullpath); #endif if (isValidDllFile(pluginData.fullpath)) { if (isScyllaPlugin) { if (getScyllaPluginName(&pluginData)) { //add valid plugin newPluginList.push_back(pluginData); } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"Cannot get scylla plugin name %s", pluginData.fullpath); #endif } } else { if (isValidImprecPlugin(pluginData.fullpath)) { wcscpy_s(pluginData.pluginName, MAX_PATH, ffd.cFileName); newPluginList.push_back(pluginData); } } } } } } while (FindNextFile(hFind, &ffd) != 0); dwError = GetLastError(); FindClose(hFind); if (dwError == ERROR_NO_MORE_FILES) { return true; } else { return false; } } bool PluginLoader::getScyllaPluginName(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 Scylla::debugLog.log(L"getPluginName :: Plugin name %s", 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); + StringConversion::ToUTF16(pluginName, pluginData->pluginName, _countof(pluginData->pluginName)); #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"getPluginName :: Plugin name mbstowcs_s %s", pluginData->pluginName); #endif - if (convertedChars > 1) + if (wcslen(pluginData->pluginName) > 1) { retValue = true; } else { retValue = false; } } else { retValue = false; } } FreeLibrary(hModule); return retValue; } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"getPluginName :: LoadLibraryEx failed %s", 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 Scylla::debugLog.log(L"buildSearchString :: GetModuleFileName failed %d", GetLastError()); #endif return false; } //wprintf(L"dirSearchString 1 %s\n\n", dirSearchString); PathRemoveFileSpec(dirSearchString); //wprintf(L"dirSearchString 2 %s\n\n", dirSearchString); PathAppend(dirSearchString, PLUGIN_DIR); wcscpy_s(baseDirPath, _countof(baseDirPath), dirSearchString); wcscat_s(dirSearchString, _countof(dirSearchString), PLUGIN_SEARCH_STRING); //wprintf(L"dirSearchString 3 %s\n\n", dirSearchString); #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"dirSearchString final %s", 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; } bool PluginLoader::isValidImprecPlugin(const WCHAR * fullpath) { def_Imprec_Trace Imprec_Trace = 0; bool retValue = false; HMODULE hModule = LoadLibraryEx(fullpath, 0, DONT_RESOLVE_DLL_REFERENCES); //do not call DllMain if (hModule) { Imprec_Trace = (def_Imprec_Trace)GetProcAddress(hModule, "Trace"); if (Imprec_Trace) { retValue = true; } else { retValue = false; } FreeLibrary(hModule); return retValue; } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"isValidImprecPlugin :: LoadLibraryEx failed %s", pluginData->fullpath); #endif return false; } } bool PluginLoader::buildSearchStringImprecPlugins() { wcscpy_s(dirSearchString, _countof(dirSearchString), baseDirPath); wcscat_s(dirSearchString, _countof(dirSearchString), PLUGIN_IMPREC_DIR); wcscpy_s(baseDirPath, _countof(baseDirPath), dirSearchString); //build imprec wrapper dll path wcscpy_s(imprecWrapperDllPath, _countof(imprecWrapperDllPath), dirSearchString); wcscat_s(imprecWrapperDllPath, _countof(imprecWrapperDllPath), PLUGIN_IMPREC_WRAPPER_DLL); if (!fileExists(imprecWrapperDllPath)) { return false; } wcscat_s(dirSearchString, _countof(dirSearchString), PLUGIN_SEARCH_STRING); return true; } bool PluginLoader::fileExists(const WCHAR * fileName) { if (GetFileAttributesW(fileName) == INVALID_FILE_ATTRIBUTES) { return false; } else { return true; } } diff --git a/Scylla/ProcessAccessHelp.cpp b/Scylla/ProcessAccessHelp.cpp index 01e1464..cfb7f3d 100644 --- a/Scylla/ProcessAccessHelp.cpp +++ b/Scylla/ProcessAccessHelp.cpp @@ -1,715 +1,715 @@ #include "ProcessAccessHelp.h" -#include "Logger.h" +#include "Scylla.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]; unsigned int ProcessAccessHelp::decomposerInstructionsCount = 0; _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 Scylla::debugLog.log(L"openProcessHandle :: There is already a process handle, HANDLE %X", 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 Scylla::debugLog.log(L"openProcessHandle :: Failed to open handle, PID %X", dwPID); #endif return false; } } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"openProcessHandle :: Wrong PID, PID %X", 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 Scylla::debugLog.log(L"NativeOpenProcess :: Failed to open handle, PID %X Error 0x%X", 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 Scylla::debugLog.log(L"readMemoryFromProcess :: hProcess == NULL"); #endif return returnValue; } if (!ReadProcessMemory(hProcess, (LPVOID)address, dataBuffer, size, &lpNumberOfBytesRead)) { if (!VirtualProtectEx(hProcess, (LPVOID)address, size, PAGE_READWRITE, &dwProtect)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryFromProcess :: Error VirtualProtectEx %X %X err: %u", address,size, GetLastError()); #endif returnValue = false; } else { if (!ReadProcessMemory(hProcess, (LPVOID)address, dataBuffer, size, &lpNumberOfBytesRead)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryFromProcess :: Error ReadProcessMemory %X %X err: %u", 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 Scylla::debugLog.log(L"readMemoryFromProcess :: Error ReadProcessMemory read %d bytes requested %d bytes", 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 Scylla::debugLog.log(L"decomposeMemory :: distorm_decompose == DECRES_INPUTERR"); #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 Scylla::debugLog.log(L"disassembleMemory :: res == DECRES_INPUTERR"); #endif return false; } else if (res == DECRES_SUCCESS) { //printf("disassembleMemory :: res == DECRES_SUCCESS\n"); return true; } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"disassembleMemory :: res == %d", 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 Scylla::debugLog.log(L"ProcessAccessHelp::getFileSize :: GetFileSizeEx failed %u", GetLastError()); #endif return 0; } else { return lpFileSize.QuadPart; } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"ProcessAccessHelp::getFileSize hFile invalid"); #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 Scylla::debugLog.log(L"readMemoryFromFile :: SetFilePointer failed error %u", dwError); #endif return false; } else { if (ReadFile(hFile, dataBuffer, size, &lpNumberOfBytesRead, 0)) { return true; } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryFromFile :: ReadFile failed - size %d - error %u", size, GetLastError()); #endif return false; } } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryFromFile :: hFile invalid"); #endif return false; } } bool ProcessAccessHelp::writeMemoryToFile(HANDLE hFile, LONG offset, DWORD size, LPCVOID 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 Scylla::debugLog.log(L"writeMemoryToFile :: SetFilePointer failed error %u", dwError); #endif return false; } else { if (WriteFile(hFile, dataBuffer, size, &lpNumberOfBytesWritten, 0)) { return true; } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"writeMemoryToFile :: WriteFile failed - size %d - error %u", size, GetLastError()); #endif return false; } } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"writeMemoryToFile :: hFile invalid"); #endif return false; } } bool ProcessAccessHelp::writeMemoryToFileEnd(HANDLE hFile, DWORD size, LPCVOID 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 Scylla::debugLog.log(L"writeMemoryToFileEnd :: WriteFile failed - size %d - error %u", size, GetLastError()); #endif return false; } } else { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"writeMemoryToFileEnd :: hFile invalid"); #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 Scylla::debugLog.log(L"readHeaderFromFile :: INVALID_HANDLE_VALUE %u", 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 Scylla::debugLog.log(L"createFileMappingView :: INVALID_HANDLE_VALUE %u", GetLastError()); #endif return NULL; } HANDLE hMappedFile = CreateFileMapping(hFile, NULL, flProtect, 0, 0, NULL); CloseHandle(hFile); if( hMappedFile == NULL ) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"createFileMappingView :: hMappedFile == NULL"); #endif return NULL; } if (GetLastError() == ERROR_ALREADY_EXISTS) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"createFileMappingView :: GetLastError() == ERROR_ALREADY_EXISTS); #endif return NULL; } LPVOID addrMappedDll = MapViewOfFile(hMappedFile, accessMap, 0, 0, 0); if( addrMappedDll == NULL ) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"createFileMappingView :: addrMappedDll == NULL"); #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 Scylla::debugLog.log(L"getProcessByName :: Error getting first Process"); #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(L"\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 Scylla::debugLog.log(L"getMemoryRegionFromAddress :: VirtualQueryEx error %u", 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 Scylla::debugLog.log(L"getSizeOfImageProcess :: VirtualQuery failed %X", 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; } 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; //.bak + null BOOL retValue = 0; WCHAR * backupFile = new WCHAR[fileNameLength]; wcscpy_s(backupFile, fileNameLength, filePath); wcscat_s(backupFile, fileNameLength, L".bak"); retValue = CopyFile(filePath, backupFile, FALSE); if (!retValue) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"createBackupFile :: CopyFile failed with error 0x%X", GetLastError()); #endif } delete [] backupFile; return retValue != 0; } diff --git a/Scylla/ProcessAccessHelp.h b/Scylla/ProcessAccessHelp.h index 5eb3a93..a219673 100644 --- a/Scylla/ProcessAccessHelp.h +++ b/Scylla/ProcessAccessHelp.h @@ -1,202 +1,206 @@ #pragma once -#include #include #include -#include -#include +#include /************************************************************************/ /* distorm */ /************************************************************************/ -#include "distorm.h" +#include // 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 { +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; } const WCHAR * getFilename() const { const WCHAR* slash = wcsrchr(fullPath, L'\\'); if(slash) { return slash+1; } return fullPath; } }; -class ApiInfo { - public: - char name[MAX_PATH]; - WORD hint; - DWORD_PTR va; - DWORD_PTR rva; - WORD ordinal; - bool isForwarded; - ModuleInfo * module; +class ApiInfo +{ +public: + + char name[MAX_PATH]; + WORD hint; + DWORD_PTR va; + DWORD_PTR rva; + WORD ordinal; + bool isForwarded; + ModuleInfo * module; }; -class ProcessAccessHelp { +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 const size_t 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, LPCVOID dataBuffer); /* * Write memory to file end */ static bool writeMemoryToFileEnd(HANDLE hFile, DWORD size, LPCVOID 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 */ static 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 DWORD getEntryPointFromFile(const WCHAR * filePath); static bool createBackupFile(const WCHAR * filePath); }; diff --git a/Scylla/ProcessLister.h b/Scylla/ProcessLister.h index 4a61362..a93630e 100644 --- a/Scylla/ProcessLister.h +++ b/Scylla/ProcessLister.h @@ -1,73 +1,70 @@ #pragma once #include #include #include -#include - -#pragma comment(lib, "Psapi.lib") - +#include 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(L"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/SystemInformation.cpp b/Scylla/SystemInformation.cpp index da8918d..efc9f1c 100644 --- a/Scylla/SystemInformation.cpp +++ b/Scylla/SystemInformation.cpp @@ -1,76 +1,76 @@ #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(L"kernel32.dll"), "GetNativeSystemInfo"); if (_GetNativeSystemInfo) { _GetNativeSystemInfo(&si); } else { GetSystemInfo(&si); } bool isX64 = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64; bool isX86 = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL; DWORD major = osvi.dwMajorVersion; DWORD minor = osvi.dwMinorVersion; if(isX64 && major == 5 && minor == 2) { currenOS = WIN_XP_64; } else if(isX86 && major == 5 && minor == 1) { currenOS = WIN_XP_32; } - if(isX64 && major == 6 && minor == 0) + else if(isX64 && major == 6 && minor == 0) { currenOS = WIN_VISTA_64; } else if(isX86 && major == 6 && minor == 0) { currenOS = WIN_VISTA_32; } else if(isX64 && major == 6 && minor == 1) { currenOS = WIN_7_64; } else if(isX86 && major == 6 && minor == 1) { currenOS = WIN_7_32; } else if(isX64 && major == 6 && minor == 2) { currenOS = WIN_8_64; } else if(isX86 && major == 6 && minor == 2) { currenOS = WIN_8_32; } else { currenOS = UNKNOWN_OS; } return (currenOS != UNKNOWN_OS); } diff --git a/Scylla/SystemInformation.h b/Scylla/SystemInformation.h index e687a6a..aef7f69 100644 --- a/Scylla/SystemInformation.h +++ b/Scylla/SystemInformation.h @@ -1,24 +1,25 @@ #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, WIN_8_32, WIN_8_64 }; typedef void (WINAPI *def_GetNativeSystemInfo)(LPSYSTEM_INFO lpSystemInfo); -class SystemInformation { +class SystemInformation +{ public: + static OPERATING_SYSTEM currenOS; static bool getSystemInformation(); -private: }; diff --git a/Scylla/Thunks.h b/Scylla/Thunks.h index 9cb935b..5c31d19 100644 --- a/Scylla/Thunks.h +++ b/Scylla/Thunks.h @@ -1,44 +1,43 @@ #pragma once #include -#include #include // WTL #include #include -#include //CTreeItem +#include // CTreeItem class ImportThunk { public: WCHAR moduleName[MAX_PATH]; char name[MAX_PATH]; DWORD_PTR va; DWORD_PTR rva; WORD ordinal; DWORD_PTR apiAddressVA; WORD hint; bool valid; bool suspect; CTreeItem hTreeItem; DWORD_PTR key; void invalidate(); }; class ImportModuleThunk { public: WCHAR moduleName[MAX_PATH]; std::map thunkList; DWORD_PTR firstThunk; CTreeItem hTreeItem; DWORD_PTR key; DWORD_PTR getFirstThunk() const; bool isValid() const; }; diff --git a/Scylla/TreeImportExport.cpp b/Scylla/TreeImportExport.cpp index a6cf3e2..43eb64c 100644 --- a/Scylla/TreeImportExport.cpp +++ b/Scylla/TreeImportExport.cpp @@ -1,346 +1,337 @@ - #include "TreeImportExport.h" #include "Architecture.h" #include "Scylla.h" - -#define DEBUG_COMMENTS +#include "StringConversion.h" bool TreeImportExport::exportTreeList(const WCHAR * targetXmlFile, std::map & moduleList, const Process * process, const DWORD_PTR addressOEP, const DWORD_PTR addressIAT, const DWORD sizeIAT) { TiXmlDocument doc; - TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", "" ); + TiXmlDeclaration * decl = new TiXmlDeclaration( "1.0", "", ""); doc.LinkEndChild(decl); TiXmlElement * rootElement = new TiXmlElement("target"); setTargetInformation(rootElement, process,addressOEP,addressIAT,sizeIAT); addModuleListToRootElement(rootElement, moduleList); doc.LinkEndChild(rootElement); return saveXmlToFile(doc,targetXmlFile); } bool TreeImportExport::importTreeList(const WCHAR * targetXmlFile, std::map & moduleList, DWORD_PTR * addressOEP, DWORD_PTR * addressIAT, DWORD * sizeIAT) { TiXmlElement * targetElement; TiXmlDocument doc; char * buffer = readXmlFile(targetXmlFile); int count = 0; moduleList.clear(); if (buffer) { doc.Parse(buffer); if (doc.Error()) { Scylla::windowLog.log(L"Load Tree :: Error parsing xml %S: %S\r\n", doc.Value(), doc.ErrorDesc()); delete [] buffer; return false; } targetElement = doc.FirstChildElement(); *addressOEP = ConvertStringToDwordPtr(targetElement->Attribute("oep_va")); *addressIAT = ConvertStringToDwordPtr(targetElement->Attribute("iat_va")); *sizeIAT = (DWORD)ConvertStringToDwordPtr(targetElement->Attribute("iat_size")); //test = targetElement->Attribute("filename"); parseAllElementModules(targetElement, moduleList); delete [] buffer; } return true; } void TreeImportExport::setTargetInformation(TiXmlElement * rootElement, const Process * process, const DWORD_PTR addressOEP, const DWORD_PTR addressIAT, const DWORD sizeIAT) { - size_t stringLength = 0; - - wcstombs_s(&stringLength, xmlStringBuffer, (size_t)_countof(xmlStringBuffer), process->filename, (size_t)_countof(process->filename)); - - + StringConversion::ToASCII(process->filename, xmlStringBuffer, _countof(xmlStringBuffer)); rootElement->SetAttribute("filename", xmlStringBuffer); ConvertDwordPtrToString(addressOEP); - rootElement->SetAttribute("oep_va",xmlStringBuffer); + rootElement->SetAttribute("oep_va", xmlStringBuffer); ConvertDwordPtrToString(addressIAT); - rootElement->SetAttribute("iat_va",xmlStringBuffer); + rootElement->SetAttribute("iat_va", xmlStringBuffer); ConvertDwordPtrToString(sizeIAT); - rootElement->SetAttribute("iat_size",xmlStringBuffer); + rootElement->SetAttribute("iat_size", xmlStringBuffer); } char * TreeImportExport::readXmlFile(const WCHAR * xmlFilePath) { FILE * pFile = 0; long lSize = 0; char * buffer = 0; if (_wfopen_s(&pFile,xmlFilePath,L"r") == NULL) { fseek(pFile, 0, SEEK_END); lSize = ftell(pFile); fseek(pFile, 0, SEEK_SET); if (lSize > 2) { buffer = new char[lSize + sizeof(char)]; ZeroMemory(buffer, lSize + sizeof(char)); fread(buffer, sizeof(char), lSize, pFile); if (!feof(pFile) || ferror(pFile)) { delete [] buffer; buffer = 0; } } fclose (pFile); return buffer; } else { return 0; } } bool TreeImportExport::saveXmlToFile(TiXmlDocument doc, const WCHAR * xmlFilePath) { FILE * pFile = 0; if (_wfopen_s(&pFile,xmlFilePath,L"w") == NULL) { doc.Print(pFile); fclose (pFile); return true; } else { return false; } } void TreeImportExport::addModuleListToRootElement( TiXmlElement * rootElement, std::map & moduleList ) { std::map::iterator mapIt; std::map::iterator mapIt2; ImportModuleThunk * importModuleThunk = 0; ImportThunk * importThunk = 0; TiXmlElement * moduleElement; TiXmlElement * importElement; for ( mapIt = moduleList.begin() ; mapIt != moduleList.end(); mapIt++ ) { importModuleThunk = &((*mapIt).second); moduleElement = getModuleXmlElement(importModuleThunk); for ( mapIt2 = (*mapIt).second.thunkList.begin() ; mapIt2 != (*mapIt).second.thunkList.end(); mapIt2++ ) { importThunk = &((*mapIt2).second); importElement = getImportXmlElement(importThunk); moduleElement->LinkEndChild(importElement); } rootElement->LinkEndChild(moduleElement); } } TiXmlElement * TreeImportExport::getModuleXmlElement(const ImportModuleThunk * importModuleThunk) { - size_t stringLength = 0; TiXmlElement * moduleElement = new TiXmlElement("module"); - wcstombs_s(&stringLength, xmlStringBuffer, (size_t)_countof(xmlStringBuffer), importModuleThunk->moduleName, (size_t)_countof(importModuleThunk->moduleName)); - + StringConversion::ToASCII(importModuleThunk->moduleName, xmlStringBuffer, _countof(xmlStringBuffer)); moduleElement->SetAttribute("filename", xmlStringBuffer); ConvertDwordPtrToString(importModuleThunk->getFirstThunk()); moduleElement->SetAttribute("first_thunk_rva",xmlStringBuffer); return moduleElement; } TiXmlElement * TreeImportExport::getImportXmlElement(const ImportThunk * importThunk) { TiXmlElement * importElement = 0; if (importThunk->valid) { importElement = new TiXmlElement("import_valid"); if(importThunk->name[0] != '\0') { importElement->SetAttribute("name",importThunk->name); } ConvertWordToString(importThunk->ordinal); importElement->SetAttribute("ordinal",xmlStringBuffer); ConvertWordToString(importThunk->hint); importElement->SetAttribute("hint",xmlStringBuffer); ConvertBoolToString(importThunk->suspect); importElement->SetAttribute("suspect", xmlStringBuffer); } else { importElement = new TiXmlElement("import_invalid"); } ConvertDwordPtrToString(importThunk->rva); importElement->SetAttribute("iat_rva", xmlStringBuffer); ConvertDwordPtrToString(importThunk->apiAddressVA); importElement->SetAttribute("address_va",xmlStringBuffer); return importElement; } void TreeImportExport::ConvertBoolToString(const bool boolValue) { if (boolValue) { - strcpy_s(xmlStringBuffer,_countof(xmlStringBuffer),"1"); + strcpy_s(xmlStringBuffer,_countof(xmlStringBuffer), "1"); } else { - strcpy_s(xmlStringBuffer,_countof(xmlStringBuffer),"0"); + strcpy_s(xmlStringBuffer,_countof(xmlStringBuffer), "0"); } } bool TreeImportExport::ConvertStringToBool(const char * strValue) { if (strValue) { if (strValue[0] == '1') { return true; } } return false; } void TreeImportExport::ConvertDwordPtrToString(const DWORD_PTR dwValue) { sprintf_s(xmlStringBuffer, _countof(xmlStringBuffer), PRINTF_DWORD_PTR_FULL_S, dwValue); } DWORD_PTR TreeImportExport::ConvertStringToDwordPtr(const char * strValue) { DWORD_PTR result = 0; if (strValue) { #ifdef _WIN64 result = _strtoi64(strValue, NULL, 16); #else result = strtoul(strValue, NULL, 16); #endif } return result; } void TreeImportExport::ConvertWordToString(const WORD dwValue) { sprintf_s(xmlStringBuffer, _countof(xmlStringBuffer), "%04X", dwValue); } WORD TreeImportExport::ConvertStringToWord(const char * strValue) { WORD result = 0; if (strValue) { result = (WORD)strtoul(strValue, NULL, 16); } return result; } void TreeImportExport::parseAllElementModules( TiXmlElement * targetElement, std::map & moduleList ) { TiXmlElement * moduleElement = 0; ImportModuleThunk importModuleThunk; - size_t convertedChars = 0; const char * filename = 0; for(moduleElement = targetElement->FirstChildElement(); moduleElement; moduleElement = moduleElement->NextSiblingElement()) { filename = moduleElement->Attribute("filename"); if (filename) { + StringConversion::ToUTF16(filename, importModuleThunk.moduleName, _countof(importModuleThunk.moduleName)); - mbstowcs_s(&convertedChars, importModuleThunk.moduleName, _countof(importModuleThunk.moduleName), filename, _TRUNCATE); importModuleThunk.firstThunk = ConvertStringToDwordPtr(moduleElement->Attribute("first_thunk_rva")); importModuleThunk.thunkList.clear(); parseAllElementImports(moduleElement, &importModuleThunk); moduleList.insert(std::pair(importModuleThunk.firstThunk, importModuleThunk)); } } } void TreeImportExport::parseAllElementImports( TiXmlElement * moduleElement, ImportModuleThunk * importModuleThunk ) { TiXmlElement * importElement = 0; ImportThunk importThunk; const char * temp = 0; for(importElement = moduleElement->FirstChildElement(); importElement; importElement = importElement->NextSiblingElement()) { temp = importElement->Value(); if (!strcmp(temp, "import_valid")) { temp = importElement->Attribute("name"); if (temp) { strcpy_s(importThunk.name, _countof(importThunk.name),temp); } else { importThunk.name[0] = 0; } wcscpy_s(importThunk.moduleName,_countof(importThunk.moduleName), importModuleThunk->moduleName); importThunk.suspect = ConvertStringToBool(importElement->Attribute("suspect")); importThunk.ordinal = ConvertStringToWord(importElement->Attribute("ordinal")); importThunk.hint = ConvertStringToWord(importElement->Attribute("hint")); importThunk.valid = true; } else { importThunk.valid = false; importThunk.suspect = true; } importThunk.apiAddressVA = ConvertStringToDwordPtr(importElement->Attribute("address_va")); importThunk.rva = ConvertStringToDwordPtr(importElement->Attribute("iat_rva")); if (importThunk.rva != 0) { importModuleThunk->thunkList.insert(std::pair(importThunk.rva, importThunk)); } } } diff --git a/Scylla/TreeImportExport.h b/Scylla/TreeImportExport.h index 33c3893..443807a 100644 --- a/Scylla/TreeImportExport.h +++ b/Scylla/TreeImportExport.h @@ -1,42 +1,38 @@ - #pragma once -#include +#include #include "ProcessLister.h" #include "Thunks.h" -#include "tinyxml.h" - - +#include class TreeImportExport { public: + bool exportTreeList(const WCHAR * targetXmlFile, std::map & moduleList, const Process * process, const DWORD_PTR addressOEP, const DWORD_PTR addressIAT, const DWORD sizeIAT); - bool importTreeList(const WCHAR * targetXmlFile, std::map & moduleList, DWORD_PTR * addressOEP, DWORD_PTR * addressIAT, DWORD * sizeIAT); private: + char xmlStringBuffer[100]; void addModuleListToRootElement( TiXmlElement * rootElement, std::map & moduleList ); TiXmlElement * getModuleXmlElement(const ImportModuleThunk * importModuleThunk); TiXmlElement * getImportXmlElement(const ImportThunk * importThunk); bool saveXmlToFile(TiXmlDocument doc, const WCHAR * xmlFilePath); char * readXmlFile(const WCHAR * xmlFilePath); void setTargetInformation(TiXmlElement * rootElement, const Process * process, const DWORD_PTR addressOEP, const DWORD_PTR addressIAT, const DWORD sizeIAT); void ConvertBoolToString(const bool boolValue); void ConvertWordToString(const WORD dwValue); void ConvertDwordPtrToString(const DWORD_PTR dwValue); DWORD_PTR ConvertStringToDwordPtr(const char * strValue); WORD ConvertStringToWord(const char * strValue); bool ConvertStringToBool(const char * strValue); - - void parseAllElementModules( TiXmlElement * targetElement, std::map & moduleList ); void parseAllElementImports( TiXmlElement * moduleElement, ImportModuleThunk * importModuleThunk ); -}; \ No newline at end of file +}; diff --git a/Scylla/hexedit.h b/Scylla/hexedit.h index 0e68680..dabdf6e 100644 --- a/Scylla/hexedit.h +++ b/Scylla/hexedit.h @@ -1,235 +1,235 @@ #pragma once #ifndef __cplusplus #error WTL requires C++ compilation (use a .cpp suffix) #endif #ifndef __ATLMISC_H__ #error hexedit.h requires atlmisc.h to be included first #endif #ifndef __ATLCTRLS_H__ - #error hexedit.h requires atlmisc.h to be included first requires atlctrls.h to be included first + #error hexedit.h requires atlctrls.h to be included first #endif /* #ifdef _WIN64 address = _wcstoui64(hexString, NULL, 16); #else address = wcstoul(hexString, NULL, 16); #endif */ template< class T, typename NUM_T, class TBase = CEdit, class TWinTraits = CControlWinTraits > class ATL_NO_VTABLE CHexEditImpl : public CWindowImpl< T, TBase, TWinTraits > { public: static const short int BASE = 16; static const size_t DIGITS = sizeof(NUM_T) * 2; // 2 digits / byte static const size_t STRSIZE = DIGITS + 1; static const TCHAR Digits[]; static const TCHAR OldProcProp[]; // Operations BOOL SubclassWindow(HWND hWnd) { ATLASSERT(m_hWnd == NULL); ATLASSERT(::IsWindow(hWnd)); BOOL bRet = CWindowImpl< T, TBase, TWinTraits >::SubclassWindow(hWnd); if( bRet ) _Init(); return bRet; } NUM_T GetValue() const { ATLASSERT(::IsWindow(m_hWnd)); TCHAR String[STRSIZE] = { 0 }; GetWindowText(String, _countof(String)); return _StringToNum(String); } void SetValue(NUM_T Num, bool Fill = true) { ATLASSERT(::IsWindow(m_hWnd)); TCHAR String[STRSIZE] = { 0 }; _NumToString(Num, String, Fill); SetWindowText(String); } // Implementation void _Init() { ATLASSERT(::IsWindow(m_hWnd)); LimitText(DIGITS); } bool _IsValidChar(TCHAR Char) const { return ((NUM_T)-1 != _CharToNum(Char)); } bool _IsValidString(const TCHAR String[STRSIZE]) const { for(int i = 0; String[i]; i++) { if(!_IsValidChar(String[i])) return false; } return true; } NUM_T _CharToNum(TCHAR Char) const { Char = _totupper(Char); for(int i = 0; Digits[i]; i++) { if(Char == Digits[i]) return i; } return -1; } NUM_T _StringToNum(const TCHAR String[STRSIZE]) const { NUM_T CharNum; NUM_T Num = 0; for(int i = 0; String[i]; i++) { CharNum = _CharToNum(String[i]); if(CharNum == (NUM_T)-1) break; Num *= BASE; Num += CharNum; } return Num; } TCHAR _NumToChar(NUM_T Num) const { return Digits[Num % BASE]; } void _NumToString(NUM_T Num, TCHAR String[STRSIZE], bool Fill) const { NUM_T Nums[DIGITS]; int i, j; for(i = DIGITS-1; i >= 0; i--) { Nums[i] = Num % BASE; Num /= BASE; } for(i = j = 0; i < DIGITS; i++) { // Only copy num if : non-null OR Fill OR non-null encountered before OR last num if(Nums[i] || Fill || j || i == DIGITS-1) { String[j++] = _NumToChar(Nums[i]); } } String[j] = '\0'; } bool _GetClipboardText(TCHAR String[STRSIZE]) { #ifdef UNICODE const UINT Format = CF_UNICODETEXT; #else const UINT Format = CF_TEXT; #endif bool RetVal = false; if(IsClipboardFormatAvailable(Format) && OpenClipboard()) { HANDLE HMem = GetClipboardData(Format); if(HMem) { _tcsncpy_s(String, STRSIZE, (TCHAR *)GlobalLock(HMem), STRSIZE-1); String[STRSIZE-1] = '\0'; GlobalUnlock(HMem); RetVal = true; } CloseClipboard(); } return RetVal; } // Message map and handlers BEGIN_MSG_MAP_EX(CHexEditImpl) MESSAGE_HANDLER_EX(WM_CREATE, OnCreate) MSG_WM_SETTEXT(OnSetText) MSG_WM_PASTE(OnPaste) MSG_WM_CHAR(OnChar) END_MSG_MAP() LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam) { LRESULT lRes = DefWindowProc(); _Init(); return lRes; } int OnSetText(LPCTSTR lpstrText) { bool PassThrough = (_tcslen(lpstrText) <= DIGITS) && _IsValidString(lpstrText); if(!PassThrough) { MessageBeep(-1); return FALSE; } SetMsgHandled(FALSE); return TRUE; } void OnPaste() { TCHAR String[STRSIZE]; bool PassThrough = !_GetClipboardText(String) || _IsValidString(String); if(!PassThrough) { MessageBeep(-1); return; } SetMsgHandled(FALSE); return; } void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { // ignore all printable chars (incl. space) which are not valid digits bool PassThrough = !_istprint((TCHAR)nChar) || _IsValidChar((TCHAR)nChar); if(!PassThrough) { MessageBeep(-1); return; } SetMsgHandled(FALSE); return; } }; template< class T, typename NUM_T, class TBase, class TWinTraits > const TCHAR CHexEditImpl< T, NUM_T, TBase, TWinTraits >::Digits[] = _T("0123456789ABCDEF"); template class CHexEdit : public CHexEditImpl, NUM_T> { public: DECLARE_WND_CLASS(_T("WTL_HexEdit")) };