diff --git a/Scylla/ApiReader.cpp b/Scylla/ApiReader.cpp index 340878c..9b9092b 100644 --- a/Scylla/ApiReader.cpp +++ b/Scylla/ApiReader.cpp @@ -1,1186 +1,1201 @@ #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::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, 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, ".dll"); 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, 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 (vaApi) { if (ordinal) { *vaApi = (DWORD_PTR)GetProcAddress(hModule, (LPCSTR)ordinal); } else { *vaApi = (DWORD_PTR)GetProcAddress(hModule, searchFunctionName); } *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; size_t c = 0; size_t countDuplicates = apiList.count(virtualAddress); int countHighPriority = 0; ApiInfo *apiFound = 0; if (countDuplicates == 0) { 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::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 * 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 == 0) - { - Scylla::windowLog.log(L"getApiByVirtualAddress :: No Api found " PRINTF_DWORD_PTR_FULL, pIATAddress[i]); - } - if (apiFound == (ApiInfo *)1) - { + if (!isInvalidMemoryForIat(pIATAddress[i])) + { + if ( (pIATAddress[i] > minApiAddress) && (pIATAddress[i] < maxApiAddress) ) + { + + apiFound = getApiByVirtualAddress(pIATAddress[i], &isSuspect); + if (apiFound == 0) + { + Scylla::windowLog.log(L"getApiByVirtualAddress :: No Api found " PRINTF_DWORD_PTR_FULL, pIATAddress[i]); + } + if (apiFound == (ApiInfo *)1) + { #ifdef DEBUG_COMMENTS - Scylla::debugLog.log(L"apiFound == (ApiInfo *)1 -> " PRINTF_DWORD_PTR_FULL, pIATAddress[i]); + Scylla::debugLog.log(L"apiFound == (ApiInfo *)1 -> " PRINTF_DWORD_PTR_FULL, pIATAddress[i]); #endif - } - else if (apiFound) - { - countApiFound++; + } + 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); + 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]); - } + 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, moduleName); (*moduleThunkList).insert(std::pair(firstThunk,module)); return true; } void ApiReader::addUnknownModuleToModuleList(DWORD_PTR firstThunk) { ImportModuleThunk module; module.firstThunk = firstThunk; wcscpy_s(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, apiFound->module->getFilename()); strcpy_s(import.name, apiFound->name); module->thunkList.insert(std::pair(import.rva, import)); return true; } void ApiReader::clearAll() { minApiAddress = -1; maxApiAddress = 0; for ( stdext::hash_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, L"?"); strcpy_s(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; } } + +bool ApiReader::isInvalidMemoryForIat( DWORD_PTR address ) +{ + if (address == 0) + return true; + + if (address == -1) + return true; + + MEMORY_BASIC_INFORMATION memBasic = {0}; + + if (VirtualQueryEx(ProcessAccessHelp::hProcess, (LPCVOID)address, &memBasic, sizeof(MEMORY_BASIC_INFORMATION))) + { + if((memBasic.State == MEM_COMMIT) && ProcessAccessHelp::isPageExecutable(memBasic.Protect)) + { + return false; + } + else + { + return true; + } + } + else + { + return true; + } +} diff --git a/Scylla/ApiReader.h b/Scylla/ApiReader.h index 2fe0a4d..425083b 100644 --- a/Scylla/ApiReader.h +++ b/Scylla/ApiReader.h @@ -1,74 +1,75 @@ #pragma once #include #include #include #include "ProcessAccessHelp.h" #include "Thunks.h" typedef std::pair API_Pair; class ApiReader : public ProcessAccessHelp { public: static stdext::hash_multimap apiList; //api look up table static std::map * moduleThunkList; //store found apis static DWORD_PTR minApiAddress; static DWORD_PTR maxApiAddress; /* * Read all APIs from target process */ void readApisFromModuleList(); bool isApiAddressValid(DWORD_PTR virtualAddress); ApiInfo * getApiByVirtualAddress(DWORD_PTR virtualAddress, bool * isSuspect); void readAndParseIAT(DWORD_PTR addressIAT, DWORD sizeIAT, std::map &moduleListNew ); void addFoundApiToModuleList(DWORD_PTR iatAddress, ApiInfo * apiFound, bool isNewModule, bool isSuspect); 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 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_map::iterator it1,size_t countDuplicates, bool hasName, bool hasUnicodeAnsiName, bool hasNoUnderlineInName, bool hasPrioDll,bool hasPrio0Dll,bool hasPrio1Dll, bool hasPrio2Dll, bool firstWin ); + bool isInvalidMemoryForIat( DWORD_PTR address ); }; diff --git a/Scylla/IATReferenceScan.cpp b/Scylla/IATReferenceScan.cpp index 9d560be..4a35510 100644 --- a/Scylla/IATReferenceScan.cpp +++ b/Scylla/IATReferenceScan.cpp @@ -1,767 +1,736 @@ #include "IATReferenceScan.h" #include "Scylla.h" #include "Architecture.h" #include //#define DEBUG_COMMENTS FileLog IATReferenceScan::directImportLog(L"Scylla_direct_imports.log"); int IATReferenceScan::numberOfFoundDirectImports() { return (int)iatDirectImportList.size(); } int IATReferenceScan::numberOfFoundUniqueDirectImports() { std::set apiPointers; for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { IATReference * ref = &(*iter); apiPointers.insert(ref->targetAddressInIat); } return (int)apiPointers.size(); } int IATReferenceScan::numberOfDirectImportApisNotInIat() { std::set apiPointers; for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { IATReference * ref = &(*iter); if (ref->targetPointer == 0) { apiPointers.insert(ref->targetAddressInIat); } } return (int)apiPointers.size(); } int IATReferenceScan::getSizeInBytesOfJumpTableInSection() { return (numberOfFoundUniqueDirectImports() * 6); //for x86 and x64 the same size, FF25 00000000 } void IATReferenceScan::startScan(DWORD_PTR imageBase, DWORD imageSize, DWORD_PTR iatAddress, DWORD iatSize) { MEMORY_BASIC_INFORMATION memBasic = {0}; IatAddressVA = iatAddress; IatSize = iatSize; ImageBase = imageBase; ImageSize = imageSize; if (ScanForNormalImports) { iatReferenceList.clear(); iatReferenceList.reserve(200); } if (ScanForDirectImports) { iatDirectImportList.clear(); iatDirectImportList.reserve(50); } DWORD_PTR section = imageBase; do { if (!VirtualQueryEx(ProcessAccessHelp::hProcess, (LPCVOID)section, &memBasic, sizeof(MEMORY_BASIC_INFORMATION))) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"VirtualQueryEx failed %d", GetLastError()); #endif break; } else { - if (isPageExecutable(memBasic.Protect)) + if (ProcessAccessHelp::isPageExecutable(memBasic.Protect)) { //do read and scan scanMemoryPage(memBasic.BaseAddress, memBasic.RegionSize); } } section = (DWORD_PTR)((SIZE_T)section + memBasic.RegionSize); } while (section < (imageBase + imageSize)); } //void IATReferenceScan::patchNewIatBaseMemory(DWORD_PTR newIatBaseAddress) //{ // NewIatAddressVA = newIatBaseAddress; // // for (std::vector::iterator iter = iatReferenceList.begin(); iter != iatReferenceList.end(); iter++) // { // patchReferenceInMemory(&(*iter)); // } //} // //void IATReferenceScan::patchNewIatBaseFile(DWORD_PTR newIatBaseAddress) //{ // NewIatAddressVA = newIatBaseAddress; // // for (std::vector::iterator iter = iatReferenceList.begin(); iter != iatReferenceList.end(); iter++) // { // patchReferenceInFile(&(*iter)); // } //} void IATReferenceScan::patchDirectImportsMemory( bool junkByteAfterInstruction ) { JunkByteAfterInstruction = junkByteAfterInstruction; for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { patchDirectImportInMemory(&(*iter)); } } - -bool IATReferenceScan::isPageExecutable( DWORD Protect ) -{ - if (Protect & PAGE_NOCACHE) Protect ^= PAGE_NOCACHE; - - if (Protect & PAGE_WRITECOMBINE) Protect ^= PAGE_WRITECOMBINE; - - switch(Protect) - { - case PAGE_EXECUTE: - { - return true; - } - case PAGE_EXECUTE_READ: - { - return true; - } - case PAGE_EXECUTE_READWRITE: - { - return true; - } - case PAGE_EXECUTE_WRITECOPY: - { - return true; - } - default: - return false; - } - -} - void IATReferenceScan::scanMemoryPage( PVOID BaseAddress, SIZE_T RegionSize ) { BYTE * dataBuffer = (BYTE *)calloc(RegionSize, 1); BYTE * currentPos = dataBuffer; int currentSize = (int)RegionSize; DWORD_PTR currentOffset = (DWORD_PTR)BaseAddress; _DecodeResult res; unsigned int instructionsCount = 0, next = 0; if (!dataBuffer) return; if (ProcessAccessHelp::readMemoryFromProcess((DWORD_PTR)BaseAddress, RegionSize, (LPVOID)dataBuffer)) { while (1) { ZeroMemory(&ProcessAccessHelp::decomposerCi, sizeof(_CodeInfo)); ProcessAccessHelp::decomposerCi.code = currentPos; ProcessAccessHelp::decomposerCi.codeLen = currentSize; ProcessAccessHelp::decomposerCi.dt = ProcessAccessHelp::dt; ProcessAccessHelp::decomposerCi.codeOffset = currentOffset; instructionsCount = 0; res = distorm_decompose(&ProcessAccessHelp::decomposerCi, ProcessAccessHelp::decomposerResult, sizeof(ProcessAccessHelp::decomposerResult)/sizeof(ProcessAccessHelp::decomposerResult[0]), &instructionsCount); if (res == DECRES_INPUTERR) { break; } for (unsigned int i = 0; i < instructionsCount; i++) { if (ProcessAccessHelp::decomposerResult[i].flags != FLAG_NOT_DECODABLE) { analyzeInstruction(&ProcessAccessHelp::decomposerResult[i]); } } if (res == DECRES_SUCCESS) break; // All instructions were decoded. else if (instructionsCount == 0) break; next = (unsigned long)(ProcessAccessHelp::decomposerResult[instructionsCount-1].addr - ProcessAccessHelp::decomposerResult[0].addr); if (ProcessAccessHelp::decomposerResult[instructionsCount-1].flags != FLAG_NOT_DECODABLE) { next += ProcessAccessHelp::decomposerResult[instructionsCount-1].size; } currentPos += next; currentOffset += next; currentSize -= next; } } free(dataBuffer); } void IATReferenceScan::analyzeInstruction( _DInst * instruction ) { if (ScanForNormalImports) { findNormalIatReference(instruction); } if (ScanForDirectImports) { findDirectIatReferenceMov(instruction); #ifndef _WIN64 findDirectIatReferenceCallJmp(instruction); findDirectIatReferenceLea(instruction); findDirectIatReferencePush(instruction); #endif } } void IATReferenceScan::findNormalIatReference( _DInst * instruction ) { #ifdef DEBUG_COMMENTS _DecodedInst inst; #endif IATReference ref; if (META_GET_FC(instruction->meta) == FC_CALL || META_GET_FC(instruction->meta) == FC_UNC_BRANCH) { if (instruction->size >= 5) { if (META_GET_FC(instruction->meta) == FC_CALL) { ref.type = IAT_REFERENCE_PTR_CALL; } else { ref.type = IAT_REFERENCE_PTR_JMP; } ref.addressVA = (DWORD_PTR)instruction->addr; ref.instructionSize = instruction->size; #ifdef _WIN64 if (instruction->flags & FLAG_RIP_RELATIVE) { #ifdef DEBUG_COMMENTS distorm_format(&ProcessAccessHelp::decomposerCi, instruction, &inst); Scylla::debugLog.log(PRINTF_DWORD_PTR_FULL L" " PRINTF_DWORD_PTR_FULL L" %S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL, (DWORD_PTR)instruction->addr, ImageBase, inst.mnemonic.p, inst.operands.p, instruction->ops[0].type, instruction->size, INSTRUCTION_GET_RIP_TARGET(instruction)); #endif if (INSTRUCTION_GET_RIP_TARGET(instruction) >= IatAddressVA && INSTRUCTION_GET_RIP_TARGET(instruction) < (IatAddressVA + IatSize)) { ref.targetPointer = INSTRUCTION_GET_RIP_TARGET(instruction); getIatEntryAddress(&ref); //Scylla::debugLog.log(L"iat entry "PRINTF_DWORD_PTR_FULL,ref.targetAddressInIat); iatReferenceList.push_back(ref); } } #else if (instruction->ops[0].type == O_DISP) { //jmp dword ptr || call dword ptr #ifdef DEBUG_COMMENTS distorm_format(&ProcessAccessHelp::decomposerCi, instruction, &inst); Scylla::debugLog.log(PRINTF_DWORD_PTR_FULL L" " PRINTF_DWORD_PTR_FULL L" %S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL, (DWORD_PTR)instruction->addr, ImageBase, inst.mnemonic.p, inst.operands.p, instruction->ops[0].type, instruction->size, instruction->disp); #endif if (instruction->disp >= IatAddressVA && instruction->disp < (IatAddressVA + IatSize)) { ref.targetPointer = (DWORD_PTR)instruction->disp; getIatEntryAddress(&ref); //Scylla::debugLog.log(L"iat entry "PRINTF_DWORD_PTR_FULL,ref.targetAddressInIat); iatReferenceList.push_back(ref); } } #endif } } } void IATReferenceScan::getIatEntryAddress( IATReference * ref ) { if (!ProcessAccessHelp::readMemoryFromProcess(ref->targetPointer, sizeof(DWORD_PTR), &ref->targetAddressInIat)) { ref->targetAddressInIat = 0; } } bool IATReferenceScan::isAddressValidImageMemory( DWORD_PTR address ) { MEMORY_BASIC_INFORMATION memBasic = {0}; if (!VirtualQueryEx(ProcessAccessHelp::hProcess, (LPCVOID)address, &memBasic, sizeof(MEMORY_BASIC_INFORMATION))) { return false; } - return (memBasic.Type == MEM_IMAGE && isPageExecutable(memBasic.Protect)); + return (memBasic.Type == MEM_IMAGE && ProcessAccessHelp::isPageExecutable(memBasic.Protect)); } void IATReferenceScan::patchReferenceInMemory( IATReference * ref ) { DWORD_PTR newIatAddressPointer = ref->targetPointer - IatAddressVA + NewIatAddressRVA; DWORD patchBytes = 0; #ifdef _WIN64 patchBytes = (DWORD)(newIatAddressPointer - ref->addressVA - 6); #else patchBytes = newIatAddressPointer; #endif ProcessAccessHelp::writeMemoryToProcess(ref->addressVA + 2, sizeof(DWORD), &patchBytes); } void IATReferenceScan::patchDirectImportInMemory( IATReference * ref ) { DWORD patchBytes = 0; BYTE patchPreBytes[2]; if (ref->targetPointer) { patchPreBytes[0] = 0xFF; if (ref->type == IAT_REFERENCE_DIRECT_CALL) //FF15 { patchPreBytes[1] = 0x15; } else if (ref->type == IAT_REFERENCE_DIRECT_JMP) //FF25 { patchPreBytes[1] = 0x25; } else { return; } if (!JunkByteAfterInstruction) { ref->addressVA -= 1; } ProcessAccessHelp::writeMemoryToProcess(ref->addressVA, 2, patchPreBytes); #ifdef _WIN64 patchBytes = (DWORD)(ref->targetPointer - ref->addressVA - 6); #else patchBytes = ref->targetPointer; #endif ProcessAccessHelp::writeMemoryToProcess(ref->addressVA + 2, sizeof(DWORD), &patchBytes); } } DWORD_PTR IATReferenceScan::lookUpIatForPointer( DWORD_PTR addr ) { if (!iatBackup) { iatBackup = (DWORD_PTR *)calloc(IatSize + sizeof(DWORD_PTR), 1); if (!iatBackup) { return 0; } if (!ProcessAccessHelp::readMemoryFromProcess(IatAddressVA, IatSize, iatBackup)) { free(iatBackup); iatBackup = 0; return 0; } } for (int i = 0; i < ((int)IatSize / (int)sizeof(DWORD_PTR));i++) { if (iatBackup[i] == addr) { return (DWORD_PTR)&iatBackup[i] - (DWORD_PTR)iatBackup + IatAddressVA; } } return 0; } void IATReferenceScan::patchNewIat(DWORD_PTR stdImagebase, DWORD_PTR newIatBaseAddress, PeParser * peParser) { NewIatAddressRVA = newIatBaseAddress; DWORD patchBytes = 0; for (std::vector::iterator iter = iatReferenceList.begin(); iter != iatReferenceList.end(); iter++) { IATReference * ref = &(*iter); DWORD_PTR newIatAddressPointer = (ref->targetPointer - IatAddressVA) + NewIatAddressRVA + stdImagebase; #ifdef _WIN64 patchBytes = (DWORD)(newIatAddressPointer - (ref->addressVA - ImageBase + stdImagebase) - 6); #else patchBytes = newIatAddressPointer; #endif DWORD_PTR patchOffset = peParser->convertRVAToOffsetRelative(ref->addressVA - ImageBase); int index = peParser->convertRVAToOffsetVectorIndex(ref->addressVA - ImageBase); BYTE * memory = peParser->getSectionMemoryByIndex(index); DWORD memorySize = peParser->getSectionMemorySizeByIndex(index); if (memorySize < (DWORD)(patchOffset + 6)) { Scylla::debugLog.log(L"Error - Cannot fix IAT reference RVA: " PRINTF_DWORD_PTR_FULL, ref->addressVA - ImageBase); } else { memory += patchOffset + 2; *((DWORD *)memory) = patchBytes; } //Scylla::debugLog.log(L"address %X old %X new %X",ref->addressVA, ref->targetPointer, newIatAddressPointer); } } void IATReferenceScan::printDirectImportLog() { IATReferenceScan::directImportLog.log(L"------------------------------------------------------------"); IATReferenceScan::directImportLog.log(L"ImageBase " PRINTF_DWORD_PTR_FULL L" ImageSize %08X IATAddress " PRINTF_DWORD_PTR_FULL L" IATSize 0x%X", ImageBase, ImageSize, IatAddressVA, IatSize); int count = 0; bool isSuspect = false; for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { IATReference * ref = &(*iter); ApiInfo * apiInfo = apiReader->getApiByVirtualAddress(ref->targetAddressInIat, &isSuspect); count++; WCHAR * type = L"U"; if (ref->type == IAT_REFERENCE_DIRECT_CALL) { type = L"CALL"; } else if (ref->type == IAT_REFERENCE_DIRECT_JMP) { type = L"JMP"; } else if (ref->type == IAT_REFERENCE_DIRECT_MOV) { type = L"MOV"; } else if (ref->type == IAT_REFERENCE_DIRECT_PUSH) { type = L"PUSH"; } else if (ref->type == IAT_REFERENCE_DIRECT_LEA) { type = L"LEA"; } IATReferenceScan::directImportLog.log(L"%04d AddrVA " PRINTF_DWORD_PTR_FULL L" Type %s Value " PRINTF_DWORD_PTR_FULL L" IatRefPointer " PRINTF_DWORD_PTR_FULL L" Api %s %S", count, ref->addressVA, type, ref->targetAddressInIat, ref->targetPointer,apiInfo->module->getFilename(), apiInfo->name); } IATReferenceScan::directImportLog.log(L"------------------------------------------------------------"); } void IATReferenceScan::findDirectIatReferenceCallJmp( _DInst * instruction ) { IATReference ref; if (META_GET_FC(instruction->meta) == FC_CALL || META_GET_FC(instruction->meta) == FC_UNC_BRANCH) { if ((instruction->size >= 5) && (instruction->ops[0].type == O_PC)) //CALL/JMP 0x00000000 { if (META_GET_FC(instruction->meta) == FC_CALL) { ref.type = IAT_REFERENCE_DIRECT_CALL; } else { ref.type = IAT_REFERENCE_DIRECT_JMP; } ref.targetAddressInIat = (DWORD_PTR)INSTRUCTION_GET_TARGET(instruction); checkMemoryRangeAndAddToList(&ref, instruction); } } } void IATReferenceScan::findDirectIatReferenceMov( _DInst * instruction ) { IATReference ref; ref.type = IAT_REFERENCE_DIRECT_MOV; if (instruction->opcode == I_MOV) { #ifdef _WIN64 if (instruction->size >= 7) //MOV REGISTER, 0xFFFFFFFFFFFFFFFF #else if (instruction->size >= 5) //MOV REGISTER, 0xFFFFFFFF #endif { if (instruction->ops[0].type == O_REG && instruction->ops[1].type == O_IMM) { ref.targetAddressInIat = (DWORD_PTR)instruction->imm.qword; checkMemoryRangeAndAddToList(&ref, instruction); } } } } void IATReferenceScan::findDirectIatReferencePush( _DInst * instruction ) { IATReference ref; ref.type = IAT_REFERENCE_DIRECT_PUSH; if (instruction->size >= 5 && instruction->opcode == I_PUSH) { ref.targetAddressInIat = (DWORD_PTR)instruction->imm.qword; checkMemoryRangeAndAddToList(&ref, instruction); } } void IATReferenceScan::findDirectIatReferenceLea( _DInst * instruction ) { IATReference ref; ref.type = IAT_REFERENCE_DIRECT_LEA; if (instruction->size >= 5 && instruction->opcode == I_LEA) { if (instruction->ops[0].type == O_REG && instruction->ops[1].type == O_DISP) //LEA EDX, [0xb58bb8] { ref.targetAddressInIat = (DWORD_PTR)instruction->disp; checkMemoryRangeAndAddToList(&ref, instruction); } } } void IATReferenceScan::checkMemoryRangeAndAddToList( IATReference * ref, _DInst * instruction ) { #ifdef DEBUG_COMMENTS _DecodedInst inst; #endif if (ref->targetAddressInIat > 0x000FFFFF && ref->targetAddressInIat != (DWORD_PTR)-1) { if ((ref->targetAddressInIat < ImageBase) || (ref->targetAddressInIat > (ImageBase+ImageSize))) //outside pe image { //if (isAddressValidImageMemory(ref->targetAddressInIat)) { bool isSuspect = false; if (apiReader->getApiByVirtualAddress(ref->targetAddressInIat, &isSuspect) != 0) { ref->addressVA = (DWORD_PTR)instruction->addr; ref->instructionSize = instruction->size; ref->targetPointer = lookUpIatForPointer(ref->targetAddressInIat); #ifdef DEBUG_COMMENTS distorm_format(&ProcessAccessHelp::decomposerCi, instruction, &inst); Scylla::debugLog.log(PRINTF_DWORD_PTR_FULL L" " PRINTF_DWORD_PTR_FULL L" %S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL,(DWORD_PTR)instruction->addr, ImageBase, inst.mnemonic.p, inst.operands.p, instruction->ops[0].type, instruction->size, ref->targetAddressInIat); #endif iatDirectImportList.push_back(*ref); } } } } } void IATReferenceScan::patchDirectJumpTableEntry(DWORD_PTR targetIatPointer, DWORD_PTR stdImagebase, DWORD directImportsJumpTableRVA, PeParser * peParser, BYTE * jmpTableMemory, DWORD newIatBase ) { DWORD patchBytes = 0; for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { IATReference * ref = &(*iter); //only one jmp in table for different direct imports with same iat address if (ref->targetPointer == targetIatPointer) { //patch dump DWORD patchOffset = (DWORD)peParser->convertRVAToOffsetRelative(ref->addressVA - ImageBase); int index = peParser->convertRVAToOffsetVectorIndex(ref->addressVA - ImageBase); BYTE * memory = peParser->getSectionMemoryByIndex(index); DWORD memorySize = peParser->getSectionMemorySizeByIndex(index); DWORD sectionRVA = peParser->getSectionAddressRVAByIndex(index); if (ref->type == IAT_REFERENCE_DIRECT_CALL || ref->type == IAT_REFERENCE_DIRECT_JMP) { #ifndef _WIN64 if (ref->instructionSize == 5) { patchBytes = directImportsJumpTableRVA - (ref->addressVA - ImageBase) - 5; patchDirectImportInDump32(1, 5, patchBytes, memory, memorySize, false, patchOffset, sectionRVA); } #endif } else if (ref->type == IAT_REFERENCE_DIRECT_PUSH || ref->type == IAT_REFERENCE_DIRECT_MOV) { #ifndef _WIN64 if (ref->instructionSize == 5) //for x86 { patchBytes = directImportsJumpTableRVA + stdImagebase; patchDirectImportInDump32(1, 5, patchBytes, memory, memorySize, true, patchOffset, sectionRVA); } #else if (ref->instructionSize == 10) //for x64 { DWORD_PTR patchBytes64 = directImportsJumpTableRVA + stdImagebase; patchDirectImportInDump64(2, 10, patchBytes64, memory, memorySize, true, patchOffset, sectionRVA); } #endif } else if (ref->type == IAT_REFERENCE_DIRECT_LEA) { #ifndef _WIN64 if (ref->instructionSize == 6) { patchBytes = directImportsJumpTableRVA + stdImagebase; patchDirectImportInDump32(2, 6, patchBytes, memory, memorySize, true, patchOffset, sectionRVA); } #endif } } } } void IATReferenceScan::patchDirectJumpTable( DWORD_PTR stdImagebase, DWORD directImportsJumpTableRVA, PeParser * peParser, BYTE * jmpTableMemory, DWORD newIatBase ) { std::set apiPointers; for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { IATReference * ref = &(*iter); apiPointers.insert(ref->targetPointer); } DWORD patchBytes; for (std::set::iterator apiIter = apiPointers.begin(); apiIter != apiPointers.end(); apiIter++) { DWORD_PTR refTargetPointer = *apiIter; if (newIatBase) //create new iat in section { refTargetPointer = (*apiIter - IatAddressVA) + newIatBase + ImageBase; } //create jump table in section DWORD_PTR newIatAddressPointer = refTargetPointer - ImageBase + stdImagebase; #ifdef _WIN64 patchBytes = (DWORD)(newIatAddressPointer - (directImportsJumpTableRVA + stdImagebase) - 6); #else patchBytes = newIatAddressPointer; DWORD relocOffset = (directImportsJumpTableRVA + 2); directImportLog.log(L"Relocation direct imports fix: Base RVA %08X Type HIGHLOW Offset %04X RelocTableEntry %04X", relocOffset & 0xFFFFF000, relocOffset & 0x00000FFF, (IMAGE_REL_BASED_HIGHLOW << 12) + (relocOffset & 0x00000FFF)); #endif jmpTableMemory[0] = 0xFF; jmpTableMemory[1] = 0x25; *((DWORD *)&jmpTableMemory[2]) = patchBytes; patchDirectJumpTableEntry(*apiIter, stdImagebase, directImportsJumpTableRVA, peParser, jmpTableMemory, newIatBase); jmpTableMemory += 6; directImportsJumpTableRVA += 6; } } void IATReferenceScan::patchDirectImportInDump32( int patchPreFixBytes, int instructionSize, DWORD patchBytes, BYTE * memory, DWORD memorySize, bool generateReloc, DWORD patchOffset, DWORD sectionRVA ) { if (memorySize < (DWORD)(patchOffset + instructionSize)) { Scylla::debugLog.log(L"Error - Cannot fix direct import reference RVA: %X", sectionRVA + patchOffset); } else { memory += patchOffset + patchPreFixBytes; if (generateReloc) { DWORD relocOffset = sectionRVA + patchOffset+ patchPreFixBytes; directImportLog.log(L"Relocation direct imports fix: Base RVA %08X Type HIGHLOW Offset %04X RelocTableEntry %04X", relocOffset & 0xFFFFF000, relocOffset & 0x00000FFF, (IMAGE_REL_BASED_HIGHLOW << 12) + (relocOffset & 0x00000FFF)); } *((DWORD *)memory) = patchBytes; } } void IATReferenceScan::patchDirectImportInDump64( int patchPreFixBytes, int instructionSize, DWORD_PTR patchBytes, BYTE * memory, DWORD memorySize, bool generateReloc, DWORD patchOffset, DWORD sectionRVA ) { if (memorySize < (DWORD)(patchOffset + instructionSize)) { Scylla::debugLog.log(L"Error - Cannot fix direct import reference RVA: %X", sectionRVA + patchOffset); } else { memory += patchOffset + patchPreFixBytes; if (generateReloc) { DWORD relocOffset = sectionRVA + patchOffset+ patchPreFixBytes; directImportLog.log(L"Relocation direct imports fix: Base RVA %08X Type DIR64 Offset %04X RelocTableEntry %04X", relocOffset & 0xFFFFF000, relocOffset & 0x00000FFF, (IMAGE_REL_BASED_DIR64 << 12) + (relocOffset & 0x00000FFF)); } *((DWORD_PTR *)memory) = patchBytes; } } DWORD IATReferenceScan::addAdditionalApisToList() { std::set apiPointers; for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { IATReference * ref = &(*iter); if (ref->targetPointer == 0) { apiPointers.insert(ref->targetAddressInIat); } } DWORD_PTR iatAddy = IatAddressVA + IatSize; DWORD newIatSize = IatSize; bool isSuspect = false; for (std::set::iterator apiIter = apiPointers.begin(); apiIter != apiPointers.end(); apiIter++) { for (std::vector::iterator iter = iatDirectImportList.begin(); iter != iatDirectImportList.end(); iter++) { IATReference * ref = &(*iter); if (ref->targetPointer == 0 && ref->targetAddressInIat == *apiIter) { ref->targetPointer = iatAddy; ApiInfo * apiInfo = apiReader->getApiByVirtualAddress(ref->targetAddressInIat, &isSuspect); apiReader->addFoundApiToModuleList(iatAddy, apiInfo, true, isSuspect); } } iatAddy += sizeof(DWORD_PTR); newIatSize += sizeof(DWORD_PTR); } return newIatSize; } diff --git a/Scylla/IATReferenceScan.h b/Scylla/IATReferenceScan.h index 068f742..3efe752 100644 --- a/Scylla/IATReferenceScan.h +++ b/Scylla/IATReferenceScan.h @@ -1,127 +1,126 @@ #pragma once #include #include "ProcessAccessHelp.h" #include "PeParser.h" #include "Logger.h" #include "ApiReader.h" enum IATReferenceType { IAT_REFERENCE_PTR_JMP, IAT_REFERENCE_PTR_CALL, IAT_REFERENCE_DIRECT_JMP, IAT_REFERENCE_DIRECT_CALL, IAT_REFERENCE_DIRECT_MOV, IAT_REFERENCE_DIRECT_PUSH, IAT_REFERENCE_DIRECT_LEA }; class IATReference { public: DWORD_PTR addressVA; //Address of reference DWORD_PTR targetPointer; //Place inside IAT DWORD_PTR targetAddressInIat; //WIN API? BYTE instructionSize; IATReferenceType type; }; class IATReferenceScan { public: IATReferenceScan() { apiReader = 0; IatAddressVA = 0; IatSize = 0; ImageBase = 0; ImageSize = 0; iatBackup = 0; ScanForDirectImports = false; ScanForNormalImports = true; } ~IATReferenceScan() { iatReferenceList.clear(); iatDirectImportList.clear(); if (iatBackup) { free(iatBackup); } } bool ScanForDirectImports; bool ScanForNormalImports; bool JunkByteAfterInstruction; ApiReader * apiReader; void startScan(DWORD_PTR imageBase, DWORD imageSize, DWORD_PTR iatAddress, DWORD iatSize); //void patchNewIatBaseMemory(DWORD_PTR newIatBaseAddress); //void patchNewIatBaseFile(DWORD_PTR newIatBaseAddress); void patchNewIat(DWORD_PTR stdImagebase, DWORD_PTR newIatBaseAddress, PeParser * peParser); void patchDirectJumpTable( DWORD_PTR imageBase, DWORD directImportsJumpTableRVA, PeParser * peParser, BYTE * jmpTableMemory, DWORD newIatBase); void patchDirectImportsMemory(bool junkByteAfterInstruction); int numberOfFoundDirectImports(); int numberOfFoundUniqueDirectImports(); int numberOfDirectImportApisNotInIat(); int getSizeInBytesOfJumpTableInSection(); static FileLog directImportLog; void printDirectImportLog(); void changeIatBaseOfDirectImports( DWORD newIatBaseAddressRVA ); DWORD addAdditionalApisToList(); private: DWORD_PTR NewIatAddressRVA; DWORD_PTR IatAddressVA; DWORD IatSize; DWORD_PTR ImageBase; DWORD ImageSize; DWORD_PTR * iatBackup; std::vector iatReferenceList; std::vector iatDirectImportList; - bool isPageExecutable( DWORD Protect ); void scanMemoryPage( PVOID BaseAddress, SIZE_T RegionSize ); void analyzeInstruction( _DInst * instruction ); void findNormalIatReference( _DInst * instruction ); void getIatEntryAddress( IATReference* ref ); void findDirectIatReferenceCallJmp( _DInst * instruction ); bool isAddressValidImageMemory( DWORD_PTR address ); void patchReferenceInMemory( IATReference * ref ); void patchReferenceInFile( IATReference* ref ); void patchDirectImportInMemory( IATReference * iter ); DWORD_PTR lookUpIatForPointer( DWORD_PTR addr ); void findDirectIatReferenceMov( _DInst * instruction ); void findDirectIatReferencePush( _DInst * instruction ); void checkMemoryRangeAndAddToList( IATReference * ref, _DInst * instruction ); void findDirectIatReferenceLea( _DInst * instruction ); void patchDirectImportInDump32( int patchPreFixBytes, int instructionSize, DWORD patchBytes, BYTE * memory, DWORD memorySize, bool generateReloc, DWORD patchOffset, DWORD sectionRVA ); void patchDirectImportInDump64( int patchPreFixBytes, int instructionSize, DWORD_PTR patchBytes, BYTE * memory, DWORD memorySize, bool generateReloc, DWORD patchOffset, DWORD sectionRVA ); void patchDirectJumpTableEntry(DWORD_PTR targetIatPointer, DWORD_PTR stdImagebase, DWORD directImportsJumpTableRVA, PeParser * peParser, BYTE * jmpTableMemory, DWORD newIatBase ); }; /* PE64 ---------- 000000013FF82D87 FF15 137C0A00 CALL QWORD [RIP+0xA7C13] Result: 000000014002A9A0 000000013F65C952 FF25 F8EA0B00 JMP QWORD [RIP+0xBEAF8] Result: 000000013F71B450 PE32 ---------- 0120FFA5 FF15 8C6D2601 CALL DWORD [0x01266D8C] 0120FF52 FF25 D4722601 JMP DWORD [0x012672D4] */ diff --git a/Scylla/IATSearch.cpp b/Scylla/IATSearch.cpp index def09a9..130f3f1 100644 --- a/Scylla/IATSearch.cpp +++ b/Scylla/IATSearch.cpp @@ -1,538 +1,509 @@ #include "IATSearch.h" #include "Scylla.h" #include "Architecture.h" //#define DEBUG_COMMENTS bool IATSearch::searchImportAddressTableInProcess( DWORD_PTR startAddress, DWORD_PTR* addressIAT, DWORD* sizeIAT, bool advanced ) { DWORD_PTR addressInIAT = 0; *addressIAT = 0; *sizeIAT = 0; if (advanced) { return findIATAdvanced(startAddress, addressIAT, sizeIAT); } addressInIAT = findAPIAddressInIAT(startAddress); if(!addressInIAT) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"searchImportAddressTableInProcess :: addressInIAT not found, startAddress " PRINTF_DWORD_PTR_FULL, startAddress); #endif return false; } else { return findIATStartAndSize(addressInIAT, addressIAT,sizeIAT); } } bool IATSearch::findIATAdvanced( DWORD_PTR startAddress, DWORD_PTR* addressIAT, DWORD* sizeIAT ) { BYTE *dataBuffer; DWORD_PTR baseAddress; SIZE_T memorySize; findExecutableMemoryPagesByStartAddress(startAddress, &baseAddress, &memorySize); if (memorySize == 0) return false; dataBuffer = new BYTE[memorySize]; if (!readMemoryFromProcess((DWORD_PTR)baseAddress, memorySize,dataBuffer)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findAPIAddressInIAT2 :: error reading memory"); #endif return false; } std::set iatPointers; DWORD_PTR next; BYTE * tempBuf = dataBuffer; while(decomposeMemory(tempBuf, memorySize, (DWORD_PTR)baseAddress) && decomposerInstructionsCount != 0) { findIATPointers(iatPointers); next = (DWORD_PTR)(decomposerResult[decomposerInstructionsCount - 1].addr - baseAddress); next += decomposerResult[decomposerInstructionsCount - 1].size; // Advance ptr and recalc offset. tempBuf += next; if (memorySize <= next) { break; } memorySize -= next; baseAddress += next; } if (iatPointers.size() == 0) return false; filterIATPointersList(iatPointers); *addressIAT = *(iatPointers.begin()); *sizeIAT = (DWORD)(*(--iatPointers.end()) - *(iatPointers.begin()) + sizeof(DWORD_PTR)); Scylla::windowLog.log(L"IAT Search Advanced: Found %d (0x%X) possible IAT entries.", iatPointers.size(), iatPointers.size()); Scylla::windowLog.log(L"IAT Search Advanced: Possible IAT first " PRINTF_DWORD_PTR_FULL L" last " PRINTF_DWORD_PTR_FULL L" entry.", *(iatPointers.begin()), *(--iatPointers.end())); delete [] dataBuffer; return true; } DWORD_PTR IATSearch::findAPIAddressInIAT(DWORD_PTR startAddress) { const size_t MEMORY_READ_SIZE = 200; BYTE dataBuffer[MEMORY_READ_SIZE]; DWORD_PTR iatPointer = 0; int counter = 0; // to detect stolen api memoryAddress = 0; memorySize = 0; do { counter++; if (!readMemoryFromProcess(startAddress, sizeof(dataBuffer), dataBuffer)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findAPIAddressInIAT :: error reading memory " PRINTF_DWORD_PTR_FULL, startAddress); #endif return 0; } if (decomposeMemory(dataBuffer, sizeof(dataBuffer), startAddress)) { iatPointer = findIATPointer(); if (iatPointer) { if (isIATPointerValid(iatPointer)) { return iatPointer; } } } startAddress = findNextFunctionAddress(); //printf("startAddress %08X\n",startAddress); } while (startAddress != 0 && counter != 8); return 0; } DWORD_PTR IATSearch::findNextFunctionAddress() { #ifdef DEBUG_COMMENTS _DecodedInst inst; #endif for (unsigned int i = 0; i < decomposerInstructionsCount; i++) { if (decomposerResult[i].flags != FLAG_NOT_DECODABLE) { if (META_GET_FC(decomposerResult[i].meta) == FC_CALL || META_GET_FC(decomposerResult[i].meta) == FC_UNC_BRANCH) { if (decomposerResult[i].size >= 5) { if (decomposerResult[i].ops[0].type == O_PC) { #ifdef DEBUG_COMMENTS distorm_format(&decomposerCi, &decomposerResult[i], &inst); Scylla::debugLog.log(L"%S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL, inst.mnemonic.p, inst.operands.p, decomposerResult[i].ops[0].type, decomposerResult[i].size, INSTRUCTION_GET_TARGET(&decomposerResult[i])); #endif return (DWORD_PTR)INSTRUCTION_GET_TARGET(&decomposerResult[i]); } } } } } return 0; } DWORD_PTR IATSearch::findIATPointer() { #ifdef DEBUG_COMMENTS _DecodedInst inst; #endif for (unsigned int i = 0; i < decomposerInstructionsCount; i++) { if (decomposerResult[i].flags != FLAG_NOT_DECODABLE) { if (META_GET_FC(decomposerResult[i].meta) == FC_CALL || META_GET_FC(decomposerResult[i].meta) == FC_UNC_BRANCH) { if (decomposerResult[i].size >= 5) { #ifdef _WIN64 if (decomposerResult[i].flags & FLAG_RIP_RELATIVE) { #ifdef DEBUG_COMMENTS distorm_format(&decomposerCi, &decomposerResult[i], &inst); Scylla::debugLog.log(L"%S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL, inst.mnemonic.p, inst.operands.p, decomposerResult[i].ops[0].type, decomposerResult[i].size, INSTRUCTION_GET_RIP_TARGET(&decomposerResult[i])); #endif return INSTRUCTION_GET_RIP_TARGET(&decomposerResult[i]); } #else if (decomposerResult[i].ops[0].type == O_DISP) { //jmp dword ptr || call dword ptr #ifdef DEBUG_COMMENTS distorm_format(&decomposerCi, &decomposerResult[i], &inst); Scylla::debugLog.log(L"%S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL, inst.mnemonic.p, inst.operands.p, decomposerResult[i].ops[0].type, decomposerResult[i].size, decomposerResult[i].disp); #endif return (DWORD_PTR)decomposerResult[i].disp; } #endif } } } } return 0; } bool IATSearch::isIATPointerValid(DWORD_PTR iatPointer) { DWORD_PTR apiAddress = 0; if (!readMemoryFromProcess(iatPointer,sizeof(DWORD_PTR),&apiAddress)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"isIATPointerValid :: error reading memory"); #endif return false; } //printf("Win api ? %08X\n",apiAddress); if (isApiAddressValid(apiAddress) != 0) { return true; } else { //maybe redirected import? //if the address is 2 times inside a memory region it is possible a redirected api if (apiAddress > memoryAddress && apiAddress < (memoryAddress+memorySize)) { return true; } else { getMemoryRegionFromAddress(apiAddress, &memoryAddress, &memorySize); return false; } } } -bool IATSearch::isPageExecutable(DWORD value) -{ - if (value & PAGE_NOCACHE) value ^= PAGE_NOCACHE; - - if (value & PAGE_WRITECOMBINE) value ^= PAGE_WRITECOMBINE; - - switch(value) - { - case PAGE_EXECUTE: - { - return true; - } - case PAGE_EXECUTE_READ: - { - return true; - } - case PAGE_EXECUTE_READWRITE: - { - return true; - } - case PAGE_EXECUTE_WRITECOPY: - { - return true; - } - default: - return false; - } -} - bool IATSearch::findIATStartAndSize(DWORD_PTR address, DWORD_PTR * addressIAT, DWORD * sizeIAT) { MEMORY_BASIC_INFORMATION memBasic = {0}; BYTE *dataBuffer = 0; if (VirtualQueryEx(hProcess,(LPCVOID)address, &memBasic, sizeof(MEMORY_BASIC_INFORMATION)) != sizeof(MEMORY_BASIC_INFORMATION)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findIATStartAddress :: VirtualQueryEx error %u", GetLastError()); #endif return false; } //(sizeof(DWORD_PTR) * 3) added to prevent buffer overflow dataBuffer = new BYTE[memBasic.RegionSize + (sizeof(DWORD_PTR) * 3)]; ZeroMemory(dataBuffer, memBasic.RegionSize + (sizeof(DWORD_PTR) * 3)); if (!readMemoryFromProcess((DWORD_PTR)memBasic.BaseAddress, memBasic.RegionSize, dataBuffer)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findIATStartAddress :: error reading memory"); #endif return false; } //printf("address %X memBasic.BaseAddress %X memBasic.RegionSize %X\n",address,memBasic.BaseAddress,memBasic.RegionSize); *addressIAT = findIATStartAddress((DWORD_PTR)memBasic.BaseAddress, address, dataBuffer); *sizeIAT = findIATSize((DWORD_PTR)memBasic.BaseAddress, *addressIAT, dataBuffer, (DWORD)memBasic.RegionSize); delete [] dataBuffer; return true; } DWORD_PTR IATSearch::findIATStartAddress(DWORD_PTR baseAddress, DWORD_PTR startAddress, BYTE * dataBuffer) { DWORD_PTR *pIATAddress = 0; pIATAddress = (DWORD_PTR *)((startAddress - baseAddress) + (DWORD_PTR)dataBuffer); while((DWORD_PTR)pIATAddress != (DWORD_PTR)dataBuffer) { if ( (*pIATAddress < 0xFFFF) || !isAddressAccessable(*pIATAddress) ) { if ( (*(pIATAddress - 1) < 0xFFFF) || !isAddressAccessable(*(pIATAddress - 1)) ) { //IAT end if ((DWORD_PTR)(pIATAddress - 2) >= (DWORD_PTR)dataBuffer) { if (!isApiAddressValid(*(pIATAddress - 2))) { return (((DWORD_PTR)pIATAddress - (DWORD_PTR)dataBuffer) + baseAddress); } } else { return (((DWORD_PTR)pIATAddress - (DWORD_PTR)dataBuffer) + baseAddress); } } } pIATAddress--; } return baseAddress; } DWORD IATSearch::findIATSize(DWORD_PTR baseAddress, DWORD_PTR iatAddress, BYTE * dataBuffer, DWORD bufferSize) { DWORD_PTR *pIATAddress = 0; pIATAddress = (DWORD_PTR *)((iatAddress - baseAddress) + (DWORD_PTR)dataBuffer); #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findIATSize :: baseAddress %X iatAddress %X dataBuffer %X pIATAddress %X", baseAddress, iatAddress, dataBuffer, pIATAddress); #endif while((DWORD_PTR)pIATAddress < ((DWORD_PTR)dataBuffer + bufferSize - 1)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findIATSize :: %X %X %X", pIATAddress, *pIATAddress, *(pIATAddress + 1)); #endif if ( (*pIATAddress < 0xFFFF) || !isAddressAccessable(*pIATAddress) ) //normal is 0 { if ( (*(pIATAddress + 1) < 0xFFFF) || !isAddressAccessable(*(pIATAddress + 1)) ) { //IAT end if (!isApiAddressValid(*(pIATAddress + 2))) { return (DWORD)((DWORD_PTR)pIATAddress - (DWORD_PTR)dataBuffer - (iatAddress - baseAddress)); } } } pIATAddress++; } return bufferSize; } bool IATSearch::isAddressAccessable(DWORD_PTR address) { BYTE junk[3]; SIZE_T numberOfBytesRead = 0; if (ReadProcessMemory(hProcess, (LPCVOID)address, junk, sizeof(junk), &numberOfBytesRead)) { if (numberOfBytesRead == sizeof(junk)) { if (junk[0] != 0x00) { return true; } } } return false; } void IATSearch::findIATPointers(std::set & iatPointers) { #ifdef DEBUG_COMMENTS _DecodedInst inst; #endif for (unsigned int i = 0; i < decomposerInstructionsCount; i++) { if (decomposerResult[i].flags != FLAG_NOT_DECODABLE) { if (META_GET_FC(decomposerResult[i].meta) == FC_CALL || META_GET_FC(decomposerResult[i].meta) == FC_UNC_BRANCH) { if (decomposerResult[i].size >= 5) { #ifdef _WIN64 if (decomposerResult[i].flags & FLAG_RIP_RELATIVE) { #ifdef DEBUG_COMMENTS distorm_format(&decomposerCi, &decomposerResult[i], &inst); Scylla::debugLog.log(L"%S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL, inst.mnemonic.p, inst.operands.p, decomposerResult[i].ops[0].type, decomposerResult[i].size, INSTRUCTION_GET_RIP_TARGET(&decomposerResult[i])); #endif iatPointers.insert(INSTRUCTION_GET_RIP_TARGET(&decomposerResult[i])); } #else if (decomposerResult[i].ops[0].type == O_DISP) { //jmp dword ptr || call dword ptr #ifdef DEBUG_COMMENTS distorm_format(&decomposerCi, &decomposerResult[i], &inst); Scylla::debugLog.log(L"%S %S %d %d - target address: " PRINTF_DWORD_PTR_FULL, inst.mnemonic.p, inst.operands.p, decomposerResult[i].ops[0].type, decomposerResult[i].size, decomposerResult[i].disp); #endif iatPointers.insert((DWORD_PTR)decomposerResult[i].disp); } #endif } } } } } void IATSearch::findExecutableMemoryPagesByStartAddress( DWORD_PTR startAddress, DWORD_PTR* baseAddress, SIZE_T* memorySize ) { MEMORY_BASIC_INFORMATION memBasic = {0}; DWORD_PTR tempAddress; *memorySize = 0; *baseAddress = 0; if (VirtualQueryEx(hProcess,(LPCVOID)startAddress, &memBasic, sizeof(MEMORY_BASIC_INFORMATION)) != sizeof(MEMORY_BASIC_INFORMATION)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"findIATStartAddress :: VirtualQueryEx error %u", GetLastError()); #endif return; } //search down do { *memorySize = memBasic.RegionSize; *baseAddress = (DWORD_PTR)memBasic.BaseAddress; tempAddress = (DWORD_PTR)memBasic.BaseAddress - 1; if (VirtualQueryEx(hProcess, (LPCVOID)tempAddress, &memBasic, sizeof(MEMORY_BASIC_INFORMATION)) != sizeof(MEMORY_BASIC_INFORMATION)) { break; } } while (isPageExecutable(memBasic.Protect)); tempAddress = *baseAddress; memBasic.RegionSize = *memorySize; *memorySize = 0; //search up do { tempAddress += memBasic.RegionSize; *memorySize += memBasic.RegionSize; if (VirtualQueryEx(hProcess, (LPCVOID)tempAddress, &memBasic, sizeof(MEMORY_BASIC_INFORMATION)) != sizeof(MEMORY_BASIC_INFORMATION)) { break; } } while (isPageExecutable(memBasic.Protect)); } void IATSearch::filterIATPointersList( std::set & iatPointers ) { std::set::iterator iter; if (iatPointers.size() <= 2) { return; } iter = iatPointers.begin(); std::advance(iter, iatPointers.size() / 2); //start in the middle, important! DWORD_PTR lastPointer = *iter; iter++; for (; iter != iatPointers.end(); iter++) { if ((*iter - lastPointer) > 0x100) //check difference { iatPointers.erase(iter, iatPointers.end()); break; } else { lastPointer = *iter; } } bool erased = true; while(erased) { iter = iatPointers.begin(); lastPointer = *iter; iter++; for (; iter != iatPointers.end(); iter++) { if ((*iter - lastPointer) > 0x100) //check difference { iter--; iatPointers.erase(iter); erased = true; break; } else { erased = false; lastPointer = *iter; } } } } diff --git a/Scylla/IATSearch.h b/Scylla/IATSearch.h index 6182034..46acc95 100644 --- a/Scylla/IATSearch.h +++ b/Scylla/IATSearch.h @@ -1,35 +1,34 @@ #pragma once #include "ApiReader.h" #include class IATSearch : protected ApiReader { public: DWORD_PTR memoryAddress; SIZE_T memorySize; bool searchImportAddressTableInProcess(DWORD_PTR startAddress, DWORD_PTR* addressIAT, DWORD* sizeIAT, bool advanced); private: DWORD_PTR findAPIAddressInIAT(DWORD_PTR startAddress); bool findIATAdvanced(DWORD_PTR startAddress,DWORD_PTR* addressIAT, DWORD* sizeIAT); 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); void findIATPointers(std::set & iatPointers); - bool isPageExecutable(DWORD value); void findExecutableMemoryPagesByStartAddress( DWORD_PTR startAddress, DWORD_PTR* baseAddress, SIZE_T* memorySize ); void filterIATPointersList( std::set & iatPointers ); }; diff --git a/Scylla/ProcessAccessHelp.cpp b/Scylla/ProcessAccessHelp.cpp index 028fda0..cc9ffc7 100644 --- a/Scylla/ProcessAccessHelp.cpp +++ b/Scylla/ProcessAccessHelp.cpp @@ -1,884 +1,914 @@ #include "ProcessAccessHelp.h" #include "Scylla.h" #include "NativeWinApi.h" #include "PeParser.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|PROCESS_SUSPEND_RESUME|PROCESS_TERMINATE, 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() { if (hProcess) { CloseHandle(hProcess); hProcess = 0; } moduleList.clear(); targetImageBase = 0; selectedModule = 0; } bool ProcessAccessHelp::readMemoryPartlyFromProcess(DWORD_PTR address, SIZE_T size, LPVOID dataBuffer) { DWORD_PTR addressPart = 0; DWORD_PTR readBytes = 0; DWORD_PTR bytesToRead = 0; MEMORY_BASIC_INFORMATION memBasic = {0}; bool returnValue = false; if (!hProcess) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryPartlyFromProcess :: hProcess == NULL"); #endif return returnValue; } if (!readMemoryFromProcess(address, size, dataBuffer)) { addressPart = address; do { if (!VirtualQueryEx(ProcessAccessHelp::hProcess,(LPCVOID)addressPart,&memBasic,sizeof(memBasic))) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryPartlyFromProcess :: Error VirtualQueryEx %X %X err: %u", addressPart,size, GetLastError()); #endif break; } bytesToRead = memBasic.RegionSize; if ( (readBytes+bytesToRead) > size) { bytesToRead = size - readBytes; } if (memBasic.State == MEM_COMMIT) { if (!readMemoryFromProcess(addressPart, bytesToRead, (LPVOID)((DWORD_PTR)dataBuffer + readBytes))) { break; } } else { ZeroMemory((LPVOID)((DWORD_PTR)dataBuffer + readBytes),bytesToRead); } readBytes += bytesToRead; addressPart += memBasic.RegionSize; } while (readBytes < size); if (readBytes == size) { returnValue = true; } } else { returnValue = true; } return returnValue; } bool ProcessAccessHelp::writeMemoryToProcess(DWORD_PTR address, SIZE_T size, LPVOID dataBuffer) { SIZE_T lpNumberOfBytesWritten = 0; if (!hProcess) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryFromProcess :: hProcess == NULL"); #endif return false; } return (WriteProcessMemory(hProcess,(LPVOID)address, dataBuffer, size,&lpNumberOfBytesWritten) != FALSE); } 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)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"readMemoryFromProcess :: Error ReadProcessMemory %X %X err: %u", address, size, GetLastError()); #endif 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 true; //not all instructions fit in buffer } } 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); } 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) { 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::writeMemoryToNewFile(const WCHAR * file,DWORD size, LPCVOID dataBuffer) { HANDLE hFile = CreateFile(file, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { bool resultValue = writeMemoryToFile(hFile,0,size,dataBuffer); CloseHandle(hFile); return resultValue; } else { 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) && dataBuffer) { 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, 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) { PeParser peFile(filePath, false); return peFile.getEntryPoint(); } 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; } DWORD ProcessAccessHelp::getModuleHandlesFromProcess(const HANDLE hProcess, HMODULE ** hMods) { DWORD count = 30; DWORD cbNeeded = 0; bool notEnough = true; *hMods = new HMODULE[count]; do { if (!EnumProcessModules(hProcess, *hMods, count * sizeof(HMODULE), &cbNeeded)) { #ifdef DEBUG_COMMENTS Scylla::debugLog.log(L"getModuleHandlesFromProcess :: EnumProcessModules failed count %d", count); #endif delete [] *hMods; return 0; } if ((count * sizeof(HMODULE)) < cbNeeded) { delete [] *hMods; count = cbNeeded / sizeof(HMODULE); *hMods = new HMODULE[count]; } else { notEnough = false; } } while (notEnough); return cbNeeded / sizeof(HMODULE); } void ProcessAccessHelp::setCurrentProcessAsTarget() { ProcessAccessHelp::hProcess = GetCurrentProcess(); } bool ProcessAccessHelp::suspendProcess() { if (NativeWinApi::NtSuspendProcess) { if (NT_SUCCESS( NativeWinApi::NtSuspendProcess(ProcessAccessHelp::hProcess) )) { return true; } } return false; } bool ProcessAccessHelp::resumeProcess() { if (NativeWinApi::NtResumeProcess) { if (NT_SUCCESS( NativeWinApi::NtResumeProcess(ProcessAccessHelp::hProcess) )) { return true; } } return false; } bool ProcessAccessHelp::terminateProcess() { if (NativeWinApi::NtTerminateProcess) { if (NT_SUCCESS( NativeWinApi::NtTerminateProcess(ProcessAccessHelp::hProcess, 0) )) { return true; } } return false; +} + +bool ProcessAccessHelp::isPageExecutable( DWORD Protect ) +{ + if (Protect & PAGE_NOCACHE) Protect ^= PAGE_NOCACHE; + if (Protect & PAGE_GUARD) Protect ^= PAGE_GUARD; + if (Protect & PAGE_WRITECOMBINE) Protect ^= PAGE_WRITECOMBINE; + + switch(Protect) + { + case PAGE_EXECUTE: + { + return true; + } + case PAGE_EXECUTE_READ: + { + return true; + } + case PAGE_EXECUTE_READWRITE: + { + return true; + } + case PAGE_EXECUTE_WRITECOPY: + { + return true; + } + default: + return false; + } + } \ No newline at end of file diff --git a/Scylla/ProcessAccessHelp.h b/Scylla/ProcessAccessHelp.h index 2f309ec..a997f63 100644 --- a/Scylla/ProcessAccessHelp.h +++ b/Scylla/ProcessAccessHelp.h @@ -1,227 +1,228 @@ #pragma once #include #include #include /************************************************************************/ /* distorm */ /************************************************************************/ #include #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 { 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 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 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); static bool writeMemoryToProcess(DWORD_PTR address, SIZE_T size, LPVOID dataBuffer); /* * Read memory from target process and ignore no data pages */ static bool readMemoryPartlyFromProcess(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 new file */ static bool writeMemoryToNewFile(const WCHAR * file,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); static DWORD getModuleHandlesFromProcess(const HANDLE hProcess, HMODULE ** hMods ); static void setCurrentProcessAsTarget(); static bool suspendProcess(); static bool resumeProcess(); static bool terminateProcess(); + static bool isPageExecutable( DWORD Protect ); };