Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions installer/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Can be invoked directly by users or spawned by Binary Ninja API.
*
* Usage:
* windbg-installer install [--path <dir>] [--windbg-version <ver>] [--quiet] [--json]
* windbg-installer install [--path <dir>] [--windbg-version <ver>] [--bundle <file>] [--quiet] [--json]
* windbg-installer version [--path <dir>] [--json]
* windbg-installer --help
*
Expand Down Expand Up @@ -237,6 +237,8 @@ void PrintUsage(const char* programName) {
<< " (default: %APPDATA%\\Binary Ninja\\windbg)\n"
<< " --windbg-version <ver>\n"
<< " WinDbg version to install (default: " << kDefaultVersion << ")\n"
<< " --bundle <file>\n"
<< " Install an already-downloaded .msixbundle (for testing)\n"
<< " --update Update mode: wait for Binary Ninja to exit first\n"
<< " (use this when WinDbg DLLs may be loaded)\n"
<< " --quiet Suppress progress output (exit code only)\n"
Expand Down Expand Up @@ -267,7 +269,8 @@ void PrintBanner() {
}

/* Command: install */
int CmdInstall(const std::string& installPath, const std::string& version, OutputMode mode, bool isUpdate) {
int CmdInstall(const std::string& installPath, const std::string& version, OutputMode mode, bool isUpdate,
const std::string& bundlePath) {
/* Determine and print install path */
std::string targetPath = installPath.empty() ? GetDefaultInstallPath() : installPath;

Expand Down Expand Up @@ -295,6 +298,15 @@ int CmdInstall(const std::string& installPath, const std::string& version, Outpu
config.installPath = targetPath;
config.version = version;
config.updateSettings = true;
config.localBundlePath = bundlePath;

if (!bundlePath.empty()) {
if (mode == OutputMode::Human) {
std::cout << " Using local bundle (skipping download): " << bundlePath << "\n\n";
} else if (mode == OutputMode::Json) {
std::cout << "{\"type\":\"info\",\"localBundle\":\"" << bundlePath << "\"}" << std::endl;
}
}

std::string lastStep;

Expand Down Expand Up @@ -439,6 +451,7 @@ int main(int argc, char* argv[]) {
std::string command;
std::string installPath;
std::string version;
std::string bundlePath;
OutputMode mode = OutputMode::Human;
bool isUpdate = false;

Expand All @@ -460,6 +473,13 @@ int main(int argc, char* argv[]) {
std::cerr << "Error: --windbg-version requires a version argument\n";
return 1;
}
} else if (strcmp(argv[i], "--bundle") == 0) {
if (i + 1 < argc) {
bundlePath = argv[++i];
} else {
std::cerr << "Error: --bundle requires a file path argument\n";
return 1;
}
} else if (strcmp(argv[i], "--quiet") == 0 || strcmp(argv[i], "-q") == 0) {
mode = OutputMode::Quiet;
} else if (strcmp(argv[i], "--json") == 0) {
Expand Down Expand Up @@ -493,7 +513,11 @@ int main(int argc, char* argv[]) {

/* Execute command */
if (command == "install") {
return CmdInstall(installPath, version, mode, isUpdate);
if (!bundlePath.empty() && version.empty()) {
std::cerr << "Error: --bundle requires --windbg-version so the installed version is recorded correctly\n";
return 1;
}
return CmdInstall(installPath, version, mode, isUpdate, bundlePath);
} else if (command == "version") {
return CmdVersion(installPath, mode);
} else {
Expand Down
61 changes: 37 additions & 24 deletions installer/windbg_installer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,31 +171,44 @@ InstallResult Install(const InstallConfig& config) {
std::string version = config.version.empty() ? kDefaultVersion : config.version;
Log(logCallback, LOG_INFO, "Installing WinDbg/TTD version " + version);

/* Step 2: Download MSIX bundle (this is the main download that shows progress) */
std::string msixUrl = BuildMsixBundleUrl(version);
ReportProgress(progressCallback, "Downloading WinDbg/TTD package from:", 0);
ReportProgress(progressCallback, msixUrl, 0);

/* Note: the extension must be a recognized MSIX/APPX extension (not .zip) so that
* WinVerifyTrust engages the AppX signature provider during Step 2.5 verification. */
std::string msixPath = GetTempFilePath(".msixbundle");
tempFiles.push_back(msixPath);

auto msixDownloadProgressCb = [&](const DownloadProgress& dp) {
/* Report download percentage (0-100%) directly - this is the only step that needs progress display */
int percent = 0;
if (dp.totalBytes > 0) {
percent = (int)(100 * dp.bytesDownloaded / dp.totalBytes);
std::string msixPath;
if (!config.localBundlePath.empty()) {
if (!fs::exists(config.localBundlePath)) {
std::string error = "Local bundle not found: " + config.localBundlePath;
Log(logCallback, LOG_ERROR, error);
CleanupTempFiles(tempFiles, logCallback);
return InstallResult(false, error);
}
msixPath = config.localBundlePath;
ReportProgress(progressCallback, "Using local MSIX bundle (skipping download)...", 0);
Log(logCallback, LOG_INFO, "Using local MSIX bundle, skipping download: " + msixPath);
} else {
/* Step 2: Download MSIX bundle (this is the main download that shows progress) */
std::string msixUrl = BuildMsixBundleUrl(version);
ReportProgress(progressCallback, "Downloading WinDbg/TTD package from:", 0);
ReportProgress(progressCallback, msixUrl, 0);

/* Note: the extension must be a recognized MSIX/APPX extension (not .zip) so that
* WinVerifyTrust engages the AppX signature provider during Step 2.5 verification. */
msixPath = GetTempFilePath(".msixbundle");
tempFiles.push_back(msixPath);

auto msixDownloadProgressCb = [&](const DownloadProgress& dp) {
/* Report download percentage (0-100%) directly - this is the only step that needs progress display */
int percent = 0;
if (dp.totalBytes > 0) {
percent = (int)(100 * dp.bytesDownloaded / dp.totalBytes);
}
ReportProgress(progressCallback, "Downloading...", percent,
dp.bytesDownloaded, dp.totalBytes, dp.bytesPerSecond);
};

if (!DownloadFileWithProgress(msixUrl, msixPath, msixDownloadProgressCb, logCallback)) {
std::string error = "Failed to download MSIX bundle";
Log(logCallback, LOG_ERROR, error);
CleanupTempFiles(tempFiles, logCallback);
return InstallResult(false, error);
}
ReportProgress(progressCallback, "Downloading...", percent,
dp.bytesDownloaded, dp.totalBytes, dp.bytesPerSecond);
};

if (!DownloadFileWithProgress(msixUrl, msixPath, msixDownloadProgressCb, logCallback)) {
std::string error = "Failed to download MSIX bundle";
Log(logCallback, LOG_ERROR, error);
CleanupTempFiles(tempFiles, logCallback);
return InstallResult(false, error);
}

/* Step 2.5: Verify the downloaded bundle is genuinely signed by Microsoft.
Expand Down
1 change: 1 addition & 0 deletions installer/windbg_installer.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ struct InstallConfig {
std::string installPath; /* Override default install path (empty = use default) */
std::string version; /* WinDbg version to install (empty = kDefaultVersion) */
bool updateSettings; /* Whether to update Binary Ninja settings (default: true) */
std::string localBundlePath; /* Already-downloaded .msixbundle to install without deleting */
ProgressCallback onProgress; /* Progress callback */
LogCallback onLog; /* Logging callback */

Expand Down