From 91797669ee0c6536a2ea6b0809411e4db0a284b5 Mon Sep 17 00:00:00 2001 From: Mykhailo Pylyp Date: Tue, 20 Jul 2021 10:33:00 +0300 Subject: [PATCH] [Bug Report Tool] Report installation folder structure (#12425) * Report installation folder structure * Hanlde case when GetModuleFileName fails * spelling * PR comments --- .github/actions/spell-check/expect.txt | 5 + .../BugReportTool/BugReportTool.vcxproj | 4 +- .../BugReportTool.vcxproj.filters | 2 + .../BugReportTool/InstallationFolder.cpp | 216 ++++++++++++++++++ .../BugReportTool/InstallationFolder.h | 7 + tools/BugReportTool/BugReportTool/Main.cpp | 3 + 6 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 tools/BugReportTool/BugReportTool/InstallationFolder.cpp create mode 100644 tools/BugReportTool/BugReportTool/InstallationFolder.h diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 244ac0200b..f3cc098738 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -192,6 +192,7 @@ buildtools buildtransitive BValue bytearray +CALG callbackptr Camer Cangjie @@ -707,6 +708,7 @@ Hardlines HARDWAREINPUT hashcode Hashset +HASHVAL hbitmap hbmp hbr @@ -714,6 +716,8 @@ HBRBACKGROUND HBRUSH hcblack HCERTSTORE +HCRYPTHASH +HCRYPTPROV hcwhite hdc HDF @@ -2214,6 +2218,7 @@ VDId vec VERBSONLY VERBW +VERIFYCONTEXT verrsrc VERSIONINFO Versioning diff --git a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj index ed31bd4245..476c1116ac 100644 --- a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj +++ b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj @@ -30,7 +30,7 @@ Console - Wevtapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + Version.lib;Wevtapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) @@ -38,6 +38,7 @@ TurnOffAllWarnings + @@ -56,6 +57,7 @@ + diff --git a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters index d7099bdaac..64d306e745 100644 --- a/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters +++ b/tools/BugReportTool/BugReportTool/BugReportTool.vcxproj.filters @@ -13,6 +13,7 @@ + @@ -30,5 +31,6 @@ + \ No newline at end of file diff --git a/tools/BugReportTool/BugReportTool/InstallationFolder.cpp b/tools/BugReportTool/BugReportTool/InstallationFolder.cpp new file mode 100644 index 0000000000..82887c89e9 --- /dev/null +++ b/tools/BugReportTool/BugReportTool/InstallationFolder.cpp @@ -0,0 +1,216 @@ +#include "InstallationFolder.h" + +#include +#include +#include +#include + +using namespace std; +using std::filesystem::directory_iterator; +using std::filesystem::path; + +wstring GetVersion(path filePath) +{ + DWORD verHandle = 0; + UINT size = 0; + LPVOID lpBuffer = nullptr; + DWORD verSize = GetFileVersionInfoSize(filePath.c_str(), &verHandle); + wstring version = L"None"; + + if (verSize != 0) + { + LPSTR verData = new char[verSize]; + + if (GetFileVersionInfo(filePath.c_str(), verHandle, verSize, verData)) + { + if (VerQueryValue(verData, L"\\", &lpBuffer, &size)) + { + if (size) + { + VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer; + if (verInfo->dwSignature == 0xfeef04bd) + { + version = + std::to_wstring((verInfo->dwFileVersionMS >> 16) & 0xffff) + L"." + + std::to_wstring((verInfo->dwFileVersionMS >> 0) & 0xffff) + L"." + + std::to_wstring((verInfo->dwFileVersionLS >> 16) & 0xffff) + L"." + + std::to_wstring((verInfo->dwFileVersionLS >> 0) & 0xffff); + } + } + } + } + + delete[] verData; + } + + return version; +} + +optional GetRootPath() +{ + WCHAR modulePath[MAX_PATH]; + if (!GetModuleFileName(NULL, modulePath, MAX_PATH)) + { + return nullopt; + } + + path rootPath = path(modulePath); + rootPath = rootPath.remove_filename(); + rootPath = rootPath.append(".."); + return std::filesystem::canonical(rootPath); +} + +wstring GetChecksum(path filePath) +{ + DWORD dwStatus = 0; + BOOL bResult = FALSE; + HCRYPTPROV hProv = 0; + HCRYPTHASH hHash = 0; + HANDLE hFile = NULL; + constexpr int bufferSize = 1024; + BYTE rgbFile[bufferSize]; + DWORD cbRead = 0; + constexpr int md5Length = 16; + BYTE rgbHash[md5Length]; + DWORD cbHash = 0; + CHAR rgbDigits[] = "0123456789abcdef"; + LPCWSTR filename = filePath.c_str(); + hFile = CreateFile(filename, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL); + + if (INVALID_HANDLE_VALUE == hFile) + { + return L"CreateFile() failed. " + get_last_error_or_default(GetLastError()); + } + + // Get handle to the crypto provider + if (!CryptAcquireContext(&hProv, + NULL, + NULL, + PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + { + CloseHandle(hFile); + return L"CryptAcquireContext() failed. " + get_last_error_or_default(GetLastError()); + } + + if (!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) + { + CloseHandle(hFile); + CryptReleaseContext(hProv, 0); + return L"CryptCreateHash() failed. " + get_last_error_or_default(GetLastError()); + } + + while (bResult = ReadFile(hFile, rgbFile, bufferSize, &cbRead, NULL)) + { + if (0 == cbRead) + { + break; + } + + if (!CryptHashData(hHash, rgbFile, cbRead, 0)) + { + CryptReleaseContext(hProv, 0); + CryptDestroyHash(hHash); + CloseHandle(hFile); + return L"CryptHashData() failed. " + get_last_error_or_default(GetLastError());; + } + } + + if (!bResult) + { + CryptReleaseContext(hProv, 0); + CryptDestroyHash(hHash); + CloseHandle(hFile); + return L"ReadFile() failed. " + get_last_error_or_default(GetLastError());; + } + + cbHash = md5Length; + std::wstring result = L""; + if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0)) + { + for (DWORD i = 0; i < cbHash; i++) + { + result += rgbDigits[rgbHash[i] >> 4]; + result += rgbDigits[rgbHash[i] & 0xf]; + } + } + else + { + std::wstring result = L"CryptGetHashParam() failed. " + get_last_error_or_default(GetLastError());; + } + + CryptDestroyHash(hHash); + CryptReleaseContext(hProv, 0); + CloseHandle(hFile); + + return result; +} + +class Reporter +{ +private: + std::wofstream os; + std::wofstream GetOutputStream(const path& tmpDir) + { + auto path = tmpDir; + path += "installationFolderStructure.txt"; + std::wofstream os = std::wofstream(path); + return os; + } +public: + Reporter(const path& tmpDir) + { + os = GetOutputStream(tmpDir); + } + + void Report(path dirPath, int indentation = 0) + { + set> paths; + try + { + directory_iterator end_it; + for (directory_iterator it(dirPath); it != end_it; ++it) + { + paths.insert({ it->path(), it->is_directory() }); + } + } + catch (filesystem::filesystem_error err) + { + os << err.what() << endl; + } + + for (auto filePair : paths) + { + auto filePath = filePair.first; + auto isDirectory = filePair.second; + + auto fileName = filePath.wstring().substr(dirPath.wstring().size() + 1); + os << wstring(indentation, ' ') << fileName << " "; + if (!isDirectory) + { + os << GetVersion(filePath) << " " << GetChecksum(filePath); + } + + os << endl; + if (isDirectory) + { + Report(filePath, indentation + 2); + } + } + } +}; + +void InstallationFolder::ReportStructure(const path& tmpDir) +{ + auto rootPath = GetRootPath(); + if (rootPath) + { + Reporter(tmpDir).Report(rootPath.value()); + } +} diff --git a/tools/BugReportTool/BugReportTool/InstallationFolder.h b/tools/BugReportTool/BugReportTool/InstallationFolder.h new file mode 100644 index 0000000000..69bc1babb4 --- /dev/null +++ b/tools/BugReportTool/BugReportTool/InstallationFolder.h @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace InstallationFolder +{ + void ReportStructure(const std::filesystem::path& tmpDir); +}; diff --git a/tools/BugReportTool/BugReportTool/Main.cpp b/tools/BugReportTool/BugReportTool/Main.cpp index 9a7d2ee19a..011f8e6029 100644 --- a/tools/BugReportTool/BugReportTool/Main.cpp +++ b/tools/BugReportTool/BugReportTool/Main.cpp @@ -17,6 +17,7 @@ #include "ReportMonitorInfo.h" #include "RegistryUtils.h" #include "EventViewer.h" +#include "InstallationFolder.h" using namespace std; using namespace std::filesystem; @@ -298,6 +299,8 @@ int wmain(int argc, wchar_t* argv[], wchar_t*) return 1; } + InstallationFolder::ReportStructure(reportDir); + // Hide sensitive information HideUserPrivateInfo(reportDir);